aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2011-07-10 03:47:01 -0400
committerWey-Yi Guy <wey-yi.w.guy@intel.com>2011-07-21 10:29:12 -0400
commit48d42c426947d8ffba0caa3cf9c58be6903302e0 (patch)
tree7ae7a2e9956329df0419c89fa7f63ad53fedcc81 /drivers
parent2e27799621f9b6dc69d9fac5e365cb867eac539c (diff)
iwlagn: SCD configuration for AMPDU moves to transport layer
All the configurations of the HW for AMPDU are now in the transport layer. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c225
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.h13
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h8
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c224
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.c10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h17
8 files changed, 274 insertions, 239 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index ece3202427f3..932425d019dd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -96,132 +96,8 @@ static inline int get_fifo_from_tid(struct iwl_rxon_context *ctx, u16 tid)
96 return -EINVAL; 96 return -EINVAL;
97} 97}
98 98
99/** 99static int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, int sta_id,
100 * iwlagn_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array 100 int tid)
101 */
102void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
103 struct iwl_tx_queue *txq,
104 u16 byte_cnt)
105{
106 struct iwlagn_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
107 int write_ptr = txq->q.write_ptr;
108 int txq_id = txq->q.id;
109 u8 sec_ctl = 0;
110 u8 sta_id = 0;
111 u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
112 __le16 bc_ent;
113
114 WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
115
116 sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
117 sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl;
118
119 switch (sec_ctl & TX_CMD_SEC_MSK) {
120 case TX_CMD_SEC_CCM:
121 len += CCMP_MIC_LEN;
122 break;
123 case TX_CMD_SEC_TKIP:
124 len += TKIP_ICV_LEN;
125 break;
126 case TX_CMD_SEC_WEP:
127 len += WEP_IV_LEN + WEP_ICV_LEN;
128 break;
129 }
130
131 bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
132
133 scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
134
135 if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
136 scd_bc_tbl[txq_id].
137 tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
138}
139
140static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
141 struct iwl_tx_queue *txq)
142{
143 struct iwlagn_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
144 int txq_id = txq->q.id;
145 int read_ptr = txq->q.read_ptr;
146 u8 sta_id = 0;
147 __le16 bc_ent;
148
149 WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
150
151 if (txq_id != priv->cmd_queue)
152 sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id;
153
154 bc_ent = cpu_to_le16(1 | (sta_id << 12));
155 scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
156
157 if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
158 scd_bc_tbl[txq_id].
159 tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
160}
161
162static int iwlagn_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
163 u16 txq_id)
164{
165 u32 tbl_dw_addr;
166 u32 tbl_dw;
167 u16 scd_q2ratid;
168
169 scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK;
170
171 tbl_dw_addr = priv->scd_base_addr +
172 SCD_TRANS_TBL_OFFSET_QUEUE(txq_id);
173
174 tbl_dw = iwl_read_targ_mem(priv, tbl_dw_addr);
175
176 if (txq_id & 0x1)
177 tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
178 else
179 tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
180
181 iwl_write_targ_mem(priv, tbl_dw_addr, tbl_dw);
182
183 return 0;
184}
185
186static void iwlagn_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id)
187{
188 /* Simply stop the queue, but don't change any configuration;
189 * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
190 iwl_write_prph(priv,
191 SCD_QUEUE_STATUS_BITS(txq_id),
192 (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
193 (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
194}
195
196void iwlagn_set_wr_ptrs(struct iwl_priv *priv,
197 int txq_id, u32 index)
198{
199 iwl_write_direct32(priv, HBUS_TARG_WRPTR,
200 (index & 0xff) | (txq_id << 8));
201 iwl_write_prph(priv, SCD_QUEUE_RDPTR(txq_id), index);
202}
203
204void iwlagn_tx_queue_set_status(struct iwl_priv *priv,
205 struct iwl_tx_queue *txq,
206 int tx_fifo_id, int scd_retry)
207{
208 int txq_id = txq->q.id;
209 int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
210
211 iwl_write_prph(priv, SCD_QUEUE_STATUS_BITS(txq_id),
212 (active << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
213 (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) |
214 (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
215 SCD_QUEUE_STTS_REG_MSK);
216
217 txq->sched_retry = scd_retry;
218
219 IWL_DEBUG_INFO(priv, "%s %s Queue %d on FIFO %d\n",
220 active ? "Activate" : "Deactivate",
221 scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
222}
223
224static int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, int sta_id, int tid)
225{ 101{
226 if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) || 102 if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
227 (IWLAGN_FIRST_AMPDU_QUEUE + 103 (IWLAGN_FIRST_AMPDU_QUEUE +
@@ -238,99 +114,6 @@ static int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, int sta_id,
238 return iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); 114 return iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
239} 115}
240 116
241void iwlagn_txq_agg_queue_setup(struct iwl_priv *priv,
242 struct ieee80211_sta *sta,
243 int tid, int frame_limit)
244{
245 int sta_id, tx_fifo, txq_id, ssn_idx;
246 u16 ra_tid;
247 unsigned long flags;
248 struct iwl_tid_data *tid_data;
249
250 sta_id = iwl_sta_id(sta);
251 if (WARN_ON(sta_id == IWL_INVALID_STATION))
252 return;
253 if (WARN_ON(tid >= MAX_TID_COUNT))
254 return;
255
256 spin_lock_irqsave(&priv->sta_lock, flags);
257 tid_data = &priv->stations[sta_id].tid[tid];
258 ssn_idx = SEQ_TO_SN(tid_data->seq_number);
259 txq_id = tid_data->agg.txq_id;
260 tx_fifo = tid_data->agg.tx_fifo;
261 spin_unlock_irqrestore(&priv->sta_lock, flags);
262
263 ra_tid = BUILD_RAxTID(sta_id, tid);
264
265 spin_lock_irqsave(&priv->lock, flags);
266
267 /* Stop this Tx queue before configuring it */
268 iwlagn_tx_queue_stop_scheduler(priv, txq_id);
269
270 /* Map receiver-address / traffic-ID to this queue */
271 iwlagn_tx_queue_set_q2ratid(priv, ra_tid, txq_id);
272
273 /* Set this queue as a chain-building queue */
274 iwl_set_bits_prph(priv, SCD_QUEUECHAIN_SEL, (1<<txq_id));
275
276 /* enable aggregations for the queue */
277 iwl_set_bits_prph(priv, SCD_AGGR_SEL, (1<<txq_id));
278
279 /* Place first TFD at index corresponding to start sequence number.
280 * Assumes that ssn_idx is valid (!= 0xFFF) */
281 priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
282 priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
283 iwlagn_set_wr_ptrs(priv, txq_id, ssn_idx);
284
285 /* Set up Tx window size and frame limit for this queue */
286 iwl_write_targ_mem(priv, priv->scd_base_addr +
287 SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
288 sizeof(u32),
289 ((frame_limit <<
290 SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
291 SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
292 ((frame_limit <<
293 SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
294 SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
295
296 iwl_set_bits_prph(priv, SCD_INTERRUPT_MASK, (1 << txq_id));
297
298 /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
299 iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
300
301 spin_unlock_irqrestore(&priv->lock, flags);
302}
303
304static int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
305 u16 ssn_idx, u8 tx_fifo)
306{
307 if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
308 (IWLAGN_FIRST_AMPDU_QUEUE +
309 priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) {
310 IWL_ERR(priv,
311 "queue number out of range: %d, must be %d to %d\n",
312 txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
313 IWLAGN_FIRST_AMPDU_QUEUE +
314 priv->cfg->base_params->num_of_ampdu_queues - 1);
315 return -EINVAL;
316 }
317
318 iwlagn_tx_queue_stop_scheduler(priv, txq_id);
319
320 iwl_clear_bits_prph(priv, SCD_AGGR_SEL, (1 << txq_id));
321
322 priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
323 priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
324 /* supposes that ssn_idx is valid (!= 0xFFF) */
325 iwlagn_set_wr_ptrs(priv, txq_id, ssn_idx);
326
327 iwl_clear_bits_prph(priv, SCD_INTERRUPT_MASK, (1 << txq_id));
328 iwl_txq_ctx_deactivate(priv, txq_id);
329 iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
330
331 return 0;
332}
333
334static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, 117static void iwlagn_tx_cmd_protection(struct iwl_priv *priv,
335 struct ieee80211_tx_info *info, 118 struct ieee80211_tx_info *info,
336 __le16 fc, __le32 *tx_flags) 119 __le16 fc, __le32 *tx_flags)
@@ -850,7 +633,7 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
850 * to deactivate the uCode queue, just return "success" to allow 633 * to deactivate the uCode queue, just return "success" to allow
851 * mac80211 to clean up it own data. 634 * mac80211 to clean up it own data.
852 */ 635 */
853 iwlagn_txq_agg_disable(priv, txq_id, ssn, tx_fifo_id); 636 trans_txq_agg_disable(priv, txq_id, ssn, tx_fifo_id);
854 spin_unlock_irqrestore(&priv->lock, flags); 637 spin_unlock_irqrestore(&priv->lock, flags);
855 638
856 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); 639 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
@@ -879,7 +662,7 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv,
879 u16 ssn = SEQ_TO_SN(tid_data->seq_number); 662 u16 ssn = SEQ_TO_SN(tid_data->seq_number);
880 int tx_fifo = get_fifo_from_tid(ctx, tid); 663 int tx_fifo = get_fifo_from_tid(ctx, tid);
881 IWL_DEBUG_HT(priv, "HW queue empty: continue DELBA flow\n"); 664 IWL_DEBUG_HT(priv, "HW queue empty: continue DELBA flow\n");
882 iwlagn_txq_agg_disable(priv, txq_id, ssn, tx_fifo); 665 trans_txq_agg_disable(priv, txq_id, ssn, tx_fifo);
883 tid_data->agg.state = IWL_AGG_OFF; 666 tid_data->agg.state = IWL_AGG_OFF;
884 ieee80211_stop_tx_ba_cb_irqsafe(ctx->vif, addr, tid); 667 ieee80211_stop_tx_ba_cb_irqsafe(ctx->vif, addr, tid);
885 } 668 }
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 503c78606bdc..9ba5fcd82880 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2461,7 +2461,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
2461 case IEEE80211_AMPDU_TX_OPERATIONAL: 2461 case IEEE80211_AMPDU_TX_OPERATIONAL:
2462 buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); 2462 buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
2463 2463
2464 iwlagn_txq_agg_queue_setup(priv, sta, tid, buf_size); 2464 trans_txq_agg_setup(priv, iwl_sta_id(sta), tid, buf_size);
2465 2465
2466 /* 2466 /*
2467 * If the limit is 0, then it wasn't initialised yet, 2467 * If the limit is 0, then it wasn't initialised yet,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
index 57c70b840a52..0a8417a2c29a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
@@ -127,15 +127,7 @@ int iwl_prepare_card_hw(struct iwl_priv *priv);
127int iwlagn_start_device(struct iwl_priv *priv); 127int iwlagn_start_device(struct iwl_priv *priv);
128 128
129/* tx queue */ 129/* tx queue */
130void iwlagn_set_wr_ptrs(struct iwl_priv *priv, 130/*TODO: this one should go to transport layer */
131 int txq_id, u32 index);
132void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
133 struct iwl_tx_queue *txq,
134 u16 byte_cnt);
135
136void iwlagn_tx_queue_set_status(struct iwl_priv *priv,
137 struct iwl_tx_queue *txq,
138 int tx_fifo_id, int scd_retry);
139void iwl_free_tfds_in_queue(struct iwl_priv *priv, 131void iwl_free_tfds_in_queue(struct iwl_priv *priv,
140 int sta_id, int tid, int freed); 132 int sta_id, int tid, int freed);
141 133
@@ -188,9 +180,6 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif,
188 struct ieee80211_sta *sta, u16 tid, u16 *ssn); 180 struct ieee80211_sta *sta, u16 tid, u16 *ssn);
189int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, 181int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif,
190 struct ieee80211_sta *sta, u16 tid); 182 struct ieee80211_sta *sta, u16 tid);
191void iwlagn_txq_agg_queue_setup(struct iwl_priv *priv,
192 struct ieee80211_sta *sta,
193 int tid, int frame_limit);
194int iwlagn_txq_check_empty(struct iwl_priv *priv, 183int iwlagn_txq_check_empty(struct iwl_priv *priv,
195 int sta_id, u8 tid, int txq_id); 184 int sta_id, u8 tid, int txq_id);
196void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, 185void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv,
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index d6d6fe92c474..b18a9a88d7be 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -1245,6 +1245,9 @@ struct iwl_trans;
1245 * @send_cmd_pdu:send a host command: flags can be CMD_* 1245 * @send_cmd_pdu:send a host command: flags can be CMD_*
1246 * @get_tx_cmd: returns a pointer to a new Tx cmd for the upper layer use 1246 * @get_tx_cmd: returns a pointer to a new Tx cmd for the upper layer use
1247 * @tx: send an skb 1247 * @tx: send an skb
1248 * @txq_agg_setup: setup a tx queue for AMPDU - will be called once the HW is
1249 * ready and a successful ADDBA response has been received.
1250 * @txq_agg_disable: de-configure a Tx queue to send AMPDUs
1248 * @kick_nic: remove the RESET from the embedded CPU and let it run 1251 * @kick_nic: remove the RESET from the embedded CPU and let it run
1249 * @sync_irq: the upper layer will typically disable interrupt and call this 1252 * @sync_irq: the upper layer will typically disable interrupt and call this
1250 * handler. After this handler returns, it is guaranteed that all 1253 * handler. After this handler returns, it is guaranteed that all
@@ -1272,6 +1275,11 @@ struct iwl_trans_ops {
1272 struct iwl_tx_cmd *tx_cmd, int txq_id, __le16 fc, bool ampdu, 1275 struct iwl_tx_cmd *tx_cmd, int txq_id, __le16 fc, bool ampdu,
1273 struct iwl_rxon_context *ctx); 1276 struct iwl_rxon_context *ctx);
1274 1277
1278 int (*txq_agg_disable)(struct iwl_priv *priv, u16 txq_id,
1279 u16 ssn_idx, u8 tx_fifo);
1280 void (*txq_agg_setup)(struct iwl_priv *priv, int sta_id, int tid,
1281 int frame_limit);
1282
1275 void (*kick_nic)(struct iwl_priv *priv); 1283 void (*kick_nic)(struct iwl_priv *priv);
1276 1284
1277 void (*sync_irq)(struct iwl_priv *priv); 1285 void (*sync_irq)(struct iwl_priv *priv);
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h b/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
index 3015facd775e..b79330d84185 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h
@@ -66,7 +66,17 @@ int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd);
66int __must_check iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u32 flags, 66int __must_check iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u32 flags,
67 u16 len, const void *data); 67 u16 len, const void *data);
68void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); 68void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb);
69 69void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
70 struct iwl_tx_queue *txq,
71 u16 byte_cnt);
72int iwl_trans_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
73 u16 ssn_idx, u8 tx_fifo);
74void iwl_trans_set_wr_ptrs(struct iwl_priv *priv,
75 int txq_id, u32 index);
76void iwl_trans_tx_queue_set_status(struct iwl_priv *priv,
77 struct iwl_tx_queue *txq,
78 int tx_fifo_id, int scd_retry);
79void iwl_trans_txq_agg_setup(struct iwl_priv *priv, int sta_id, int tid,
80 int frame_limit);
70 81
71#endif /* __iwl_trans_int_pcie_h__ */ 82#endif /* __iwl_trans_int_pcie_h__ */
72
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
index f3b531b34475..9cecb1076280 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c
@@ -35,9 +35,56 @@
35#include "iwl-dev.h" 35#include "iwl-dev.h"
36#include "iwl-core.h" 36#include "iwl-core.h"
37#include "iwl-io.h" 37#include "iwl-io.h"
38#include "iwl-sta.h"
38#include "iwl-helpers.h" 39#include "iwl-helpers.h"
39#include "iwl-trans-int-pcie.h" 40#include "iwl-trans-int-pcie.h"
40 41
42/* TODO:this file should _not_ include the external API header file
43 * (iwl-trans.h). This is needed as a W/A until reclaim functions will move to
44 * the transport layer */
45#include "iwl-trans.h"
46
47/**
48 * iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
49 */
50void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
51 struct iwl_tx_queue *txq,
52 u16 byte_cnt)
53{
54 struct iwlagn_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
55 int write_ptr = txq->q.write_ptr;
56 int txq_id = txq->q.id;
57 u8 sec_ctl = 0;
58 u8 sta_id = 0;
59 u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
60 __le16 bc_ent;
61
62 WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
63
64 sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
65 sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl;
66
67 switch (sec_ctl & TX_CMD_SEC_MSK) {
68 case TX_CMD_SEC_CCM:
69 len += CCMP_MIC_LEN;
70 break;
71 case TX_CMD_SEC_TKIP:
72 len += TKIP_ICV_LEN;
73 break;
74 case TX_CMD_SEC_WEP:
75 len += WEP_IV_LEN + WEP_ICV_LEN;
76 break;
77 }
78
79 bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
80
81 scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
82
83 if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
84 scd_bc_tbl[txq_id].
85 tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
86}
87
41/** 88/**
42 * iwl_txq_update_write_ptr - Send new write index to hardware 89 * iwl_txq_update_write_ptr - Send new write index to hardware
43 */ 90 */
@@ -291,6 +338,183 @@ int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
291 return 0; 338 return 0;
292} 339}
293 340
341/*TODO: this functions should NOT be exported from trans module - export it
342 * until the reclaim flow will be brought to the transport module too */
343void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
344 struct iwl_tx_queue *txq)
345{
346 struct iwlagn_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
347 int txq_id = txq->q.id;
348 int read_ptr = txq->q.read_ptr;
349 u8 sta_id = 0;
350 __le16 bc_ent;
351
352 WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
353
354 if (txq_id != priv->cmd_queue)
355 sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id;
356
357 bc_ent = cpu_to_le16(1 | (sta_id << 12));
358 scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
359
360 if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
361 scd_bc_tbl[txq_id].
362 tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
363}
364
365static int iwlagn_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
366 u16 txq_id)
367{
368 u32 tbl_dw_addr;
369 u32 tbl_dw;
370 u16 scd_q2ratid;
371
372 scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK;
373
374 tbl_dw_addr = priv->scd_base_addr +
375 SCD_TRANS_TBL_OFFSET_QUEUE(txq_id);
376
377 tbl_dw = iwl_read_targ_mem(priv, tbl_dw_addr);
378
379 if (txq_id & 0x1)
380 tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
381 else
382 tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
383
384 iwl_write_targ_mem(priv, tbl_dw_addr, tbl_dw);
385
386 return 0;
387}
388
389static void iwlagn_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id)
390{
391 /* Simply stop the queue, but don't change any configuration;
392 * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
393 iwl_write_prph(priv,
394 SCD_QUEUE_STATUS_BITS(txq_id),
395 (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)|
396 (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
397}
398
399void iwl_trans_set_wr_ptrs(struct iwl_priv *priv,
400 int txq_id, u32 index)
401{
402 iwl_write_direct32(priv, HBUS_TARG_WRPTR,
403 (index & 0xff) | (txq_id << 8));
404 iwl_write_prph(priv, SCD_QUEUE_RDPTR(txq_id), index);
405}
406
407void iwl_trans_tx_queue_set_status(struct iwl_priv *priv,
408 struct iwl_tx_queue *txq,
409 int tx_fifo_id, int scd_retry)
410{
411 int txq_id = txq->q.id;
412 int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
413
414 iwl_write_prph(priv, SCD_QUEUE_STATUS_BITS(txq_id),
415 (active << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
416 (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) |
417 (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
418 SCD_QUEUE_STTS_REG_MSK);
419
420 txq->sched_retry = scd_retry;
421
422 IWL_DEBUG_INFO(priv, "%s %s Queue %d on FIFO %d\n",
423 active ? "Activate" : "Deactivate",
424 scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
425}
426
427void iwl_trans_txq_agg_setup(struct iwl_priv *priv, int sta_id, int tid,
428 int frame_limit)
429{
430 int tx_fifo, txq_id, ssn_idx;
431 u16 ra_tid;
432 unsigned long flags;
433 struct iwl_tid_data *tid_data;
434
435 if (WARN_ON(sta_id == IWL_INVALID_STATION))
436 return;
437 if (WARN_ON(tid >= MAX_TID_COUNT))
438 return;
439
440 spin_lock_irqsave(&priv->sta_lock, flags);
441 tid_data = &priv->stations[sta_id].tid[tid];
442 ssn_idx = SEQ_TO_SN(tid_data->seq_number);
443 txq_id = tid_data->agg.txq_id;
444 tx_fifo = tid_data->agg.tx_fifo;
445 spin_unlock_irqrestore(&priv->sta_lock, flags);
446
447 ra_tid = BUILD_RAxTID(sta_id, tid);
448
449 spin_lock_irqsave(&priv->lock, flags);
450
451 /* Stop this Tx queue before configuring it */
452 iwlagn_tx_queue_stop_scheduler(priv, txq_id);
453
454 /* Map receiver-address / traffic-ID to this queue */
455 iwlagn_tx_queue_set_q2ratid(priv, ra_tid, txq_id);
456
457 /* Set this queue as a chain-building queue */
458 iwl_set_bits_prph(priv, SCD_QUEUECHAIN_SEL, (1<<txq_id));
459
460 /* enable aggregations for the queue */
461 iwl_set_bits_prph(priv, SCD_AGGR_SEL, (1<<txq_id));
462
463 /* Place first TFD at index corresponding to start sequence number.
464 * Assumes that ssn_idx is valid (!= 0xFFF) */
465 priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
466 priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
467 iwl_trans_set_wr_ptrs(priv, txq_id, ssn_idx);
468
469 /* Set up Tx window size and frame limit for this queue */
470 iwl_write_targ_mem(priv, priv->scd_base_addr +
471 SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
472 sizeof(u32),
473 ((frame_limit <<
474 SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
475 SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
476 ((frame_limit <<
477 SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
478 SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
479
480 iwl_set_bits_prph(priv, SCD_INTERRUPT_MASK, (1 << txq_id));
481
482 /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
483 iwl_trans_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
484
485 spin_unlock_irqrestore(&priv->lock, flags);
486}
487
488int iwl_trans_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
489 u16 ssn_idx, u8 tx_fifo)
490{
491 if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) ||
492 (IWLAGN_FIRST_AMPDU_QUEUE +
493 priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) {
494 IWL_ERR(priv,
495 "queue number out of range: %d, must be %d to %d\n",
496 txq_id, IWLAGN_FIRST_AMPDU_QUEUE,
497 IWLAGN_FIRST_AMPDU_QUEUE +
498 priv->cfg->base_params->num_of_ampdu_queues - 1);
499 return -EINVAL;
500 }
501
502 iwlagn_tx_queue_stop_scheduler(priv, txq_id);
503
504 iwl_clear_bits_prph(priv, SCD_AGGR_SEL, (1 << txq_id));
505
506 priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
507 priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
508 /* supposes that ssn_idx is valid (!= 0xFFF) */
509 iwl_trans_set_wr_ptrs(priv, txq_id, ssn_idx);
510
511 iwl_clear_bits_prph(priv, SCD_INTERRUPT_MASK, (1 << txq_id));
512 iwl_txq_ctx_deactivate(priv, txq_id);
513 iwl_trans_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
514
515 return 0;
516}
517
294/*************** HOST COMMAND QUEUE FUNCTIONS *****/ 518/*************** HOST COMMAND QUEUE FUNCTIONS *****/
295 519
296/** 520/**
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c
index 639b4692e459..bddc12d27d06 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.c
@@ -686,7 +686,7 @@ static void iwl_trans_tx_start(struct iwl_priv *priv)
686 else 686 else
687 queue_to_fifo = iwlagn_default_queue_to_tx_fifo; 687 queue_to_fifo = iwlagn_default_queue_to_tx_fifo;
688 688
689 iwlagn_set_wr_ptrs(priv, priv->cmd_queue, 0); 689 iwl_trans_set_wr_ptrs(priv, priv->cmd_queue, 0);
690 690
691 /* make sure all queue are not stopped */ 691 /* make sure all queue are not stopped */
692 memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped)); 692 memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped));
@@ -712,7 +712,7 @@ static void iwl_trans_tx_start(struct iwl_priv *priv)
712 712
713 if (ac != IWL_AC_UNSET) 713 if (ac != IWL_AC_UNSET)
714 iwl_set_swq_id(&priv->txq[i], ac, i); 714 iwl_set_swq_id(&priv->txq[i], ac, i);
715 iwlagn_tx_queue_set_status(priv, &priv->txq[i], fifo, 0); 715 iwl_trans_tx_queue_set_status(priv, &priv->txq[i], fifo, 0);
716 } 716 }
717 717
718 spin_unlock_irqrestore(&priv->lock, flags); 718 spin_unlock_irqrestore(&priv->lock, flags);
@@ -919,7 +919,7 @@ static int iwl_trans_tx(struct iwl_priv *priv, struct sk_buff *skb,
919 919
920 /* Set up entry for this TFD in Tx byte-count array */ 920 /* Set up entry for this TFD in Tx byte-count array */
921 if (ampdu) 921 if (ampdu)
922 iwlagn_txq_update_byte_cnt_tbl(priv, txq, 922 iwl_trans_txq_update_byte_cnt_tbl(priv, txq,
923 le16_to_cpu(tx_cmd->len)); 923 le16_to_cpu(tx_cmd->len));
924 924
925 dma_sync_single_for_device(priv->bus.dev, txcmd_phys, firstlen, 925 dma_sync_single_for_device(priv->bus.dev, txcmd_phys, firstlen,
@@ -986,6 +986,10 @@ static const struct iwl_trans_ops trans_ops = {
986 986
987 .get_tx_cmd = iwl_trans_get_tx_cmd, 987 .get_tx_cmd = iwl_trans_get_tx_cmd,
988 .tx = iwl_trans_tx, 988 .tx = iwl_trans_tx,
989
990 .txq_agg_disable = iwl_trans_txq_agg_disable,
991 .txq_agg_setup = iwl_trans_txq_agg_setup,
992
989 .kick_nic = iwl_trans_kick_nic, 993 .kick_nic = iwl_trans_kick_nic,
990 994
991 .sync_irq = iwl_trans_sync_irq, 995 .sync_irq = iwl_trans_sync_irq,
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 9a950492ca73..215f4d5f30bf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -119,6 +119,18 @@ static inline int trans_tx(struct iwl_priv *priv, struct sk_buff *skb,
119 return priv->trans.ops->tx(priv, skb, tx_cmd, txq_id, fc, ampdu, ctx); 119 return priv->trans.ops->tx(priv, skb, tx_cmd, txq_id, fc, ampdu, ctx);
120} 120}
121 121
122static inline int trans_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
123 u16 ssn_idx, u8 tx_fifo)
124{
125 return priv->trans.ops->txq_agg_disable(priv, txq_id, ssn_idx, tx_fifo);
126}
127
128static inline void trans_txq_agg_setup(struct iwl_priv *priv, int sta_id,
129 int tid, int frame_limit)
130{
131 priv->trans.ops->txq_agg_setup(priv, sta_id, tid, frame_limit);
132}
133
122static inline void trans_kick_nic(struct iwl_priv *priv) 134static inline void trans_kick_nic(struct iwl_priv *priv)
123{ 135{
124 priv->trans.ops->kick_nic(priv); 136 priv->trans.ops->kick_nic(priv);
@@ -135,3 +147,8 @@ static inline void trans_free(struct iwl_priv *priv)
135} 147}
136 148
137int iwl_trans_register(struct iwl_priv *priv); 149int iwl_trans_register(struct iwl_priv *priv);
150
151/*TODO: this functions should NOT be exported from trans module - export it
152 * until the reclaim flow will be brought to the transport module too */
153void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
154 struct iwl_tx_queue *txq);