Merge ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6
[pandora-kernel.git] / drivers / net / wireless / rt2x00 / rt2800pci.c
index 7951cda..6ac0ff2 100644 (file)
@@ -200,11 +200,22 @@ static void rt2800pci_start_queue(struct data_queue *queue)
                rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
                break;
        case QID_BEACON:
+               /*
+                * Allow beacon tasklets to be scheduled for periodic
+                * beacon updates.
+                */
+               tasklet_enable(&rt2x00dev->tbtt_tasklet);
+               tasklet_enable(&rt2x00dev->pretbtt_tasklet);
+
                rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
                rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
                rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
                rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 1);
                rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+               rt2800_register_read(rt2x00dev, INT_TIMER_EN, &reg);
+               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 1);
+               rt2800_register_write(rt2x00dev, INT_TIMER_EN, reg);
                break;
        default:
                break;
@@ -250,6 +261,16 @@ static void rt2800pci_stop_queue(struct data_queue *queue)
                rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
                rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_GEN, 0);
                rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
+
+               rt2800_register_read(rt2x00dev, INT_TIMER_EN, &reg);
+               rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER, 0);
+               rt2800_register_write(rt2x00dev, INT_TIMER_EN, reg);
+
+               /*
+                * Wait for tbtt tasklets to finish.
+                */
+               tasklet_disable(&rt2x00dev->tbtt_tasklet);
+               tasklet_disable(&rt2x00dev->pretbtt_tasklet);
                break;
        default:
                break;
@@ -397,9 +418,9 @@ static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev)
 static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
                                 enum dev_state state)
 {
-       int mask = (state == STATE_RADIO_IRQ_ON) ||
-                  (state == STATE_RADIO_IRQ_ON_ISR);
+       int mask = (state == STATE_RADIO_IRQ_ON);
        u32 reg;
+       unsigned long flags;
 
        /*
         * When interrupts are being enabled, the interrupt registers
@@ -408,8 +429,17 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
        if (state == STATE_RADIO_IRQ_ON) {
                rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
                rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
+
+               /*
+                * Enable tasklets. The beacon related tasklets are
+                * enabled when the beacon queue is started.
+                */
+               tasklet_enable(&rt2x00dev->txstatus_tasklet);
+               tasklet_enable(&rt2x00dev->rxdone_tasklet);
+               tasklet_enable(&rt2x00dev->autowake_tasklet);
        }
 
+       spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
        rt2800_register_read(rt2x00dev, INT_MASK_CSR, &reg);
        rt2x00_set_field32(&reg, INT_MASK_CSR_RXDELAYINT, 0);
        rt2x00_set_field32(&reg, INT_MASK_CSR_TXDELAYINT, 0);
@@ -430,6 +460,17 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field32(&reg, INT_MASK_CSR_RX_COHERENT, 0);
        rt2x00_set_field32(&reg, INT_MASK_CSR_TX_COHERENT, 0);
        rt2800_register_write(rt2x00dev, INT_MASK_CSR, 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);
+               tasklet_disable(&rt2x00dev->autowake_tasklet);
+       }
 }
 
 static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev)
@@ -475,39 +516,23 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev)
 
 static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev)
 {
-       u32 reg;
-
-       rt2800_disable_radio(rt2x00dev);
-
-       rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001280);
-
-       rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX4, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX5, 1);
-       rt2x00_set_field32(&reg, WPDMA_RST_IDX_DRX_IDX0, 1);
-       rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
-
-       rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
-       rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
+       if (rt2x00_is_soc(rt2x00dev)) {
+               rt2800_disable_radio(rt2x00dev);
+               rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0);
+               rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0);
+       }
 }
 
 static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev,
                               enum dev_state state)
 {
-       /*
-        * Always put the device to sleep (even when we intend to wakeup!)
-        * if the device is booting and wasn't asleep it will return
-        * failure when attempting to wakeup.
-        */
-       rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0xff, 2);
-
        if (state == STATE_AWAKE) {
-               rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0);
+               rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0x02);
                rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKUP);
+       } else if (state == STATE_SLEEP) {
+               rt2800_register_write(rt2x00dev, H2M_MAILBOX_STATUS, 0xffffffff);
+               rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, 0xffffffff);
+               rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0x01, 0xff, 0x01);
        }
 
        return 0;
@@ -538,9 +563,7 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
                rt2800pci_set_state(rt2x00dev, STATE_SLEEP);
                break;
        case STATE_RADIO_IRQ_ON:
-       case STATE_RADIO_IRQ_ON_ISR:
        case STATE_RADIO_IRQ_OFF:
-       case STATE_RADIO_IRQ_OFF_ISR:
                rt2800pci_toggle_irq(rt2x00dev, state);
                break;
        case STATE_DEEP_SLEEP:
@@ -652,6 +675,12 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
                 */
                rxdesc->flags |= RX_FLAG_IV_STRIPPED;
 
+               /*
+                * The hardware has already checked the Michael Mic and has
+                * stripped it from the frame. Signal this to mac80211.
+                */
+               rxdesc->flags |= RX_FLAG_MMIC_STRIPPED;
+
                if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS)
                        rxdesc->flags |= RX_FLAG_DECRYPTED;
                else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC)
@@ -726,45 +755,60 @@ static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
        }
 }
 
-static void rt2800pci_txstatus_tasklet(unsigned long data)
+static void rt2800pci_enable_interrupt(struct rt2x00_dev *rt2x00dev,
+                                      struct rt2x00_field32 irq_field)
 {
-       rt2800pci_txdone((struct rt2x00_dev *)data);
-}
-
-static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance)
-{
-       struct rt2x00_dev *rt2x00dev = dev_instance;
-       u32 reg = rt2x00dev->irqvalue[0];
+       unsigned long flags;
+       u32 reg;
 
        /*
-        * 1 - Pre TBTT interrupt.
+        * Enable a single interrupt. The interrupt mask register
+        * access needs locking.
         */
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
-               rt2x00lib_pretbtt(rt2x00dev);
+       spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags);
+       rt2800_register_read(rt2x00dev, INT_MASK_CSR, &reg);
+       rt2x00_set_field32(&reg, irq_field, 1);
+       rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
+}
 
-       /*
-        * 2 - Beacondone interrupt.
-        */
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT))
-               rt2x00lib_beacondone(rt2x00dev);
+static void rt2800pci_txstatus_tasklet(unsigned long data)
+{
+       rt2800pci_txdone((struct rt2x00_dev *)data);
 
        /*
-        * 3 - Rx ring done interrupt.
+        * No need to enable the tx status interrupt here as we always
+        * leave it enabled to minimize the possibility of a tx status
+        * register overflow. See comment in interrupt handler.
         */
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
-               rt2x00pci_rxdone(rt2x00dev);
+}
 
-       /*
-        * 4 - Auto wakeup interrupt.
-        */
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP))
-               rt2800pci_wakeup(rt2x00dev);
+static void rt2800pci_pretbtt_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       rt2x00lib_pretbtt(rt2x00dev);
+       rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT);
+}
 
-       /* Enable interrupts again. */
-       rt2x00dev->ops->lib->set_device_state(rt2x00dev,
-                                             STATE_RADIO_IRQ_ON_ISR);
+static void rt2800pci_tbtt_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       rt2x00lib_beacondone(rt2x00dev);
+       rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT);
+}
 
-       return IRQ_HANDLED;
+static void rt2800pci_rxdone_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       rt2x00pci_rxdone(rt2x00dev);
+       rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE);
+}
+
+static void rt2800pci_autowake_tasklet(unsigned long data)
+{
+       struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+       rt2800pci_wakeup(rt2x00dev);
+       rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP);
 }
 
 static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
@@ -810,8 +854,8 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
 static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
 {
        struct rt2x00_dev *rt2x00dev = dev_instance;
-       u32 reg;
-       irqreturn_t ret = IRQ_HANDLED;
+       u32 reg, mask;
+       unsigned long flags;
 
        /* Read status and ACK all interrupts */
        rt2800_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
@@ -823,38 +867,44 @@ static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
        if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
                return IRQ_HANDLED;
 
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
-               rt2800pci_txstatus_interrupt(rt2x00dev);
+       /*
+        * 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;
 
-       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT) ||
-           rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT) ||
-           rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE) ||
-           rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) {
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) {
+               rt2800pci_txstatus_interrupt(rt2x00dev);
                /*
-                * All other interrupts are handled in the interrupt thread.
-                * Store irqvalue for use in the interrupt thread.
+                * Never disable the TX_FIFO_STATUS interrupt.
                 */
-               rt2x00dev->irqvalue[0] = reg;
+               rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1);
+       }
 
-               /*
-                * Disable interrupts, will be enabled again in the
-                * interrupt thread.
-               */
-               rt2x00dev->ops->lib->set_device_state(rt2x00dev,
-                                                     STATE_RADIO_IRQ_OFF_ISR);
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
+               tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet);
 
-               /*
-                * Leave the TX_FIFO_STATUS interrupt enabled to not lose any
-                * tx status reports.
-                */
-               rt2800_register_read(rt2x00dev, INT_MASK_CSR, &reg);
-               rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, 1);
-               rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT))
+               tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet);
 
-               ret = IRQ_WAKE_THREAD;
-       }
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
+               tasklet_schedule(&rt2x00dev->rxdone_tasklet);
 
-       return ret;
+       if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP))
+               tasklet_schedule(&rt2x00dev->autowake_tasklet);
+
+       /*
+        * 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);
+       rt2800_register_read(rt2x00dev, INT_MASK_CSR, &reg);
+       reg &= mask;
+       rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg);
+       spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags);
+
+       return IRQ_HANDLED;
 }
 
 /*
@@ -969,8 +1019,11 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
 
 static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .irq_handler            = rt2800pci_interrupt,
-       .irq_handler_thread     = rt2800pci_interrupt_thread,
-       .txstatus_tasklet       = rt2800pci_txstatus_tasklet,
+       .txstatus_tasklet       = rt2800pci_txstatus_tasklet,
+       .pretbtt_tasklet        = rt2800pci_pretbtt_tasklet,
+       .tbtt_tasklet           = rt2800pci_tbtt_tasklet,
+       .rxdone_tasklet         = rt2800pci_rxdone_tasklet,
+       .autowake_tasklet       = rt2800pci_autowake_tasklet,
        .probe_hw               = rt2800pci_probe_hw,
        .get_firmware_name      = rt2800pci_get_firmware_name,
        .check_firmware         = rt2800_check_firmware,
@@ -990,6 +1043,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
        .write_tx_desc          = rt2800pci_write_tx_desc,
        .write_tx_data          = rt2800_write_tx_data,
        .write_beacon           = rt2800_write_beacon,
+       .clear_beacon           = rt2800_clear_beacon,
        .fill_rxdone            = rt2800pci_fill_rxdone,
        .config_shared_key      = rt2800_config_shared_key,
        .config_pairwise_key    = rt2800_config_pairwise_key,