diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/pcie/trans.c')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 269 | 
1 files changed, 135 insertions, 134 deletions
| diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 7d319b6863fe..5bafb3f46eb8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -19,11 +19,6 @@   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   * General Public License for more details.   * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - *   * The full GNU General Public License is included in this distribution   * in the file called COPYING.   * @@ -92,7 +87,7 @@  #define IWL_FW_MEM_EXTENDED_START	0x40000  #define IWL_FW_MEM_EXTENDED_END		0x57FFF -static void iwl_trans_pcie_dump_regs(struct iwl_trans *trans) +void iwl_trans_pcie_dump_regs(struct iwl_trans *trans)  {  #define PCI_DUMP_SIZE	64  #define PREFIX_LEN	32 @@ -190,72 +185,42 @@ static void iwl_trans_pcie_sw_reset(struct iwl_trans *trans)  static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)  { -	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - -	if (!trans_pcie->fw_mon_page) -		return; +	int i; -	dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys, -		       trans_pcie->fw_mon_size, DMA_FROM_DEVICE); -	__free_pages(trans_pcie->fw_mon_page, -		     get_order(trans_pcie->fw_mon_size)); -	trans_pcie->fw_mon_page = NULL; -	trans_pcie->fw_mon_phys = 0; -	trans_pcie->fw_mon_size = 0; +	for (i = 0; i < trans->num_blocks; i++) { +		dma_free_coherent(trans->dev, trans->fw_mon[i].size, +				  trans->fw_mon[i].block, +				  trans->fw_mon[i].physical); +		trans->fw_mon[i].block = NULL; +		trans->fw_mon[i].physical = 0; +		trans->fw_mon[i].size = 0; +		trans->num_blocks--; +	}  } -void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) +static void iwl_pcie_alloc_fw_monitor_block(struct iwl_trans *trans, +					    u8 max_power, u8 min_power)  { -	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); -	struct page *page = NULL; -	dma_addr_t phys; +	void *cpu_addr = NULL; +	dma_addr_t phys = 0;  	u32 size = 0;  	u8 power; -	if (!max_power) { -		/* default max_power is maximum */ -		max_power = 26; -	} else { -		max_power += 11; -	} - -	if (WARN(max_power > 26, -		 "External buffer size for monitor is too big %d, check the FW TLV\n", -		 max_power)) -		return; - -	if (trans_pcie->fw_mon_page) { -		dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys, -					   trans_pcie->fw_mon_size, -					   DMA_FROM_DEVICE); -		return; -	} - -	phys = 0; -	for (power = max_power; power >= 11; power--) { -		int order; - +	for (power = max_power; power >= min_power; power--) {  		size = BIT(power); -		order = get_order(size); -		page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO, -				   order); -		if (!page) +		cpu_addr = dma_alloc_coherent(trans->dev, size, &phys, +					      GFP_KERNEL | __GFP_NOWARN | +					      __GFP_ZERO | __GFP_COMP); +		if (!cpu_addr)  			continue; -		phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order, -				    DMA_FROM_DEVICE); -		if (dma_mapping_error(trans->dev, phys)) { -			__free_pages(page, order); -			page = NULL; -			continue; -		}  		IWL_INFO(trans, -			 "Allocated 0x%08x bytes (order %d) for firmware monitor.\n", -			 size, order); +			 "Allocated 0x%08x bytes for firmware monitor.\n", +			 size);  		break;  	} -	if (WARN_ON_ONCE(!page)) +	if (WARN_ON_ONCE(!cpu_addr))  		return;  	if (power != max_power) @@ -264,9 +229,34 @@ void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power)  			(unsigned long)BIT(power - 10),  			(unsigned long)BIT(max_power - 10)); -	trans_pcie->fw_mon_page = page; -	trans_pcie->fw_mon_phys = phys; -	trans_pcie->fw_mon_size = size; +	trans->fw_mon[trans->num_blocks].block = cpu_addr; +	trans->fw_mon[trans->num_blocks].physical = phys; +	trans->fw_mon[trans->num_blocks].size = size; +	trans->num_blocks++; +} + +void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) +{ +	if (!max_power) { +		/* default max_power is maximum */ +		max_power = 26; +	} else { +		max_power += 11; +	} + +	if (WARN(max_power > 26, +		 "External buffer size for monitor is too big %d, check the FW TLV\n", +		 max_power)) +		return; + +	/* +	 * This function allocats the default fw monitor. +	 * The optional additional ones will be allocated in runtime +	 */ +	if (trans->num_blocks) +		return; + +	iwl_pcie_alloc_fw_monitor_block(trans, max_power, 11);  }  static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) @@ -930,7 +920,6 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,  void iwl_pcie_apply_destination(struct iwl_trans *trans)  { -	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);  	const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg_dest_tlv;  	int i; @@ -942,7 +931,7 @@ void iwl_pcie_apply_destination(struct iwl_trans *trans)  	else  		IWL_WARN(trans, "PCI should have external buffer debug\n"); -	for (i = 0; i < trans->dbg_dest_reg_num; i++) { +	for (i = 0; i < trans->dbg_n_dest_reg; i++) {  		u32 addr = le32_to_cpu(dest->reg_ops[i].addr);  		u32 val = le32_to_cpu(dest->reg_ops[i].val); @@ -981,18 +970,18 @@ void iwl_pcie_apply_destination(struct iwl_trans *trans)  	}  monitor: -	if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) { +	if (dest->monitor_mode == EXTERNAL_MODE && trans->fw_mon[0].size) {  		iwl_write_prph(trans, le32_to_cpu(dest->base_reg), -			       trans_pcie->fw_mon_phys >> dest->base_shift); +			       trans->fw_mon[0].physical >> dest->base_shift);  		if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000)  			iwl_write_prph(trans, le32_to_cpu(dest->end_reg), -				       (trans_pcie->fw_mon_phys + -					trans_pcie->fw_mon_size - 256) >> +				       (trans->fw_mon[0].physical + +					trans->fw_mon[0].size - 256) >>  						dest->end_shift);  		else  			iwl_write_prph(trans, le32_to_cpu(dest->end_reg), -				       (trans_pcie->fw_mon_phys + -					trans_pcie->fw_mon_size) >> +				       (trans->fw_mon[0].physical + +					trans->fw_mon[0].size) >>  						dest->end_shift);  	}  } @@ -1000,7 +989,6 @@ monitor:  static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,  				const struct fw_img *image)  { -	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);  	int ret = 0;  	int first_ucode_section; @@ -1030,12 +1018,12 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,  	    trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) {  		iwl_pcie_alloc_fw_monitor(trans, 0); -		if (trans_pcie->fw_mon_size) { +		if (trans->fw_mon[0].size) {  			iwl_write_prph(trans, MON_BUFF_BASE_ADDR, -				       trans_pcie->fw_mon_phys >> 4); +				       trans->fw_mon[0].physical >> 4);  			iwl_write_prph(trans, MON_BUFF_END_ADDR, -				       (trans_pcie->fw_mon_phys + -					trans_pcie->fw_mon_size) >> 4); +				       (trans->fw_mon[0].physical + +					trans->fw_mon[0].size) >> 4);  		}  	} else if (trans->dbg_dest_tlv) {  		iwl_pcie_apply_destination(trans); @@ -1262,13 +1250,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)  	trans_pcie->is_down = true;  	/* Stop dbgc before stopping device */ -	if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { -		iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); -	} else { -		iwl_write_prph(trans, DBGC_IN_SAMPLE, 0); -		udelay(100); -		iwl_write_prph(trans, DBGC_OUT_CTRL, 0); -	} +	_iwl_fw_dbg_stop_recording(trans, NULL);  	/* tell the device to stop sending interrupts */  	iwl_disable_interrupts(trans); @@ -1830,18 +1812,30 @@ static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs)  	return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs);  } +static u32 iwl_trans_pcie_prph_msk(struct iwl_trans *trans) +{ +	if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22560) +		return 0x00FFFFFF; +	else +		return 0x000FFFFF; +} +  static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg)  { +	u32 mask = iwl_trans_pcie_prph_msk(trans); +  	iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, -			       ((reg & 0x000FFFFF) | (3 << 24))); +			       ((reg & mask) | (3 << 24)));  	return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT);  }  static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr,  				      u32 val)  { +	u32 mask = iwl_trans_pcie_prph_msk(trans); +  	iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR, -			       ((addr & 0x000FFFFF) | (3 << 24))); +			       ((addr & mask) | (3 << 24)));  	iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val);  } @@ -2013,7 +2007,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans,  		if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) {  			struct iwl_trans_pcie_removal *removal; -			if (trans_pcie->scheduled_for_removal) +			if (test_bit(STATUS_TRANS_DEAD, &trans->status))  				goto err;  			IWL_ERR(trans, "Device gone - scheduling removal!\n"); @@ -2039,7 +2033,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans,  			 * we don't need to clear this flag, because  			 * the trans will be freed and reallocated.  			*/ -			trans_pcie->scheduled_for_removal = true; +			set_bit(STATUS_TRANS_DEAD, &trans->status);  			removal->pdev = to_pci_dev(trans->dev);  			INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); @@ -2266,6 +2260,10 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, int txq_idx)  	unsigned long now = jiffies;  	u8 wr_ptr; +	/* Make sure the NIC is still alive in the bus */ +	if (test_bit(STATUS_TRANS_DEAD, &trans->status)) +		return -ENODEV; +  	if (!test_bit(txq_idx, trans_pcie->queue_used))  		return -EINVAL; @@ -2861,10 +2859,9 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,  			    struct iwl_fw_error_dump_data **data,  			    u32 monitor_len)  { -	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);  	u32 len = 0; -	if ((trans_pcie->fw_mon_page && +	if ((trans->num_blocks &&  	     trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||  	    trans->dbg_dest_tlv) {  		struct iwl_fw_error_dump_fw_mon *fw_mon_data; @@ -2892,22 +2889,12 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,  			cpu_to_le32(iwl_read_prph(trans, base));  		len += sizeof(**data) + sizeof(*fw_mon_data); -		if (trans_pcie->fw_mon_page) { -			/* -			 * The firmware is now asserted, it won't write anything -			 * to the buffer. CPU can take ownership to fetch the -			 * data. The buffer will be handed back to the device -			 * before the firmware will be restarted. -			 */ -			dma_sync_single_for_cpu(trans->dev, -						trans_pcie->fw_mon_phys, -						trans_pcie->fw_mon_size, -						DMA_FROM_DEVICE); +		if (trans->num_blocks) {  			memcpy(fw_mon_data->data, -			       page_address(trans_pcie->fw_mon_page), -			       trans_pcie->fw_mon_size); +			       trans->fw_mon[0].block, +			       trans->fw_mon[0].size); -			monitor_len = trans_pcie->fw_mon_size; +			monitor_len = trans->fw_mon[0].size;  		} else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) {  			/*  			 * Update pointers to reflect actual values after @@ -2943,36 +2930,15 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,  	return len;  } -static struct iwl_trans_dump_data -*iwl_trans_pcie_dump_data(struct iwl_trans *trans, -			  const struct iwl_fw_dbg_trigger_tlv *trigger) +static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, int *len)  { -	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); -	struct iwl_fw_error_dump_data *data; -	struct iwl_txq *cmdq = trans_pcie->txq[trans_pcie->cmd_queue]; -	struct iwl_fw_error_dump_txcmd *txcmd; -	struct iwl_trans_dump_data *dump_data; -	u32 len, num_rbs = 0; -	u32 monitor_len; -	int i, ptr; -	bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && -			!trans->cfg->mq_rx_supported && -			trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_RB); - -	/* transport dump header */ -	len = sizeof(*dump_data); - -	/* host commands */ -	len += sizeof(*data) + -		cmdq->n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); - -	/* FW monitor */ -	if (trans_pcie->fw_mon_page) { -		len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + -		       trans_pcie->fw_mon_size; -		monitor_len = trans_pcie->fw_mon_size; +	if (trans->num_blocks) { +		*len += sizeof(struct iwl_fw_error_dump_data) + +			sizeof(struct iwl_fw_error_dump_fw_mon) + +			trans->fw_mon[0].size; +		return trans->fw_mon[0].size;  	} else if (trans->dbg_dest_tlv) { -		u32 base, end, cfg_reg; +		u32 base, end, cfg_reg, monitor_len;  		if (trans->dbg_dest_tlv->version == 1) {  			cfg_reg = le32_to_cpu(trans->dbg_dest_tlv->base_reg); @@ -3002,11 +2968,39 @@ static struct iwl_trans_dump_data  				end += (1 << trans->dbg_dest_tlv->end_shift);  			monitor_len = end - base;  		} -		len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + -		       monitor_len; -	} else { -		monitor_len = 0; +		*len += sizeof(struct iwl_fw_error_dump_data) + +			sizeof(struct iwl_fw_error_dump_fw_mon) + +			monitor_len; +		return monitor_len;  	} +	return 0; +} + +static struct iwl_trans_dump_data +*iwl_trans_pcie_dump_data(struct iwl_trans *trans, +			  const struct iwl_fw_dbg_trigger_tlv *trigger) +{ +	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); +	struct iwl_fw_error_dump_data *data; +	struct iwl_txq *cmdq = trans_pcie->txq[trans_pcie->cmd_queue]; +	struct iwl_fw_error_dump_txcmd *txcmd; +	struct iwl_trans_dump_data *dump_data; +	u32 len, num_rbs = 0; +	u32 monitor_len; +	int i, ptr; +	bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && +			!trans->cfg->mq_rx_supported && +			trans->dbg_dump_mask & BIT(IWL_FW_ERROR_DUMP_RB); + +	/* transport dump header */ +	len = sizeof(*dump_data); + +	/* host commands */ +	len += sizeof(*data) + +		cmdq->n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); + +	/* FW monitor */ +	monitor_len = iwl_trans_get_fw_monitor_len(trans, &len);  	if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) {  		if (!(trans->dbg_dump_mask & @@ -3175,7 +3169,6 @@ static void iwl_trans_pcie_resume(struct iwl_trans *trans)  	.ref = iwl_trans_pcie_ref,					\  	.unref = iwl_trans_pcie_unref,					\  	.dump_data = iwl_trans_pcie_dump_data,				\ -	.dump_regs = iwl_trans_pcie_dump_regs,				\  	.d3_suspend = iwl_trans_pcie_d3_suspend,			\  	.d3_resume = iwl_trans_pcie_d3_resume @@ -3277,6 +3270,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,  				       PCIE_LINK_STATE_CLKPM);  	} +	trans_pcie->def_rx_queue = 0; +  	if (cfg->use_tfh) {  		addr_size = 64;  		trans_pcie->max_tbs = IWL_TFH_NUM_TBS; @@ -3327,6 +3322,12 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,  	iwl_disable_interrupts(trans);  	trans->hw_rev = iwl_read32(trans, CSR_HW_REV); +	if (trans->hw_rev == 0xffffffff) { +		dev_err(&pdev->dev, "HW_REV=0xFFFFFFFF, PCI issues?\n"); +		ret = -EIO; +		goto out_no_pci; +	} +  	/*  	 * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have  	 * changed, and now the revision step also includes bit 0-1 (no more |