diff options
Diffstat (limited to 'drivers/net/wireless/ath5k/eeprom.c')
-rw-r--r-- | drivers/net/wireless/ath5k/eeprom.c | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath5k/eeprom.c b/drivers/net/wireless/ath5k/eeprom.c new file mode 100644 index 000000000000..a883839b6a9f --- /dev/null +++ b/drivers/net/wireless/ath5k/eeprom.c | |||
@@ -0,0 +1,466 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> | ||
3 | * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | /*************************************\ | ||
20 | * EEPROM access functions and helpers * | ||
21 | \*************************************/ | ||
22 | |||
23 | #include "ath5k.h" | ||
24 | #include "reg.h" | ||
25 | #include "debug.h" | ||
26 | #include "base.h" | ||
27 | |||
28 | /* | ||
29 | * Read from eeprom | ||
30 | */ | ||
31 | static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data) | ||
32 | { | ||
33 | u32 status, timeout; | ||
34 | |||
35 | ATH5K_TRACE(ah->ah_sc); | ||
36 | /* | ||
37 | * Initialize EEPROM access | ||
38 | */ | ||
39 | if (ah->ah_version == AR5K_AR5210) { | ||
40 | AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_EEAE); | ||
41 | (void)ath5k_hw_reg_read(ah, AR5K_EEPROM_BASE + (4 * offset)); | ||
42 | } else { | ||
43 | ath5k_hw_reg_write(ah, offset, AR5K_EEPROM_BASE); | ||
44 | AR5K_REG_ENABLE_BITS(ah, AR5K_EEPROM_CMD, | ||
45 | AR5K_EEPROM_CMD_READ); | ||
46 | } | ||
47 | |||
48 | for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) { | ||
49 | status = ath5k_hw_reg_read(ah, AR5K_EEPROM_STATUS); | ||
50 | if (status & AR5K_EEPROM_STAT_RDDONE) { | ||
51 | if (status & AR5K_EEPROM_STAT_RDERR) | ||
52 | return -EIO; | ||
53 | *data = (u16)(ath5k_hw_reg_read(ah, AR5K_EEPROM_DATA) & | ||
54 | 0xffff); | ||
55 | return 0; | ||
56 | } | ||
57 | udelay(15); | ||
58 | } | ||
59 | |||
60 | return -ETIMEDOUT; | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * Translate binary channel representation in EEPROM to frequency | ||
65 | */ | ||
66 | static u16 ath5k_eeprom_bin2freq(struct ath5k_hw *ah, u16 bin, | ||
67 | unsigned int mode) | ||
68 | { | ||
69 | u16 val; | ||
70 | |||
71 | if (bin == AR5K_EEPROM_CHANNEL_DIS) | ||
72 | return bin; | ||
73 | |||
74 | if (mode == AR5K_EEPROM_MODE_11A) { | ||
75 | if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2) | ||
76 | val = (5 * bin) + 4800; | ||
77 | else | ||
78 | val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 : | ||
79 | (bin * 10) + 5100; | ||
80 | } else { | ||
81 | if (ah->ah_ee_version > AR5K_EEPROM_VERSION_3_2) | ||
82 | val = bin + 2300; | ||
83 | else | ||
84 | val = bin + 2400; | ||
85 | } | ||
86 | |||
87 | return val; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Read antenna infos from eeprom | ||
92 | */ | ||
93 | static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset, | ||
94 | unsigned int mode) | ||
95 | { | ||
96 | struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; | ||
97 | u32 o = *offset; | ||
98 | u16 val; | ||
99 | int ret, i = 0; | ||
100 | |||
101 | AR5K_EEPROM_READ(o++, val); | ||
102 | ee->ee_switch_settling[mode] = (val >> 8) & 0x7f; | ||
103 | ee->ee_ant_tx_rx[mode] = (val >> 2) & 0x3f; | ||
104 | ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; | ||
105 | |||
106 | AR5K_EEPROM_READ(o++, val); | ||
107 | ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; | ||
108 | ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; | ||
109 | ee->ee_ant_control[mode][i++] = val & 0x3f; | ||
110 | |||
111 | AR5K_EEPROM_READ(o++, val); | ||
112 | ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f; | ||
113 | ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f; | ||
114 | ee->ee_ant_control[mode][i] = (val << 2) & 0x3f; | ||
115 | |||
116 | AR5K_EEPROM_READ(o++, val); | ||
117 | ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3; | ||
118 | ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f; | ||
119 | ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f; | ||
120 | ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; | ||
121 | |||
122 | AR5K_EEPROM_READ(o++, val); | ||
123 | ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; | ||
124 | ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; | ||
125 | ee->ee_ant_control[mode][i++] = val & 0x3f; | ||
126 | |||
127 | /* Get antenna modes */ | ||
128 | ah->ah_antenna[mode][0] = | ||
129 | (ee->ee_ant_control[mode][0] << 4) | 0x1; | ||
130 | ah->ah_antenna[mode][AR5K_ANT_FIXED_A] = | ||
131 | ee->ee_ant_control[mode][1] | | ||
132 | (ee->ee_ant_control[mode][2] << 6) | | ||
133 | (ee->ee_ant_control[mode][3] << 12) | | ||
134 | (ee->ee_ant_control[mode][4] << 18) | | ||
135 | (ee->ee_ant_control[mode][5] << 24); | ||
136 | ah->ah_antenna[mode][AR5K_ANT_FIXED_B] = | ||
137 | ee->ee_ant_control[mode][6] | | ||
138 | (ee->ee_ant_control[mode][7] << 6) | | ||
139 | (ee->ee_ant_control[mode][8] << 12) | | ||
140 | (ee->ee_ant_control[mode][9] << 18) | | ||
141 | (ee->ee_ant_control[mode][10] << 24); | ||
142 | |||
143 | /* return new offset */ | ||
144 | *offset = o; | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Read supported modes from eeprom | ||
151 | */ | ||
152 | static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, | ||
153 | unsigned int mode) | ||
154 | { | ||
155 | struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; | ||
156 | u32 o = *offset; | ||
157 | u16 val; | ||
158 | int ret; | ||
159 | |||
160 | AR5K_EEPROM_READ(o++, val); | ||
161 | ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff; | ||
162 | ee->ee_thr_62[mode] = val & 0xff; | ||
163 | |||
164 | if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) | ||
165 | ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28; | ||
166 | |||
167 | AR5K_EEPROM_READ(o++, val); | ||
168 | ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff; | ||
169 | ee->ee_tx_frm2xpa_enable[mode] = val & 0xff; | ||
170 | |||
171 | AR5K_EEPROM_READ(o++, val); | ||
172 | ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff; | ||
173 | |||
174 | if ((val & 0xff) & 0x80) | ||
175 | ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1); | ||
176 | else | ||
177 | ee->ee_noise_floor_thr[mode] = val & 0xff; | ||
178 | |||
179 | if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) | ||
180 | ee->ee_noise_floor_thr[mode] = | ||
181 | mode == AR5K_EEPROM_MODE_11A ? -54 : -1; | ||
182 | |||
183 | AR5K_EEPROM_READ(o++, val); | ||
184 | ee->ee_xlna_gain[mode] = (val >> 5) & 0xff; | ||
185 | ee->ee_x_gain[mode] = (val >> 1) & 0xf; | ||
186 | ee->ee_xpd[mode] = val & 0x1; | ||
187 | |||
188 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) | ||
189 | ee->ee_fixed_bias[mode] = (val >> 13) & 0x1; | ||
190 | |||
191 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) { | ||
192 | AR5K_EEPROM_READ(o++, val); | ||
193 | ee->ee_false_detect[mode] = (val >> 6) & 0x7f; | ||
194 | |||
195 | if (mode == AR5K_EEPROM_MODE_11A) | ||
196 | ee->ee_xr_power[mode] = val & 0x3f; | ||
197 | else { | ||
198 | ee->ee_ob[mode][0] = val & 0x7; | ||
199 | ee->ee_db[mode][0] = (val >> 3) & 0x7; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_4) { | ||
204 | ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN; | ||
205 | ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA; | ||
206 | } else { | ||
207 | ee->ee_i_gain[mode] = (val >> 13) & 0x7; | ||
208 | |||
209 | AR5K_EEPROM_READ(o++, val); | ||
210 | ee->ee_i_gain[mode] |= (val << 3) & 0x38; | ||
211 | |||
212 | if (mode == AR5K_EEPROM_MODE_11G) | ||
213 | ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff; | ||
214 | } | ||
215 | |||
216 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && | ||
217 | mode == AR5K_EEPROM_MODE_11A) { | ||
218 | ee->ee_i_cal[mode] = (val >> 8) & 0x3f; | ||
219 | ee->ee_q_cal[mode] = (val >> 3) & 0x1f; | ||
220 | } | ||
221 | |||
222 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6 && | ||
223 | mode == AR5K_EEPROM_MODE_11G) | ||
224 | ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; | ||
225 | |||
226 | /* return new offset */ | ||
227 | *offset = o; | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * Initialize eeprom & capabilities structs | ||
234 | */ | ||
235 | int ath5k_eeprom_init(struct ath5k_hw *ah) | ||
236 | { | ||
237 | struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; | ||
238 | unsigned int mode, i; | ||
239 | int ret; | ||
240 | u32 offset; | ||
241 | u16 val; | ||
242 | |||
243 | /* Initial TX thermal adjustment values */ | ||
244 | ee->ee_tx_clip = 4; | ||
245 | ee->ee_pwd_84 = ee->ee_pwd_90 = 1; | ||
246 | ee->ee_gain_select = 1; | ||
247 | |||
248 | /* | ||
249 | * Read values from EEPROM and store them in the capability structure | ||
250 | */ | ||
251 | AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); | ||
252 | AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); | ||
253 | AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); | ||
254 | AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); | ||
255 | AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); | ||
256 | |||
257 | /* Return if we have an old EEPROM */ | ||
258 | if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0) | ||
259 | return 0; | ||
260 | |||
261 | #ifdef notyet | ||
262 | /* | ||
263 | * Validate the checksum of the EEPROM date. There are some | ||
264 | * devices with invalid EEPROMs. | ||
265 | */ | ||
266 | for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) { | ||
267 | AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); | ||
268 | cksum ^= val; | ||
269 | } | ||
270 | if (cksum != AR5K_EEPROM_INFO_CKSUM) { | ||
271 | ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum); | ||
272 | return -EIO; | ||
273 | } | ||
274 | #endif | ||
275 | |||
276 | AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version), | ||
277 | ee_ant_gain); | ||
278 | |||
279 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { | ||
280 | AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); | ||
281 | AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); | ||
282 | } | ||
283 | |||
284 | if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { | ||
285 | AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val); | ||
286 | ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; | ||
287 | ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; | ||
288 | |||
289 | AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val); | ||
290 | ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; | ||
291 | ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Get conformance test limit values | ||
296 | */ | ||
297 | offset = AR5K_EEPROM_CTL(ah->ah_ee_version); | ||
298 | ee->ee_ctls = AR5K_EEPROM_N_CTLS(ah->ah_ee_version); | ||
299 | |||
300 | for (i = 0; i < ee->ee_ctls; i++) { | ||
301 | AR5K_EEPROM_READ(offset++, val); | ||
302 | ee->ee_ctl[i] = (val >> 8) & 0xff; | ||
303 | ee->ee_ctl[i + 1] = val & 0xff; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * Get values for 802.11a (5GHz) | ||
308 | */ | ||
309 | mode = AR5K_EEPROM_MODE_11A; | ||
310 | |||
311 | ee->ee_turbo_max_power[mode] = | ||
312 | AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); | ||
313 | |||
314 | offset = AR5K_EEPROM_MODES_11A(ah->ah_ee_version); | ||
315 | |||
316 | ret = ath5k_eeprom_read_ants(ah, &offset, mode); | ||
317 | if (ret) | ||
318 | return ret; | ||
319 | |||
320 | AR5K_EEPROM_READ(offset++, val); | ||
321 | ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); | ||
322 | ee->ee_ob[mode][3] = (val >> 5) & 0x7; | ||
323 | ee->ee_db[mode][3] = (val >> 2) & 0x7; | ||
324 | ee->ee_ob[mode][2] = (val << 1) & 0x7; | ||
325 | |||
326 | AR5K_EEPROM_READ(offset++, val); | ||
327 | ee->ee_ob[mode][2] |= (val >> 15) & 0x1; | ||
328 | ee->ee_db[mode][2] = (val >> 12) & 0x7; | ||
329 | ee->ee_ob[mode][1] = (val >> 9) & 0x7; | ||
330 | ee->ee_db[mode][1] = (val >> 6) & 0x7; | ||
331 | ee->ee_ob[mode][0] = (val >> 3) & 0x7; | ||
332 | ee->ee_db[mode][0] = val & 0x7; | ||
333 | |||
334 | ret = ath5k_eeprom_read_modes(ah, &offset, mode); | ||
335 | if (ret) | ||
336 | return ret; | ||
337 | |||
338 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) { | ||
339 | AR5K_EEPROM_READ(offset++, val); | ||
340 | ee->ee_margin_tx_rx[mode] = val & 0x3f; | ||
341 | } | ||
342 | |||
343 | /* | ||
344 | * Get values for 802.11b (2.4GHz) | ||
345 | */ | ||
346 | mode = AR5K_EEPROM_MODE_11B; | ||
347 | offset = AR5K_EEPROM_MODES_11B(ah->ah_ee_version); | ||
348 | |||
349 | ret = ath5k_eeprom_read_ants(ah, &offset, mode); | ||
350 | if (ret) | ||
351 | return ret; | ||
352 | |||
353 | AR5K_EEPROM_READ(offset++, val); | ||
354 | ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); | ||
355 | ee->ee_ob[mode][1] = (val >> 4) & 0x7; | ||
356 | ee->ee_db[mode][1] = val & 0x7; | ||
357 | |||
358 | ret = ath5k_eeprom_read_modes(ah, &offset, mode); | ||
359 | if (ret) | ||
360 | return ret; | ||
361 | |||
362 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { | ||
363 | AR5K_EEPROM_READ(offset++, val); | ||
364 | ee->ee_cal_pier[mode][0] = | ||
365 | ath5k_eeprom_bin2freq(ah, val & 0xff, mode); | ||
366 | ee->ee_cal_pier[mode][1] = | ||
367 | ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode); | ||
368 | |||
369 | AR5K_EEPROM_READ(offset++, val); | ||
370 | ee->ee_cal_pier[mode][2] = | ||
371 | ath5k_eeprom_bin2freq(ah, val & 0xff, mode); | ||
372 | } | ||
373 | |||
374 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) | ||
375 | ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; | ||
376 | |||
377 | /* | ||
378 | * Get values for 802.11g (2.4GHz) | ||
379 | */ | ||
380 | mode = AR5K_EEPROM_MODE_11G; | ||
381 | offset = AR5K_EEPROM_MODES_11G(ah->ah_ee_version); | ||
382 | |||
383 | ret = ath5k_eeprom_read_ants(ah, &offset, mode); | ||
384 | if (ret) | ||
385 | return ret; | ||
386 | |||
387 | AR5K_EEPROM_READ(offset++, val); | ||
388 | ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); | ||
389 | ee->ee_ob[mode][1] = (val >> 4) & 0x7; | ||
390 | ee->ee_db[mode][1] = val & 0x7; | ||
391 | |||
392 | ret = ath5k_eeprom_read_modes(ah, &offset, mode); | ||
393 | if (ret) | ||
394 | return ret; | ||
395 | |||
396 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { | ||
397 | AR5K_EEPROM_READ(offset++, val); | ||
398 | ee->ee_cal_pier[mode][0] = | ||
399 | ath5k_eeprom_bin2freq(ah, val & 0xff, mode); | ||
400 | ee->ee_cal_pier[mode][1] = | ||
401 | ath5k_eeprom_bin2freq(ah, (val >> 8) & 0xff, mode); | ||
402 | |||
403 | AR5K_EEPROM_READ(offset++, val); | ||
404 | ee->ee_turbo_max_power[mode] = val & 0x7f; | ||
405 | ee->ee_xr_power[mode] = (val >> 7) & 0x3f; | ||
406 | |||
407 | AR5K_EEPROM_READ(offset++, val); | ||
408 | ee->ee_cal_pier[mode][2] = | ||
409 | ath5k_eeprom_bin2freq(ah, val & 0xff, mode); | ||
410 | |||
411 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) | ||
412 | ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; | ||
413 | |||
414 | AR5K_EEPROM_READ(offset++, val); | ||
415 | ee->ee_i_cal[mode] = (val >> 8) & 0x3f; | ||
416 | ee->ee_q_cal[mode] = (val >> 3) & 0x1f; | ||
417 | |||
418 | if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { | ||
419 | AR5K_EEPROM_READ(offset++, val); | ||
420 | ee->ee_cck_ofdm_gain_delta = val & 0xff; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | /* | ||
425 | * Read 5GHz EEPROM channels | ||
426 | */ | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | /* | ||
432 | * Read the MAC address from eeprom | ||
433 | */ | ||
434 | int ath5k_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) | ||
435 | { | ||
436 | u8 mac_d[ETH_ALEN]; | ||
437 | u32 total, offset; | ||
438 | u16 data; | ||
439 | int octet, ret; | ||
440 | |||
441 | memset(mac, 0, ETH_ALEN); | ||
442 | memset(mac_d, 0, ETH_ALEN); | ||
443 | |||
444 | ret = ath5k_hw_eeprom_read(ah, 0x20, &data); | ||
445 | if (ret) | ||
446 | return ret; | ||
447 | |||
448 | for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) { | ||
449 | ret = ath5k_hw_eeprom_read(ah, offset, &data); | ||
450 | if (ret) | ||
451 | return ret; | ||
452 | |||
453 | total += data; | ||
454 | mac_d[octet + 1] = data & 0xff; | ||
455 | mac_d[octet] = data >> 8; | ||
456 | octet += 2; | ||
457 | } | ||
458 | |||
459 | memcpy(mac, mac_d, ETH_ALEN); | ||
460 | |||
461 | if (!total || total == 3 * 0xffff) | ||
462 | return -EINVAL; | ||
463 | |||
464 | return 0; | ||
465 | } | ||
466 | |||