Commit bb78146c authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/controller/xilinx'

- Fix off-by-one error in INTx IRQ handler that caused INTx interrupts to
  be lost or delivered as the wrong interrupt (Sean Anderson)

- Rate-limit misc interrupt messages (Sean Anderson)

- Turn off the clock on probe failure and device removal (Sean Anderson)

- Add DT binding and driver support for enabling/disabling PHYs (Sean
  Anderson)

- Add PCIe phy bindings for the ZCU102 (Sean Anderson)

- Add support for Xilinx QDMA Soft IP PCIe Root Port Bridge to DT binding
  and xilinx-dma-pl driver (Thippeswamy Havalige)

* pci/controller/xilinx:
  PCI: xilinx-xdma: Add Xilinx QDMA Root Port driver
  dt-bindings: PCI: xilinx-xdma: Add schemas for Xilinx QDMA PCIe Root Port Bridge
  arm64: zynqmp: Add PCIe phys property for ZCU102
  PCI: xilinx-nwl: Add PHY support
  dt-bindings: pci: xilinx-nwl: Add phys property
  PCI: xilinx-nwl: Clean up clock on probe failure/removal
  PCI: xilinx-nwl: Rate-limit misc interrupt messages
  PCI: xilinx-nwl: Fix register misspelling
  PCI: xilinx-nwl: Fix off-by-one in INTx IRQ handler
parents 11e32bbe 6ac72179
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -61,6 +61,11 @@ properties:
  interrupt-map:
    maxItems: 4

  phys:
    minItems: 1
    maxItems: 4
    description: One phy per logical lane, in order

  power-domains:
    maxItems: 1

@@ -110,6 +115,7 @@ examples:
  - |
    #include <dt-bindings/interrupt-controller/arm-gic.h>
    #include <dt-bindings/interrupt-controller/irq.h>
    #include <dt-bindings/phy/phy.h>
    #include <dt-bindings/power/xlnx-zynqmp-power.h>
    soc {
        #address-cells = <2>;
@@ -138,6 +144,7 @@ examples:
                            <0x0 0x0 0x0 0x3 &pcie_intc 0x3>,
                            <0x0 0x0 0x0 0x4 &pcie_intc 0x4>;
            msi-parent = <&nwl_pcie>;
            phys = <&psgtr 0 PHY_TYPE_PCIE 0 0>;
            power-domains = <&zynqmp_firmware PD_PCIE>;
            iommus = <&smmu 0x4d0>;
            pcie_intc: legacy-interrupt-controller {
+34 −2
Original line number Diff line number Diff line
@@ -14,10 +14,21 @@ allOf:

properties:
  compatible:
    const: xlnx,xdma-host-3.00
    enum:
      - xlnx,xdma-host-3.00
      - xlnx,qdma-host-3.00

  reg:
    maxItems: 1
    items:
      - description: configuration region and XDMA bridge register.
      - description: QDMA bridge register.
    minItems: 1

  reg-names:
    items:
      - const: cfg
      - const: breg
    minItems: 1

  ranges:
    maxItems: 2
@@ -76,6 +87,27 @@ required:
  - "#interrupt-cells"
  - interrupt-controller

if:
  properties:
    compatible:
      contains:
        enum:
          - xlnx,qdma-host-3.00
then:
  properties:
    reg:
      minItems: 2
    reg-names:
      minItems: 2
  required:
    - reg-names
else:
  properties:
    reg:
      maxItems: 1
    reg-names:
      maxItems: 1

unevaluatedProperties: false

examples:
+1 −0
Original line number Diff line number Diff line
@@ -941,6 +941,7 @@ conf-pull-none {

&pcie {
	status = "okay";
	phys = <&psgtr 0 PHY_TYPE_PCIE 0 0>;
};

&psgtr {
+52 −1
Original line number Diff line number Diff line
@@ -71,10 +71,24 @@

/* Phy Status/Control Register definitions */
#define XILINX_PCIE_DMA_REG_PSCR_LNKUP	BIT(11)
#define QDMA_BRIDGE_BASE_OFF		0xcd8

/* Number of MSI IRQs */
#define XILINX_NUM_MSI_IRQS	64

enum xilinx_pl_dma_version {
	XDMA,
	QDMA,
};

/**
 * struct xilinx_pl_dma_variant - PL DMA PCIe variant information
 * @version: DMA version
 */
struct xilinx_pl_dma_variant {
	enum xilinx_pl_dma_version version;
};

struct xilinx_msi {
	struct irq_domain	*msi_domain;
	unsigned long		*bitmap;
@@ -88,6 +102,7 @@ struct xilinx_msi {
 * struct pl_dma_pcie - PCIe port information
 * @dev: Device pointer
 * @reg_base: IO Mapped Register Base
 * @cfg_base: IO Mapped Configuration Base
 * @irq: Interrupt number
 * @cfg: Holds mappings of config space window
 * @phys_reg_base: Physical address of reg base
@@ -97,10 +112,12 @@ struct xilinx_msi {
 * @msi: MSI information
 * @intx_irq: INTx error interrupt number
 * @lock: Lock protecting shared register access
 * @variant: PL DMA PCIe version check pointer
 */
struct pl_dma_pcie {
	struct device			*dev;
	void __iomem			*reg_base;
	void __iomem			*cfg_base;
	int				irq;
	struct pci_config_window	*cfg;
	phys_addr_t			phys_reg_base;
@@ -110,15 +127,22 @@ struct pl_dma_pcie {
	struct xilinx_msi		msi;
	int				intx_irq;
	raw_spinlock_t			lock;
	const struct xilinx_pl_dma_variant   *variant;
};

static inline u32 pcie_read(struct pl_dma_pcie *port, u32 reg)
{
	if (port->variant->version == QDMA)
		return readl(port->reg_base + reg + QDMA_BRIDGE_BASE_OFF);

	return readl(port->reg_base + reg);
}

static inline void pcie_write(struct pl_dma_pcie *port, u32 val, u32 reg)
{
	if (port->variant->version == QDMA)
		writel(val, port->reg_base + reg + QDMA_BRIDGE_BASE_OFF);
	else
		writel(val, port->reg_base + reg);
}

@@ -173,6 +197,9 @@ static void __iomem *xilinx_pl_dma_pcie_map_bus(struct pci_bus *bus,
	if (!xilinx_pl_dma_pcie_valid_device(bus, devfn))
		return NULL;

	if (port->variant->version == QDMA)
		return port->cfg_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);

	return port->reg_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);
}

@@ -724,6 +751,15 @@ static int xilinx_pl_dma_pcie_parse_dt(struct pl_dma_pcie *port,

	port->reg_base = port->cfg->win;

	if (port->variant->version == QDMA) {
		port->cfg_base = port->cfg->win;
		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg");
		port->reg_base = devm_ioremap_resource(dev, res);
		if (IS_ERR(port->reg_base))
			return PTR_ERR(port->reg_base);
		port->phys_reg_base = res->start;
	}

	err = xilinx_request_msi_irq(port);
	if (err) {
		pci_ecam_free(port->cfg);
@@ -753,6 +789,8 @@ static int xilinx_pl_dma_pcie_probe(struct platform_device *pdev)
	if (!bus)
		return -ENODEV;

	port->variant = of_device_get_match_data(dev);

	err = xilinx_pl_dma_pcie_parse_dt(port, bus->res);
	if (err) {
		dev_err(dev, "Parsing DT failed\n");
@@ -784,9 +822,22 @@ static int xilinx_pl_dma_pcie_probe(struct platform_device *pdev)
	return err;
}

static const struct xilinx_pl_dma_variant xdma_host = {
	.version = XDMA,
};

static const struct xilinx_pl_dma_variant qdma_host = {
	.version = QDMA,
};

static const struct of_device_id xilinx_pl_dma_pcie_of_match[] = {
	{
		.compatible = "xlnx,xdma-host-3.00",
		.data = &xdma_host,
	},
	{
		.compatible = "xlnx,qdma-host-3.00",
		.data = &qdma_host,
	},
	{}
};
+116 −23
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/pci-ecam.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/irqchip/chained_irq.h>

@@ -80,8 +81,8 @@
#define MSGF_MISC_SR_NON_FATAL_DEV	BIT(22)
#define MSGF_MISC_SR_FATAL_DEV		BIT(23)
#define MSGF_MISC_SR_LINK_DOWN		BIT(24)
#define MSGF_MSIC_SR_LINK_AUTO_BWIDTH	BIT(25)
#define MSGF_MSIC_SR_LINK_BWIDTH	BIT(26)
#define MSGF_MISC_SR_LINK_AUTO_BWIDTH	BIT(25)
#define MSGF_MISC_SR_LINK_BWIDTH	BIT(26)

#define MSGF_MISC_SR_MASKALL		(MSGF_MISC_SR_RXMSG_AVAIL | \
					MSGF_MISC_SR_RXMSG_OVER | \
@@ -96,8 +97,8 @@
					MSGF_MISC_SR_NON_FATAL_DEV | \
					MSGF_MISC_SR_FATAL_DEV | \
					MSGF_MISC_SR_LINK_DOWN | \
					MSGF_MSIC_SR_LINK_AUTO_BWIDTH | \
					MSGF_MSIC_SR_LINK_BWIDTH)
					MSGF_MISC_SR_LINK_AUTO_BWIDTH | \
					MSGF_MISC_SR_LINK_BWIDTH)

/* Legacy interrupt status mask bits */
#define MSGF_LEG_SR_INTA		BIT(0)
@@ -157,6 +158,7 @@ struct nwl_pcie {
	void __iomem *breg_base;
	void __iomem *pcireg_base;
	void __iomem *ecam_base;
	struct phy *phy[4];
	phys_addr_t phys_breg_base;	/* Physical Bridge Register Base */
	phys_addr_t phys_pcie_reg_base;	/* Physical PCIe Controller Base */
	phys_addr_t phys_ecam_base;	/* Physical Configuration Base */
@@ -267,42 +269,42 @@ static irqreturn_t nwl_pcie_misc_handler(int irq, void *data)
		return IRQ_NONE;

	if (misc_stat & MSGF_MISC_SR_RXMSG_OVER)
		dev_err(dev, "Received Message FIFO Overflow\n");
		dev_err_ratelimited(dev, "Received Message FIFO Overflow\n");

	if (misc_stat & MSGF_MISC_SR_SLAVE_ERR)
		dev_err(dev, "Slave error\n");
		dev_err_ratelimited(dev, "Slave error\n");

	if (misc_stat & MSGF_MISC_SR_MASTER_ERR)
		dev_err(dev, "Master error\n");
		dev_err_ratelimited(dev, "Master error\n");

	if (misc_stat & MSGF_MISC_SR_I_ADDR_ERR)
		dev_err(dev, "In Misc Ingress address translation error\n");
		dev_err_ratelimited(dev, "In Misc Ingress address translation error\n");

	if (misc_stat & MSGF_MISC_SR_E_ADDR_ERR)
		dev_err(dev, "In Misc Egress address translation error\n");
		dev_err_ratelimited(dev, "In Misc Egress address translation error\n");

	if (misc_stat & MSGF_MISC_SR_FATAL_AER)
		dev_err(dev, "Fatal Error in AER Capability\n");
		dev_err_ratelimited(dev, "Fatal Error in AER Capability\n");

	if (misc_stat & MSGF_MISC_SR_NON_FATAL_AER)
		dev_err(dev, "Non-Fatal Error in AER Capability\n");
		dev_err_ratelimited(dev, "Non-Fatal Error in AER Capability\n");

	if (misc_stat & MSGF_MISC_SR_CORR_AER)
		dev_err(dev, "Correctable Error in AER Capability\n");
		dev_err_ratelimited(dev, "Correctable Error in AER Capability\n");

	if (misc_stat & MSGF_MISC_SR_UR_DETECT)
		dev_err(dev, "Unsupported request Detected\n");
		dev_err_ratelimited(dev, "Unsupported request Detected\n");

	if (misc_stat & MSGF_MISC_SR_NON_FATAL_DEV)
		dev_err(dev, "Non-Fatal Error Detected\n");
		dev_err_ratelimited(dev, "Non-Fatal Error Detected\n");

	if (misc_stat & MSGF_MISC_SR_FATAL_DEV)
		dev_err(dev, "Fatal Error Detected\n");
		dev_err_ratelimited(dev, "Fatal Error Detected\n");

	if (misc_stat & MSGF_MSIC_SR_LINK_AUTO_BWIDTH)
	if (misc_stat & MSGF_MISC_SR_LINK_AUTO_BWIDTH)
		dev_info(dev, "Link Autonomous Bandwidth Management Status bit set\n");

	if (misc_stat & MSGF_MSIC_SR_LINK_BWIDTH)
	if (misc_stat & MSGF_MISC_SR_LINK_BWIDTH)
		dev_info(dev, "Link Bandwidth Management Status bit set\n");

	/* Clear misc interrupt status */
@@ -371,7 +373,7 @@ static void nwl_mask_intx_irq(struct irq_data *data)
	u32 mask;
	u32 val;

	mask = 1 << (data->hwirq - 1);
	mask = 1 << data->hwirq;
	raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags);
	val = nwl_bridge_readl(pcie, MSGF_LEG_MASK);
	nwl_bridge_writel(pcie, (val & (~mask)), MSGF_LEG_MASK);
@@ -385,7 +387,7 @@ static void nwl_unmask_intx_irq(struct irq_data *data)
	u32 mask;
	u32 val;

	mask = 1 << (data->hwirq - 1);
	mask = 1 << data->hwirq;
	raw_spin_lock_irqsave(&pcie->leg_mask_lock, flags);
	val = nwl_bridge_readl(pcie, MSGF_LEG_MASK);
	nwl_bridge_writel(pcie, (val | mask), MSGF_LEG_MASK);
@@ -514,6 +516,60 @@ static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
	return 0;
}

static void nwl_pcie_phy_power_off(struct nwl_pcie *pcie, int i)
{
	int err = phy_power_off(pcie->phy[i]);

	if (err)
		dev_err(pcie->dev, "could not power off phy %d (err=%d)\n", i,
			err);
}

static void nwl_pcie_phy_exit(struct nwl_pcie *pcie, int i)
{
	int err = phy_exit(pcie->phy[i]);

	if (err)
		dev_err(pcie->dev, "could not exit phy %d (err=%d)\n", i, err);
}

static int nwl_pcie_phy_enable(struct nwl_pcie *pcie)
{
	int i, ret;

	for (i = 0; i < ARRAY_SIZE(pcie->phy); i++) {
		ret = phy_init(pcie->phy[i]);
		if (ret)
			goto err;

		ret = phy_power_on(pcie->phy[i]);
		if (ret) {
			nwl_pcie_phy_exit(pcie, i);
			goto err;
		}
	}

	return 0;

err:
	while (i--) {
		nwl_pcie_phy_power_off(pcie, i);
		nwl_pcie_phy_exit(pcie, i);
	}

	return ret;
}

static void nwl_pcie_phy_disable(struct nwl_pcie *pcie)
{
	int i;

	for (i = ARRAY_SIZE(pcie->phy); i--;) {
		nwl_pcie_phy_power_off(pcie, i);
		nwl_pcie_phy_exit(pcie, i);
	}
}

static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
{
	struct device *dev = pcie->dev;
@@ -725,6 +781,7 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
{
	struct device *dev = pcie->dev;
	struct resource *res;
	int i;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg");
	pcie->breg_base = devm_ioremap_resource(dev, res);
@@ -752,6 +809,18 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
	irq_set_chained_handler_and_data(pcie->irq_intx,
					 nwl_pcie_leg_handler, pcie);


	for (i = 0; i < ARRAY_SIZE(pcie->phy); i++) {
		pcie->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
		if (PTR_ERR(pcie->phy[i]) == -ENODEV) {
			pcie->phy[i] = NULL;
			break;
		}

		if (IS_ERR(pcie->phy[i]))
			return PTR_ERR(pcie->phy[i]);
	}

	return 0;
}

@@ -772,6 +841,7 @@ static int nwl_pcie_probe(struct platform_device *pdev)
		return -ENODEV;

	pcie = pci_host_bridge_priv(bridge);
	platform_set_drvdata(pdev, pcie);

	pcie->dev = dev;

@@ -791,16 +861,22 @@ static int nwl_pcie_probe(struct platform_device *pdev)
		return err;
	}

	err = nwl_pcie_phy_enable(pcie);
	if (err) {
		dev_err(dev, "could not enable PHYs\n");
		goto err_clk;
	}

	err = nwl_pcie_bridge_init(pcie);
	if (err) {
		dev_err(dev, "HW Initialization failed\n");
		return err;
		goto err_phy;
	}

	err = nwl_pcie_init_irq_domain(pcie);
	if (err) {
		dev_err(dev, "Failed creating IRQ Domain\n");
		return err;
		goto err_phy;
	}

	bridge->sysdata = pcie;
@@ -810,11 +886,27 @@ static int nwl_pcie_probe(struct platform_device *pdev)
		err = nwl_pcie_enable_msi(pcie);
		if (err < 0) {
			dev_err(dev, "failed to enable MSI support: %d\n", err);
			return err;
			goto err_phy;
		}
	}

	return pci_host_probe(bridge);
	err = pci_host_probe(bridge);
	if (!err)
		return 0;

err_phy:
	nwl_pcie_phy_disable(pcie);
err_clk:
	clk_disable_unprepare(pcie->clk);
	return err;
}

static void nwl_pcie_remove(struct platform_device *pdev)
{
	struct nwl_pcie *pcie = platform_get_drvdata(pdev);

	nwl_pcie_phy_disable(pcie);
	clk_disable_unprepare(pcie->clk);
}

static struct platform_driver nwl_pcie_driver = {
@@ -824,5 +916,6 @@ static struct platform_driver nwl_pcie_driver = {
		.of_match_table = nwl_pcie_of_match,
	},
	.probe = nwl_pcie_probe,
	.remove_new = nwl_pcie_remove,
};
builtin_platform_driver(nwl_pcie_driver);