Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
Note: File does not exist in v4.17.
  1/******************************************************************************
  2 *
  3 * This file is provided under a dual BSD/GPLv2 license.  When using or
  4 * redistributing this file, you may do so under either license.
  5 *
  6 * GPL LICENSE SUMMARY
  7 *
  8 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
  9 *
 10 * This program is free software; you can redistribute it and/or modify
 11 * it under the terms of version 2 of the GNU General Public License as
 12 * published by the Free Software Foundation.
 13 *
 14 * This program is distributed in the hope that it will be useful, but
 15 * WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with this program; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
 22 * USA
 23 *
 24 * The full GNU General Public License is included in this distribution
 25 * in the file called LICENSE.GPL.
 26 *
 27 * Contact Information:
 28 *  Intel Linux Wireless <ilw@linux.intel.com>
 29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 30 *
 31 * BSD LICENSE
 32 *
 33 * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved.
 34 * All rights reserved.
 35 *
 36 * Redistribution and use in source and binary forms, with or without
 37 * modification, are permitted provided that the following conditions
 38 * are met:
 39 *
 40 *  * Redistributions of source code must retain the above copyright
 41 *    notice, this list of conditions and the following disclaimer.
 42 *  * Redistributions in binary form must reproduce the above copyright
 43 *    notice, this list of conditions and the following disclaimer in
 44 *    the documentation and/or other materials provided with the
 45 *    distribution.
 46 *  * Neither the name Intel Corporation nor the names of its
 47 *    contributors may be used to endorse or promote products derived
 48 *    from this software without specific prior written permission.
 49 *
 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 61 *****************************************************************************/
 62
 63
 64#include <linux/kernel.h>
 65#include <linux/module.h>
 66#include <linux/slab.h>
 67#include <linux/init.h>
 68
 69#include <net/mac80211.h>
 70
 71#include "iwl-commands.h"
 72#include "iwl-dev.h"
 73#include "iwl-core.h"
 74#include "iwl-debug.h"
 75#include "iwl-agn.h"
 76#include "iwl-io.h"
 77
 78/******************************************************************************
 79 *
 80 * EEPROM related functions
 81 *
 82******************************************************************************/
 83
 84int iwl_eeprom_check_version(struct iwl_priv *priv)
 85{
 86	u16 eeprom_ver;
 87	u16 calib_ver;
 88
 89	eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
 90	calib_ver = iwlagn_eeprom_calib_version(priv);
 91
 92	if (eeprom_ver < priv->cfg->eeprom_ver ||
 93	    calib_ver < priv->cfg->eeprom_calib_ver)
 94		goto err;
 95
 96	IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n",
 97		 eeprom_ver, calib_ver);
 98
 99	return 0;
100err:
101	IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x "
102		  "CALIB=0x%x < 0x%x\n",
103		  eeprom_ver, priv->cfg->eeprom_ver,
104		  calib_ver,  priv->cfg->eeprom_calib_ver);
105	return -EINVAL;
106
107}
108
109int iwl_eeprom_check_sku(struct iwl_priv *priv)
110{
111	u16 radio_cfg;
112
113	if (!priv->cfg->sku) {
114		/* not using sku overwrite */
115		priv->cfg->sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP);
116		if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE &&
117		    !priv->cfg->ht_params) {
118			IWL_ERR(priv, "Invalid 11n configuration\n");
119			return -EINVAL;
120		}
121	}
122	if (!priv->cfg->sku) {
123		IWL_ERR(priv, "Invalid device sku\n");
124		return -EINVAL;
125	}
126
127	IWL_INFO(priv, "Device SKU: 0X%x\n", priv->cfg->sku);
128
129	if (!priv->cfg->valid_tx_ant && !priv->cfg->valid_rx_ant) {
130		/* not using .cfg overwrite */
131		radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
132		priv->cfg->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg);
133		priv->cfg->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg);
134		if (!priv->cfg->valid_tx_ant || !priv->cfg->valid_rx_ant) {
135			IWL_ERR(priv, "Invalid chain (0X%x, 0X%x)\n",
136				priv->cfg->valid_tx_ant,
137				priv->cfg->valid_rx_ant);
138			return -EINVAL;
139		}
140		IWL_INFO(priv, "Valid Tx ant: 0X%x, Valid Rx ant: 0X%x\n",
141			 priv->cfg->valid_tx_ant, priv->cfg->valid_rx_ant);
142	}
143	/*
144	 * for some special cases,
145	 * EEPROM did not reflect the correct antenna setting
146	 * so overwrite the valid tx/rx antenna from .cfg
147	 */
148	return 0;
149}
150
151void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac)
152{
153	const u8 *addr = iwl_eeprom_query_addr(priv,
154					EEPROM_MAC_ADDRESS);
155	memcpy(mac, addr, ETH_ALEN);
156}
157
158/**
159 * iwl_get_max_txpower_avg - get the highest tx power from all chains.
160 *     find the highest tx power from all chains for the channel
161 */
162static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
163		struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
164		int element, s8 *max_txpower_in_half_dbm)
165{
166	s8 max_txpower_avg = 0; /* (dBm) */
167
168	/* Take the highest tx power from any valid chains */
169	if ((priv->cfg->valid_tx_ant & ANT_A) &&
170	    (enhanced_txpower[element].chain_a_max > max_txpower_avg))
171		max_txpower_avg = enhanced_txpower[element].chain_a_max;
172	if ((priv->cfg->valid_tx_ant & ANT_B) &&
173	    (enhanced_txpower[element].chain_b_max > max_txpower_avg))
174		max_txpower_avg = enhanced_txpower[element].chain_b_max;
175	if ((priv->cfg->valid_tx_ant & ANT_C) &&
176	    (enhanced_txpower[element].chain_c_max > max_txpower_avg))
177		max_txpower_avg = enhanced_txpower[element].chain_c_max;
178	if (((priv->cfg->valid_tx_ant == ANT_AB) |
179	    (priv->cfg->valid_tx_ant == ANT_BC) |
180	    (priv->cfg->valid_tx_ant == ANT_AC)) &&
181	    (enhanced_txpower[element].mimo2_max > max_txpower_avg))
182		max_txpower_avg =  enhanced_txpower[element].mimo2_max;
183	if ((priv->cfg->valid_tx_ant == ANT_ABC) &&
184	    (enhanced_txpower[element].mimo3_max > max_txpower_avg))
185		max_txpower_avg = enhanced_txpower[element].mimo3_max;
186
187	/*
188	 * max. tx power in EEPROM is in 1/2 dBm format
189	 * convert from 1/2 dBm to dBm (round-up convert)
190	 * but we also do not want to loss 1/2 dBm resolution which
191	 * will impact performance
192	 */
193	*max_txpower_in_half_dbm = max_txpower_avg;
194	return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1);
195}
196
197static void
198iwlcore_eeprom_enh_txp_read_element(struct iwl_priv *priv,
199				    struct iwl_eeprom_enhanced_txpwr *txp,
200				    s8 max_txpower_avg)
201{
202	int ch_idx;
203	bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ;
204	enum ieee80211_band band;
205
206	band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ?
207		IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
208
209	for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) {
210		struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx];
211
212		/* update matching channel or from common data only */
213		if (txp->channel != 0 && ch_info->channel != txp->channel)
214			continue;
215
216		/* update matching band only */
217		if (band != ch_info->band)
218			continue;
219
220		if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) {
221			ch_info->max_power_avg = max_txpower_avg;
222			ch_info->curr_txpow = max_txpower_avg;
223			ch_info->scan_power = max_txpower_avg;
224		}
225
226		if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg)
227			ch_info->ht40_max_power_avg = max_txpower_avg;
228	}
229}
230
231#define EEPROM_TXP_OFFS	(0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT)
232#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr)
233#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE)
234
235#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \
236			    ? # x " " : "")
237
238void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv)
239{
240	struct iwl_eeprom_enhanced_txpwr *txp_array, *txp;
241	int idx, entries;
242	__le16 *txp_len;
243	s8 max_txp_avg, max_txp_avg_halfdbm;
244
245	BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8);
246
247	/* the length is in 16-bit words, but we want entries */
248	txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS);
249	entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN;
250
251	txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS);
252
253	for (idx = 0; idx < entries; idx++) {
254		txp = &txp_array[idx];
255		/* skip invalid entries */
256		if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID))
257			continue;
258
259		IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n",
260				 (txp->channel && (txp->flags &
261					IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ?
262					"Common " : (txp->channel) ?
263					"Channel" : "Common",
264				 (txp->channel),
265				 TXP_CHECK_AND_PRINT(VALID),
266				 TXP_CHECK_AND_PRINT(BAND_52G),
267				 TXP_CHECK_AND_PRINT(OFDM),
268				 TXP_CHECK_AND_PRINT(40MHZ),
269				 TXP_CHECK_AND_PRINT(HT_AP),
270				 TXP_CHECK_AND_PRINT(RES1),
271				 TXP_CHECK_AND_PRINT(RES2),
272				 TXP_CHECK_AND_PRINT(COMMON_TYPE),
273				 txp->flags);
274		IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x "
275				 "chain_B: 0X%02x chain_C: 0X%02x\n",
276				 txp->chain_a_max, txp->chain_b_max,
277				 txp->chain_c_max);
278		IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x "
279				 "MIMO3: 0x%02x High 20_on_40: 0x%02x "
280				 "Low 20_on_40: 0x%02x\n",
281				 txp->mimo2_max, txp->mimo3_max,
282				 ((txp->delta_20_in_40 & 0xf0) >> 4),
283				 (txp->delta_20_in_40 & 0x0f));
284
285		max_txp_avg = iwl_get_max_txpower_avg(priv, txp_array, idx,
286						      &max_txp_avg_halfdbm);
287
288		/*
289		 * Update the user limit values values to the highest
290		 * power supported by any channel
291		 */
292		if (max_txp_avg > priv->tx_power_user_lmt)
293			priv->tx_power_user_lmt = max_txp_avg;
294		if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm)
295			priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm;
296
297		iwlcore_eeprom_enh_txp_read_element(priv, txp, max_txp_avg);
298	}
299}