diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2011-07-10 03:47:01 -0400 |
---|---|---|
committer | Wey-Yi Guy <wey-yi.w.guy@intel.com> | 2011-07-21 10:29:12 -0400 |
commit | 48d42c426947d8ffba0caa3cf9c58be6903302e0 (patch) | |
tree | 7ae7a2e9956329df0419c89fa7f63ad53fedcc81 /drivers | |
parent | 2e27799621f9b6dc69d9fac5e365cb867eac539c (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.c | 225 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 13 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h | 14 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c | 224 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.c | 10 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans.h | 17 |
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 | /** | 99 | static 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 | */ | ||
102 | void 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 | |||
140 | static 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 | |||
162 | static 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 | |||
186 | static 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 | |||
196 | void 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 | |||
204 | void 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 | |||
224 | static 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 | ||
241 | void 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 | |||
304 | static 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 | |||
334 | static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, | 117 | static 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); | |||
127 | int iwlagn_start_device(struct iwl_priv *priv); | 127 | int iwlagn_start_device(struct iwl_priv *priv); |
128 | 128 | ||
129 | /* tx queue */ | 129 | /* tx queue */ |
130 | void iwlagn_set_wr_ptrs(struct iwl_priv *priv, | 130 | /*TODO: this one should go to transport layer */ |
131 | int txq_id, u32 index); | ||
132 | void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv, | ||
133 | struct iwl_tx_queue *txq, | ||
134 | u16 byte_cnt); | ||
135 | |||
136 | void iwlagn_tx_queue_set_status(struct iwl_priv *priv, | ||
137 | struct iwl_tx_queue *txq, | ||
138 | int tx_fifo_id, int scd_retry); | ||
139 | void iwl_free_tfds_in_queue(struct iwl_priv *priv, | 131 | void 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); |
189 | int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, | 181 | int 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); |
191 | void iwlagn_txq_agg_queue_setup(struct iwl_priv *priv, | ||
192 | struct ieee80211_sta *sta, | ||
193 | int tid, int frame_limit); | ||
194 | int iwlagn_txq_check_empty(struct iwl_priv *priv, | 183 | int 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); |
196 | void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, | 185 | void 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); | |||
66 | int __must_check iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u32 flags, | 66 | int __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); |
68 | void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); | 68 | void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); |
69 | 69 | void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_priv *priv, | |
70 | struct iwl_tx_queue *txq, | ||
71 | u16 byte_cnt); | ||
72 | int iwl_trans_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, | ||
73 | u16 ssn_idx, u8 tx_fifo); | ||
74 | void iwl_trans_set_wr_ptrs(struct iwl_priv *priv, | ||
75 | int txq_id, u32 index); | ||
76 | void iwl_trans_tx_queue_set_status(struct iwl_priv *priv, | ||
77 | struct iwl_tx_queue *txq, | ||
78 | int tx_fifo_id, int scd_retry); | ||
79 | void 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 | */ | ||
50 | void 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 */ | ||
343 | void 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 | |||
365 | static 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 | |||
389 | static 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 | |||
399 | void 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 | |||
407 | void 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 | |||
427 | void 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 | |||
488 | int 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 | ||
122 | static 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 | |||
128 | static 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 | |||
122 | static inline void trans_kick_nic(struct iwl_priv *priv) | 134 | static 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 | ||
137 | int iwl_trans_register(struct iwl_priv *priv); | 149 | int 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 */ | ||
153 | void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, | ||
154 | struct iwl_tx_queue *txq); | ||