aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWey-Yi Guy <wey-yi.w.guy@intel.com>2011-09-15 14:46:41 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-09-19 15:58:30 -0400
commit701cb0997f42196a42c3566da1d35451b4b899e2 (patch)
tree4b98df3216c6a026f2bc6876df880d1b917f95ec
parent1dd9124e2911b34744672c91ad865f39711f5542 (diff)
iwlagn: merge eeprom access into single file
After driver split and no need to support legacy devices, there is no reason we need to separate the NVM access into different files, merge those. Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c299
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom.c239
3 files changed, 235 insertions, 305 deletions
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 48ab9142af38..ae1d816cc4ee 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_IWLAGN) += iwlagn.o
3iwlagn-objs := iwl-agn.o iwl-agn-rs.o 3iwlagn-objs := iwl-agn.o iwl-agn-rs.o
4iwlagn-objs += iwl-agn-ucode.o iwl-agn-tx.o 4iwlagn-objs += iwl-agn-ucode.o iwl-agn-tx.o
5iwlagn-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o 5iwlagn-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o
6iwlagn-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-eeprom.o 6iwlagn-objs += iwl-agn-tt.o iwl-agn-sta.o
7 7
8iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o 8iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o
9iwlagn-objs += iwl-rx.o iwl-sta.o 9iwlagn-objs += iwl-rx.o iwl-sta.o
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c
deleted file mode 100644
index c62ddc2a31bd..000000000000
--- a/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c
+++ /dev/null
@@ -1,299 +0,0 @@
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
198iwl_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 iwl_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 iwl_eeprom_enh_txp_read_element(priv, txp, max_txp_avg);
298 }
299}
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
index 80ee65be9cd1..0b669417b0a6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c
@@ -72,6 +72,7 @@
72#include "iwl-dev.h" 72#include "iwl-dev.h"
73#include "iwl-core.h" 73#include "iwl-core.h"
74#include "iwl-debug.h" 74#include "iwl-debug.h"
75#include "iwl-agn.h"
75#include "iwl-eeprom.h" 76#include "iwl-eeprom.h"
76#include "iwl-io.h" 77#include "iwl-io.h"
77 78
@@ -138,7 +139,7 @@ static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */
138 139
139/****************************************************************************** 140/******************************************************************************
140 * 141 *
141 * EEPROM related functions 142 * generic NVM functions
142 * 143 *
143******************************************************************************/ 144******************************************************************************/
144 145
@@ -214,6 +215,93 @@ static int iwl_eeprom_verify_signature(struct iwl_priv *priv)
214 return ret; 215 return ret;
215} 216}
216 217
218u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset)
219{
220 if (!priv->eeprom)
221 return 0;
222 return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8);
223}
224
225int iwl_eeprom_check_version(struct iwl_priv *priv)
226{
227 u16 eeprom_ver;
228 u16 calib_ver;
229
230 eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
231 calib_ver = iwlagn_eeprom_calib_version(priv);
232
233 if (eeprom_ver < priv->cfg->eeprom_ver ||
234 calib_ver < priv->cfg->eeprom_calib_ver)
235 goto err;
236
237 IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n",
238 eeprom_ver, calib_ver);
239
240 return 0;
241err:
242 IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x "
243 "CALIB=0x%x < 0x%x\n",
244 eeprom_ver, priv->cfg->eeprom_ver,
245 calib_ver, priv->cfg->eeprom_calib_ver);
246 return -EINVAL;
247
248}
249
250int iwl_eeprom_check_sku(struct iwl_priv *priv)
251{
252 u16 radio_cfg;
253
254 if (!priv->cfg->sku) {
255 /* not using sku overwrite */
256 priv->cfg->sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP);
257 if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE &&
258 !priv->cfg->ht_params) {
259 IWL_ERR(priv, "Invalid 11n configuration\n");
260 return -EINVAL;
261 }
262 }
263 if (!priv->cfg->sku) {
264 IWL_ERR(priv, "Invalid device sku\n");
265 return -EINVAL;
266 }
267
268 IWL_INFO(priv, "Device SKU: 0X%x\n", priv->cfg->sku);
269
270 if (!priv->cfg->valid_tx_ant && !priv->cfg->valid_rx_ant) {
271 /* not using .cfg overwrite */
272 radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG);
273 priv->cfg->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg);
274 priv->cfg->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg);
275 if (!priv->cfg->valid_tx_ant || !priv->cfg->valid_rx_ant) {
276 IWL_ERR(priv, "Invalid chain (0X%x, 0X%x)\n",
277 priv->cfg->valid_tx_ant,
278 priv->cfg->valid_rx_ant);
279 return -EINVAL;
280 }
281 IWL_INFO(priv, "Valid Tx ant: 0X%x, Valid Rx ant: 0X%x\n",
282 priv->cfg->valid_tx_ant, priv->cfg->valid_rx_ant);
283 }
284 /*
285 * for some special cases,
286 * EEPROM did not reflect the correct antenna setting
287 * so overwrite the valid tx/rx antenna from .cfg
288 */
289 return 0;
290}
291
292void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac)
293{
294 const u8 *addr = iwl_eeprom_query_addr(priv,
295 EEPROM_MAC_ADDRESS);
296 memcpy(mac, addr, ETH_ALEN);
297}
298
299/******************************************************************************
300 *
301 * OTP related functions
302 *
303******************************************************************************/
304
217static void iwl_set_otp_access(struct iwl_priv *priv, enum iwl_access_mode mode) 305static void iwl_set_otp_access(struct iwl_priv *priv, enum iwl_access_mode mode)
218{ 306{
219 iwl_read32(bus(priv), CSR_OTP_GP_REG); 307 iwl_read32(bus(priv), CSR_OTP_GP_REG);
@@ -407,11 +495,152 @@ static int iwl_find_otp_image(struct iwl_priv *priv,
407 return -EINVAL; 495 return -EINVAL;
408} 496}
409 497
410u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset) 498/******************************************************************************
499 *
500 * Tx Power related functions
501 *
502******************************************************************************/
503/**
504 * iwl_get_max_txpower_avg - get the highest tx power from all chains.
505 * find the highest tx power from all chains for the channel
506 */
507static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv,
508 struct iwl_eeprom_enhanced_txpwr *enhanced_txpower,
509 int element, s8 *max_txpower_in_half_dbm)
411{ 510{
412 if (!priv->eeprom) 511 s8 max_txpower_avg = 0; /* (dBm) */
413 return 0; 512
414 return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); 513 /* Take the highest tx power from any valid chains */
514 if ((priv->cfg->valid_tx_ant & ANT_A) &&
515 (enhanced_txpower[element].chain_a_max > max_txpower_avg))
516 max_txpower_avg = enhanced_txpower[element].chain_a_max;
517 if ((priv->cfg->valid_tx_ant & ANT_B) &&
518 (enhanced_txpower[element].chain_b_max > max_txpower_avg))
519 max_txpower_avg = enhanced_txpower[element].chain_b_max;
520 if ((priv->cfg->valid_tx_ant & ANT_C) &&
521 (enhanced_txpower[element].chain_c_max > max_txpower_avg))
522 max_txpower_avg = enhanced_txpower[element].chain_c_max;
523 if (((priv->cfg->valid_tx_ant == ANT_AB) |
524 (priv->cfg->valid_tx_ant == ANT_BC) |
525 (priv->cfg->valid_tx_ant == ANT_AC)) &&
526 (enhanced_txpower[element].mimo2_max > max_txpower_avg))
527 max_txpower_avg = enhanced_txpower[element].mimo2_max;
528 if ((priv->cfg->valid_tx_ant == ANT_ABC) &&
529 (enhanced_txpower[element].mimo3_max > max_txpower_avg))
530 max_txpower_avg = enhanced_txpower[element].mimo3_max;
531
532 /*
533 * max. tx power in EEPROM is in 1/2 dBm format
534 * convert from 1/2 dBm to dBm (round-up convert)
535 * but we also do not want to loss 1/2 dBm resolution which
536 * will impact performance
537 */
538 *max_txpower_in_half_dbm = max_txpower_avg;
539 return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1);
540}
541
542static void
543iwl_eeprom_enh_txp_read_element(struct iwl_priv *priv,
544 struct iwl_eeprom_enhanced_txpwr *txp,
545 s8 max_txpower_avg)
546{
547 int ch_idx;
548 bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ;
549 enum ieee80211_band band;
550
551 band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ?
552 IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
553
554 for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) {
555 struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx];
556
557 /* update matching channel or from common data only */
558 if (txp->channel != 0 && ch_info->channel != txp->channel)
559 continue;
560
561 /* update matching band only */
562 if (band != ch_info->band)
563 continue;
564
565 if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) {
566 ch_info->max_power_avg = max_txpower_avg;
567 ch_info->curr_txpow = max_txpower_avg;
568 ch_info->scan_power = max_txpower_avg;
569 }
570
571 if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg)
572 ch_info->ht40_max_power_avg = max_txpower_avg;
573 }
574}
575
576#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT)
577#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr)
578#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE)
579
580#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \
581 ? # x " " : "")
582
583void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv)
584{
585 struct iwl_eeprom_enhanced_txpwr *txp_array, *txp;
586 int idx, entries;
587 __le16 *txp_len;
588 s8 max_txp_avg, max_txp_avg_halfdbm;
589
590 BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8);
591
592 /* the length is in 16-bit words, but we want entries */
593 txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS);
594 entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN;
595
596 txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS);
597
598 for (idx = 0; idx < entries; idx++) {
599 txp = &txp_array[idx];
600 /* skip invalid entries */
601 if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID))
602 continue;
603
604 IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n",
605 (txp->channel && (txp->flags &
606 IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ?
607 "Common " : (txp->channel) ?
608 "Channel" : "Common",
609 (txp->channel),
610 TXP_CHECK_AND_PRINT(VALID),
611 TXP_CHECK_AND_PRINT(BAND_52G),
612 TXP_CHECK_AND_PRINT(OFDM),
613 TXP_CHECK_AND_PRINT(40MHZ),
614 TXP_CHECK_AND_PRINT(HT_AP),
615 TXP_CHECK_AND_PRINT(RES1),
616 TXP_CHECK_AND_PRINT(RES2),
617 TXP_CHECK_AND_PRINT(COMMON_TYPE),
618 txp->flags);
619 IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x "
620 "chain_B: 0X%02x chain_C: 0X%02x\n",
621 txp->chain_a_max, txp->chain_b_max,
622 txp->chain_c_max);
623 IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x "
624 "MIMO3: 0x%02x High 20_on_40: 0x%02x "
625 "Low 20_on_40: 0x%02x\n",
626 txp->mimo2_max, txp->mimo3_max,
627 ((txp->delta_20_in_40 & 0xf0) >> 4),
628 (txp->delta_20_in_40 & 0x0f));
629
630 max_txp_avg = iwl_get_max_txpower_avg(priv, txp_array, idx,
631 &max_txp_avg_halfdbm);
632
633 /*
634 * Update the user limit values values to the highest
635 * power supported by any channel
636 */
637 if (max_txp_avg > priv->tx_power_user_lmt)
638 priv->tx_power_user_lmt = max_txp_avg;
639 if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm)
640 priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm;
641
642 iwl_eeprom_enh_txp_read_element(priv, txp, max_txp_avg);
643 }
415} 644}
416 645
417/** 646/**