Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bcf3cfd0 authored by Helmut Schaa's avatar Helmut Schaa Committed by John W. Linville
Browse files

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: default avatarHelmut Schaa <helmut.schaa@googlemail.com>
Acked-by: default avatarGertjan van Wingerde <gwingerde@gmail.com>
Signed-off-by: default avatarIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 16222a0d
Loading
Loading
Loading
Loading
+115 −39
Original line number Original line Diff line number Diff line
@@ -645,6 +645,11 @@ static void rt2400pci_start_queue(struct data_queue *queue)
		rt2x00pci_register_write(rt2x00dev, RXCSR0, reg);
		rt2x00pci_register_write(rt2x00dev, RXCSR0, reg);
		break;
		break;
	case QID_BEACON:
	case QID_BEACON:
		/*
		 * Allow the tbtt tasklet to be scheduled.
		 */
		tasklet_enable(&rt2x00dev->tbtt_tasklet);

		rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
		rt2x00pci_register_read(rt2x00dev, CSR14, &reg);
		rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
		rt2x00_set_field32(&reg, CSR14_TSF_COUNT, 1);
		rt2x00_set_field32(&reg, CSR14_TBCN, 1);
		rt2x00_set_field32(&reg, CSR14_TBCN, 1);
@@ -706,6 +711,11 @@ static void rt2400pci_stop_queue(struct data_queue *queue)
		rt2x00_set_field32(&reg, CSR14_TBCN, 0);
		rt2x00_set_field32(&reg, CSR14_TBCN, 0);
		rt2x00_set_field32(&reg, CSR14_BEACON_GEN, 0);
		rt2x00_set_field32(&reg, CSR14_BEACON_GEN, 0);
		rt2x00pci_register_write(rt2x00dev, CSR14, reg);
		rt2x00pci_register_write(rt2x00dev, CSR14, reg);

		/*
		 * Wait for possibly running tbtt tasklets.
		 */
		tasklet_disable(&rt2x00dev->tbtt_tasklet);
		break;
		break;
	default:
	default:
		break;
		break;
@@ -964,6 +974,7 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
	int mask = (state == STATE_RADIO_IRQ_OFF) ||
	int mask = (state == STATE_RADIO_IRQ_OFF) ||
		   (state == STATE_RADIO_IRQ_OFF_ISR);
		   (state == STATE_RADIO_IRQ_OFF_ISR);
	u32 reg;
	u32 reg;
	unsigned long flags;


	/*
	/*
	 * When interrupts are being enabled, the interrupt registers
	 * When interrupts are being enabled, the interrupt registers
@@ -972,12 +983,20 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
	if (state == STATE_RADIO_IRQ_ON) {
	if (state == STATE_RADIO_IRQ_ON) {
		rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
		rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
		rt2x00pci_register_write(rt2x00dev, CSR7, reg);
		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.
	 * Only toggle the interrupts bits we are going to use.
	 * Non-checked interrupt bits are disabled by default.
	 * Non-checked interrupt bits are disabled by default.
	 */
	 */
	spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);

	rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
	rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
	rt2x00_set_field32(&reg, CSR8_TBCN_EXPIRE, mask);
	rt2x00_set_field32(&reg, CSR8_TBCN_EXPIRE, mask);
	rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, mask);
	rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, mask);
@@ -985,6 +1004,17 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
	rt2x00_set_field32(&reg, CSR8_TXDONE_PRIORING, mask);
	rt2x00_set_field32(&reg, CSR8_TXDONE_PRIORING, mask);
	rt2x00_set_field32(&reg, CSR8_RXDONE, mask);
	rt2x00_set_field32(&reg, CSR8_RXDONE, mask);
	rt2x00pci_register_write(rt2x00dev, CSR8, reg);
	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 before
		 * disabling the interrupts.
		 */
		tasklet_disable(&rt2x00dev->txstatus_tasklet);
		tasklet_disable(&rt2x00dev->rxdone_tasklet);
	}
}
}


static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev)
static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev)
@@ -1285,57 +1315,71 @@ static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev,
	}
	}
}
}


static irqreturn_t rt2400pci_interrupt_thread(int irq, void *dev_instance)
static void rt2400pci_enable_interrupt(struct rt2x00_dev *rt2x00dev,
				       struct rt2x00_field32 irq_field)
{
{
	struct rt2x00_dev *rt2x00dev = dev_instance;
	unsigned long flags;
	u32 reg = rt2x00dev->irqvalue[0];
	u32 reg;


	/*
	/*
	 * Handle interrupts, walk through all bits
	 * Enable a single interrupt. The interrupt mask register
	 * and run the tasks, the bits are checked in order of
	 * access needs locking.
	 * priority.
	 */
	 */
	spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);


	/*
	rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
	 * 1 - Beacon timer expired interrupt.
	rt2x00_set_field32(&reg, irq_field, 0);
	 */
	rt2x00pci_register_write(rt2x00dev, CSR8, reg);
	if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE))
		rt2x00lib_beacondone(rt2x00dev);


	/*
	spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
	 * 2 - Rx ring done interrupt.
}
	 */
	if (rt2x00_get_field32(reg, CSR7_RXDONE))
		rt2x00pci_rxdone(rt2x00dev);


	/*
static void rt2400pci_txstatus_tasklet(unsigned long data)
	 * 3 - Atim ring transmit done interrupt.
{
	 */
	struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
	if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING))
	u32 reg;
		rt2400pci_txdone(rt2x00dev, QID_ATIM);
	unsigned long flags;


	/*
	/*
	 * 4 - Priority ring transmit done interrupt.
	 * Handle all tx queues.
	 */
	 */
	if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING))
	rt2400pci_txdone(rt2x00dev, QID_ATIM);
	rt2400pci_txdone(rt2x00dev, QID_AC_VO);
	rt2400pci_txdone(rt2x00dev, QID_AC_VO);
	rt2400pci_txdone(rt2x00dev, QID_AC_VI);


	/*
	/*
	 * 5 - Tx ring transmit done interrupt.
	 * Enable all TXDONE interrupts again.
	 */
	 */
	if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING))
	spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
		rt2400pci_txdone(rt2x00dev, QID_AC_VI);


	/* Enable interrupts again. */
	rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
	rt2x00dev->ops->lib->set_device_state(rt2x00dev,
	rt2x00_set_field32(&reg, CSR8_TXDONE_TXRING, 0);
					      STATE_RADIO_IRQ_ON_ISR);
	rt2x00_set_field32(&reg, CSR8_TXDONE_ATIMRING, 0);
	return IRQ_HANDLED;
	rt2x00_set_field32(&reg, CSR8_TXDONE_PRIORING, 0);
	rt2x00pci_register_write(rt2x00dev, CSR8, reg);

	spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
}

static void rt2400pci_tbtt_tasklet(unsigned long data)
{
	struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
	rt2x00lib_beacondone(rt2x00dev);
	rt2400pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE);
}

static void rt2400pci_rxdone_tasklet(unsigned long data)
{
	struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
	rt2x00pci_rxdone(rt2x00dev);
	rt2400pci_enable_interrupt(rt2x00dev, CSR8_RXDONE);
}
}


static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance)
static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance)
{
{
	struct rt2x00_dev *rt2x00dev = dev_instance;
	struct rt2x00_dev *rt2x00dev = dev_instance;
	u32 reg;
	u32 reg, mask;
	unsigned long flags;


	/*
	/*
	 * Get the interrupt sources & saved to local variable.
	 * Get the interrupt sources & saved to local variable.
@@ -1350,14 +1394,44 @@ static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance)
	if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
	if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
		return IRQ_HANDLED;
		return IRQ_HANDLED;


	/* Store irqvalues for use in the interrupt thread. */
	mask = reg;
	rt2x00dev->irqvalue[0] = 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);


	/* Disable interrupts, will be enabled again in the interrupt thread. */
	if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) ||
	rt2x00dev->ops->lib->set_device_state(rt2x00dev,
	    rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) ||
					      STATE_RADIO_IRQ_OFF_ISR);
	    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);
	}


	return IRQ_WAKE_THREAD;
	/*
	 * 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);

	rt2x00pci_register_read(rt2x00dev, CSR8, &reg);
	reg |= mask;
	rt2x00pci_register_write(rt2x00dev, CSR8, reg);

	spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);



	return IRQ_HANDLED;
}
}


/*
/*
@@ -1651,7 +1725,9 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {


static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
	.irq_handler		= rt2400pci_interrupt,
	.irq_handler		= rt2400pci_interrupt,
	.irq_handler_thread	= rt2400pci_interrupt_thread,
	.txstatus_tasklet	= rt2400pci_txstatus_tasklet,
	.tbtt_tasklet		= rt2400pci_tbtt_tasklet,
	.rxdone_tasklet		= rt2400pci_rxdone_tasklet,
	.probe_hw		= rt2400pci_probe_hw,
	.probe_hw		= rt2400pci_probe_hw,
	.initialize		= rt2x00pci_initialize,
	.initialize		= rt2x00pci_initialize,
	.uninitialize		= rt2x00pci_uninitialize,
	.uninitialize		= rt2x00pci_uninitialize,