/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2008 Atheros Communications Inc.
*
* 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 <sys/ethernet.h>
#include <sys/byteorder.h>
#include <inet/wifi_ioctl.h>
#include "arn_core.h"
#include "arn_hw.h"
#include "arn_reg.h"
#include "arn_phy.h"
static void
{
drv_usecwait(100);
}
static inline uint16_t
{
if (fbin == AR5416_BCHAN_UNUSED)
return (fbin);
}
static inline int16_t
{
rv = targetLeft;
} else {
}
return (rv);
}
static inline boolean_t
{
uint16_t i;
return (B_TRUE);
}
return (B_TRUE);
}
for (i = 0; i < listSize - 1; i++) {
return (B_TRUE);
}
*indexL = i;
return (B_FALSE);
}
}
return (B_FALSE);
}
static boolean_t
{
return (B_FALSE);
}
return (B_TRUE);
}
/* ARGSUSED */
static int
{
"using flash but eepom\n"));
return (0);
}
static boolean_t
{
return (B_TRUE);
}
static inline boolean_t
{
if (ath9k_hw_use_flash(ah))
else
}
static boolean_t
{
eep_start_loc = 64;
if (!ath9k_hw_use_flash(ah)) {
"Reading from EEPROM, not flash\n"));
}
"Unable to read eeprom region \n"));
return (B_FALSE);
}
eep_data++;
}
return (B_TRUE);
}
static boolean_t
{
eep_data)) {
"Unable to read eeprom region\n"));
return (B_FALSE);
}
eep_data++;
}
return (B_TRUE);
}
};
static inline boolean_t
{
}
static int
{
"%s: Reading Magic # failed\n", __func__));
return (B_FALSE);
}
if (!ath9k_hw_use_flash(ah)) {
if (magic != AR5416_EEPROM_MAGIC) {
if (magic2 == AR5416_EEPROM_MAGIC) {
size = sizeof (struct ar5416_eeprom_def);
addr++) {
eepdata++;
"0x%04X ", *eepdata));
"arn: "
"%s\n", __func__));
}
} else {
"Invalid EEPROM Magic. "
"endianness mismatch.\n"));
return (EINVAL);
}
}
}
if (need_swap)
else
if (el > sizeof (struct ar5416_eeprom_def))
else
for (i = 0; i < el; i++)
if (need_swap) {
"EEPROM Endianness is not native.. Changing \n"));
&eep->modalHeader[j];
for (i = 0; i < AR5416_MAX_CHAINS; i++) {
}
for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {
}
}
}
"Bad EEPROM checksum 0x%x or revision 0x%04x\n",
return (EINVAL);
}
return (0);
}
static int
{
int i, addr;
if (!ath9k_hw_use_flash(ah)) {
&magic)) {
"Reading Magic # failed\n"));
return (B_FALSE);
}
"Read Magic = 0x%04X\n", magic));
if (magic != AR5416_EEPROM_MAGIC) {
if (magic2 == AR5416_EEPROM_MAGIC) {
eepdata++;
"0x%04X ", *eepdata));
}
} else {
"Invalid EEPROM Magic. "
"endianness mismatch.\n"));
return (EINVAL);
}
}
}
if (need_swap)
else
if (el > sizeof (struct ar5416_eeprom_def))
else
for (i = 0; i < el; i++)
if (need_swap) {
"EEPROM Endianness is not native.. Changing \n"));
for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
}
for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {
}
}
"Bad EEPROM checksum 0x%x or revision 0x%04x\n",
return (EINVAL);
}
return (0);
}
static int
};
static inline int
{
}
static inline boolean_t
{
uint16_t i, k;
if (idxR < 1)
idxR = 1;
else
k = (uint16_t)
pRetVpdList[i] = (uint8_t)k;
currPwr += 2;
}
return (B_TRUE);
}
static void
struct ath9k_channel *chan,
struct cal_data_per_freq_4k *pRawDataSet,
{
#define TMP_VAL_VPD_TABLE \
int i, j, k;
break;
}
if (match) {
for (i = 0; i < numXpdGains; i++) {
vpdTableI[i]);
}
} else {
for (i = 0; i < numXpdGains; i++) {
maxPwrT4[i] =
vpdTableL[i]);
vpdTableR[i]);
vpdTableI[i][j] =
(chan)),
}
}
}
k = 0;
for (i = 0; i < numXpdGains; i++) {
if (i == (numXpdGains - 1))
pPdGainBoundaries[i] =
else
pPdGainBoundaries[i] =
pPdGainBoundaries[i] =
if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah)) {
pPdGainBoundaries[0] = 23;
} else {
minDelta = 0;
}
if (i == 0) {
if (AR_SREV_9280_10_OR_LATER(ah))
else
ss = 0;
} else {
(minPwrT4[i] / 2)) -
}
pPDADCValues[k++] =
ss++;
}
maxIndex =
(k < (AR5416_NUM_PDADC_VALUES - 1))) {
pPDADCValues[k++] = (uint8_t)
ss++;
}
}
}
while (i < AR5416_EEP4K_PD_GAINS_IN_MASK) {
i++;
}
while (k < AR5416_NUM_PDADC_VALUES) {
k++;
}
return;
}
static void
struct ath9k_channel *chan,
struct cal_data_per_freq *pRawDataSet,
{
int i, j, k;
break;
}
match =
if (match) {
for (i = 0; i < numXpdGains; i++) {
vpdTableI[i]);
}
} else {
for (i = 0; i < numXpdGains; i++) {
maxPwrT4[i] =
vpdTableL[i]);
vpdTableR[i]);
vpdTableI[i][j] =
(chan)),
}
}
}
k = 0;
for (i = 0; i < numXpdGains; i++) {
if (i == (numXpdGains - 1))
pPdGainBoundaries[i] =
else
pPdGainBoundaries[i] =
pPdGainBoundaries[i] =
if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah)) {
pPdGainBoundaries[0] = 23;
} else {
minDelta = 0;
}
if (i == 0) {
if (AR_SREV_9280_10_OR_LATER(ah))
else
ss = 0;
} else {
(minPwrT4[i] / 2)) -
}
pPDADCValues[k++] =
ss++;
}
(minPwrT4[i] / 2));
}
(k < (AR5416_NUM_PDADC_VALUES - 1))) {
tmpVal =
(int16_t)
255 : tmpVal);
ss++;
}
}
}
while (i < AR5416_PD_GAINS_IN_MASK) {
i++;
}
while (k < AR5416_NUM_PDADC_VALUES) {
k++;
}
}
static void
struct ath9k_channel *chan,
struct cal_target_power_leg *powInfo,
struct cal_target_power_leg *pNewPower,
{
int i;
IS_CHAN_2GHZ(chan))) {
matchIndex = 0;
} else {
for (i = 0; (i < numChannels) &&
IS_CHAN_2GHZ(chan))) {
matchIndex = i;
break;
} else if ((freq <
IS_CHAN_2GHZ(chan))) &&
IS_CHAN_2GHZ(chan)))) {
lowIndex = i - 1;
break;
}
}
matchIndex = i - 1;
}
if (matchIndex != -1) {
} else {
IS_CHAN_2GHZ(chan));
IS_CHAN_2GHZ(chan));
for (i = 0; i < numRates; i++) {
}
}
}
static void
struct ath9k_channel *chan,
struct cal_target_power_ht *powInfo,
struct cal_target_power_ht *pNewPower,
{
int i;
if (freq <=
matchIndex = 0;
} else {
for (i = 0; (i < numChannels) &&
IS_CHAN_2GHZ(chan))) {
matchIndex = i;
break;
} else
if ((freq <
IS_CHAN_2GHZ(chan))) &&
IS_CHAN_2GHZ(chan)))) {
lowIndex = i - 1;
break;
}
}
matchIndex = i - 1;
}
if (matchIndex != -1) {
} else {
IS_CHAN_2GHZ(chan));
IS_CHAN_2GHZ(chan));
for (i = 0; i < numRates; i++) {
}
}
}
static uint16_t
struct cal_ctl_edges *pRdEdgesPower,
{
int i;
for (i = 0; (i < num_band_edges) &&
is2GHz)) {
break;
} else if ((i > 0) &&
is2GHz))) {
}
break;
}
}
return (twiceMaxEdgePower);
}
static boolean_t
{
} else {
}
if (IS_CHAN_2GHZ(chan)) {
} else {
}
numXpdGain = 0;
for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) {
if (numXpdGain >= AR5416_NUM_PD_GAINS)
break;
(uint16_t)(AR5416_PD_GAINS_IN_MASK - i);
numXpdGain++;
}
}
xpdGainValues[0]);
xpdGainValues[1]);
xpdGainValues[2]);
for (i = 0; i < AR5416_MAX_CHAINS; i++) {
if (AR_SREV_5416_V20_OR_LATER(ah) &&
(i != 0)) {
} else
regChainOffset = i * 0x1000;
if (IS_CHAN_2GHZ(chan))
else
if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
SM(gainBoundaries[0],
}
for (j = 0; j < 32; j++) {
"PDADC (%d,%4x): %4.4x %8.8x\n",
i, regChainOffset, regOffset,
reg32));
"PDADC: Chain %d | PDADC %3d "
"Value %3d | PDADC %3d Value %3d | "
"PDADC %3d Value %3d | PDADC %3d "
"Value %3d |\n",
4 * j + 3,
regOffset += 4;
}
}
}
*pTxPowerIndexOffset = 0;
return (B_TRUE);
}
static boolean_t
{
} else {
}
numXpdGain = 0;
for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) {
if (numXpdGain >= AR5416_NUM_PD_GAINS)
break;
(uint16_t)(AR5416_PD_GAINS_IN_MASK - i);
numXpdGain++;
}
}
xpdGainValues[0]);
xpdGainValues[1]);
xpdGainValues[2]);
for (i = 0; i < AR5416_MAX_CHAINS; i++) {
if (AR_SREV_5416_V20_OR_LATER(ah) &&
(i != 0)) {
} else
regChainOffset = i * 0x1000;
if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
SM(gainBoundaries[0],
}
for (j = 0; j < 32; j++) {
"PDADC (%d,%4x): %4.4x %8.8x\n",
i, regChainOffset, regOffset,
reg32));
"PDADC: Chain %d | "
"PDADC %3d Value %3d | "
"PDADC %3d Value %3d | "
"PDADC %3d Value %3d | "
"PDADC %3d Value %3d |\n",
4 * j + 3,
regOffset += 4;
}
}
}
*pTxPowerIndexOffset = 0;
return (B_TRUE);
}
static boolean_t
struct ath9k_channel *chan,
{
int i;
0, { 0, 0, 0, 0}
};
0, { 0, 0, 0, 0} }, targetPowerCckExt = {
0, { 0, 0, 0, 0 }
};
0, {0, 0, 0, 0}
};
};
int tx_chainmask;
}
switch (ar5416_get_ntxchains(tx_chainmask)) {
case 1:
break;
case 2:
break;
case 3:
break;
}
if (IS_CHAN_2GHZ(chan)) {
if (IS_CHAN_HT40(chan)) {
}
} else {
if (IS_CHAN_HT40(chan)) {
}
}
if (isHt40CtlMode)
else
"LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, "
"EXT_ADDITIVE %d\n",
i++) {
"LOOP-Ctlidx %d: cfgCtl 0x%2.2x "
"pCtlMode 0x%2.2x ctlIndex 0x%2.2x "
"chan %d\n",
if ((((cfgCtl & ~CTL_MODE_M) |
(((cfgCtl & ~CTL_MODE_M) |
SD_NO_CTL))) {
(tx_chainmask) - 1],
"MATCH-EE_IDX %d: ch %d is2 %d "
"2xMinEdge %d chainmask %d chains %d\n",
} else {
break;
}
}
}
"SEL-Min ctlMode %d pCtlMode %d "
"2xMaxEdge %d sP %d minCtlPwr %d\n",
case CTL_11B:
i++) {
targetPowerCck.tPow2x[i] =
}
break;
case CTL_11A:
case CTL_11G:
i++) {
targetPowerOfdm.tPow2x[i] =
}
break;
case CTL_5GHT20:
case CTL_2GHT20:
i++) {
targetPowerHt20.tPow2x[i] =
}
break;
case CTL_11B_EXT:
targetPowerCckExt.tPow2x[0] =
break;
case CTL_11A_EXT:
case CTL_11G_EXT:
targetPowerOfdmExt.tPow2x[0] =
break;
case CTL_5GHT40:
case CTL_2GHT40:
i++) {
targetPowerHt40.tPow2x[i] =
}
break;
default:
break;
}
}
if (IS_CHAN_2GHZ(chan)) {
;
;
}
if (IS_CHAN_HT40(chan)) {
ratesArray[rateHt40_0 + i] =
}
if (IS_CHAN_2GHZ(chan)) {
}
}
return (B_TRUE);
}
static boolean_t
struct ath9k_channel *chan,
{
int i;
0, { 0, 0, 0, 0}
};
0, { 0, 0, 0, 0} }, targetPowerCckExt = {
0, { 0, 0, 0, 0 }
};
0, {0, 0, 0, 0}
};
};
int tx_chainmask;
}
if (IS_CHAN_HT40(chan)) {
}
if (isHt40CtlMode)
else
"LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, "
"EXT_ADDITIVE %d\n",
for (i = 0; (i < AR5416_NUM_CTLS) &&
" LOOP-Ctlidx %d: cfgCtl 0x%2.2x "
"pCtlMode 0x%2.2x ctlIndex 0x%2.2x "
"chan %d\n",
if ((((cfgCtl & ~CTL_MODE_M) |
(((cfgCtl & ~CTL_MODE_M) |
SD_NO_CTL))) {
(tx_chainmask) - 1],
" MATCH-EE_IDX %d: ch %d is2 %d "
"2xMinEdge %d chainmask %d chains %d\n",
(tx_chainmask)));
} else {
break;
}
}
}
" SEL-Min ctlMode %d pCtlMode %d "
"2xMaxEdge %d sP %d minCtlPwr %d\n",
case CTL_11B:
i++) {
targetPowerCck.tPow2x[i] =
}
break;
case CTL_11G:
i++) {
targetPowerOfdm.tPow2x[i] =
}
break;
case CTL_2GHT20:
i++) {
targetPowerHt20.tPow2x[i] =
}
break;
case CTL_11B_EXT:
break;
case CTL_11G_EXT:
break;
case CTL_2GHT40:
i++) {
targetPowerHt40.tPow2x[i] =
}
break;
default:
break;
}
}
if (IS_CHAN_HT40(chan)) {
ratesArray[rateHt40_0 + i] =
}
}
return (B_TRUE);
}
static int
{
int i;
}
&ratesArray[0], cfgCtl,
powerLimit)) {
"ath9k_hw_set_txpower: unable to set "
"tx power per rate table\n"));
return (EIO);
}
"ath9k_hw_set_txpower: unable to set power table\n"));
return (EIO);
}
for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
if (ratesArray[i] > AR5416_MAX_RATE_POWER)
}
if (AR_SREV_9280_10_OR_LATER(ah)) {
for (i = 0; i < Ar5416RateSize; i++)
}
if (IS_CHAN_2GHZ(chan)) {
}
if (IS_CHAN_HT40(chan)) {
ht40PowerIncForPdadc, 24) |
ht40PowerIncForPdadc, 16) |
ht40PowerIncForPdadc, 8) |
ht40PowerIncForPdadc, 0));
ht40PowerIncForPdadc, 24) |
ht40PowerIncForPdadc, 16) |
ht40PowerIncForPdadc, 8) |
ht40PowerIncForPdadc, 0));
}
i = rate6mb;
if (IS_CHAN_HT40(chan))
i = rateHt40_0;
else if (IS_CHAN_HT20(chan))
i = rateHt20_0;
if (AR_SREV_9280_10_OR_LATER(ah))
else
return (0);
}
static int
struct ath9k_channel *chan,
{
int i;
}
&ratesArray[0], cfgCtl,
powerLimit)) {
"ath9k_hw_set_txpower: unable to set "
"tx power per rate table\n"));
return (EIO);
}
"ath9k_hw_set_txpower: unable to set power table\n"));
return (EIO);
}
for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
if (ratesArray[i] > AR5416_MAX_RATE_POWER)
}
if (AR_SREV_9280_10_OR_LATER(ah)) {
for (i = 0; i < Ar5416RateSize; i++)
}
if (IS_CHAN_2GHZ(chan)) {
}
if (IS_CHAN_HT40(chan)) {
ht40PowerIncForPdadc, 24) |
ht40PowerIncForPdadc, 16) |
ht40PowerIncForPdadc, 8) |
ht40PowerIncForPdadc, 0));
ht40PowerIncForPdadc, 24) |
ht40PowerIncForPdadc, 16) |
ht40PowerIncForPdadc, 8) |
ht40PowerIncForPdadc, 0));
}
i = rate6mb;
if (IS_CHAN_HT40(chan))
i = rateHt40_0;
else if (IS_CHAN_HT20(chan))
i = rateHt20_0;
if (AR_SREV_9280_10_OR_LATER(ah))
else
return (0);
}
int
struct ath9k_channel *chan,
{
int val;
return (val);
}
static void
{
return;
return;
} else {
freqCount++;
while (freqCount < 3) {
break;
if (resetFreqBin >= freqBin) {
(uint8_t)
} else {
break;
}
freqCount++;
}
}
if (IS_CHAN_2GHZ(chan)) {
} else {
}
}
/* ARGSUSED */
static void
{
return;
return;
biaslevel << 3;
}
}
void
{
}
/* XXX: Clean me up, make me more legible */
static boolean_t
struct ath9k_channel *chan)
{
int i, regChainOffset;
for (i = 0; i < AR5416_MAX_CHAINS; i++) {
if (AR_SREV_9280(ah)) {
if (i >= 2)
break;
}
if (AR_SREV_5416_V20_OR_LATER(ah) &&
(i != 0))
else
regChainOffset = i * 0x1000;
pModal->antCtrlChain[i]);
if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
if (AR_SREV_9280_10_OR_LATER(ah)) {
pModal->
bswMargin[i]);
pModal->
bswAtten[i]);
pModal->
xatten2Margin[i]);
pModal->
xatten2Db[i]);
} else {
bswMargin[i],
}
}
if (AR_SREV_9280_10_OR_LATER(ah)) {
pModal->rxTxMarginCh[i]);
} else {
}
}
}
if (AR_SREV_9280_10_OR_LATER(ah)) {
if (IS_CHAN_2GHZ(chan)) {
} else {
}
pModal->xpaBiasLvl);
pModal->local_bias);
}
if (!AR_SREV_9280_10_OR_LATER(ah))
if (AR_SREV_9280_10_OR_LATER(ah)) {
} else {
}
}
if (IS_CHAN_HT40(chan))
}
return (B_TRUE);
}
static boolean_t
struct ath9k_channel *chan)
{
int regChainOffset;
txRxAttenLocal = 23;
regChainOffset = 0;
pModal->antCtrlChain[0]);
pModal->xatten2Margin[0]);
}
if (AR_SREV_9285_11(ah))
/* Initialize Ant Diversity settings from EEPROM */
regVal &= (~(0x7f000000));
}
"EEPROM Model version is set to 1 \n"));
} else {
int i;
for (i = 0; i < 5; i++) {
}
}
if (AR_SREV_9285_11(ah))
}
if (IS_CHAN_HT40(chan))
}
return (B_TRUE);
}
{
return (val);
}
static int
struct ath9k_channel *chan,
{
switch (index) {
case 0:
return (0);
case 1:
*config =
>> 16);
return (0);
}
}
break;
default:
break;
}
return (-EINVAL);
}
/* ARGSUSED */
static int
struct ath9k_channel *chan,
{
switch (index) {
case 0:
return (0);
default:
break;
}
return (EINVAL);
}
int
struct ath9k_channel *chan,
{
int val;
return (val);
}
/* ARGSUSED */
static uint8_t
enum ath9k_band freq_band)
{
return (1);
}
static uint8_t
enum ath9k_band freq_band)
{
num_ant_config = 1;
num_ant_config += 1;
return (num_ant_config);
}
enum ath9k_band freq_band)
{
return (val);
}
{
#define EEP_MAP4K_SPURCHAN \
#define EEP_DEF_SPURCHAN \
"Getting spur idx %d is2Ghz. %d val %x\n",
case SPUR_DISABLE:
break;
case SPUR_ENABLE_IOCTL:
"Getting spur val from new loc. %d\n", spur_val));
break;
case SPUR_ENABLE_EEPROM:
else
break;
}
return (spur_val);
}
static uint32_t
enum eeprom_param param)
{
switch (param) {
case EEP_NFTHRESH_2:
case AR_EEPROM_MAC(0):
case AR_EEPROM_MAC(1):
case AR_EEPROM_MAC(2):
case EEP_REG_0:
case EEP_REG_1:
case EEP_OP_CAP:
case EEP_OP_MODE:
return (pBase->opCapFlags);
case EEP_RF_SILENT:
case EEP_OB_2:
case EEP_DB_2:
case EEP_MINOR_REV:
case EEP_TX_MASK:
case EEP_RX_MASK:
/* 2.6.30 */
case EEP_FRAC_N_5G:
return (0);
default:
return (0);
}
}
{
switch (param) {
case EEP_NFTHRESH_5:
return (pModal[0].noiseFloorThreshCh[0]);
case EEP_NFTHRESH_2:
case AR_EEPROM_MAC(0):
case AR_EEPROM_MAC(1):
case AR_EEPROM_MAC(2):
case EEP_REG_0:
case EEP_REG_1:
case EEP_OP_CAP:
case EEP_OP_MODE:
return (pBase->opCapFlags);
case EEP_RF_SILENT:
case EEP_OB_5:
case EEP_DB_5:
case EEP_OB_2:
case EEP_DB_2:
case EEP_MINOR_REV:
case EEP_TX_MASK:
case EEP_RX_MASK:
case EEP_RXGAIN_TYPE:
return (pBase->rxGainType);
case EEP_TXGAIN_TYPE:
return (pBase->txGainType);
/* 2.6.30 */
case EEP_OL_PWRCTRL:
else
return (B_FALSE);
case EEP_RC_CHAIN_MASK:
return (pBase->rcChainMask);
else
return (0);
case EEP_DAC_HPWR_5G:
return (pBase->dacHiPwrMode_5G);
else
return (0);
case EEP_FRAC_N_5G:
else
return (0);
default:
return (0);
}
}
{
return (val);
}
int
{
int status;
if (ath9k_hw_use_flash(ah))
(void) ath9k_hw_flash_map(ah);
if (AR_SREV_9285(ah))
else
if (!ath9k_hw_fill_eeprom(ah))
return (EIO);
return (status);
}