diff options
author | Helmut Schaa <helmut.schaa@googlemail.com> | 2011-01-30 07:19:37 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-01-31 15:06:23 -0500 |
commit | 16222a0d06f5032d7e8bc7c65e8bf299e21f413a (patch) | |
tree | c3d7592fe6ddc199d4885e18bd60e0db5a1a3fc6 | |
parent | 5846a550b5838ea7fe8e280caff159a5ddb5c7e1 (diff) |
rt2x00: Convert rt2500pci interrupt handling to use tasklets
Fix interrupt processing on slow machines by using individual tasklets
for each different device interrupt. This ensures that while a RX or TX
status tasklet is scheduled only the according device interrupt is
masked and other interrupts such as TBTT can still be processed.
Also, this allows us to use tasklet_hi_schedule for TBTT processing
which is required to not send out beacons with a wrong DTIM count (due
to delayed periodic beacon updates). Furthermore, this improves the
latency between the TBTT and sending out buffered multi- and broadcast
traffic.
As a nice bonus, the interrupt handling overhead should be much lower.
Compile-tested only.
Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>
Acked-by: Gertjan van Wingerde <gwingerde@gmail.com>
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2500pci.c | 150 |
1 files changed, 111 insertions, 39 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 5225ae1899fe..7daa483c3742 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c | |||
@@ -735,6 +735,11 @@ static void rt2500pci_start_queue(struct data_queue *queue) | |||
735 | rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); | 735 | rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); |
736 | break; | 736 | break; |
737 | case QID_BEACON: | 737 | case QID_BEACON: |
738 | /* | ||
739 | * Allow the tbtt tasklet to be scheduled. | ||
740 | */ | ||
741 | tasklet_enable(&rt2x00dev->tbtt_tasklet); | ||
742 | |||
738 | rt2x00pci_register_read(rt2x00dev, CSR14, ®); | 743 | rt2x00pci_register_read(rt2x00dev, CSR14, ®); |
739 | rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); | 744 | rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); |
740 | rt2x00_set_field32(®, CSR14_TBCN, 1); | 745 | rt2x00_set_field32(®, CSR14_TBCN, 1); |
@@ -796,6 +801,11 @@ static void rt2500pci_stop_queue(struct data_queue *queue) | |||
796 | rt2x00_set_field32(®, CSR14_TBCN, 0); | 801 | rt2x00_set_field32(®, CSR14_TBCN, 0); |
797 | rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); | 802 | rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); |
798 | rt2x00pci_register_write(rt2x00dev, CSR14, reg); | 803 | rt2x00pci_register_write(rt2x00dev, CSR14, reg); |
804 | |||
805 | /* | ||
806 | * Wait for possibly running tbtt tasklets. | ||
807 | */ | ||
808 | tasklet_disable(&rt2x00dev->tbtt_tasklet); | ||
799 | break; | 809 | break; |
800 | default: | 810 | default: |
801 | break; | 811 | break; |
@@ -1119,6 +1129,7 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
1119 | int mask = (state == STATE_RADIO_IRQ_OFF) || | 1129 | int mask = (state == STATE_RADIO_IRQ_OFF) || |
1120 | (state == STATE_RADIO_IRQ_OFF_ISR); | 1130 | (state == STATE_RADIO_IRQ_OFF_ISR); |
1121 | u32 reg; | 1131 | u32 reg; |
1132 | unsigned long flags; | ||
1122 | 1133 | ||
1123 | /* | 1134 | /* |
1124 | * When interrupts are being enabled, the interrupt registers | 1135 | * When interrupts are being enabled, the interrupt registers |
@@ -1127,12 +1138,20 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
1127 | if (state == STATE_RADIO_IRQ_ON) { | 1138 | if (state == STATE_RADIO_IRQ_ON) { |
1128 | rt2x00pci_register_read(rt2x00dev, CSR7, ®); | 1139 | rt2x00pci_register_read(rt2x00dev, CSR7, ®); |
1129 | rt2x00pci_register_write(rt2x00dev, CSR7, reg); | 1140 | rt2x00pci_register_write(rt2x00dev, CSR7, reg); |
1141 | |||
1142 | /* | ||
1143 | * Enable tasklets. | ||
1144 | */ | ||
1145 | tasklet_enable(&rt2x00dev->txstatus_tasklet); | ||
1146 | tasklet_enable(&rt2x00dev->rxdone_tasklet); | ||
1130 | } | 1147 | } |
1131 | 1148 | ||
1132 | /* | 1149 | /* |
1133 | * Only toggle the interrupts bits we are going to use. | 1150 | * Only toggle the interrupts bits we are going to use. |
1134 | * Non-checked interrupt bits are disabled by default. | 1151 | * Non-checked interrupt bits are disabled by default. |
1135 | */ | 1152 | */ |
1153 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
1154 | |||
1136 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); | 1155 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
1137 | rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); | 1156 | rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); |
1138 | rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); | 1157 | rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); |
@@ -1140,6 +1159,16 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
1140 | rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); | 1159 | rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); |
1141 | rt2x00_set_field32(®, CSR8_RXDONE, mask); | 1160 | rt2x00_set_field32(®, CSR8_RXDONE, mask); |
1142 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); | 1161 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); |
1162 | |||
1163 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
1164 | |||
1165 | if (state == STATE_RADIO_IRQ_OFF) { | ||
1166 | /* | ||
1167 | * Ensure that all tasklets are finished. | ||
1168 | */ | ||
1169 | tasklet_disable(&rt2x00dev->txstatus_tasklet); | ||
1170 | tasklet_disable(&rt2x00dev->rxdone_tasklet); | ||
1171 | } | ||
1143 | } | 1172 | } |
1144 | 1173 | ||
1145 | static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev) | 1174 | static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev) |
@@ -1418,58 +1447,71 @@ static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, | |||
1418 | } | 1447 | } |
1419 | } | 1448 | } |
1420 | 1449 | ||
1421 | static irqreturn_t rt2500pci_interrupt_thread(int irq, void *dev_instance) | 1450 | static void rt2500pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, |
1451 | struct rt2x00_field32 irq_field) | ||
1422 | { | 1452 | { |
1423 | struct rt2x00_dev *rt2x00dev = dev_instance; | 1453 | unsigned long flags; |
1424 | u32 reg = rt2x00dev->irqvalue[0]; | 1454 | u32 reg; |
1425 | 1455 | ||
1426 | /* | 1456 | /* |
1427 | * Handle interrupts, walk through all bits | 1457 | * Enable a single interrupt. The interrupt mask register |
1428 | * and run the tasks, the bits are checked in order of | 1458 | * access needs locking. |
1429 | * priority. | ||
1430 | */ | 1459 | */ |
1460 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
1431 | 1461 | ||
1432 | /* | 1462 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
1433 | * 1 - Beacon timer expired interrupt. | 1463 | rt2x00_set_field32(®, irq_field, 0); |
1434 | */ | 1464 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); |
1435 | if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) | ||
1436 | rt2x00lib_beacondone(rt2x00dev); | ||
1437 | 1465 | ||
1438 | /* | 1466 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); |
1439 | * 2 - Rx ring done interrupt. | 1467 | } |
1440 | */ | ||
1441 | if (rt2x00_get_field32(reg, CSR7_RXDONE)) | ||
1442 | rt2x00pci_rxdone(rt2x00dev); | ||
1443 | 1468 | ||
1444 | /* | 1469 | static void rt2500pci_txstatus_tasklet(unsigned long data) |
1445 | * 3 - Atim ring transmit done interrupt. | 1470 | { |
1446 | */ | 1471 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; |
1447 | if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING)) | 1472 | u32 reg; |
1448 | rt2500pci_txdone(rt2x00dev, QID_ATIM); | 1473 | unsigned long flags; |
1449 | 1474 | ||
1450 | /* | 1475 | /* |
1451 | * 4 - Priority ring transmit done interrupt. | 1476 | * Handle all tx queues. |
1452 | */ | 1477 | */ |
1453 | if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING)) | 1478 | rt2500pci_txdone(rt2x00dev, QID_ATIM); |
1454 | rt2500pci_txdone(rt2x00dev, QID_AC_VO); | 1479 | rt2500pci_txdone(rt2x00dev, QID_AC_VO); |
1480 | rt2500pci_txdone(rt2x00dev, QID_AC_VI); | ||
1455 | 1481 | ||
1456 | /* | 1482 | /* |
1457 | * 5 - Tx ring transmit done interrupt. | 1483 | * Enable all TXDONE interrupts again. |
1458 | */ | 1484 | */ |
1459 | if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) | 1485 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); |
1460 | rt2500pci_txdone(rt2x00dev, QID_AC_VI); | ||
1461 | 1486 | ||
1462 | /* Enable interrupts again. */ | 1487 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
1463 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | 1488 | rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); |
1464 | STATE_RADIO_IRQ_ON_ISR); | 1489 | rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); |
1490 | rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); | ||
1491 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); | ||
1465 | 1492 | ||
1466 | return IRQ_HANDLED; | 1493 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); |
1494 | } | ||
1495 | |||
1496 | static void rt2500pci_tbtt_tasklet(unsigned long data) | ||
1497 | { | ||
1498 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
1499 | rt2x00lib_beacondone(rt2x00dev); | ||
1500 | rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); | ||
1501 | } | ||
1502 | |||
1503 | static void rt2500pci_rxdone_tasklet(unsigned long data) | ||
1504 | { | ||
1505 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
1506 | rt2x00pci_rxdone(rt2x00dev); | ||
1507 | rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); | ||
1467 | } | 1508 | } |
1468 | 1509 | ||
1469 | static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) | 1510 | static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) |
1470 | { | 1511 | { |
1471 | struct rt2x00_dev *rt2x00dev = dev_instance; | 1512 | struct rt2x00_dev *rt2x00dev = dev_instance; |
1472 | u32 reg; | 1513 | u32 reg, mask; |
1514 | unsigned long flags; | ||
1473 | 1515 | ||
1474 | /* | 1516 | /* |
1475 | * Get the interrupt sources & saved to local variable. | 1517 | * Get the interrupt sources & saved to local variable. |
@@ -1484,14 +1526,42 @@ static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) | |||
1484 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | 1526 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) |
1485 | return IRQ_HANDLED; | 1527 | return IRQ_HANDLED; |
1486 | 1528 | ||
1487 | /* Store irqvalues for use in the interrupt thread. */ | 1529 | mask = reg; |
1488 | rt2x00dev->irqvalue[0] = reg; | 1530 | |
1531 | /* | ||
1532 | * Schedule tasklets for interrupt handling. | ||
1533 | */ | ||
1534 | if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) | ||
1535 | tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); | ||
1536 | |||
1537 | if (rt2x00_get_field32(reg, CSR7_RXDONE)) | ||
1538 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
1539 | |||
1540 | if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || | ||
1541 | rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || | ||
1542 | rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { | ||
1543 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
1544 | /* | ||
1545 | * Mask out all txdone interrupts. | ||
1546 | */ | ||
1547 | rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); | ||
1548 | rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); | ||
1549 | rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); | ||
1550 | } | ||
1551 | |||
1552 | /* | ||
1553 | * Disable all interrupts for which a tasklet was scheduled right now, | ||
1554 | * the tasklet will reenable the appropriate interrupts. | ||
1555 | */ | ||
1556 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
1489 | 1557 | ||
1490 | /* Disable interrupts, will be enabled again in the interrupt thread. */ | 1558 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
1491 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | 1559 | reg |= mask; |
1492 | STATE_RADIO_IRQ_OFF_ISR); | 1560 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); |
1561 | |||
1562 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
1493 | 1563 | ||
1494 | return IRQ_WAKE_THREAD; | 1564 | return IRQ_HANDLED; |
1495 | } | 1565 | } |
1496 | 1566 | ||
1497 | /* | 1567 | /* |
@@ -1948,7 +2018,9 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = { | |||
1948 | 2018 | ||
1949 | static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { | 2019 | static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { |
1950 | .irq_handler = rt2500pci_interrupt, | 2020 | .irq_handler = rt2500pci_interrupt, |
1951 | .irq_handler_thread = rt2500pci_interrupt_thread, | 2021 | .txstatus_tasklet = rt2500pci_txstatus_tasklet, |
2022 | .tbtt_tasklet = rt2500pci_tbtt_tasklet, | ||
2023 | .rxdone_tasklet = rt2500pci_rxdone_tasklet, | ||
1952 | .probe_hw = rt2500pci_probe_hw, | 2024 | .probe_hw = rt2500pci_probe_hw, |
1953 | .initialize = rt2x00pci_initialize, | 2025 | .initialize = rt2x00pci_initialize, |
1954 | .uninitialize = rt2x00pci_uninitialize, | 2026 | .uninitialize = rt2x00pci_uninitialize, |