iwlwifi: fix problem when rf_killswitch change during suspend/resume
[deliverable/linux.git] / drivers / net / wireless / iwlwifi / iwl3945-base.c
index 8d603257b9268d9f4d2fdd943f477eda8f619658..b9a74f5eea5155aac41623f6f995252113b91b46 100644 (file)
@@ -1566,7 +1566,7 @@ static void get_eeprom_mac(struct iwl3945_priv *priv, u8 *mac)
  */
 int iwl3945_eeprom_init(struct iwl3945_priv *priv)
 {
-       u16 *e = (u16 *)&priv->eeprom;
+       __le16 *e = (__le16 *)&priv->eeprom;
        u32 gp = iwl3945_read32(priv, CSR_EEPROM_GP);
        u32 r;
        int sz = sizeof(priv->eeprom);
@@ -1609,7 +1609,7 @@ int iwl3945_eeprom_init(struct iwl3945_priv *priv)
                        IWL_ERROR("Time out reading EEPROM[%d]", addr);
                        return -ETIMEDOUT;
                }
-               e[addr / 2] = le16_to_cpu(r >> 16);
+               e[addr / 2] = cpu_to_le16(r >> 16);
        }
 
        return 0;
@@ -2767,8 +2767,8 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv,
                goto drop_unlock;
        }
 
-       if (!priv->interface_id) {
-               IWL_DEBUG_DROP("Dropping - !priv->interface_id\n");
+       if (!priv->vif) {
+               IWL_DEBUG_DROP("Dropping - !priv->vif\n");
                goto drop_unlock;
        }
 
@@ -3549,7 +3549,7 @@ static void iwl3945_bg_beacon_update(struct work_struct *work)
        struct sk_buff *beacon;
 
        /* Pull updated AP beacon from mac80211. will fail if not in AP mode */
-       beacon = ieee80211_beacon_get(priv->hw, priv->interface_id, NULL);
+       beacon = ieee80211_beacon_get(priv->hw, priv->vif, NULL);
 
        if (!beacon) {
                IWL_ERROR("update beacon failed\n");
@@ -6179,31 +6179,12 @@ static void iwl3945_alive_start(struct iwl3945_priv *priv)
        }
 
        iwl3945_init_geos(priv);
+       iwl3945_reset_channel_flag(priv);
 
        if (iwl3945_is_rfkill(priv))
                return;
 
-       if (!priv->mac80211_registered) {
-               /* Unlock so any user space entry points can call back into
-                * the driver without a deadlock... */
-               mutex_unlock(&priv->mutex);
-               iwl3945_rate_control_register(priv->hw);
-               rc = ieee80211_register_hw(priv->hw);
-               priv->hw->conf.beacon_int = 100;
-               mutex_lock(&priv->mutex);
-
-               if (rc) {
-                       iwl3945_rate_control_unregister(priv->hw);
-                       IWL_ERROR("Failed to register network "
-                                 "device (error %d)\n", rc);
-                       return;
-               }
-
-               priv->mac80211_registered = 1;
-
-               iwl3945_reset_channel_flag(priv);
-       } else
-               ieee80211_start_queues(priv->hw);
+       ieee80211_start_queues(priv->hw);
 
        priv->active_rate = priv->rates_mask;
        priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK;
@@ -6236,6 +6217,7 @@ static void iwl3945_alive_start(struct iwl3945_priv *priv)
        iwl3945_reg_txpower_periodic(priv);
 
        IWL_DEBUG_INFO("ALIVE processing complete.\n");
+       wake_up_interruptible(&priv->wait_command_queue);
 
        if (priv->error_recovering)
                iwl3945_error_recovery(priv);
@@ -6348,7 +6330,6 @@ static void iwl3945_down(struct iwl3945_priv *priv)
 
 static int __iwl3945_up(struct iwl3945_priv *priv)
 {
-       DECLARE_MAC_BUF(mac);
        int rc, i;
 
        if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
@@ -6359,7 +6340,19 @@ static int __iwl3945_up(struct iwl3945_priv *priv)
        if (test_bit(STATUS_RF_KILL_SW, &priv->status)) {
                IWL_WARNING("Radio disabled by SW RF kill (module "
                            "parameter)\n");
-               return 0;
+               return -ENODEV;
+       }
+
+       /* If platform's RF_KILL switch is NOT set to KILL */
+       if (iwl3945_read32(priv, CSR_GP_CNTRL) &
+                               CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
+               clear_bit(STATUS_RF_KILL_HW, &priv->status);
+       else {
+               set_bit(STATUS_RF_KILL_HW, &priv->status);
+               if (!test_bit(STATUS_IN_SUSPEND, &priv->status)) {
+                       IWL_WARNING("Radio disabled by HW RF Kill switch\n");
+                       return -ENODEV;
+               }
        }
 
        if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) {
@@ -6392,7 +6385,11 @@ static int __iwl3945_up(struct iwl3945_priv *priv)
         * This will be used to initialize the on-board processor's
         * data SRAM for a clean start when the runtime program first loads. */
        memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr,
-                       priv->ucode_data.len);
+              priv->ucode_data.len);
+
+       /* We return success when we resume from suspend and rf_kill is on. */
+       if (test_bit(STATUS_RF_KILL_HW, &priv->status))
+               return 0;
 
        for (i = 0; i < MAX_HW_RESTARTS; i++) {
 
@@ -6411,13 +6408,6 @@ static int __iwl3945_up(struct iwl3945_priv *priv)
                /* start card; "initialize" will load runtime ucode */
                iwl3945_nic_start(priv);
 
-               /* MAC Address location in EEPROM is same for 3945/4965 */
-               get_eeprom_mac(priv, priv->mac_addr);
-               IWL_DEBUG_INFO("MAC address: %s\n",
-                              print_mac(mac, priv->mac_addr));
-
-               SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
-
                IWL_DEBUG_INFO(DRV_NAME " is coming up\n");
 
                return 0;
@@ -6780,7 +6770,7 @@ static void iwl3945_bg_post_associate(struct work_struct *data)
 
        mutex_lock(&priv->mutex);
 
-       if (!priv->interface_id || !priv->is_open) {
+       if (!priv->vif || !priv->is_open) {
                mutex_unlock(&priv->mutex);
                return;
        }
@@ -6906,23 +6896,83 @@ static void iwl3945_bg_scan_completed(struct work_struct *work)
  *
  *****************************************************************************/
 
+#define UCODE_READY_TIMEOUT    (2 * HZ)
+
 static int iwl3945_mac_start(struct ieee80211_hw *hw)
 {
        struct iwl3945_priv *priv = hw->priv;
+       int ret;
 
        IWL_DEBUG_MAC80211("enter\n");
 
+       if (pci_enable_device(priv->pci_dev)) {
+               IWL_ERROR("Fail to pci_enable_device\n");
+               return -ENODEV;
+       }
+       pci_restore_state(priv->pci_dev);
+       pci_enable_msi(priv->pci_dev);
+
+       ret = request_irq(priv->pci_dev->irq, iwl3945_isr, IRQF_SHARED,
+                         DRV_NAME, priv);
+       if (ret) {
+               IWL_ERROR("Error allocating IRQ %d\n", priv->pci_dev->irq);
+               goto out_disable_msi;
+       }
+
        /* we should be verifying the device is ready to be opened */
        mutex_lock(&priv->mutex);
 
-       priv->is_open = 1;
+       memset(&priv->staging_rxon, 0, sizeof(struct iwl3945_rxon_cmd));
+       /* fetch ucode file from disk, alloc and copy to bus-master buffers ...
+        * ucode filename and max sizes are card-specific. */
 
-       if (!iwl3945_is_rfkill(priv))
-               ieee80211_start_queues(priv->hw);
+       if (!priv->ucode_code.len) {
+               ret = iwl3945_read_ucode(priv);
+               if (ret) {
+                       IWL_ERROR("Could not read microcode: %d\n", ret);
+                       mutex_unlock(&priv->mutex);
+                       goto out_release_irq;
+               }
+       }
+
+       ret = __iwl3945_up(priv);
 
        mutex_unlock(&priv->mutex);
+
+       if (ret)
+               goto out_release_irq;
+
+       IWL_DEBUG_INFO("Start UP work.\n");
+
+       if (test_bit(STATUS_IN_SUSPEND, &priv->status))
+               return 0;
+
+       /* Wait for START_ALIVE from ucode. Otherwise callbacks from
+        * mac80211 will not be run successfully. */
+       ret = wait_event_interruptible_timeout(priv->wait_command_queue,
+                       test_bit(STATUS_READY, &priv->status),
+                       UCODE_READY_TIMEOUT);
+       if (!ret) {
+               if (!test_bit(STATUS_READY, &priv->status)) {
+                       IWL_ERROR("Wait for START_ALIVE timeout after %dms.\n",
+                                 jiffies_to_msecs(UCODE_READY_TIMEOUT));
+                       ret = -ETIMEDOUT;
+                       goto out_release_irq;
+               }
+       }
+
+       priv->is_open = 1;
        IWL_DEBUG_MAC80211("leave\n");
        return 0;
+
+out_release_irq:
+       free_irq(priv->pci_dev->irq, priv);
+out_disable_msi:
+       pci_disable_msi(priv->pci_dev);
+       pci_disable_device(priv->pci_dev);
+       priv->is_open = 0;
+       IWL_DEBUG_MAC80211("leave - failed\n");
+       return ret;
 }
 
 static void iwl3945_mac_stop(struct ieee80211_hw *hw)
@@ -6931,23 +6981,30 @@ static void iwl3945_mac_stop(struct ieee80211_hw *hw)
 
        IWL_DEBUG_MAC80211("enter\n");
 
+       if (!priv->is_open) {
+               IWL_DEBUG_MAC80211("leave - skip\n");
+               return;
+       }
 
-       mutex_lock(&priv->mutex);
-       /* stop mac, cancel any scan request and clear
-        * RXON_FILTER_ASSOC_MSK BIT
-        */
        priv->is_open = 0;
-       if (!iwl3945_is_ready_rf(priv)) {
-               IWL_DEBUG_MAC80211("leave - RF not ready\n");
+
+       if (iwl3945_is_ready_rf(priv)) {
+               /* stop mac, cancel any scan request and clear
+                * RXON_FILTER_ASSOC_MSK BIT
+                */
+               mutex_lock(&priv->mutex);
+               iwl3945_scan_cancel_timeout(priv, 100);
+               cancel_delayed_work(&priv->post_associate);
                mutex_unlock(&priv->mutex);
-               return;
        }
 
-       iwl3945_scan_cancel_timeout(priv, 100);
-       cancel_delayed_work(&priv->post_associate);
-       priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-       iwl3945_commit_rxon(priv);
-       mutex_unlock(&priv->mutex);
+       iwl3945_down(priv);
+
+       flush_workqueue(priv->workqueue);
+       free_irq(priv->pci_dev->irq, priv);
+       pci_disable_msi(priv->pci_dev);
+       pci_save_state(priv->pci_dev);
+       pci_disable_device(priv->pci_dev);
 
        IWL_DEBUG_MAC80211("leave\n");
 }
@@ -6981,15 +7038,15 @@ static int iwl3945_mac_add_interface(struct ieee80211_hw *hw,
        unsigned long flags;
        DECLARE_MAC_BUF(mac);
 
-       IWL_DEBUG_MAC80211("enter: id %d, type %d\n", conf->if_id, conf->type);
+       IWL_DEBUG_MAC80211("enter: type %d\n", conf->type);
 
-       if (priv->interface_id) {
-               IWL_DEBUG_MAC80211("leave - interface_id != 0\n");
+       if (priv->vif) {
+               IWL_DEBUG_MAC80211("leave - vif != NULL\n");
                return -EOPNOTSUPP;
        }
 
        spin_lock_irqsave(&priv->lock, flags);
-       priv->interface_id = conf->if_id;
+       priv->vif = conf->vif;
 
        spin_unlock_irqrestore(&priv->lock, flags);
 
@@ -7000,11 +7057,12 @@ static int iwl3945_mac_add_interface(struct ieee80211_hw *hw,
                memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN);
        }
 
-       iwl3945_set_mode(priv, conf->type);
+       if (iwl3945_is_ready(priv))
+               iwl3945_set_mode(priv, conf->type);
 
-       IWL_DEBUG_MAC80211("leave\n");
        mutex_unlock(&priv->mutex);
 
+       IWL_DEBUG_MAC80211("leave\n");
        return 0;
 }
 
@@ -7157,7 +7215,8 @@ static void iwl3945_config_ap(struct iwl3945_priv *priv)
         * clear sta table, add BCAST sta... */
 }
 
-static int iwl3945_mac_config_interface(struct ieee80211_hw *hw, int if_id,
+static int iwl3945_mac_config_interface(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
                                    struct ieee80211_if_conf *conf)
 {
        struct iwl3945_priv *priv = hw->priv;
@@ -7177,9 +7236,11 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw, int if_id,
                return 0;
        }
 
+       if (!iwl3945_is_alive(priv))
+               return -EAGAIN;
+
        mutex_lock(&priv->mutex);
 
-       IWL_DEBUG_MAC80211("enter: interface id %d\n", if_id);
        if (conf->bssid)
                IWL_DEBUG_MAC80211("bssid: %s\n",
                                   print_mac(mac, conf->bssid));
@@ -7196,8 +7257,8 @@ static int iwl3945_mac_config_interface(struct ieee80211_hw *hw, int if_id,
                return 0;
        }
 
-       if (priv->interface_id != if_id) {
-               IWL_DEBUG_MAC80211("leave - interface_id != if_id\n");
+       if (priv->vif != vif) {
+               IWL_DEBUG_MAC80211("leave - priv->vif != vif\n");
                mutex_unlock(&priv->mutex);
                return 0;
        }
@@ -7295,8 +7356,8 @@ static void iwl3945_mac_remove_interface(struct ieee80211_hw *hw,
                priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
                iwl3945_commit_rxon(priv);
        }
-       if (priv->interface_id == conf->if_id) {
-               priv->interface_id = 0;
+       if (priv->vif == conf->vif) {
+               priv->vif = NULL;
                memset(priv->bssid, 0, ETH_ALEN);
                memset(priv->essid, 0, IW_ESSID_MAX_SIZE);
                priv->essid_len = 0;
@@ -7304,7 +7365,6 @@ static void iwl3945_mac_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&priv->mutex);
 
        IWL_DEBUG_MAC80211("leave\n");
-
 }
 
 static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len)
@@ -8390,6 +8450,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
        struct iwl3945_priv *priv;
        struct ieee80211_hw *hw;
        int i;
+       DECLARE_MAC_BUF(mac);
 
        /* Disabling hardware scan means that mac80211 will perform scans
         * "the hard way", rather than using device's scan. */
@@ -8542,7 +8603,6 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
        /* Device-specific setup */
        if (iwl3945_hw_set_hw_setting(priv)) {
                IWL_ERROR("failed to set hw settings\n");
-               mutex_unlock(&priv->mutex);
                goto out_iounmap;
        }
 
@@ -8567,50 +8627,53 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
 
        iwl3945_disable_interrupts(priv);
 
-       pci_enable_msi(pdev);
-
-       err = request_irq(pdev->irq, iwl3945_isr, IRQF_SHARED, DRV_NAME, priv);
-       if (err) {
-               IWL_ERROR("Error allocating IRQ %d\n", pdev->irq);
-               goto out_disable_msi;
-       }
-
-       mutex_lock(&priv->mutex);
-
        err = sysfs_create_group(&pdev->dev.kobj, &iwl3945_attribute_group);
        if (err) {
                IWL_ERROR("failed to create sysfs device attributes\n");
-               mutex_unlock(&priv->mutex);
                goto out_release_irq;
        }
 
-       /* fetch ucode file from disk, alloc and copy to bus-master buffers ...
-        * ucode filename and max sizes are card-specific. */
-       err = iwl3945_read_ucode(priv);
+       /* nic init */
+       iwl3945_set_bit(priv, CSR_GIO_CHICKEN_BITS,
+                    CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER);
+
+        iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+        err = iwl3945_poll_bit(priv, CSR_GP_CNTRL,
+                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+                          CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
+        if (err < 0) {
+                IWL_DEBUG_INFO("Failed to init the card\n");
+               goto out_remove_sysfs;
+        }
+       /* Read the EEPROM */
+       err = iwl3945_eeprom_init(priv);
        if (err) {
-               IWL_ERROR("Could not read microcode: %d\n", err);
-               mutex_unlock(&priv->mutex);
-               goto out_pci_alloc;
+               IWL_ERROR("Unable to init EEPROM\n");
+               goto out_remove_sysfs;
        }
+       /* MAC Address location in EEPROM same for 3945/4965 */
+       get_eeprom_mac(priv, priv->mac_addr);
+       IWL_DEBUG_INFO("MAC address: %s\n", print_mac(mac, priv->mac_addr));
+       SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
 
-       mutex_unlock(&priv->mutex);
-
-       IWL_DEBUG_INFO("Queueing UP work.\n");
+       iwl3945_rate_control_register(priv->hw);
+       err = ieee80211_register_hw(priv->hw);
+       if (err) {
+               IWL_ERROR("Failed to register network device (error %d)\n", err);
+               goto out_remove_sysfs;
+       }
 
-       queue_work(priv->workqueue, &priv->up);
+       priv->hw->conf.beacon_int = 100;
+       priv->mac80211_registered = 1;
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
 
        return 0;
 
- out_pci_alloc:
-       iwl3945_dealloc_ucode_pci(priv);
-
+ out_remove_sysfs:
        sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group);
 
  out_release_irq:
-       free_irq(pdev->irq, priv);
-
- out_disable_msi:
-       pci_disable_msi(pdev);
        destroy_workqueue(priv->workqueue);
        priv->workqueue = NULL;
        iwl3945_unset_hw_setting(priv);
@@ -8676,8 +8739,6 @@ static void iwl3945_pci_remove(struct pci_dev *pdev)
        destroy_workqueue(priv->workqueue);
        priv->workqueue = NULL;
 
-       free_irq(pdev->irq, priv);
-       pci_disable_msi(pdev);
        pci_iounmap(pdev, priv->hw_base);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
@@ -8700,89 +8761,27 @@ static int iwl3945_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct iwl3945_priv *priv = pci_get_drvdata(pdev);
 
-       set_bit(STATUS_IN_SUSPEND, &priv->status);
-
-       /* Take down the device; powers it off, etc. */
-       iwl3945_down(priv);
-
-       if (priv->mac80211_registered)
-               ieee80211_stop_queues(priv->hw);
+       if (priv->is_open) {
+               set_bit(STATUS_IN_SUSPEND, &priv->status);
+               iwl3945_mac_stop(priv->hw);
+               priv->is_open = 1;
+       }
 
-       pci_save_state(pdev);
-       pci_disable_device(pdev);
        pci_set_power_state(pdev, PCI_D3hot);
 
        return 0;
 }
 
-static void iwl3945_resume(struct iwl3945_priv *priv)
-{
-       unsigned long flags;
-
-       /* The following it a temporary work around due to the
-        * suspend / resume not fully initializing the NIC correctly.
-        * Without all of the following, resume will not attempt to take
-        * down the NIC (it shouldn't really need to) and will just try
-        * and bring the NIC back up.  However that fails during the
-        * ucode verification process.  This then causes iwl3945_down to be
-        * called *after* iwl3945_hw_nic_init() has succeeded -- which
-        * then lets the next init sequence succeed.  So, we've
-        * replicated all of that NIC init code here... */
-
-       iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF);
-
-       iwl3945_hw_nic_init(priv);
-
-       iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
-       iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR,
-                   CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
-       iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF);
-       iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
-       iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
-
-       /* tell the device to stop sending interrupts */
-       iwl3945_disable_interrupts(priv);
-
-       spin_lock_irqsave(&priv->lock, flags);
-       iwl3945_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
-
-       if (!iwl3945_grab_nic_access(priv)) {
-               iwl3945_write_prph(priv, APMG_CLK_DIS_REG,
-                                        APMG_CLK_VAL_DMA_CLK_RQT);
-               iwl3945_release_nic_access(priv);
-       }
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       udelay(5);
-
-       iwl3945_hw_nic_reset(priv);
-
-       /* Bring the device back up */
-       clear_bit(STATUS_IN_SUSPEND, &priv->status);
-       queue_work(priv->workqueue, &priv->up);
-}
-
 static int iwl3945_pci_resume(struct pci_dev *pdev)
 {
        struct iwl3945_priv *priv = pci_get_drvdata(pdev);
-       int err;
-
-       printk(KERN_INFO "Coming out of suspend...\n");
 
        pci_set_power_state(pdev, PCI_D0);
-       err = pci_enable_device(pdev);
-       pci_restore_state(pdev);
 
-       /*
-        * Suspend/Resume resets the PCI configuration space, so we have to
-        * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries
-        * from interfering with C3 CPU state. pci_restore_state won't help
-        * here since it only restores the first 64 bytes pci config header.
-        */
-       pci_write_config_byte(pdev, 0x41, 0x00);
-
-       iwl3945_resume(priv);
+       if (priv->is_open)
+               iwl3945_mac_start(priv->hw);
 
+       clear_bit(STATUS_IN_SUSPEND, &priv->status);
        return 0;
 }
 
This page took 0.035433 seconds and 5 git commands to generate.