diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c new file mode 100644 index 00000000000..b8347db850e --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c | |||
@@ -0,0 +1,299 @@ | |||
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 | |||
84 | int 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; | ||
100 | err: | ||
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 | |||
109 | int 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 | |||
151 | void 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 | */ | ||
162 | static 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 | |||
197 | static void | ||
198 | iwlcore_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 | |||
238 | void 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 | } | ||