diff options
author | Gabor Juhos <juhosg@openwrt.org> | 2013-10-17 03:42:21 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-10-18 14:06:59 -0400 |
commit | 8d03e77218ff4bc59e4645438acbd3c5c7e0f654 (patch) | |
tree | 78610b13d390385c4b486078d1118297bb8a9517 /drivers/net/wireless/rt2x00/rt2800pci.c | |
parent | b5cfde3fd9ff44c4ba831815a441d2b5a5af0b2f (diff) |
rt2x00: rt2800pci: move interrupt functions to the rt2800mmio module
Move the functions into a separate module, in order
to make those usable from other modules.
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2800pci.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800pci.c | 388 |
1 files changed, 0 insertions, 388 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index a829ce59e026..12454b0a6383 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c | |||
@@ -448,45 +448,6 @@ static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) | |||
448 | /* | 448 | /* |
449 | * Device state switch handlers. | 449 | * Device state switch handlers. |
450 | */ | 450 | */ |
451 | static void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, | ||
452 | enum dev_state state) | ||
453 | { | ||
454 | u32 reg; | ||
455 | unsigned long flags; | ||
456 | |||
457 | /* | ||
458 | * When interrupts are being enabled, the interrupt registers | ||
459 | * should clear the register to assure a clean state. | ||
460 | */ | ||
461 | if (state == STATE_RADIO_IRQ_ON) { | ||
462 | rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | ||
463 | rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); | ||
464 | } | ||
465 | |||
466 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
467 | reg = 0; | ||
468 | if (state == STATE_RADIO_IRQ_ON) { | ||
469 | rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); | ||
470 | rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); | ||
471 | rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); | ||
472 | rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); | ||
473 | rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); | ||
474 | } | ||
475 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
476 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
477 | |||
478 | if (state == STATE_RADIO_IRQ_OFF) { | ||
479 | /* | ||
480 | * Wait for possibly running tasklets to finish. | ||
481 | */ | ||
482 | tasklet_kill(&rt2x00dev->txstatus_tasklet); | ||
483 | tasklet_kill(&rt2x00dev->rxdone_tasklet); | ||
484 | tasklet_kill(&rt2x00dev->autowake_tasklet); | ||
485 | tasklet_kill(&rt2x00dev->tbtt_tasklet); | ||
486 | tasklet_kill(&rt2x00dev->pretbtt_tasklet); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) | 451 | static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) |
491 | { | 452 | { |
492 | u32 reg; | 453 | u32 reg; |
@@ -628,355 +589,6 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, | |||
628 | } | 589 | } |
629 | 590 | ||
630 | /* | 591 | /* |
631 | * Interrupt functions. | ||
632 | */ | ||
633 | static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) | ||
634 | { | ||
635 | struct ieee80211_conf conf = { .flags = 0 }; | ||
636 | struct rt2x00lib_conf libconf = { .conf = &conf }; | ||
637 | |||
638 | rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); | ||
639 | } | ||
640 | |||
641 | static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) | ||
642 | { | ||
643 | __le32 *txwi; | ||
644 | u32 word; | ||
645 | int wcid, tx_wcid; | ||
646 | |||
647 | wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); | ||
648 | |||
649 | txwi = rt2800_drv_get_txwi(entry); | ||
650 | rt2x00_desc_read(txwi, 1, &word); | ||
651 | tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); | ||
652 | |||
653 | return (tx_wcid == wcid); | ||
654 | } | ||
655 | |||
656 | static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) | ||
657 | { | ||
658 | u32 status = *(u32 *)data; | ||
659 | |||
660 | /* | ||
661 | * rt2800pci hardware might reorder frames when exchanging traffic | ||
662 | * with multiple BA enabled STAs. | ||
663 | * | ||
664 | * For example, a tx queue | ||
665 | * [ STA1 | STA2 | STA1 | STA2 ] | ||
666 | * can result in tx status reports | ||
667 | * [ STA1 | STA1 | STA2 | STA2 ] | ||
668 | * when the hw decides to aggregate the frames for STA1 into one AMPDU. | ||
669 | * | ||
670 | * To mitigate this effect, associate the tx status to the first frame | ||
671 | * in the tx queue with a matching wcid. | ||
672 | */ | ||
673 | if (rt2800mmio_txdone_entry_check(entry, status) && | ||
674 | !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
675 | /* | ||
676 | * Got a matching frame, associate the tx status with | ||
677 | * the frame | ||
678 | */ | ||
679 | entry->status = status; | ||
680 | set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); | ||
681 | return true; | ||
682 | } | ||
683 | |||
684 | /* Check the next frame */ | ||
685 | return false; | ||
686 | } | ||
687 | |||
688 | static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) | ||
689 | { | ||
690 | u32 status = *(u32 *)data; | ||
691 | |||
692 | /* | ||
693 | * Find the first frame without tx status and assign this status to it | ||
694 | * regardless if it matches or not. | ||
695 | */ | ||
696 | if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
697 | /* | ||
698 | * Got a matching frame, associate the tx status with | ||
699 | * the frame | ||
700 | */ | ||
701 | entry->status = status; | ||
702 | set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); | ||
703 | return true; | ||
704 | } | ||
705 | |||
706 | /* Check the next frame */ | ||
707 | return false; | ||
708 | } | ||
709 | static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, | ||
710 | void *data) | ||
711 | { | ||
712 | if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { | ||
713 | rt2800_txdone_entry(entry, entry->status, | ||
714 | rt2800mmio_get_txwi(entry)); | ||
715 | return false; | ||
716 | } | ||
717 | |||
718 | /* No more frames to release */ | ||
719 | return true; | ||
720 | } | ||
721 | |||
722 | static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) | ||
723 | { | ||
724 | struct data_queue *queue; | ||
725 | u32 status; | ||
726 | u8 qid; | ||
727 | int max_tx_done = 16; | ||
728 | |||
729 | while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { | ||
730 | qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); | ||
731 | if (unlikely(qid >= QID_RX)) { | ||
732 | /* | ||
733 | * Unknown queue, this shouldn't happen. Just drop | ||
734 | * this tx status. | ||
735 | */ | ||
736 | rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", | ||
737 | qid); | ||
738 | break; | ||
739 | } | ||
740 | |||
741 | queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); | ||
742 | if (unlikely(queue == NULL)) { | ||
743 | /* | ||
744 | * The queue is NULL, this shouldn't happen. Stop | ||
745 | * processing here and drop the tx status | ||
746 | */ | ||
747 | rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", | ||
748 | qid); | ||
749 | break; | ||
750 | } | ||
751 | |||
752 | if (unlikely(rt2x00queue_empty(queue))) { | ||
753 | /* | ||
754 | * The queue is empty. Stop processing here | ||
755 | * and drop the tx status. | ||
756 | */ | ||
757 | rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", | ||
758 | qid); | ||
759 | break; | ||
760 | } | ||
761 | |||
762 | /* | ||
763 | * Let's associate this tx status with the first | ||
764 | * matching frame. | ||
765 | */ | ||
766 | if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
767 | Q_INDEX, &status, | ||
768 | rt2800mmio_txdone_find_entry)) { | ||
769 | /* | ||
770 | * We cannot match the tx status to any frame, so just | ||
771 | * use the first one. | ||
772 | */ | ||
773 | if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
774 | Q_INDEX, &status, | ||
775 | rt2800mmio_txdone_match_first)) { | ||
776 | rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", | ||
777 | qid); | ||
778 | break; | ||
779 | } | ||
780 | } | ||
781 | |||
782 | /* | ||
783 | * Release all frames with a valid tx status. | ||
784 | */ | ||
785 | rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, | ||
786 | Q_INDEX, NULL, | ||
787 | rt2800mmio_txdone_release_entries); | ||
788 | |||
789 | if (--max_tx_done == 0) | ||
790 | break; | ||
791 | } | ||
792 | |||
793 | return !max_tx_done; | ||
794 | } | ||
795 | |||
796 | static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, | ||
797 | struct rt2x00_field32 irq_field) | ||
798 | { | ||
799 | u32 reg; | ||
800 | |||
801 | /* | ||
802 | * Enable a single interrupt. The interrupt mask register | ||
803 | * access needs locking. | ||
804 | */ | ||
805 | spin_lock_irq(&rt2x00dev->irqmask_lock); | ||
806 | rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
807 | rt2x00_set_field32(®, irq_field, 1); | ||
808 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
809 | spin_unlock_irq(&rt2x00dev->irqmask_lock); | ||
810 | } | ||
811 | |||
812 | static void rt2800mmio_txstatus_tasklet(unsigned long data) | ||
813 | { | ||
814 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
815 | if (rt2800mmio_txdone(rt2x00dev)) | ||
816 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
817 | |||
818 | /* | ||
819 | * No need to enable the tx status interrupt here as we always | ||
820 | * leave it enabled to minimize the possibility of a tx status | ||
821 | * register overflow. See comment in interrupt handler. | ||
822 | */ | ||
823 | } | ||
824 | |||
825 | static void rt2800mmio_pretbtt_tasklet(unsigned long data) | ||
826 | { | ||
827 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
828 | rt2x00lib_pretbtt(rt2x00dev); | ||
829 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
830 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); | ||
831 | } | ||
832 | |||
833 | static void rt2800mmio_tbtt_tasklet(unsigned long data) | ||
834 | { | ||
835 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
836 | struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; | ||
837 | u32 reg; | ||
838 | |||
839 | rt2x00lib_beacondone(rt2x00dev); | ||
840 | |||
841 | if (rt2x00dev->intf_ap_count) { | ||
842 | /* | ||
843 | * The rt2800pci hardware tbtt timer is off by 1us per tbtt | ||
844 | * causing beacon skew and as a result causing problems with | ||
845 | * some powersaving clients over time. Shorten the beacon | ||
846 | * interval every 64 beacons by 64us to mitigate this effect. | ||
847 | */ | ||
848 | if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { | ||
849 | rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); | ||
850 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, | ||
851 | (rt2x00dev->beacon_int * 16) - 1); | ||
852 | rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); | ||
853 | } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { | ||
854 | rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); | ||
855 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, | ||
856 | (rt2x00dev->beacon_int * 16)); | ||
857 | rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); | ||
858 | } | ||
859 | drv_data->tbtt_tick++; | ||
860 | drv_data->tbtt_tick %= BCN_TBTT_OFFSET; | ||
861 | } | ||
862 | |||
863 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
864 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); | ||
865 | } | ||
866 | |||
867 | static void rt2800mmio_rxdone_tasklet(unsigned long data) | ||
868 | { | ||
869 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
870 | if (rt2x00mmio_rxdone(rt2x00dev)) | ||
871 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
872 | else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
873 | rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); | ||
874 | } | ||
875 | |||
876 | static void rt2800mmio_autowake_tasklet(unsigned long data) | ||
877 | { | ||
878 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
879 | rt2800mmio_wakeup(rt2x00dev); | ||
880 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
881 | rt2800mmio_enable_interrupt(rt2x00dev, | ||
882 | INT_MASK_CSR_AUTO_WAKEUP); | ||
883 | } | ||
884 | |||
885 | static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) | ||
886 | { | ||
887 | u32 status; | ||
888 | int i; | ||
889 | |||
890 | /* | ||
891 | * The TX_FIFO_STATUS interrupt needs special care. We should | ||
892 | * read TX_STA_FIFO but we should do it immediately as otherwise | ||
893 | * the register can overflow and we would lose status reports. | ||
894 | * | ||
895 | * Hence, read the TX_STA_FIFO register and copy all tx status | ||
896 | * reports into a kernel FIFO which is handled in the txstatus | ||
897 | * tasklet. We use a tasklet to process the tx status reports | ||
898 | * because we can schedule the tasklet multiple times (when the | ||
899 | * interrupt fires again during tx status processing). | ||
900 | * | ||
901 | * Furthermore we don't disable the TX_FIFO_STATUS | ||
902 | * interrupt here but leave it enabled so that the TX_STA_FIFO | ||
903 | * can also be read while the tx status tasklet gets executed. | ||
904 | * | ||
905 | * Since we have only one producer and one consumer we don't | ||
906 | * need to lock the kfifo. | ||
907 | */ | ||
908 | for (i = 0; i < rt2x00dev->tx->limit; i++) { | ||
909 | rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); | ||
910 | |||
911 | if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) | ||
912 | break; | ||
913 | |||
914 | if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) { | ||
915 | rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); | ||
916 | break; | ||
917 | } | ||
918 | } | ||
919 | |||
920 | /* Schedule the tasklet for processing the tx status. */ | ||
921 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
922 | } | ||
923 | |||
924 | static irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) | ||
925 | { | ||
926 | struct rt2x00_dev *rt2x00dev = dev_instance; | ||
927 | u32 reg, mask; | ||
928 | |||
929 | /* Read status and ACK all interrupts */ | ||
930 | rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | ||
931 | rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); | ||
932 | |||
933 | if (!reg) | ||
934 | return IRQ_NONE; | ||
935 | |||
936 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | ||
937 | return IRQ_HANDLED; | ||
938 | |||
939 | /* | ||
940 | * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits | ||
941 | * for interrupts and interrupt masks we can just use the value of | ||
942 | * INT_SOURCE_CSR to create the interrupt mask. | ||
943 | */ | ||
944 | mask = ~reg; | ||
945 | |||
946 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { | ||
947 | rt2800mmio_txstatus_interrupt(rt2x00dev); | ||
948 | /* | ||
949 | * Never disable the TX_FIFO_STATUS interrupt. | ||
950 | */ | ||
951 | rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); | ||
952 | } | ||
953 | |||
954 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) | ||
955 | tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); | ||
956 | |||
957 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) | ||
958 | tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); | ||
959 | |||
960 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) | ||
961 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
962 | |||
963 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) | ||
964 | tasklet_schedule(&rt2x00dev->autowake_tasklet); | ||
965 | |||
966 | /* | ||
967 | * Disable all interrupts for which a tasklet was scheduled right now, | ||
968 | * the tasklet will reenable the appropriate interrupts. | ||
969 | */ | ||
970 | spin_lock(&rt2x00dev->irqmask_lock); | ||
971 | rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
972 | reg &= mask; | ||
973 | rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
974 | spin_unlock(&rt2x00dev->irqmask_lock); | ||
975 | |||
976 | return IRQ_HANDLED; | ||
977 | } | ||
978 | |||
979 | /* | ||
980 | * Device probe functions. | 592 | * Device probe functions. |
981 | */ | 593 | */ |
982 | static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) | 594 | static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) |