diff options
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2800pci.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2800pci.c | 192 |
1 files changed, 131 insertions, 61 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index bfc2fc5c1c22..8f4dfc3d8023 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c | |||
@@ -200,11 +200,22 @@ static void rt2800pci_start_queue(struct data_queue *queue) | |||
200 | rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); | 200 | rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); |
201 | break; | 201 | break; |
202 | case QID_BEACON: | 202 | case QID_BEACON: |
203 | /* | ||
204 | * Allow beacon tasklets to be scheduled for periodic | ||
205 | * beacon updates. | ||
206 | */ | ||
207 | tasklet_enable(&rt2x00dev->tbtt_tasklet); | ||
208 | tasklet_enable(&rt2x00dev->pretbtt_tasklet); | ||
209 | |||
203 | rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); | 210 | rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); |
204 | rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); | 211 | rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); |
205 | rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); | 212 | rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); |
206 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); | 213 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); |
207 | rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); | 214 | rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); |
215 | |||
216 | rt2800_register_read(rt2x00dev, INT_TIMER_EN, ®); | ||
217 | rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); | ||
218 | rt2800_register_write(rt2x00dev, INT_TIMER_EN, reg); | ||
208 | break; | 219 | break; |
209 | default: | 220 | default: |
210 | break; | 221 | break; |
@@ -250,6 +261,16 @@ static void rt2800pci_stop_queue(struct data_queue *queue) | |||
250 | rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); | 261 | rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); |
251 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); | 262 | rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); |
252 | rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); | 263 | rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); |
264 | |||
265 | rt2800_register_read(rt2x00dev, INT_TIMER_EN, ®); | ||
266 | rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); | ||
267 | rt2800_register_write(rt2x00dev, INT_TIMER_EN, reg); | ||
268 | |||
269 | /* | ||
270 | * Wait for tbtt tasklets to finish. | ||
271 | */ | ||
272 | tasklet_disable(&rt2x00dev->tbtt_tasklet); | ||
273 | tasklet_disable(&rt2x00dev->pretbtt_tasklet); | ||
253 | break; | 274 | break; |
254 | default: | 275 | default: |
255 | break; | 276 | break; |
@@ -397,9 +418,9 @@ static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) | |||
397 | static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | 418 | static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, |
398 | enum dev_state state) | 419 | enum dev_state state) |
399 | { | 420 | { |
400 | int mask = (state == STATE_RADIO_IRQ_ON) || | 421 | int mask = (state == STATE_RADIO_IRQ_ON); |
401 | (state == STATE_RADIO_IRQ_ON_ISR); | ||
402 | u32 reg; | 422 | u32 reg; |
423 | unsigned long flags; | ||
403 | 424 | ||
404 | /* | 425 | /* |
405 | * When interrupts are being enabled, the interrupt registers | 426 | * When interrupts are being enabled, the interrupt registers |
@@ -408,8 +429,17 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
408 | if (state == STATE_RADIO_IRQ_ON) { | 429 | if (state == STATE_RADIO_IRQ_ON) { |
409 | rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | 430 | rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, ®); |
410 | rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, reg); | 431 | rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, reg); |
432 | |||
433 | /* | ||
434 | * Enable tasklets. The beacon related tasklets are | ||
435 | * enabled when the beacon queue is started. | ||
436 | */ | ||
437 | tasklet_enable(&rt2x00dev->txstatus_tasklet); | ||
438 | tasklet_enable(&rt2x00dev->rxdone_tasklet); | ||
439 | tasklet_enable(&rt2x00dev->autowake_tasklet); | ||
411 | } | 440 | } |
412 | 441 | ||
442 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
413 | rt2800_register_read(rt2x00dev, INT_MASK_CSR, ®); | 443 | rt2800_register_read(rt2x00dev, INT_MASK_CSR, ®); |
414 | rt2x00_set_field32(®, INT_MASK_CSR_RXDELAYINT, 0); | 444 | rt2x00_set_field32(®, INT_MASK_CSR_RXDELAYINT, 0); |
415 | rt2x00_set_field32(®, INT_MASK_CSR_TXDELAYINT, 0); | 445 | rt2x00_set_field32(®, INT_MASK_CSR_TXDELAYINT, 0); |
@@ -430,6 +460,17 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
430 | rt2x00_set_field32(®, INT_MASK_CSR_RX_COHERENT, 0); | 460 | rt2x00_set_field32(®, INT_MASK_CSR_RX_COHERENT, 0); |
431 | rt2x00_set_field32(®, INT_MASK_CSR_TX_COHERENT, 0); | 461 | rt2x00_set_field32(®, INT_MASK_CSR_TX_COHERENT, 0); |
432 | rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); | 462 | rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); |
463 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
464 | |||
465 | if (state == STATE_RADIO_IRQ_OFF) { | ||
466 | /* | ||
467 | * Ensure that all tasklets are finished before | ||
468 | * disabling the interrupts. | ||
469 | */ | ||
470 | tasklet_disable(&rt2x00dev->txstatus_tasklet); | ||
471 | tasklet_disable(&rt2x00dev->rxdone_tasklet); | ||
472 | tasklet_disable(&rt2x00dev->autowake_tasklet); | ||
473 | } | ||
433 | } | 474 | } |
434 | 475 | ||
435 | static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) | 476 | static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) |
@@ -522,9 +563,7 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, | |||
522 | rt2800pci_set_state(rt2x00dev, STATE_SLEEP); | 563 | rt2800pci_set_state(rt2x00dev, STATE_SLEEP); |
523 | break; | 564 | break; |
524 | case STATE_RADIO_IRQ_ON: | 565 | case STATE_RADIO_IRQ_ON: |
525 | case STATE_RADIO_IRQ_ON_ISR: | ||
526 | case STATE_RADIO_IRQ_OFF: | 566 | case STATE_RADIO_IRQ_OFF: |
527 | case STATE_RADIO_IRQ_OFF_ISR: | ||
528 | rt2800pci_toggle_irq(rt2x00dev, state); | 567 | rt2800pci_toggle_irq(rt2x00dev, state); |
529 | break; | 568 | break; |
530 | case STATE_DEEP_SLEEP: | 569 | case STATE_DEEP_SLEEP: |
@@ -636,6 +675,12 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry, | |||
636 | */ | 675 | */ |
637 | rxdesc->flags |= RX_FLAG_IV_STRIPPED; | 676 | rxdesc->flags |= RX_FLAG_IV_STRIPPED; |
638 | 677 | ||
678 | /* | ||
679 | * The hardware has already checked the Michael Mic and has | ||
680 | * stripped it from the frame. Signal this to mac80211. | ||
681 | */ | ||
682 | rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; | ||
683 | |||
639 | if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) | 684 | if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) |
640 | rxdesc->flags |= RX_FLAG_DECRYPTED; | 685 | rxdesc->flags |= RX_FLAG_DECRYPTED; |
641 | else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) | 686 | else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) |
@@ -710,45 +755,60 @@ static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev) | |||
710 | } | 755 | } |
711 | } | 756 | } |
712 | 757 | ||
713 | static void rt2800pci_txstatus_tasklet(unsigned long data) | 758 | static void rt2800pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, |
714 | { | 759 | struct rt2x00_field32 irq_field) |
715 | rt2800pci_txdone((struct rt2x00_dev *)data); | ||
716 | } | ||
717 | |||
718 | static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance) | ||
719 | { | 760 | { |
720 | struct rt2x00_dev *rt2x00dev = dev_instance; | 761 | unsigned long flags; |
721 | u32 reg = rt2x00dev->irqvalue[0]; | 762 | u32 reg; |
722 | 763 | ||
723 | /* | 764 | /* |
724 | * 1 - Pre TBTT interrupt. | 765 | * Enable a single interrupt. The interrupt mask register |
766 | * access needs locking. | ||
725 | */ | 767 | */ |
726 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) | 768 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); |
727 | rt2x00lib_pretbtt(rt2x00dev); | 769 | rt2800_register_read(rt2x00dev, INT_MASK_CSR, ®); |
770 | rt2x00_set_field32(®, irq_field, 1); | ||
771 | rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
772 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
773 | } | ||
728 | 774 | ||
729 | /* | 775 | static void rt2800pci_txstatus_tasklet(unsigned long data) |
730 | * 2 - Beacondone interrupt. | 776 | { |
731 | */ | 777 | rt2800pci_txdone((struct rt2x00_dev *)data); |
732 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) | ||
733 | rt2x00lib_beacondone(rt2x00dev); | ||
734 | 778 | ||
735 | /* | 779 | /* |
736 | * 3 - Rx ring done interrupt. | 780 | * No need to enable the tx status interrupt here as we always |
781 | * leave it enabled to minimize the possibility of a tx status | ||
782 | * register overflow. See comment in interrupt handler. | ||
737 | */ | 783 | */ |
738 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) | 784 | } |
739 | rt2x00pci_rxdone(rt2x00dev); | ||
740 | 785 | ||
741 | /* | 786 | static void rt2800pci_pretbtt_tasklet(unsigned long data) |
742 | * 4 - Auto wakeup interrupt. | 787 | { |
743 | */ | 788 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; |
744 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) | 789 | rt2x00lib_pretbtt(rt2x00dev); |
745 | rt2800pci_wakeup(rt2x00dev); | 790 | rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); |
791 | } | ||
746 | 792 | ||
747 | /* Enable interrupts again. */ | 793 | static void rt2800pci_tbtt_tasklet(unsigned long data) |
748 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | 794 | { |
749 | STATE_RADIO_IRQ_ON_ISR); | 795 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; |
796 | rt2x00lib_beacondone(rt2x00dev); | ||
797 | rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); | ||
798 | } | ||
750 | 799 | ||
751 | return IRQ_HANDLED; | 800 | static void rt2800pci_rxdone_tasklet(unsigned long data) |
801 | { | ||
802 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
803 | rt2x00pci_rxdone(rt2x00dev); | ||
804 | rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); | ||
805 | } | ||
806 | |||
807 | static void rt2800pci_autowake_tasklet(unsigned long data) | ||
808 | { | ||
809 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
810 | rt2800pci_wakeup(rt2x00dev); | ||
811 | rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP); | ||
752 | } | 812 | } |
753 | 813 | ||
754 | static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) | 814 | static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) |
@@ -794,8 +854,8 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) | |||
794 | static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) | 854 | static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) |
795 | { | 855 | { |
796 | struct rt2x00_dev *rt2x00dev = dev_instance; | 856 | struct rt2x00_dev *rt2x00dev = dev_instance; |
797 | u32 reg; | 857 | u32 reg, mask; |
798 | irqreturn_t ret = IRQ_HANDLED; | 858 | unsigned long flags; |
799 | 859 | ||
800 | /* Read status and ACK all interrupts */ | 860 | /* Read status and ACK all interrupts */ |
801 | rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, ®); | 861 | rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, ®); |
@@ -807,38 +867,44 @@ static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) | |||
807 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | 867 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) |
808 | return IRQ_HANDLED; | 868 | return IRQ_HANDLED; |
809 | 869 | ||
810 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) | 870 | /* |
811 | rt2800pci_txstatus_interrupt(rt2x00dev); | 871 | * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits |
872 | * for interrupts and interrupt masks we can just use the value of | ||
873 | * INT_SOURCE_CSR to create the interrupt mask. | ||
874 | */ | ||
875 | mask = ~reg; | ||
812 | 876 | ||
813 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT) || | 877 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { |
814 | rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT) || | 878 | rt2800pci_txstatus_interrupt(rt2x00dev); |
815 | rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE) || | ||
816 | rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) { | ||
817 | /* | 879 | /* |
818 | * All other interrupts are handled in the interrupt thread. | 880 | * Never disable the TX_FIFO_STATUS interrupt. |
819 | * Store irqvalue for use in the interrupt thread. | ||
820 | */ | 881 | */ |
821 | rt2x00dev->irqvalue[0] = reg; | 882 | rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); |
883 | } | ||
822 | 884 | ||
823 | /* | 885 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) |
824 | * Disable interrupts, will be enabled again in the | 886 | tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); |
825 | * interrupt thread. | ||
826 | */ | ||
827 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | ||
828 | STATE_RADIO_IRQ_OFF_ISR); | ||
829 | 887 | ||
830 | /* | 888 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) |
831 | * Leave the TX_FIFO_STATUS interrupt enabled to not lose any | 889 | tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); |
832 | * tx status reports. | ||
833 | */ | ||
834 | rt2800_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
835 | rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); | ||
836 | rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
837 | 890 | ||
838 | ret = IRQ_WAKE_THREAD; | 891 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) |
839 | } | 892 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); |
840 | 893 | ||
841 | return ret; | 894 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) |
895 | tasklet_schedule(&rt2x00dev->autowake_tasklet); | ||
896 | |||
897 | /* | ||
898 | * Disable all interrupts for which a tasklet was scheduled right now, | ||
899 | * the tasklet will reenable the appropriate interrupts. | ||
900 | */ | ||
901 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
902 | rt2800_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
903 | reg &= mask; | ||
904 | rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
905 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
906 | |||
907 | return IRQ_HANDLED; | ||
842 | } | 908 | } |
843 | 909 | ||
844 | /* | 910 | /* |
@@ -953,8 +1019,11 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = { | |||
953 | 1019 | ||
954 | static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { | 1020 | static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { |
955 | .irq_handler = rt2800pci_interrupt, | 1021 | .irq_handler = rt2800pci_interrupt, |
956 | .irq_handler_thread = rt2800pci_interrupt_thread, | 1022 | .txstatus_tasklet = rt2800pci_txstatus_tasklet, |
957 | .txstatus_tasklet = rt2800pci_txstatus_tasklet, | 1023 | .pretbtt_tasklet = rt2800pci_pretbtt_tasklet, |
1024 | .tbtt_tasklet = rt2800pci_tbtt_tasklet, | ||
1025 | .rxdone_tasklet = rt2800pci_rxdone_tasklet, | ||
1026 | .autowake_tasklet = rt2800pci_autowake_tasklet, | ||
958 | .probe_hw = rt2800pci_probe_hw, | 1027 | .probe_hw = rt2800pci_probe_hw, |
959 | .get_firmware_name = rt2800pci_get_firmware_name, | 1028 | .get_firmware_name = rt2800pci_get_firmware_name, |
960 | .check_firmware = rt2800_check_firmware, | 1029 | .check_firmware = rt2800_check_firmware, |
@@ -974,6 +1043,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { | |||
974 | .write_tx_desc = rt2800pci_write_tx_desc, | 1043 | .write_tx_desc = rt2800pci_write_tx_desc, |
975 | .write_tx_data = rt2800_write_tx_data, | 1044 | .write_tx_data = rt2800_write_tx_data, |
976 | .write_beacon = rt2800_write_beacon, | 1045 | .write_beacon = rt2800_write_beacon, |
1046 | .clear_beacon = rt2800_clear_beacon, | ||
977 | .fill_rxdone = rt2800pci_fill_rxdone, | 1047 | .fill_rxdone = rt2800pci_fill_rxdone, |
978 | .config_shared_key = rt2800_config_shared_key, | 1048 | .config_shared_key = rt2800_config_shared_key, |
979 | .config_pairwise_key = rt2800_config_pairwise_key, | 1049 | .config_pairwise_key = rt2800_config_pairwise_key, |