Nick Kossifidis | c6e387a | 2008-08-29 22:45:39 +0300 | [diff] [blame^] | 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 | |