]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: imx6: Parse 'reset-gpios' in Root Port nodes
authorSherry Sun <sherry.sun@nxp.com>
Wed, 22 Apr 2026 09:35:41 +0000 (17:35 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 18 May 2026 23:05:27 +0000 (18:05 -0500)
The current DT binding for pci-imx6 specifies the 'reset-gpios' property
in the host bridge node. However, the PERST# signal logically belongs to
individual Root Ports rather than the host bridge itself. This becomes
important when supporting PCIe Key E connector and the PCI power control
framework for pci-imx6 driver, which requires properties to be specified
in Root Port nodes.

Parse 'reset-gpios' from Root Port nodes and the PCIe bridge nodes under
the Root Port using the common helper pci_host_common_parse_ports(), and
update the reset GPIO handling to use the parsed port list from
bridge->ports. To maintain DT backwards compatibility, fall back to the
legacy method of parsing the host bridge node if the reset property is not
present in the Root Port nodes.

Since now the reset GPIO is obtained with GPIOD_ASIS flag, it may be in
input mode, so use gpiod_direction_output() instead of
gpiod_set_value_cansleep() to ensure the reset GPIO is properly
configured as output before setting its value.

Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Richard Zhu <hongxing.zhu@nxp.com>
Link: https://patch.msgid.link/20260422093549.407022-5-sherry.sun@nxp.com
drivers/pci/controller/dwc/pci-imx6.c

index 5335c2b140fb548b3254f1cbc8fc51a5a18656e5..773ab65b2aface07e4d0391e4e0dd12fd60f8910 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/pm_runtime.h>
 
 #include "../../pci.h"
+#include "../pci-host-common.h"
 #include "pcie-designware.h"
 
 #define IMX8MQ_GPR_PCIE_REF_USE_PAD            BIT(9)
@@ -152,7 +153,6 @@ struct imx_lut_data {
 
 struct imx_pcie {
        struct dw_pcie          *pci;
-       struct gpio_desc        *reset_gpiod;
        struct clk_bulk_data    *clks;
        int                     num_clks;
        bool                    supports_clkreq;
@@ -1219,6 +1219,41 @@ static void imx_pcie_disable_device(struct pci_host_bridge *bridge,
        imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev));
 }
 
+static int imx_pcie_parse_legacy_binding(struct imx_pcie *pcie)
+{
+       struct device *dev = pcie->pci->dev;
+       struct pci_host_bridge *bridge = pcie->pci->pp.bridge;
+       struct pci_host_port *port;
+       struct pci_host_perst *perst;
+       struct gpio_desc *reset;
+
+       reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+       if (IS_ERR(reset))
+               return PTR_ERR(reset);
+
+       if (!reset)
+               return 0;
+
+       port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+       if (!port)
+               return -ENOMEM;
+
+       perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
+       if (!perst)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&port->perst);
+       perst->desc = reset;
+       INIT_LIST_HEAD(&perst->list);
+       list_add_tail(&perst->list, &port->perst);
+
+       INIT_LIST_HEAD(&port->list);
+       list_add_tail(&port->list, &bridge->ports);
+
+       return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
+                                       &bridge->ports);
+}
+
 static void imx_pcie_vpcie_aux_disable(void *data)
 {
        struct regulator *vpcie_aux = data;
@@ -1228,14 +1263,26 @@ static void imx_pcie_vpcie_aux_disable(void *data)
 
 static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert)
 {
+       struct dw_pcie *pci = imx_pcie->pci;
+       struct pci_host_bridge *bridge = pci->pp.bridge;
+       struct pci_host_perst *perst;
+       struct pci_host_port *port;
+
+       if (!bridge || list_empty(&bridge->ports))
+               return;
+
        if (assert) {
-               gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1);
+               list_for_each_entry(port, &bridge->ports, list) {
+                       list_for_each_entry(perst, &port->perst, list)
+                               gpiod_direction_output(perst->desc, 1);
+               }
        } else {
-               if (imx_pcie->reset_gpiod) {
-                       msleep(PCIE_T_PVPERL_MS);
-                       gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0);
-                       msleep(PCIE_RESET_CONFIG_WAIT_MS);
+               mdelay(PCIE_T_PVPERL_MS);
+               list_for_each_entry(port, &bridge->ports, list) {
+                       list_for_each_entry(perst, &port->perst, list)
+                               gpiod_direction_output(perst->desc, 0);
                }
+               mdelay(PCIE_RESET_CONFIG_WAIT_MS);
        }
 }
 
@@ -1244,8 +1291,28 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
        struct device *dev = pci->dev;
        struct imx_pcie *imx_pcie = to_imx_pcie(pci);
+       struct pci_host_bridge *bridge = pp->bridge;
        int ret;
 
+       if (bridge && list_empty(&bridge->ports)) {
+               /* Parse Root Port nodes if present */
+               ret = pci_host_common_parse_ports(dev, bridge);
+               if (ret) {
+                       if (ret != -ENODEV) {
+                               dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret);
+                               return ret;
+                       }
+
+                       /*
+                        * Fall back to legacy binding for DT backwards
+                        * compatibility
+                        */
+                       ret = imx_pcie_parse_legacy_binding(imx_pcie);
+                       if (ret)
+                               return ret;
+               }
+       }
+
        imx_pcie_assert_perst(imx_pcie, true);
 
        /* Keep 3.3Vaux supply enabled for entire PCIe controller lifecycle */
@@ -1699,13 +1766,6 @@ static int imx_pcie_probe(struct platform_device *pdev)
                        return PTR_ERR(imx_pcie->phy_base);
        }
 
-       /* Fetch GPIOs */
-       imx_pcie->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
-       if (IS_ERR(imx_pcie->reset_gpiod))
-               return dev_err_probe(dev, PTR_ERR(imx_pcie->reset_gpiod),
-                                    "unable to get reset gpio\n");
-       gpiod_set_consumer_name(imx_pcie->reset_gpiod, "PCIe reset");
-
        /* Fetch clocks */
        imx_pcie->num_clks = devm_clk_bulk_get_all(dev, &imx_pcie->clks);
        if (imx_pcie->num_clks < 0)