mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
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
This commit is contained in:
commit
bb78146c18
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user