PCI: tegra: Add support for GPIO based PERST#
authorManikanta Maddireddy <mmaddireddy@nvidia.com>
Tue, 18 Jun 2019 18:02:05 +0000 (23:32 +0530)
committerLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Fri, 5 Jul 2019 12:57:58 +0000 (13:57 +0100)
Tegra PCIe has fixed per port SFIO line to signal PERST#, which can be
controlled by AFI port register. However, if a platform routes a
different GPIO to the PCIe slot, then port register cannot control it.
Add support for GPIO based PERST# signal for such platforms. GPIO number
comes from per port PCIe device tree node. PCIe driver probe doesn't
fail if per port "reset-gpios" property is not populated, so platforms
that require this workaround must make sure that the DT property is not
missed in the corresponding device tree.

Link: https://lore.kernel.org/linux-pci/20190705084850.30777-1-jonathanh@nvidia.com/
Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
[lorenzo.pieralisi@arm.com: squashed in fix in Link]
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Thierry Reding <treding@nvidia.com>
drivers/pci/controller/pci-tegra.c

index 0fd1c1a8c1b9f8bfec9e1477713d8ec4a5b73717..6086c741cef7993067d2e19bfad2691842f7f032 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/export.h>
+#include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
 #include <linux/iopoll.h>
 #include <linux/irq.h>
@@ -399,6 +400,8 @@ struct tegra_pcie_port {
        unsigned int lanes;
 
        struct phy **phys;
+
+       struct gpio_desc *reset_gpio;
 };
 
 struct tegra_pcie_bus {
@@ -544,15 +547,23 @@ static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
        unsigned long value;
 
        /* pulse reset signal */
-       value = afi_readl(port->pcie, ctrl);
-       value &= ~AFI_PEX_CTRL_RST;
-       afi_writel(port->pcie, value, ctrl);
+       if (port->reset_gpio) {
+               gpiod_set_value(port->reset_gpio, 1);
+       } else {
+               value = afi_readl(port->pcie, ctrl);
+               value &= ~AFI_PEX_CTRL_RST;
+               afi_writel(port->pcie, value, ctrl);
+       }
 
        usleep_range(1000, 2000);
 
-       value = afi_readl(port->pcie, ctrl);
-       value |= AFI_PEX_CTRL_RST;
-       afi_writel(port->pcie, value, ctrl);
+       if (port->reset_gpio) {
+               gpiod_set_value(port->reset_gpio, 0);
+       } else {
+               value = afi_readl(port->pcie, ctrl);
+               value |= AFI_PEX_CTRL_RST;
+               afi_writel(port->pcie, value, ctrl);
+       }
 }
 
 static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
@@ -2218,6 +2229,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
                struct tegra_pcie_port *rp;
                unsigned int index;
                u32 value;
+               char *label;
 
                err = of_pci_get_devfn(port);
                if (err < 0) {
@@ -2276,6 +2288,31 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
                if (IS_ERR(rp->base))
                        return PTR_ERR(rp->base);
 
+               label = devm_kasprintf(dev, GFP_KERNEL, "pex-reset-%u", index);
+               if (!label) {
+                       dev_err(dev, "failed to create reset GPIO label\n");
+                       return -ENOMEM;
+               }
+
+               /*
+                * Returns -ENOENT if reset-gpios property is not populated
+                * and in this case fall back to using AFI per port register
+                * to toggle PERST# SFIO line.
+                */
+               rp->reset_gpio = devm_gpiod_get_from_of_node(dev, port,
+                                                            "reset-gpios", 0,
+                                                            GPIOD_OUT_LOW,
+                                                            label);
+               if (IS_ERR(rp->reset_gpio)) {
+                       if (PTR_ERR(rp->reset_gpio) == -ENOENT) {
+                               rp->reset_gpio = NULL;
+                       } else {
+                               dev_err(dev, "failed to get reset GPIO: %d\n",
+                                       err);
+                               return PTR_ERR(rp->reset_gpio);
+                       }
+               }
+
                list_add_tail(&rp->list, &pcie->ports);
        }