Commit 075a3833 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'remotes/lorenzo/pci/aardvark'

  - Train link immediately after enabling link training to avoid issues
    with Compex WLE900VX and Turris MOX devices (Pali Rohár)

  - Remove ASPM config and let the PCI core do it (Pali Rohár)

  - Interpret zero 'max-link-speed' value as invalid (Pali Rohár)

  - Respect the 'max-link-speed' property and improve link training (Marek
    Behún)

  - Issue PERST via GPIO (Pali Rohár)

  - Add PHY support (Marek Behún)

  - Use standard PCIe capability macros (Pali Rohár)

  - Document new 'max-link-speed', 'phys', and 'reset-gpios' properties
    (Marek Behún)

* remotes/lorenzo/pci/aardvark:
  dt-bindings: PCI: aardvark: Describe new properties
  PCI: aardvark: Replace custom macros by standard linux/pci_regs.h macros
  PCI: aardvark: Add PHY support
  PCI: aardvark: Add FIXME comment for PCIE_CORE_CMD_STATUS_REG access
  PCI: aardvark: Issue PERST via GPIO
  PCI: aardvark: Improve link training
  PCI: of: Zero max-link-speed value is invalid
  PCI: aardvark: Don't blindly enable ASPM L0s and don't write to read-only register
  PCI: aardvark: Train link immediately after enabling training
parents 39a1af76 e89897c9
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ contain the following properties:
 - interrupt-map-mask and interrupt-map: standard PCI properties to
   define the mapping of the PCIe interface to interrupt numbers.
 - bus-range: PCI bus numbers covered
 - phys: the PCIe PHY handle
 - max-link-speed: see pci.txt
 - reset-gpios: see pci.txt

In addition, the Device Tree describing an Aardvark PCIe controller
must include a sub-node that describes the legacy interrupt controller
@@ -48,6 +51,7 @@ Example:
				<0 0 0 2 &pcie_intc 1>,
				<0 0 0 3 &pcie_intc 2>,
				<0 0 0 4 &pcie_intc 3>;
		phys = <&comphy1 0>;
		pcie_intc: interrupt-controller {
			interrupt-controller;
			#interrupt-cells = <1>;
+219 −44
Original line number Diff line number Diff line
@@ -9,15 +9,18 @@
 */

#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/phy/phy.h>
#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"
@@ -31,16 +34,6 @@
#define     PCIE_CORE_CMD_MEM_IO_REQ_EN				BIT(2)
#define PCIE_CORE_DEV_REV_REG					0x8
#define PCIE_CORE_PCIEXP_CAP					0xc0
#define PCIE_CORE_DEV_CTRL_STATS_REG				0xc8
#define     PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE	(0 << 4)
#define     PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT	5
#define     PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE		(0 << 11)
#define     PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT	12
#define     PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ		0x2
#define PCIE_CORE_LINK_CTRL_STAT_REG				0xd0
#define     PCIE_CORE_LINK_L0S_ENTRY				BIT(0)
#define     PCIE_CORE_LINK_TRAINING				BIT(5)
#define     PCIE_CORE_LINK_WIDTH_SHIFT				20
#define PCIE_CORE_ERR_CAPCTL_REG				0x118
#define     PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX			BIT(5)
#define     PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN			BIT(6)
@@ -101,6 +94,8 @@
#define     PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE	BIT(5)
#define     PCIE_CORE_CTRL2_OB_WIN_ENABLE	BIT(6)
#define     PCIE_CORE_CTRL2_MSI_ENABLE		BIT(10)
#define PCIE_CORE_REF_CLK_REG			(CONTROL_BASE_ADDR + 0x14)
#define     PCIE_CORE_REF_CLK_TX_ENABLE		BIT(1)
#define PCIE_MSG_LOG_REG			(CONTROL_BASE_ADDR + 0x30)
#define PCIE_ISR0_REG				(CONTROL_BASE_ADDR + 0x40)
#define PCIE_MSG_PM_PME_MASK			BIT(7)
@@ -201,7 +196,10 @@ struct advk_pcie {
	struct mutex msi_used_lock;
	u16 msi_msg;
	int root_bus_nr;
	int link_gen;
	struct pci_bridge_emul bridge;
	struct gpio_desc *reset_gpio;
	struct phy *phy;
};

static inline void advk_writel(struct advk_pcie *pcie, u32 val, u64 reg)
@@ -214,6 +212,11 @@ static inline u32 advk_readl(struct advk_pcie *pcie, u64 reg)
	return readl(pcie->base + reg);
}

static inline u16 advk_read16(struct advk_pcie *pcie, u64 reg)
{
	return advk_readl(pcie, (reg & ~0x3)) >> ((reg & 0x3) * 8);
}

static int advk_pcie_link_up(struct advk_pcie *pcie)
{
	u32 val, ltssm_state;
@@ -225,20 +228,16 @@ static int advk_pcie_link_up(struct advk_pcie *pcie)

static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
{
	struct device *dev = &pcie->pdev->dev;
	int retries;

	/* check if the link is up or not */
	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
		if (advk_pcie_link_up(pcie)) {
			dev_info(dev, "link up\n");
		if (advk_pcie_link_up(pcie))
			return 0;
		}

		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
	}

	dev_err(dev, "link never came up\n");
	return -ETIMEDOUT;
}

@@ -253,10 +252,115 @@ static void advk_pcie_wait_for_retrain(struct advk_pcie *pcie)
	}
}

static int advk_pcie_train_at_gen(struct advk_pcie *pcie, int gen)
{
	int ret, neg_gen;
	u32 reg;

	/* Setup link speed */
	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
	reg &= ~PCIE_GEN_SEL_MSK;
	if (gen == 3)
		reg |= SPEED_GEN_3;
	else if (gen == 2)
		reg |= SPEED_GEN_2;
	else
		reg |= SPEED_GEN_1;
	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);

	/*
	 * Enable link training. This is not needed in every call to this
	 * function, just once suffices, but it does not break anything either.
	 */
	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
	reg |= LINK_TRAINING_EN;
	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);

	/*
	 * Start link training immediately after enabling it.
	 * This solves problems for some buggy cards.
	 */
	reg = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + PCI_EXP_LNKCTL);
	reg |= PCI_EXP_LNKCTL_RL;
	advk_writel(pcie, reg, PCIE_CORE_PCIEXP_CAP + PCI_EXP_LNKCTL);

	ret = advk_pcie_wait_for_link(pcie);
	if (ret)
		return ret;

	reg = advk_read16(pcie, PCIE_CORE_PCIEXP_CAP + PCI_EXP_LNKSTA);
	neg_gen = reg & PCI_EXP_LNKSTA_CLS;

	return neg_gen;
}

static void advk_pcie_train_link(struct advk_pcie *pcie)
{
	struct device *dev = &pcie->pdev->dev;
	int neg_gen = -1, gen;

	/*
	 * Try link training at link gen specified by device tree property
	 * 'max-link-speed'. If this fails, iteratively train at lower gen.
	 */
	for (gen = pcie->link_gen; gen > 0; --gen) {
		neg_gen = advk_pcie_train_at_gen(pcie, gen);
		if (neg_gen > 0)
			break;
	}

	if (neg_gen < 0)
		goto err;

	/*
	 * After successful training if negotiated gen is lower than requested,
	 * train again on negotiated gen. This solves some stability issues for
	 * some buggy gen1 cards.
	 */
	if (neg_gen < gen) {
		gen = neg_gen;
		neg_gen = advk_pcie_train_at_gen(pcie, gen);
	}

	if (neg_gen == gen) {
		dev_info(dev, "link up at gen %i\n", gen);
		return;
	}

err:
	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);

	/* Enable TX */
	reg = advk_readl(pcie, PCIE_CORE_REF_CLK_REG);
	reg |= PCIE_CORE_REF_CLK_TX_ENABLE;
	advk_writel(pcie, reg, PCIE_CORE_REF_CLK_REG);

	/* Set to Direct mode */
	reg = advk_readl(pcie, CTRL_CONFIG_REG);
	reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT);
@@ -275,36 +379,26 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
		PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV;
	advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG);

	/* Set PCIe Device Control and Status 1 PF0 register */
	reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE |
		(7 << PCIE_CORE_DEV_CTRL_STATS_MAX_PAYLOAD_SZ_SHIFT) |
		PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE |
		(PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SZ <<
		 PCIE_CORE_DEV_CTRL_STATS_MAX_RD_REQ_SIZE_SHIFT);
	advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG);
	/* Set PCIe Device Control register */
	reg = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + PCI_EXP_DEVCTL);
	reg &= ~PCI_EXP_DEVCTL_RELAX_EN;
	reg &= ~PCI_EXP_DEVCTL_NOSNOOP_EN;
	reg &= ~PCI_EXP_DEVCTL_READRQ;
	reg |= PCI_EXP_DEVCTL_PAYLOAD; /* Set max payload size */
	reg |= PCI_EXP_DEVCTL_READRQ_512B;
	advk_writel(pcie, reg, PCIE_CORE_PCIEXP_CAP + PCI_EXP_DEVCTL);

	/* Program PCIe Control 2 to disable strict ordering */
	reg = PCIE_CORE_CTRL2_RESERVED |
		PCIE_CORE_CTRL2_TD_ENABLE;
	advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);

	/* Set GEN2 */
	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
	reg &= ~PCIE_GEN_SEL_MSK;
	reg |= SPEED_GEN_2;
	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);

	/* Set lane X1 */
	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
	reg &= ~LANE_CNT_MSK;
	reg |= LANE_COUNT_1;
	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);

	/* Enable link training */
	reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
	reg |= LINK_TRAINING_EN;
	advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);

	/* Enable MSI */
	reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
	reg |= PCIE_CORE_CTRL2_MSI_ENABLE;
@@ -340,23 +434,22 @@ 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.
	 */
	msleep(PCI_PM_D3COLD_WAIT);

	/* Start link training */
	reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG);
	reg |= PCIE_CORE_LINK_TRAINING;
	advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG);

	advk_pcie_wait_for_link(pcie);

	reg = PCIE_CORE_LINK_L0S_ENTRY |
		(1 << PCIE_CORE_LINK_WIDTH_SHIFT);
	advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
	advk_pcie_train_link(pcie);

	/*
	 * FIXME: The following register update is suspicious. This register is
	 * applicable only when the PCI controller is configured for Endpoint
	 * mode, not as a Root Complex. But apparently when this code is
	 * removed, some cards stop working. This should be investigated and
	 * a comment explaining this should be put here.
	 */
	reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG);
	reg |= PCIE_CORE_CMD_MEM_ACCESS_EN |
		PCIE_CORE_CMD_IO_ACCESS_EN |
@@ -952,6 +1045,62 @@ static irqreturn_t advk_pcie_irq_handler(int irq, void *arg)
	return IRQ_HANDLED;
}

static void __maybe_unused advk_pcie_disable_phy(struct advk_pcie *pcie)
{
	phy_power_off(pcie->phy);
	phy_exit(pcie->phy);
}

static int advk_pcie_enable_phy(struct advk_pcie *pcie)
{
	int ret;

	if (!pcie->phy)
		return 0;

	ret = phy_init(pcie->phy);
	if (ret)
		return ret;

	ret = phy_set_mode(pcie->phy, PHY_MODE_PCIE);
	if (ret) {
		phy_exit(pcie->phy);
		return ret;
	}

	ret = phy_power_on(pcie->phy);
	if (ret) {
		phy_exit(pcie->phy);
		return ret;
	}

	return 0;
}

static int advk_pcie_setup_phy(struct advk_pcie *pcie)
{
	struct device *dev = &pcie->pdev->dev;
	struct device_node *node = dev->of_node;
	int ret = 0;

	pcie->phy = devm_of_phy_get(dev, node, NULL);
	if (IS_ERR(pcie->phy) && (PTR_ERR(pcie->phy) == -EPROBE_DEFER))
		return PTR_ERR(pcie->phy);

	/* Old bindings miss the PHY handle */
	if (IS_ERR(pcie->phy)) {
		dev_warn(dev, "PHY unavailable (%ld)\n", PTR_ERR(pcie->phy));
		pcie->phy = NULL;
		return 0;
	}

	ret = advk_pcie_enable_phy(pcie);
	if (ret)
		dev_err(dev, "Failed to initialize PHY (%d)\n", ret);

	return ret;
}

static int advk_pcie_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
@@ -992,6 +1141,32 @@ 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;
	else
		pcie->link_gen = ret;

	ret = advk_pcie_setup_phy(pcie);
	if (ret)
		return ret;

	advk_pcie_setup_hw(pcie);

	advk_sw_pci_bridge_init(pcie);
+1 −1
Original line number Diff line number Diff line
@@ -592,7 +592,7 @@ int of_pci_get_max_link_speed(struct device_node *node)
	u32 max_link_speed;

	if (of_property_read_u32(node, "max-link-speed", &max_link_speed) ||
	    max_link_speed > 4)
	    max_link_speed == 0 || max_link_speed > 4)
		return -EINVAL;

	return max_link_speed;