diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-tx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-tx.c | 108 |
1 files changed, 95 insertions, 13 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 1e481f3fcabf..b798fbabc3b6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c | |||
@@ -29,6 +29,7 @@ | |||
29 | 29 | ||
30 | #include <linux/etherdevice.h> | 30 | #include <linux/etherdevice.h> |
31 | #include <linux/sched.h> | 31 | #include <linux/sched.h> |
32 | #include <linux/slab.h> | ||
32 | #include <net/mac80211.h> | 33 | #include <net/mac80211.h> |
33 | #include "iwl-eeprom.h" | 34 | #include "iwl-eeprom.h" |
34 | #include "iwl-dev.h" | 35 | #include "iwl-dev.h" |
@@ -230,10 +231,34 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) | |||
230 | struct iwl_queue *q = &txq->q; | 231 | struct iwl_queue *q = &txq->q; |
231 | struct device *dev = &priv->pci_dev->dev; | 232 | struct device *dev = &priv->pci_dev->dev; |
232 | int i; | 233 | int i; |
234 | bool huge = false; | ||
233 | 235 | ||
234 | if (q->n_bd == 0) | 236 | if (q->n_bd == 0) |
235 | return; | 237 | return; |
236 | 238 | ||
239 | for (; q->read_ptr != q->write_ptr; | ||
240 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { | ||
241 | /* we have no way to tell if it is a huge cmd ATM */ | ||
242 | i = get_cmd_index(q, q->read_ptr, 0); | ||
243 | |||
244 | if (txq->meta[i].flags & CMD_SIZE_HUGE) { | ||
245 | huge = true; | ||
246 | continue; | ||
247 | } | ||
248 | |||
249 | pci_unmap_single(priv->pci_dev, | ||
250 | pci_unmap_addr(&txq->meta[i], mapping), | ||
251 | pci_unmap_len(&txq->meta[i], len), | ||
252 | PCI_DMA_BIDIRECTIONAL); | ||
253 | } | ||
254 | if (huge) { | ||
255 | i = q->n_window; | ||
256 | pci_unmap_single(priv->pci_dev, | ||
257 | pci_unmap_addr(&txq->meta[i], mapping), | ||
258 | pci_unmap_len(&txq->meta[i], len), | ||
259 | PCI_DMA_BIDIRECTIONAL); | ||
260 | } | ||
261 | |||
237 | /* De-alloc array of command/tx buffers */ | 262 | /* De-alloc array of command/tx buffers */ |
238 | for (i = 0; i <= TFD_CMD_SLOTS; i++) | 263 | for (i = 0; i <= TFD_CMD_SLOTS; i++) |
239 | kfree(txq->cmd[i]); | 264 | kfree(txq->cmd[i]); |
@@ -448,6 +473,26 @@ out_free_arrays: | |||
448 | } | 473 | } |
449 | EXPORT_SYMBOL(iwl_tx_queue_init); | 474 | EXPORT_SYMBOL(iwl_tx_queue_init); |
450 | 475 | ||
476 | void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq, | ||
477 | int slots_num, u32 txq_id) | ||
478 | { | ||
479 | int actual_slots = slots_num; | ||
480 | |||
481 | if (txq_id == IWL_CMD_QUEUE_NUM) | ||
482 | actual_slots++; | ||
483 | |||
484 | memset(txq->meta, 0, sizeof(struct iwl_cmd_meta) * actual_slots); | ||
485 | |||
486 | txq->need_update = 0; | ||
487 | |||
488 | /* Initialize queue's high/low-water marks, and head/tail indexes */ | ||
489 | iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id); | ||
490 | |||
491 | /* Tell device where to find queue */ | ||
492 | priv->cfg->ops->lib->txq_init(priv, txq); | ||
493 | } | ||
494 | EXPORT_SYMBOL(iwl_tx_queue_reset); | ||
495 | |||
451 | /** | 496 | /** |
452 | * iwl_hw_txq_ctx_free - Free TXQ Context | 497 | * iwl_hw_txq_ctx_free - Free TXQ Context |
453 | * | 498 | * |
@@ -459,8 +504,7 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv) | |||
459 | 504 | ||
460 | /* Tx queues */ | 505 | /* Tx queues */ |
461 | if (priv->txq) { | 506 | if (priv->txq) { |
462 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; | 507 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) |
463 | txq_id++) | ||
464 | if (txq_id == IWL_CMD_QUEUE_NUM) | 508 | if (txq_id == IWL_CMD_QUEUE_NUM) |
465 | iwl_cmd_queue_free(priv); | 509 | iwl_cmd_queue_free(priv); |
466 | else | 510 | else |
@@ -476,15 +520,15 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv) | |||
476 | EXPORT_SYMBOL(iwl_hw_txq_ctx_free); | 520 | EXPORT_SYMBOL(iwl_hw_txq_ctx_free); |
477 | 521 | ||
478 | /** | 522 | /** |
479 | * iwl_txq_ctx_reset - Reset TX queue context | 523 | * iwl_txq_ctx_alloc - allocate TX queue context |
480 | * Destroys all DMA structures and initialize them again | 524 | * Allocate all Tx DMA structures and initialize them |
481 | * | 525 | * |
482 | * @param priv | 526 | * @param priv |
483 | * @return error code | 527 | * @return error code |
484 | */ | 528 | */ |
485 | int iwl_txq_ctx_reset(struct iwl_priv *priv) | 529 | int iwl_txq_ctx_alloc(struct iwl_priv *priv) |
486 | { | 530 | { |
487 | int ret = 0; | 531 | int ret; |
488 | int txq_id, slots_num; | 532 | int txq_id, slots_num; |
489 | unsigned long flags; | 533 | unsigned long flags; |
490 | 534 | ||
@@ -542,8 +586,31 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv) | |||
542 | return ret; | 586 | return ret; |
543 | } | 587 | } |
544 | 588 | ||
589 | void iwl_txq_ctx_reset(struct iwl_priv *priv) | ||
590 | { | ||
591 | int txq_id, slots_num; | ||
592 | unsigned long flags; | ||
593 | |||
594 | spin_lock_irqsave(&priv->lock, flags); | ||
595 | |||
596 | /* Turn off all Tx DMA fifos */ | ||
597 | priv->cfg->ops->lib->txq_set_sched(priv, 0); | ||
598 | |||
599 | /* Tell NIC where to find the "keep warm" buffer */ | ||
600 | iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4); | ||
601 | |||
602 | spin_unlock_irqrestore(&priv->lock, flags); | ||
603 | |||
604 | /* Alloc and init all Tx queues, including the command queue (#4) */ | ||
605 | for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { | ||
606 | slots_num = txq_id == IWL_CMD_QUEUE_NUM ? | ||
607 | TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; | ||
608 | iwl_tx_queue_reset(priv, &priv->txq[txq_id], slots_num, txq_id); | ||
609 | } | ||
610 | } | ||
611 | |||
545 | /** | 612 | /** |
546 | * iwl_txq_ctx_stop - Stop all Tx DMA channels, free Tx queue memory | 613 | * iwl_txq_ctx_stop - Stop all Tx DMA channels |
547 | */ | 614 | */ |
548 | void iwl_txq_ctx_stop(struct iwl_priv *priv) | 615 | void iwl_txq_ctx_stop(struct iwl_priv *priv) |
549 | { | 616 | { |
@@ -563,9 +630,6 @@ void iwl_txq_ctx_stop(struct iwl_priv *priv) | |||
563 | 1000); | 630 | 1000); |
564 | } | 631 | } |
565 | spin_unlock_irqrestore(&priv->lock, flags); | 632 | spin_unlock_irqrestore(&priv->lock, flags); |
566 | |||
567 | /* Deallocate memory for all Tx queues */ | ||
568 | iwl_hw_txq_ctx_free(priv); | ||
569 | } | 633 | } |
570 | EXPORT_SYMBOL(iwl_txq_ctx_stop); | 634 | EXPORT_SYMBOL(iwl_txq_ctx_stop); |
571 | 635 | ||
@@ -1075,6 +1139,14 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) | |||
1075 | 1139 | ||
1076 | spin_lock_irqsave(&priv->hcmd_lock, flags); | 1140 | spin_lock_irqsave(&priv->hcmd_lock, flags); |
1077 | 1141 | ||
1142 | /* If this is a huge cmd, mark the huge flag also on the meta.flags | ||
1143 | * of the _original_ cmd. This is used for DMA mapping clean up. | ||
1144 | */ | ||
1145 | if (cmd->flags & CMD_SIZE_HUGE) { | ||
1146 | idx = get_cmd_index(q, q->write_ptr, 0); | ||
1147 | txq->meta[idx].flags = CMD_SIZE_HUGE; | ||
1148 | } | ||
1149 | |||
1078 | idx = get_cmd_index(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); | 1150 | idx = get_cmd_index(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); |
1079 | out_cmd = txq->cmd[idx]; | 1151 | out_cmd = txq->cmd[idx]; |
1080 | out_meta = &txq->meta[idx]; | 1152 | out_meta = &txq->meta[idx]; |
@@ -1252,6 +1324,7 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) | |||
1252 | bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); | 1324 | bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); |
1253 | struct iwl_device_cmd *cmd; | 1325 | struct iwl_device_cmd *cmd; |
1254 | struct iwl_cmd_meta *meta; | 1326 | struct iwl_cmd_meta *meta; |
1327 | struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; | ||
1255 | 1328 | ||
1256 | /* If a Tx command is being handled and it isn't in the actual | 1329 | /* If a Tx command is being handled and it isn't in the actual |
1257 | * command queue then there a command routing bug has been introduced | 1330 | * command queue then there a command routing bug has been introduced |
@@ -1265,9 +1338,17 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) | |||
1265 | return; | 1338 | return; |
1266 | } | 1339 | } |
1267 | 1340 | ||
1268 | cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge); | 1341 | /* If this is a huge cmd, clear the huge flag on the meta.flags |
1269 | cmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index]; | 1342 | * of the _original_ cmd. So that iwl_cmd_queue_free won't unmap |
1270 | meta = &priv->txq[IWL_CMD_QUEUE_NUM].meta[cmd_index]; | 1343 | * the DMA buffer for the scan (huge) command. |
1344 | */ | ||
1345 | if (huge) { | ||
1346 | cmd_index = get_cmd_index(&txq->q, index, 0); | ||
1347 | txq->meta[cmd_index].flags = 0; | ||
1348 | } | ||
1349 | cmd_index = get_cmd_index(&txq->q, index, huge); | ||
1350 | cmd = txq->cmd[cmd_index]; | ||
1351 | meta = &txq->meta[cmd_index]; | ||
1271 | 1352 | ||
1272 | pci_unmap_single(priv->pci_dev, | 1353 | pci_unmap_single(priv->pci_dev, |
1273 | pci_unmap_addr(meta, mapping), | 1354 | pci_unmap_addr(meta, mapping), |
@@ -1289,6 +1370,7 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) | |||
1289 | get_cmd_string(cmd->hdr.cmd)); | 1370 | get_cmd_string(cmd->hdr.cmd)); |
1290 | wake_up_interruptible(&priv->wait_command_queue); | 1371 | wake_up_interruptible(&priv->wait_command_queue); |
1291 | } | 1372 | } |
1373 | meta->flags = 0; | ||
1292 | } | 1374 | } |
1293 | EXPORT_SYMBOL(iwl_tx_cmd_complete); | 1375 | EXPORT_SYMBOL(iwl_tx_cmd_complete); |
1294 | 1376 | ||