linux/drivers/net/wireless/ath/ath9k/eeprom.c
Martin Blumenstingl 4bca5303eb ath9k: define all EEPROM fields in Little Endian format
The ar9300_eeprom logic is already using only 8-bit (endian neutral),
__le16 and __le32 fields to state explicitly how the values should be
interpreted.
All other EEPROM implementations (4k, 9287 and def) were using u16 and
u32 fields with additional logic to swap the values (read from the
original EEPROM) so they match the current CPUs endianness.

The EEPROM format defaults to "all values are Little Endian", indicated
by the absence of the AR5416_EEPMISC_BIG_ENDIAN in the u8 EEPMISC
register. If we detect that the EEPROM indicates Big Endian mode
(AR5416_EEPMISC_BIG_ENDIAN is set in the EEPMISC register) then we'll
swap the values to convert them into Little Endian. This is done by
activating the EEPMISC based logic in ath9k_hw_nvram_swap_data even if
AH_NO_EEP_SWAP is set (this makes ath9k behave like the FreeBSD driver,
which also does not have a flag to enable swapping based on the
AR5416_EEPMISC_BIG_ENDIAN bit). Before this logic was only used to
enable swapping when "current CPU endianness != EEPROM endianness".

After changing all relevant fields to __le16 and __le32 sparse was used
to check that all code which reads any of these fields uses
le{16,32}_to_cpu.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
2016-12-15 10:26:42 +02:00

682 lines
17 KiB
C

/*
* Copyright (c) 2008-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "hw.h"
#include <linux/ath9k_platform.h>
void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val)
{
REG_WRITE(ah, reg, val);
if (ah->config.analog_shiftreg)
udelay(100);
}
void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask,
u32 shift, u32 val)
{
REG_RMW(ah, reg, ((val << shift) & mask), mask);
if (ah->config.analog_shiftreg)
udelay(100);
}
int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight,
int16_t targetLeft, int16_t targetRight)
{
int16_t rv;
if (srcRight == srcLeft) {
rv = targetLeft;
} else {
rv = (int16_t) (((target - srcLeft) * targetRight +
(srcRight - target) * targetLeft) /
(srcRight - srcLeft));
}
return rv;
}
bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize,
u16 *indexL, u16 *indexR)
{
u16 i;
if (target <= pList[0]) {
*indexL = *indexR = 0;
return true;
}
if (target >= pList[listSize - 1]) {
*indexL = *indexR = (u16) (listSize - 1);
return true;
}
for (i = 0; i < listSize - 1; i++) {
if (pList[i] == target) {
*indexL = *indexR = i;
return true;
}
if (target < pList[i + 1]) {
*indexL = i;
*indexR = (u16) (i + 1);
return false;
}
}
return false;
}
void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data,
int eep_start_loc, int size)
{
int i = 0, j, addr;
u32 addrdata[8];
u32 data[8];
for (addr = 0; addr < size; addr++) {
addrdata[i] = AR5416_EEPROM_OFFSET +
((addr + eep_start_loc) << AR5416_EEPROM_S);
i++;
if (i == 8) {
REG_READ_MULTI(ah, addrdata, data, i);
for (j = 0; j < i; j++) {
*eep_data = data[j];
eep_data++;
}
i = 0;
}
}
if (i != 0) {
REG_READ_MULTI(ah, addrdata, data, i);
for (j = 0; j < i; j++) {
*eep_data = data[j];
eep_data++;
}
}
}
static bool ath9k_hw_nvram_read_array(u16 *blob, size_t blob_size,
off_t offset, u16 *data)
{
if (offset > blob_size)
return false;
*data = blob[offset];
return true;
}
static bool ath9k_hw_nvram_read_pdata(struct ath9k_platform_data *pdata,
off_t offset, u16 *data)
{
return ath9k_hw_nvram_read_array(pdata->eeprom_data,
ARRAY_SIZE(pdata->eeprom_data),
offset, data);
}
static bool ath9k_hw_nvram_read_firmware(const struct firmware *eeprom_blob,
off_t offset, u16 *data)
{
return ath9k_hw_nvram_read_array((u16 *) eeprom_blob->data,
eeprom_blob->size / sizeof(u16),
offset, data);
}
bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data)
{
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_platform_data *pdata = ah->dev->platform_data;
bool ret;
if (ah->eeprom_blob)
ret = ath9k_hw_nvram_read_firmware(ah->eeprom_blob, off, data);
else if (pdata && !pdata->use_eeprom && pdata->eeprom_data)
ret = ath9k_hw_nvram_read_pdata(pdata, off, data);
else
ret = common->bus_ops->eeprom_read(common, off, data);
if (!ret)
ath_dbg(common, EEPROM,
"unable to read eeprom region at offset %u\n", off);
return ret;
}
int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size)
{
u16 magic;
u16 *eepdata;
int i;
bool needs_byteswap = false;
struct ath_common *common = ath9k_hw_common(ah);
if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
ath_err(common, "Reading Magic # failed\n");
return -EIO;
}
if (swab16(magic) == AR5416_EEPROM_MAGIC) {
needs_byteswap = true;
ath_dbg(common, EEPROM,
"EEPROM needs byte-swapping to correct endianness.\n");
} else if (magic != AR5416_EEPROM_MAGIC) {
if (ath9k_hw_use_flash(ah)) {
ath_dbg(common, EEPROM,
"Ignoring invalid EEPROM magic (0x%04x).\n",
magic);
} else {
ath_err(common,
"Invalid EEPROM magic (0x%04x).\n", magic);
return -EINVAL;
}
}
if (needs_byteswap) {
if (ah->ah_flags & AH_NO_EEP_SWAP) {
ath_info(common,
"Ignoring endianness difference in EEPROM magic bytes.\n");
} else {
eepdata = (u16 *)(&ah->eeprom);
for (i = 0; i < size; i++)
eepdata[i] = swab16(eepdata[i]);
}
}
if (ah->eep_ops->get_eepmisc(ah) & AR5416_EEPMISC_BIG_ENDIAN) {
*swap_needed = true;
ath_dbg(common, EEPROM,
"Big Endian EEPROM detected according to EEPMISC register.\n");
} else {
*swap_needed = false;
}
return 0;
}
bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size)
{
u32 i, sum = 0;
u16 *eepdata = (u16 *)(&ah->eeprom);
struct ath_common *common = ath9k_hw_common(ah);
for (i = 0; i < size; i++)
sum ^= eepdata[i];
if (sum != 0xffff) {
ath_err(common, "Bad EEPROM checksum 0x%x\n", sum);
return false;
}
return true;
}
bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev)
{
struct ath_common *common = ath9k_hw_common(ah);
if (ah->eep_ops->get_eeprom_ver(ah) != version ||
ah->eep_ops->get_eeprom_rev(ah) < minrev) {
ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n",
ah->eep_ops->get_eeprom_ver(ah),
ah->eep_ops->get_eeprom_rev(ah));
return false;
}
return true;
}
void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
u8 *pVpdList, u16 numIntercepts,
u8 *pRetVpdList)
{
u16 i, k;
u8 currPwr = pwrMin;
u16 idxL = 0, idxR = 0;
for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) {
ath9k_hw_get_lower_upper_index(currPwr, pPwrList,
numIntercepts, &(idxL),
&(idxR));
if (idxR < 1)
idxR = 1;
if (idxL == numIntercepts - 1)
idxL = (u16) (numIntercepts - 2);
if (pPwrList[idxL] == pPwrList[idxR])
k = pVpdList[idxL];
else
k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] +
(pPwrList[idxR] - currPwr) * pVpdList[idxL]) /
(pPwrList[idxR] - pPwrList[idxL]));
pRetVpdList[i] = (u8) k;
currPwr += 2;
}
}
void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah,
struct ath9k_channel *chan,
struct cal_target_power_leg *powInfo,
u16 numChannels,
struct cal_target_power_leg *pNewPower,
u16 numRates, bool isExtTarget)
{
struct chan_centers centers;
u16 clo, chi;
int i;
int matchIndex = -1, lowIndex = -1;
u16 freq;
ath9k_hw_get_channel_centers(ah, chan, &centers);
freq = (isExtTarget) ? centers.ext_center : centers.ctl_center;
if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel,
IS_CHAN_2GHZ(chan))) {
matchIndex = 0;
} else {
for (i = 0; (i < numChannels) &&
(powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel,
IS_CHAN_2GHZ(chan))) {
matchIndex = i;
break;
} else if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel,
IS_CHAN_2GHZ(chan)) && i > 0 &&
freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel,
IS_CHAN_2GHZ(chan))) {
lowIndex = i - 1;
break;
}
}
if ((matchIndex == -1) && (lowIndex == -1))
matchIndex = i - 1;
}
if (matchIndex != -1) {
*pNewPower = powInfo[matchIndex];
} else {
clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
IS_CHAN_2GHZ(chan));
chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
IS_CHAN_2GHZ(chan));
for (i = 0; i < numRates; i++) {
pNewPower->tPow2x[i] =
(u8)ath9k_hw_interpolate(freq, clo, chi,
powInfo[lowIndex].tPow2x[i],
powInfo[lowIndex + 1].tPow2x[i]);
}
}
}
void ath9k_hw_get_target_powers(struct ath_hw *ah,
struct ath9k_channel *chan,
struct cal_target_power_ht *powInfo,
u16 numChannels,
struct cal_target_power_ht *pNewPower,
u16 numRates, bool isHt40Target)
{
struct chan_centers centers;
u16 clo, chi;
int i;
int matchIndex = -1, lowIndex = -1;
u16 freq;
ath9k_hw_get_channel_centers(ah, chan, &centers);
freq = isHt40Target ? centers.synth_center : centers.ctl_center;
if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) {
matchIndex = 0;
} else {
for (i = 0; (i < numChannels) &&
(powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel,
IS_CHAN_2GHZ(chan))) {
matchIndex = i;
break;
} else
if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel,
IS_CHAN_2GHZ(chan)) && i > 0 &&
freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel,
IS_CHAN_2GHZ(chan))) {
lowIndex = i - 1;
break;
}
}
if ((matchIndex == -1) && (lowIndex == -1))
matchIndex = i - 1;
}
if (matchIndex != -1) {
*pNewPower = powInfo[matchIndex];
} else {
clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel,
IS_CHAN_2GHZ(chan));
chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel,
IS_CHAN_2GHZ(chan));
for (i = 0; i < numRates; i++) {
pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq,
clo, chi,
powInfo[lowIndex].tPow2x[i],
powInfo[lowIndex + 1].tPow2x[i]);
}
}
}
u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower,
bool is2GHz, int num_band_edges)
{
u16 twiceMaxEdgePower = MAX_RATE_POWER;
int i;
for (i = 0; (i < num_band_edges) &&
(pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) {
if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) {
twiceMaxEdgePower = CTL_EDGE_TPOWER(pRdEdgesPower[i].ctl);
break;
} else if ((i > 0) &&
(freq < ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel,
is2GHz))) {
if (ath9k_hw_fbin2freq(pRdEdgesPower[i - 1].bChannel,
is2GHz) < freq &&
CTL_EDGE_FLAGS(pRdEdgesPower[i - 1].ctl)) {
twiceMaxEdgePower =
CTL_EDGE_TPOWER(pRdEdgesPower[i - 1].ctl);
}
break;
}
}
return twiceMaxEdgePower;
}
u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit,
u8 antenna_reduction)
{
u16 reduction = antenna_reduction;
/*
* Reduce scaled Power by number of chains active
* to get the per chain tx power level.
*/
switch (ar5416_get_ntxchains(ah->txchainmask)) {
case 1:
break;
case 2:
reduction += POWER_CORRECTION_FOR_TWO_CHAIN;
break;
case 3:
reduction += POWER_CORRECTION_FOR_THREE_CHAIN;
break;
}
if (power_limit > reduction)
power_limit -= reduction;
else
power_limit = 0;
return power_limit;
}
void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
switch (ar5416_get_ntxchains(ah->txchainmask)) {
case 1:
break;
case 2:
regulatory->max_power_level += POWER_CORRECTION_FOR_TWO_CHAIN;
break;
case 3:
regulatory->max_power_level += POWER_CORRECTION_FOR_THREE_CHAIN;
break;
default:
ath_dbg(common, EEPROM, "Invalid chainmask configuration\n");
break;
}
}
void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah,
struct ath9k_channel *chan,
void *pRawDataSet,
u8 *bChans, u16 availPiers,
u16 tPdGainOverlap,
u16 *pPdGainBoundaries, u8 *pPDADCValues,
u16 numXpdGains)
{
int i, j, k;
int16_t ss;
u16 idxL = 0, idxR = 0, numPiers;
static u8 vpdTableL[AR5416_NUM_PD_GAINS]
[AR5416_MAX_PWR_RANGE_IN_HALF_DB];
static u8 vpdTableR[AR5416_NUM_PD_GAINS]
[AR5416_MAX_PWR_RANGE_IN_HALF_DB];
static u8 vpdTableI[AR5416_NUM_PD_GAINS]
[AR5416_MAX_PWR_RANGE_IN_HALF_DB];
u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR;
u8 minPwrT4[AR5416_NUM_PD_GAINS];
u8 maxPwrT4[AR5416_NUM_PD_GAINS];
int16_t vpdStep;
int16_t tmpVal;
u16 sizeCurrVpdTable, maxIndex, tgtIndex;
bool match;
int16_t minDelta = 0;
struct chan_centers centers;
int pdgain_boundary_default;
struct cal_data_per_freq *data_def = pRawDataSet;
struct cal_data_per_freq_4k *data_4k = pRawDataSet;
struct cal_data_per_freq_ar9287 *data_9287 = pRawDataSet;
bool eeprom_4k = AR_SREV_9285(ah) || AR_SREV_9271(ah);
int intercepts;
if (AR_SREV_9287(ah))
intercepts = AR9287_PD_GAIN_ICEPTS;
else
intercepts = AR5416_PD_GAIN_ICEPTS;
memset(&minPwrT4, 0, AR5416_NUM_PD_GAINS);
ath9k_hw_get_channel_centers(ah, chan, &centers);
for (numPiers = 0; numPiers < availPiers; numPiers++) {
if (bChans[numPiers] == AR5416_BCHAN_UNUSED)
break;
}
match = ath9k_hw_get_lower_upper_index((u8)FREQ2FBIN(centers.synth_center,
IS_CHAN_2GHZ(chan)),
bChans, numPiers, &idxL, &idxR);
if (match) {
if (AR_SREV_9287(ah)) {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_9287[idxL].pwrPdg[i][0];
maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_9287[idxL].pwrPdg[i],
data_9287[idxL].vpdPdg[i],
intercepts,
vpdTableI[i]);
}
} else if (eeprom_4k) {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_4k[idxL].pwrPdg[i][0];
maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_4k[idxL].pwrPdg[i],
data_4k[idxL].vpdPdg[i],
intercepts,
vpdTableI[i]);
}
} else {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_def[idxL].pwrPdg[i][0];
maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_def[idxL].pwrPdg[i],
data_def[idxL].vpdPdg[i],
intercepts,
vpdTableI[i]);
}
}
} else {
for (i = 0; i < numXpdGains; i++) {
if (AR_SREV_9287(ah)) {
pVpdL = data_9287[idxL].vpdPdg[i];
pPwrL = data_9287[idxL].pwrPdg[i];
pVpdR = data_9287[idxR].vpdPdg[i];
pPwrR = data_9287[idxR].pwrPdg[i];
} else if (eeprom_4k) {
pVpdL = data_4k[idxL].vpdPdg[i];
pPwrL = data_4k[idxL].pwrPdg[i];
pVpdR = data_4k[idxR].vpdPdg[i];
pPwrR = data_4k[idxR].pwrPdg[i];
} else {
pVpdL = data_def[idxL].vpdPdg[i];
pPwrL = data_def[idxL].pwrPdg[i];
pVpdR = data_def[idxR].vpdPdg[i];
pPwrR = data_def[idxR].pwrPdg[i];
}
minPwrT4[i] = max(pPwrL[0], pPwrR[0]);
maxPwrT4[i] =
min(pPwrL[intercepts - 1],
pPwrR[intercepts - 1]);
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
pPwrL, pVpdL,
intercepts,
vpdTableL[i]);
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
pPwrR, pVpdR,
intercepts,
vpdTableR[i]);
for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) {
vpdTableI[i][j] =
(u8)(ath9k_hw_interpolate((u16)
FREQ2FBIN(centers.
synth_center,
IS_CHAN_2GHZ
(chan)),
bChans[idxL], bChans[idxR],
vpdTableL[i][j], vpdTableR[i][j]));
}
}
}
k = 0;
for (i = 0; i < numXpdGains; i++) {
if (i == (numXpdGains - 1))
pPdGainBoundaries[i] =
(u16)(maxPwrT4[i] / 2);
else
pPdGainBoundaries[i] =
(u16)((maxPwrT4[i] + minPwrT4[i + 1]) / 4);
pPdGainBoundaries[i] =
min((u16)MAX_RATE_POWER, pPdGainBoundaries[i]);
minDelta = 0;
if (i == 0) {
if (AR_SREV_9280_20_OR_LATER(ah))
ss = (int16_t)(0 - (minPwrT4[i] / 2));
else
ss = 0;
} else {
ss = (int16_t)((pPdGainBoundaries[i - 1] -
(minPwrT4[i] / 2)) -
tPdGainOverlap + 1 + minDelta);
}
vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]);
vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep);
pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal);
ss++;
}
sizeCurrVpdTable = (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1);
tgtIndex = (u8)(pPdGainBoundaries[i] + tPdGainOverlap -
(minPwrT4[i] / 2));
maxIndex = (tgtIndex < sizeCurrVpdTable) ?
tgtIndex : sizeCurrVpdTable;
while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) {
pPDADCValues[k++] = vpdTableI[i][ss++];
}
vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] -
vpdTableI[i][sizeCurrVpdTable - 2]);
vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
if (tgtIndex >= maxIndex) {
while ((ss <= tgtIndex) &&
(k < (AR5416_NUM_PDADC_VALUES - 1))) {
tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] +
(ss - maxIndex + 1) * vpdStep));
pPDADCValues[k++] = (u8)((tmpVal > 255) ?
255 : tmpVal);
ss++;
}
}
}
if (eeprom_4k)
pdgain_boundary_default = 58;
else
pdgain_boundary_default = pPdGainBoundaries[i - 1];
while (i < AR5416_PD_GAINS_IN_MASK) {
pPdGainBoundaries[i] = pdgain_boundary_default;
i++;
}
while (k < AR5416_NUM_PDADC_VALUES) {
pPDADCValues[k] = pPDADCValues[k - 1];
k++;
}
}
int ath9k_hw_eeprom_init(struct ath_hw *ah)
{
int status;
if (AR_SREV_9300_20_OR_LATER(ah))
ah->eep_ops = &eep_ar9300_ops;
else if (AR_SREV_9287(ah)) {
ah->eep_ops = &eep_ar9287_ops;
} else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) {
ah->eep_ops = &eep_4k_ops;
} else {
ah->eep_ops = &eep_def_ops;
}
if (!ah->eep_ops->fill_eeprom(ah))
return -EIO;
status = ah->eep_ops->check_eeprom(ah);
return status;
}