]> git.baikalelectronics.ru Git - kernel.git/commitdiff
PCI: brcmstb: Disable/enable regulators in suspend/resume
authorJim Quinlan <jim2101024@gmail.com>
Mon, 25 Jul 2022 15:12:54 +0000 (11:12 -0400)
committerBjorn Helgaas <bhelgaas@google.com>
Wed, 27 Jul 2022 16:53:08 +0000 (11:53 -0500)
If we found power regulators for a device below the Root Port, disable them
during suspend and re-enable them during resume.

If any downstream device can be a wakeup device, do not turn off the
regulators as the device will need them on.

[bhelgaas: drop unused regulator_oops, skip wrapping of .add_bus()/
.remove_bus(), move brcm_pcie_start_link() to .add_bus() in previous patch,
squash WOL checking into this patch]
Link: https://lore.kernel.org/r/20220725151258.42574-6-jim2101024@gmail.com
Link: https://lore.kernel.org/r/20220725151258.42574-7-jim2101024@gmail.com
Signed-off-by: Jim Quinlan <jim2101024@gmail.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Florian Fainelli <f.fainelli@gmail.com>
drivers/pci/controller/pcie-brcmstb.c

index 3092e262f07b09273a9fded2f2501ef540bdcce1..23b44a88be1b53a32426ebab3d7f632d2290698d 100644 (file)
@@ -262,6 +262,7 @@ struct brcm_pcie {
        void                    (*perst_set)(struct brcm_pcie *pcie, u32 val);
        void                    (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
        struct subdev_regulators *sr;
+       bool                    ep_wakeup_capable;
 };
 
 static inline bool is_bmips(const struct brcm_pcie *pcie)
@@ -1244,9 +1245,21 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
        pcie->bridge_sw_init_set(pcie, 1);
 }
 
-static int brcm_pcie_suspend(struct device *dev)
+static int pci_dev_may_wakeup(struct pci_dev *dev, void *data)
+{
+       bool *ret = data;
+
+       if (device_may_wakeup(&dev->dev)) {
+               *ret = true;
+               dev_info(&dev->dev, "Possible wake-up device; regulators will not be disabled\n");
+       }
+       return (int) *ret;
+}
+
+static int brcm_pcie_suspend_noirq(struct device *dev)
 {
        struct brcm_pcie *pcie = dev_get_drvdata(dev);
+       struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
        int ret;
 
        brcm_pcie_turn_off(pcie);
@@ -1264,12 +1277,31 @@ static int brcm_pcie_suspend(struct device *dev)
                return ret;
        }
 
+       if (pcie->sr) {
+               /*
+                * Now turn off the regulators, but if at least one
+                * downstream device is enabled as a wake-up source, do not
+                * turn off regulators.
+                */
+               pcie->ep_wakeup_capable = false;
+               pci_walk_bus(bridge->bus, pci_dev_may_wakeup,
+                            &pcie->ep_wakeup_capable);
+               if (!pcie->ep_wakeup_capable) {
+                       ret = regulator_bulk_disable(pcie->sr->num_supplies,
+                                                    pcie->sr->supplies);
+                       if (ret) {
+                               dev_err(dev, "Could not turn off regulators\n");
+                               reset_control_reset(pcie->rescal);
+                               return ret;
+                       }
+               }
+       }
        clk_disable_unprepare(pcie->clk);
 
        return 0;
 }
 
-static int brcm_pcie_resume(struct device *dev)
+static int brcm_pcie_resume_noirq(struct device *dev)
 {
        struct brcm_pcie *pcie = dev_get_drvdata(dev);
        void __iomem *base;
@@ -1304,15 +1336,37 @@ static int brcm_pcie_resume(struct device *dev)
        if (ret)
                goto err_reset;
 
+       if (pcie->sr) {
+               if (pcie->ep_wakeup_capable) {
+                       /*
+                        * We are resuming from a suspend.  In the suspend we
+                        * did not disable the power supplies, so there is
+                        * no need to enable them (and falsely increase their
+                        * usage count).
+                        */
+                       pcie->ep_wakeup_capable = false;
+               } else {
+                       ret = regulator_bulk_enable(pcie->sr->num_supplies,
+                                                   pcie->sr->supplies);
+                       if (ret) {
+                               dev_err(dev, "Could not turn on regulators\n");
+                               goto err_reset;
+                       }
+               }
+       }
+
        ret = brcm_pcie_start_link(pcie);
        if (ret)
-               goto err_reset;
+               goto err_regulator;
 
        if (pcie->msi)
                brcm_msi_set_regs(pcie->msi);
 
        return 0;
 
+err_regulator:
+       if (pcie->sr)
+               regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
 err_reset:
        reset_control_rearm(pcie->rescal);
 err_disable_clk:
@@ -1541,8 +1595,8 @@ fail:
 MODULE_DEVICE_TABLE(of, brcm_pcie_match);
 
 static const struct dev_pm_ops brcm_pcie_pm_ops = {
-       .suspend = brcm_pcie_suspend,
-       .resume = brcm_pcie_resume,
+       .suspend_noirq = brcm_pcie_suspend_noirq,
+       .resume_noirq = brcm_pcie_resume_noirq,
 };
 
 static struct platform_driver brcm_pcie_driver = {