iwlwifi: 9000: update device id and FW serial number
[cascardo/linux.git] / drivers / net / wireless / intel / iwlwifi / pcie / drv.c
index 00335ea..fb8b5ec 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -66,6 +67,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/module.h>
+#include <linux/pm_runtime.h>
 #include <linux/pci.h>
 #include <linux/pci-aspm.h>
 #include <linux/acpi.h>
@@ -483,15 +485,15 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
 /* 9000 Series */
        {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)},
        {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl5165_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0A10, iwl9560_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0010, iwl9560_2ac_cfg)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl5165_2ac_cfg)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0310, iwl5165_2ac_cfg)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0510, iwl5165_2ac_cfg)},
        {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl5165_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x0410, iwl9260_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0210, iwl9560_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0410, iwl9560_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x2526, 0x0610, iwl9560_2ac_cfg)},
 #endif /* CONFIG_IWLMVM */
 
        {0}
@@ -627,6 +629,31 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (ret)
                goto out_free_drv;
 
+       /* if RTPM is in use, enable it in our device */
+       if (iwl_trans->runtime_pm_mode != IWL_PLAT_PM_MODE_DISABLED) {
+               /* We explicitly set the device to active here to
+                * clear contingent errors.
+                */
+               pm_runtime_set_active(&pdev->dev);
+
+               pm_runtime_set_autosuspend_delay(&pdev->dev,
+                                        iwlwifi_mod_params.d0i3_entry_delay);
+               pm_runtime_use_autosuspend(&pdev->dev);
+
+               /* We are not supposed to call pm_runtime_allow() by
+                * ourselves, but let userspace enable runtime PM via
+                * sysfs.  However, since we don't enable this from
+                * userspace yet, we need to allow/forbid() ourselves.
+               */
+               pm_runtime_allow(&pdev->dev);
+       }
+
+       /* The PCI device starts with a reference taken and we are
+        * supposed to release it here.  But to simplify the
+        * interaction with the opmode, we don't do it now, but let
+        * the opmode release it when it's ready.
+        */
+
        return 0;
 
 out_free_drv:
@@ -641,7 +668,17 @@ static void iwl_pci_remove(struct pci_dev *pdev)
        struct iwl_trans *trans = pci_get_drvdata(pdev);
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
+       /* if RTPM was in use, restore it to the state before probe */
+       if (trans->runtime_pm_mode != IWL_PLAT_PM_MODE_DISABLED) {
+               /* We should not call forbid here, but we do for now.
+                * Check the comment to pm_runtime_allow() in
+                * iwl_pci_probe().
+                */
+               pm_runtime_forbid(trans->dev);
+       }
+
        iwl_drv_stop(trans_pcie->drv);
+
        iwl_trans_pcie_free(trans);
 }
 
@@ -693,15 +730,173 @@ static int iwl_pci_resume(struct device *device)
        return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume);
+int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int ret;
+
+       if (test_bit(STATUS_FW_ERROR, &trans->status))
+               return 0;
+
+       set_bit(STATUS_TRANS_GOING_IDLE, &trans->status);
+
+       /* config the fw */
+       ret = iwl_op_mode_enter_d0i3(trans->op_mode);
+       if (ret == 1) {
+               IWL_DEBUG_RPM(trans, "aborting d0i3 entrance\n");
+               clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status);
+               return -EBUSY;
+       }
+       if (ret)
+               goto err;
+
+       ret = wait_event_timeout(trans_pcie->d0i3_waitq,
+                                test_bit(STATUS_TRANS_IDLE, &trans->status),
+                                msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT));
+       if (!ret) {
+               IWL_ERR(trans, "Timeout entering D0i3\n");
+               ret = -ETIMEDOUT;
+               goto err;
+       }
+
+       clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status);
+
+       return 0;
+err:
+       clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status);
+       iwl_trans_fw_error(trans);
+       return ret;
+}
+
+int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int ret;
+
+       /* sometimes a D0i3 entry is not followed through */
+       if (!test_bit(STATUS_TRANS_IDLE, &trans->status))
+               return 0;
+
+       /* config the fw */
+       ret = iwl_op_mode_exit_d0i3(trans->op_mode);
+       if (ret)
+               goto err;
+
+       /* we clear STATUS_TRANS_IDLE only when D0I3_END command is completed */
+
+       ret = wait_event_timeout(trans_pcie->d0i3_waitq,
+                                !test_bit(STATUS_TRANS_IDLE, &trans->status),
+                                msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT));
+       if (!ret) {
+               IWL_ERR(trans, "Timeout exiting D0i3\n");
+               ret = -ETIMEDOUT;
+               goto err;
+       }
+
+       return 0;
+err:
+       clear_bit(STATUS_TRANS_IDLE, &trans->status);
+       iwl_trans_fw_error(trans);
+       return ret;
+}
+
+#ifdef CONFIG_IWLWIFI_PCIE_RTPM
+static int iwl_pci_runtime_suspend(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct iwl_trans *trans = pci_get_drvdata(pdev);
+       int ret;
+
+       IWL_DEBUG_RPM(trans, "entering runtime suspend\n");
+
+       if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+               ret = iwl_pci_fw_enter_d0i3(trans);
+               if (ret < 0)
+                       return ret;
+       }
+
+       trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3;
+
+       iwl_trans_d3_suspend(trans, false, false);
+
+       return 0;
+}
+
+static int iwl_pci_runtime_resume(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct iwl_trans *trans = pci_get_drvdata(pdev);
+       enum iwl_d3_status d3_status;
+
+       IWL_DEBUG_RPM(trans, "exiting runtime suspend (resume)\n");
+
+       iwl_trans_d3_resume(trans, &d3_status, false, false);
+
+       if (test_bit(STATUS_DEVICE_ENABLED, &trans->status))
+               return iwl_pci_fw_exit_d0i3(trans);
+
+       return 0;
+}
+
+static int iwl_pci_system_prepare(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct iwl_trans *trans = pci_get_drvdata(pdev);
+
+       IWL_DEBUG_RPM(trans, "preparing for system suspend\n");
+
+       /* This is called before entering system suspend and before
+        * the runtime resume is called.  Set the suspending flag to
+        * prevent the wakelock from being taken.
+        */
+       trans->suspending = true;
+
+       /* Wake the device up from runtime suspend before going to
+        * platform suspend.  This is needed because we don't know
+        * whether wowlan any is set and, if it's not, mac80211 will
+        * disconnect (in which case, we can't be in D0i3).
+        */
+       pm_runtime_resume(device);
+
+       return 0;
+}
+
+static void iwl_pci_system_complete(struct device *device)
+{
+       struct pci_dev *pdev = to_pci_dev(device);
+       struct iwl_trans *trans = pci_get_drvdata(pdev);
+
+       IWL_DEBUG_RPM(trans, "completing system suspend\n");
+
+       /* This is called as a counterpart to the prepare op.  It is
+        * called either when suspending fails or when suspend
+        * completed successfully.  Now there's no risk of grabbing
+        * the wakelock anymore, so we can release the suspending
+        * flag.
+        */
+       trans->suspending = false;
+}
+#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
+
+static const struct dev_pm_ops iwl_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(iwl_pci_suspend,
+                               iwl_pci_resume)
+#ifdef CONFIG_IWLWIFI_PCIE_RTPM
+       SET_RUNTIME_PM_OPS(iwl_pci_runtime_suspend,
+                          iwl_pci_runtime_resume,
+                          NULL)
+       .prepare = iwl_pci_system_prepare,
+       .complete = iwl_pci_system_complete,
+#endif /* CONFIG_IWLWIFI_PCIE_RTPM */
+};
 
 #define IWL_PM_OPS     (&iwl_dev_pm_ops)
 
-#else
+#else /* CONFIG_PM_SLEEP */
 
 #define IWL_PM_OPS     NULL
 
-#endif
+#endif /* CONFIG_PM_SLEEP */
 
 static struct pci_driver iwl_pci_driver = {
        .name = DRV_NAME,