Skip to content

Commit

Permalink
PCI: aardvark: Issue PERST via GPIO
Browse files Browse the repository at this point in the history
Add support for issuing PERST via GPIO specified in 'reset-gpios'
property (as described in PCI device tree bindings).

Some buggy cards (e.g. Compex WLE900VX or WLE1216) are not detected
after reboot when PERST is not issued during driver initialization.

If bootloader already enabled link training then issuing PERST has no
effect for some buggy cards (e.g. Compex WLE900VX) and these cards are
not detected. We therefore clear the LINK_TRAINING_EN register before.

It was observed that Compex WLE900VX card needs to be in PERST reset
for at least 10ms if bootloader enabled link training.

Tested on Turris MOX.

Link: https://lore.kernel.org/r/20200430080625.26070-6-pali@kernel.org
Tested-by: Tomasz Maciej Nowak <tmn505@gmail.com>
Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
  • Loading branch information
Pali Rohár authored and Lorenzo Pieralisi committed May 18, 2020
1 parent 43fc679 commit 5169a98
Showing 1 changed file with 42 additions and 1 deletion.
43 changes: 42 additions & 1 deletion drivers/pci/controller/pci-aardvark.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
Expand All @@ -18,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_pci.h>

#include "../pci.h"
Expand Down Expand Up @@ -204,6 +206,7 @@ struct advk_pcie {
int root_bus_nr;
int link_gen;
struct pci_bridge_emul bridge;
struct gpio_desc *reset_gpio;
};

static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg)
Expand Down Expand Up @@ -330,10 +333,31 @@ static void advk_pcie_train_link(struct advk_pcie *pcie)
dev_err(dev, "link never came up\n");
}

static void advk_pcie_issue_perst(struct advk_pcie *pcie)
{
u32 reg;

if (!pcie->reset_gpio)
return;

/* PERST does not work for some cards when link training is enabled */
reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
reg &= ~LINK_TRAINING_EN;
advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);

/* 10ms delay is needed for some cards */
dev_info(&pcie->pdev->dev, "issuing PERST via reset GPIO for 10ms\n");
gpiod_set_value_cansleep(pcie->reset_gpio, 1);
usleep_range(10000, 11000);
gpiod_set_value_cansleep(pcie->reset_gpio, 0);
}

static void advk_pcie_setup_hw(struct advk_pcie *pcie)
{
u32 reg;

advk_pcie_issue_perst(pcie);

/* Set to Direct mode */
reg = advk_readl(pcie, CTRL_CONFIG_REG);
reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT);
Expand Down Expand Up @@ -406,7 +430,8 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)

/*
* PERST# signal could have been asserted by pinctrl subsystem before
* probe() callback has been called, making the endpoint going into
* probe() callback has been called or issued explicitly by reset gpio
* function advk_pcie_issue_perst(), making the endpoint going into
* fundamental reset. As required by PCI Express spec a delay for at
* least 100ms after such a reset before link training is needed.
*/
Expand Down Expand Up @@ -1046,6 +1071,22 @@ static int advk_pcie_probe(struct platform_device *pdev)
}
pcie->root_bus_nr = bus->start;

pcie->reset_gpio = devm_gpiod_get_from_of_node(dev, dev->of_node,
"reset-gpios", 0,
GPIOD_OUT_LOW,
"pcie1-reset");
ret = PTR_ERR_OR_ZERO(pcie->reset_gpio);
if (ret) {
if (ret == -ENOENT) {
pcie->reset_gpio = NULL;
} else {
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get reset-gpio: %i\n",
ret);
return ret;
}
}

ret = of_pci_get_max_link_speed(dev->of_node);
if (ret <= 0 || ret > 3)
pcie->link_gen = 3;
Expand Down

0 comments on commit 5169a98

Please sign in to comment.