diff options
author | Helmut Schaa <helmut.schaa@googlemail.com> | 2011-01-30 07:20:05 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-01-31 15:06:23 -0500 |
commit | bcf3cfd047d599cd0e6758c422bd7a4835c00d4a (patch) | |
tree | 4d65eb070e5b5e70d6494816c5b147017936b69f /drivers/net/wireless/rt2x00/rt2400pci.c | |
parent | 16222a0d06f5032d7e8bc7c65e8bf299e21f413a (diff) |
rt2x00: Convert rt2400pci 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>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2400pci.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2400pci.c | 154 |
1 files changed, 115 insertions, 39 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index cb28e1b2f1e2..b324917106e6 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c | |||
@@ -645,6 +645,11 @@ static void rt2400pci_start_queue(struct data_queue *queue) | |||
645 | rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); | 645 | rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); |
646 | break; | 646 | break; |
647 | case QID_BEACON: | 647 | case QID_BEACON: |
648 | /* | ||
649 | * Allow the tbtt tasklet to be scheduled. | ||
650 | */ | ||
651 | tasklet_enable(&rt2x00dev->tbtt_tasklet); | ||
652 | |||
648 | rt2x00pci_register_read(rt2x00dev, CSR14, ®); | 653 | rt2x00pci_register_read(rt2x00dev, CSR14, ®); |
649 | rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); | 654 | rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); |
650 | rt2x00_set_field32(®, CSR14_TBCN, 1); | 655 | rt2x00_set_field32(®, CSR14_TBCN, 1); |
@@ -706,6 +711,11 @@ static void rt2400pci_stop_queue(struct data_queue *queue) | |||
706 | rt2x00_set_field32(®, CSR14_TBCN, 0); | 711 | rt2x00_set_field32(®, CSR14_TBCN, 0); |
707 | rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); | 712 | rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); |
708 | rt2x00pci_register_write(rt2x00dev, CSR14, reg); | 713 | rt2x00pci_register_write(rt2x00dev, CSR14, reg); |
714 | |||
715 | /* | ||
716 | * Wait for possibly running tbtt tasklets. | ||
717 | */ | ||
718 | tasklet_disable(&rt2x00dev->tbtt_tasklet); | ||
709 | break; | 719 | break; |
710 | default: | 720 | default: |
711 | break; | 721 | break; |
@@ -964,6 +974,7 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
964 | int mask = (state == STATE_RADIO_IRQ_OFF) || | 974 | int mask = (state == STATE_RADIO_IRQ_OFF) || |
965 | (state == STATE_RADIO_IRQ_OFF_ISR); | 975 | (state == STATE_RADIO_IRQ_OFF_ISR); |
966 | u32 reg; | 976 | u32 reg; |
977 | unsigned long flags; | ||
967 | 978 | ||
968 | /* | 979 | /* |
969 | * When interrupts are being enabled, the interrupt registers | 980 | * When interrupts are being enabled, the interrupt registers |
@@ -972,12 +983,20 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
972 | if (state == STATE_RADIO_IRQ_ON) { | 983 | if (state == STATE_RADIO_IRQ_ON) { |
973 | rt2x00pci_register_read(rt2x00dev, CSR7, ®); | 984 | rt2x00pci_register_read(rt2x00dev, CSR7, ®); |
974 | rt2x00pci_register_write(rt2x00dev, CSR7, reg); | 985 | rt2x00pci_register_write(rt2x00dev, CSR7, reg); |
986 | |||
987 | /* | ||
988 | * Enable tasklets. | ||
989 | */ | ||
990 | tasklet_enable(&rt2x00dev->txstatus_tasklet); | ||
991 | tasklet_enable(&rt2x00dev->rxdone_tasklet); | ||
975 | } | 992 | } |
976 | 993 | ||
977 | /* | 994 | /* |
978 | * Only toggle the interrupts bits we are going to use. | 995 | * Only toggle the interrupts bits we are going to use. |
979 | * Non-checked interrupt bits are disabled by default. | 996 | * Non-checked interrupt bits are disabled by default. |
980 | */ | 997 | */ |
998 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
999 | |||
981 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); | 1000 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
982 | rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); | 1001 | rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); |
983 | rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); | 1002 | rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); |
@@ -985,6 +1004,17 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
985 | rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); | 1004 | rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); |
986 | rt2x00_set_field32(®, CSR8_RXDONE, mask); | 1005 | rt2x00_set_field32(®, CSR8_RXDONE, mask); |
987 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); | 1006 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); |
1007 | |||
1008 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
1009 | |||
1010 | if (state == STATE_RADIO_IRQ_OFF) { | ||
1011 | /* | ||
1012 | * Ensure that all tasklets are finished before | ||
1013 | * disabling the interrupts. | ||
1014 | */ | ||
1015 | tasklet_disable(&rt2x00dev->txstatus_tasklet); | ||
1016 | tasklet_disable(&rt2x00dev->rxdone_tasklet); | ||
1017 | } | ||
988 | } | 1018 | } |
989 | 1019 | ||
990 | static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev) | 1020 | static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev) |
@@ -1285,57 +1315,71 @@ static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, | |||
1285 | } | 1315 | } |
1286 | } | 1316 | } |
1287 | 1317 | ||
1288 | static irqreturn_t rt2400pci_interrupt_thread(int irq, void *dev_instance) | 1318 | static void rt2400pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, |
1319 | struct rt2x00_field32 irq_field) | ||
1289 | { | 1320 | { |
1290 | struct rt2x00_dev *rt2x00dev = dev_instance; | 1321 | unsigned long flags; |
1291 | u32 reg = rt2x00dev->irqvalue[0]; | 1322 | u32 reg; |
1292 | 1323 | ||
1293 | /* | 1324 | /* |
1294 | * Handle interrupts, walk through all bits | 1325 | * Enable a single interrupt. The interrupt mask register |
1295 | * and run the tasks, the bits are checked in order of | 1326 | * access needs locking. |
1296 | * priority. | ||
1297 | */ | 1327 | */ |
1328 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
1298 | 1329 | ||
1299 | /* | 1330 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
1300 | * 1 - Beacon timer expired interrupt. | 1331 | rt2x00_set_field32(®, irq_field, 0); |
1301 | */ | 1332 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); |
1302 | if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) | ||
1303 | rt2x00lib_beacondone(rt2x00dev); | ||
1304 | 1333 | ||
1305 | /* | 1334 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); |
1306 | * 2 - Rx ring done interrupt. | 1335 | } |
1307 | */ | ||
1308 | if (rt2x00_get_field32(reg, CSR7_RXDONE)) | ||
1309 | rt2x00pci_rxdone(rt2x00dev); | ||
1310 | 1336 | ||
1311 | /* | 1337 | static void rt2400pci_txstatus_tasklet(unsigned long data) |
1312 | * 3 - Atim ring transmit done interrupt. | 1338 | { |
1313 | */ | 1339 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; |
1314 | if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING)) | 1340 | u32 reg; |
1315 | rt2400pci_txdone(rt2x00dev, QID_ATIM); | 1341 | unsigned long flags; |
1316 | 1342 | ||
1317 | /* | 1343 | /* |
1318 | * 4 - Priority ring transmit done interrupt. | 1344 | * Handle all tx queues. |
1319 | */ | 1345 | */ |
1320 | if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING)) | 1346 | rt2400pci_txdone(rt2x00dev, QID_ATIM); |
1321 | rt2400pci_txdone(rt2x00dev, QID_AC_VO); | 1347 | rt2400pci_txdone(rt2x00dev, QID_AC_VO); |
1348 | rt2400pci_txdone(rt2x00dev, QID_AC_VI); | ||
1322 | 1349 | ||
1323 | /* | 1350 | /* |
1324 | * 5 - Tx ring transmit done interrupt. | 1351 | * Enable all TXDONE interrupts again. |
1325 | */ | 1352 | */ |
1326 | if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) | 1353 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); |
1327 | rt2400pci_txdone(rt2x00dev, QID_AC_VI); | ||
1328 | 1354 | ||
1329 | /* Enable interrupts again. */ | 1355 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
1330 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | 1356 | rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); |
1331 | STATE_RADIO_IRQ_ON_ISR); | 1357 | rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); |
1332 | return IRQ_HANDLED; | 1358 | rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); |
1359 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); | ||
1360 | |||
1361 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
1362 | } | ||
1363 | |||
1364 | static void rt2400pci_tbtt_tasklet(unsigned long data) | ||
1365 | { | ||
1366 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
1367 | rt2x00lib_beacondone(rt2x00dev); | ||
1368 | rt2400pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); | ||
1369 | } | ||
1370 | |||
1371 | static void rt2400pci_rxdone_tasklet(unsigned long data) | ||
1372 | { | ||
1373 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
1374 | rt2x00pci_rxdone(rt2x00dev); | ||
1375 | rt2400pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); | ||
1333 | } | 1376 | } |
1334 | 1377 | ||
1335 | static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) | 1378 | static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) |
1336 | { | 1379 | { |
1337 | struct rt2x00_dev *rt2x00dev = dev_instance; | 1380 | struct rt2x00_dev *rt2x00dev = dev_instance; |
1338 | u32 reg; | 1381 | u32 reg, mask; |
1382 | unsigned long flags; | ||
1339 | 1383 | ||
1340 | /* | 1384 | /* |
1341 | * Get the interrupt sources & saved to local variable. | 1385 | * Get the interrupt sources & saved to local variable. |
@@ -1350,14 +1394,44 @@ static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) | |||
1350 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | 1394 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) |
1351 | return IRQ_HANDLED; | 1395 | return IRQ_HANDLED; |
1352 | 1396 | ||
1353 | /* Store irqvalues for use in the interrupt thread. */ | 1397 | mask = reg; |
1354 | rt2x00dev->irqvalue[0] = reg; | 1398 | |
1399 | /* | ||
1400 | * Schedule tasklets for interrupt handling. | ||
1401 | */ | ||
1402 | if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) | ||
1403 | tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); | ||
1404 | |||
1405 | if (rt2x00_get_field32(reg, CSR7_RXDONE)) | ||
1406 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
1407 | |||
1408 | if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || | ||
1409 | rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || | ||
1410 | rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { | ||
1411 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
1412 | /* | ||
1413 | * Mask out all txdone interrupts. | ||
1414 | */ | ||
1415 | rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); | ||
1416 | rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); | ||
1417 | rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); | ||
1418 | } | ||
1355 | 1419 | ||
1356 | /* Disable interrupts, will be enabled again in the interrupt thread. */ | 1420 | /* |
1357 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | 1421 | * Disable all interrupts for which a tasklet was scheduled right now, |
1358 | STATE_RADIO_IRQ_OFF_ISR); | 1422 | * the tasklet will reenable the appropriate interrupts. |
1423 | */ | ||
1424 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
1359 | 1425 | ||
1360 | return IRQ_WAKE_THREAD; | 1426 | rt2x00pci_register_read(rt2x00dev, CSR8, ®); |
1427 | reg |= mask; | ||
1428 | rt2x00pci_register_write(rt2x00dev, CSR8, reg); | ||
1429 | |||
1430 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
1431 | |||
1432 | |||
1433 | |||
1434 | return IRQ_HANDLED; | ||
1361 | } | 1435 | } |
1362 | 1436 | ||
1363 | /* | 1437 | /* |
@@ -1651,7 +1725,9 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = { | |||
1651 | 1725 | ||
1652 | static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { | 1726 | static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { |
1653 | .irq_handler = rt2400pci_interrupt, | 1727 | .irq_handler = rt2400pci_interrupt, |
1654 | .irq_handler_thread = rt2400pci_interrupt_thread, | 1728 | .txstatus_tasklet = rt2400pci_txstatus_tasklet, |
1729 | .tbtt_tasklet = rt2400pci_tbtt_tasklet, | ||
1730 | .rxdone_tasklet = rt2400pci_rxdone_tasklet, | ||
1655 | .probe_hw = rt2400pci_probe_hw, | 1731 | .probe_hw = rt2400pci_probe_hw, |
1656 | .initialize = rt2x00pci_initialize, | 1732 | .initialize = rt2x00pci_initialize, |
1657 | .uninitialize = rt2x00pci_uninitialize, | 1733 | .uninitialize = rt2x00pci_uninitialize, |