2008-08-29 22:45:39 +03:00
/*
* Copyright ( c ) 2004 - 2008 Reyk Floeter < reyk @ openbsd . org >
* Copyright ( c ) 2006 - 2008 Nick Kossifidis < mickflemm @ gmail . com >
*
* Permission to use , copy , modify , and 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 .
*
*/
/********************************************\
2011-11-25 20:40:25 +02:00
Queue Control Unit , DCF Control Unit Functions
2008-08-29 22:45:39 +03:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-03-18 17:30:52 -07:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-08-29 22:45:39 +03:00
# include "ath5k.h"
# include "reg.h"
# include "debug.h"
2011-11-25 20:40:31 +02:00
# include <linux/log2.h>
2008-08-29 22:45:39 +03:00
2011-11-25 20:40:25 +02:00
/**
* DOC : Queue Control Unit ( QCU ) / DCF Control Unit ( DCU ) functions
*
* Here we setup parameters for the 12 available TX queues . Note that
* on the various registers we can usually only map the first 10 of them so
* basically we have 10 queues to play with . Each queue has a matching
* QCU that controls when the queue will get triggered and multiple QCUs
* can be mapped to a single DCU that controls the various DFS parameters
* for the various queues . In our setup we have a 1 : 1 mapping between QCUs
* and DCUs allowing us to have different DFS settings for each queue .
*
* When a frame goes into a TX queue , QCU decides when it ' ll trigger a
* transmission based on various criteria ( such as how many data we have inside
* it ' s buffer or - if it ' s a beacon queue - if it ' s time to fire up the queue
* based on TSF etc ) , DCU adds backoff , IFSes etc and then a scheduler
* ( arbitrator ) decides the priority of each QCU based on it ' s configuration
* ( e . g . beacons are always transmitted when they leave DCU bypassing all other
* frames from other queues waiting to be transmitted ) . After a frame leaves
* the DCU it goes to PCU for further processing and then to PHY for
* the actual transmission .
*/
2010-11-23 20:36:45 +02:00
/******************\
* Helper functions *
\ * * * * * * * * * * * * * * * * * */
2011-11-25 20:40:25 +02:00
/**
* ath5k_hw_num_tx_pending ( ) - Get number of pending frames for a given queue
* @ ah : The & struct ath5k_hw
2011-11-25 20:40:30 +02:00
* @ queue : One of enum ath5k_tx_queue_id
2008-08-29 22:45:39 +03:00
*/
2011-11-25 20:40:25 +02:00
u32
ath5k_hw_num_tx_pending ( struct ath5k_hw * ah , unsigned int queue )
2008-08-29 22:45:39 +03:00
{
2010-11-23 20:36:45 +02:00
u32 pending ;
AR5K_ASSERT_ENTRY ( queue , ah - > ah_capabilities . cap_queues . q_tx_num ) ;
/* Return if queue is declared inactive */
if ( ah - > ah_txq [ queue ] . tqi_type = = AR5K_TX_QUEUE_INACTIVE )
return false ;
/* XXX: How about AR5K_CFG_TXCNT ? */
if ( ah - > ah_version = = AR5K_AR5210 )
return false ;
pending = ath5k_hw_reg_read ( ah , AR5K_QUEUE_STATUS ( queue ) ) ;
pending & = AR5K_QCU_STS_FRMPENDCNT ;
/* It's possible to have no frames pending even if TXE
* is set . To indicate that q has not stopped return
* true */
if ( ! pending & & AR5K_REG_READ_Q ( ah , AR5K_QCU_TXE , queue ) )
return true ;
return pending ;
}
2011-11-25 20:40:25 +02:00
/**
* ath5k_hw_release_tx_queue ( ) - Set a transmit queue inactive
* @ ah : The & struct ath5k_hw
2011-11-25 20:40:30 +02:00
* @ queue : One of enum ath5k_tx_queue_id
2010-11-23 20:36:45 +02:00
*/
2011-11-25 20:40:25 +02:00
void
ath5k_hw_release_tx_queue ( struct ath5k_hw * ah , unsigned int queue )
2010-11-23 20:36:45 +02:00
{
if ( WARN_ON ( queue > = ah - > ah_capabilities . cap_queues . q_tx_num ) )
return ;
/* This queue will be skipped in further operations */
ah - > ah_txq [ queue ] . tqi_type = AR5K_TX_QUEUE_INACTIVE ;
/*For SIMR setup*/
AR5K_Q_DISABLE_BITS ( ah - > ah_txq_status , queue ) ;
2008-08-29 22:45:39 +03:00
}
2011-11-25 20:40:25 +02:00
/**
* ath5k_cw_validate ( ) - Make sure the given cw is valid
* @ cw_req : The contention window value to check
*
2010-09-17 11:37:12 +09:00
* Make sure cw is a power of 2 minus 1 and smaller than 1024
*/
2011-11-25 20:40:25 +02:00
static u16
ath5k_cw_validate ( u16 cw_req )
2010-09-17 11:37:12 +09:00
{
cw_req = min ( cw_req , ( u16 ) 1023 ) ;
2011-11-25 20:40:31 +02:00
/* Check if cw_req + 1 a power of 2 */
if ( is_power_of_2 ( cw_req + 1 ) )
return cw_req ;
2010-09-17 11:37:12 +09:00
2011-11-25 20:40:31 +02:00
/* Check if cw_req is a power of 2 */
if ( is_power_of_2 ( cw_req ) )
return cw_req - 1 ;
/* If none of the above is correct
* find the closest power of 2 */
cw_req = ( u16 ) roundup_pow_of_two ( cw_req ) - 1 ;
return cw_req ;
2010-09-17 11:37:12 +09:00
}
2011-11-25 20:40:25 +02:00
/**
* ath5k_hw_get_tx_queueprops ( ) - Get properties for a transmit queue
* @ ah : The & struct ath5k_hw
2011-11-25 20:40:30 +02:00
* @ queue : One of enum ath5k_tx_queue_id
2011-11-25 20:40:25 +02:00
* @ queue_info : The & struct ath5k_txq_info to fill
2010-11-23 20:36:45 +02:00
*/
2011-11-25 20:40:25 +02:00
int
ath5k_hw_get_tx_queueprops ( struct ath5k_hw * ah , int queue ,
2010-11-23 20:36:45 +02:00
struct ath5k_txq_info * queue_info )
{
memcpy ( queue_info , & ah - > ah_txq [ queue ] , sizeof ( struct ath5k_txq_info ) ) ;
return 0 ;
}
2011-11-25 20:40:25 +02:00
/**
* ath5k_hw_set_tx_queueprops ( ) - Set properties for a transmit queue
* @ ah : The & struct ath5k_hw
2011-11-25 20:40:30 +02:00
* @ queue : One of enum ath5k_tx_queue_id
2011-11-25 20:40:25 +02:00
* @ qinfo : The & struct ath5k_txq_info to use
*
* Returns 0 on success or - EIO if queue is inactive
2008-08-29 22:45:39 +03:00
*/
2011-11-25 20:40:25 +02:00
int
ath5k_hw_set_tx_queueprops ( struct ath5k_hw * ah , int queue ,
2010-09-17 11:37:12 +09:00
const struct ath5k_txq_info * qinfo )
2008-08-29 22:45:39 +03:00
{
2010-09-17 11:37:12 +09:00
struct ath5k_txq_info * qi ;
2008-08-29 22:45:39 +03:00
AR5K_ASSERT_ENTRY ( queue , ah - > ah_capabilities . cap_queues . q_tx_num ) ;
2010-09-17 11:37:12 +09:00
qi = & ah - > ah_txq [ queue ] ;
if ( qi - > tqi_type = = AR5K_TX_QUEUE_INACTIVE )
2008-08-29 22:45:39 +03:00
return - EIO ;
2010-09-17 11:37:12 +09:00
/* copy and validate values */
qi - > tqi_type = qinfo - > tqi_type ;
qi - > tqi_subtype = qinfo - > tqi_subtype ;
qi - > tqi_flags = qinfo - > tqi_flags ;
/*
* According to the docs : Although the AIFS field is 8 bit wide ,
* the maximum supported value is 0xFC . Setting it higher than that
* will cause the DCU to hang .
*/
qi - > tqi_aifs = min ( qinfo - > tqi_aifs , ( u8 ) 0xFC ) ;
qi - > tqi_cw_min = ath5k_cw_validate ( qinfo - > tqi_cw_min ) ;
qi - > tqi_cw_max = ath5k_cw_validate ( qinfo - > tqi_cw_max ) ;
qi - > tqi_cbr_period = qinfo - > tqi_cbr_period ;
qi - > tqi_cbr_overflow_limit = qinfo - > tqi_cbr_overflow_limit ;
qi - > tqi_burst_time = qinfo - > tqi_burst_time ;
qi - > tqi_ready_time = qinfo - > tqi_ready_time ;
2008-08-29 22:45:39 +03:00
/*XXX: Is this supported on 5210 ?*/
2010-09-17 11:37:12 +09:00
/*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/
if ( ( qinfo - > tqi_type = = AR5K_TX_QUEUE_DATA & &
( ( qinfo - > tqi_subtype = = AR5K_WME_AC_VI ) | |
( qinfo - > tqi_subtype = = AR5K_WME_AC_VO ) ) ) | |
qinfo - > tqi_type = = AR5K_TX_QUEUE_UAPSD )
qi - > tqi_flags | = AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS ;
2008-08-29 22:45:39 +03:00
return 0 ;
}
2011-11-25 20:40:25 +02:00
/**
* ath5k_hw_setup_tx_queue ( ) - Initialize a transmit queue
* @ ah : The & struct ath5k_hw
* @ queue_type : One of enum ath5k_tx_queue
* @ queue_info : The & struct ath5k_txq_info to use
*
* Returns 0 on success , - EINVAL on invalid arguments
2008-08-29 22:45:39 +03:00
*/
2011-11-25 20:40:25 +02:00
int
ath5k_hw_setup_tx_queue ( struct ath5k_hw * ah , enum ath5k_tx_queue queue_type ,
2008-08-29 22:45:39 +03:00
struct ath5k_txq_info * queue_info )
{
unsigned int queue ;
int ret ;
/*
* Get queue by type
*/
2010-12-07 11:08:12 +09:00
/* 5210 only has 2 queues */
if ( ah - > ah_capabilities . cap_queues . q_tx_num = = 2 ) {
2008-08-29 22:45:39 +03:00
switch ( queue_type ) {
case AR5K_TX_QUEUE_DATA :
queue = AR5K_TX_QUEUE_ID_NOQCU_DATA ;
break ;
case AR5K_TX_QUEUE_BEACON :
case AR5K_TX_QUEUE_CAB :
queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON ;
break ;
default :
return - EINVAL ;
}
} else {
switch ( queue_type ) {
case AR5K_TX_QUEUE_DATA :
2014-11-30 21:52:57 +01:00
queue = queue_info - > tqi_subtype ;
2008-08-29 22:45:39 +03:00
break ;
case AR5K_TX_QUEUE_UAPSD :
queue = AR5K_TX_QUEUE_ID_UAPSD ;
break ;
case AR5K_TX_QUEUE_BEACON :
queue = AR5K_TX_QUEUE_ID_BEACON ;
break ;
case AR5K_TX_QUEUE_CAB :
queue = AR5K_TX_QUEUE_ID_CAB ;
break ;
default :
return - EINVAL ;
}
}
/*
* Setup internal queue structure
*/
memset ( & ah - > ah_txq [ queue ] , 0 , sizeof ( struct ath5k_txq_info ) ) ;
ah - > ah_txq [ queue ] . tqi_type = queue_type ;
if ( queue_info ! = NULL ) {
queue_info - > tqi_type = queue_type ;
ret = ath5k_hw_set_tx_queueprops ( ah , queue , queue_info ) ;
if ( ret )
return ret ;
}
/*
* We use ah_txq_status to hold a temp value for
* the Secondary interrupt mask registers on 5211 +
* check out ath5k_hw_reset_tx_queue
*/
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_status , queue ) ;
return queue ;
}
2010-11-23 20:36:45 +02:00
/*******************************\
* Single QCU / DCU initialization *
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-08-29 22:45:39 +03:00
2011-11-25 20:40:25 +02:00
/**
* ath5k_hw_set_tx_retry_limits ( ) - Set tx retry limits on DCU
* @ ah : The & struct ath5k_hw
2011-11-25 20:40:30 +02:00
* @ queue : One of enum ath5k_tx_queue_id
2011-11-25 20:40:25 +02:00
*
* This function is used when initializing a queue , to set
* retry limits based on ah - > ah_retry_ * and the chipset used .
2010-11-23 21:07:04 +02:00
*/
2011-11-25 20:40:25 +02:00
void
ath5k_hw_set_tx_retry_limits ( struct ath5k_hw * ah ,
2011-01-28 16:52:11 +09:00
unsigned int queue )
2010-11-23 21:07:04 +02:00
{
/* Single data queue on AR5210 */
if ( ah - > ah_version = = AR5K_AR5210 ) {
struct ath5k_txq_info * tq = & ah - > ah_txq [ queue ] ;
if ( queue > 0 )
return ;
ath5k_hw_reg_write ( ah ,
( tq - > tqi_cw_min < < AR5K_NODCU_RETRY_LMT_CW_MIN_S )
2011-01-28 16:52:11 +09:00
| AR5K_REG_SM ( ah - > ah_retry_long ,
AR5K_NODCU_RETRY_LMT_SLG_RETRY )
| AR5K_REG_SM ( ah - > ah_retry_short ,
AR5K_NODCU_RETRY_LMT_SSH_RETRY )
| AR5K_REG_SM ( ah - > ah_retry_long ,
AR5K_NODCU_RETRY_LMT_LG_RETRY )
| AR5K_REG_SM ( ah - > ah_retry_short ,
AR5K_NODCU_RETRY_LMT_SH_RETRY ) ,
2010-11-23 21:07:04 +02:00
AR5K_NODCU_RETRY_LMT ) ;
/* DCU on AR5211+ */
} else {
ath5k_hw_reg_write ( ah ,
2011-01-28 16:52:11 +09:00
AR5K_REG_SM ( ah - > ah_retry_long ,
AR5K_DCU_RETRY_LMT_RTS )
| AR5K_REG_SM ( ah - > ah_retry_long ,
AR5K_DCU_RETRY_LMT_STA_RTS )
| AR5K_REG_SM ( max ( ah - > ah_retry_long , ah - > ah_retry_short ) ,
AR5K_DCU_RETRY_LMT_STA_DATA ) ,
2010-11-23 21:07:04 +02:00
AR5K_QUEUE_DFS_RETRY_LIMIT ( queue ) ) ;
}
}
2010-11-23 21:19:45 +02:00
/**
2011-11-25 20:40:25 +02:00
* ath5k_hw_reset_tx_queue ( ) - Initialize a single hw queue
* @ ah : The & struct ath5k_hw
2011-11-25 20:40:30 +02:00
* @ queue : One of enum ath5k_tx_queue_id
2010-11-23 21:19:45 +02:00
*
2011-11-25 20:40:30 +02:00
* Set DCF properties for the given transmit queue on DCU
2010-11-23 21:19:45 +02:00
* and configures all queue - specific parameters .
2008-08-29 22:45:39 +03:00
*/
2011-11-25 20:40:25 +02:00
int
ath5k_hw_reset_tx_queue ( struct ath5k_hw * ah , unsigned int queue )
2008-08-29 22:45:39 +03:00
{
struct ath5k_txq_info * tq = & ah - > ah_txq [ queue ] ;
AR5K_ASSERT_ENTRY ( queue , ah - > ah_capabilities . cap_queues . q_tx_num ) ;
2010-11-23 21:19:45 +02:00
/* Skip if queue inactive or if we are on AR5210
* that doesn ' t have QCU / DCU */
if ( ( ah - > ah_version = = AR5K_AR5210 ) | |
( tq - > tqi_type = = AR5K_TX_QUEUE_INACTIVE ) )
2008-08-29 22:45:39 +03:00
return 0 ;
2010-11-23 21:19:45 +02:00
/*
* Set contention window ( cw_min / cw_max )
* and arbitrated interframe space ( aifs ) . . .
*/
ath5k_hw_reg_write ( ah ,
AR5K_REG_SM ( tq - > tqi_cw_min , AR5K_DCU_LCL_IFS_CW_MIN ) |
AR5K_REG_SM ( tq - > tqi_cw_max , AR5K_DCU_LCL_IFS_CW_MAX ) |
AR5K_REG_SM ( tq - > tqi_aifs , AR5K_DCU_LCL_IFS_AIFS ) ,
AR5K_QUEUE_DFS_LOCAL_IFS ( queue ) ) ;
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
/*
* Set tx retry limits for this queue
*/
ath5k_hw_set_tx_retry_limits ( ah , queue ) ;
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
/*
* Set misc registers
*/
2010-11-23 21:07:04 +02:00
2010-11-23 21:19:45 +02:00
/* Enable DCU to wait for next fragment from QCU */
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_DFS_MISC ( queue ) ,
AR5K_DCU_MISC_FRAG_WAIT ) ;
2009-01-06 17:27:06 +02:00
2010-11-23 21:19:45 +02:00
/* On Maui and Spirit use the global seqnum on DCU */
if ( ah - > ah_mac_version < AR5K_SREV_AR5211 )
2009-01-06 17:27:06 +02:00
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_DFS_MISC ( queue ) ,
2010-11-23 21:19:45 +02:00
AR5K_DCU_MISC_SEQNUM_CTL ) ;
/* Constant bit rate period */
if ( tq - > tqi_cbr_period ) {
ath5k_hw_reg_write ( ah , AR5K_REG_SM ( tq - > tqi_cbr_period ,
AR5K_QCU_CBRCFG_INTVAL ) |
AR5K_REG_SM ( tq - > tqi_cbr_overflow_limit ,
AR5K_QCU_CBRCFG_ORN_THRES ) ,
AR5K_QUEUE_CBRCFG ( queue ) ) ;
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_MISC ( queue ) ,
AR5K_QCU_MISC_FRSHED_CBR ) ;
if ( tq - > tqi_cbr_overflow_limit )
2008-08-29 22:45:39 +03:00
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_MISC ( queue ) ,
AR5K_QCU_MISC_CBR_THRES_ENABLE ) ;
2010-11-23 21:19:45 +02:00
}
/* Ready time interval */
if ( tq - > tqi_ready_time & & ( tq - > tqi_type ! = AR5K_TX_QUEUE_CAB ) )
ath5k_hw_reg_write ( ah , AR5K_REG_SM ( tq - > tqi_ready_time ,
AR5K_QCU_RDYTIMECFG_INTVAL ) |
AR5K_QCU_RDYTIMECFG_ENABLE ,
AR5K_QUEUE_RDYTIMECFG ( queue ) ) ;
if ( tq - > tqi_burst_time ) {
ath5k_hw_reg_write ( ah , AR5K_REG_SM ( tq - > tqi_burst_time ,
AR5K_DCU_CHAN_TIME_DUR ) |
AR5K_DCU_CHAN_TIME_ENABLE ,
AR5K_QUEUE_DFS_CHANNEL_TIME ( queue ) ) ;
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE )
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_MISC ( queue ) ,
2008-08-29 22:45:39 +03:00
AR5K_QCU_MISC_RDY_VEOL_POLICY ) ;
2010-11-23 21:19:45 +02:00
}
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
/* Enable/disable Post frame backoff */
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE )
ath5k_hw_reg_write ( ah , AR5K_DCU_MISC_POST_FR_BKOFF_DIS ,
AR5K_QUEUE_DFS_MISC ( queue ) ) ;
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
/* Enable/disable fragmentation burst backoff */
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE )
ath5k_hw_reg_write ( ah , AR5K_DCU_MISC_BACKOFF_FRAG ,
AR5K_QUEUE_DFS_MISC ( queue ) ) ;
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
/*
* Set registers by queue type
*/
switch ( tq - > tqi_type ) {
case AR5K_TX_QUEUE_BEACON :
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_MISC ( queue ) ,
2008-08-29 22:45:39 +03:00
AR5K_QCU_MISC_FRSHED_DBA_GT |
2008-09-29 02:09:09 +03:00
AR5K_QCU_MISC_CBREXP_BCN_DIS |
2008-08-29 22:45:39 +03:00
AR5K_QCU_MISC_BCN_ENABLE ) ;
2010-11-23 21:19:45 +02:00
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_DFS_MISC ( queue ) ,
2008-08-29 22:45:39 +03:00
( AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL < <
AR5K_DCU_MISC_ARBLOCK_CTL_S ) |
2009-04-30 15:55:47 -04:00
AR5K_DCU_MISC_ARBLOCK_IGNORE |
2008-08-29 22:45:39 +03:00
AR5K_DCU_MISC_POST_FR_BKOFF_DIS |
AR5K_DCU_MISC_BCN_ENABLE ) ;
2010-11-23 21:19:45 +02:00
break ;
case AR5K_TX_QUEUE_CAB :
/* XXX: use BCN_SENT_GT, if we can figure out how */
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_MISC ( queue ) ,
AR5K_QCU_MISC_FRSHED_DBA_GT |
AR5K_QCU_MISC_CBREXP_DIS |
AR5K_QCU_MISC_CBREXP_BCN_DIS ) ;
ath5k_hw_reg_write ( ah , ( ( tq - > tqi_ready_time -
( AR5K_TUNE_SW_BEACON_RESP -
AR5K_TUNE_DMA_BEACON_RESP ) -
AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF ) * 1024 ) |
AR5K_QCU_RDYTIMECFG_ENABLE ,
AR5K_QUEUE_RDYTIMECFG ( queue ) ) ;
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_DFS_MISC ( queue ) ,
( AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL < <
AR5K_DCU_MISC_ARBLOCK_CTL_S ) ) ;
break ;
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
case AR5K_TX_QUEUE_UAPSD :
AR5K_REG_ENABLE_BITS ( ah , AR5K_QUEUE_MISC ( queue ) ,
AR5K_QCU_MISC_CBREXP_DIS ) ;
break ;
2009-01-06 17:27:06 +02:00
2010-11-23 21:19:45 +02:00
case AR5K_TX_QUEUE_DATA :
default :
2008-08-29 22:45:39 +03:00
break ;
2010-11-23 21:19:45 +02:00
}
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
/* TODO: Handle frame compression */
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
/*
* Enable interrupts for this tx queue
* in the secondary interrupt mask registers
*/
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_txok , queue ) ;
2008-08-29 22:45:39 +03:00
2010-11-23 21:19:45 +02:00
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_txerr , queue ) ;
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_txurn , queue ) ;
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_txdesc , queue ) ;
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_txeol , queue ) ;
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_cbrorn , queue ) ;
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_cbrurn , queue ) ;
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_qtrig , queue ) ;
if ( tq - > tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE )
AR5K_Q_ENABLE_BITS ( ah - > ah_txq_imr_nofrm , queue ) ;
/* Update secondary interrupt mask registers */
/* Filter out inactive queues */
ah - > ah_txq_imr_txok & = ah - > ah_txq_status ;
ah - > ah_txq_imr_txerr & = ah - > ah_txq_status ;
ah - > ah_txq_imr_txurn & = ah - > ah_txq_status ;
ah - > ah_txq_imr_txdesc & = ah - > ah_txq_status ;
ah - > ah_txq_imr_txeol & = ah - > ah_txq_status ;
ah - > ah_txq_imr_cbrorn & = ah - > ah_txq_status ;
ah - > ah_txq_imr_cbrurn & = ah - > ah_txq_status ;
ah - > ah_txq_imr_qtrig & = ah - > ah_txq_status ;
ah - > ah_txq_imr_nofrm & = ah - > ah_txq_status ;
ath5k_hw_reg_write ( ah , AR5K_REG_SM ( ah - > ah_txq_imr_txok ,
AR5K_SIMR0_QCU_TXOK ) |
AR5K_REG_SM ( ah - > ah_txq_imr_txdesc ,
AR5K_SIMR0_QCU_TXDESC ) ,
AR5K_SIMR0 ) ;
ath5k_hw_reg_write ( ah , AR5K_REG_SM ( ah - > ah_txq_imr_txerr ,
AR5K_SIMR1_QCU_TXERR ) |
AR5K_REG_SM ( ah - > ah_txq_imr_txeol ,
AR5K_SIMR1_QCU_TXEOL ) ,
AR5K_SIMR1 ) ;
/* Update SIMR2 but don't overwrite rest simr2 settings */
AR5K_REG_DISABLE_BITS ( ah , AR5K_SIMR2 , AR5K_SIMR2_QCU_TXURN ) ;
AR5K_REG_ENABLE_BITS ( ah , AR5K_SIMR2 ,
AR5K_REG_SM ( ah - > ah_txq_imr_txurn ,
AR5K_SIMR2_QCU_TXURN ) ) ;
ath5k_hw_reg_write ( ah , AR5K_REG_SM ( ah - > ah_txq_imr_cbrorn ,
AR5K_SIMR3_QCBRORN ) |
AR5K_REG_SM ( ah - > ah_txq_imr_cbrurn ,
AR5K_SIMR3_QCBRURN ) ,
AR5K_SIMR3 ) ;
ath5k_hw_reg_write ( ah , AR5K_REG_SM ( ah - > ah_txq_imr_qtrig ,
AR5K_SIMR4_QTRIG ) , AR5K_SIMR4 ) ;
/* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
ath5k_hw_reg_write ( ah , AR5K_REG_SM ( ah - > ah_txq_imr_nofrm ,
AR5K_TXNOFRM_QCU ) , AR5K_TXNOFRM ) ;
/* No queue has TXNOFRM enabled, disable the interrupt
* by setting AR5K_TXNOFRM to zero */
if ( ah - > ah_txq_imr_nofrm = = 0 )
ath5k_hw_reg_write ( ah , 0 , AR5K_TXNOFRM ) ;
/* Set QCU mask for this DCU to save power */
AR5K_REG_WRITE_Q ( ah , AR5K_QUEUE_QCUMASK ( queue ) , queue ) ;
2008-08-29 22:45:39 +03:00
return 0 ;
}
2010-11-23 20:36:45 +02:00
/**************************\
* Global QCU / DCU functions *
\ * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-11-23 21:19:45 +02:00
/**
2011-11-25 20:40:25 +02:00
* ath5k_hw_set_ifs_intervals ( ) - Set global inter - frame spaces on DCU
* @ ah : The & struct ath5k_hw
* @ slot_time : Slot time in us
2010-11-23 21:19:45 +02:00
*
* Sets the global IFS intervals on DCU ( also works on AR5210 ) for
* the given slot time and the current bwmode .
2008-08-29 22:45:39 +03:00
*/
2010-11-23 21:19:45 +02:00
int ath5k_hw_set_ifs_intervals ( struct ath5k_hw * ah , unsigned int slot_time )
2008-08-29 22:45:39 +03:00
{
2010-11-23 21:19:45 +02:00
struct ieee80211_channel * channel = ah - > ah_current_channel ;
2016-04-12 15:56:15 +02:00
enum nl80211_band band ;
2013-08-14 08:01:36 +02:00
struct ieee80211_supported_band * sband ;
2010-11-23 21:19:45 +02:00
struct ieee80211_rate * rate ;
u32 ack_tx_time , eifs , eifs_clock , sifs , sifs_clock ;
2009-12-21 22:50:50 +01:00
u32 slot_time_clock = ath5k_hw_htoclock ( ah , slot_time ) ;
2013-08-14 08:01:36 +02:00
u32 rate_flags , i ;
2009-12-21 22:50:49 +01:00
if ( slot_time < 6 | | slot_time_clock > AR5K_SLOT_TIME_MAX )
2008-08-29 22:45:39 +03:00
return - EINVAL ;
2010-11-23 21:19:45 +02:00
sifs = ath5k_hw_get_default_sifs ( ah ) ;
2011-04-09 23:10:20 +02:00
sifs_clock = ath5k_hw_htoclock ( ah , sifs - 2 ) ;
2010-11-23 21:19:45 +02:00
/* EIFS
* Txtime of ack at lowest rate + SIFS + DIFS
* ( DIFS = SIFS + 2 * Slot time )
*
* Note : HAL has some predefined values for EIFS
* Turbo : ( 37 + 2 * 6 )
* Default : ( 74 + 2 * 9 )
* Half : ( 149 + 2 * 13 )
* Quarter : ( 298 + 2 * 21 )
*
* ( 74 + 2 * 6 ) for AR5210 default and turbo !
*
* According to the formula we have
* ack_tx_time = 25 for turbo and
* ack_tx_time = 42.5 * clock multiplier
* for default / half / quarter .
*
* This can ' t be right , 42 is what we would get
* from ath5k_hw_get_frame_dur_for_bwmode or
* ieee80211_generic_frame_duration for zero frame
* length and without SIFS !
*
* Also we have different lowest rate for 802.11 a
*/
2016-04-12 15:56:15 +02:00
if ( channel - > band = = NL80211_BAND_5GHZ )
band = NL80211_BAND_5GHZ ;
2008-08-29 22:45:39 +03:00
else
2016-04-12 15:56:15 +02:00
band = NL80211_BAND_2GHZ ;
2010-11-23 21:19:45 +02:00
2013-08-14 08:01:36 +02:00
switch ( ah - > ah_bwmode ) {
case AR5K_BWMODE_5MHZ :
rate_flags = IEEE80211_RATE_SUPPORTS_5MHZ ;
break ;
case AR5K_BWMODE_10MHZ :
rate_flags = IEEE80211_RATE_SUPPORTS_10MHZ ;
break ;
default :
rate_flags = 0 ;
break ;
}
sband = & ah - > sbands [ band ] ;
rate = NULL ;
for ( i = 0 ; i < sband - > n_bitrates ; i + + ) {
if ( ( rate_flags & sband - > bitrates [ i ] . flags ) ! = rate_flags )
continue ;
rate = & sband - > bitrates [ i ] ;
break ;
}
if ( WARN_ON ( ! rate ) )
return - EINVAL ;
2012-04-11 08:47:56 +02:00
ack_tx_time = ath5k_hw_get_frame_duration ( ah , band , 10 , rate , false ) ;
2010-11-23 21:19:45 +02:00
/* ack_tx_time includes an SIFS already */
eifs = ack_tx_time + sifs + 2 * slot_time ;
eifs_clock = ath5k_hw_htoclock ( ah , eifs ) ;
/* Set IFS settings on AR5210 */
if ( ah - > ah_version = = AR5K_AR5210 ) {
u32 pifs , pifs_clock , difs , difs_clock ;
/* Set slot time */
ath5k_hw_reg_write ( ah , slot_time_clock , AR5K_SLOT_TIME ) ;
/* Set EIFS */
eifs_clock = AR5K_REG_SM ( eifs_clock , AR5K_IFS1_EIFS ) ;
/* PIFS = Slot time + SIFS */
pifs = slot_time + sifs ;
pifs_clock = ath5k_hw_htoclock ( ah , pifs ) ;
pifs_clock = AR5K_REG_SM ( pifs_clock , AR5K_IFS1_PIFS ) ;
/* DIFS = SIFS + 2 * Slot time */
difs = sifs + 2 * slot_time ;
difs_clock = ath5k_hw_htoclock ( ah , difs ) ;
/* Set SIFS/DIFS */
ath5k_hw_reg_write ( ah , ( difs_clock < <
AR5K_IFS0_DIFS_S ) | sifs_clock ,
AR5K_IFS0 ) ;
/* Set PIFS/EIFS and preserve AR5K_INIT_CARR_SENSE_EN */
ath5k_hw_reg_write ( ah , pifs_clock | eifs_clock |
( AR5K_INIT_CARR_SENSE_EN < < AR5K_IFS1_CS_EN_S ) ,
AR5K_IFS1 ) ;
return 0 ;
}
/* Set IFS slot time */
ath5k_hw_reg_write ( ah , slot_time_clock , AR5K_DCU_GBL_IFS_SLOT ) ;
/* Set EIFS interval */
ath5k_hw_reg_write ( ah , eifs_clock , AR5K_DCU_GBL_IFS_EIFS ) ;
/* Set SIFS interval in usecs */
AR5K_REG_WRITE_BITS ( ah , AR5K_DCU_GBL_IFS_MISC ,
AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC ,
sifs ) ;
/* Set SIFS interval in clock cycles */
ath5k_hw_reg_write ( ah , sifs_clock , AR5K_DCU_GBL_IFS_SIFS ) ;
2008-08-29 22:45:39 +03:00
return 0 ;
}
2010-11-23 21:19:45 +02:00
2011-11-25 20:40:25 +02:00
/**
* ath5k_hw_init_queues ( ) - Initialize tx queues
* @ ah : The & struct ath5k_hw
*
* Initializes all tx queues based on information on
* ah - > ah_txq * set by the driver
*/
int
ath5k_hw_init_queues ( struct ath5k_hw * ah )
2010-11-23 20:36:45 +02:00
{
int i , ret ;
/* TODO: HW Compression support for data queues */
/* TODO: Burst prefetch for data queues */
/*
* Reset queues and start beacon timers at the end of the reset routine
* This also sets QCU mask on each DCU for 1 : 1 qcu to dcu mapping
* Note : If we want we can assign multiple qcus on one dcu .
*/
2010-11-23 21:19:45 +02:00
if ( ah - > ah_version ! = AR5K_AR5210 )
for ( i = 0 ; i < ah - > ah_capabilities . cap_queues . q_tx_num ; i + + ) {
ret = ath5k_hw_reset_tx_queue ( ah , i ) ;
if ( ret ) {
2011-07-14 20:21:55 -04:00
ATH5K_ERR ( ah ,
2010-11-23 21:19:45 +02:00
" failed to reset TX queue #%d \n " , i ) ;
return ret ;
}
2010-11-23 20:36:45 +02:00
}
2010-11-23 21:19:45 +02:00
else
/* No QCU/DCU on AR5210, just set tx
* retry limits . We set IFS parameters
* on ath5k_hw_set_ifs_intervals */
ath5k_hw_set_tx_retry_limits ( ah , 0 ) ;
2010-11-23 20:36:45 +02:00
2010-11-23 21:21:50 +02:00
/* Set the turbo flag when operating on 40MHz */
if ( ah - > ah_bwmode = = AR5K_BWMODE_40MHZ )
AR5K_REG_ENABLE_BITS ( ah , AR5K_DCU_GBL_IFS_MISC ,
AR5K_DCU_GBL_IFS_MISC_TURBO_MODE ) ;
2010-11-23 21:24:54 +02:00
/* If we didn't set IFS timings through
* ath5k_hw_set_coverage_class make sure
* we set them here */
if ( ! ah - > ah_coverage_class ) {
unsigned int slot_time = ath5k_hw_get_default_slottime ( ah ) ;
ath5k_hw_set_ifs_intervals ( ah , slot_time ) ;
}
2010-11-23 20:36:45 +02:00
return 0 ;
}