diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 5335c2b140fb..773ab65b2afa 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -34,6 +34,7 @@ #include #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); - } 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); + list_for_each_entry(port, &bridge->ports, list) { + list_for_each_entry(perst, &port->perst, list) + gpiod_direction_output(perst->desc, 1); } + } else { + 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)