diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800pci.c | 150 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00.h | 17 | ||||
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00dev.c | 34 |
3 files changed, 188 insertions, 13 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 005ee153e0cc..3806454b827b 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c | |||
@@ -660,6 +660,63 @@ static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev) | |||
660 | rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); | 660 | rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); |
661 | } | 661 | } |
662 | 662 | ||
663 | static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev) | ||
664 | { | ||
665 | struct data_queue *queue; | ||
666 | struct queue_entry *entry; | ||
667 | u32 status; | ||
668 | u8 qid; | ||
669 | |||
670 | while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) { | ||
671 | /* Now remove the tx status from the FIFO */ | ||
672 | if (kfifo_out(&rt2x00dev->txstatus_fifo, &status, | ||
673 | sizeof(status)) != sizeof(status)) { | ||
674 | WARN_ON(1); | ||
675 | break; | ||
676 | } | ||
677 | |||
678 | qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_TYPE) - 1; | ||
679 | if (qid >= QID_RX) { | ||
680 | /* | ||
681 | * Unknown queue, this shouldn't happen. Just drop | ||
682 | * this tx status. | ||
683 | */ | ||
684 | WARNING(rt2x00dev, "Got TX status report with " | ||
685 | "unexpected pid %u, dropping", qid); | ||
686 | break; | ||
687 | } | ||
688 | |||
689 | queue = rt2x00queue_get_queue(rt2x00dev, qid); | ||
690 | if (unlikely(queue == NULL)) { | ||
691 | /* | ||
692 | * The queue is NULL, this shouldn't happen. Stop | ||
693 | * processing here and drop the tx status | ||
694 | */ | ||
695 | WARNING(rt2x00dev, "Got TX status for an unavailable " | ||
696 | "queue %u, dropping", qid); | ||
697 | break; | ||
698 | } | ||
699 | |||
700 | if (rt2x00queue_empty(queue)) { | ||
701 | /* | ||
702 | * The queue is empty. Stop processing here | ||
703 | * and drop the tx status. | ||
704 | */ | ||
705 | WARNING(rt2x00dev, "Got TX status for an empty " | ||
706 | "queue %u, dropping", qid); | ||
707 | break; | ||
708 | } | ||
709 | |||
710 | entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); | ||
711 | rt2800_txdone_entry(entry, status); | ||
712 | } | ||
713 | } | ||
714 | |||
715 | static void rt2800pci_txstatus_tasklet(unsigned long data) | ||
716 | { | ||
717 | rt2800pci_txdone((struct rt2x00_dev *)data); | ||
718 | } | ||
719 | |||
663 | static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance) | 720 | static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance) |
664 | { | 721 | { |
665 | struct rt2x00_dev *rt2x00dev = dev_instance; | 722 | struct rt2x00_dev *rt2x00dev = dev_instance; |
@@ -684,13 +741,7 @@ static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance) | |||
684 | rt2x00pci_rxdone(rt2x00dev); | 741 | rt2x00pci_rxdone(rt2x00dev); |
685 | 742 | ||
686 | /* | 743 | /* |
687 | * 4 - Tx done interrupt. | 744 | * 4 - Auto wakeup interrupt. |
688 | */ | ||
689 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) | ||
690 | rt2800_txdone(rt2x00dev); | ||
691 | |||
692 | /* | ||
693 | * 5 - Auto wakeup interrupt. | ||
694 | */ | 745 | */ |
695 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) | 746 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) |
696 | rt2800pci_wakeup(rt2x00dev); | 747 | rt2800pci_wakeup(rt2x00dev); |
@@ -702,10 +753,58 @@ static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance) | |||
702 | return IRQ_HANDLED; | 753 | return IRQ_HANDLED; |
703 | } | 754 | } |
704 | 755 | ||
756 | static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) | ||
757 | { | ||
758 | u32 status; | ||
759 | int i; | ||
760 | |||
761 | /* | ||
762 | * The TX_FIFO_STATUS interrupt needs special care. We should | ||
763 | * read TX_STA_FIFO but we should do it immediately as otherwise | ||
764 | * the register can overflow and we would lose status reports. | ||
765 | * | ||
766 | * Hence, read the TX_STA_FIFO register and copy all tx status | ||
767 | * reports into a kernel FIFO which is handled in the txstatus | ||
768 | * tasklet. We use a tasklet to process the tx status reports | ||
769 | * because we can schedule the tasklet multiple times (when the | ||
770 | * interrupt fires again during tx status processing). | ||
771 | * | ||
772 | * Furthermore we don't disable the TX_FIFO_STATUS | ||
773 | * interrupt here but leave it enabled so that the TX_STA_FIFO | ||
774 | * can also be read while the interrupt thread gets executed. | ||
775 | * | ||
776 | * Since we have only one producer and one consumer we don't | ||
777 | * need to lock the kfifo. | ||
778 | */ | ||
779 | for (i = 0; i < TX_ENTRIES; i++) { | ||
780 | rt2800_register_read(rt2x00dev, TX_STA_FIFO, &status); | ||
781 | |||
782 | if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) | ||
783 | break; | ||
784 | |||
785 | if (kfifo_is_full(&rt2x00dev->txstatus_fifo)) { | ||
786 | WARNING(rt2x00dev, "TX status FIFO overrun," | ||
787 | " drop tx status report.\n"); | ||
788 | break; | ||
789 | } | ||
790 | |||
791 | if (kfifo_in(&rt2x00dev->txstatus_fifo, &status, | ||
792 | sizeof(status)) != sizeof(status)) { | ||
793 | WARNING(rt2x00dev, "TX status FIFO overrun," | ||
794 | "drop tx status report.\n"); | ||
795 | break; | ||
796 | } | ||
797 | } | ||
798 | |||
799 | /* Schedule the tasklet for processing the tx status. */ | ||
800 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
801 | } | ||
802 | |||
705 | static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) | 803 | static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) |
706 | { | 804 | { |
707 | struct rt2x00_dev *rt2x00dev = dev_instance; | 805 | struct rt2x00_dev *rt2x00dev = dev_instance; |
708 | u32 reg; | 806 | u32 reg; |
807 | irqreturn_t ret = IRQ_HANDLED; | ||
709 | 808 | ||
710 | /* Read status and ACK all interrupts */ | 809 | /* Read status and ACK all interrupts */ |
711 | rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | 810 | rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, ®); |
@@ -717,15 +816,38 @@ static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) | |||
717 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | 816 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) |
718 | return IRQ_HANDLED; | 817 | return IRQ_HANDLED; |
719 | 818 | ||
720 | /* Store irqvalue for use in the interrupt thread. */ | 819 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) |
721 | rt2x00dev->irqvalue[0] = reg; | 820 | rt2800pci_txstatus_interrupt(rt2x00dev); |
821 | |||
822 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT) || | ||
823 | rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT) || | ||
824 | rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE) || | ||
825 | rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) { | ||
826 | /* | ||
827 | * All other interrupts are handled in the interrupt thread. | ||
828 | * Store irqvalue for use in the interrupt thread. | ||
829 | */ | ||
830 | rt2x00dev->irqvalue[0] = reg; | ||
722 | 831 | ||
723 | /* Disable interrupts, will be enabled again in the interrupt thread. */ | 832 | /* |
724 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | 833 | * Disable interrupts, will be enabled again in the |
725 | STATE_RADIO_IRQ_OFF_ISR); | 834 | * interrupt thread. |
835 | */ | ||
836 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | ||
837 | STATE_RADIO_IRQ_OFF_ISR); | ||
726 | 838 | ||
839 | /* | ||
840 | * Leave the TX_FIFO_STATUS interrupt enabled to not lose any | ||
841 | * tx status reports. | ||
842 | */ | ||
843 | rt2800_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
844 | rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); | ||
845 | rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
727 | 846 | ||
728 | return IRQ_WAKE_THREAD; | 847 | ret = IRQ_WAKE_THREAD; |
848 | } | ||
849 | |||
850 | return ret; | ||
729 | } | 851 | } |
730 | 852 | ||
731 | /* | 853 | /* |
@@ -788,6 +910,7 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev) | |||
788 | __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); | 910 | __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); |
789 | __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags); | 911 | __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags); |
790 | __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags); | 912 | __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags); |
913 | __set_bit(DRIVER_REQUIRE_TXSTATUS_FIFO, &rt2x00dev->flags); | ||
791 | if (!modparam_nohwcrypt) | 914 | if (!modparam_nohwcrypt) |
792 | __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); | 915 | __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); |
793 | __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags); | 916 | __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags); |
@@ -837,6 +960,7 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = { | |||
837 | static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { | 960 | static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { |
838 | .irq_handler = rt2800pci_interrupt, | 961 | .irq_handler = rt2800pci_interrupt, |
839 | .irq_handler_thread = rt2800pci_interrupt_thread, | 962 | .irq_handler_thread = rt2800pci_interrupt_thread, |
963 | .txstatus_tasklet = rt2800pci_txstatus_tasklet, | ||
840 | .probe_hw = rt2800pci_probe_hw, | 964 | .probe_hw = rt2800pci_probe_hw, |
841 | .get_firmware_name = rt2800pci_get_firmware_name, | 965 | .get_firmware_name = rt2800pci_get_firmware_name, |
842 | .check_firmware = rt2800_check_firmware, | 966 | .check_firmware = rt2800_check_firmware, |
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 7832a5996a8c..bab10adae537 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/mutex.h> | 36 | #include <linux/mutex.h> |
37 | #include <linux/etherdevice.h> | 37 | #include <linux/etherdevice.h> |
38 | #include <linux/input-polldev.h> | 38 | #include <linux/input-polldev.h> |
39 | #include <linux/kfifo.h> | ||
39 | 40 | ||
40 | #include <net/mac80211.h> | 41 | #include <net/mac80211.h> |
41 | 42 | ||
@@ -522,6 +523,11 @@ struct rt2x00lib_ops { | |||
522 | irq_handler_t irq_handler_thread; | 523 | irq_handler_t irq_handler_thread; |
523 | 524 | ||
524 | /* | 525 | /* |
526 | * TX status tasklet handler. | ||
527 | */ | ||
528 | void (*txstatus_tasklet) (unsigned long data); | ||
529 | |||
530 | /* | ||
525 | * Device init handlers. | 531 | * Device init handlers. |
526 | */ | 532 | */ |
527 | int (*probe_hw) (struct rt2x00_dev *rt2x00dev); | 533 | int (*probe_hw) (struct rt2x00_dev *rt2x00dev); |
@@ -651,6 +657,7 @@ enum rt2x00_flags { | |||
651 | DRIVER_REQUIRE_DMA, | 657 | DRIVER_REQUIRE_DMA, |
652 | DRIVER_REQUIRE_COPY_IV, | 658 | DRIVER_REQUIRE_COPY_IV, |
653 | DRIVER_REQUIRE_L2PAD, | 659 | DRIVER_REQUIRE_L2PAD, |
660 | DRIVER_REQUIRE_TXSTATUS_FIFO, | ||
654 | 661 | ||
655 | /* | 662 | /* |
656 | * Driver features | 663 | * Driver features |
@@ -884,6 +891,16 @@ struct rt2x00_dev { | |||
884 | * and interrupt thread routine. | 891 | * and interrupt thread routine. |
885 | */ | 892 | */ |
886 | u32 irqvalue[2]; | 893 | u32 irqvalue[2]; |
894 | |||
895 | /* | ||
896 | * FIFO for storing tx status reports between isr and tasklet. | ||
897 | */ | ||
898 | struct kfifo txstatus_fifo; | ||
899 | |||
900 | /* | ||
901 | * Tasklet for processing tx status reports (rt2800pci). | ||
902 | */ | ||
903 | struct tasklet_struct txstatus_tasklet; | ||
887 | }; | 904 | }; |
888 | 905 | ||
889 | /* | 906 | /* |
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 053fdd3bd720..b03e6e41dc1e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c | |||
@@ -813,6 +813,30 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) | |||
813 | rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; | 813 | rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; |
814 | 814 | ||
815 | /* | 815 | /* |
816 | * Allocate tx status FIFO for driver use. | ||
817 | */ | ||
818 | if (test_bit(DRIVER_REQUIRE_TXSTATUS_FIFO, &rt2x00dev->flags) && | ||
819 | rt2x00dev->ops->lib->txstatus_tasklet) { | ||
820 | /* | ||
821 | * Allocate txstatus fifo and tasklet, we use a size of 512 | ||
822 | * for the kfifo which is big enough to store 512/4=128 tx | ||
823 | * status reports. In the worst case (tx status for all tx | ||
824 | * queues gets reported before we've got a chance to handle | ||
825 | * them) 24*4=384 tx status reports need to be cached. | ||
826 | */ | ||
827 | status = kfifo_alloc(&rt2x00dev->txstatus_fifo, 512, | ||
828 | GFP_KERNEL); | ||
829 | if (status) | ||
830 | return status; | ||
831 | |||
832 | /* tasklet for processing the tx status reports. */ | ||
833 | tasklet_init(&rt2x00dev->txstatus_tasklet, | ||
834 | rt2x00dev->ops->lib->txstatus_tasklet, | ||
835 | (unsigned long)rt2x00dev); | ||
836 | |||
837 | } | ||
838 | |||
839 | /* | ||
816 | * Register HW. | 840 | * Register HW. |
817 | */ | 841 | */ |
818 | status = ieee80211_register_hw(rt2x00dev->hw); | 842 | status = ieee80211_register_hw(rt2x00dev->hw); |
@@ -1028,6 +1052,16 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) | |||
1028 | cancel_work_sync(&rt2x00dev->txdone_work); | 1052 | cancel_work_sync(&rt2x00dev->txdone_work); |
1029 | 1053 | ||
1030 | /* | 1054 | /* |
1055 | * Free the tx status fifo. | ||
1056 | */ | ||
1057 | kfifo_free(&rt2x00dev->txstatus_fifo); | ||
1058 | |||
1059 | /* | ||
1060 | * Kill the tx status tasklet. | ||
1061 | */ | ||
1062 | tasklet_kill(&rt2x00dev->txstatus_tasklet); | ||
1063 | |||
1064 | /* | ||
1031 | * Uninitialize device. | 1065 | * Uninitialize device. |
1032 | */ | 1066 | */ |
1033 | rt2x00lib_uninitialize(rt2x00dev); | 1067 | rt2x00lib_uninitialize(rt2x00dev); |