diff options
-rw-r--r-- | drivers/net/wireless/iwlwifi/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-1000.c | 10 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-5000.c | 253 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-6000.c | 20 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 268 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.h | 17 |
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 |
11 | obj-$(CONFIG_IWLAGN) += iwlagn.o | 11 | obj-$(CONFIG_IWLAGN) += iwlagn.o |
12 | iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o iwl-agn-ict.o | 12 | iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o iwl-agn-ict.o |
13 | iwlagn-objs += iwl-agn-ucode.o iwl-agn-hcmd.o | 13 | iwlagn-objs += iwl-agn-ucode.o iwl-agn-hcmd.o iwl-agn-tx.o |
14 | 14 | ||
15 | iwlagn-$(CONFIG_IWL4965) += iwl-4965.o | 15 | iwlagn-$(CONFIG_IWL4965) += iwl-4965.o |
16 | iwlagn-$(CONFIG_IWL5000) += iwl-5000.o | 16 | iwlagn-$(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 | ||
163 | static struct iwl_lib_ops iwl1000_lib = { | 163 | static 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 | ||
361 | static 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 | |||
369 | static 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 | |||
389 | int iwl5000_alive_notify(struct iwl_priv *priv) | 361 | int 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 | */ | ||
545 | void 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 | |||
585 | void 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 | |||
607 | static 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 | } | ||
630 | static 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 | |||
640 | int 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 | |||
703 | int 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 | */ | ||
737 | void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask) | ||
738 | { | ||
739 | iwl_write_prph(priv, IWL50_SCD_TXFACT, mask); | ||
740 | } | ||
741 | |||
742 | |||
743 | static inline u32 iwl5000_get_scd_ssn(struct iwl5000_tx_resp *tx_resp) | 514 | static 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 | ||
1064 | struct iwl_lib_ops iwl5000_lib = { | 835 | struct 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 | ||
1122 | static struct iwl_lib_ops iwl5150_lib = { | 893 | static 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 | ||
227 | static struct iwl_lib_ops iwl6000_lib = { | 227 | static 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 | ||
294 | static struct iwl_lib_ops iwl6050_lib = { | 294 | static 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 | */ | ||
44 | void 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 | |||
84 | void 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 | |||
106 | static 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 | |||
130 | static 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 | |||
140 | void 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 | |||
148 | void 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 | |||
168 | int 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 | |||
231 | int 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 | */ | ||
265 | void 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 */ |
81 | int iwlagn_load_ucode(struct iwl_priv *priv); | 81 | int iwlagn_load_ucode(struct iwl_priv *priv); |
82 | 82 | ||
83 | /* tx queue */ | ||
84 | void iwlagn_set_wr_ptrs(struct iwl_priv *priv, | ||
85 | int txq_id, u32 index); | ||
86 | void iwlagn_tx_queue_set_status(struct iwl_priv *priv, | ||
87 | struct iwl_tx_queue *txq, | ||
88 | int tx_fifo_id, int scd_retry); | ||
89 | void iwlagn_txq_update_byte_cnt_tbl(struct iwl_priv *priv, | ||
90 | struct iwl_tx_queue *txq, | ||
91 | u16 byte_cnt); | ||
92 | void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, | ||
93 | struct iwl_tx_queue *txq); | ||
94 | int iwlagn_txq_agg_enable(struct iwl_priv *priv, int txq_id, | ||
95 | int tx_fifo, int sta_id, int tid, u16 ssn_idx); | ||
96 | int iwlagn_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, | ||
97 | u16 ssn_idx, u8 tx_fifo); | ||
98 | void iwlagn_txq_set_sched(struct iwl_priv *priv, u32 mask); | ||
99 | |||
83 | #endif /* __iwl_agn_h__ */ | 100 | #endif /* __iwl_agn_h__ */ |