Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 236950
b: refs/heads/master
c: 5846a55
h: refs/heads/master
v: v3
  • Loading branch information
Helmut Schaa authored and John W. Linville committed Jan 31, 2011
1 parent 91f916c commit 0bba06a
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 46 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a9d61e9e779579c66c0d4c8af203d51dbca1473c
refs/heads/master: 5846a550b5838ea7fe8e280caff159a5ddb5c7e1
175 changes: 130 additions & 45 deletions trunk/drivers/net/wireless/rt2x00/rt61pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,11 @@ static void rt61pci_start_queue(struct data_queue *queue)
rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg);
break;
case QID_BEACON:
/*
* Allow the tbtt tasklet to be scheduled.
*/
tasklet_enable(&rt2x00dev->tbtt_tasklet);

rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, &reg);
rt2x00_set_field32(&reg, TXRX_CSR9_TSF_TICKING, 1);
rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 1);
Expand Down Expand Up @@ -1221,6 +1226,11 @@ static void rt61pci_stop_queue(struct data_queue *queue)
rt2x00_set_field32(&reg, TXRX_CSR9_TBTT_ENABLE, 0);
rt2x00_set_field32(&reg, TXRX_CSR9_BEACON_GEN, 0);
rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);

/*
* Wait for possibly running tbtt tasklets.
*/
tasklet_disable(&rt2x00dev->tbtt_tasklet);
break;
default:
break;
Expand Down Expand Up @@ -1710,6 +1720,7 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
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
Expand All @@ -1721,12 +1732,21 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,

rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, &reg);
rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg);

/*
* Enable tasklets.
*/
tasklet_enable(&rt2x00dev->txstatus_tasklet);
tasklet_enable(&rt2x00dev->rxdone_tasklet);
tasklet_enable(&rt2x00dev->autowake_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, INT_MASK_CSR, &reg);
rt2x00_set_field32(&reg, INT_MASK_CSR_TXDONE, mask);
rt2x00_set_field32(&reg, INT_MASK_CSR_RXDONE, mask);
Expand All @@ -1746,6 +1766,17 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_7, mask);
rt2x00_set_field32(&reg, MCU_INT_MASK_CSR_TWAKEUP, mask);
rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, 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);
tasklet_disable(&rt2x00dev->autowake_tasklet);
}
}

static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev)
Expand Down Expand Up @@ -2223,61 +2254,80 @@ static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev)
rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
}

static irqreturn_t rt61pci_interrupt_thread(int irq, void *dev_instance)
static void rt61pci_enable_interrupt(struct rt2x00_dev *rt2x00dev,
struct rt2x00_field32 irq_field)
{
struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg = rt2x00dev->irqvalue[0];
u32 reg_mcu = rt2x00dev->irqvalue[1];
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 - Rx ring done interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE))
rt2x00pci_rxdone(rt2x00dev);
rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, &reg);
rt2x00_set_field32(&reg, irq_field, 0);
rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg);

/*
* 2 - Tx ring done interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE))
rt61pci_txdone(rt2x00dev);
spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
}

/*
* 3 - Handle MCU command done.
*/
if (reg_mcu)
rt2x00pci_register_write(rt2x00dev,
M2H_CMD_DONE_CSR, 0xffffffff);
static void rt61pci_enable_mcu_interrupt(struct rt2x00_dev *rt2x00dev,
struct rt2x00_field32 irq_field)
{
unsigned long flags;
u32 reg;

/*
* 4 - MCU Autowakeup interrupt.
* Enable a single MCU interrupt. The interrupt mask register
* access needs locking.
*/
if (rt2x00_get_field32(reg_mcu, MCU_INT_SOURCE_CSR_TWAKEUP))
rt61pci_wakeup(rt2x00dev);
spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);

/*
* 5 - Beacon done interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE))
rt2x00lib_beacondone(rt2x00dev);
rt2x00pci_register_read(rt2x00dev, MCU_INT_MASK_CSR, &reg);
rt2x00_set_field32(&reg, irq_field, 0);
rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg);

/* Enable interrupts again. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_ON_ISR);
return IRQ_HANDLED;
spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
}

static void rt61pci_txstatus_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt61pci_txdone(rt2x00dev);
rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TXDONE);
}

static void rt61pci_tbtt_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2x00lib_beacondone(rt2x00dev);
rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_BEACON_DONE);
}

static void rt61pci_rxdone_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt2x00pci_rxdone(rt2x00dev);
rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RXDONE);
}

static void rt61pci_autowake_tasklet(unsigned long data)
{
struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
rt61pci_wakeup(rt2x00dev);
rt2x00pci_register_write(rt2x00dev,
M2H_CMD_DONE_CSR, 0xffffffff);
rt61pci_enable_mcu_interrupt(rt2x00dev, MCU_INT_MASK_CSR_TWAKEUP);
}

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

/*
* Get the interrupt sources & saved to local variable.
Expand All @@ -2295,14 +2345,46 @@ static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance)
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;

/* Store irqvalues for use in the interrupt thread. */
rt2x00dev->irqvalue[0] = reg;
rt2x00dev->irqvalue[1] = reg_mcu;
/*
* Schedule tasklets for interrupt handling.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE))
tasklet_schedule(&rt2x00dev->rxdone_tasklet);

if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE))
tasklet_schedule(&rt2x00dev->txstatus_tasklet);

if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE))
tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet);

if (rt2x00_get_field32(reg_mcu, MCU_INT_SOURCE_CSR_TWAKEUP))
tasklet_schedule(&rt2x00dev->autowake_tasklet);

/*
* Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits
* for interrupts and interrupt masks we can just use the value of
* INT_SOURCE_CSR to create the interrupt mask.
*/
mask = reg;
mask_mcu = reg_mcu;

/*
* 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, INT_MASK_CSR, &reg);
reg |= mask;
rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg);

/* Disable interrupts, will be enabled again in the interrupt thread. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_OFF_ISR);
return IRQ_WAKE_THREAD;
rt2x00pci_register_read(rt2x00dev, MCU_INT_MASK_CSR, &reg);
reg |= mask_mcu;
rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg);

spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);

return IRQ_HANDLED;
}

/*
Expand Down Expand Up @@ -2896,7 +2978,10 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {

static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
.irq_handler = rt61pci_interrupt,
.irq_handler_thread = rt61pci_interrupt_thread,
.txstatus_tasklet = rt61pci_txstatus_tasklet,
.tbtt_tasklet = rt61pci_tbtt_tasklet,
.rxdone_tasklet = rt61pci_rxdone_tasklet,
.autowake_tasklet = rt61pci_autowake_tasklet,
.probe_hw = rt61pci_probe_hw,
.get_firmware_name = rt61pci_get_firmware_name,
.check_firmware = rt61pci_check_firmware,
Expand Down

0 comments on commit 0bba06a

Please sign in to comment.