diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/fw.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw.c | 160 | 
1 files changed, 143 insertions, 17 deletions
| diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index ca38e9817374..bc5eac4960e1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -112,25 +112,27 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,  	struct iwl_mvm *mvm =  		container_of(notif_wait, struct iwl_mvm, notif_wait);  	struct iwl_mvm_alive_data *alive_data = data; -	struct mvm_alive_resp *palive; +	struct mvm_alive_resp_ver1 *palive1;  	struct mvm_alive_resp_ver2 *palive2; +	struct mvm_alive_resp *palive; -	if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) { -		palive = (void *)pkt->data; +	if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) { +		palive1 = (void *)pkt->data;  		mvm->support_umac_log = false;  		mvm->error_event_table = -			le32_to_cpu(palive->error_event_table_ptr); -		mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr); -		alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr); +			le32_to_cpu(palive1->error_event_table_ptr); +		mvm->log_event_table = +			le32_to_cpu(palive1->log_event_table_ptr); +		alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr); -		alive_data->valid = le16_to_cpu(palive->status) == +		alive_data->valid = le16_to_cpu(palive1->status) ==  				    IWL_ALIVE_STATUS_OK;  		IWL_DEBUG_FW(mvm,  			     "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", -			     le16_to_cpu(palive->status), palive->ver_type, -			     palive->ver_subtype, palive->flags); -	} else { +			     le16_to_cpu(palive1->status), palive1->ver_type, +			     palive1->ver_subtype, palive1->flags); +	} else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) {  		palive2 = (void *)pkt->data;  		mvm->error_event_table = @@ -156,6 +158,33 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,  		IWL_DEBUG_FW(mvm,  			     "UMAC version: Major - 0x%x, Minor - 0x%x\n",  			     palive2->umac_major, palive2->umac_minor); +	} else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) { +		palive = (void *)pkt->data; + +		mvm->error_event_table = +			le32_to_cpu(palive->error_event_table_ptr); +		mvm->log_event_table = +			le32_to_cpu(palive->log_event_table_ptr); +		alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr); +		mvm->umac_error_event_table = +			le32_to_cpu(palive->error_info_addr); +		mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr); +		mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size); + +		alive_data->valid = le16_to_cpu(palive->status) == +				    IWL_ALIVE_STATUS_OK; +		if (mvm->umac_error_event_table) +			mvm->support_umac_log = true; + +		IWL_DEBUG_FW(mvm, +			     "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", +			     le16_to_cpu(palive->status), palive->ver_type, +			     palive->ver_subtype, palive->flags); + +		IWL_DEBUG_FW(mvm, +			     "UMAC version: Major - 0x%x, Minor - 0x%x\n", +			     le32_to_cpu(palive->umac_major), +			     le32_to_cpu(palive->umac_minor));  	}  	return true; @@ -188,8 +217,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,  	struct iwl_sf_region st_fwrd_space;  	if (ucode_type == IWL_UCODE_REGULAR && -	    iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_CUSTOM) && -	    iwl_fw_dbg_conf_enabled(mvm->fw, FW_DBG_CUSTOM)) +	    iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE))  		fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);  	else  		fw = iwl_get_ucode_image(mvm, ucode_type); @@ -451,20 +479,97 @@ exit:  	iwl_free_resp(&cmd);  } -void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm) +int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, +				struct iwl_mvm_dump_desc *desc, +				unsigned int delay)  { +	if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) +		return -EBUSY; + +	if (WARN_ON(mvm->fw_dump_desc)) +		iwl_mvm_free_fw_dump_desc(mvm); + +	IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", +		 le32_to_cpu(desc->trig_desc.type)); + +	mvm->fw_dump_desc = desc; +  	/* stop recording */  	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {  		iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);  	} else {  		iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0); -		iwl_write_prph(mvm->trans, DBGC_OUT_CTRL, 0); +		/* wait before we collect the data till the DBGC stop */ +		udelay(100); +	} + +	queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); + +	return 0; +} + +int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, +			   const char *str, size_t len, unsigned int delay) +{ +	struct iwl_mvm_dump_desc *desc; + +	desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); +	if (!desc) +		return -ENOMEM; + +	desc->len = len; +	desc->trig_desc.type = cpu_to_le32(trig); +	memcpy(desc->trig_desc.data, str, len); + +	return iwl_mvm_fw_dbg_collect_desc(mvm, desc, delay); +} + +int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, +				struct iwl_fw_dbg_trigger_tlv *trigger, +				const char *fmt, ...) +{ +	unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); +	u16 occurrences = le16_to_cpu(trigger->occurrences); +	int ret, len = 0; +	char buf[64]; + +	if (!occurrences) +		return 0; + +	if (fmt) { +		va_list ap; + +		buf[sizeof(buf) - 1] = '\0'; + +		va_start(ap, fmt); +		vsnprintf(buf, sizeof(buf), fmt, ap); +		va_end(ap); + +		/* check for truncation */ +		if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) +			buf[sizeof(buf) - 1] = '\0'; + +		len = strlen(buf) + 1;  	} -	schedule_work(&mvm->fw_error_dump_wk); +	ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, +				     len, delay); +	if (ret) +		return ret; + +	trigger->occurrences = cpu_to_le16(occurrences - 1); +	return 0;  } -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf conf_id) +static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm) +{ +	if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) +		iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); +	else +		iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1); +} + +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)  {  	u8 *ptr;  	int ret; @@ -474,6 +579,14 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf conf_id)  		      "Invalid configuration %d\n", conf_id))  		return -EINVAL; +	/* EARLY START - firmware's configuration is hard coded */ +	if ((!mvm->fw->dbg_conf_tlv[conf_id] || +	     !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && +	    conf_id == FW_DBG_START_FROM_ALIVE) { +		iwl_mvm_restart_early_start(mvm); +		return 0; +	} +  	if (!mvm->fw->dbg_conf_tlv[conf_id])  		return -EINVAL; @@ -583,7 +696,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)  		IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");  	mvm->fw_dbg_conf = FW_DBG_INVALID; -	iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM); +	/* if we have a destination, assume EARLY START */ +	if (mvm->fw->dbg_dest_tlv) +		mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE; +	iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE);  	ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));  	if (ret) @@ -640,6 +756,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm)  	if (ret)  		goto error; +	/* +	 * RTNL is not taken during Ct-kill, but we don't need to scan/Tx +	 * anyway, so don't init MCC. +	 */ +	if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) { +		ret = iwl_mvm_init_mcc(mvm); +		if (ret) +			goto error; +	} +  	if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {  		ret = iwl_mvm_config_scan(mvm);  		if (ret) |