Merge tag 'pci-v5.12-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI updates from Bjorn Helgaas: "Enumeration: - Remove unnecessary locking around _OSC (Bjorn Helgaas) - Clarify message about _OSC failure (Bjorn Helgaas) - Remove notification of PCIe bandwidth changes (Bjorn Helgaas) - Tidy checking of syscall user config accessors (Heiner Kallweit) Resource management: - Decline to resize resources if boot config must be preserved (Ard Biesheuvel) - Fix pci_register_io_range() memory leak (Geert Uytterhoeven) Error handling (Keith Busch): - Clear error status from the correct device - Retain error recovery status so drivers can use it after reset - Log the type of Port (Root or Switch Downstream) that we reset - Always request a reset for Downstream Ports in frozen state Endpoint framework and NTB (Kishon Vijay Abraham I): - Make *_get_first_free_bar() take into account 64 bit BAR - Add helper API to get the 'next' unreserved BAR - Make *_free_bar() return error codes on failure - Remove unused pci_epf_match_device() - Add support to associate secondary EPC with EPF - Add support in configfs to associate two EPCs with EPF - Add pci_epc_ops to map MSI IRQ - Add pci_epf_ops to expose function-specific attrs - Allow user to create sub-directory of 'EPF Device' directory - Implement ->msi_map_irq() ops for cadence - Configure LM_EP_FUNC_CFG based on epc->function_num_map for cadence - Add EP function driver to provide NTB functionality - Add support for EPF PCI Non-Transparent Bridge - Add specification for PCI NTB function device - Add PCI endpoint NTB function user guide - Add configfs binding documentation for pci-ntb endpoint function Broadcom STB PCIe controller driver: - Add support for BCM4908 and external PERST# signal controller (Rafał Miłecki) Cadence PCIe controller driver: - Retrain Link to work around Gen2 training defect (Nadeem Athani) - Fix merge botch in cdns_pcie_host_map_dma_ranges() (Krzysztof Wilczyński) Freescale Layerscape PCIe controller driver: - Add LX2160A rev2 EP mode support (Hou Zhiqiang) - Convert to builtin_platform_driver() (Michael Walle) MediaTek PCIe controller driver: - Fix OF node reference leak (Krzysztof Wilczyński) Microchip PolarFlare PCIe controller driver: - Add Microchip PolarFire PCIe controller driver (Daire McNamara) Qualcomm PCIe controller driver: - Use PHY_REFCLK_USE_PAD only for ipq8064 (Ansuel Smith) - Add support for ddrss_sf_tbu clock for sm8250 (Dmitry Baryshkov) Renesas R-Car PCIe controller driver: - Drop PCIE_RCAR config option (Lad Prabhakar) - Always allocate MSI addresses in 32bit space (Marek Vasut) Rockchip PCIe controller driver: - Add FriendlyARM NanoPi M4B DT binding (Chen-Yu Tsai) - Make 'ep-gpios' DT property optional (Chen-Yu Tsai) Synopsys DesignWare PCIe controller driver: - Work around ECRC configuration hardware defect (Vidya Sagar) - Drop support for config space in DT 'ranges' (Rob Herring) - Change size to u64 for EP outbound iATU (Shradha Todi) - Add upper limit address for outbound iATU (Shradha Todi) - Make dw_pcie ops optional (Jisheng Zhang) - Remove unnecessary dw_pcie_ops from al driver (Jisheng Zhang) Xilinx Versal CPM PCIe controller driver: - Fix OF node reference leak (Pan Bian) Miscellaneous: - Remove tango host controller driver (Arnd Bergmann) - Remove IRQ handler & data together (altera-msi, brcmstb, dwc) (Martin Kaiser) - Fix xgene-msi race in installing chained IRQ handler (Martin Kaiser) - Apply CONFIG_PCI_DEBUG to entire drivers/pci hierarchy (Junhao He) - Fix pci-bridge-emul array overruns (Russell King) - Remove obsolete uses of WARN_ON(in_interrupt()) (Sebastian Andrzej Siewior)" * tag 'pci-v5.12-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (69 commits) PCI: qcom: Use PHY_REFCLK_USE_PAD only for ipq8064 PCI: qcom: Add support for ddrss_sf_tbu clock dt-bindings: PCI: qcom: Document ddrss_sf_tbu clock for sm8250 PCI: al: Remove useless dw_pcie_ops PCI: dwc: Don't assume the ops in dw_pcie always exist PCI: dwc: Add upper limit address for outbound iATU PCI: dwc: Change size to u64 for EP outbound iATU PCI: dwc: Drop support for config space in 'ranges' PCI: layerscape: Convert to builtin_platform_driver() PCI: layerscape: Add LX2160A rev2 EP mode support dt-bindings: PCI: layerscape: Add LX2160A rev2 compatible strings PCI: dwc: Work around ECRC configuration issue PCI/portdrv: Report reset for frozen channel PCI/AER: Specify the type of Port that was reset PCI/ERR: Retain status from error notification PCI/AER: Clear AER status from Root Port when resetting Downstream Port PCI/ERR: Clear status of the reporting device dt-bindings: arm: rockchip: Add FriendlyARM NanoPi M4B PCI: rockchip: Make 'ep-gpios' DT property optional Documentation: PCI: Add PCI endpoint NTB function user guide ...
This commit is contained in:
@ -55,15 +55,6 @@ config PCI_RCAR_GEN2
|
||||
There are 3 internal PCI controllers available with a single
|
||||
built-in EHCI/OHCI host controller present on each one.
|
||||
|
||||
config PCIE_RCAR
|
||||
bool "Renesas R-Car PCIe controller"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
depends on PCI_MSI_IRQ_DOMAIN
|
||||
select PCIE_RCAR_HOST
|
||||
help
|
||||
Say Y here if you want PCIe controller support on R-Car SoCs.
|
||||
This option will be removed after arm64 defconfig is updated.
|
||||
|
||||
config PCIE_RCAR_HOST
|
||||
bool "Renesas R-Car PCIe host controller"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
@ -242,20 +233,6 @@ config PCIE_MEDIATEK
|
||||
Say Y here if you want to enable PCIe controller support on
|
||||
MediaTek SoCs.
|
||||
|
||||
config PCIE_TANGO_SMP8759
|
||||
bool "Tango SMP8759 PCIe controller (DANGEROUS)"
|
||||
depends on ARCH_TANGO && PCI_MSI && OF
|
||||
depends on BROKEN
|
||||
select PCI_HOST_COMMON
|
||||
help
|
||||
Say Y here to enable PCIe controller support for Sigma Designs
|
||||
Tango SMP8759-based systems.
|
||||
|
||||
Note: The SMP8759 controller multiplexes PCI config and MMIO
|
||||
accesses, and Linux doesn't provide a way to serialize them.
|
||||
This can lead to data corruption if drivers perform concurrent
|
||||
config and MMIO accesses.
|
||||
|
||||
config VMD
|
||||
depends on PCI_MSI && X86_64 && SRCU
|
||||
tristate "Intel Volume Management Device Driver"
|
||||
@ -273,7 +250,7 @@ config VMD
|
||||
|
||||
config PCIE_BRCMSTB
|
||||
tristate "Broadcom Brcmstb PCIe host controller"
|
||||
depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
|
||||
depends on ARCH_BRCMSTB || ARCH_BCM2835 || ARCH_BCM4908 || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on PCI_MSI_IRQ_DOMAIN
|
||||
default ARCH_BRCMSTB
|
||||
@ -298,6 +275,16 @@ config PCI_LOONGSON
|
||||
Say Y here if you want to enable PCI controller support on
|
||||
Loongson systems.
|
||||
|
||||
config PCIE_MICROCHIP_HOST
|
||||
bool "Microchip AXI PCIe host bridge support"
|
||||
depends on PCI_MSI && OF
|
||||
select PCI_MSI_IRQ_DOMAIN
|
||||
select GENERIC_MSI_IRQ_DOMAIN
|
||||
select PCI_HOST_COMMON
|
||||
help
|
||||
Say Y here if you want kernel to support the Microchip AXI PCIe
|
||||
Host Bridge driver.
|
||||
|
||||
config PCIE_HISI_ERR
|
||||
depends on ACPI_APEI_GHES && (ARM64 || COMPILE_TEST)
|
||||
bool "HiSilicon HIP PCIe controller error handling driver"
|
||||
|
@ -27,7 +27,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
|
||||
obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o
|
||||
obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
|
||||
obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
|
||||
obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
|
||||
obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
|
||||
obj-$(CONFIG_VMD) += vmd.o
|
||||
obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
|
||||
obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
|
||||
|
@ -64,6 +64,7 @@ enum j721e_pcie_mode {
|
||||
|
||||
struct j721e_pcie_data {
|
||||
enum j721e_pcie_mode mode;
|
||||
bool quirk_retrain_flag;
|
||||
};
|
||||
|
||||
static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset)
|
||||
@ -280,6 +281,7 @@ static struct pci_ops cdns_ti_pcie_host_ops = {
|
||||
|
||||
static const struct j721e_pcie_data j721e_pcie_rc_data = {
|
||||
.mode = PCI_MODE_RC,
|
||||
.quirk_retrain_flag = true,
|
||||
};
|
||||
|
||||
static const struct j721e_pcie_data j721e_pcie_ep_data = {
|
||||
@ -388,6 +390,7 @@ static int j721e_pcie_probe(struct platform_device *pdev)
|
||||
|
||||
bridge->ops = &cdns_ti_pcie_host_ops;
|
||||
rc = pci_host_bridge_priv(bridge);
|
||||
rc->quirk_retrain_flag = data->quirk_retrain_flag;
|
||||
|
||||
cdns_pcie = &rc->pcie;
|
||||
cdns_pcie->dev = dev;
|
||||
|
@ -382,6 +382,57 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_pcie_ep_map_msi_irq(struct pci_epc *epc, u8 fn,
|
||||
phys_addr_t addr, u8 interrupt_num,
|
||||
u32 entry_size, u32 *msi_data,
|
||||
u32 *msi_addr_offset)
|
||||
{
|
||||
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
|
||||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
u64 pci_addr, pci_addr_mask = 0xff;
|
||||
u16 flags, mme, data, data_mask;
|
||||
u8 msi_count;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/* Check whether the MSI feature has been enabled by the PCI host. */
|
||||
flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
|
||||
if (!(flags & PCI_MSI_FLAGS_ENABLE))
|
||||
return -EINVAL;
|
||||
|
||||
/* Get the number of enabled MSIs */
|
||||
mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
|
||||
msi_count = 1 << mme;
|
||||
if (!interrupt_num || interrupt_num > msi_count)
|
||||
return -EINVAL;
|
||||
|
||||
/* Compute the data value to be written. */
|
||||
data_mask = msi_count - 1;
|
||||
data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64);
|
||||
data = data & ~data_mask;
|
||||
|
||||
/* Get the PCI address where to write the data into. */
|
||||
pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI);
|
||||
pci_addr <<= 32;
|
||||
pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO);
|
||||
pci_addr &= GENMASK_ULL(63, 2);
|
||||
|
||||
for (i = 0; i < interrupt_num; i++) {
|
||||
ret = cdns_pcie_ep_map_addr(epc, fn, addr,
|
||||
pci_addr & ~pci_addr_mask,
|
||||
entry_size);
|
||||
if (ret)
|
||||
return ret;
|
||||
addr = addr + entry_size;
|
||||
}
|
||||
|
||||
*msi_data = data;
|
||||
*msi_addr_offset = pci_addr & pci_addr_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn,
|
||||
u16 interrupt_num)
|
||||
{
|
||||
@ -455,18 +506,13 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
|
||||
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
struct device *dev = pcie->dev;
|
||||
struct pci_epf *epf;
|
||||
u32 cfg;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* BIT(0) is hardwired to 1, hence function 0 is always enabled
|
||||
* and can't be disabled anyway.
|
||||
*/
|
||||
cfg = BIT(0);
|
||||
list_for_each_entry(epf, &epc->pci_epf, list)
|
||||
cfg |= BIT(epf->func_no);
|
||||
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
|
||||
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, epc->function_num_map);
|
||||
|
||||
ret = cdns_pcie_start_link(pcie);
|
||||
if (ret) {
|
||||
@ -481,6 +527,7 @@ static const struct pci_epc_features cdns_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = true,
|
||||
.align = 256,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
@ -500,6 +547,7 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
|
||||
.set_msix = cdns_pcie_ep_set_msix,
|
||||
.get_msix = cdns_pcie_ep_get_msix,
|
||||
.raise_irq = cdns_pcie_ep_raise_irq,
|
||||
.map_msi_irq = cdns_pcie_ep_map_msi_irq,
|
||||
.start = cdns_pcie_ep_start,
|
||||
.get_features = cdns_pcie_ep_get_features,
|
||||
};
|
||||
|
@ -77,6 +77,68 @@ static struct pci_ops cdns_pcie_host_ops = {
|
||||
.write = pci_generic_config_write,
|
||||
};
|
||||
|
||||
static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie)
|
||||
{
|
||||
struct device *dev = pcie->dev;
|
||||
int retries;
|
||||
|
||||
/* Check if the link is up or not */
|
||||
for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
|
||||
if (cdns_pcie_link_up(pcie)) {
|
||||
dev_info(dev, "Link up\n");
|
||||
return 0;
|
||||
}
|
||||
usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int cdns_pcie_retrain(struct cdns_pcie *pcie)
|
||||
{
|
||||
u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
|
||||
u16 lnk_stat, lnk_ctl;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Set retrain bit if current speed is 2.5 GB/s,
|
||||
* but the PCIe root port support is > 2.5 GB/s.
|
||||
*/
|
||||
|
||||
lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off +
|
||||
PCI_EXP_LNKCAP));
|
||||
if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
|
||||
return ret;
|
||||
|
||||
lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
|
||||
if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
|
||||
lnk_ctl = cdns_pcie_rp_readw(pcie,
|
||||
pcie_cap_off + PCI_EXP_LNKCTL);
|
||||
lnk_ctl |= PCI_EXP_LNKCTL_RL;
|
||||
cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL,
|
||||
lnk_ctl);
|
||||
|
||||
ret = cdns_pcie_host_wait_for_link(pcie);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc)
|
||||
{
|
||||
struct cdns_pcie *pcie = &rc->pcie;
|
||||
int ret;
|
||||
|
||||
ret = cdns_pcie_host_wait_for_link(pcie);
|
||||
|
||||
/*
|
||||
* Retrain link for Gen2 training defect
|
||||
* if quirk flag is set.
|
||||
*/
|
||||
if (!ret && rc->quirk_retrain_flag)
|
||||
ret = cdns_pcie_retrain(pcie);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
|
||||
{
|
||||
@ -321,9 +383,10 @@ static int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc)
|
||||
|
||||
resource_list_for_each_entry(entry, &bridge->dma_ranges) {
|
||||
err = cdns_pcie_host_bar_config(rc, entry);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_err(dev, "Fail to configure IB using dma-ranges\n");
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -398,23 +461,6 @@ static int cdns_pcie_host_init(struct device *dev,
|
||||
return cdns_pcie_host_init_address_translation(rc);
|
||||
}
|
||||
|
||||
static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie)
|
||||
{
|
||||
struct device *dev = pcie->dev;
|
||||
int retries;
|
||||
|
||||
/* Check if the link is up or not */
|
||||
for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
|
||||
if (cdns_pcie_link_up(pcie)) {
|
||||
dev_info(dev, "Link up\n");
|
||||
return 0;
|
||||
}
|
||||
usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
|
||||
{
|
||||
struct device *dev = rc->pcie.dev;
|
||||
@ -457,7 +503,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cdns_pcie_host_wait_for_link(pcie);
|
||||
ret = cdns_pcie_host_start_link(rc);
|
||||
if (ret)
|
||||
dev_dbg(dev, "PCIe link never came up\n");
|
||||
|
||||
|
@ -119,7 +119,7 @@
|
||||
* Root Port Registers (PCI configuration space for the root port function)
|
||||
*/
|
||||
#define CDNS_PCIE_RP_BASE 0x00200000
|
||||
|
||||
#define CDNS_PCIE_RP_CAP_OFFSET 0xc0
|
||||
|
||||
/*
|
||||
* Address Translation Registers
|
||||
@ -291,6 +291,7 @@ struct cdns_pcie {
|
||||
* @device_id: PCI device ID
|
||||
* @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 and RP_NO_BAR if it's free or
|
||||
* available
|
||||
* @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2
|
||||
*/
|
||||
struct cdns_pcie_rc {
|
||||
struct cdns_pcie pcie;
|
||||
@ -299,6 +300,7 @@ struct cdns_pcie_rc {
|
||||
u32 vendor_id;
|
||||
u32 device_id;
|
||||
bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
|
||||
bool quirk_retrain_flag;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -414,6 +416,13 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie,
|
||||
cdns_pcie_write_sz(addr, 0x2, value);
|
||||
}
|
||||
|
||||
static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg)
|
||||
{
|
||||
void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg;
|
||||
|
||||
return cdns_pcie_read_sz(addr, 0x2);
|
||||
}
|
||||
|
||||
/* Endpoint Function register access */
|
||||
static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn,
|
||||
u32 reg, u8 value)
|
||||
|
@ -115,10 +115,17 @@ static const struct ls_pcie_ep_drvdata ls2_ep_drvdata = {
|
||||
.dw_pcie_ops = &dw_ls_pcie_ep_ops,
|
||||
};
|
||||
|
||||
static const struct ls_pcie_ep_drvdata lx2_ep_drvdata = {
|
||||
.func_offset = 0x8000,
|
||||
.ops = &ls_pcie_ep_ops,
|
||||
.dw_pcie_ops = &dw_ls_pcie_ep_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id ls_pcie_ep_of_match[] = {
|
||||
{ .compatible = "fsl,ls1046a-pcie-ep", .data = &ls1_ep_drvdata },
|
||||
{ .compatible = "fsl,ls1088a-pcie-ep", .data = &ls2_ep_drvdata },
|
||||
{ .compatible = "fsl,ls2088a-pcie-ep", .data = &ls2_ep_drvdata },
|
||||
{ .compatible = "fsl,lx2160ar2-pcie-ep", .data = &lx2_ep_drvdata },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@ -232,7 +232,7 @@ static const struct of_device_id ls_pcie_of_match[] = {
|
||||
{ },
|
||||
};
|
||||
|
||||
static int __init ls_pcie_probe(struct platform_device *pdev)
|
||||
static int ls_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dw_pcie *pci;
|
||||
@ -271,10 +271,11 @@ static int __init ls_pcie_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static struct platform_driver ls_pcie_driver = {
|
||||
.probe = ls_pcie_probe,
|
||||
.driver = {
|
||||
.name = "layerscape-pcie",
|
||||
.of_match_table = ls_pcie_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);
|
||||
builtin_platform_driver(ls_pcie_driver);
|
||||
|
@ -314,9 +314,6 @@ static const struct dw_pcie_host_ops al_pcie_host_ops = {
|
||||
.host_init = al_pcie_host_init,
|
||||
};
|
||||
|
||||
static const struct dw_pcie_ops dw_pcie_ops = {
|
||||
};
|
||||
|
||||
static int al_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -334,7 +331,6 @@ static int al_pcie_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
pci->dev = dev;
|
||||
pci->ops = &dw_pcie_ops;
|
||||
pci->pp.ops = &al_pcie_host_ops;
|
||||
|
||||
al_pcie->pci = pci;
|
||||
|
@ -434,10 +434,8 @@ static void dw_pcie_ep_stop(struct pci_epc *epc)
|
||||
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
|
||||
if (!pci->ops->stop_link)
|
||||
return;
|
||||
|
||||
pci->ops->stop_link(pci);
|
||||
if (pci->ops && pci->ops->stop_link)
|
||||
pci->ops->stop_link(pci);
|
||||
}
|
||||
|
||||
static int dw_pcie_ep_start(struct pci_epc *epc)
|
||||
@ -445,7 +443,7 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
|
||||
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
|
||||
if (!pci->ops->start_link)
|
||||
if (!pci->ops || !pci->ops->start_link)
|
||||
return -EINVAL;
|
||||
|
||||
return pci->ops->start_link(pci);
|
||||
|
@ -258,10 +258,8 @@ int dw_pcie_allocate_domains(struct pcie_port *pp)
|
||||
|
||||
static void dw_pcie_free_msi(struct pcie_port *pp)
|
||||
{
|
||||
if (pp->msi_irq) {
|
||||
irq_set_chained_handler(pp->msi_irq, NULL);
|
||||
irq_set_handler_data(pp->msi_irq, NULL);
|
||||
}
|
||||
if (pp->msi_irq)
|
||||
irq_set_chained_handler_and_data(pp->msi_irq, NULL, NULL);
|
||||
|
||||
irq_domain_remove(pp->msi_domain);
|
||||
irq_domain_remove(pp->irq_domain);
|
||||
@ -305,8 +303,13 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
||||
if (cfg_res) {
|
||||
pp->cfg0_size = resource_size(cfg_res);
|
||||
pp->cfg0_base = cfg_res->start;
|
||||
} else if (!pp->va_cfg0_base) {
|
||||
|
||||
pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, cfg_res);
|
||||
if (IS_ERR(pp->va_cfg0_base))
|
||||
return PTR_ERR(pp->va_cfg0_base);
|
||||
} else {
|
||||
dev_err(dev, "Missing *config* reg space\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pci->dbi_base) {
|
||||
@ -322,38 +325,12 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
||||
|
||||
pp->bridge = bridge;
|
||||
|
||||
/* Get the I/O and memory ranges from DT */
|
||||
resource_list_for_each_entry(win, &bridge->windows) {
|
||||
switch (resource_type(win->res)) {
|
||||
case IORESOURCE_IO:
|
||||
pp->io_size = resource_size(win->res);
|
||||
pp->io_bus_addr = win->res->start - win->offset;
|
||||
pp->io_base = pci_pio_to_address(win->res->start);
|
||||
break;
|
||||
case 0:
|
||||
dev_err(dev, "Missing *config* reg space\n");
|
||||
pp->cfg0_size = resource_size(win->res);
|
||||
pp->cfg0_base = win->res->start;
|
||||
if (!pci->dbi_base) {
|
||||
pci->dbi_base = devm_pci_remap_cfgspace(dev,
|
||||
pp->cfg0_base,
|
||||
pp->cfg0_size);
|
||||
if (!pci->dbi_base) {
|
||||
dev_err(dev, "Error with ioremap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pp->va_cfg0_base) {
|
||||
pp->va_cfg0_base = devm_pci_remap_cfgspace(dev,
|
||||
pp->cfg0_base, pp->cfg0_size);
|
||||
if (!pp->va_cfg0_base) {
|
||||
dev_err(dev, "Error with ioremap in function\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Get the I/O range from DT */
|
||||
win = resource_list_first_type(&bridge->windows, IORESOURCE_IO);
|
||||
if (win) {
|
||||
pp->io_size = resource_size(win->res);
|
||||
pp->io_bus_addr = win->res->start - win->offset;
|
||||
pp->io_base = pci_pio_to_address(win->res->start);
|
||||
}
|
||||
|
||||
if (pci->link_gen < 1)
|
||||
@ -425,7 +402,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
||||
dw_pcie_setup_rc(pp);
|
||||
dw_pcie_msi_init(pp);
|
||||
|
||||
if (!dw_pcie_link_up(pci) && pci->ops->start_link) {
|
||||
if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
|
||||
ret = pci->ops->start_link(pci);
|
||||
if (ret)
|
||||
goto err_free_msi;
|
||||
|
@ -141,7 +141,7 @@ u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size)
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (pci->ops->read_dbi)
|
||||
if (pci->ops && pci->ops->read_dbi)
|
||||
return pci->ops->read_dbi(pci, pci->dbi_base, reg, size);
|
||||
|
||||
ret = dw_pcie_read(pci->dbi_base + reg, size, &val);
|
||||
@ -156,7 +156,7 @@ void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pci->ops->write_dbi) {
|
||||
if (pci->ops && pci->ops->write_dbi) {
|
||||
pci->ops->write_dbi(pci, pci->dbi_base, reg, size, val);
|
||||
return;
|
||||
}
|
||||
@ -171,7 +171,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pci->ops->write_dbi2) {
|
||||
if (pci->ops && pci->ops->write_dbi2) {
|
||||
pci->ops->write_dbi2(pci, pci->dbi_base2, reg, size, val);
|
||||
return;
|
||||
}
|
||||
@ -186,7 +186,7 @@ static u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 reg)
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (pci->ops->read_dbi)
|
||||
if (pci->ops && pci->ops->read_dbi)
|
||||
return pci->ops->read_dbi(pci, pci->atu_base, reg, 4);
|
||||
|
||||
ret = dw_pcie_read(pci->atu_base + reg, 4, &val);
|
||||
@ -200,7 +200,7 @@ static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 reg, u32 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pci->ops->write_dbi) {
|
||||
if (pci->ops && pci->ops->write_dbi) {
|
||||
pci->ops->write_dbi(pci, pci->atu_base, reg, 4, val);
|
||||
return;
|
||||
}
|
||||
@ -225,6 +225,47 @@ static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg,
|
||||
dw_pcie_writel_atu(pci, offset + reg, val);
|
||||
}
|
||||
|
||||
static inline u32 dw_pcie_enable_ecrc(u32 val)
|
||||
{
|
||||
/*
|
||||
* DesignWare core version 4.90A has a design issue where the 'TD'
|
||||
* bit in the Control register-1 of the ATU outbound region acts
|
||||
* like an override for the ECRC setting, i.e., the presence of TLP
|
||||
* Digest (ECRC) in the outgoing TLPs is solely determined by this
|
||||
* bit. This is contrary to the PCIe spec which says that the
|
||||
* enablement of the ECRC is solely determined by the AER
|
||||
* registers.
|
||||
*
|
||||
* Because of this, even when the ECRC is enabled through AER
|
||||
* registers, the transactions going through ATU won't have TLP
|
||||
* Digest as there is no way the PCI core AER code could program
|
||||
* the TD bit which is specific to the DesignWare core.
|
||||
*
|
||||
* The best way to handle this scenario is to program the TD bit
|
||||
* always. It affects only the traffic from root port to downstream
|
||||
* devices.
|
||||
*
|
||||
* At this point,
|
||||
* When ECRC is enabled in AER registers, everything works normally
|
||||
* When ECRC is NOT enabled in AER registers, then,
|
||||
* on Root Port:- TLP Digest (DWord size) gets appended to each packet
|
||||
* even through it is not required. Since downstream
|
||||
* TLPs are mostly for configuration accesses and BAR
|
||||
* accesses, they are not in critical path and won't
|
||||
* have much negative effect on the performance.
|
||||
* on End Point:- TLP Digest is received for some/all the packets coming
|
||||
* from the root port. TLP Digest is ignored because,
|
||||
* as per the PCIe Spec r5.0 v1.0 section 2.2.3
|
||||
* "TLP Digest Rules", when an endpoint receives TLP
|
||||
* Digest when its ECRC check functionality is disabled
|
||||
* in AER registers, received TLP Digest is just ignored.
|
||||
* Since there is no issue or error reported either side, best way to
|
||||
* handle the scenario is to program TD bit by default.
|
||||
*/
|
||||
|
||||
return val | PCIE_ATU_TD;
|
||||
}
|
||||
|
||||
static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
|
||||
int index, int type,
|
||||
u64 cpu_addr, u64 pci_addr,
|
||||
@ -248,6 +289,8 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
|
||||
val = type | PCIE_ATU_FUNC_NUM(func_no);
|
||||
val = upper_32_bits(size - 1) ?
|
||||
val | PCIE_ATU_INCREASE_REGION_SIZE : val;
|
||||
if (pci->version == 0x490A)
|
||||
val = dw_pcie_enable_ecrc(val);
|
||||
dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
|
||||
dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
|
||||
PCIE_ATU_ENABLE);
|
||||
@ -273,7 +316,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
|
||||
{
|
||||
u32 retries, val;
|
||||
|
||||
if (pci->ops->cpu_addr_fixup)
|
||||
if (pci->ops && pci->ops->cpu_addr_fixup)
|
||||
cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr);
|
||||
|
||||
if (pci->iatu_unroll_enabled) {
|
||||
@ -290,12 +333,19 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
|
||||
upper_32_bits(cpu_addr));
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
|
||||
lower_32_bits(cpu_addr + size - 1));
|
||||
if (pci->version >= 0x460A)
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
|
||||
upper_32_bits(cpu_addr + size - 1));
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
|
||||
lower_32_bits(pci_addr));
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
|
||||
upper_32_bits(pci_addr));
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type |
|
||||
PCIE_ATU_FUNC_NUM(func_no));
|
||||
val = type | PCIE_ATU_FUNC_NUM(func_no);
|
||||
val = ((upper_32_bits(size - 1)) && (pci->version >= 0x460A)) ?
|
||||
val | PCIE_ATU_INCREASE_REGION_SIZE : val;
|
||||
if (pci->version == 0x490A)
|
||||
val = dw_pcie_enable_ecrc(val);
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
|
||||
|
||||
/*
|
||||
@ -321,7 +371,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
|
||||
|
||||
void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
int type, u64 cpu_addr, u64 pci_addr,
|
||||
u32 size)
|
||||
u64 size)
|
||||
{
|
||||
__dw_pcie_prog_outbound_atu(pci, func_no, index, type,
|
||||
cpu_addr, pci_addr, size);
|
||||
@ -481,7 +531,7 @@ int dw_pcie_link_up(struct dw_pcie *pci)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (pci->ops->link_up)
|
||||
if (pci->ops && pci->ops->link_up)
|
||||
return pci->ops->link_up(pci);
|
||||
|
||||
val = readl(pci->dbi_base + PCIE_PORT_DEBUG1);
|
||||
|
@ -86,6 +86,7 @@
|
||||
#define PCIE_ATU_TYPE_IO 0x2
|
||||
#define PCIE_ATU_TYPE_CFG0 0x4
|
||||
#define PCIE_ATU_TYPE_CFG1 0x5
|
||||
#define PCIE_ATU_TD BIT(8)
|
||||
#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
|
||||
#define PCIE_ATU_CR2 0x908
|
||||
#define PCIE_ATU_ENABLE BIT(31)
|
||||
@ -99,6 +100,7 @@
|
||||
#define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
|
||||
#define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x91C
|
||||
#define PCIE_ATU_UPPER_LIMIT 0x924
|
||||
|
||||
#define PCIE_MISC_CONTROL_1_OFF 0x8BC
|
||||
#define PCIE_DBI_RO_WR_EN BIT(0)
|
||||
@ -297,7 +299,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
|
||||
u64 size);
|
||||
void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
int type, u64 cpu_addr, u64 pci_addr,
|
||||
u32 size);
|
||||
u64 size);
|
||||
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
int bar, u64 cpu_addr,
|
||||
enum dw_pcie_as_type as_type);
|
||||
|
@ -159,8 +159,10 @@ struct qcom_pcie_resources_2_3_3 {
|
||||
struct reset_control *rst[7];
|
||||
};
|
||||
|
||||
/* 6 clocks typically, 7 for sm8250 */
|
||||
struct qcom_pcie_resources_2_7_0 {
|
||||
struct clk_bulk_data clks[6];
|
||||
struct clk_bulk_data clks[7];
|
||||
int num_clks;
|
||||
struct regulator_bulk_data supplies[2];
|
||||
struct reset_control *pci_reset;
|
||||
struct clk *pipe_clk;
|
||||
@ -398,7 +400,9 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie)
|
||||
|
||||
/* enable external reference clock */
|
||||
val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK);
|
||||
val &= ~PHY_REFCLK_USE_PAD;
|
||||
/* USE_PAD is required only for ipq806x */
|
||||
if (!of_device_is_compatible(node, "qcom,pcie-apq8064"))
|
||||
val &= ~PHY_REFCLK_USE_PAD;
|
||||
val |= PHY_REFCLK_SSP_EN;
|
||||
writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK);
|
||||
|
||||
@ -1152,8 +1156,14 @@ static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie)
|
||||
res->clks[3].id = "bus_slave";
|
||||
res->clks[4].id = "slave_q2a";
|
||||
res->clks[5].id = "tbu";
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,pcie-sm8250")) {
|
||||
res->clks[6].id = "ddrss_sf_tbu";
|
||||
res->num_clks = 7;
|
||||
} else {
|
||||
res->num_clks = 6;
|
||||
}
|
||||
|
||||
ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks);
|
||||
ret = devm_clk_bulk_get(dev, res->num_clks, res->clks);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1175,7 +1185,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks);
|
||||
ret = clk_bulk_prepare_enable(res->num_clks, res->clks);
|
||||
if (ret < 0)
|
||||
goto err_disable_regulators;
|
||||
|
||||
@ -1227,7 +1237,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
|
||||
|
||||
return 0;
|
||||
err_disable_clocks:
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
|
||||
clk_bulk_disable_unprepare(res->num_clks, res->clks);
|
||||
err_disable_regulators:
|
||||
regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
|
||||
|
||||
@ -1238,7 +1248,7 @@ static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
|
||||
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
|
||||
clk_bulk_disable_unprepare(res->num_clks, res->clks);
|
||||
regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,8 @@ int pci_host_common_probe(struct platform_device *pdev)
|
||||
if (!bridge)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, bridge);
|
||||
|
||||
of_pci_check_probe_only();
|
||||
|
||||
/* Parse and map our Configuration Space windows */
|
||||
@ -78,8 +80,6 @@ int pci_host_common_probe(struct platform_device *pdev)
|
||||
bridge->sysdata = cfg;
|
||||
bridge->ops = (struct pci_ops *)&ops->pci_ops;
|
||||
|
||||
platform_set_drvdata(pdev, bridge);
|
||||
|
||||
return pci_host_probe(bridge);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_host_common_probe);
|
||||
|
@ -1714,7 +1714,7 @@ static void prepopulate_bars(struct hv_pcibus_device *hbus)
|
||||
* resumed and suspended again: see hibernation_snapshot() and
|
||||
* hibernation_platform_enter().
|
||||
*
|
||||
* If the memory enable bit is already set, Hyper-V sliently ignores
|
||||
* If the memory enable bit is already set, Hyper-V silently ignores
|
||||
* the below BAR updates, and the related PCI device driver can not
|
||||
* work, because reading from the device register(s) always returns
|
||||
* 0xFFFFFFFF.
|
||||
|
@ -384,13 +384,9 @@ static int xgene_msi_hwirq_alloc(unsigned int cpu)
|
||||
if (!msi_group->gic_irq)
|
||||
continue;
|
||||
|
||||
irq_set_chained_handler(msi_group->gic_irq,
|
||||
xgene_msi_isr);
|
||||
err = irq_set_handler_data(msi_group->gic_irq, msi_group);
|
||||
if (err) {
|
||||
pr_err("failed to register GIC IRQ handler\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
irq_set_chained_handler_and_data(msi_group->gic_irq,
|
||||
xgene_msi_isr, msi_group);
|
||||
|
||||
/*
|
||||
* Statically allocate MSI GIC IRQs to each CPU core.
|
||||
* With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
|
||||
|
@ -173,12 +173,13 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
|
||||
|
||||
/*
|
||||
* The v1 controller has a bug in its Configuration Request
|
||||
* Retry Status (CRS) logic: when CRS is enabled and we read the
|
||||
* Vendor and Device ID of a non-existent device, the controller
|
||||
* fabricates return data of 0xFFFF0001 ("device exists but is not
|
||||
* ready") instead of 0xFFFFFFFF ("device does not exist"). This
|
||||
* causes the PCI core to retry the read until it times out.
|
||||
* Avoid this by not claiming to support CRS.
|
||||
* Retry Status (CRS) logic: when CRS Software Visibility is
|
||||
* enabled and we read the Vendor and Device ID of a non-existent
|
||||
* device, the controller fabricates return data of 0xFFFF0001
|
||||
* ("device exists but is not ready") instead of 0xFFFFFFFF
|
||||
* ("device does not exist"). This causes the PCI core to retry
|
||||
* the read until it times out. Avoid this by not claiming to
|
||||
* support CRS SV.
|
||||
*/
|
||||
if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
|
||||
((where & ~0x3) == XGENE_V1_PCI_EXP_CAP + PCI_EXP_RTCTL))
|
||||
|
@ -204,8 +204,7 @@ static int altera_msi_remove(struct platform_device *pdev)
|
||||
struct altera_msi *msi = platform_get_drvdata(pdev);
|
||||
|
||||
msi_writel(msi, 0, MSI_INTMASK);
|
||||
irq_set_chained_handler(msi->irq, NULL);
|
||||
irq_set_handler_data(msi->irq, NULL);
|
||||
irq_set_chained_handler_and_data(msi->irq, NULL, NULL);
|
||||
|
||||
altera_free_domains(msi);
|
||||
|
||||
|
@ -97,6 +97,7 @@
|
||||
|
||||
#define PCIE_MISC_REVISION 0x406c
|
||||
#define BRCM_PCIE_HW_REV_33 0x0303
|
||||
#define BRCM_PCIE_HW_REV_3_20 0x0320
|
||||
|
||||
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070
|
||||
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000
|
||||
@ -187,6 +188,7 @@
|
||||
struct brcm_pcie;
|
||||
static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val);
|
||||
static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val);
|
||||
static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val);
|
||||
static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val);
|
||||
static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val);
|
||||
|
||||
@ -203,6 +205,7 @@ enum {
|
||||
|
||||
enum pcie_type {
|
||||
GENERIC,
|
||||
BCM4908,
|
||||
BCM7278,
|
||||
BCM2711,
|
||||
};
|
||||
@ -227,6 +230,13 @@ static const struct pcie_cfg_data generic_cfg = {
|
||||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
};
|
||||
|
||||
static const struct pcie_cfg_data bcm4908_cfg = {
|
||||
.offsets = pcie_offsets,
|
||||
.type = BCM4908,
|
||||
.perst_set = brcm_pcie_perst_set_4908,
|
||||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
};
|
||||
|
||||
static const int pcie_offset_bcm7278[] = {
|
||||
[RGR1_SW_INIT_1] = 0xc010,
|
||||
[EXT_CFG_INDEX] = 0x9000,
|
||||
@ -279,6 +289,7 @@ struct brcm_pcie {
|
||||
const int *reg_offsets;
|
||||
enum pcie_type type;
|
||||
struct reset_control *rescal;
|
||||
struct reset_control *perst_reset;
|
||||
int num_memc;
|
||||
u64 memc_size[PCIE_BRCM_MAX_MEMC];
|
||||
u32 hw_rev;
|
||||
@ -603,8 +614,7 @@ static void brcm_msi_remove(struct brcm_pcie *pcie)
|
||||
|
||||
if (!msi)
|
||||
return;
|
||||
irq_set_chained_handler(msi->irq, NULL);
|
||||
irq_set_handler_data(msi->irq, NULL);
|
||||
irq_set_chained_handler_and_data(msi->irq, NULL, NULL);
|
||||
brcm_free_domains(msi);
|
||||
}
|
||||
|
||||
@ -735,6 +745,17 @@ static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32
|
||||
writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
|
||||
}
|
||||
|
||||
static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
|
||||
{
|
||||
if (WARN_ONCE(!pcie->perst_reset, "missing PERST# reset controller\n"))
|
||||
return;
|
||||
|
||||
if (val)
|
||||
reset_control_assert(pcie->perst_reset);
|
||||
else
|
||||
reset_control_deassert(pcie->perst_reset);
|
||||
}
|
||||
|
||||
static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val)
|
||||
{
|
||||
u32 tmp;
|
||||
@ -1194,6 +1215,7 @@ static int brcm_pcie_remove(struct platform_device *pdev)
|
||||
|
||||
static const struct of_device_id brcm_pcie_match[] = {
|
||||
{ .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg },
|
||||
{ .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg },
|
||||
{ .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg },
|
||||
{ .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg },
|
||||
{ .compatible = "brcm,bcm7216-pcie", .data = &bcm7278_cfg },
|
||||
@ -1250,6 +1272,11 @@ static int brcm_pcie_probe(struct platform_device *pdev)
|
||||
clk_disable_unprepare(pcie->clk);
|
||||
return PTR_ERR(pcie->rescal);
|
||||
}
|
||||
pcie->perst_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "perst");
|
||||
if (IS_ERR(pcie->perst_reset)) {
|
||||
clk_disable_unprepare(pcie->clk);
|
||||
return PTR_ERR(pcie->perst_reset);
|
||||
}
|
||||
|
||||
ret = reset_control_deassert(pcie->rescal);
|
||||
if (ret)
|
||||
@ -1267,6 +1294,10 @@ static int brcm_pcie_probe(struct platform_device *pdev)
|
||||
goto fail;
|
||||
|
||||
pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION);
|
||||
if (pcie->type == BCM4908 && pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) {
|
||||
dev_err(pcie->dev, "hardware revision with unsupported PERST# setup\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
|
||||
if (pci_msi_enabled() && msi_np == pcie->np) {
|
||||
|
@ -1035,14 +1035,14 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
|
||||
err = of_pci_get_devfn(child);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to parse devfn: %d\n", err);
|
||||
return err;
|
||||
goto error_put_node;
|
||||
}
|
||||
|
||||
slot = PCI_SLOT(err);
|
||||
|
||||
err = mtk_pcie_parse_port(pcie, child, slot);
|
||||
if (err)
|
||||
return err;
|
||||
goto error_put_node;
|
||||
}
|
||||
|
||||
err = mtk_pcie_subsys_powerup(pcie);
|
||||
@ -1058,6 +1058,9 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
|
||||
mtk_pcie_subsys_powerdown(pcie);
|
||||
|
||||
return 0;
|
||||
error_put_node:
|
||||
of_node_put(child);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mtk_pcie_probe(struct platform_device *pdev)
|
||||
|
1138
drivers/pci/controller/pcie-microchip-host.c
Normal file
1138
drivers/pci/controller/pcie-microchip-host.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -735,7 +735,7 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host)
|
||||
}
|
||||
|
||||
/* setup MSI data target */
|
||||
msi->pages = __get_free_pages(GFP_KERNEL, 0);
|
||||
msi->pages = __get_free_pages(GFP_KERNEL | GFP_DMA32, 0);
|
||||
rcar_pcie_hw_enable_msi(host);
|
||||
|
||||
return 0;
|
||||
|
@ -82,7 +82,7 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
|
||||
}
|
||||
|
||||
rockchip->mgmt_sticky_rst = devm_reset_control_get_exclusive(dev,
|
||||
"mgmt-sticky");
|
||||
"mgmt-sticky");
|
||||
if (IS_ERR(rockchip->mgmt_sticky_rst)) {
|
||||
if (PTR_ERR(rockchip->mgmt_sticky_rst) != -EPROBE_DEFER)
|
||||
dev_err(dev, "missing mgmt-sticky reset property in node\n");
|
||||
@ -118,11 +118,11 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
|
||||
}
|
||||
|
||||
if (rockchip->is_rc) {
|
||||
rockchip->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(rockchip->ep_gpio)) {
|
||||
dev_err(dev, "missing ep-gpios property in node\n");
|
||||
return PTR_ERR(rockchip->ep_gpio);
|
||||
}
|
||||
rockchip->ep_gpio = devm_gpiod_get_optional(dev, "ep",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(rockchip->ep_gpio))
|
||||
return dev_err_probe(dev, PTR_ERR(rockchip->ep_gpio),
|
||||
"failed to get ep GPIO\n");
|
||||
}
|
||||
|
||||
rockchip->aclk_pcie = devm_clk_get(dev, "aclk");
|
||||
|
@ -1,341 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#define MSI_MAX 256
|
||||
|
||||
#define SMP8759_MUX 0x48
|
||||
#define SMP8759_TEST_OUT 0x74
|
||||
#define SMP8759_DOORBELL 0x7c
|
||||
#define SMP8759_STATUS 0x80
|
||||
#define SMP8759_ENABLE 0xa0
|
||||
|
||||
struct tango_pcie {
|
||||
DECLARE_BITMAP(used_msi, MSI_MAX);
|
||||
u64 msi_doorbell;
|
||||
spinlock_t used_msi_lock;
|
||||
void __iomem *base;
|
||||
struct irq_domain *dom;
|
||||
};
|
||||
|
||||
static void tango_msi_isr(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct tango_pcie *pcie = irq_desc_get_handler_data(desc);
|
||||
unsigned long status, base, virq, idx, pos = 0;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
spin_lock(&pcie->used_msi_lock);
|
||||
|
||||
while ((pos = find_next_bit(pcie->used_msi, MSI_MAX, pos)) < MSI_MAX) {
|
||||
base = round_down(pos, 32);
|
||||
status = readl_relaxed(pcie->base + SMP8759_STATUS + base / 8);
|
||||
for_each_set_bit(idx, &status, 32) {
|
||||
virq = irq_find_mapping(pcie->dom, base + idx);
|
||||
generic_handle_irq(virq);
|
||||
}
|
||||
pos = base + 32;
|
||||
}
|
||||
|
||||
spin_unlock(&pcie->used_msi_lock);
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void tango_ack(struct irq_data *d)
|
||||
{
|
||||
struct tango_pcie *pcie = d->chip_data;
|
||||
u32 offset = (d->hwirq / 32) * 4;
|
||||
u32 bit = BIT(d->hwirq % 32);
|
||||
|
||||
writel_relaxed(bit, pcie->base + SMP8759_STATUS + offset);
|
||||
}
|
||||
|
||||
static void update_msi_enable(struct irq_data *d, bool unmask)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct tango_pcie *pcie = d->chip_data;
|
||||
u32 offset = (d->hwirq / 32) * 4;
|
||||
u32 bit = BIT(d->hwirq % 32);
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&pcie->used_msi_lock, flags);
|
||||
val = readl_relaxed(pcie->base + SMP8759_ENABLE + offset);
|
||||
val = unmask ? val | bit : val & ~bit;
|
||||
writel_relaxed(val, pcie->base + SMP8759_ENABLE + offset);
|
||||
spin_unlock_irqrestore(&pcie->used_msi_lock, flags);
|
||||
}
|
||||
|
||||
static void tango_mask(struct irq_data *d)
|
||||
{
|
||||
update_msi_enable(d, false);
|
||||
}
|
||||
|
||||
static void tango_unmask(struct irq_data *d)
|
||||
{
|
||||
update_msi_enable(d, true);
|
||||
}
|
||||
|
||||
static int tango_set_affinity(struct irq_data *d, const struct cpumask *mask,
|
||||
bool force)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void tango_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
|
||||
{
|
||||
struct tango_pcie *pcie = d->chip_data;
|
||||
msg->address_lo = lower_32_bits(pcie->msi_doorbell);
|
||||
msg->address_hi = upper_32_bits(pcie->msi_doorbell);
|
||||
msg->data = d->hwirq;
|
||||
}
|
||||
|
||||
static struct irq_chip tango_chip = {
|
||||
.irq_ack = tango_ack,
|
||||
.irq_mask = tango_mask,
|
||||
.irq_unmask = tango_unmask,
|
||||
.irq_set_affinity = tango_set_affinity,
|
||||
.irq_compose_msi_msg = tango_compose_msi_msg,
|
||||
};
|
||||
|
||||
static void msi_ack(struct irq_data *d)
|
||||
{
|
||||
irq_chip_ack_parent(d);
|
||||
}
|
||||
|
||||
static void msi_mask(struct irq_data *d)
|
||||
{
|
||||
pci_msi_mask_irq(d);
|
||||
irq_chip_mask_parent(d);
|
||||
}
|
||||
|
||||
static void msi_unmask(struct irq_data *d)
|
||||
{
|
||||
pci_msi_unmask_irq(d);
|
||||
irq_chip_unmask_parent(d);
|
||||
}
|
||||
|
||||
static struct irq_chip msi_chip = {
|
||||
.name = "MSI",
|
||||
.irq_ack = msi_ack,
|
||||
.irq_mask = msi_mask,
|
||||
.irq_unmask = msi_unmask,
|
||||
};
|
||||
|
||||
static struct msi_domain_info msi_dom_info = {
|
||||
.flags = MSI_FLAG_PCI_MSIX
|
||||
| MSI_FLAG_USE_DEF_DOM_OPS
|
||||
| MSI_FLAG_USE_DEF_CHIP_OPS,
|
||||
.chip = &msi_chip,
|
||||
};
|
||||
|
||||
static int tango_irq_domain_alloc(struct irq_domain *dom, unsigned int virq,
|
||||
unsigned int nr_irqs, void *args)
|
||||
{
|
||||
struct tango_pcie *pcie = dom->host_data;
|
||||
unsigned long flags;
|
||||
int pos;
|
||||
|
||||
spin_lock_irqsave(&pcie->used_msi_lock, flags);
|
||||
pos = find_first_zero_bit(pcie->used_msi, MSI_MAX);
|
||||
if (pos >= MSI_MAX) {
|
||||
spin_unlock_irqrestore(&pcie->used_msi_lock, flags);
|
||||
return -ENOSPC;
|
||||
}
|
||||
__set_bit(pos, pcie->used_msi);
|
||||
spin_unlock_irqrestore(&pcie->used_msi_lock, flags);
|
||||
irq_domain_set_info(dom, virq, pos, &tango_chip,
|
||||
pcie, handle_edge_irq, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tango_irq_domain_free(struct irq_domain *dom, unsigned int virq,
|
||||
unsigned int nr_irqs)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_data *d = irq_domain_get_irq_data(dom, virq);
|
||||
struct tango_pcie *pcie = d->chip_data;
|
||||
|
||||
spin_lock_irqsave(&pcie->used_msi_lock, flags);
|
||||
__clear_bit(d->hwirq, pcie->used_msi);
|
||||
spin_unlock_irqrestore(&pcie->used_msi_lock, flags);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops dom_ops = {
|
||||
.alloc = tango_irq_domain_alloc,
|
||||
.free = tango_irq_domain_free,
|
||||
};
|
||||
|
||||
static int smp8759_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
struct tango_pcie *pcie = dev_get_drvdata(cfg->parent);
|
||||
int ret;
|
||||
|
||||
/* Reads in configuration space outside devfn 0 return garbage */
|
||||
if (devfn != 0)
|
||||
return PCIBIOS_FUNC_NOT_SUPPORTED;
|
||||
|
||||
/*
|
||||
* PCI config and MMIO accesses are muxed. Linux doesn't have a
|
||||
* mutual exclusion mechanism for config vs. MMIO accesses, so
|
||||
* concurrent accesses may cause corruption.
|
||||
*/
|
||||
writel_relaxed(1, pcie->base + SMP8759_MUX);
|
||||
ret = pci_generic_config_read(bus, devfn, where, size, val);
|
||||
writel_relaxed(0, pcie->base + SMP8759_MUX);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int smp8759_config_write(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 val)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
struct tango_pcie *pcie = dev_get_drvdata(cfg->parent);
|
||||
int ret;
|
||||
|
||||
writel_relaxed(1, pcie->base + SMP8759_MUX);
|
||||
ret = pci_generic_config_write(bus, devfn, where, size, val);
|
||||
writel_relaxed(0, pcie->base + SMP8759_MUX);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pci_ecam_ops smp8759_ecam_ops = {
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = smp8759_config_read,
|
||||
.write = smp8759_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static int tango_pcie_link_up(struct tango_pcie *pcie)
|
||||
{
|
||||
void __iomem *test_out = pcie->base + SMP8759_TEST_OUT;
|
||||
int i;
|
||||
|
||||
writel_relaxed(16, test_out);
|
||||
for (i = 0; i < 10; ++i) {
|
||||
u32 ltssm_state = readl_relaxed(test_out) >> 8;
|
||||
if ((ltssm_state & 0x1f) == 0xf) /* L0 */
|
||||
return 1;
|
||||
usleep_range(3000, 4000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tango_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct tango_pcie *pcie;
|
||||
struct resource *res;
|
||||
struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
|
||||
struct irq_domain *msi_dom, *irq_dom;
|
||||
struct of_pci_range_parser parser;
|
||||
struct of_pci_range range;
|
||||
int virq, offset;
|
||||
|
||||
dev_warn(dev, "simultaneous PCI config and MMIO accesses may cause data corruption\n");
|
||||
add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);
|
||||
|
||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
pcie->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(pcie->base))
|
||||
return PTR_ERR(pcie->base);
|
||||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
||||
if (!tango_pcie_link_up(pcie))
|
||||
return -ENODEV;
|
||||
|
||||
if (of_pci_dma_range_parser_init(&parser, dev->of_node) < 0)
|
||||
return -ENOENT;
|
||||
|
||||
if (of_pci_range_parser_one(&parser, &range) == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
range.pci_addr += range.size;
|
||||
pcie->msi_doorbell = range.pci_addr + res->start + SMP8759_DOORBELL;
|
||||
|
||||
for (offset = 0; offset < MSI_MAX / 8; offset += 4)
|
||||
writel_relaxed(0, pcie->base + SMP8759_ENABLE + offset);
|
||||
|
||||
virq = platform_get_irq(pdev, 1);
|
||||
if (virq < 0)
|
||||
return virq;
|
||||
|
||||
irq_dom = irq_domain_create_linear(fwnode, MSI_MAX, &dom_ops, pcie);
|
||||
if (!irq_dom) {
|
||||
dev_err(dev, "Failed to create IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
msi_dom = pci_msi_create_irq_domain(fwnode, &msi_dom_info, irq_dom);
|
||||
if (!msi_dom) {
|
||||
dev_err(dev, "Failed to create MSI domain\n");
|
||||
irq_domain_remove(irq_dom);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pcie->dom = irq_dom;
|
||||
spin_lock_init(&pcie->used_msi_lock);
|
||||
irq_set_chained_handler_and_data(virq, tango_msi_isr, pcie);
|
||||
|
||||
return pci_host_common_probe(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id tango_pcie_ids[] = {
|
||||
{
|
||||
.compatible = "sigma,smp8759-pcie",
|
||||
.data = &smp8759_ecam_ops,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver tango_pcie_driver = {
|
||||
.probe = tango_pcie_probe,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = tango_pcie_ids,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(tango_pcie_driver);
|
||||
|
||||
/*
|
||||
* The root complex advertises the wrong device class.
|
||||
* Header Type 1 is for PCI-to-PCI bridges.
|
||||
*/
|
||||
static void tango_fixup_class(struct pci_dev *dev)
|
||||
{
|
||||
dev->class = PCI_CLASS_BRIDGE_PCI << 8;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_class);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_class);
|
||||
|
||||
/*
|
||||
* The root complex exposes a "fake" BAR, which is used to filter
|
||||
* bus-to-system accesses. Only accesses within the range defined by this
|
||||
* BAR are forwarded to the host, others are ignored.
|
||||
*
|
||||
* By default, the DMA framework expects an identity mapping, and DRAM0 is
|
||||
* mapped at 0x80000000.
|
||||
*/
|
||||
static void tango_fixup_bar(struct pci_dev *dev)
|
||||
{
|
||||
dev->non_compliant_bars = true;
|
||||
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0x80000000);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_bar);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_bar);
|
@ -404,6 +404,7 @@ static int xilinx_cpm_pcie_init_irq_domain(struct xilinx_cpm_pcie_port *port)
|
||||
return 0;
|
||||
out:
|
||||
xilinx_cpm_free_irq_domains(port);
|
||||
of_node_put(pcie_intc_node);
|
||||
dev_err(dev, "Failed to allocate IRQ domains\n");
|
||||
|
||||
return -ENOMEM;
|
||||
|
Reference in New Issue
Block a user