summaryrefslogtreecommitdiffstats
path: root/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv.c')
-rw-r--r--sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv.c337
1 files changed, 337 insertions, 0 deletions
diff --git a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv.c b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv.c
new file mode 100644
index 0000000..e13c00b
--- /dev/null
+++ b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv.c
@@ -0,0 +1,337 @@
+/*
+ * 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"
+
+#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 ( AH9300(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);
+ /* 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);
+ /* 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);
+}
OpenPOWER on IntegriCloud