diff options
author | Zhu Yi <yi.zhu@intel.com> | 2008-01-24 05:19:38 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:09:58 -0500 |
commit | e655b9f03f41c7a84fb74d6619abf844d7f2ab65 (patch) | |
tree | 219be4a1e49e9d630874684b6996e9d0a73d166c /drivers/net/wireless/iwlwifi/iwl3945-base.c | |
parent | 5a66926aa9230810704fd5a127966215fd58881e (diff) |
iwlwifi: fix problem when rf_killswitch change during suspend/resume
After we delay device initialization until interface up, there are more
conditions for the hardware rf_kill switch states during suspend and
resume. For example, before suspend we can have interface up or down,
rf_kill enable or disable; before resume we can have rf_kill enable or
disable. So there are totally 2^3 = 8 conditions to handle. This patch
addressed this problem and makes sure every condition works correctly.
This patch also merges the device suspend and resume handlers with the
mac_start and mac_stop code since they are basically doing the same
thing.
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl3945-base.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl3945-base.c | 124 |
1 files changed, 46 insertions, 78 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index e0e9bbdf140f..b9a74f5eea51 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c | |||
@@ -6340,7 +6340,19 @@ static int __iwl3945_up(struct iwl3945_priv *priv) | |||
6340 | if (test_bit(STATUS_RF_KILL_SW, &priv->status)) { | 6340 | if (test_bit(STATUS_RF_KILL_SW, &priv->status)) { |
6341 | IWL_WARNING("Radio disabled by SW RF kill (module " | 6341 | IWL_WARNING("Radio disabled by SW RF kill (module " |
6342 | "parameter)\n"); | 6342 | "parameter)\n"); |
6343 | return 0; | 6343 | return -ENODEV; |
6344 | } | ||
6345 | |||
6346 | /* If platform's RF_KILL switch is NOT set to KILL */ | ||
6347 | if (iwl3945_read32(priv, CSR_GP_CNTRL) & | ||
6348 | CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) | ||
6349 | clear_bit(STATUS_RF_KILL_HW, &priv->status); | ||
6350 | else { | ||
6351 | set_bit(STATUS_RF_KILL_HW, &priv->status); | ||
6352 | if (!test_bit(STATUS_IN_SUSPEND, &priv->status)) { | ||
6353 | IWL_WARNING("Radio disabled by HW RF Kill switch\n"); | ||
6354 | return -ENODEV; | ||
6355 | } | ||
6344 | } | 6356 | } |
6345 | 6357 | ||
6346 | if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) { | 6358 | if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) { |
@@ -6375,6 +6387,10 @@ static int __iwl3945_up(struct iwl3945_priv *priv) | |||
6375 | memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr, | 6387 | memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr, |
6376 | priv->ucode_data.len); | 6388 | priv->ucode_data.len); |
6377 | 6389 | ||
6390 | /* We return success when we resume from suspend and rf_kill is on. */ | ||
6391 | if (test_bit(STATUS_RF_KILL_HW, &priv->status)) | ||
6392 | return 0; | ||
6393 | |||
6378 | for (i = 0; i < MAX_HW_RESTARTS; i++) { | 6394 | for (i = 0; i < MAX_HW_RESTARTS; i++) { |
6379 | 6395 | ||
6380 | iwl3945_clear_stations_table(priv); | 6396 | iwl3945_clear_stations_table(priv); |
@@ -6919,12 +6935,18 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw) | |||
6919 | } | 6935 | } |
6920 | } | 6936 | } |
6921 | 6937 | ||
6922 | IWL_DEBUG_INFO("Start UP work.\n"); | 6938 | ret = __iwl3945_up(priv); |
6923 | __iwl3945_up(priv); | ||
6924 | 6939 | ||
6925 | priv->is_open = 1; | ||
6926 | mutex_unlock(&priv->mutex); | 6940 | mutex_unlock(&priv->mutex); |
6927 | 6941 | ||
6942 | if (ret) | ||
6943 | goto out_release_irq; | ||
6944 | |||
6945 | IWL_DEBUG_INFO("Start UP work.\n"); | ||
6946 | |||
6947 | if (test_bit(STATUS_IN_SUSPEND, &priv->status)) | ||
6948 | return 0; | ||
6949 | |||
6928 | /* Wait for START_ALIVE from ucode. Otherwise callbacks from | 6950 | /* Wait for START_ALIVE from ucode. Otherwise callbacks from |
6929 | * mac80211 will not be run successfully. */ | 6951 | * mac80211 will not be run successfully. */ |
6930 | ret = wait_event_interruptible_timeout(priv->wait_command_queue, | 6952 | ret = wait_event_interruptible_timeout(priv->wait_command_queue, |
@@ -6939,6 +6961,7 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw) | |||
6939 | } | 6961 | } |
6940 | } | 6962 | } |
6941 | 6963 | ||
6964 | priv->is_open = 1; | ||
6942 | IWL_DEBUG_MAC80211("leave\n"); | 6965 | IWL_DEBUG_MAC80211("leave\n"); |
6943 | return 0; | 6966 | return 0; |
6944 | 6967 | ||
@@ -6946,6 +6969,9 @@ out_release_irq: | |||
6946 | free_irq(priv->pci_dev->irq, priv); | 6969 | free_irq(priv->pci_dev->irq, priv); |
6947 | out_disable_msi: | 6970 | out_disable_msi: |
6948 | pci_disable_msi(priv->pci_dev); | 6971 | pci_disable_msi(priv->pci_dev); |
6972 | pci_disable_device(priv->pci_dev); | ||
6973 | priv->is_open = 0; | ||
6974 | IWL_DEBUG_MAC80211("leave - failed\n"); | ||
6949 | return ret; | 6975 | return ret; |
6950 | } | 6976 | } |
6951 | 6977 | ||
@@ -6955,12 +6981,17 @@ static void iwl3945_mac_stop(struct ieee80211_hw *hw) | |||
6955 | 6981 | ||
6956 | IWL_DEBUG_MAC80211("enter\n"); | 6982 | IWL_DEBUG_MAC80211("enter\n"); |
6957 | 6983 | ||
6958 | /* stop mac, cancel any scan request and clear | 6984 | if (!priv->is_open) { |
6959 | * RXON_FILTER_ASSOC_MSK BIT | 6985 | IWL_DEBUG_MAC80211("leave - skip\n"); |
6960 | */ | 6986 | return; |
6987 | } | ||
6988 | |||
6961 | priv->is_open = 0; | 6989 | priv->is_open = 0; |
6962 | 6990 | ||
6963 | if (iwl3945_is_ready_rf(priv)) { | 6991 | if (iwl3945_is_ready_rf(priv)) { |
6992 | /* stop mac, cancel any scan request and clear | ||
6993 | * RXON_FILTER_ASSOC_MSK BIT | ||
6994 | */ | ||
6964 | mutex_lock(&priv->mutex); | 6995 | mutex_lock(&priv->mutex); |
6965 | iwl3945_scan_cancel_timeout(priv, 100); | 6996 | iwl3945_scan_cancel_timeout(priv, 100); |
6966 | cancel_delayed_work(&priv->post_associate); | 6997 | cancel_delayed_work(&priv->post_associate); |
@@ -7334,7 +7365,6 @@ static void iwl3945_mac_remove_interface(struct ieee80211_hw *hw, | |||
7334 | mutex_unlock(&priv->mutex); | 7365 | mutex_unlock(&priv->mutex); |
7335 | 7366 | ||
7336 | IWL_DEBUG_MAC80211("leave\n"); | 7367 | IWL_DEBUG_MAC80211("leave\n"); |
7337 | |||
7338 | } | 7368 | } |
7339 | 7369 | ||
7340 | static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) | 7370 | static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) |
@@ -8731,89 +8761,27 @@ static int iwl3945_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
8731 | { | 8761 | { |
8732 | struct iwl3945_priv *priv = pci_get_drvdata(pdev); | 8762 | struct iwl3945_priv *priv = pci_get_drvdata(pdev); |
8733 | 8763 | ||
8734 | set_bit(STATUS_IN_SUSPEND, &priv->status); | 8764 | if (priv->is_open) { |
8735 | 8765 | set_bit(STATUS_IN_SUSPEND, &priv->status); | |
8736 | /* Take down the device; powers it off, etc. */ | 8766 | iwl3945_mac_stop(priv->hw); |
8737 | iwl3945_down(priv); | 8767 | priv->is_open = 1; |
8738 | 8768 | } | |
8739 | if (priv->mac80211_registered) | ||
8740 | ieee80211_stop_queues(priv->hw); | ||
8741 | 8769 | ||
8742 | pci_save_state(pdev); | ||
8743 | pci_disable_device(pdev); | ||
8744 | pci_set_power_state(pdev, PCI_D3hot); | 8770 | pci_set_power_state(pdev, PCI_D3hot); |
8745 | 8771 | ||
8746 | return 0; | 8772 | return 0; |
8747 | } | 8773 | } |
8748 | 8774 | ||
8749 | static void iwl3945_resume(struct iwl3945_priv *priv) | ||
8750 | { | ||
8751 | unsigned long flags; | ||
8752 | |||
8753 | /* The following it a temporary work around due to the | ||
8754 | * suspend / resume not fully initializing the NIC correctly. | ||
8755 | * Without all of the following, resume will not attempt to take | ||
8756 | * down the NIC (it shouldn't really need to) and will just try | ||
8757 | * and bring the NIC back up. However that fails during the | ||
8758 | * ucode verification process. This then causes iwl3945_down to be | ||
8759 | * called *after* iwl3945_hw_nic_init() has succeeded -- which | ||
8760 | * then lets the next init sequence succeed. So, we've | ||
8761 | * replicated all of that NIC init code here... */ | ||
8762 | |||
8763 | iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF); | ||
8764 | |||
8765 | iwl3945_hw_nic_init(priv); | ||
8766 | |||
8767 | iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); | ||
8768 | iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, | ||
8769 | CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); | ||
8770 | iwl3945_write32(priv, CSR_INT, 0xFFFFFFFF); | ||
8771 | iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); | ||
8772 | iwl3945_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); | ||
8773 | |||
8774 | /* tell the device to stop sending interrupts */ | ||
8775 | iwl3945_disable_interrupts(priv); | ||
8776 | |||
8777 | spin_lock_irqsave(&priv->lock, flags); | ||
8778 | iwl3945_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | ||
8779 | |||
8780 | if (!iwl3945_grab_nic_access(priv)) { | ||
8781 | iwl3945_write_prph(priv, APMG_CLK_DIS_REG, | ||
8782 | APMG_CLK_VAL_DMA_CLK_RQT); | ||
8783 | iwl3945_release_nic_access(priv); | ||
8784 | } | ||
8785 | spin_unlock_irqrestore(&priv->lock, flags); | ||
8786 | |||
8787 | udelay(5); | ||
8788 | |||
8789 | iwl3945_hw_nic_reset(priv); | ||
8790 | |||
8791 | /* Bring the device back up */ | ||
8792 | clear_bit(STATUS_IN_SUSPEND, &priv->status); | ||
8793 | queue_work(priv->workqueue, &priv->up); | ||
8794 | } | ||
8795 | |||
8796 | static int iwl3945_pci_resume(struct pci_dev *pdev) | 8775 | static int iwl3945_pci_resume(struct pci_dev *pdev) |
8797 | { | 8776 | { |
8798 | struct iwl3945_priv *priv = pci_get_drvdata(pdev); | 8777 | struct iwl3945_priv *priv = pci_get_drvdata(pdev); |
8799 | int err; | ||
8800 | |||
8801 | printk(KERN_INFO "Coming out of suspend...\n"); | ||
8802 | 8778 | ||
8803 | pci_set_power_state(pdev, PCI_D0); | 8779 | pci_set_power_state(pdev, PCI_D0); |
8804 | err = pci_enable_device(pdev); | ||
8805 | pci_restore_state(pdev); | ||
8806 | 8780 | ||
8807 | /* | 8781 | if (priv->is_open) |
8808 | * Suspend/Resume resets the PCI configuration space, so we have to | 8782 | iwl3945_mac_start(priv->hw); |
8809 | * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries | ||
8810 | * from interfering with C3 CPU state. pci_restore_state won't help | ||
8811 | * here since it only restores the first 64 bytes pci config header. | ||
8812 | */ | ||
8813 | pci_write_config_byte(pdev, 0x41, 0x00); | ||
8814 | |||
8815 | iwl3945_resume(priv); | ||
8816 | 8783 | ||
8784 | clear_bit(STATUS_IN_SUSPEND, &priv->status); | ||
8817 | return 0; | 8785 | return 0; |
8818 | } | 8786 | } |
8819 | 8787 | ||