Commit bb78146c authored by Bjorn Helgaas's avatar Bjorn Helgaas

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
......@@ -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 {
......
......@@ -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:
......
......@@ -941,6 +941,7 @@ conf-pull-none {
&pcie {
status = "okay";
phys = <&psgtr 0 PHY_TYPE_PCIE 0 0>;
};
&psgtr {
......
......@@ -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,16 +127,23 @@ 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)
{
writel(val, port->reg_base + reg);
if (port->variant->version == QDMA)
writel(val, port->reg_base + reg + QDMA_BRIDGE_BASE_OFF);
else
writel(val, port->reg_base + reg);
}
static inline bool xilinx_pl_dma_pcie_link_up(struct pl_dma_pcie *port)
......@@ -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,
},
{}
};
......
......@@ -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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment