1*6e778a7eSPedro F. Giffuni /*-
2*6e778a7eSPedro F. Giffuni * SPDX-License-Identifier: ISC
3*6e778a7eSPedro F. Giffuni *
459efa8b5SSam Leffler * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
514779705SSam Leffler * Copyright (c) 2002-2004 Atheros Communications, Inc.
614779705SSam Leffler *
714779705SSam Leffler * Permission to use, copy, modify, and/or distribute this software for any
814779705SSam Leffler * purpose with or without fee is hereby granted, provided that the above
914779705SSam Leffler * copyright notice and this permission notice appear in all copies.
1014779705SSam Leffler *
1114779705SSam Leffler * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1214779705SSam Leffler * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1314779705SSam Leffler * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414779705SSam Leffler * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1514779705SSam Leffler * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1614779705SSam Leffler * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1714779705SSam Leffler * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1814779705SSam Leffler */
1914779705SSam Leffler #include "opt_ah.h"
2014779705SSam Leffler
2114779705SSam Leffler #include "ah.h"
2214779705SSam Leffler #include "ah_internal.h"
2314779705SSam Leffler
2414779705SSam Leffler #include "ar5210/ar5210.h"
2514779705SSam Leffler #include "ar5210/ar5210reg.h"
2614779705SSam Leffler #include "ar5210/ar5210phy.h"
2714779705SSam Leffler
2814779705SSam Leffler #include "ah_eeprom_v1.h"
2914779705SSam Leffler
3014779705SSam Leffler typedef struct {
3114779705SSam Leffler uint32_t Offset;
3214779705SSam Leffler uint32_t Value;
3314779705SSam Leffler } REGISTER_VAL;
3414779705SSam Leffler
3514779705SSam Leffler static const REGISTER_VAL ar5k0007_init[] = {
3614779705SSam Leffler #include "ar5210/ar5k_0007.ini"
3714779705SSam Leffler };
3814779705SSam Leffler
3914779705SSam Leffler /* Default Power Settings for channels outside of EEPROM range */
4014779705SSam Leffler static const uint8_t ar5k0007_pwrSettings[17] = {
4114779705SSam Leffler /* gain delta pc dac */
4214779705SSam Leffler /* 54 48 36 24 18 12 9 54 48 36 24 18 12 9 6 ob db */
4314779705SSam Leffler 9, 9, 0, 0, 0, 0, 0, 2, 2, 6, 6, 6, 6, 6, 6, 2, 2
4414779705SSam Leffler };
4514779705SSam Leffler
4614779705SSam Leffler /*
4714779705SSam Leffler * The delay, in usecs, between writing AR_RC with a reset
4814779705SSam Leffler * request and waiting for the chip to settle. If this is
4914779705SSam Leffler * too short then the chip does not come out of sleep state.
5014779705SSam Leffler * Note this value was empirically derived and may be dependent
5114779705SSam Leffler * on the host machine (don't know--the problem was identified
5214779705SSam Leffler * on an IBM 570e laptop; 10us delays worked on other systems).
5314779705SSam Leffler */
5414779705SSam Leffler #define AR_RC_SETTLE_TIME 20000
5514779705SSam Leffler
5614779705SSam Leffler static HAL_BOOL ar5210SetResetReg(struct ath_hal *,
5714779705SSam Leffler uint32_t resetMask, u_int delay);
5859efa8b5SSam Leffler static HAL_BOOL ar5210SetChannel(struct ath_hal *, struct ieee80211_channel *);
5914779705SSam Leffler static void ar5210SetOperatingMode(struct ath_hal *, int opmode);
6014779705SSam Leffler
6114779705SSam Leffler /*
6214779705SSam Leffler * Places the device in and out of reset and then places sane
6314779705SSam Leffler * values in the registers based on EEPROM config, initialization
6414779705SSam Leffler * vectors (as determined by the mode), and station configuration
6514779705SSam Leffler *
6614779705SSam Leffler * bChannelChange is used to preserve DMA/PCU registers across
6714779705SSam Leffler * a HW Reset during channel change.
6814779705SSam Leffler */
6914779705SSam Leffler HAL_BOOL
ar5210Reset(struct ath_hal * ah,HAL_OPMODE opmode,struct ieee80211_channel * chan,HAL_BOOL bChannelChange,HAL_RESET_TYPE resetType,HAL_STATUS * status)7014779705SSam Leffler ar5210Reset(struct ath_hal *ah, HAL_OPMODE opmode,
7159efa8b5SSam Leffler struct ieee80211_channel *chan, HAL_BOOL bChannelChange,
72f50e4ebfSAdrian Chadd HAL_RESET_TYPE resetType,
7359efa8b5SSam Leffler HAL_STATUS *status)
7414779705SSam Leffler {
7514779705SSam Leffler #define N(a) (sizeof (a) /sizeof (a[0]))
7614779705SSam Leffler #define FAIL(_code) do { ecode = _code; goto bad; } while (0)
7714779705SSam Leffler struct ath_hal_5210 *ahp = AH5210(ah);
7814779705SSam Leffler const HAL_EEPROM_v1 *ee = AH_PRIVATE(ah)->ah_eeprom;
7914779705SSam Leffler HAL_CHANNEL_INTERNAL *ichan;
8014779705SSam Leffler HAL_STATUS ecode;
8114779705SSam Leffler uint32_t ledstate;
8214779705SSam Leffler int i, q;
8314779705SSam Leffler
8414779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_RESET,
8514779705SSam Leffler "%s: opmode %u channel %u/0x%x %s channel\n", __func__,
8659efa8b5SSam Leffler opmode, chan->ic_freq, chan->ic_flags,
8714779705SSam Leffler bChannelChange ? "change" : "same");
8814779705SSam Leffler
8959efa8b5SSam Leffler if (!IEEE80211_IS_CHAN_5GHZ(chan)) {
9014779705SSam Leffler /* Only 11a mode */
9179649302SGavin Atkinson HALDEBUG(ah, HAL_DEBUG_ANY, "%s: channel not 5GHz\n", __func__);
9214779705SSam Leffler FAIL(HAL_EINVAL);
9314779705SSam Leffler }
9414779705SSam Leffler /*
9514779705SSam Leffler * Map public channel to private.
9614779705SSam Leffler */
9714779705SSam Leffler ichan = ath_hal_checkchannel(ah, chan);
9814779705SSam Leffler if (ichan == AH_NULL) {
9914779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
10014779705SSam Leffler "%s: invalid channel %u/0x%x; no mapping\n",
10159efa8b5SSam Leffler __func__, chan->ic_freq, chan->ic_flags);
10214779705SSam Leffler FAIL(HAL_EINVAL);
10314779705SSam Leffler }
10414779705SSam Leffler switch (opmode) {
10514779705SSam Leffler case HAL_M_STA:
10614779705SSam Leffler case HAL_M_IBSS:
10714779705SSam Leffler case HAL_M_HOSTAP:
10814779705SSam Leffler case HAL_M_MONITOR:
10914779705SSam Leffler break;
11014779705SSam Leffler default:
11114779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid operating mode %u\n",
11214779705SSam Leffler __func__, opmode);
11314779705SSam Leffler FAIL(HAL_EINVAL);
11414779705SSam Leffler break;
11514779705SSam Leffler }
11614779705SSam Leffler
11714779705SSam Leffler ledstate = OS_REG_READ(ah, AR_PCICFG) &
11814779705SSam Leffler (AR_PCICFG_LED_PEND | AR_PCICFG_LED_ACT);
11914779705SSam Leffler
12014779705SSam Leffler if (!ar5210ChipReset(ah, chan)) {
12114779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n",
12214779705SSam Leffler __func__);
12314779705SSam Leffler FAIL(HAL_EIO);
12414779705SSam Leffler }
12514779705SSam Leffler
12614779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr));
12714779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4));
12814779705SSam Leffler ar5210SetOperatingMode(ah, opmode);
12914779705SSam Leffler
13014779705SSam Leffler switch (opmode) {
13114779705SSam Leffler case HAL_M_HOSTAP:
13214779705SSam Leffler OS_REG_WRITE(ah, AR_BCR, INIT_BCON_CNTRL_REG);
13314779705SSam Leffler OS_REG_WRITE(ah, AR_PCICFG,
13414779705SSam Leffler AR_PCICFG_LED_ACT | AR_PCICFG_LED_BCTL);
13514779705SSam Leffler break;
13614779705SSam Leffler case HAL_M_IBSS:
13714779705SSam Leffler OS_REG_WRITE(ah, AR_BCR, INIT_BCON_CNTRL_REG | AR_BCR_BCMD);
13814779705SSam Leffler OS_REG_WRITE(ah, AR_PCICFG,
13914779705SSam Leffler AR_PCICFG_CLKRUNEN | AR_PCICFG_LED_PEND | AR_PCICFG_LED_BCTL);
14014779705SSam Leffler break;
14114779705SSam Leffler case HAL_M_STA:
14214779705SSam Leffler OS_REG_WRITE(ah, AR_BCR, INIT_BCON_CNTRL_REG);
14314779705SSam Leffler OS_REG_WRITE(ah, AR_PCICFG,
14414779705SSam Leffler AR_PCICFG_CLKRUNEN | AR_PCICFG_LED_PEND | AR_PCICFG_LED_BCTL);
14514779705SSam Leffler break;
14614779705SSam Leffler case HAL_M_MONITOR:
14714779705SSam Leffler OS_REG_WRITE(ah, AR_BCR, INIT_BCON_CNTRL_REG);
14814779705SSam Leffler OS_REG_WRITE(ah, AR_PCICFG,
14914779705SSam Leffler AR_PCICFG_LED_ACT | AR_PCICFG_LED_BCTL);
15014779705SSam Leffler break;
15114779705SSam Leffler }
15214779705SSam Leffler
15314779705SSam Leffler /* Restore previous led state */
15414779705SSam Leffler OS_REG_WRITE(ah, AR_PCICFG, OS_REG_READ(ah, AR_PCICFG) | ledstate);
15514779705SSam Leffler
1569b34359bSAdrian Chadd #if 0
15714779705SSam Leffler OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid));
15814779705SSam Leffler OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4));
1599b34359bSAdrian Chadd #endif
1609b34359bSAdrian Chadd /* BSSID, association id, ps-poll */
1619b34359bSAdrian Chadd ar5210WriteAssocid(ah, ahp->ah_bssid, ahp->ah_associd);
16214779705SSam Leffler
16314779705SSam Leffler OS_REG_WRITE(ah, AR_TXDP0, 0);
16414779705SSam Leffler OS_REG_WRITE(ah, AR_TXDP1, 0);
16514779705SSam Leffler OS_REG_WRITE(ah, AR_RXDP, 0);
16614779705SSam Leffler
16714779705SSam Leffler /*
16814779705SSam Leffler * Initialize interrupt state.
16914779705SSam Leffler */
17014779705SSam Leffler (void) OS_REG_READ(ah, AR_ISR); /* cleared on read */
17114779705SSam Leffler OS_REG_WRITE(ah, AR_IMR, 0);
17214779705SSam Leffler OS_REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
17314779705SSam Leffler ahp->ah_maskReg = 0;
17414779705SSam Leffler
17514779705SSam Leffler (void) OS_REG_READ(ah, AR_BSR); /* cleared on read */
17614779705SSam Leffler OS_REG_WRITE(ah, AR_TXCFG, AR_DMASIZE_128B);
17714779705SSam Leffler OS_REG_WRITE(ah, AR_RXCFG, AR_DMASIZE_128B);
17814779705SSam Leffler
17914779705SSam Leffler OS_REG_WRITE(ah, AR_TOPS, 8); /* timeout prescale */
18014779705SSam Leffler OS_REG_WRITE(ah, AR_RXNOFRM, 8); /* RX no frame timeout */
18114779705SSam Leffler OS_REG_WRITE(ah, AR_RPGTO, 0); /* RX frame gap timeout */
18214779705SSam Leffler OS_REG_WRITE(ah, AR_TXNOFRM, 0); /* TX no frame timeout */
18314779705SSam Leffler
18414779705SSam Leffler OS_REG_WRITE(ah, AR_SFR, 0);
18514779705SSam Leffler OS_REG_WRITE(ah, AR_MIBC, 0); /* unfreeze ctrs + clr state */
18614779705SSam Leffler OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr);
18714779705SSam Leffler OS_REG_WRITE(ah, AR_CFP_DUR, 0);
18814779705SSam Leffler
18914779705SSam Leffler ar5210SetRxFilter(ah, 0); /* nothing for now */
19014779705SSam Leffler OS_REG_WRITE(ah, AR_MCAST_FIL0, 0); /* multicast filter */
19114779705SSam Leffler OS_REG_WRITE(ah, AR_MCAST_FIL1, 0); /* XXX was 2 */
19214779705SSam Leffler
19314779705SSam Leffler OS_REG_WRITE(ah, AR_TX_MASK0, 0);
19414779705SSam Leffler OS_REG_WRITE(ah, AR_TX_MASK1, 0);
19514779705SSam Leffler OS_REG_WRITE(ah, AR_CLR_TMASK, 1);
19614779705SSam Leffler OS_REG_WRITE(ah, AR_TRIG_LEV, 1); /* minimum */
19714779705SSam Leffler
198143cfad7SAdrian Chadd ar5210UpdateDiagReg(ah, 0);
19914779705SSam Leffler
20014779705SSam Leffler OS_REG_WRITE(ah, AR_CFP_PERIOD, 0);
20114779705SSam Leffler OS_REG_WRITE(ah, AR_TIMER0, 0); /* next beacon time */
20214779705SSam Leffler OS_REG_WRITE(ah, AR_TSF_L32, 0); /* local clock */
20314779705SSam Leffler OS_REG_WRITE(ah, AR_TIMER1, ~0); /* next DMA beacon alert */
20414779705SSam Leffler OS_REG_WRITE(ah, AR_TIMER2, ~0); /* next SW beacon alert */
20514779705SSam Leffler OS_REG_WRITE(ah, AR_TIMER3, 1); /* next ATIM window */
20614779705SSam Leffler
20714779705SSam Leffler /* Write the INI values for PHYreg initialization */
20814779705SSam Leffler for (i = 0; i < N(ar5k0007_init); i++) {
20914779705SSam Leffler uint32_t reg = ar5k0007_init[i].Offset;
21014779705SSam Leffler /* On channel change, don't reset the PCU registers */
21114779705SSam Leffler if (!(bChannelChange && (0x8000 <= reg && reg < 0x9000)))
21214779705SSam Leffler OS_REG_WRITE(ah, reg, ar5k0007_init[i].Value);
21314779705SSam Leffler }
21414779705SSam Leffler
21514779705SSam Leffler /* Setup the transmit power values for cards since 0x0[0-2]05 */
21614779705SSam Leffler if (!ar5210SetTransmitPower(ah, chan)) {
21714779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
21814779705SSam Leffler "%s: error init'ing transmit power\n", __func__);
21914779705SSam Leffler FAIL(HAL_EIO);
22014779705SSam Leffler }
22114779705SSam Leffler
22214779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(10),
22314779705SSam Leffler (OS_REG_READ(ah, AR_PHY(10)) & 0xFFFF00FF) |
22414779705SSam Leffler (ee->ee_xlnaOn << 8));
22514779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(13),
22614779705SSam Leffler (ee->ee_xpaOff << 24) | (ee->ee_xpaOff << 16) |
22714779705SSam Leffler (ee->ee_xpaOn << 8) | ee->ee_xpaOn);
22814779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(17),
22914779705SSam Leffler (OS_REG_READ(ah, AR_PHY(17)) & 0xFFFFC07F) |
23014779705SSam Leffler ((ee->ee_antenna >> 1) & 0x3F80));
23114779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(18),
23214779705SSam Leffler (OS_REG_READ(ah, AR_PHY(18)) & 0xFFFC0FFF) |
23314779705SSam Leffler ((ee->ee_antenna << 10) & 0x3F000));
23414779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(25),
23514779705SSam Leffler (OS_REG_READ(ah, AR_PHY(25)) & 0xFFF80FFF) |
23614779705SSam Leffler ((ee->ee_thresh62 << 12) & 0x7F000));
23714779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(68),
23814779705SSam Leffler (OS_REG_READ(ah, AR_PHY(68)) & 0xFFFFFFFC) |
23914779705SSam Leffler (ee->ee_antenna & 0x3));
24014779705SSam Leffler
24159efa8b5SSam Leffler if (!ar5210SetChannel(ah, chan)) {
24214779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set channel\n",
24314779705SSam Leffler __func__);
24414779705SSam Leffler FAIL(HAL_EIO);
24514779705SSam Leffler }
24659efa8b5SSam Leffler if (bChannelChange && !IEEE80211_IS_CHAN_DFS(chan))
24759efa8b5SSam Leffler chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT;
24814779705SSam Leffler
24914779705SSam Leffler /* Activate the PHY */
25014779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ENABLE);
25114779705SSam Leffler
25214779705SSam Leffler OS_DELAY(1000); /* Wait a bit (1 msec) */
25314779705SSam Leffler
25414779705SSam Leffler /* calibrate the HW and poll the bit going to 0 for completion */
25514779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_AGCCTL,
25614779705SSam Leffler OS_REG_READ(ah, AR_PHY_AGCCTL) | AR_PHY_AGC_CAL);
25714779705SSam Leffler (void) ath_hal_wait(ah, AR_PHY_AGCCTL, AR_PHY_AGC_CAL, 0);
25814779705SSam Leffler
25914779705SSam Leffler /* Perform noise floor calibration and set status */
26014779705SSam Leffler if (!ar5210CalNoiseFloor(ah, ichan)) {
26159efa8b5SSam Leffler chan->ic_state |= IEEE80211_CHANSTATE_CWINT;
26214779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY,
26314779705SSam Leffler "%s: noise floor calibration failed\n", __func__);
26414779705SSam Leffler FAIL(HAL_EIO);
26514779705SSam Leffler }
26614779705SSam Leffler
26714779705SSam Leffler for (q = 0; q < HAL_NUM_TX_QUEUES; q++)
26814779705SSam Leffler ar5210ResetTxQueue(ah, q);
26914779705SSam Leffler
27014779705SSam Leffler if (AH_PRIVATE(ah)->ah_rfkillEnabled)
27114779705SSam Leffler ar5210EnableRfKill(ah);
27214779705SSam Leffler
27314779705SSam Leffler /*
27414779705SSam Leffler * Writing to AR_BEACON will start timers. Hence it should be
27514779705SSam Leffler * the last register to be written. Do not reset tsf, do not
27614779705SSam Leffler * enable beacons at this point, but preserve other values
27714779705SSam Leffler * like beaconInterval.
27814779705SSam Leffler */
27914779705SSam Leffler OS_REG_WRITE(ah, AR_BEACON,
28014779705SSam Leffler (OS_REG_READ(ah, AR_BEACON) &
28114779705SSam Leffler ~(AR_BEACON_EN | AR_BEACON_RESET_TSF)));
28214779705SSam Leffler
28314779705SSam Leffler /* Restore user-specified slot time and timeouts */
28414779705SSam Leffler if (ahp->ah_sifstime != (u_int) -1)
28514779705SSam Leffler ar5210SetSifsTime(ah, ahp->ah_sifstime);
28614779705SSam Leffler if (ahp->ah_slottime != (u_int) -1)
28714779705SSam Leffler ar5210SetSlotTime(ah, ahp->ah_slottime);
28814779705SSam Leffler if (ahp->ah_acktimeout != (u_int) -1)
28914779705SSam Leffler ar5210SetAckTimeout(ah, ahp->ah_acktimeout);
29014779705SSam Leffler if (ahp->ah_ctstimeout != (u_int) -1)
29114779705SSam Leffler ar5210SetCTSTimeout(ah, ahp->ah_ctstimeout);
29214779705SSam Leffler if (AH_PRIVATE(ah)->ah_diagreg != 0)
293143cfad7SAdrian Chadd ar5210UpdateDiagReg(ah, AH_PRIVATE(ah)->ah_diagreg);
29414779705SSam Leffler
29514779705SSam Leffler AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */
29614779705SSam Leffler
29714779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_RESET, "%s: done\n", __func__);
29814779705SSam Leffler
29914779705SSam Leffler return AH_TRUE;
30014779705SSam Leffler bad:
3018698ea65SSam Leffler if (status != AH_NULL)
30214779705SSam Leffler *status = ecode;
30314779705SSam Leffler return AH_FALSE;
30414779705SSam Leffler #undef FAIL
30514779705SSam Leffler #undef N
30614779705SSam Leffler }
30714779705SSam Leffler
30814779705SSam Leffler static void
ar5210SetOperatingMode(struct ath_hal * ah,int opmode)30914779705SSam Leffler ar5210SetOperatingMode(struct ath_hal *ah, int opmode)
31014779705SSam Leffler {
31114779705SSam Leffler struct ath_hal_5210 *ahp = AH5210(ah);
31214779705SSam Leffler uint32_t val;
31314779705SSam Leffler
31414779705SSam Leffler val = OS_REG_READ(ah, AR_STA_ID1) & 0xffff;
31514779705SSam Leffler switch (opmode) {
31614779705SSam Leffler case HAL_M_HOSTAP:
31714779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID1, val
31814779705SSam Leffler | AR_STA_ID1_AP
31914779705SSam Leffler | AR_STA_ID1_NO_PSPOLL
32014779705SSam Leffler | AR_STA_ID1_DESC_ANTENNA
32114779705SSam Leffler | ahp->ah_staId1Defaults);
32214779705SSam Leffler break;
32314779705SSam Leffler case HAL_M_IBSS:
32414779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID1, val
32514779705SSam Leffler | AR_STA_ID1_ADHOC
32614779705SSam Leffler | AR_STA_ID1_NO_PSPOLL
32714779705SSam Leffler | AR_STA_ID1_DESC_ANTENNA
32814779705SSam Leffler | ahp->ah_staId1Defaults);
32914779705SSam Leffler break;
33014779705SSam Leffler case HAL_M_STA:
33114779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID1, val
33214779705SSam Leffler | AR_STA_ID1_NO_PSPOLL
33314779705SSam Leffler | AR_STA_ID1_PWR_SV
33414779705SSam Leffler | ahp->ah_staId1Defaults);
33514779705SSam Leffler break;
33614779705SSam Leffler case HAL_M_MONITOR:
33714779705SSam Leffler OS_REG_WRITE(ah, AR_STA_ID1, val
33814779705SSam Leffler | AR_STA_ID1_NO_PSPOLL
33914779705SSam Leffler | ahp->ah_staId1Defaults);
34014779705SSam Leffler break;
34114779705SSam Leffler }
34214779705SSam Leffler }
34314779705SSam Leffler
34414779705SSam Leffler void
ar5210SetPCUConfig(struct ath_hal * ah)34514779705SSam Leffler ar5210SetPCUConfig(struct ath_hal *ah)
34614779705SSam Leffler {
34714779705SSam Leffler ar5210SetOperatingMode(ah, AH_PRIVATE(ah)->ah_opmode);
34814779705SSam Leffler }
34914779705SSam Leffler
35014779705SSam Leffler /*
35114779705SSam Leffler * Places the PHY and Radio chips into reset. A full reset
35214779705SSam Leffler * must be called to leave this state. The PCI/MAC/PCU are
35314779705SSam Leffler * not placed into reset as we must receive interrupt to
35414779705SSam Leffler * re-enable the hardware.
35514779705SSam Leffler */
35614779705SSam Leffler HAL_BOOL
ar5210PhyDisable(struct ath_hal * ah)35714779705SSam Leffler ar5210PhyDisable(struct ath_hal *ah)
35814779705SSam Leffler {
35914779705SSam Leffler return ar5210SetResetReg(ah, AR_RC_RPHY, 10);
36014779705SSam Leffler }
36114779705SSam Leffler
36214779705SSam Leffler /*
36314779705SSam Leffler * Places all of hardware into reset
36414779705SSam Leffler */
36514779705SSam Leffler HAL_BOOL
ar5210Disable(struct ath_hal * ah)36614779705SSam Leffler ar5210Disable(struct ath_hal *ah)
36714779705SSam Leffler {
36814779705SSam Leffler #define AR_RC_HW (AR_RC_RPCU | AR_RC_RDMA | AR_RC_RPHY | AR_RC_RMAC)
36914779705SSam Leffler if (!ar5210SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
37014779705SSam Leffler return AH_FALSE;
37114779705SSam Leffler
37214779705SSam Leffler /*
37314779705SSam Leffler * Reset the HW - PCI must be reset after the rest of the
37414779705SSam Leffler * device has been reset
37514779705SSam Leffler */
37614779705SSam Leffler if (!ar5210SetResetReg(ah, AR_RC_HW, AR_RC_SETTLE_TIME))
37714779705SSam Leffler return AH_FALSE;
37814779705SSam Leffler OS_DELAY(1000);
37914779705SSam Leffler (void) ar5210SetResetReg(ah, AR_RC_HW | AR_RC_RPCI, AR_RC_SETTLE_TIME);
38014779705SSam Leffler OS_DELAY(2100); /* 8245 @ 96Mhz hangs with 2000us. */
38114779705SSam Leffler
38214779705SSam Leffler return AH_TRUE;
38314779705SSam Leffler #undef AR_RC_HW
38414779705SSam Leffler }
38514779705SSam Leffler
38614779705SSam Leffler /*
38714779705SSam Leffler * Places the hardware into reset and then pulls it out of reset
38814779705SSam Leffler */
38914779705SSam Leffler HAL_BOOL
ar5210ChipReset(struct ath_hal * ah,struct ieee80211_channel * chan)39059efa8b5SSam Leffler ar5210ChipReset(struct ath_hal *ah, struct ieee80211_channel *chan)
39114779705SSam Leffler {
39214779705SSam Leffler #define AR_RC_HW (AR_RC_RPCU | AR_RC_RDMA | AR_RC_RPHY | AR_RC_RMAC)
39314779705SSam Leffler
39414779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_RESET, "%s turbo %s\n", __func__,
39559efa8b5SSam Leffler chan && IEEE80211_IS_CHAN_TURBO(chan) ?
39659efa8b5SSam Leffler "enabled" : "disabled");
39714779705SSam Leffler
39814779705SSam Leffler if (!ar5210SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
39914779705SSam Leffler return AH_FALSE;
40014779705SSam Leffler
40114779705SSam Leffler /* Place chip in turbo before reset to cleanly reset clocks */
40214779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_FRCTL,
40359efa8b5SSam Leffler chan && IEEE80211_IS_CHAN_TURBO(chan) ? AR_PHY_TURBO_MODE : 0);
40414779705SSam Leffler
40514779705SSam Leffler /*
40614779705SSam Leffler * Reset the HW.
40714779705SSam Leffler * PCI must be reset after the rest of the device has been reset.
40814779705SSam Leffler */
40914779705SSam Leffler if (!ar5210SetResetReg(ah, AR_RC_HW, AR_RC_SETTLE_TIME))
41014779705SSam Leffler return AH_FALSE;
41114779705SSam Leffler OS_DELAY(1000);
41214779705SSam Leffler if (!ar5210SetResetReg(ah, AR_RC_HW | AR_RC_RPCI, AR_RC_SETTLE_TIME))
41314779705SSam Leffler return AH_FALSE;
41414779705SSam Leffler OS_DELAY(2100); /* 8245 @ 96Mhz hangs with 2000us. */
41514779705SSam Leffler
41614779705SSam Leffler /*
41714779705SSam Leffler * Bring out of sleep mode (AGAIN)
41814779705SSam Leffler *
41914779705SSam Leffler * WARNING WARNING WARNING
42014779705SSam Leffler *
42114779705SSam Leffler * There is a problem with the chip where it doesn't always indicate
42214779705SSam Leffler * that it's awake, so initializePowerUp() will fail.
42314779705SSam Leffler */
42414779705SSam Leffler if (!ar5210SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
42514779705SSam Leffler return AH_FALSE;
42614779705SSam Leffler
42714779705SSam Leffler /* Clear warm reset reg */
42814779705SSam Leffler return ar5210SetResetReg(ah, 0, 10);
42914779705SSam Leffler #undef AR_RC_HW
43014779705SSam Leffler }
43114779705SSam Leffler
43214779705SSam Leffler enum {
43314779705SSam Leffler FIRPWR_M = 0x03fc0000,
43414779705SSam Leffler FIRPWR_S = 18,
43514779705SSam Leffler KCOARSEHIGH_M = 0x003f8000,
43614779705SSam Leffler KCOARSEHIGH_S = 15,
43714779705SSam Leffler KCOARSELOW_M = 0x00007f80,
43814779705SSam Leffler KCOARSELOW_S = 7,
43914779705SSam Leffler ADCSAT_ICOUNT_M = 0x0001f800,
44014779705SSam Leffler ADCSAT_ICOUNT_S = 11,
44114779705SSam Leffler ADCSAT_THRESH_M = 0x000007e0,
44214779705SSam Leffler ADCSAT_THRESH_S = 5
44314779705SSam Leffler };
44414779705SSam Leffler
44514779705SSam Leffler /*
44614779705SSam Leffler * Recalibrate the lower PHY chips to account for temperature/environment
44714779705SSam Leffler * changes.
44814779705SSam Leffler */
44914779705SSam Leffler HAL_BOOL
ar5210PerCalibrationN(struct ath_hal * ah,struct ieee80211_channel * chan,u_int chainMask,HAL_BOOL longCal,HAL_BOOL * isCalDone)45059efa8b5SSam Leffler ar5210PerCalibrationN(struct ath_hal *ah,
45159efa8b5SSam Leffler struct ieee80211_channel *chan, u_int chainMask,
45214779705SSam Leffler HAL_BOOL longCal, HAL_BOOL *isCalDone)
45314779705SSam Leffler {
45414779705SSam Leffler uint32_t regBeacon;
45514779705SSam Leffler uint32_t reg9858, reg985c, reg9868;
45614779705SSam Leffler HAL_CHANNEL_INTERNAL *ichan;
45714779705SSam Leffler
45814779705SSam Leffler ichan = ath_hal_checkchannel(ah, chan);
45959efa8b5SSam Leffler if (ichan == AH_NULL)
46014779705SSam Leffler return AH_FALSE;
46114779705SSam Leffler /* Disable tx and rx */
462143cfad7SAdrian Chadd ar5210UpdateDiagReg(ah,
46314779705SSam Leffler OS_REG_READ(ah, AR_DIAG_SW) | (AR_DIAG_SW_DIS_TX | AR_DIAG_SW_DIS_RX));
46414779705SSam Leffler
46514779705SSam Leffler /* Disable Beacon Enable */
46614779705SSam Leffler regBeacon = OS_REG_READ(ah, AR_BEACON);
46714779705SSam Leffler OS_REG_WRITE(ah, AR_BEACON, regBeacon & ~AR_BEACON_EN);
46814779705SSam Leffler
46914779705SSam Leffler /* Delay 4ms to ensure that all tx and rx activity has ceased */
47014779705SSam Leffler OS_DELAY(4000);
47114779705SSam Leffler
47214779705SSam Leffler /* Disable AGC to radio traffic */
47314779705SSam Leffler OS_REG_WRITE(ah, 0x9808, OS_REG_READ(ah, 0x9808) | 0x08000000);
47414779705SSam Leffler /* Wait for the AGC traffic to cease. */
47514779705SSam Leffler OS_DELAY(10);
47614779705SSam Leffler
47714779705SSam Leffler /* Change Channel to relock synth */
47859efa8b5SSam Leffler if (!ar5210SetChannel(ah, chan))
47914779705SSam Leffler return AH_FALSE;
48014779705SSam Leffler
48114779705SSam Leffler /* wait for the synthesizer lock to stabilize */
48214779705SSam Leffler OS_DELAY(1000);
48314779705SSam Leffler
48414779705SSam Leffler /* Re-enable AGC to radio traffic */
48514779705SSam Leffler OS_REG_WRITE(ah, 0x9808, OS_REG_READ(ah, 0x9808) & (~0x08000000));
48614779705SSam Leffler
48714779705SSam Leffler /*
48814779705SSam Leffler * Configure the AGC so that it is highly unlikely (if not
48914779705SSam Leffler * impossible) for it to send any gain changes to the analog
49014779705SSam Leffler * chip. We store off the current values so that they can
49114779705SSam Leffler * be rewritten below. Setting the following values:
49214779705SSam Leffler * firpwr = -1
49314779705SSam Leffler * Kcoursehigh = -1
49414779705SSam Leffler * Kcourselow = -127
49514779705SSam Leffler * ADCsat_icount = 2
49614779705SSam Leffler * ADCsat_thresh = 12
49714779705SSam Leffler */
49814779705SSam Leffler reg9858 = OS_REG_READ(ah, 0x9858);
49914779705SSam Leffler reg985c = OS_REG_READ(ah, 0x985c);
50014779705SSam Leffler reg9868 = OS_REG_READ(ah, 0x9868);
50114779705SSam Leffler
50214779705SSam Leffler OS_REG_WRITE(ah, 0x9858, (reg9858 & ~FIRPWR_M) |
50314779705SSam Leffler ((-1 << FIRPWR_S) & FIRPWR_M));
50414779705SSam Leffler OS_REG_WRITE(ah, 0x985c,
50514779705SSam Leffler (reg985c & ~(KCOARSEHIGH_M | KCOARSELOW_M)) |
50614779705SSam Leffler ((-1 << KCOARSEHIGH_S) & KCOARSEHIGH_M) |
50714779705SSam Leffler ((-127 << KCOARSELOW_S) & KCOARSELOW_M));
50814779705SSam Leffler OS_REG_WRITE(ah, 0x9868,
50914779705SSam Leffler (reg9868 & ~(ADCSAT_ICOUNT_M | ADCSAT_THRESH_M)) |
51014779705SSam Leffler ((2 << ADCSAT_ICOUNT_S) & ADCSAT_ICOUNT_M) |
51114779705SSam Leffler ((12 << ADCSAT_THRESH_S) & ADCSAT_THRESH_M));
51214779705SSam Leffler
51314779705SSam Leffler /* Wait for AGC changes to be enacted */
51414779705SSam Leffler OS_DELAY(20);
51514779705SSam Leffler
51614779705SSam Leffler /*
51714779705SSam Leffler * We disable RF mix/gain stages for the PGA to avoid a
51814779705SSam Leffler * race condition that will occur with receiving a frame
51914779705SSam Leffler * and performing the AGC calibration. This will be
52014779705SSam Leffler * re-enabled at the end of offset cal. We turn off AGC
52114779705SSam Leffler * writes during this write as it will go over the analog bus.
52214779705SSam Leffler */
52314779705SSam Leffler OS_REG_WRITE(ah, 0x9808, OS_REG_READ(ah, 0x9808) | 0x08000000);
52414779705SSam Leffler OS_DELAY(10); /* wait for the AGC traffic to cease */
52514779705SSam Leffler OS_REG_WRITE(ah, 0x98D4, 0x21);
52614779705SSam Leffler OS_REG_WRITE(ah, 0x9808, OS_REG_READ(ah, 0x9808) & (~0x08000000));
52714779705SSam Leffler
52814779705SSam Leffler /* wait to make sure that additional AGC traffic has quiesced */
52914779705SSam Leffler OS_DELAY(1000);
53014779705SSam Leffler
53114779705SSam Leffler /* AGC calibration (this was added to make the NF threshold check work) */
53214779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_AGCCTL,
53314779705SSam Leffler OS_REG_READ(ah, AR_PHY_AGCCTL) | AR_PHY_AGC_CAL);
5343a4a1441SRui Paulo if (!ath_hal_wait(ah, AR_PHY_AGCCTL, AR_PHY_AGC_CAL, 0)) {
53514779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: AGC calibration timeout\n",
53614779705SSam Leffler __func__);
5373a4a1441SRui Paulo }
53814779705SSam Leffler
53914779705SSam Leffler /* Rewrite our AGC values we stored off earlier (return AGC to normal operation) */
54014779705SSam Leffler OS_REG_WRITE(ah, 0x9858, reg9858);
54114779705SSam Leffler OS_REG_WRITE(ah, 0x985c, reg985c);
54214779705SSam Leffler OS_REG_WRITE(ah, 0x9868, reg9868);
54314779705SSam Leffler
54414779705SSam Leffler /* Perform noise floor and set status */
54514779705SSam Leffler if (!ar5210CalNoiseFloor(ah, ichan)) {
54614779705SSam Leffler /*
54714779705SSam Leffler * Delay 5ms before retrying the noise floor -
54814779705SSam Leffler * just to make sure. We're in an error
54914779705SSam Leffler * condition here
55014779705SSam Leffler */
55114779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_NFCAL | HAL_DEBUG_PERCAL,
55214779705SSam Leffler "%s: Performing 2nd Noise Cal\n", __func__);
55314779705SSam Leffler OS_DELAY(5000);
55414779705SSam Leffler if (!ar5210CalNoiseFloor(ah, ichan))
55559efa8b5SSam Leffler chan->ic_state |= IEEE80211_CHANSTATE_CWINT;
55614779705SSam Leffler }
55714779705SSam Leffler
55814779705SSam Leffler /* Clear tx and rx disable bit */
559143cfad7SAdrian Chadd ar5210UpdateDiagReg(ah,
56014779705SSam Leffler OS_REG_READ(ah, AR_DIAG_SW) & ~(AR_DIAG_SW_DIS_TX | AR_DIAG_SW_DIS_RX));
56114779705SSam Leffler
56214779705SSam Leffler /* Re-enable Beacons */
56314779705SSam Leffler OS_REG_WRITE(ah, AR_BEACON, regBeacon);
56414779705SSam Leffler
56514779705SSam Leffler *isCalDone = AH_TRUE;
56614779705SSam Leffler
56714779705SSam Leffler return AH_TRUE;
56814779705SSam Leffler }
56914779705SSam Leffler
57014779705SSam Leffler HAL_BOOL
ar5210PerCalibration(struct ath_hal * ah,struct ieee80211_channel * chan,HAL_BOOL * isIQdone)57159efa8b5SSam Leffler ar5210PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan,
57259efa8b5SSam Leffler HAL_BOOL *isIQdone)
57314779705SSam Leffler {
57414779705SSam Leffler return ar5210PerCalibrationN(ah, chan, 0x1, AH_TRUE, isIQdone);
57514779705SSam Leffler }
57614779705SSam Leffler
57714779705SSam Leffler HAL_BOOL
ar5210ResetCalValid(struct ath_hal * ah,const struct ieee80211_channel * chan)57859efa8b5SSam Leffler ar5210ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan)
57914779705SSam Leffler {
58014779705SSam Leffler return AH_TRUE;
58114779705SSam Leffler }
58214779705SSam Leffler
58314779705SSam Leffler /*
58414779705SSam Leffler * Writes the given reset bit mask into the reset register
58514779705SSam Leffler */
58614779705SSam Leffler static HAL_BOOL
ar5210SetResetReg(struct ath_hal * ah,uint32_t resetMask,u_int delay)58714779705SSam Leffler ar5210SetResetReg(struct ath_hal *ah, uint32_t resetMask, u_int delay)
58814779705SSam Leffler {
58914779705SSam Leffler uint32_t mask = resetMask ? resetMask : ~0;
59014779705SSam Leffler HAL_BOOL rt;
59114779705SSam Leffler
59214779705SSam Leffler OS_REG_WRITE(ah, AR_RC, resetMask);
59314779705SSam Leffler /* need to wait at least 128 clocks when reseting PCI before read */
59414779705SSam Leffler OS_DELAY(delay);
59514779705SSam Leffler
59614779705SSam Leffler resetMask &= AR_RC_RPCU | AR_RC_RDMA | AR_RC_RPHY | AR_RC_RMAC;
59714779705SSam Leffler mask &= AR_RC_RPCU | AR_RC_RDMA | AR_RC_RPHY | AR_RC_RMAC;
59814779705SSam Leffler rt = ath_hal_wait(ah, AR_RC, mask, resetMask);
59914779705SSam Leffler if ((resetMask & AR_RC_RMAC) == 0) {
60014779705SSam Leffler if (isBigEndian()) {
60114779705SSam Leffler /*
602a47f39daSAdrian Chadd * Set CFG, little-endian for descriptor accesses.
60314779705SSam Leffler */
604a47f39daSAdrian Chadd mask = INIT_CONFIG_STATUS | AR_CFG_SWTD | AR_CFG_SWRD;
605a47f39daSAdrian Chadd OS_REG_WRITE(ah, AR_CFG, mask);
60614779705SSam Leffler } else
60714779705SSam Leffler OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS);
60814779705SSam Leffler }
60914779705SSam Leffler return rt;
61014779705SSam Leffler }
61114779705SSam Leffler
61214779705SSam Leffler /*
61314779705SSam Leffler * Returns: the pcdac value
61414779705SSam Leffler */
61514779705SSam Leffler static uint8_t
getPcdac(struct ath_hal * ah,const struct tpcMap * pRD,uint8_t dBm)61614779705SSam Leffler getPcdac(struct ath_hal *ah, const struct tpcMap *pRD, uint8_t dBm)
61714779705SSam Leffler {
61814779705SSam Leffler int32_t i;
61914779705SSam Leffler int useNextEntry = AH_FALSE;
62014779705SSam Leffler uint32_t interp;
62114779705SSam Leffler
62214779705SSam Leffler for (i = AR_TP_SCALING_ENTRIES - 1; i >= 0; i--) {
62314779705SSam Leffler /* Check for exact entry */
62414779705SSam Leffler if (dBm == AR_I2DBM(i)) {
62514779705SSam Leffler if (pRD->pcdac[i] != 63)
62614779705SSam Leffler return pRD->pcdac[i];
62714779705SSam Leffler useNextEntry = AH_TRUE;
62814779705SSam Leffler } else if (dBm + 1 == AR_I2DBM(i) && i > 0) {
62914779705SSam Leffler /* Interpolate for between entry with a logish scale */
63014779705SSam Leffler if (pRD->pcdac[i] != 63 && pRD->pcdac[i-1] != 63) {
63114779705SSam Leffler interp = (350 * (pRD->pcdac[i] - pRD->pcdac[i-1])) + 999;
63214779705SSam Leffler interp = (interp / 1000) + pRD->pcdac[i-1];
63314779705SSam Leffler return interp;
63414779705SSam Leffler }
63514779705SSam Leffler useNextEntry = AH_TRUE;
63614779705SSam Leffler } else if (useNextEntry == AH_TRUE) {
63714779705SSam Leffler /* Grab the next lowest */
63814779705SSam Leffler if (pRD->pcdac[i] != 63)
63914779705SSam Leffler return pRD->pcdac[i];
64014779705SSam Leffler }
64114779705SSam Leffler }
64214779705SSam Leffler
64314779705SSam Leffler /* Return the lowest Entry if we haven't returned */
64414779705SSam Leffler for (i = 0; i < AR_TP_SCALING_ENTRIES; i++)
64514779705SSam Leffler if (pRD->pcdac[i] != 63)
64614779705SSam Leffler return pRD->pcdac[i];
64714779705SSam Leffler
64814779705SSam Leffler /* No value to return from table */
64914779705SSam Leffler #ifdef AH_DEBUG
65014779705SSam Leffler ath_hal_printf(ah, "%s: empty transmit power table?\n", __func__);
65114779705SSam Leffler #endif
65214779705SSam Leffler return 1;
65314779705SSam Leffler }
65414779705SSam Leffler
65514779705SSam Leffler /*
65614779705SSam Leffler * Find or interpolates the gainF value from the table ptr.
65714779705SSam Leffler */
65814779705SSam Leffler static uint8_t
getGainF(struct ath_hal * ah,const struct tpcMap * pRD,uint8_t pcdac,uint8_t * dBm)65914779705SSam Leffler getGainF(struct ath_hal *ah, const struct tpcMap *pRD,
66014779705SSam Leffler uint8_t pcdac, uint8_t *dBm)
66114779705SSam Leffler {
66214779705SSam Leffler uint32_t interp;
66314779705SSam Leffler int low, high, i;
66414779705SSam Leffler
66514779705SSam Leffler low = high = -1;
66614779705SSam Leffler
66714779705SSam Leffler for (i = 0; i < AR_TP_SCALING_ENTRIES; i++) {
66814779705SSam Leffler if(pRD->pcdac[i] == 63)
66914779705SSam Leffler continue;
67014779705SSam Leffler if (pcdac == pRD->pcdac[i]) {
67114779705SSam Leffler *dBm = AR_I2DBM(i);
67214779705SSam Leffler return pRD->gainF[i]; /* Exact Match */
67314779705SSam Leffler }
67414779705SSam Leffler if (pcdac > pRD->pcdac[i])
67514779705SSam Leffler low = i;
67614779705SSam Leffler if (pcdac < pRD->pcdac[i]) {
67714779705SSam Leffler high = i;
67814779705SSam Leffler if (low == -1) {
67914779705SSam Leffler *dBm = AR_I2DBM(i);
68014779705SSam Leffler /* PCDAC is lower than lowest setting */
68114779705SSam Leffler return pRD->gainF[i];
68214779705SSam Leffler }
68314779705SSam Leffler break;
68414779705SSam Leffler }
68514779705SSam Leffler }
68614779705SSam Leffler if (i >= AR_TP_SCALING_ENTRIES && low == -1) {
68714779705SSam Leffler /* No settings were found */
68814779705SSam Leffler #ifdef AH_DEBUG
68914779705SSam Leffler ath_hal_printf(ah,
69014779705SSam Leffler "%s: no valid entries in the pcdac table: %d\n",
69114779705SSam Leffler __func__, pcdac);
69214779705SSam Leffler #endif
69314779705SSam Leffler return 63;
69414779705SSam Leffler }
69514779705SSam Leffler if (i >= AR_TP_SCALING_ENTRIES) {
69614779705SSam Leffler /* PCDAC setting was above the max setting in the table */
69714779705SSam Leffler *dBm = AR_I2DBM(low);
69814779705SSam Leffler return pRD->gainF[low];
69914779705SSam Leffler }
70014779705SSam Leffler /* Only exact if table has no missing entries */
70114779705SSam Leffler *dBm = (low + high) + 3;
70214779705SSam Leffler
70314779705SSam Leffler /*
70414779705SSam Leffler * Perform interpolation between low and high values to find gainF
70514779705SSam Leffler * linearly scale the pcdac between low and high
70614779705SSam Leffler */
70714779705SSam Leffler interp = ((pcdac - pRD->pcdac[low]) * 1000) /
70814779705SSam Leffler (pRD->pcdac[high] - pRD->pcdac[low]);
70914779705SSam Leffler /*
71014779705SSam Leffler * Multiply the scale ratio by the gainF difference
71114779705SSam Leffler * (plus a rnd up factor)
71214779705SSam Leffler */
71314779705SSam Leffler interp = ((interp * (pRD->gainF[high] - pRD->gainF[low])) + 999) / 1000;
71414779705SSam Leffler
71514779705SSam Leffler /* Add ratioed gain_f to low gain_f value */
71614779705SSam Leffler return interp + pRD->gainF[low];
71714779705SSam Leffler }
71814779705SSam Leffler
71914779705SSam Leffler HAL_BOOL
ar5210SetTxPowerLimit(struct ath_hal * ah,uint32_t limit)72014779705SSam Leffler ar5210SetTxPowerLimit(struct ath_hal *ah, uint32_t limit)
72114779705SSam Leffler {
72214779705SSam Leffler AH_PRIVATE(ah)->ah_powerLimit = AH_MIN(limit, AR5210_MAX_RATE_POWER);
72314779705SSam Leffler /* XXX flush to h/w */
72414779705SSam Leffler return AH_TRUE;
72514779705SSam Leffler }
72614779705SSam Leffler
72714779705SSam Leffler /*
72814779705SSam Leffler * Get TXPower values and set them in the radio
72914779705SSam Leffler */
73014779705SSam Leffler static HAL_BOOL
setupPowerSettings(struct ath_hal * ah,const struct ieee80211_channel * chan,uint8_t cp[17])73159efa8b5SSam Leffler setupPowerSettings(struct ath_hal *ah, const struct ieee80211_channel *chan,
73259efa8b5SSam Leffler uint8_t cp[17])
73314779705SSam Leffler {
73459efa8b5SSam Leffler uint16_t freq = ath_hal_gethwchannel(ah, chan);
73514779705SSam Leffler const HAL_EEPROM_v1 *ee = AH_PRIVATE(ah)->ah_eeprom;
73614779705SSam Leffler uint8_t gainFRD, gainF36, gainF48, gainF54;
73714779705SSam Leffler uint8_t dBmRD, dBm36, dBm48, dBm54, dontcare;
73814779705SSam Leffler uint32_t rd, group;
73914779705SSam Leffler const struct tpcMap *pRD;
74014779705SSam Leffler
74114779705SSam Leffler /* Set OB/DB Values regardless of channel */
74214779705SSam Leffler cp[15] = (ee->ee_biasCurrents >> 4) & 0x7;
74314779705SSam Leffler cp[16] = ee->ee_biasCurrents & 0x7;
74414779705SSam Leffler
74559efa8b5SSam Leffler if (freq < 5170 || freq > 5320) {
74614779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u\n",
74759efa8b5SSam Leffler __func__, freq);
74814779705SSam Leffler return AH_FALSE;
74914779705SSam Leffler }
75014779705SSam Leffler
75114779705SSam Leffler HALASSERT(ee->ee_version >= AR_EEPROM_VER1 &&
75214779705SSam Leffler ee->ee_version < AR_EEPROM_VER3);
75314779705SSam Leffler
75414779705SSam Leffler /* Match regulatory domain */
75514779705SSam Leffler for (rd = 0; rd < AR_REG_DOMAINS_MAX; rd++)
75614779705SSam Leffler if (AH_PRIVATE(ah)->ah_currentRD == ee->ee_regDomain[rd])
75714779705SSam Leffler break;
75814779705SSam Leffler if (rd == AR_REG_DOMAINS_MAX) {
75914779705SSam Leffler #ifdef AH_DEBUG
76014779705SSam Leffler ath_hal_printf(ah,
76114779705SSam Leffler "%s: no calibrated regulatory domain matches the "
76214779705SSam Leffler "current regularly domain (0x%0x)\n", __func__,
76314779705SSam Leffler AH_PRIVATE(ah)->ah_currentRD);
76414779705SSam Leffler #endif
76514779705SSam Leffler return AH_FALSE;
76614779705SSam Leffler }
76759efa8b5SSam Leffler group = ((freq - 5170) / 10);
76814779705SSam Leffler
76914779705SSam Leffler if (group > 11) {
77014779705SSam Leffler /* Pull 5.29 into the 5.27 group */
77114779705SSam Leffler group--;
77214779705SSam Leffler }
77314779705SSam Leffler
77414779705SSam Leffler /* Integer divide will set group from 0 to 4 */
77514779705SSam Leffler group = group / 3;
77614779705SSam Leffler pRD = &ee->ee_tpc[group];
77714779705SSam Leffler
77814779705SSam Leffler /* Set PC DAC Values */
77914779705SSam Leffler cp[14] = pRD->regdmn[rd];
78014779705SSam Leffler cp[9] = AH_MIN(pRD->regdmn[rd], pRD->rate36);
78114779705SSam Leffler cp[8] = AH_MIN(pRD->regdmn[rd], pRD->rate48);
78214779705SSam Leffler cp[7] = AH_MIN(pRD->regdmn[rd], pRD->rate54);
78314779705SSam Leffler
78414779705SSam Leffler /* Find Corresponding gainF values for RD, 36, 48, 54 */
78514779705SSam Leffler gainFRD = getGainF(ah, pRD, pRD->regdmn[rd], &dBmRD);
78614779705SSam Leffler gainF36 = getGainF(ah, pRD, cp[9], &dBm36);
78714779705SSam Leffler gainF48 = getGainF(ah, pRD, cp[8], &dBm48);
78814779705SSam Leffler gainF54 = getGainF(ah, pRD, cp[7], &dBm54);
78914779705SSam Leffler
79014779705SSam Leffler /* Power Scale if requested */
79114779705SSam Leffler if (AH_PRIVATE(ah)->ah_tpScale != HAL_TP_SCALE_MAX) {
79214779705SSam Leffler static const uint16_t tpcScaleReductionTable[5] =
79314779705SSam Leffler { 0, 3, 6, 9, AR5210_MAX_RATE_POWER };
79414779705SSam Leffler uint16_t tpScale;
79514779705SSam Leffler
79614779705SSam Leffler tpScale = tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale];
79714779705SSam Leffler if (dBmRD < tpScale+3)
79814779705SSam Leffler dBmRD = 3; /* min */
79914779705SSam Leffler else
80014779705SSam Leffler dBmRD -= tpScale;
80114779705SSam Leffler cp[14] = getPcdac(ah, pRD, dBmRD);
80214779705SSam Leffler gainFRD = getGainF(ah, pRD, cp[14], &dontcare);
80314779705SSam Leffler dBm36 = AH_MIN(dBm36, dBmRD);
80414779705SSam Leffler cp[9] = getPcdac(ah, pRD, dBm36);
80514779705SSam Leffler gainF36 = getGainF(ah, pRD, cp[9], &dontcare);
80614779705SSam Leffler dBm48 = AH_MIN(dBm48, dBmRD);
80714779705SSam Leffler cp[8] = getPcdac(ah, pRD, dBm48);
80814779705SSam Leffler gainF48 = getGainF(ah, pRD, cp[8], &dontcare);
80914779705SSam Leffler dBm54 = AH_MIN(dBm54, dBmRD);
81014779705SSam Leffler cp[7] = getPcdac(ah, pRD, dBm54);
81114779705SSam Leffler gainF54 = getGainF(ah, pRD, cp[7], &dontcare);
81214779705SSam Leffler }
81314779705SSam Leffler /* Record current dBm at rate 6 */
81414779705SSam Leffler AH_PRIVATE(ah)->ah_maxPowerLevel = 2*dBmRD;
81514779705SSam Leffler
81614779705SSam Leffler cp[13] = cp[12] = cp[11] = cp[10] = cp[14];
81714779705SSam Leffler
81814779705SSam Leffler /* Set GainF Values */
81914779705SSam Leffler cp[0] = gainFRD - gainF54;
82014779705SSam Leffler cp[1] = gainFRD - gainF48;
82114779705SSam Leffler cp[2] = gainFRD - gainF36;
82214779705SSam Leffler /* 9, 12, 18, 24 have no gain_delta from 6 */
82314779705SSam Leffler cp[3] = cp[4] = cp[5] = cp[6] = 0;
82414779705SSam Leffler return AH_TRUE;
82514779705SSam Leffler }
82614779705SSam Leffler
82714779705SSam Leffler /*
82814779705SSam Leffler * Places the device in and out of reset and then places sane
82914779705SSam Leffler * values in the registers based on EEPROM config, initialization
83014779705SSam Leffler * vectors (as determined by the mode), and station configuration
83114779705SSam Leffler */
83214779705SSam Leffler HAL_BOOL
ar5210SetTransmitPower(struct ath_hal * ah,const struct ieee80211_channel * chan)83359efa8b5SSam Leffler ar5210SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan)
83414779705SSam Leffler {
83514779705SSam Leffler #define N(a) (sizeof (a) / sizeof (a[0]))
83614779705SSam Leffler static const uint32_t pwr_regs_start[17] = {
83714779705SSam Leffler 0x00000000, 0x00000000, 0x00000000,
83814779705SSam Leffler 0x00000000, 0x00000000, 0xf0000000,
83914779705SSam Leffler 0xcc000000, 0x00000000, 0x00000000,
84014779705SSam Leffler 0x00000000, 0x0a000000, 0x000000e2,
84114779705SSam Leffler 0x0a000020, 0x01000002, 0x01000018,
84214779705SSam Leffler 0x40000000, 0x00000418
84314779705SSam Leffler };
84414779705SSam Leffler uint16_t i;
84514779705SSam Leffler uint8_t cp[sizeof(ar5k0007_pwrSettings)];
84614779705SSam Leffler uint32_t pwr_regs[17];
84714779705SSam Leffler
84814779705SSam Leffler OS_MEMCPY(pwr_regs, pwr_regs_start, sizeof(pwr_regs));
84914779705SSam Leffler OS_MEMCPY(cp, ar5k0007_pwrSettings, sizeof(cp));
85014779705SSam Leffler
85114779705SSam Leffler /* Check the EEPROM tx power calibration settings */
85214779705SSam Leffler if (!setupPowerSettings(ah, chan, cp)) {
85314779705SSam Leffler #ifdef AH_DEBUG
85414779705SSam Leffler ath_hal_printf(ah, "%s: unable to setup power settings\n",
85514779705SSam Leffler __func__);
85614779705SSam Leffler #endif
85714779705SSam Leffler return AH_FALSE;
85814779705SSam Leffler }
85914779705SSam Leffler if (cp[15] < 1 || cp[15] > 5) {
86014779705SSam Leffler #ifdef AH_DEBUG
86114779705SSam Leffler ath_hal_printf(ah, "%s: OB out of range (%u)\n",
86214779705SSam Leffler __func__, cp[15]);
86314779705SSam Leffler #endif
86414779705SSam Leffler return AH_FALSE;
86514779705SSam Leffler }
86614779705SSam Leffler if (cp[16] < 1 || cp[16] > 5) {
86714779705SSam Leffler #ifdef AH_DEBUG
86814779705SSam Leffler ath_hal_printf(ah, "%s: DB out of range (%u)\n",
86914779705SSam Leffler __func__, cp[16]);
87014779705SSam Leffler #endif
87114779705SSam Leffler return AH_FALSE;
87214779705SSam Leffler }
87314779705SSam Leffler
87414779705SSam Leffler /* reverse bits of the transmit power array */
87514779705SSam Leffler for (i = 0; i < 7; i++)
87614779705SSam Leffler cp[i] = ath_hal_reverseBits(cp[i], 5);
87714779705SSam Leffler for (i = 7; i < 15; i++)
87814779705SSam Leffler cp[i] = ath_hal_reverseBits(cp[i], 6);
87914779705SSam Leffler
88014779705SSam Leffler /* merge transmit power values into the register - quite gross */
88114779705SSam Leffler pwr_regs[0] |= ((cp[1] << 5) & 0xE0) | (cp[0] & 0x1F);
88214779705SSam Leffler pwr_regs[1] |= ((cp[3] << 7) & 0x80) | ((cp[2] << 2) & 0x7C) |
88314779705SSam Leffler ((cp[1] >> 3) & 0x03);
88414779705SSam Leffler pwr_regs[2] |= ((cp[4] << 4) & 0xF0) | ((cp[3] >> 1) & 0x0F);
88514779705SSam Leffler pwr_regs[3] |= ((cp[6] << 6) & 0xC0) | ((cp[5] << 1) & 0x3E) |
88614779705SSam Leffler ((cp[4] >> 4) & 0x01);
88714779705SSam Leffler pwr_regs[4] |= ((cp[7] << 3) & 0xF8) | ((cp[6] >> 2) & 0x07);
88814779705SSam Leffler pwr_regs[5] |= ((cp[9] << 7) & 0x80) | ((cp[8] << 1) & 0x7E) |
88914779705SSam Leffler ((cp[7] >> 5) & 0x01);
89014779705SSam Leffler pwr_regs[6] |= ((cp[10] << 5) & 0xE0) | ((cp[9] >> 1) & 0x1F);
89114779705SSam Leffler pwr_regs[7] |= ((cp[11] << 3) & 0xF8) | ((cp[10] >> 3) & 0x07);
89214779705SSam Leffler pwr_regs[8] |= ((cp[12] << 1) & 0x7E) | ((cp[11] >> 5) & 0x01);
89314779705SSam Leffler pwr_regs[9] |= ((cp[13] << 5) & 0xE0);
89414779705SSam Leffler pwr_regs[10] |= ((cp[14] << 3) & 0xF8) | ((cp[13] >> 3) & 0x07);
89514779705SSam Leffler pwr_regs[11] |= ((cp[14] >> 5) & 0x01);
89614779705SSam Leffler
89714779705SSam Leffler /* Set OB */
89814779705SSam Leffler pwr_regs[8] |= (ath_hal_reverseBits(cp[15], 3) << 7) & 0x80;
89914779705SSam Leffler pwr_regs[9] |= (ath_hal_reverseBits(cp[15], 3) >> 1) & 0x03;
90014779705SSam Leffler
90114779705SSam Leffler /* Set DB */
90214779705SSam Leffler pwr_regs[9] |= (ath_hal_reverseBits(cp[16], 3) << 2) & 0x1C;
90314779705SSam Leffler
90414779705SSam Leffler /* Write the registers */
90514779705SSam Leffler for (i = 0; i < N(pwr_regs)-1; i++)
90614779705SSam Leffler OS_REG_WRITE(ah, 0x0000989c, pwr_regs[i]);
90714779705SSam Leffler /* last write is a flush */
90814779705SSam Leffler OS_REG_WRITE(ah, 0x000098d4, pwr_regs[i]);
90914779705SSam Leffler
91014779705SSam Leffler return AH_TRUE;
91114779705SSam Leffler #undef N
91214779705SSam Leffler }
91314779705SSam Leffler
91414779705SSam Leffler /*
91514779705SSam Leffler * Takes the MHz channel value and sets the Channel value
91614779705SSam Leffler *
91714779705SSam Leffler * ASSUMES: Writes enabled to analog bus before AGC is active
91814779705SSam Leffler * or by disabling the AGC.
91914779705SSam Leffler */
92014779705SSam Leffler static HAL_BOOL
ar5210SetChannel(struct ath_hal * ah,struct ieee80211_channel * chan)92159efa8b5SSam Leffler ar5210SetChannel(struct ath_hal *ah, struct ieee80211_channel *chan)
92214779705SSam Leffler {
92359efa8b5SSam Leffler uint16_t freq = ath_hal_gethwchannel(ah, chan);
92414779705SSam Leffler uint32_t data;
92514779705SSam Leffler
92614779705SSam Leffler /* Set the Channel */
92759efa8b5SSam Leffler data = ath_hal_reverseBits((freq - 5120)/10, 5);
92814779705SSam Leffler data = (data << 1) | 0x41;
92914779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(0x27), data);
93014779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(0x30), 0);
93114779705SSam Leffler AH_PRIVATE(ah)->ah_curchan = chan;
93214779705SSam Leffler return AH_TRUE;
93314779705SSam Leffler }
93414779705SSam Leffler
93514779705SSam Leffler int16_t
ar5210GetNoiseFloor(struct ath_hal * ah)93614779705SSam Leffler ar5210GetNoiseFloor(struct ath_hal *ah)
93714779705SSam Leffler {
93814779705SSam Leffler int16_t nf;
93914779705SSam Leffler
94014779705SSam Leffler nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff;
94114779705SSam Leffler if (nf & 0x100)
94214779705SSam Leffler nf = 0 - ((nf ^ 0x1ff) + 1);
94314779705SSam Leffler return nf;
94414779705SSam Leffler }
94514779705SSam Leffler
94614779705SSam Leffler #define NORMAL_NF_THRESH (-72)
94714779705SSam Leffler /*
94814779705SSam Leffler * Peform the noisefloor calibration and check for
94914779705SSam Leffler * any constant channel interference
95014779705SSam Leffler *
95114779705SSam Leffler * Returns: TRUE for a successful noise floor calibration; else FALSE
95214779705SSam Leffler */
95314779705SSam Leffler HAL_BOOL
ar5210CalNoiseFloor(struct ath_hal * ah,HAL_CHANNEL_INTERNAL * ichan)95459efa8b5SSam Leffler ar5210CalNoiseFloor(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan)
95514779705SSam Leffler {
95614779705SSam Leffler int32_t nf, nfLoops;
95714779705SSam Leffler
95814779705SSam Leffler /* Calibrate the noise floor */
95914779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_AGCCTL,
96014779705SSam Leffler OS_REG_READ(ah, AR_PHY_AGCCTL) | AR_PHY_AGC_NF);
96114779705SSam Leffler
96214779705SSam Leffler /* Do not read noise floor until it has done the first update */
96314779705SSam Leffler if (!ath_hal_wait(ah, AR_PHY_AGCCTL, AR_PHY_AGC_NF, 0)) {
96414779705SSam Leffler #ifdef ATH_HAL_DEBUG
96514779705SSam Leffler ath_hal_printf(ah, " -PHY NF Reg state: 0x%x\n",
96614779705SSam Leffler OS_REG_READ(ah, AR_PHY_AGCCTL));
96714779705SSam Leffler ath_hal_printf(ah, " -MAC Reset Reg state: 0x%x\n",
96814779705SSam Leffler OS_REG_READ(ah, AR_RC));
96914779705SSam Leffler ath_hal_printf(ah, " -PHY Active Reg state: 0x%x\n",
97014779705SSam Leffler OS_REG_READ(ah, AR_PHY_ACTIVE));
97114779705SSam Leffler #endif /* ATH_HAL_DEBUG */
97214779705SSam Leffler return AH_FALSE;
97314779705SSam Leffler }
97414779705SSam Leffler
97514779705SSam Leffler nf = 0;
97614779705SSam Leffler /* Keep checking until the floor is below the threshold or the nf is done */
97714779705SSam Leffler for (nfLoops = 0; ((nfLoops < 21) && (nf > NORMAL_NF_THRESH)); nfLoops++) {
97814779705SSam Leffler OS_DELAY(1000); /* Sleep for 1 ms */
97914779705SSam Leffler nf = ar5210GetNoiseFloor(ah);
98014779705SSam Leffler }
98114779705SSam Leffler
98214779705SSam Leffler if (nf > NORMAL_NF_THRESH) {
98314779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Bad noise cal %d\n",
98414779705SSam Leffler __func__, nf);
98559efa8b5SSam Leffler ichan->rawNoiseFloor = 0;
98614779705SSam Leffler return AH_FALSE;
98714779705SSam Leffler }
98859efa8b5SSam Leffler ichan->rawNoiseFloor = nf;
98914779705SSam Leffler return AH_TRUE;
99014779705SSam Leffler }
99114779705SSam Leffler
99214779705SSam Leffler /*
99314779705SSam Leffler * Adjust NF based on statistical values for 5GHz frequencies.
99414779705SSam Leffler */
99514779705SSam Leffler int16_t
ar5210GetNfAdjust(struct ath_hal * ah,const HAL_CHANNEL_INTERNAL * c)99614779705SSam Leffler ar5210GetNfAdjust(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *c)
99714779705SSam Leffler {
99814779705SSam Leffler return 0;
99914779705SSam Leffler }
100014779705SSam Leffler
100114779705SSam Leffler HAL_RFGAIN
ar5210GetRfgain(struct ath_hal * ah)100214779705SSam Leffler ar5210GetRfgain(struct ath_hal *ah)
100314779705SSam Leffler {
100414779705SSam Leffler return HAL_RFGAIN_INACTIVE;
100514779705SSam Leffler }
1006