/*
* Copyright (c) 2008-2009 Atheros Communications 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 "hw.h"
static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
struct ath9k_tx_queue_info *qi)
{
ath_print(ath9k_hw_common(ah), ATH_DBG_INTERRUPT,
"tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n",
ah->txok_interrupt_mask, ah->txerr_interrupt_mask,
ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask,
ah->txurn_interrupt_mask);
REG_WRITE(ah, AR_IMR_S0,
SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK)
| SM(ah->txdesc_interrupt_mask, AR_IMR_S0_QCU_TXDESC));
REG_WRITE(ah, AR_IMR_S1,
SM(ah->txerr_interrupt_mask, AR_IMR_S1_QCU_TXERR)
| SM(ah->txeol_interrupt_mask, AR_IMR_S1_QCU_TXEOL));
REG_RMW_FIELD(ah, AR_IMR_S2,
AR_IMR_S2_QCU_TXURN, ah->txurn_interrupt_mask);
}
u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q)
{
return REG_READ(ah, AR_QTXDP(q));
}
EXPORT_SYMBOL(ath9k_hw_gettxbuf);
void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp)
{
REG_WRITE(ah, AR_QTXDP(q), txdp);
}
EXPORT_SYMBOL(ath9k_hw_puttxbuf);
void ath9k_hw_txstart(struct ath_hw *ah, u32 q)
{
ath_print(ath9k_hw_common(ah), ATH_DBG_QUEUE,
"Enable TXE on queue: %u\n", q);
REG_WRITE(ah, AR_Q_TXE, 1 << q);
}
EXPORT_SYMBOL(ath9k_hw_txstart);
u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q)
{
u32 npend;
npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT;
if (npend == 0) {
if (REG_READ(ah, AR_Q_TXE) & (1 << q))
npend = 1;
}
return npend;
}
EXPORT_SYMBOL(ath9k_hw_numtxpending);
/**
* ath9k_hw_updatetxtriglevel - adjusts the frame trigger level
*
* @ah: atheros hardware struct
* @bIncTrigLevel: whether or not the frame trigger level should be updated
*
* The frame trigger level specifies the minimum number of bytes,
* in units of 64 bytes, that must be DMA'ed into the PCU TX FIFO
* before the PCU will initiate sending the frame on the air. This can
* mean we initiate transmit before a full frame is on the PCU TX FIFO.
* Resets to 0x1 (meaning 64 bytes or a full frame, whichever occurs
* first)
*
* Caution must be taken to ensure to set the frame trigger level based
* on the DMA request size. For example if the DMA request size is set to
* 128 bytes the trigger level cannot exceed 6 * 64 = 384. This is because
* there need to be enough space in the tx FIFO for the requested transfer
* size. Hence the tx FIFO will stop with 512 - 128 = 384 bytes. If we set
* the threshold to a value beyond 6, then the transmit will hang.
*
* Current dual stream devices have a PCU TX FIFO size of 8 KB.
* Current single stream devices have a PCU TX FIFO size of 4 KB, however,
* there is a hardware issue which forces us to use 2 KB instead so the
* frame trigger level must not exceed 2 KB for these chipsets.
*/
bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
{
u32 txcfg, curLevel, newLevel;
enum ath9k_int omask;
if (ah->tx_trig_level >= ah->config.max_txtrig_level)
return false;
omask = ath9k_hw_set_interrupts(ah, ah->mask_reg & ~ATH9K_INT_GLOBAL);
txcfg = REG_READ(ah, AR_TXCFG);
curLevel = MS(txcfg, AR_FTRIG);
newLevel = curLevel;
if (bIncTrigLevel) {
if (curLevel < ah->config.max_txtrig_level)
newLevel++;
} else if (curLevel > MIN_TX_FIFO_THRESHOLD)
newLevel--;
if (newLevel != curLevel)
REG_WRITE(ah, AR_TXCFG,
(txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));
ath9k_hw_set_interrupts(ah, omask);
ah->tx_trig_level = newLevel;
return newLevel != curLevel;
}
EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel);
bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
{
#define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */
#define ATH9K_TIME_QUANTUM 100 /* usec */
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct