aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@intel.com>2015-08-18 09:02:38 -0400
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2016-02-01 09:40:17 -0500
commit4cbb8e50338a2f2453ba399ce52562e0a111fc1f (patch)
tree97268e5142b0aa391441ae818dbbb0f782b73141
parentb3ff1270566d41eb8ab2d67844bf17b7fa9fee78 (diff)
iwlwifi: pcie: add RTPM support when wifi is enabled
Enable runtime power management (RTPM) for PCIe devices and implement the corresponding functions to enable D0i3 mode when the device is idle. Additionally, remove some unnecessary #ifdef's because the RTPM code will not be called if runtime PM is not configured. Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/drv.c98
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/internal.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans.c43
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/tx.c17
5 files changed, 141 insertions, 27 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index 82fb3a97a46d..fe170a3fb1a6 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -736,6 +736,11 @@ enum iwl_plat_pm_mode {
736 IWL_PLAT_PM_MODE_D0I3, 736 IWL_PLAT_PM_MODE_D0I3,
737}; 737};
738 738
739/* Max time to wait for trans to become idle/non-idle on d0i3
740 * enter/exit (in msecs).
741 */
742#define IWL_TRANS_IDLE_TIMEOUT 2000
743
739/** 744/**
740 * struct iwl_trans - transport common data 745 * struct iwl_trans - transport common data
741 * 746 *
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index 676d2391eb66..16b579a5aa6b 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -67,9 +67,7 @@
67#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 67#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
68 68
69#include <linux/module.h> 69#include <linux/module.h>
70#ifdef CONFIG_IWLWIFI_PCIE_RTPM
71#include <linux/pm_runtime.h> 70#include <linux/pm_runtime.h>
72#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
73#include <linux/pci.h> 71#include <linux/pci.h>
74#include <linux/pci-aspm.h> 72#include <linux/pci-aspm.h>
75#include <linux/acpi.h> 73#include <linux/acpi.h>
@@ -627,13 +625,15 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
627 if (ret) 625 if (ret)
628 goto out_free_drv; 626 goto out_free_drv;
629 627
630#ifdef CONFIG_IWLWIFI_PCIE_RTPM 628 /* if RTPM is in use, enable it in our device */
631 pm_runtime_set_active(&pdev->dev); 629 if (iwl_trans->runtime_pm_mode != IWL_PLAT_PM_MODE_DISABLED) {
632 pm_runtime_set_autosuspend_delay(&pdev->dev, 630 pm_runtime_set_active(&pdev->dev);
631 pm_runtime_set_autosuspend_delay(&pdev->dev,
633 iwlwifi_mod_params.d0i3_entry_delay); 632 iwlwifi_mod_params.d0i3_entry_delay);
634 pm_runtime_use_autosuspend(&pdev->dev); 633 pm_runtime_use_autosuspend(&pdev->dev);
635 pm_runtime_allow(&pdev->dev); 634 pm_runtime_allow(&pdev->dev);
636#endif 635 }
636
637 return 0; 637 return 0;
638 638
639out_free_drv: 639out_free_drv:
@@ -700,17 +700,90 @@ static int iwl_pci_resume(struct device *device)
700 return 0; 700 return 0;
701} 701}
702 702
703int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans)
704{
705 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
706 int ret;
707
708 if (test_bit(STATUS_FW_ERROR, &trans->status))
709 return 0;
710
711 set_bit(STATUS_TRANS_GOING_IDLE, &trans->status);
712
713 /* config the fw */
714 ret = iwl_op_mode_enter_d0i3(trans->op_mode);
715 if (ret == 1) {
716 IWL_DEBUG_RPM(trans, "aborting d0i3 entrance\n");
717 clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status);
718 return -EBUSY;
719 }
720 if (ret)
721 goto err;
722
723 ret = wait_event_timeout(trans_pcie->d0i3_waitq,
724 test_bit(STATUS_TRANS_IDLE, &trans->status),
725 msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT));
726 if (!ret) {
727 IWL_ERR(trans, "Timeout entering D0i3\n");
728 ret = -ETIMEDOUT;
729 goto err;
730 }
731
732 clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status);
733
734 return 0;
735err:
736 clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status);
737 iwl_trans_fw_error(trans);
738 return ret;
739}
740
741int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans)
742{
743 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
744 int ret;
745
746 /* sometimes a D0i3 entry is not followed through */
747 if (!test_bit(STATUS_TRANS_IDLE, &trans->status))
748 return 0;
749
750 /* config the fw */
751 ret = iwl_op_mode_exit_d0i3(trans->op_mode);
752 if (ret)
753 goto err;
754
755 /* we clear STATUS_TRANS_IDLE only when D0I3_END command is completed */
756
757 ret = wait_event_timeout(trans_pcie->d0i3_waitq,
758 !test_bit(STATUS_TRANS_IDLE, &trans->status),
759 msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT));
760 if (!ret) {
761 IWL_ERR(trans, "Timeout exiting D0i3\n");
762 ret = -ETIMEDOUT;
763 goto err;
764 }
765
766 return 0;
767err:
768 clear_bit(STATUS_TRANS_IDLE, &trans->status);
769 iwl_trans_fw_error(trans);
770 return ret;
771}
772
703#ifdef CONFIG_IWLWIFI_PCIE_RTPM 773#ifdef CONFIG_IWLWIFI_PCIE_RTPM
704static int iwl_pci_runtime_suspend(struct device *device) 774static int iwl_pci_runtime_suspend(struct device *device)
705{ 775{
706 struct pci_dev *pdev = to_pci_dev(device); 776 struct pci_dev *pdev = to_pci_dev(device);
707 struct iwl_trans *trans = pci_get_drvdata(pdev); 777 struct iwl_trans *trans = pci_get_drvdata(pdev);
778 int ret;
708 779
709 IWL_DEBUG_RPM(trans, "entering runtime suspend\n"); 780 IWL_DEBUG_RPM(trans, "entering runtime suspend\n");
710 781
711 /* For now we only allow D0I3 if the device is off */ 782 if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
712 if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) 783 ret = iwl_pci_fw_enter_d0i3(trans);
713 return -EBUSY; 784 if (ret < 0)
785 return ret;
786 }
714 787
715 trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3; 788 trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3;
716 789
@@ -729,7 +802,8 @@ static int iwl_pci_runtime_resume(struct device *device)
729 802
730 iwl_trans_d3_resume(trans, &d3_status, false); 803 iwl_trans_d3_resume(trans, &d3_status, false);
731 804
732 trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; 805 if (test_bit(STATUS_DEVICE_ENABLED, &trans->status))
806 return iwl_pci_fw_exit_d0i3(trans);
733 807
734 return 0; 808 return 0;
735} 809}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
index bdda7028c393..7bc02e0cdd93 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
@@ -2,6 +2,7 @@
2 * 2 *
3 * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved. 3 * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved.
4 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH 4 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
5 * Copyright(c) 2016 Intel Deutschland GmbH
5 * 6 *
6 * Portions of this file are derived from the ipw3945 project, as well 7 * Portions of this file are derived from the ipw3945 project, as well
7 * as portions of the ieee80211 subsystem header files. 8 * as portions of the ieee80211 subsystem header files.
@@ -374,6 +375,7 @@ struct iwl_trans_pcie {
374 bool ucode_write_complete; 375 bool ucode_write_complete;
375 wait_queue_head_t ucode_write_waitq; 376 wait_queue_head_t ucode_write_waitq;
376 wait_queue_head_t wait_command_queue; 377 wait_queue_head_t wait_command_queue;
378 wait_queue_head_t d0i3_waitq;
377 379
378 u8 cmd_queue; 380 u8 cmd_queue;
379 u8 cmd_fifo; 381 u8 cmd_fifo;
@@ -594,4 +596,7 @@ static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
594} 596}
595#endif 597#endif
596 598
599int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans);
600int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans);
601
597#endif /* __iwl_trans_int_pcie_h__ */ 602#endif /* __iwl_trans_int_pcie_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index db94fe1e1bc6..cfdc7f6e554a 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -72,9 +72,7 @@
72#include <linux/bitops.h> 72#include <linux/bitops.h>
73#include <linux/gfp.h> 73#include <linux/gfp.h>
74#include <linux/vmalloc.h> 74#include <linux/vmalloc.h>
75#ifdef CONFIG_IWLWIFI_PCIE_RTPM
76#include <linux/pm_runtime.h> 75#include <linux/pm_runtime.h>
77#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
78 76
79#include "iwl-drv.h" 77#include "iwl-drv.h"
80#include "iwl-trans.h" 78#include "iwl-trans.h"
@@ -1197,9 +1195,6 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
1197 if (hw_rfkill != was_hw_rfkill) 1195 if (hw_rfkill != was_hw_rfkill)
1198 iwl_trans_pcie_rf_kill(trans, hw_rfkill); 1196 iwl_trans_pcie_rf_kill(trans, hw_rfkill);
1199 1197
1200#ifdef CONFIG_IWLWIFI_PCIE_RTPM
1201 pm_runtime_put_sync(trans->dev);
1202#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
1203 /* re-take ownership to prevent other users from stealing the deivce */ 1198 /* re-take ownership to prevent other users from stealing the deivce */
1204 iwl_pcie_prepare_card_hw(trans); 1199 iwl_pcie_prepare_card_hw(trans);
1205} 1200}
@@ -1359,9 +1354,10 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
1359 /* ... rfkill can call stop_device and set it false if needed */ 1354 /* ... rfkill can call stop_device and set it false if needed */
1360 iwl_trans_pcie_rf_kill(trans, hw_rfkill); 1355 iwl_trans_pcie_rf_kill(trans, hw_rfkill);
1361 1356
1362#ifdef CONFIG_IWLWIFI_PCIE_RTPM 1357 /* Make sure we sync here, because we'll need full access later */
1363 pm_runtime_get_sync(trans->dev); 1358 if (low_power)
1364#endif /* CONFIG_IWLWIFI_PCIE_RTPM */ 1359 pm_runtime_resume(trans->dev);
1360
1365 return 0; 1361 return 0;
1366} 1362}
1367 1363
@@ -1485,10 +1481,9 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
1485 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 1481 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
1486 int i; 1482 int i;
1487 1483
1488#ifdef CONFIG_IWLWIFI_PCIE_RTPM
1489 /* TODO: check if this is really needed */ 1484 /* TODO: check if this is really needed */
1490 pm_runtime_disable(trans->dev); 1485 pm_runtime_disable(trans->dev);
1491#endif /* CONFIG_IWLWIFI_PCIE_RTPM */ 1486
1492 synchronize_irq(trans_pcie->pci_dev->irq); 1487 synchronize_irq(trans_pcie->pci_dev->irq);
1493 1488
1494 iwl_pcie_tx_free(trans); 1489 iwl_pcie_tx_free(trans);
@@ -1844,9 +1839,7 @@ void iwl_trans_pcie_ref(struct iwl_trans *trans)
1844 spin_lock_irqsave(&trans_pcie->ref_lock, flags); 1839 spin_lock_irqsave(&trans_pcie->ref_lock, flags);
1845 IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); 1840 IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count);
1846 trans_pcie->ref_count++; 1841 trans_pcie->ref_count++;
1847#ifdef CONFIG_IWLWIFI_PCIE_RTPM
1848 pm_runtime_get(&trans_pcie->pci_dev->dev); 1842 pm_runtime_get(&trans_pcie->pci_dev->dev);
1849#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
1850 spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); 1843 spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
1851} 1844}
1852 1845
@@ -1865,10 +1858,9 @@ void iwl_trans_pcie_unref(struct iwl_trans *trans)
1865 return; 1858 return;
1866 } 1859 }
1867 trans_pcie->ref_count--; 1860 trans_pcie->ref_count--;
1868#ifdef CONFIG_IWLWIFI_PCIE_RTPM 1861
1869 pm_runtime_mark_last_busy(&trans_pcie->pci_dev->dev); 1862 pm_runtime_mark_last_busy(&trans_pcie->pci_dev->dev);
1870 pm_runtime_put_autosuspend(&trans_pcie->pci_dev->dev); 1863 pm_runtime_put_autosuspend(&trans_pcie->pci_dev->dev);
1871#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
1872 1864
1873 spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); 1865 spin_unlock_irqrestore(&trans_pcie->ref_lock, flags);
1874} 1866}
@@ -2536,6 +2528,22 @@ static struct iwl_trans_dump_data
2536 return dump_data; 2528 return dump_data;
2537} 2529}
2538 2530
2531#ifdef CONFIG_PM_SLEEP
2532static int iwl_trans_pcie_suspend(struct iwl_trans *trans)
2533{
2534 if (trans->runtime_pm_mode == IWL_PLAT_PM_MODE_D0I3)
2535 return iwl_pci_fw_enter_d0i3(trans);
2536
2537 return 0;
2538}
2539
2540static void iwl_trans_pcie_resume(struct iwl_trans *trans)
2541{
2542 if (trans->runtime_pm_mode == IWL_PLAT_PM_MODE_D0I3)
2543 iwl_pci_fw_exit_d0i3(trans);
2544}
2545#endif /* CONFIG_PM_SLEEP */
2546
2539static const struct iwl_trans_ops trans_ops_pcie = { 2547static const struct iwl_trans_ops trans_ops_pcie = {
2540 .start_hw = iwl_trans_pcie_start_hw, 2548 .start_hw = iwl_trans_pcie_start_hw,
2541 .op_mode_leave = iwl_trans_pcie_op_mode_leave, 2549 .op_mode_leave = iwl_trans_pcie_op_mode_leave,
@@ -2546,6 +2554,11 @@ static const struct iwl_trans_ops trans_ops_pcie = {
2546 .d3_suspend = iwl_trans_pcie_d3_suspend, 2554 .d3_suspend = iwl_trans_pcie_d3_suspend,
2547 .d3_resume = iwl_trans_pcie_d3_resume, 2555 .d3_resume = iwl_trans_pcie_d3_resume,
2548 2556
2557#ifdef CONFIG_PM_SLEEP
2558 .suspend = iwl_trans_pcie_suspend,
2559 .resume = iwl_trans_pcie_resume,
2560#endif /* CONFIG_PM_SLEEP */
2561
2549 .send_cmd = iwl_trans_pcie_send_hcmd, 2562 .send_cmd = iwl_trans_pcie_send_hcmd,
2550 2563
2551 .tx = iwl_trans_pcie_tx, 2564 .tx = iwl_trans_pcie_tx,
@@ -2735,6 +2748,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
2735 /* Initialize the wait queue for commands */ 2748 /* Initialize the wait queue for commands */
2736 init_waitqueue_head(&trans_pcie->wait_command_queue); 2749 init_waitqueue_head(&trans_pcie->wait_command_queue);
2737 2750
2751 init_waitqueue_head(&trans_pcie->d0i3_waitq);
2752
2738 ret = iwl_pcie_alloc_ict(trans); 2753 ret = iwl_pcie_alloc_ict(trans);
2739 if (ret) 2754 if (ret)
2740 goto out_pci_disable_msi; 2755 goto out_pci_disable_msi;
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
index b0b0fd9e2eff..c499345ba526 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
@@ -1,7 +1,8 @@
1/****************************************************************************** 1/******************************************************************************
2 * 2 *
3 * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. 3 * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
4 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 4 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
5 * Copyright(c) 2016 Intel Deutschland GmbH
5 * 6 *
6 * Portions of this file are derived from the ipw3945 project, as well 7 * Portions of this file are derived from the ipw3945 project, as well
7 * as portions of the ieee80211 subsystem header files. 8 * as portions of the ieee80211 subsystem header files.
@@ -1727,6 +1728,20 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
1727 wake_up(&trans_pcie->wait_command_queue); 1728 wake_up(&trans_pcie->wait_command_queue);
1728 } 1729 }
1729 1730
1731 if (meta->flags & CMD_MAKE_TRANS_IDLE) {
1732 IWL_DEBUG_INFO(trans, "complete %s - mark trans as idle\n",
1733 iwl_get_cmd_string(trans, cmd->hdr.cmd));
1734 set_bit(STATUS_TRANS_IDLE, &trans->status);
1735 wake_up(&trans_pcie->d0i3_waitq);
1736 }
1737
1738 if (meta->flags & CMD_WAKE_UP_TRANS) {
1739 IWL_DEBUG_INFO(trans, "complete %s - clear trans idle flag\n",
1740 iwl_get_cmd_string(trans, cmd->hdr.cmd));
1741 clear_bit(STATUS_TRANS_IDLE, &trans->status);
1742 wake_up(&trans_pcie->d0i3_waitq);
1743 }
1744
1730 meta->flags = 0; 1745 meta->flags = 0;
1731 1746
1732 spin_unlock_bh(&txq->lock); 1747 spin_unlock_bh(&txq->lock);