/*
* 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 */
OS_MARK(ah, AH_MARK_RX_CTL, AH_MARK_RX_CTL_DMA_STOP);
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);
OS_MARK(ah, AH_MARK_RX_CTL,
status ? AH_MARK_RX_CTL_DMA_STOP_OK : AH_MARK_RX_CTL_DMA_STOP_ERR);
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) || AR_SREV_HONEYBEE(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);
}