diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/qcu.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath5k/qcu.c | 235 |
1 files changed, 154 insertions, 81 deletions
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index 2c9c9e793d4..0583c69d26d 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -17,24 +17,50 @@ */ /********************************************\ -Queue Control Unit, DFS Control Unit Functions +Queue Control Unit, DCF Control Unit Functions \********************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "ath5k.h" #include "reg.h" #include "debug.h" -#include "base.h" +#include <linux/log2.h> + +/** + * 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. + */ /******************\ * Helper functions * \******************/ -/* - * Get number of pending frames - * for a specific queue [5211+] +/** + * ath5k_hw_num_tx_pending() - Get number of pending frames for a given queue + * @ah: The &struct ath5k_hw + * @queue: One of enum ath5k_tx_queue_id */ -u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) +u32 +ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) { u32 pending; AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); @@ -59,10 +85,13 @@ u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) return pending; } -/* - * Set a transmit queue inactive +/** + * ath5k_hw_release_tx_queue() - Set a transmit queue inactive + * @ah: The &struct ath5k_hw + * @queue: One of enum ath5k_tx_queue_id */ -void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) +void +ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) { if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num)) return; @@ -73,34 +102,56 @@ void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue); } -/* +/** + * ath5k_cw_validate() - Make sure the given cw is valid + * @cw_req: The contention window value to check + * * Make sure cw is a power of 2 minus 1 and smaller than 1024 */ -static u16 ath5k_cw_validate(u16 cw_req) +static u16 +ath5k_cw_validate(u16 cw_req) { - u32 cw = 1; cw_req = min(cw_req, (u16)1023); - while (cw < cw_req) - cw = (cw << 1) | 1; + /* Check if cw_req + 1 a power of 2 */ + if (is_power_of_2(cw_req + 1)) + return cw_req; + + /* 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; + return cw_req; } -/* - * Get properties for a transmit queue +/** + * ath5k_hw_get_tx_queueprops() - Get properties for a transmit queue + * @ah: The &struct ath5k_hw + * @queue: One of enum ath5k_tx_queue_id + * @queue_info: The &struct ath5k_txq_info to fill */ -int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, +int +ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, struct ath5k_txq_info *queue_info) { memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info)); return 0; } -/* - * Set properties for a transmit queue +/** + * ath5k_hw_set_tx_queueprops() - Set properties for a transmit queue + * @ah: The &struct ath5k_hw + * @queue: One of enum ath5k_tx_queue_id + * @qinfo: The &struct ath5k_txq_info to use + * + * Returns 0 on success or -EIO if queue is inactive */ -int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, +int +ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, const struct ath5k_txq_info *qinfo) { struct ath5k_txq_info *qi; @@ -140,10 +191,16 @@ int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, return 0; } -/* - * Initialize a transmit queue +/** + * 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 */ -int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, +int +ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, struct ath5k_txq_info *queue_info) { unsigned int queue; @@ -185,13 +242,6 @@ int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, case AR5K_TX_QUEUE_CAB: queue = AR5K_TX_QUEUE_ID_CAB; break; - case AR5K_TX_QUEUE_XR_DATA: - if (ah->ah_version != AR5K_AR5212) - ATH5K_ERR(ah->ah_sc, - "XR data queues only supported in" - " 5212!\n"); - queue = AR5K_TX_QUEUE_ID_XR_DATA; - break; default: return -EINVAL; } @@ -225,27 +275,18 @@ int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, * Single QCU/DCU initialization * \*******************************/ -/* - * Set tx retry limits on DCU +/** + * ath5k_hw_set_tx_retry_limits() - Set tx retry limits on DCU + * @ah: The &struct ath5k_hw + * @queue: One of enum ath5k_tx_queue_id + * + * This function is used when initializing a queue, to set + * retry limits based on ah->ah_retry_* and the chipset used. */ -static void ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, - unsigned int queue) +void +ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, + unsigned int queue) { - u32 retry_lg, retry_sh; - - /* - * Calculate and set retry limits - */ - if (ah->ah_software_retry) { - /* XXX Need to test this */ - retry_lg = ah->ah_limit_tx_retries; - retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ? - AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg; - } else { - retry_lg = AR5K_INIT_LG_RETRY; - retry_sh = AR5K_INIT_SH_RETRY; - } - /* Single data queue on AR5210 */ if (ah->ah_version == AR5K_AR5210) { struct ath5k_txq_info *tq = &ah->ah_txq[queue]; @@ -255,37 +296,38 @@ static void ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, ath5k_hw_reg_write(ah, (tq->tqi_cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) - | AR5K_REG_SM(AR5K_INIT_SLG_RETRY, - AR5K_NODCU_RETRY_LMT_SLG_RETRY) - | AR5K_REG_SM(AR5K_INIT_SSH_RETRY, - AR5K_NODCU_RETRY_LMT_SSH_RETRY) - | AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY) - | AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY), + | 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), AR5K_NODCU_RETRY_LMT); /* DCU on AR5211+ */ } else { ath5k_hw_reg_write(ah, - AR5K_REG_SM(AR5K_INIT_SLG_RETRY, - AR5K_DCU_RETRY_LMT_SLG_RETRY) | - AR5K_REG_SM(AR5K_INIT_SSH_RETRY, - AR5K_DCU_RETRY_LMT_SSH_RETRY) | - AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) | - AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY), + 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), AR5K_QUEUE_DFS_RETRY_LIMIT(queue)); } - return; } /** - * ath5k_hw_reset_tx_queue - Initialize a single hw queue + * ath5k_hw_reset_tx_queue() - Initialize a single hw queue + * @ah: The &struct ath5k_hw + * @queue: One of enum ath5k_tx_queue_id * - * @ah The &struct ath5k_hw - * @queue The hw queue number - * - * Set DFS properties for the given transmit queue on DCU + * Set DCF properties for the given transmit queue on DCU * and configures all queue-specific parameters. */ -int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) +int +ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) { struct ath5k_txq_info *tq = &ah->ah_txq[queue]; @@ -513,10 +555,9 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) \**************************/ /** - * ath5k_hw_set_ifs_intervals - Set global inter-frame spaces on DCU - * - * @ah The &struct ath5k_hw - * @slot_time Slot time in us + * ath5k_hw_set_ifs_intervals() - Set global inter-frame spaces on DCU + * @ah: The &struct ath5k_hw + * @slot_time: Slot time in us * * Sets the global IFS intervals on DCU (also works on AR5210) for * the given slot time and the current bwmode. @@ -524,16 +565,18 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) { struct ieee80211_channel *channel = ah->ah_current_channel; - struct ath5k_softc *sc = ah->ah_sc; + enum ieee80211_band band; + struct ieee80211_supported_band *sband; struct ieee80211_rate *rate; u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock; u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time); + u32 rate_flags, i; if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX) return -EINVAL; sifs = ath5k_hw_get_default_sifs(ah); - sifs_clock = ath5k_hw_htoclock(ah, sifs); + sifs_clock = ath5k_hw_htoclock(ah, sifs - 2); /* EIFS * Txtime of ack at lowest rate + SIFS + DIFS @@ -559,12 +602,34 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) * * Also we have different lowest rate for 802.11a */ - if (channel->hw_value & CHANNEL_5GHZ) - rate = &sc->sbands[IEEE80211_BAND_5GHZ].bitrates[0]; + if (channel->band == IEEE80211_BAND_5GHZ) + band = IEEE80211_BAND_5GHZ; else - rate = &sc->sbands[IEEE80211_BAND_2GHZ].bitrates[0]; + band = IEEE80211_BAND_2GHZ; - ack_tx_time = ath5k_hw_get_frame_duration(ah, 10, rate); + 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; + + ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false); /* ack_tx_time includes an SIFS already */ eifs = ack_tx_time + sifs + 2 * slot_time; @@ -620,7 +685,15 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) } -int ath5k_hw_init_queues(struct ath5k_hw *ah) +/** + * 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) { int i, ret; @@ -636,7 +709,7 @@ int ath5k_hw_init_queues(struct ath5k_hw *ah) for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) { ret = ath5k_hw_reset_tx_queue(ah, i); if (ret) { - ATH5K_ERR(ah->ah_sc, + ATH5K_ERR(ah, "failed to reset TX queue #%d\n", i); return ret; } |
