rt2x00pci_register_write(rt2x00dev, RXCSR0, reg);
break;
case QID_BEACON:
+ /*
+ * Allow the tbtt tasklet to be scheduled.
+ */
+ tasklet_enable(&rt2x00dev->tbtt_tasklet);
+
rt2x00pci_register_read(rt2x00dev, CSR14, ®);
rt2x00_set_field32(®, CSR14_TSF_COUNT, 1);
rt2x00_set_field32(®, CSR14_TBCN, 1);
rt2x00_set_field32(®, CSR14_TBCN, 0);
rt2x00_set_field32(®, CSR14_BEACON_GEN, 0);
rt2x00pci_register_write(rt2x00dev, CSR14, reg);
+
+ /*
+ * Wait for possibly running tbtt tasklets.
+ */
+ tasklet_disable(&rt2x00dev->tbtt_tasklet);
break;
default:
break;
int mask = (state == STATE_RADIO_IRQ_OFF) ||
(state == STATE_RADIO_IRQ_OFF_ISR);
u32 reg;
+ unsigned long flags;
/*
* When interrupts are being enabled, the interrupt registers
if (state == STATE_RADIO_IRQ_ON) {
rt2x00pci_register_read(rt2x00dev, CSR7, ®);
rt2x00pci_register_write(rt2x00dev, CSR7, reg);
+
+ /*
+ * Enable tasklets.
+ */
+ tasklet_enable(&rt2x00dev->txstatus_tasklet);
+ tasklet_enable(&rt2x00dev->rxdone_tasklet);
}
/*
* Only toggle the interrupts bits we are going to use.
* Non-checked interrupt bits are disabled by default.
*/
+ spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
+
rt2x00pci_register_read(rt2x00dev, CSR8, ®);
rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask);
rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask);
rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask);
rt2x00_set_field32(®, CSR8_RXDONE, mask);
rt2x00pci_register_write(rt2x00dev, CSR8, reg);
+
+ spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
+
+ if (state == STATE_RADIO_IRQ_OFF) {
+ /*
+ * Ensure that all tasklets are finished.
+ */
+ tasklet_disable(&rt2x00dev->txstatus_tasklet);
+ tasklet_disable(&rt2x00dev->rxdone_tasklet);
+ }
}
static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev)
}
}
-static irqreturn_t rt2500pci_interrupt_thread(int irq, void *dev_instance)
+static void rt2500pci_enable_interrupt(struct rt2x00_dev *rt2x00dev,
+ struct rt2x00_field32 irq_field)
{
- struct rt2x00_dev *rt2x00dev = dev_instance;
- u32 reg = rt2x00dev->irqvalue[0];
+ unsigned long flags;
+ u32 reg;
/*
- * Handle interrupts, walk through all bits
- * and run the tasks, the bits are checked in order of
- * priority.
+ * Enable a single interrupt. The interrupt mask register
+ * access needs locking.
*/
+ spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
- /*
- * 1 - Beacon timer expired interrupt.
- */
- if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE))
- rt2x00lib_beacondone(rt2x00dev);
+ rt2x00pci_register_read(rt2x00dev, CSR8, ®);
+ rt2x00_set_field32(®, irq_field, 0);
+ rt2x00pci_register_write(rt2x00dev, CSR8, reg);
- /*
- * 2 - Rx ring done interrupt.
- */
- if (rt2x00_get_field32(reg, CSR7_RXDONE))
- rt2x00pci_rxdone(rt2x00dev);
+ spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
+}
- /*
- * 3 - Atim ring transmit done interrupt.
- */
- if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING))
- rt2500pci_txdone(rt2x00dev, QID_ATIM);
+static void rt2500pci_txstatus_tasklet(unsigned long data)
+{
+ struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+ u32 reg;
+ unsigned long flags;
/*
- * 4 - Priority ring transmit done interrupt.
+ * Handle all tx queues.
*/
- if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING))
- rt2500pci_txdone(rt2x00dev, QID_AC_VO);
+ rt2500pci_txdone(rt2x00dev, QID_ATIM);
+ rt2500pci_txdone(rt2x00dev, QID_AC_VO);
+ rt2500pci_txdone(rt2x00dev, QID_AC_VI);
/*
- * 5 - Tx ring transmit done interrupt.
+ * Enable all TXDONE interrupts again.
*/
- if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING))
- rt2500pci_txdone(rt2x00dev, QID_AC_VI);
+ spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
- /* Enable interrupts again. */
- rt2x00dev->ops->lib->set_device_state(rt2x00dev,
- STATE_RADIO_IRQ_ON_ISR);
+ rt2x00pci_register_read(rt2x00dev, CSR8, ®);
+ rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0);
+ rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0);
+ rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0);
+ rt2x00pci_register_write(rt2x00dev, CSR8, reg);
- return IRQ_HANDLED;
+ spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
+}
+
+static void rt2500pci_tbtt_tasklet(unsigned long data)
+{
+ struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+ rt2x00lib_beacondone(rt2x00dev);
+ rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE);
+}
+
+static void rt2500pci_rxdone_tasklet(unsigned long data)
+{
+ struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+ rt2x00pci_rxdone(rt2x00dev);
+ rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE);
}
static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance)
{
struct rt2x00_dev *rt2x00dev = dev_instance;
- u32 reg;
+ u32 reg, mask;
+ unsigned long flags;
/*
* Get the interrupt sources & saved to local variable.
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;
- /* Store irqvalues for use in the interrupt thread. */
- rt2x00dev->irqvalue[0] = reg;
+ mask = reg;
+
+ /*
+ * Schedule tasklets for interrupt handling.
+ */
+ if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE))
+ tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet);
+
+ if (rt2x00_get_field32(reg, CSR7_RXDONE))
+ tasklet_schedule(&rt2x00dev->rxdone_tasklet);
+
+ if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) ||
+ rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) ||
+ rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) {
+ tasklet_schedule(&rt2x00dev->txstatus_tasklet);
+ /*
+ * Mask out all txdone interrupts.
+ */
+ rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1);
+ rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1);
+ rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1);
+ }
+
+ /*
+ * Disable all interrupts for which a tasklet was scheduled right now,
+ * the tasklet will reenable the appropriate interrupts.
+ */
+ spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
- /* Disable interrupts, will be enabled again in the interrupt thread. */
- rt2x00dev->ops->lib->set_device_state(rt2x00dev,
- STATE_RADIO_IRQ_OFF_ISR);
+ rt2x00pci_register_read(rt2x00dev, CSR8, ®);
+ reg |= mask;
+ rt2x00pci_register_write(rt2x00dev, CSR8, reg);
+
+ spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
- return IRQ_WAKE_THREAD;
+ return IRQ_HANDLED;
}
/*
static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
.irq_handler = rt2500pci_interrupt,
- .irq_handler_thread = rt2500pci_interrupt_thread,
+ .txstatus_tasklet = rt2500pci_txstatus_tasklet,
+ .tbtt_tasklet = rt2500pci_tbtt_tasklet,
+ .rxdone_tasklet = rt2500pci_rxdone_tasklet,
.probe_hw = rt2500pci_probe_hw,
.initialize = rt2x00pci_initialize,
.uninitialize = rt2x00pci_uninitialize,