/* * 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 */