iwlcore: support ICT interrupt
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / iwl-agn.c
index 43bc8a66864e52165fe8be1755daa5b98143c381..fa24b019c62cff0edf5af6de8906351ffc634635 100644 (file)
@@ -503,24 +503,12 @@ int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv,
 int iwl_hw_tx_queue_init(struct iwl_priv *priv,
                         struct iwl_tx_queue *txq)
 {
-       int ret;
-       unsigned long flags;
        int txq_id = txq->q.id;
 
-       spin_lock_irqsave(&priv->lock, flags);
-       ret = iwl_grab_nic_access(priv);
-       if (ret) {
-               spin_unlock_irqrestore(&priv->lock, flags);
-               return ret;
-       }
-
        /* Circular buffer (TFD queue in DRAM) physical base address */
        iwl_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id),
                             txq->q.dma_addr >> 8);
 
-       iwl_release_nic_access(priv);
-       spin_unlock_irqrestore(&priv->lock, flags);
-
        return 0;
 }
 
@@ -709,6 +697,7 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
        struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
        u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
        unsigned long status = priv->status;
+       unsigned long reg_flags;
 
        IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n",
                          (flags & HW_CARD_DISABLED) ? "Kill" : "On",
@@ -720,32 +709,25 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
                iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
                            CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
 
-               if (!iwl_grab_nic_access(priv)) {
-                       iwl_write_direct32(
-                               priv, HBUS_TARG_MBX_C,
-                               HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
-
-                       iwl_release_nic_access(priv);
-               }
+               iwl_write_direct32(priv, HBUS_TARG_MBX_C,
+                                       HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
 
                if (!(flags & RXON_CARD_DISABLED)) {
                        iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
                                    CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
-                       if (!iwl_grab_nic_access(priv)) {
-                               iwl_write_direct32(
-                                       priv, HBUS_TARG_MBX_C,
+                       iwl_write_direct32(priv, HBUS_TARG_MBX_C,
                                        HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
 
-                               iwl_release_nic_access(priv);
-                       }
                }
 
                if (flags & RF_CARD_DISABLED) {
                        iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
                                    CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
                        iwl_read32(priv, CSR_UCODE_DRV_GP1);
+                       spin_lock_irqsave(&priv->reg_lock, reg_flags);
                        if (!iwl_grab_nic_access(priv))
                                iwl_release_nic_access(priv);
+                       spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
                }
        }
 
@@ -774,14 +756,6 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
 
 int iwl_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
 {
-       int ret;
-       unsigned long flags;
-
-       spin_lock_irqsave(&priv->lock, flags);
-       ret = iwl_grab_nic_access(priv);
-       if (ret)
-               goto err;
-
        if (src == IWL_PWR_SRC_VAUX) {
                if (pci_pme_capable(priv->pci_dev, PCI_D3cold))
                        iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
@@ -793,10 +767,7 @@ int iwl_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
                                       ~APMG_PS_CTRL_MSK_PWR_SRC);
        }
 
-       iwl_release_nic_access(priv);
-err:
-       spin_unlock_irqrestore(&priv->lock, flags);
-       return ret;
+       return 0;
 }
 
 /**
@@ -966,7 +937,7 @@ static inline void iwl_synchronize_irq(struct iwl_priv *priv)
        tasklet_kill(&priv->irq_tasklet);
 }
 
-static void iwl_irq_tasklet(struct iwl_priv *priv)
+static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
 {
        u32 inta, handled = 0;
        u32 inta_fh;
@@ -1150,6 +1121,174 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 
+/* tasklet for iwlagn interrupt */
+static void iwl_irq_tasklet(struct iwl_priv *priv)
+{
+       u32 inta = 0;
+       u32 handled = 0;
+       unsigned long flags;
+#ifdef CONFIG_IWLWIFI_DEBUG
+       u32 inta_mask;
+#endif
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       /* Ack/clear/reset pending uCode interrupts.
+        * Note:  Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
+        */
+       iwl_write32(priv, CSR_INT, priv->inta);
+
+       inta = priv->inta;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+       if (priv->debug_level & IWL_DL_ISR) {
+               /* just for debug */
+               inta_mask = iwl_read32(priv, CSR_INT_MASK);
+               IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x\n ",
+                               inta, inta_mask);
+       }
+#endif
+       /* saved interrupt in inta variable now we can reset priv->inta */
+       priv->inta = 0;
+
+       /* Now service all interrupt bits discovered above. */
+       if (inta & CSR_INT_BIT_HW_ERR) {
+               IWL_ERR(priv, "Microcode HW error detected.  Restarting.\n");
+
+               /* Tell the device to stop sending interrupts */
+               iwl_disable_interrupts(priv);
+
+               priv->isr_stats.hw++;
+               iwl_irq_handle_error(priv);
+
+               handled |= CSR_INT_BIT_HW_ERR;
+
+               spin_unlock_irqrestore(&priv->lock, flags);
+
+               return;
+       }
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+       if (priv->debug_level & (IWL_DL_ISR)) {
+               /* NIC fires this, but we don't use it, redundant with WAKEUP */
+               if (inta & CSR_INT_BIT_SCD) {
+                       IWL_DEBUG_ISR(priv, "Scheduler finished to transmit "
+                                     "the frame/frames.\n");
+                       priv->isr_stats.sch++;
+               }
+
+               /* Alive notification via Rx interrupt will do the real work */
+               if (inta & CSR_INT_BIT_ALIVE) {
+                       IWL_DEBUG_ISR(priv, "Alive interrupt\n");
+                       priv->isr_stats.alive++;
+               }
+       }
+#endif
+       /* Safely ignore these bits for debug checks below */
+       inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
+
+       /* HW RF KILL switch toggled */
+       if (inta & CSR_INT_BIT_RF_KILL) {
+               int hw_rf_kill = 0;
+               if (!(iwl_read32(priv, CSR_GP_CNTRL) &
+                               CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
+                       hw_rf_kill = 1;
+
+               IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n",
+                               hw_rf_kill ? "disable radio" : "enable radio");
+
+               priv->isr_stats.rfkill++;
+
+               /* driver only loads ucode once setting the interface up.
+                * the driver allows loading the ucode even if the radio
+                * is killed. Hence update the killswitch state here. The
+                * rfkill handler will care about restarting if needed.
+                */
+               if (!test_bit(STATUS_ALIVE, &priv->status)) {
+                       if (hw_rf_kill)
+                               set_bit(STATUS_RF_KILL_HW, &priv->status);
+                       else
+                               clear_bit(STATUS_RF_KILL_HW, &priv->status);
+                       queue_work(priv->workqueue, &priv->rf_kill);
+               }
+
+               handled |= CSR_INT_BIT_RF_KILL;
+       }
+
+       /* Chip got too hot and stopped itself */
+       if (inta & CSR_INT_BIT_CT_KILL) {
+               IWL_ERR(priv, "Microcode CT kill error detected.\n");
+               priv->isr_stats.ctkill++;
+               handled |= CSR_INT_BIT_CT_KILL;
+       }
+
+       /* Error detected by uCode */
+       if (inta & CSR_INT_BIT_SW_ERR) {
+               IWL_ERR(priv, "Microcode SW error detected. "
+                       " Restarting 0x%X.\n", inta);
+               priv->isr_stats.sw++;
+               priv->isr_stats.sw_err = inta;
+               iwl_irq_handle_error(priv);
+               handled |= CSR_INT_BIT_SW_ERR;
+       }
+
+       /* uCode wakes up after power-down sleep */
+       if (inta & CSR_INT_BIT_WAKEUP) {
+               IWL_DEBUG_ISR(priv, "Wakeup interrupt\n");
+               iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
+               iwl_txq_update_write_ptr(priv, &priv->txq[0]);
+               iwl_txq_update_write_ptr(priv, &priv->txq[1]);
+               iwl_txq_update_write_ptr(priv, &priv->txq[2]);
+               iwl_txq_update_write_ptr(priv, &priv->txq[3]);
+               iwl_txq_update_write_ptr(priv, &priv->txq[4]);
+               iwl_txq_update_write_ptr(priv, &priv->txq[5]);
+
+               priv->isr_stats.wakeup++;
+
+               handled |= CSR_INT_BIT_WAKEUP;
+       }
+
+       /* All uCode command responses, including Tx command responses,
+        * Rx "responses" (frame-received notification), and other
+        * notifications from uCode come through here*/
+       if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
+               IWL_DEBUG_ISR(priv, "Rx interrupt\n");
+               iwl_write32(priv, CSR_FH_INT_STATUS, CSR49_FH_INT_RX_MASK);
+               iwl_rx_handle(priv);
+               priv->isr_stats.rx++;
+               handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
+       }
+
+       if (inta & CSR_INT_BIT_FH_TX) {
+               iwl_write32(priv, CSR_FH_INT_STATUS, CSR49_FH_INT_TX_MASK);
+               IWL_DEBUG_ISR(priv, "Tx interrupt\n");
+               priv->isr_stats.tx++;
+               handled |= CSR_INT_BIT_FH_TX;
+               /* FH finished to write, send event */
+               priv->ucode_write_complete = 1;
+               wake_up_interruptible(&priv->wait_command_queue);
+       }
+
+       if (inta & ~handled) {
+               IWL_ERR(priv, "Unhandled INTA bits 0x%08x\n", inta & ~handled);
+               priv->isr_stats.unhandled++;
+       }
+
+       if (inta & ~CSR_INI_SET_MASK) {
+               IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n",
+                        inta & ~CSR_INI_SET_MASK);
+       }
+
+
+       /* Re-enable all interrupts */
+       /* only Re-enable if diabled by irq */
+       if (test_bit(STATUS_INT_ENABLED, &priv->status))
+               iwl_enable_interrupts(priv);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+}
+
 
 /******************************************************************************
  *
@@ -1579,6 +1718,8 @@ static void __iwl_down(struct iwl_priv *priv)
                       test_bit(STATUS_EXIT_PENDING, &priv->status) <<
                                STATUS_EXIT_PENDING;
 
+       /* device going down, Stop using ICT table */
+       iwl_disable_ict(priv);
        spin_lock_irqsave(&priv->lock, flags);
        iwl_clear_bit(priv, CSR_GP_CNTRL,
                         CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -1587,13 +1728,8 @@ static void __iwl_down(struct iwl_priv *priv)
        iwl_txq_ctx_stop(priv);
        iwl_rxq_stop(priv);
 
-       spin_lock_irqsave(&priv->lock, flags);
-       if (!iwl_grab_nic_access(priv)) {
-               iwl_write_prph(priv, APMG_CLK_DIS_REG,
-                                        APMG_CLK_VAL_DMA_CLK_RQT);
-               iwl_release_nic_access(priv);
-       }
-       spin_unlock_irqrestore(&priv->lock, flags);
+       iwl_write_prph(priv, APMG_CLK_DIS_REG,
+                               APMG_CLK_VAL_DMA_CLK_RQT);
 
        udelay(5);
 
@@ -1667,6 +1803,8 @@ static int __iwl_up(struct iwl_priv *priv)
 
        /* clear (again), then enable host interrupts */
        iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
+       /* enable dram interrupt */
+       iwl_reset_ict(priv);
        iwl_enable_interrupts(priv);
 
        /* really make sure rfkill handshake bits are cleared */
@@ -1838,7 +1976,6 @@ void iwl_post_associate(struct iwl_priv *priv)
        if (!priv->vif || !priv->is_open)
                return;
 
-       iwl_power_cancel_timeout(priv);
        iwl_scan_cancel_timeout(priv, 200);
 
        conf = ieee80211_get_hw_conf(priv->hw);
@@ -1914,7 +2051,7 @@ void iwl_post_associate(struct iwl_priv *priv)
         * If chain noise has already been run, then we need to enable
         * power management here */
        if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE)
-               iwl_power_enable_management(priv);
+               iwl_power_update_mode(priv, 0);
 
        /* Enable Rx differential gain and sensitivity calibrations */
        iwl_chain_noise_reset(priv);
@@ -2299,8 +2436,10 @@ static ssize_t show_version(struct device *d,
 
        if (priv->eeprom) {
                eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
-               pos += sprintf(buf + pos, "EEPROM version: 0x%x\n",
-                                eeprom_ver);
+               pos += sprintf(buf + pos, "NVM Type: %s, version: 0x%x\n",
+                              (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
+                              ? "OTP" : "EEPROM", eeprom_ver);
+
        } else {
                pos += sprintf(buf + pos, "EEPROM not initialzed\n");
        }
@@ -2465,32 +2604,37 @@ static ssize_t show_power_level(struct device *d,
 {
        struct iwl_priv *priv = dev_get_drvdata(d);
        int mode = priv->power_data.user_power_setting;
-       int system = priv->power_data.system_power_setting;
        int level = priv->power_data.power_mode;
        char *p = buf;
 
-       switch (system) {
-       case IWL_POWER_SYS_AUTO:
-               p += sprintf(p, "SYSTEM:auto");
-               break;
-       case IWL_POWER_SYS_AC:
-               p += sprintf(p, "SYSTEM:ac");
-               break;
-       case IWL_POWER_SYS_BATTERY:
-               p += sprintf(p, "SYSTEM:battery");
-               break;
-       }
-
-       p += sprintf(p, "\tMODE:%s", (mode < IWL_POWER_AUTO) ?
-                       "fixed" : "auto");
-       p += sprintf(p, "\tINDEX:%d", level);
-       p += sprintf(p, "\n");
+       p += sprintf(p, "INDEX:%d\t", level);
+       p += sprintf(p, "USER:%d\n", mode);
        return p - buf + 1;
 }
 
 static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level,
                   store_power_level);
 
+static ssize_t show_qos(struct device *d,
+                               struct device_attribute *attr, char *buf)
+{
+       struct iwl_priv *priv = dev_get_drvdata(d);
+       char *p = buf;
+       int   q;
+
+       for (q = 0; q < AC_NUM; q++) {
+               p += sprintf(p, "\tcw_min\tcw_max\taifsn\ttxop\n");
+               p += sprintf(p, "AC[%d]\t%u\t%u\t%u\t%u\n", q,
+                            priv->qos_data.def_qos_parm.ac[q].cw_min,
+                            priv->qos_data.def_qos_parm.ac[q].cw_max,
+                            priv->qos_data.def_qos_parm.ac[q].aifsn,
+                            priv->qos_data.def_qos_parm.ac[q].edca_txop);
+       }
+
+       return p - buf + 1;
+}
+
+static DEVICE_ATTR(qos, S_IRUGO, show_qos, NULL);
 
 static ssize_t show_statistics(struct device *d,
                               struct device_attribute *attr, char *buf)
@@ -2553,7 +2697,6 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
        INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start);
 
        iwl_setup_scan_deferred_work(priv);
-       iwl_setup_power_deferred_work(priv);
 
        if (priv->cfg->ops->lib->setup_deferred_work)
                priv->cfg->ops->lib->setup_deferred_work(priv);
@@ -2562,8 +2705,12 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
        priv->statistics_periodic.data = (unsigned long)priv;
        priv->statistics_periodic.function = iwl_bg_statistics_periodic;
 
-       tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
-                    iwl_irq_tasklet, (unsigned long)priv);
+       if (!priv->cfg->use_isr_legacy)
+               tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+                       iwl_irq_tasklet, (unsigned long)priv);
+       else
+               tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+                       iwl_irq_tasklet_legacy, (unsigned long)priv);
 }
 
 static void iwl_cancel_deferred_work(struct iwl_priv *priv)
@@ -2573,7 +2720,6 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
 
        cancel_delayed_work_sync(&priv->init_alive_start);
        cancel_delayed_work(&priv->scan_check);
-       cancel_delayed_work_sync(&priv->set_power_save);
        cancel_delayed_work(&priv->alive_start);
        cancel_work_sync(&priv->beacon_update);
        del_timer_sync(&priv->statistics_periodic);
@@ -2590,7 +2736,7 @@ static struct attribute *iwl_sysfs_entries[] = {
        &dev_attr_debug_level.attr,
 #endif
        &dev_attr_version.attr,
-
+       &dev_attr_qos.attr,
        NULL
 };
 
@@ -2703,6 +2849,10 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                (unsigned long long) pci_resource_len(pdev, 0));
        IWL_DEBUG_INFO(priv, "pci_resource_base = %p\n", priv->hw_base);
 
+       /* this spin lock will be used in apm_ops.init and EEPROM access
+        * we should init now
+        */
+       spin_lock_init(&priv->reg_lock);
        iwl_hw_detect(priv);
        IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n",
                priv->cfg->name, priv->hw_rev);
@@ -2761,8 +2911,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_enable_msi(priv->pci_dev);
 
-       err = request_irq(priv->pci_dev->irq, iwl_isr, IRQF_SHARED,
-                         DRV_NAME, priv);
+       iwl_alloc_isr_ict(priv);
+       err = request_irq(priv->pci_dev->irq, priv->cfg->ops->lib->isr,
+                         IRQF_SHARED, DRV_NAME, priv);
        if (err) {
                IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq);
                goto out_disable_msi;
@@ -2819,6 +2970,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group);
  out_free_irq:
        free_irq(priv->pci_dev->irq, priv);
+       iwl_free_isr_ict(priv);
  out_disable_msi:
        pci_disable_msi(priv->pci_dev);
        iwl_uninit_drv(priv);
@@ -2900,6 +3052,8 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
 
        iwl_uninit_drv(priv);
 
+       iwl_free_isr_ict(priv);
+
        if (priv->ibss_beacon)
                dev_kfree_skb(priv->ibss_beacon);
 
@@ -2942,7 +3096,9 @@ static struct pci_device_id iwl_hw_card_ids[] = {
        {IWL_PCI_DEVICE(0x0085, 0x1112, iwl6000_2ag_cfg)},
        {IWL_PCI_DEVICE(0x0082, 0x1122, iwl6000_2ag_cfg)},
        {IWL_PCI_DEVICE(0x422B, PCI_ANY_ID, iwl6000_3agn_cfg)},
+       {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000_2agn_cfg)},
        {IWL_PCI_DEVICE(0x4238, PCI_ANY_ID, iwl6000_3agn_cfg)},
+       {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0082, PCI_ANY_ID, iwl6000_2agn_cfg)},
        {IWL_PCI_DEVICE(0x0085, PCI_ANY_ID, iwl6000_3agn_cfg)},
        {IWL_PCI_DEVICE(0x0086, PCI_ANY_ID, iwl6050_3agn_cfg)},
This page took 0.029366 seconds and 5 git commands to generate.