linux/drivers/net/wireless/ath/ath9k/eeprom.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008-2011 Atheros Communications Inc.
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include "hw.h"
  18
  19void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val)
  20{
  21        REG_WRITE(ah, reg, val);
  22
  23        if (ah->config.analog_shiftreg)
  24                udelay(100);
  25}
  26
  27void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask,
  28                               u32 shift, u32 val)
  29{
  30        u32 regVal;
  31
  32        regVal = REG_READ(ah, reg) & ~mask;
  33        regVal |= (val << shift) & mask;
  34
  35        REG_WRITE(ah, reg, regVal);
  36
  37        if (ah->config.analog_shiftreg)
  38                udelay(100);
  39}
  40
  41int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight,
  42                             int16_t targetLeft, int16_t targetRight)
  43{
  44        int16_t rv;
  45
  46        if (srcRight == srcLeft) {
  47                rv = targetLeft;
  48        } else {
  49                rv = (int16_t) (((target - srcLeft) * targetRight +
  50                                 (srcRight - target) * targetLeft) /
  51                                (srcRight - srcLeft));
  52        }
  53        return rv;
  54}
  55
  56bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize,
  57                                    u16 *indexL, u16 *indexR)
  58{
  59        u16 i;
  60
  61        if (target <= pList[0]) {
  62                *indexL = *indexR = 0;
  63                return true;
  64        }
  65        if (target >= pList[listSize - 1]) {
  66                *indexL = *indexR = (u16) (listSize - 1);
  67                return true;
  68        }
  69
  70        for (i = 0; i < listSize - 1; i++) {
  71                if (pList[i] == target) {
  72                        *indexL = *indexR = i;
  73                        return true;
  74                }
  75                if (target < pList[i + 1]) {
  76                        *indexL = i;
  77                        *indexR = (u16) (i + 1);
  78                        return false;
  79                }
  80        }
  81        return false;
  82}
  83
  84void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data,
  85                                  int eep_start_loc, int size)
  86{
  87        int i = 0, j, addr;
  88        u32 addrdata[8];
  89        u32 data[8];
  90
  91        for (addr = 0; addr < size; addr++) {
  92                addrdata[i] = AR5416_EEPROM_OFFSET +
  93                        ((addr + eep_start_loc) << AR5416_EEPROM_S);
  94                i++;
  95                if (i == 8) {
  96                        REG_READ_MULTI(ah, addrdata, data, i);
  97
  98                        for (j = 0; j < i; j++) {
  99                                *eep_data = data[j];
 100                                eep_data++;
 101                        }
 102                        i = 0;
 103                }
 104        }
 105
 106        if (i != 0) {
 107                REG_READ_MULTI(ah, addrdata, data, i);
 108
 109                for (j = 0; j < i; j++) {
 110                        *eep_data = data[j];
 111                        eep_data++;
 112                }
 113        }
 114}
 115
 116static bool ath9k_hw_nvram_read_blob(struct ath_hw *ah, u32 off,
 117                                     u16 *data)
 118{
 119        u16 *blob_data;
 120
 121        if (off * sizeof(u16) > ah->eeprom_blob->size)
 122                return false;
 123
 124        blob_data = (u16 *)ah->eeprom_blob->data;
 125        *data =  blob_data[off];
 126        return true;
 127}
 128
 129bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data)
 130{
 131        struct ath_common *common = ath9k_hw_common(ah);
 132        bool ret;
 133
 134        if (ah->eeprom_blob)
 135                ret = ath9k_hw_nvram_read_blob(ah, off, data);
 136        else
 137                ret = common->bus_ops->eeprom_read(common, off, data);
 138
 139        if (!ret)
 140                ath_dbg(common, EEPROM,
 141                        "unable to read eeprom region at offset %u\n", off);
 142
 143        return ret;
 144}
 145
 146void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
 147                             u8 *pVpdList, u16 numIntercepts,
 148                             u8 *pRetVpdList)
 149{
 150        u16 i, k;
 151        u8 currPwr = pwrMin;
 152        u16 idxL = 0, idxR = 0;
 153
 154        for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) {
 155                ath9k_hw_get_lower_upper_index(currPwr, pPwrList,
 156                                               numIntercepts, &(idxL),
 157                                               &(idxR));
 158                if (idxR < 1)
 159                        idxR = 1;
 160                if (idxL == numIntercepts - 1)
 161                        idxL = (u16) (numIntercepts - 2);
 162                if (pPwrList[idxL] == pPwrList[idxR])
 163                        k = pVpdList[idxL];
 164                else
 165                        k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] +
 166                                   (pPwrList[idxR] - currPwr) * pVpdList[idxL]) /
 167                                  (pPwrList[idxR] - pPwrList[idxL]));
 168                pRetVpdList[i] = (u8) k;
 169                currPwr += 2;
 170        }
 171}
 172
 173void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah,
 174                                       struct ath9k_channel *chan,
 175                                       struct cal_target_power_leg *powInfo,
 176                                       u16 numChannels,
 177                                       struct cal_target_power_leg *pNewPower,
 178                                       u16 numRates, bool isExtTarget)
 179{
 180        struct chan_centers centers;
 181        u16 clo, chi;
 182        int i;
 183        int matchIndex = -1, lowIndex = -1;
 184        u16 freq;
 185
 186        ath9k_hw_get_channel_centers(ah, chan, &centers);
 187        freq = (isExtTarget) ? centers.ext_center : centers.ctl_center;
 188
 189        if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel,
 190                                       IS_CHAN_2GHZ(chan))) {
 191                matchIndex = 0;
 192        } else {
 193                for (i = 0; (i < numChannels) &&
 194                             (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
 195                        if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel,
 196                                                       IS_CHAN_2GHZ(chan))) {
 197                                matchIndex = i;
 198                                break;
 199                        } else if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel,
 200                                                IS_CHAN_2GHZ(chan)) && i > 0 &&
 201                                   freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel,
 202                                                IS_CHAN_2GHZ(chan))) {
 203                                lowIndex = i - 1;
 204                                break;
 205                        }
 206                }
 207                if ((matchIndex == -1) && (lowIndex == -1))
 208                        matchIndex = i - 1;
 209        }
 210
 211        if (matchIndex != -1) {
 212                *pNewPower = powInfo[matchIndex];
 213        } else {
 214                clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
 215                                         IS_CHAN_2GHZ(chan));
 216                chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
 217                                         IS_CHAN_2GHZ(chan));
 218
 219                for (i = 0; i < numRates; i++) {
 220                        pNewPower->tPow2x[i] =
 221                                (u8)ath9k_hw_interpolate(freq, clo, chi,
 222                                                powInfo[lowIndex].tPow2x[i],
 223                                                powInfo[lowIndex + 1].tPow2x[i]);
 224                }
 225        }
 226}
 227
 228void ath9k_hw_get_target_powers(struct ath_hw *ah,
 229                                struct ath9k_channel *chan,
 230                                struct cal_target_power_ht *powInfo,
 231                                u16 numChannels,
 232                                struct cal_target_power_ht *pNewPower,
 233                                u16 numRates, bool isHt40Target)
 234{
 235        struct chan_centers centers;
 236        u16 clo, chi;
 237        int i;
 238        int matchIndex = -1, lowIndex = -1;
 239        u16 freq;
 240
 241        ath9k_hw_get_channel_centers(ah, chan, &centers);
 242        freq = isHt40Target ? centers.synth_center : centers.ctl_center;
 243
 244        if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) {
 245                matchIndex = 0;
 246        } else {
 247                for (i = 0; (i < numChannels) &&
 248                             (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
 249                        if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel,
 250                                                       IS_CHAN_2GHZ(chan))) {
 251                                matchIndex = i;
 252                                break;
 253                        } else
 254                                if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel,
 255                                                IS_CHAN_2GHZ(chan)) && i > 0 &&
 256                                    freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel,
 257                                                IS_CHAN_2GHZ(chan))) {
 258                                        lowIndex = i - 1;
 259                                        break;
 260                                }
 261                }
 262                if ((matchIndex == -1) && (lowIndex == -1))
 263                        matchIndex = i - 1;
 264        }
 265
 266        if (matchIndex != -1) {
 267                *pNewPower = powInfo[matchIndex];
 268        } else {
 269                clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
 270                                         IS_CHAN_2GHZ(chan));
 271                chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
 272                                         IS_CHAN_2GHZ(chan));
 273
 274                for (i = 0; i < numRates; i++) {
 275                        pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq,
 276                                                clo, chi,
 277                                                powInfo[lowIndex].tPow2x[i],
 278                                                powInfo[lowIndex + 1].tPow2x[i]);
 279                }
 280        }
 281}
 282
 283u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower,
 284                                bool is2GHz, int num_band_edges)
 285{
 286        u16 twiceMaxEdgePower = MAX_RATE_POWER;
 287        int i;
 288
 289        for (i = 0; (i < num_band_edges) &&
 290                     (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
 291                if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) {
 292                        twiceMaxEdgePower = CTL_EDGE_TPOWER(pRdEdgesPower[i].ctl);
 293                        break;
 294                } else if ((i > 0) &&
 295                           (freq < ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel,
 296                                                      is2GHz))) {
 297                        if (ath9k_hw_fbin2freq(pRdEdgesPower[i - 1].bChannel,
 298                                               is2GHz) < freq &&
 299                            CTL_EDGE_FLAGS(pRdEdgesPower[i - 1].ctl)) {
 300                                twiceMaxEdgePower =
 301                                        CTL_EDGE_TPOWER(pRdEdgesPower[i - 1].ctl);
 302                        }
 303                        break;
 304                }
 305        }
 306
 307        return twiceMaxEdgePower;
 308}
 309
 310u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit,
 311                              u8 antenna_reduction)
 312{
 313        u16 reduction = antenna_reduction;
 314
 315        /*
 316         * Reduce scaled Power by number of chains active
 317         * to get the per chain tx power level.
 318         */
 319        switch (ar5416_get_ntxchains(ah->txchainmask)) {
 320        case 1:
 321                break;
 322        case 2:
 323                reduction += POWER_CORRECTION_FOR_TWO_CHAIN;
 324                break;
 325        case 3:
 326                reduction += POWER_CORRECTION_FOR_THREE_CHAIN;
 327                break;
 328        }
 329
 330        if (power_limit > reduction)
 331                power_limit -= reduction;
 332        else
 333                power_limit = 0;
 334
 335        return power_limit;
 336}
 337
 338void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah)
 339{
 340        struct ath_common *common = ath9k_hw_common(ah);
 341        struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
 342
 343        switch (ar5416_get_ntxchains(ah->txchainmask)) {
 344        case 1:
 345                break;
 346        case 2:
 347                regulatory->max_power_level += POWER_CORRECTION_FOR_TWO_CHAIN;
 348                break;
 349        case 3:
 350                regulatory->max_power_level += POWER_CORRECTION_FOR_THREE_CHAIN;
 351                break;
 352        default:
 353                ath_dbg(common, EEPROM, "Invalid chainmask configuration\n");
 354                break;
 355        }
 356}
 357
 358void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
 359                                struct ath9k_channel *chan,
 360                                void *pRawDataSet,
 361                                u8 *bChans, u16 availPiers,
 362                                u16 tPdGainOverlap,
 363                                u16 *pPdGainBoundaries, u8 *pPDADCValues,
 364                                u16 numXpdGains)
 365{
 366        int i, j, k;
 367        int16_t ss;
 368        u16 idxL = 0, idxR = 0, numPiers;
 369        static u8 vpdTableL[AR5416_NUM_PD_GAINS]
 370                [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
 371        static u8 vpdTableR[AR5416_NUM_PD_GAINS]
 372                [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
 373        static u8 vpdTableI[AR5416_NUM_PD_GAINS]
 374                [AR5416_MAX_PWR_RANGE_IN_HALF_DB];
 375
 376        u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR;
 377        u8 minPwrT4[AR5416_NUM_PD_GAINS];
 378        u8 maxPwrT4[AR5416_NUM_PD_GAINS];
 379        int16_t vpdStep;
 380        int16_t tmpVal;
 381        u16 sizeCurrVpdTable, maxIndex, tgtIndex;
 382        bool match;
 383        int16_t minDelta = 0;
 384        struct chan_centers centers;
 385        int pdgain_boundary_default;
 386        struct cal_data_per_freq *data_def = pRawDataSet;
 387        struct cal_data_per_freq_4k *data_4k = pRawDataSet;
 388        struct cal_data_per_freq_ar9287 *data_9287 = pRawDataSet;
 389        bool eeprom_4k = AR_SREV_9285(ah) || AR_SREV_9271(ah);
 390        int intercepts;
 391
 392        if (AR_SREV_9287(ah))
 393                intercepts = AR9287_PD_GAIN_ICEPTS;
 394        else
 395                intercepts = AR5416_PD_GAIN_ICEPTS;
 396
 397        memset(&minPwrT4, 0, AR5416_NUM_PD_GAINS);
 398        ath9k_hw_get_channel_centers(ah, chan, &centers);
 399
 400        for (numPiers = 0; numPiers < availPiers; numPiers++) {
 401                if (bChans[numPiers] == AR5416_BCHAN_UNUSED)
 402                        break;
 403        }
 404
 405        match = ath9k_hw_get_lower_upper_index((u8)FREQ2FBIN(centers.synth_center,
 406                                                             IS_CHAN_2GHZ(chan)),
 407                                               bChans, numPiers, &idxL, &idxR);
 408
 409        if (match) {
 410                if (AR_SREV_9287(ah)) {
 411                        /* FIXME: array overrun? */
 412                        for (i = 0; i < numXpdGains; i++) {
 413                                minPwrT4[i] = data_9287[idxL].pwrPdg[i][0];
 414                                maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4];
 415                                ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
 416                                                data_9287[idxL].pwrPdg[i],
 417                                                data_9287[idxL].vpdPdg[i],
 418                                                intercepts,
 419                                                vpdTableI[i]);
 420                        }
 421                } else if (eeprom_4k) {
 422                        for (i = 0; i < numXpdGains; i++) {
 423                                minPwrT4[i] = data_4k[idxL].pwrPdg[i][0];
 424                                maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4];
 425                                ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
 426                                                data_4k[idxL].pwrPdg[i],
 427                                                data_4k[idxL].vpdPdg[i],
 428                                                intercepts,
 429                                                vpdTableI[i]);
 430                        }
 431                } else {
 432                        for (i = 0; i < numXpdGains; i++) {
 433                                minPwrT4[i] = data_def[idxL].pwrPdg[i][0];
 434                                maxPwrT4[i] = data_def[idxL].pwrPdg[i][4];
 435                                ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
 436                                                data_def[idxL].pwrPdg[i],
 437                                                data_def[idxL].vpdPdg[i],
 438                                                intercepts,
 439                                                vpdTableI[i]);
 440                        }
 441                }
 442        } else {
 443                for (i = 0; i < numXpdGains; i++) {
 444                        if (AR_SREV_9287(ah)) {
 445                                pVpdL = data_9287[idxL].vpdPdg[i];
 446                                pPwrL = data_9287[idxL].pwrPdg[i];
 447                                pVpdR = data_9287[idxR].vpdPdg[i];
 448                                pPwrR = data_9287[idxR].pwrPdg[i];
 449                        } else if (eeprom_4k) {
 450                                pVpdL = data_4k[idxL].vpdPdg[i];
 451                                pPwrL = data_4k[idxL].pwrPdg[i];
 452                                pVpdR = data_4k[idxR].vpdPdg[i];
 453                                pPwrR = data_4k[idxR].pwrPdg[i];
 454                        } else {
 455                                pVpdL = data_def[idxL].vpdPdg[i];
 456                                pPwrL = data_def[idxL].pwrPdg[i];
 457                                pVpdR = data_def[idxR].vpdPdg[i];
 458                                pPwrR = data_def[idxR].pwrPdg[i];
 459                        }
 460
 461                        minPwrT4[i] = max(pPwrL[0], pPwrR[0]);
 462
 463                        maxPwrT4[i] =
 464                                min(pPwrL[intercepts - 1],
 465                                    pPwrR[intercepts - 1]);
 466
 467
 468                        ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
 469                                                pPwrL, pVpdL,
 470                                                intercepts,
 471                                                vpdTableL[i]);
 472                        ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
 473                                                pPwrR, pVpdR,
 474                                                intercepts,
 475                                                vpdTableR[i]);
 476
 477                        for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) {
 478                                vpdTableI[i][j] =
 479                                        (u8)(ath9k_hw_interpolate((u16)
 480                                             FREQ2FBIN(centers.
 481                                                       synth_center,
 482                                                       IS_CHAN_2GHZ
 483                                                       (chan)),
 484                                             bChans[idxL], bChans[idxR],
 485                                             vpdTableL[i][j], vpdTableR[i][j]));
 486                        }
 487                }
 488        }
 489
 490        k = 0;
 491
 492        for (i = 0; i < numXpdGains; i++) {
 493                if (i == (numXpdGains - 1))
 494                        pPdGainBoundaries[i] =
 495                                (u16)(maxPwrT4[i] / 2);
 496                else
 497                        pPdGainBoundaries[i] =
 498                                (u16)((maxPwrT4[i] + minPwrT4[i + 1]) / 4);
 499
 500                pPdGainBoundaries[i] =
 501                        min((u16)MAX_RATE_POWER, pPdGainBoundaries[i]);
 502
 503                minDelta = 0;
 504
 505                if (i == 0) {
 506                        if (AR_SREV_9280_20_OR_LATER(ah))
 507                                ss = (int16_t)(0 - (minPwrT4[i] / 2));
 508                        else
 509                                ss = 0;
 510                } else {
 511                        ss = (int16_t)((pPdGainBoundaries[i - 1] -
 512                                        (minPwrT4[i] / 2)) -
 513                                       tPdGainOverlap + 1 + minDelta);
 514                }
 515                vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]);
 516                vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
 517
 518                while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
 519                        tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep);
 520                        pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal);
 521                        ss++;
 522                }
 523
 524                sizeCurrVpdTable = (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1);
 525                tgtIndex = (u8)(pPdGainBoundaries[i] + tPdGainOverlap -
 526                                (minPwrT4[i] / 2));
 527                maxIndex = (tgtIndex < sizeCurrVpdTable) ?
 528                        tgtIndex : sizeCurrVpdTable;
 529
 530                while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
 531                        pPDADCValues[k++] = vpdTableI[i][ss++];
 532                }
 533
 534                vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] -
 535                                    vpdTableI[i][sizeCurrVpdTable - 2]);
 536                vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
 537
 538                if (tgtIndex >= maxIndex) {
 539                        while ((ss <= tgtIndex) &&
 540                               (k < (AR5416_NUM_PDADC_VALUES - 1))) {
 541                                tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] +
 542                                                    (ss - maxIndex + 1) * vpdStep));
 543                                pPDADCValues[k++] = (u8)((tmpVal > 255) ?
 544                                                         255 : tmpVal);
 545                                ss++;
 546                        }
 547                }
 548        }
 549
 550        if (eeprom_4k)
 551                pdgain_boundary_default = 58;
 552        else
 553                pdgain_boundary_default = pPdGainBoundaries[i - 1];
 554
 555        while (i < AR5416_PD_GAINS_IN_MASK) {
 556                pPdGainBoundaries[i] = pdgain_boundary_default;
 557                i++;
 558        }
 559
 560        while (k < AR5416_NUM_PDADC_VALUES) {
 561                pPDADCValues[k] = pPDADCValues[k - 1];
 562                k++;
 563        }
 564}
 565
 566int ath9k_hw_eeprom_init(struct ath_hw *ah)
 567{
 568        int status;
 569
 570        if (AR_SREV_9300_20_OR_LATER(ah))
 571                ah->eep_ops = &eep_ar9300_ops;
 572        else if (AR_SREV_9287(ah)) {
 573                ah->eep_ops = &eep_ar9287_ops;
 574        } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) {
 575                ah->eep_ops = &eep_4k_ops;
 576        } else {
 577                ah->eep_ops = &eep_def_ops;
 578        }
 579
 580        if (!ah->eep_ops->fill_eeprom(ah))
 581                return -EIO;
 582
 583        status = ah->eep_ops->check_eeprom(ah);
 584
 585        return status;
 586}
 587