diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-11 17:20:38 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-11 17:20:38 -0800 |
| commit | 1c2b4a4c2bcb950f182eeeb33d94b565607608cf (patch) | |
| tree | 736a30dfd9604700677724f9315ced950c7dbb05 /drivers/pci/controller | |
| parent | 61e629596fabd7f60cc3748a603703c5d9b58428 (diff) | |
| parent | dff645f564c38332502140f3ef643f659114c45f (diff) | |
Merge tag 'pci-v7.0-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
Pull PCI updates from Bjorn Helgaas:
"Enumeration:
- Don't try to enable Extended Tags on VFs since that bit is Reserved
and causes misleading log messages (Håkon Bugge)
- Initialize Endpoint Read Completion Boundary to match Root Port,
regardless of ACPI _HPX (Håkon Bugge)
- Apply _HPX PCIe Setting Record only to AER configuration, and only
when OS owns PCIe hotplug but not AER, to avoid clobbering Extended
Tag and Relaxed Ordering settings (Håkon Bugge)
Resource management:
- Move CardBus code to setup-cardbus.c and only build it when
CONFIG_CARDBUS is set (Ilpo Järvinen)
- Fix bridge window alignment with optional resources, where
additional alignment requirement was previously lost (Ilpo
Järvinen)
- Stop over-estimating bridge window size since they are now assigned
without any gaps between them (Ilpo Järvinen)
- Increase resource MAX_IORES_LEVEL to avoid /proc/iomem flattening
for nested bridges and endpoints (Ilpo Järvinen)
- Add pbus_mem_size_optional() to handle sizes of optional resources
(SR-IOV VF BARs, expansion ROMs, bridge windows) (Ilpo Järvinen)
- Don't claim disabled bridge windows to avoid spurious claim
failures (Ilpo Järvinen)
Driver binding:
- Fix device reference leak in pcie_port_remove_service() (Uwe
Kleine-König)
- Move pcie_port_bus_match() and pcie_port_bus_type to PCIe-specific
portdrv.c (Uwe Kleine-König)
- Convert portdrv to use pcie_port_bus_type.probe() and .remove()
callbacks so .probe() and .remove() can eventually be removed from
struct device_driver (Uwe Kleine-König)
Error handling:
- Clear stale errors on reporting agents upon probe so they don't
look like recent errors (Lukas Wunner)
- Add generic RAS tracepoint for hotplug events (Shuai Xue)
- Add RAS tracepoint for link speed changes (Shuai Xue)
Power management:
- Avoid redundant delay on transition from D3hot to D3cold if the
device was already in D3hot (Brian Norris)
- Prevent runtime suspend until devices are fully initialized to
avoid saving incompletely configured device state (Brian Norris)
Power control:
- Add power_on/off callbacks with generic signature to pwrseq,
tc9563, and slot drivers so they can be used by pwrctrl core
(Manivannan Sadhasivam)
- Add PCIe M.2 connector support to the slot pwrctrl driver
(Manivannan Sadhasivam)
- Switch to pwrctrl interfaces to create, destroy, and power on/off
devices, calling them from host controller drivers instead of the
PCI core (Manivannan Sadhasivam)
- Drop qcom .assert_perst() callbacks since this is now done by the
controller driver instead of the pwrctrl driver (Manivannan
Sadhasivam)
Virtualization:
- Remove an incorrect unlock in pci_slot_trylock() error handling
(Jinhui Guo)
- Lock the bridge device for slot reset (Keith Busch)
- Enable ACS after IOMMU configuration on OF platforms so ACS is
enabled an all devices; previously the first device enumerated
(typically a Root Port) didn't have ACS enabled (Manivannan
Sadhasivam)
- Disable ACS Source Validation for IDT 0x80b5 and 0x8090 switches to
work around hardware erratum; previously ACS SV was only
temporarily disabled, which worked for enumeration but not after
reset (Manivannan Sadhasivam)
Peer-to-peer DMA:
- Release per-CPU pgmap ref when vm_insert_page() fails to avoid hang
when removing the PCI device (Hou Tao)
- Remove incorrect p2pmem_alloc_mmap() warning about page refcount
(Hou Tao)
Endpoint framework:
- Add configfs sub-groups synchronously to avoid NULL pointer
dereference when racing with removal (Liu Song)
- Fix swapped parameters in pci_{primary/secondary}_epc_epf_unlink()
functions (Manikanta Maddireddy)
ASPEED PCIe controller driver:
- Add ASPEED Root Complex DT binding and driver (Jacky Chou)
Freescale i.MX6 PCIe controller driver:
- Add DT binding and driver support for an optional external refclock
in addition to the refclock from the internal PLL (Richard Zhu)
- Fix CLKREQ# control so host asserts it during enumeration and
Endpoints can use it afterwards to exit the L1.2 link state
(Richard Zhu)
NVIDIA Tegra PCIe controller driver:
- Export irq_domain_free_irqs() to allow PCI/MSI drivers that tear
down MSI domains to be built as modules (Aaron Kling)
- Allow pci-tegra to be built as a module (Aaron Kling)
NVIDIA Tegra194 PCIe controller driver:
- Relax Kconfig so tegra194 can be built for platforms beyond
Tegra194 (Vidya Sagar)
Qualcomm PCIe controller driver:
- Merge SC8180x DT binding into SM8150 (Krzysztof Kozlowski)
- Move SDX55, SDM845, QCS404, IPQ5018, IPQ6018, IPQ8074 Gen3,
IPQ8074, IPQ4019, IPQ9574, APQ8064, MSM8996, APQ8084 to dedicated
schema (Krzysztof Kozlowski)
- Add DT binding and driver support for SA8255p Endpoint being
configured by firmware (Mrinmay Sarkar)
- Parse PERST# from all PCIe bridge nodes for future platforms that
will have PERST# in Switch Downstream Ports as well as in Root
Ports (Manivannan Sadhasivam)
Renesas RZ/G3S PCIe controller driver:
- Use pci_generic_config_write() since the writability provided by
the custom wrapper is unnecessary (Claudiu Beznea)
SOPHGO PCIe controller driver:
- Disable ASPM L0s and L1 on Sophgo 2044 PCIe Root Ports (Inochi
Amaoto)
Synopsys DesignWare PCIe controller driver:
- Extend PCI_FIND_NEXT_CAP() and PCI_FIND_NEXT_EXT_CAP() to return a
pointer to the preceding Capability, to allow removal of
Capabilities that are advertised but not fully implemented (Qiang
Yu)
- Remove MSI and MSI-X Capabilities in platforms that can't support
them, so the PCI core automatically falls back to INTx (Qiang Yu)
- Add ASPM L1.1 and L1.2 Substates context to debugfs ltssm_status
for drivers that support this (Shawn Lin)
- Skip PME_Turn_Off broadcast and L2/L3 transition during suspend if
link is not up to avoid an unnecessary timeout (Manivannan
Sadhasivam)
- Revert dw-rockchip, qcom, and DWC core changes that used link-up
IRQs to trigger enumeration instead of waiting for link to be up
because the PCI core doesn't allocate bus number space for
hierarchies that might be attached (Niklas Cassel)
- Make endpoint iATU entry for MSI permanent instead of programming
it dynamically, which is slow and racy with respect to other
concurrent traffic, e.g., eDMA (Koichiro Den)
- Use iMSI-RX MSI target address when possible to fix endpoints using
32-bit MSI (Shawn Lin)
- Allow DWC host controller driver probe to continue if device is not
found or found but inactive; only fail when there's an error with
the link (Manivannan Sadhasivam)
- For controllers like NXP i.MX6QP and i.MX7D, where LTSSM registers
are not accessible after PME_Turn_Off, simply wait 10ms instead of
polling for L2/L3 Ready (Richard Zhu)
- Use multiple iATU entries to map large bridge windows and DMA
ranges when necessary instead of failing (Samuel Holland)
- Add EPC dynamic_inbound_mapping feature bit for Endpoint
Controllers that can update BAR inbound address translation without
requiring EPF driver to clear/reset the BAR first, and advertise it
for DWC-based Endpoints (Koichiro Den)
- Add EPC subrange_mapping feature bit for Endpoint Controllers that
can map multiple independent inbound regions in a single BAR,
implement subrange mapping, advertise it for DWC-based Endpoints,
and add Endpoint selftests for it (Koichiro Den)
- Make resizable BARs work for Endpoint multi-PF configurations;
previously it only worked for PF 0 (Aksh Garg)
- Fix Endpoint non-PF 0 support for BAR configuration, ATU mappings,
and Address Match Mode (Aksh Garg)
- Set up iATU when ECAM is enabled; previously IO and MEM outbound
windows weren't programmed, and ECAM-related iATU entries weren't
restored after suspend/resume, so config accesses failed (Krishna
Chaitanya Chundru)
Miscellaneous:
- Use system_percpu_wq and WQ_PERCPU to explicitly request per-CPU
work so WQ_UNBOUND can eventually be removed (Marco Crivellari)"
* tag 'pci-v7.0-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci: (176 commits)
PCI/bwctrl: Disable BW controller on Intel P45 using a quirk
PCI: Disable ACS SV for IDT 0x8090 switch
PCI: Disable ACS SV for IDT 0x80b5 switch
PCI: Cache ACS Capabilities register
PCI: Enable ACS after configuring IOMMU for OF platforms
PCI: Add ACS quirk for Pericom PI7C9X2G404 switches [12d8:b404]
PCI: Add ACS quirk for Qualcomm Hamoa & Glymur
PCI: Use device_lock_assert() to verify device lock is held
PCI: Use lockdep_assert_held(pci_bus_sem) to verify lock is held
PCI: Fix pci_slot_lock () device locking
PCI: Fix pci_slot_trylock() error handling
PCI: Mark Nvidia GB10 to avoid bus reset
PCI: Mark ASM1164 SATA controller to avoid bus reset
PCI: host-generic: Avoid reporting incorrect 'missing reg property' error
PCI/PME: Replace RMW of Root Status register with direct write
PCI/AER: Clear stale errors on reporting agents upon probe
PCI: Don't claim disabled bridge windows
PCI: rzg3s-host: Fix device node reference leak in rzg3s_pcie_host_parse_port()
PCI: dwc: Fix missing iATU setup when ECAM is enabled
PCI: dwc: Clean up iATU index usage in dw_pcie_iatu_setup()
...
Diffstat (limited to 'drivers/pci/controller')
33 files changed, 2172 insertions, 513 deletions
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index c254d2b8bf17..5aaed8ac6e44 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -58,6 +58,22 @@ config PCI_VERSATILE bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE || COMPILE_TEST +config PCIE_ASPEED + bool "ASPEED PCIe controller" + depends on ARCH_ASPEED || COMPILE_TEST + depends on OF + depends on PCI_MSI + select IRQ_MSI_LIB + help + Enable this option to support the PCIe controller found on ASPEED + SoCs. + + This driver provides initialization and management for PCIe + Root Complex functionality, including INTx and MSI support. + + Select Y if your platform uses an ASPEED SoC and requires PCIe + connectivity. + config PCIE_BRCMSTB tristate "Broadcom Brcmstb PCIe controller" depends on ARCH_BRCMSTB || ARCH_BCM2835 || ARCH_BCMBCA || \ @@ -232,7 +248,7 @@ config PCI_HYPERV_INTERFACE driver. config PCI_TEGRA - bool "NVIDIA Tegra PCIe controller" + tristate "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA || COMPILE_TEST depends on PCI_MSI select IRQ_MSI_LIB @@ -243,6 +259,7 @@ config PCI_TEGRA config PCIE_RCAR_HOST bool "Renesas R-Car PCIe controller (host mode)" depends on ARCH_RENESAS || COMPILE_TEST + depends on OF depends on PCI_MSI select IRQ_MSI_LIB help diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 229929a945c2..ac8db283f0fe 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o +obj-$(CONFIG_PCIE_ASPEED) += pcie-aspeed.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index ecd1b0312400..6f2501479c70 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -620,9 +620,11 @@ static int j721e_pcie_probe(struct platform_device *pdev) gpiod_set_value_cansleep(pcie->reset_gpio, 1); } - ret = cdns_pcie_host_setup(rc); - if (ret < 0) - goto err_pcie_setup; + if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) { + ret = cdns_pcie_host_setup(rc); + if (ret < 0) + goto err_pcie_setup; + } break; case PCI_MODE_EP: @@ -632,9 +634,11 @@ static int j721e_pcie_probe(struct platform_device *pdev) goto err_get_sync; } - ret = cdns_pcie_ep_setup(ep); - if (ret < 0) - goto err_pcie_setup; + if (IS_ENABLED(CONFIG_PCI_J721E_EP)) { + ret = cdns_pcie_ep_setup(ep); + if (ret < 0) + goto err_pcie_setup; + } break; } @@ -659,10 +663,11 @@ static void j721e_pcie_remove(struct platform_device *pdev) struct cdns_pcie_ep *ep; struct cdns_pcie_rc *rc; - if (pcie->mode == PCI_MODE_RC) { + if (IS_ENABLED(CONFIG_PCI_J721E_HOST) && + pcie->mode == PCI_MODE_RC) { rc = container_of(cdns_pcie, struct cdns_pcie_rc, pcie); cdns_pcie_host_disable(rc); - } else { + } else if (IS_ENABLED(CONFIG_PCI_J721E_EP)) { ep = container_of(cdns_pcie, struct cdns_pcie_ep, pcie); cdns_pcie_ep_disable(ep); } @@ -728,10 +733,12 @@ static int j721e_pcie_resume_noirq(struct device *dev) gpiod_set_value_cansleep(pcie->reset_gpio, 1); } - ret = cdns_pcie_host_link_setup(rc); - if (ret < 0) { - clk_disable_unprepare(pcie->refclk); - return ret; + if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) { + ret = cdns_pcie_host_link_setup(rc); + if (ret < 0) { + clk_disable_unprepare(pcie->refclk); + return ret; + } } /* @@ -741,10 +748,12 @@ static int j721e_pcie_resume_noirq(struct device *dev) for (enum cdns_pcie_rp_bar bar = RP_BAR0; bar <= RP_NO_BAR; bar++) rc->avail_ib_bar[bar] = true; - ret = cdns_pcie_host_init(rc); - if (ret) { - clk_disable_unprepare(pcie->refclk); - return ret; + if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) { + ret = cdns_pcie_host_init(rc); + if (ret) { + clk_disable_unprepare(pcie->refclk); + return ret; + } } } diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.c b/drivers/pci/controller/cadence/pcie-cadence-host-common.c index 15415d7f35ee..2b0211870f02 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host-common.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c @@ -173,11 +173,21 @@ int cdns_pcie_host_dma_ranges_cmp(void *priv, const struct list_head *a, const struct list_head *b) { struct resource_entry *entry1, *entry2; + u64 size1, size2; entry1 = container_of(a, struct resource_entry, node); entry2 = container_of(b, struct resource_entry, node); - return resource_size(entry2->res) - resource_size(entry1->res); + size1 = resource_size(entry1->res); + size2 = resource_size(entry2->res); + + if (size1 > size2) + return -1; + + if (size1 < size2) + return 1; + + return 0; } EXPORT_SYMBOL_GPL(cdns_pcie_host_dma_ranges_cmp); diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c index e6f1a4ac0fb7..a1eada56edba 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.c +++ b/drivers/pci/controller/cadence/pcie-cadence.c @@ -13,13 +13,13 @@ u8 cdns_pcie_find_capability(struct cdns_pcie *pcie, u8 cap) { return PCI_FIND_NEXT_CAP(cdns_pcie_read_cfg, PCI_CAPABILITY_LIST, - cap, pcie); + cap, NULL, pcie); } EXPORT_SYMBOL_GPL(cdns_pcie_find_capability); u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap) { - return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, pcie); + return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, NULL, pcie); } EXPORT_SYMBOL_GPL(cdns_pcie_find_ext_capability); diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 519b59422b47..d0aa031397fa 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -228,7 +228,7 @@ config PCIE_TEGRA194 config PCIE_TEGRA194_HOST tristate "NVIDIA Tegra194 (and later) PCIe controller (host mode)" - depends on ARCH_TEGRA_194_SOC || COMPILE_TEST + depends on (ARCH_TEGRA && ARM64) || COMPILE_TEST depends on PCI_MSI select PCIE_DW_HOST select PHY_TEGRA194_P2U @@ -243,7 +243,7 @@ config PCIE_TEGRA194_HOST config PCIE_TEGRA194_EP tristate "NVIDIA Tegra194 (and later) PCIe controller (endpoint mode)" - depends on ARCH_TEGRA_194_SOC || COMPILE_TEST + depends on (ARCH_TEGRA && ARM64) || COMPILE_TEST depends on PCI_ENDPOINT select PCIE_DW_EP select PHY_TEGRA194_P2U diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 01cfd9aeb0b8..d5d26229063f 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -424,6 +424,7 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features dra7xx_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, }; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 4668fc9648bf..a5b8d0b71677 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -52,6 +52,8 @@ #define IMX95_PCIE_REF_CLKEN BIT(23) #define IMX95_PCIE_PHY_CR_PARA_SEL BIT(9) #define IMX95_PCIE_SS_RW_REG_1 0xf4 +#define IMX95_PCIE_CLKREQ_OVERRIDE_EN BIT(8) +#define IMX95_PCIE_CLKREQ_OVERRIDE_VAL BIT(9) #define IMX95_PCIE_SYS_AUX_PWR_DET BIT(31) #define IMX95_PE0_GEN_CTRL_1 0x1050 @@ -114,6 +116,7 @@ enum imx_pcie_variants { #define IMX_PCIE_FLAG_BROKEN_SUSPEND BIT(9) #define IMX_PCIE_FLAG_HAS_LUT BIT(10) #define IMX_PCIE_FLAG_8GT_ECN_ERR051586 BIT(11) +#define IMX_PCIE_FLAG_SKIP_L23_READY BIT(12) #define imx_check_flag(pci, val) (pci->drvdata->flags & val) @@ -136,6 +139,7 @@ struct imx_pcie_drvdata { int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable); int (*core_reset)(struct imx_pcie *pcie, bool assert); int (*wait_pll_lock)(struct imx_pcie *pcie); + void (*clr_clkreq_override)(struct imx_pcie *pcie); const struct dw_pcie_host_ops *ops; }; @@ -149,6 +153,8 @@ struct imx_pcie { struct gpio_desc *reset_gpiod; struct clk_bulk_data *clks; int num_clks; + bool supports_clkreq; + bool enable_ext_refclk; struct regmap *iomuxc_gpr; u16 msi_ctrl; u32 controller_id; @@ -241,6 +247,8 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie) static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { + bool ext = imx_pcie->enable_ext_refclk; + /* * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready * Through Beacon or PERST# De-assertion @@ -259,13 +267,12 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, - IMX95_PCIE_PHY_GEN_CTRL, - IMX95_PCIE_REF_USE_PAD, 0); - regmap_update_bits(imx_pcie->iomuxc_gpr, - IMX95_PCIE_SS_RW_REG_0, + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, + ext ? IMX95_PCIE_REF_USE_PAD : 0, + IMX95_PCIE_REF_USE_PAD); + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, - IMX95_PCIE_REF_CLKEN); + ext ? 0 : IMX95_PCIE_REF_CLKEN); return 0; } @@ -685,7 +692,7 @@ static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) return 0; } -static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +static void imx8mm_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) { int offset = imx_pcie_grp_offset(imx_pcie); @@ -695,6 +702,11 @@ static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) regmap_update_bits(imx_pcie->iomuxc_gpr, offset, IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN, enable ? IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN : 0); +} + +static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +{ + imx8mm_pcie_clkreq_override(imx_pcie, enable); return 0; } @@ -706,6 +718,32 @@ static int imx7d_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) return 0; } +static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) +{ + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1, + IMX95_PCIE_CLKREQ_OVERRIDE_EN, + enable ? IMX95_PCIE_CLKREQ_OVERRIDE_EN : 0); + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1, + IMX95_PCIE_CLKREQ_OVERRIDE_VAL, + enable ? IMX95_PCIE_CLKREQ_OVERRIDE_VAL : 0); +} + +static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +{ + imx95_pcie_clkreq_override(imx_pcie, enable); + return 0; +} + +static void imx8mm_pcie_clr_clkreq_override(struct imx_pcie *imx_pcie) +{ + imx8mm_pcie_clkreq_override(imx_pcie, false); +} + +static void imx95_pcie_clr_clkreq_override(struct imx_pcie *imx_pcie) +{ + imx95_pcie_clkreq_override(imx_pcie, false); +} + static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie) { struct dw_pcie *pci = imx_pcie->pci; @@ -1322,6 +1360,12 @@ static void imx_pcie_host_post_init(struct dw_pcie_rp *pp) dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); dw_pcie_dbi_ro_wr_dis(pci); } + + /* Clear CLKREQ# override if supports_clkreq is true and link is up */ + if (dw_pcie_link_up(pci) && imx_pcie->supports_clkreq) { + if (imx_pcie->drvdata->clr_clkreq_override) + imx_pcie->drvdata->clr_clkreq_override(imx_pcie); + } } /* @@ -1387,6 +1431,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features imx8m_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, @@ -1396,6 +1441,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = { }; static const struct pci_epc_features imx8q_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, @@ -1416,6 +1462,7 @@ static const struct pci_epc_features imx8q_pcie_epc_features = { * BAR5 | Enable | 32-bit | 64 KB | Programmable Size */ static const struct pci_epc_features imx95_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, }, .align = SZ_4K, @@ -1602,7 +1649,7 @@ static int imx_pcie_probe(struct platform_device *pdev) struct imx_pcie *imx_pcie; struct device_node *np; struct device_node *node = dev->of_node; - int ret, domain; + int i, ret, domain; u16 val; imx_pcie = devm_kzalloc(dev, sizeof(*imx_pcie), GFP_KERNEL); @@ -1653,6 +1700,9 @@ static int imx_pcie_probe(struct platform_device *pdev) if (imx_pcie->num_clks < 0) return dev_err_probe(dev, imx_pcie->num_clks, "failed to get clocks\n"); + for (i = 0; i < imx_pcie->num_clks; i++) + if (strncmp(imx_pcie->clks[i].id, "extref", 6) == 0) + imx_pcie->enable_ext_refclk = true; if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHYDRV)) { imx_pcie->phy = devm_phy_get(dev, "pcie-phy"); @@ -1740,6 +1790,7 @@ static int imx_pcie_probe(struct platform_device *pdev) /* Limit link speed */ pci->max_link_speed = 1; of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed); + imx_pcie->supports_clkreq = of_property_read_bool(node, "supports-clkreq"); ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux"); if (ret < 0 && ret != -ENODEV) @@ -1777,6 +1828,8 @@ static int imx_pcie_probe(struct platform_device *pdev) */ imx_pcie_add_lut_by_rid(imx_pcie, 0); } else { + if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY)) + pci->pp.skip_l23_ready = true; pci->pp.use_atu_msg = true; ret = dw_pcie_host_init(&pci->pp); if (ret < 0) @@ -1838,6 +1891,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .variant = IMX6QP, .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | + IMX_PCIE_FLAG_SKIP_L23_READY | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", @@ -1854,6 +1908,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .variant = IMX7D, .flags = IMX_PCIE_FLAG_SUPPORTS_SUSPEND | IMX_PCIE_FLAG_HAS_APP_RESET | + IMX_PCIE_FLAG_SKIP_L23_READY | IMX_PCIE_FLAG_HAS_PHY_RESET, .gpr = "fsl,imx7d-iomuxc-gpr", .mode_off[0] = IOMUXC_GPR12, @@ -1873,6 +1928,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, .init_phy = imx8mq_pcie_init_phy, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8MM] = { .variant = IMX8MM, @@ -1883,6 +1939,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8MP] = { .variant = IMX8MP, @@ -1893,6 +1950,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8Q] = { .variant = IMX8Q, @@ -1913,6 +1971,8 @@ static const struct imx_pcie_drvdata drvdata[] = { .core_reset = imx95_pcie_core_reset, .init_phy = imx95_pcie_init_phy, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, + .enable_ref_clk = imx95_pcie_enable_ref_clk, + .clr_clkreq_override = imx95_pcie_clr_clkreq_override, }, [IMX8MQ_EP] = { .variant = IMX8MQ_EP, @@ -1969,6 +2029,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .core_reset = imx95_pcie_core_reset, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, .epc_features = &imx95_pcie_epc_features, + .enable_ref_clk = imx95_pcie_enable_ref_clk, .mode = DW_PCIE_EP_TYPE, }, }; diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index f86d9111f863..20fa4dadb82a 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -930,6 +930,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features ks_pcie_am654_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .msix_capable = true, .bar[BAR_0] = { .type = BAR_RESERVED, }, diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index f4a136ee2daf..e994b75986c3 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -370,6 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features artpec6_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, }; diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index 0fbf86c0b97e..0d1340c9b364 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -443,63 +443,13 @@ static ssize_t counter_value_read(struct file *file, char __user *buf, return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); } -static const char *ltssm_status_string(enum dw_pcie_ltssm ltssm) -{ - const char *str; - - switch (ltssm) { -#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3); - default: - str = "DW_PCIE_LTSSM_UNKNOWN"; - break; - } - - return str + strlen("DW_PCIE_LTSSM_"); -} - static int ltssm_status_show(struct seq_file *s, void *v) { struct dw_pcie *pci = s->private; enum dw_pcie_ltssm val; val = dw_pcie_get_ltssm(pci); - seq_printf(s, "%s (0x%02x)\n", ltssm_status_string(val), val); + seq_printf(s, "%s (0x%02x)\n", dw_pcie_ltssm_status_string(val), val); return 0; } diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 19571ac2b961..7e7844ff0f7e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -72,47 +72,15 @@ EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar); static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) { return PCI_FIND_NEXT_CAP(dw_pcie_ep_read_cfg, PCI_CAPABILITY_LIST, - cap, ep, func_no); + cap, NULL, ep, func_no); } -/** - * dw_pcie_ep_hide_ext_capability - Hide a capability from the linked list - * @pci: DWC PCI device - * @prev_cap: Capability preceding the capability that should be hidden - * @cap: Capability that should be hidden - * - * Return: 0 if success, errno otherwise. - */ -int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap) +static u16 dw_pcie_ep_find_ext_capability(struct dw_pcie_ep *ep, + u8 func_no, u8 cap) { - u16 prev_cap_offset, cap_offset; - u32 prev_cap_header, cap_header; - - prev_cap_offset = dw_pcie_find_ext_capability(pci, prev_cap); - if (!prev_cap_offset) - return -EINVAL; - - prev_cap_header = dw_pcie_readl_dbi(pci, prev_cap_offset); - cap_offset = PCI_EXT_CAP_NEXT(prev_cap_header); - cap_header = dw_pcie_readl_dbi(pci, cap_offset); - - /* cap must immediately follow prev_cap. */ - if (PCI_EXT_CAP_ID(cap_header) != cap) - return -EINVAL; - - /* Clear next ptr. */ - prev_cap_header &= ~GENMASK(31, 20); - - /* Set next ptr to next ptr of cap. */ - prev_cap_header |= cap_header & GENMASK(31, 20); - - dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi(pci, prev_cap_offset, prev_cap_header); - dw_pcie_dbi_ro_wr_dis(pci); - - return 0; + return PCI_FIND_NEXT_EXT_CAP(dw_pcie_ep_read_cfg, 0, + cap, NULL, ep, func_no); } -EXPORT_SYMBOL_GPL(dw_pcie_ep_hide_ext_capability); static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr) @@ -139,18 +107,23 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, return 0; } -static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, - dma_addr_t parent_bus_addr, enum pci_barno bar, - size_t size) +/* BAR Match Mode inbound iATU mapping */ +static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type, + dma_addr_t parent_bus_addr, enum pci_barno bar, + size_t size) { int ret; u32 free_win; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); - if (!ep->bar_to_atu[bar]) + if (!ep_func) + return -EINVAL; + + if (!ep_func->bar_to_atu[bar]) free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows); else - free_win = ep->bar_to_atu[bar] - 1; + free_win = ep_func->bar_to_atu[bar] - 1; if (free_win >= pci->num_ib_windows) { dev_err(pci->dev, "No free inbound window\n"); @@ -168,12 +141,190 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, * Always increment free_win before assignment, since value 0 is used to identify * unallocated mapping. */ - ep->bar_to_atu[bar] = free_win + 1; + ep_func->bar_to_atu[bar] = free_win + 1; set_bit(free_win, ep->ib_window_map); return 0; } +static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, u8 func_no, enum pci_barno bar) +{ + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct device *dev = pci->dev; + unsigned int i, num; + u32 atu_index; + u32 *indexes; + + if (!ep_func) + return; + + /* Tear down the BAR Match Mode mapping, if any. */ + if (ep_func->bar_to_atu[bar]) { + atu_index = ep_func->bar_to_atu[bar] - 1; + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); + clear_bit(atu_index, ep->ib_window_map); + ep_func->bar_to_atu[bar] = 0; + } + + /* Tear down all Address Match Mode mappings, if any. */ + indexes = ep_func->ib_atu_indexes[bar]; + num = ep_func->num_ib_atu_indexes[bar]; + ep_func->ib_atu_indexes[bar] = NULL; + ep_func->num_ib_atu_indexes[bar] = 0; + if (!indexes) + return; + for (i = 0; i < num; i++) { + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, indexes[i]); + clear_bit(indexes[i], ep->ib_window_map); + } + devm_kfree(dev, indexes); +} + +static u64 dw_pcie_ep_read_bar_assigned(struct dw_pcie_ep *ep, u8 func_no, + enum pci_barno bar, int flags) +{ + u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); + u32 lo, hi; + u64 addr; + + lo = dw_pcie_ep_readl_dbi(ep, func_no, reg); + + if (flags & PCI_BASE_ADDRESS_SPACE) + return lo & PCI_BASE_ADDRESS_IO_MASK; + + addr = lo & PCI_BASE_ADDRESS_MEM_MASK; + if (!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + return addr; + + hi = dw_pcie_ep_readl_dbi(ep, func_no, reg + 4); + return addr | ((u64)hi << 32); +} + +static int dw_pcie_ep_validate_submap(struct dw_pcie_ep *ep, + const struct pci_epf_bar_submap *submap, + unsigned int num_submap, size_t bar_size) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 align = pci->region_align; + size_t off = 0; + unsigned int i; + size_t size; + + if (!align || !IS_ALIGNED(bar_size, align)) + return -EINVAL; + + /* + * The submap array order defines the BAR layout (submap[0] starts + * at offset 0 and each entry immediately follows the previous + * one). Here, validate that it forms a strict, gapless + * decomposition of the BAR: + * - each entry has a non-zero size + * - sizes, implicit offsets and phys_addr are aligned to + * pci->region_align + * - each entry lies within the BAR range + * - the entries exactly cover the whole BAR + * + * Note: dw_pcie_prog_inbound_atu() also checks alignment for the + * PCI address and the target phys_addr, but validating up-front + * avoids partially programming iATU windows in vain. + */ + for (i = 0; i < num_submap; i++) { + size = submap[i].size; + + if (!size) + return -EINVAL; + + if (!IS_ALIGNED(size, align) || !IS_ALIGNED(off, align)) + return -EINVAL; + + if (!IS_ALIGNED(submap[i].phys_addr, align)) + return -EINVAL; + + if (off > bar_size || size > bar_size - off) + return -EINVAL; + + off += size; + } + if (off != bar_size) + return -EINVAL; + + return 0; +} + +/* Address Match Mode inbound iATU mapping */ +static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type, + const struct pci_epf_bar *epf_bar) +{ + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + const struct pci_epf_bar_submap *submap = epf_bar->submap; + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + struct device *dev = pci->dev; + u64 pci_addr, parent_bus_addr; + u64 size, base, off = 0; + int free_win, ret; + unsigned int i; + u32 *indexes; + + if (!ep_func || !epf_bar->num_submap || !submap || !epf_bar->size) + return -EINVAL; + + ret = dw_pcie_ep_validate_submap(ep, submap, epf_bar->num_submap, + epf_bar->size); + if (ret) + return ret; + + base = dw_pcie_ep_read_bar_assigned(ep, func_no, bar, epf_bar->flags); + if (!base) { + dev_err(dev, + "BAR%u not assigned, cannot set up sub-range mappings\n", + bar); + return -EINVAL; + } + + indexes = devm_kcalloc(dev, epf_bar->num_submap, sizeof(*indexes), + GFP_KERNEL); + if (!indexes) + return -ENOMEM; + + ep_func->ib_atu_indexes[bar] = indexes; + ep_func->num_ib_atu_indexes[bar] = 0; + + for (i = 0; i < epf_bar->num_submap; i++) { + size = submap[i].size; + parent_bus_addr = submap[i].phys_addr; + + if (off > (~0ULL) - base) { + ret = -EINVAL; + goto err; + } + + pci_addr = base + off; + off += size; + + free_win = find_first_zero_bit(ep->ib_window_map, + pci->num_ib_windows); + if (free_win >= pci->num_ib_windows) { + ret = -ENOSPC; + goto err; + } + + ret = dw_pcie_prog_inbound_atu(pci, free_win, type, + parent_bus_addr, pci_addr, size); + if (ret) + goto err; + + set_bit(free_win, ep->ib_window_map); + indexes[i] = free_win; + ep_func->num_ib_atu_indexes[bar] = i + 1; + } + return 0; +err: + dw_pcie_ep_clear_ib_maps(ep, func_no, bar); + return ret; +} + static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, struct dw_pcie_ob_atu_cfg *atu) { @@ -204,35 +355,34 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); enum pci_barno bar = epf_bar->barno; - u32 atu_index = ep->bar_to_atu[bar] - 1; + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); - if (!ep->bar_to_atu[bar]) + if (!ep_func || !ep_func->epf_bar[bar]) return; __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags); - dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); - clear_bit(atu_index, ep->ib_window_map); - ep->epf_bar[bar] = NULL; - ep->bar_to_atu[bar] = 0; + dw_pcie_ep_clear_ib_maps(ep, func_no, bar); + + ep_func->epf_bar[bar] = NULL; } -static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci, +static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie_ep *ep, u8 func_no, enum pci_barno bar) { u32 reg, bar_index; unsigned int offset, nbars; int i; - offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + offset = dw_pcie_ep_find_ext_capability(ep, func_no, PCI_EXT_CAP_ID_REBAR); if (!offset) return offset; - reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL); nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, reg); for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) { - reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL); bar_index = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, reg); if (bar_index == bar) return offset; @@ -253,7 +403,7 @@ static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no, u32 rebar_cap, rebar_ctrl; int ret; - rebar_offset = dw_pcie_ep_get_rebar_offset(pci, bar); + rebar_offset = dw_pcie_ep_get_rebar_offset(ep, func_no, bar); if (!rebar_offset) return -EINVAL; @@ -283,16 +433,16 @@ static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no, * 1 MB to 128 TB. Bits 31:16 in PCI_REBAR_CTRL define "supported sizes" * bits for sizes 256 TB to 8 EB. Disallow sizes 256 TB to 8 EB. */ - rebar_ctrl = dw_pcie_readl_dbi(pci, rebar_offset + PCI_REBAR_CTRL); + rebar_ctrl = dw_pcie_ep_readl_dbi(ep, func_no, rebar_offset + PCI_REBAR_CTRL); rebar_ctrl &= ~GENMASK(31, 16); - dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl); + dw_pcie_ep_writel_dbi(ep, func_no, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl); /* * The "selected size" (bits 13:8) in PCI_REBAR_CTRL are automatically * updated when writing PCI_REBAR_CAP, see "Figure 3-26 Resizable BAR * Example for 32-bit Memory BAR0" in DWC EP databook 5.96a. */ - dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CAP, rebar_cap); + dw_pcie_ep_writel_dbi(ep, func_no, rebar_offset + PCI_REBAR_CAP, rebar_cap); dw_pcie_dbi_ro_wr_dis(pci); @@ -341,12 +491,16 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); enum pci_barno bar = epf_bar->barno; size_t size = epf_bar->size; enum pci_epc_bar_type bar_type; int flags = epf_bar->flags; int ret, type; + if (!ep_func) + return -EINVAL; + /* * DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs * 1 and 2 to form a 64-bit BAR. @@ -360,21 +514,38 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, * calling clear_bar() would clear the BAR's PCI address assigned by the * host). */ - if (ep->epf_bar[bar]) { + if (ep_func->epf_bar[bar]) { /* * We can only dynamically change a BAR if the new BAR size and * BAR flags do not differ from the existing configuration. */ - if (ep->epf_bar[bar]->barno != bar || - ep->epf_bar[bar]->size != size || - ep->epf_bar[bar]->flags != flags) + if (ep_func->epf_bar[bar]->barno != bar || + ep_func->epf_bar[bar]->size != size || + ep_func->epf_bar[bar]->flags != flags) return -EINVAL; /* + * When dynamically changing a BAR, tear down any existing + * mappings before re-programming. + */ + if (ep_func->epf_bar[bar]->num_submap || epf_bar->num_submap) + dw_pcie_ep_clear_ib_maps(ep, func_no, bar); + + /* * When dynamically changing a BAR, skip writing the BAR reg, as * that would clear the BAR's PCI address assigned by the host. */ goto config_atu; + } else { + /* + * Subrange mapping is an update-only operation. The BAR + * must have been configured once without submaps so that + * subsequent set_bar() calls can update inbound mappings + * without touching the BAR register (and clobbering the + * host-assigned address). + */ + if (epf_bar->num_submap) + return -EINVAL; } bar_type = dw_pcie_ep_get_bar_type(ep, bar); @@ -408,12 +579,16 @@ config_atu: else type = PCIE_ATU_TYPE_IO; - ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar, - size); + if (epf_bar->num_submap) + ret = dw_pcie_ep_ib_atu_addr(ep, func_no, type, epf_bar); + else + ret = dw_pcie_ep_ib_atu_bar(ep, func_no, type, + epf_bar->phys_addr, bar, size); + if (ret) return ret; - ep->epf_bar[bar] = epf_bar; + ep_func->epf_bar[bar] = epf_bar; return 0; } @@ -601,6 +776,16 @@ 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); + /* + * Tear down the dedicated outbound window used for MSI + * generation. This avoids leaking an iATU window across + * endpoint stop/start cycles. + */ + if (ep->msi_iatu_mapped) { + dw_pcie_ep_unmap_addr(epc, 0, 0, ep->msi_mem_phys); + ep->msi_iatu_mapped = false; + } + dw_pcie_stop_link(pci); } @@ -702,14 +887,37 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, msg_addr = ((u64)msg_addr_upper) << 32 | msg_addr_lower; msg_addr = dw_pcie_ep_align_addr(epc, msg_addr, &map_size, &offset); - ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, - map_size); - if (ret) - return ret; - writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset); + /* + * Program the outbound iATU once and keep it enabled. + * + * The spec warns that updating iATU registers while there are + * operations in flight on the AXI bridge interface is not + * supported, so we avoid reprogramming the region on every MSI, + * specifically unmapping immediately after writel(). + */ + if (!ep->msi_iatu_mapped) { + ret = dw_pcie_ep_map_addr(epc, func_no, 0, + ep->msi_mem_phys, msg_addr, + map_size); + if (ret) + return ret; + + ep->msi_iatu_mapped = true; + ep->msi_msg_addr = msg_addr; + ep->msi_map_size = map_size; + } else if (WARN_ON_ONCE(ep->msi_msg_addr != msg_addr || + ep->msi_map_size != map_size)) { + /* + * The host changed the MSI target address or the required + * mapping size changed. Reprogramming the iATU at runtime is + * unsafe on this controller, so bail out instead of trying to + * update the existing region. + */ + return -EINVAL; + } - dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys); + writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset); return 0; } @@ -775,7 +983,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, bir = FIELD_GET(PCI_MSIX_TABLE_BIR, tbl_offset); tbl_offset &= PCI_MSIX_TABLE_OFFSET; - msix_tbl = ep->epf_bar[bir]->addr + tbl_offset; + msix_tbl = ep_func->epf_bar[bir]->addr + tbl_offset; msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr; msg_data = msix_tbl[(interrupt_num - 1)].msg_data; vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl; @@ -836,20 +1044,20 @@ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep) } EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit); -static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) +static void dw_pcie_ep_init_rebar_registers(struct dw_pcie_ep *ep, u8 func_no) { - struct dw_pcie_ep *ep = &pci->ep; - unsigned int offset; - unsigned int nbars; + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + unsigned int offset, nbars; enum pci_barno bar; u32 reg, i, val; - offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + if (!ep_func) + return; - dw_pcie_dbi_ro_wr_en(pci); + offset = dw_pcie_ep_find_ext_capability(ep, func_no, PCI_EXT_CAP_ID_REBAR); if (offset) { - reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL); nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, reg); /* @@ -870,16 +1078,28 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) * the controller when RESBAR_CAP_REG is written, which * is why RESBAR_CAP_REG is written here. */ - val = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + val = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL); bar = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, val); - if (ep->epf_bar[bar]) - pci_epc_bar_size_to_rebar_cap(ep->epf_bar[bar]->size, &val); + if (ep_func->epf_bar[bar]) + pci_epc_bar_size_to_rebar_cap(ep_func->epf_bar[bar]->size, &val); else val = BIT(4); - dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, val); + dw_pcie_ep_writel_dbi(ep, func_no, offset + PCI_REBAR_CAP, val); } } +} + +static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) +{ + struct dw_pcie_ep *ep = &pci->ep; + u8 funcs = ep->epc->max_functions; + u8 func_no; + + dw_pcie_dbi_ro_wr_en(pci); + + for (func_no = 0; func_no < funcs; func_no++) + dw_pcie_ep_init_rebar_registers(ep, func_no); dw_pcie_setup(pci); dw_pcie_dbi_ro_wr_dis(pci); @@ -967,6 +1187,18 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) if (ep->ops->init) ep->ops->init(ep); + /* + * PCIe r6.0, section 7.9.15 states that for endpoints that support + * PTM, this capability structure is required in exactly one + * function, which controls the PTM behavior of all PTM capable + * functions. This indicates the PTM capability structure + * represents controller-level registers rather than per-function + * registers. + * + * Therefore, PTM capability registers are configured using the + * standard DBI accessors, instead of func_no indexed per-function + * accessors. + */ ptm_cap_base = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM); /* @@ -1087,6 +1319,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) struct device *dev = pci->dev; INIT_LIST_HEAD(&ep->func_list); + ep->msi_iatu_mapped = false; + ep->msi_msg_addr = 0; + ep->msi_map_size = 0; epc = devm_pci_epc_create(dev, &epc_ops); if (IS_ERR(epc)) { diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index f116591975ff..6ae6189e9b8a 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -244,7 +244,7 @@ void dw_pcie_msi_init(struct dw_pcie_rp *pp) u64 msi_target = (u64)pp->msi_data; u32 ctrl, num_ctrls; - if (!pci_msi_enabled() || !pp->has_msi_ctrl) + if (!pci_msi_enabled() || !pp->use_imsi_rx) return; num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; @@ -356,10 +356,20 @@ int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) * order not to miss MSI TLPs from those devices the MSI target * address has to be within the lowest 4GB. * - * Note until there is a better alternative found the reservation is - * done by allocating from the artificially limited DMA-coherent - * memory. + * Per DWC databook r6.21a, section 3.10.2.3, the incoming MWr TLP + * targeting the MSI_CTRL_ADDR is terminated by the iMSI-RX and never + * appears on the AXI bus. So MSI_CTRL_ADDR address doesn't need to be + * mapped and can be any memory that doesn't get allocated for the BAR + * memory. Since most of the platforms provide 32-bit address for + * 'config' region, try cfg0_base as the first option for the MSI target + * address if it's a 32-bit address. Otherwise, try 32-bit and 64-bit + * coherent memory allocation one by one. */ + if (!(pp->cfg0_base & GENMASK_ULL(63, 32))) { + pp->msi_data = pp->cfg0_base; + return 0; + } + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); if (!ret) msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64), &pp->msi_data, @@ -582,15 +592,15 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) } if (pci_msi_enabled()) { - pp->has_msi_ctrl = !(pp->ops->msi_init || + pp->use_imsi_rx = !(pp->ops->msi_init || of_property_present(np, "msi-parent") || of_property_present(np, "msi-map")); /* - * For the has_msi_ctrl case the default assignment is handled + * For the use_imsi_rx case the default assignment is handled * in the dw_pcie_msi_host_init(). */ - if (!pp->has_msi_ctrl && !pp->num_vectors) { + if (!pp->use_imsi_rx && !pp->num_vectors) { pp->num_vectors = MSI_DEF_NUM_VECTORS; } else if (pp->num_vectors > MAX_MSI_IRQS) { dev_err(dev, "Invalid number of vectors\n"); @@ -602,7 +612,7 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) ret = pp->ops->msi_init(pp); if (ret < 0) goto err_deinit_host; - } else if (pp->has_msi_ctrl) { + } else if (pp->use_imsi_rx) { ret = dw_pcie_msi_host_init(pp); if (ret < 0) goto err_deinit_host; @@ -620,14 +630,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_free_msi; - if (pp->ecam_enabled) { - ret = dw_pcie_config_ecam_iatu(pp); - if (ret) { - dev_err(dev, "Failed to configure iATU in ECAM mode\n"); - goto err_free_msi; - } - } - /* * Allocate the resource for MSG TLP before programming the iATU * outbound window in dw_pcie_setup_rc(). Since the allocation depends @@ -655,13 +657,12 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) } /* - * Note: Skip the link up delay only when a Link Up IRQ is present. - * If there is no Link Up IRQ, we should not bypass the delay - * because that would require users to manually rescan for devices. + * Only fail on timeout error. Other errors indicate the device may + * become available later, so continue without failing. */ - if (!pp->use_linkup_irq) - /* Ignore errors, the link may come up later */ - dw_pcie_wait_for_link(pci); + ret = dw_pcie_wait_for_link(pci); + if (ret == -ETIMEDOUT) + goto err_stop_link; ret = pci_host_probe(bridge); if (ret) @@ -681,7 +682,7 @@ err_remove_edma: dw_pcie_edma_remove(pci); err_free_msi: - if (pp->has_msi_ctrl) + if (pp->use_imsi_rx) dw_pcie_free_msi(pp); err_deinit_host: @@ -709,7 +710,7 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp) dw_pcie_edma_remove(pci); - if (pp->has_msi_ctrl) + if (pp->use_imsi_rx) dw_pcie_free_msi(pp); if (pp->ops->deinit) @@ -846,19 +847,10 @@ static void __iomem *dw_pcie_ecam_conf_map_bus(struct pci_bus *bus, unsigned int return pci->dbi_base + where; } -static int dw_pcie_op_assert_perst(struct pci_bus *bus, bool assert) -{ - struct dw_pcie_rp *pp = bus->sysdata; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - return dw_pcie_assert_perst(pci, assert); -} - static struct pci_ops dw_pcie_ops = { .map_bus = dw_pcie_own_conf_map_bus, .read = pci_generic_config_read, .write = pci_generic_config_write, - .assert_perst = dw_pcie_op_assert_perst, }; static struct pci_ops dw_pcie_ecam_ops = { @@ -872,9 +864,10 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct dw_pcie_ob_atu_cfg atu = { 0 }; struct resource_entry *entry; + int ob_iatu_index; + int ib_iatu_index; int i, ret; - /* Note the very first outbound ATU is used for CFG IOs */ if (!pci->num_ob_windows) { dev_err(pci->dev, "No outbound iATU found\n"); return -EINVAL; @@ -890,37 +883,74 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) for (i = 0; i < pci->num_ib_windows; i++) dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i); - i = 0; + /* + * NOTE: For outbound address translation, outbound iATU at index 0 is + * reserved for CFG IOs (dw_pcie_other_conf_map_bus()), thus start at + * index 1. + * + * If using ECAM, outbound iATU at index 0 and index 1 is reserved for + * CFG IOs. + */ + if (pp->ecam_enabled) { + ob_iatu_index = 2; + ret = dw_pcie_config_ecam_iatu(pp); + if (ret) { + dev_err(pci->dev, "Failed to configure iATU in ECAM mode\n"); + return ret; + } + } else { + ob_iatu_index = 1; + } + resource_list_for_each_entry(entry, &pp->bridge->windows) { + resource_size_t res_size; + if (resource_type(entry->res) != IORESOURCE_MEM) continue; - if (pci->num_ob_windows <= ++i) - break; - - atu.index = i; atu.type = PCIE_ATU_TYPE_MEM; atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset; atu.pci_addr = entry->res->start - entry->offset; /* Adjust iATU size if MSG TLP region was allocated before */ if (pp->msg_res && pp->msg_res->parent == entry->res) - atu.size = resource_size(entry->res) - + res_size = resource_size(entry->res) - resource_size(pp->msg_res); else - atu.size = resource_size(entry->res); + res_size = resource_size(entry->res); + + while (res_size > 0) { + /* + * Return failure if we run out of windows in the + * middle. Otherwise, we would end up only partially + * mapping a single resource. + */ + if (ob_iatu_index >= pci->num_ob_windows) { + dev_err(pci->dev, "Cannot add outbound window for region: %pr\n", + entry->res); + return -ENOMEM; + } - ret = dw_pcie_prog_outbound_atu(pci, &atu); - if (ret) { - dev_err(pci->dev, "Failed to set MEM range %pr\n", - entry->res); - return ret; + atu.index = ob_iatu_index; + atu.size = MIN(pci->region_limit + 1, res_size); + + ret = dw_pcie_prog_outbound_atu(pci, &atu); + if (ret) { + dev_err(pci->dev, "Failed to set MEM range %pr\n", + entry->res); + return ret; + } + + ob_iatu_index++; + atu.parent_bus_addr += atu.size; + atu.pci_addr += atu.size; + res_size -= atu.size; } } if (pp->io_size) { - if (pci->num_ob_windows > ++i) { - atu.index = i; + if (ob_iatu_index < pci->num_ob_windows) { + atu.index = ob_iatu_index; atu.type = PCIE_ATU_TYPE_IO; atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; @@ -932,40 +962,71 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) entry->res); return ret; } + ob_iatu_index++; } else { + /* + * If there are not enough outbound windows to give I/O + * space its own iATU, the outbound iATU at index 0 will + * be shared between I/O space and CFG IOs, by + * temporarily reconfiguring the iATU to CFG space, in + * order to do a CFG IO, and then immediately restoring + * it to I/O space. This is only implemented when using + * dw_pcie_other_conf_map_bus(), which is not the case + * when using ECAM. + */ + if (pp->ecam_enabled) { + dev_err(pci->dev, "Cannot add outbound window for I/O\n"); + return -ENOMEM; + } pp->cfg0_io_shared = true; } } - if (pci->num_ob_windows <= i) - dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n", - pci->num_ob_windows); - - pp->msg_atu_index = i; + if (pp->use_atu_msg) { + if (ob_iatu_index >= pci->num_ob_windows) { + dev_err(pci->dev, "Cannot add outbound window for MSG TLP\n"); + return -ENOMEM; + } + pp->msg_atu_index = ob_iatu_index++; + } - i = 0; + ib_iatu_index = 0; resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) { + resource_size_t res_start, res_size, window_size; + if (resource_type(entry->res) != IORESOURCE_MEM) continue; - if (pci->num_ib_windows <= i) - break; + res_size = resource_size(entry->res); + res_start = entry->res->start; + while (res_size > 0) { + /* + * Return failure if we run out of windows in the + * middle. Otherwise, we would end up only partially + * mapping a single resource. + */ + if (ib_iatu_index >= pci->num_ib_windows) { + dev_err(pci->dev, "Cannot add inbound window for region: %pr\n", + entry->res); + return -ENOMEM; + } - ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, - entry->res->start, - entry->res->start - entry->offset, - resource_size(entry->res)); - if (ret) { - dev_err(pci->dev, "Failed to set DMA range %pr\n", - entry->res); - return ret; + window_size = MIN(pci->region_limit + 1, res_size); + ret = dw_pcie_prog_inbound_atu(pci, ib_iatu_index, + PCIE_ATU_TYPE_MEM, res_start, + res_start - entry->offset, window_size); + if (ret) { + dev_err(pci->dev, "Failed to set DMA range %pr\n", + entry->res); + return ret; + } + + ib_iatu_index++; + res_start += window_size; + res_size -= window_size; } } - if (pci->num_ib_windows <= i) - dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n", - pci->num_ib_windows); - return 0; } @@ -1087,7 +1148,7 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) * the platform uses its own address translation component rather than * ATU, so we should not program the ATU here. */ - if (pp->bridge->child_ops == &dw_child_pcie_ops) { + if (pp->bridge->child_ops == &dw_child_pcie_ops || pp->ecam_enabled) { ret = dw_pcie_iatu_setup(pp); if (ret) return ret; @@ -1104,6 +1165,17 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) dw_pcie_dbi_ro_wr_dis(pci); + /* + * The iMSI-RX module does not support receiving MSI or MSI-X generated + * by the Root Port. If iMSI-RX is used as the MSI controller, remove + * the MSI and MSI-X capabilities of the Root Port to allow the drivers + * to fall back to INTx instead. + */ + if (pp->use_imsi_rx) { + dw_pcie_remove_capability(pci, PCI_CAP_ID_MSI); + dw_pcie_remove_capability(pci, PCI_CAP_ID_MSIX); + } + return 0; } EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); @@ -1147,8 +1219,11 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) int dw_pcie_suspend_noirq(struct dw_pcie *pci) { u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + int ret = 0; u32 val; - int ret; + + if (!dw_pcie_link_up(pci)) + goto stop_link; /* * If L1SS is supported, then do not put the link into L2 as some @@ -1165,6 +1240,16 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) return ret; } + /* + * Some SoCs do not support reading the LTSSM register after + * PME_Turn_Off broadcast. For those SoCs, skip waiting for L2/L3 Ready + * state and wait 10ms as recommended in PCIe spec r6.0, sec 5.3.3.2.1. + */ + if (pci->pp.skip_l23_ready) { + mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000); + goto stop_link; + } + ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE || val <= DW_PCIE_LTSSM_DETECT_WAIT, @@ -1183,6 +1268,7 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) */ udelay(1); +stop_link: dw_pcie_stop_link(pci); if (pci->pp.ops->deinit) pci->pp.ops->deinit(&pci->pp); @@ -1220,6 +1306,9 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) if (ret) return ret; + if (pci->pp.ops->post_init) + pci->pp.ops->post_init(&pci->pp); + return ret; } EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 12f41886c65d..8530746ec5cb 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -61,6 +61,7 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features dw_plat_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .msix_capable = true, }; diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 75fc8b767fcc..5741c09dde7f 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -226,16 +226,70 @@ void dw_pcie_version_detect(struct dw_pcie *pci) u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap) { return PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap, - pci); + NULL, pci); } EXPORT_SYMBOL_GPL(dw_pcie_find_capability); u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap) { - return PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, pci); + return PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, NULL, pci); } EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability); +void dw_pcie_remove_capability(struct dw_pcie *pci, u8 cap) +{ + u8 cap_pos, pre_pos, next_pos; + u16 reg; + + cap_pos = PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap, + &pre_pos, pci); + if (!cap_pos) + return; + + reg = dw_pcie_readw_dbi(pci, cap_pos); + next_pos = (reg & 0xff00) >> 8; + + dw_pcie_dbi_ro_wr_en(pci); + if (pre_pos == PCI_CAPABILITY_LIST) + dw_pcie_writeb_dbi(pci, PCI_CAPABILITY_LIST, next_pos); + else + dw_pcie_writeb_dbi(pci, pre_pos + 1, next_pos); + dw_pcie_dbi_ro_wr_dis(pci); +} +EXPORT_SYMBOL_GPL(dw_pcie_remove_capability); + +void dw_pcie_remove_ext_capability(struct dw_pcie *pci, u8 cap) +{ + int cap_pos, next_pos, pre_pos; + u32 pre_header, header; + + cap_pos = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, &pre_pos, pci); + if (!cap_pos) + return; + + header = dw_pcie_readl_dbi(pci, cap_pos); + + /* + * If the first cap at offset PCI_CFG_SPACE_SIZE is removed, + * only set its capid to zero as it cannot be skipped. + */ + if (cap_pos == PCI_CFG_SPACE_SIZE) { + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi(pci, cap_pos, header & 0xffff0000); + dw_pcie_dbi_ro_wr_dis(pci); + return; + } + + pre_header = dw_pcie_readl_dbi(pci, pre_pos); + next_pos = PCI_EXT_CAP_NEXT(header); + + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi(pci, pre_pos, + (pre_header & 0xfffff) | (next_pos << 20)); + dw_pcie_dbi_ro_wr_dis(pci); +} +EXPORT_SYMBOL_GPL(dw_pcie_remove_ext_capability); + static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id, u16 vsec_id) { @@ -246,7 +300,7 @@ static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id, return 0; while ((vsec = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, vsec, - PCI_EXT_CAP_ID_VNDR, pci))) { + PCI_EXT_CAP_ID_VNDR, NULL, pci))) { header = dw_pcie_readl_dbi(pci, vsec + PCI_VNDR_HEADER); if (PCI_VNDR_HEADER_ID(header) == vsec_id) return vsec; @@ -478,6 +532,9 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u32 retries, val; u64 limit_addr; + if (atu->index >= pci->num_ob_windows) + return -ENOSPC; + limit_addr = parent_bus_addr + atu->size - 1; if ((limit_addr & ~pci->region_limit) != (parent_bus_addr & ~pci->region_limit) || @@ -551,6 +608,9 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, u64 limit_addr = pci_addr + size - 1; u32 retries, val; + if (index >= pci->num_ib_windows) + return -ENOSPC; + if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) || !IS_ALIGNED(parent_bus_addr, pci->region_align) || !IS_ALIGNED(pci_addr, pci->region_align) || !size) { @@ -639,9 +699,69 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index) dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0); } +const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm) +{ + const char *str; + + switch (ltssm) { +#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_1); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_2); + default: + str = "DW_PCIE_LTSSM_UNKNOWN"; + break; + } + + return str + strlen("DW_PCIE_LTSSM_"); +} + +/** + * dw_pcie_wait_for_link - Wait for the PCIe link to be up + * @pci: DWC instance + * + * Returns: 0 if link is up, -ENODEV if device is not found, -EIO if the device + * is found but not active and -ETIMEDOUT if the link fails to come up for other + * reasons. + */ int dw_pcie_wait_for_link(struct dw_pcie *pci) { - u32 offset, val; + u32 offset, val, ltssm; int retries; /* Check if the link is up or not */ @@ -653,7 +773,29 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) } if (retries >= PCIE_LINK_WAIT_MAX_RETRIES) { - dev_info(pci->dev, "Phy link never came up\n"); + /* + * If the link is in Detect.Quiet or Detect.Active state, it + * indicates that no device is detected. + */ + ltssm = dw_pcie_get_ltssm(pci); + if (ltssm == DW_PCIE_LTSSM_DETECT_QUIET || + ltssm == DW_PCIE_LTSSM_DETECT_ACT) { + dev_info(pci->dev, "Device not found\n"); + return -ENODEV; + + /* + * If the link is in POLL.{Active/Compliance} state, then the + * device is found to be connected to the bus, but it is not + * active i.e., the device firmware might not yet initialized. + */ + } else if (ltssm == DW_PCIE_LTSSM_POLL_ACTIVE || + ltssm == DW_PCIE_LTSSM_POLL_COMPLIANCE) { + dev_info(pci->dev, "Device found, but not active\n"); + return -EIO; + } + + dev_err(pci->dev, "Link failed to come up. LTSSM: %s\n", + dw_pcie_ltssm_status_string(ltssm)); return -ETIMEDOUT; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 403f6cfe8191..ae6389dd9caa 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -305,6 +305,10 @@ /* Default eDMA LLP memory size */ #define DMA_LLP_MEM_SIZE PAGE_SIZE +/* Common struct pci_epc_feature bits among DWC EP glue drivers */ +#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true, \ + .subrange_mapping = true + struct dw_pcie; struct dw_pcie_rp; struct dw_pcie_ep; @@ -388,6 +392,10 @@ enum dw_pcie_ltssm { DW_PCIE_LTSSM_RCVRY_EQ2 = 0x22, DW_PCIE_LTSSM_RCVRY_EQ3 = 0x23, + /* Vendor glue drivers provide pseudo L1 substates from get_ltssm() */ + DW_PCIE_LTSSM_L1_1 = 0x141, + DW_PCIE_LTSSM_L1_2 = 0x142, + DW_PCIE_LTSSM_UNKNOWN = 0xFFFFFFFF, }; @@ -412,7 +420,7 @@ struct dw_pcie_host_ops { }; struct dw_pcie_rp { - bool has_msi_ctrl:1; + bool use_imsi_rx:1; bool cfg0_io_shared:1; u64 cfg0_base; void __iomem *va_cfg0_base; @@ -434,11 +442,11 @@ struct dw_pcie_rp { bool use_atu_msg; int msg_atu_index; struct resource *msg_res; - bool use_linkup_irq; struct pci_eq_presets presets; struct pci_config_window *cfg; bool ecam_enabled; bool native_ecam; + bool skip_l23_ready; }; struct dw_pcie_ep_ops { @@ -463,6 +471,12 @@ struct dw_pcie_ep_func { u8 func_no; u8 msi_cap; /* MSI capability offset */ u8 msix_cap; /* MSI-X capability offset */ + u8 bar_to_atu[PCI_STD_NUM_BARS]; + struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS]; + + /* Only for Address Match Mode inbound iATU */ + u32 *ib_atu_indexes[PCI_STD_NUM_BARS]; + unsigned int num_ib_atu_indexes[PCI_STD_NUM_BARS]; }; struct dw_pcie_ep { @@ -472,13 +486,16 @@ struct dw_pcie_ep { phys_addr_t phys_base; size_t addr_size; size_t page_size; - u8 bar_to_atu[PCI_STD_NUM_BARS]; phys_addr_t *outbound_addr; unsigned long *ib_window_map; unsigned long *ob_window_map; void __iomem *msi_mem; phys_addr_t msi_mem_phys; - struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS]; + + /* MSI outbound iATU state */ + bool msi_iatu_mapped; + u64 msi_msg_addr; + size_t msi_map_size; }; struct dw_pcie_ops { @@ -493,7 +510,6 @@ struct dw_pcie_ops { enum dw_pcie_ltssm (*get_ltssm)(struct dw_pcie *pcie); int (*start_link)(struct dw_pcie *pcie); void (*stop_link)(struct dw_pcie *pcie); - int (*assert_perst)(struct dw_pcie *pcie, bool assert); }; struct debugfs_info { @@ -562,6 +578,8 @@ void dw_pcie_version_detect(struct dw_pcie *pci); u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap); u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap); +void dw_pcie_remove_capability(struct dw_pcie *pci, u8 cap); +void dw_pcie_remove_ext_capability(struct dw_pcie *pci, u8 cap); u16 dw_pcie_find_rasdes_capability(struct dw_pcie *pci); u16 dw_pcie_find_ptm_capability(struct dw_pcie *pci); @@ -798,14 +816,6 @@ static inline void dw_pcie_stop_link(struct dw_pcie *pci) pci->ops->stop_link(pci); } -static inline int dw_pcie_assert_perst(struct dw_pcie *pci, bool assert) -{ - if (pci->ops && pci->ops->assert_perst) - return pci->ops->assert_perst(pci, assert); - - return 0; -} - static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci) { u32 val; @@ -818,6 +828,8 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci) return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val); } +const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm); + #ifdef CONFIG_PCIE_DW_HOST int dw_pcie_suspend_noirq(struct dw_pcie *pci); int dw_pcie_resume_noirq(struct dw_pcie *pci); @@ -896,7 +908,6 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no, u16 interrupt_num); void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); -int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap); struct dw_pcie_ep_func * dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no); #else @@ -954,12 +965,6 @@ static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) { } -static inline int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, - u8 prev_cap, u8 cap) -{ - return 0; -} - static inline struct dw_pcie_ep_func * dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no) { diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index f8605fe61a41..5b17da63151d 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -68,6 +68,11 @@ #define PCIE_CLKREQ_NOT_READY FIELD_PREP_WM16(BIT(0), 0) #define PCIE_CLKREQ_PULL_DOWN FIELD_PREP_WM16(GENMASK(13, 12), 1) +/* RASDES TBA information */ +#define PCIE_CLIENT_CDM_RASDES_TBA_INFO_CMN 0x154 +#define PCIE_CLIENT_CDM_RASDES_TBA_L1_1 BIT(4) +#define PCIE_CLIENT_CDM_RASDES_TBA_L1_2 BIT(5) + /* Hot Reset Control Register */ #define PCIE_CLIENT_HOT_RESET_CTRL 0x180 #define PCIE_LTSSM_APP_DLY2_EN BIT(1) @@ -80,6 +85,8 @@ #define PCIE_LINKUP_MASK GENMASK(17, 16) #define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0) +#define PCIE_TYPE0_HDR_DBI2_OFFSET 0x100000 + struct rockchip_pcie { struct dw_pcie pci; void __iomem *apb_base; @@ -181,11 +188,26 @@ static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip) return 0; } -static u32 rockchip_pcie_get_ltssm(struct rockchip_pcie *rockchip) +static u32 rockchip_pcie_get_ltssm_reg(struct rockchip_pcie *rockchip) { return rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_LTSSM_STATUS); } +static enum dw_pcie_ltssm rockchip_pcie_get_ltssm(struct dw_pcie *pci) +{ + struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); + u32 val = rockchip_pcie_readl_apb(rockchip, + PCIE_CLIENT_CDM_RASDES_TBA_INFO_CMN); + + if (val & PCIE_CLIENT_CDM_RASDES_TBA_L1_1) + return DW_PCIE_LTSSM_L1_1; + + if (val & PCIE_CLIENT_CDM_RASDES_TBA_L1_2) + return DW_PCIE_LTSSM_L1_2; + + return rockchip_pcie_get_ltssm_reg(rockchip) & PCIE_LTSSM_STATUS_MASK; +} + static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip) { rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM, @@ -201,7 +223,7 @@ static void rockchip_pcie_disable_ltssm(struct rockchip_pcie *rockchip) static bool rockchip_pcie_link_up(struct dw_pcie *pci) { struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); - u32 val = rockchip_pcie_get_ltssm(rockchip); + u32 val = rockchip_pcie_get_ltssm_reg(rockchip); return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP; } @@ -292,6 +314,8 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) if (irq < 0) return irq; + pci->dbi_base2 = pci->dbi_base + PCIE_TYPE0_HDR_DBI2_OFFSET; + ret = rockchip_pcie_init_irq_domain(rockchip); if (ret < 0) dev_err(dev, "failed to init irq domain\n"); @@ -302,6 +326,10 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) rockchip_pcie_configure_l1ss(pci); rockchip_pcie_enable_l0s(pci); + /* Disable Root Ports BAR0 and BAR1 as they report bogus size */ + dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0); + dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_1, 0x0); + return 0; } @@ -327,9 +355,7 @@ static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep) if (!of_device_is_compatible(dev->of_node, "rockchip,rk3588-pcie-ep")) return; - if (dw_pcie_ep_hide_ext_capability(pci, PCI_EXT_CAP_ID_SECPCI, - PCI_EXT_CAP_ID_ATS)) - dev_err(dev, "failed to hide ATS capability\n"); + dw_pcie_remove_ext_capability(pci, PCI_EXT_CAP_ID_ATS); } static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep) @@ -364,6 +390,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .msix_capable = true, @@ -384,6 +411,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { * BARs) would be overwritten, resulting in (all other BARs) no longer working. */ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .msix_capable = true, @@ -485,36 +513,9 @@ static const struct dw_pcie_ops dw_pcie_ops = { .link_up = rockchip_pcie_link_up, .start_link = rockchip_pcie_start_link, .stop_link = rockchip_pcie_stop_link, + .get_ltssm = rockchip_pcie_get_ltssm, }; -static irqreturn_t rockchip_pcie_rc_sys_irq_thread(int irq, void *arg) -{ - struct rockchip_pcie *rockchip = arg; - struct dw_pcie *pci = &rockchip->pci; - struct dw_pcie_rp *pp = &pci->pp; - struct device *dev = pci->dev; - u32 reg; - - reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC); - rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC); - - dev_dbg(dev, "PCIE_CLIENT_INTR_STATUS_MISC: %#x\n", reg); - dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm(rockchip)); - - if (reg & PCIE_RDLH_LINK_UP_CHGED) { - if (rockchip_pcie_link_up(pci)) { - msleep(PCIE_RESET_CONFIG_WAIT_MS); - dev_dbg(dev, "Received Link up event. Starting enumeration!\n"); - /* Rescan the bus to enumerate endpoint devices */ - pci_lock_rescan_remove(); - pci_rescan_bus(pp->bridge->bus); - pci_unlock_rescan_remove(); - } - } - - return IRQ_HANDLED; -} - static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) { struct rockchip_pcie *rockchip = arg; @@ -526,7 +527,7 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC); dev_dbg(dev, "PCIE_CLIENT_INTR_STATUS_MISC: %#x\n", reg); - dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm(rockchip)); + dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm_reg(rockchip)); if (reg & PCIE_LINK_REQ_RST_NOT_INT) { dev_dbg(dev, "hot reset or link-down reset\n"); @@ -547,29 +548,14 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) return IRQ_HANDLED; } -static int rockchip_pcie_configure_rc(struct platform_device *pdev, - struct rockchip_pcie *rockchip) +static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip) { - struct device *dev = &pdev->dev; struct dw_pcie_rp *pp; - int irq, ret; u32 val; if (!IS_ENABLED(CONFIG_PCIE_ROCKCHIP_DW_HOST)) return -ENODEV; - irq = platform_get_irq_byname(pdev, "sys"); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(dev, irq, NULL, - rockchip_pcie_rc_sys_irq_thread, - IRQF_ONESHOT, "pcie-sys-rc", rockchip); - if (ret) { - dev_err(dev, "failed to request PCIe sys IRQ\n"); - return ret; - } - /* LTSSM enable control mode */ val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1); rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL); @@ -580,19 +566,8 @@ static int rockchip_pcie_configure_rc(struct platform_device *pdev, pp = &rockchip->pci.pp; pp->ops = &rockchip_pcie_host_ops; - pp->use_linkup_irq = true; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - /* unmask DLL up/down indicator */ - val = FIELD_PREP_WM16(PCIE_RDLH_LINK_UP_CHGED, 0); - rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC); - - return ret; + return dw_pcie_host_init(pp); } static int rockchip_pcie_configure_ep(struct platform_device *pdev, @@ -711,7 +686,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) switch (data->mode) { case DW_PCIE_RC_TYPE: - ret = rockchip_pcie_configure_rc(pdev, rockchip); + ret = rockchip_pcie_configure_rc(rockchip); if (ret) goto deinit_clk; break; diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 60e74ac782af..2666a9c3d67e 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -309,6 +309,7 @@ static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features keembay_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .msix_capable = true, .bar[BAR_0] = { .only_64bit = true, }, diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c index 47745749f75c..b3ec38099fa3 100644 --- a/drivers/pci/controller/dwc/pcie-nxp-s32g.c +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c @@ -282,12 +282,12 @@ static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp) ret = s32g_pcie_parse_port(s32g_pp, of_port); if (ret) - goto err_port; + break; } -err_port: - list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) - list_del(&port->list); + if (ret) + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) + list_del(&port->list); return ret; } diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index f1bc0ac81a92..18460f01b2c6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -168,11 +168,13 @@ enum qcom_pcie_ep_link_status { * @hdma_support: HDMA support on this SoC * @override_no_snoop: Override NO_SNOOP attribute in TLP to enable cache snooping * @disable_mhi_ram_parity_check: Disable MHI RAM data parity error check + * @firmware_managed: Set if the controller is firmware managed */ struct qcom_pcie_ep_cfg { bool hdma_support; bool override_no_snoop; bool disable_mhi_ram_parity_check; + bool firmware_managed; }; /** @@ -377,6 +379,14 @@ err_disable_clk: static void qcom_pcie_disable_resources(struct qcom_pcie_ep *pcie_ep) { + struct device *dev = pcie_ep->pci.dev; + + pm_runtime_put(dev); + + /* Skip resource disablement if controller is firmware-managed */ + if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed) + return; + icc_set_bw(pcie_ep->icc_mem, 0, 0); phy_power_off(pcie_ep->phy); phy_exit(pcie_ep->phy); @@ -390,12 +400,24 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) u32 val, offset; int ret; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "Failed to enable device: %d\n", ret); + return ret; + } + + /* Skip resource enablement if controller is firmware-managed */ + if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed) + goto skip_resources_enable; + ret = qcom_pcie_enable_resources(pcie_ep); if (ret) { dev_err(dev, "Failed to enable resources: %d\n", ret); + pm_runtime_put(dev); return ret; } +skip_resources_enable: /* Perform cleanup that requires refclk */ pci_epc_deinit_notify(pci->ep.epc); dw_pcie_ep_cleanup(&pci->ep); @@ -630,6 +652,17 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev, return ret; } + pcie_ep->reset = devm_gpiod_get(dev, "reset", GPIOD_IN); + if (IS_ERR(pcie_ep->reset)) + return PTR_ERR(pcie_ep->reset); + + pcie_ep->wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW); + if (IS_ERR(pcie_ep->wake)) + return PTR_ERR(pcie_ep->wake); + + if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed) + return 0; + pcie_ep->num_clks = devm_clk_bulk_get_all(dev, &pcie_ep->clks); if (pcie_ep->num_clks < 0) { dev_err(dev, "Failed to get clocks\n"); @@ -640,14 +673,6 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev, if (IS_ERR(pcie_ep->core_reset)) return PTR_ERR(pcie_ep->core_reset); - pcie_ep->reset = devm_gpiod_get(dev, "reset", GPIOD_IN); - if (IS_ERR(pcie_ep->reset)) - return PTR_ERR(pcie_ep->reset); - - pcie_ep->wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW); - if (IS_ERR(pcie_ep->wake)) - return PTR_ERR(pcie_ep->wake); - pcie_ep->phy = devm_phy_optional_get(dev, "pciephy"); if (IS_ERR(pcie_ep->phy)) ret = PTR_ERR(pcie_ep->phy); @@ -820,6 +845,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep) } static const struct pci_epc_features qcom_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .align = SZ_4K, @@ -874,6 +900,12 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie_ep); + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + ret = qcom_pcie_ep_get_resources(pdev, pcie_ep); if (ret) return ret; @@ -894,6 +926,12 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev) goto err_disable_irqs; } + ret = pm_runtime_put_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to suspend device: %d\n", ret); + goto err_disable_irqs; + } + pcie_ep->debugfs = debugfs_create_dir(name, NULL); qcom_pcie_ep_init_debugfs(pcie_ep); @@ -930,7 +968,15 @@ static const struct qcom_pcie_ep_cfg cfg_1_34_0 = { .disable_mhi_ram_parity_check = true, }; +static const struct qcom_pcie_ep_cfg cfg_1_34_0_fw_managed = { + .hdma_support = true, + .override_no_snoop = true, + .disable_mhi_ram_parity_check = true, + .firmware_managed = true, +}; + static const struct of_device_id qcom_pcie_ep_match[] = { + { .compatible = "qcom,sa8255p-pcie-ep", .data = &cfg_1_34_0_fw_managed}, { .compatible = "qcom,sa8775p-pcie-ep", .data = &cfg_1_34_0}, { .compatible = "qcom,sdx55-pcie-ep", }, { .compatible = "qcom,sm8450-pcie-ep", }, diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 5a318487b2b3..67a16af69ddc 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -24,6 +24,7 @@ #include <linux/of_pci.h> #include <linux/pci.h> #include <linux/pci-ecam.h> +#include <linux/pci-pwrctrl.h> #include <linux/pm_opp.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> @@ -55,9 +56,6 @@ #define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8 #define PARF_Q2A_FLUSH 0x1ac #define PARF_LTSSM 0x1b0 -#define PARF_INT_ALL_STATUS 0x224 -#define PARF_INT_ALL_CLEAR 0x228 -#define PARF_INT_ALL_MASK 0x22c #define PARF_SID_OFFSET 0x234 #define PARF_BDF_TRANSLATE_CFG 0x24c #define PARF_DBI_BASE_ADDR_V2 0x350 @@ -134,10 +132,6 @@ /* PARF_LTSSM register fields */ #define LTSSM_EN BIT(8) -/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */ -#define PARF_INT_ALL_LINK_UP BIT(13) -#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23) - /* PARF_NO_SNOOP_OVERRIDE register fields */ #define WR_NO_SNOOP_OVERRIDE_EN BIT(1) #define RD_NO_SNOOP_OVERRIDE_EN BIT(3) @@ -267,10 +261,15 @@ struct qcom_pcie_cfg { bool no_l0s; }; +struct qcom_pcie_perst { + struct list_head list; + struct gpio_desc *desc; +}; + struct qcom_pcie_port { struct list_head list; - struct gpio_desc *reset; struct phy *phy; + struct list_head perst; }; struct qcom_pcie { @@ -289,27 +288,30 @@ struct qcom_pcie { #define to_qcom_pcie(x) dev_get_drvdata((x)->dev) -static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert) +static void __qcom_pcie_perst_assert(struct qcom_pcie *pcie, bool assert) { + struct qcom_pcie_perst *perst; struct qcom_pcie_port *port; int val = assert ? 1 : 0; - list_for_each_entry(port, &pcie->ports, list) - gpiod_set_value_cansleep(port->reset, val); + list_for_each_entry(port, &pcie->ports, list) { + list_for_each_entry(perst, &port->perst, list) + gpiod_set_value_cansleep(perst->desc, val); + } usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); } -static void qcom_ep_reset_assert(struct qcom_pcie *pcie) +static void qcom_pcie_perst_assert(struct qcom_pcie *pcie) { - qcom_perst_assert(pcie, true); + __qcom_pcie_perst_assert(pcie, true); } -static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) +static void qcom_pcie_perst_deassert(struct qcom_pcie *pcie) { - /* Ensure that PERST has been asserted for at least 100 ms */ + /* Ensure that PERST# has been asserted for at least 100 ms */ msleep(PCIE_T_PVPERL_MS); - qcom_perst_assert(pcie, false); + __qcom_pcie_perst_assert(pcie, false); } static int qcom_pcie_start_link(struct dw_pcie *pci) @@ -641,18 +643,6 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie) return 0; } -static int qcom_pcie_assert_perst(struct dw_pcie *pci, bool assert) -{ - struct qcom_pcie *pcie = to_qcom_pcie(pci); - - if (assert) - qcom_ep_reset_assert(pcie); - else - qcom_ep_reset_deassert(pcie); - - return 0; -} - static void qcom_pcie_2_3_2_ltssm_enable(struct qcom_pcie *pcie) { u32 val; @@ -1299,7 +1289,7 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) struct qcom_pcie *pcie = to_qcom_pcie(pci); int ret; - qcom_ep_reset_assert(pcie); + qcom_pcie_perst_assert(pcie); ret = pcie->cfg->ops->init(pcie); if (ret) @@ -1309,15 +1299,25 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_deinit; + ret = pci_pwrctrl_create_devices(pci->dev); + if (ret) + goto err_disable_phy; + + ret = pci_pwrctrl_power_on_devices(pci->dev); + if (ret) + goto err_pwrctrl_destroy; + if (pcie->cfg->ops->post_init) { ret = pcie->cfg->ops->post_init(pcie); if (ret) - goto err_disable_phy; + goto err_pwrctrl_power_off; } qcom_pcie_clear_aspm_l0s(pcie->pci); + dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX); + dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC); - qcom_ep_reset_deassert(pcie); + qcom_pcie_perst_deassert(pcie); if (pcie->cfg->ops->config_sid) { ret = pcie->cfg->ops->config_sid(pcie); @@ -1328,7 +1328,12 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) return 0; err_assert_reset: - qcom_ep_reset_assert(pcie); + qcom_pcie_perst_assert(pcie); +err_pwrctrl_power_off: + pci_pwrctrl_power_off_devices(pci->dev); +err_pwrctrl_destroy: + if (ret != -EPROBE_DEFER) + pci_pwrctrl_destroy_devices(pci->dev); err_disable_phy: qcom_pcie_phy_power_off(pcie); err_deinit: @@ -1342,7 +1347,13 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct qcom_pcie *pcie = to_qcom_pcie(pci); - qcom_ep_reset_assert(pcie); + qcom_pcie_perst_assert(pcie); + + /* + * No need to destroy pwrctrl devices as this function only gets called + * during system suspend as of now. + */ + pci_pwrctrl_power_off_devices(pci->dev); qcom_pcie_phy_power_off(pcie); pcie->cfg->ops->deinit(pcie); } @@ -1496,7 +1507,6 @@ static const struct qcom_pcie_cfg cfg_fw_managed = { static const struct dw_pcie_ops dw_pcie_ops = { .link_up = qcom_pcie_link_up, .start_link = qcom_pcie_start_link, - .assert_perst = qcom_pcie_assert_perst, }; static int qcom_pcie_icc_init(struct qcom_pcie *pcie) @@ -1635,37 +1645,11 @@ static void qcom_pcie_init_debugfs(struct qcom_pcie *pcie) qcom_pcie_link_transition_count); } -static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data) -{ - struct qcom_pcie *pcie = data; - struct dw_pcie_rp *pp = &pcie->pci->pp; - struct device *dev = pcie->pci->dev; - u32 status = readl_relaxed(pcie->parf + PARF_INT_ALL_STATUS); - - writel_relaxed(status, pcie->parf + PARF_INT_ALL_CLEAR); - - if (FIELD_GET(PARF_INT_ALL_LINK_UP, status)) { - msleep(PCIE_RESET_CONFIG_WAIT_MS); - dev_dbg(dev, "Received Link up event. Starting enumeration!\n"); - /* Rescan the bus to enumerate endpoint devices */ - pci_lock_rescan_remove(); - pci_rescan_bus(pp->bridge->bus); - pci_unlock_rescan_remove(); - - qcom_pcie_icc_opp_update(pcie); - } else { - dev_WARN_ONCE(dev, 1, "Received unknown event. INT_STATUS: 0x%08x\n", - status); - } - - return IRQ_HANDLED; -} - static void qcom_pci_free_msi(void *ptr) { struct dw_pcie_rp *pp = (struct dw_pcie_rp *)ptr; - if (pp && pp->has_msi_ctrl) + if (pp && pp->use_imsi_rx) dw_pcie_free_msi(pp); } @@ -1689,7 +1673,7 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg) if (ret) return ret; - pp->has_msi_ctrl = true; + pp->use_imsi_rx = true; dw_pcie_msi_init(pp); return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp); @@ -1704,18 +1688,58 @@ static const struct pci_ecam_ops pci_qcom_ecam_ops = { } }; -static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node) +/* Parse PERST# from all nodes in depth first manner starting from @np */ +static int qcom_pcie_parse_perst(struct qcom_pcie *pcie, + struct qcom_pcie_port *port, + struct device_node *np) { struct device *dev = pcie->pci->dev; - struct qcom_pcie_port *port; + struct qcom_pcie_perst *perst; struct gpio_desc *reset; - struct phy *phy; int ret; - reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node), - "reset", GPIOD_OUT_HIGH, "PERST#"); - if (IS_ERR(reset)) + if (!of_find_property(np, "reset-gpios", NULL)) + goto parse_child_node; + + reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset", + GPIOD_OUT_HIGH, "PERST#"); + if (IS_ERR(reset)) { + /* + * FIXME: GPIOLIB currently supports exclusive GPIO access only. + * Non exclusive access is broken. But shared PERST# requires + * non-exclusive access. So once GPIOLIB properly supports it, + * implement it here. + */ + if (PTR_ERR(reset) == -EBUSY) + dev_err(dev, "Shared PERST# is not supported\n"); + return PTR_ERR(reset); + } + + perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); + if (!perst) + return -ENOMEM; + + INIT_LIST_HEAD(&perst->list); + perst->desc = reset; + list_add_tail(&perst->list, &port->perst); + +parse_child_node: + for_each_available_child_of_node_scoped(np, child) { + ret = qcom_pcie_parse_perst(pcie, port, child); + if (ret) + return ret; + } + + return 0; +} + +static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node) +{ + struct device *dev = pcie->pci->dev; + struct qcom_pcie_port *port; + struct phy *phy; + int ret; phy = devm_of_phy_get(dev, node, NULL); if (IS_ERR(phy)) @@ -1729,7 +1753,12 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node if (ret) return ret; - port->reset = reset; + INIT_LIST_HEAD(&port->perst); + + ret = qcom_pcie_parse_perst(pcie, port, node); + if (ret) + return ret; + port->phy = phy; INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); @@ -1739,9 +1768,10 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) { + struct qcom_pcie_perst *perst, *tmp_perst; + struct qcom_pcie_port *port, *tmp_port; struct device *dev = pcie->pci->dev; - struct qcom_pcie_port *port, *tmp; - int ret = -ENOENT; + int ret = -ENODEV; for_each_available_child_of_node_scoped(dev->of_node, of_port) { if (!of_node_is_type(of_port, "pci")) @@ -1754,7 +1784,9 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) return ret; err_port_del: - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) { + list_for_each_entry_safe(perst, tmp_perst, &port->perst, list) + list_del(&perst->list); phy_exit(port->phy); list_del(&port->list); } @@ -1765,6 +1797,7 @@ err_port_del: static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) { struct device *dev = pcie->pci->dev; + struct qcom_pcie_perst *perst; struct qcom_pcie_port *port; struct gpio_desc *reset; struct phy *phy; @@ -1786,27 +1819,35 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) if (!port) return -ENOMEM; - port->reset = reset; + perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); + if (!perst) + return -ENOMEM; + port->phy = phy; INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); + perst->desc = reset; + INIT_LIST_HEAD(&port->perst); + INIT_LIST_HEAD(&perst->list); + list_add_tail(&perst->list, &port->perst); + return 0; } static int qcom_pcie_probe(struct platform_device *pdev) { + struct qcom_pcie_perst *perst, *tmp_perst; + struct qcom_pcie_port *port, *tmp_port; const struct qcom_pcie_cfg *pcie_cfg; unsigned long max_freq = ULONG_MAX; - struct qcom_pcie_port *port, *tmp; struct device *dev = &pdev->dev; struct dev_pm_opp *opp; struct qcom_pcie *pcie; struct dw_pcie_rp *pp; struct resource *res; struct dw_pcie *pci; - int ret, irq; - char *name; + int ret; pcie_cfg = of_device_get_match_data(dev); if (!pcie_cfg) { @@ -1939,7 +1980,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) ret = qcom_pcie_parse_ports(pcie); if (ret) { - if (ret != -ENOENT) { + if (ret != -ENODEV) { dev_err_probe(pci->dev, ret, "Failed to parse Root Port: %d\n", ret); goto err_pm_runtime_put; @@ -1957,37 +1998,12 @@ static int qcom_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie); - irq = platform_get_irq_byname_optional(pdev, "global"); - if (irq > 0) - pp->use_linkup_irq = true; - ret = dw_pcie_host_init(pp); if (ret) { - dev_err(dev, "cannot initialize host\n"); + dev_err_probe(dev, ret, "cannot initialize host\n"); goto err_phy_exit; } - name = devm_kasprintf(dev, GFP_KERNEL, "qcom_pcie_global_irq%d", - pci_domain_nr(pp->bridge->bus)); - if (!name) { - ret = -ENOMEM; - goto err_host_deinit; - } - - if (irq > 0) { - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - qcom_pcie_global_irq_thread, - IRQF_ONESHOT, name, pcie); - if (ret) { - dev_err_probe(&pdev->dev, ret, - "Failed to request Global IRQ\n"); - goto err_host_deinit; - } - - writel_relaxed(PARF_INT_ALL_LINK_UP | PARF_INT_MSI_DEV_0_7, - pcie->parf + PARF_INT_ALL_MASK); - } - qcom_pcie_icc_opp_update(pcie); if (pcie->mhi) @@ -1995,10 +2011,10 @@ static int qcom_pcie_probe(struct platform_device *pdev) return 0; -err_host_deinit: - dw_pcie_host_deinit(pp); err_phy_exit: - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) { + list_for_each_entry_safe(perst, tmp_perst, &port->perst, list) + list_del(&perst->list); phy_exit(port->phy); list_del(&port->list); } diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 80778917d2dd..a6912e85e4dd 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -420,6 +420,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features rcar_gen4_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, diff --git a/drivers/pci/controller/dwc/pcie-sophgo.c b/drivers/pci/controller/dwc/pcie-sophgo.c index ad4baaa34ffa..044088898819 100644 --- a/drivers/pci/controller/dwc/pcie-sophgo.c +++ b/drivers/pci/controller/dwc/pcie-sophgo.c @@ -161,6 +161,22 @@ static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp) raw_spin_unlock_irqrestore(&pp->lock, flags); } +static void sophgo_pcie_disable_l0s_l1(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + u32 offset, val; + + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + + dw_pcie_dbi_ro_wr_en(pci); + + val = dw_pcie_readl_dbi(pci, PCI_EXP_LNKCAP + offset); + val &= ~(PCI_EXP_LNKCAP_ASPM_L0S | PCI_EXP_LNKCAP_ASPM_L1); + dw_pcie_writel_dbi(pci, PCI_EXP_LNKCAP + offset, val); + + dw_pcie_dbi_ro_wr_dis(pci); +} + static int sophgo_pcie_host_init(struct dw_pcie_rp *pp) { int irq; @@ -171,6 +187,8 @@ static int sophgo_pcie_host_init(struct dw_pcie_rp *pp) irq_set_chained_handler_and_data(irq, sophgo_pcie_intx_handler, pp); + sophgo_pcie_disable_l0s_l1(pp); + sophgo_pcie_msi_enable(pp); return 0; diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c index 2cecf32d2b0f..c1944b40ce02 100644 --- a/drivers/pci/controller/dwc/pcie-stm32-ep.c +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c @@ -70,6 +70,7 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features stm32_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .align = SZ_64K, }; diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 0ddeef70726d..06571d806ab3 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1988,6 +1988,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features tegra_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index d6e73811216e..d52753060970 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -420,6 +420,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = { .init = uniphier_pcie_pro5_init_ep, .wait = NULL, .features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = false, .msi_capable = true, .msix_capable = false, @@ -438,6 +439,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = { .init = uniphier_pcie_nx1_init_ep, .wait = uniphier_pcie_nx1_wait_ep, .features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = false, .msi_capable = true, .msix_capable = false, diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index c473e7c03bac..d6258c1cffe5 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -32,7 +32,7 @@ struct pci_config_window *pci_host_common_ecam_create(struct device *dev, err = of_address_to_resource(dev->of_node, 0, &cfgres); if (err) { - dev_err(dev, "missing \"reg\" property\n"); + dev_err(dev, "missing or malformed \"reg\" property\n"); return ERR_PTR(err); } diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 942ddfca3bf6..512309763d1f 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2545,12 +2545,6 @@ static const struct seq_operations tegra_pcie_ports_sops = { DEFINE_SEQ_ATTRIBUTE(tegra_pcie_ports); -static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) -{ - debugfs_remove_recursive(pcie->debugfs); - pcie->debugfs = NULL; -} - static void tegra_pcie_debugfs_init(struct tegra_pcie *pcie) { pcie->debugfs = debugfs_create_dir("pcie", NULL); @@ -2624,29 +2618,6 @@ put_resources: return err; } -static void tegra_pcie_remove(struct platform_device *pdev) -{ - struct tegra_pcie *pcie = platform_get_drvdata(pdev); - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct tegra_pcie_port *port, *tmp; - - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_pcie_debugfs_exit(pcie); - - pci_stop_root_bus(host->bus); - pci_remove_root_bus(host->bus); - pm_runtime_put_sync(pcie->dev); - pm_runtime_disable(pcie->dev); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - tegra_pcie_msi_teardown(pcie); - - tegra_pcie_put_resources(pcie); - - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - tegra_pcie_port_free(port); -} - static int tegra_pcie_pm_suspend(struct device *dev) { struct tegra_pcie *pcie = dev_get_drvdata(dev); @@ -2750,6 +2721,8 @@ static struct platform_driver tegra_pcie_driver = { .pm = &tegra_pcie_pm_ops, }, .probe = tegra_pcie_probe, - .remove = tegra_pcie_remove, }; -module_platform_driver(tegra_pcie_driver); +builtin_platform_driver(tegra_pcie_driver); +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA PCI host controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c new file mode 100644 index 000000000000..3e1a39d1e648 --- /dev/null +++ b/drivers/pci/controller/pcie-aspeed.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 Aspeed Technology Inc. + */ +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqchip/irq-msi-lib.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/phy/pcie.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include "../pci.h" + +#define MAX_MSI_HOST_IRQS 64 +#define ASPEED_RESET_RC_WAIT_MS 10 + +/* AST2600 AHBC Registers */ +#define ASPEED_AHBC_KEY 0x00 +#define ASPEED_AHBC_UNLOCK_KEY 0xaeed1a03 +#define ASPEED_AHBC_UNLOCK 0x01 +#define ASPEED_AHBC_ADDR_MAPPING 0x8c +#define ASPEED_PCIE_RC_MEMORY_EN BIT(5) + +/* AST2600 H2X Controller Registers */ +#define ASPEED_H2X_INT_STS 0x08 +#define ASPEED_PCIE_TX_IDLE_CLEAR BIT(0) +#define ASPEED_PCIE_INTX_STS GENMASK(3, 0) +#define ASPEED_H2X_HOST_RX_DESC_DATA 0x0c +#define ASPEED_H2X_TX_DESC0 0x10 +#define ASPEED_H2X_TX_DESC1 0x14 +#define ASPEED_H2X_TX_DESC2 0x18 +#define ASPEED_H2X_TX_DESC3 0x1c +#define ASPEED_H2X_TX_DESC_DATA 0x20 +#define ASPEED_H2X_STS 0x24 +#define ASPEED_PCIE_TX_IDLE BIT(31) +#define ASPEED_PCIE_STATUS_OF_TX GENMASK(25, 24) +#define ASPEED_PCIE_RC_H_TX_COMPLETE BIT(25) +#define ASPEED_PCIE_TRIGGER_TX BIT(0) +#define ASPEED_H2X_AHB_ADDR_CONFIG0 0x60 +#define ASPEED_AHB_REMAP_LO_ADDR(x) (x & GENMASK(15, 4)) +#define ASPEED_AHB_MASK_LO_ADDR(x) FIELD_PREP(GENMASK(31, 20), x) +#define ASPEED_H2X_AHB_ADDR_CONFIG1 0x64 +#define ASPEED_AHB_REMAP_HI_ADDR(x) (x) +#define ASPEED_H2X_AHB_ADDR_CONFIG2 0x68 +#define ASPEED_AHB_MASK_HI_ADDR(x) (x) +#define ASPEED_H2X_DEV_CTRL 0xc0 +#define ASPEED_PCIE_RX_DMA_EN BIT(9) +#define ASPEED_PCIE_RX_LINEAR BIT(8) +#define ASPEED_PCIE_RX_MSI_SEL BIT(7) +#define ASPEED_PCIE_RX_MSI_EN BIT(6) +#define ASPEED_PCIE_UNLOCK_RX_BUFF BIT(4) +#define ASPEED_PCIE_WAIT_RX_TLP_CLR BIT(2) +#define ASPEED_PCIE_RC_RX_ENABLE BIT(1) +#define ASPEED_PCIE_RC_ENABLE BIT(0) +#define ASPEED_H2X_DEV_STS 0xc8 +#define ASPEED_PCIE_RC_RX_DONE_ISR BIT(4) +#define ASPEED_H2X_DEV_RX_DESC_DATA 0xcc +#define ASPEED_H2X_DEV_RX_DESC1 0xd4 +#define ASPEED_H2X_DEV_TX_TAG 0xfc +#define ASPEED_RC_TLP_TX_TAG_NUM 0x28 + +/* AST2700 H2X */ +#define ASPEED_H2X_CTRL 0x00 +#define ASPEED_H2X_BRIDGE_EN BIT(0) +#define ASPEED_H2X_BRIDGE_DIRECT_EN BIT(1) +#define ASPEED_H2X_CFGE_INT_STS 0x08 +#define ASPEED_CFGE_TX_IDLE BIT(0) +#define ASPEED_CFGE_RX_BUSY BIT(1) +#define ASPEED_H2X_CFGI_TLP 0x20 +#define ASPEED_CFGI_BYTE_EN_MASK GENMASK(19, 16) +#define ASPEED_CFGI_BYTE_EN(x) \ + FIELD_PREP(ASPEED_CFGI_BYTE_EN_MASK, (x)) +#define ASPEED_H2X_CFGI_WR_DATA 0x24 +#define ASPEED_CFGI_WRITE BIT(20) +#define ASPEED_H2X_CFGI_CTRL 0x28 +#define ASPEED_CFGI_TLP_FIRE BIT(0) +#define ASPEED_H2X_CFGI_RET_DATA 0x2c +#define ASPEED_H2X_CFGE_TLP_1ST 0x30 +#define ASPEED_H2X_CFGE_TLP_NEXT 0x34 +#define ASPEED_H2X_CFGE_CTRL 0x38 +#define ASPEED_CFGE_TLP_FIRE BIT(0) +#define ASPEED_H2X_CFGE_RET_DATA 0x3c +#define ASPEED_H2X_REMAP_PREF_ADDR 0x70 +#define ASPEED_REMAP_PREF_ADDR_63_32(x) (x) +#define ASPEED_H2X_REMAP_PCI_ADDR_HI 0x74 +#define ASPEED_REMAP_PCI_ADDR_63_32(x) (((x) >> 32) & GENMASK(31, 0)) +#define ASPEED_H2X_REMAP_PCI_ADDR_LO 0x78 +#define ASPEED_REMAP_PCI_ADDR_31_12(x) ((x) & GENMASK(31, 12)) + +/* AST2700 SCU */ +#define ASPEED_SCU_60 0x60 +#define ASPEED_RC_E2M_PATH_EN BIT(0) +#define ASPEED_RC_H2XS_PATH_EN BIT(16) +#define ASPEED_RC_H2XD_PATH_EN BIT(17) +#define ASPEED_RC_H2XX_PATH_EN BIT(18) +#define ASPEED_RC_UPSTREAM_MEM_EN BIT(19) +#define ASPEED_SCU_64 0x64 +#define ASPEED_RC0_DECODE_DMA_BASE(x) FIELD_PREP(GENMASK(7, 0), x) +#define ASPEED_RC0_DECODE_DMA_LIMIT(x) FIELD_PREP(GENMASK(15, 8), x) +#define ASPEED_RC1_DECODE_DMA_BASE(x) FIELD_PREP(GENMASK(23, 16), x) +#define ASPEED_RC1_DECODE_DMA_LIMIT(x) FIELD_PREP(GENMASK(31, 24), x) +#define ASPEED_SCU_70 0x70 +#define ASPEED_DISABLE_EP_FUNC 0 + +/* Macro to combine Fmt and Type into the 8-bit field */ +#define ASPEED_TLP_FMT_TYPE(fmt, type) ((((fmt) & 0x7) << 5) | ((type) & 0x1f)) +#define ASPEED_TLP_COMMON_FIELDS GENMASK(31, 24) + +/* Completion status */ +#define CPL_STS(x) FIELD_GET(GENMASK(15, 13), (x)) +/* TLP configuration type 0 and type 1 */ +#define CFG0_READ_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \ + PCIE_TLP_TYPE_CFG0_RD)) +#define CFG0_WRITE_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ + PCIE_TLP_TYPE_CFG0_WR)) +#define CFG1_READ_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \ + PCIE_TLP_TYPE_CFG1_RD)) +#define CFG1_WRITE_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ + PCIE_TLP_TYPE_CFG1_WR)) +#define CFG_PAYLOAD_SIZE 0x01 /* 1 DWORD */ +#define TLP_HEADER_BYTE_EN(x, y) ((GENMASK((x) - 1, 0) << ((y) % 4))) +#define TLP_GET_VALUE(x, y, z) \ + (((x) >> ((((z) % 4)) * 8)) & GENMASK((8 * (y)) - 1, 0)) +#define TLP_SET_VALUE(x, y, z) \ + ((((x) & GENMASK((8 * (y)) - 1, 0)) << ((((z) % 4)) * 8))) +#define AST2600_TX_DESC1_VALUE 0x00002000 +#define AST2700_TX_DESC1_VALUE 0x00401000 + +/** + * struct aspeed_pcie_port - PCIe port information + * @list: port list + * @pcie: pointer to PCIe host info + * @clk: pointer to the port clock gate + * @phy: pointer to PCIe PHY + * @perst: pointer to port reset control + * @slot: port slot + */ +struct aspeed_pcie_port { + struct list_head list; + struct aspeed_pcie *pcie; + struct clk *clk; + struct phy *phy; + struct reset_control *perst; + u32 slot; +}; + +/** + * struct aspeed_pcie - PCIe RC information + * @host: pointer to PCIe host bridge + * @dev: pointer to device structure + * @reg: PCIe host register base address + * @ahbc: pointer to AHHC register map + * @cfg: pointer to Aspeed PCIe configuration register map + * @platform: platform specific information + * @ports: list of PCIe ports + * @tx_tag: current TX tag for the port + * @root_bus_nr: bus number of the host bridge + * @h2xrst: pointer to H2X reset control + * @intx_domain: IRQ domain for INTx interrupts + * @msi_domain: IRQ domain for MSI interrupts + * @lock: mutex to protect MSI bitmap variable + * @msi_irq_in_use: bitmap to track used MSI host IRQs + * @clear_msi_twice: AST2700 workaround to clear MSI status twice + */ +struct aspeed_pcie { + struct pci_host_bridge *host; + struct device *dev; + void __iomem *reg; + struct regmap *ahbc; + struct regmap *cfg; + const struct aspeed_pcie_rc_platform *platform; + struct list_head ports; + + u8 tx_tag; + u8 root_bus_nr; + + struct reset_control *h2xrst; + + struct irq_domain *intx_domain; + struct irq_domain *msi_domain; + struct mutex lock; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_HOST_IRQS); + + bool clear_msi_twice; /* AST2700 workaround */ +}; + +/** + * struct aspeed_pcie_rc_platform - Platform information + * @setup: initialization function + * @pcie_map_ranges: function to map PCIe address ranges + * @reg_intx_en: INTx enable register offset + * @reg_intx_sts: INTx status register offset + * @reg_msi_en: MSI enable register offset + * @reg_msi_sts: MSI enable register offset + * @msi_address: HW fixed MSI address + */ +struct aspeed_pcie_rc_platform { + int (*setup)(struct platform_device *pdev); + void (*pcie_map_ranges)(struct aspeed_pcie *pcie, u64 pci_addr); + int reg_intx_en; + int reg_intx_sts; + int reg_msi_en; + int reg_msi_sts; + u32 msi_address; +}; + +static void aspeed_pcie_intx_irq_ack(struct irq_data *d) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d); + int intx_en = pcie->platform->reg_intx_en; + u32 en; + + en = readl(pcie->reg + intx_en); + en |= BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static void aspeed_pcie_intx_irq_mask(struct irq_data *d) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d); + int intx_en = pcie->platform->reg_intx_en; + u32 en; + + en = readl(pcie->reg + intx_en); + en &= ~BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static void aspeed_pcie_intx_irq_unmask(struct irq_data *d) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d); + int intx_en = pcie->platform->reg_intx_en; + u32 en; + + en = readl(pcie->reg + intx_en); + en |= BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static struct irq_chip aspeed_intx_irq_chip = { + .name = "INTx", + .irq_ack = aspeed_pcie_intx_irq_ack, + .irq_mask = aspeed_pcie_intx_irq_mask, + .irq_unmask = aspeed_pcie_intx_irq_unmask, +}; + +static int aspeed_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &aspeed_intx_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + irq_set_status_flags(irq, IRQ_LEVEL); + + return 0; +} + +static const struct irq_domain_ops aspeed_intx_domain_ops = { + .map = aspeed_pcie_intx_map, +}; + +static irqreturn_t aspeed_pcie_intr_handler(int irq, void *dev_id) +{ + struct aspeed_pcie *pcie = dev_id; + const struct aspeed_pcie_rc_platform *platform = pcie->platform; + unsigned long status; + unsigned long intx; + u32 bit; + int i; + + intx = FIELD_GET(ASPEED_PCIE_INTX_STS, + readl(pcie->reg + platform->reg_intx_sts)); + for_each_set_bit(bit, &intx, PCI_NUM_INTX) + generic_handle_domain_irq(pcie->intx_domain, bit); + + for (i = 0; i < 2; i++) { + int msi_sts_reg = platform->reg_msi_sts + (i * 4); + + status = readl(pcie->reg + msi_sts_reg); + writel(status, pcie->reg + msi_sts_reg); + + /* + * AST2700 workaround: + * The MSI status needs to clear one more time. + */ + if (pcie->clear_msi_twice) + writel(status, pcie->reg + msi_sts_reg); + + for_each_set_bit(bit, &status, 32) { + bit += (i * 32); + generic_handle_domain_irq(pcie->msi_domain, bit); + } + } + + return IRQ_HANDLED; +} + +static u32 aspeed_pcie_get_bdf_offset(struct pci_bus *bus, unsigned int devfn, + int where) +{ + return ((bus->number) << 24) | (PCI_SLOT(devfn) << 19) | + (PCI_FUNC(devfn) << 16) | (where & ~3); +} + +static int aspeed_ast2600_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val, u32 fmt_type, + bool write) +{ + struct aspeed_pcie *pcie = bus->sysdata; + u32 bdf_offset, cfg_val, isr; + int ret; + + bdf_offset = aspeed_pcie_get_bdf_offset(bus, devfn, where); + + /* Driver may set unlock RX buffer before triggering next TX config */ + cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_CTRL); + writel(ASPEED_PCIE_UNLOCK_RX_BUFF | cfg_val, + pcie->reg + ASPEED_H2X_DEV_CTRL); + + cfg_val = fmt_type | CFG_PAYLOAD_SIZE; + writel(cfg_val, pcie->reg + ASPEED_H2X_TX_DESC0); + + cfg_val = AST2600_TX_DESC1_VALUE | + FIELD_PREP(GENMASK(11, 8), pcie->tx_tag) | + TLP_HEADER_BYTE_EN(size, where); + writel(cfg_val, pcie->reg + ASPEED_H2X_TX_DESC1); + + writel(bdf_offset, pcie->reg + ASPEED_H2X_TX_DESC2); + writel(0, pcie->reg + ASPEED_H2X_TX_DESC3); + if (write) + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_TX_DESC_DATA); + + cfg_val = readl(pcie->reg + ASPEED_H2X_STS); + cfg_val |= ASPEED_PCIE_TRIGGER_TX; + writel(cfg_val, pcie->reg + ASPEED_H2X_STS); + + ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_STS, cfg_val, + (cfg_val & ASPEED_PCIE_TX_IDLE), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR tx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), cfg_val); + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + + cfg_val = readl(pcie->reg + ASPEED_H2X_INT_STS); + cfg_val |= ASPEED_PCIE_TX_IDLE_CLEAR; + writel(cfg_val, pcie->reg + ASPEED_H2X_INT_STS); + + cfg_val = readl(pcie->reg + ASPEED_H2X_STS); + switch (cfg_val & ASPEED_PCIE_STATUS_OF_TX) { + case ASPEED_PCIE_RC_H_TX_COMPLETE: + ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_DEV_STS, isr, + (isr & ASPEED_PCIE_RC_RX_DONE_ISR), 0, + 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR rx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), isr); + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + if (!write) { + cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_RX_DESC1); + if (CPL_STS(cfg_val) != PCIE_CPL_STS_SUCCESS) { + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } else { + *val = readl(pcie->reg + + ASPEED_H2X_DEV_RX_DESC_DATA); + } + } + break; + case ASPEED_PCIE_STATUS_OF_TX: + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + default: + *val = readl(pcie->reg + ASPEED_H2X_HOST_RX_DESC_DATA); + break; + } + + cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_CTRL); + cfg_val |= ASPEED_PCIE_UNLOCK_RX_BUFF; + writel(cfg_val, pcie->reg + ASPEED_H2X_DEV_CTRL); + + *val = TLP_GET_VALUE(*val, size, where); + + ret = PCIBIOS_SUCCESSFUL; +out: + cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_STS); + writel(cfg_val, pcie->reg + ASPEED_H2X_DEV_STS); + pcie->tx_tag = (pcie->tx_tag + 1) % 0x8; + return ret; +} + +static int aspeed_ast2600_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + /* + * AST2600 has only one Root Port on the root bus. + */ + if (PCI_SLOT(devfn) != 8) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2600_conf(bus, devfn, where, size, val, + CFG0_READ_FMTTYPE, false); +} + +static int aspeed_ast2600_child_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + return aspeed_ast2600_conf(bus, devfn, where, size, val, + CFG1_READ_FMTTYPE, false); +} + +static int aspeed_ast2600_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + /* + * AST2600 has only one Root Port on the root bus. + */ + if (PCI_SLOT(devfn) != 8) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2600_conf(bus, devfn, where, size, &val, + CFG0_WRITE_FMTTYPE, true); +} + +static int aspeed_ast2600_child_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + return aspeed_ast2600_conf(bus, devfn, where, size, &val, + CFG1_WRITE_FMTTYPE, true); +} + +static int aspeed_ast2700_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val, bool write) +{ + struct aspeed_pcie *pcie = bus->sysdata; + u32 cfg_val; + + cfg_val = ASPEED_CFGI_BYTE_EN(TLP_HEADER_BYTE_EN(size, where)) | + (where & ~3); + if (write) + cfg_val |= ASPEED_CFGI_WRITE; + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGI_TLP); + + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_CFGI_WR_DATA); + writel(ASPEED_CFGI_TLP_FIRE, pcie->reg + ASPEED_H2X_CFGI_CTRL); + *val = readl(pcie->reg + ASPEED_H2X_CFGI_RET_DATA); + *val = TLP_GET_VALUE(*val, size, where); + + return PCIBIOS_SUCCESSFUL; +} + +static int aspeed_ast2700_child_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val, + bool write) +{ + struct aspeed_pcie *pcie = bus->sysdata; + u32 bdf_offset, status, cfg_val; + int ret; + + bdf_offset = aspeed_pcie_get_bdf_offset(bus, devfn, where); + + cfg_val = CFG_PAYLOAD_SIZE; + if (write) + cfg_val |= (bus->number == (pcie->root_bus_nr + 1)) ? + CFG0_WRITE_FMTTYPE : + CFG1_WRITE_FMTTYPE; + else + cfg_val |= (bus->number == (pcie->root_bus_nr + 1)) ? + CFG0_READ_FMTTYPE : + CFG1_READ_FMTTYPE; + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGE_TLP_1ST); + + cfg_val = AST2700_TX_DESC1_VALUE | + FIELD_PREP(GENMASK(11, 8), pcie->tx_tag) | + TLP_HEADER_BYTE_EN(size, where); + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + + writel(bdf_offset, pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + if (write) + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + writel(ASPEED_CFGE_TX_IDLE | ASPEED_CFGE_RX_BUSY, + pcie->reg + ASPEED_H2X_CFGE_INT_STS); + writel(ASPEED_CFGE_TLP_FIRE, pcie->reg + ASPEED_H2X_CFGE_CTRL); + + ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_CFGE_INT_STS, status, + (status & ASPEED_CFGE_TX_IDLE), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR tx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), status); + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + + ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_CFGE_INT_STS, status, + (status & ASPEED_CFGE_RX_BUSY), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR rx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), status); + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + *val = readl(pcie->reg + ASPEED_H2X_CFGE_RET_DATA); + *val = TLP_GET_VALUE(*val, size, where); + + ret = PCIBIOS_SUCCESSFUL; +out: + writel(status, pcie->reg + ASPEED_H2X_CFGE_INT_STS); + pcie->tx_tag = (pcie->tx_tag + 1) % 0xf; + return ret; +} + +static int aspeed_ast2700_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + /* + * AST2700 has only one Root Port on the root bus. + */ + if (devfn != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2700_config(bus, devfn, where, size, val, false); +} + +static int aspeed_ast2700_child_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + return aspeed_ast2700_child_config(bus, devfn, where, size, val, false); +} + +static int aspeed_ast2700_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + /* + * AST2700 has only one Root Port on the root bus. + */ + if (devfn != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2700_config(bus, devfn, where, size, &val, true); +} + +static int aspeed_ast2700_child_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + return aspeed_ast2700_child_config(bus, devfn, where, size, &val, true); +} + +static struct pci_ops aspeed_ast2600_pcie_ops = { + .read = aspeed_ast2600_rd_conf, + .write = aspeed_ast2600_wr_conf, +}; + +static struct pci_ops aspeed_ast2600_pcie_child_ops = { + .read = aspeed_ast2600_child_rd_conf, + .write = aspeed_ast2600_child_wr_conf, +}; + +static struct pci_ops aspeed_ast2700_pcie_ops = { + .read = aspeed_ast2700_rd_conf, + .write = aspeed_ast2700_wr_conf, +}; + +static struct pci_ops aspeed_ast2700_pcie_child_ops = { + .read = aspeed_ast2700_child_rd_conf, + .write = aspeed_ast2700_child_wr_conf, +}; + +static void aspeed_irq_compose_msi_msg(struct irq_data *data, + struct msi_msg *msg) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(data); + + msg->address_hi = 0; + msg->address_lo = pcie->platform->msi_address; + msg->data = data->hwirq; +} + +static struct irq_chip aspeed_msi_bottom_irq_chip = { + .name = "ASPEED MSI", + .irq_compose_msi_msg = aspeed_irq_compose_msi_msg, +}; + +static int aspeed_irq_msi_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) +{ + struct aspeed_pcie *pcie = domain->host_data; + int bit; + int i; + + guard(mutex)(&pcie->lock); + + bit = bitmap_find_free_region(pcie->msi_irq_in_use, MAX_MSI_HOST_IRQS, + get_count_order(nr_irqs)); + + if (bit < 0) + return -ENOSPC; + + for (i = 0; i < nr_irqs; i++) { + irq_domain_set_info(domain, virq + i, bit + i, + &aspeed_msi_bottom_irq_chip, + domain->host_data, handle_simple_irq, NULL, + NULL); + } + + return 0; +} + +static void aspeed_irq_msi_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(data); + + guard(mutex)(&pcie->lock); + + bitmap_release_region(pcie->msi_irq_in_use, data->hwirq, + get_count_order(nr_irqs)); +} + +static const struct irq_domain_ops aspeed_msi_domain_ops = { + .alloc = aspeed_irq_msi_domain_alloc, + .free = aspeed_irq_msi_domain_free, +}; + +#define ASPEED_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_NO_AFFINITY) + +#define ASPEED_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_MULTI_PCI_MSI | \ + MSI_FLAG_PCI_MSIX) + +static const struct msi_parent_ops aspeed_msi_parent_ops = { + .required_flags = ASPEED_MSI_FLAGS_REQUIRED, + .supported_flags = ASPEED_MSI_FLAGS_SUPPORTED, + .bus_select_token = DOMAIN_BUS_PCI_MSI, + .chip_flags = MSI_CHIP_FLAG_SET_ACK, + .prefix = "ASPEED-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static int aspeed_pcie_msi_init(struct aspeed_pcie *pcie) +{ + writel(~0, pcie->reg + pcie->platform->reg_msi_en); + writel(~0, pcie->reg + pcie->platform->reg_msi_en + 0x04); + writel(~0, pcie->reg + pcie->platform->reg_msi_sts); + writel(~0, pcie->reg + pcie->platform->reg_msi_sts + 0x04); + + struct irq_domain_info info = { + .fwnode = dev_fwnode(pcie->dev), + .ops = &aspeed_msi_domain_ops, + .host_data = pcie, + .size = MAX_MSI_HOST_IRQS, + }; + + pcie->msi_domain = msi_create_parent_irq_domain(&info, + &aspeed_msi_parent_ops); + if (!pcie->msi_domain) + return dev_err_probe(pcie->dev, -ENOMEM, + "failed to create MSI domain\n"); + + return 0; +} + +static void aspeed_pcie_msi_free(struct aspeed_pcie *pcie) +{ + if (pcie->msi_domain) { + irq_domain_remove(pcie->msi_domain); + pcie->msi_domain = NULL; + } +} + +static void aspeed_pcie_irq_domain_free(void *d) +{ + struct aspeed_pcie *pcie = d; + + if (pcie->intx_domain) { + irq_domain_remove(pcie->intx_domain); + pcie->intx_domain = NULL; + } + aspeed_pcie_msi_free(pcie); +} + +static int aspeed_pcie_init_irq_domain(struct aspeed_pcie *pcie) +{ + int ret; + + pcie->intx_domain = irq_domain_add_linear(pcie->dev->of_node, + PCI_NUM_INTX, + &aspeed_intx_domain_ops, + pcie); + if (!pcie->intx_domain) { + ret = dev_err_probe(pcie->dev, -ENOMEM, + "failed to get INTx IRQ domain\n"); + goto err; + } + + writel(0, pcie->reg + pcie->platform->reg_intx_en); + writel(~0, pcie->reg + pcie->platform->reg_intx_sts); + + ret = aspeed_pcie_msi_init(pcie); + if (ret) + goto err; + + return 0; +err: + aspeed_pcie_irq_domain_free(pcie); + return ret; +} + +static int aspeed_pcie_port_init(struct aspeed_pcie_port *port) +{ + struct aspeed_pcie *pcie = port->pcie; + struct device *dev = pcie->dev; + int ret; + + ret = clk_prepare_enable(port->clk); + if (ret) + return dev_err_probe(dev, ret, + "failed to set clock for slot (%d)\n", + port->slot); + + ret = phy_init(port->phy); + if (ret) + return dev_err_probe(dev, ret, + "failed to init phy pcie for slot (%d)\n", + port->slot); + + ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); + if (ret) + return dev_err_probe(dev, ret, + "failed to set phy mode for slot (%d)\n", + port->slot); + + reset_control_deassert(port->perst); + msleep(PCIE_RESET_CONFIG_WAIT_MS); + + return 0; +} + +static void aspeed_host_reset(struct aspeed_pcie *pcie) +{ + reset_control_assert(pcie->h2xrst); + mdelay(ASPEED_RESET_RC_WAIT_MS); + reset_control_deassert(pcie->h2xrst); +} + +static void aspeed_pcie_map_ranges(struct aspeed_pcie *pcie) +{ + struct pci_host_bridge *bridge = pcie->host; + struct resource_entry *window; + + resource_list_for_each_entry(window, &bridge->windows) { + u64 pci_addr; + + if (resource_type(window->res) != IORESOURCE_MEM) + continue; + + pci_addr = window->res->start - window->offset; + pcie->platform->pcie_map_ranges(pcie, pci_addr); + break; + } +} + +static void aspeed_ast2600_pcie_map_ranges(struct aspeed_pcie *pcie, + u64 pci_addr) +{ + u32 pci_addr_lo = pci_addr & GENMASK(31, 0); + u32 pci_addr_hi = (pci_addr >> 32) & GENMASK(31, 0); + + pci_addr_lo >>= 16; + writel(ASPEED_AHB_REMAP_LO_ADDR(pci_addr_lo) | + ASPEED_AHB_MASK_LO_ADDR(0xe00), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG0); + writel(ASPEED_AHB_REMAP_HI_ADDR(pci_addr_hi), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG1); + writel(ASPEED_AHB_MASK_HI_ADDR(~0), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG2); +} + +static int aspeed_ast2600_setup(struct platform_device *pdev) +{ + struct aspeed_pcie *pcie = platform_get_drvdata(pdev); + struct device *dev = pcie->dev; + + pcie->ahbc = syscon_regmap_lookup_by_phandle(dev->of_node, + "aspeed,ahbc"); + if (IS_ERR(pcie->ahbc)) + return dev_err_probe(dev, PTR_ERR(pcie->ahbc), + "failed to map ahbc base\n"); + + aspeed_host_reset(pcie); + + regmap_write(pcie->ahbc, ASPEED_AHBC_KEY, ASPEED_AHBC_UNLOCK_KEY); + regmap_update_bits(pcie->ahbc, ASPEED_AHBC_ADDR_MAPPING, + ASPEED_PCIE_RC_MEMORY_EN, ASPEED_PCIE_RC_MEMORY_EN); + regmap_write(pcie->ahbc, ASPEED_AHBC_KEY, ASPEED_AHBC_UNLOCK); + + writel(ASPEED_H2X_BRIDGE_EN, pcie->reg + ASPEED_H2X_CTRL); + + writel(ASPEED_PCIE_RX_DMA_EN | ASPEED_PCIE_RX_LINEAR | + ASPEED_PCIE_RX_MSI_SEL | ASPEED_PCIE_RX_MSI_EN | + ASPEED_PCIE_WAIT_RX_TLP_CLR | ASPEED_PCIE_RC_RX_ENABLE | + ASPEED_PCIE_RC_ENABLE, + pcie->reg + ASPEED_H2X_DEV_CTRL); + + writel(ASPEED_RC_TLP_TX_TAG_NUM, pcie->reg + ASPEED_H2X_DEV_TX_TAG); + + pcie->host->ops = &aspeed_ast2600_pcie_ops; + pcie->host->child_ops = &aspeed_ast2600_pcie_child_ops; + + return 0; +} + +static void aspeed_ast2700_pcie_map_ranges(struct aspeed_pcie *pcie, + u64 pci_addr) +{ + writel(ASPEED_REMAP_PCI_ADDR_31_12(pci_addr), + pcie->reg + ASPEED_H2X_REMAP_PCI_ADDR_LO); + writel(ASPEED_REMAP_PCI_ADDR_63_32(pci_addr), + pcie->reg + ASPEED_H2X_REMAP_PCI_ADDR_HI); +} + +static int aspeed_ast2700_setup(struct platform_device *pdev) +{ + struct aspeed_pcie *pcie = platform_get_drvdata(pdev); + struct device *dev = pcie->dev; + + pcie->cfg = syscon_regmap_lookup_by_phandle(dev->of_node, + "aspeed,pciecfg"); + if (IS_ERR(pcie->cfg)) + return dev_err_probe(dev, PTR_ERR(pcie->cfg), + "failed to map pciecfg base\n"); + + regmap_update_bits(pcie->cfg, ASPEED_SCU_60, + ASPEED_RC_E2M_PATH_EN | ASPEED_RC_H2XS_PATH_EN | + ASPEED_RC_H2XD_PATH_EN | ASPEED_RC_H2XX_PATH_EN | + ASPEED_RC_UPSTREAM_MEM_EN, + ASPEED_RC_E2M_PATH_EN | ASPEED_RC_H2XS_PATH_EN | + ASPEED_RC_H2XD_PATH_EN | ASPEED_RC_H2XX_PATH_EN | + ASPEED_RC_UPSTREAM_MEM_EN); + regmap_write(pcie->cfg, ASPEED_SCU_64, + ASPEED_RC0_DECODE_DMA_BASE(0) | + ASPEED_RC0_DECODE_DMA_LIMIT(0xff) | + ASPEED_RC1_DECODE_DMA_BASE(0) | + ASPEED_RC1_DECODE_DMA_LIMIT(0xff)); + regmap_write(pcie->cfg, ASPEED_SCU_70, ASPEED_DISABLE_EP_FUNC); + + aspeed_host_reset(pcie); + + writel(0, pcie->reg + ASPEED_H2X_CTRL); + writel(ASPEED_H2X_BRIDGE_EN | ASPEED_H2X_BRIDGE_DIRECT_EN, + pcie->reg + ASPEED_H2X_CTRL); + + /* Prepare for 64-bit BAR pref */ + writel(ASPEED_REMAP_PREF_ADDR_63_32(0x3), + pcie->reg + ASPEED_H2X_REMAP_PREF_ADDR); + + pcie->host->ops = &aspeed_ast2700_pcie_ops; + pcie->host->child_ops = &aspeed_ast2700_pcie_child_ops; + pcie->clear_msi_twice = true; + + return 0; +} + +static void aspeed_pcie_reset_release(void *d) +{ + struct reset_control *perst = d; + + if (!perst) + return; + + reset_control_put(perst); +} + +static int aspeed_pcie_parse_port(struct aspeed_pcie *pcie, + struct device_node *node, + int slot) +{ + struct aspeed_pcie_port *port; + struct device *dev = pcie->dev; + int ret; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->clk = devm_get_clk_from_child(dev, node, NULL); + if (IS_ERR(port->clk)) + return dev_err_probe(dev, PTR_ERR(port->clk), + "failed to get pcie%d clock\n", slot); + + port->phy = devm_of_phy_get(dev, node, NULL); + if (IS_ERR(port->phy)) + return dev_err_probe(dev, PTR_ERR(port->phy), + "failed to get phy pcie%d\n", slot); + + port->perst = of_reset_control_get_exclusive(node, "perst"); + if (IS_ERR(port->perst)) + return dev_err_probe(dev, PTR_ERR(port->perst), + "failed to get pcie%d reset control\n", + slot); + ret = devm_add_action_or_reset(dev, aspeed_pcie_reset_release, + port->perst); + if (ret) + return ret; + reset_control_assert(port->perst); + + port->slot = slot; + port->pcie = pcie; + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &pcie->ports); + + ret = aspeed_pcie_port_init(port); + if (ret) + return ret; + + return 0; +} + +static int aspeed_pcie_parse_dt(struct aspeed_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node; + int ret; + + for_each_available_child_of_node_scoped(node, child) { + int slot; + const char *type; + + ret = of_property_read_string(child, "device_type", &type); + if (ret || strcmp(type, "pci")) + continue; + + ret = of_pci_get_devfn(child); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to parse devfn\n"); + + slot = PCI_SLOT(ret); + + ret = aspeed_pcie_parse_port(pcie, child, slot); + if (ret) + return ret; + } + + if (list_empty(&pcie->ports)) + return dev_err_probe(dev, -ENODEV, + "No PCIe port found in DT\n"); + + return 0; +} + +static int aspeed_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pci_host_bridge *host; + struct aspeed_pcie *pcie; + struct resource_entry *entry; + const struct aspeed_pcie_rc_platform *md; + int irq, ret; + + md = of_device_get_match_data(dev); + if (!md) + return -ENODEV; + + host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!host) + return -ENOMEM; + + pcie = pci_host_bridge_priv(host); + pcie->dev = dev; + pcie->tx_tag = 0; + platform_set_drvdata(pdev, pcie); + + pcie->platform = md; + pcie->host = host; + INIT_LIST_HEAD(&pcie->ports); + + /* Get root bus num for cfg command to decide tlp type 0 or type 1 */ + entry = resource_list_first_type(&host->windows, IORESOURCE_BUS); + if (entry) + pcie->root_bus_nr = entry->res->start; + + pcie->reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pcie->reg)) + return PTR_ERR(pcie->reg); + + pcie->h2xrst = devm_reset_control_get_exclusive(dev, "h2x"); + if (IS_ERR(pcie->h2xrst)) + return dev_err_probe(dev, PTR_ERR(pcie->h2xrst), + "failed to get h2x reset\n"); + + ret = devm_mutex_init(dev, &pcie->lock); + if (ret) + return dev_err_probe(dev, ret, "failed to init mutex\n"); + + ret = pcie->platform->setup(pdev); + if (ret) + return dev_err_probe(dev, ret, "failed to setup PCIe RC\n"); + + aspeed_pcie_map_ranges(pcie); + + ret = aspeed_pcie_parse_dt(pcie); + if (ret) + return ret; + + host->sysdata = pcie; + + ret = aspeed_pcie_init_irq_domain(pcie); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_add_action_or_reset(dev, aspeed_pcie_irq_domain_free, pcie); + if (ret) + return ret; + + ret = devm_request_irq(dev, irq, aspeed_pcie_intr_handler, IRQF_SHARED, + dev_name(dev), pcie); + if (ret) + return ret; + + return pci_host_probe(host); +} + +static const struct aspeed_pcie_rc_platform pcie_rc_ast2600 = { + .setup = aspeed_ast2600_setup, + .pcie_map_ranges = aspeed_ast2600_pcie_map_ranges, + .reg_intx_en = 0xc4, + .reg_intx_sts = 0xc8, + .reg_msi_en = 0xe0, + .reg_msi_sts = 0xe8, + .msi_address = 0x1e77005c, +}; + +static const struct aspeed_pcie_rc_platform pcie_rc_ast2700 = { + .setup = aspeed_ast2700_setup, + .pcie_map_ranges = aspeed_ast2700_pcie_map_ranges, + .reg_intx_en = 0x40, + .reg_intx_sts = 0x48, + .reg_msi_en = 0x50, + .reg_msi_sts = 0x58, + .msi_address = 0x000000f0, +}; + +static const struct of_device_id aspeed_pcie_of_match[] = { + { .compatible = "aspeed,ast2600-pcie", .data = &pcie_rc_ast2600 }, + { .compatible = "aspeed,ast2700-pcie", .data = &pcie_rc_ast2700 }, + {} +}; + +static struct platform_driver aspeed_pcie_driver = { + .driver = { + .name = "aspeed-pcie", + .of_match_table = aspeed_pcie_of_match, + .suppress_bind_attrs = true, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = aspeed_pcie_probe, +}; + +builtin_platform_driver(aspeed_pcie_driver); + +MODULE_AUTHOR("Jacky Chou <jacky_chou@aspeedtech.com>"); +MODULE_DESCRIPTION("ASPEED PCIe Root Complex"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 4b78b6528f9f..5defa5cc4c2b 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -585,8 +585,10 @@ static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port, if (IS_ENABLED(CONFIG_PCI_MSI)) { ret = mtk_pcie_allocate_msi_domains(port); - if (ret) + if (ret) { + irq_domain_remove(port->irq_domain); return ret; + } } return 0; diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 83ec66a70823..2809112e6317 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -73,6 +73,7 @@ #define RZG3S_PCI_PINTRCVIE_INTX(i) BIT(i) #define RZG3S_PCI_PINTRCVIE_MSI BIT(4) +/* Register is R/W1C, it doesn't require locking. */ #define RZG3S_PCI_PINTRCVIS 0x114 #define RZG3S_PCI_PINTRCVIS_INTX(i) BIT(i) #define RZG3S_PCI_PINTRCVIS_MSI BIT(4) @@ -114,6 +115,8 @@ #define RZG3S_PCI_MSIRE_ENA BIT(0) #define RZG3S_PCI_MSIRM(id) (0x608 + (id) * 0x10) + +/* Register is R/W1C, it doesn't require locking. */ #define RZG3S_PCI_MSIRS(id) (0x60c + (id) * 0x10) #define RZG3S_PCI_AWBASEL(id) (0x1000 + (id) * 0x20) @@ -439,28 +442,9 @@ static void __iomem *rzg3s_pcie_root_map_bus(struct pci_bus *bus, return host->pcie + where; } -/* Serialized by 'pci_lock' */ -static int rzg3s_pcie_root_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct rzg3s_pcie_host *host = bus->sysdata; - int ret; - - /* Enable access control to the CFGU */ - writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN, - host->axi + RZG3S_PCI_PERM); - - ret = pci_generic_config_write(bus, devfn, where, size, val); - - /* Disable access control to the CFGU */ - writel_relaxed(0, host->axi + RZG3S_PCI_PERM); - - return ret; -} - static struct pci_ops rzg3s_pcie_root_ops = { .read = pci_generic_config_read, - .write = rzg3s_pcie_root_write, + .write = pci_generic_config_write, .map_bus = rzg3s_pcie_root_map_bus, }; @@ -526,8 +510,6 @@ static void rzg3s_pcie_msi_irq_ack(struct irq_data *d) u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG; u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG; - guard(raw_spinlock_irqsave)(&host->hw_lock); - writel_relaxed(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id)); } @@ -859,8 +841,6 @@ static void rzg3s_pcie_intx_irq_ack(struct irq_data *d) { struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d); - guard(raw_spinlock_irqsave)(&host->hw_lock); - rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS, RZG3S_PCI_PINTRCVIS_INTX(d->hwirq), RZG3S_PCI_PINTRCVIS_INTX(d->hwirq)); @@ -1065,14 +1045,14 @@ static int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host) writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L); writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U); + /* Disable access control to the CFGU */ + writel_relaxed(0, host->axi + RZG3S_PCI_PERM); + /* Update bus info */ writeb_relaxed(primary_bus, host->pcie + PCI_PRIMARY_BUS); writeb_relaxed(secondary_bus, host->pcie + PCI_SECONDARY_BUS); writeb_relaxed(subordinate_bus, host->pcie + PCI_SUBORDINATE_BUS); - /* Disable access control to the CFGU */ - writel_relaxed(0, host->axi + RZG3S_PCI_PERM); - return 0; } @@ -1162,7 +1142,8 @@ static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host) static int rzg3s_pcie_host_parse_port(struct rzg3s_pcie_host *host) { - struct device_node *of_port = of_get_next_child(host->dev->of_node, NULL); + struct device_node *of_port __free(device_node) = + of_get_next_child(host->dev->of_node, NULL); struct rzg3s_pcie_port *port = &host->port; int ret; diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index 937ea6ae1ac4..4aa139abac16 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -302,9 +302,10 @@ static int xilinx_allocate_msi_domains(struct xilinx_pcie *pcie) return 0; } -static void xilinx_free_msi_domains(struct xilinx_pcie *pcie) +static void xilinx_free_irq_domains(struct xilinx_pcie *pcie) { irq_domain_remove(pcie->msi_domain); + irq_domain_remove(pcie->leg_domain); } /* INTx Functions */ @@ -480,8 +481,10 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie *pcie) phys_addr_t pa = ALIGN_DOWN(virt_to_phys(pcie), SZ_4K); ret = xilinx_allocate_msi_domains(pcie); - if (ret) + if (ret) { + irq_domain_remove(pcie->leg_domain); return ret; + } pcie_write(pcie, upper_32_bits(pa), XILINX_PCIE_REG_MSIBASE1); pcie_write(pcie, lower_32_bits(pa), XILINX_PCIE_REG_MSIBASE2); @@ -600,7 +603,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev) err = pci_host_probe(bridge); if (err) - xilinx_free_msi_domains(pcie); + xilinx_free_irq_domains(pcie); return err; } diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c index 3caf53c6c082..298036c3e7f9 100644 --- a/drivers/pci/controller/plda/pcie-starfive.c +++ b/drivers/pci/controller/plda/pcie-starfive.c @@ -55,7 +55,7 @@ struct starfive_jh7110_pcie { struct reset_control *resets; struct clk_bulk_data *clks; struct regmap *reg_syscon; - struct gpio_desc *power_gpio; + struct regulator *vpcie3v3; struct gpio_desc *reset_gpio; struct phy *phy; @@ -153,11 +153,13 @@ static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie, return dev_err_probe(dev, PTR_ERR(pcie->reset_gpio), "failed to get perst-gpio\n"); - pcie->power_gpio = devm_gpiod_get_optional(dev, "enable", - GPIOD_OUT_LOW); - if (IS_ERR(pcie->power_gpio)) - return dev_err_probe(dev, PTR_ERR(pcie->power_gpio), - "failed to get power-gpio\n"); + pcie->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3"); + if (IS_ERR(pcie->vpcie3v3)) { + if (PTR_ERR(pcie->vpcie3v3) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(pcie->vpcie3v3), + "failed to get vpcie3v3 regulator\n"); + pcie->vpcie3v3 = NULL; + } return 0; } @@ -270,8 +272,8 @@ static void starfive_pcie_host_deinit(struct plda_pcie_rp *plda) container_of(plda, struct starfive_jh7110_pcie, plda); starfive_pcie_clk_rst_deinit(pcie); - if (pcie->power_gpio) - gpiod_set_value_cansleep(pcie->power_gpio, 0); + if (pcie->vpcie3v3) + regulator_disable(pcie->vpcie3v3); starfive_pcie_disable_phy(pcie); } @@ -304,8 +306,11 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda) if (ret) return ret; - if (pcie->power_gpio) - gpiod_set_value_cansleep(pcie->power_gpio, 1); + if (pcie->vpcie3v3) { + ret = regulator_enable(pcie->vpcie3v3); + if (ret) + dev_err_probe(dev, ret, "failed to enable vpcie3v3 regulator\n"); + } if (pcie->reset_gpio) gpiod_set_value_cansleep(pcie->reset_gpio, 1); |
