aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-1000.c10
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-5000.c253
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-6000.c20
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-tx.c268
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.h17
6 files changed, 313 insertions, 257 deletions
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 46fad2d7226..fec44cf3c0a 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -10,7 +10,7 @@ CFLAGS_iwl-devtrace.o := -I$(src)
10# AGN 10# AGN
11obj-$(CONFIG_IWLAGN) += iwlagn.o 11obj-$(CONFIG_IWLAGN) += iwlagn.o
12iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o iwl-agn-ict.o 12iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o iwl-agn-ict.o
13iwlagn-objs += iwl-agn-ucode.o iwl-agn-hcmd.o 13iwlagn-objs += iwl-agn-ucode.o iwl-agn-hcmd.o iwl-agn-tx.o
14 14
15iwlagn-$(CONFIG_IWL4965) += iwl-4965.o 15iwlagn-$(CONFIG_IWL4965) += iwl-4965.o
16iwlagn-$(CONFIG_IWL5000) += iwl-5000.o 16iwlagn-$(CONFIG_IWL5000) += iwl-5000.o
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c
index 67d8513cff3..6bdbe2df282 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-1000.c
@@ -162,11 +162,11 @@ static int iwl1000_hw_set_hw_params(struct iwl_priv *priv)
162 162
163static struct iwl_lib_ops iwl1000_lib = { 163static struct iwl_lib_ops iwl1000_lib = {
164 .set_hw_params = iwl1000_hw_set_hw_params, 164 .set_hw_params = iwl1000_hw_set_hw_params,
165 .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, 165 .txq_update_byte_cnt_tbl = iwlagn_txq_update_byte_cnt_tbl,
166 .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, 166 .txq_inval_byte_cnt_tbl = iwlagn_txq_inval_byte_cnt_tbl,
167 .txq_set_sched = iwl5000_txq_set_sched, 167 .txq_set_sched = iwlagn_txq_set_sched,
168 .txq_agg_enable = iwl5000_txq_agg_enable, 168 .txq_agg_enable = iwlagn_txq_agg_enable,
169 .txq_agg_disable = iwl5000_txq_agg_disable, 169 .txq_agg_disable = iwlagn_txq_agg_disable,
170 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, 170 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd,
171 .txq_free_tfd = iwl_hw_txq_free_tfd, 171 .txq_free_tfd = iwl_hw_txq_free_tfd,
172 .txq_init = iwl_hw_tx_queue_init, 172 .txq_init = iwl_hw_tx_queue_init,
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 8aa382e370f..7e278956ec3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -358,34 +358,6 @@ restart:
358 queue_work(priv->workqueue, &priv->restart); 358 queue_work(priv->workqueue, &priv->restart);
359} 359}
360 360
361static void iwl5000_set_wr_ptrs(struct iwl_priv *priv,
362 int txq_id, u32 index)
363{
364 iwl_write_direct32(priv, HBUS_TARG_WRPTR,
365 (index & 0xff) | (txq_id << 8));
366 iwl_write_prph(priv, IWL50_SCD_QUEUE_RDPTR(txq_id), index);
367}
368
369static void iwl5000_tx_queue_set_status(struct iwl_priv *priv,
370 struct iwl_tx_queue *txq,
371 int tx_fifo_id, int scd_retry)
372{
373 int txq_id = txq->q.id;
374 int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
375
376 iwl_write_prph(priv, IWL50_SCD_QUEUE_STATUS_BITS(txq_id),
377 (active << IWL50_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
378 (tx_fifo_id << IWL50_SCD_QUEUE_STTS_REG_POS_TXF) |
379 (1 << IWL50_SCD_QUEUE_STTS_REG_POS_WSL) |
380 IWL50_SCD_QUEUE_STTS_REG_MSK);
381
382 txq->sched_retry = scd_retry;
383
384 IWL_DEBUG_INFO(priv, "%s %s Queue %d on FIFO %d\n",
385 active ? "Activate" : "Deactivate",
386 scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
387}
388
389int iwl5000_alive_notify(struct iwl_priv *priv) 361int iwl5000_alive_notify(struct iwl_priv *priv)
390{ 362{
391 u32 a; 363 u32 a;
@@ -448,7 +420,7 @@ int iwl5000_alive_notify(struct iwl_priv *priv)
448 /* Activate all Tx DMA/FIFO channels */ 420 /* Activate all Tx DMA/FIFO channels */
449 priv->cfg->ops->lib->txq_set_sched(priv, IWL_MASK(0, 7)); 421 priv->cfg->ops->lib->txq_set_sched(priv, IWL_MASK(0, 7));
450 422
451 iwl5000_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0); 423 iwlagn_set_wr_ptrs(priv, IWL_CMD_QUEUE_NUM, 0);
452 424
453 /* make sure all queue are not stopped */ 425 /* make sure all queue are not stopped */
454 memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped)); 426 memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped));
@@ -468,7 +440,7 @@ int iwl5000_alive_notify(struct iwl_priv *priv)
468 if (ac == IWL_TX_FIFO_UNUSED) 440 if (ac == IWL_TX_FIFO_UNUSED)
469 continue; 441 continue;
470 442
471 iwl5000_tx_queue_set_status(priv, &priv->txq[i], ac, 0); 443 iwlagn_tx_queue_set_status(priv, &priv->txq[i], ac, 0);
472 } 444 }
473 445
474 spin_unlock_irqrestore(&priv->lock, flags); 446 spin_unlock_irqrestore(&priv->lock, flags);
@@ -539,207 +511,6 @@ int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
539 return 0; 511 return 0;
540} 512}
541 513
542/**
543 * iwl5000_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
544 */
545void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
546 struct iwl_tx_queue *txq,
547 u16 byte_cnt)
548{
549 struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
550 int write_ptr = txq->q.write_ptr;
551 int txq_id = txq->q.id;
552 u8 sec_ctl = 0;
553 u8 sta_id = 0;
554 u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
555 __le16 bc_ent;
556
557 WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
558
559 if (txq_id != IWL_CMD_QUEUE_NUM) {
560 sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
561 sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl;
562
563 switch (sec_ctl & TX_CMD_SEC_MSK) {
564 case TX_CMD_SEC_CCM:
565 len += CCMP_MIC_LEN;
566 break;
567 case TX_CMD_SEC_TKIP:
568 len += TKIP_ICV_LEN;
569 break;
570 case TX_CMD_SEC_WEP:
571 len += WEP_IV_LEN + WEP_ICV_LEN;
572 break;
573 }
574 }
575
576 bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
577
578 scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
579
580 if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
581 scd_bc_tbl[txq_id].
582 tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
583}
584
585void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
586 struct iwl_tx_queue *txq)
587{
588 struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
589 int txq_id = txq->q.id;
590 int read_ptr = txq->q.read_ptr;
591 u8 sta_id = 0;
592 __le16 bc_ent;
593
594 WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
595
596 if (txq_id != IWL_CMD_QUEUE_NUM)
597 sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id;
598
599 bc_ent = cpu_to_le16(1 | (sta_id << 12));
600 scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
601
602 if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
603 scd_bc_tbl[txq_id].
604 tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
605}
606
607static int iwl5000_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
608 u16 txq_id)
609{
610 u32 tbl_dw_addr;
611 u32 tbl_dw;
612 u16 scd_q2ratid;
613
614 scd_q2ratid = ra_tid & IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK;
615
616 tbl_dw_addr = priv->scd_base_addr +
617 IWL50_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
618
619 tbl_dw = iwl_read_targ_mem(priv, tbl_dw_addr);
620
621 if (txq_id & 0x1)
622 tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
623 else
624 tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
625
626 iwl_write_targ_mem(priv, tbl_dw_addr, tbl_dw);
627
628 return 0;
629}
630static void iwl5000_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id)
631{
632 /* Simply stop the queue, but don't change any configuration;
633 * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
634 iwl_write_prph(priv,
635 IWL50_SCD_QUEUE_STATUS_BITS(txq_id),
636 (0 << IWL50_SCD_QUEUE_STTS_REG_POS_ACTIVE)|
637 (1 << IWL50_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
638}
639
640int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id,
641 int tx_fifo, int sta_id, int tid, u16 ssn_idx)
642{
643 unsigned long flags;
644 u16 ra_tid;
645
646 if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
647 (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
648 <= txq_id)) {
649 IWL_WARN(priv,
650 "queue number out of range: %d, must be %d to %d\n",
651 txq_id, IWL50_FIRST_AMPDU_QUEUE,
652 IWL50_FIRST_AMPDU_QUEUE +
653 priv->cfg->num_of_ampdu_queues - 1);
654 return -EINVAL;
655 }
656
657 ra_tid = BUILD_RAxTID(sta_id, tid);
658
659 /* Modify device's station table to Tx this TID */
660 iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
661
662 spin_lock_irqsave(&priv->lock, flags);
663
664 /* Stop this Tx queue before configuring it */
665 iwl5000_tx_queue_stop_scheduler(priv, txq_id);
666
667 /* Map receiver-address / traffic-ID to this queue */
668 iwl5000_tx_queue_set_q2ratid(priv, ra_tid, txq_id);
669
670 /* Set this queue as a chain-building queue */
671 iwl_set_bits_prph(priv, IWL50_SCD_QUEUECHAIN_SEL, (1<<txq_id));
672
673 /* enable aggregations for the queue */
674 iwl_set_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1<<txq_id));
675
676 /* Place first TFD at index corresponding to start sequence number.
677 * Assumes that ssn_idx is valid (!= 0xFFF) */
678 priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
679 priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
680 iwl5000_set_wr_ptrs(priv, txq_id, ssn_idx);
681
682 /* Set up Tx window size and frame limit for this queue */
683 iwl_write_targ_mem(priv, priv->scd_base_addr +
684 IWL50_SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
685 sizeof(u32),
686 ((SCD_WIN_SIZE <<
687 IWL50_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
688 IWL50_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
689 ((SCD_FRAME_LIMIT <<
690 IWL50_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
691 IWL50_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
692
693 iwl_set_bits_prph(priv, IWL50_SCD_INTERRUPT_MASK, (1 << txq_id));
694
695 /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
696 iwl5000_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
697
698 spin_unlock_irqrestore(&priv->lock, flags);
699
700 return 0;
701}
702
703int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
704 u16 ssn_idx, u8 tx_fifo)
705{
706 if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
707 (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
708 <= txq_id)) {
709 IWL_ERR(priv,
710 "queue number out of range: %d, must be %d to %d\n",
711 txq_id, IWL50_FIRST_AMPDU_QUEUE,
712 IWL50_FIRST_AMPDU_QUEUE +
713 priv->cfg->num_of_ampdu_queues - 1);
714 return -EINVAL;
715 }
716
717 iwl5000_tx_queue_stop_scheduler(priv, txq_id);
718
719 iwl_clear_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1 << txq_id));
720
721 priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
722 priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
723 /* supposes that ssn_idx is valid (!= 0xFFF) */
724 iwl5000_set_wr_ptrs(priv, txq_id, ssn_idx);
725
726 iwl_clear_bits_prph(priv, IWL50_SCD_INTERRUPT_MASK, (1 << txq_id));
727 iwl_txq_ctx_deactivate(priv, txq_id);
728 iwl5000_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
729
730 return 0;
731}
732
733/*
734 * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
735 * must be called under priv->lock and mac access
736 */
737void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask)
738{
739 iwl_write_prph(priv, IWL50_SCD_TXFACT, mask);
740}
741
742
743static inline u32 iwl5000_get_scd_ssn(struct iwl5000_tx_resp *tx_resp) 514static inline u32 iwl5000_get_scd_ssn(struct iwl5000_tx_resp *tx_resp)
744{ 515{
745 return le32_to_cpup((__le32 *)&tx_resp->status + 516 return le32_to_cpup((__le32 *)&tx_resp->status +
@@ -1063,11 +834,11 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
1063 834
1064struct iwl_lib_ops iwl5000_lib = { 835struct iwl_lib_ops iwl5000_lib = {
1065 .set_hw_params = iwl5000_hw_set_hw_params, 836 .set_hw_params = iwl5000_hw_set_hw_params,
1066 .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, 837 .txq_update_byte_cnt_tbl = iwlagn_txq_update_byte_cnt_tbl,
1067 .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, 838 .txq_inval_byte_cnt_tbl = iwlagn_txq_inval_byte_cnt_tbl,
1068 .txq_set_sched = iwl5000_txq_set_sched, 839 .txq_set_sched = iwlagn_txq_set_sched,
1069 .txq_agg_enable = iwl5000_txq_agg_enable, 840 .txq_agg_enable = iwlagn_txq_agg_enable,
1070 .txq_agg_disable = iwl5000_txq_agg_disable, 841 .txq_agg_disable = iwlagn_txq_agg_disable,
1071 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, 842 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd,
1072 .txq_free_tfd = iwl_hw_txq_free_tfd, 843 .txq_free_tfd = iwl_hw_txq_free_tfd,
1073 .txq_init = iwl_hw_tx_queue_init, 844 .txq_init = iwl_hw_tx_queue_init,
@@ -1121,11 +892,11 @@ struct iwl_lib_ops iwl5000_lib = {
1121 892
1122static struct iwl_lib_ops iwl5150_lib = { 893static struct iwl_lib_ops iwl5150_lib = {
1123 .set_hw_params = iwl5000_hw_set_hw_params, 894 .set_hw_params = iwl5000_hw_set_hw_params,
1124 .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, 895 .txq_update_byte_cnt_tbl = iwlagn_txq_update_byte_cnt_tbl,
1125 .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, 896 .txq_inval_byte_cnt_tbl = iwlagn_txq_inval_byte_cnt_tbl,
1126 .txq_set_sched = iwl5000_txq_set_sched, 897 .txq_set_sched = iwlagn_txq_set_sched,
1127 .txq_agg_enable = iwl5000_txq_agg_enable, 898 .txq_agg_enable = iwlagn_txq_agg_enable,
1128 .txq_agg_disable = iwl5000_txq_agg_disable, 899 .txq_agg_disable = iwlagn_txq_agg_disable,
1129 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, 900 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd,
1130 .txq_free_tfd = iwl_hw_txq_free_tfd, 901 .txq_free_tfd = iwl_hw_txq_free_tfd,
1131 .txq_init = iwl_hw_tx_queue_init, 902 .txq_init = iwl_hw_tx_queue_init,
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c
index aff46b08703..f3763900348 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-6000.c
@@ -226,11 +226,11 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, u16 channel)
226 226
227static struct iwl_lib_ops iwl6000_lib = { 227static struct iwl_lib_ops iwl6000_lib = {
228 .set_hw_params = iwl6000_hw_set_hw_params, 228 .set_hw_params = iwl6000_hw_set_hw_params,
229 .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, 229 .txq_update_byte_cnt_tbl = iwlagn_txq_update_byte_cnt_tbl,
230 .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, 230 .txq_inval_byte_cnt_tbl = iwlagn_txq_inval_byte_cnt_tbl,
231 .txq_set_sched = iwl5000_txq_set_sched, 231 .txq_set_sched = iwlagn_txq_set_sched,
232 .txq_agg_enable = iwl5000_txq_agg_enable, 232 .txq_agg_enable = iwlagn_txq_agg_enable,
233 .txq_agg_disable = iwl5000_txq_agg_disable, 233 .txq_agg_disable = iwlagn_txq_agg_disable,
234 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, 234 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd,
235 .txq_free_tfd = iwl_hw_txq_free_tfd, 235 .txq_free_tfd = iwl_hw_txq_free_tfd,
236 .txq_init = iwl_hw_tx_queue_init, 236 .txq_init = iwl_hw_tx_queue_init,
@@ -293,11 +293,11 @@ static const struct iwl_ops iwl6000_ops = {
293 293
294static struct iwl_lib_ops iwl6050_lib = { 294static struct iwl_lib_ops iwl6050_lib = {
295 .set_hw_params = iwl6000_hw_set_hw_params, 295 .set_hw_params = iwl6000_hw_set_hw_params,
296 .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, 296 .txq_update_byte_cnt_tbl = iwlagn_txq_update_byte_cnt_tbl,
297 .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, 297 .txq_inval_byte_cnt_tbl = iwlagn_txq_inval_byte_cnt_tbl,
298 .txq_set_sched = iwl5000_txq_set_sched, 298 .txq_set_sched = iwlagn_txq_set_sched,
299 .txq_agg_enable = iwl5000_txq_agg_enable, 299 .txq_agg_enable = iwlagn_txq_agg_enable,
300 .txq_agg_disable = iwl5000_txq_agg_disable, 300 .txq_agg_disable = iwlagn_txq_agg_disable,
301 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, 301 .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd,
302 .txq_free_tfd = iwl_hw_txq_free_tfd, 302 .txq_free_tfd = iwl_hw_txq_free_tfd,
303 .txq_init = iwl_hw_tx_queue_init, 303 .txq_init = iwl_hw_tx_queue_init,
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
new file mode 100644
index 00000000000..44b3d53e9c7
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -0,0 +1,268 @@
1/******************************************************************************
2 *
3 * GPL LICENSE SUMMARY
4 *
5 * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
19 * USA
20 *
21 * The full GNU General Public License is included in this distribution
22 * in the file called LICENSE.GPL.
23 *
24 * Contact Information:
25 * Intel Linux Wireless <ilw@linux.intel.com>
26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
27 *
28 *****************************************************************************/
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/sched.h>
34
35#include "iwl-dev.h"
36#include "iwl-core.h"
37#include "iwl-sta.h"
38#include "iwl-io.h"
39#include "iwl-5000-hw.h"
40
41/**
42 * iwlagn_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array
43 */
44void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
45 struct iwl_tx_queue *txq,
46 u16 byte_cnt)
47{
48 struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
49 int write_ptr = txq->q.write_ptr;
50 int txq_id = txq->q.id;
51 u8 sec_ctl = 0;
52 u8 sta_id = 0;
53 u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE;
54 __le16 bc_ent;
55
56 WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX);
57
58 if (txq_id != IWL_CMD_QUEUE_NUM) {
59 sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id;
60 sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl;
61
62 switch (sec_ctl & TX_CMD_SEC_MSK) {
63 case TX_CMD_SEC_CCM:
64 len += CCMP_MIC_LEN;
65 break;
66 case TX_CMD_SEC_TKIP:
67 len += TKIP_ICV_LEN;
68 break;
69 case TX_CMD_SEC_WEP:
70 len += WEP_IV_LEN + WEP_ICV_LEN;
71 break;
72 }
73 }
74
75 bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12));
76
77 scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent;
78
79 if (write_ptr < TFD_QUEUE_SIZE_BC_DUP)
80 scd_bc_tbl[txq_id].
81 tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent;
82}
83
84void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
85 struct iwl_tx_queue *txq)
86{
87 struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr;
88 int txq_id = txq->q.id;
89 int read_ptr = txq->q.read_ptr;
90 u8 sta_id = 0;
91 __le16 bc_ent;
92
93 WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX);
94
95 if (txq_id != IWL_CMD_QUEUE_NUM)
96 sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id;
97
98 bc_ent = cpu_to_le16(1 | (sta_id << 12));
99 scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent;
100
101 if (read_ptr < TFD_QUEUE_SIZE_BC_DUP)
102 scd_bc_tbl[txq_id].
103 tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
104}
105
106static int iwlagn_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid,
107 u16 txq_id)
108{
109 u32 tbl_dw_addr;
110 u32 tbl_dw;
111 u16 scd_q2ratid;
112
113 scd_q2ratid = ra_tid & IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK;
114
115 tbl_dw_addr = priv->scd_base_addr +
116 IWL50_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id);
117
118 tbl_dw = iwl_read_targ_mem(priv, tbl_dw_addr);
119
120 if (txq_id & 0x1)
121 tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF);
122 else
123 tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000);
124
125 iwl_write_targ_mem(priv, tbl_dw_addr, tbl_dw);
126
127 return 0;
128}
129
130static void iwlagn_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id)
131{
132 /* Simply stop the queue, but don't change any configuration;
133 * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
134 iwl_write_prph(priv,
135 IWL50_SCD_QUEUE_STATUS_BITS(txq_id),
136 (0 << IWL50_SCD_QUEUE_STTS_REG_POS_ACTIVE)|
137 (1 << IWL50_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
138}
139
140void iwlagn_set_wr_ptrs(struct iwl_priv *priv,
141 int txq_id, u32 index)
142{
143 iwl_write_direct32(priv, HBUS_TARG_WRPTR,
144 (index & 0xff) | (txq_id << 8));
145 iwl_write_prph(priv, IWL50_SCD_QUEUE_RDPTR(txq_id), index);
146}
147
148void iwlagn_tx_queue_set_status(struct iwl_priv *priv,
149 struct iwl_tx_queue *txq,
150 int tx_fifo_id, int scd_retry)
151{
152 int txq_id = txq->q.id;
153 int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0;
154
155 iwl_write_prph(priv, IWL50_SCD_QUEUE_STATUS_BITS(txq_id),
156 (active << IWL50_SCD_QUEUE_STTS_REG_POS_ACTIVE) |
157 (tx_fifo_id << IWL50_SCD_QUEUE_STTS_REG_POS_TXF) |
158 (1 << IWL50_SCD_QUEUE_STTS_REG_POS_WSL) |
159 IWL50_SCD_QUEUE_STTS_REG_MSK);
160
161 txq->sched_retry = scd_retry;
162
163 IWL_DEBUG_INFO(priv, "%s %s Queue %d on FIFO %d\n",
164 active ? "Activate" : "Deactivate",
165 scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id);
166}
167
168int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id,
169 int tx_fifo, int sta_id, int tid, u16 ssn_idx)
170{
171 unsigned long flags;
172 u16 ra_tid;
173
174 if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
175 (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
176 <= txq_id)) {
177 IWL_WARN(priv,
178 "queue number out of range: %d, must be %d to %d\n",
179 txq_id, IWL50_FIRST_AMPDU_QUEUE,
180 IWL50_FIRST_AMPDU_QUEUE +
181 priv->cfg->num_of_ampdu_queues - 1);
182 return -EINVAL;
183 }
184
185 ra_tid = BUILD_RAxTID(sta_id, tid);
186
187 /* Modify device's station table to Tx this TID */
188 iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
189
190 spin_lock_irqsave(&priv->lock, flags);
191
192 /* Stop this Tx queue before configuring it */
193 iwlagn_tx_queue_stop_scheduler(priv, txq_id);
194
195 /* Map receiver-address / traffic-ID to this queue */
196 iwlagn_tx_queue_set_q2ratid(priv, ra_tid, txq_id);
197
198 /* Set this queue as a chain-building queue */
199 iwl_set_bits_prph(priv, IWL50_SCD_QUEUECHAIN_SEL, (1<<txq_id));
200
201 /* enable aggregations for the queue */
202 iwl_set_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1<<txq_id));
203
204 /* Place first TFD at index corresponding to start sequence number.
205 * Assumes that ssn_idx is valid (!= 0xFFF) */
206 priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
207 priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
208 iwlagn_set_wr_ptrs(priv, txq_id, ssn_idx);
209
210 /* Set up Tx window size and frame limit for this queue */
211 iwl_write_targ_mem(priv, priv->scd_base_addr +
212 IWL50_SCD_CONTEXT_QUEUE_OFFSET(txq_id) +
213 sizeof(u32),
214 ((SCD_WIN_SIZE <<
215 IWL50_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) &
216 IWL50_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) |
217 ((SCD_FRAME_LIMIT <<
218 IWL50_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) &
219 IWL50_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
220
221 iwl_set_bits_prph(priv, IWL50_SCD_INTERRUPT_MASK, (1 << txq_id));
222
223 /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
224 iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
225
226 spin_unlock_irqrestore(&priv->lock, flags);
227
228 return 0;
229}
230
231int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
232 u16 ssn_idx, u8 tx_fifo)
233{
234 if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
235 (IWL50_FIRST_AMPDU_QUEUE + priv->cfg->num_of_ampdu_queues
236 <= txq_id)) {
237 IWL_ERR(priv,
238 "queue number out of range: %d, must be %d to %d\n",
239 txq_id, IWL50_FIRST_AMPDU_QUEUE,
240 IWL50_FIRST_AMPDU_QUEUE +
241 priv->cfg->num_of_ampdu_queues - 1);
242 return -EINVAL;
243 }
244
245 iwlagn_tx_queue_stop_scheduler(priv, txq_id);
246
247 iwl_clear_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1 << txq_id));
248
249 priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff);
250 priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff);
251 /* supposes that ssn_idx is valid (!= 0xFFF) */
252 iwlagn_set_wr_ptrs(priv, txq_id, ssn_idx);
253
254 iwl_clear_bits_prph(priv, IWL50_SCD_INTERRUPT_MASK, (1 << txq_id));
255 iwl_txq_ctx_deactivate(priv, txq_id);
256 iwlagn_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
257
258 return 0;
259}
260
261/*
262 * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask
263 * must be called under priv->lock and mac access
264 */
265void iwlagn_txq_set_sched(struct iwl_priv *priv, u32 mask)
266{
267 iwl_write_prph(priv, IWL50_SCD_TXFACT, mask);
268}
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
index 51e49df3644..2ff7b8f5df7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
@@ -80,4 +80,21 @@ bool iwl_good_ack_health(struct iwl_priv *priv,
80/* uCode */ 80/* uCode */
81int iwlagn_load_ucode(struct iwl_priv *priv); 81int iwlagn_load_ucode(struct iwl_priv *priv);
82 82
83/* tx queue */
84void iwlagn_set_wr_ptrs(struct iwl_priv *priv,
85 int txq_id, u32 index);
86void iwlagn_tx_queue_set_status(struct iwl_priv *priv,
87 struct iwl_tx_queue *txq,
88 int tx_fifo_id, int scd_retry);
89void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv,
90 struct iwl_tx_queue *txq,
91 u16 byte_cnt);
92void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv,
93 struct iwl_tx_queue *txq);
94int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id,
95 int tx_fifo, int sta_id, int tid, u16 ssn_idx);
96int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
97 u16 ssn_idx, u8 tx_fifo);
98void iwlagn_txq_set_sched(struct iwl_priv *priv, u32 mask);
99
83#endif /* __iwl_agn_h__ */ 100#endif /* __iwl_agn_h__ */