[PATCH V2 1/4] PCI: imx6: Add pci host wakeup support on imx platforms.

From: Sherry Sun
Date: Wed Dec 13 2023 - 04:28:46 EST


Add pci host wakeup feature for imx platforms.
Example of configuring the corresponding dts property under the PCI
node:
wake-gpios = <&gpio5 21 GPIO_ACTIVE_LOW>;

Signed-off-by: Sherry Sun <sherry.sun@xxxxxxx>
Reviewed-by: Richard Zhu <hongxing.zhu@xxxxxxx>
---
drivers/pci/controller/dwc/pci-imx6.c | 60 +++++++++++++++++++++++++++
1 file changed, 60 insertions(+)

diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 74703362aeec..0cf7f21adff8 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -72,6 +72,7 @@ struct imx6_pcie_drvdata {
struct imx6_pcie {
struct dw_pcie *pci;
int reset_gpio;
+ int host_wake_irq;
bool gpio_active_high;
bool link_is_up;
struct clk *pcie_bus;
@@ -1237,11 +1238,44 @@ static int imx6_pcie_resume_noirq(struct device *dev)
return 0;
}

+static int imx6_pcie_suspend(struct device *dev)
+{
+ struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
+
+ if (imx6_pcie->host_wake_irq >= 0)
+ enable_irq_wake(imx6_pcie->host_wake_irq);
+
+ return 0;
+}
+
+static int imx6_pcie_resume(struct device *dev)
+{
+ struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev);
+
+ if (imx6_pcie->host_wake_irq >= 0)
+ disable_irq_wake(imx6_pcie->host_wake_irq);
+
+ return 0;
+}
+
static const struct dev_pm_ops imx6_pcie_pm_ops = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(imx6_pcie_suspend_noirq,
imx6_pcie_resume_noirq)
+ SYSTEM_SLEEP_PM_OPS(imx6_pcie_suspend, imx6_pcie_resume)
};

+irqreturn_t host_wake_irq_handler(int irq, void *priv)
+{
+ struct imx6_pcie *imx6_pcie = priv;
+ struct device *dev = imx6_pcie->pci->dev;
+
+ /* Notify PM core we are wakeup source */
+ pm_wakeup_event(dev, 0);
+ pm_system_wakeup();
+
+ return IRQ_HANDLED;
+}
+
static int imx6_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1250,6 +1284,7 @@ static int imx6_pcie_probe(struct platform_device *pdev)
struct device_node *np;
struct resource *dbi_base;
struct device_node *node = dev->of_node;
+ struct gpio_desc *host_wake_gpio;
int ret;
u16 val;

@@ -1457,6 +1492,26 @@ static int imx6_pcie_probe(struct platform_device *pdev)
val |= PCI_MSI_FLAGS_ENABLE;
dw_pcie_writew_dbi(pci, offset + PCI_MSI_FLAGS, val);
}
+
+ /* host wakeup support */
+ imx6_pcie->host_wake_irq = -1;
+ host_wake_gpio = devm_gpiod_get_optional(dev, "wake", GPIOD_IN);
+ if (IS_ERR(host_wake_gpio))
+ return PTR_ERR(host_wake_gpio);
+
+ if (host_wake_gpio != NULL) {
+ imx6_pcie->host_wake_irq = gpiod_to_irq(host_wake_gpio);
+ ret = devm_request_threaded_irq(dev, imx6_pcie->host_wake_irq, NULL,
+ host_wake_irq_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ "host_wake", imx6_pcie);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request host_wake_irq\n");
+
+ ret = device_init_wakeup(dev, true);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to init host_wake_irq\n");
+ }
}

return 0;
@@ -1466,6 +1521,11 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
{
struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev);

+ if (imx6_pcie->host_wake_irq >= 0) {
+ device_init_wakeup(&pdev->dev, false);
+ disable_irq(imx6_pcie->host_wake_irq);
+ }
+
/* bring down link, so bootloader gets clean state in case of reboot */
imx6_pcie_assert_core_reset(imx6_pcie);
}
--
2.34.1