diff options
Diffstat (limited to 'hal/ar9300/ar9300_recv.c')
-rw-r--r-- | hal/ar9300/ar9300_recv.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/hal/ar9300/ar9300_recv.c b/hal/ar9300/ar9300_recv.c new file mode 100644 index 000000000000..ffb9536b9145 --- /dev/null +++ b/hal/ar9300/ar9300_recv.c @@ -0,0 +1,341 @@ +/* + * 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_desc.h" +#include "ah_internal.h" + +#include "ar9300/ar9300.h" +#include "ar9300/ar9300reg.h" +#include "ar9300/ar9300desc.h" + +/* + * Get the RXDP. + */ +u_int32_t +ar9300_get_rx_dp(struct ath_hal *ath, HAL_RX_QUEUE qtype) +{ + if (qtype == HAL_RX_QUEUE_HP) { + return OS_REG_READ(ath, AR_HP_RXDP); + } else { + return OS_REG_READ(ath, AR_LP_RXDP); + } +} + +/* + * Set the rx_dp. + */ +void +ar9300_set_rx_dp(struct ath_hal *ah, u_int32_t rxdp, HAL_RX_QUEUE qtype) +{ + HALASSERT((qtype == HAL_RX_QUEUE_HP) || (qtype == HAL_RX_QUEUE_LP)); + + if (qtype == HAL_RX_QUEUE_HP) { + OS_REG_WRITE(ah, AR_HP_RXDP, rxdp); + } else { + OS_REG_WRITE(ah, AR_LP_RXDP, rxdp); + } +} + +/* + * Set Receive Enable bits. + */ +void +ar9300_enable_receive(struct ath_hal *ah) +{ + OS_REG_WRITE(ah, AR_CR, 0); +} + +/* + * Set the RX abort bit. + */ +HAL_BOOL +ar9300_set_rx_abort(struct ath_hal *ah, HAL_BOOL set) +{ + if (set) { + /* Set the force_rx_abort bit */ + OS_REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + + if ( AH_PRIVATE(ah)->ah_reset_reason == HAL_RESET_BBPANIC ){ + /* depending upon the BB panic status, rx state may not return to 0, + * so skipping the wait for BB panic reset */ + OS_REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + return AH_FALSE; + } else { + HAL_BOOL okay; + okay = ath_hal_wait( + ah, AR_OBS_BUS_1, AR_OBS_BUS_1_RX_STATE, 0, AH_WAIT_TIMEOUT); + /* Wait for Rx state to return to 0 */ + if (!okay) { + /* abort: chip rx failed to go idle in 10 ms */ + OS_REG_CLR_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + + HALDEBUG(ah, HAL_DEBUG_RX, + "%s: rx failed to go idle in 10 ms RXSM=0x%x\n", + __func__, OS_REG_READ(ah, AR_OBS_BUS_1)); + + return AH_FALSE; /* failure */ + } + } + } else { + OS_REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + } + + return AH_TRUE; /* success */ +} + +/* + * Stop Receive at the DMA engine + */ +HAL_BOOL +ar9300_stop_dma_receive(struct ath_hal *ah, u_int timeout) +{ + int wait; + HAL_BOOL status, okay; + u_int32_t org_value; + +#define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ +#define AH_TIME_QUANTUM 100 /* usec */ + + if (timeout == 0) { + timeout = AH_RX_STOP_DMA_TIMEOUT; + } + + org_value = OS_REG_READ(ah, AR_MACMISC); + + OS_REG_WRITE(ah, AR_MACMISC, + ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | + (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); + + okay = ath_hal_wait( + ah, AR_DMADBG_7, AR_DMADBG_RX_STATE, 0, AH_WAIT_TIMEOUT); + /* wait for Rx DMA state machine to become idle */ + if (!okay) { + HALDEBUG(ah, HAL_DEBUG_RX, + "reg AR_DMADBG_7 is not 0, instead 0x%08x\n", + OS_REG_READ(ah, AR_DMADBG_7)); + } + + /* Set receive disable bit */ + OS_REG_WRITE(ah, AR_CR, AR_CR_RXD); + + /* Wait for rx enable bit to go low */ + for (wait = timeout / AH_TIME_QUANTUM; wait != 0; wait--) { + if ((OS_REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) { + break; + } + OS_DELAY(AH_TIME_QUANTUM); + } + + if (wait == 0) { + HALDEBUG(ah, HAL_DEBUG_RX, "%s: dma failed to stop in %d ms\n" + "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n", + __func__, + timeout / 1000, + OS_REG_READ(ah, AR_CR), + OS_REG_READ(ah, AR_DIAG_SW)); + status = AH_FALSE; + } else { + status = AH_TRUE; + } + + OS_REG_WRITE(ah, AR_MACMISC, org_value); + + return status; +#undef AH_RX_STOP_DMA_TIMEOUT +#undef AH_TIME_QUANTUM +} + +/* + * Start Transmit at the PCU engine (unpause receive) + */ +void +ar9300_start_pcu_receive(struct ath_hal *ah, HAL_BOOL is_scanning) +{ + ar9300_enable_mib_counters(ah); + ar9300_ani_reset(ah, is_scanning); + /* Clear RX_DIS and RX_ABORT after enabling phy errors in ani_reset */ + OS_REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); +} + +/* + * Stop Transmit at the PCU engine (pause receive) + */ +void +ar9300_stop_pcu_receive(struct ath_hal *ah) +{ + OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + ar9300_disable_mib_counters(ah); +} + +/* + * Set multicast filter 0 (lower 32-bits) + * filter 1 (upper 32-bits) + */ +void +ar9300_set_multicast_filter( + struct ath_hal *ah, + u_int32_t filter0, + u_int32_t filter1) +{ + OS_REG_WRITE(ah, AR_MCAST_FIL0, filter0); + OS_REG_WRITE(ah, AR_MCAST_FIL1, filter1); +} + +/* + * Get the receive filter. + */ +u_int32_t +ar9300_get_rx_filter(struct ath_hal *ah) +{ + u_int32_t bits = OS_REG_READ(ah, AR_RX_FILTER); + u_int32_t phybits = OS_REG_READ(ah, AR_PHY_ERR); + if (phybits & AR_PHY_ERR_RADAR) { + bits |= HAL_RX_FILTER_PHYRADAR; + } + if (phybits & (AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING)) { + bits |= HAL_RX_FILTER_PHYERR; + } + return bits; +} + +/* + * Set the receive filter. + */ +void +ar9300_set_rx_filter(struct ath_hal *ah, u_int32_t bits) +{ + u_int32_t phybits; + + if (AR_SREV_SCORPION(ah)) { + /* Enable Rx for 4 address frames */ + bits |= AR_RX_4ADDRESS; + } + if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { + /* HW fix for rx hang and corruption. */ + bits |= AR_RX_CONTROL_WRAPPER; + } + OS_REG_WRITE(ah, AR_RX_FILTER, + bits | AR_RX_UNCOM_BA_BAR | AR_RX_COMPR_BAR); + phybits = 0; + if (bits & HAL_RX_FILTER_PHYRADAR) { + phybits |= AR_PHY_ERR_RADAR; + } + if (bits & HAL_RX_FILTER_PHYERR) { + phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING; + } + OS_REG_WRITE(ah, AR_PHY_ERR, phybits); + if (phybits) { + OS_REG_WRITE(ah, AR_RXCFG, + OS_REG_READ(ah, AR_RXCFG) | AR_RXCFG_ZLFDMA); + } else { + OS_REG_WRITE(ah, AR_RXCFG, + OS_REG_READ(ah, AR_RXCFG) &~ AR_RXCFG_ZLFDMA); + } +} + +/* + * Select to pass PLCP headr or EVM data. + */ +HAL_BOOL +ar9300_set_rx_sel_evm(struct ath_hal *ah, HAL_BOOL sel_evm, HAL_BOOL just_query) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + HAL_BOOL old_value = ahp->ah_get_plcp_hdr == 0; + + if (just_query) { + return old_value; + } + if (sel_evm) { + OS_REG_SET_BIT(ah, AR_PCU_MISC, AR_PCU_SEL_EVM); + } else { + OS_REG_CLR_BIT(ah, AR_PCU_MISC, AR_PCU_SEL_EVM); + } + + ahp->ah_get_plcp_hdr = !sel_evm; + + return old_value; +} + +void ar9300_promisc_mode(struct ath_hal *ah, HAL_BOOL enable) +{ + u_int32_t reg_val = 0; + reg_val = OS_REG_READ(ah, AR_RX_FILTER); + if (enable){ + reg_val |= AR_RX_PROM; + } else{ /*Disable promisc mode */ + reg_val &= ~AR_RX_PROM; + } + OS_REG_WRITE(ah, AR_RX_FILTER, reg_val); +} + +void +ar9300_read_pktlog_reg( + struct ath_hal *ah, + u_int32_t *rxfilter_val, + u_int32_t *rxcfg_val, + u_int32_t *phy_err_mask_val, + u_int32_t *mac_pcu_phy_err_regval) +{ + *rxfilter_val = OS_REG_READ(ah, AR_RX_FILTER); + *rxcfg_val = OS_REG_READ(ah, AR_RXCFG); + *phy_err_mask_val = OS_REG_READ(ah, AR_PHY_ERR); + *mac_pcu_phy_err_regval = OS_REG_READ(ah, 0x8338); + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s[%d] rxfilter_val 0x%08x , rxcfg_val 0x%08x, " + "phy_err_mask_val 0x%08x mac_pcu_phy_err_regval 0x%08x\n", + __func__, __LINE__, + *rxfilter_val, *rxcfg_val, *phy_err_mask_val, *mac_pcu_phy_err_regval); +} + +void +ar9300_write_pktlog_reg( + struct ath_hal *ah, + HAL_BOOL enable, + u_int32_t rxfilter_val, + u_int32_t rxcfg_val, + u_int32_t phy_err_mask_val, + u_int32_t mac_pcu_phy_err_reg_val) +{ + if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { + /* HW fix for rx hang and corruption. */ + rxfilter_val |= AR_RX_CONTROL_WRAPPER; + } + if (enable) { /* Enable pktlog phyerr setting */ + OS_REG_WRITE(ah, AR_RX_FILTER, 0xffff | AR_RX_COMPR_BAR | rxfilter_val); + OS_REG_WRITE(ah, AR_PHY_ERR, 0xFFFFFFFF); + OS_REG_WRITE(ah, AR_RXCFG, rxcfg_val | AR_RXCFG_ZLFDMA); + OS_REG_WRITE(ah, AR_PHY_ERR_MASK_REG, mac_pcu_phy_err_reg_val | 0xFF); + } else { /* Disable phyerr and Restore regs */ + OS_REG_WRITE(ah, AR_RX_FILTER, rxfilter_val); + OS_REG_WRITE(ah, AR_PHY_ERR, phy_err_mask_val); + OS_REG_WRITE(ah, AR_RXCFG, rxcfg_val); + OS_REG_WRITE(ah, AR_PHY_ERR_MASK_REG, mac_pcu_phy_err_reg_val); + } + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s[%d] ena %d rxfilter_val 0x%08x , rxcfg_val 0x%08x, " + "phy_err_mask_val 0x%08x mac_pcu_phy_err_regval 0x%08x\n", + __func__, __LINE__, + enable, rxfilter_val, rxcfg_val, + phy_err_mask_val, mac_pcu_phy_err_reg_val); +} + +#endif /* AH_SUPPORT_AR9300 */ |