diff options
author | Helmut Schaa <helmut.schaa@googlemail.com> | 2011-01-30 07:19:08 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-01-31 15:06:23 -0500 |
commit | 5846a550b5838ea7fe8e280caff159a5ddb5c7e1 (patch) | |
tree | 5c4e2210b56c4252c0e4d1b4882efe17aec8ef18 /drivers/net/wireless/rt2x00/rt61pci.c | |
parent | a9d61e9e779579c66c0d4c8af203d51dbca1473c (diff) |
rt2x00: Convert rt61pci 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/rt61pci.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt61pci.c | 175 |
1 files changed, 130 insertions, 45 deletions
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index f14cc452eb0c..351055d4b89b 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c | |||
@@ -1142,6 +1142,11 @@ static void rt61pci_start_queue(struct data_queue *queue) | |||
1142 | rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); | 1142 | rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); |
1143 | break; | 1143 | break; |
1144 | case QID_BEACON: | 1144 | case QID_BEACON: |
1145 | /* | ||
1146 | * Allow the tbtt tasklet to be scheduled. | ||
1147 | */ | ||
1148 | tasklet_enable(&rt2x00dev->tbtt_tasklet); | ||
1149 | |||
1145 | rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); | 1150 | rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); |
1146 | rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); | 1151 | rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); |
1147 | rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); | 1152 | rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); |
@@ -1221,6 +1226,11 @@ static void rt61pci_stop_queue(struct data_queue *queue) | |||
1221 | rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); | 1226 | rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); |
1222 | rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); | 1227 | rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); |
1223 | rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); | 1228 | rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); |
1229 | |||
1230 | /* | ||
1231 | * Wait for possibly running tbtt tasklets. | ||
1232 | */ | ||
1233 | tasklet_disable(&rt2x00dev->tbtt_tasklet); | ||
1224 | break; | 1234 | break; |
1225 | default: | 1235 | default: |
1226 | break; | 1236 | break; |
@@ -1710,6 +1720,7 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
1710 | int mask = (state == STATE_RADIO_IRQ_OFF) || | 1720 | int mask = (state == STATE_RADIO_IRQ_OFF) || |
1711 | (state == STATE_RADIO_IRQ_OFF_ISR); | 1721 | (state == STATE_RADIO_IRQ_OFF_ISR); |
1712 | u32 reg; | 1722 | u32 reg; |
1723 | unsigned long flags; | ||
1713 | 1724 | ||
1714 | /* | 1725 | /* |
1715 | * When interrupts are being enabled, the interrupt registers | 1726 | * When interrupts are being enabled, the interrupt registers |
@@ -1721,12 +1732,21 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
1721 | 1732 | ||
1722 | rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); | 1733 | rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); |
1723 | rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); | 1734 | rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); |
1735 | |||
1736 | /* | ||
1737 | * Enable tasklets. | ||
1738 | */ | ||
1739 | tasklet_enable(&rt2x00dev->txstatus_tasklet); | ||
1740 | tasklet_enable(&rt2x00dev->rxdone_tasklet); | ||
1741 | tasklet_enable(&rt2x00dev->autowake_tasklet); | ||
1724 | } | 1742 | } |
1725 | 1743 | ||
1726 | /* | 1744 | /* |
1727 | * Only toggle the interrupts bits we are going to use. | 1745 | * Only toggle the interrupts bits we are going to use. |
1728 | * Non-checked interrupt bits are disabled by default. | 1746 | * Non-checked interrupt bits are disabled by default. |
1729 | */ | 1747 | */ |
1748 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
1749 | |||
1730 | rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); | 1750 | rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); |
1731 | rt2x00_set_field32(®, INT_MASK_CSR_TXDONE, mask); | 1751 | rt2x00_set_field32(®, INT_MASK_CSR_TXDONE, mask); |
1732 | rt2x00_set_field32(®, INT_MASK_CSR_RXDONE, mask); | 1752 | rt2x00_set_field32(®, INT_MASK_CSR_RXDONE, mask); |
@@ -1746,6 +1766,17 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, | |||
1746 | rt2x00_set_field32(®, MCU_INT_MASK_CSR_7, mask); | 1766 | rt2x00_set_field32(®, MCU_INT_MASK_CSR_7, mask); |
1747 | rt2x00_set_field32(®, MCU_INT_MASK_CSR_TWAKEUP, mask); | 1767 | rt2x00_set_field32(®, MCU_INT_MASK_CSR_TWAKEUP, mask); |
1748 | rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); | 1768 | rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); |
1769 | |||
1770 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
1771 | |||
1772 | if (state == STATE_RADIO_IRQ_OFF) { | ||
1773 | /* | ||
1774 | * Ensure that all tasklets are finished. | ||
1775 | */ | ||
1776 | tasklet_disable(&rt2x00dev->txstatus_tasklet); | ||
1777 | tasklet_disable(&rt2x00dev->rxdone_tasklet); | ||
1778 | tasklet_disable(&rt2x00dev->autowake_tasklet); | ||
1779 | } | ||
1749 | } | 1780 | } |
1750 | 1781 | ||
1751 | static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev) | 1782 | static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev) |
@@ -2223,61 +2254,80 @@ static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev) | |||
2223 | rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); | 2254 | rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); |
2224 | } | 2255 | } |
2225 | 2256 | ||
2226 | static irqreturn_t rt61pci_interrupt_thread(int irq, void *dev_instance) | 2257 | static void rt61pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, |
2258 | struct rt2x00_field32 irq_field) | ||
2227 | { | 2259 | { |
2228 | struct rt2x00_dev *rt2x00dev = dev_instance; | 2260 | unsigned long flags; |
2229 | u32 reg = rt2x00dev->irqvalue[0]; | 2261 | u32 reg; |
2230 | u32 reg_mcu = rt2x00dev->irqvalue[1]; | ||
2231 | 2262 | ||
2232 | /* | 2263 | /* |
2233 | * Handle interrupts, walk through all bits | 2264 | * Enable a single interrupt. The interrupt mask register |
2234 | * and run the tasks, the bits are checked in order of | 2265 | * access needs locking. |
2235 | * priority. | ||
2236 | */ | 2266 | */ |
2267 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
2237 | 2268 | ||
2238 | /* | 2269 | rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); |
2239 | * 1 - Rx ring done interrupt. | 2270 | rt2x00_set_field32(®, irq_field, 0); |
2240 | */ | 2271 | rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); |
2241 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE)) | ||
2242 | rt2x00pci_rxdone(rt2x00dev); | ||
2243 | 2272 | ||
2244 | /* | 2273 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); |
2245 | * 2 - Tx ring done interrupt. | 2274 | } |
2246 | */ | ||
2247 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) | ||
2248 | rt61pci_txdone(rt2x00dev); | ||
2249 | 2275 | ||
2250 | /* | 2276 | static void rt61pci_enable_mcu_interrupt(struct rt2x00_dev *rt2x00dev, |
2251 | * 3 - Handle MCU command done. | 2277 | struct rt2x00_field32 irq_field) |
2252 | */ | 2278 | { |
2253 | if (reg_mcu) | 2279 | unsigned long flags; |
2254 | rt2x00pci_register_write(rt2x00dev, | 2280 | u32 reg; |
2255 | M2H_CMD_DONE_CSR, 0xffffffff); | ||
2256 | 2281 | ||
2257 | /* | 2282 | /* |
2258 | * 4 - MCU Autowakeup interrupt. | 2283 | * Enable a single MCU interrupt. The interrupt mask register |
2284 | * access needs locking. | ||
2259 | */ | 2285 | */ |
2260 | if (rt2x00_get_field32(reg_mcu, MCU_INT_SOURCE_CSR_TWAKEUP)) | 2286 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); |
2261 | rt61pci_wakeup(rt2x00dev); | ||
2262 | 2287 | ||
2263 | /* | 2288 | rt2x00pci_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); |
2264 | * 5 - Beacon done interrupt. | 2289 | rt2x00_set_field32(®, irq_field, 0); |
2265 | */ | 2290 | rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); |
2266 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE)) | ||
2267 | rt2x00lib_beacondone(rt2x00dev); | ||
2268 | 2291 | ||
2269 | /* Enable interrupts again. */ | 2292 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); |
2270 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | ||
2271 | STATE_RADIO_IRQ_ON_ISR); | ||
2272 | return IRQ_HANDLED; | ||
2273 | } | 2293 | } |
2274 | 2294 | ||
2295 | static void rt61pci_txstatus_tasklet(unsigned long data) | ||
2296 | { | ||
2297 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
2298 | rt61pci_txdone(rt2x00dev); | ||
2299 | rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TXDONE); | ||
2300 | } | ||
2301 | |||
2302 | static void rt61pci_tbtt_tasklet(unsigned long data) | ||
2303 | { | ||
2304 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
2305 | rt2x00lib_beacondone(rt2x00dev); | ||
2306 | rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_BEACON_DONE); | ||
2307 | } | ||
2308 | |||
2309 | static void rt61pci_rxdone_tasklet(unsigned long data) | ||
2310 | { | ||
2311 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
2312 | rt2x00pci_rxdone(rt2x00dev); | ||
2313 | rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RXDONE); | ||
2314 | } | ||
2315 | |||
2316 | static void rt61pci_autowake_tasklet(unsigned long data) | ||
2317 | { | ||
2318 | struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; | ||
2319 | rt61pci_wakeup(rt2x00dev); | ||
2320 | rt2x00pci_register_write(rt2x00dev, | ||
2321 | M2H_CMD_DONE_CSR, 0xffffffff); | ||
2322 | rt61pci_enable_mcu_interrupt(rt2x00dev, MCU_INT_MASK_CSR_TWAKEUP); | ||
2323 | } | ||
2275 | 2324 | ||
2276 | static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) | 2325 | static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) |
2277 | { | 2326 | { |
2278 | struct rt2x00_dev *rt2x00dev = dev_instance; | 2327 | struct rt2x00_dev *rt2x00dev = dev_instance; |
2279 | u32 reg_mcu; | 2328 | u32 reg_mcu, mask_mcu; |
2280 | u32 reg; | 2329 | u32 reg, mask; |
2330 | unsigned long flags; | ||
2281 | 2331 | ||
2282 | /* | 2332 | /* |
2283 | * Get the interrupt sources & saved to local variable. | 2333 | * Get the interrupt sources & saved to local variable. |
@@ -2295,14 +2345,46 @@ static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) | |||
2295 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) | 2345 | if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) |
2296 | return IRQ_HANDLED; | 2346 | return IRQ_HANDLED; |
2297 | 2347 | ||
2298 | /* Store irqvalues for use in the interrupt thread. */ | 2348 | /* |
2299 | rt2x00dev->irqvalue[0] = reg; | 2349 | * Schedule tasklets for interrupt handling. |
2300 | rt2x00dev->irqvalue[1] = reg_mcu; | 2350 | */ |
2351 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE)) | ||
2352 | tasklet_schedule(&rt2x00dev->rxdone_tasklet); | ||
2353 | |||
2354 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) | ||
2355 | tasklet_schedule(&rt2x00dev->txstatus_tasklet); | ||
2356 | |||
2357 | if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE)) | ||
2358 | tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); | ||
2359 | |||
2360 | if (rt2x00_get_field32(reg_mcu, MCU_INT_SOURCE_CSR_TWAKEUP)) | ||
2361 | tasklet_schedule(&rt2x00dev->autowake_tasklet); | ||
2362 | |||
2363 | /* | ||
2364 | * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits | ||
2365 | * for interrupts and interrupt masks we can just use the value of | ||
2366 | * INT_SOURCE_CSR to create the interrupt mask. | ||
2367 | */ | ||
2368 | mask = reg; | ||
2369 | mask_mcu = reg_mcu; | ||
2370 | |||
2371 | /* | ||
2372 | * Disable all interrupts for which a tasklet was scheduled right now, | ||
2373 | * the tasklet will reenable the appropriate interrupts. | ||
2374 | */ | ||
2375 | spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); | ||
2376 | |||
2377 | rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); | ||
2378 | reg |= mask; | ||
2379 | rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); | ||
2301 | 2380 | ||
2302 | /* Disable interrupts, will be enabled again in the interrupt thread. */ | 2381 | rt2x00pci_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); |
2303 | rt2x00dev->ops->lib->set_device_state(rt2x00dev, | 2382 | reg |= mask_mcu; |
2304 | STATE_RADIO_IRQ_OFF_ISR); | 2383 | rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); |
2305 | return IRQ_WAKE_THREAD; | 2384 | |
2385 | spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); | ||
2386 | |||
2387 | return IRQ_HANDLED; | ||
2306 | } | 2388 | } |
2307 | 2389 | ||
2308 | /* | 2390 | /* |
@@ -2896,7 +2978,10 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = { | |||
2896 | 2978 | ||
2897 | static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { | 2979 | static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { |
2898 | .irq_handler = rt61pci_interrupt, | 2980 | .irq_handler = rt61pci_interrupt, |
2899 | .irq_handler_thread = rt61pci_interrupt_thread, | 2981 | .txstatus_tasklet = rt61pci_txstatus_tasklet, |
2982 | .tbtt_tasklet = rt61pci_tbtt_tasklet, | ||
2983 | .rxdone_tasklet = rt61pci_rxdone_tasklet, | ||
2984 | .autowake_tasklet = rt61pci_autowake_tasklet, | ||
2900 | .probe_hw = rt61pci_probe_hw, | 2985 | .probe_hw = rt61pci_probe_hw, |
2901 | .get_firmware_name = rt61pci_get_firmware_name, | 2986 | .get_firmware_name = rt61pci_get_firmware_name, |
2902 | .check_firmware = rt61pci_check_firmware, | 2987 | .check_firmware = rt61pci_check_firmware, |