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/iwl4965-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/iwl4965-base.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl4965-base.c | 133 |
1 files changed, 44 insertions, 89 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 98f09e6e9f8c..811e41b7f664 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c | |||
@@ -6761,7 +6761,6 @@ static void iwl4965_down(struct iwl4965_priv *priv) | |||
6761 | static int __iwl4965_up(struct iwl4965_priv *priv) | 6761 | static int __iwl4965_up(struct iwl4965_priv *priv) |
6762 | { | 6762 | { |
6763 | int rc, i; | 6763 | int rc, i; |
6764 | u32 hw_rf_kill = 0; | ||
6765 | 6764 | ||
6766 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { | 6765 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { |
6767 | IWL_WARNING("Exit pending; will not bring the NIC up\n"); | 6766 | IWL_WARNING("Exit pending; will not bring the NIC up\n"); |
@@ -6771,7 +6770,19 @@ static int __iwl4965_up(struct iwl4965_priv *priv) | |||
6771 | if (test_bit(STATUS_RF_KILL_SW, &priv->status)) { | 6770 | if (test_bit(STATUS_RF_KILL_SW, &priv->status)) { |
6772 | IWL_WARNING("Radio disabled by SW RF kill (module " | 6771 | IWL_WARNING("Radio disabled by SW RF kill (module " |
6773 | "parameter)\n"); | 6772 | "parameter)\n"); |
6774 | return 0; | 6773 | return -ENODEV; |
6774 | } | ||
6775 | |||
6776 | /* If platform's RF_KILL switch is NOT set to KILL */ | ||
6777 | if (iwl4965_read32(priv, CSR_GP_CNTRL) & | ||
6778 | CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) | ||
6779 | clear_bit(STATUS_RF_KILL_HW, &priv->status); | ||
6780 | else { | ||
6781 | set_bit(STATUS_RF_KILL_HW, &priv->status); | ||
6782 | if (!test_bit(STATUS_IN_SUSPEND, &priv->status)) { | ||
6783 | IWL_WARNING("Radio disabled by HW RF Kill switch\n"); | ||
6784 | return -ENODEV; | ||
6785 | } | ||
6775 | } | 6786 | } |
6776 | 6787 | ||
6777 | if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) { | 6788 | if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) { |
@@ -6806,17 +6817,9 @@ static int __iwl4965_up(struct iwl4965_priv *priv) | |||
6806 | memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr, | 6817 | memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr, |
6807 | priv->ucode_data.len); | 6818 | priv->ucode_data.len); |
6808 | 6819 | ||
6809 | /* If platform's RF_KILL switch is set to KILL, | 6820 | /* We return success when we resume from suspend and rf_kill is on. */ |
6810 | * wait for BIT_INT_RF_KILL interrupt before loading uCode | 6821 | if (test_bit(STATUS_RF_KILL_HW, &priv->status)) |
6811 | * and getting things started */ | ||
6812 | if (!(iwl4965_read32(priv, CSR_GP_CNTRL) & | ||
6813 | CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) | ||
6814 | hw_rf_kill = 1; | ||
6815 | |||
6816 | if (test_bit(STATUS_RF_KILL_HW, &priv->status) || hw_rf_kill) { | ||
6817 | IWL_WARNING("Radio disabled by HW RF Kill switch\n"); | ||
6818 | return 0; | 6822 | return 0; |
6819 | } | ||
6820 | 6823 | ||
6821 | for (i = 0; i < MAX_HW_RESTARTS; i++) { | 6824 | for (i = 0; i < MAX_HW_RESTARTS; i++) { |
6822 | 6825 | ||
@@ -7379,12 +7382,18 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw) | |||
7379 | } | 7382 | } |
7380 | } | 7383 | } |
7381 | 7384 | ||
7382 | IWL_DEBUG_INFO("Start UP work.\n"); | 7385 | ret = __iwl4965_up(priv); |
7383 | __iwl4965_up(priv); | ||
7384 | 7386 | ||
7385 | priv->is_open = 1; | ||
7386 | mutex_unlock(&priv->mutex); | 7387 | mutex_unlock(&priv->mutex); |
7387 | 7388 | ||
7389 | if (ret) | ||
7390 | goto out_release_irq; | ||
7391 | |||
7392 | IWL_DEBUG_INFO("Start UP work done.\n"); | ||
7393 | |||
7394 | if (test_bit(STATUS_IN_SUSPEND, &priv->status)) | ||
7395 | return 0; | ||
7396 | |||
7388 | /* Wait for START_ALIVE from ucode. Otherwise callbacks from | 7397 | /* Wait for START_ALIVE from ucode. Otherwise callbacks from |
7389 | * mac80211 will not be run successfully. */ | 7398 | * mac80211 will not be run successfully. */ |
7390 | ret = wait_event_interruptible_timeout(priv->wait_command_queue, | 7399 | ret = wait_event_interruptible_timeout(priv->wait_command_queue, |
@@ -7399,6 +7408,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw) | |||
7399 | } | 7408 | } |
7400 | } | 7409 | } |
7401 | 7410 | ||
7411 | priv->is_open = 1; | ||
7402 | IWL_DEBUG_MAC80211("leave\n"); | 7412 | IWL_DEBUG_MAC80211("leave\n"); |
7403 | return 0; | 7413 | return 0; |
7404 | 7414 | ||
@@ -7406,6 +7416,9 @@ out_release_irq: | |||
7406 | free_irq(priv->pci_dev->irq, priv); | 7416 | free_irq(priv->pci_dev->irq, priv); |
7407 | out_disable_msi: | 7417 | out_disable_msi: |
7408 | pci_disable_msi(priv->pci_dev); | 7418 | pci_disable_msi(priv->pci_dev); |
7419 | pci_disable_device(priv->pci_dev); | ||
7420 | priv->is_open = 0; | ||
7421 | IWL_DEBUG_MAC80211("leave - failed\n"); | ||
7409 | return ret; | 7422 | return ret; |
7410 | } | 7423 | } |
7411 | 7424 | ||
@@ -7415,12 +7428,17 @@ static void iwl4965_mac_stop(struct ieee80211_hw *hw) | |||
7415 | 7428 | ||
7416 | IWL_DEBUG_MAC80211("enter\n"); | 7429 | IWL_DEBUG_MAC80211("enter\n"); |
7417 | 7430 | ||
7418 | /* stop mac, cancel any scan request and clear | 7431 | if (!priv->is_open) { |
7419 | * RXON_FILTER_ASSOC_MSK BIT | 7432 | IWL_DEBUG_MAC80211("leave - skip\n"); |
7420 | */ | 7433 | return; |
7434 | } | ||
7435 | |||
7421 | priv->is_open = 0; | 7436 | priv->is_open = 0; |
7422 | 7437 | ||
7423 | if (iwl4965_is_ready_rf(priv)) { | 7438 | if (iwl4965_is_ready_rf(priv)) { |
7439 | /* stop mac, cancel any scan request and clear | ||
7440 | * RXON_FILTER_ASSOC_MSK BIT | ||
7441 | */ | ||
7424 | mutex_lock(&priv->mutex); | 7442 | mutex_lock(&priv->mutex); |
7425 | iwl4965_scan_cancel_timeout(priv, 100); | 7443 | iwl4965_scan_cancel_timeout(priv, 100); |
7426 | cancel_delayed_work(&priv->post_associate); | 7444 | cancel_delayed_work(&priv->post_associate); |
@@ -8152,7 +8170,6 @@ static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw) | |||
8152 | mutex_unlock(&priv->mutex); | 8170 | mutex_unlock(&priv->mutex); |
8153 | 8171 | ||
8154 | IWL_DEBUG_MAC80211("leave\n"); | 8172 | IWL_DEBUG_MAC80211("leave\n"); |
8155 | |||
8156 | } | 8173 | } |
8157 | 8174 | ||
8158 | static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, | 8175 | static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, |
@@ -9327,89 +9344,27 @@ static int iwl4965_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
9327 | { | 9344 | { |
9328 | struct iwl4965_priv *priv = pci_get_drvdata(pdev); | 9345 | struct iwl4965_priv *priv = pci_get_drvdata(pdev); |
9329 | 9346 | ||
9330 | set_bit(STATUS_IN_SUSPEND, &priv->status); | 9347 | if (priv->is_open) { |
9331 | 9348 | set_bit(STATUS_IN_SUSPEND, &priv->status); | |
9332 | /* Take down the device; powers it off, etc. */ | 9349 | iwl4965_mac_stop(priv->hw); |
9333 | iwl4965_down(priv); | 9350 | priv->is_open = 1; |
9334 | 9351 | } | |
9335 | if (priv->mac80211_registered) | ||
9336 | ieee80211_stop_queues(priv->hw); | ||
9337 | 9352 | ||
9338 | pci_save_state(pdev); | ||
9339 | pci_disable_device(pdev); | ||
9340 | pci_set_power_state(pdev, PCI_D3hot); | 9353 | pci_set_power_state(pdev, PCI_D3hot); |
9341 | 9354 | ||
9342 | return 0; | 9355 | return 0; |
9343 | } | 9356 | } |
9344 | 9357 | ||
9345 | static void iwl4965_resume(struct iwl4965_priv *priv) | ||
9346 | { | ||
9347 | unsigned long flags; | ||
9348 | |||
9349 | /* The following it a temporary work around due to the | ||
9350 | * suspend / resume not fully initializing the NIC correctly. | ||
9351 | * Without all of the following, resume will not attempt to take | ||
9352 | * down the NIC (it shouldn't really need to) and will just try | ||
9353 | * and bring the NIC back up. However that fails during the | ||
9354 | * ucode verification process. This then causes iwl4965_down to be | ||
9355 | * called *after* iwl4965_hw_nic_init() has succeeded -- which | ||
9356 | * then lets the next init sequence succeed. So, we've | ||
9357 | * replicated all of that NIC init code here... */ | ||
9358 | |||
9359 | iwl4965_write32(priv, CSR_INT, 0xFFFFFFFF); | ||
9360 | |||
9361 | iwl4965_hw_nic_init(priv); | ||
9362 | |||
9363 | iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); | ||
9364 | iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, | ||
9365 | CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); | ||
9366 | iwl4965_write32(priv, CSR_INT, 0xFFFFFFFF); | ||
9367 | iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); | ||
9368 | iwl4965_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); | ||
9369 | |||
9370 | /* tell the device to stop sending interrupts */ | ||
9371 | iwl4965_disable_interrupts(priv); | ||
9372 | |||
9373 | spin_lock_irqsave(&priv->lock, flags); | ||
9374 | iwl4965_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); | ||
9375 | |||
9376 | if (!iwl4965_grab_nic_access(priv)) { | ||
9377 | iwl4965_write_prph(priv, APMG_CLK_DIS_REG, | ||
9378 | APMG_CLK_VAL_DMA_CLK_RQT); | ||
9379 | iwl4965_release_nic_access(priv); | ||
9380 | } | ||
9381 | spin_unlock_irqrestore(&priv->lock, flags); | ||
9382 | |||
9383 | udelay(5); | ||
9384 | |||
9385 | iwl4965_hw_nic_reset(priv); | ||
9386 | |||
9387 | /* Bring the device back up */ | ||
9388 | clear_bit(STATUS_IN_SUSPEND, &priv->status); | ||
9389 | queue_work(priv->workqueue, &priv->up); | ||
9390 | } | ||
9391 | |||
9392 | static int iwl4965_pci_resume(struct pci_dev *pdev) | 9358 | static int iwl4965_pci_resume(struct pci_dev *pdev) |
9393 | { | 9359 | { |
9394 | struct iwl4965_priv *priv = pci_get_drvdata(pdev); | 9360 | struct iwl4965_priv *priv = pci_get_drvdata(pdev); |
9395 | int err; | ||
9396 | |||
9397 | printk(KERN_INFO "Coming out of suspend...\n"); | ||
9398 | 9361 | ||
9399 | pci_set_power_state(pdev, PCI_D0); | 9362 | pci_set_power_state(pdev, PCI_D0); |
9400 | err = pci_enable_device(pdev); | ||
9401 | pci_restore_state(pdev); | ||
9402 | 9363 | ||
9403 | /* | 9364 | if (priv->is_open) |
9404 | * Suspend/Resume resets the PCI configuration space, so we have to | 9365 | iwl4965_mac_start(priv->hw); |
9405 | * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries | ||
9406 | * from interfering with C3 CPU state. pci_restore_state won't help | ||
9407 | * here since it only restores the first 64 bytes pci config header. | ||
9408 | */ | ||
9409 | pci_write_config_byte(pdev, 0x41, 0x00); | ||
9410 | |||
9411 | iwl4965_resume(priv); | ||
9412 | 9366 | ||
9367 | clear_bit(STATUS_IN_SUSPEND, &priv->status); | ||
9413 | return 0; | 9368 | return 0; |
9414 | } | 9369 | } |
9415 | 9370 | ||