diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/utils.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/utils.c | 257 | 
1 files changed, 232 insertions, 25 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index a9c35749143..aa9fc77e841 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -5,7 +5,7 @@   *   * GPL LICENSE SUMMARY   * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@   *   * BSD LICENSE   * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -64,6 +64,7 @@  #include "iwl-debug.h"  #include "iwl-io.h" +#include "iwl-prph.h"  #include "mvm.h"  #include "fw-api-rs.h" @@ -143,7 +144,7 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,  		      "cmd flags %x", cmd->flags))  		return -EINVAL; -	cmd->flags |= CMD_SYNC | CMD_WANT_SKB; +	cmd->flags |= CMD_WANT_SKB;  	ret = iwl_trans_send_cmd(mvm->trans, cmd);  	if (ret == -ERFKILL) { @@ -168,8 +169,8 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,  		goto out_free_resp;  	} -	resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; -	if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) { +	resp_len = iwl_rx_packet_payload_len(pkt); +	if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {  		ret = -EIO;  		goto out_free_resp;  	} @@ -289,8 +290,8 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)  	return last_idx;  } -static struct { -	char *name; +static const struct { +	const char *name;  	u8 num;  } advanced_lookup[] = {  	{ "NMI_INTERRUPT_WDG", 0x34 }, @@ -376,9 +377,67 @@ struct iwl_error_event_table {  	u32 flow_handler;	/* FH read/write pointers, RX credit */  } __packed; +/* + * UMAC error struct - relevant starting from family 8000 chip. + * Note: This structure is read from the device with IO accesses, + * and the reading already does the endian conversion. As it is + * read with u32-sized accesses, any members with a different size + * need to be ordered correctly though! + */ +struct iwl_umac_error_event_table { +	u32 valid;		/* (nonzero) valid, (0) log is empty */ +	u32 error_id;		/* type of error */ +	u32 pc;			/* program counter */ +	u32 blink1;		/* branch link */ +	u32 blink2;		/* branch link */ +	u32 ilink1;		/* interrupt link */ +	u32 ilink2;		/* interrupt link */ +	u32 data1;		/* error-specific data */ +	u32 data2;		/* error-specific data */ +	u32 line;		/* source code line of error */ +	u32 umac_ver;		/* umac version */ +} __packed; +  #define ERROR_START_OFFSET  (1 * sizeof(u32))  #define ERROR_ELEM_SIZE     (7 * sizeof(u32)) +static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) +{ +	struct iwl_trans *trans = mvm->trans; +	struct iwl_umac_error_event_table table; +	u32 base; + +	base = mvm->umac_error_event_table; + +	if (base < 0x800000 || base >= 0x80C000) { +		IWL_ERR(mvm, +			"Not valid error log pointer 0x%08X for %s uCode\n", +			base, +			(mvm->cur_ucode == IWL_UCODE_INIT) +					? "Init" : "RT"); +		return; +	} + +	iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); + +	if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { +		IWL_ERR(trans, "Start IWL Error Log Dump:\n"); +		IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", +			mvm->status, table.valid); +	} + +	IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, +		desc_lookup(table.error_id)); +	IWL_ERR(mvm, "0x%08X | umac uPc\n", table.pc); +	IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1); +	IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2); +	IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1); +	IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2); +	IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1); +	IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2); +	IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_ver); +} +  void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)  {  	struct iwl_trans *trans = mvm->trans; @@ -394,7 +453,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)  			base = mvm->fw->inst_errlog_ptr;  	} -	if (base < 0x800000 || base >= 0x80C000) { +	if (base < 0x800000) {  		IWL_ERR(mvm,  			"Not valid error log pointer 0x%08X for %s uCode\n",  			base, @@ -411,6 +470,10 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)  			mvm->status, table.valid);  	} +	/* Do not change this output - scripts rely on it */ + +	IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); +  	trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,  				      table.data1, table.data2, table.data3,  				      table.blink1, table.blink2, table.ilink1, @@ -451,30 +514,75 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)  	IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);  	IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);  	IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); + +	if (mvm->support_umac_log) +		iwl_mvm_dump_umac_error_log(mvm);  } -void iwl_mvm_dump_sram(struct iwl_mvm *mvm) +#ifdef CONFIG_IWLWIFI_DEBUGFS +void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)  {  	const struct fw_img *img; -	int ofs, len = 0; -	u8 *buf; +	u32 ofs, sram_len; +	void *sram; -	if (!mvm->ucode_loaded) +	if (!mvm->ucode_loaded || mvm->fw_error_sram || mvm->fw_error_dump)  		return;  	img = &mvm->fw->img[mvm->cur_ucode];  	ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; -	len = img->sec[IWL_UCODE_SECTION_DATA].len; +	sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + +	sram = kzalloc(sram_len, GFP_ATOMIC); +	if (!sram) +		return; + +	iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len); +	mvm->fw_error_sram = sram; +	mvm->fw_error_sram_len = sram_len; +} + +void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm) +{ +	int i, reg_val; +	unsigned long flags; + +	if (!mvm->ucode_loaded || mvm->fw_error_rxf || mvm->fw_error_dump) +		return; + +	/* reading buffer size */ +	reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR); +	mvm->fw_error_rxf_len = +		(reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS; + +	/* the register holds the value divided by 128 */ +	mvm->fw_error_rxf_len = mvm->fw_error_rxf_len << 7; + +	if (!mvm->fw_error_rxf_len) +		return; -	buf = kzalloc(len, GFP_KERNEL); -	if (!buf) +	mvm->fw_error_rxf =  kzalloc(mvm->fw_error_rxf_len, GFP_ATOMIC); +	if (!mvm->fw_error_rxf) { +		mvm->fw_error_rxf_len = 0;  		return; +	} -	iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len); -	iwl_print_hex_error(mvm->trans, buf, len); +	if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) { +		kfree(mvm->fw_error_rxf); +		mvm->fw_error_rxf = NULL; +		mvm->fw_error_rxf_len = 0; +		return; +	} -	kfree(buf); +	for (i = 0; i < (mvm->fw_error_rxf_len / sizeof(u32)); i++) { +		iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR, +				     i * sizeof(u32)); +		mvm->fw_error_rxf[i] = +			iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_ADDR); +	} +	iwl_trans_release_nic_access(mvm->trans, &flags);  } +#endif  /**   * iwl_mvm_send_lq_cmd() - Send link quality command @@ -486,22 +594,18 @@ void iwl_mvm_dump_sram(struct iwl_mvm *mvm)   * this case to clear the state indicating that station creation is in   * progress.   */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, -			u8 flags, bool init) +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init)  {  	struct iwl_host_cmd cmd = {  		.id = LQ_CMD,  		.len = { sizeof(struct iwl_lq_cmd), }, -		.flags = flags, +		.flags = init ? 0 : CMD_ASYNC,  		.data = { lq, },  	};  	if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT))  		return -EINVAL; -	if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) -		return -EINVAL; -  	return iwl_mvm_send_cmd(mvm, &cmd);  } @@ -518,10 +622,20 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			 enum ieee80211_smps_mode smps_request)  {  	struct iwl_mvm_vif *mvmvif; -	enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; +	enum ieee80211_smps_mode smps_mode;  	int i;  	lockdep_assert_held(&mvm->mutex); + +	/* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ +	if (num_of_ant(mvm->fw->valid_rx_ant) == 1) +		return; + +	if (vif->type == NL80211_IFTYPE_AP) +		smps_mode = IEEE80211_SMPS_OFF; +	else +		smps_mode = IEEE80211_SMPS_AUTOMATIC; +  	mvmvif = iwl_mvm_vif_from_mac80211(vif);  	mvmvif->smps_requests[req_type] = smps_request;  	for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { @@ -535,3 +649,96 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	ieee80211_request_smps(vif, smps_mode);  } + +static void iwl_mvm_diversity_iter(void *_data, u8 *mac, +				   struct ieee80211_vif *vif) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	bool *result = _data; +	int i; + +	for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { +		if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC || +		    mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) +			*result = false; +	} +} + +bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) +{ +	bool result = true; + +	lockdep_assert_held(&mvm->mutex); + +	if (num_of_ant(mvm->fw->valid_rx_ant) == 1) +		return false; + +	if (!mvm->cfg->rx_with_siso_diversity) +		return false; + +	ieee80211_iterate_active_interfaces_atomic( +			mvm->hw, IEEE80211_IFACE_ITER_NORMAL, +			iwl_mvm_diversity_iter, &result); + +	return result; +} + +int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, +			       bool value) +{ +	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); +	int res; + +	lockdep_assert_held(&mvm->mutex); + +	if (mvmvif->low_latency == value) +		return 0; + +	mvmvif->low_latency = value; + +	res = iwl_mvm_update_quotas(mvm, NULL); +	if (res) +		return res; + +	iwl_mvm_bt_coex_vif_change(mvm); + +	return iwl_mvm_power_update_mac(mvm); +} + +static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ +	bool *result = _data; + +	if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif))) +		*result = true; +} + +bool iwl_mvm_low_latency(struct iwl_mvm *mvm) +{ +	bool result = false; + +	ieee80211_iterate_active_interfaces_atomic( +			mvm->hw, IEEE80211_IFACE_ITER_NORMAL, +			iwl_mvm_ll_iter, &result); + +	return result; +} + +static void iwl_mvm_idle_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ +	bool *idle = _data; + +	if (!vif->bss_conf.idle) +		*idle = false; +} + +bool iwl_mvm_is_idle(struct iwl_mvm *mvm) +{ +	bool idle = true; + +	ieee80211_iterate_active_interfaces_atomic( +			mvm->hw, IEEE80211_IFACE_ITER_NORMAL, +			iwl_mvm_idle_iter, &idle); + +	return idle; +}  | 
