diff options
Diffstat (limited to 'hal/ar9300/ar9300_reset.c')
-rw-r--r-- | hal/ar9300/ar9300_reset.c | 6072 |
1 files changed, 6072 insertions, 0 deletions
diff --git a/hal/ar9300/ar9300_reset.c b/hal/ar9300/ar9300_reset.c new file mode 100644 index 000000000000..58ebc1ec076a --- /dev/null +++ b/hal/ar9300/ar9300_reset.c @@ -0,0 +1,6072 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, 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 "opt_ah.h" + +#ifdef AH_SUPPORT_AR9300 + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" +#include "ah_desc.h" + +#include "ar9300.h" +#include "ar9300reg.h" +#include "ar9300phy.h" +#include "ar9300desc.h" + +#define FIX_NOISE_FLOOR 1 + + + +/* Additional Time delay to wait after activiting the Base band */ +#define BASE_ACTIVATE_DELAY 100 /* usec */ +#define RTC_PLL_SETTLE_DELAY 100 /* usec */ +#define COEF_SCALE_S 24 +#define HT40_CHANNEL_CENTER_SHIFT 10 /* MHz */ + +#define DELPT 32 + +extern HAL_BOOL ar9300_reset_tx_queue(struct ath_hal *ah, u_int q); +extern u_int32_t ar9300_num_tx_pending(struct ath_hal *ah, u_int q); + + +#define MAX_MEASUREMENT 8 +#define MAXIQCAL 3 +struct coeff_t { + int32_t mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; + int32_t phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; + int32_t iqc_coeff[2]; + int last_nmeasurement; + HAL_BOOL last_cal; +}; + +static HAL_BOOL ar9300_tx_iq_cal_hw_run(struct ath_hal *ah); +static void ar9300_tx_iq_cal_post_proc(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, + int iqcal_idx, int max_iqcal, HAL_BOOL is_cal_reusable, HAL_BOOL apply_last_corr); +static void ar9300_tx_iq_cal_outlier_detection(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, + u_int32_t num_chains, struct coeff_t *coeff, HAL_BOOL is_cal_reusable); +#if ATH_SUPPORT_CAL_REUSE +static void ar9300_tx_iq_cal_apply(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan); +#endif + + +static inline void ar9300_prog_ini(struct ath_hal *ah, struct ar9300_ini_array *ini_arr, int column); +static inline void ar9300_set_rf_mode(struct ath_hal *ah, HAL_CHANNEL *chan); +static inline HAL_BOOL ar9300_init_cal(struct ath_hal *ah, HAL_CHANNEL *chan, HAL_BOOL skip_if_none, HAL_BOOL apply_last_corr); +static inline void ar9300_init_user_settings(struct ath_hal *ah); + +#ifdef HOST_OFFLOAD +/* + * For usb offload solution, some USB registers must be tuned + * to gain better stability/performance but these registers + * might be changed while doing wlan reset so do this here + */ +#define WAR_USB_DISABLE_PLL_LOCK_DETECT(__ah) \ +do { \ + if (AR_SREV_HORNET(__ah) || AR_SREV_WASP(__ah)) { \ + volatile u_int32_t *usb_ctrl_r1 = (u_int32_t *) 0xb8116c84; \ + volatile u_int32_t *usb_ctrl_r2 = (u_int32_t *) 0xb8116c88; \ + *usb_ctrl_r1 = (*usb_ctrl_r1 & 0xffefffff); \ + *usb_ctrl_r2 = (*usb_ctrl_r2 & 0xfc1fffff) | (1 << 21) | (3 << 22); \ + } \ +} while (0) +#else +#define WAR_USB_DISABLE_PLL_LOCK_DETECT(__ah) +#endif + +static inline void +ar9300_attach_hw_platform(struct ath_hal *ah) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + + ahp->ah_hwp = HAL_TRUE_CHIP; + return; +} + +/* Adjust various register settings based on half/quarter rate clock setting. + * This includes: +USEC, TX/RX latency, + * + IFS params: slot, eifs, misc etc. + * SIFS stays the same. + */ +static void +ar9300_set_ifs_timing(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + u_int32_t tx_lat, rx_lat, usec, slot, regval, eifs; + + regval = OS_REG_READ(ah, AR_USEC); + regval &= ~(AR_USEC_RX_LATENCY | AR_USEC_TX_LATENCY | AR_USEC_USEC); + if (IS_CHAN_HALF_RATE(chan)) { /* half rates */ + slot = ar9300_mac_to_clks(ah, AR_SLOT_HALF); + eifs = ar9300_mac_to_clks(ah, AR_EIFS_HALF); + if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* fast clock */ + rx_lat = SM(AR_RX_LATENCY_HALF_FAST_CLOCK, AR_USEC_RX_LATENCY); + tx_lat = SM(AR_TX_LATENCY_HALF_FAST_CLOCK, AR_USEC_TX_LATENCY); + usec = SM(AR_USEC_HALF_FAST_CLOCK, AR_USEC_USEC); + } else { + rx_lat = SM(AR_RX_LATENCY_HALF, AR_USEC_RX_LATENCY); + tx_lat = SM(AR_TX_LATENCY_HALF, AR_USEC_TX_LATENCY); + usec = SM(AR_USEC_HALF, AR_USEC_USEC); + } + } else { /* quarter rate */ + slot = ar9300_mac_to_clks(ah, AR_SLOT_QUARTER); + eifs = ar9300_mac_to_clks(ah, AR_EIFS_QUARTER); + if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* fast clock */ + rx_lat = SM(AR_RX_LATENCY_QUARTER_FAST_CLOCK, AR_USEC_RX_LATENCY); + tx_lat = SM(AR_TX_LATENCY_QUARTER_FAST_CLOCK, AR_USEC_TX_LATENCY); + usec = SM(AR_USEC_QUARTER_FAST_CLOCK, AR_USEC_USEC); + } else { + rx_lat = SM(AR_RX_LATENCY_QUARTER, AR_USEC_RX_LATENCY); + tx_lat = SM(AR_TX_LATENCY_QUARTER, AR_USEC_TX_LATENCY); + usec = SM(AR_USEC_QUARTER, AR_USEC_USEC); + } + } + + OS_REG_WRITE(ah, AR_USEC, (usec | regval | tx_lat | rx_lat)); + OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); + OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); +} + + +/* + * This inline function configures the chip either + * to encrypt/decrypt management frames or pass thru + */ +static inline void +ar9300_init_mfp(struct ath_hal * ah) +{ + u_int32_t mfpcap, mfp_qos; + + ath_hal_getcapability(ah, HAL_CAP_MFP, 0, &mfpcap); + + if (mfpcap == HAL_MFP_QOSDATA) { + /* Treat like legacy hardware. Do not touch the MFP registers. */ + HALDEBUG(ah, HAL_DEBUG_RESET, "%s forced to use QOSDATA\n", __func__); + return; + } + + /* MFP support (Sowl 1.0 or greater) */ + if (mfpcap == HAL_MFP_HW_CRYPTO) { + /* configure hardware MFP support */ + HALDEBUG(ah, HAL_DEBUG_RESET, "%s using HW crypto\n", __func__); + OS_REG_RMW_FIELD(ah, + AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT, AR_AES_MUTE_MASK1_FC_MGMT_MFP); + OS_REG_RMW(ah, + AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE, + AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT); + /* + * Mask used to construct AAD for CCMP-AES + * Cisco spec defined bits 0-3 as mask + * IEEE802.11w defined as bit 4. + */ + if (ath_hal_get_mfp_qos(ah)) { + mfp_qos = AR_MFP_QOS_MASK_IEEE; + } else { + mfp_qos = AR_MFP_QOS_MASK_CISCO; + } + OS_REG_RMW_FIELD(ah, + AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_MGMT_QOS, mfp_qos); + } else if (mfpcap == HAL_MFP_PASSTHRU) { + /* Disable en/decrypt by hardware */ + HALDEBUG(ah, HAL_DEBUG_RESET, "%s using passthru\n", __func__); + OS_REG_RMW(ah, + AR_PCU_MISC_MODE2, + AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT, + AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE); + } +} + +void +ar9300_get_channel_centers(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, + CHAN_CENTERS *centers) +{ + int8_t extoff; + struct ath_hal_9300 *ahp = AH9300(ah); + + if (!IS_CHAN_HT40(chan)) { + centers->ctl_center = centers->ext_center = + centers->synth_center = chan->channel; + return; + } + + HALASSERT(IS_CHAN_HT40(chan)); + + /* + * In 20/40 phy mode, the center frequency is + * "between" the primary and extension channels. + */ + if (chan->channel_flags & CHANNEL_HT40PLUS) { + centers->synth_center = chan->channel + HT40_CHANNEL_CENTER_SHIFT; + extoff = 1; + } else { + centers->synth_center = chan->channel - HT40_CHANNEL_CENTER_SHIFT; + extoff = -1; + } + + centers->ctl_center = + centers->synth_center - (extoff * HT40_CHANNEL_CENTER_SHIFT); + centers->ext_center = + centers->synth_center + + (extoff * ((ahp->ah_ext_prot_spacing == HAL_HT_EXTPROTSPACING_20) ? + HT40_CHANNEL_CENTER_SHIFT : 15)); +} + +/* + * Read the noise-floor values from the HW. + * Specifically, read the minimum clear-channel assessment value for + * each chain, for both the control and extension channels. + * (The received power level during clear-channel periods is the + * noise floor.) + * These noise floor values computed by the HW will be stored in the + * NF history buffer. + * The HW sometimes produces bogus NF values. To avoid using these + * bogus values, the NF data is (a) range-limited, and (b) filtered. + * However, this data-processing is done when reading the NF values + * out of the history buffer. The history buffer stores the raw values. + * This allows the NF history buffer to be used to check for interference. + * A single high NF reading might be a bogus HW value, but if the NF + * readings are consistently high, it must be due to interference. + * This is the purpose of storing raw NF values in the history buffer, + * rather than processed values. By looking at a history of NF values + * that have not been range-limited, we can check if they are consistently + * high (due to interference). + */ +#define AH_NF_SIGN_EXTEND(nf) \ + ((nf) & 0x100) ? \ + 0 - (((nf) ^ 0x1ff) + 1) : \ + (nf) +void +ar9300_upload_noise_floor(struct ath_hal *ah, int is_2g, + int16_t nfarray[NUM_NF_READINGS]) +{ + int16_t nf; + int chan, chain; + u_int32_t regs[NUM_NF_READINGS] = { + /* control channel */ + AR_PHY_CCA_0, /* chain 0 */ + AR_PHY_CCA_1, /* chain 1 */ + AR_PHY_CCA_2, /* chain 2 */ + /* extension channel */ + AR_PHY_EXT_CCA, /* chain 0 */ + AR_PHY_EXT_CCA_1, /* chain 1 */ + AR_PHY_EXT_CCA_2, /* chain 2 */ + }; + u_int8_t chainmask; + + /* + * Within a given channel (ctl vs. ext), the CH0, CH1, and CH2 + * masks and shifts are the same, though they differ for the + * control vs. extension channels. + */ + u_int32_t masks[2] = { + AR_PHY_MINCCA_PWR, /* control channel */ + AR_PHY_EXT_MINCCA_PWR, /* extention channel */ + }; + u_int8_t shifts[2] = { + AR_PHY_MINCCA_PWR_S, /* control channel */ + AR_PHY_EXT_MINCCA_PWR_S, /* extention channel */ + }; + + /* + * Force NF calibration for all chains. + */ + if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { + chainmask = 0x01; + } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah)) { + chainmask = 0x03; + } else { + chainmask = 0x07; + } + + for (chan = 0; chan < 2 /*ctl,ext*/; chan++) { + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + int i; + + if (!((chainmask >> chain) & 0x1)) { + continue; + } + i = chan * AR9300_MAX_CHAINS + chain; + nf = (OS_REG_READ(ah, regs[i]) & masks[chan]) >> shifts[chan]; + nfarray[i] = AH_NF_SIGN_EXTEND(nf); + } + } +} + +/* ar9300_get_min_cca_pwr - + * Used by the scan function for a quick read of the noise floor. + * This is used to detect presence of CW interference such as video bridge. + * The noise floor is assumed to have been already started during reset + * called during channel change. The function checks if the noise floor + * reading is done. In case it has been done, it reads the noise floor value. + * If the noise floor calibration has not been finished, it assumes this is + * due to presence of CW interference an returns a high value for noise floor, + * derived from the CW interference threshold + margin fudge factor. + */ +#define BAD_SCAN_NF_MARGIN (30) +int16_t ar9300_get_min_cca_pwr(struct ath_hal *ah) +{ + int16_t nf; + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + + if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0) { + nf = MS(OS_REG_READ(ah, AR_PHY_CCA_0), AR9280_PHY_MINCCA_PWR); + if (nf & 0x100) { + nf = 0 - ((nf ^ 0x1ff) + 1); + } + } else { + /* NF calibration is not done, assume CW interference */ + nf = ahpriv->nfp->nominal + ahpriv->nf_cw_int_delta + + BAD_SCAN_NF_MARGIN; + } + return nf; +} + + +/* + * Noise Floor values for all chains. + * Most recently updated values from the NF history buffer are used. + */ +void ar9300_chain_noise_floor(struct ath_hal *ah, int16_t *nf_buf, + HAL_CHANNEL *chan, int is_scan) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + int i, nf_hist_len, recent_nf_index = 0; + HAL_NFCAL_HIST_FULL *h; + u_int8_t rx_chainmask = ahp->ah_rx_chainmask | (ahp->ah_rx_chainmask << 3); + HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); + HALASSERT(ichan); + +#ifdef ATH_NF_PER_CHAN + /* Fill 0 if valid internal channel is not found */ + if (ichan == AH_NULL) { + OS_MEMZERO(nf_buf, sizeof(nf_buf[0])*NUM_NF_READINGS); + return; + } + h = &ichan->nf_cal_hist; + nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; +#else + /* + * If a scan is not in progress, then the most recent value goes + * into ahpriv->nf_cal_hist. If a scan is in progress, then + * the most recent value goes into ichan->nf_cal_hist. + * Thus, return the value from ahpriv->nf_cal_hist if there's + * no scan, and if the specified channel is the current channel. + * Otherwise, return the noise floor from ichan->nf_cal_hist. + */ + if ((!is_scan) && chan->channel == AH_PRIVATE(ah)->ah_curchan->channel) { + h = &AH_PRIVATE(ah)->nf_cal_hist; + nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; + } else { + /* Fill 0 if valid internal channel is not found */ + if (ichan == AH_NULL) { + OS_MEMZERO(nf_buf, sizeof(nf_buf[0])*NUM_NF_READINGS); + return; + } + /* + * It is okay to treat a HAL_NFCAL_HIST_SMALL struct as if it were a + * HAL_NFCAL_HIST_FULL struct, as long as only the index 0 of the + * nf_cal_buffer is used (nf_cal_buffer[0][0:NUM_NF_READINGS-1]) + */ + h = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; + nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; + } +#endif + /* Get most recently updated values from nf cal history buffer */ + recent_nf_index = + (h->base.curr_index) ? h->base.curr_index - 1 : nf_hist_len - 1; + + for (i = 0; i < NUM_NF_READINGS; i++) { + /* Fill 0 for unsupported chains */ + if (!(rx_chainmask & (1 << i))) { + nf_buf[i] = 0; + continue; + } + nf_buf[i] = h->nf_cal_buffer[recent_nf_index][i]; + } +} + + +/* + * Pick up the medium one in the noise floor buffer and update the + * corresponding range for valid noise floor values + */ +static int16_t +ar9300_get_nf_hist_mid(struct ath_hal *ah, HAL_NFCAL_HIST_FULL *h, int reading, + int hist_len) +{ + int16_t nfval; + int16_t sort[HAL_NF_CAL_HIST_LEN_FULL]; /* upper bound for hist_len */ + int i, j; + + for (i = 0; i < hist_len; i++) { + sort[i] = h->nf_cal_buffer[i][reading]; + HALDEBUG(ah, HAL_DEBUG_NF_CAL, + "nf_cal_buffer[%d][%d] = %d\n", i, reading, (int)sort[i]); + } + for (i = 0; i < hist_len - 1; i++) { + for (j = 1; j < hist_len - i; j++) { + if (sort[j] > sort[j - 1]) { + nfval = sort[j]; + sort[j] = sort[j - 1]; + sort[j - 1] = nfval; + } + } + } + nfval = sort[(hist_len - 1) >> 1]; + + return nfval; +} + +static int16_t ar9300_limit_nf_range(struct ath_hal *ah, int16_t nf) +{ + if (nf < AH_PRIVATE(ah)->nfp->min) { + return AH_PRIVATE(ah)->nfp->nominal; + } else if (nf > AH_PRIVATE(ah)->nfp->max) { + return AH_PRIVATE(ah)->nfp->max; + } + return nf; +} + +#ifndef ATH_NF_PER_CHAN +inline static void +ar9300_reset_nf_hist_buff(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) +{ + HAL_CHAN_NFCAL_HIST *h = &ichan->nf_cal_hist; + HAL_NFCAL_HIST_FULL *home = &AH_PRIVATE(ah)->nf_cal_hist; + int i; + + /* + * Copy the value for the channel in question into the home-channel + * NF history buffer. The channel NF is probably a value filled in by + * a prior background channel scan, but if no scan has been done then + * it is the nominal noise floor filled in by ath_hal_init_NF_buffer + * for this chip and the channel's band. + * Replicate this channel NF into all entries of the home-channel NF + * history buffer. + * If the channel NF was filled in by a channel scan, it has not had + * bounds limits applied to it yet - do so now. It is important to + * apply bounds limits to the priv_nf value that gets loaded into the + * WLAN chip's min_cca_pwr register field. It is also necessary to + * apply bounds limits to the nf_cal_buffer[] elements. Since we are + * replicating a single NF reading into all nf_cal_buffer elements, + * if the single reading were above the CW_INT threshold, the CW_INT + * check in ar9300_get_nf would immediately conclude that CW interference + * is present, even though we're not supposed to set CW_INT unless + * NF values are _consistently_ above the CW_INT threshold. + * Applying the bounds limits to the nf_cal_buffer contents fixes this + * problem. + */ + for (i = 0; i < NUM_NF_READINGS; i ++) { + int j; + int16_t nf; + /* + * No need to set curr_index, since it already has a value in + * the range [0..HAL_NF_CAL_HIST_LEN_FULL), and all nf_cal_buffer + * values will be the same. + */ + nf = ar9300_limit_nf_range(ah, h->nf_cal_buffer[0][i]); + for (j = 0; j < HAL_NF_CAL_HIST_LEN_FULL; j++) { + home->nf_cal_buffer[j][i] = nf; + } + AH_PRIVATE(ah)->nf_cal_hist.base.priv_nf[i] = nf; + } +} +#endif + +/* + * Update the noise floor buffer as a ring buffer + */ +static int16_t +ar9300_update_nf_hist_buff(struct ath_hal *ah, HAL_NFCAL_HIST_FULL *h, + int16_t *nfarray, int hist_len) +{ + int i, nr; + int16_t nf_no_lim_chain0; + + nf_no_lim_chain0 = ar9300_get_nf_hist_mid(ah, h, 0, hist_len); + + HALDEBUG(ah, HAL_DEBUG_NF_CAL, "%s[%d] BEFORE\n", __func__, __LINE__); + for (nr = 0; nr < HAL_NF_CAL_HIST_LEN_FULL; nr++) { + for (i = 0; i < NUM_NF_READINGS; i++) { + HALDEBUG(ah, HAL_DEBUG_NF_CAL, + "nf_cal_buffer[%d][%d] = %d\n", + nr, i, (int)h->nf_cal_buffer[nr][i]); + } + } + for (i = 0; i < NUM_NF_READINGS; i++) { + h->nf_cal_buffer[h->base.curr_index][i] = nfarray[i]; + h->base.priv_nf[i] = ar9300_limit_nf_range( + ah, ar9300_get_nf_hist_mid(ah, h, i, hist_len)); + } + HALDEBUG(ah, HAL_DEBUG_NF_CAL, "%s[%d] AFTER\n", __func__, __LINE__); + for (nr = 0; nr < HAL_NF_CAL_HIST_LEN_FULL; nr++) { + for (i = 0; i < NUM_NF_READINGS; i++) { + HALDEBUG(ah, HAL_DEBUG_NF_CAL, + "nf_cal_buffer[%d][%d] = %d\n", + nr, i, (int)h->nf_cal_buffer[nr][i]); + } + } + + if (++h->base.curr_index >= hist_len) { + h->base.curr_index = 0; + } + + return nf_no_lim_chain0; +} + +#if UNUSED +static HAL_BOOL +get_noise_floor_thresh(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *chan, + int16_t *nft) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + + switch (chan->channel_flags & CHANNEL_ALL_NOTURBO) { + case CHANNEL_A: + case CHANNEL_A_HT20: + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + *nft = (int8_t)ar9300_eeprom_get(ahp, EEP_NFTHRESH_5); + break; + case CHANNEL_B: + case CHANNEL_G: + case CHANNEL_G_HT20: + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + *nft = (int8_t)ar9300_eeprom_get(ahp, EEP_NFTHRESH_2); + break; + default: + HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel flags 0x%x\n", + __func__, chan->channel_flags); + return AH_FALSE; + } + return AH_TRUE; +} +#endif + +/* + * Read the NF and check it against the noise floor threshhold + */ +#define IS(_c, _f) (((_c)->channel_flags & _f) || 0) +static int +ar9300_store_new_nf(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, int is_scan) +{ + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + int nf_hist_len; + int16_t nf_no_lim; + int16_t nfarray[NUM_NF_READINGS] = {0}; + HAL_NFCAL_HIST_FULL *h; + int is_2g = 0; + + if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { + u_int32_t tsf32, nf_cal_dur_tsf; + /* + * The reason the NF calibration did not complete may just be that + * not enough time has passed since the NF calibration was started, + * because under certain conditions (when first moving to a new + * channel) the NF calibration may be checked very repeatedly. + * Or, there may be CW interference keeping the NF calibration + * from completing. Check the delta time between when the NF + * calibration was started and now to see whether the NF calibration + * should have already completed (but hasn't, probably due to CW + * interference), or hasn't had enough time to finish yet. + */ + /* + * AH_NF_CAL_DUR_MAX_TSF - A conservative maximum time that the + * HW should need to finish a NF calibration. If the HW + * does not complete a NF calibration within this time period, + * there must be a problem - probably CW interference. + * AH_NF_CAL_PERIOD_MAX_TSF - A conservative maximum time between + * check of the HW's NF calibration being finished. + * If the difference between the current TSF and the TSF + * recorded when the NF calibration started is larger than this + * value, the TSF must have been reset. + * In general, we expect the TSF to only be reset during + * regular operation for STAs, not for APs. However, an + * AP's TSF could be reset when joining an IBSS. + * There's an outside chance that this could result in the + * CW_INT flag being erroneously set, if the TSF adjustment + * is smaller than AH_NF_CAL_PERIOD_MAX_TSF but larger than + * AH_NF_CAL_DUR_TSF. However, even if this does happen, + * it shouldn't matter, as the IBSS case shouldn't be + * concerned about CW_INT. + */ + /* AH_NF_CAL_DUR_TSF - 90 sec in usec units */ + #define AH_NF_CAL_DUR_TSF (90 * 1000 * 1000) + /* AH_NF_CAL_PERIOD_MAX_TSF - 180 sec in usec units */ + #define AH_NF_CAL_PERIOD_MAX_TSF (180 * 1000 * 1000) + /* wraparound handled by using unsigned values */ + tsf32 = ar9300_get_tsf32(ah); + nf_cal_dur_tsf = tsf32 - AH9300(ah)->nf_tsf32; + if (nf_cal_dur_tsf > AH_NF_CAL_PERIOD_MAX_TSF) { + /* + * The TSF must have gotten reset during the NF cal - + * just reset the NF TSF timestamp, so the next time + * this function is called, the timestamp comparison + * will be valid. + */ + AH9300(ah)->nf_tsf32 = tsf32; + } else if (nf_cal_dur_tsf > AH_NF_CAL_DUR_TSF) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: NF did not complete in calibration window\n", __func__); + /* the NF incompletion is probably due to CW interference */ + chan->channel_flags |= CHANNEL_CW_INT; + } + return 0; /* HW's NF measurement not finished */ + } + HALDEBUG(ah, HAL_DEBUG_NF_CAL, + "%s[%d] chan %d\n", __func__, __LINE__, chan->channel); + is_2g = IS(chan, CHANNEL_2GHZ); + ar9300_upload_noise_floor(ah, is_2g, nfarray); + + /* Update the NF buffer for each chain masked by chainmask */ +#ifdef ATH_NF_PER_CHAN + h = &chan->nf_cal_hist; + nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; +#else + if (is_scan) { + /* + * This channel's NF cal info is just a HAL_NFCAL_HIST_SMALL struct + * rather than a HAL_NFCAL_HIST_FULL struct. + * As long as we only use the first history element of nf_cal_buffer + * (nf_cal_buffer[0][0:NUM_NF_READINGS-1]), we can use + * HAL_NFCAL_HIST_SMALL and HAL_NFCAL_HIST_FULL interchangeably. + */ + h = (HAL_NFCAL_HIST_FULL *) &chan->nf_cal_hist; + nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; + } else { + h = &AH_PRIVATE(ah)->nf_cal_hist; + nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; + } +#endif + + /* + * nf_no_lim = median value from NF history buffer without bounds limits, + * priv_nf = median value from NF history buffer with bounds limits. + */ + nf_no_lim = ar9300_update_nf_hist_buff(ah, h, nfarray, nf_hist_len); + chan->raw_noise_floor = h->base.priv_nf[0]; + + /* check if there is interference */ + chan->channel_flags &= (~CHANNEL_CW_INT); + /* + * Use AR9300_EMULATION to check for emulation purpose as PCIE Device ID + * 0xABCD is recognized as valid Osprey as WAR in some EVs. + */ + if (nf_no_lim > ahpriv->nfp->nominal + ahpriv->nf_cw_int_delta) { + /* + * Since this CW interference check is being applied to the + * median element of the NF history buffer, this indicates that + * the CW interference is persistent. A single high NF reading + * will not show up in the median, and thus will not cause the + * CW_INT flag to be set. + */ + HALDEBUG(ah, HAL_DEBUG_NF_CAL, + "%s: NF Cal: CW interferer detected through NF: %d\n", + __func__, nf_no_lim); + chan->channel_flags |= CHANNEL_CW_INT; + } + return 1; /* HW's NF measurement finished */ +} +#undef IS + +static inline void +ar9300_get_delta_slope_values(struct ath_hal *ah, u_int32_t coef_scaled, + u_int32_t *coef_mantissa, u_int32_t *coef_exponent) +{ + u_int32_t coef_exp, coef_man; + + /* + * ALGO -> coef_exp = 14-floor(log2(coef)); + * floor(log2(x)) is the highest set bit position + */ + for (coef_exp = 31; coef_exp > 0; coef_exp--) { + if ((coef_scaled >> coef_exp) & 0x1) { + break; + } + } + /* A coef_exp of 0 is a legal bit position but an unexpected coef_exp */ + HALASSERT(coef_exp); + coef_exp = 14 - (coef_exp - COEF_SCALE_S); + + + /* + * ALGO -> coef_man = floor(coef* 2^coef_exp+0.5); + * The coefficient is already shifted up for scaling + */ + coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); + + *coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp); + *coef_exponent = coef_exp - 16; +} + +#define MAX_ANALOG_START 319 /* XXX */ + +/* + * Delta slope coefficient computation. + * Required for OFDM operation. + */ +static void +ar9300_set_delta_slope(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan) +{ + u_int32_t coef_scaled, ds_coef_exp, ds_coef_man; + u_int32_t fclk = COEFF; /* clock * 2.5 */ + + u_int32_t clock_mhz_scaled = 0x1000000 * fclk; + CHAN_CENTERS centers; + + /* + * half and quarter rate can divide the scaled clock by 2 or 4 + * scale for selected channel bandwidth + */ + if (IS_CHAN_HALF_RATE(chan)) { + clock_mhz_scaled = clock_mhz_scaled >> 1; + } else if (IS_CHAN_QUARTER_RATE(chan)) { + clock_mhz_scaled = clock_mhz_scaled >> 2; + } + + /* + * ALGO -> coef = 1e8/fcarrier*fclock/40; + * scaled coef to provide precision for this floating calculation + */ + ar9300_get_channel_centers(ah, chan, ¢ers); + coef_scaled = clock_mhz_scaled / centers.synth_center; + + ar9300_get_delta_slope_values(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); + + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); + + /* + * For Short GI, + * scaled coeff is 9/10 that of normal coeff + */ + coef_scaled = (9 * coef_scaled) / 10; + + ar9300_get_delta_slope_values(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); + + /* for short gi */ + OS_REG_RMW_FIELD(ah, AR_PHY_SGI_DELTA, AR_PHY_SGI_DSC_MAN, ds_coef_man); + OS_REG_RMW_FIELD(ah, AR_PHY_SGI_DELTA, AR_PHY_SGI_DSC_EXP, ds_coef_exp); +} + +#define IS(_c, _f) (((_c)->channel_flags & _f) || 0) + +static inline HAL_CHANNEL_INTERNAL* +ar9300_check_chan(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + if ((IS(chan, CHANNEL_2GHZ) ^ IS(chan, CHANNEL_5GHZ)) == 0) { + HALDEBUG(ah, HAL_DEBUG_CHANNEL, + "%s: invalid channel %u/0x%x; not marked as 2GHz or 5GHz\n", + __func__, chan->channel, chan->channel_flags); + return AH_NULL; + } + + if ((IS(chan, CHANNEL_OFDM) ^ IS(chan, CHANNEL_CCK) ^ + IS(chan, CHANNEL_HT20) ^ IS(chan, CHANNEL_HT40PLUS) ^ + IS(chan, CHANNEL_HT40MINUS)) == 0) + { + HALDEBUG(ah, HAL_DEBUG_CHANNEL, + "%s: invalid channel %u/0x%x; not marked as " + "OFDM or CCK or HT20 or HT40PLUS or HT40MINUS\n", + __func__, chan->channel, chan->channel_flags); + return AH_NULL; + } + + return (ath_hal_checkchannel(ah, chan)); +} +#undef IS + +static void +ar9300_set_11n_regs(struct ath_hal *ah, HAL_CHANNEL *chan, + HAL_HT_MACMODE macmode) +{ + u_int32_t phymode; + struct ath_hal_9300 *ahp = AH9300(ah); + u_int32_t enable_dac_fifo; + + /* XXX */ + enable_dac_fifo = + OS_REG_READ(ah, AR_PHY_GEN_CTRL) & AR_PHY_GC_ENABLE_DAC_FIFO; + + /* Enable 11n HT, 20 MHz */ + phymode = + AR_PHY_GC_HT_EN | AR_PHY_GC_SINGLE_HT_LTF1 | AR_PHY_GC_SHORT_GI_40 + | enable_dac_fifo; + /* Configure baseband for dynamic 20/40 operation */ + if (IS_CHAN_HT40(chan)) { + phymode |= AR_PHY_GC_DYN2040_EN; + /* Configure control (primary) channel at +-10MHz */ + if (chan->channel_flags & CHANNEL_HT40PLUS) { + phymode |= AR_PHY_GC_DYN2040_PRI_CH; + } + + /* Configure 20/25 spacing */ + if (ahp->ah_ext_prot_spacing == HAL_HT_EXTPROTSPACING_25) { + phymode |= AR_PHY_GC_DYN2040_EXT_CH; + } + } + + /* make sure we preserve INI settings */ + phymode |= OS_REG_READ(ah, AR_PHY_GEN_CTRL); + + /* EV 62881/64991 - turn off Green Field detection for Maverick STA beta */ + phymode &= ~AR_PHY_GC_GF_DETECT_EN; + + OS_REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode); + + /* Set IFS timing for half/quarter rates */ + if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) { + u_int32_t modeselect = OS_REG_READ(ah, AR_PHY_MODE); + + if (IS_CHAN_HALF_RATE(chan)) { + modeselect |= AR_PHY_MS_HALF_RATE; + } else if (IS_CHAN_QUARTER_RATE(chan)) { + modeselect |= AR_PHY_MS_QUARTER_RATE; + } + OS_REG_WRITE(ah, AR_PHY_MODE, modeselect); + + ar9300_set_ifs_timing(ah, chan); + OS_REG_RMW_FIELD( + ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW, 0x3); + } + + /* Configure MAC for 20/40 operation */ + ar9300_set_11n_mac2040(ah, macmode); + + /* global transmit timeout (25 TUs default)*/ + /* XXX - put this elsewhere??? */ + OS_REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); + + /* carrier sense timeout */ + OS_REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); +} + +/* + * Spur mitigation for MRC CCK + */ +static void +ar9300_spur_mitigate_mrc_cck(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + int i; + /* spur_freq_for_osprey - hardcoded by Systems team for now. */ + u_int32_t spur_freq_for_osprey[4] = { 2420, 2440, 2464, 2480 }; + u_int32_t spur_freq_for_jupiter[2] = { 2440, 2464}; + int cur_bb_spur, negative = 0, cck_spur_freq; + u_int8_t* spur_fbin_ptr = NULL; + int synth_freq; + int range = 10; + int max_spurcounts = OSPREY_EEPROM_MODAL_SPURS; + + /* + * Need to verify range +/- 10 MHz in control channel, otherwise spur + * is out-of-band and can be ignored. + */ + if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || + AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { + spur_fbin_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 1); + if (spur_fbin_ptr[0] == 0) { + return; /* No spur in the mode */ + } + if (IS_CHAN_HT40(chan)) { + range = 19; + if (OS_REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) + == 0x0) + { + synth_freq = chan->channel + 10; + } else { + synth_freq = chan->channel - 10; + } + } else { + range = 10; + synth_freq = chan->channel; + } + } else if(AR_SREV_JUPITER(ah)) { + range = 5; + max_spurcounts = 2; /* Hardcoded by Jupiter Systems team for now. */ + synth_freq = chan->channel; + } else { + range = 10; + max_spurcounts = 4; /* Hardcoded by Osprey Systems team for now. */ + synth_freq = chan->channel; + } + + for (i = 0; i < max_spurcounts; i++) { + negative = 0; + + if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || + AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { + cur_bb_spur = + FBIN2FREQ(spur_fbin_ptr[i], HAL_FREQ_BAND_2GHZ) - synth_freq; + } else if(AR_SREV_JUPITER(ah)) { + cur_bb_spur = spur_freq_for_jupiter[i] - synth_freq; + } else { + cur_bb_spur = spur_freq_for_osprey[i] - synth_freq; + } + + if (cur_bb_spur < 0) { + negative = 1; + cur_bb_spur = -cur_bb_spur; + } + if (cur_bb_spur < range) { + cck_spur_freq = (int)((cur_bb_spur << 19) / 11); + if (negative == 1) { + cck_spur_freq = -cck_spur_freq; + } + cck_spur_freq = cck_spur_freq & 0xfffff; + /*OS_REG_WRITE_field(ah, BB_agc_control.ycok_max, 0x7);*/ + OS_REG_RMW_FIELD(ah, + AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x7); + /*OS_REG_WRITE_field(ah, BB_cck_spur_mit.spur_rssi_thr, 0x7f);*/ + OS_REG_RMW_FIELD(ah, + AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_SPUR_RSSI_THR, 0x7f); + /*OS_REG_WRITE(ah, BB_cck_spur_mit.spur_filter_type, 0x2);*/ + OS_REG_RMW_FIELD(ah, + AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_SPUR_FILTER_TYPE, 0x2); + /*OS_REG_WRITE(ah, BB_cck_spur_mit.use_cck_spur_mit, 0x1);*/ + OS_REG_RMW_FIELD(ah, + AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT, 0x1); + /*OS_REG_WRITE(ah, BB_cck_spur_mit.cck_spur_freq, cck_spur_freq);*/ + OS_REG_RMW_FIELD(ah, + AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, + cck_spur_freq); + return; + } + } + + /*OS_REG_WRITE(ah, BB_agc_control.ycok_max, 0x5);*/ + OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x5); + /*OS_REG_WRITE(ah, BB_cck_spur_mit.use_cck_spur_mit, 0x0);*/ + OS_REG_RMW_FIELD(ah, + AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT, 0x0); + /*OS_REG_WRITE(ah, BB_cck_spur_mit.cck_spur_freq, 0x0);*/ + OS_REG_RMW_FIELD(ah, + AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, 0x0); +} + +/* Spur mitigation for OFDM */ +static void +ar9300_spur_mitigate_ofdm(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + int synth_freq; + int range = 10; + int freq_offset = 0; + int spur_freq_sd = 0; + int spur_subchannel_sd = 0; + int spur_delta_phase = 0; + int mask_index = 0; + int i; + int mode; + u_int8_t* spur_chans_ptr; + + if (IS_CHAN_5GHZ(chan)) { + spur_chans_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 0); + mode = 0; + } else { + spur_chans_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 1); + mode = 1; + } + + if (IS_CHAN_HT40(chan)) { + range = 19; + if (OS_REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) + == 0x0) + { + synth_freq = chan->channel - 10; + } else { + synth_freq = chan->channel + 10; + } + } else { + range = 10; + synth_freq = chan->channel; + } + + /* Clean all spur register fields */ + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER, 0); + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_FREQ_SD, 0); + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_DELTA_PHASE, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR, 0); + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI, 0); + OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT, 0); + OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_MASK_PPM, 0); + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_PILOT_MASK, 0); + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_CHAN_MASK, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, 0); + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0); + OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0); + + i = 0; + while (spur_chans_ptr[i] && i < 5) { + freq_offset = FBIN2FREQ(spur_chans_ptr[i], mode) - synth_freq; + if (abs(freq_offset) < range) { + /* + printf( + "Spur Mitigation for OFDM: Synth Frequency = %d, " + "Spur Frequency = %d\n", + synth_freq, FBIN2FREQ(spur_chans_ptr[i], mode)); + */ + if (IS_CHAN_HT40(chan)) { + if (freq_offset < 0) { + if (OS_REG_READ_FIELD( + ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) + { + spur_subchannel_sd = 1; + } else { + spur_subchannel_sd = 0; + } + spur_freq_sd = ((freq_offset + 10) << 9) / 11; + } else { + if (OS_REG_READ_FIELD(ah, + AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) + { + spur_subchannel_sd = 0; + } else { + spur_subchannel_sd = 1; + } + spur_freq_sd = ((freq_offset - 10) << 9) / 11; + } + spur_delta_phase = (freq_offset << 17) / 5; + } else { + spur_subchannel_sd = 0; + spur_freq_sd = (freq_offset << 9) / 11; + spur_delta_phase = (freq_offset << 18) / 5; + } + spur_freq_sd = spur_freq_sd & 0x3ff; + spur_delta_phase = spur_delta_phase & 0xfffff; + /* + printf( + "spur_subchannel_sd = %d, spur_freq_sd = 0x%x, " + "spur_delta_phase = 0x%x\n", spur_subchannel_sd, + spur_freq_sd, spur_delta_phase); + */ + + /* OFDM Spur mitigation */ + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER, 0x1); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_FREQ_SD, spur_freq_sd); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_DELTA_PHASE, + spur_delta_phase); + OS_REG_RMW_FIELD(ah, + AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD, + spur_subchannel_sd); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC, 0x1); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR, + 0x1); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI, 0x1); + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH, 34); + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI, 1); + + /* + * Do not subtract spur power from noise floor for wasp. + * This causes the maximum client test (on Veriwave) to fail + * when run on spur channel (2464 MHz). + * Refer to ev#82746 and ev#82744. + */ + if (!AR_SREV_WASP(ah) && (OS_REG_READ_FIELD(ah, AR_PHY_MODE, + AR_PHY_MODE_DYNAMIC) == 0x1)) { + OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, + AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT, 1); + } + + mask_index = (freq_offset << 4) / 5; + if (mask_index < 0) { + mask_index = mask_index - 1; + } + mask_index = mask_index & 0x7f; + /*printf("Bin 0x%x\n", mask_index);*/ + + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_MASK_PPM, 0x1); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_PILOT_MASK, 0x1); + OS_REG_RMW_FIELD(ah, + AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_CHAN_MASK, 0x1); + OS_REG_RMW_FIELD(ah, + AR_PHY_PILOT_SPUR_MASK, + AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, mask_index); + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, + mask_index); + OS_REG_RMW_FIELD(ah, + AR_PHY_CHAN_SPUR_MASK, + AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, mask_index); + OS_REG_RMW_FIELD(ah, + AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, + 0xc); + OS_REG_RMW_FIELD(ah, + AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, + 0xc); + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0xa0); + OS_REG_RMW_FIELD(ah, + AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0xff); + /* + printf("BB_timing_control_4 = 0x%x\n", + OS_REG_READ(ah, AR_PHY_TIMING4)); + printf("BB_timing_control_11 = 0x%x\n", + OS_REG_READ(ah, AR_PHY_TIMING11)); + printf("BB_ext_chan_scorr_thr = 0x%x\n", + OS_REG_READ(ah, AR_PHY_SFCORR_EXT)); + printf("BB_spur_mask_controls = 0x%x\n", + OS_REG_READ(ah, AR_PHY_SPUR_REG)); + printf("BB_pilot_spur_mask = 0x%x\n", + OS_REG_READ(ah, AR_PHY_PILOT_SPUR_MASK)); + printf("BB_chan_spur_mask = 0x%x\n", + OS_REG_READ(ah, AR_PHY_CHAN_SPUR_MASK)); + printf("BB_vit_spur_mask_A = 0x%x\n", + OS_REG_READ(ah, AR_PHY_SPUR_MASK_A)); + */ + break; + } + i++; + } +} + + +/* + * Convert to baseband spur frequency given input channel frequency + * and compute register settings below. + */ +static void +ar9300_spur_mitigate(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + ar9300_spur_mitigate_ofdm(ah, chan); + ar9300_spur_mitigate_mrc_cck(ah, chan); +} + + +/************************************************************** + * ar9300_channel_change + * Assumes caller wants to change channel, and not reset. + */ +static inline HAL_BOOL +ar9300_channel_change(struct ath_hal *ah, HAL_CHANNEL *chan, + HAL_CHANNEL_INTERNAL *ichan, HAL_HT_MACMODE macmode) +{ + + u_int32_t synth_delay, qnum; + struct ath_hal_9300 *ahp = AH9300(ah); + + /* TX must be stopped by now */ + for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { + if (ar9300_num_tx_pending(ah, qnum)) { + HALDEBUG(ah, HAL_DEBUG_QUEUE, + "%s: Transmit frames pending on queue %d\n", __func__, qnum); + HALASSERT(0); + return AH_FALSE; + } + } + + + /* + * Kill last Baseband Rx Frame - Request analog bus grant + */ + OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN); + if (!ath_hal_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN, + AR_PHY_RFBUS_GRANT_EN, AH_WAIT_TIMEOUT)) + { + HALDEBUG(ah, HAL_DEBUG_PHY_IO, + "%s: Could not kill baseband RX\n", __func__); + return AH_FALSE; + } + + + /* Setup 11n MAC/Phy mode registers */ + ar9300_set_11n_regs(ah, chan, macmode); + + /* + * Change the synth + */ + if (!ahp->ah_rf_hal.set_channel(ah, ichan)) { + HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: failed to set channel\n", __func__); + return AH_FALSE; + } + + /* + * Some registers get reinitialized during ATH_INI_POST INI programming. + */ + ar9300_init_user_settings(ah); + + /* + * Setup the transmit power values. + * + * After the public to private hal channel mapping, ichan contains the + * valid regulatory power value. + * ath_hal_getctl and ath_hal_getantennaallowed look up ichan from chan. + */ + if (ar9300_eeprom_set_transmit_power( + ah, &ahp->ah_eeprom, ichan, ath_hal_getctl(ah, chan), + ath_hal_getantennaallowed(ah, chan), + ath_hal_get_twice_max_regpower(AH_PRIVATE(ah), ichan, chan), + AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_power_limit)) != HAL_OK) + { + HALDEBUG(ah, HAL_DEBUG_EEPROM, + "%s: error init'ing transmit power\n", __func__); + return AH_FALSE; + } + + /* + * Release the RFBus Grant. + */ + OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); + + /* + * Write spur immunity and delta slope for OFDM enabled modes (A, G, Turbo) + */ + if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) { + ar9300_set_delta_slope(ah, ichan); + } else { + /* Set to Ini default */ + OS_REG_WRITE(ah, AR_PHY_TIMING3, 0x9c0a9f6b); + OS_REG_WRITE(ah, AR_PHY_SGI_DELTA, 0x00046384); + } + + ar9300_spur_mitigate(ah, chan); + + + /* + * Wait for the frequency synth to settle (synth goes on via PHY_ACTIVE_EN). + * Read the phy active delay register. Value is in 100ns increments. + */ + synth_delay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; + if (IS_CHAN_CCK(chan)) { + synth_delay = (4 * synth_delay) / 22; + } else { + synth_delay /= 10; + } + + OS_DELAY(synth_delay + BASE_ACTIVATE_DELAY); + + /* + * Do calibration. + */ + + return AH_TRUE; +} + +void +ar9300_set_operating_mode(struct ath_hal *ah, int opmode) +{ + u_int32_t val; + + val = OS_REG_READ(ah, AR_STA_ID1); + val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC); + switch (opmode) { + case HAL_M_HOSTAP: + OS_REG_WRITE(ah, AR_STA_ID1, + val | AR_STA_ID1_STA_AP | AR_STA_ID1_KSRCH_MODE); + OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); + break; + case HAL_M_IBSS: + OS_REG_WRITE(ah, AR_STA_ID1, + val | AR_STA_ID1_ADHOC | AR_STA_ID1_KSRCH_MODE); + OS_REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); + break; + case HAL_M_STA: + case HAL_M_MONITOR: + OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE); + break; + } +} + +/* XXX need the logic for Osprey */ +inline void +ar9300_init_pll(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + u_int32_t pll; + u_int8_t clk_25mhz = AH9300(ah)->clk_25mhz; + + if (AR_SREV_HORNET(ah)) { + if (clk_25mhz) { + /* Hornet uses PLL_CONTROL_2. Xtal is 25MHz for Hornet. + * REFDIV set to 0x1. + * $xtal_freq = 25; + * $PLL2_div = (704/$xtal_freq); # 176 * 4 = 704. + * MAC and BB run at 176 MHz. + * $PLL2_divint = int($PLL2_div); + * $PLL2_divfrac = $PLL2_div - $PLL2_divint; + * $PLL2_divfrac = int($PLL2_divfrac * 0x4000); # 2^14 + * $PLL2_Val = ($PLL2_divint & 0x3f) << 19 | (0x1) << 14 | + * $PLL2_divfrac & 0x3fff; + * Therefore, $PLL2_Val = 0xe04a3d + */ +#define DPLL2_KD_VAL 0x1D +#define DPLL2_KI_VAL 0x06 +#define DPLL3_PHASE_SHIFT_VAL 0x1 + + /* Rewrite DDR PLL2 and PLL3 */ + /* program DDR PLL ki and kd value, ki=0x6, kd=0x1d */ + OS_REG_WRITE(ah, AR_HORNET_CH0_DDR_DPLL2, 0x18e82f01); + + /* program DDR PLL phase_shift to 0x1 */ + OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_DDR_DPLL3, + AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); + + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); + OS_DELAY(1000); + + /* program refdiv, nint, frac to RTC register */ + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL2, 0xe04a3d); + + /* program BB PLL ki and kd value, ki=0x6, kd=0x1d */ + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_KD, DPLL2_KD_VAL); + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_KI, DPLL2_KI_VAL); + + /* program BB PLL phase_shift to 0x1 */ + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, + AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); + } else { /* 40MHz */ +#undef DPLL2_KD_VAL +#undef DPLL2_KI_VAL +#define DPLL2_KD_VAL 0x3D +#define DPLL2_KI_VAL 0x06 + /* Rewrite DDR PLL2 and PLL3 */ + /* program DDR PLL ki and kd value, ki=0x6, kd=0x3d */ + OS_REG_WRITE(ah, AR_HORNET_CH0_DDR_DPLL2, 0x19e82f01); + + /* program DDR PLL phase_shift to 0x1 */ + OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_DDR_DPLL3, + AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); + + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); + OS_DELAY(1000); + + /* program refdiv, nint, frac to RTC register */ + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL2, 0x886666); + + /* program BB PLL ki and kd value, ki=0x6, kd=0x3d */ + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_KD, DPLL2_KD_VAL); + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_KI, DPLL2_KI_VAL); + + /* program BB PLL phase_shift to 0x1 */ + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, + AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); + } + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); + OS_DELAY(1000); + } else if (AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_PLL_PWD, 0x1); + + /* program BB PLL ki and kd value, ki=0x4, kd=0x40 */ + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_KD, 0x40); + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_KI, 0x4); + + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, + AR_PHY_BB_DPLL1_REFDIV, 0x5); + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, + AR_PHY_BB_DPLL1_NINI, 0x58); + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, + AR_PHY_BB_DPLL1_NFRAC, 0x0); + + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_OUTDIV, 0x1); + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_LOCAL_PLL, 0x1); + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_EN_NEGTRIG, 0x1); + + /* program BB PLL phase_shift to 0x6 */ + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, + AR_PHY_BB_DPLL3_PHASE_SHIFT, 0x6); + + OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, + AR_PHY_BB_DPLL2_PLL_PWD, 0x0); + OS_DELAY(1000); + + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); + OS_DELAY(1000); + } else if (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { +#define SRIF_PLL 1 + u_int32_t regdata, pll2_divint, pll2_divfrac; + +#ifndef SRIF_PLL + u_int32_t pll2_clkmode; +#endif + +#ifdef SRIF_PLL + u_int32_t refdiv; +#endif + if (clk_25mhz) { +#ifndef SRIF_PLL + pll2_divint = 0x1c; + pll2_divfrac = 0xa3d7; +#else + pll2_divint = 0x54; + pll2_divfrac = 0x1eb85; + refdiv = 3; +#endif + } else { +#ifndef SRIF_PLL + pll2_divint = 0x11; + pll2_divfrac = 0x26666; +#else + if (AR_SREV_WASP(ah)) { + pll2_divint = 88; + pll2_divfrac = 0; + refdiv = 5; + } else { + pll2_divint = 0x11; + pll2_divfrac = 0x26666; + refdiv = 1; + } +#endif + } +#ifndef SRIF_PLL + pll2_clkmode = 0x3d; +#endif + /* PLL programming through SRIF Local Mode */ + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); /* Bypass mode */ + OS_DELAY(1000); + do { + regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); + regdata = regdata | (0x1 << 16); + OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); /* PWD_PLL set to 1 */ + OS_DELAY(100); + /* override int, frac, refdiv */ +#ifndef SRIF_PLL + OS_REG_WRITE(ah, AR_PHY_PLL_CONTROL, + ((1 << 27) | (pll2_divint << 18) | pll2_divfrac)); +#else + OS_REG_WRITE(ah, AR_PHY_PLL_CONTROL, + ((refdiv << 27) | (pll2_divint << 18) | pll2_divfrac)); +#endif + OS_DELAY(100); + regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); +#ifndef SRIF_PLL + regdata = (regdata & 0x80071fff) | + (0x1 << 30) | (0x1 << 13) | (0x6 << 26) | (pll2_clkmode << 19); +#else + if (AR_SREV_WASP(ah)) { + regdata = (regdata & 0x80071fff) | + (0x1 << 30) | (0x1 << 13) | (0x4 << 26) | (0x18 << 19); + } else { + regdata = (regdata & 0x80071fff) | + (0x3 << 30) | (0x1 << 13) | (0x4 << 26) | (0x60 << 19); + } +#endif + /* Ki, Kd, Local PLL, Outdiv */ + OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); + regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); + regdata = (regdata & 0xfffeffff); + OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); /* PWD_PLL set to 0 */ + OS_DELAY(1000); + if (AR_SREV_WASP(ah)) { + /* clear do measure */ + regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); + regdata &= ~(1 << 30); + OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); + OS_DELAY(100); + + /* set do measure */ + regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); + regdata |= (1 << 30); + OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); + + /* wait for measure done */ + do { + regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL4); + } while ((regdata & (1 << 3)) == 0); + + /* clear do measure */ + regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); + regdata &= ~(1 << 30); + OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); + + /* get measure sqsum dvc */ + regdata = (OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3) & 0x007FFFF8) >> 3; + } else { + break; + } + } while (regdata >= 0x40000); + + /* Remove from Bypass mode */ + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); + OS_DELAY(1000); + } else { + pll = SM(0x5, AR_RTC_PLL_REFDIV); + + /* Supposedly not needed on Osprey */ +#if 0 + if (chan && IS_CHAN_HALF_RATE(chan)) { + pll |= SM(0x1, AR_RTC_PLL_CLKSEL); + } else if (chan && IS_CHAN_QUARTER_RATE(chan)) { + pll |= SM(0x2, AR_RTC_PLL_CLKSEL); + } +#endif + if (chan && IS_CHAN_5GHZ(chan)) { + pll |= SM(0x28, AR_RTC_PLL_DIV); + /* + * When doing fast clock, set PLL to 0x142c + */ + if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { + pll = 0x142c; + } + } else { + pll |= SM(0x2c, AR_RTC_PLL_DIV); + } + + OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); + } + + /* TODO: + * For multi-band owl, switch between bands by reiniting the PLL. + */ + OS_DELAY(RTC_PLL_SETTLE_DELAY); + + OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, + AR_RTC_FORCE_DERIVED_CLK | AR_RTC_PCIE_RST_PWDN_EN); + + if (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { + if (clk_25mhz) { + OS_REG_WRITE(ah, + AR_RTC_DERIVED_RTC_CLK, (0x17c << 1)); /* 32KHz sleep clk */ + OS_REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7); + OS_REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae); + } else { + OS_REG_WRITE(ah, + AR_RTC_DERIVED_RTC_CLK, (0x261 << 1)); /* 32KHz sleep clk */ + OS_REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400); + OS_REG_WRITE(ah, AR_SLP32_INC, 0x0001e800); + } + OS_DELAY(100); + } +} + +static inline HAL_BOOL +ar9300_set_reset(struct ath_hal *ah, int type) +{ + u_int32_t rst_flags; + u_int32_t tmp_reg; + + HALASSERT(type == HAL_RESET_WARM || type == HAL_RESET_COLD); + + /* + * RTC Force wake should be done before resetting the MAC. + * MDK/ART does it that way. + */ + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); + OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ + OS_REG_WRITE(ah, + AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); + + /* Reset AHB */ + /* Bug26871 */ + tmp_reg = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE)); + if (AR_SREV_WASP(ah)) { + if (tmp_reg & (AR9340_INTR_SYNC_LOCAL_TIMEOUT)) { + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), 0); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_HOSTIF); + } + } else { + if (tmp_reg & (AR9300_INTR_SYNC_LOCAL_TIMEOUT | AR9300_INTR_SYNC_RADM_CPL_TIMEOUT)) { + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), 0); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_HOSTIF); + } + else { + /* NO AR_RC_AHB in Osprey */ + /*OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_AHB);*/ + } + } + + rst_flags = AR_RTC_RC_MAC_WARM; + if (type == HAL_RESET_COLD) { + rst_flags |= AR_RTC_RC_MAC_COLD; + } + +#ifdef AH_SUPPORT_HORNET + /* Hornet WAR: trigger SoC to reset WMAC if ... + * (1) doing cold reset. Ref: EV 69254 + * (2) beacon pending. Ref: EV 70983 + */ + if (AR_SREV_HORNET(ah) && + (ar9300_num_tx_pending( + ah, AH_PRIVATE(ah)->ah_caps.hal_total_queues - 1) != 0 || + type == HAL_RESET_COLD)) + { + u_int32_t time_out; +#define AR_SOC_RST_RESET 0xB806001C +#define AR_SOC_BOOT_STRAP 0xB80600AC +#define AR_SOC_WLAN_RST 0x00000800 /* WLAN reset */ +#define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); +#define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) + HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Hornet SoC reset WMAC.\n", __func__); + + REG_WRITE(AR_SOC_RST_RESET, + REG_READ(AR_SOC_RST_RESET) | AR_SOC_WLAN_RST); + REG_WRITE(AR_SOC_RST_RESET, + REG_READ(AR_SOC_RST_RESET) & (~AR_SOC_WLAN_RST)); + + time_out = 0; + + while (1) { + tmp_reg = REG_READ(AR_SOC_BOOT_STRAP); + if ((tmp_reg & 0x10) == 0) { + break; + } + if (time_out > 20) { + break; + } + OS_DELAY(10000); + time_out++; + } + + OS_REG_WRITE(ah, AR_RTC_RESET, 1); +#undef REG_READ +#undef REG_WRITE +#undef AR_SOC_WLAN_RST +#undef AR_SOC_RST_RESET +#undef AR_SOC_BOOT_STRAP + } +#endif /* AH_SUPPORT_HORNET */ + +#ifdef AH_SUPPORT_SCORPION + if (AR_SREV_SCORPION(ah)) { +#define DDR_CTL_CONFIG_ADDRESS 0xb8000000 +#define DDR_CTL_CONFIG_OFFSET 0x0108 +#define DDR_CTL_CONFIG_CLIENT_ACTIVITY_MSB 29 +#define DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB 21 +#define DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK 0x3fe00000 +#define DDR_CTL_CONFIG_CLIENT_ACTIVITY_GET(x) (((x) & DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK) >> DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB) +#define DDR_CTL_CONFIG_CLIENT_ACTIVITY_SET(x) (((x) << DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB) & DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK) +#define MAC_DMA_CFG_ADDRESS 0xb8100000 +#define MAC_DMA_CFG_OFFSET 0x0014 + +#define MAC_DMA_CFG_HALT_REQ_MSB 11 +#define MAC_DMA_CFG_HALT_REQ_LSB 11 +#define MAC_DMA_CFG_HALT_REQ_MASK 0x00000800 +#define MAC_DMA_CFG_HALT_REQ_GET(x) (((x) & MAC_DMA_CFG_HALT_REQ_MASK) >> MAC_DMA_CFG_HALT_REQ_LSB) +#define MAC_DMA_CFG_HALT_REQ_SET(x) (((x) << MAC_DMA_CFG_HALT_REQ_LSB) & MAC_DMA_CFG_HALT_REQ_MASK) +#define MAC_DMA_CFG_HALT_ACK_MSB 12 +#define MAC_DMA_CFG_HALT_ACK_LSB 12 +#define MAC_DMA_CFG_HALT_ACK_MASK 0x00001000 +#define MAC_DMA_CFG_HALT_ACK_GET(x) (((x) & MAC_DMA_CFG_HALT_ACK_MASK) >> MAC_DMA_CFG_HALT_ACK_LSB) +#define MAC_DMA_CFG_HALT_ACK_SET(x) (((x) << MAC_DMA_CFG_HALT_ACK_LSB) & MAC_DMA_CFG_HALT_ACK_MASK) + +#define RST_RESET 0xB806001c +#define RTC_RESET (1<<27) + +#define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) +#define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); + +#define DDR_REG_READ(_ah, _reg) \ + *((volatile u_int32_t *)( DDR_CTL_CONFIG_ADDRESS + (_reg))) +#define DDR_REG_WRITE(_ah, _reg, _val) \ + *((volatile u_int32_t *)(DDR_CTL_CONFIG_ADDRESS + (_reg))) = (_val) + + OS_REG_WRITE(ah,MAC_DMA_CFG_OFFSET, (OS_REG_READ(ah,MAC_DMA_CFG_OFFSET) & ~MAC_DMA_CFG_HALT_REQ_MASK) | + MAC_DMA_CFG_HALT_REQ_SET(1)); + + { + int count; + u_int32_t data; + + count = 0; + while (!MAC_DMA_CFG_HALT_ACK_GET(OS_REG_READ(ah, MAC_DMA_CFG_OFFSET) )) + { + count++; + if (count > 10) { + ath_hal_printf(ah, "Halt ACK timeout\n"); + break; + } + OS_DELAY(10); + } + + data = DDR_REG_READ(ah,DDR_CTL_CONFIG_OFFSET); + ath_hal_printf(ah, "check DDR Activity - HIGH\n"); + + count = 0; + while (DDR_CTL_CONFIG_CLIENT_ACTIVITY_GET(data)) { + // AVE_DEBUG(0,"DDR Activity - HIGH\n"); + ath_hal_printf(ah, "DDR Activity - HIGH\n"); + count++; + OS_DELAY(10); + data = DDR_REG_READ(ah,DDR_CTL_CONFIG_OFFSET); + if (count > 10) { + ath_hal_printf(ah, "DDR Activity timeout\n"); + break; + } + } + } + + + { + //Force RTC reset + REG_WRITE(RST_RESET, (REG_READ(RST_RESET) | RTC_RESET)); + OS_DELAY(10); + REG_WRITE(RST_RESET, (REG_READ(RST_RESET) & ~RTC_RESET)); + OS_DELAY(10); + OS_REG_WRITE(ah, AR_RTC_RESET, 0); + OS_DELAY(10); + OS_REG_WRITE(ah, AR_RTC_RESET, 1); + OS_DELAY(10); + ath_hal_printf(ah,"%s: Scorpion SoC RTC reset done.\n", __func__); + } +#undef REG_READ +#undef REG_WRITE + } +#endif /* AH_SUPPORT_SCORPION */ + + /* + * Set Mac(BB,Phy) Warm Reset + */ + OS_REG_WRITE(ah, AR_RTC_RC, rst_flags); + + OS_DELAY(50); /* XXX 50 usec */ + + /* + * Clear resets and force wakeup + */ + OS_REG_WRITE(ah, AR_RTC_RC, 0); + if (!ath_hal_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) { + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s: RTC stuck in MAC reset\n", __FUNCTION__); + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s: AR_RTC_RC = 0x%x\n", __func__, OS_REG_READ(ah, AR_RTC_RC)); + return AH_FALSE; + } + + /* Clear AHB reset */ + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), 0); + + ar9300_attach_hw_platform(ah); + + return AH_TRUE; +} + +static inline HAL_BOOL +ar9300_set_reset_power_on(struct ath_hal *ah) +{ + /* Force wake */ + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); + OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ + OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); + /* + * RTC reset and clear. Some delay in between is needed + * to give the chip time to settle. + */ + OS_REG_WRITE(ah, AR_RTC_RESET, 0); + OS_DELAY(2); + OS_REG_WRITE(ah, AR_RTC_RESET, 1); + + /* + * Poll till RTC is ON + */ +#define AH_RTC_POLL_TIMEOUT AH_WAIT_TIMEOUT + if (!ath_hal_wait(ah, + AR_RTC_STATUS, AR_RTC_STATUS_M, + AR_RTC_STATUS_ON, AH_RTC_POLL_TIMEOUT)) + { + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s: RTC not waking up for %d\n", __FUNCTION__, AH_WAIT_TIMEOUT); + return AH_FALSE; + } + + /* + * Read Revisions from Chip right after RTC is on for the first time. + * This helps us detect the chip type early and initialize it accordingly. + */ + ar9300_read_revisions(ah); + + /* + * Warm reset if we aren't really powering on, + * just restarting the driver. + */ + return ar9300_set_reset(ah, HAL_RESET_WARM); +} + +/* + * Write the given reset bit mask into the reset register + */ +HAL_BOOL +ar9300_set_reset_reg(struct ath_hal *ah, u_int32_t type) +{ + HAL_BOOL ret = AH_FALSE; + + /* + * Set force wake + */ + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); + OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ + OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); + + switch (type) { + case HAL_RESET_POWER_ON: + ret = ar9300_set_reset_power_on(ah); + break; + case HAL_RESET_WARM: + case HAL_RESET_COLD: + ret = ar9300_set_reset(ah, type); + break; + default: + break; + } + +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support) { + OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); + } +#endif + + return ret; +} + +/* + * Places the PHY and Radio chips into reset. A full reset + * must be called to leave this state. The PCI/MAC/PCU are + * not placed into reset as we must receive interrupt to + * re-enable the hardware. + */ +HAL_BOOL +ar9300_phy_disable(struct ath_hal *ah) +{ + if (!ar9300_set_reset_reg(ah, HAL_RESET_WARM)) { + return AH_FALSE; + } + +#ifdef ATH_SUPPORT_LED +#define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) +#define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); +#define ATH_GPIO_OE 0xB8040000 +#define ATH_GPIO_OUT 0xB8040008 /* GPIO Ouput Value reg.*/ + if (AR_SREV_WASP(ah)) { + if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { + REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 13))); + } + else { + REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 12))); + } + } + else if (AR_SREV_SCORPION(ah)) { + if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { + REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 13))); + } + else { + REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 12))); + } + /* Turn off JMPST led */ + REG_WRITE(ATH_GPIO_OUT, (REG_READ(ATH_GPIO_OUT) | (0x1 << 15))); + } +#undef REG_READ +#undef REG_WRITE +#endif + + if ( AR_SREV_OSPREY(ah) ) { + OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX1), 0x0, 0x1f); + } + + + ar9300_init_pll(ah, AH_NULL); + + return AH_TRUE; +} + +/* + * Places all of hardware into reset + */ +HAL_BOOL +ar9300_disable(struct ath_hal *ah) +{ + if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { + return AH_FALSE; + } + if (!ar9300_set_reset_reg(ah, HAL_RESET_COLD)) { + return AH_FALSE; + } + + ar9300_init_pll(ah, AH_NULL); + + return AH_TRUE; +} + +/* + * TODO: Only write the PLL if we're changing to or from CCK mode + * + * WARNING: The order of the PLL and mode registers must be correct. + */ +static inline void +ar9300_set_rf_mode(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + u_int32_t rf_mode = 0; + + if (chan == AH_NULL) { + return; + } + switch (AH9300(ah)->ah_hwp) { + case HAL_TRUE_CHIP: + rf_mode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) ? + AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; + break; + default: + HALASSERT(0); + break; + } + /* Phy mode bits for 5GHz channels requiring Fast Clock */ + if ( IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { + rf_mode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); + } + OS_REG_WRITE(ah, AR_PHY_MODE, rf_mode); +} + +/* + * Places the hardware into reset and then pulls it out of reset + */ +HAL_BOOL +ar9300_chip_reset(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->channel : 0); + + /* + * Warm reset is optimistic. + */ + if (!ar9300_set_reset_reg(ah, HAL_RESET_WARM)) { + return AH_FALSE; + } + + /* Bring out of sleep mode (AGAIN) */ + if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { + return AH_FALSE; + } + + ahp->ah_chip_full_sleep = AH_FALSE; + + if (AR_SREV_HORNET(ah)) { + ar9300_internal_regulator_apply(ah); + } + + ar9300_init_pll(ah, chan); + + /* + * Perform warm reset before the mode/PLL/turbo registers + * are changed in order to deactivate the radio. Mode changes + * with an active radio can result in corrupted shifts to the + * radio device. + */ + ar9300_set_rf_mode(ah, chan); + + return AH_TRUE; +} + +/* ar9300_setup_calibration + * Setup HW to collect samples used for current cal + */ +inline static void +ar9300_setup_calibration(struct ath_hal *ah, HAL_CAL_LIST *curr_cal) +{ + /* Select calibration to run */ + switch (curr_cal->cal_data->cal_type) { + case IQ_MISMATCH_CAL: + /* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */ + OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, + AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, + curr_cal->cal_data->cal_count_max); + OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: starting IQ Mismatch Calibration\n", __func__); + + /* Kick-off cal */ + OS_REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); + + break; + case TEMP_COMP_CAL: + if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || + AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { + OS_REG_RMW_FIELD(ah, + AR_HORNET_CH0_THERM, AR_PHY_65NM_CH0_THERM_LOCAL, 1); + OS_REG_RMW_FIELD(ah, + AR_HORNET_CH0_THERM, AR_PHY_65NM_CH0_THERM_START, 1); + } else if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { + OS_REG_RMW_FIELD(ah, + AR_PHY_65NM_CH0_THERM_JUPITER, AR_PHY_65NM_CH0_THERM_LOCAL, 1); + OS_REG_RMW_FIELD(ah, + AR_PHY_65NM_CH0_THERM_JUPITER, AR_PHY_65NM_CH0_THERM_START, 1); + } else { + OS_REG_RMW_FIELD(ah, + AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_LOCAL, 1); + OS_REG_RMW_FIELD(ah, + AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_START, 1); + } + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: starting Temperature Compensation Calibration\n", __func__); + break; + default: + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s called with incorrect calibration type.\n", __func__); + } +} + +/* ar9300_reset_calibration + * Initialize shared data structures and prepare a cal to be run. + */ +inline static void +ar9300_reset_calibration(struct ath_hal *ah, HAL_CAL_LIST *curr_cal) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + int i; + + /* Setup HW for new calibration */ + ar9300_setup_calibration(ah, curr_cal); + + /* Change SW state to RUNNING for this calibration */ + curr_cal->cal_state = CAL_RUNNING; + + /* Reset data structures shared between different calibrations */ + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + ahp->ah_meas0.sign[i] = 0; + ahp->ah_meas1.sign[i] = 0; + ahp->ah_meas2.sign[i] = 0; + ahp->ah_meas3.sign[i] = 0; + } + + ahp->ah_cal_samples = 0; +} + +#ifdef XXX_UNUSED_FUNCTION +/* + * Find out which of the RX chains are enabled + */ +static u_int32_t +ar9300_get_rx_chain_mask(struct ath_hal *ah) +{ + u_int32_t ret_val = OS_REG_READ(ah, AR_PHY_RX_CHAINMASK); + /* The bits [2:0] indicate the rx chain mask and are to be + * interpreted as follows: + * 00x => Only chain 0 is enabled + * 01x => Chain 1 and 0 enabled + * 1xx => Chain 2,1 and 0 enabled + */ + return (ret_val & 0x7); +} +#endif + +static void +ar9300_get_nf_hist_base(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, + int is_scan, int16_t nf[]) +{ + HAL_NFCAL_BASE *h_base; + +#ifdef ATH_NF_PER_CHAN + h_base = &chan->nf_cal_hist.base; +#else + if (is_scan) { + /* + * The channel we are currently on is not the home channel, + * so we shouldn't use the home channel NF buffer's values on + * this channel. Instead, use the NF single value already + * read for this channel. (Or, if we haven't read the NF for + * this channel yet, the SW default for this chip/band will + * be used.) + */ + h_base = &chan->nf_cal_hist.base; + } else { + /* use the home channel NF info */ + h_base = &AH_PRIVATE(ah)->nf_cal_hist.base; + } +#endif + OS_MEMCPY(nf, h_base->priv_nf, sizeof(h_base->priv_nf)); +} + +HAL_BOOL +ar9300_load_nf(struct ath_hal *ah, int16_t nf[]) +{ + int i, j; + int32_t val; + /* XXX where are EXT regs defined */ + const u_int32_t ar9300_cca_regs[] = { + AR_PHY_CCA_0, + AR_PHY_CCA_1, + AR_PHY_CCA_2, + AR_PHY_EXT_CCA, + AR_PHY_EXT_CCA_1, + AR_PHY_EXT_CCA_2, + }; + u_int8_t chainmask; + + /* + * Force NF calibration for all chains, otherwise Vista station + * would conduct a bad performance + */ + if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { + chainmask = 0x9; + } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah)) { + chainmask = 0x1b; + } else { + chainmask = 0x3F; + } + + /* + * Write filtered NF values into max_cca_pwr register parameter + * so we can load below. + */ + for (i = 0; i < NUM_NF_READINGS; i++) { + if (chainmask & (1 << i)) { + val = OS_REG_READ(ah, ar9300_cca_regs[i]); + val &= 0xFFFFFE00; + val |= (((u_int32_t)(nf[i]) << 1) & 0x1ff); + OS_REG_WRITE(ah, ar9300_cca_regs[i], val); + } + } + + /* + * Load software filtered NF value into baseband internal min_cca_pwr + * variable. + */ + OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); + OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + + /* Wait for load to complete, should be fast, a few 10s of us. */ + /* Changed the max delay 250us back to 10000us, since 250us often + * results in NF load timeout and causes deaf condition + * during stress testing 12/12/2009 + */ + for (j = 0; j < 10000; j++) { + if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0){ + break; + } + OS_DELAY(10); + } + if (j == 10000) { + /* + * We timed out waiting for the noisefloor to load, probably + * due to an in-progress rx. Simply return here and allow + * the load plenty of time to complete before the next + * calibration interval. We need to avoid trying to load -50 + * (which happens below) while the previous load is still in + * progress as this can cause rx deafness (see EV 66368,62830). + * Instead by returning here, the baseband nf cal will + * just be capped by our present noisefloor until the next + * calibration timer. + */ + HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, + "%s: *** TIMEOUT while waiting for nf to load: " + "AR_PHY_AGC_CONTROL=0x%x ***\n", + __func__, OS_REG_READ(ah, AR_PHY_AGC_CONTROL)); + return AH_FALSE; + } + + /* + * Restore max_cca_power register parameter again so that we're not capped + * by the median we just loaded. This will be initial (and max) value + * of next noise floor calibration the baseband does. + */ + for (i = 0; i < NUM_NF_READINGS; i++) { + if (chainmask & (1 << i)) { + val = OS_REG_READ(ah, ar9300_cca_regs[i]); + val &= 0xFFFFFE00; + val |= (((u_int32_t)(-50) << 1) & 0x1ff); + OS_REG_WRITE(ah, ar9300_cca_regs[i], val); + } + } + return AH_TRUE; +} + +/* ar9300_per_calibration + * Generic calibration routine. + * Recalibrate the lower PHY chips to account for temperature/environment + * changes. + */ +inline static void +ar9300_per_calibration(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, + u_int8_t rxchainmask, HAL_CAL_LIST *curr_cal, HAL_BOOL *is_cal_done) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + + /* Cal is assumed not done until explicitly set below */ + *is_cal_done = AH_FALSE; + + /* Calibration in progress. */ + if (curr_cal->cal_state == CAL_RUNNING) { + /* Check to see if it has finished. */ + if (!(OS_REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) { + int i, num_chains = 0; + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (rxchainmask & (1 << i)) { + num_chains++; + } + } + + /* + * Accumulate cal measures for active chains + */ + curr_cal->cal_data->cal_collect(ah, num_chains); + + ahp->ah_cal_samples++; + + if (ahp->ah_cal_samples >= curr_cal->cal_data->cal_num_samples) { + /* + * Process accumulated data + */ + curr_cal->cal_data->cal_post_proc(ah, num_chains); + + /* Calibration has finished. */ + ichan->cal_valid |= curr_cal->cal_data->cal_type; + curr_cal->cal_state = CAL_DONE; + *is_cal_done = AH_TRUE; + } else { + /* Set-up collection of another sub-sample until we + * get desired number + */ + ar9300_setup_calibration(ah, curr_cal); + } + } + } else if (!(ichan->cal_valid & curr_cal->cal_data->cal_type)) { + /* If current cal is marked invalid in channel, kick it off */ + ar9300_reset_calibration(ah, curr_cal); + } +} + +static void +ar9300_start_nf_cal(struct ath_hal *ah) +{ + OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); + OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + AH9300(ah)->nf_tsf32 = ar9300_get_tsf32(ah); +} + +/* ar9300_calibration + * Wrapper for a more generic Calibration routine. Primarily to abstract to + * upper layers whether there is 1 or more calibrations to be run. + */ +HAL_BOOL +ar9300_calibration(struct ath_hal *ah, HAL_CHANNEL *chan, u_int8_t rxchainmask, + HAL_BOOL do_nf_cal, HAL_BOOL *is_cal_done, int is_scan, + u_int32_t *sched_cals) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + HAL_CAL_LIST *curr_cal = ahp->ah_cal_list_curr; + HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); + int16_t nf_buf[NUM_NF_READINGS]; + + *is_cal_done = AH_TRUE; + + + /* XXX: For initial wasp bringup - disable periodic calibration */ + /* Invalid channel check */ + if (ichan == AH_NULL) { + HALDEBUG(ah, HAL_DEBUG_CHANNEL, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channel_flags); + return AH_FALSE; + } + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Entering, Doing NF Cal = %d\n", __func__, do_nf_cal); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Chain 0 Rx IQ Cal Correction 0x%08x\n", + __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); + if (!AR_SREV_HORNET(ah) && !AR_SREV_POSEIDON(ah) && !AR_SREV_APHRODITE(ah)) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Chain 1 Rx IQ Cal Correction 0x%08x\n", + __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B1)); + if (!AR_SREV_WASP(ah) && !AR_SREV_JUPITER(ah)) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Chain 2 Rx IQ Cal Correction 0x%08x\n", + __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B2)); + } + } + + OS_MARK(ah, AH_MARK_PERCAL, chan->channel); + + /* For given calibration: + * 1. Call generic cal routine + * 2. When this cal is done (is_cal_done) if we have more cals waiting + * (eg after reset), mask this to upper layers by not propagating + * is_cal_done if it is set to TRUE. + * Instead, change is_cal_done to FALSE and setup the waiting cal(s) + * to be run. + */ + if (curr_cal && (curr_cal->cal_data->cal_type & *sched_cals) && + (curr_cal->cal_state == CAL_RUNNING || + curr_cal->cal_state == CAL_WAITING)) + { + ar9300_per_calibration(ah, ichan, rxchainmask, curr_cal, is_cal_done); + + if (*is_cal_done == AH_TRUE) { + ahp->ah_cal_list_curr = curr_cal = curr_cal->cal_next; + + if (curr_cal && curr_cal->cal_state == CAL_WAITING) { + *is_cal_done = AH_FALSE; + ar9300_reset_calibration(ah, curr_cal); + } else { + *sched_cals &= ~IQ_MISMATCH_CAL; + } + } + } + + /* Do NF cal only at longer intervals */ + if (do_nf_cal) { + int nf_done; + + /* Get the value from the previous NF cal and update history buffer */ + nf_done = ar9300_store_new_nf(ah, ichan, is_scan); + if (ichan->channel_flags & CHANNEL_CW_INT) { + chan->channel_flags |= CHANNEL_CW_INT; + } + ichan->channel_flags &= (~CHANNEL_CW_INT); + + if (nf_done) { + /* + * Load the NF from history buffer of the current channel. + * NF is slow time-variant, so it is OK to use a historical value. + */ + ar9300_get_nf_hist_base(ah, + AH_PRIVATE(ah)->ah_curchan, is_scan, nf_buf); + ar9300_load_nf(ah, nf_buf); + + /* start NF calibration, without updating BB NF register*/ + ar9300_start_nf_cal(ah); + } + } + return AH_TRUE; +} + +/* ar9300_iq_cal_collect + * Collect data from HW to later perform IQ Mismatch Calibration + */ +void +ar9300_iq_cal_collect(struct ath_hal *ah, u_int8_t num_chains) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + int i; + + /* + * Accumulate IQ cal measures for active chains + */ + for (i = 0; i < num_chains; i++) { + ahp->ah_total_power_meas_i[i] = OS_REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ahp->ah_total_power_meas_q[i] = OS_REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ahp->ah_total_iq_corr_meas[i] = + (int32_t) OS_REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%d: Chn %d " + "Reg Offset(0x%04x)pmi=0x%08x; " + "Reg Offset(0x%04x)pmq=0x%08x; " + "Reg Offset (0x%04x)iqcm=0x%08x;\n", + ahp->ah_cal_samples, + i, + (unsigned) AR_PHY_CAL_MEAS_0(i), + ahp->ah_total_power_meas_i[i], + (unsigned) AR_PHY_CAL_MEAS_1(i), + ahp->ah_total_power_meas_q[i], + (unsigned) AR_PHY_CAL_MEAS_2(i), + ahp->ah_total_iq_corr_meas[i]); + } +} + +/* ar9300_iq_calibration + * Use HW data to perform IQ Mismatch Calibration + */ +void +ar9300_iq_calibration(struct ath_hal *ah, u_int8_t num_chains) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + u_int32_t power_meas_q, power_meas_i, iq_corr_meas; + u_int32_t q_coff_denom, i_coff_denom; + int32_t q_coff, i_coff; + int iq_corr_neg, i; + static const u_int32_t offset_array[3] = { + AR_PHY_RX_IQCAL_CORR_B0, + AR_PHY_RX_IQCAL_CORR_B1, + AR_PHY_RX_IQCAL_CORR_B2, + }; + + for (i = 0; i < num_chains; i++) { + power_meas_i = ahp->ah_total_power_meas_i[i]; + power_meas_q = ahp->ah_total_power_meas_q[i]; + iq_corr_meas = ahp->ah_total_iq_corr_meas[i]; + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Starting IQ Cal and Correction for Chain %d\n", i); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Orignal: Chn %diq_corr_meas = 0x%08x\n", + i, ahp->ah_total_iq_corr_meas[i]); + + iq_corr_neg = 0; + + /* iq_corr_meas is always negative. */ + if (iq_corr_meas > 0x80000000) { + iq_corr_meas = (0xffffffff - iq_corr_meas) + 1; + iq_corr_neg = 1; + } + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Chn %d pwr_meas_i = 0x%08x\n", i, power_meas_i); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Chn %d pwr_meas_q = 0x%08x\n", i, power_meas_q); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "iq_corr_neg is 0x%08x\n", iq_corr_neg); + + i_coff_denom = (power_meas_i / 2 + power_meas_q / 2) / 256; + q_coff_denom = power_meas_q / 64; + + /* Protect against divide-by-0 */ + if ((i_coff_denom != 0) && (q_coff_denom != 0)) { + /* IQ corr_meas is already negated if iqcorr_neg == 1 */ + i_coff = iq_corr_meas / i_coff_denom; + q_coff = power_meas_i / q_coff_denom - 64; + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Chn %d i_coff = 0x%08x\n", i, i_coff); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Chn %d q_coff = 0x%08x\n", i, q_coff); + + /* Force bounds on i_coff */ + if (i_coff >= 63) { + i_coff = 63; + } else if (i_coff <= -63) { + i_coff = -63; + } + + /* Negate i_coff if iq_corr_neg == 0 */ + if (iq_corr_neg == 0x0) { + i_coff = -i_coff; + } + + /* Force bounds on q_coff */ + if (q_coff >= 63) { + q_coff = 63; + } else if (q_coff <= -63) { + q_coff = -63; + } + + i_coff = i_coff & 0x7f; + q_coff = q_coff & 0x7f; + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Chn %d : i_coff = 0x%x q_coff = 0x%x\n", i, i_coff, q_coff); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Register offset (0x%04x) before update = 0x%x\n", + offset_array[i], OS_REG_READ(ah, offset_array[i])); + + OS_REG_RMW_FIELD(ah, offset_array[i], + AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, i_coff); + OS_REG_RMW_FIELD(ah, offset_array[i], + AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, q_coff); + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Register offset (0x%04x) QI COFF (bitfields 0x%08x) " + "after update = 0x%x\n", + offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, + OS_REG_READ(ah, offset_array[i])); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) " + "after update = 0x%x\n", + offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, + OS_REG_READ(ah, offset_array[i])); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "IQ Cal and Correction done for Chain %d\n", i); + } + } + + OS_REG_SET_BIT(ah, + AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "IQ Cal and Correction (offset 0x%04x) enabled " + "(bit position 0x%08x). New Value 0x%08x\n", + (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), + AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, + OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); +} + +/* + * Set a limit on the overall output power. Used for dynamic + * transmit power control and the like. + * + * NB: limit is in units of 0.5 dbM. + */ +HAL_BOOL +ar9300_set_tx_power_limit(struct ath_hal *ah, u_int32_t limit, + u_int16_t extra_txpow, u_int16_t tpc_in_db) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + HAL_CHANNEL_INTERNAL *ichan = ahpriv->ah_curchan; + HAL_CHANNEL *chan = (HAL_CHANNEL *)ichan; + + if (NULL == chan) { + return AH_FALSE; + } + ahpriv->ah_power_limit = AH_MIN(limit, MAX_RATE_POWER); + ahpriv->ah_extra_txpow = extra_txpow; + + if(chan == NULL) { + return AH_FALSE; + } + if (ar9300_eeprom_set_transmit_power(ah, &ahp->ah_eeprom, ichan, + ath_hal_getctl(ah, chan), ath_hal_getantennaallowed(ah, chan), + ath_hal_get_twice_max_regpower(ahpriv, ichan, chan), + AH_MIN(MAX_RATE_POWER, ahpriv->ah_power_limit)) != HAL_OK) + { + return AH_FALSE; + } + return AH_TRUE; +} + +/* + * Exported call to check for a recent gain reading and return + * the current state of the thermal calibration gain engine. + */ +HAL_RFGAIN +ar9300_get_rfgain(struct ath_hal *ah) +{ + return HAL_RFGAIN_INACTIVE; +} + +#define HAL_GREEN_AP_RX_MASK 0x1 + +static inline void +ar9300_init_chain_masks(struct ath_hal *ah, int rx_chainmask, int tx_chainmask) +{ + if (AH_PRIVATE(ah)->green_ap_ps_on ) { + rx_chainmask = HAL_GREEN_AP_RX_MASK; + } + if (rx_chainmask == 0x5) { + OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); + } + OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); + OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); + + /* + * Adaptive Power Management: + * Some 3 stream chips exceed the PCIe power requirements. + * This workaround will reduce power consumption by using 2 tx chains + * for 1 and 2 stream rates (5 GHz only). + * + * Set the self gen mask to 2 tx chains when APM is enabled. + * + */ + if (AH_PRIVATE(ah)->ah_caps.hal_enable_apm && (tx_chainmask == 0x7)) { + OS_REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); + } + else { + OS_REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask); + } + + if (tx_chainmask == 0x5) { + OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); + } +} + +/* + * Override INI values with chip specific configuration. + */ +static inline void +ar9300_override_ini(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + u_int32_t val; + HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; + + /* + * Set the RX_ABORT and RX_DIS and clear it only after + * RXE is set for MAC. This prevents frames with + * corrupted descriptor status. + */ + OS_REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + /* + * For Merlin and above, there is a new feature that allows Multicast + * search based on both MAC Address and Key ID. + * By default, this feature is enabled. + * But since the driver is not using this feature, we switch it off; + * otherwise multicast search based on MAC addr only will fail. + */ + val = OS_REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE); + OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, + val | AR_BUG_58603_FIX_ENABLE | AR_AGG_WEP_ENABLE); + + + /* Osprey revision specific configuration */ + + /* Osprey 2.0+ - if SW RAC support is disabled, must also disable + * the Osprey 2.0 hardware RAC fix. + */ + if (p_cap->hal_isr_rac_support == AH_FALSE) { + OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_MISSING_TX_INTR_FIX_ENABLE); + } + + /* try to enable old pal if it is needed for h/w green tx */ + ar9300_hwgreentx_set_pal_spare(ah, 1); +} + +static inline void +ar9300_prog_ini(struct ath_hal *ah, struct ar9300_ini_array *ini_arr, + int column) +{ + int i, reg_writes = 0; + + /* New INI format: Array may be undefined (pre, core, post arrays) */ + if (ini_arr->ia_array == NULL) { + return; + } + + /* + * New INI format: Pre, core, and post arrays for a given subsystem may be + * modal (> 2 columns) or non-modal (2 columns). + * Determine if the array is non-modal and force the column to 1. + */ + if (column >= ini_arr->ia_columns) { + column = 1; + } + + for (i = 0; i < ini_arr->ia_rows; i++) { + u_int32_t reg = INI_RA(ini_arr, i, 0); + u_int32_t val = INI_RA(ini_arr, i, column); + + /* + ** Determine if this is a shift register value + ** (reg >= 0x16000 && reg < 0x17000 for Osprey) , + ** and insert the configured delay if so. + ** -this delay is not required for Osprey (EV#71410) + */ + OS_REG_WRITE(ah, reg, val); + WAR_6773(reg_writes); + + } +} + +static inline HAL_STATUS +ar9300_process_ini(struct ath_hal *ah, HAL_CHANNEL *chan, + HAL_CHANNEL_INTERNAL *ichan, HAL_HT_MACMODE macmode) +{ + int reg_writes = 0; + struct ath_hal_9300 *ahp = AH9300(ah); + u_int modes_index, modes_txgaintable_index = 0; + int i; + HAL_STATUS status; + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + /* Setup the indices for the next set of register array writes */ + /* TODO: + * If the channel marker is indicative of the current mode rather + * than capability, we do not need to check the phy mode below. + */ + switch (chan->channel_flags & CHANNEL_ALL) { + case CHANNEL_A: + case CHANNEL_A_HT20: + if (AR_SREV_SCORPION(ah)){ + if (chan->channel <= 5350){ + modes_txgaintable_index = 1; + }else if ((chan->channel > 5350) && (chan->channel <= 5600)){ + modes_txgaintable_index = 3; + }else if (chan->channel > 5600){ + modes_txgaintable_index = 5; + } + } + modes_index = 1; + break; + + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + if (AR_SREV_SCORPION(ah)){ + if (chan->channel <= 5350){ + modes_txgaintable_index = 2; + }else if ((chan->channel > 5350) && (chan->channel <= 5600)){ + modes_txgaintable_index = 4; + }else if (chan->channel > 5600){ + modes_txgaintable_index = 6; + } + } + modes_index = 2; + break; + + case CHANNEL_PUREG: + case CHANNEL_G_HT20: + case CHANNEL_B: + if (AR_SREV_SCORPION(ah)){ + modes_txgaintable_index = 8; + } + modes_index = 4; + break; + + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + if (AR_SREV_SCORPION(ah)){ + modes_txgaintable_index = 7; + } + modes_index = 3; + break; + + case CHANNEL_108G: + modes_index = 5; + break; + + default: + HALASSERT(0); + return HAL_EINVAL; + } + +#if 0 + /* Set correct Baseband to analog shift setting to access analog chips. */ + OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); +#endif + + HALDEBUG(ah, HAL_DEBUG_RESET, + "ar9300_process_ini: " + "Skipping OS-REG-WRITE(ah, AR-PHY(0), 0x00000007)\n"); + HALDEBUG(ah, HAL_DEBUG_RESET, + "ar9300_process_ini: no ADDac programming\n"); + + + /* + * Osprey 2.0+ - new INI format. + * Each subsystem has a pre, core, and post array. + */ + for (i = 0; i < ATH_INI_NUM_SPLIT; i++) { + ar9300_prog_ini(ah, &ahp->ah_ini_soc[i], modes_index); + ar9300_prog_ini(ah, &ahp->ah_ini_mac[i], modes_index); + ar9300_prog_ini(ah, &ahp->ah_ini_bb[i], modes_index); + ar9300_prog_ini(ah, &ahp->ah_ini_radio[i], modes_index); + if ((i == ATH_INI_POST) && (AR_SREV_JUPITER_20(ah) || AR_SREV_APHRODITE(ah))) { + ar9300_prog_ini(ah, &ahp->ah_ini_radio_post_sys2ant, modes_index); + } + + } + + if (!(AR_SREV_SOC(ah))) { + /* Doubler issue : Some board doesn't work well with MCS15. Turn off doubler after freq locking is complete*/ + //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); + OS_REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ + //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); + + OS_REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ + OS_REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ + OS_DELAY(200); + + //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); + OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH0_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ + OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH1_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ + OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH2_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ + //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); + + OS_DELAY(1); + + //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); + OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ + OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ + OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ + //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); + + OS_DELAY(200); + + //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_SYNTH12, OS_REG_READ(ah, AR_PHY_65NM_CH0_SYNTH12)); + OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_SYNTH12, AR_PHY_65NM_CH0_SYNTH12_VREFMUL3, 0xf); + //OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH0_SYNTH12, 1<< 16); /* clr charge pump */ + //ath_hal_printf(ah, "%s[%d] ==== After reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_SYNTH12, OS_REG_READ(ah, AR_PHY_65NM_CH0_SYNTH12)); + + OS_REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ + OS_REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ + OS_REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ + //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); + } + + /* Write rxgain Array Parameters */ + REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain, 1, reg_writes); + HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Rx Gain programming\n"); + + if (AR_SREV_SCORPION(ah)) { + /* Write rxgain bounds Array */ + REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain_bounds, modes_index, reg_writes); + HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Rx Gain table bounds programming\n"); + } + /* UB124 xLNA settings */ + if (AR_SREV_WASP(ah) && ar9300_rx_gain_index_get(ah) == 2) { +#define REG_WRITE(_reg,_val) *((volatile u_int32_t *)(_reg)) = (_val); +#define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) + u_int32_t val; + /* B8040000: bit[0]=0, bit[3]=0; */ + val = REG_READ(0xB8040000); + val &= 0xfffffff6; + REG_WRITE(0xB8040000, val); + /* B804002c: bit[31:24]=0x2e; bit[7:0]=0x2f; */ + val = REG_READ(0xB804002c); + val &= 0x00ffff00; + val |= 0x2e00002f; + REG_WRITE(0xB804002c, val); + /* B804006c: bit[1]=1; */ + val = REG_READ(0xB804006c); + val |= 0x2; + REG_WRITE(0xB804006c, val); +#undef REG_READ +#undef REG_WRITE + } + + + /* Write txgain Array Parameters */ + if (AR_SREV_SCORPION(ah)) { + REG_WRITE_ARRAY(&ahp->ah_ini_modes_txgain, modes_txgaintable_index, + reg_writes); + }else{ + REG_WRITE_ARRAY(&ahp->ah_ini_modes_txgain, modes_index, reg_writes); + } + HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Tx Gain programming\n"); + + + /* For 5GHz channels requiring Fast Clock, apply different modal values */ + if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { + HALDEBUG(ah, HAL_DEBUG_RESET, + "%s: Fast clock enabled, use special ini values\n", __func__); + REG_WRITE_ARRAY(&ahp->ah_ini_modes_additional, modes_index, reg_writes); + } + + if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { + HALDEBUG(ah, HAL_DEBUG_RESET, + "%s: use xtal ini for AH9300(ah)->clk_25mhz: %d\n", + __func__, AH9300(ah)->clk_25mhz); + REG_WRITE_ARRAY( + &ahp->ah_ini_modes_additional, 1/*modes_index*/, reg_writes); + } + + if (AR_SREV_WASP(ah) && (AH9300(ah)->clk_25mhz == 0)) { + HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Apply 40MHz ini settings\n", __func__); + REG_WRITE_ARRAY( + &ahp->ah_ini_modes_additional_40mhz, 1/*modesIndex*/, reg_writes); + } + + if (2484 == chan->channel) { + ar9300_prog_ini(ah, &ahp->ah_ini_japan2484, 1); + } + +#if 0 + if (AR_SREV_JUPITER_20(ah) || AR_SREV_APHRODITE(ah)) { + ar9300_prog_ini(ah, &ahp->ah_ini_BTCOEX_MAX_TXPWR, 1); + } +#endif + + /* Override INI with chip specific configuration */ + ar9300_override_ini(ah, chan); + + /* Setup 11n MAC/Phy mode registers */ + ar9300_set_11n_regs(ah, chan, macmode); + + /* + * Moved ar9300_init_chain_masks() here to ensure the swap bit is set before + * the pdadc table is written. Swap must occur before any radio dependent + * replicated register access. The pdadc curve addressing in particular + * depends on the consistent setting of the swap bit. + */ + ar9300_init_chain_masks(ah, ahp->ah_rx_chainmask, ahp->ah_tx_chainmask); + + /* + * Setup the transmit power values. + * + * After the public to private hal channel mapping, ichan contains the + * valid regulatory power value. + * ath_hal_getctl and ath_hal_getantennaallowed look up ichan from chan. + */ + status = ar9300_eeprom_set_transmit_power(ah, &ahp->ah_eeprom, ichan, + ath_hal_getctl(ah, chan), ath_hal_getantennaallowed(ah, chan), + ath_hal_get_twice_max_regpower(ahpriv, ichan, chan), + AH_MIN(MAX_RATE_POWER, ahpriv->ah_power_limit)); + if (status != HAL_OK) { + HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, + "%s: error init'ing transmit power\n", __func__); + return HAL_EIO; + } + + + return HAL_OK; +#undef N +} + +/* ar9300_is_cal_supp + * Determine if calibration is supported by device and channel flags + */ +inline static HAL_BOOL +ar9300_is_cal_supp(struct ath_hal *ah, HAL_CHANNEL *chan, + HAL_CAL_TYPES cal_type) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + HAL_BOOL retval = AH_FALSE; + + switch (cal_type & ahp->ah_supp_cals) { + case IQ_MISMATCH_CAL: + /* Run IQ Mismatch for non-CCK only */ + if (!IS_CHAN_B(chan)) { + retval = AH_TRUE; + } + break; + case TEMP_COMP_CAL: + retval = AH_TRUE; + break; + } + + return retval; +} + + +#if 0 +/* ar9285_pa_cal + * PA Calibration for Kite 1.1 and later versions of Kite. + * - from system's team. + */ +static inline void +ar9285_pa_cal(struct ath_hal *ah) +{ + u_int32_t reg_val; + int i, lo_gn, offs_6_1, offs_0; + u_int8_t reflo; + u_int32_t phy_test2_reg_val, phy_adc_ctl_reg_val; + u_int32_t an_top2_reg_val, phy_tst_dac_reg_val; + + + /* Kite 1.1 WAR for Bug 35666 + * Increase the LDO value to 1.28V before accessing analog Reg */ + if (AR_SREV_KITE_11(ah)) { + OS_REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14) ); + } + an_top2_reg_val = OS_REG_READ(ah, AR9285_AN_TOP2); + + /* set pdv2i pdrxtxbb */ + reg_val = OS_REG_READ(ah, AR9285_AN_RXTXBB1); + reg_val |= ((0x1 << 5) | (0x1 << 7)); + OS_REG_WRITE(ah, AR9285_AN_RXTXBB1, reg_val); + + /* clear pwddb */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G7); + reg_val &= 0xfffffffd; + OS_REG_WRITE(ah, AR9285_AN_RF2G7, reg_val); + + /* clear enpacal */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); + reg_val &= 0xfffff7ff; + OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); + + /* set offcal */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G2); + reg_val |= (0x1 << 12); + OS_REG_WRITE(ah, AR9285_AN_RF2G2, reg_val); + + /* set pdpadrv1=pdpadrv2=pdpaout=1 */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); + reg_val |= (0x7 << 23); + OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); + + /* Read back reflo, increase it by 1 and write it. */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); + reflo = ((reg_val >> 26) & 0x7); + + if (reflo < 0x7) { + reflo++; + } + reg_val = ((reg_val & 0xe3ffffff) | (reflo << 26)); + OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); + + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); + reflo = ((reg_val >> 26) & 0x7); + + /* use TX single carrier to transmit + * dac const + * reg. 15 + */ + phy_tst_dac_reg_val = OS_REG_READ(ah, AR_PHY_TSTDAC_CONST); + OS_REG_WRITE(ah, AR_PHY_TSTDAC_CONST, ((0x7ff << 11) | 0x7ff)); + reg_val = OS_REG_READ(ah, AR_PHY_TSTDAC_CONST); + + /* source is dac const + * reg. 2 + */ + phy_test2_reg_val = OS_REG_READ(ah, AR_PHY_TEST2); + OS_REG_WRITE(ah, AR_PHY_TEST2, ((0x1 << 7) | (0x1 << 1))); + reg_val = OS_REG_READ(ah, AR_PHY_TEST2); + + /* set dac on + * reg. 11 + */ + phy_adc_ctl_reg_val = OS_REG_READ(ah, AR_PHY_ADC_CTL); + OS_REG_WRITE(ah, AR_PHY_ADC_CTL, 0x80008000); + reg_val = OS_REG_READ(ah, AR_PHY_ADC_CTL); + + OS_REG_WRITE(ah, AR9285_AN_TOP2, (0x1 << 27) | (0x1 << 17) | (0x1 << 16) | + (0x1 << 14) | (0x1 << 12) | (0x1 << 11) | + (0x1 << 7) | (0x1 << 5)); + + OS_DELAY(10); /* 10 usec */ + + /* clear off[6:0] */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); + reg_val &= 0xfc0fffff; + OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); + reg_val &= 0xfdffffff; + OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); + + offs_6_1 = 0; + for (i = 6; i > 0; i--) { + /* sef off[$k]==1 */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); + reg_val &= 0xfc0fffff; + reg_val = reg_val | (0x1 << (19 + i)) | ((offs_6_1) << 20); + OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); + lo_gn = (OS_REG_READ(ah, AR9285_AN_RF2G9)) & 0x1; + offs_6_1 = offs_6_1 | (lo_gn << (i - 1)); + } + + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); + reg_val &= 0xfc0fffff; + reg_val = reg_val | ((offs_6_1 - 1) << 20); + OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); + + /* set off_0=1; */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); + reg_val &= 0xfdffffff; + reg_val = reg_val | (0x1 << 25); + OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); + + lo_gn = OS_REG_READ(ah, AR9285_AN_RF2G9) & 0x1; + offs_0 = lo_gn; + + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); + reg_val &= 0xfdffffff; + reg_val = reg_val | (offs_0 << 25); + OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); + + /* clear pdv2i */ + reg_val = OS_REG_READ(ah, AR9285_AN_RXTXBB1); + reg_val &= 0xffffff5f; + OS_REG_WRITE(ah, AR9285_AN_RXTXBB1, reg_val); + + /* set enpacal */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); + reg_val |= (0x1 << 11); + OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); + + /* clear offcal */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G2); + reg_val &= 0xffffefff; + OS_REG_WRITE(ah, AR9285_AN_RF2G2, reg_val); + + /* set pdpadrv1=pdpadrv2=pdpaout=0 */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); + reg_val &= 0xfc7fffff; + OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); + + /* Read back reflo, decrease it by 1 and write it. */ + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); + reflo = (reg_val >> 26) & 0x7; + if (reflo) { + reflo--; + } + reg_val = ((reg_val & 0xe3ffffff) | (reflo << 26)); + OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); + reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); + reflo = (reg_val >> 26) & 0x7; + + /* write back registers */ + OS_REG_WRITE(ah, AR_PHY_TSTDAC_CONST, phy_tst_dac_reg_val); + OS_REG_WRITE(ah, AR_PHY_TEST2, phy_test2_reg_val); + OS_REG_WRITE(ah, AR_PHY_ADC_CTL, phy_adc_ctl_reg_val); + OS_REG_WRITE(ah, AR9285_AN_TOP2, an_top2_reg_val); + + /* Kite 1.1 WAR for Bug 35666 + * Decrease the LDO value back to 1.20V */ + if (AR_SREV_KITE_11(ah)) { + OS_REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); + } +} +#endif + +/* ar9300_run_init_cals + * Runs non-periodic calibrations + */ +inline static HAL_BOOL +ar9300_run_init_cals(struct ath_hal *ah, int init_cal_count) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + HAL_CHANNEL_INTERNAL ichan; /* bogus */ + HAL_BOOL is_cal_done; + HAL_CAL_LIST *curr_cal; + int i; + + curr_cal = ahp->ah_cal_list_curr; + if (curr_cal == AH_NULL) { + return AH_FALSE; + } + ichan.cal_valid = 0; + + for (i = 0; i < init_cal_count; i++) { + /* Reset this Cal */ + ar9300_reset_calibration(ah, curr_cal); + /* Poll for offset calibration complete */ + if (!ath_hal_wait( + ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL, 0, AH_WAIT_TIMEOUT)) + { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Cal %d failed to complete in 100ms.\n", + __func__, curr_cal->cal_data->cal_type); + /* Re-initialize list pointers for periodic cals */ + ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr + = AH_NULL; + return AH_FALSE; + } + /* Run this cal */ + ar9300_per_calibration( + ah, &ichan, ahp->ah_rx_chainmask, curr_cal, &is_cal_done); + if (is_cal_done == AH_FALSE) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Not able to run Init Cal %d.\n", __func__, + curr_cal->cal_data->cal_type); + } + if (curr_cal->cal_next) { + curr_cal = curr_cal->cal_next; + } + } + + /* Re-initialize list pointers for periodic cals */ + ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = AH_NULL; + return AH_TRUE; +} + +#if 0 +static void +ar9300_tx_carrier_leak_war(struct ath_hal *ah) +{ + unsigned long tx_gain_table_max; + unsigned long reg_bb_cl_map_0_b0 = 0xffffffff; + unsigned long reg_bb_cl_map_1_b0 = 0xffffffff; + unsigned long reg_bb_cl_map_2_b0 = 0xffffffff; + unsigned long reg_bb_cl_map_3_b0 = 0xffffffff; + unsigned long tx_gain, cal_run = 0; + unsigned long cal_gain[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; + unsigned long cal_gain_index[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; + unsigned long new_gain[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; + int i, j; + + OS_MEMSET(new_gain, 0, sizeof(new_gain)); + /*printf(" Running TxCarrierLeakWAR\n");*/ + + /* process tx gain table, we use cl_map_hw_gen=0. */ + OS_REG_RMW_FIELD(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_MAP_HW_GEN, 0); + + //the table we used is txbb_gc[2:0], 1dB[2:1]. + tx_gain_table_max = OS_REG_READ_FIELD(ah, + AR_PHY_TPC_7, AR_PHY_TPC_7_TX_GAIN_TABLE_MAX); + + for (i = 0; i <= tx_gain_table_max; i++) { + tx_gain = OS_REG_READ(ah, AR_PHY_TXGAIN_TAB(1) + i * 4); + cal_gain[i] = (((tx_gain >> 5)& 0x7) << 2) | + (((tx_gain >> 1) & 0x3) << 0); + if (i == 0) { + cal_gain_index[i] = cal_run; + new_gain[i] = 1; + cal_run++; + } else { + new_gain[i] = 1; + for (j = 0; j < i; j++) { + /* + printf("i=%d, j=%d cal_gain[$i]=0x%04x\n", i, j, cal_gain[i]); + */ + if (new_gain[i]) { + if ((cal_gain[i] != cal_gain[j])) { + new_gain[i] = 1; + } else { + /* if old gain found, use old cal_run value. */ + new_gain[i] = 0; + cal_gain_index[i] = cal_gain_index[j]; + } + } + } + /* if new gain found, increase cal_run */ + if (new_gain[i] == 1) { + cal_gain_index[i] = cal_run; + cal_run++; + } + } + + reg_bb_cl_map_0_b0 = (reg_bb_cl_map_0_b0 & ~(0x1 << i)) | + ((cal_gain_index[i] >> 0 & 0x1) << i); + reg_bb_cl_map_1_b0 = (reg_bb_cl_map_1_b0 & ~(0x1 << i)) | + ((cal_gain_index[i] >> 1 & 0x1) << i); + reg_bb_cl_map_2_b0 = (reg_bb_cl_map_2_b0 & ~(0x1 << i)) | + ((cal_gain_index[i] >> 2 & 0x1) << i); + reg_bb_cl_map_3_b0 = (reg_bb_cl_map_3_b0 & ~(0x1 << i)) | + ((cal_gain_index[i] >> 3 & 0x1) << i); + + /* + printf("i=%2d, cal_gain[$i]= 0x%04x, cal_run= %d, " + "cal_gain_index[i]=%d, new_gain[i] = %d\n", + i, cal_gain[i], cal_run, cal_gain_index[i], new_gain[i]); + */ + } + OS_REG_WRITE(ah, AR_PHY_CL_MAP_0_B0, reg_bb_cl_map_0_b0); + OS_REG_WRITE(ah, AR_PHY_CL_MAP_1_B0, reg_bb_cl_map_1_b0); + OS_REG_WRITE(ah, AR_PHY_CL_MAP_2_B0, reg_bb_cl_map_2_b0); + OS_REG_WRITE(ah, AR_PHY_CL_MAP_3_B0, reg_bb_cl_map_3_b0); + if (AR_SREV_WASP(ah)) { + OS_REG_WRITE(ah, AR_PHY_CL_MAP_0_B1, reg_bb_cl_map_0_b0); + OS_REG_WRITE(ah, AR_PHY_CL_MAP_1_B1, reg_bb_cl_map_1_b0); + OS_REG_WRITE(ah, AR_PHY_CL_MAP_2_B1, reg_bb_cl_map_2_b0); + OS_REG_WRITE(ah, AR_PHY_CL_MAP_3_B1, reg_bb_cl_map_3_b0); + } +} +#endif + + +static inline void +ar9300_invalidate_saved_cals(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) +{ +#if ATH_SUPPORT_CAL_REUSE + if (AH_PRIVATE(ah)->ah_config.ath_hal_cal_reuse & + ATH_CAL_REUSE_REDO_IN_FULL_RESET) + { + ichan->one_time_txiqcal_done = AH_FALSE; + ichan->one_time_txclcal_done = AH_FALSE; + } +#endif +} + +static inline HAL_BOOL +ar9300_restore_rtt_cals(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) +{ + HAL_BOOL restore_status = AH_FALSE; + + return restore_status; +} + +/* ar9300_init_cal + * Initialize Calibration infrastructure + */ +static inline HAL_BOOL +ar9300_init_cal_internal(struct ath_hal *ah, HAL_CHANNEL *chan, + HAL_CHANNEL_INTERNAL *ichan, HAL_BOOL enable_rtt, + HAL_BOOL do_rtt_cal, HAL_BOOL skip_if_none, HAL_BOOL apply_last_iqcorr) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + HAL_BOOL txiqcal_success_flag = AH_FALSE; + HAL_BOOL cal_done = AH_FALSE; + int iqcal_idx = 0; + HAL_BOOL do_sep_iq_cal = AH_FALSE; + HAL_BOOL do_agc_cal = do_rtt_cal; + HAL_BOOL is_cal_reusable = AH_TRUE; +#if ATH_SUPPORT_CAL_REUSE + HAL_BOOL cal_reuse_enable = AH_PRIVATE(ah)->ah_config.ath_hal_cal_reuse & + ATH_CAL_REUSE_ENABLE; + HAL_BOOL clc_success = AH_FALSE; + int32_t ch_idx, j, cl_tab_reg; + u_int32_t BB_cl_tab_entry = MAX_BB_CL_TABLE_ENTRY; + u_int32_t BB_cl_tab_b[AR9300_MAX_CHAINS] = { + AR_PHY_CL_TAB_0, + AR_PHY_CL_TAB_1, + AR_PHY_CL_TAB_2 + }; +#endif + + if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { + /* Hornet: 1 x 1 */ + ahp->ah_rx_cal_chainmask = 0x1; + ahp->ah_tx_cal_chainmask = 0x1; + } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah)) { + /* Wasp/Jupiter: 2 x 2 */ + ahp->ah_rx_cal_chainmask = 0x3; + ahp->ah_tx_cal_chainmask = 0x3; + } else { + /* + * Osprey needs to be configured for the correct chain mode + * before running AGC/TxIQ cals. + */ + if (ahp->ah_enterprise_mode & AR_ENT_OTP_CHAIN2_DISABLE) { + /* chain 2 disabled - 2 chain mode */ + ahp->ah_rx_cal_chainmask = 0x3; + ahp->ah_tx_cal_chainmask = 0x3; + } else { + ahp->ah_rx_cal_chainmask = 0x7; + ahp->ah_tx_cal_chainmask = 0x7; + } + } + ar9300_init_chain_masks(ah, ahp->ah_rx_cal_chainmask, ahp->ah_tx_cal_chainmask); + + + if (ahp->tx_cl_cal_enable) { +#if ATH_SUPPORT_CAL_REUSE + /* disable Carrie Leak or set do_agc_cal accordingly */ + if (cal_reuse_enable && ichan->one_time_txclcal_done) + { + OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + } else +#endif /* ATH_SUPPORT_CAL_REUSE */ + { + OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + do_agc_cal = AH_TRUE; + } + } + + /* Do Tx IQ Calibration here for osprey hornet and wasp */ + /* XXX: For initial wasp bringup - check and enable this */ + /* EV 74233: Tx IQ fails to complete for half/quarter rates */ + if (!(IS_CHAN_HALF_RATE(ichan) || IS_CHAN_QUARTER_RATE(ichan))) { + if (ahp->tx_iq_cal_enable) { + /* this should be eventually moved to INI file */ + OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1(ah), + AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, DELPT); + + /* + * For poseidon and later chips, + * Tx IQ cal HW run will be a part of AGC calibration + */ + if (ahp->tx_iq_cal_during_agc_cal) { + /* + * txiqcal_success_flag always set to 1 to run + * ar9300_tx_iq_cal_post_proc + * if following AGC cal passes + */ +#if ATH_SUPPORT_CAL_REUSE + if (!cal_reuse_enable || !ichan->one_time_txiqcal_done) + { + txiqcal_success_flag = AH_TRUE; + OS_REG_WRITE(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), + OS_REG_READ(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah)) | + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); + } else { + OS_REG_WRITE(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), + OS_REG_READ(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah)) & + (~AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)); + } +#else + if (OS_REG_READ_FIELD(ah, + AR_PHY_TX_IQCAL_CONTROL_0(ah), + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)){ + if (apply_last_iqcorr == AH_TRUE) { + OS_REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); + txiqcal_success_flag = AH_FALSE; + } else { + txiqcal_success_flag = AH_TRUE; + } + }else{ + txiqcal_success_flag = AH_FALSE; + } +#endif + if (txiqcal_success_flag) { + do_agc_cal = AH_TRUE; + } + } else +#if ATH_SUPPORT_CAL_REUSE + if (!cal_reuse_enable || !ichan->one_time_txiqcal_done) +#endif + { + do_sep_iq_cal = AH_TRUE; + do_agc_cal = AH_TRUE; + } + } + } + +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support && + IS_CHAN_2GHZ(ichan) && + (ahp->ah_mci_bt_state == MCI_BT_AWAKE) && + do_agc_cal && + !(AH_PRIVATE(ah)->ah_config.ath_hal_mci_config & + ATH_MCI_CONFIG_DISABLE_MCI_CAL)) + { + u_int32_t payload[4] = {0, 0, 0, 0}; + + /* Send CAL_REQ only when BT is AWAKE. */ + HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Send WLAN_CAL_REQ 0x%X\n", + __func__, ahp->ah_mci_wlan_cal_seq); + MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_REQ); + payload[MCI_GPM_WLAN_CAL_W_SEQUENCE] = ahp->ah_mci_wlan_cal_seq++; + ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); + + /* Wait BT_CAL_GRANT for 50ms */ + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) %s: Wait for BT_CAL_GRANT\n", __func__); + if (ar9300_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) + { + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) %s: Got BT_CAL_GRANT.\n", __func__); + } + else { + is_cal_reusable = AH_FALSE; + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) %s: BT is not responding.\n", __func__); + } + } +#endif /* ATH_SUPPORT_MCI */ + + if (do_sep_iq_cal) + { + /* enable Tx IQ Calibration HW for osprey/hornet/wasp */ + txiqcal_success_flag = ar9300_tx_iq_cal_hw_run(ah); + OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); + OS_DELAY(5); + OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + } +#if 0 + if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { + ar9300_tx_carrier_leak_war(ah); + } +#endif + /* + * Calibrate the AGC + * + * Tx IQ cal is a part of AGC cal for Jupiter/Poseidon, etc. + * please enable the bit of txiqcal_control_0[31] in INI file + * for Jupiter/Poseidon/etc. + */ + if(!AR_SREV_SCORPION(ah)) { + if (do_agc_cal || !skip_if_none) { + OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, + OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); + + /* Poll for offset calibration complete */ + cal_done = ath_hal_wait(ah, + AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); + if (!cal_done) { + HALDEBUG(ah, HAL_DEBUG_FCS_RTT, + "(FCS) CAL NOT DONE!!! - %d\n", ichan->channel); + } + } else { + cal_done = AH_TRUE; + } + /* + * Tx IQ cal post-processing in SW + * This part of code should be common to all chips, + * no chip specific code for Jupiter/Posdeion except for register names. + */ + if (txiqcal_success_flag) { + ar9300_tx_iq_cal_post_proc(ah,ichan, 1, 1,is_cal_reusable,false); + } + } else { + if (!txiqcal_success_flag) { + OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, + OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); + if (!ath_hal_wait( ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, + 0, AH_WAIT_TIMEOUT)) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: offset calibration failed to complete in 1ms; " + "noisy environment?\n", __func__); + return AH_FALSE; + } + if (apply_last_iqcorr == AH_TRUE) { + ar9300_tx_iq_cal_post_proc(ah, ichan, 0, 0, is_cal_reusable, AH_TRUE); + } + } else { + for (iqcal_idx=0;iqcal_idx<MAXIQCAL;iqcal_idx++) { + OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, + OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); + + /* Poll for offset calibration complete */ + if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT)) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: offset calibration failed to complete in 1ms; " + "noisy environment?\n", __func__); + return AH_FALSE; + } + /* + * Tx IQ cal post-processing in SW + * This part of code should be common to all chips, + * no chip specific code for Jupiter/Posdeion except for register names. + */ + ar9300_tx_iq_cal_post_proc(ah, ichan, iqcal_idx+1, MAXIQCAL, is_cal_reusable, AH_FALSE); + } + } + } + + +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support && + IS_CHAN_2GHZ(ichan) && + (ahp->ah_mci_bt_state == MCI_BT_AWAKE) && + do_agc_cal && + !(AH_PRIVATE(ah)->ah_config.ath_hal_mci_config & + ATH_MCI_CONFIG_DISABLE_MCI_CAL)) + { + u_int32_t payload[4] = {0, 0, 0, 0}; + + HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Send WLAN_CAL_DONE 0x%X\n", + __func__, ahp->ah_mci_wlan_cal_done); + MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); + payload[MCI_GPM_WLAN_CAL_W_SEQUENCE] = ahp->ah_mci_wlan_cal_done++; + ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); + } +#endif /* ATH_SUPPORT_MCI */ + + + if (!cal_done && !AR_SREV_SCORPION(ah) ) + { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: offset calibration failed to complete in 1ms; " + "noisy environment?\n", __func__); + return AH_FALSE; + } + +#if 0 + /* Beacon stuck fix, refer to EV 120056 */ + if(IS_CHAN_2GHZ(chan) && AR_SREV_SCORPION(ah)) + OS_REG_WRITE(ah, AR_PHY_TIMING5, OS_REG_READ(ah,AR_PHY_TIMING5) & ~AR_PHY_TIMING5_CYCPWR_THR1_ENABLE); +#endif + +#if 0 + /* Do PA Calibration */ + if (AR_SREV_KITE(ah) && AR_SREV_KITE_11_OR_LATER(ah)) { + ar9285_pa_cal(ah); + } +#endif + +#if ATH_SUPPORT_CAL_REUSE + if (ichan->one_time_txiqcal_done) { + ar9300_tx_iq_cal_apply(ah, ichan); + HALDEBUG(ah, HAL_DEBUG_FCS_RTT, + "(FCS) TXIQCAL applied - %d\n", ichan->channel); + } +#endif /* ATH_SUPPORT_CAL_REUSE */ + +#if ATH_SUPPORT_CAL_REUSE + if (cal_reuse_enable && ahp->tx_cl_cal_enable) + { + clc_success = (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & + AR_PHY_AGC_CONTROL_CLC_SUCCESS) ? 1 : 0; + + if (ichan->one_time_txclcal_done) + { + /* reapply CL cal results */ + for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { + if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { + continue; + } + cl_tab_reg = BB_cl_tab_b[ch_idx]; + for (j = 0; j < BB_cl_tab_entry; j++) { + OS_REG_WRITE(ah, cl_tab_reg, ichan->tx_clcal[ch_idx][j]); + cl_tab_reg += 4;; + } + } + HALDEBUG(ah, HAL_DEBUG_FCS_RTT, + "(FCS) TX CL CAL applied - %d\n", ichan->channel); + } + else if (is_cal_reusable && clc_success) { + /* save CL cal results */ + for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { + if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { + continue; + } + cl_tab_reg = BB_cl_tab_b[ch_idx]; + for (j = 0; j < BB_cl_tab_entry; j++) { + ichan->tx_clcal[ch_idx][j] = OS_REG_READ(ah, cl_tab_reg); + cl_tab_reg += 4; + } + } + ichan->one_time_txclcal_done = AH_TRUE; + HALDEBUG(ah, HAL_DEBUG_FCS_RTT, + "(FCS) TX CL CAL saved - %d\n", ichan->channel); + } + } +#endif /* ATH_SUPPORT_CAL_REUSE */ + + + /* Revert chainmasks to their original values before NF cal */ + ar9300_init_chain_masks(ah, ahp->ah_rx_chainmask, ahp->ah_tx_chainmask); + +#if !FIX_NOISE_FLOOR + /* + * Do NF calibration after DC offset and other CALs. + * Per system engineers, noise floor value can sometimes be 20 dB + * higher than normal value if DC offset and noise floor cal are + * triggered at the same time. + */ + OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, + OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_NF); +#endif + + /* Initialize list pointers */ + ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = AH_NULL; + + /* + * Enable IQ, ADC Gain, ADC DC Offset Cals + */ + /* Setup all non-periodic, init time only calibrations */ + /* XXX: Init DC Offset not working yet */ +#ifdef not_yet + if (AH_TRUE == ar9300_is_cal_supp(ah, chan, ADC_DC_INIT_CAL)) { + INIT_CAL(&ahp->ah_adc_dc_cal_init_data); + INSERT_CAL(ahp, &ahp->ah_adc_dc_cal_init_data); + } + + /* Initialize current pointer to first element in list */ + ahp->ah_cal_list_curr = ahp->ah_cal_list; + + if (ahp->ah_cal_list_curr) { + if (ar9300_run_init_cals(ah, 0) == AH_FALSE) { + return AH_FALSE; + } + } +#endif + /* end - Init time calibrations */ + + /* If Cals are supported, add them to list via INIT/INSERT_CAL */ + if (AH_TRUE == ar9300_is_cal_supp(ah, chan, IQ_MISMATCH_CAL)) { + INIT_CAL(&ahp->ah_iq_cal_data); + INSERT_CAL(ahp, &ahp->ah_iq_cal_data); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: enabling IQ Calibration.\n", __func__); + } + if (AH_TRUE == ar9300_is_cal_supp(ah, chan, TEMP_COMP_CAL)) { + INIT_CAL(&ahp->ah_temp_comp_cal_data); + INSERT_CAL(ahp, &ahp->ah_temp_comp_cal_data); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: enabling Temperature Compensation Calibration.\n", __func__); + } + + /* Initialize current pointer to first element in list */ + ahp->ah_cal_list_curr = ahp->ah_cal_list; + + /* Reset state within current cal */ + if (ahp->ah_cal_list_curr) { + ar9300_reset_calibration(ah, ahp->ah_cal_list_curr); + } + + /* Mark all calibrations on this channel as being invalid */ + ichan->cal_valid = 0; + + return AH_TRUE; +} + +static inline HAL_BOOL +ar9300_init_cal(struct ath_hal *ah, HAL_CHANNEL *chan, HAL_BOOL skip_if_none, HAL_BOOL apply_last_iqcorr) +{ + HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); + HAL_BOOL do_rtt_cal = AH_TRUE; + HAL_BOOL enable_rtt = AH_FALSE; + + HALASSERT(ichan); + + + + return ar9300_init_cal_internal(ah, chan, ichan, enable_rtt, do_rtt_cal, skip_if_none, apply_last_iqcorr); +} + +/* ar9300_reset_cal_valid + * Entry point for upper layers to restart current cal. + * Reset the calibration valid bit in channel. + */ +void +ar9300_reset_cal_valid(struct ath_hal *ah, HAL_CHANNEL *chan, + HAL_BOOL *is_cal_done, u_int32_t cal_type) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); + HAL_CAL_LIST *curr_cal = ahp->ah_cal_list_curr; + + *is_cal_done = AH_TRUE; + + if (curr_cal == AH_NULL) { + return; + } + if (ichan == AH_NULL) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channel_flags); + return; + } + + if (!(cal_type & IQ_MISMATCH_CAL)) { + *is_cal_done = AH_FALSE; + return; + } + + /* Expected that this calibration has run before, post-reset. + * Current state should be done + */ + if (curr_cal->cal_state != CAL_DONE) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Calibration state incorrect, %d\n", + __func__, curr_cal->cal_state); + return; + } + + /* Verify Cal is supported on this channel */ + if (ar9300_is_cal_supp(ah, chan, curr_cal->cal_data->cal_type) == AH_FALSE) { + return; + } + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Resetting Cal %d state for channel %u/0x%x\n", __func__, + curr_cal->cal_data->cal_type, chan->channel, chan->channel_flags); + + /* Disable cal validity in channel */ + ichan->cal_valid &= ~curr_cal->cal_data->cal_type; + curr_cal->cal_state = CAL_WAITING; + /* Indicate to upper layers that we need polling */ + *is_cal_done = AH_FALSE; +} + +static inline void +ar9300_set_dma(struct ath_hal *ah) +{ + u_int32_t regval; + struct ath_hal_9300 *ahp = AH9300(ah); + +#if 0 + /* + * set AHB_MODE not to do cacheline prefetches + */ + regval = OS_REG_READ(ah, AR_AHB_MODE); + OS_REG_WRITE(ah, AR_AHB_MODE, regval | AR_AHB_PREFETCH_RD_EN); +#endif + + /* + * let mac dma reads be in 128 byte chunks + */ + regval = OS_REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK; + OS_REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B); + + /* + * Restore TX Trigger Level to its pre-reset value. + * The initial value depends on whether aggregation is enabled, and is + * adjusted whenever underruns are detected. + */ + /* + OS_REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, AH_PRIVATE(ah)->ah_tx_trig_level); + */ + /* + * Osprey 1.0 bug (EV 61936). Don't change trigger level from .ini default. + * Osprey 2.0 - hardware recommends using the default INI settings. + */ +#if 0 + OS_REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, 0x3f); +#endif + /* + * let mac dma writes be in 128 byte chunks + */ + regval = OS_REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK; + OS_REG_WRITE(ah, AR_RXCFG, regval | AR_RXCFG_DMASZ_128B); + + /* + * Setup receive FIFO threshold to hold off TX activities + */ + OS_REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); + + /* + * reduce the number of usable entries in PCU TXBUF to avoid + * wrap around bugs. (bug 20428) + */ + + if (AR_SREV_WASP(ah) && + (AH_PRIVATE((ah))->ah_macRev > AR_SREV_REVISION_WASP_12)) { + /* Wasp 1.3 fix for EV#85395 requires usable entries + * to be set to 0x500 + */ + OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, 0x500); + } else { + OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_PCU_TXBUF_CTRL_USABLE_SIZE); + } + + /* + * Enable HPQ for UAPSD + */ + if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { + OS_REG_WRITE(ah, AR_HP_Q_CONTROL, + AR_HPQ_ENABLE | AR_HPQ_UAPSD | AR_HPQ_UAPSD_TRIGGER_EN); + } + + /* + * set the transmit status ring + */ + ar9300_reset_tx_status_ring(ah); + + /* + * set rxbp threshold. Must be non-zero for RX_EOL to occur. + * For Osprey 2.0+, keep the original thresholds + * otherwise performance is lost due to excessive RX EOL interrupts. + */ + OS_REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_HP, 0x1); + OS_REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_LP, 0x1); + + /* + * set receive buffer size. + */ + if (ahp->rx_buf_size) { + OS_REG_WRITE(ah, AR_DATABUF, ahp->rx_buf_size); + } +} + +static inline void +ar9300_init_bb(struct ath_hal *ah, HAL_CHANNEL *chan) +{ + u_int32_t synth_delay; + + /* + * Wait for the frequency synth to settle (synth goes on + * via AR_PHY_ACTIVE_EN). Read the phy active delay register. + * Value is in 100ns increments. + */ + synth_delay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; + if (IS_CHAN_CCK(chan)) { + synth_delay = (4 * synth_delay) / 22; + } else { + synth_delay /= 10; + } + + /* Activate the PHY (includes baseband activate + synthesizer on) */ + OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + /* + * There is an issue if the AP starts the calibration before + * the base band timeout completes. This could result in the + * rx_clear AH_FALSE triggering. As a workaround we add delay an + * extra BASE_ACTIVATE_DELAY usecs to ensure this condition + * does not happen. + */ + OS_DELAY(synth_delay + BASE_ACTIVATE_DELAY); +} + +static inline void +ar9300_init_interrupt_masks(struct ath_hal *ah, HAL_OPMODE opmode) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + u_int32_t msi_cfg = 0; + u_int32_t sync_en_def = AR9300_INTR_SYNC_DEFAULT; + + /* + * Setup interrupt handling. Note that ar9300_reset_tx_queue + * manipulates the secondary IMR's as queues are enabled + * and disabled. This is done with RMW ops to insure the + * settings we make here are preserved. + */ + ahp->ah_mask_reg = + AR_IMR_TXERR | AR_IMR_TXURN | + AR_IMR_RXERR | AR_IMR_RXORN | + AR_IMR_BCNMISC; + + if (ahp->ah_intr_mitigation_rx) { + /* enable interrupt mitigation for rx */ + ahp->ah_mask_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR | AR_IMR_RXOK_HP; + msi_cfg |= AR_INTCFG_MSI_RXINTM | AR_INTCFG_MSI_RXMINTR; + } else { + ahp->ah_mask_reg |= AR_IMR_RXOK_LP | AR_IMR_RXOK_HP; + msi_cfg |= AR_INTCFG_MSI_RXOK; + } + if (ahp->ah_intr_mitigation_tx) { + /* enable interrupt mitigation for tx */ + ahp->ah_mask_reg |= AR_IMR_TXINTM | AR_IMR_TXMINTR; + msi_cfg |= AR_INTCFG_MSI_TXINTM | AR_INTCFG_MSI_TXMINTR; + } else { + ahp->ah_mask_reg |= AR_IMR_TXOK; + msi_cfg |= AR_INTCFG_MSI_TXOK; + } + if (opmode == HAL_M_HOSTAP) { + ahp->ah_mask_reg |= AR_IMR_MIB; + } + + OS_REG_WRITE(ah, AR_IMR, ahp->ah_mask_reg); + OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT); + ahp->ah_mask2Reg = OS_REG_READ(ah, AR_IMR_S2); + + if (AH_PRIVATE(ah)->ah_config.ath_hal_enable_msi) { + /* Cache MSI register value */ + ahp->ah_msi_reg = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_MSI)); + ahp->ah_msi_reg |= AR_PCIE_MSI_HW_DBI_WR_EN; + if (AR_SREV_POSEIDON(ah)) { + ahp->ah_msi_reg &= AR_PCIE_MSI_HW_INT_PENDING_ADDR_MSI_64; + } else { + ahp->ah_msi_reg &= AR_PCIE_MSI_HW_INT_PENDING_ADDR; + } + /* Program MSI configuration */ + OS_REG_WRITE(ah, AR_INTCFG, msi_cfg); + } + + /* + * debug - enable to see all synchronous interrupts status + */ + /* Clear any pending sync cause interrupts */ + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE), 0xFFFFFFFF); + + /* Allow host interface sync interrupt sources to set cause bit */ + if (AR_SREV_POSEIDON(ah)) { + sync_en_def = AR9300_INTR_SYNC_DEF_NO_HOST1_PERR; + } + else if (AR_SREV_WASP(ah)) { + sync_en_def = AR9340_INTR_SYNC_DEFAULT; + } + OS_REG_WRITE(ah, + AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), sync_en_def); + + /* _Disable_ host interface sync interrupt when cause bits set */ + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_MASK), 0); + + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_ENABLE), 0); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_MASK), 0); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_ENABLE), 0); + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_MASK), 0); +} + +static inline void +ar9300_init_qos(struct ath_hal *ah) +{ + OS_REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa); /* XXX magic */ + OS_REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210); /* XXX magic */ + + /* Turn on NOACK Support for QoS packets */ + OS_REG_WRITE(ah, AR_QOS_NO_ACK, + SM(2, AR_QOS_NO_ACK_TWO_BIT) | + SM(5, AR_QOS_NO_ACK_BIT_OFF) | + SM(0, AR_QOS_NO_ACK_BYTE_OFF)); + + /* + * initialize TXOP for all TIDs + */ + OS_REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); + OS_REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); + OS_REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); + OS_REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); + OS_REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); +} + +static inline void +ar9300_init_user_settings(struct ath_hal *ah) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + + /* Restore user-specified settings */ + HALDEBUG(ah, HAL_DEBUG_RESET, + "--AP %s ahp->ah_misc_mode 0x%x\n", __func__, ahp->ah_misc_mode); + if (ahp->ah_misc_mode != 0) { + OS_REG_WRITE(ah, + AR_PCU_MISC, OS_REG_READ(ah, AR_PCU_MISC) | ahp->ah_misc_mode); + } + if (ahp->ah_get_plcp_hdr) { + OS_REG_CLR_BIT(ah, AR_PCU_MISC, AR_PCU_SEL_EVM); + } + if (ahp->ah_slot_time != (u_int) -1) { + ar9300_set_slot_time(ah, ahp->ah_slot_time); + } + if (ahp->ah_ack_timeout != (u_int) -1) { + ar9300_set_ack_timeout(ah, ahp->ah_ack_timeout); + } + if (AH_PRIVATE(ah)->ah_diagreg != 0) { + OS_REG_SET_BIT(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); + } + if (ahp->ah_beacon_rssi_threshold != 0) { + ar9300_set_hw_beacon_rssi_threshold(ah, ahp->ah_beacon_rssi_threshold); + } +#ifdef ATH_SUPPORT_DFS + if (ahp->ah_cac_quiet_enabled) { + ar9300_cac_tx_quiet(ah, 1); + } +#endif /* ATH_SUPPORT_DFS */ +} + +int +ar9300_get_spur_info(struct ath_hal * ah, int *enable, int len, u_int16_t *freq) +{ + struct ath_hal_private *ap = AH_PRIVATE(ah); + int i, j; + + for (i = 0; i < len; i++) { + freq[i] = 0; + } + + *enable = ap->ah_config.ath_hal_spur_mode; + for (i = 0, j = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + if (ap->ah_config.ath_hal_spur_chans[i][0] != AR_NO_SPUR) { + freq[j++] = ap->ah_config.ath_hal_spur_chans[i][0]; + HALDEBUG(ah, HAL_DEBUG_ANI, + "1. get spur %d\n", ap->ah_config.ath_hal_spur_chans[i][0]); + } + if (ap->ah_config.ath_hal_spur_chans[i][1] != AR_NO_SPUR) { + freq[j++] = ap->ah_config.ath_hal_spur_chans[i][1]; + HALDEBUG(ah, HAL_DEBUG_ANI, + "2. get spur %d\n", ap->ah_config.ath_hal_spur_chans[i][1]); + } + } + + return 0; +} + +#define ATH_HAL_2GHZ_FREQ_MIN 20000 +#define ATH_HAL_2GHZ_FREQ_MAX 29999 +#define ATH_HAL_5GHZ_FREQ_MIN 50000 +#define ATH_HAL_5GHZ_FREQ_MAX 59999 + +int +ar9300_set_spur_info(struct ath_hal * ah, int enable, int len, u_int16_t *freq) +{ + struct ath_hal_private *ap = AH_PRIVATE(ah); + int i, j, k; + + ap->ah_config.ath_hal_spur_mode = enable; + + if (ap->ah_config.ath_hal_spur_mode == SPUR_ENABLE_IOCTL) { + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + ap->ah_config.ath_hal_spur_chans[i][0] = AR_NO_SPUR; + ap->ah_config.ath_hal_spur_chans[i][1] = AR_NO_SPUR; + } + for (i = 0, j = 0, k = 0; i < len; i++) { + if (freq[i] > ATH_HAL_2GHZ_FREQ_MIN && + freq[i] < ATH_HAL_2GHZ_FREQ_MAX) + { + /* 2GHz Spur */ + if (j < AR_EEPROM_MODAL_SPURS) { + ap->ah_config.ath_hal_spur_chans[j++][1] = freq[i]; + HALDEBUG(ah, HAL_DEBUG_ANI, "1 set spur %d\n", freq[i]); + } + } else if (freq[i] > ATH_HAL_5GHZ_FREQ_MIN && + freq[i] < ATH_HAL_5GHZ_FREQ_MAX) + { + /* 5Ghz Spur */ + if (k < AR_EEPROM_MODAL_SPURS) { + ap->ah_config.ath_hal_spur_chans[k++][0] = freq[i]; + HALDEBUG(ah, HAL_DEBUG_ANI, "2 set spur %d\n", freq[i]); + } + } + } + } + + return 0; +} + +#define ar9300_check_op_mode(_opmode) \ + ((_opmode == HAL_M_STA) || (_opmode == HAL_M_IBSS) ||\ + (_opmode == HAL_M_HOSTAP) || (_opmode == HAL_M_MONITOR)) + + + + +#ifndef ATH_NF_PER_CHAN +/* +* To fixed first reset noise floor value not correct issue +* For ART need it to fixed low rate sens too low issue +*/ +static int +First_NFCal(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, + int is_scan, HAL_CHANNEL *chan) +{ + HAL_NFCAL_HIST_FULL *nfh; + int i, j, k; + int16_t nfarray[NUM_NF_READINGS] = {0}; + int is_2g = 0; + int nf_hist_len; + int stats = 0; + + int16_t nf_buf[NUM_NF_READINGS]; +#define IS(_c, _f) (((_c)->channel_flags & _f) || 0) + + + if ((!is_scan) && + chan->channel == AH_PRIVATE(ah)->ah_curchan->channel) + { + nfh = &AH_PRIVATE(ah)->nf_cal_hist; + } else { + nfh = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; + } + + ar9300_start_nf_cal(ah); + for (j = 0; j < 10000; j++) { + if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0){ + break; + } + OS_DELAY(10); + } + if (j < 10000) { + is_2g = IS(ichan, CHANNEL_2GHZ); + ar9300_upload_noise_floor(ah, is_2g, nfarray); + + if (is_scan) { + /* + * This channel's NF cal info is just a HAL_NFCAL_HIST_SMALL struct + * rather than a HAL_NFCAL_HIST_FULL struct. + * As long as we only use the first history element of nf_cal_buffer + * (nf_cal_buffer[0][0:NUM_NF_READINGS-1]), we can use + * HAL_NFCAL_HIST_SMALL and HAL_NFCAL_HIST_FULL interchangeably. + */ + nfh = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; + nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; + } else { + nfh = &AH_PRIVATE(ah)->nf_cal_hist; + nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; + } + + for (i = 0; i < NUM_NF_READINGS; i ++) { + for (k = 0; k < HAL_NF_CAL_HIST_LEN_FULL; k++) { + nfh->nf_cal_buffer[k][i] = nfarray[i]; + } + nfh->base.priv_nf[i] = ar9300_limit_nf_range(ah, + ar9300_get_nf_hist_mid(ah, nfh, i, nf_hist_len)); + } + + + //ar9300StoreNewNf(ah, ichan, is_scan); + + /* + * See if the NF value from the old channel should be + * retained when switching to a new channel. + * TBD: this may need to be changed, as it wipes out the + * purpose of saving NF values for each channel. + */ + for (i = 0; i < NUM_NF_READINGS; i++) + { + if (IS_CHAN_2GHZ(chan)) + { + if (nfh->nf_cal_buffer[0][i] < + AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_2GHZ) + { + ichan->nf_cal_hist.nf_cal_buffer[0][i] = + AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; + } + } else { + if (AR_SREV_AR9580(ah)) { + if (nfh->nf_cal_buffer[0][i] < + AR_PHY_CCA_NOM_VAL_PEACOCK_5GHZ) + { + ichan->nf_cal_hist.nf_cal_buffer[0][i] = + AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; + } + } else { + if (nfh->nf_cal_buffer[0][i] < + AR_PHY_CCA_NOM_VAL_OSPREY_5GHZ) + { + ichan->nf_cal_hist.nf_cal_buffer[0][i] = + AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; + } + } + } + } + /* + * Copy the channel's NF buffer, which may have been modified + * just above here, to the full NF history buffer. + */ + ar9300_reset_nf_hist_buff(ah, ichan); + ar9300_get_nf_hist_base(ah, + AH_PRIVATE(ah)->ah_curchan, is_scan, nf_buf); + ar9300_load_nf(ah, nf_buf); + stats = 0; + } else { + stats = 1; + } +#undef IS + return stats; +} +#endif + + +/* + * Places the device in and out of reset and then places sane + * values in the registers based on EEPROM config, initialization + * vectors (as determined by the mode), and station configuration + * + * b_channel_change is used to preserve DMA/PCU registers across + * a HW Reset during channel change. + */ +HAL_BOOL +ar9300_reset(struct ath_hal *ah, HAL_OPMODE opmode, HAL_CHANNEL *chan, + HAL_HT_MACMODE macmode, u_int8_t txchainmask, u_int8_t rxchainmask, + HAL_HT_EXTPROTSPACING extprotspacing, HAL_BOOL b_channel_change, + HAL_STATUS *status, int is_scan) +{ +#define FAIL(_code) do { ecode = _code; goto bad; } while (0) + u_int32_t save_led_state; + struct ath_hal_9300 *ahp = AH9300(ah); + struct ath_hal_private *ap = AH_PRIVATE(ah); + HAL_CHANNEL_INTERNAL *ichan; + HAL_CHANNEL_INTERNAL *curchan = ap->ah_curchan; +#if ATH_SUPPORT_MCI + HAL_BOOL save_full_sleep = ahp->ah_chip_full_sleep; +#endif + u_int32_t save_def_antenna; + u_int32_t mac_sta_id1; + HAL_STATUS ecode; + int i, rx_chainmask; + int nf_hist_buff_reset = 0; + int16_t nf_buf[NUM_NF_READINGS]; +#ifdef ATH_FORCE_PPM + u_int32_t save_force_val, tmp_reg; +#endif + HAL_BOOL stopped, cal_ret; + HAL_BOOL apply_last_iqcorr = AH_FALSE; + + if (OS_REG_READ(ah, AR_IER) == AR_IER_ENABLE) { + HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "** Reset called with WLAN " + "interrupt enabled %08x **\n", ar9300_get_interrupts(ah)); + } + + /* + * Set the status to "ok" by default to cover the cases + * where we return AH_FALSE without going to "bad" + */ + HALASSERT(status); + *status = HAL_OK; + if ((AH_PRIVATE(ah)->ah_config.ath_hal_sta_update_tx_pwr_enable)) { + AH_PRIVATE(ah)->green_tx_status = HAL_RSSI_TX_POWER_NONE; + } + +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support && + (AR_SREV_JUPITER_20(ah) || AR_SREV_APHRODITE(ah))) + { + ar9300_mci_2g5g_changed(ah, IS_CHAN_2GHZ(chan)); + } +#endif + + ahp->ah_ext_prot_spacing = extprotspacing; + ahp->ah_tx_chainmask = txchainmask & ap->ah_caps.hal_tx_chain_mask; + ahp->ah_rx_chainmask = rxchainmask & ap->ah_caps.hal_rx_chain_mask; + ahp->ah_tx_cal_chainmask = ap->ah_caps.hal_tx_chain_mask; + ahp->ah_rx_cal_chainmask = ap->ah_caps.hal_rx_chain_mask; + HALASSERT(ar9300_check_op_mode(opmode)); + + OS_MARK(ah, AH_MARK_RESET, b_channel_change); + + /* + * Map public channel to private. + */ + ichan = ar9300_check_chan(ah, chan); + if (ichan == AH_NULL) { + HALDEBUG(ah, HAL_DEBUG_CHANNEL, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channel_flags); + FAIL(HAL_EINVAL); + } + + ichan->paprd_table_write_done = 0; /* Clear PAPRD table write flag */ + chan->paprd_table_write_done = 0; /* Clear PAPRD table write flag */ + + if (ar9300_get_power_mode(ah) != HAL_PM_FULL_SLEEP) { + /* Need to stop RX DMA before reset otherwise chip might hang */ + stopped = ar9300_set_rx_abort(ah, AH_TRUE); /* abort and disable PCU */ + ar9300_set_rx_filter(ah, 0); + stopped &= ar9300_stop_dma_receive(ah, 0); /* stop and disable RX DMA */ + if (!stopped) { + /* + * During the transition from full sleep to reset, + * recv DMA regs are not available to be read + */ + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s[%d]: ar9300_stop_dma_receive failed\n", __func__, __LINE__); + b_channel_change = AH_FALSE; + } + } else { + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s[%d]: Chip is already in full sleep\n", __func__, __LINE__); + } + +#if ATH_SUPPORT_MCI + if ((AH_PRIVATE(ah)->ah_caps.hal_mci_support) && + (ahp->ah_mci_bt_state == MCI_BT_CAL_START)) + { + u_int32_t payload[4] = {0, 0, 0, 0}; + + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) %s: Stop rx for BT cal.\n", __func__); + ahp->ah_mci_bt_state = MCI_BT_CAL; + + /* + * MCIFIX: disable mci interrupt here. This is to avoid SW_MSG_DONE or + * RX_MSG bits to trigger MCI_INT and lead to mci_intr reentry. + */ + ar9300_mci_disable_interrupt(ah); + + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) %s: Send WLAN_CAL_GRANT\n", __func__); + MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); + ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); + + /* Wait BT calibration to be completed for 25ms */ + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) %s: BT is calibrating.\n", __func__); + if (ar9300_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_DONE, 0, 25000)) { + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) %s: Got BT_CAL_DONE.\n", __func__); + } + else { + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) %s: ### BT cal takes too long. Force bt_state to be bt_awake.\n", + __func__); + } + ahp->ah_mci_bt_state = MCI_BT_AWAKE; + /* MCIFIX: enable mci interrupt here */ + ar9300_mci_enable_interrupt(ah); + + return AH_TRUE; + } +#endif + + /* Bring out of sleep mode */ + if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { + *status = HAL_INV_PMODE; + return AH_FALSE; + } + + /* Check the Rx mitigation config again, it might have changed + * during attach in ath_vap_attach. + */ + if (AH_PRIVATE(ah)->ah_config.ath_hal_intr_mitigation_rx != 0) { + ahp->ah_intr_mitigation_rx = AH_TRUE; + } else { + ahp->ah_intr_mitigation_rx = AH_FALSE; + } + + /* Get the value from the previous NF cal and update history buffer */ + if (curchan && (ahp->ah_chip_full_sleep != AH_TRUE)) { + ar9300_store_new_nf(ah, curchan, is_scan); + } + + /* + * Account for the effect of being in either the 2 GHz or 5 GHz band + * on the nominal, max allowable, and min allowable noise floor values. + */ + ap->nfp = IS_CHAN_2GHZ(chan) ? &ap->nf_2GHz : &ap->nf_5GHz; + + if (AR_SREV_SCORPION(ah) && curchan && (chan->channel == curchan->channel) && + ((chan->channel_flags & (CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER)) == + (curchan->channel_flags & + (CHANNEL_ALL | CHANNEL_HALF | CHANNEL_QUARTER)))) { + apply_last_iqcorr = AH_TRUE; + } + +#ifndef ATH_NF_PER_CHAN + /* + * If there's only one full-size home-channel NF history buffer + * rather than a full-size NF history buffer per channel, decide + * whether to (re)initialize the home-channel NF buffer. + * If this is just a channel change for a scan, or if the channel + * is not being changed, don't mess up the home channel NF history + * buffer with NF values from this scanned channel. If we're + * changing the home channel to a new channel, reset the home-channel + * NF history buffer with the most accurate NF known for the new channel. + */ + if (!is_scan && (!ap->ah_curchan || + ap->ah_curchan->channel != chan->channel || + ap->ah_curchan->channel_flags != chan->channel_flags)) + { + nf_hist_buff_reset = 1; + ar9300_reset_nf_hist_buff(ah, ichan); + } +#endif + /* + * Fast channel change (Change synthesizer based on channel freq + * without resetting chip) + * Don't do it when + * - Flag is not set + * - Chip is just coming out of full sleep + * - Channel to be set is same as current channel + * - Channel flags are different, like when moving from 2GHz to 5GHz + * channels + * - Merlin: Switching in/out of fast clock enabled channels + * (not currently coded, since fast clock is enabled + * across the 5GHz band + * and we already do a full reset when switching in/out + * of 5GHz channels) + */ + if (b_channel_change && + (ahp->ah_chip_full_sleep != AH_TRUE) && + (AH_PRIVATE(ah)->ah_curchan != AH_NULL) && + ((chan->channel != AH_PRIVATE(ah)->ah_curchan->channel) && + (((CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER) & chan->channel_flags) == + ((CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER) & AH_PRIVATE(ah)->ah_curchan->channel_flags)))) + { + if (ar9300_channel_change(ah, chan, ichan, macmode)) { + chan->channel_flags = ichan->channel_flags; + chan->priv_flags = ichan->priv_flags; + AH_PRIVATE(ah)->ah_curchan->ah_channel_time = 0; + AH_PRIVATE(ah)->ah_curchan->ah_tsf_last = ar9300_get_tsf64(ah); + + /* + * Load the NF from history buffer of the current channel. + * NF is slow time-variant, so it is OK to use a historical value. + */ + ar9300_get_nf_hist_base(ah, + AH_PRIVATE(ah)->ah_curchan, is_scan, nf_buf); + ar9300_load_nf(ah, nf_buf); + + /* start NF calibration, without updating BB NF register*/ + ar9300_start_nf_cal(ah); + + /* + * If channel_change completed and DMA was stopped + * successfully - skip the rest of reset + */ + if (AH9300(ah)->ah_dma_stuck != AH_TRUE) { + WAR_USB_DISABLE_PLL_LOCK_DETECT(ah); +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support && ahp->ah_mci_ready) + { + ar9300_mci_2g5g_switch(ah, AH_TRUE); + } +#endif + return AH_TRUE; + } + } + } + +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support) { + ar9300_mci_disable_interrupt(ah); + if (ahp->ah_mci_ready && !save_full_sleep) { + ar9300_mci_mute_bt(ah); + OS_DELAY(20); + OS_REG_WRITE(ah, AR_BTCOEX_CTRL, 0); + } + + ahp->ah_mci_bt_state = MCI_BT_SLEEP; + ahp->ah_mci_ready = AH_FALSE; + } +#endif + + AH9300(ah)->ah_dma_stuck = AH_FALSE; +#ifdef ATH_FORCE_PPM + /* Preserve force ppm state */ + save_force_val = + OS_REG_READ(ah, AR_PHY_TIMING2) & + (AR_PHY_TIMING2_USE_FORCE | AR_PHY_TIMING2_FORCE_VAL); +#endif + /* + * Preserve the antenna on a channel change + */ + save_def_antenna = OS_REG_READ(ah, AR_DEF_ANTENNA); + if (0 == ahp->ah_smartantenna_enable ) + { + if (save_def_antenna == 0) { + save_def_antenna = 1; + } + } + + /* Save hardware flag before chip reset clears the register */ + mac_sta_id1 = OS_REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; + + /* Save led state from pci config register */ + save_led_state = OS_REG_READ(ah, AR_CFG_LED) & + (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | + AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW); + + /* Mark PHY inactive prior to reset, to be undone in ar9300_init_bb () */ + ar9300_mark_phy_inactive(ah); + + if (!ar9300_chip_reset(ah, chan)) { + HALDEBUG(ah, HAL_DEBUG_RESET, "%s: chip reset failed\n", __func__); + FAIL(HAL_EIO); + } + + OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); + + + /* Disable JTAG */ + OS_REG_SET_BIT(ah, + AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), AR_GPIO_JTAG_DISABLE); + + /* + * Note that ar9300_init_chain_masks() is called from within + * ar9300_process_ini() to ensure the swap bit is set before + * the pdadc table is written. + */ + ecode = ar9300_process_ini(ah, chan, ichan, macmode); + if (ecode != HAL_OK) { + goto bad; + } + + ahp->ah_immunity_on = AH_FALSE; + + if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { + ahp->tx_iq_cal_enable = OS_REG_READ_FIELD(ah, + AR_PHY_TX_IQCAL_CONTROL_0(ah), + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL) ? + 1 : 0; + } + ahp->tx_cl_cal_enable = (OS_REG_READ(ah, AR_PHY_CL_CAL_CTL) & + AR_PHY_CL_CAL_ENABLE) ? 1 : 0; + + /* For devices with full HW RIFS Rx support (Sowl/Howl/Merlin, etc), + * restore register settings from prior to reset. + */ + if ((AH_PRIVATE(ah)->ah_curchan != AH_NULL) && + (ar9300_get_capability(ah, HAL_CAP_LDPCWAR, 0, AH_NULL) == HAL_OK)) + { + /* Re-program RIFS Rx policy after reset */ + ar9300_set_rifs_delay(ah, ahp->ah_rifs_enabled); + } + +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support) { + ar9300_mci_reset(ah, AH_FALSE, IS_CHAN_2GHZ(chan), save_full_sleep); + } +#endif + + /* Initialize Management Frame Protection */ + ar9300_init_mfp(ah); + + ahp->ah_immunity_vals[0] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M1_THRESH_LOW); + ahp->ah_immunity_vals[1] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2_THRESH_LOW); + ahp->ah_immunity_vals[2] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M1_THRESH); + ahp->ah_immunity_vals[3] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2_THRESH); + ahp->ah_immunity_vals[4] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2COUNT_THR); + ahp->ah_immunity_vals[5] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW); + + /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ + if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) { + ar9300_set_delta_slope(ah, ichan); + } + + ar9300_spur_mitigate(ah, chan); + if (!ar9300_eeprom_set_board_values(ah, ichan)) { + HALDEBUG(ah, HAL_DEBUG_EEPROM, + "%s: error setting board options\n", __func__); + FAIL(HAL_EIO); + } + +#ifdef ATH_HAL_WAR_REG16284_APH128 + /* temp work around, will be removed. */ + if (AR_SREV_WASP(ah)) { + OS_REG_WRITE(ah, 0x16284, 0x1553e000); + } +#endif + + OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); + + OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr)); + OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4) + | mac_sta_id1 + | AR_STA_ID1_RTS_USE_DEF + | (ap->ah_config.ath_hal_6mb_ack ? AR_STA_ID1_ACKCTS_6MB : 0) + | ahp->ah_sta_id1_defaults + ); + ar9300_set_operating_mode(ah, opmode); + + /* Set Venice BSSID mask according to current state */ + OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssid_mask)); + OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssid_mask + 4)); + + /* Restore previous antenna */ + OS_REG_WRITE(ah, AR_DEF_ANTENNA, save_def_antenna); +#ifdef ATH_FORCE_PPM + /* Restore force ppm state */ + tmp_reg = OS_REG_READ(ah, AR_PHY_TIMING2) & + ~(AR_PHY_TIMING2_USE_FORCE | AR_PHY_TIMING2_FORCE_VAL); + OS_REG_WRITE(ah, AR_PHY_TIMING2, tmp_reg | save_force_val); +#endif + + /* then our BSSID and assocID */ + OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); + OS_REG_WRITE(ah, AR_BSS_ID1, + LE_READ_2(ahp->ah_bssid + 4) | + ((ahp->ah_assoc_id & 0x3fff) << AR_BSS_ID1_AID_S)); + + OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */ + + OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, INIT_RSSI_THR); + + /* HW beacon processing */ + OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_BCN_WEIGHT, + INIT_RSSI_BEACON_WEIGHT); + OS_REG_SET_BIT(ah, AR_HWBCNPROC1, AR_HWBCNPROC1_CRC_ENABLE | + AR_HWBCNPROC1_EXCLUDE_TIM_ELM); + if (AH_PRIVATE(ah)->ah_config.ath_hal_beacon_filter_interval) { + OS_REG_RMW_FIELD(ah, AR_HWBCNPROC2, AR_HWBCNPROC2_FILTER_INTERVAL, + AH_PRIVATE(ah)->ah_config.ath_hal_beacon_filter_interval); + OS_REG_SET_BIT(ah, AR_HWBCNPROC2, + AR_HWBCNPROC2_FILTER_INTERVAL_ENABLE); + } + + + /* + * Set Channel now modifies bank 6 parameters for FOWL workaround + * to force rf_pwd_icsyndiv bias current as function of synth + * frequency.Thus must be called after ar9300_process_ini() to ensure + * analog register cache is valid. + */ + if (!ahp->ah_rf_hal.set_channel(ah, ichan)) { + FAIL(HAL_EIO); + } + + + OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); + + /* Set 1:1 QCU to DCU mapping for all queues */ + for (i = 0; i < AR_NUM_DCU; i++) { + OS_REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); + } + + ahp->ah_intr_txqs = 0; + for (i = 0; i < AH_PRIVATE(ah)->ah_caps.hal_total_queues; i++) { + ar9300_reset_tx_queue(ah, i); + } + + ar9300_init_interrupt_masks(ah, opmode); + + /* Reset ier reference count to disabled */ + OS_ATOMIC_SET(&ahp->ah_ier_ref_count, 1); + if (ath_hal_isrfkillenabled(ah)) { + ar9300_enable_rf_kill(ah); + } + + /* must be called AFTER ini is processed */ + ar9300_ani_init_defaults(ah, macmode); + + ar9300_init_qos(ah); + + ar9300_init_user_settings(ah); + + + AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ + + OS_MARK(ah, AH_MARK_RESET_DONE, 0); + + /* + * disable seq number generation in hw + */ + OS_REG_WRITE(ah, AR_STA_ID1, + OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); + + ar9300_set_dma(ah); + + /* + * program OBS bus to see MAC interrupts + */ +#if ATH_SUPPORT_MCI + if (!AH_PRIVATE(ah)->ah_caps.hal_mci_support) { + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_OBS), 8); + } +#else + OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_OBS), 8); +#endif + + + /* enabling AR_GTTM_IGNORE_IDLE in GTTM register so that + GTT timer will not increment if the channel idle indicates + the air is busy or NAV is still counting down */ + OS_REG_WRITE(ah, AR_GTTM, AR_GTTM_IGNORE_IDLE); + + /* + * GTT debug mode setting + */ + /* + OS_REG_WRITE(ah, 0x64, 0x00320000); + OS_REG_WRITE(ah, 0x68, 7); + OS_REG_WRITE(ah, 0x4080, 0xC); + */ + /* + * Disable general interrupt mitigation by setting MIRT = 0x0 + * Rx and tx interrupt mitigation are conditionally enabled below. + */ + OS_REG_WRITE(ah, AR_MIRT, 0); + if (ahp->ah_intr_mitigation_rx) { + /* + * Enable Interrupt Mitigation for Rx. + * If no build-specific limits for the rx interrupt mitigation + * timer have been specified, use conservative defaults. + */ + #ifndef AH_RIMT_VAL_LAST + #define AH_RIMT_LAST_MICROSEC 500 + #endif + #ifndef AH_RIMT_VAL_FIRST + #define AH_RIMT_FIRST_MICROSEC 2000 + #endif +#ifndef HOST_OFFLOAD + OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, AH_RIMT_LAST_MICROSEC); + OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, AH_RIMT_FIRST_MICROSEC); +#else + /* lower mitigation level to reduce latency for offload arch. */ + OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, + (AH_RIMT_LAST_MICROSEC >> 2)); + OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, + (AH_RIMT_FIRST_MICROSEC >> 2)); +#endif + } + + if (ahp->ah_intr_mitigation_tx) { + /* + * Enable Interrupt Mitigation for Tx. + * If no build-specific limits for the tx interrupt mitigation + * timer have been specified, use the values preferred for + * the carrier group's products. + */ + #ifndef AH_TIMT_LAST + #define AH_TIMT_LAST_MICROSEC 300 + #endif + #ifndef AH_TIMT_FIRST + #define AH_TIMT_FIRST_MICROSEC 750 + #endif + OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, AH_TIMT_LAST_MICROSEC); + OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, AH_TIMT_FIRST_MICROSEC); + } + + rx_chainmask = ahp->ah_rx_chainmask; + + OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); + OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); + + ar9300_init_bb(ah, chan); + + /* BB Step 7: Calibration */ + ar9300_invalidate_saved_cals(ah, ichan); + cal_ret = ar9300_init_cal(ah, chan, AH_FALSE, apply_last_iqcorr); + +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support && ahp->ah_mci_ready) { + if (IS_CHAN_2GHZ(chan) && + (ahp->ah_mci_bt_state == MCI_BT_SLEEP)) + { + if (ar9300_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) || + ar9300_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) + { + /* + * BT is sleeping. Check if BT wakes up duing WLAN + * calibration. If BT wakes up during WLAN calibration, need + * to go through all message exchanges again and recal. + */ + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "(MCI) ### %s: BT wakes up during WLAN calibration.\n", + __func__); + OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, + AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | + AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE); + HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) send REMOTE_RESET\n"); + ar9300_mci_remote_reset(ah, AH_TRUE); + ar9300_mci_send_sys_waking(ah, AH_TRUE); + OS_DELAY(1); + if (IS_CHAN_2GHZ(chan)) { + ar9300_mci_send_lna_transfer(ah, AH_TRUE); + } + ahp->ah_mci_bt_state = MCI_BT_AWAKE; + + /* Redo calibration */ + HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Re-calibrate.\n", + __func__); + ar9300_invalidate_saved_cals(ah, ichan); + cal_ret = ar9300_init_cal(ah, chan, AH_FALSE, ar9300_init_cal); + } + } + ar9300_mci_enable_interrupt(ah); + } +#endif + + if (!cal_ret) { + HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Init Cal Failed\n", __func__); + FAIL(HAL_ESELFTEST); + } + + ar9300_init_txbf(ah); +#if 0 + /* + * WAR for owl 1.0 - restore chain mask for 2-chain cfgs after cal + */ + rx_chainmask = ahp->ah_rx_chainmask; + if ((rx_chainmask == 0x5) || (rx_chainmask == 0x3)) { + OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); + OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); + } +#endif + + /* Restore previous led state */ + OS_REG_WRITE(ah, AR_CFG_LED, save_led_state | AR_CFG_SCLK_32KHZ); + +#ifdef ATH_BT_COEX + if (ahp->ah_bt_coex_config_type != HAL_BT_COEX_CFG_NONE) { + ar9300_init_bt_coex(ah); + +#if ATH_SUPPORT_MCI + if (AH_PRIVATE(ah)->ah_caps.hal_mci_support && ahp->ah_mci_ready) { + /* Check BT state again to make sure it's not changed. */ + ar9300_mci_sync_bt_state(ah); + ar9300_mci_2g5g_switch(ah, AH_TRUE); + + if ((ahp->ah_mci_bt_state == MCI_BT_AWAKE) && + (ahp->ah_mci_query_bt == AH_TRUE)) + { + ahp->ah_mci_need_flush_btinfo = AH_TRUE; + } + } +#endif + } +#endif + + /* Start TSF2 for generic timer 8-15. */ + ar9300_start_tsf2(ah); + + /* MIMO Power save setting */ + if (ar9300_get_capability(ah, HAL_CAP_DYNAMIC_SMPS, 0, AH_NULL) == HAL_OK) { + ar9300_set_sm_power_mode(ah, ahp->ah_sm_power_mode); + } + + /* + * For big endian systems turn on swapping for descriptors + */ +#if AH_BYTE_ORDER == AH_BIG_ENDIAN + if (AR_SREV_HORNET(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { + OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTB | AR_CFG_SWRB, 0); + } else { + ar9300_init_cfg_reg(ah); + } +#endif + + if ( AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { + OS_REG_RMW(ah, AR_CFG_LED, AR_CFG_LED_ASSOC_CTL, AR_CFG_LED_ASSOC_CTL); + } + +#if !(defined(ART_BUILD)) && defined(ATH_SUPPORT_LED) +#define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); +#define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) +#define ATH_GPIO_OUT_FUNCTION3 0xB8040038 +#define ATH_GPIO_OE 0xB8040000 + if ( AR_SREV_WASP(ah)) { + if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { + REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff << 8))) | (0x33 << 8) ); + REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) & (~(0x1 << 13) ))); + } + else { + + /* Disable 2G WLAN LED. During ath_open, reset function is called even before channel is set. + So 2GHz is taken as default and it also blinks. Hence + to avoid both from blinking, disable 2G led while in 5G mode */ + + REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) | (1 << 13) )); + REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff))) | (0x33) ); + REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) & (~(0x1 << 12) ))); + } + + } + else if (AR_SREV_SCORPION(ah)) { + if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { + REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff << 8))) | (0x2F << 8) ); + REG_WRITE(ATH_GPIO_OE, (( REG_READ(ATH_GPIO_OE) & (~(0x1 << 13) )) | (0x1 << 12))); + } else if (IS_CHAN_5GHZ((AH_PRIVATE(ah)->ah_curchan))) { + REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff))) | (0x2F) ); + REG_WRITE(ATH_GPIO_OE, (( REG_READ(ATH_GPIO_OE) & (~(0x1 << 12) )) | (0x1 << 13))); + } + } +#undef REG_READ +#undef REG_WRITE +#endif + + chan->channel_flags = ichan->channel_flags; + chan->priv_flags = ichan->priv_flags; + +#if FIX_NOISE_FLOOR + ar9300_get_nf_hist_base(ah, AH_PRIVATE(ah)->ah_curchan, is_scan, nf_buf); + ar9300_load_nf(ah, nf_buf); + if (nf_hist_buff_reset == 1) + { + nf_hist_buff_reset = 0; + #ifndef ATH_NF_PER_CHAN + if (First_NFCal(ah, ichan, is_scan, chan)){ + } + #endif /* ATH_NF_PER_CHAN */ + } + else{ + ar9300_start_nf_cal(ah); + } +#endif + +#ifdef AH_SUPPORT_AR9300 + /* BB Panic Watchdog */ + if (ar9300_get_capability(ah, HAL_CAP_BB_PANIC_WATCHDOG, 0, AH_NULL) == + HAL_OK) + { + ar9300_config_bb_panic_watchdog(ah); + } +#endif + + /* While receiving unsupported rate frame receive state machine + * gets into a state 0xb and if phy_restart happens when rx + * state machine is in 0xb state, BB would go hang, if we + * see 0xb state after first bb panic, make sure that we + * disable the phy_restart. + * + * There may be multiple panics, make sure that we always do + * this if we see this panic at least once. This is required + * because reset seems to be writing from INI file. + */ + if ((ar9300_get_capability(ah, HAL_CAP_PHYRESTART_CLR_WAR, 0, AH_NULL) + == HAL_OK) && (((MS((AH_PRIVATE(ah)->ah_bb_panic_last_status), + AR_PHY_BB_WD_RX_OFDM_SM)) == 0xb) || + AH_PRIVATE(ah)->ah_phyrestart_disabled) ) + { + ar9300_disable_phy_restart(ah, 1); + } + + + + ahp->ah_radar1 = MS(OS_REG_READ(ah, AR_PHY_RADAR_1), + AR_PHY_RADAR_1_CF_BIN_THRESH); + ahp->ah_dc_offset = MS(OS_REG_READ(ah, AR_PHY_TIMING2), + AR_PHY_TIMING2_DC_OFFSET); + ahp->ah_disable_cck = MS(OS_REG_READ(ah, AR_PHY_MODE), + AR_PHY_MODE_DISABLE_CCK); + + if (ap->ah_enable_keysearch_always) { + ar9300_enable_keysearch_always(ah, 1); + } + +#if ATH_LOW_POWER_ENABLE +#define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val) +#define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) + if (AR_SREV_OSPREY(ah)) { + REG_WRITE(0xb4000080, REG_READ(0xb4000080) | 3); + OS_REG_WRITE(ah, AR_RTC_RESET, 1); + OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), + AR_PCIE_PM_CTRL_ENA); + OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_SPARE), 0xffffffff); + } +#undef REG_READ +#undef REG_WRITE +#endif /* ATH_LOW_POWER_ENABLE */ + + WAR_USB_DISABLE_PLL_LOCK_DETECT(ah); + + /* H/W Green TX */ + ar9300_control_signals_for_green_tx_mode(ah); + /* Smart Antenna, only for 5GHz on Scropion */ + if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan)) && AR_SREV_SCORPION(ah)) { + ahp->ah_smartantenna_enable = 0; + } + + ar9300_set_smart_antenna(ah, ahp->ah_smartantenna_enable); + + + return AH_TRUE; +bad: + OS_MARK(ah, AH_MARK_RESET_DONE, ecode); + *status = ecode; + + return AH_FALSE; +#undef FAIL +} + +void +ar9300_green_ap_ps_on_off( struct ath_hal *ah, u_int16_t on_off) +{ + /* Set/reset the ps flag */ + AH_PRIVATE(ah)->green_ap_ps_on = !!on_off; +} + +/* + * This function returns 1, where it is possible to do + * single-chain power save. + */ +u_int16_t +ar9300_is_single_ant_power_save_possible(struct ath_hal *ah) +{ + return AH_TRUE; +} + +/* To avoid compilation warnings. Functions not used when EMULATION. */ +/* + * ar9300_find_mag_approx() + */ +static int32_t +ar9300_find_mag_approx(struct ath_hal *ah, int32_t in_re, int32_t in_im) +{ + int32_t abs_i = abs(in_re); + int32_t abs_q = abs(in_im); + int32_t max_abs, min_abs; + + if (abs_i > abs_q) { + max_abs = abs_i; + min_abs = abs_q; + } else { + max_abs = abs_q; + min_abs = abs_i; + } + + return (max_abs - (max_abs / 32) + (min_abs / 8) + (min_abs / 4)); +} + +/* + * ar9300_solve_iq_cal() + * solve 4x4 linear equation used in loopback iq cal. + */ +static HAL_BOOL +ar9300_solve_iq_cal( + struct ath_hal *ah, + int32_t sin_2phi_1, + int32_t cos_2phi_1, + int32_t sin_2phi_2, + int32_t cos_2phi_2, + int32_t mag_a0_d0, + int32_t phs_a0_d0, + int32_t mag_a1_d0, + int32_t phs_a1_d0, + int32_t solved_eq[]) +{ + int32_t f1 = cos_2phi_1 - cos_2phi_2; + int32_t f3 = sin_2phi_1 - sin_2phi_2; + int32_t f2; + int32_t mag_tx, phs_tx, mag_rx, phs_rx; + const int32_t result_shift = 1 << 15; + + f2 = (((int64_t)f1 * (int64_t)f1) / result_shift) + (((int64_t)f3 * (int64_t)f3) / result_shift); + + if (0 == f2) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d).\n", + __func__, __LINE__); + return AH_FALSE; + } + + /* magnitude mismatch, tx */ + mag_tx = f1 * (mag_a0_d0 - mag_a1_d0) + f3 * (phs_a0_d0 - phs_a1_d0); + /* phase mismatch, tx */ + phs_tx = f3 * (-mag_a0_d0 + mag_a1_d0) + f1 * (phs_a0_d0 - phs_a1_d0); + + mag_tx = (mag_tx / f2); + phs_tx = (phs_tx / f2); + + /* magnitude mismatch, rx */ + mag_rx = + mag_a0_d0 - (cos_2phi_1 * mag_tx + sin_2phi_1 * phs_tx) / result_shift; + /* phase mismatch, rx */ + phs_rx = + phs_a0_d0 + (sin_2phi_1 * mag_tx - cos_2phi_1 * phs_tx) / result_shift; + + solved_eq[0] = mag_tx; + solved_eq[1] = phs_tx; + solved_eq[2] = mag_rx; + solved_eq[3] = phs_rx; + + return AH_TRUE; +} + +/* + * ar9300_calc_iq_corr() + */ +static HAL_BOOL +ar9300_calc_iq_corr(struct ath_hal *ah, int32_t chain_idx, + const int32_t iq_res[], int32_t iqc_coeff[]) +{ + int32_t i2_m_q2_a0_d0, i2_p_q2_a0_d0, iq_corr_a0_d0; + int32_t i2_m_q2_a0_d1, i2_p_q2_a0_d1, iq_corr_a0_d1; + int32_t i2_m_q2_a1_d0, i2_p_q2_a1_d0, iq_corr_a1_d0; + int32_t i2_m_q2_a1_d1, i2_p_q2_a1_d1, iq_corr_a1_d1; + int32_t mag_a0_d0, mag_a1_d0, mag_a0_d1, mag_a1_d1; + int32_t phs_a0_d0, phs_a1_d0, phs_a0_d1, phs_a1_d1; + int32_t sin_2phi_1, cos_2phi_1, sin_2phi_2, cos_2phi_2; + int32_t mag_tx, phs_tx, mag_rx, phs_rx; + int32_t solved_eq[4], mag_corr_tx, phs_corr_tx, mag_corr_rx, phs_corr_rx; + int32_t q_q_coff, q_i_coff; + const int32_t res_scale = 1 << 15; + const int32_t delpt_shift = 1 << 8; + int32_t mag1, mag2; + + i2_m_q2_a0_d0 = iq_res[0] & 0xfff; + i2_p_q2_a0_d0 = (iq_res[0] >> 12) & 0xfff; + iq_corr_a0_d0 = ((iq_res[0] >> 24) & 0xff) + ((iq_res[1] & 0xf) << 8); + + if (i2_m_q2_a0_d0 > 0x800) { + i2_m_q2_a0_d0 = -((0xfff - i2_m_q2_a0_d0) + 1); + } + if (iq_corr_a0_d0 > 0x800) { + iq_corr_a0_d0 = -((0xfff - iq_corr_a0_d0) + 1); + } + + i2_m_q2_a0_d1 = (iq_res[1] >> 4) & 0xfff; + i2_p_q2_a0_d1 = (iq_res[2] & 0xfff); + iq_corr_a0_d1 = (iq_res[2] >> 12) & 0xfff; + + if (i2_m_q2_a0_d1 > 0x800) { + i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1); + } + if (iq_corr_a0_d1 > 0x800) { + iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1); + } + + i2_m_q2_a1_d0 = ((iq_res[2] >> 24) & 0xff) + ((iq_res[3] & 0xf) << 8); + i2_p_q2_a1_d0 = (iq_res[3] >> 4) & 0xfff; + iq_corr_a1_d0 = iq_res[4] & 0xfff; + + if (i2_m_q2_a1_d0 > 0x800) { + i2_m_q2_a1_d0 = -((0xfff - i2_m_q2_a1_d0) + 1); + } + if (iq_corr_a1_d0 > 0x800) { + iq_corr_a1_d0 = -((0xfff - iq_corr_a1_d0) + 1); + } + + i2_m_q2_a1_d1 = (iq_res[4] >> 12) & 0xfff; + i2_p_q2_a1_d1 = ((iq_res[4] >> 24) & 0xff) + ((iq_res[5] & 0xf) << 8); + iq_corr_a1_d1 = (iq_res[5] >> 4) & 0xfff; + + if (i2_m_q2_a1_d1 > 0x800) { + i2_m_q2_a1_d1 = -((0xfff - i2_m_q2_a1_d1) + 1); + } + if (iq_corr_a1_d1 > 0x800) { + iq_corr_a1_d1 = -((0xfff - iq_corr_a1_d1) + 1); + } + + if ((i2_p_q2_a0_d0 == 0) || + (i2_p_q2_a0_d1 == 0) || + (i2_p_q2_a1_d0 == 0) || + (i2_p_q2_a1_d1 == 0)) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Divide by 0(%d):\na0_d0=%d\na0_d1=%d\na2_d0=%d\na1_d1=%d\n", + __func__, __LINE__, + i2_p_q2_a0_d0, i2_p_q2_a0_d1, i2_p_q2_a1_d0, i2_p_q2_a1_d1); + return AH_FALSE; + } + + if ((i2_p_q2_a0_d0 <= 1024) || (i2_p_q2_a0_d0 > 2047) || + (i2_p_q2_a1_d0 < 0) || (i2_p_q2_a1_d1 < 0) || + (i2_p_q2_a0_d0 <= i2_m_q2_a0_d0) || + (i2_p_q2_a0_d0 <= iq_corr_a0_d0) || + (i2_p_q2_a0_d1 <= i2_m_q2_a0_d1) || + (i2_p_q2_a0_d1 <= iq_corr_a0_d1) || + (i2_p_q2_a1_d0 <= i2_m_q2_a1_d0) || + (i2_p_q2_a1_d0 <= iq_corr_a1_d0) || + (i2_p_q2_a1_d1 <= i2_m_q2_a1_d1) || + (i2_p_q2_a1_d1 <= iq_corr_a1_d1)) { + return AH_FALSE; + } + + mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0; + phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0; + + mag_a0_d1 = (i2_m_q2_a0_d1 * res_scale) / i2_p_q2_a0_d1; + phs_a0_d1 = (iq_corr_a0_d1 * res_scale) / i2_p_q2_a0_d1; + + mag_a1_d0 = (i2_m_q2_a1_d0 * res_scale) / i2_p_q2_a1_d0; + phs_a1_d0 = (iq_corr_a1_d0 * res_scale) / i2_p_q2_a1_d0; + + mag_a1_d1 = (i2_m_q2_a1_d1 * res_scale) / i2_p_q2_a1_d1; + phs_a1_d1 = (iq_corr_a1_d1 * res_scale) / i2_p_q2_a1_d1; + + /* without analog phase shift */ + sin_2phi_1 = (((mag_a0_d0 - mag_a0_d1) * delpt_shift) / DELPT); + /* without analog phase shift */ + cos_2phi_1 = (((phs_a0_d1 - phs_a0_d0) * delpt_shift) / DELPT); + /* with analog phase shift */ + sin_2phi_2 = (((mag_a1_d0 - mag_a1_d1) * delpt_shift) / DELPT); + /* with analog phase shift */ + cos_2phi_2 = (((phs_a1_d1 - phs_a1_d0) * delpt_shift) / DELPT); + + /* force sin^2 + cos^2 = 1; */ + /* find magnitude by approximation */ + mag1 = ar9300_find_mag_approx(ah, cos_2phi_1, sin_2phi_1); + mag2 = ar9300_find_mag_approx(ah, cos_2phi_2, sin_2phi_2); + + if ((mag1 == 0) || (mag2 == 0)) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Divide by 0(%d): mag1=%d, mag2=%d\n", + __func__, __LINE__, mag1, mag2); + return AH_FALSE; + } + + /* normalization sin and cos by mag */ + sin_2phi_1 = (sin_2phi_1 * res_scale / mag1); + cos_2phi_1 = (cos_2phi_1 * res_scale / mag1); + sin_2phi_2 = (sin_2phi_2 * res_scale / mag2); + cos_2phi_2 = (cos_2phi_2 * res_scale / mag2); + + /* calculate IQ mismatch */ + if (AH_FALSE== ar9300_solve_iq_cal(ah, + sin_2phi_1, cos_2phi_1, sin_2phi_2, cos_2phi_2, mag_a0_d0, + phs_a0_d0, mag_a1_d0, phs_a1_d0, solved_eq)) + { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Call to ar9300_solve_iq_cal failed.\n", __func__); + return AH_FALSE; + } + + mag_tx = solved_eq[0]; + phs_tx = solved_eq[1]; + mag_rx = solved_eq[2]; + phs_rx = solved_eq[3]; + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: chain %d: mag mismatch=%d phase mismatch=%d\n", + __func__, chain_idx, mag_tx / res_scale, phs_tx / res_scale); + + if (res_scale == mag_tx) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Divide by 0(%d): mag_tx=%d, res_scale=%d\n", + __func__, __LINE__, mag_tx, res_scale); + return AH_FALSE; + } + + /* calculate and quantize Tx IQ correction factor */ + mag_corr_tx = (mag_tx * res_scale) / (res_scale - mag_tx); + phs_corr_tx = -phs_tx; + + q_q_coff = (mag_corr_tx * 128 / res_scale); + q_i_coff = (phs_corr_tx * 256 / res_scale); + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: tx chain %d: mag corr=%d phase corr=%d\n", + __func__, chain_idx, q_q_coff, q_i_coff); + + if (q_i_coff < -63) { + q_i_coff = -63; + } + if (q_i_coff > 63) { + q_i_coff = 63; + } + if (q_q_coff < -63) { + q_q_coff = -63; + } + if (q_q_coff > 63) { + q_q_coff = 63; + } + + iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff); + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: tx chain %d: iq corr coeff=%x\n", + __func__, chain_idx, iqc_coeff[0]); + + if (-mag_rx == res_scale) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Divide by 0(%d): mag_rx=%d, res_scale=%d\n", + __func__, __LINE__, mag_rx, res_scale); + return AH_FALSE; + } + + /* calculate and quantize Rx IQ correction factors */ + mag_corr_rx = (-mag_rx * res_scale) / (res_scale + mag_rx); + phs_corr_rx = -phs_rx; + + q_q_coff = (mag_corr_rx * 128 / res_scale); + q_i_coff = (phs_corr_rx * 256 / res_scale); + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: rx chain %d: mag corr=%d phase corr=%d\n", + __func__, chain_idx, q_q_coff, q_i_coff); + + if (q_i_coff < -63) { + q_i_coff = -63; + } + if (q_i_coff > 63) { + q_i_coff = 63; + } + if (q_q_coff < -63) { + q_q_coff = -63; + } + if (q_q_coff > 63) { + q_q_coff = 63; + } + + iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff); + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: rx chain %d: iq corr coeff=%x\n", + __func__, chain_idx, iqc_coeff[1]); + + return AH_TRUE; +} + +#define MAX_MAG_DELTA 11 //maximum magnitude mismatch delta across gains +#define MAX_PHS_DELTA 10 //maximum phase mismatch delta across gains +#define ABS(x) ((x) >= 0 ? (x) : (-(x))) + + u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { + { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, + }; + +static void +ar9300_tx_iq_cal_outlier_detection(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, u_int32_t num_chains, + struct coeff_t *coeff,HAL_BOOL is_cal_reusable) +{ + int nmeasurement, ch_idx, im; + int32_t magnitude, phase; + int32_t magnitude_max, phase_max; + int32_t magnitude_min, phase_min; + + int32_t magnitude_max_idx, phase_max_idx; + int32_t magnitude_min_idx, phase_min_idx; + + int32_t magnitude_avg, phase_avg; + int32_t outlier_mag_idx = 0; + int32_t outlier_phs_idx = 0; + + + if (AR_SREV_POSEIDON(ah)) { + HALASSERT(num_chains == 0x1); + + tx_corr_coeff[0][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; + tx_corr_coeff[1][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; + tx_corr_coeff[2][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; + tx_corr_coeff[3][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; + tx_corr_coeff[4][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; + tx_corr_coeff[5][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; + tx_corr_coeff[6][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; + tx_corr_coeff[7][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; + } + + for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { + nmeasurement = OS_REG_READ_FIELD(ah, + AR_PHY_TX_IQCAL_STATUS_B0(ah), AR_PHY_CALIBRATED_GAINS_0); + if (nmeasurement > MAX_MEASUREMENT) { + nmeasurement = MAX_MEASUREMENT; + } + + if (!AR_SREV_SCORPION(ah)) { + /* + * reset max/min variable to min/max values so that + * we always start with 1st calibrated gain value + */ + magnitude_max = -64; + phase_max = -64; + magnitude_min = 63; + phase_min = 63; + magnitude_avg = 0; + phase_avg = 0; + magnitude_max_idx = 0; + magnitude_min_idx = 0; + phase_max_idx = 0; + phase_min_idx = 0; + + /* detect outlier only if nmeasurement > 1 */ + if (nmeasurement > 1) { + /* printf("----------- start outlier detection -----------\n"); */ + /* + * find max/min and phase/mag mismatch across all calibrated gains + */ + for (im = 0; im < nmeasurement; im++) { + magnitude = coeff->mag_coeff[ch_idx][im][0]; + phase = coeff->phs_coeff[ch_idx][im][0]; + + magnitude_avg = magnitude_avg + magnitude; + phase_avg = phase_avg + phase; + if (magnitude > magnitude_max) { + magnitude_max = magnitude; + magnitude_max_idx = im; + } + if (magnitude < magnitude_min) { + magnitude_min = magnitude; + magnitude_min_idx = im; + } + if (phase > phase_max) { + phase_max = phase; + phase_max_idx = im; + } + if (phase < phase_min) { + phase_min = phase; + phase_min_idx = im; + } + } + /* find average (exclude max abs value) */ + for (im = 0; im < nmeasurement; im++) { + magnitude = coeff->mag_coeff[ch_idx][im][0]; + phase = coeff->phs_coeff[ch_idx][im][0]; + if ((ABS(magnitude) < ABS(magnitude_max)) || + (ABS(magnitude) < ABS(magnitude_min))) + { + magnitude_avg = magnitude_avg + magnitude; + } + if ((ABS(phase) < ABS(phase_max)) || + (ABS(phase) < ABS(phase_min))) + { + phase_avg = phase_avg + phase; + } + } + magnitude_avg = magnitude_avg / (nmeasurement - 1); + phase_avg = phase_avg / (nmeasurement - 1); + + /* detect magnitude outlier */ + if (ABS(magnitude_max - magnitude_min) > MAX_MAG_DELTA) { + if (ABS(magnitude_max - magnitude_avg) > + ABS(magnitude_min - magnitude_avg)) + { + /* max is outlier, force to avg */ + outlier_mag_idx = magnitude_max_idx; + } else { + /* min is outlier, force to avg */ + outlier_mag_idx = magnitude_min_idx; + } + coeff->mag_coeff[ch_idx][outlier_mag_idx][0] = magnitude_avg; + coeff->phs_coeff[ch_idx][outlier_mag_idx][0] = phase_avg; + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "[ch%d][outlier mag gain%d]:: " + "mag_avg = %d (/128), phase_avg = %d (/256)\n", + ch_idx, outlier_mag_idx, magnitude_avg, phase_avg); + } + /* detect phase outlier */ + if (ABS(phase_max - phase_min) > MAX_PHS_DELTA) { + if (ABS(phase_max-phase_avg) > ABS(phase_min - phase_avg)) { + /* max is outlier, force to avg */ + outlier_phs_idx = phase_max_idx; + } else{ + /* min is outlier, force to avg */ + outlier_phs_idx = phase_min_idx; + } + coeff->mag_coeff[ch_idx][outlier_phs_idx][0] = magnitude_avg; + coeff->phs_coeff[ch_idx][outlier_phs_idx][0] = phase_avg; + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "[ch%d][outlier phs gain%d]:: " + "mag_avg = %d (/128), phase_avg = %d (/256)\n", + ch_idx, outlier_phs_idx, magnitude_avg, phase_avg); + } + } + } + + /*printf("------------ after outlier detection -------------\n");*/ + for (im = 0; im < nmeasurement; im++) { + magnitude = coeff->mag_coeff[ch_idx][im][0]; + phase = coeff->phs_coeff[ch_idx][im][0]; + + #if 0 + printf("[ch%d][gain%d]:: mag = %d (/128), phase = %d (/256)\n", + ch_idx, im, magnitude, phase); + #endif + + coeff->iqc_coeff[0] = (phase & 0x7f) | ((magnitude & 0x7f) << 7); + + if ((im % 2) == 0) { + OS_REG_RMW_FIELD(ah, + tx_corr_coeff[im][ch_idx], + AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, + coeff->iqc_coeff[0]); + } else { + OS_REG_RMW_FIELD(ah, + tx_corr_coeff[im][ch_idx], + AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, + coeff->iqc_coeff[0]); + } +#if ATH_SUPPORT_CAL_REUSE + ichan->tx_corr_coeff[im][ch_idx] = coeff->iqc_coeff[0]; +#endif + } +#if ATH_SUPPORT_CAL_REUSE + ichan->num_measures[ch_idx] = nmeasurement; +#endif + } + + OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, + AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); + OS_REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, + AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); + +#if ATH_SUPPORT_CAL_REUSE + if (is_cal_reusable) { + ichan->one_time_txiqcal_done = AH_TRUE; + HALDEBUG(ah, HAL_DEBUG_FCS_RTT, + "(FCS) TXIQCAL saved - %d\n", ichan->channel); + } +#endif +} + +#if ATH_SUPPORT_CAL_REUSE +static void +ar9300_tx_iq_cal_apply(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + int nmeasurement, ch_idx, im; + + u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { + { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, + }; + + if (AR_SREV_POSEIDON(ah)) { + HALASSERT(ahp->ah_tx_cal_chainmask == 0x1); + + tx_corr_coeff[0][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; + tx_corr_coeff[1][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; + tx_corr_coeff[2][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; + tx_corr_coeff[3][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; + tx_corr_coeff[4][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; + tx_corr_coeff[5][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; + tx_corr_coeff[6][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; + tx_corr_coeff[7][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; + } + + for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { + if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { + continue; + } + nmeasurement = ichan->num_measures[ch_idx]; + + for (im = 0; im < nmeasurement; im++) { + if ((im % 2) == 0) { + OS_REG_RMW_FIELD(ah, + tx_corr_coeff[im][ch_idx], + AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, + ichan->tx_corr_coeff[im][ch_idx]); + } else { + OS_REG_RMW_FIELD(ah, + tx_corr_coeff[im][ch_idx], + AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, + ichan->tx_corr_coeff[im][ch_idx]); + } + } + } + + OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, + AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); + OS_REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, + AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); +} +#endif + +/* + * ar9300_tx_iq_cal_hw_run is only needed for osprey/wasp/hornet + * It is not needed for jupiter/poseidon. + */ +HAL_BOOL +ar9300_tx_iq_cal_hw_run(struct ath_hal *ah) +{ + int is_tx_gain_forced; + + is_tx_gain_forced = OS_REG_READ_FIELD(ah, + AR_PHY_TX_FORCED_GAIN, AR_PHY_TXGAIN_FORCE); + if (is_tx_gain_forced) { + /*printf("Tx gain can not be forced during tx I/Q cal!\n");*/ + OS_REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TXGAIN_FORCE, 0); + } + + /* enable tx IQ cal */ + OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START(ah), + AR_PHY_TX_IQCAL_START_DO_CAL, AR_PHY_TX_IQCAL_START_DO_CAL); + + if (!ath_hal_wait(ah, + AR_PHY_TX_IQCAL_START(ah), AR_PHY_TX_IQCAL_START_DO_CAL, 0, + AH_WAIT_TIMEOUT)) + { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Tx IQ Cal is never completed.\n", __func__); + return AH_FALSE; + } + return AH_TRUE; +} + +static void +ar9300_tx_iq_cal_post_proc(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, + int iqcal_idx, int max_iqcal,HAL_BOOL is_cal_reusable, HAL_BOOL apply_last_corr) +{ + int nmeasurement=0, im, ix, iy, temp; + struct ath_hal_9300 *ahp = AH9300(ah); + u_int32_t txiqcal_status[AR9300_MAX_CHAINS] = { + AR_PHY_TX_IQCAL_STATUS_B0(ah), + AR_PHY_TX_IQCAL_STATUS_B1, + AR_PHY_TX_IQCAL_STATUS_B2, + }; + const u_int32_t chan_info_tab[] = { + AR_PHY_CHAN_INFO_TAB_0, + AR_PHY_CHAN_INFO_TAB_1, + AR_PHY_CHAN_INFO_TAB_2, + }; + int32_t iq_res[6]; + int32_t ch_idx, j; + u_int32_t num_chains = 0; + static struct coeff_t coeff; + txiqcal_status[0] = AR_PHY_TX_IQCAL_STATUS_B0(ah); + + for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { + if (ahp->ah_tx_chainmask & (1 << ch_idx)) { + num_chains++; + } + } + + if (apply_last_corr) { + if (coeff.last_cal == AH_TRUE) { + int32_t magnitude, phase; + int ch_idx, im; + u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { + { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, + { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, + AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, + }; + for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { + for (im = 0; im < coeff.last_nmeasurement; im++) { + magnitude = coeff.mag_coeff[ch_idx][im][0]; + phase = coeff.phs_coeff[ch_idx][im][0]; + +#if 0 + printf("[ch%d][gain%d]:: mag = %d (/128), phase = %d (/256)\n", + ch_idx, im, magnitude, phase); +#endif + + coeff.iqc_coeff[0] = (phase & 0x7f) | ((magnitude & 0x7f) << 7); + if ((im % 2) == 0) { + OS_REG_RMW_FIELD(ah, + tx_corr_coeff[im][ch_idx], + AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, + coeff.iqc_coeff[0]); + } else { + OS_REG_RMW_FIELD(ah, + tx_corr_coeff[im][ch_idx], + AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, + coeff.iqc_coeff[0]); + } + } + } + OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, + AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); + } + return; + } + + + for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { + nmeasurement = OS_REG_READ_FIELD(ah, + AR_PHY_TX_IQCAL_STATUS_B0(ah), AR_PHY_CALIBRATED_GAINS_0); + if (nmeasurement > MAX_MEASUREMENT) { + nmeasurement = MAX_MEASUREMENT; + } + + for (im = 0; im < nmeasurement; im++) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Doing Tx IQ Cal for chain %d.\n", __func__, ch_idx); + if (OS_REG_READ(ah, txiqcal_status[ch_idx]) & + AR_PHY_TX_IQCAL_STATUS_FAILED) + { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Tx IQ Cal failed for chain %d.\n", __func__, ch_idx); + goto TX_IQ_CAL_FAILED_; + } + + for (j = 0; j < 3; j++) { + u_int32_t idx = 2 * j; + /* 3 registers for each calibration result */ + u_int32_t offset = 4 * (3 * im + j); + + OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, + AR_PHY_CHAN_INFO_TAB_S2_READ, 0); + /* 32 bits */ + iq_res[idx] = OS_REG_READ(ah, chan_info_tab[ch_idx] + offset); + OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, + AR_PHY_CHAN_INFO_TAB_S2_READ, 1); + /* 16 bits */ + iq_res[idx + 1] = 0xffff & + OS_REG_READ(ah, chan_info_tab[ch_idx] + offset); + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: IQ RES[%d]=0x%x IQ_RES[%d]=0x%x\n", + __func__, idx, iq_res[idx], idx + 1, iq_res[idx + 1]); + } + + if (AH_FALSE== ar9300_calc_iq_corr( + ah, ch_idx, iq_res, coeff.iqc_coeff)) + { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: Failed in calculation of IQ correction.\n", + __func__); + goto TX_IQ_CAL_FAILED_; + } + + coeff.phs_coeff[ch_idx][im][iqcal_idx-1] = coeff.iqc_coeff[0] & 0x7f; + coeff.mag_coeff[ch_idx][im][iqcal_idx-1] = (coeff.iqc_coeff[0] >> 7) & 0x7f; + if (coeff.mag_coeff[ch_idx][im][iqcal_idx-1] > 63) { + coeff.mag_coeff[ch_idx][im][iqcal_idx-1] -= 128; + } + if (coeff.phs_coeff[ch_idx][im][iqcal_idx-1] > 63) { + coeff.phs_coeff[ch_idx][im][iqcal_idx-1] -= 128; + } +#if 0 + ath_hal_printf(ah, "IQCAL::[ch%d][gain%d]:: mag = %d phase = %d \n", + ch_idx, im, coeff.mag_coeff[ch_idx][im][iqcal_idx-1], + coeff.phs_coeff[ch_idx][im][iqcal_idx-1]); +#endif + } + } + //last iteration; calculate mag and phs + if (iqcal_idx == max_iqcal) { + if (max_iqcal>1) { + for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { + for (im = 0; im < nmeasurement; im++) { + //sort mag and phs + for( ix=0;ix<max_iqcal-1;ix++){ + for( iy=ix+1;iy<=max_iqcal-1;iy++){ + if(coeff.mag_coeff[ch_idx][im][iy] < + coeff.mag_coeff[ch_idx][im][ix]) { + //swap + temp=coeff.mag_coeff[ch_idx][im][ix]; + coeff.mag_coeff[ch_idx][im][ix] = coeff.mag_coeff[ch_idx][im][iy]; + coeff.mag_coeff[ch_idx][im][iy] = temp; + } + if(coeff.phs_coeff[ch_idx][im][iy] < + coeff.phs_coeff[ch_idx][im][ix]){ + //swap + temp=coeff.phs_coeff[ch_idx][im][ix]; + coeff.phs_coeff[ch_idx][im][ix]=coeff.phs_coeff[ch_idx][im][iy]; + coeff.phs_coeff[ch_idx][im][iy]=temp; + } + } + } + //select median; 3rd entry in the sorted array + coeff.mag_coeff[ch_idx][im][0] = + coeff.mag_coeff[ch_idx][im][max_iqcal/2]; + coeff.phs_coeff[ch_idx][im][0] = + coeff.phs_coeff[ch_idx][im][max_iqcal/2]; + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "IQCAL: Median [ch%d][gain%d]:: mag = %d phase = %d \n", + ch_idx, im,coeff.mag_coeff[ch_idx][im][0], + coeff.phs_coeff[ch_idx][im][0]); + } + } + } + ar9300_tx_iq_cal_outlier_detection(ah,ichan, num_chains, &coeff,is_cal_reusable); + } + + + coeff.last_nmeasurement = nmeasurement; + coeff.last_cal = AH_TRUE; + + return; + +TX_IQ_CAL_FAILED_: + /* no need to print this, it is AGC failure not chip stuck */ + /*ath_hal_printf(ah, "Tx IQ Cal failed(%d)\n", line);*/ + coeff.last_cal = AH_FALSE; + return; +} + + +/* + * ar9300_disable_phy_restart + * + * In some BBpanics, we can disable the phyrestart + * disable_phy_restart + * != 0, disable the phy restart in h/w + * == 0, enable the phy restart in h/w + */ +void ar9300_disable_phy_restart(struct ath_hal *ah, int disable_phy_restart) +{ + u_int32_t val; + + val = OS_REG_READ(ah, AR_PHY_RESTART); + if (disable_phy_restart) { + val &= ~AR_PHY_RESTART_ENA; + AH_PRIVATE(ah)->ah_phyrestart_disabled = 1; + } else { + val |= AR_PHY_RESTART_ENA; + AH_PRIVATE(ah)->ah_phyrestart_disabled = 0; + } + OS_REG_WRITE(ah, AR_PHY_RESTART, val); + + val = OS_REG_READ(ah, AR_PHY_RESTART); +} + +HAL_BOOL +ar9300_interference_is_present(struct ath_hal *ah) +{ + int i; + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + + /* This function is called after a stuck beacon, if EACS is enabled. + * If CW interference is severe, then HW goes into a loop of continuous + * stuck beacons and resets. On reset the NF cal history is cleared. + * So the median value of the history cannot be used - + * hence check if any value (Chain 0/Primary Channel) + * is outside the bounds. + */ + HAL_NFCAL_HIST_FULL *h = AH_HOME_CHAN_NFCAL_HIST(ah); + for (i = 0; i < HAL_NF_CAL_HIST_LEN_FULL; i++) { + if (h->nf_cal_buffer[i][0] > + ahpriv->nfp->nominal + ahpriv->nf_cw_int_delta) + { + return AH_TRUE; + } + + } + return AH_FALSE; +} + +#if ATH_SUPPORT_CRDC +void +ar9300_crdc_rx_notify(struct ath_hal *ah, struct ath_rx_status *rxs) +{ + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + int rssi_index; + + if ((!AR_SREV_WASP(ah)) || + (!ahpriv->ah_config.ath_hal_crdc_enable)) { + return; + } + + if (rxs->rs_isaggr && rxs->rs_moreaggr) { + return; + } + + if ((rxs->rs_rssi_ctl0 >= HAL_RSSI_BAD) || + (rxs->rs_rssi_ctl1 >= HAL_RSSI_BAD)) { + return; + } + + rssi_index = ah->ah_crdc_rssi_ptr % HAL_MAX_CRDC_RSSI_SAMPLE; + + ah->ah_crdc_rssi_sample[0][rssi_index] = rxs->rs_rssi_ctl0; + ah->ah_crdc_rssi_sample[1][rssi_index] = rxs->rs_rssi_ctl1; + + ah->ah_crdc_rssi_ptr++; +} + +static int +ar9300_crdc_avg_rssi(struct ath_hal *ah, int chain) +{ + int crdc_rssi_sum = 0; + int crdc_rssi_ptr = ah->ah_crdc_rssi_ptr, i; + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + int crdc_window = ahpriv->ah_config.ath_hal_crdc_window; + + if (crdc_window > HAL_MAX_CRDC_RSSI_SAMPLE) { + crdc_window = HAL_MAX_CRDC_RSSI_SAMPLE; + } + + for (i = 1; i <= crdc_window; i++) { + crdc_rssi_sum += + ah->ah_crdc_rssi_sample[chain] + [(crdc_rssi_ptr - i) % HAL_MAX_CRDC_RSSI_SAMPLE]; + } + + return crdc_rssi_sum / crdc_window; +} + +static void +ar9300_crdc_activate(struct ath_hal *ah, int rssi_diff, int enable) +{ + int val, orig_val; + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + int crdc_numerator = ahpriv->ah_config.ath_hal_crdc_numerator; + int crdc_denominator = ahpriv->ah_config.ath_hal_crdc_denominator; + int c = (rssi_diff * crdc_numerator) / crdc_denominator; + + val = orig_val = OS_REG_READ(ah, AR_PHY_MULTICHAIN_CTRL); + val &= 0xffffff00; + if (enable) { + val |= 0x1; + val |= ((c << 1) & 0xff); + } + OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_CTRL, val); + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "diff: %02d comp: %02d reg: %08x %08x\n", + rssi_diff, c, orig_val, val); +} + + +void ar9300_chain_rssi_diff_compensation(struct ath_hal *ah) +{ + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + int crdc_window = ahpriv->ah_config.ath_hal_crdc_window; + int crdc_rssi_ptr = ah->ah_crdc_rssi_ptr; + int crdc_rssi_thresh = ahpriv->ah_config.ath_hal_crdc_rssithresh; + int crdc_diff_thresh = ahpriv->ah_config.ath_hal_crdc_diffthresh; + int avg_rssi[2], avg_rssi_diff; + + if ((!AR_SREV_WASP(ah)) || + (!ahpriv->ah_config.ath_hal_crdc_enable)) { + if (ah->ah_crdc_rssi_ptr) { + ar9300_crdc_activate(ah, 0, 0); + ah->ah_crdc_rssi_ptr = 0; + } + return; + } + + if (crdc_window > HAL_MAX_CRDC_RSSI_SAMPLE) { + crdc_window = HAL_MAX_CRDC_RSSI_SAMPLE; + } + + if (crdc_rssi_ptr < crdc_window) { + return; + } + + avg_rssi[0] = ar9300_crdc_avg_rssi(ah, 0); + avg_rssi[1] = ar9300_crdc_avg_rssi(ah, 1); + avg_rssi_diff = avg_rssi[1] - avg_rssi[0]; + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "crdc: avg: %02d %02d ", + avg_rssi[0], avg_rssi[1]); + + if ((avg_rssi[0] < crdc_rssi_thresh) && + (avg_rssi[1] < crdc_rssi_thresh)) { + ar9300_crdc_activate(ah, 0, 0); + } else { + if (ABS(avg_rssi_diff) >= crdc_diff_thresh) { + ar9300_crdc_activate(ah, avg_rssi_diff, 1); + } else { + ar9300_crdc_activate(ah, 0, 1); + } + } +} +#endif + +#if ATH_ANT_DIV_COMB +HAL_BOOL +ar9300_ant_ctrl_set_lna_div_use_bt_ant(struct ath_hal *ah, HAL_BOOL enable, HAL_CHANNEL *chan) +{ + u_int32_t value; + u_int32_t regval; + struct ath_hal_9300 *ahp = AH9300(ah); + HAL_CHANNEL_INTERNAL *ichan; + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + HAL_CAPABILITIES *pcap = &ahpriv->ah_caps; + + if (AR_SREV_POSEIDON(ah)) { + // Make sure this scheme is only used for WB225(Astra) + ahp->ah_lna_div_use_bt_ant_enable = enable; + + ichan = ar9300_check_chan(ah, chan); + if ( ichan == AH_NULL ) { + HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channel_flags); + return AH_FALSE; + } + + if ( enable == TRUE ) { + pcap->hal_ant_div_comb_support = TRUE; + } else { + pcap->hal_ant_div_comb_support = pcap->hal_ant_div_comb_support_org; + } + +#define AR_SWITCH_TABLE_COM2_ALL (0xffffff) +#define AR_SWITCH_TABLE_COM2_ALL_S (0) + value = ar9300_ant_ctrl_common2_get(ah, IS_CHAN_2GHZ(ichan)); + if ( enable == TRUE ) { + value &= ~AR_SWITCH_TABLE_COM2_ALL; + value |= ahpriv->ah_config.ath_hal_ant_ctrl_comm2g_switch_enable; + } + OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value); + + value = ar9300_eeprom_get(ahp, EEP_ANTDIV_control); + /* main_lnaconf, alt_lnaconf, main_tb, alt_tb */ + regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); + regval &= (~ANT_DIV_CONTROL_ALL); /* clear bit 25~30 */ + regval |= (value & 0x3f) << ANT_DIV_CONTROL_ALL_S; + /* enable_lnadiv */ + regval &= (~MULTICHAIN_GAIN_CTRL__ENABLE_ANT_DIV_LNADIV__MASK); + regval |= ((value >> 6) & 0x1) << + MULTICHAIN_GAIN_CTRL__ENABLE_ANT_DIV_LNADIV__SHIFT; + if ( enable == TRUE ) { + regval |= ANT_DIV_ENABLE; + } + OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); + + /* enable fast_div */ + regval = OS_REG_READ(ah, AR_PHY_CCK_DETECT); + regval &= (~BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__MASK); + regval |= ((value >> 7) & 0x1) << + BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__SHIFT; + if ( enable == TRUE ) { + regval |= FAST_DIV_ENABLE; + } + OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); + + if ( AR_SREV_POSEIDON_11_OR_LATER(ah) ) { + if (pcap->hal_ant_div_comb_support) { + /* If support DivComb, set MAIN to LNA1 and ALT to LNA2 at the first beginning */ + regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); + /* clear bit 25~30 main_lnaconf, alt_lnaconf, main_tb, alt_tb */ + regval &= (~(MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__MASK | + MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__MASK | + MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_GAINTB__MASK | + MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_GAINTB__MASK)); + regval |= (HAL_ANT_DIV_COMB_LNA1 << + MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__SHIFT); + regval |= (HAL_ANT_DIV_COMB_LNA2 << + MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__SHIFT); + OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); + } + } + + return AH_TRUE; + } else { + return AH_TRUE; + } +} +#endif /* ATH_ANT_DIV_COMB */ + +#endif /* AH_SUPPORT_AR9300 */ |