summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-23 12:44:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-23 12:44:10 -0700
commit570f7e331f5febb30f1384817463c7e42b65ca7d (patch)
tree1b8b905be28128b36b991d69241768b99454772d
parentb6b019a1d9b90ac51174f0ca15b1338b61506bf9 (diff)
parent9324becc398a1f9e67c91a3d652a7c56e5afc7f4 (diff)
downloadlinux-570f7e331f5febb30f1384817463c7e42b65ca7d.tar.gz
linux-570f7e331f5febb30f1384817463c7e42b65ca7d.zip
Merge tag 'pci-v7.3-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
Pull PCI updates from Bjorn Helgaas: "Resource management: - Add hotplug reservation only once (not at each level of the hierarchy) so bridge windows don't grow more than necessary (Ilpo Järvinen) Driver binding: - Rework device matching so device ID lifetime only needs to cover the probe path since dynamic IDs can be removed at any time (Gary Guo) Error handling: - Update mappings of AER errors to agent & layer and log them for each individual error when multiple errors detected (Lukas Wunner) - Log Error Source only once, not twice in separate messages (Lukas Wunner) - Emit TLP Log only for unmasked errors (Lukas Wunner) - Support Advisory Non-Fatal Errors (Lukas Wunner) - Allow DPC on all Downstream Ports, not just Root Ports, when OS controls AER (Darshit Shah) ASPM: - Program the same ASPM Control values for every function of multi-function devices, as recommended by the PCIe spec (Krishna Chaitanya Chundru) - Avoid L0s for Realtek RTS525A, where it causes an AER interrupt storm (Max Lee) - Avoid ASPM L0s, L1, and L1 PM Substates based on 'aspm-no-l0s', 'aspm-no-l1' [1], and 'aspm-no-l1ss' DT properties (Krishna Chaitanya Chundru) Power management: - Allow D3 for native hotplug-capable Root Ports on non-x86 platforms (we avoid D3 for these ports on x86 because some old platforms didn't validate it) (Manivannan Sadhasivam) - Allow portdrv to claim Ports even if they don't support services (AER, PME, DPC, hotplug, etc) so it can do power management (Brian Norris) Power control: - Add support for PCIe WAKE# interrupt when described via DT (Krishna Chaitanya Chundru) - For the TC9563 PCIe switch: - Take a reference on the I2C adapter to avoid uninterruptible hang when unloading an I2C module while in-use (Johan Hovold) - Update DT binding and driver to restrict Tx Amplitude, DFE and N_FTS to USP, DSP1 and DSP2 (Manivannan Sadhasivam) - Power off only external-facing ports (DSP1, DSP2), leaving USP and DSP3 (aka VDSP) powered up (Manivannan Sadhasivam) - Move integrated MAC Endpoint out of the list of internal ports and configure it separately (Manivannan Sadhasivam) Virtualization: - Add ACS quirk for Pericom PI7C9X2G608 switches (Tim Harvey) - Fix a long-standing bug in the Intel PCH Root Port MPC ACS quirk that didn't update the intended INTEL_MPC_REG_IRBNCE bit because it used a 16-bit config write when a 32-bit write was intended (Mohamad Raizudeen) Procfs: - Avoid spurious runtime PM wakeup on config space accesses that are outside config space and fail before reaching PCI (Krzysztof Wilczyński) - Warn on user-space writes to kernel-exclusive config space regions, as we already do for sysfs (Krzysztof Wilczyński) - Check credentials of opener, not reader, for config space reads, as we already do for sysfs (Krzysztof Wilczyński) Sysfs: - In pci_write_legacy_io(), avoid out-of-bounds reads from the user buffer and fix incorrect ioport write data (1-byte writes on little-endian powerpc, 2- and 4-byte writes on big-endian powerpc) (Krzysztof Wilczyński) - In pci_read_legacy_io(), fix incorrect ioport read data for 2- and 4-byte reads on big-endian powerpc (Krzysztof Wilczyński) - Fix I/O port accessor argument order in Alpha pci_legacy_write() (Krzysztof Wilczyński) - Avoid spurious runtime PM wakeup on config space accesses that are outside config space and fail before reaching PCI (Krzysztof Wilczyński) - Return -EINVAL, not -ENODEV, for mmap of I/O BAR that fails because the arch doesn't support it, as we do for procfs (Krzysztof Wilczyński) - Check for LOCKDOWN_PCI_ACCESS for legacy_io and legacy_mem, as we do for other config space accessors (Krzysztof Wilczyński) Peer-to-peer DMA: - Add Nvidia Vera Rubin to list of platforms that support P2PDMA (Leon Romanovsky) Endpoint framework: - Check doorbell SUCCESS bit in pci_endpoint_test to avoid treating some failures as successes (Niklas Cassel) - Fail doorbell test when the trigger IRQ is missed (Niklas Cassel) New native PCIe controller drivers: - Add DT binding and driver for NVIDIA Tegra264 (Thierry Reding) Native PCIe controllers: - Use common wait time definitions for PCIe link monitoring instead of defining driver-private duplicates (Thierry Reding) Generic host bridge driver: - Fix NULL pointer dereference that caused enumeration failures on 32-bit CAM systems (Steffen Persvold) Amlogic Meson PCIe controller driver: - Correct the PERST# GPIO state so it remains asserted until power and REFCLK become stable to fix enumeration failure (Ronald Claveau) ASPEED PCIe controller driver: - Switch to irq_domain_create_linear() so we can obsolete irq_domain_add_linear() (Jiri Slaby) Cadence PCIe controller driver: - Add MODULE_DEVICE_TABLE to generate module aliases for OF-based module autoloading (Pengpeng Hou) - Add debugfs 'ltssm_status' file for LGA- and HPA-based Cadence controllers (Hans Zhang) - Support up to x4 (not x2) lanes for J200 (Takuma Fujiwara) - Fix host/endpoint dependencies for cadence-plat driver to fix link error when cadence-plat is built-in but the host or endpoint driver is modular (Aksh Garg) Freescale i.MX6 PCIe controller driver: - Add imx6 intr/aer/pme interrupt lines for i.MX95 (Richard Zhu) - Remove PERST# checking from pci_host_common_parse_port() so callers can decide whether to fall back to legacy DT binding with PERST# in the host bridge (Sherry Sun) - Fix build issues when PCI_PWRCTRL_GENERIC or PCI_HOST_COMMON is a module (Arnd Bergmann) - Create pwrctrl devices only once by doing it from imx_pcie_probe() instead of imx_pcie_host_init(), which is used during both probe and resume (Sherry Sun) - Use 'dw_pcie_rp->skip_pwrctrl_off' to avoid powering off devices during suspend to preserve wakeup capability (Sherry Sun) - Add runtime PM support for i.MX95 to allow dynamic power management when the link is idle (Richard Zhu) Intel VMD host bridge driver: - Support device ID 0x28C1 and assume that BIOS has already enumerated the hierarchy below VMD and stored bus range info for OS to use (Nirmal Patel) - Add support for VMCONFIG BUS_RESTRICT_CFG=3, which makes it possible to enumerate downstream devices on Intel Arrow Lake-HX systems and probably others (Ali Alaei) - Pay attention to _OSC negotiation for VMD hierarchy only when running on bare metal, not when running in a VM (Nirmal Patel) - Add Nova Lake (NVL) and Dunlow (DNL) Device IDs (Szymon Durawa) MediaTek PCIe controller driver: - Add support for PCIe controller in EcoNet EN7528 and EN751221 SoCs (Caleb James DeLisle) MediaTek PCIe Gen3 controller driver: - Add mediatek-gen3 'memory-region' for restricted DMA buffer (Chen-Yu Tsai) NVIDIA Tegra264 PCIe controller driver: - Distinguish Tegra264 C0 PCIe controller for internal GPU from C1-C5 controllers so the unit address matches the first 'reg' entry (Thierry Reding) - Add Tegra264 Root Port stanzas to prepare for generic WAKE# handling (Thierry Reding) Qualcomm PCIe controller driver: - Add IPQ9650 compatible with global interrupt (Kathiravan Thirumoorthy) - Add IPQ5210 compatible with IPQ9574 fallback (Varadarajan Narayanan) - Add DT binding and driver support for Hawi SoC (Matthew Leung) - Skip PERST# GPIOs provided by downstream PCIe devices, which should be handled by drivers of those devices (Manivannan Sadhasivam) - Stop advertising Attention Button Present (no Qcom SoCs support Attention Buttons) so pciehp can use Presence Detect Changed events (Qiang Yu) Renesas R-Car PCIe controller driver: - Add rcar-gen4-pci-host optional 'msi-parent' for GIT ITS (Marek Vasut) - When MSI is enabled but iMSI-RX is not used, configure AXIINTC to allow GIT ITS to handle MSI (Marek Vasut) - Refactor GIC600 implementation to make it easier to add platforms that only support 32-bit addressing (Marek Vasut) - Add Renesas R-Car Gen4 S4/V4H/V4M to the list of GIC600 integrations that only support 32-bit addressing (Marek Vasut) Renesas RZ/G3S PCIe controller driver: - Add DT binding and driver support for RZ/V2H(P) SoC, which contains two PCIe controllers, configured either as a single x4 link or two independent x2 link controllers (Lad Prabhakar) SpacemiT K1 PCIe controller driver: - Add missing MODULE_DEVICE_TABLE() to generate module alias info for OF-based module autoloading (Pengpeng Hou) StarFive PCIe controller driver: - Fix resource leaks on error paths in host_init() (Ali Tariq) - Fix runtime PM handling and teardown ordering to avoid register access while power or clocks are disabled (Ali Tariq) - Check for runtime PM resume failure to avoid register access while power or clocks are disabled (Ali Tariq) Synopsys DesignWare PCIe controller driver: - Add LECARC PMU IDs to the DWC RAS/DES VSEC list so it can take advantage of the existing debugfs support for silicon debug, error injection, and event counters (Brett Zhou) - Factor pcie_valid_speed() and pci_bus_speed2lnkctl2() out of bwctrl so they can be shared by the DWC core (Hans Zhang) - Flush MSI writes from endpoint before unmapping the iATU, as we already do for MSI-X writes (Niklas Cassel) - Unmap MSI iATU window before mapping MSI-X window, to avoid a subsequent MSI write using a disabled aperture and losing the interrupt (Niklas Cassel) - Change endpoint .pre_init() and .init() callbacks to return errors and handle them (Marek Vasut) UltraRISC PCIe controller driver: - Add 'core', 'dbi', and 'aux' clocks to DT binding and manage them in the driver (Jia Wang) - Use module_platform_driver() since this may be built as a module, though not removable because IRQs can't be safely disposed (Jia Wang) MicroSemi Switchtec management driver: - Add Microchip PCI1008 device ID and include it in NTB DMA alias quirk (Logan Gunthorpe) Miscellaneous: - Document how to write PCI Host Controller drivers (Manivannan Sadhasivam) - Fix typos in documentation (D'Orus Tsitera) - Use %pe format specifier to print error pointers so we get symbolic errname when available (Krzysztof Wilczyński)" * tag 'pci-v7.3-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci: (124 commits) PCI: vmd: Add Nova Lake (NVL) and Dunlow (DNL) Device IDs PCI: tegra264: Add Tegra264 support dt-bindings: PCI: tegra264: Switch to PCIe Root Port bindings dt-bindings: PCI: tegra264: Strictly distinguish C0 from C1-C5 PCI/AER: Support Advisory Non-Fatal Errors PCI: Fix 32-bit config write in Intel PCH Root Port MPC ACS quirk PCI: dwc: Handle return value from endpoint .pre_init callback PCI: dwc: Handle return value from endpoint .init callback PCI: dwc: Add PCI ID for LECARC PCIe PMU PCI/ASPM: Mask ASPM states based on Devicetree properties PCI/ASPM: Disable/restore ASPM on every function for multi-function devices Documentation: PCI: Document how to write PCI Host Controller drivers PCI/ASPM: Use pcie_capability_clear_and_set_word() for ASPM disable/restore PCI: Add support for PCIe WAKE# interrupt PCI: Allow D3 for native hotplug-capable Root Ports on non-x86 platforms dt-bindings: PCI: Correct white-space style PCI/ASPM: Avoid L0s for Realtek RTS525A PCI: ultrarisc: Use module_platform_driver() PCI: ultrarisc: Get and enable DP1000 PCIe controller clocks dt-bindings: PCI: ultrarisc: Add required DP1000 PCIe clocks ...
-rw-r--r--Documentation/ABI/testing/debugfs-cdns-pcie5
-rw-r--r--Documentation/ABI/testing/sysfs-bus-pci2
-rw-r--r--Documentation/PCI/controller/index.rst1
-rw-r--r--Documentation/PCI/controller/pci-controller-drivers.rst402
-rw-r--r--Documentation/arch/arm64/silicon-errata.rst1
-rw-r--r--Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml25
-rw-r--r--Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml6
-rw-r--r--Documentation/devicetree/bindings/pci/nvidia,tegra264-pcie.yaml109
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,hawi-pcie.yaml201
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml15
-rw-r--r--Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml3
-rw-r--r--Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml34
-rw-r--r--Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml8
-rw-r--r--Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml12
-rw-r--r--Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml21
-rw-r--r--Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml16
-rw-r--r--MAINTAINERS6
-rw-r--r--arch/alpha/kernel/pci-sysfs.c32
-rw-r--r--arch/arm64/Kconfig9
-rw-r--r--arch/powerpc/kernel/pci-common.c9
-rw-r--r--drivers/ata/ata_generic.c6
-rw-r--r--drivers/char/agp/amd-k7-agp.c26
-rw-r--r--drivers/char/agp/via-agp.c308
-rw-r--r--drivers/dma/switchtec_dma.c1
-rw-r--r--drivers/ipack/carriers/tpci200.c1
-rw-r--r--drivers/ipack/carriers/tpci200.h1
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c24
-rw-r--r--drivers/misc/pci_endpoint_test.c38
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.c11
-rw-r--r--drivers/pci/controller/Kconfig12
-rw-r--r--drivers/pci/controller/Makefile1
-rw-r--r--drivers/pci/controller/cadence/Kconfig13
-rw-r--r--drivers/pci/controller/cadence/Makefile1
-rw-r--r--drivers/pci/controller/cadence/pci-j721e.c4
-rw-r--r--drivers/pci/controller/cadence/pci-sky1.c5
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-debugfs.c263
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-ep.c3
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-common.c6
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-hpa.c21
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host.c9
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-lga-regs.h5
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-plat.c1
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence.h198
-rw-r--r--drivers/pci/controller/dwc/Kconfig7
-rw-r--r--drivers/pci/controller/dwc/pci-dra7xx.c4
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c72
-rw-r--r--drivers/pci/controller/dwc/pci-keystone.c10
-rw-r--r--drivers/pci/controller/dwc/pci-layerscape-ep.c6
-rw-r--r--drivers/pci/controller/dwc/pci-meson.c4
-rw-r--r--drivers/pci/controller/dwc/pcie-artpec6.c4
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-ep.c33
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-host.c10
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.c28
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h5
-rw-r--r--drivers/pci/controller/dwc/pcie-dw-rockchip.c138
-rw-r--r--drivers/pci/controller/dwc/pcie-keembay.c4
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c194
-rw-r--r--drivers/pci/controller/dwc/pcie-rcar-gen4.c124
-rw-r--r--drivers/pci/controller/dwc/pcie-spacemit-k1.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-tegra194.c16
-rw-r--r--drivers/pci/controller/dwc/pcie-ultrarisc.c87
-rw-r--r--drivers/pci/controller/mobiveil/pcie-mobiveil.c4
-rw-r--r--drivers/pci/controller/mobiveil/pcie-mobiveil.h5
-rw-r--r--drivers/pci/controller/pci-aardvark.c9
-rw-r--r--drivers/pci/controller/pci-host-common.c64
-rw-r--r--drivers/pci/controller/pci-host-common.h1
-rw-r--r--drivers/pci/controller/pci-host-generic.c11
-rw-r--r--drivers/pci/controller/pci-tegra.c3
-rw-r--r--drivers/pci/controller/pci-xgene.c5
-rw-r--r--drivers/pci/controller/pcie-aspeed.c8
-rw-r--r--drivers/pci/controller/pcie-mediatek.c154
-rw-r--r--drivers/pci/controller/pcie-rockchip.c4
-rw-r--r--drivers/pci/controller/pcie-rzg3s-host.c233
-rw-r--r--drivers/pci/controller/pcie-tegra264.c467
-rw-r--r--drivers/pci/controller/pcie-xilinx-nwl.c9
-rw-r--r--drivers/pci/controller/plda/pcie-plda-host.c52
-rw-r--r--drivers/pci/controller/plda/pcie-starfive.c32
-rw-r--r--drivers/pci/controller/vmd.c203
-rw-r--r--drivers/pci/doe.c4
-rw-r--r--drivers/pci/ecam.c13
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c3
-rw-r--r--drivers/pci/of.c79
-rw-r--r--drivers/pci/p2pdma.c5
-rw-r--r--drivers/pci/pci-acpi.c4
-rw-r--r--drivers/pci/pci-driver.c194
-rw-r--r--drivers/pci/pci-sysfs.c177
-rw-r--r--drivers/pci/pci.c33
-rw-r--r--drivers/pci/pci.h72
-rw-r--r--drivers/pci/pcie/aer.c275
-rw-r--r--drivers/pci/pcie/aspm.c149
-rw-r--r--drivers/pci/pcie/bwctrl.c22
-rw-r--r--drivers/pci/pcie/err.c6
-rw-r--r--drivers/pci/pcie/portdrv.c30
-rw-r--r--drivers/pci/probe.c2
-rw-r--r--drivers/pci/proc.c16
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c144
-rw-r--r--drivers/pci/quirks.c10
-rw-r--r--drivers/pci/remove.c1
-rw-r--r--drivers/pci/search.c8
-rw-r--r--drivers/pci/setup-bus.c2
-rw-r--r--drivers/pci/switch/switchtec.c1
-rw-r--r--drivers/scsi/nsp32.c8
-rw-r--r--drivers/scsi/nsp32.h8
-rw-r--r--include/linux/of_pci.h6
-rw-r--r--include/linux/pci-ecam.h3
-rw-r--r--include/linux/pci.h4
-rw-r--r--include/linux/pci_ids.h1
-rw-r--r--include/linux/pcie-dwc.h4
110 files changed, 4181 insertions, 993 deletions
diff --git a/Documentation/ABI/testing/debugfs-cdns-pcie b/Documentation/ABI/testing/debugfs-cdns-pcie
new file mode 100644
index 000000000000..be963784c446
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-cdns-pcie
@@ -0,0 +1,5 @@
+What: /sys/kernel/debug/cdns_pcie_<dev>/ltssm_status
+Date: July 2026
+Contact: Hans Zhang <18255117159@163.com>
+Description: (RO) Read will return the current PCIe LTSSM state in both
+ string and raw value. \ No newline at end of file
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index b767db2c52cb..55ea1db749a1 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -171,7 +171,7 @@ Description:
all devices attached through the subordinate bus of a specific
bridge device, writing 1 to this will try to do it. This will
affect all devices attached to the system through this bridge
- similiar to writing 1 to their individual "reset" file, so use
+ similar to writing 1 to their individual "reset" file, so use
with caution.
What: /sys/bus/pci/devices/.../vpd
diff --git a/Documentation/PCI/controller/index.rst b/Documentation/PCI/controller/index.rst
index c2ce9ccdcfa0..c15e2c9ac855 100644
--- a/Documentation/PCI/controller/index.rst
+++ b/Documentation/PCI/controller/index.rst
@@ -7,4 +7,5 @@ PCI Native Host Bridge and Endpoint Drivers
.. toctree::
:maxdepth: 2
+ pci-controller-drivers
rcar-pcie-firmware
diff --git a/Documentation/PCI/controller/pci-controller-drivers.rst b/Documentation/PCI/controller/pci-controller-drivers.rst
new file mode 100644
index 000000000000..749a8bf7646e
--- /dev/null
+++ b/Documentation/PCI/controller/pci-controller-drivers.rst
@@ -0,0 +1,402 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================================
+Writing PCI Host Controller Drivers
+===================================
+
+:Author: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
+
+Introduction
+============
+
+A PCI Host Controller driver controls PCI Root Complex (RC) hardware. The
+Root Complex hardware comprises a single PCI Host Bridge and one or more
+Root Port or Root Complex Integrated Endpoint (RCiEP) devices::
+
+ +------------------+
+ | CPU |
+ +------------------+
+ |
+ +--------------------------------------------+
+ | | Root |
+ | +------------------+ Complex |
+ | | Host Bridge | |
+ | +------------------+ |
+ | | |
+ | Bus 0 | |
+ | +------------|----------+ |
+ | | | | |
+ | +----------+ +----------+ +-------+ |
+ | | Root | | Root | | RCiEP | |
+ | | Port | | Port | +-------+ |
+ | +----------+ +----------+ |
+ | | | |
+ | Bus 1 | Bus 2 | |
+ | | | |
+ +-------|-------------|----------------------+
+ | |
+ +-----------+ +-----------+
+ | Endpoint | | Endpoint |
+ +-----------+ +-----------+
+
+Host Bridge: Used to connect CPU(s) to the PCI hierarchy.
+
+Root Port: Virtual PCI-PCI bridge connecting the Host Bridge to a PCI bus.
+
+RCiEP: Embedded PCIe Endpoint inside Root Complex connected to the Host
+Bridge.
+
+Endpoint: PCIe device connected to a Root Port through a Link.
+
+Enumeration
+===========
+
+The Host Bridge device is not discoverable, so it is typically enumerated
+with the help of firmware interfaces like ACPI or Devicetree. But the Root
+Port and RCiEP devices are discoverable through the standard enumeration
+process defined in the PCIe spec.
+
+A Host Controller driver usually configures both Host Bridge and Root
+Port(s) based on platform requirements. In the case of ACPI on standardized
+platforms (e.g. x86), no platform-specific host controller driver is
+required as the firmware configures the Root Complex before OS boot and
+exposes the resource information through ACPI tables. For more info, refer
+to :doc:`../acpi-info`.
+
+For Devicetree platforms, a dedicated host controller driver is often
+required because the Root Complex hardware typically needs vendor-specific
+initialization like PHY, clocks, power domains, and there is no standard
+mechanism equivalent to ACPI/MCFG to convey resource information to the OS.
+So on these platforms, Root Complex hardware is enumerated through
+Devicetree nodes as below::
+
+ pcie@10000000 {
+ compatible = "vendor,soc-pcie";
+ reg = <0x0 0x10000000 0x0 0x1000>,
+ <0x0 0x10001000 0x0 0x1000>;
+ reg-names = "cfg", "app";
+ device_type = "pci";
+ bus-range = <0x00 0xff>;
+ linux,pci-domain = <0>;
+ num-lanes = <4>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ ranges = <0x01000000 0x0 0x00000000 0x0 0x20000000 0x0 0x00100000>,
+ <0x02000000 0x0 0x20100000 0x0 0x20100000 0x0 0x1ff00000>;
+ dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
+
+ clocks = <&clkc PCIE_CORE_CLK>,
+ <&clkc PCIE_AUX_CLK>;
+ clock-names = "core", "aux";
+ resets = <&reset PCIE_RESET>;
+ power-domains = <&power PCIE_PD>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &gic 0 0 GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &gic 0 0 GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &gic 0 0 GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+
+ pcie@0 {
+ compatible = "pciclass,0604";
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+
+ phys = <&pcie_phy>;
+ reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&gpio 11 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+
+Note the presence of two nodes in the above example. The ``pcie@10000000``
+node represents a PCI Host Bridge device, and ``pcie@0`` represents a
+single Root Port device. The Host Bridge node should contain properties
+associated with the Host Bridge device such as ranges, interrupts, clocks,
+power-domains etc... and the Root Port node should contain port-specific
+properties such as phys, reset-gpios, wake-gpios etc...
+
+NOTE: Legacy Devicetrees used a single node to describe both Host Bridge
+and Root Port devices, but that design is now deprecated.
+
+Driver Design
+=============
+
+Prerequisites
+-------------
+
+Before starting to write a new Host Controller driver, check if any of the
+existing drivers can be reused. For example, if the Root Complex supports
+the Enhanced Configuration Access Mechanism (ECAM) and the bootloader has
+configured the ECAM mapping before OS boot, the ``CONFIG_PCI_HOST_GENERIC``
+driver can be used.
+
+If the Root Complex hardware (IP) is from IP vendors such as Synopsys or
+Cadence, the existing ``CONFIG_PCIE_DW_PLAT_HOST`` and
+``CONFIG_PCIE_CADENCE_PLAT_HOST`` drivers can be reused. If not, then check
+if any of the existing glue drivers available for these IPs could be
+reused.
+
+If the Root Complex hardware is designed in-house by the SoC vendor, check
+if there is an existing driver from the vendor for their previous
+generation Root Complex hardware. Often, the existing driver can be
+reused with minimal modifications.
+
+Only if the Root Complex doesn't satisfy above prerequisites should a new
+Host Controller driver be written.
+
+Probe
+-----
+
+During the Host Controller driver probe(), it initializes the Root Complex
+hardware and registers the Host Bridge with the PCI core. The typical steps
+are described below.
+
+Initialize Resources
+~~~~~~~~~~~~~~~~~~~~
+
+At the start of the probe(), initialize Host Bridge-specific resources such
+as clocks, PHYs, regulators, and resets. These resources are described in
+the Host Bridge Devicetree node and should be brought up before accessing
+the controller hardware.
+
+NOTE: Use the devm_*() managed APIs wherever possible so the resources are
+released automatically on probe failure and on driver detach.
+
+Configuration Space Access
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The PCI core accesses the Configuration Space of the enumerated devices
+through the callbacks provided by the driver in struct pci_ops. These
+callbacks abstract how the Root Complex generates a Configuration Request
+for a given Bus, Device and Function number.
+
+If the Root Complex supports ECAM, the generic accessors can be reused by
+using pci_ecam_map_bus() along with pci_generic_config_read() and
+pci_generic_config_write(). Such drivers can often be built on top of
+pci_host_common_probe() without providing any custom accessors.
+
+Setup Address Translation
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Host Bridge translates accesses between the CPU address domain and the
+PCI address domain in both directions:
+
+- Outbound: CPU addresses are translated to PCI bus addresses for the
+ Memory and I/O accesses initiated by the CPU towards the downstream
+ devices. These windows are derived from the ``ranges`` property of the
+ Host Bridge Devicetree node.
+
+- Inbound: PCI bus addresses are translated to system memory addresses for
+ the accesses (such as DMA) initiated by the downstream devices. These
+ windows are derived from the ``dma-ranges`` property.
+
+The PCI core parses ``ranges`` and ``dma-ranges`` into the Host Bridge
+resource lists, and the driver programs one translation window per entry.
+Note that the CPU address and the PCI bus address of a window may differ,
+so the offset between them has to be accounted for while programming the
+windows.
+
+NOTE: If the hardware supports ECAM, it is strongly recommended to use ECAM
+for the Configuration Space so a translation window need not be
+reprogrammed for every Configuration access.
+
+Interrupt Handling
+~~~~~~~~~~~~~~~~~~
+
+Downstream devices can signal interrupts either through INTx or through
+Message Signaled Interrupts (MSI/MSI-X). The driver has to enable the
+mechanisms supported by the Root Complex.
+
+INTx interrupts are conveyed to the Root Complex through the Assert_INTx
+and Deassert_INTx messages and are then reported as system interrupts. The
+driver typically creates an IRQ domain for the four interrupts (INTA to
+INTD) and demultiplexes an incoming interrupt to the corresponding virtual
+IRQ.
+
+An MSI/MSI-X is signaled by the downstream device as a Memory Write to a
+Root Complex-specific address. There are two ways to handle them:
+
+- If the Root Complex integrates its own MSI controller, the driver has to
+ create an MSI IRQ domain, program the MSI target address and demultiplex
+ the incoming MSIs to the corresponding virtual IRQs. MSI-X is handled
+ through the same domain.
+
+- If the MSIs are handled by an external interrupt controller (such as the
+ GIC ITS), the Root Complex Devicetree node needs to have an
+ ``msi-parent`` property and the driver need not implement an MSI
+ controller.
+
+
+Powering up the Slot/Endpoint
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Power ON any slots or Endpoints connected to the bus with the help of the
+PWRCTRL subsystem APIs such as pci_pwrctrl_create_devices() and
+pci_pwrctrl_power_on_devices(). Note that this requires defining the
+supplies in the Root Port or Endpoint Devicetree node.
+
+Link Training
+~~~~~~~~~~~~~
+
+Once the resources are initialized, the driver has to initiate Link
+training by enabling the LTSSM (Link Training and Status State Machine) of
+the Root Port. If a PERST# signal is present, it should be deasserted to
+bring the downstream device out of fundamental reset before enabling the
+LTSSM.
+
+Before PERST# is deasserted, the driver must satisfy the power sequencing
+delays defined by the PCI Express Card Electromechanical (CEM)
+Specification. The power supplies must be stable for at least T_PVPERL
+(``PCIE_T_PVPERL_MS``, 100 ms) and the reference clock must be stable for
+at least T_PERST-CLK (``PCIE_T_PERST_CLK_US``, 100 us) before PERST# is
+deasserted.
+
+After the LTSSM is enabled, the driver should wait (with a timeout) for the
+LTSSM to reach the L0 state, indicating that the Link is up.
+
+Once the Link is up, the PCI Express Base Specification (Conventional
+Reset) requires software to wait for at least ``PCIE_RESET_CONFIG_WAIT_MS``
+(100 ms) before sending the first Configuration Request to the downstream
+device. For a Link operating up to 5.0 GT/s, this delay is counted from the
+exit of the Conventional Reset (PERST# deassertion), while for a Link
+operating above 5.0 GT/s it is counted from the completion of Link
+training. The driver should honor this delay before the bus is scanned.
+
+NOTE: A failure to establish the Link should NOT be treated as a probe
+failure unless the Root Port is not Hotplug capable. If the Root Port is
+Hotplug capable, then the driver should still register the Host Bridge and
+scan the bus, so that the downstream device can be discovered later when
+the Link comes up.
+
+Register the Host Bridge
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Finally, allocate the Host Bridge device with devm_pci_alloc_host_bridge(),
+assign the Configuration Space accessors (struct pci_ops) to it, and start
+the bus scan by calling pci_host_probe(). This is the last step of the
+probe(). pci_host_probe() creates the Root bus for the Host Bridge and
+scans/enumerates all the Root Port, RCiEP and Endpoint devices connected to
+the bus.
+
+If the Root Complex IP is from a known IP vendor, the IP specific helpers
+should be reused for the above operations wherever applicable.
+
+Power Management
+----------------
+
+A Host Controller driver participates in both runtime and system-wide power
+management. In both cases, the driver is responsible for the power state of
+the Root Complex hardware, while the PCI core manages the power state of
+the enumerated devices.
+
+Runtime PM
+~~~~~~~~~~
+
+Runtime PM allows the Root Complex hardware to be powered down when it is
+idle. The driver typically enables runtime PM with pm_runtime_enable() and
+takes a reference with pm_runtime_get_sync() during probe(), so that the
+controller stays powered while it is in use. The reference is dropped in
+remove().
+
+If the Root Complex can be powered down when idle, the driver implements
+the runtime_suspend and runtime_resume callbacks to disable and enable the
+controller resources such as the clocks, PHYs, and power domain. These
+callbacks should manage only the controller resources and must not touch
+the state of the enumerated devices, which is handled by the PCI core.
+
+System PM
+~~~~~~~~~
+
+During system suspend and resume, the driver has to save and restore the
+state of the Root Complex and put the Link into a low power state.
+
+These operations are performed in the _noirq() PM callbacks (for example,
+using NOIRQ_SYSTEM_SLEEP_PM_OPS()), because the controller resources such
+as the clocks and PHY are shared by all the child devices. Suspending them
+earlier would break the child devices whose own suspend callbacks may still
+access their Configuration Space.
+
+In the suspend callback, the driver should:
+
+- Broadcast a PME_Turn_Off message and wait for the PME_TO_Ack, so that the
+ Link can transition to the L2/L3 state.
+- Stop the LTSSM and disable the controller resources such as the clocks,
+ PHY and power domain.
+- Save any controller state that is not retained across the low power
+ state.
+- Power off the downstream devices using pci_pwrctrl_power_off_devices().
+
+In the resume callback, the driver should reverse the above by enabling the
+controller resources, restoring the saved state, re-initializing the Root
+Complex and re-establishing the Link as done during probe().
+
+NOTE: If the Link is in the ASPM L1 (or L1 substates) state, some drivers
+keep the Link in L1 across suspend for a faster resume, instead of
+transitioning it to L2/L3. This is a driver policy decision based on the
+platform and the devices connected.
+
+Shutdown
+--------
+
+The shutdown() callback is invoked during system reboot or when
+transitioning to a new kernel through kexec. Its purpose is to quiesce the
+Root Complex so that the downstream devices cannot corrupt the memory or
+interrupt the new kernel.
+
+The driver should:
+
+- Disable the interrupts (INTx and MSI) reported by the Root Complex so
+ that no spurious interrupt is delivered to the new kernel.
+- Broadcast a PME_Turn_Off message and stop the LTSSM to bring the Link
+ down so that any in-flight DMA from the downstream devices is stopped
+ before the reset.
+- Power down the controller resources.
+
+Unlike remove(), shutdown() does not need to tear down the software state
+such as the Root bus, since the system is going down anyway.
+
+NOTE: shutdown() is optional. It is mainly required on platforms where the
+downstream devices could perform DMA or raise interrupts during the
+transition to reboot or kexec.
+
+Remove
+------
+
+remove() is called when the driver is detached, and it should undo
+everything done in probe() in the reverse order.
+
+The first step is to remove the enumerated devices and the Root bus, by
+calling pci_stop_root_bus() followed by pci_remove_root_bus() under the
+pci_lock_rescan_remove() lock. This detaches all the child devices before
+the controller resources are released.
+
+After the bus is removed, the driver should:
+
+- Disable the interrupts reported by the Root Complex.
+- Stop the LTSSM to bring the Link down.
+- Power down the PHY and disable the clocks, regulators and resets.
+- Drop the runtime PM reference with pm_runtime_put_sync() and disable
+ runtime PM with pm_runtime_disable().
+
+Resources allocated through the devm_*() APIs are released automatically
+after remove() returns and need not be freed explicitly.
+
+NOTE: A Host Controller driver is encouraged to be built as a loadable
+module, but it should not be removed at runtime if it implements its own
+IRQ domains such as MSI or INTx controllers. The IRQ mappings created for
+such domains can persist even after the interrupts are released and cannot
+be disposed of safely, so tearing down the IRQ domains on removal is
+fragile. Such drivers should therefore prevent their removal. See the
+following thread for more details:
+https://lore.kernel.org/linux-pci/87k085xekg.wl-maz@kernel.org/
diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
index 61ceb4e7e70f..ac3248b9f2f3 100644
--- a/Documentation/arch/arm64/silicon-errata.rst
+++ b/Documentation/arch/arm64/silicon-errata.rst
@@ -364,6 +364,7 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| Qualcomm Tech. | Kryo4xx Gold | N/A | ARM64_ERRATUM_1286807 |
+----------------+-----------------+-----------------+-----------------------------+
+| Renesas | S4/V4H/V4M | N/A | RENESAS_ERRATUM_GEN4GICITS1 |
+----------------+-----------------+-----------------+-----------------------------+
| Rockchip | RK3588 | #3588001 | ROCKCHIP_ERRATUM_3588001 |
+----------------+-----------------+-----------------+-----------------------------+
diff --git a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
index 812ef5957cfc..328b8156cd94 100644
--- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
@@ -214,7 +214,7 @@ examples:
dma-ranges = <0x42000000 0x1 0x00000000 0x0 0x40000000 0x0 0x80000000>,
<0x42000000 0x1 0x80000000 0x3 0x00000000 0x0 0x80000000>;
brcm,enable-ssc;
- brcm,scb-sizes = <0x0000000080000000 0x0000000080000000>;
+ brcm,scb-sizes = <0x0000000080000000 0x0000000080000000>;
/* PCIe bridge, Root Port */
pci@0,0 {
diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
index e8b8131f5f23..4f56e8e4f100 100644
--- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
@@ -58,12 +58,18 @@ properties:
items:
- description: builtin MSI controller.
- description: builtin DMA controller.
+ - description: PCIe event interrupt.
+ - description: builtin AER SPI standalone interrupt line.
+ - description: builtin PME SPI standalone interrupt line.
interrupt-names:
minItems: 1
items:
- const: msi
- const: dma
+ - const: intr
+ - const: aer
+ - const: pme
reset-gpio:
deprecated: true
@@ -249,6 +255,25 @@ allOf:
- const: ref
- const: extref # Optional
+ - if:
+ properties:
+ compatible:
+ enum:
+ - fsl,imx6q-pcie
+ - fsl,imx6sx-pcie
+ - fsl,imx6qp-pcie
+ - fsl,imx7d-pcie
+ - fsl,imx8mm-pcie
+ - fsl,imx8mp-pcie
+ - fsl,imx8mq-pcie
+ - fsl,imx8q-pcie
+ then:
+ properties:
+ interrupts:
+ maxItems: 2
+ interrupt-names:
+ maxItems: 2
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
index 4db700fc36ba..b88717dec1fa 100644
--- a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
+++ b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
@@ -51,6 +51,7 @@ properties:
- mediatek,mt7981-pcie
- mediatek,mt7986-pcie
- mediatek,mt8188-pcie
+ - mediatek,mt8189-pcie
- mediatek,mt8195-pcie
- const: mediatek,mt8192-pcie
- items:
@@ -115,6 +116,10 @@ properties:
power-domains:
maxItems: 1
+ memory-region:
+ maxItems: 1
+ description: phandle to restricted DMA buffer
+
mediatek,pbus-csr:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
@@ -193,6 +198,7 @@ allOf:
contains:
enum:
- mediatek,mt8188-pcie
+ - mediatek,mt8189-pcie
- mediatek,mt8195-pcie
then:
properties:
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra264-pcie.yaml b/Documentation/devicetree/bindings/pci/nvidia,tegra264-pcie.yaml
index dc4f8725c9f5..f0114defc04e 100644
--- a/Documentation/devicetree/bindings/pci/nvidia,tegra264-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/nvidia,tegra264-pcie.yaml
@@ -10,32 +10,23 @@ maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
+description: |
+ Of the six PCIe controllers found on Tegra264, one (C0) is used for the
+ internal GPU and the other five (C1-C5) are routed to connectors such as
+ PCI or M.2 slots. Therefore the UPHY registers (XPL) exist only for C1
+ through C5, but not for C0.
+
properties:
compatible:
const: nvidia,tegra264-pcie
reg:
- description: |
- Of the six PCIe controllers found on Tegra264, one (C0) is used for the
- internal GPU and the other five (C1-C5) are routed to connectors such as
- PCI or M.2 slots. Therefore the UPHY registers (XPL) exist only for C1
- through C5, but not for C0.
minItems: 4
- items:
- - description: ECAM-compatible configuration space
- - description: application layer registers
- - description: transaction layer registers
- - description: privileged transaction layer registers
- - description: data link/physical layer registers (not available on C0)
+ maxItems: 5
reg-names:
minItems: 4
- items:
- - const: ecam
- - const: xal
- - const: xtl
- - const: xtl-pri
- - const: xpl
+ maxItems: 5
interrupts:
minItems: 1
@@ -61,6 +52,49 @@ properties:
- description: PCIe controller ID
maximum: 5
+patternProperties:
+ '^pcie@':
+ type: object
+ $ref: /schemas/pci/pci-pci-bridge.yaml#
+ unevaluatedProperties: false
+
+allOf:
+ - $ref: /schemas/pci/pci-host-bridge.yaml#
+ - oneOf:
+ - description: C0 controller (no UPHY)
+ properties:
+ reg:
+ items:
+ - description: application layer registers
+ - description: transaction layer registers
+ - description: privileged transaction layer registers
+ - description: ECAM compatible configuration space
+
+ reg-names:
+ items:
+ - const: xal
+ - const: xtl
+ - const: xtl-pri
+ - const: ecam
+
+ - description: C1-C5 controllers (with UPHY)
+ properties:
+ reg:
+ items:
+ - description: application layer registers
+ - description: transaction layer registers
+ - description: privileged transaction layer registers
+ - description: data link/physical layer registers
+ - description: ECAM compatible configuration space
+
+ reg-names:
+ items:
+ - const: xal
+ - const: xtl
+ - const: xtl-pri
+ - const: xpl
+ - const: ecam
+
required:
- interrupt-map
- interrupt-map-mask
@@ -68,9 +102,6 @@ required:
- msi-map
- nvidia,bpmp
-allOf:
- - $ref: /schemas/pci/pci-host-bridge.yaml#
-
unevaluatedProperties: false
examples:
@@ -81,11 +112,11 @@ examples:
pci@c000000 {
compatible = "nvidia,tegra264-pcie";
- reg = <0xd0 0xb0000000 0x0 0x10000000>,
- <0x00 0x0c000000 0x0 0x00004000>,
+ reg = <0x00 0x0c000000 0x0 0x00004000>,
<0x00 0x0c004000 0x0 0x00001000>,
- <0x00 0x0c005000 0x0 0x00001000>;
- reg-names = "ecam", "xal", "xtl", "xtl-pri";
+ <0x00 0x0c005000 0x0 0x00001000>,
+ <0xd0 0xb0000000 0x0 0x10000000>;
+ reg-names = "xal", "xtl", "xtl-pri", "ecam";
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
@@ -105,9 +136,18 @@ examples:
ranges = <0x81000000 0x00 0x84000000 0xd0 0x84000000 0x00 0x00200000>,
<0x82000000 0x00 0x20000000 0x00 0x20000000 0x00 0x08000000>,
<0xc3000000 0xd0 0xc0000000 0xd0 0xc0000000 0x07 0xc0000000>;
- bus-range = <0x0 0xff>;
nvidia,bpmp = <&bpmp 0>;
+
+ pcie@0 {
+ device_type = "pci";
+ compatible = "pciclass,0604";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
};
};
@@ -118,12 +158,12 @@ examples:
pci@8400000 {
compatible = "nvidia,tegra264-pcie";
- reg = <0xa8 0xb0000000 0x0 0x10000000>,
- <0x00 0x08400000 0x0 0x00004000>,
+ reg = <0x00 0x08400000 0x0 0x00004000>,
<0x00 0x08404000 0x0 0x00001000>,
<0x00 0x08405000 0x0 0x00001000>,
- <0x00 0x08410000 0x0 0x00010000>;
- reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl";
+ <0x00 0x08410000 0x0 0x00010000>,
+ <0xa8 0xb0000000 0x0 0x10000000>;
+ reg-names = "xal", "xtl", "xtl-pri", "xpl", "ecam";
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
@@ -142,8 +182,17 @@ examples:
ranges = <0x81000000 0x00 0x84000000 0xa8 0x84000000 0x00 0x00200000>,
<0x82000000 0x00 0x28000000 0x00 0x28000000 0x00 0x08000000>,
<0xc3000000 0xa8 0xc0000000 0xa8 0xc0000000 0x07 0xc0000000>;
- bus-range = <0x00 0xff>;
nvidia,bpmp = <&bpmp 1>;
+
+ pcie@0 {
+ device_type = "pci";
+ compatible = "pciclass,0604";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
};
};
diff --git a/Documentation/devicetree/bindings/pci/qcom,hawi-pcie.yaml b/Documentation/devicetree/bindings/pci/qcom,hawi-pcie.yaml
new file mode 100644
index 000000000000..aba89b599155
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,hawi-pcie.yaml
@@ -0,0 +1,201 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,hawi-pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Hawi PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+description:
+ Qualcomm Hawi SoC (and compatible) PCIe root complex controller is based on
+ the Synopsys DesignWare PCIe IP.
+
+properties:
+ compatible:
+ oneOf:
+ - const: qcom,hawi-pcie
+ - items:
+ - enum:
+ - qcom,maili-pcie
+ - const: qcom,hawi-pcie
+
+ reg:
+ items:
+ - description: Qualcomm specific registers
+ - description: DesignWare PCIe registers
+ - description: External local bus interface registers
+ - description: ATU address space
+ - description: PCIe configuration space
+ - description: MHI registers
+
+ reg-names:
+ items:
+ - const: parf
+ - const: dbi
+ - const: elbi
+ - const: atu
+ - const: config
+ - const: mhi
+
+ clocks:
+ items:
+ - description: PCIe Auxiliary clock
+ - description: PCIe Configuration clock
+ - description: PCIe Master AXI clock
+ - description: PCIe Slave AXI clock
+ - description: PCIe Slave Q2A AXI clock
+ - description: PCIe Aggre NoC AXI clock
+ - description: PCIe Config NoC AXI clock
+
+ clock-names:
+ items:
+ - const: aux
+ - const: cfg
+ - const: bus_master
+ - const: bus_slave
+ - const: slave_q2a
+ - const: noc_aggr
+ - const: cnoc_sf_axi
+
+ interrupts:
+ minItems: 9
+ maxItems: 9
+
+ interrupt-names:
+ items:
+ - const: msi0
+ - const: msi1
+ - const: msi2
+ - const: msi3
+ - const: msi4
+ - const: msi5
+ - const: msi6
+ - const: msi7
+ - const: global
+
+ resets:
+ items:
+ - description: PCIe core reset
+ - description: PCIe link down reset
+
+ reset-names:
+ items:
+ - const: pci
+ - const: link_down
+
+required:
+ - power-domains
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interconnect/qcom,icc.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@1c00000 {
+ compatible = "qcom,hawi-pcie";
+ reg = <0 0x01c00000 0 0x3000>,
+ <0 0x40000000 0 0xf1d>,
+ <0 0x40000f20 0 0xa8>,
+ <0 0x40001000 0 0x1000>,
+ <0 0x40100000 0 0x100000>,
+ <0 0x01c03000 0 0x1000>;
+ reg-names = "parf", "dbi", "elbi", "atu", "config", "mhi";
+ ranges = <0x01000000 0x0 0x00000000 0x0 0x40200000 0x0 0x100000>,
+ <0x02000000 0x0 0x40300000 0x0 0x40300000 0x0 0x3d00000>;
+
+ bus-range = <0x00 0xff>;
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ num-lanes = <2>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ clocks = <&gcc_pcie_0_aux_clk>,
+ <&gcc_pcie_0_cfg_ahb_clk>,
+ <&gcc_pcie_0_mstr_axi_clk>,
+ <&gcc_pcie_0_slv_axi_clk>,
+ <&gcc_pcie_0_slv_q2a_axi_clk>,
+ <&gcc_aggre_noc_pcie_axi_clk>,
+ <&gcc_cnoc_pcie_sf_axi_clk>;
+ clock-names = "aux",
+ "cfg",
+ "bus_master",
+ "bus_slave",
+ "slave_q2a",
+ "noc_aggr",
+ "cnoc_sf_axi";
+
+ dma-coherent;
+
+ interrupts = <GIC_ESPI 205 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 206 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 207 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 208 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 209 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 210 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 211 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 212 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 204 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi0", "msi1", "msi2", "msi3",
+ "msi4", "msi5", "msi6", "msi7", "global";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 0 GIC_ESPI 213 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc 0 0 GIC_ESPI 214 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc 0 0 GIC_ESPI 215 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc 0 0 GIC_ESPI 216 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ interconnects = <&pcie_anoc_master_pcie_0 QCOM_ICC_TAG_ALWAYS
+ &mc_virt_slave_ebi1 QCOM_ICC_TAG_ALWAYS>,
+ <&gem_noc_master_appss_proc QCOM_ICC_TAG_ACTIVE_ONLY
+ &cnoc_main_slave_pcie_0 QCOM_ICC_TAG_ACTIVE_ONLY>;
+ interconnect-names = "pcie-mem", "cpu-pcie";
+
+ iommu-map = <0x0 &apps_smmu 0x1000 0x1>,
+ <0x100 &apps_smmu 0x1001 0x1>;
+
+ pinctrl-0 = <&pcie0_default_state>;
+ pinctrl-names = "default";
+
+ power-domains = <&gcc_pcie_0_phy_gdsc>;
+
+ resets = <&gcc_pcie_0_bcr>,
+ <&gcc_pcie_0_link_down_bcr>;
+ reset-names = "pci", "link_down";
+
+ msi-map = <0x0 &gic_its 0x1000 0x1>,
+ <0x100 &gic_its 0x1001 0x1>;
+ msi-map-mask = <0xff00>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+
+ phys = <&pcie0_phy>;
+ wake-gpios = <&tlmm 104 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&tlmm 102 GPIO_ACTIVE_LOW>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml
index 4be342cc04e1..533079811881 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml
@@ -17,8 +17,10 @@ properties:
- qcom,pcie-ipq9574
- items:
- enum:
+ - qcom,pcie-ipq5210
- qcom,pcie-ipq5332
- qcom,pcie-ipq5424
+ - qcom,pcie-ipq9650
- const: qcom,pcie-ipq9574
reg:
@@ -82,6 +84,19 @@ required:
allOf:
- $ref: qcom,pcie-common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,pcie-ipq5210
+ - qcom,pcie-ipq9650
+ then:
+ properties:
+ interrupts:
+ minItems: 9
+ interrupt-names:
+ minItems: 9
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
index bb3f843c59d9..64f456c6a75a 100644
--- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
@@ -66,6 +66,9 @@ properties:
max-link-speed:
maximum: 4
+ msi-parent:
+ maxItems: 1
+
num-lanes:
maximum: 4
diff --git a/Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml b/Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml
index 065b7508d288..1f0ab9ccd1dd 100644
--- a/Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml
+++ b/Documentation/devicetree/bindings/pci/renesas,pci-rcar-gen2.yaml
@@ -155,7 +155,7 @@ examples:
#include <dt-bindings/clock/r8a7790-cpg-mssr.h>
#include <dt-bindings/power/r8a7790-sysc.h>
- pci@ee090000 {
+ pci@ee090000 {
compatible = "renesas,pci-r8a7790", "renesas,pci-rcar-gen2";
device_type = "pci";
reg = <0xee090000 0xc00>,
diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
index 90086909e921..09d78bd1a577 100644
--- a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
@@ -14,7 +14,7 @@ description: |
with PCIe Base Specification 4.0 and supports different link speeds
depending on the SoC variant:
- Gen2 (5 GT/s): RZ/G3S
- - Gen3 (8 GT/s): RZ/G3E, RZ/V2N
+ - Gen3 (8 GT/s): RZ/G3E, RZ/V2H(P), RZ/V2N
properties:
compatible:
@@ -22,6 +22,7 @@ properties:
- enum:
- renesas,r9a08g045-pcie # RZ/G3S
- renesas,r9a09g047-pcie # RZ/G3E
+ - renesas,r9a09g057-pcie # RZ/V2H(P)
- items:
- const: renesas,r9a09g056-pcie # RZ/V2N
- const: renesas,r9a09g047-pcie
@@ -139,7 +140,13 @@ properties:
- clkl1pm clock request state
- power off information in L2 state
- errors (fatal, non-fatal, correctable)
- $ref: /schemas/types.yaml#/definitions/phandle
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: Phandle to system controller
+ - description: PCIe controller index
+ enum: [0, 1]
+ minItems: 1
patternProperties:
"^pcie@0,[0-0]$":
@@ -220,7 +227,9 @@ allOf:
properties:
compatible:
contains:
- const: renesas,r9a09g047-pcie
+ enum:
+ - renesas,r9a09g047-pcie
+ - renesas,r9a09g057-pcie
then:
properties:
interrupts:
@@ -235,6 +244,25 @@ allOf:
maxItems: 1
reset-names:
maxItems: 1
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: renesas,r9a09g057-pcie
+ then:
+ properties:
+ num-lanes:
+ enum: [2, 4]
+ renesas,sysc:
+ items:
+ - minItems: 2
+ required:
+ - num-lanes
+ else:
+ properties:
+ renesas,sysc:
+ items:
+ - maxItems: 1
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml b/Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml
index 74195c1f5292..afde18c5a4fa 100644
--- a/Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml
+++ b/Documentation/devicetree/bindings/pci/ti,am65-pci-ep.yaml
@@ -63,10 +63,10 @@ examples:
pcie0_ep: pcie-ep@5500000 {
compatible = "ti,am654-pcie-ep";
- reg = <0x5500000 0x1000>,
- <0x5501000 0x1000>,
- <0x10000000 0x8000000>,
- <0x5506000 0x1000>;
+ reg = <0x5500000 0x1000>,
+ <0x5501000 0x1000>,
+ <0x10000000 0x8000000>,
+ <0x5506000 0x1000>;
reg-names = "app", "dbics", "addr_space", "atu";
power-domains = <&k3_pds 120 TI_SCI_PD_EXCLUSIVE>;
ti,syscon-pcie-mode = <&scm_conf 0x4060>;
diff --git a/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml b/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml
index 98f6c7f1b1a6..db92125a2f2d 100644
--- a/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/ti,am65-pci-host.yaml
@@ -121,12 +121,12 @@ examples:
pcie0_rc: pcie@5500000 {
compatible = "ti,am654-pcie-rc";
- reg = <0x5500000 0x1000>,
- <0x5501000 0x1000>,
- <0x10000000 0x2000>,
- <0x5506000 0x1000>,
- <0x2900000 0x1000>,
- <0x2908000 0x1000>;
+ reg = <0x5500000 0x1000>,
+ <0x5501000 0x1000>,
+ <0x10000000 0x2000>,
+ <0x5506000 0x1000>,
+ <0x2900000 0x1000>,
+ <0x2908000 0x1000>;
reg-names = "app", "dbics", "config", "atu", "vmap_lp", "vmap_hp";
power-domains = <&k3_pds 120 TI_SCI_PD_EXCLUSIVE>;
#address-cells = <3>;
diff --git a/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml b/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
index b3ad05d90201..f084830c6d0c 100644
--- a/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
+++ b/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
@@ -54,7 +54,7 @@ properties:
- description: I2C slave address
patternProperties:
- "^pcie@[1-3],0$":
+ "^pcie@[1-2],0$":
description:
child nodes describing the internal downstream ports of
the tc9563 switch.
@@ -64,6 +64,21 @@ patternProperties:
- $ref: /schemas/pci/pci-pci-bridge.yaml#
unevaluatedProperties: false
+ "^pcie@[3],0$":
+ description:
+ child node describing the internal downstream port connected to the
+ integrated Ethernet MAC endpoint of the tc9563 switch.
+ type: object
+ $ref: /schemas/pci/pci-pci-bridge.yaml#
+ properties:
+ n-fts: false
+ patternProperties:
+ "^ethernet@0,[0-1]$":
+ type: object
+ properties:
+ n-fts: false
+ unevaluatedProperties: false
+
$defs:
tc9563-node:
type: object
@@ -154,6 +169,8 @@ examples:
device_type = "pci";
ranges;
bus-range = <0x04 0xff>;
+
+ toshiba,tx-amplitude-microvolt = <10>;
};
pcie@3,0 {
@@ -165,8 +182,6 @@ examples:
ranges;
bus-range = <0x05 0xff>;
- toshiba,tx-amplitude-microvolt = <10>;
-
ethernet@0,0 {
reg = <0x50000 0x0 0x0 0x0 0x0>;
};
diff --git a/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml b/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml
index 512b935bf5d1..d9d1488e915a 100644
--- a/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml
@@ -43,6 +43,18 @@ properties:
- description: Legacy INTC interrupt
- description: Legacy INTD interrupt
+ clocks:
+ items:
+ - description: PCIe core clock
+ - description: Data Bus Interface (DBI) clock
+ - description: Auxiliary clock
+
+ clock-names:
+ items:
+ - const: core
+ - const: dbi
+ - const: aux
+
interrupt-names:
items:
- const: msi
@@ -55,6 +67,8 @@ required:
- compatible
- reg
- reg-names
+ - clocks
+ - clock-names
- interrupts
- interrupt-names
@@ -71,6 +85,8 @@ examples:
reg = <0x0 0x21000000 0x0 0x01000000>,
<0x0 0x4fff0000 0x0 0x00010000>;
reg-names = "dbi", "config";
+ clocks = <&clkc 9>, <&clkc 7>, <&clkc 10>;
+ clock-names = "core", "dbi", "aux";
ranges = <0x81000000 0x0 0x4fbf0000 0x0 0x4fbf0000 0x0 0x00400000>,
<0x82000000 0x0 0x40000000 0x0 0x40000000 0x0 0x0fbf0000>,
<0xc3000000 0x40 0x00000000 0x40 0x00000000 0xd 0x00000000>;
diff --git a/MAINTAINERS b/MAINTAINERS
index 76f9cc4e62a7..cae7da554b5e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20819,8 +20819,8 @@ M: Thierry Reding <thierry.reding@kernel.org>
L: linux-tegra@vger.kernel.org
L: linux-pci@vger.kernel.org
S: Supported
-F: Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
-F: drivers/pci/controller/pci-tegra.c
+F: Documentation/devicetree/bindings/pci/*tegra*
+F: drivers/pci/controller/*tegra*
PCI DRIVER FOR NXP LAYERSCAPE GEN4 CONTROLLER
M: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
@@ -20961,6 +20961,7 @@ B: https://bugzilla.kernel.org
C: irc://irc.oftc.net/linux-pci
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
F: Documentation/ABI/testing/debugfs-pcie-ptm
+F: Documentation/PCI/controller/
F: Documentation/devicetree/bindings/pci/
F: Documentation/trace/events-pci-controller.rst
F: drivers/pci/controller/
@@ -21123,6 +21124,7 @@ M: Manivannan Sadhasivam <mani@kernel.org>
L: linux-pci@vger.kernel.org
L: linux-arm-msm@vger.kernel.org
S: Maintained
+F: Documentation/devicetree/bindings/pci/qcom,*.yaml
F: drivers/pci/controller/dwc/pcie-qcom-common.c
F: drivers/pci/controller/dwc/pcie-qcom.c
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
index 94dbc470cd6c..67f9822f7626 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -102,25 +102,25 @@ static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj,
return pci_mmap_resource(kobj, attr, vma, 0);
}
-#define __pci_dev_resource_attr(_bar, _name, _suffix, _mmap) \
-static const struct bin_attribute \
-pci_dev_resource##_bar##_suffix##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0600 }, \
- .private = (void *)(unsigned long)(_bar), \
- .mmap = (_mmap), \
+#define __pci_dev_resource_attr(_suffix, _bar, _name, _mmap) \
+static const struct bin_attribute \
+pci_dev_resource##_bar##_suffix##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0600 }, \
+ .private = (void *)(unsigned long)(_bar), \
+ .mmap = (_mmap), \
}
-#define pci_dev_resource_attr(_bar) \
- __pci_dev_resource_attr(_bar, resource##_bar,, \
- pci_mmap_resource_dense)
+#define pci_dev_resource_attr(_bar) \
+ __pci_dev_resource_attr(, _bar, resource##_bar, \
+ pci_mmap_resource_dense)
#define pci_dev_resource_sparse_attr(_bar) \
- __pci_dev_resource_attr(_bar, resource##_bar##_sparse, _sparse, \
- pci_mmap_resource_sparse)
+ __pci_dev_resource_attr(_sparse, _bar, resource##_bar##_sparse, \
+ pci_mmap_resource_sparse)
#define pci_dev_resource_dense_attr(_bar) \
- __pci_dev_resource_attr(_bar, resource##_bar##_dense, _dense, \
- pci_mmap_resource_dense)
+ __pci_dev_resource_attr(_dense, _bar, resource##_bar##_dense, \
+ pci_mmap_resource_dense)
static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
{
@@ -224,17 +224,17 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
switch(size) {
case 1:
- outb(port, val);
+ outb(val, port);
return 1;
case 2:
if (port & 1)
return -EINVAL;
- outw(port, val);
+ outw(val, port);
return 2;
case 4:
if (port & 3)
return -EINVAL;
- outl(port, val);
+ outl(val, port);
return 4;
}
return -EINVAL;
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index a269f73b7653..b5a51b0ef944 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1427,6 +1427,15 @@ config NVIDIA_OLYMPUS_1027_ERRATUM
If unsure, say Y.
+config RENESAS_ERRATUM_GEN4GICITS1
+ bool "Renesas R-Car Gen4: GIC600 can not access physical addresses above 4 GiB"
+ default y
+ help
+ The Renesas R-Car Gen4 S4/V4H/V4M GIC600 SoC integrations have AXI
+ addressing limited to the first 32-bit of physical address space.
+
+ If unsure, say Y.
+
config ROCKCHIP_ERRATUM_3568002
bool "Rockchip 3568002: GIC600 can not access physical addresses higher than 4GB"
default y
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 3c4ca90e2ab7..4fc52c21fe5d 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -626,19 +626,14 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
return -ENXIO;
addr = hose->io_base_virt + port;
- /* WARNING: The generic code is idiotic. It gets passed a pointer
- * to what can be a 1, 2 or 4 byte quantity and always reads that
- * as a u32, which means that we have to correct the location of
- * the data read within those 32 bits for size 1 and 2
- */
switch(size) {
case 1:
- out_8(addr, val >> 24);
+ out_8(addr, val);
return 1;
case 2:
if (port & 1)
return -EINVAL;
- out_le16(addr, val >> 16);
+ out_le16(addr, val);
return 2;
case 4:
if (port & 3)
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index e70b6c089cf1..18ea740ca582 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -51,11 +51,11 @@ enum {
static int generic_set_mode(struct ata_link *link, struct ata_device **unused)
{
struct ata_port *ap = link->ap;
- const struct pci_device_id *id = ap->host->private_data;
+ unsigned long driver_data = (unsigned long)ap->host->private_data;
int dma_enabled = 0;
struct ata_device *dev;
- if (id->driver_data & ATA_GEN_FORCE_DMA) {
+ if (driver_data & ATA_GEN_FORCE_DMA) {
dma_enabled = 0xff;
} else if (ap->ioaddr.bmdma_addr) {
/* Bits 5 and 6 indicate if DMA is active on master/slave */
@@ -206,7 +206,7 @@ static int ata_generic_init_one(struct pci_dev *dev, const struct pci_device_id
return rc;
pcim_pin_device(dev);
}
- return ata_pci_bmdma_init_one(dev, ppi, &generic_sht, (void *)id, 0);
+ return ata_pci_bmdma_init_one(dev, ppi, &generic_sht, (void *)id->driver_data, 0);
}
static const struct pci_device_id ata_generic[] = {
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c
index 898ff30ffd46..4d201e71c517 100644
--- a/drivers/char/agp/amd-k7-agp.c
+++ b/drivers/char/agp/amd-k7-agp.c
@@ -387,37 +387,17 @@ static const struct agp_bridge_driver amd_irongate_driver = {
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
};
-static struct agp_device_ids amd_agp_device_ids[] =
-{
- {
- .device_id = PCI_DEVICE_ID_AMD_FE_GATE_7006,
- .chipset_name = "Irongate",
- },
- {
- .device_id = PCI_DEVICE_ID_AMD_FE_GATE_700E,
- .chipset_name = "761",
- },
- {
- .device_id = PCI_DEVICE_ID_AMD_FE_GATE_700C,
- .chipset_name = "760MP",
- },
- { }, /* dummy final entry, always present */
-};
-
static int agp_amdk7_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct agp_bridge_data *bridge;
u8 cap_ptr;
- int j;
cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
if (!cap_ptr)
return -ENODEV;
- j = ent - agp_amdk7_pci_table;
- dev_info(&pdev->dev, "AMD %s chipset\n",
- amd_agp_device_ids[j].chipset_name);
+ dev_info(&pdev->dev, "AMD %s chipset\n", (const char *)ent->driver_data);
bridge = agp_alloc_bridge();
if (!bridge)
@@ -492,7 +472,6 @@ static int agp_amdk7_resume(struct device *dev)
return amd_irongate_driver.configure();
}
-/* must be the same order as name table above */
static const struct pci_device_id agp_amdk7_pci_table[] = {
{
.class = (PCI_CLASS_BRIDGE_HOST << 8),
@@ -501,6 +480,7 @@ static const struct pci_device_id agp_amdk7_pci_table[] = {
.device = PCI_DEVICE_ID_AMD_FE_GATE_7006,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)"Irongate",
},
{
.class = (PCI_CLASS_BRIDGE_HOST << 8),
@@ -509,6 +489,7 @@ static const struct pci_device_id agp_amdk7_pci_table[] = {
.device = PCI_DEVICE_ID_AMD_FE_GATE_700E,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)"761",
},
{
.class = (PCI_CLASS_BRIDGE_HOST << 8),
@@ -517,6 +498,7 @@ static const struct pci_device_id agp_amdk7_pci_table[] = {
.device = PCI_DEVICE_ID_AMD_FE_GATE_700C,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)"760MP",
},
{ }
};
diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c
index 8b19a5d1a09b..ab3b73dd080a 100644
--- a/drivers/char/agp/via-agp.c
+++ b/drivers/char/agp/via-agp.c
@@ -221,204 +221,6 @@ static const struct agp_bridge_driver via_driver = {
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
};
-static struct agp_device_ids via_agp_device_ids[] =
-{
- {
- .device_id = PCI_DEVICE_ID_VIA_82C597_0,
- .chipset_name = "Apollo VP3",
- },
-
- {
- .device_id = PCI_DEVICE_ID_VIA_82C598_0,
- .chipset_name = "Apollo MVP3",
- },
-
- {
- .device_id = PCI_DEVICE_ID_VIA_8501_0,
- .chipset_name = "Apollo MVP4",
- },
-
- /* VT8601 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8601_0,
- .chipset_name = "Apollo ProMedia/PLE133Ta",
- },
-
- /* VT82C693A / VT28C694T */
- {
- .device_id = PCI_DEVICE_ID_VIA_82C691_0,
- .chipset_name = "Apollo Pro 133",
- },
-
- {
- .device_id = PCI_DEVICE_ID_VIA_8371_0,
- .chipset_name = "KX133",
- },
-
- /* VT8633 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8633_0,
- .chipset_name = "Pro 266",
- },
-
- {
- .device_id = PCI_DEVICE_ID_VIA_XN266,
- .chipset_name = "Apollo Pro266",
- },
-
- /* VT8361 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8361,
- .chipset_name = "KLE133",
- },
-
- /* VT8365 / VT8362 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8363_0,
- .chipset_name = "Twister-K/KT133x/KM133",
- },
-
- /* VT8753A */
- {
- .device_id = PCI_DEVICE_ID_VIA_8753_0,
- .chipset_name = "P4X266",
- },
-
- /* VT8366 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8367_0,
- .chipset_name = "KT266/KY266x/KT333",
- },
-
- /* VT8633 (for CuMine/ Celeron) */
- {
- .device_id = PCI_DEVICE_ID_VIA_8653_0,
- .chipset_name = "Pro266T",
- },
-
- /* KM266 / PM266 */
- {
- .device_id = PCI_DEVICE_ID_VIA_XM266,
- .chipset_name = "PM266/KM266",
- },
-
- /* CLE266 */
- {
- .device_id = PCI_DEVICE_ID_VIA_862X_0,
- .chipset_name = "CLE266",
- },
-
- {
- .device_id = PCI_DEVICE_ID_VIA_8377_0,
- .chipset_name = "KT400/KT400A/KT600",
- },
-
- /* VT8604 / VT8605 / VT8603
- * (Apollo Pro133A chipset with S3 Savage4) */
- {
- .device_id = PCI_DEVICE_ID_VIA_8605_0,
- .chipset_name = "ProSavage PM133/PL133/PN133"
- },
-
- /* P4M266x/P4N266 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8703_51_0,
- .chipset_name = "P4M266x/P4N266",
- },
-
- /* VT8754 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8754C_0,
- .chipset_name = "PT800",
- },
-
- /* P4X600 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8763_0,
- .chipset_name = "P4X600"
- },
-
- /* KM400 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8378_0,
- .chipset_name = "KM400/KM400A",
- },
-
- /* PT880 */
- {
- .device_id = PCI_DEVICE_ID_VIA_PT880,
- .chipset_name = "PT880",
- },
-
- /* PT880 Ultra */
- {
- .device_id = PCI_DEVICE_ID_VIA_PT880ULTRA,
- .chipset_name = "PT880 Ultra",
- },
-
- /* PT890 */
- {
- .device_id = PCI_DEVICE_ID_VIA_8783_0,
- .chipset_name = "PT890",
- },
-
- /* PM800/PN800/PM880/PN880 */
- {
- .device_id = PCI_DEVICE_ID_VIA_PX8X0_0,
- .chipset_name = "PM800/PN800/PM880/PN880",
- },
- /* KT880 */
- {
- .device_id = PCI_DEVICE_ID_VIA_3269_0,
- .chipset_name = "KT880",
- },
- /* KTxxx/Px8xx */
- {
- .device_id = PCI_DEVICE_ID_VIA_83_87XX_1,
- .chipset_name = "VT83xx/VT87xx/KTxxx/Px8xx",
- },
- /* P4M800 */
- {
- .device_id = PCI_DEVICE_ID_VIA_3296_0,
- .chipset_name = "P4M800",
- },
- /* P4M800CE */
- {
- .device_id = PCI_DEVICE_ID_VIA_P4M800CE,
- .chipset_name = "VT3314",
- },
- /* VT3324 / CX700 */
- {
- .device_id = PCI_DEVICE_ID_VIA_VT3324,
- .chipset_name = "CX700",
- },
- /* VT3336 - this is a chipset for AMD Athlon/K8 CPU. Due to K8's unique
- * architecture, the AGP resource and behavior are different from
- * the traditional AGP which resides only in chipset. AGP is used
- * by 3D driver which wasn't available for the VT3336 and VT3364
- * generation until now. Unfortunately, by testing, VT3364 works
- * but VT3336 doesn't. - explanation from via, just leave this as
- * as a placeholder to avoid future patches adding it back in.
- */
-#if 0
- {
- .device_id = PCI_DEVICE_ID_VIA_VT3336,
- .chipset_name = "VT3336",
- },
-#endif
- /* P4M890 */
- {
- .device_id = PCI_DEVICE_ID_VIA_P4M890,
- .chipset_name = "P4M890",
- },
- /* P4M900 */
- {
- .device_id = PCI_DEVICE_ID_VIA_VT3364,
- .chipset_name = "P4M900",
- },
- { }, /* dummy final entry, always present */
-};
-
/*
* VIA's AGP3 chipsets do magick to put the AGP bridge compliant
@@ -437,17 +239,14 @@ static void check_via_agp3 (struct agp_bridge_data *bridge)
static int agp_via_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
- struct agp_device_ids *devs = via_agp_device_ids;
struct agp_bridge_data *bridge;
- int j = 0;
u8 cap_ptr;
cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
if (!cap_ptr)
return -ENODEV;
- j = ent - agp_via_pci_table;
- printk (KERN_INFO PFX "Detected VIA %s chipset\n", devs[j].chipset_name);
+ dev_info(&pdev->dev, "Detected VIA %s chipset\n", (const char *)ent->driver_data);
bridge = agp_alloc_bridge();
if (!bridge)
@@ -501,9 +300,8 @@ static int agp_via_resume(struct device *dev)
return 0;
}
-/* must be the same order as name table above */
static const struct pci_device_id agp_via_pci_table[] = {
-#define ID(x) \
+#define ID(x, name) \
{ \
.class = (PCI_CLASS_BRIDGE_HOST << 8), \
.class_mask = ~0, \
@@ -511,39 +309,77 @@ static const struct pci_device_id agp_via_pci_table[] = {
.device = x, \
.subvendor = PCI_ANY_ID, \
.subdevice = PCI_ANY_ID, \
+ .driver_data = (kernel_ulong_t)name, \
}
- ID(PCI_DEVICE_ID_VIA_82C597_0),
- ID(PCI_DEVICE_ID_VIA_82C598_0),
- ID(PCI_DEVICE_ID_VIA_8501_0),
- ID(PCI_DEVICE_ID_VIA_8601_0),
- ID(PCI_DEVICE_ID_VIA_82C691_0),
- ID(PCI_DEVICE_ID_VIA_8371_0),
- ID(PCI_DEVICE_ID_VIA_8633_0),
- ID(PCI_DEVICE_ID_VIA_XN266),
- ID(PCI_DEVICE_ID_VIA_8361),
- ID(PCI_DEVICE_ID_VIA_8363_0),
- ID(PCI_DEVICE_ID_VIA_8753_0),
- ID(PCI_DEVICE_ID_VIA_8367_0),
- ID(PCI_DEVICE_ID_VIA_8653_0),
- ID(PCI_DEVICE_ID_VIA_XM266),
- ID(PCI_DEVICE_ID_VIA_862X_0),
- ID(PCI_DEVICE_ID_VIA_8377_0),
- ID(PCI_DEVICE_ID_VIA_8605_0),
- ID(PCI_DEVICE_ID_VIA_8703_51_0),
- ID(PCI_DEVICE_ID_VIA_8754C_0),
- ID(PCI_DEVICE_ID_VIA_8763_0),
- ID(PCI_DEVICE_ID_VIA_8378_0),
- ID(PCI_DEVICE_ID_VIA_PT880),
- ID(PCI_DEVICE_ID_VIA_PT880ULTRA),
- ID(PCI_DEVICE_ID_VIA_8783_0),
- ID(PCI_DEVICE_ID_VIA_PX8X0_0),
- ID(PCI_DEVICE_ID_VIA_3269_0),
- ID(PCI_DEVICE_ID_VIA_83_87XX_1),
- ID(PCI_DEVICE_ID_VIA_3296_0),
- ID(PCI_DEVICE_ID_VIA_P4M800CE),
- ID(PCI_DEVICE_ID_VIA_VT3324),
- ID(PCI_DEVICE_ID_VIA_P4M890),
- ID(PCI_DEVICE_ID_VIA_VT3364),
+ ID(PCI_DEVICE_ID_VIA_82C597_0, "Apollo VP3"),
+ ID(PCI_DEVICE_ID_VIA_82C598_0, "Apollo MVP3"),
+ ID(PCI_DEVICE_ID_VIA_8501_0, "Apollo MVP4"),
+ /* VT8601 */
+ ID(PCI_DEVICE_ID_VIA_8601_0, "Apollo ProMedia/PLE133Ta"),
+ /* VT82C693A / VT28C694T */
+ ID(PCI_DEVICE_ID_VIA_82C691_0, "Apollo Pro 133"),
+ ID(PCI_DEVICE_ID_VIA_8371_0, "KX133"),
+ /* VT8633 */
+ ID(PCI_DEVICE_ID_VIA_8633_0, "Pro 266"),
+ ID(PCI_DEVICE_ID_VIA_XN266, "Apollo Pro266"),
+ /* VT8361 */
+ ID(PCI_DEVICE_ID_VIA_8361, "KLE133"),
+ /* VT8365 / VT8362 */
+ ID(PCI_DEVICE_ID_VIA_8363_0, "Twister-K/KT133x/KM133"),
+ /* VT8753A */
+ ID(PCI_DEVICE_ID_VIA_8753_0, "P4X266"),
+ /* VT8366 */
+ ID(PCI_DEVICE_ID_VIA_8367_0, "KT266/KY266x/KT333"),
+ /* VT8633 (for CuMine/ Celeron) */
+ ID(PCI_DEVICE_ID_VIA_8653_0, "Pro266T"),
+ /* KM266 / PM266 */
+ ID(PCI_DEVICE_ID_VIA_XM266, "PM266/KM266"),
+ /* CLE266 */
+ ID(PCI_DEVICE_ID_VIA_862X_0, "CLE266"),
+ ID(PCI_DEVICE_ID_VIA_8377_0, "KT400/KT400A/KT600"),
+ /* VT8604 / VT8605 / VT8603 (Apollo Pro133A chipset with S3 Savage4) */
+ ID(PCI_DEVICE_ID_VIA_8605_0, "ProSavage PM133/PL133/PN133"),
+ /* P4M266x/P4N266 */
+ ID(PCI_DEVICE_ID_VIA_8703_51_0, "P4M266x/P4N266"),
+ /* VT8754 */
+ ID(PCI_DEVICE_ID_VIA_8754C_0, "PT800"),
+ /* P4X600 */
+ ID(PCI_DEVICE_ID_VIA_8763_0, "P4X600"),
+ /* KM400 */
+ ID(PCI_DEVICE_ID_VIA_8378_0, "KM400/KM400A"),
+ /* PT880 */
+ ID(PCI_DEVICE_ID_VIA_PT880, "PT880"),
+ /* PT880 Ultra */
+ ID(PCI_DEVICE_ID_VIA_PT880ULTRA, "PT880 Ultra"),
+ /* PT890 */
+ ID(PCI_DEVICE_ID_VIA_8783_0, "PT890"),
+ /* PM800/PN800/PM880/PN880 */
+ ID(PCI_DEVICE_ID_VIA_PX8X0_0, "PM800/PN800/PM880/PN880"),
+ /* KT880 */
+ ID(PCI_DEVICE_ID_VIA_3269_0, "KT880"),
+ /* KTxxx/Px8xx */
+ ID(PCI_DEVICE_ID_VIA_83_87XX_1, "VT83xx/VT87xx/KTxxx/Px8xx"),
+ /* P4M800 */
+ ID(PCI_DEVICE_ID_VIA_3296_0, "P4M800"),
+ /* P4M800CE */
+ ID(PCI_DEVICE_ID_VIA_P4M800CE, "VT3314"),
+ /* VT3324 / CX700 */
+ ID(PCI_DEVICE_ID_VIA_VT3324, "CX700"),
+ /* VT3336 - this is a chipset for AMD Athlon/K8 CPU. Due to K8's unique
+ * architecture, the AGP resource and behavior are different from
+ * the traditional AGP which resides only in chipset. AGP is used
+ * by 3D driver which wasn't available for the VT3336 and VT3364
+ * generation until now. Unfortunately, by testing, VT3364 works
+ * but VT3336 doesn't. - explanation from via, just leave this as
+ * a placeholder to avoid future patches adding it back in.
+ */
+#if 0
+ ID(PCI_DEVICE_ID_VIA_VT3336, "VT3336"),
+#endif
+ /* P4M890 */
+ ID(PCI_DEVICE_ID_VIA_P4M890, "P4M890"),
+ /* P4M900 */
+ ID(PCI_DEVICE_ID_VIA_VT3364, "P4M900"),
{ }
};
diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c
index 71d9868ce613..c133535d3765 100644
--- a/drivers/dma/switchtec_dma.c
+++ b/drivers/dma/switchtec_dma.c
@@ -1424,6 +1424,7 @@ static const struct pci_device_id switchtec_dma_pci_tbl[] = {
SW_ID(PCI_VENDOR_ID_EFAR, 0x1004), /* PCI1004 16XG4 */
SW_ID(PCI_VENDOR_ID_EFAR, 0x1005), /* PCI1005 16XG4 */
SW_ID(PCI_VENDOR_ID_EFAR, 0x1006), /* PCI1006 16XG4 */
+ SW_ID(PCI_VENDOR_ID_EFAR, 0x1008), /* PCI1008 16XG4 */
{0}
};
MODULE_DEVICE_TABLE(pci, switchtec_dma_pci_tbl);
diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c
index 05dcb6675cd6..1cf51f763293 100644
--- a/drivers/ipack/carriers/tpci200.c
+++ b/drivers/ipack/carriers/tpci200.c
@@ -562,7 +562,6 @@ static int tpci200_pci_probe(struct pci_dev *pdev,
/* Save struct pci_dev pointer */
tpci200->info->pdev = pdev;
- tpci200->info->id_table = (struct pci_device_id *)id;
/* register the device and initialize it */
ret = tpci200_install(tpci200);
diff --git a/drivers/ipack/carriers/tpci200.h b/drivers/ipack/carriers/tpci200.h
index e79ac64abcff..a2bf3125794b 100644
--- a/drivers/ipack/carriers/tpci200.h
+++ b/drivers/ipack/carriers/tpci200.h
@@ -145,7 +145,6 @@ struct tpci200_slot {
*/
struct tpci200_infos {
struct pci_dev *pdev;
- struct pci_device_id *id_table;
struct tpci200_regs __iomem *interface_regs;
void __iomem *cfg_regs;
struct ipack_bus_device *ipack_bus;
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index ac697e60a2f9..e9807af23537 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -4894,10 +4894,22 @@ static bool __maybe_unused its_enable_quirk_hip09_162100801(void *data)
return true;
}
-static bool __maybe_unused its_enable_rk3568002(void *data)
+static const char * const dma_32bit_impaired_platforms[] = {
+#ifdef CONFIG_RENESAS_ERRATUM_GEN4GICITS1
+ "renesas,r8a779f0",
+ "renesas,r8a779g0",
+ "renesas,r8a779h0",
+#endif
+#ifdef CONFIG_ROCKCHIP_ERRATUM_3568002
+ "rockchip,rk3566",
+ "rockchip,rk3568",
+#endif
+ NULL,
+};
+
+static bool its_enable_dma32(void *data)
{
- if (!of_machine_is_compatible("rockchip,rk3566") &&
- !of_machine_is_compatible("rockchip,rk3568"))
+ if (!of_machine_compatible_match(dma_32bit_impaired_platforms))
return false;
gfp_flags_quirk |= GFP_DMA32;
@@ -4972,14 +4984,12 @@ static const struct gic_quirk its_quirks[] = {
.property = "dma-noncoherent",
.init = its_set_non_coherent,
},
-#ifdef CONFIG_ROCKCHIP_ERRATUM_3568002
{
- .desc = "ITS: Rockchip erratum RK3568002",
+ .desc = "ITS: Broken GIC600 integration limited to 32bit PA",
.iidr = 0x0201743b,
.mask = 0xffffffff,
- .init = its_enable_rk3568002,
+ .init = its_enable_dma32,
},
-#endif
{
}
};
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 3635741c3e7a..7686d777a27e 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -1071,6 +1071,7 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
struct pci_dev *pdev = test->pdev;
struct device *dev = &pdev->dev;
int irq_type = test->irq_type;
+ int ret = 0;
enum pci_barno bar;
u32 data, status;
u32 addr;
@@ -1093,7 +1094,7 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
left = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));
status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
- if (!left || (status & STATUS_DOORBELL_ENABLE_FAIL)) {
+ if (!left || !(status & STATUS_DOORBELL_ENABLE_SUCCESS)) {
dev_err(dev, "Failed to enable doorbell\n");
return -EINVAL;
}
@@ -1119,8 +1120,11 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
- if (!left || !(status & STATUS_DOORBELL_SUCCESS))
+ if (!left || !(status & STATUS_DOORBELL_SUCCESS)) {
dev_err(dev, "Failed to trigger doorbell in endpoint\n");
+ /* Store error code, but continue to disable doorbell. */
+ ret = -EINVAL;
+ }
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
COMMAND_DISABLE_DOORBELL);
@@ -1129,15 +1133,12 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
status |= pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
- if (status & STATUS_DOORBELL_DISABLE_FAIL) {
+ if (!(status & STATUS_DOORBELL_DISABLE_SUCCESS)) {
dev_err(dev, "Failed to disable doorbell\n");
return -EINVAL;
}
- if (!(status & STATUS_DOORBELL_SUCCESS))
- return -EINVAL;
-
- return 0;
+ return ret;
}
static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
@@ -1325,6 +1326,8 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev,
misc_device->parent = &pdev->dev;
misc_device->fops = &pci_endpoint_test_fops;
+ pci_save_state(pdev);
+
ret = misc_register(misc_device);
if (ret) {
dev_err(dev, "Failed to register device\n");
@@ -1452,12 +1455,33 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
};
MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
+static pci_ers_result_t pci_endpoint_test_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ if (state == pci_channel_io_perm_failure)
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t pci_endpoint_test_slot_reset(struct pci_dev *pdev)
+{
+ pci_restore_state(pdev);
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static const struct pci_error_handlers pci_endpoint_test_err_handler = {
+ .error_detected = pci_endpoint_test_error_detected,
+ .slot_reset = pci_endpoint_test_slot_reset,
+};
+
static struct pci_driver pci_endpoint_test_driver = {
.name = DRV_MODULE_NAME,
.id_table = pci_endpoint_test_tbl,
.probe = pci_endpoint_test_probe,
.remove = pci_endpoint_test_remove,
.sriov_configure = pci_sriov_configure_simple,
+ .err_handler = &pci_endpoint_test_err_handler,
};
module_pci_driver(pci_endpoint_test_driver);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c
index 0da85d36647d..bfe3268dfdc1 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c
@@ -130,7 +130,6 @@ struct mlxsw_pci {
} comp;
} cmd;
struct mlxsw_bus_info bus_info;
- const struct pci_device_id *id;
enum mlxsw_pci_cqe_v max_cqe_ver; /* Maximal supported CQE version */
u8 num_cqs; /* Number of CQs */
u8 num_sdqs; /* Number of SDQs */
@@ -1768,7 +1767,6 @@ static void mlxsw_pci_mbox_free(struct mlxsw_pci *mlxsw_pci,
}
static int mlxsw_pci_sys_ready_wait(struct mlxsw_pci *mlxsw_pci,
- const struct pci_device_id *id,
u32 *p_sys_status)
{
unsigned long end;
@@ -1839,7 +1837,7 @@ static int mlxsw_pci_reset_sw(struct mlxsw_pci *mlxsw_pci)
}
static int
-mlxsw_pci_reset(struct mlxsw_pci *mlxsw_pci, const struct pci_device_id *id)
+mlxsw_pci_reset(struct mlxsw_pci *mlxsw_pci)
{
struct pci_dev *pdev = mlxsw_pci->pdev;
bool pci_reset_sbr_supported = false;
@@ -1848,7 +1846,7 @@ mlxsw_pci_reset(struct mlxsw_pci *mlxsw_pci, const struct pci_device_id *id)
u32 sys_status;
int err;
- err = mlxsw_pci_sys_ready_wait(mlxsw_pci, id, &sys_status);
+ err = mlxsw_pci_sys_ready_wait(mlxsw_pci, &sys_status);
if (err) {
dev_err(&pdev->dev, "Failed to reach system ready status before reset. Status is 0x%x\n",
sys_status);
@@ -1880,7 +1878,7 @@ mlxsw_pci_reset(struct mlxsw_pci *mlxsw_pci, const struct pci_device_id *id)
if (err)
return err;
- err = mlxsw_pci_sys_ready_wait(mlxsw_pci, id, &sys_status);
+ err = mlxsw_pci_sys_ready_wait(mlxsw_pci, &sys_status);
if (err) {
dev_err(&pdev->dev, "Failed to reach system ready status after reset. Status is 0x%x\n",
sys_status);
@@ -1932,7 +1930,7 @@ static int mlxsw_pci_init(void *bus_priv, struct mlxsw_core *mlxsw_core,
if (!mbox)
return -ENOMEM;
- err = mlxsw_pci_reset(mlxsw_pci, mlxsw_pci->id);
+ err = mlxsw_pci_reset(mlxsw_pci);
if (err)
goto err_reset;
@@ -2464,7 +2462,6 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mlxsw_pci->bus_info.device_name = pci_name(mlxsw_pci->pdev);
mlxsw_pci->bus_info.dev = &pdev->dev;
mlxsw_pci->bus_info.read_clock_capable = true;
- mlxsw_pci->id = id;
err = mlxsw_core_bus_device_register(&mlxsw_pci->bus_info,
&mlxsw_pci_bus, mlxsw_pci, false,
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 2247709ef6d6..d246bbe37948 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -209,7 +209,7 @@ config PCI_MVEBU
config PCIE_MEDIATEK
tristate "MediaTek PCIe controller"
- depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST
+ depends on ARCH_AIROHA || ARCH_MEDIATEK || ECONET || COMPILE_TEST
depends on OF
depends on PCI_MSI
select IRQ_MSI_LIB
@@ -255,7 +255,15 @@ config PCI_TEGRA
select IRQ_MSI_LIB
help
Say Y here if you want support for the PCIe host controller found
- on NVIDIA Tegra SoCs.
+ on NVIDIA Tegra SoCs (Tegra20 through Tegra186).
+
+config PCIE_TEGRA264
+ tristate "NVIDIA Tegra264 PCIe controller"
+ depends on ARCH_TEGRA || COMPILE_TEST
+ select PCI_ECAM
+ help
+ Say Y here if you want support for the PCIe Host Controller found
+ on NVIDIA Tegra264 SoCs.
config PCIE_RCAR_HOST
bool "Renesas R-Car PCIe controller (host mode)"
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index ac8db283f0fe..d478743b5142 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PCI_HYPERV_INTERFACE) += pci-hyperv-intf.o
obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
+obj-$(CONFIG_PCIE_TEGRA264) += pcie-tegra264.o
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar.o pcie-rcar-host.o
obj-$(CONFIG_PCIE_RCAR_EP) += pcie-rcar.o pcie-rcar-ep.o
diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig
index 9e651d545973..69efd7582369 100644
--- a/drivers/pci/controller/cadence/Kconfig
+++ b/drivers/pci/controller/cadence/Kconfig
@@ -6,6 +6,15 @@ menu "Cadence-based PCIe controllers"
config PCIE_CADENCE
tristate
+config PCIE_CADENCE_DEBUGFS
+ bool "Cadence PCIe debugfs entries"
+ depends on DEBUG_FS
+ depends on PCIE_CADENCE_HOST || PCIE_CADENCE_EP
+ help
+ Say Y here to enable debugfs entries for the PCIe controller. These
+ entries provide various debug features related to the controller and
+ the LTSSM status of link can be displayed.
+
config PCIE_CADENCE_HOST
tristate
depends on OF
@@ -20,11 +29,12 @@ config PCIE_CADENCE_EP
config PCIE_CADENCE_PLAT
tristate
+ select PCIE_CADENCE_HOST if PCIE_CADENCE_PLAT_HOST != n
+ select PCIE_CADENCE_EP if PCIE_CADENCE_PLAT_EP != n
config PCIE_CADENCE_PLAT_HOST
tristate "Cadence platform PCIe controller (host mode)"
depends on OF
- select PCIE_CADENCE_HOST
select PCIE_CADENCE_PLAT
help
Say Y here if you want to support the Cadence PCIe platform controller in
@@ -35,7 +45,6 @@ config PCIE_CADENCE_PLAT_EP
tristate "Cadence platform PCIe controller (endpoint mode)"
depends on OF
depends on PCI_ENDPOINT
- select PCIE_CADENCE_EP
select PCIE_CADENCE_PLAT
help
Say Y here if you want to support the Cadence PCIe platform controller in
diff --git a/drivers/pci/controller/cadence/Makefile b/drivers/pci/controller/cadence/Makefile
index b8ec1cecfaa8..80be0b885ac8 100644
--- a/drivers/pci/controller/cadence/Makefile
+++ b/drivers/pci/controller/cadence/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
pcie-cadence-mod-y := pcie-cadence-hpa.o pcie-cadence.o
+pcie-cadence-mod-$(CONFIG_PCIE_CADENCE_DEBUGFS) += pcie-cadence-debugfs.o
pcie-cadence-host-mod-y := pcie-cadence-host-common.o pcie-cadence-host.o pcie-cadence-host-hpa.o
pcie-cadence-ep-mod-y := pcie-cadence-ep.o
diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c
index ae916e7b1927..822602836b12 100644
--- a/drivers/pci/controller/cadence/pci-j721e.c
+++ b/drivers/pci/controller/cadence/pci-j721e.c
@@ -383,7 +383,7 @@ static const struct j721e_pcie_data j7200_pcie_rc_data = {
.quirk_detect_quiet_flag = true,
.linkdown_irq_regfield = J7200_LINK_DOWN,
.byte_access_allowed = true,
- .max_lanes = 2,
+ .max_lanes = 4,
};
static const struct j721e_pcie_data j7200_pcie_ep_data = {
@@ -391,7 +391,7 @@ static const struct j721e_pcie_data j7200_pcie_ep_data = {
.quirk_detect_quiet_flag = true,
.linkdown_irq_regfield = J7200_LINK_DOWN,
.quirk_disable_flr = true,
- .max_lanes = 2,
+ .max_lanes = 4,
};
static const struct j721e_pcie_data am64_pcie_rc_data = {
diff --git a/drivers/pci/controller/cadence/pci-sky1.c b/drivers/pci/controller/cadence/pci-sky1.c
index cd55c64e58a9..b8632f1d3156 100644
--- a/drivers/pci/controller/cadence/pci-sky1.c
+++ b/drivers/pci/controller/cadence/pci-sky1.c
@@ -174,6 +174,7 @@ static int sky1_pcie_probe(struct platform_device *pdev)
cdns_pcie->reg_base = pcie->reg_base;
cdns_pcie->msg_res = pcie->msg_res;
cdns_pcie->is_rc = true;
+ cdns_pcie->is_hpa = true;
reg_off = devm_kzalloc(dev, sizeof(*reg_off), GFP_KERNEL);
if (!reg_off) {
@@ -220,6 +221,10 @@ MODULE_DEVICE_TABLE(of, of_sky1_pcie_match);
static void sky1_pcie_remove(struct platform_device *pdev)
{
struct sky1_pcie *pcie = platform_get_drvdata(pdev);
+ struct cdns_pcie_rc *rc;
+
+ rc = container_of(pcie->cdns_pcie, struct cdns_pcie_rc, pcie);
+ cdns_pcie_hpa_host_disable(rc);
pci_ecam_free(pcie->cfg);
}
diff --git a/drivers/pci/controller/cadence/pcie-cadence-debugfs.c b/drivers/pci/controller/cadence/pcie-cadence-debugfs.c
new file mode 100644
index 000000000000..0a308f95e9f6
--- /dev/null
+++ b/drivers/pci/controller/cadence/pcie-cadence-debugfs.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence PCIe controller debugfs driver
+ *
+ * Copyright (C) 2026 Hans Zhang <18255117159@163.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "pcie-cadence.h"
+
+#define CDNS_DEBUGFS_BUF_MAX 128
+
+static const char *cdns_pcie_lga_ltssm_status_string(enum cdns_pcie_lga_ltssm ltssm)
+{
+ const char *str;
+
+ switch (ltssm) {
+#define CDNS_PCIE_LGA_LTSSM_NAME(n) case n: str = #n; break
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_DETECT_QUIET);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_DETECT_ACTIVE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_POLLING_ACTIVE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_POLLING_COMPLIANCE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_POLLING_CONFIGURATION);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LINKWIDTH_START);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LINKWIDTH_ACCEPT);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LANENUM_ACCEPT);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LANENUM_WAIT);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_COMPLETE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_IDLE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_RCVRLOCK);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_SPEED);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_RCVRCFG);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_IDLE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L0);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RX_L0S_ENTRY);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RX_L0S_IDLE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RX_L0S_FTS);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_TX_L0S_ENTRY);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_TX_L0S_IDLE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_TX_L0S_FTS);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L1_ENTRY);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L1_IDLE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L2_IDLE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L2_TRANSMITWAKE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_DISABLED);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_ENTRY_MASTER);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_ACTIVE_MASTER);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_EXIT_MASTER);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_ENTRY_SLAVE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_ACTIVE_SLAVE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_EXIT_SLAVE);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_HOT_RESET);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_0);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_1);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_2);
+ CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_3);
+ default:
+ str = "CDNS_PCIE_LGA_LTSSM_UNKNOWN";
+ break;
+ }
+
+ return str + strlen("CDNS_PCIE_LGA_LTSSM_");
+}
+
+static const char *cdns_pcie_hpa_ltssm_status_string(enum cdns_pcie_hpa_ltssm ltssm)
+{
+ const char *str;
+
+ switch (ltssm) {
+#define CDNS_PCIE_HPA_LTSSM_NAME(n) case n: str = #n; break
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_QUIET);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_QUIET_ENTRY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RCVR_DETECTED_ST);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RCVR_DETECTED_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_ACC_RC);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_RC);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_RC_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_RC);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_ACC_EP);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_EP);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_EP_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_EP);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_EP_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_IDLE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_IDLE_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_4);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0_STATE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_LOCK);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_LOCK_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_CFG);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_CFG_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_IDLE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_IDLE_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_4);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_5);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_6);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_7);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_HOT_RESET);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_HOT_RESET_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_HOT_RESET_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_HOT_RESET_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_ENTRY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_4);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_5);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_WAIT_FOR_LINK_TX);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_FTS_ENTRY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_FTS_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_FTS_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_ST);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_4);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_5);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_6);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_7);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_8);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ENTRY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ENTRY_FROM_RECOVERY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_EXIT_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_EXIT);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_4);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_5);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ACTIVE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_ENTRY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_4);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_IDLE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_EXIT);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_ENTRY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_4);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_5);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_IDLE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_3);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_4);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_5);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_FROM_RECOVERY);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ACTIVE);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE0);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE2_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE2_2);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE3_1);
+ CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE3_2);
+ default:
+ str = "CDNS_PCIE_HPA_LTSSM_UNKNOWN";
+ break;
+ }
+
+ return str + strlen("CDNS_PCIE_HPA_LTSSM_");
+}
+
+static int ltssm_status_show(struct seq_file *s, void *v)
+{
+ struct cdns_pcie *pci = s->private;
+ enum cdns_pcie_lga_ltssm lga_ltssm;
+ enum cdns_pcie_hpa_ltssm hpa_ltssm;
+ const char *str_ltssm;
+ u32 val;
+
+ if (pci->is_hpa) {
+ val = cdns_pcie_hpa_readl(pci, REG_BANK_IP_REG,
+ CDNS_PCIE_HPA_PHY_DBG_STS_REG0);
+ hpa_ltssm = FIELD_GET(CDNS_PCIE_HPA_LTSSM_STATUS_MASK, val);
+ str_ltssm = cdns_pcie_hpa_ltssm_status_string(hpa_ltssm);
+ } else {
+ val = cdns_pcie_readl(pci, CDNS_PCIE_LM_BASE);
+ lga_ltssm = FIELD_GET(CDNS_PCIE_LGA_LTSSM_STATUS_MASK, val);
+ str_ltssm = cdns_pcie_lga_ltssm_status_string(lga_ltssm);
+ }
+
+ seq_printf(s, "%s (0x%02x)\n", str_ltssm,
+ pci->is_hpa ? hpa_ltssm : lga_ltssm);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(ltssm_status);
+
+static void cdns_pcie_ltssm_debugfs_init(struct cdns_pcie *pci, struct dentry *dir)
+{
+ debugfs_create_file("ltssm_status", 0444, dir, pci,
+ &ltssm_status_fops);
+}
+
+void cdns_pcie_debugfs_deinit(struct cdns_pcie *pci)
+{
+ if (!pci->debug_dir)
+ return;
+
+ debugfs_remove_recursive(pci->debug_dir);
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_debugfs_deinit);
+
+void cdns_pcie_debugfs_init(struct cdns_pcie *pci)
+{
+ char dirname[CDNS_DEBUGFS_BUF_MAX];
+ struct device *dev = pci->dev;
+
+ /* Create main directory for each platform driver. */
+ snprintf(dirname, CDNS_DEBUGFS_BUF_MAX, "cdns_pcie_%s", dev_name(dev));
+ pci->debug_dir = debugfs_create_dir(dirname, NULL);
+
+ cdns_pcie_ltssm_debugfs_init(pci, pci->debug_dir);
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_debugfs_init);
diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c
index c0e1194a936b..370b19f4d38f 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-ep.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c
@@ -655,6 +655,7 @@ void cdns_pcie_ep_disable(struct cdns_pcie_ep *ep)
struct device *dev = ep->pcie.dev;
struct pci_epc *epc = to_pci_epc(dev);
+ cdns_pcie_debugfs_deinit(&ep->pcie);
pci_epc_deinit_notify(epc);
pci_epc_mem_free_addr(epc, ep->irq_phys_addr, ep->irq_cpu_addr,
SZ_128K);
@@ -761,6 +762,8 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
pci_epc_init_notify(epc);
+ cdns_pcie_debugfs_init(pcie);
+
return 0;
free_epc_mem:
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.c b/drivers/pci/controller/cadence/pcie-cadence-host-common.c
index 18e4b6c760b5..0ef4396151b4 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-host-common.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c
@@ -16,6 +16,8 @@
#include "pcie-cadence-host-common.h"
#include "../pci-host-common.h"
+#include "../../pci.h"
+
#define LINK_RETRAIN_TIMEOUT HZ
u64 bar_max_size[] = {
@@ -54,12 +56,12 @@ int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie,
int retries;
/* Check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) {
if (pcie_link_up(pcie)) {
dev_info(dev, "Link up\n");
return 0;
}
- usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+ msleep(PCIE_LINK_WAIT_SLEEP_MS);
}
return -ETIMEDOUT;
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
index 8ef58ed01daa..54d7221f775e 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
@@ -313,6 +313,19 @@ int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc)
}
EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_link_setup);
+void cdns_pcie_hpa_host_disable(struct cdns_pcie_rc *rc)
+{
+ struct pci_host_bridge *bridge;
+
+ cdns_pcie_debugfs_deinit(&rc->pcie);
+ bridge = pci_host_bridge_from_priv(rc);
+ pci_lock_rescan_remove();
+ pci_stop_root_bus(bridge->bus);
+ pci_remove_root_bus(bridge->bus);
+ pci_unlock_rescan_remove();
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_disable);
+
int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc)
{
struct device *dev = rc->pcie.dev;
@@ -368,7 +381,13 @@ int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc)
if (!bridge->ops)
bridge->ops = &cdns_pcie_hpa_host_ops;
- return pci_host_probe(bridge);
+ ret = pci_host_probe(bridge);
+ if (ret)
+ return ret;
+
+ cdns_pcie_debugfs_init(pcie);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_setup);
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c
index c12cedf05ad5..15aabaac3dcd 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-host.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-host.c
@@ -365,6 +365,7 @@ void cdns_pcie_host_disable(struct cdns_pcie_rc *rc)
{
struct pci_host_bridge *bridge;
+ cdns_pcie_debugfs_deinit(&rc->pcie);
bridge = pci_host_bridge_from_priv(rc);
pci_lock_rescan_remove();
pci_stop_root_bus(bridge->bus);
@@ -429,7 +430,13 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
if (!bridge->ops)
bridge->ops = &cdns_pcie_host_ops;
- return pci_host_probe(bridge);
+ ret = pci_host_probe(bridge);
+ if (ret)
+ return ret;
+
+ cdns_pcie_debugfs_init(pcie);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(cdns_pcie_host_setup);
diff --git a/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h b/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h
index 857b2140c5d2..15dc4fcaf45d 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h
+++ b/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h
@@ -10,11 +10,6 @@
#include <linux/bitfield.h>
-/* Parameters for the waiting for link up routine */
-#define LINK_WAIT_MAX_RETRIES 10
-#define LINK_WAIT_USLEEP_MIN 90000
-#define LINK_WAIT_USLEEP_MAX 100000
-
/* Local Management Registers */
#define CDNS_PCIE_LM_BASE 0x00100000
diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c
index b067a3296dd3..a1ea24fc3b63 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-plat.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c
@@ -163,6 +163,7 @@ static const struct of_device_id cdns_plat_pcie_of_match[] = {
},
{},
};
+MODULE_DEVICE_TABLE(of, cdns_plat_pcie_of_match);
static struct platform_driver cdns_plat_pcie_driver = {
.driver = {
diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h
index 042a4c49bb9a..b4d51adefea5 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.h
+++ b/drivers/pci/controller/cadence/pcie-cadence.h
@@ -14,6 +14,9 @@
#include "pcie-cadence-lga-regs.h"
#include "pcie-cadence-hpa-regs.h"
+#define CDNS_PCIE_LGA_LTSSM_STATUS_MASK GENMASK(29, 24)
+#define CDNS_PCIE_HPA_LTSSM_STATUS_MASK GENMASK(27, 20)
+
enum cdns_pcie_rp_bar {
RP_BAR_UNDEFINED = -1,
RP_BAR0,
@@ -42,6 +45,180 @@ enum cdns_pcie_reg_bank {
REG_BANKS_MAX,
};
+enum cdns_pcie_lga_ltssm {
+ CDNS_PCIE_LGA_LTSSM_DETECT_QUIET = 0x00,
+ CDNS_PCIE_LGA_LTSSM_DETECT_ACTIVE = 0x01,
+ CDNS_PCIE_LGA_LTSSM_POLLING_ACTIVE = 0x02,
+ CDNS_PCIE_LGA_LTSSM_POLLING_COMPLIANCE = 0x03,
+ CDNS_PCIE_LGA_LTSSM_POLLING_CONFIGURATION = 0x04,
+ CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LINKWIDTH_START = 0x05,
+ CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LINKWIDTH_ACCEPT = 0x06,
+ CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LANENUM_ACCEPT = 0x07,
+ CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LANENUM_WAIT = 0x08,
+ CDNS_PCIE_LGA_LTSSM_CONFIGURATION_COMPLETE = 0x09,
+ CDNS_PCIE_LGA_LTSSM_CONFIGURATION_IDLE = 0x0A,
+ CDNS_PCIE_LGA_LTSSM_RECOVERY_RCVRLOCK = 0x0B,
+ CDNS_PCIE_LGA_LTSSM_RECOVERY_SPEED = 0x0C,
+ CDNS_PCIE_LGA_LTSSM_RECOVERY_RCVRCFG = 0x0D,
+ CDNS_PCIE_LGA_LTSSM_RECOVERY_IDLE = 0x0E,
+ CDNS_PCIE_LGA_LTSSM_L0 = 0x10,
+ CDNS_PCIE_LGA_LTSSM_RX_L0S_ENTRY = 0x11,
+ CDNS_PCIE_LGA_LTSSM_RX_L0S_IDLE = 0x12,
+ CDNS_PCIE_LGA_LTSSM_RX_L0S_FTS = 0x13,
+ CDNS_PCIE_LGA_LTSSM_TX_L0S_ENTRY = 0x14,
+ CDNS_PCIE_LGA_LTSSM_TX_L0S_IDLE = 0x15,
+ CDNS_PCIE_LGA_LTSSM_TX_L0S_FTS = 0x16,
+ CDNS_PCIE_LGA_LTSSM_L1_ENTRY = 0x17,
+ CDNS_PCIE_LGA_LTSSM_L1_IDLE = 0x18,
+ CDNS_PCIE_LGA_LTSSM_L2_IDLE = 0x19,
+ CDNS_PCIE_LGA_LTSSM_L2_TRANSMITWAKE = 0x1A,
+ CDNS_PCIE_LGA_LTSSM_DISABLED = 0x20,
+ CDNS_PCIE_LGA_LTSSM_LOOPBACK_ENTRY_MASTER = 0x21,
+ CDNS_PCIE_LGA_LTSSM_LOOPBACK_ACTIVE_MASTER = 0x22,
+ CDNS_PCIE_LGA_LTSSM_LOOPBACK_EXIT_MASTER = 0x23,
+ CDNS_PCIE_LGA_LTSSM_LOOPBACK_ENTRY_SLAVE = 0x24,
+ CDNS_PCIE_LGA_LTSSM_LOOPBACK_ACTIVE_SLAVE = 0x25,
+ CDNS_PCIE_LGA_LTSSM_LOOPBACK_EXIT_SLAVE = 0x26,
+ CDNS_PCIE_LGA_LTSSM_HOT_RESET = 0x27,
+ CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_0 = 0x28,
+ CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_1 = 0x29,
+ CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_2 = 0x2A,
+ CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_3 = 0x2B,
+ CDNS_PCIE_LGA_LTSSM_UNKNOWN = 0xFFFFFFFF,
+};
+
+enum cdns_pcie_hpa_ltssm {
+ CDNS_PCIE_HPA_LTSSM_DETECT_QUIET = 0,
+ CDNS_PCIE_HPA_LTSSM_DETECT_QUIET_ENTRY = 1,
+ CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE = 2,
+ CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_1 = 3,
+ CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_2 = 4,
+ CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_3 = 5,
+ CDNS_PCIE_HPA_LTSSM_RCVR_DETECTED_ST = 6,
+ CDNS_PCIE_HPA_LTSSM_RCVR_DETECTED_1 = 7,
+ CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE = 8,
+ CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_1 = 9,
+ CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_2 = 10,
+ CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_3 = 11,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE = 12,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_1 = 13,
+ CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG = 14,
+ CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG_1 = 15,
+ CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG_2 = 16,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC = 17,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC_1 = 18,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC_2 = 19,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LW_ACC_RC = 20,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_RC = 21,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_RC_1 = 22,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_RC = 23,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP = 24,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP_1 = 25,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP_2 = 26,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LW_ACC_EP = 27,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_EP = 28,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_EP_1 = 29,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_EP = 30,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_EP_1 = 31,
+ CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_1 = 32,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE = 33,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE_1 = 34,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE_2 = 35,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_IDLE = 36,
+ CDNS_PCIE_HPA_LTSSM_CONFIG_IDLE_1 = 37,
+ CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_2 = 38,
+ CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_3 = 39,
+ CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_4 = 40,
+ CDNS_PCIE_HPA_LTSSM_L0_STATE = 41,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_LOCK = 42,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_LOCK_1 = 43,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_CFG = 44,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_CFG_1 = 45,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_IDLE = 46,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_IDLE_1 = 47,
+ CDNS_PCIE_HPA_LTSSM_DISABLE_LINK = 48,
+ CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_1 = 49,
+ CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_2 = 50,
+ CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_3 = 51,
+ CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_4 = 52,
+ CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_5 = 53,
+ CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_6 = 54,
+ CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_7 = 55,
+ CDNS_PCIE_HPA_LTSSM_HOT_RESET = 56,
+ CDNS_PCIE_HPA_LTSSM_HOT_RESET_1 = 57,
+ CDNS_PCIE_HPA_LTSSM_HOT_RESET_2 = 58,
+ CDNS_PCIE_HPA_LTSSM_HOT_RESET_3 = 59,
+ CDNS_PCIE_HPA_LTSSM_L0S_ENTRY = 60,
+ CDNS_PCIE_HPA_LTSSM_L0S_1 = 61,
+ CDNS_PCIE_HPA_LTSSM_L0S_2 = 62,
+ CDNS_PCIE_HPA_LTSSM_L0S_3 = 63,
+ CDNS_PCIE_HPA_LTSSM_L0S_4 = 64,
+ CDNS_PCIE_HPA_LTSSM_L0S_5 = 65,
+ CDNS_PCIE_HPA_LTSSM_WAIT_FOR_LINK_TX = 66,
+ CDNS_PCIE_HPA_LTSSM_TX_FTS_ENTRY = 67,
+ CDNS_PCIE_HPA_LTSSM_TX_FTS_1 = 68,
+ CDNS_PCIE_HPA_LTSSM_TX_FTS_2 = 69,
+ CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_ST = 70,
+ CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_1 = 71,
+ CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_2 = 72,
+ CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_3 = 73,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED = 74,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_1 = 75,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_2 = 76,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_3 = 77,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23 = 78,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_1 = 79,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_2 = 80,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_3 = 81,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_4 = 82,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_5 = 83,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_6 = 84,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_7 = 85,
+ CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_8 = 86,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ENTRY = 87,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ENTRY_FROM_RECOVERY = 88,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_EXIT_1 = 89,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_EXIT = 90,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_1 = 91,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_2 = 92,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_3 = 93,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_4 = 94,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_5 = 95,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ACTIVE = 96,
+ CDNS_PCIE_HPA_LTSSM_L1_ENTRY = 97,
+ CDNS_PCIE_HPA_LTSSM_L1_1 = 98,
+ CDNS_PCIE_HPA_LTSSM_L1_2 = 99,
+ CDNS_PCIE_HPA_LTSSM_L1_3 = 100,
+ CDNS_PCIE_HPA_LTSSM_L1_4 = 101,
+ CDNS_PCIE_HPA_LTSSM_L1_IDLE = 102,
+ CDNS_PCIE_HPA_LTSSM_L1_EXIT = 103,
+ CDNS_PCIE_HPA_LTSSM_L2_ENTRY = 104,
+ CDNS_PCIE_HPA_LTSSM_L2_1 = 105,
+ CDNS_PCIE_HPA_LTSSM_L2_2 = 106,
+ CDNS_PCIE_HPA_LTSSM_L2_3 = 107,
+ CDNS_PCIE_HPA_LTSSM_L2_4 = 108,
+ CDNS_PCIE_HPA_LTSSM_L2_5 = 109,
+ CDNS_PCIE_HPA_LTSSM_L2_IDLE = 110,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY = 111,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_1 = 112,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_2 = 113,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_3 = 114,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_4 = 115,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_5 = 116,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_FROM_RECOVERY = 117,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ACTIVE = 118,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT = 119,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT_1 = 120,
+ CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT_2 = 121,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE0 = 122,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE1 = 123,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE2_1 = 124,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE2_2 = 125,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE3_1 = 126,
+ CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE3_2 = 127,
+ CDNS_PCIE_HPA_LTSSM_UNKNOWN = 0xFFFFFFFF,
+};
+
struct cdns_pcie_ops {
int (*start_link)(struct cdns_pcie *pcie);
void (*stop_link)(struct cdns_pcie *pcie);
@@ -80,6 +257,7 @@ struct cdns_plat_pcie_of_data {
* @msg_res: Region for send message to map PCI accesses
* @dev: PCIe controller
* @is_rc: tell whether the PCIe controller mode is Root Complex or Endpoint.
+ * @is_hpa: indicates if the architecture is HPA
* @phy_count: number of supported PHY devices
* @phy: list of pointers to specific PHY control blocks
* @link: list of pointers to corresponding device link representations
@@ -87,6 +265,7 @@ struct cdns_plat_pcie_of_data {
* wrapper
* @cdns_pcie_reg_offsets: Register bank offsets for different SoC
* @max_link_speed: Maximum supported link speed
+ * @debug_dir: debugfs node
*/
struct cdns_pcie {
void __iomem *reg_base;
@@ -94,12 +273,14 @@ struct cdns_pcie {
struct resource *msg_res;
struct device *dev;
bool is_rc;
+ bool is_hpa;
int phy_count;
struct phy **phy;
struct device_link **link;
const struct cdns_pcie_ops *ops;
const struct cdns_plat_pcie_of_data *cdns_pcie_reg_offsets;
int max_link_speed;
+ struct dentry *debug_dir;
};
/**
@@ -447,6 +628,7 @@ void cdns_pcie_host_disable(struct cdns_pcie_rc *rc);
void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
int where);
int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc);
+void cdns_pcie_hpa_host_disable(struct cdns_pcie_rc *rc);
#else
static inline int cdns_pcie_host_link_setup(struct cdns_pcie_rc *rc)
{
@@ -472,6 +654,10 @@ static inline void cdns_pcie_host_disable(struct cdns_pcie_rc *rc)
{
}
+static inline void cdns_pcie_hpa_host_disable(struct cdns_pcie_rc *rc)
+{
+}
+
static inline void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
int where)
{
@@ -535,4 +721,16 @@ bool cdns_pcie_hpa_link_up(struct cdns_pcie *pcie);
extern const struct dev_pm_ops cdns_pcie_pm_ops;
+#if IS_ENABLED(CONFIG_PCIE_CADENCE_DEBUGFS)
+void cdns_pcie_debugfs_deinit(struct cdns_pcie *pci);
+void cdns_pcie_debugfs_init(struct cdns_pcie *pci);
+#else
+static inline void cdns_pcie_debugfs_deinit(struct cdns_pcie *pci)
+{
+}
+static inline void cdns_pcie_debugfs_init(struct cdns_pcie *pci)
+{
+}
+#endif
+
#endif /* _PCIE_CADENCE_H */
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index aa0b784c85b4..dcfbe7e229fd 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -126,7 +126,9 @@ config PCI_IMX6_EP
depends on ARCH_MXC || COMPILE_TEST
depends on PCI_ENDPOINT
select PCIE_DW_EP
+ select PCI_HOST_COMMON
select PCI_IMX6
+ select PCI_PWRCTRL_GENERIC
help
Enables support for the PCIe controller in the i.MX SoCs to
work in endpoint mode. The PCI controller on i.MX is based
@@ -344,7 +346,7 @@ config PCIE_RCAR_GEN4
config PCIE_RCAR_GEN4_HOST
tristate "Renesas R-Car Gen4 PCIe controller (host mode)"
- depends on ARCH_RENESAS || COMPILE_TEST
+ depends on ARM64 && (ARCH_RENESAS || COMPILE_TEST)
depends on PCI_MSI
select PCIE_DW_HOST
select PCIE_RCAR_GEN4
@@ -355,7 +357,7 @@ config PCIE_RCAR_GEN4_HOST
config PCIE_RCAR_GEN4_EP
tristate "Renesas R-Car Gen4 PCIe controller (endpoint mode)"
- depends on ARCH_RENESAS || COMPILE_TEST
+ depends on ARM64 && (ARCH_RENESAS || COMPILE_TEST)
depends on PCI_ENDPOINT
select PCIE_DW_EP
select PCIE_RCAR_GEN4
@@ -374,6 +376,7 @@ config PCIE_ROCKCHIP_DW_HOST
depends on OF
select PCIE_DW_HOST
select PCIE_ROCKCHIP_DW
+ select PCI_HOST_COMMON
help
Enables support for the DesignWare PCIe controller in the
Rockchip SoC (except RK3399) to work in host mode.
diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
index 3fc889944f02..6ae5b27e27b3 100644
--- a/drivers/pci/controller/dwc/pci-dra7xx.c
+++ b/drivers/pci/controller/dwc/pci-dra7xx.c
@@ -373,12 +373,14 @@ static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = {
.init = dra7xx_pcie_host_init,
};
-static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep)
+static int dra7xx_pcie_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
dra7xx_pcie_enable_wrapper_interrupts(dra7xx);
+
+ return 0;
}
static void dra7xx_pcie_raise_intx_irq(struct dra7xx_pcie *dra7xx)
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index f7389b5437df..39790e66b98d 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -121,6 +121,7 @@ enum imx_pcie_variants {
#define IMX_PCIE_FLAG_SKIP_L23_READY BIT(12)
/* Preserve MSI capability for platforms that require it */
#define IMX_PCIE_FLAG_KEEP_MSI_CAP BIT(13)
+#define IMX_PCIE_FLAG_PM_RUNTIME BIT(14)
#define imx_check_flag(pci, val) (pci->drvdata->flags & val)
@@ -1321,6 +1322,18 @@ static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert)
}
}
+static bool imx_pcie_perst_found(struct pci_host_bridge *bridge)
+{
+ struct pci_host_port *port;
+
+ list_for_each_entry(port, &bridge->ports, list) {
+ if (!list_empty(&port->perst))
+ return true;
+ }
+
+ return false;
+}
+
static int imx_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -1333,15 +1346,12 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
/* Parse Root Port nodes if present */
ret = pci_host_common_parse_ports(dev, bridge);
if (ret) {
- if (ret != -ENODEV) {
- dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret);
- return ret;
- }
+ dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret);
+ return ret;
+ }
- /*
- * Fall back to legacy binding for DT backwards
- * compatibility
- */
+ /* Fall back to legacy binding for DT backwards compatibility */
+ if (!imx_pcie_perst_found(bridge)) {
ret = imx_pcie_parse_legacy_binding(imx_pcie);
if (ret)
return ret;
@@ -1376,16 +1386,12 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
}
}
- ret = pci_pwrctrl_create_devices(dev);
- if (ret) {
- dev_err(dev, "failed to create pwrctrl devices\n");
- goto err_reg_disable;
- }
-
- ret = pci_pwrctrl_power_on_devices(dev);
- if (ret) {
- dev_err(dev, "failed to power on pwrctrl devices\n");
- goto err_pwrctrl_destroy;
+ if (!pp->skip_pwrctrl_off) {
+ ret = pci_pwrctrl_power_on_devices(dev);
+ if (ret) {
+ dev_err(dev, "failed to power on pwrctrl devices\n");
+ goto err_reg_disable;
+ }
}
ret = imx_pcie_clk_enable(imx_pcie);
@@ -1455,10 +1461,8 @@ err_phy_exit:
err_clk_disable:
imx_pcie_clk_disable(imx_pcie);
err_pwrctrl_power_off:
- pci_pwrctrl_power_off_devices(dev);
-err_pwrctrl_destroy:
- if (ret != -EPROBE_DEFER)
- pci_pwrctrl_destroy_devices(dev);
+ if (!pp->skip_pwrctrl_off)
+ pci_pwrctrl_power_off_devices(dev);
err_reg_disable:
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
@@ -1478,7 +1482,8 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp)
}
imx_pcie_clk_disable(imx_pcie);
- pci_pwrctrl_power_off_devices(pci->dev);
+ if (!pci->pp.skip_pwrctrl_off)
+ pci_pwrctrl_power_off_devices(pci->dev);
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
}
@@ -1950,11 +1955,15 @@ static int imx_pcie_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = pci_pwrctrl_create_devices(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to create pwrctrl devices\n");
+
pci->use_parent_dt_ranges = true;
if (imx_pcie->drvdata->mode == DW_PCIE_EP_TYPE) {
ret = imx_add_pcie_ep(imx_pcie, pdev);
if (ret < 0)
- return ret;
+ goto err_pwrctrl_destroy;
/*
* FIXME: Only single Device (EPF) is supported due to the
@@ -1962,6 +1971,13 @@ static int imx_pcie_probe(struct platform_device *pdev)
*/
imx_pcie_add_lut_by_rid(imx_pcie, 0);
} else {
+ if (imx_pcie->drvdata->flags & IMX_PCIE_FLAG_PM_RUNTIME) {
+ pm_runtime_no_callbacks(dev);
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret < 0)
+ return ret;
+ }
+
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY))
pci->pp.skip_l23_ready = true;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_KEEP_MSI_CAP))
@@ -1969,7 +1985,7 @@ static int imx_pcie_probe(struct platform_device *pdev)
pci->pp.use_atu_msg = true;
ret = dw_pcie_host_init(&pci->pp);
if (ret < 0)
- return ret;
+ goto err_pwrctrl_destroy;
if (pci_msi_enabled()) {
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI);
@@ -1981,6 +1997,11 @@ static int imx_pcie_probe(struct platform_device *pdev)
}
return 0;
+
+err_pwrctrl_destroy:
+ if (ret != -EPROBE_DEFER)
+ pci_pwrctrl_destroy_devices(dev);
+ return ret;
}
static void imx_pcie_shutdown(struct platform_device *pdev)
@@ -2108,6 +2129,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.flags = IMX_PCIE_FLAG_HAS_SERDES |
IMX_PCIE_FLAG_HAS_LUT |
IMX_PCIE_FLAG_8GT_ECN_ERR051586 |
+ IMX_PCIE_FLAG_PM_RUNTIME |
IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
.ltssm_off = IMX95_PE0_GEN_CTRL_3,
.ltssm_mask = IMX95_PCIE_LTSSM_EN,
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index 278d2dba1db0..602516239a57 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -876,7 +876,7 @@ static const struct dw_pcie_ops ks_pcie_dw_pcie_ops = {
.write_dbi2 = ks_pcie_am654_write_dbi2,
};
-static void ks_pcie_am654_ep_init(struct dw_pcie_ep *ep)
+static int ks_pcie_am654_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
int flags;
@@ -885,6 +885,8 @@ static void ks_pcie_am654_ep_init(struct dw_pcie_ep *ep)
flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32;
dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, APP_ADDR_SPACE_0 - 1);
dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, flags);
+
+ return 0;
}
static void ks_pcie_am654_raise_intx_irq(struct keystone_pcie *ks_pcie)
@@ -1389,13 +1391,17 @@ static int ks_pcie_fault(unsigned long addr, unsigned int fsr,
static int __init ks_pcie_init(void)
{
+ struct device_node *np;
/*
* PCIe access errors that result into OCP errors are caught by ARM as
* "External aborts"
*/
- if (of_find_matching_node(NULL, ks_pcie_of_match))
+ np = of_find_matching_node(NULL, ks_pcie_of_match);
+ if (np) {
+ of_node_put(np);
hook_fault_code(17, ks_pcie_fault, SIGBUS, 0,
"Asynchronous external abort");
+ }
return platform_driver_register(&ks_pcie_driver);
}
diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c
index 8936975ff104..527c57114dcf 100644
--- a/drivers/pci/controller/dwc/pci-layerscape-ep.c
+++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c
@@ -147,7 +147,7 @@ ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
return pcie->ls_epc;
}
-static void ls_pcie_ep_init(struct dw_pcie_ep *ep)
+static int ls_pcie_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct ls_pcie_ep *pcie = to_ls_pcie_ep(pci);
@@ -155,10 +155,12 @@ static void ls_pcie_ep_init(struct dw_pcie_ep *ep)
ep_func = dw_pcie_ep_get_func_from_ep(ep, 0);
if (!ep_func)
- return;
+ return -ENODEV;
pcie->ls_epc->msi_capable = ep_func->msi_cap ? true : false;
pcie->ls_epc->msix_capable = ep_func->msix_cap ? true : false;
+
+ return 0;
}
static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
index 7a4da9fae1ea..8559d132dcde 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -397,11 +397,11 @@ static int meson_pcie_probe(struct platform_device *pdev)
mp->phy = devm_phy_get(dev, "pcie");
if (IS_ERR(mp->phy)) {
- dev_err(dev, "get phy failed, %ld\n", PTR_ERR(mp->phy));
+ dev_err(dev, "get phy failed, %pe\n", mp->phy);
return PTR_ERR(mp->phy);
}
- mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ mp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(mp->reset_gpio)) {
dev_err(dev, "get reset gpio failed\n");
return PTR_ERR(mp->reset_gpio);
diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
index 5cd227dda9a1..7f0ba506dd1c 100644
--- a/drivers/pci/controller/dwc/pcie-artpec6.c
+++ b/drivers/pci/controller/dwc/pcie-artpec6.c
@@ -335,7 +335,7 @@ static const struct dw_pcie_host_ops artpec6_pcie_host_ops = {
.init = artpec6_pcie_host_init,
};
-static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep)
+static int artpec6_pcie_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
@@ -344,6 +344,8 @@ static void artpec6_pcie_ep_init(struct dw_pcie_ep *ep)
artpec6_pcie_init_phy(artpec6_pcie);
artpec6_pcie_deassert_core_reset(artpec6_pcie);
artpec6_pcie_wait_for_phy(artpec6_pcie);
+
+ return 0;
}
static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7d2794945704..de8ee3db4360 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -1032,6 +1032,8 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
* there is no unified way to check if we have operations in
* flight, thus we don't know if we should WARN() or not.
*/
+ /* flush posted write before unmap */
+ readl(ep->msi_mem + ep->msi_iatu_mapped_offset);
dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
ep->msi_iatu_mapped = false;
}
@@ -1044,6 +1046,7 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
return ret;
ep->msi_iatu_mapped = true;
+ ep->msi_iatu_mapped_offset = offset;
ep->msi_msg_addr = msg_addr;
ep->msi_map_size = map_size;
}
@@ -1124,6 +1127,17 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
return -EPERM;
}
+ /*
+ * ep->msi_iatu_mapped means that an MSI target address is cached,
+ * unmap it first so that we can reuse ep->msi_mem_phys for MSI-X.
+ */
+ if (ep->msi_iatu_mapped) {
+ /* flush posted write before unmap */
+ readl(ep->msi_mem + ep->msi_iatu_mapped_offset);
+ dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
+ ep->msi_iatu_mapped = false;
+ }
+
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);
@@ -1153,6 +1167,11 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ if (ep->msi_iatu_mapped) {
+ dw_pcie_ep_unmap_addr(ep->epc, 0, 0, ep->msi_mem_phys);
+ ep->msi_iatu_mapped = false;
+ }
+
dwc_pcie_debugfs_deinit(pci);
dw_pcie_edma_remove(pci);
}
@@ -1367,8 +1386,11 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
list_add_tail(&ep_func->list, &ep->func_list);
}
- if (ep->ops->init)
- ep->ops->init(ep);
+ if (ep->ops->init) {
+ ret = ep->ops->init(ep);
+ if (ret)
+ goto err_remove_edma;
+ }
dw_pcie_ep_disable_bars(ep);
@@ -1521,8 +1543,11 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
if (ret)
return ret;
- if (ep->ops->pre_init)
- ep->ops->pre_init(ep);
+ if (ep->ops->pre_init) {
+ ret = ep->ops->pre_init(ep);
+ if (ret)
+ return ret;
+ }
ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
ep->page_size);
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 06722259d2e3..f5a38e6fd8d7 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -587,6 +587,12 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
return ret;
+ if (pci_msi_enabled()) {
+ pp->use_imsi_rx = !(pp->ops->msi_init ||
+ of_property_present(np, "msi-parent") ||
+ of_property_present(np, "msi-map"));
+ }
+
if (pp->ops->init) {
ret = pp->ops->init(pp);
if (ret)
@@ -594,10 +600,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
}
if (pci_msi_enabled()) {
- pp->use_imsi_rx = !(pp->ops->msi_init ||
- of_property_present(np, "msi-parent") ||
- of_property_present(np, "msi-map"));
-
/*
* For the use_imsi_rx case the default assignment is handled
* in the dw_pcie_msi_host_init().
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index ec4722ed9303..593388f29bdd 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -842,8 +842,10 @@ EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup);
static void dw_pcie_link_set_max_speed(struct dw_pcie *pci)
{
- u32 cap, ctrl2, link_speed;
+ u32 cap, ctrl2;
+ enum pci_bus_speed link_speed;
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ u16 ctrl2_speed;
cap = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
@@ -860,30 +862,18 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci)
ctrl2 = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCTL2);
ctrl2 &= ~PCI_EXP_LNKCTL2_TLS;
- switch (pcie_get_link_speed(pci->max_link_speed)) {
- case PCIE_SPEED_2_5GT:
- link_speed = PCI_EXP_LNKCTL2_TLS_2_5GT;
- break;
- case PCIE_SPEED_5_0GT:
- link_speed = PCI_EXP_LNKCTL2_TLS_5_0GT;
- break;
- case PCIE_SPEED_8_0GT:
- link_speed = PCI_EXP_LNKCTL2_TLS_8_0GT;
- break;
- case PCIE_SPEED_16_0GT:
- link_speed = PCI_EXP_LNKCTL2_TLS_16_0GT;
- break;
- default:
+ link_speed = pcie_get_link_speed(pci->max_link_speed);
+ ctrl2_speed = pci_bus_speed2lnkctl2(link_speed);
+ if (ctrl2_speed == 0) {
/* Use hardware capability */
- link_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, cap);
+ ctrl2_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, cap);
ctrl2 &= ~PCI_EXP_LNKCTL2_HASD;
- break;
}
- dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCTL2, ctrl2 | link_speed);
+ dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCTL2, ctrl2 | ctrl2_speed);
cap &= ~((u32)PCI_EXP_LNKCAP_SLS);
- dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, cap | link_speed);
+ dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, cap | ctrl2_speed);
}
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index de4b245b1758..0735ae940924 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -474,8 +474,8 @@ struct dw_pcie_rp {
};
struct dw_pcie_ep_ops {
- void (*pre_init)(struct dw_pcie_ep *ep);
- void (*init)(struct dw_pcie_ep *ep);
+ int (*pre_init)(struct dw_pcie_ep *ep);
+ int (*init)(struct dw_pcie_ep *ep);
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
unsigned int type, u16 interrupt_num);
const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
@@ -518,6 +518,7 @@ struct dw_pcie_ep {
/* MSI outbound iATU state */
bool msi_iatu_mapped;
+ size_t msi_iatu_mapped_offset;
u64 msi_msg_addr;
size_t msi_map_size;
};
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 731d93663cca..af26a07c0c9e 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -26,6 +26,7 @@
#include <trace/events/pci_controller.h>
#include "../../pci.h"
+#include "../pci-host-common.h"
#include "pcie-designware.h"
/*
@@ -122,6 +123,9 @@ struct rockchip_pcie_of_data {
const struct pci_epc_features *epc_features;
};
+static int rockchip_pcie_rc_reset_root_port(struct pci_host_bridge *bridge,
+ struct pci_dev *pdev);
+
static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip, u32 reg)
{
return readl_relaxed(rockchip->apb_base + reg);
@@ -436,6 +440,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
rockchip_pcie_configure_l1ss(pci);
rockchip_pcie_enable_l0s(pci);
+ pp->bridge->reset_root_port = rockchip_pcie_rc_reset_root_port;
/* Disable Root Ports BAR0 and BAR1 as they report bogus size */
dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0);
@@ -469,12 +474,14 @@ static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep)
dw_pcie_remove_ext_capability(pci, PCI_EXT_CAP_ID_ATS);
}
-static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep)
+static int rockchip_pcie_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
rockchip_pcie_enable_l0s(pci);
rockchip_pcie_ep_hide_broken_ats_cap_rk3588(ep);
+
+ return 0;
};
static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
@@ -634,6 +641,32 @@ static const struct dw_pcie_ops dw_pcie_ops = {
.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;
+ struct pci_dev *port;
+ 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_reg(rockchip));
+
+ if (reg & PCIE_LINK_REQ_RST_NOT_INT) {
+ dev_dbg(dev, "hot reset or link-down reset\n");
+ for_each_pci_bridge(port, pp->bridge->bus) {
+ if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT)
+ pci_host_handle_link_down(port);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)
{
struct rockchip_pcie *rockchip = arg;
@@ -666,14 +699,29 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)
return IRQ_HANDLED;
}
-static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip)
+static int rockchip_pcie_configure_rc(struct platform_device *pdev,
+ 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);
@@ -685,7 +733,17 @@ static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip)
pp = &rockchip->pci.pp;
pp->ops = &rockchip_pcie_host_ops;
- return dw_pcie_host_init(pp);
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ /* unmask hot reset/link-down reset */
+ val = FIELD_PREP_WM16(PCIE_LINK_REQ_RST_NOT_INT, 0);
+ rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC);
+
+ return ret;
}
static int rockchip_pcie_configure_ep(struct platform_device *pdev,
@@ -804,7 +862,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
switch (data->mode) {
case DW_PCIE_RC_TYPE:
- ret = rockchip_pcie_configure_rc(rockchip);
+ ret = rockchip_pcie_configure_rc(pdev, rockchip);
if (ret)
goto deinit_clk;
break;
@@ -829,6 +887,78 @@ deinit_phy:
return ret;
}
+static int rockchip_pcie_rc_reset_root_port(struct pci_host_bridge *bridge,
+ struct pci_dev *pdev)
+{
+ struct pci_bus *bus = bridge->bus;
+ struct dw_pcie_rp *pp = bus->sysdata;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
+ struct device *dev = rockchip->pci.dev;
+ u32 val;
+ int ret;
+
+ dw_pcie_stop_link(pci);
+ clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks);
+ rockchip_pcie_phy_deinit(rockchip);
+
+ ret = reset_control_assert(rockchip->rst);
+ if (ret)
+ return ret;
+
+ ret = rockchip_pcie_phy_init(rockchip);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(rockchip->rst);
+ if (ret)
+ goto deinit_phy;
+
+ ret = rockchip_pcie_clk_init(rockchip);
+ if (ret)
+ goto deinit_phy;
+
+ ret = pp->ops->init(pp);
+ if (ret) {
+ dev_err(dev, "Host init failed: %d\n", ret);
+ goto deinit_clk;
+ }
+
+ /* LTSSM enable control mode */
+ val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1);
+ rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
+
+ rockchip_pcie_writel_apb(rockchip,
+ PCIE_CLIENT_SET_MODE(PCIE_CLIENT_MODE_RC),
+ PCIE_CLIENT_GENERAL_CON);
+
+ ret = dw_pcie_setup_rc(pp);
+ if (ret) {
+ dev_err(dev, "Failed to setup RC: %d\n", ret);
+ goto deinit_clk;
+ }
+
+ /* unmask hot reset/link-down reset */
+ val = FIELD_PREP_WM16(PCIE_LINK_REQ_RST_NOT_INT, 0);
+ rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC);
+
+ ret = dw_pcie_start_link(pci);
+ if (ret)
+ goto deinit_clk;
+
+ /* Ignore errors, the link may come up later */
+ dw_pcie_wait_for_link(pci);
+ dev_dbg(dev, "Root Port reset completed\n");
+ return ret;
+
+deinit_clk:
+ clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks);
+deinit_phy:
+ rockchip_pcie_phy_deinit(rockchip);
+
+ return ret;
+}
+
static const struct rockchip_pcie_of_data rockchip_pcie_rc_of_data_rk3568 = {
.mode = DW_PCIE_RC_TYPE,
};
diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
index 42fb5f24a223..b11e66c10fff 100644
--- a/drivers/pci/controller/dwc/pcie-keembay.c
+++ b/drivers/pci/controller/dwc/pcie-keembay.c
@@ -278,12 +278,14 @@ static int keembay_pcie_setup_msi_irq(struct keembay_pcie *pcie)
return 0;
}
-static void keembay_pcie_ep_init(struct dw_pcie_ep *ep)
+static int keembay_pcie_ep_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct keembay_pcie *pcie = dev_get_drvdata(pci->dev);
writel(EDMA_INT_EN, pcie->apb_base + PCIE_REGS_INTERRUPT_ENABLE);
+
+ return 0;
}
static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index d8eb52857f69..b58a607b713f 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -56,6 +56,10 @@
#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_STATUS 0x230
#define PARF_SID_OFFSET 0x234
#define PARF_BDF_TRANSLATE_CFG 0x24c
#define PARF_DBI_BASE_ADDR_V2 0x350
@@ -133,6 +137,13 @@
/* PARF_LTSSM register fields */
#define LTSSM_EN BIT(8)
#define PARF_LTSSM_STATE_MASK GENMASK(5, 0)
+#define SW_CLEAR_FLUSH_MODE BIT(10)
+#define FLUSH_MODE BIT(11)
+
+/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */
+#define INT_ALL_LINK_DOWN 1
+#define PARF_INT_ALL_LINK_DOWN BIT(INT_ALL_LINK_DOWN)
+#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23)
/* PARF_NO_SNOOP_OVERRIDE register fields */
#define WR_NO_SNOOP_OVERRIDE_EN BIT(1)
@@ -144,6 +155,9 @@
/* PARF_BDF_TO_SID_CFG fields */
#define BDF_TO_SID_BYPASS BIT(0)
+/* PARF_STATUS fields */
+#define FLUSH_COMPLETED BIT(8)
+
/* ELBI_SYS_CTRL register fields */
#define ELBI_SYS_CTRL_LT_ENABLE BIT(0)
#define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4)
@@ -172,6 +186,7 @@
PCIE_CAP_SLOT_POWER_LIMIT_SCALE)
#define PERST_DELAY_US 1000
+#define FLUSH_TIMEOUT_US 100
#define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0))
@@ -291,10 +306,13 @@ struct qcom_pcie {
struct dentry *debugfs;
struct list_head ports;
struct gpio_desc *reset;
+ int global_irq;
bool use_pm_opp;
};
#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
+static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge,
+ struct pci_dev *pdev);
static void __qcom_pcie_perst_assert(struct qcom_pcie *pcie, bool assert)
{
@@ -358,7 +376,7 @@ static void qcom_pcie_clear_aspm_l0s(struct dw_pcie *pci)
dw_pcie_dbi_ro_wr_dis(pci);
}
-static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci)
+static void qcom_pcie_set_slot_cap(struct dw_pcie *pci)
{
u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
u32 val;
@@ -372,6 +390,12 @@ static void qcom_pcie_set_slot_nccs(struct dw_pcie *pci)
*/
val = readl(pci->dbi_base + offset + PCI_EXP_SLTCAP);
val |= PCI_EXP_SLTCAP_NCCS;
+
+ /*
+ * Qcom PCIe Root Ports do not support Attention Button, so clear
+ * Attention Button Present in Slot Capabilities.
+ */
+ val &= ~PCI_EXP_SLTCAP_ABP;
writel(val, pci->dbi_base + offset + PCI_EXP_SLTCAP);
dw_pcie_dbi_ro_wr_dis(pci);
@@ -580,7 +604,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie)
writel(CFG_BRIDGE_SB_INIT,
pci->dbi_base + AXI_MSTR_RESP_COMP_CTRL1);
- qcom_pcie_set_slot_nccs(pcie->pci);
+ qcom_pcie_set_slot_cap(pcie->pci);
return 0;
}
@@ -660,7 +684,7 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie)
writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT);
}
- qcom_pcie_set_slot_nccs(pcie->pci);
+ qcom_pcie_set_slot_cap(pcie->pci);
return 0;
}
@@ -759,7 +783,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie)
val |= EN;
writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2);
- qcom_pcie_set_slot_nccs(pcie->pci);
+ qcom_pcie_set_slot_cap(pcie->pci);
return 0;
}
@@ -1078,7 +1102,7 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)
writel(WR_NO_SNOOP_OVERRIDE_EN | RD_NO_SNOOP_OVERRIDE_EN,
pcie->parf + PARF_NO_SNOOP_OVERRIDE);
- qcom_pcie_set_slot_nccs(pcie->pci);
+ qcom_pcie_set_slot_cap(pcie->pci);
return 0;
}
@@ -1406,6 +1430,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
goto err_assert_reset;
}
+ pp->bridge->reset_root_port = qcom_pcie_reset_root_port;
+
return 0;
err_assert_reset:
@@ -1734,6 +1760,75 @@ static int qcom_pcie_set_max_opp(struct device *dev)
return ret;
}
+/*
+ * Qcom PCIe controllers only support one Root Port per controller instance. So
+ * this function ignores the 'pci_dev' associated with the Root Port and just
+ * resets the host bridge, which in turn resets the Root Port also.
+ */
+static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge,
+ struct pci_dev *pdev)
+{
+ struct device *dev = bridge->dev.parent;
+ struct qcom_pcie *pcie = dev_get_drvdata(dev);
+ struct dw_pcie *pci = pcie->pci;
+ struct dw_pcie_rp *pp = &pci->pp;
+ u32 val;
+ int ret;
+
+ /* Wait for the pending transactions to be completed */
+ ret = readl_relaxed_poll_timeout(pcie->parf + PARF_STATUS, val,
+ val & FLUSH_COMPLETED, 10,
+ FLUSH_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "Flush completion failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Clear the FLUSH_MODE to allow the core to be reset */
+ val = readl(pcie->parf + PARF_LTSSM);
+ val |= SW_CLEAR_FLUSH_MODE;
+ writel(val, pcie->parf + PARF_LTSSM);
+
+ /* Wait for the FLUSH_MODE to clear */
+ ret = readl_relaxed_poll_timeout(pcie->parf + PARF_LTSSM, val,
+ !(val & FLUSH_MODE), 10,
+ FLUSH_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "Flush mode clear failed: %d\n", ret);
+ return ret;
+ }
+
+ qcom_pcie_host_deinit(pp);
+
+ ret = qcom_pcie_host_init(pp);
+ if (ret) {
+ dev_err(dev, "Host init failed\n");
+ return ret;
+ }
+
+ ret = dw_pcie_setup_rc(pp);
+ if (ret)
+ return ret;
+
+ /*
+ * Re-enable global IRQ events as the PARF_INT_ALL_MASK register is
+ * non-sticky.
+ */
+ if (pcie->global_irq)
+ writel_relaxed(PARF_INT_ALL_LINK_DOWN | PARF_INT_MSI_DEV_0_7,
+ pcie->parf + PARF_INT_ALL_MASK);
+
+ qcom_pcie_start_link(pci);
+
+ ret = dw_pcie_wait_for_link(pci);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "Root Port reset completed\n");
+
+ return 0;
+}
+
static int qcom_pcie_link_transition_count(struct seq_file *s, void *data)
{
struct qcom_pcie *pcie = (struct qcom_pcie *)dev_get_drvdata(s->private);
@@ -1771,6 +1866,27 @@ 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;
+ struct pci_dev *port;
+ unsigned long status = readl_relaxed(pcie->parf + PARF_INT_ALL_STATUS);
+
+ writel_relaxed(status, pcie->parf + PARF_INT_ALL_CLEAR);
+
+ if (test_and_clear_bit(INT_ALL_LINK_DOWN, &status)) {
+ dev_dbg(dev, "Received Link down event\n");
+ for_each_pci_bridge(port, pp->bridge->bus) {
+ if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT)
+ pci_host_handle_link_down(port);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
static void qcom_pci_free_msi(void *ptr)
{
struct dw_pcie_rp *pp = (struct dw_pcie_rp *)ptr;
@@ -1820,6 +1936,23 @@ static const struct pci_ecam_ops pci_qcom_ecam_ops = {
}
};
+/* Check if @node is a child of @dev in DT */
+static bool qcom_pcie_is_child_node(struct device *dev,
+ struct device_node *node)
+{
+ struct device_node *parent;
+
+ for (parent = of_get_parent(node); parent;
+ parent = of_get_next_parent(parent)) {
+ if (parent == dev->of_node) {
+ of_node_put(parent);
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* 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,
@@ -1827,6 +1960,7 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
{
struct device *dev = pcie->pci->dev;
struct qcom_pcie_perst *perst;
+ struct device_node *gpio_np;
struct gpio_desc *reset;
int ret;
@@ -1840,6 +1974,25 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
if (!of_find_property(np, "reset-gpios", NULL))
goto parse_child_node;
+ /*
+ * Skip GPIOs provided by a PCIe device which is a child of the Root
+ * Complex (e.g., a PCIe switch with GPIO controller capability). Such
+ * controllers won't be available at RC probe time and their PERST#
+ * should be controlled by the respective PCI client driver
+ * implementation.
+ */
+ gpio_np = of_parse_phandle(np, "reset-gpios", 0);
+ if (!gpio_np) {
+ dev_err(dev, "Failed to parse GPIO provider\n");
+ return -EINVAL;
+ }
+
+ if (qcom_pcie_is_child_node(dev, gpio_np)) {
+ of_node_put(gpio_np);
+ goto parse_child_node;
+ }
+ of_node_put(gpio_np);
+
reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset",
GPIOD_OUT_HIGH, "PERST#");
if (IS_ERR(reset)) {
@@ -1990,7 +2143,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
struct dw_pcie_rp *pp;
struct resource *res;
struct dw_pcie *pci;
- int ret;
+ int ret, irq;
pcie_cfg = of_device_get_match_data(dev);
if (!pcie_cfg) {
@@ -2135,6 +2288,32 @@ static int qcom_pcie_probe(struct platform_device *pdev)
goto err_phy_exit;
}
+ irq = platform_get_irq_byname_optional(pdev, "global");
+ if (irq > 0) {
+ const char *name;
+
+ 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;
+ }
+
+ 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_DOWN | PARF_INT_MSI_DEV_0_7,
+ pcie->parf + PARF_INT_ALL_MASK);
+
+ pcie->global_irq = irq;
+ }
+
qcom_pcie_icc_opp_update(pcie);
if (pcie->mhi)
@@ -2142,6 +2321,8 @@ 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_port, &pcie->ports, list) {
list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
@@ -2282,6 +2463,7 @@ disable_icc_cpu:
}
static const struct of_device_id qcom_pcie_match[] = {
+ { .compatible = "qcom,hawi-pcie", .data = &cfg_1_9_0 },
{ .compatible = "qcom,pcie-apq8064", .data = &cfg_2_1_0 },
{ .compatible = "qcom,pcie-apq8084", .data = &cfg_1_0_0 },
{ .compatible = "qcom,pcie-ipq4019", .data = &cfg_2_4_0 },
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 8b03c42f8c84..fbe465a29068 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -13,8 +13,11 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/irqchip/arm-gic-v3.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -31,6 +34,10 @@
#define DEVICE_TYPE_RC BIT(4)
#define BIFUR_MOD_SET_ON BIT(0)
+/* MSI Capability */
+#define MSICAP0 0x0050
+#define MSICAP0_MSIE BIT(16)
+
/* PCIe Interrupt Status 0 */
#define PCIEINTSTS0 0x0084
@@ -55,6 +62,14 @@
#define APP_HOLD_PHY_RST BIT(16)
#define APP_LTSSM_ENABLE BIT(0)
+/* INTC address */
+#define AXIINTCADDR 0x0a00
+
+/* INTC control & mask */
+#define AXIINTCCONT 0x0a04
+#define INTC_EN BIT(31)
+#define INTC_MASK GENMASK(11, 3)
+
/* PCIe Power Management Control */
#define PCIEPWRMNGCTRL 0x0070
#define APP_CLK_REQ_N BIT(11)
@@ -305,13 +320,103 @@ static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev)
return rcar;
}
+static int rcar_gen4_pcie_host_msi_addr(struct dw_pcie_rp *pp, u32 *msi_addr)
+{
+ struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
+ struct device_node *msi_node = NULL;
+ struct device *dev = dw->dev;
+ struct resource res;
+ u64 addr;
+ int ret;
+
+ /*
+ * Either the "msi-parent" or the "msi-map" phandle needs to exist
+ * to obtain the MSI node.
+ */
+ of_msi_xlate(dev, &msi_node, 0);
+ if (!msi_node)
+ return -ENODEV;
+
+ /* Check if "msi-parent" or the "msi-map" points to ARM GICv3 ITS. */
+ if (!of_device_is_compatible(msi_node, "arm,gic-v3-its"))
+ return dev_err_probe(dev, -ENODEV, "Compatible MSI controller not found\n");
+
+ /* Derive GITS_TRANSLATER address from GICv3 */
+ ret = of_address_to_resource(msi_node, 0, &res);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "MSI controller resources not obtained\n");
+
+ addr = res.start + GITS_TRANSLATER;
+ if (addr >= SZ_4G)
+ return dev_err_probe(dev, -EINVAL, "MSI controller address above 32bit range\n");
+
+ *msi_addr = addr;
+ return 0;
+}
+
+static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
+ struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
+ u32 val;
+ int ret;
+
+ /* Make sure MSICAP0 MSIE is configured. */
+ val = dw_pcie_readl_dbi(dw, MSICAP0);
+ if (pci_msi_enabled())
+ val |= MSICAP0_MSIE;
+ else
+ val &= ~MSICAP0_MSIE;
+ dw_pcie_writel_dbi(dw, MSICAP0, val);
+
+ if (!pci_msi_enabled() || pp->use_imsi_rx) {
+ /* Clear AXIINTC mapping. */
+ writel(0, rcar->base + AXIINTCADDR);
+ writel(0, rcar->base + AXIINTCCONT);
+ } else {
+ ret = rcar_gen4_pcie_host_msi_addr(pp, &val);
+ if (ret)
+ goto err;
+
+ /* Point AXIINTC to GIC ITS and enable. */
+ writel(val, rcar->base + AXIINTCADDR);
+ writel(INTC_EN | INTC_MASK, rcar->base + AXIINTCCONT);
+ }
+
+ /* Configure MSI interrupt signal */
+ val = readl(rcar->base + PCIEINTSTS0EN);
+ if (pci_msi_enabled())
+ val |= MSI_CTRL_INT;
+ else
+ val &= ~MSI_CTRL_INT;
+ writel(val, rcar->base + PCIEINTSTS0EN);
+
+ return 0;
+
+err:
+ /* Deconfigure MSICAP0 MSIE. */
+ val = dw_pcie_readl_dbi(dw, MSICAP0);
+ val &= ~MSICAP0_MSIE;
+ dw_pcie_writel_dbi(dw, MSICAP0, val);
+
+ /* Clear AXIINTC mapping. */
+ writel(0, rcar->base + AXIINTCADDR);
+ writel(0, rcar->base + AXIINTCCONT);
+
+ /* Deconfigure MSI interrupt signal */
+ val = readl(rcar->base + PCIEINTSTS0EN);
+ val &= ~MSI_CTRL_INT;
+ writel(val, rcar->base + PCIEINTSTS0EN);
+
+ return ret;
+}
+
/* Host mode */
static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
- u32 val;
gpiod_set_value_cansleep(dw->pe_rst, 1);
@@ -328,16 +433,19 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_0, 0x0);
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_1, 0x0);
- /* Enable MSI interrupt signal */
- val = readl(rcar->base + PCIEINTSTS0EN);
- val |= MSI_CTRL_INT;
- writel(val, rcar->base + PCIEINTSTS0EN);
+ ret = rcar_gen4_pcie_host_msi_init(pp);
+ if (ret)
+ goto err;
msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
gpiod_set_value_cansleep(dw->pe_rst, 0);
return 0;
+
+err:
+ rcar_gen4_pcie_common_deinit(rcar);
+ return ret;
}
static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
@@ -373,7 +481,7 @@ static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
}
/* Endpoint mode */
-static void rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
+static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
{
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
@@ -381,9 +489,11 @@ static void rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
ret = rcar_gen4_pcie_common_init(rcar);
if (ret)
- return;
+ return ret;
writel(PCIEDMAINTSTSEN_INIT, rcar->base + PCIEDMAINTSTSEN);
+
+ return 0;
}
static void rcar_gen4_pcie_ep_deinit(struct rcar_gen4_pcie *rcar)
diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
index 59a806004d40..0564c46e2f48 100644
--- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c
+++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
@@ -338,6 +338,7 @@ static const struct of_device_id k1_pcie_of_match_table[] = {
{ .compatible = "spacemit,k1-pcie", },
{ }
};
+MODULE_DEVICE_TABLE(of, k1_pcie_of_match_table);
static struct platform_driver k1_pcie_driver = {
.probe = k1_pcie_probe,
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 795cef5a915d..63a0f56cad8e 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -2196,15 +2196,15 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
if (IS_ERR(pcie->pex_ctl_supply)) {
ret = PTR_ERR(pcie->pex_ctl_supply);
if (ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to get regulator: %ld\n",
- PTR_ERR(pcie->pex_ctl_supply));
+ dev_err(dev, "Failed to get regulator: %pe\n",
+ pcie->pex_ctl_supply);
return ret;
}
pcie->core_clk = devm_clk_get(dev, "core");
if (IS_ERR(pcie->core_clk)) {
- dev_err(dev, "Failed to get core clock: %ld\n",
- PTR_ERR(pcie->core_clk));
+ dev_err(dev, "Failed to get core clock: %pe\n",
+ pcie->core_clk);
return PTR_ERR(pcie->core_clk);
}
@@ -2226,8 +2226,8 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
pcie->core_apb_rst = devm_reset_control_get(dev, "apb");
if (IS_ERR(pcie->core_apb_rst)) {
- dev_err(dev, "Failed to get APB reset: %ld\n",
- PTR_ERR(pcie->core_apb_rst));
+ dev_err(dev, "Failed to get APB reset: %pe\n",
+ pcie->core_apb_rst);
return PTR_ERR(pcie->core_apb_rst);
}
@@ -2268,8 +2268,8 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
pcie->core_rst = devm_reset_control_get(dev, "core");
if (IS_ERR(pcie->core_rst)) {
- dev_err(dev, "Failed to get core reset: %ld\n",
- PTR_ERR(pcie->core_rst));
+ dev_err(dev, "Failed to get core reset: %pe\n",
+ pcie->core_rst);
return PTR_ERR(pcie->core_rst);
}
diff --git a/drivers/pci/controller/dwc/pcie-ultrarisc.c b/drivers/pci/controller/dwc/pcie-ultrarisc.c
index 6ee661ceff67..51e89180490d 100644
--- a/drivers/pci/controller/dwc/pcie-ultrarisc.c
+++ b/drivers/pci/controller/dwc/pcie-ultrarisc.c
@@ -5,6 +5,7 @@
* Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd.
*/
+#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -23,6 +24,12 @@
#define ULTRARISC_PCIE_COMP_TIMEOUT_65_210MS 0x6
+struct ultrarisc_pcie {
+ struct dw_pcie pci;
+ struct clk_bulk_data *clks;
+ int num_clks;
+};
+
static struct pci_ops ultrarisc_pci_ops = {
.map_bus = dw_pcie_own_conf_map_bus,
.read = pci_generic_config_read32,
@@ -98,17 +105,47 @@ static const struct dw_pcie_ops dw_pcie_ops = {
.start_link = ultrarisc_pcie_start_link,
};
+static int ultrarisc_pcie_enable_clks(struct ultrarisc_pcie *ultra)
+{
+ return clk_bulk_prepare_enable(ultra->num_clks, ultra->clks);
+}
+
+static void ultrarisc_pcie_disable_clks(void *data)
+{
+ struct ultrarisc_pcie *ultra = data;
+
+ clk_bulk_disable_unprepare(ultra->num_clks, ultra->clks);
+}
+
+static int ultrarisc_pcie_init_clks(struct ultrarisc_pcie *ultra)
+{
+ struct device *dev = ultra->pci.dev;
+ int ret;
+
+ ultra->num_clks = devm_clk_bulk_get_all(dev, &ultra->clks);
+ if (ultra->num_clks < 0)
+ return dev_err_probe(dev, ultra->num_clks, "Failed to get clocks\n");
+
+ ret = ultrarisc_pcie_enable_clks(ultra);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable clocks\n");
+
+ return devm_add_action_or_reset(dev, ultrarisc_pcie_disable_clks, ultra);
+}
+
static int ultrarisc_pcie_probe(struct platform_device *pdev)
{
+ struct ultrarisc_pcie *ultra;
struct device *dev = &pdev->dev;
struct dw_pcie_rp *pp;
struct dw_pcie *pci;
int ret;
- pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
- if (!pci)
+ ultra = devm_kzalloc(dev, sizeof(*ultra), GFP_KERNEL);
+ if (!ultra)
return -ENOMEM;
+ pci = &ultra->pci;
pci->dev = dev;
pci->ops = &dw_pcie_ops;
@@ -117,7 +154,11 @@ static int ultrarisc_pcie_probe(struct platform_device *pdev)
pp = &pci->pp;
- platform_set_drvdata(pdev, pci);
+ platform_set_drvdata(pdev, ultra);
+
+ ret = ultrarisc_pcie_init_clks(ultra);
+ if (ret)
+ return ret;
pp->num_vectors = MAX_MSI_IRQS;
/* No L2/L3 Ready indication is available on this platform */
@@ -135,16 +176,46 @@ static int ultrarisc_pcie_probe(struct platform_device *pdev)
static int ultrarisc_pcie_suspend_noirq(struct device *dev)
{
- struct dw_pcie *pci = dev_get_drvdata(dev);
+ struct ultrarisc_pcie *ultra = dev_get_drvdata(dev);
+ struct dw_pcie *pci = &ultra->pci;
+ int ret;
+
+ /*
+ * A failed resume leaves the DWC suspended and the clocks disabled.
+ * A later suspend must not access the controller or disable them again.
+ */
+ if (pci->suspended)
+ return 0;
+
+ ret = dw_pcie_suspend_noirq(pci);
+ if (ret)
+ return ret;
+
+ if (pci->suspended)
+ ultrarisc_pcie_disable_clks(ultra);
- return dw_pcie_suspend_noirq(pci);
+ return 0;
}
static int ultrarisc_pcie_resume_noirq(struct device *dev)
{
- struct dw_pcie *pci = dev_get_drvdata(dev);
+ struct ultrarisc_pcie *ultra = dev_get_drvdata(dev);
+ struct dw_pcie *pci = &ultra->pci;
+ int ret;
- return dw_pcie_resume_noirq(pci);
+ if (pci->suspended) {
+ ret = ultrarisc_pcie_enable_clks(ultra);
+ if (ret)
+ return ret;
+
+ ret = dw_pcie_resume_noirq(pci);
+ if (ret) {
+ ultrarisc_pcie_disable_clks(ultra);
+ return ret;
+ }
+ }
+
+ return 0;
}
static const struct dev_pm_ops ultrarisc_pcie_pm_ops = {
@@ -169,7 +240,7 @@ static struct platform_driver ultrarisc_pcie_driver = {
},
.probe = ultrarisc_pcie_probe,
};
-builtin_platform_driver(ultrarisc_pcie_driver);
+module_platform_driver(ultrarisc_pcie_driver);
MODULE_DESCRIPTION("UltraRISC DP1000 DWC PCIe host controller");
MODULE_LICENSE("GPL");
diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
index 62ecbaeb0a60..e8346851c49b 100644
--- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
@@ -218,11 +218,11 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
int retries;
/* check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) {
if (mobiveil_pcie_link_up(pcie))
return 0;
- usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX);
+ msleep(PCIE_LINK_WAIT_SLEEP_MS);
}
dev_err(&pcie->pdev->dev, "link never came up\n");
diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
index 7246de6a7176..11010a99e27c 100644
--- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
@@ -122,11 +122,6 @@
#define IB_WIN_SIZE ((u64)256 * 1024 * 1024 * 1024)
#define MAX_PIO_WINDOWS 8
-/* Parameters for the waiting for link up routine */
-#define LINK_WAIT_MAX_RETRIES 10
-#define LINK_WAIT_MIN 90000
-#define LINK_WAIT_MAX 100000
-
#define PAGED_ADDR_BNDRY 0xc00
#define OFFSET_TO_PAGE_ADDR(off) \
((off & PAGE_LO_MASK) | PAGED_ADDR_BNDRY)
diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
index fd9c7d53e8a7..ecb81ac73019 100644
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -256,9 +256,6 @@ enum {
#define PIO_RETRY_CNT 750000 /* 1.5 s */
#define PIO_RETRY_DELAY 2 /* 2 us*/
-#define LINK_WAIT_MAX_RETRIES 10
-#define LINK_WAIT_USLEEP_MIN 90000
-#define LINK_WAIT_USLEEP_MAX 100000
#define RETRAIN_WAIT_MAX_RETRIES 10
#define RETRAIN_WAIT_USLEEP_US 2000
@@ -350,13 +347,13 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
int retries;
/* check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) {
if (advk_pcie_link_up(pcie)) {
pci_host_common_link_train_delay(pcie->link_gen);
return 0;
}
- usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+ msleep(PCIE_LINK_WAIT_SLEEP_MS);
}
return -ETIMEDOUT;
@@ -1722,7 +1719,7 @@ static int advk_pcie_setup_phy(struct advk_pcie *pcie)
/* Old bindings miss the PHY handle */
if (IS_ERR(pcie->phy)) {
- dev_warn(dev, "PHY unavailable (%ld)\n", PTR_ERR(pcie->phy));
+ dev_warn(dev, "PHY unavailable (%pe)\n", pcie->phy);
pcie->phy = NULL;
return 0;
}
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index 2ce6f4b66133..a23907a875e5 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -13,9 +13,11 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
+#include <linux/pci.h>
#include <linux/pci-ecam.h>
#include <linux/platform_device.h>
+#include "../pci.h"
#include "pci-host-common.h"
/**
@@ -108,8 +110,7 @@ parse_child_node:
* dependencies and the driver may fail to operate if required resources
* are missing.
*
- * Return: 0 on success, -ENODEV if PERST# found in RC node (legacy binding
- * should be used), Other negative error codes on failure.
+ * Return: 0 on success, negative error codes on failure.
*/
static int pci_host_common_parse_port(struct device *dev,
struct pci_host_bridge *bridge,
@@ -128,22 +129,6 @@ static int pci_host_common_parse_port(struct device *dev,
if (ret)
return ret;
- /*
- * 1. PERST# found in RP or its child nodes - list is not empty,
- * continue
- *
- * 2. PERST# not found in RP/children, but found in RC node -
- * return -ENODEV to fallback legacy binding
- *
- * 3. PERST# not found anywhere - list is empty, continue (optional
- * PERST#)
- */
- if (list_empty(&port->perst)) {
- if (of_property_present(dev->of_node, "reset-gpios") ||
- of_property_present(dev->of_node, "reset-gpio"))
- return -ENODEV;
- }
-
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &bridge->ports);
@@ -158,13 +143,11 @@ static int pci_host_common_parse_port(struct device *dev,
* Iterate through child nodes of the host bridge and parse Root Port
* properties (currently only reset GPIOs).
*
- * Return: 0 on success, -ENODEV if no ports found or PERST# found in RC
- * node (legacy binding should be used), Other negative error codes on
- * failure.
+ * Return: 0 on success or ports not found, negative error codes on failure.
*/
int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *bridge)
{
- int ret = -ENODEV;
+ int ret = 0;
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
@@ -174,8 +157,8 @@ int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *brid
goto err_cleanup;
}
- if (ret)
- return ret;
+ if (list_empty(&bridge->ports))
+ return 0;
return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
&bridge->ports);
@@ -342,5 +325,38 @@ bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge,
}
EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible);
+static pci_ers_result_t pci_host_reset_root_port(struct pci_dev *dev)
+{
+ int ret;
+
+ pci_lock_rescan_remove();
+ ret = pci_bus_error_reset(dev);
+ pci_unlock_rescan_remove();
+ if (ret) {
+ pci_err(dev, "Failed to reset Root Port: %d\n", ret);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ pci_info(dev, "Root Port has been reset\n");
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void pci_host_recover_root_port(struct pci_dev *port)
+{
+#if IS_ENABLED(CONFIG_PCIEAER)
+ pcie_do_recovery(port, pci_channel_io_frozen, pci_host_reset_root_port);
+#else
+ pci_host_reset_root_port(port);
+#endif
+}
+
+void pci_host_handle_link_down(struct pci_dev *port)
+{
+ pci_info(port, "Recovering Root Port due to Link Down\n");
+ pci_host_recover_root_port(port);
+}
+EXPORT_SYMBOL_GPL(pci_host_handle_link_down);
+
MODULE_DESCRIPTION("Common library for PCI host controller drivers");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h
index 9f0f36a32221..51afd65d3c4a 100644
--- a/drivers/pci/controller/pci-host-common.h
+++ b/drivers/pci/controller/pci-host-common.h
@@ -48,6 +48,7 @@ int pci_host_common_init(struct platform_device *pdev,
struct pci_host_bridge *bridge,
const struct pci_ecam_ops *ops);
void pci_host_common_remove(struct platform_device *pdev);
+void pci_host_handle_link_down(struct pci_dev *port);
struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops);
diff --git a/drivers/pci/controller/pci-host-generic.c b/drivers/pci/controller/pci-host-generic.c
index c1bc0d34348f..9e85c6e9b425 100644
--- a/drivers/pci/controller/pci-host-generic.c
+++ b/drivers/pci/controller/pci-host-generic.c
@@ -16,15 +16,6 @@
#include "pci-host-common.h"
-static const struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = {
- .bus_shift = 16,
- .pci_ops = {
- .map_bus = pci_ecam_map_bus,
- .read = pci_generic_config_read,
- .write = pci_generic_config_write,
- }
-};
-
static bool pci_dw_valid_device(struct pci_bus *bus, unsigned int devfn)
{
struct pci_config_window *cfg = bus->sysdata;
@@ -60,7 +51,7 @@ static const struct pci_ecam_ops pci_dw_ecam_bus_ops = {
static const struct of_device_id gen_pci_of_match[] = {
{ .compatible = "pci-host-cam-generic",
- .data = &gen_pci_cfg_cam_bus_ops },
+ .data = &pci_generic_cam_ops },
{ .compatible = "pci-host-ecam-generic",
.data = &pci_generic_ecam_ops },
diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c
index 229c69534476..59b56446c507 100644
--- a/drivers/pci/controller/pci-tegra.c
+++ b/drivers/pci/controller/pci-tegra.c
@@ -1356,8 +1356,7 @@ static int tegra_pcie_port_get_phys(struct tegra_pcie_port *port)
for (i = 0; i < port->lanes; i++) {
phy = devm_of_phy_optional_get_index(dev, port->np, "pcie", i);
if (IS_ERR(phy)) {
- dev_err(dev, "failed to get PHY#%u: %ld\n", i,
- PTR_ERR(phy));
+ dev_err(dev, "failed to get PHY#%u: %pe\n", i, phy);
return PTR_ERR(phy);
}
diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c
index b95afa35201d..83c9a2930eec 100644
--- a/drivers/pci/controller/pci-xgene.c
+++ b/drivers/pci/controller/pci-xgene.c
@@ -58,7 +58,6 @@
#define XGENE_PCIE_IP_VER_2 2
struct xgene_pcie {
- struct device_node *node;
struct device *dev;
struct clk *clk;
void __iomem *csr_base;
@@ -526,7 +525,7 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie *port,
static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie *port)
{
- struct device_node *np = port->node;
+ struct device_node *np = port->dev->of_node;
struct of_pci_range range;
struct of_pci_range_parser parser;
struct device *dev = port->dev;
@@ -612,7 +611,6 @@ static bool xgene_check_pcie_msi_ready(void)
static int xgene_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *dn = dev->of_node;
struct xgene_pcie *port;
struct pci_host_bridge *bridge;
int ret;
@@ -627,7 +625,6 @@ static int xgene_pcie_probe(struct platform_device *pdev)
port = pci_host_bridge_priv(bridge);
- port->node = of_node_get(dn);
port->dev = dev;
port->version = XGENE_PCIE_IP_VER_1;
diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c
index 9aa9e14c6148..74b03a55643f 100644
--- a/drivers/pci/controller/pcie-aspeed.c
+++ b/drivers/pci/controller/pcie-aspeed.c
@@ -725,10 +725,10 @@ 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);
+ pcie->intx_domain = irq_domain_create_linear(dev_fwnode(pcie->dev),
+ 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");
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index 8b57c3d75b94..a60d1ae076f8 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -10,11 +10,13 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-msi-lib.h>
#include <linux/irqdomain.h>
+#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/msi.h>
@@ -78,6 +80,7 @@
#define PCIE_CONF_VEND_ID 0x100
#define PCIE_CONF_DEVICE_ID 0x102
+#define PCIE_CONF_REV_CLASS 0x104
#define PCIE_CONF_CLASS_ID 0x106
#define PCIE_INT_MASK 0x420
@@ -90,6 +93,11 @@
#define MSI_MASK BIT(23)
#define MTK_MSI_IRQS_NUM 32
+#define EN7528_HOST_MODE 0x00804201
+#define EN7528_LINKUP_REG 0x50
+#define EN7528_RC0_LINKUP BIT(1)
+#define EN7528_RC1_LINKUP BIT(2)
+
#define PCIE_AHB_TRANS_BASE0_L 0x438
#define PCIE_AHB_TRANS_BASE0_H 0x43c
#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
@@ -145,12 +153,15 @@ struct mtk_pcie_port;
* @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed
* @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block
* @MTK_PCIE_SKIP_RSTB: Skip calling RSTB bits on PCIe probe
+ * @MTK_PCIE_RETRAIN: Retrain link to bridge after startup because some
+ * Gen2-capable devices start as Gen1.
*/
enum mtk_pcie_quirks {
MTK_PCIE_FIX_CLASS_ID = BIT(0),
MTK_PCIE_FIX_DEVICE_ID = BIT(1),
MTK_PCIE_NO_MSI = BIT(2),
MTK_PCIE_SKIP_RSTB = BIT(3),
+ MTK_PCIE_RETRAIN = BIT(4),
};
/**
@@ -756,6 +767,134 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
return 0;
}
+static int mtk_pcie_startup_port_en7528(struct mtk_pcie_port *port)
+{
+ struct mtk_pcie *pcie = port->pcie;
+ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ struct resource *mem = NULL;
+ struct resource_entry *entry;
+ u32 val, link_mask;
+ int err;
+
+ entry = resource_list_first_type(&host->windows, IORESOURCE_MEM);
+ if (entry)
+ mem = entry->res;
+ if (!mem)
+ return -EINVAL;
+
+ if (!pcie->cfg) {
+ dev_err(pcie->dev, "EN7528: pciecfg syscon not available\n");
+ return -EINVAL;
+ }
+
+ /* Assert all reset signals */
+ writel(0, port->base + PCIE_RST_CTRL);
+
+ /*
+ * Enable PCIe link down reset, if link status changed from link up to
+ * link down, this will reset MAC control registers and configuration
+ * space.
+ */
+ writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
+
+ msleep(PCIE_T_PVPERL_MS);
+
+ /* De-assert PHY, PE, PIPE, MAC and configuration reset */
+ val = readl(port->base + PCIE_RST_CTRL);
+ val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
+ PCIE_MAC_SRSTB | PCIE_CRSTB;
+ writel(val, port->base + PCIE_RST_CTRL);
+
+ writel(PCIE_CLASS_CODE | PCIE_REVISION_ID,
+ port->base + PCIE_CONF_REV_CLASS);
+ writel(EN7528_HOST_MODE, port->base);
+
+ link_mask = (port->slot == 0) ? EN7528_RC0_LINKUP : EN7528_RC1_LINKUP;
+
+ /* 100ms timeout value should be enough for Gen1/2 training */
+ err = regmap_read_poll_timeout(pcie->cfg, EN7528_LINKUP_REG, val,
+ !!(val & link_mask), 20,
+ PCI_PM_D3COLD_WAIT * USEC_PER_MSEC);
+ if (err) {
+ dev_err(pcie->dev, "EN7528: port%d link timeout\n", port->slot);
+ return -ETIMEDOUT;
+ }
+
+ /* Activate INTx interrupts */
+ val = readl(port->base + PCIE_INT_MASK);
+ val &= ~INTX_MASK;
+ writel(val, port->base + PCIE_INT_MASK);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ mtk_pcie_enable_msi(port);
+
+ /* Set AHB to PCIe translation windows */
+ val = lower_32_bits(mem->start) |
+ AHB2PCIE_SIZE(fls(resource_size(mem)));
+ writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
+
+ val = upper_32_bits(mem->start);
+ writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
+
+ writel(WIN_ENABLE, port->base + PCIE_AXI_WINDOW0);
+
+ if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
+ dev_info(pcie->dev,
+ "module not built-in, Gen2 unavailable even if supported\n");
+
+ return 0;
+}
+
+/**
+ * mtk_pcie_retrain - retrain the root bridge link if needed
+ * @dev: The device, for use in logging
+ * @host: The host bridge which contains the link
+ *
+ * Due to what is likely a hardware bug, some devices (notably EcoNet) start up
+ * as Gen1, and must be retrained once after initial configuration in order to
+ * reach Gen2.
+ *
+ * These devices always self-identify as Gen2 capable, but sometimes the PHY is
+ * only capable of Gen1 operation, and sometimes the PCIe card (e.g. wifi) is
+ * only Gen1 capable. Therefore it is most convenient to retrain every port
+ * after startup.
+ */
+static int mtk_pcie_retrain(struct device *dev, struct pci_host_bridge *host)
+{
+ struct pci_dev *rp;
+ int ret = -ENOENT;
+ u16 lnksta = 0;
+ u32 speed;
+
+ /* Should already have been warned about during startup_port */
+ if (!IS_BUILTIN(CONFIG_PCIE_MEDIATEK))
+ return 0;
+
+ guard(rwsem_read)(&pci_bus_sem);
+
+ for_each_pci_bridge(rp, host->bus) {
+ if (pci_pcie_type(rp) != PCI_EXP_TYPE_ROOT_PORT)
+ continue;
+
+#if IS_BUILTIN(CONFIG_PCIE_MEDIATEK)
+ ret = pcie_retrain_link(rp, true);
+#endif
+
+ if (ret)
+ return dev_err_probe(&rp->dev, ret,
+ "failed to retrain port\n");
+
+ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta);
+ speed = lnksta & PCI_EXP_LNKSTA_CLS;
+
+ pci_info(rp, "link retrained, speed %s\n",
+ pci_speed_string(pcie_link_speed[speed]));
+
+ }
+
+ return 0;
+}
+
static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn, int where)
{
@@ -1169,6 +1308,13 @@ static int mtk_pcie_probe(struct platform_device *pdev)
if (err)
goto put_resources;
+ /*
+ * Ignore error because pci_host_probe() was already called, and in any
+ * case it is possible that the port will still work as Gen1.
+ */
+ if (pcie->soc->quirks & MTK_PCIE_RETRAIN)
+ mtk_pcie_retrain(dev, host);
+
return 0;
put_resources:
@@ -1290,8 +1436,16 @@ static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = {
.quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_FIX_DEVICE_ID,
};
+static const struct mtk_pcie_soc mtk_pcie_soc_en7528 = {
+ .ops = &mtk_pcie_ops_v2,
+ .startup = mtk_pcie_startup_port_en7528,
+ .setup_irq = mtk_pcie_setup_irq,
+ .quirks = MTK_PCIE_RETRAIN,
+};
+
static const struct of_device_id mtk_pcie_ids[] = {
{ .compatible = "airoha,an7583-pcie", .data = &mtk_pcie_soc_an7583 },
+ { .compatible = "econet,en7528-pcie", .data = &mtk_pcie_soc_en7528 },
{ .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_mt2712 },
diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c
index 0f88da378805..8d78ab1b31f0 100644
--- a/drivers/pci/controller/pcie-rockchip.c
+++ b/drivers/pci/controller/pcie-rockchip.c
@@ -232,8 +232,8 @@ int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip)
if (IS_ERR(phy)) {
if (PTR_ERR(phy) != -EPROBE_DEFER)
- dev_err(dev, "missing phy for lane %d: %ld\n",
- i, PTR_ERR(phy));
+ dev_err(dev, "missing phy for lane %d: %pe\n",
+ i, phy);
return PTR_ERR(phy);
}
diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
index 66f687304c1c..a1a78bdb7af1 100644
--- a/drivers/pci/controller/pcie-rzg3s-host.c
+++ b/drivers/pci/controller/pcie-rzg3s-host.c
@@ -181,6 +181,16 @@
#define RZG3S_REQ_ISSUE_TIMEOUT_US 2500
/**
+ * enum rzg3s_sysc_link_mode - PCIe link configuration modes
+ * @RZG3S_SYSC_LINK_MODE_SINGLE_X4: Single port with x4 lanes
+ * @RZG3S_SYSC_LINK_MODE_DUAL_X2: Dual ports with x2 lanes each
+ */
+enum rzg3s_sysc_link_mode {
+ RZG3S_SYSC_LINK_MODE_SINGLE_X4 = 1,
+ RZG3S_SYSC_LINK_MODE_DUAL_X2 = 3,
+};
+
+/**
* struct rzg3s_sysc_function - System Controller function descriptor
* @offset: Register offset from the System Controller base address
* @mask: Bit mask for the function within the register
@@ -195,12 +205,14 @@ struct rzg3s_sysc_function {
* @RZG3S_SYSC_FUNC_ID_RST_RSM_B: RST_RSM_B SYSC function ID
* @RZG3S_SYSC_FUNC_ID_L1_ALLOW: L1 allow SYSC function ID
* @RZG3S_SYSC_FUNC_ID_MODE: Mode SYSC function ID
+ * @RZG3S_SYSC_FUNC_ID_LINK_MASTER: Link master SYSC function ID
* @RZG3S_SYSC_FUNC_ID_MAX: Max SYSC function ID
*/
enum rzg3s_sysc_func_id {
RZG3S_SYSC_FUNC_ID_RST_RSM_B,
RZG3S_SYSC_FUNC_ID_L1_ALLOW,
RZG3S_SYSC_FUNC_ID_MODE,
+ RZG3S_SYSC_FUNC_ID_LINK_MASTER,
RZG3S_SYSC_FUNC_ID_MAX,
};
@@ -242,6 +254,18 @@ struct rzg3s_pcie_msi {
int irq;
};
+/**
+ * enum rzg3s_pcie_controller_id - RZ/G3S PCIe controller IDs
+ * @RZG3S_PCIE_CONTROLLER_ID_0: PCIe controller 0
+ * @RZG3S_PCIE_CONTROLLER_ID_1: PCIe controller 1
+ * @RZG3S_PCIE_CONTROLLER_ID_MAX: Max PCIe controllers
+ */
+enum rzg3s_pcie_controller_id {
+ RZG3S_PCIE_CONTROLLER_ID_0,
+ RZG3S_PCIE_CONTROLLER_ID_1,
+ RZG3S_PCIE_CONTROLLER_ID_MAX,
+};
+
struct rzg3s_pcie_host;
/**
@@ -250,24 +274,28 @@ struct rzg3s_pcie_host;
* @config_pre_init: Optional callback for SoC-specific pre-configuration
* @config_post_init: Callback for SoC-specific post-configuration
* @config_deinit: Callback for SoC-specific de-initialization
+ * @setup_lanes: Callback for setting up the number of lanes
* @power_resets: array with the resets that need to be de-asserted after
* power-on
* @cfg_resets: array with the resets that need to be de-asserted after
* configuration
- * @sysc_info: SYSC info
+ * @sysc_info: System Controller info for each controller
* @num_power_resets: number of power resets
* @num_cfg_resets: number of configuration resets
+ * @num_pcie_controllers: number of PCIe controllers
*/
struct rzg3s_pcie_soc_data {
int (*init_phy)(struct rzg3s_pcie_host *host);
void (*config_pre_init)(struct rzg3s_pcie_host *host);
int (*config_post_init)(struct rzg3s_pcie_host *host);
int (*config_deinit)(struct rzg3s_pcie_host *host);
+ int (*setup_lanes)(struct rzg3s_pcie_host *host);
const char * const *power_resets;
const char * const *cfg_resets;
- struct rzg3s_sysc_info sysc_info;
+ struct rzg3s_sysc_info sysc_info[RZG3S_PCIE_CONTROLLER_ID_MAX];
u8 num_power_resets;
u8 num_cfg_resets;
+ u8 num_pcie_controllers;
};
/**
@@ -297,6 +325,8 @@ struct rzg3s_pcie_port {
* @hw_lock: lock for access to the HW resources
* @intx_irqs: INTx interrupts
* @max_link_speed: maximum supported link speed
+ * @controller_id: PCIe controller identifier, used for System Controller access
+ * @num_lanes: The number of lanes
*/
struct rzg3s_pcie_host {
void __iomem *axi;
@@ -312,10 +342,24 @@ struct rzg3s_pcie_host {
raw_spinlock_t hw_lock;
int intx_irqs[PCI_NUM_INTX];
int max_link_speed;
+ enum rzg3s_pcie_controller_id controller_id;
+ u8 num_lanes;
};
#define rzg3s_msi_to_host(_msi) container_of(_msi, struct rzg3s_pcie_host, msi)
+/*
+ * RZ/V2H(P) supports a total of 4 lanes shared across two controllers.
+ * rzv2h_lane_lock serialises both the counter update and the SYSC
+ * register write so that concurrent async probes cannot race on the
+ * shared LINK_MASTER register (offset 0x1060).
+ * rzv2h_num_total_lanes tracks global lane usage to prevent
+ * over-allocation or invalid bifurcation modes.
+ */
+#define RZV2H_PCIE_MAX_LANES 4
+static DEFINE_SPINLOCK(rzv2h_lane_lock);
+static u8 rzv2h_num_total_lanes;
+
static int rzg3s_sysc_config_func(struct rzg3s_sysc *sysc,
enum rzg3s_sysc_func_id fid, u32 val)
{
@@ -1142,6 +1186,13 @@ static int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host)
rzg3s_pcie_update_bits(host->pcie, PCI_CLASS_REVISION, mask,
field_prep(mask, PCI_CLASS_BRIDGE_PCI_NORMAL));
+ if (host->num_lanes) {
+ rzg3s_pcie_update_bits(host->pcie + RZG3S_PCI_CFG_PCIEC,
+ PCI_EXP_LNKCAP, PCI_EXP_LNKCAP_MLW,
+ FIELD_PREP(PCI_EXP_LNKCAP_MLW,
+ host->num_lanes));
+ }
+
/* Disable access control to the CFGU */
writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
@@ -1277,9 +1328,9 @@ static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host)
for (i = 0; i < data->num_cfg_resets; i++)
host->cfg_resets[i].id = data->cfg_resets[i];
- ret = devm_reset_control_bulk_get_exclusive(host->dev,
- data->num_power_resets,
- host->power_resets);
+ ret = devm_reset_control_bulk_get_shared(host->dev,
+ data->num_power_resets,
+ host->power_resets);
if (ret)
return ret;
@@ -1674,6 +1725,76 @@ teardown_irqdomain:
return ret;
}
+static int rzg3s_pcie_get_controller_id(struct rzg3s_pcie_host *host)
+{
+ struct device_node *np = host->dev->of_node;
+ struct of_phandle_args sysc_args;
+ int ret;
+
+ if (host->data->num_pcie_controllers == 1)
+ return 0;
+
+ ret = of_parse_phandle_with_fixed_args(np, "renesas,sysc", 1, 0, &sysc_args);
+ if (ret)
+ return ret;
+
+ of_node_put(sysc_args.np);
+
+ if (sysc_args.args[0] >= host->data->num_pcie_controllers ||
+ sysc_args.args[0] >= RZG3S_PCIE_CONTROLLER_ID_MAX)
+ return -EINVAL;
+
+ host->controller_id = sysc_args.args[0];
+
+ return 0;
+}
+
+static int rzv2h_pcie_setup_lanes(struct rzg3s_pcie_host *host)
+{
+ struct device_node *np = host->dev->of_node;
+ u32 num_lanes;
+ int ret;
+
+ ret = of_property_read_u32(np, "num-lanes", &num_lanes);
+ if (ret)
+ return ret;
+
+ /*
+ * RZ/V2H(P) supports up to 4 lanes, but only in single x4 mode
+ * for the first controller. Dual x2 mode is supported with 2
+ * lanes for both controllers.
+ */
+ if (num_lanes != 4 && num_lanes != 2)
+ return -EINVAL;
+
+ if (host->controller_id == RZG3S_PCIE_CONTROLLER_ID_1 && num_lanes > 2)
+ return -EINVAL;
+
+ guard(spinlock)(&rzv2h_lane_lock);
+ if (rzv2h_num_total_lanes + num_lanes > RZV2H_PCIE_MAX_LANES)
+ return -EINVAL;
+
+ ret = rzg3s_sysc_config_func(host->sysc, RZG3S_SYSC_FUNC_ID_LINK_MASTER,
+ num_lanes == 2 ?
+ RZG3S_SYSC_LINK_MODE_DUAL_X2 :
+ RZG3S_SYSC_LINK_MODE_SINGLE_X4);
+ if (ret)
+ return ret;
+
+ rzv2h_num_total_lanes += num_lanes;
+ host->num_lanes = num_lanes;
+
+ return 0;
+}
+
+static void rzv2h_pcie_release_lanes(void *data)
+{
+ struct rzg3s_pcie_host *host = data;
+
+ guard(spinlock)(&rzv2h_lane_lock);
+ rzv2h_num_total_lanes -= host->num_lanes;
+}
+
static int rzg3s_pcie_probe(struct platform_device *pdev)
{
struct pci_host_bridge *bridge;
@@ -1698,8 +1819,12 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
if (!host->sysc)
return -ENOMEM;
+ ret = rzg3s_pcie_get_controller_id(host);
+ if (ret)
+ return ret;
+
sysc = host->sysc;
- sysc->info = &host->data->sysc_info;
+ sysc->info = &host->data->sysc_info[host->controller_id];
host->axi = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(host->axi))
@@ -1727,6 +1852,16 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
if (ret)
goto port_refclk_put;
+ if (host->data->setup_lanes) {
+ ret = host->data->setup_lanes(host);
+ if (ret)
+ goto sysc_signal_restore;
+
+ ret = devm_add_action_or_reset(dev, rzv2h_pcie_release_lanes, host);
+ if (ret)
+ goto sysc_signal_restore;
+ }
+
ret = rzg3s_pcie_resets_prepare_and_get(host);
if (ret)
goto sysc_signal_restore;
@@ -1841,6 +1976,16 @@ static int rzg3s_pcie_resume_noirq(struct device *dev)
if (ret)
return ret;
+ if (host->num_lanes) {
+ ret = rzg3s_sysc_config_func(host->sysc,
+ RZG3S_SYSC_FUNC_ID_LINK_MASTER,
+ host->num_lanes == 2 ?
+ RZG3S_SYSC_LINK_MODE_DUAL_X2 :
+ RZG3S_SYSC_LINK_MODE_SINGLE_X4);
+ if (ret)
+ goto assert_rst_rsm_b;
+ }
+
ret = rzg3s_pcie_power_resets_deassert(host);
if (ret)
goto assert_rst_rsm_b;
@@ -1888,14 +2033,17 @@ static const struct rzg3s_pcie_soc_data rzg3s_soc_data = {
.num_power_resets = ARRAY_SIZE(rzg3s_soc_power_resets),
.cfg_resets = rzg3s_soc_cfg_resets,
.num_cfg_resets = ARRAY_SIZE(rzg3s_soc_cfg_resets),
+ .num_pcie_controllers = 1,
.config_post_init = rzg3s_pcie_config_post_init,
.config_deinit = rzg3s_pcie_config_deinit,
.init_phy = rzg3s_soc_pcie_init_phy,
.sysc_info = {
- .functions = {
- [RZG3S_SYSC_FUNC_ID_RST_RSM_B] = {
- .offset = 0xd74,
- .mask = BIT(0),
+ [RZG3S_PCIE_CONTROLLER_ID_0] = {
+ .functions = {
+ [RZG3S_SYSC_FUNC_ID_RST_RSM_B] = {
+ .offset = 0xd74,
+ .mask = BIT(0),
+ },
},
},
},
@@ -1906,18 +2054,65 @@ static const char * const rzg3e_soc_power_resets[] = { "aresetn" };
static const struct rzg3s_pcie_soc_data rzg3e_soc_data = {
.power_resets = rzg3e_soc_power_resets,
.num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets),
+ .num_pcie_controllers = 1,
.config_pre_init = rzg3e_pcie_config_pre_init,
.config_post_init = rzg3e_pcie_config_post_init,
.config_deinit = rzg3e_pcie_config_deinit,
.sysc_info = {
- .functions = {
- [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = {
- .offset = 0x1020,
- .mask = BIT(0),
+ [RZG3S_PCIE_CONTROLLER_ID_0] = {
+ .functions = {
+ [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = {
+ .offset = 0x1020,
+ .mask = BIT(0),
+ },
+ [RZG3S_SYSC_FUNC_ID_MODE] = {
+ .offset = 0x1024,
+ .mask = BIT(0),
+ },
},
- [RZG3S_SYSC_FUNC_ID_MODE] = {
- .offset = 0x1024,
- .mask = BIT(0),
+ },
+ },
+};
+
+static const struct rzg3s_pcie_soc_data rzv2h_soc_data = {
+ .power_resets = rzg3e_soc_power_resets,
+ .num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets),
+ .num_pcie_controllers = 2,
+ .config_pre_init = rzg3e_pcie_config_pre_init,
+ .config_post_init = rzg3e_pcie_config_post_init,
+ .config_deinit = rzg3e_pcie_config_deinit,
+ .setup_lanes = rzv2h_pcie_setup_lanes,
+ .sysc_info = {
+ [RZG3S_PCIE_CONTROLLER_ID_0] = {
+ .functions = {
+ [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = {
+ .offset = 0x1020,
+ .mask = BIT(0),
+ },
+ [RZG3S_SYSC_FUNC_ID_MODE] = {
+ .offset = 0x1024,
+ .mask = BIT(0),
+ },
+ [RZG3S_SYSC_FUNC_ID_LINK_MASTER] = {
+ .offset = 0x1060,
+ .mask = GENMASK(9, 8),
+ },
+ },
+ },
+ [RZG3S_PCIE_CONTROLLER_ID_1] = {
+ .functions = {
+ [RZG3S_SYSC_FUNC_ID_L1_ALLOW] = {
+ .offset = 0x1050,
+ .mask = BIT(0),
+ },
+ [RZG3S_SYSC_FUNC_ID_MODE] = {
+ .offset = 0x1054,
+ .mask = BIT(0),
+ },
+ [RZG3S_SYSC_FUNC_ID_LINK_MASTER] = {
+ .offset = 0x1060,
+ .mask = GENMASK(9, 8),
+ },
},
},
},
@@ -1932,6 +2127,10 @@ static const struct of_device_id rzg3s_pcie_of_match[] = {
.compatible = "renesas,r9a09g047-pcie",
.data = &rzg3e_soc_data,
},
+ {
+ .compatible = "renesas,r9a09g057-pcie",
+ .data = &rzv2h_soc_data,
+ },
{}
};
diff --git a/drivers/pci/controller/pcie-tegra264.c b/drivers/pci/controller/pcie-tegra264.c
new file mode 100644
index 000000000000..653136db401e
--- /dev/null
+++ b/drivers/pci/controller/pcie-tegra264.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCIe host controller driver for Tegra264 SoC
+ *
+ * Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/pci-ecam.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+#include <soc/tegra/fuse.h>
+
+#include "../pci.h"
+
+/* XAL registers */
+#define XAL_RC_ECAM_BASE_HI 0x00
+#define XAL_RC_ECAM_BASE_LO 0x04
+#define XAL_RC_ECAM_BUSMASK 0x08
+#define XAL_RC_IO_BASE_HI 0x0c
+#define XAL_RC_IO_BASE_LO 0x10
+#define XAL_RC_IO_LIMIT_HI 0x14
+#define XAL_RC_IO_LIMIT_LO 0x18
+#define XAL_RC_MEM_32BIT_BASE_HI 0x1c
+#define XAL_RC_MEM_32BIT_BASE_LO 0x20
+#define XAL_RC_MEM_32BIT_LIMIT_HI 0x24
+#define XAL_RC_MEM_32BIT_LIMIT_LO 0x28
+#define XAL_RC_MEM_64BIT_BASE_HI 0x2c
+#define XAL_RC_MEM_64BIT_BASE_LO 0x30
+#define XAL_RC_MEM_64BIT_LIMIT_HI 0x34
+#define XAL_RC_MEM_64BIT_LIMIT_LO 0x38
+#define XAL_RC_BAR_CNTL_STANDARD 0x40
+#define XAL_RC_BAR_CNTL_STANDARD_IOBAR_EN BIT(0)
+#define XAL_RC_BAR_CNTL_STANDARD_32B_BAR_EN BIT(1)
+#define XAL_RC_BAR_CNTL_STANDARD_64B_BAR_EN BIT(2)
+
+/* XTL registers */
+#define XTL_RC_PCIE_CFG_LINK_CAPS 0x56
+#define XTL_RC_PCIE_CFG_LINK_STATUS 0x5a
+
+#define XTL_RC_MGMT_PERST_CONTROL 0x218
+#define XTL_RC_MGMT_PERST_CONTROL_PERST_O_N BIT(0)
+
+#define XTL_RC_MGMT_CLOCK_CONTROL 0x47c
+#define XTL_RC_MGMT_CLOCK_CONTROL_PEX_CLKREQ_I_N_PIN_USE_CONV_TO_PRSNT BIT(9)
+
+struct tegra264_pcie {
+ struct device *dev;
+
+ /* I/O memory */
+ void __iomem *xal;
+ void __iomem *xtl;
+ void __iomem *ecam;
+
+ /* bridge configuration */
+ struct pci_config_window *cfg;
+ struct pci_host_bridge *bridge;
+
+ /* BPMP and bandwidth management */
+ struct icc_path *icc_path;
+ struct tegra_bpmp *bpmp;
+ u32 ctl_id;
+
+ bool supports_hotplug;
+ bool link_up;
+};
+
+static void tegra264_pcie_power_off(struct tegra264_pcie *pcie)
+{
+ struct tegra_bpmp_message msg = {};
+ struct mrq_pcie_request req = {};
+ int err;
+
+ req.cmd = CMD_PCIE_RP_CONTROLLER_OFF;
+ req.rp_ctrlr_off.rp_controller = pcie->ctl_id;
+
+ msg.mrq = MRQ_PCIE;
+ msg.tx.data = &req;
+ msg.tx.size = sizeof(req);
+
+ err = tegra_bpmp_transfer(pcie->bpmp, &msg);
+ if (err)
+ dev_err(pcie->dev, "failed to turn off PCIe #%u: %pe\n",
+ pcie->ctl_id, ERR_PTR(err));
+
+ if (msg.rx.ret)
+ dev_err(pcie->dev, "failed to turn off PCIe #%u: %d\n",
+ pcie->ctl_id, msg.rx.ret);
+}
+
+static void tegra264_pcie_icc_set(struct tegra264_pcie *pcie)
+{
+ u32 value, speed, width;
+ int err;
+
+ /*
+ * If the link is up, read the negotiated speed and width fields from
+ * the link status register. Otherwise, read the corresponding values
+ * from the link capabilities register to ensure the link works after
+ * hotplug.
+ *
+ * Ideally we'll want to update this dynamically, either on hotplug
+ * or bandwidth change notifications. Neither of those are currently
+ * possible, so this is as good as it gets for now.
+ */
+ if (pcie->link_up) {
+ value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_STATUS);
+ speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, value);
+ width = FIELD_GET(PCI_EXP_LNKSTA_NLW, value);
+ } else {
+ value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_CAPS);
+ speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, value);
+ width = FIELD_GET(PCI_EXP_LNKCAP_MLW, value);
+ }
+
+ value = Mbps_to_icc(width * PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]));
+
+ /*
+ * We don't want to error out here because a boot-critical device
+ * could be connected to this root port. Failure to set the bandwidth
+ * request may have an adverse impact on performance, but it is not
+ * generally fatal, so we opt to continue regardless so that users
+ * get a chance to fix things.
+ */
+ err = icc_set_bw(pcie->icc_path, value, value);
+ if (err < 0)
+ dev_err(pcie->dev,
+ "failed to request bandwidth (%u kBps): %pe\n",
+ value, ERR_PTR(err));
+}
+
+/*
+ * The various memory regions used by the controller (I/O, memory, ECAM) are
+ * set up during early boot and have hardware-level protections in place. If
+ * the DT ranges don't match what's been setup, the controller won't be able
+ * to write the address endpoints properly, so make sure to validate that DT
+ * and firmware programming agree on these ranges.
+ */
+static bool tegra264_pcie_valid_ranges(struct platform_device *pdev)
+{
+ struct tegra264_pcie *pcie = platform_get_drvdata(pdev);
+ struct device_node *np = pcie->dev->of_node;
+ struct of_pci_range_parser parser;
+ phys_addr_t phys, limit, hi, lo;
+ struct of_pci_range range;
+ struct resource *res;
+ bool status = true;
+ u32 value;
+ int err;
+
+ err = of_pci_range_parser_init(&parser, np);
+ if (err < 0)
+ return false;
+
+ for_each_of_pci_range(&parser, &range) {
+ unsigned int addr_hi, addr_lo, limit_hi, limit_lo, enable;
+ unsigned long type = range.flags & IORESOURCE_TYPE_BITS;
+ phys_addr_t start, end, mask;
+ const char *region = NULL;
+
+ end = range.cpu_addr + range.size - 1;
+ start = range.cpu_addr;
+
+ switch (type) {
+ case IORESOURCE_IO:
+ addr_hi = XAL_RC_IO_BASE_HI;
+ addr_lo = XAL_RC_IO_BASE_LO;
+ limit_hi = XAL_RC_IO_LIMIT_HI;
+ limit_lo = XAL_RC_IO_LIMIT_LO;
+ enable = XAL_RC_BAR_CNTL_STANDARD_IOBAR_EN;
+ mask = SZ_64K - 1;
+ region = "I/O";
+ break;
+
+ case IORESOURCE_MEM:
+ if (range.flags & IORESOURCE_PREFETCH) {
+ addr_hi = XAL_RC_MEM_64BIT_BASE_HI;
+ addr_lo = XAL_RC_MEM_64BIT_BASE_LO;
+ limit_hi = XAL_RC_MEM_64BIT_LIMIT_HI;
+ limit_lo = XAL_RC_MEM_64BIT_LIMIT_LO;
+ enable = XAL_RC_BAR_CNTL_STANDARD_64B_BAR_EN;
+ region = "prefetchable memory";
+ } else {
+ addr_hi = XAL_RC_MEM_32BIT_BASE_HI;
+ addr_lo = XAL_RC_MEM_32BIT_BASE_LO;
+ limit_hi = XAL_RC_MEM_32BIT_LIMIT_HI;
+ limit_lo = XAL_RC_MEM_32BIT_LIMIT_LO;
+ enable = XAL_RC_BAR_CNTL_STANDARD_32B_BAR_EN;
+ region = "memory";
+ }
+
+ mask = SZ_1M - 1;
+ break;
+ }
+
+ /* not interested in anything that's not I/O or memory */
+ if (!region)
+ continue;
+
+ /* don't check regions that haven't been enabled */
+ value = readl(pcie->xal + XAL_RC_BAR_CNTL_STANDARD);
+ if ((value & enable) == 0)
+ continue;
+
+ hi = readl(pcie->xal + addr_hi);
+ lo = readl(pcie->xal + addr_lo);
+ phys = ((hi << 16) << 16) | lo;
+
+ hi = readl(pcie->xal + limit_hi);
+ lo = readl(pcie->xal + limit_lo);
+ limit = ((hi << 16) << 16) | lo | mask;
+
+ if (phys != start || limit != end) {
+ dev_err(pcie->dev,
+ "%s region mismatch: %pap-%pap -> %pap-%pap\n",
+ region, &phys, &limit, &start, &end);
+ status = false;
+ }
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecam");
+ if (!res)
+ return false;
+
+ hi = readl(pcie->xal + XAL_RC_ECAM_BASE_HI);
+ lo = readl(pcie->xal + XAL_RC_ECAM_BASE_LO);
+ phys = ((hi << 16) << 16) | lo;
+
+ value = readl(pcie->xal + XAL_RC_ECAM_BUSMASK);
+ limit = phys + ((value + 1) << 20) - 1;
+
+ if (phys != res->start || limit != res->end) {
+ dev_err(pcie->dev,
+ "ECAM region mismatch: %pap-%pap -> %pap-%pap\n",
+ &phys, &limit, &res->start, &res->end);
+ status = false;
+ }
+
+ return status;
+}
+
+static bool tegra264_pcie_supports_hotplug(struct tegra264_pcie *pcie)
+{
+ u32 value = readl(pcie->xtl + XTL_RC_MGMT_CLOCK_CONTROL);
+
+ return (value & XTL_RC_MGMT_CLOCK_CONTROL_PEX_CLKREQ_I_N_PIN_USE_CONV_TO_PRSNT) != 0;
+}
+
+static bool tegra264_pcie_link_up(struct tegra264_pcie *pcie,
+ enum pci_bus_speed *speed)
+{
+ u16 value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_STATUS);
+
+ if (value & PCI_EXP_LNKSTA_DLLLA) {
+ if (speed)
+ *speed = pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS,
+ value)];
+
+ return true;
+ }
+
+ return false;
+}
+
+static void tegra264_pcie_init(struct tegra264_pcie *pcie)
+{
+ enum pci_bus_speed speed;
+ unsigned int i;
+ u32 value;
+
+ /* bring the endpoint out of reset */
+ value = readl(pcie->xtl + XTL_RC_MGMT_PERST_CONTROL);
+ value |= XTL_RC_MGMT_PERST_CONTROL_PERST_O_N;
+ writel(value, pcie->xtl + XTL_RC_MGMT_PERST_CONTROL);
+
+ for (i = 0; i < PCIE_LINK_WAIT_MAX_RETRIES; i++) {
+ if (tegra264_pcie_link_up(pcie, NULL))
+ break;
+
+ msleep(PCIE_LINK_WAIT_SLEEP_MS);
+ }
+
+ pcie->supports_hotplug = tegra264_pcie_supports_hotplug(pcie);
+ pcie->link_up = tegra264_pcie_link_up(pcie, &speed);
+
+ if (pcie->link_up) {
+ msleep(PCIE_RESET_CONFIG_WAIT_MS);
+ dev_info(pcie->dev, "PCIe #%u link is up (speed: %s)\n",
+ pcie->ctl_id, pci_speed_string(speed));
+ tegra264_pcie_icc_set(pcie);
+ } else {
+ dev_info(pcie->dev, "PCIe #%u link is down\n", pcie->ctl_id);
+
+ /*
+ * Make sure to reset the bandwidth requirements if the link
+ * is down but hotplug-capable.
+ */
+ if (pcie->supports_hotplug)
+ tegra264_pcie_icc_set(pcie);
+ }
+}
+
+static int tegra264_pcie_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pci_host_bridge *bridge;
+ struct tegra264_pcie *pcie;
+ struct resource_entry *bus;
+ struct resource *res;
+ int err;
+
+ bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct tegra264_pcie));
+ if (!bridge)
+ return dev_err_probe(dev, -ENOMEM,
+ "failed to allocate host bridge\n");
+
+ pcie = pci_host_bridge_priv(bridge);
+ platform_set_drvdata(pdev, pcie);
+ pcie->bridge = bridge;
+ pcie->dev = dev;
+
+ pcie->xal = devm_platform_ioremap_resource_byname(pdev, "xal");
+ if (IS_ERR(pcie->xal))
+ return dev_err_probe(dev, PTR_ERR(pcie->xal),
+ "failed to map XAL memory\n");
+
+ pcie->xtl = devm_platform_ioremap_resource_byname(pdev, "xtl-pri");
+ if (IS_ERR(pcie->xtl))
+ return dev_err_probe(dev, PTR_ERR(pcie->xtl),
+ "failed to map XTL-PRI memory\n");
+
+ bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
+ if (!bus)
+ return dev_err_probe(dev, -ENODEV,
+ "failed to get bus resources\n");
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecam");
+ if (!res)
+ return dev_err_probe(dev, -ENXIO,
+ "failed to get ECAM resource\n");
+
+ pcie->icc_path = devm_of_icc_get(dev, "write");
+ if (IS_ERR(pcie->icc_path))
+ return dev_err_probe(dev, PTR_ERR(pcie->icc_path),
+ "failed to get ICC\n");
+
+ pcie->bpmp = tegra_bpmp_get_with_id(dev, &pcie->ctl_id);
+ if (IS_ERR(pcie->bpmp))
+ return dev_err_probe(dev, PTR_ERR(pcie->bpmp),
+ "failed to get BPMP\n");
+
+ err = devm_pm_runtime_set_active_enabled(dev);
+ if (err < 0) {
+ dev_err_probe(dev, err, "failed to enable runtime PM\n");
+ goto err_put_bpmp;
+ }
+
+ err = pm_runtime_resume_and_get(dev);
+ if (err < 0) {
+ dev_err_probe(dev, err, "failed to power on device\n");
+ goto err_put_bpmp;
+ }
+
+ /* sanity check that programmed ranges match what's in DT */
+ if (!tegra264_pcie_valid_ranges(pdev)) {
+ err = -EINVAL;
+ goto err_put_pm;
+ }
+
+ pcie->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops);
+ if (IS_ERR(pcie->cfg)) {
+ err = dev_err_probe(dev, PTR_ERR(pcie->cfg),
+ "failed to create ECAM\n");
+ goto err_put_pm;
+ }
+
+ bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops;
+ bridge->sysdata = pcie->cfg;
+ pcie->ecam = pcie->cfg->win;
+
+ tegra264_pcie_init(pcie);
+
+ /*
+ * Fail if the link isn't up and doesn't support hotplug, no device
+ * will ever be able to be added on this bus.
+ */
+ if (!pcie->link_up && !pcie->supports_hotplug) {
+ err = dev_err_probe(pcie->dev, -ENODEV,
+ "PCIe #%u link is down and not hotplug-capable, turning off\n",
+ pcie->ctl_id);
+ tegra264_pcie_power_off(pcie);
+ goto err_free_ecam;
+ }
+
+ err = pci_host_probe(bridge);
+ if (err < 0) {
+ dev_err_probe(dev, err, "failed to register Host Bridge\n");
+ goto err_free_ecam;
+ }
+
+ return 0;
+
+err_free_ecam:
+ pci_ecam_free(pcie->cfg);
+err_put_pm:
+ pm_runtime_put_sync(dev);
+err_put_bpmp:
+ tegra_bpmp_put(pcie->bpmp);
+
+ return err;
+}
+
+static void tegra264_pcie_remove(struct platform_device *pdev)
+{
+ struct tegra264_pcie *pcie = platform_get_drvdata(pdev);
+
+ /*
+ * If we undo tegra264_pcie_init() then link goes down and need
+ * controller reset to bring up the link again. Remove intention is
+ * to clean up the root bridge and re-enumerate during bind.
+ */
+ pci_lock_rescan_remove();
+ pci_stop_root_bus(pcie->bridge->bus);
+ pci_remove_root_bus(pcie->bridge->bus);
+ pci_unlock_rescan_remove();
+
+ pm_runtime_put_sync(&pdev->dev);
+ tegra_bpmp_put(pcie->bpmp);
+ pci_ecam_free(pcie->cfg);
+}
+
+static const struct of_device_id tegra264_pcie_of_match[] = {
+ {
+ .compatible = "nvidia,tegra264-pcie",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tegra264_pcie_of_match);
+
+static struct platform_driver tegra264_pcie_driver = {
+ .probe = tegra264_pcie_probe,
+ .remove = tegra264_pcie_remove,
+ .driver = {
+ .name = "tegra264-pcie",
+ .of_match_table = tegra264_pcie_of_match,
+ },
+};
+module_platform_driver(tegra264_pcie_driver);
+
+MODULE_AUTHOR("Manikanta Maddireddy <mmaddireddy@nvidia.com>");
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Tegra264 PCIe host controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c
index 7db2c96c6cec..0dee19fa24ca 100644
--- a/drivers/pci/controller/pcie-xilinx-nwl.c
+++ b/drivers/pci/controller/pcie-xilinx-nwl.c
@@ -140,11 +140,6 @@
#define PCIE_PHY_LINKUP_BIT BIT(0)
#define PHY_RDY_LINKUP_BIT BIT(1)
-/* Parameters for the waiting for link up routine */
-#define LINK_WAIT_MAX_RETRIES 10
-#define LINK_WAIT_USLEEP_MIN 90000
-#define LINK_WAIT_USLEEP_MAX 100000
-
struct nwl_msi { /* MSI information */
DECLARE_BITMAP(bitmap, INT_PCI_MSI_NR);
struct irq_domain *dev_domain;
@@ -203,10 +198,10 @@ static int nwl_wait_for_link(struct nwl_pcie *pcie)
int retries;
/* check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) {
if (nwl_phy_link_up(pcie))
return 0;
- usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+ msleep(PCIE_LINK_WAIT_SLEEP_MS);
}
dev_err(dev, "PHY link never came up\n");
diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c
index f9a34f323ad8..fd1a11b4c8eb 100644
--- a/drivers/pci/controller/plda/pcie-plda-host.c
+++ b/drivers/pci/controller/plda/pcie-plda-host.c
@@ -419,6 +419,8 @@ static int plda_pcie_init_irq_domains(struct plda_pcie_rp *port)
return plda_allocate_msi_domains(port);
}
+static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie);
+
int plda_init_interrupts(struct platform_device *pdev,
struct plda_pcie_rp *port,
const struct plda_event *event)
@@ -440,14 +442,17 @@ int plda_init_interrupts(struct platform_device *pdev,
}
port->irq = platform_get_irq(pdev, 0);
- if (port->irq < 0)
- return -ENODEV;
+ if (port->irq < 0) {
+ ret = -ENODEV;
+ goto err_irq_domain_deinit;
+ }
for_each_set_bit(i, &port->events_bitmap, port->num_events) {
event_irq = irq_create_mapping(port->event_domain, i);
if (!event_irq) {
dev_err(dev, "failed to map hwirq %d\n", i);
- return -ENXIO;
+ ret = -ENXIO;
+ goto err_irq_domain_deinit;
}
if (event->request_event_irq)
@@ -459,7 +464,7 @@ int plda_init_interrupts(struct platform_device *pdev,
if (ret) {
dev_err(dev, "failed to request IRQ %d\n", event_irq);
- return ret;
+ goto err_irq_domain_deinit;
}
}
@@ -467,7 +472,8 @@ int plda_init_interrupts(struct platform_device *pdev,
event->intx_event);
if (!port->intx_irq) {
dev_err(dev, "failed to map INTx interrupt\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto err_irq_domain_deinit;
}
/* Plug the INTx chained handler */
@@ -475,8 +481,11 @@ int plda_init_interrupts(struct platform_device *pdev,
port->msi_irq = irq_create_mapping(port->event_domain,
event->msi_event);
- if (!port->msi_irq)
- return -ENXIO;
+ if (!port->msi_irq) {
+ dev_err(dev, "failed to map MSI interrupt\n");
+ ret = -ENXIO;
+ goto err_irq_domain_deinit;
+ }
/* Plug the MSI chained handler */
irq_set_chained_handler_and_data(port->msi_irq, plda_handle_msi, port);
@@ -485,6 +494,11 @@ int plda_init_interrupts(struct platform_device *pdev,
irq_set_chained_handler_and_data(port->irq, plda_handle_event, port);
return 0;
+
+err_irq_domain_deinit:
+ plda_pcie_irq_domain_deinit(port);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(plda_init_interrupts);
@@ -559,9 +573,27 @@ EXPORT_SYMBOL_GPL(plda_pcie_setup_iomems);
static void plda_pcie_irq_domain_deinit(struct plda_pcie_rp *pcie)
{
- irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
- irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL);
- irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL);
+ u32 i, event_irq;
+
+ if (pcie->irq > 0)
+ irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
+ if (pcie->msi_irq > 0)
+ irq_set_chained_handler_and_data(pcie->msi_irq, NULL, NULL);
+ if (pcie->intx_irq > 0)
+ irq_set_chained_handler_and_data(pcie->intx_irq, NULL, NULL);
+
+ for_each_set_bit(i, &pcie->events_bitmap, pcie->num_events) {
+ event_irq = irq_find_mapping(pcie->event_domain, i);
+ if (event_irq) {
+ devm_free_irq(pcie->dev, event_irq, pcie);
+ irq_dispose_mapping(event_irq);
+ }
+ }
+
+ if (pcie->intx_irq)
+ irq_dispose_mapping(pcie->intx_irq);
+ if (pcie->msi_irq)
+ irq_dispose_mapping(pcie->msi_irq);
irq_domain_remove(pcie->msi.dev_domain);
diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c
index 298036c3e7f9..02d5aa6dc14b 100644
--- a/drivers/pci/controller/plda/pcie-starfive.c
+++ b/drivers/pci/controller/plda/pcie-starfive.c
@@ -45,11 +45,6 @@
#define STG_SYSCON_LNKSTA_OFFSET 0x170
#define DATA_LINK_ACTIVE BIT(5)
-/* Parameters for the waiting for link up routine */
-#define LINK_WAIT_MAX_RETRIES 10
-#define LINK_WAIT_USLEEP_MIN 90000
-#define LINK_WAIT_USLEEP_MAX 100000
-
struct starfive_jh7110_pcie {
struct plda_pcie_rp plda;
struct reset_control *resets;
@@ -217,12 +212,12 @@ static int starfive_pcie_host_wait_for_link(struct starfive_jh7110_pcie *pcie)
int retries;
/* Check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ for (retries = 0; retries < PCIE_LINK_WAIT_MAX_RETRIES; retries++) {
if (starfive_pcie_link_up(&pcie->plda)) {
dev_info(pcie->plda.dev, "port link up\n");
return 0;
}
- usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+ msleep(PCIE_LINK_WAIT_SLEEP_MS);
}
return -ETIMEDOUT;
@@ -304,12 +299,14 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda)
ret = starfive_pcie_clk_rst_init(pcie);
if (ret)
- return ret;
+ goto err_disable_phy;
if (pcie->vpcie3v3) {
ret = regulator_enable(pcie->vpcie3v3);
- if (ret)
+ if (ret) {
dev_err_probe(dev, ret, "failed to enable vpcie3v3 regulator\n");
+ goto err_clk_rst;
+ }
}
if (pcie->reset_gpio)
@@ -379,6 +376,13 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda)
dev_info(dev, "port link down\n");
return 0;
+
+err_clk_rst:
+ starfive_pcie_clk_rst_deinit(pcie);
+err_disable_phy:
+ starfive_pcie_disable_phy(pcie);
+
+ return ret;
}
static const struct plda_pcie_host_ops sf_host_ops = {
@@ -410,7 +414,11 @@ static int starfive_pcie_probe(struct platform_device *pdev)
return ret;
pm_runtime_enable(&pdev->dev);
- pm_runtime_get_sync(&pdev->dev);
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret < 0) {
+ pm_runtime_disable(&pdev->dev);
+ return dev_err_probe(dev, ret, "failed to resume device\n");
+ }
plda->host_ops = &sf_host_ops;
plda->num_events = PLDA_MAX_EVENT_NUM;
@@ -436,9 +444,9 @@ static void starfive_pcie_remove(struct platform_device *pdev)
{
struct starfive_jh7110_pcie *pcie = platform_get_drvdata(pdev);
- pm_runtime_put(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
plda_pcie_host_deinit(&pcie->plda);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
platform_set_drvdata(pdev, NULL);
}
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index d4ae250d4bc6..241023ecf677 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -37,6 +37,12 @@
#define MB2_SHADOW_OFFSET 0x2000
#define MB2_SHADOW_SIZE 16
+/* DMR BAR4 register offsets */
+#define SHADOW_MEMBAR1_28C1 0x2818 /* MEMBAR1 physical address */
+#define SHADOW_MEMBAR2_28C1 0x2820 /* MEMBAR2 physical address */
+#define BASE_ID_REG_28C1 0x2840
+#define MEMBAR2_OFFSET_28C1 0x30d0
+
enum vmd_features {
/*
* Device may contain registers which hint the physical location of the
@@ -77,6 +83,15 @@ enum vmd_features {
* proper power management of the SoC.
*/
VMD_FEAT_BIOS_PM_QUIRK = (1 << 5),
+
+ /*
+ * Newer VMD with device ID 0x28C1 has unique settings compared to its
+ * predecessor where BIOS enumerates the entire VMD device tree and
+ * stores respective configurations including bus start range and
+ * shadow registers in VMD MMIO space in VMD BAR4/BAR5, otherwise
+ * referred to as MEMBAR2 or MSI-X BAR.
+ */
+ VMD_FEAT_USE_BIOS_INFO = (1 << 6),
};
#define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */
@@ -142,6 +157,7 @@ struct vmd_dev {
u8 first_vec;
char *name;
int instance;
+ unsigned long features;
};
static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
@@ -366,6 +382,9 @@ static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool enable)
{
u16 reg;
+ if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO))
+ return;
+
pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, &reg);
reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) :
(reg | VMCONFIG_MSI_REMAP);
@@ -389,11 +408,22 @@ static void vmd_remove_irq_domain(struct vmd_dev *vmd)
}
}
+static unsigned int vmd_bus_to_ecam(struct vmd_dev *vmd, unsigned int busnr)
+{
+ if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO))
+ return busnr;
+
+ return busnr - vmd->busn_start;
+}
+
static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
unsigned int devfn, int reg, int len)
{
- unsigned int busnr_ecam = bus->number - vmd->busn_start;
- u32 offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg);
+ unsigned int busnr_ecam;
+ u32 offset;
+
+ busnr_ecam = vmd_bus_to_ecam(vmd, bus->number);
+ offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg);
if (offset + len >= resource_size(&vmd->dev->resource[VMD_CFGBAR]))
return NULL;
@@ -518,22 +548,37 @@ static inline void vmd_acpi_begin(void) { }
static inline void vmd_acpi_end(void) { }
#endif /* CONFIG_ACPI */
+static resource_size_t vmd_cfgbar_ecam_space(struct vmd_dev *vmd)
+{
+ resource_size_t cfgbar_buses;
+ unsigned int ecam_start;
+
+ cfgbar_buses = resource_size(&vmd->dev->resource[VMD_CFGBAR]) >> 20;
+ ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[0].start);
+ if (ecam_start >= cfgbar_buses)
+ return 0;
+
+ return cfgbar_buses - ecam_start;
+}
static void vmd_domain_reset(struct vmd_dev *vmd)
{
u16 bus, max_buses = resource_size(&vmd->resources[0]);
u8 dev, functions, fn, hdr_type;
+ unsigned int ecam_bus;
char __iomem *base;
+ max_buses = min_t(u16, max_buses, vmd_cfgbar_ecam_space(vmd));
for (bus = 0; bus < max_buses; bus++) {
+ ecam_bus = vmd_bus_to_ecam(vmd, vmd->resources[0].start + bus);
for (dev = 0; dev < 32; dev++) {
- base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus,
+ base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus,
PCI_DEVFN(dev, 0), 0);
hdr_type = readb(base + PCI_HEADER_TYPE);
functions = (hdr_type & PCI_HEADER_TYPE_MFD) ? 8 : 1;
for (fn = 0; fn < functions; fn++) {
- base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus,
+ base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus,
PCI_DEVFN(dev, fn), 0);
hdr_type = readb(base + PCI_HEADER_TYPE) &
@@ -640,6 +685,8 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
pci_read_config_word(dev, PCI_REG_VMCAP, &reg);
if (BUS_RESTRICT_CAP(reg)) {
pci_read_config_word(dev, PCI_REG_VMCONFIG, &reg);
+ if (PCI_POSSIBLE_ERROR(reg))
+ return -ENODEV;
switch (BUS_RESTRICT_CFG(reg)) {
case 0:
@@ -648,6 +695,7 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
case 1:
vmd->busn_start = 128;
break;
+ case 3:
case 2:
vmd->busn_start = 224;
break;
@@ -661,6 +709,46 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
return 0;
}
+static int vmd_get_bus_info_from_bar4(struct vmd_dev *vmd,
+ resource_size_t *offset1,
+ resource_size_t *offset2)
+{
+ u64 phys1, phys2, bar4_2840;
+ void __iomem *bar4;
+ u32 base_id;
+ u8 base_bus;
+
+ bar4 = pci_ioremap_bar(vmd->dev, 4);
+ if (!bar4)
+ return -ENOMEM;
+
+ /* Read shadow registers for MEMBAR1 and MEMBAR2 physical addresses */
+ phys1 = readq(bar4 + SHADOW_MEMBAR1_28C1);
+ phys2 = readq(bar4 + SHADOW_MEMBAR2_28C1);
+
+ /*
+ * Read and set bus start number from Base ID register. 24-bit Base ID
+ * register is part of 64-bit shadowed reqid hide range register and
+ * holds segment, bus, device and function.
+ */
+ bar4_2840 = readq(bar4 + BASE_ID_REG_28C1);
+ base_id = bar4_2840 & 0xFFFFFF;
+ base_bus = base_id >> 8;
+ vmd->busn_start = base_bus;
+
+ /* Calculate offsets like vmd_get_phys_offsets() does */
+ if (phys1)
+ *offset1 = vmd->dev->resource[VMD_MEMBAR1].start -
+ (phys1 & PCI_BASE_ADDRESS_MEM_MASK);
+ if (phys2)
+ *offset2 = vmd->dev->resource[VMD_MEMBAR2].start -
+ (phys2 & PCI_BASE_ADDRESS_MEM_MASK);
+
+ pci_iounmap(vmd->dev, bar4);
+
+ return 0;
+}
+
static irqreturn_t vmd_irq(int irq, void *data)
{
struct vmd_irq_list *irqs = data;
@@ -711,6 +799,52 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd)
return 0;
}
+static int vmd_prepare_offsets_and_bus(struct vmd_dev *vmd,
+ unsigned long features,
+ resource_size_t *membar2_offset,
+ resource_size_t *offset1,
+ resource_size_t *offset2)
+{
+ int ret;
+
+ /*
+ * Shadow registers may exist in certain VMD device IDs which allow
+ * guests to correctly assign host physical addresses to the root ports
+ * and child devices. These registers will either return the host value
+ * or 0, depending on an enable bit in the VMD device.
+ *
+ * For certain VMD devices (i.e. 0x28C1), BIOS places device info
+ * in BAR4 shadow registers to determine the base bus number and memory
+ * offsets.
+ */
+ if (features & VMD_FEAT_USE_BIOS_INFO) {
+ *membar2_offset = MEMBAR2_OFFSET_28C1;
+ ret = vmd_get_bus_info_from_bar4(vmd, offset1, offset2);
+ if (ret)
+ return ret;
+ } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) {
+ *membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE;
+ ret = vmd_get_phys_offsets(vmd, true, offset1, offset2);
+ if (ret)
+ return ret;
+ } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) {
+ ret = vmd_get_phys_offsets(vmd, false, offset1, offset2);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Certain VMD devices may have a root port configuration option which
+ * limits the bus range to between 0-127, 128-255, or 224-255.
+ */
+ if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) {
+ ret = vmd_get_bus_number_start(vmd);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
/*
* Since VMD is an aperture to regular PCIe root ports, only allow it to
* control features that the OS is allowed to control on the physical PCI bus.
@@ -780,42 +914,25 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
LIST_HEAD(resources);
resource_size_t offset[2] = {0};
resource_size_t membar2_offset = 0x2000;
+ resource_size_t busn_end;
struct pci_bus *child;
struct pci_dev *dev;
+ bool vmd_in_guest;
int ret;
- /*
- * Shadow registers may exist in certain VMD device ids which allow
- * guests to correctly assign host physical addresses to the root ports
- * and child devices. These registers will either return the host value
- * or 0, depending on an enable bit in the VMD device.
- */
- if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) {
- membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE;
- ret = vmd_get_phys_offsets(vmd, true, &offset[0], &offset[1]);
- if (ret)
- return ret;
- } else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) {
- ret = vmd_get_phys_offsets(vmd, false, &offset[0], &offset[1]);
- if (ret)
- return ret;
- }
-
- /*
- * Certain VMD devices may have a root port configuration option which
- * limits the bus range to between 0-127, 128-255, or 224-255
- */
- if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) {
- ret = vmd_get_bus_number_start(vmd);
- if (ret)
- return ret;
- }
+ ret = vmd_prepare_offsets_and_bus(vmd, features, &membar2_offset,
+ &offset[0], &offset[1]);
+ if (ret)
+ return ret;
+ /* Do not let resource[0] end go out of bounds */
res = &vmd->dev->resource[VMD_CFGBAR];
+ busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1;
+ busn_end = min_t(resource_size_t, busn_end, 0xff);
vmd->resources[0] = (struct resource) {
.name = "VMD CFGBAR",
.start = vmd->busn_start,
- .end = vmd->busn_start + (resource_size(res) >> 20) - 1,
+ .end = busn_end,
.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
};
@@ -862,14 +979,16 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
.parent = res,
};
+ /* Non-zero offset means guest/direct assign view. */
+ vmd_in_guest = offset[0] || offset[1];
+
/*
* Currently MSI remapping must be enabled in guest passthrough mode
* due to some missing interrupt remapping plumbing. This is probably
* acceptable because the guest is usually CPU-limited and MSI
* remapping doesn't become a performance bottleneck.
*/
- if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) ||
- offset[0] || offset[1]) {
+ if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || vmd_in_guest) {
ret = vmd_alloc_irqs(vmd);
if (ret)
return ret;
@@ -910,8 +1029,13 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
return -ENODEV;
}
- vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
- to_pci_host_bridge(vmd->bus->bridge));
+ /*
+ * Don't copy _OSC control flags from root bridge if running in a VM, as
+ * they don't reflect the physical root bridge capabilities.
+ */
+ if (!vmd_in_guest)
+ vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
+ to_pci_host_bridge(vmd->bus->bridge));
vmd_attach_resources(vmd);
if (vmd->irq_domain)
@@ -998,6 +1122,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
vmd->dev = dev;
vmd->sysdata.domain = PCI_DOMAIN_NR_NOT_SET;
+ vmd->features = features;
vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL);
if (vmd->instance < 0)
return vmd->instance;
@@ -1114,6 +1239,10 @@ static const struct pci_device_id vmd_ids[] = {
.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
VMD_FEAT_HAS_BUS_RESTRICTIONS |
VMD_FEAT_CAN_BYPASS_MSI_REMAP,},
+ {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_28C1),
+ .driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
+ VMD_FEAT_CAN_BYPASS_MSI_REMAP |
+ VMD_FEAT_USE_BIOS_INFO,},
{PCI_VDEVICE(INTEL, 0x467f),
.driver_data = VMD_FEATS_CLIENT,},
{PCI_VDEVICE(INTEL, 0x4c3d),
@@ -1132,6 +1261,10 @@ static const struct pci_device_id vmd_ids[] = {
.driver_data = VMD_FEATS_CLIENT,},
{PCI_VDEVICE(INTEL, 0xb07f),
.driver_data = VMD_FEATS_CLIENT,},
+ {PCI_VDEVICE(INTEL, 0xd70b),
+ .driver_data = VMD_FEATS_CLIENT,},
+ {PCI_VDEVICE(INTEL, 0xd73b),
+ .driver_data = VMD_FEATS_CLIENT,},
{0,}
};
MODULE_DEVICE_TABLE(pci, vmd_ids);
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index 7b41da4ec11a..ac95b1d2d999 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -858,8 +858,8 @@ void pci_doe_init(struct pci_dev *pdev)
PCI_EXT_CAP_ID_DOE))) {
doe_mb = pci_doe_create_mb(pdev, offset);
if (IS_ERR(doe_mb)) {
- pci_err(pdev, "[%x] failed to create mailbox: %ld\n",
- offset, PTR_ERR(doe_mb));
+ pci_err(pdev, "[%x] failed to create mailbox: %pe\n",
+ offset, doe_mb);
continue;
}
diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c
index 119de32ff07b..a9b3bce2492f 100644
--- a/drivers/pci/ecam.c
+++ b/drivers/pci/ecam.c
@@ -208,6 +208,19 @@ const struct pci_ecam_ops pci_generic_ecam_ops = {
};
EXPORT_SYMBOL_GPL(pci_generic_ecam_ops);
+/* CAM ops */
+const struct pci_ecam_ops pci_generic_cam_ops = {
+ .bus_shift = 16,
+ .pci_ops = {
+ .add_bus = pci_ecam_add_bus,
+ .remove_bus = pci_ecam_remove_bus,
+ .map_bus = pci_ecam_map_bus,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
+ }
+};
+EXPORT_SYMBOL_GPL(pci_generic_cam_ops);
+
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
/* ECAM ops for 32-bit access only (non-compliant) */
const struct pci_ecam_ops pci_32b_ops = {
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index f8016ed24523..7337bd1ac6df 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -258,8 +258,7 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
if (subevent == 0x80) {
pr_debug("%s: generating bus event\n", __func__);
- acpi_bus_generate_netlink_event(note->device->pnp.device_class,
- dev_name(&note->device->dev),
+ acpi_bus_generate_netlink_event("", dev_name(&note->device->dev),
note->event, detail);
} else
note->event = event;
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index 8b18c4ba845c..a51dff91b196 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -7,6 +7,7 @@
#define pr_fmt(fmt) "PCI: OF: " fmt
#include <linux/cleanup.h>
+#include <linux/gpio/consumer.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/pci.h>
@@ -15,6 +16,7 @@
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include "pci.h"
#ifdef CONFIG_PCI
@@ -586,6 +588,83 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
return irq_create_of_mapping(&oirq);
}
EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci);
+
+static void pci_configure_wake_irq(struct pci_dev *pdev, struct gpio_desc *wake)
+{
+ int ret, wake_irq, irq_type;
+
+ wake_irq = gpiod_to_irq(wake);
+ if (wake_irq < 0) {
+ pci_err(pdev, "Failed to get wake IRQ: %d\n", wake_irq);
+ return;
+ }
+
+ /*
+ * dev_pm_set_dedicated_wake_irq() associates a wakeup IRQ with the
+ * device and requests it, but the PM core keeps it disabled by
+ * default. The IRQ is enabled only when the device is allowed to
+ * wake the system (during system suspend and after runtime
+ * suspend), and only if device wakeup is enabled.
+ *
+ * When the wake IRQ fires, the wakeirq handler invokes
+ * pm_runtime_resume() to bring the device back to an active power
+ * state (e.g. from D3cold to D0). Once the device is active and
+ * the link is usable, the endpoint may signal a PME, which is then
+ * handled by the PCI core (either via PME polling or the PCIe PME
+ * service driver) to wakeup the particular endpoint.
+ */
+ ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, wake_irq);
+ if (ret < 0) {
+ pci_err(pdev, "Failed to set WAKE# IRQ: %d\n", ret);
+ return;
+ }
+
+ irq_type = gpiod_is_active_low(wake) ? IRQ_TYPE_LEVEL_LOW :
+ IRQ_TYPE_LEVEL_HIGH;
+ ret = irq_set_irq_type(wake_irq, irq_type);
+ if (ret < 0) {
+ dev_pm_clear_wake_irq(&pdev->dev);
+ pci_err(pdev, "Failed to set irq_type: %d\n", ret);
+ return;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+}
+
+void pci_configure_of_wake_gpio(struct pci_dev *dev)
+{
+ struct device_node *dn = pci_device_to_OF_node(dev);
+ struct gpio_desc *gpio;
+
+ if (!dn && !dev->wake)
+ return;
+ /*
+ * fwnode_gpiod_get() may fail with -EBUSY (e.g. shared WAKE#), but
+ * the actual WAKE# trigger from the device would still work and
+ * the host controller driver will enable power to the topology.
+ *
+ * -EPROBE_DEFER cannot be propagated here since pci_device_add()
+ * has no retry mechanism.
+ */
+ gpio = fwnode_gpiod_get(of_fwnode_handle(dn), "wake", GPIOD_IN, NULL);
+ if (!IS_ERR(gpio)) {
+ dev->wake = gpio;
+ pci_configure_wake_irq(dev, gpio);
+ }
+}
+
+void pci_remove_of_wake_gpio(struct pci_dev *dev)
+{
+ struct device_node *dn = pci_device_to_OF_node(dev);
+
+ if (!dn)
+ return;
+
+ device_init_wakeup(&dev->dev, false);
+ dev_pm_clear_wake_irq(&dev->dev);
+ gpiod_put(dev->wake);
+ dev->wake = NULL;
+}
#endif /* CONFIG_OF_IRQ */
static int pci_parse_request_of_pci_ranges(struct device *dev,
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index b2d5266f8653..9334eb314663 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -564,6 +564,11 @@ static const struct pci_p2pdma_whitelist_entry {
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_6XXX, 0},
/* Google SoCs. */
{PCI_VENDOR_ID_GOOGLE, PCI_ANY_ID, 0},
+ /* Nvidia CPUs */
+ {PCI_VENDOR_ID_NVIDIA, 0x2f95, 0},
+ {PCI_VENDOR_ID_NVIDIA, 0x2f96, 0},
+ {PCI_VENDOR_ID_NVIDIA, 0x2f97, 0},
+ {PCI_VENDOR_ID_NVIDIA, 0x2f98, 0},
{}
};
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 4d0f2cb6c695..42d545edd7fa 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -1636,8 +1636,8 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root)
cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops);
if (IS_ERR(cfg)) {
- dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res,
- PTR_ERR(cfg));
+ dev_err(dev, "%04x:%pR error %pe mapping ECAM\n", seg, bus_res,
+ cfg);
return NULL;
}
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index f36778e62ac1..e16aa59dd7ac 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -30,6 +30,47 @@ struct pci_dynid {
};
/**
+ * do_pci_add_dynid - Add a new PCI device ID to this driver and re-probe
+ * @drv: target PCI driver
+ * @id: ID to be added
+ * @check_dup: whether to check if matching ID is already present
+ *
+ * Add a new dynamic PCI device ID to this driver and causes the driver to
+ * probe for all devices again. @drv must have been registered prior to calling
+ * this function.
+ *
+ * Context: Does GFP_KERNEL allocation.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+static int do_pci_add_dynid(struct pci_driver *drv,
+ const struct pci_device_id *id,
+ bool check_dup)
+{
+ struct pci_dynid *dynid, *existing_dynid;
+
+ dynid = kzalloc_obj(*dynid);
+ if (!dynid)
+ return -ENOMEM;
+
+ dynid->id = *id;
+
+ scoped_guard(spinlock, &drv->dynids.lock) {
+ if (check_dup) {
+ list_for_each_entry(existing_dynid, &drv->dynids.list, node) {
+ if (pci_match_one_id(&existing_dynid->id, id)) {
+ kfree(dynid);
+ return -EEXIST;
+ }
+ }
+ }
+ list_add_tail(&dynid->node, &drv->dynids.list);
+ }
+
+ return driver_attach(&drv->driver);
+}
+
+/**
* pci_add_dynid - add a new PCI device ID to this driver and re-probe devices
* @drv: target pci driver
* @vendor: PCI vendor ID
@@ -56,25 +97,17 @@ int pci_add_dynid(struct pci_driver *drv,
unsigned int class, unsigned int class_mask,
unsigned long driver_data)
{
- struct pci_dynid *dynid;
-
- dynid = kzalloc_obj(*dynid);
- if (!dynid)
- return -ENOMEM;
-
- dynid->id.vendor = vendor;
- dynid->id.device = device;
- dynid->id.subvendor = subvendor;
- dynid->id.subdevice = subdevice;
- dynid->id.class = class;
- dynid->id.class_mask = class_mask;
- dynid->id.driver_data = driver_data;
-
- spin_lock(&drv->dynids.lock);
- list_add_tail(&dynid->node, &drv->dynids.list);
- spin_unlock(&drv->dynids.lock);
-
- return driver_attach(&drv->driver);
+ struct pci_device_id id = {
+ .vendor = vendor,
+ .device = device,
+ .subvendor = subvendor,
+ .subdevice = subdevice,
+ .class = class,
+ .class_mask = class_mask,
+ .driver_data = driver_data,
+ };
+
+ return do_pci_add_dynid(drv, &id, false);
}
EXPORT_SYMBOL_GPL(pci_add_dynid);
@@ -91,6 +124,31 @@ static void pci_free_dynids(struct pci_driver *drv)
}
/**
+ * do_pci_match_id - See if a PCI ID matches a given pci_id table
+ * @ids: array of PCI device ID structures to search in
+ * @dev_id: the actual PCI device ID structure to match against.
+ * @include_override_only: also match against device ID entries marked as
+ * override only.
+ *
+ * Return: the matching pci_device_id structure or %NULL if there is no match.
+ */
+static const struct pci_device_id *
+do_pci_match_id(const struct pci_device_id *ids,
+ const struct pci_device_id *dev_id,
+ bool include_override_only)
+{
+ if (ids) {
+ while (ids->vendor || ids->subvendor || ids->class_mask) {
+ if ((!ids->override_only || include_override_only) &&
+ pci_match_one_id(ids, dev_id))
+ return ids;
+ ids++;
+ }
+ }
+ return NULL;
+}
+
+/**
* pci_match_id - See if a PCI device matches a given pci_id table
* @ids: array of PCI device ID structures to search in
* @dev: the PCI device structure to match against.
@@ -105,14 +163,9 @@ static void pci_free_dynids(struct pci_driver *drv)
const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
struct pci_dev *dev)
{
- if (ids) {
- while (ids->vendor || ids->subvendor || ids->class_mask) {
- if (pci_match_one_device(ids, dev))
- return ids;
- ids++;
- }
- }
- return NULL;
+ struct pci_device_id dev_id = pci_id_from_device(dev);
+
+ return do_pci_match_id(ids, &dev_id, true);
}
EXPORT_SYMBOL(pci_match_id);
@@ -127,6 +180,7 @@ static const struct pci_device_id pci_device_id_any = {
* pci_match_device - See if a device matches a driver's list of IDs
* @drv: the PCI driver to match against
* @dev: the PCI device structure to match against
+ * @id_copy: place to store copy of pci_device_id for dynamic ID
*
* Used by a driver to check whether a PCI device is in its list of
* supported devices or in the dynids list, which may have been augmented
@@ -134,10 +188,11 @@ static const struct pci_device_id pci_device_id_any = {
* structure or %NULL if there is no match.
*/
static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
- struct pci_dev *dev)
+ struct pci_dev *dev,
+ struct pci_device_id *id_copy)
{
- struct pci_dynid *dynid;
- const struct pci_device_id *found_id = NULL, *ids;
+ const struct pci_device_id *found_id = NULL;
+ struct pci_device_id dev_id;
int ret;
/* When driver_override is set, only bind to the matching driver */
@@ -145,45 +200,29 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
if (ret == 0)
return NULL;
+ dev_id = pci_id_from_device(dev);
/* Look at the dynamic ids first, before the static ones */
- spin_lock(&drv->dynids.lock);
- list_for_each_entry(dynid, &drv->dynids.list, node) {
- if (pci_match_one_device(&dynid->id, dev)) {
- found_id = &dynid->id;
- break;
+ scoped_guard(spinlock, &drv->dynids.lock) {
+ struct pci_dynid *dynid;
+
+ list_for_each_entry(dynid, &drv->dynids.list, node) {
+ if (pci_match_one_id(&dynid->id, &dev_id)) {
+ *id_copy = dynid->id;
+ return id_copy;
+ }
}
}
- spin_unlock(&drv->dynids.lock);
+ found_id = do_pci_match_id(drv->id_table, &dev_id, ret > 0);
if (found_id)
return found_id;
- for (ids = drv->id_table; (found_id = pci_match_id(ids, dev));
- ids = found_id + 1) {
- /*
- * The match table is split based on driver_override.
- * In case override_only was set, enforce driver_override
- * matching.
- */
- if (found_id->override_only) {
- if (ret > 0)
- return found_id;
- } else {
- return found_id;
- }
- }
-
/* driver_override will always match, send a dummy id */
if (ret > 0)
return &pci_device_id_any;
return NULL;
}
-static void _pci_free_device(struct device *dev)
-{
- kfree(to_pci_dev(dev));
-}
-
/**
* new_id_store - sysfs frontend to pci_add_dynid()
* @driver: target device driver
@@ -197,38 +236,22 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,
{
struct pci_driver *pdrv = to_pci_driver(driver);
const struct pci_device_id *ids = pdrv->id_table;
- u32 vendor, device, subvendor = PCI_ANY_ID,
- subdevice = PCI_ANY_ID, class = 0, class_mask = 0;
- unsigned long driver_data = 0;
+ struct pci_device_id id = {
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID
+ };
int fields;
int retval = 0;
fields = sscanf(buf, "%x %x %x %x %x %x %lx",
- &vendor, &device, &subvendor, &subdevice,
- &class, &class_mask, &driver_data);
+ &id.vendor, &id.device, &id.subvendor, &id.subdevice,
+ &id.class, &id.class_mask, &id.driver_data);
if (fields < 2)
return -EINVAL;
if (fields != 7) {
- struct pci_dev *pdev = kzalloc_obj(*pdev);
- if (!pdev)
- return -ENOMEM;
-
- pdev->vendor = vendor;
- pdev->device = device;
- pdev->subsystem_vendor = subvendor;
- pdev->subsystem_device = subdevice;
- pdev->class = class;
- pdev->dev.release = _pci_free_device;
-
- device_initialize(&pdev->dev);
- if (pci_match_device(pdrv, pdev))
- retval = -EEXIST;
-
- put_device(&pdev->dev);
-
- if (retval)
- return retval;
+ if (do_pci_match_id(pdrv->id_table, &id, false))
+ return -EEXIST;
}
/* Only accept driver_data values that match an existing id_table
@@ -236,7 +259,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,
if (ids) {
retval = -EINVAL;
while (ids->vendor || ids->subvendor || ids->class_mask) {
- if (driver_data == ids->driver_data) {
+ if (id.driver_data == ids->driver_data) {
retval = 0;
break;
}
@@ -246,8 +269,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,
return retval;
}
- retval = pci_add_dynid(pdrv, vendor, device, subvendor, subdevice,
- class, class_mask, driver_data);
+ retval = do_pci_add_dynid(pdrv, &id, fields != 7);
if (retval)
return retval;
return count;
@@ -445,12 +467,13 @@ void pci_probe_flush_workqueue(void)
static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
{
const struct pci_device_id *id;
+ struct pci_device_id id_copy;
int error = 0;
if (drv->probe) {
error = -ENODEV;
- id = pci_match_device(drv, pci_dev);
+ id = pci_match_device(drv, pci_dev, &id_copy);
if (id)
error = pci_call_probe(drv, pci_dev, id);
}
@@ -1538,12 +1561,13 @@ static int pci_bus_match(struct device *dev, const struct device_driver *drv)
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *pci_drv;
const struct pci_device_id *found_id;
+ struct pci_device_id id_copy;
if (pci_dev_binding_disallowed(pci_dev))
return 0;
pci_drv = (struct pci_driver *)to_pci_driver(drv);
- found_id = pci_match_device(pci_drv, pci_dev);
+ found_id = pci_match_device(pci_drv, pci_dev, &id_copy);
if (found_id)
return 1;
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index d6083552b287..1f21856aac8a 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -719,7 +719,7 @@ static ssize_t pci_read_config(struct file *filp, struct kobject *kobj,
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
- if (off > size)
+ if (off >= size)
return 0;
if (off + count > size) {
size -= off;
@@ -800,7 +800,7 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
}
- if (off > dev->cfg_size)
+ if (off >= dev->cfg_size)
return 0;
if (off + count > dev->cfg_size) {
size = dev->cfg_size - off;
@@ -872,6 +872,27 @@ static const struct attribute_group pci_dev_config_attr_group = {
};
#ifdef HAVE_PCI_LEGACY
+
+#define pci_legacy_resource_io_attr(_suffix, _size) \
+static const struct bin_attribute pci_legacy_io##_suffix##_attr = { \
+ .attr = { .name = "legacy_io" __stringify(_suffix), .mode = 0600 }, \
+ .size = (_size), \
+ .read = pci_read_legacy_io, \
+ .write = pci_write_legacy_io, \
+ .f_mapping = iomem_get_mapping, \
+ .llseek = pci_llseek_resource_legacy, \
+ .mmap = pci_mmap_legacy_io, \
+}
+
+#define pci_legacy_resource_mem_attr(_suffix, _size) \
+static const struct bin_attribute pci_legacy_mem##_suffix##_attr = { \
+ .attr = { .name = "legacy_mem" __stringify(_suffix), .mode = 0600 }, \
+ .size = (_size), \
+ .f_mapping = iomem_get_mapping, \
+ .llseek = pci_llseek_resource_legacy, \
+ .mmap = pci_mmap_legacy_mem, \
+}
+
/**
* pci_read_legacy_io - read byte(s) from legacy I/O port space
* @filp: open sysfs file
@@ -889,12 +910,30 @@ static ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj,
char *buf, loff_t off, size_t count)
{
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
+ u32 val = 0;
+ int ret;
/* Only support 1, 2 or 4 byte accesses */
if (count != 1 && count != 2 && count != 4)
return -EINVAL;
- return pci_legacy_read(bus, off, (u32 *)buf, count);
+ ret = pci_legacy_read(bus, off, &val, count);
+ if (ret < 0)
+ return ret;
+
+ switch (count) {
+ case 1:
+ buf[0] = *(u8 *)&val;
+ break;
+ case 2:
+ put_unaligned_le16(*(u16 *)&val, buf);
+ break;
+ case 4:
+ put_unaligned_le32(val, buf);
+ break;
+ }
+
+ return ret;
}
/**
@@ -914,12 +953,24 @@ static ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj,
char *buf, loff_t off, size_t count)
{
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
+ u32 val;
- /* Only support 1, 2 or 4 byte accesses */
- if (count != 1 && count != 2 && count != 4)
+ /* Only support 1, 2 or 4 byte accesses. */
+ switch (count) {
+ case 1:
+ val = *(u8 *)buf;
+ break;
+ case 2:
+ val = get_unaligned_le16(buf);
+ break;
+ case 4:
+ val = get_unaligned_le32(buf);
+ break;
+ default:
return -EINVAL;
+ }
- return pci_legacy_write(bus, off, *(u32 *)buf, count);
+ return pci_legacy_write(bus, off, val, count);
}
/**
@@ -938,6 +989,11 @@ static int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj,
struct vm_area_struct *vma)
{
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
+ int ret;
+
+ ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+ if (ret)
+ return ret;
return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem);
}
@@ -958,6 +1014,11 @@ static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
struct vm_area_struct *vma)
{
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
+ int ret;
+
+ ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+ if (ret)
+ return ret;
return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
}
@@ -974,6 +1035,11 @@ static inline umode_t __pci_legacy_is_visible(struct kobject *kobj,
bool sparse)
{
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
+ int ret;
+
+ ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+ if (ret)
+ return ret;
if (pci_legacy_has_sparse(bus, type) != sparse)
return 0;
@@ -1015,41 +1081,11 @@ static loff_t pci_llseek_resource_legacy(struct file *filep,
return fixed_size_llseek(filep, offset, whence, attr->size);
}
-static const struct bin_attribute pci_legacy_io_attr = {
- .attr = { .name = "legacy_io", .mode = 0600 },
- .size = PCI_LEGACY_IO_SIZE,
- .read = pci_read_legacy_io,
- .write = pci_write_legacy_io,
- .mmap = pci_mmap_legacy_io,
- .llseek = pci_llseek_resource_legacy,
- .f_mapping = iomem_get_mapping,
-};
-
-static const struct bin_attribute pci_legacy_io_sparse_attr = {
- .attr = { .name = "legacy_io_sparse", .mode = 0600 },
- .size = PCI_LEGACY_IO_SIZE << 5,
- .read = pci_read_legacy_io,
- .write = pci_write_legacy_io,
- .mmap = pci_mmap_legacy_io,
- .llseek = pci_llseek_resource_legacy,
- .f_mapping = iomem_get_mapping,
-};
-
-static const struct bin_attribute pci_legacy_mem_attr = {
- .attr = { .name = "legacy_mem", .mode = 0600 },
- .size = PCI_LEGACY_MEM_SIZE,
- .mmap = pci_mmap_legacy_mem,
- .llseek = pci_llseek_resource_legacy,
- .f_mapping = iomem_get_mapping,
-};
+pci_legacy_resource_io_attr(, PCI_LEGACY_IO_SIZE);
+pci_legacy_resource_io_attr(_sparse, PCI_LEGACY_IO_SIZE << 5);
-static const struct bin_attribute pci_legacy_mem_sparse_attr = {
- .attr = { .name = "legacy_mem_sparse", .mode = 0600 },
- .size = PCI_LEGACY_MEM_SIZE << 5,
- .mmap = pci_mmap_legacy_mem,
- .llseek = pci_llseek_resource_legacy,
- .f_mapping = iomem_get_mapping,
-};
+pci_legacy_resource_mem_attr(, PCI_LEGACY_MEM_SIZE);
+pci_legacy_resource_mem_attr(_sparse, PCI_LEGACY_MEM_SIZE << 5);
static const struct bin_attribute *const pci_legacy_io_attrs[] = {
&pci_legacy_io_attr,
@@ -1240,16 +1276,25 @@ static loff_t pci_llseek_resource(struct file *filep,
* attribute, it's not going to work, so override it as well.
*/
#if arch_can_pci_mmap_io()
-# define __PCI_RESOURCE_IO_MMAP_ATTRS \
- .f_mapping = iomem_get_mapping, \
- .llseek = pci_llseek_resource, \
+# define __PCI_RESOURCE_IO_MMAP_ATTRS \
+ .f_mapping = iomem_get_mapping, \
+ .llseek = pci_llseek_resource, \
.mmap = pci_mmap_resource_uc,
#else
-# define __PCI_RESOURCE_IO_MMAP_ATTRS
+static int pci_mmap_resource_io_unsupported(struct file *filp,
+ struct kobject *kobj,
+ const struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ return -EINVAL;
+}
+
+# define __PCI_RESOURCE_IO_MMAP_ATTRS \
+ .mmap = pci_mmap_resource_io_unsupported,
#endif
#define pci_dev_resource_io_attr(_bar) \
-static const struct bin_attribute dev_resource##_bar##_io_attr = { \
+static const struct bin_attribute pci_dev_resource##_bar##_io_attr = { \
.attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \
.private = (void *)(unsigned long)(_bar), \
.read = pci_read_resource, \
@@ -1258,7 +1303,7 @@ static const struct bin_attribute dev_resource##_bar##_io_attr = { \
}
#define pci_dev_resource_uc_attr(_bar) \
-static const struct bin_attribute dev_resource##_bar##_uc_attr = { \
+static const struct bin_attribute pci_dev_resource##_bar##_uc_attr = { \
.attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \
.private = (void *)(unsigned long)(_bar), \
.f_mapping = iomem_get_mapping, \
@@ -1266,8 +1311,8 @@ static const struct bin_attribute dev_resource##_bar##_uc_attr = { \
.mmap = pci_mmap_resource_uc, \
}
-#define pci_dev_resource_wc_attr(_bar) \
-static const struct bin_attribute dev_resource##_bar##_wc_attr = { \
+#define pci_dev_resource_wc_attr(_bar) \
+static const struct bin_attribute pci_dev_resource##_bar##_wc_attr = { \
.attr = { .name = "resource" __stringify(_bar) "_wc", .mode = 0600 }, \
.private = (void *)(unsigned long)(_bar), \
.f_mapping = iomem_get_mapping, \
@@ -1353,32 +1398,32 @@ pci_dev_resource_wc_attr(4);
pci_dev_resource_wc_attr(5);
static const struct bin_attribute *const pci_dev_resource_io_attrs[] = {
- &dev_resource0_io_attr,
- &dev_resource1_io_attr,
- &dev_resource2_io_attr,
- &dev_resource3_io_attr,
- &dev_resource4_io_attr,
- &dev_resource5_io_attr,
+ &pci_dev_resource0_io_attr,
+ &pci_dev_resource1_io_attr,
+ &pci_dev_resource2_io_attr,
+ &pci_dev_resource3_io_attr,
+ &pci_dev_resource4_io_attr,
+ &pci_dev_resource5_io_attr,
NULL,
};
static const struct bin_attribute *const pci_dev_resource_uc_attrs[] = {
- &dev_resource0_uc_attr,
- &dev_resource1_uc_attr,
- &dev_resource2_uc_attr,
- &dev_resource3_uc_attr,
- &dev_resource4_uc_attr,
- &dev_resource5_uc_attr,
+ &pci_dev_resource0_uc_attr,
+ &pci_dev_resource1_uc_attr,
+ &pci_dev_resource2_uc_attr,
+ &pci_dev_resource3_uc_attr,
+ &pci_dev_resource4_uc_attr,
+ &pci_dev_resource5_uc_attr,
NULL,
};
static const struct bin_attribute *const pci_dev_resource_wc_attrs[] = {
- &dev_resource0_wc_attr,
- &dev_resource1_wc_attr,
- &dev_resource2_wc_attr,
- &dev_resource3_wc_attr,
- &dev_resource4_wc_attr,
- &dev_resource5_wc_attr,
+ &pci_dev_resource0_wc_attr,
+ &pci_dev_resource1_wc_attr,
+ &pci_dev_resource2_wc_attr,
+ &pci_dev_resource3_wc_attr,
+ &pci_dev_resource4_wc_attr,
+ &pci_dev_resource5_wc_attr,
NULL,
};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a73d1a3a8939..b2879a6be5f8 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -17,6 +17,7 @@
#include <linux/lockdep.h>
#include <linux/msi.h>
#include <linux/of.h>
+#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/slab.h>
@@ -1136,6 +1137,16 @@ static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
return acpi_pci_bridge_d3(dev);
}
+void platform_pci_configure_wake(struct pci_dev *dev)
+{
+ pci_configure_of_wake_gpio(dev);
+}
+
+void platform_pci_remove_wake(struct pci_dev *dev)
+{
+ pci_remove_of_wake_gpio(dev);
+}
+
/**
* pci_update_current_state - Read power state of given device and cache it
* @dev: PCI device to handle.
@@ -3042,11 +3053,11 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
return true;
/*
- * Hotplug ports handled natively by the OS were not validated
- * by vendors for runtime D3 at least until 2018 because there
- * was no OS support.
+ * Hotplug ports handled natively by the OS on x86 platforms
+ * were not validated by vendors for runtime D3 at least until
+ * 2018 because there was no OS support.
*/
- if (bridge->is_pciehp)
+ if (IS_ENABLED(CONFIG_X86) && bridge->is_pciehp)
return false;
if (dmi_check_system(bridge_d3_blacklist))
@@ -4866,6 +4877,19 @@ void pci_reset_secondary_bus(struct pci_dev *dev)
void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
{
+ struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
+ int ret;
+
+ if (pci_is_root_bus(dev->bus) && host->reset_root_port) {
+ ret = host->reset_root_port(host, dev);
+ if (ret)
+ pci_err(dev, "Failed to reset Root Port: %d\n", ret);
+ else
+ pci_restore_state(dev);
+
+ return;
+ }
+
pci_reset_secondary_bus(dev);
}
@@ -5716,6 +5740,7 @@ int pci_bus_error_reset(struct pci_dev *bridge)
{
return pci_reset_bridge(bridge, PCI_RESET_NO_RESTORE);
}
+EXPORT_SYMBOL_GPL(pci_bus_error_reset);
int pci_try_reset_bridge(struct pci_dev *bridge)
{
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4469e1a77f3c..ba3c3fddddc2 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -2,6 +2,7 @@
#ifndef DRIVERS_PCI_H
#define DRIVERS_PCI_H
+#include <linux/bug.h>
#include <linux/align.h>
#include <linux/bitfield.h>
#include <linux/pci.h>
@@ -285,6 +286,8 @@ void pci_msix_init(struct pci_dev *dev);
bool pci_bridge_d3_possible(struct pci_dev *dev);
void pci_bridge_d3_update(struct pci_dev *dev);
int pci_bridge_wait_for_secondary_bus(struct pci_dev *dev, char *reset_type);
+void platform_pci_configure_wake(struct pci_dev *dev);
+void platform_pci_remove_wake(struct pci_dev *dev);
static inline bool pci_bus_rrs_vendor_id(u32 l)
{
@@ -442,21 +445,40 @@ static inline int pci_setup_cardbus(char *str) { return -ENOENT; }
#endif /* CONFIG_CARDBUS */
/**
- * pci_match_one_device - Tell if a PCI device structure has a matching
- * PCI device id structure
- * @id: single PCI device id structure to match
- * @dev: the PCI device structure to match against
+ * pci_id_from_device - Obtain a pci_device_id from a PCI device
+ * @dev: the PCI device
*
- * Returns the matching pci_device_id structure or %NULL if there is no match.
+ * Return: a pci_device_id filled.
*/
-static inline const struct pci_device_id *
-pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
+static inline struct pci_device_id pci_id_from_device(const struct pci_dev *dev)
{
- if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
- (id->device == PCI_ANY_ID || id->device == dev->device) &&
- (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
- (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
- !((id->class ^ dev->class) & id->class_mask))
+ return (struct pci_device_id) {
+ .vendor = dev->vendor,
+ .device = dev->device,
+ .subvendor = dev->subsystem_vendor,
+ .subdevice = dev->subsystem_device,
+ .class = dev->class,
+ };
+}
+
+/**
+ * pci_match_one_id - Tell if a PCI device ID matches a needle PCI device ID
+ * @id: single PCI device id structure to match against (needle)
+ * @dev_id: the actual ID from the PCI device
+ *
+ * ID can be retrieved from device using pci_id_from_device().
+ *
+ * Return: the matching pci_device_id structure or %NULL if there is no match.
+ */
+static inline const struct pci_device_id *
+pci_match_one_id(const struct pci_device_id *id,
+ const struct pci_device_id *dev_id)
+{
+ if ((id->vendor == PCI_ANY_ID || id->vendor == dev_id->vendor) &&
+ (id->device == PCI_ANY_ID || id->device == dev_id->device) &&
+ (id->subvendor == PCI_ANY_ID || id->subvendor == dev_id->subvendor) &&
+ (id->subdevice == PCI_ANY_ID || id->subdevice == dev_id->subdevice) &&
+ !((id->class ^ dev_id->class) & id->class_mask))
return id;
return NULL;
}
@@ -599,6 +621,28 @@ void pci_bus_put(struct pci_bus *bus);
(speed) == PCIE_SPEED_2_5GT ? 2500*8/10 : \
0)
+static inline bool pcie_valid_speed(enum pci_bus_speed speed)
+{
+ return (speed >= PCIE_SPEED_2_5GT) && (speed <= PCIE_SPEED_64_0GT);
+}
+
+static inline u16 pci_bus_speed2lnkctl2(enum pci_bus_speed speed)
+{
+ static const u8 speed_conv[] = {
+ [PCIE_SPEED_2_5GT] = PCI_EXP_LNKCTL2_TLS_2_5GT,
+ [PCIE_SPEED_5_0GT] = PCI_EXP_LNKCTL2_TLS_5_0GT,
+ [PCIE_SPEED_8_0GT] = PCI_EXP_LNKCTL2_TLS_8_0GT,
+ [PCIE_SPEED_16_0GT] = PCI_EXP_LNKCTL2_TLS_16_0GT,
+ [PCIE_SPEED_32_0GT] = PCI_EXP_LNKCTL2_TLS_32_0GT,
+ [PCIE_SPEED_64_0GT] = PCI_EXP_LNKCTL2_TLS_64_0GT,
+ };
+
+ if (WARN_ON_ONCE(!pcie_valid_speed(speed)))
+ return 0;
+
+ return speed_conv[speed];
+}
+
static inline int pcie_dev_speed_mbps(enum pci_bus_speed speed)
{
switch (speed) {
@@ -828,6 +872,9 @@ static inline bool pci_dev_binding_disallowed(struct pci_dev *dev)
* @tlp_header_valid: Indicates if TLP field contains error information
* @status: COR/UNCOR error status
* @mask: COR/UNCOR mask
+ * @anfe_status: Advisory Non-Fatal Errors, i.e. Uncorrectable Errors signaled
+ * as Correctable Errors (PCIe r7.0 sec 6.2.4.3). Only used if @severity
+ * is AER_CORRECTABLE and @status has Advisory Non-Fatal Error Status set.
* @tlp: Transaction packet information
*/
struct aer_err_info {
@@ -850,6 +897,7 @@ struct aer_err_info {
unsigned int status;
unsigned int mask;
+ u32 anfe_status;
struct pcie_tlp_log tlp;
};
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index c4fd9c0b2a54..d8dcd238fda1 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -96,6 +96,20 @@ struct aer_info {
struct ratelimit_state nonfatal_ratelimit;
};
+#define AER_ANFE (PCI_ERR_UNC_UNX_COMP | \
+ PCI_ERR_UNC_ATOMEG | \
+ PCI_ERR_UNC_DMWR_BLK)
+
+#define AER_POSSIBLE_ANFE (PCI_ERR_UNC_POISON_TLP | \
+ PCI_ERR_UNC_POISON_BLK | \
+ PCI_ERR_UNC_ECRC | \
+ PCI_ERR_UNC_UNSUP | \
+ PCI_ERR_UNC_COMP_TIME | \
+ PCI_ERR_UNC_COMP_ABORT | \
+ PCI_ERR_UNC_ACSV | \
+ PCI_ERR_UNC_TLPPRE | \
+ PCI_ERR_UNC_PCRC_CHECK)
+
#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \
PCI_ERR_UNC_POISON_BLK | \
PCI_ERR_UNC_ECRC| \
@@ -410,6 +424,15 @@ void pci_aer_init(struct pci_dev *dev)
n = pcie_cap_has_rtctl(dev) ? 5 : 4;
pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ERR, sizeof(u32) * n);
+ /*
+ * Advisory Non-Fatal Errors are masked by default (PCIe r7.0, sec
+ * 7.8.4.6).
+ */
+ if (pcie_aer_is_native(dev) && dev->devcap & PCI_EXP_DEVCAP_RBER)
+ pci_clear_and_set_config_dword(dev,
+ dev->aer_cap + PCI_ERR_COR_MASK,
+ PCI_ERR_COR_ADV_NFAT, 0);
+
pci_aer_clear_status(dev);
if (pci_aer_available())
@@ -428,23 +451,38 @@ void pci_aer_exit(struct pci_dev *dev)
#define AER_AGENT_REQUESTER 1
#define AER_AGENT_COMPLETER 2
#define AER_AGENT_TRANSMITTER 3
+#define AER_AGENT_COMPONENT 4
+#define AER_AGENT_UNDEF 5
#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \
- 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP))
+ 0 : PCI_ERR_UNC_COMP_TIME)
#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \
0 : PCI_ERR_UNC_COMP_ABORT)
#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \
- (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0)
+ (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : \
+ (PCI_ERR_UNC_POISON_BLK|PCI_ERR_UNC_ATOMEG| \
+ PCI_ERR_UNC_DMWR_BLK|PCI_ERR_UNC_XLAT_BLK| \
+ PCI_ERR_UNC_TLPPRE))
+#define AER_AGENT_COMPONENT_MASK(t) ((t == AER_CORRECTABLE) ? \
+ (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : \
+ (PCI_ERR_UNC_INTN|PCI_ERR_UNC_SURPDN))
+#define AER_AGENT_UNDEF_MASK(t) ((t == AER_CORRECTABLE) ? \
+ PCI_ERR_COR_ADV_NFAT : 0)
+
#define AER_GET_AGENT(t, e) \
((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \
(e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \
(e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \
+ (e & AER_AGENT_COMPONENT_MASK(t)) ? AER_AGENT_COMPONENT : \
+ (e & AER_AGENT_UNDEF_MASK(t)) ? AER_AGENT_UNDEF : \
AER_AGENT_RECEIVER)
#define AER_PHYSICAL_LAYER_ERROR 0
#define AER_DATA_LINK_LAYER_ERROR 1
#define AER_TRANSACTION_LAYER_ERROR 2
+#define AER_GENERAL_ERROR 3
+#define AER_UNDEF_ERROR 4
#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \
PCI_ERR_COR_RCVR : 0)
@@ -452,11 +490,17 @@ void pci_aer_exit(struct pci_dev *dev)
(PCI_ERR_COR_BAD_TLP| \
PCI_ERR_COR_BAD_DLLP| \
PCI_ERR_COR_REP_ROLL| \
- PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP)
+ PCI_ERR_COR_REP_TIMER) : (PCI_ERR_UNC_DLP|PCI_ERR_UNC_SURPDN))
+#define AER_GENERAL_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \
+ (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : PCI_ERR_UNC_INTN)
+#define AER_UNDEF_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \
+ PCI_ERR_COR_ADV_NFAT : 0)
#define AER_GET_LAYER_ERROR(t, e) \
((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \
(e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \
+ (e & AER_GENERAL_ERROR_MASK(t)) ? AER_GENERAL_ERROR : \
+ (e & AER_UNDEF_ERROR_MASK(t)) ? AER_UNDEF_ERROR : \
AER_TRANSACTION_LAYER_ERROR)
/*
@@ -471,7 +515,9 @@ static const char * const aer_error_severity_string[] = {
static const char *aer_error_layer[] = {
"Physical Layer",
"Data Link Layer",
- "Transaction Layer"
+ "Transaction Layer",
+ "General",
+ "",
};
static const char *aer_correctable_error_string[] = {
@@ -545,10 +591,12 @@ static const char *aer_uncorrectable_error_string[] = {
};
static const char *aer_agent_string[] = {
- "Receiver ID",
- "Requester ID",
- "Completer ID",
- "Transmitter ID"
+ "Receiver",
+ "Requester",
+ "Completer",
+ "Transmitter",
+ "Component",
+ "",
};
#define aer_stats_dev_attr(name, stats_array, strings_array, \
@@ -812,6 +860,46 @@ static int aer_ratelimit(struct pci_dev *dev, unsigned int severity)
}
}
+static u32 aer_compute_anfe_status(u16 devsta, u32 uncor_status,
+ u32 uncor_mask, u32 uncor_severity)
+{
+ u32 anfe_status;
+
+ /*
+ * Uncorrectable Errors must be unmasked and have Non-Fatal severity
+ * to qualify as Advisory Non-Fatal Errors (PCIe r7.0 sec 6.2.4.3).
+ */
+ uncor_status &= ~uncor_mask & ~uncor_severity;
+
+ /* Some Non-Fatal Errors are always Advisory (PCIe r7.0 sec 6.2.7). */
+ anfe_status = uncor_status & AER_ANFE;
+
+ /*
+ * Others may be Advisory at the discretion of the detecting agent.
+ * That's impossible to discern if the agent signaled ERR_NONFATAL
+ * in addition to ERR_COR. Assume none are Advisory in that case
+ * to ensure that the Uncorrectable Error code path is taken.
+ */
+ if (!(devsta & PCI_EXP_DEVSTA_NFED))
+ anfe_status |= uncor_status & AER_POSSIBLE_ANFE;
+
+ return anfe_status;
+}
+
+static u32 aer_get_anfe_status(struct pci_dev *dev)
+{
+ u32 uncor_status, uncor_mask, uncor_severity;
+ u16 devsta, aer = dev->aer_cap;
+
+ pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &uncor_status);
+ pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &uncor_mask);
+ pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_SEVER, &uncor_severity);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &devsta);
+
+ return aer_compute_anfe_status(devsta, uncor_status, uncor_mask,
+ uncor_severity);
+}
+
static bool tlp_header_logged(u32 status, u32 capctl)
{
/* Errors for which a header is always logged (PCIe r7.0 sec 6.2.7) */
@@ -830,8 +918,8 @@ static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
{
const char **strings;
unsigned long status = info->status & ~info->mask;
+ const char *errmsg, *agent, *layer;
const char *level = info->level;
- const char *errmsg;
int i;
if (info->severity == AER_CORRECTABLE)
@@ -841,10 +929,18 @@ static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
for_each_set_bit(i, &status, 32) {
errmsg = strings[i];
- if (!errmsg)
+ agent = aer_agent_string[AER_GET_AGENT(info->severity, BIT(i))];
+ layer = aer_error_layer[AER_GET_LAYER_ERROR(info->severity,
+ BIT(i))];
+ if (!errmsg) {
errmsg = "Unknown Error Bit";
+ agent = "";
+ layer = "";
+ }
- aer_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg,
+ aer_printk(level, dev, " [%2d] %-17s | %-11s | %-17s%s\n",
+ i, errmsg, agent, layer,
+ info->severity != AER_CORRECTABLE &&
info->first_error == i ? " (First)" : "");
}
}
@@ -854,18 +950,18 @@ static void aer_print_source(struct pci_dev *dev, struct aer_err_info *info,
{
u16 source = info->id;
- pci_info(dev, "%s%s error message received from %04x:%02x:%02x.%d%s\n",
+ pci_info(dev, "%s%s Error message%s from %04x:%02x:%02x.%d%s\n",
info->multi_error_valid ? "Multiple " : "",
aer_error_severity_string[info->severity],
+ info->multi_error_valid ? "s received, first one" : " received",
pci_domain_nr(dev->bus), PCI_BUS_NUM(source),
PCI_SLOT(source), PCI_FUNC(source),
- found ? "" : " (no details found");
+ found ? "" : " (no details found)");
}
void aer_print_error(struct aer_err_info *info, int i)
{
struct pci_dev *dev;
- int layer, agent, id;
const char *level = info->level;
const char *bus_type = aer_err_bus(info);
@@ -873,39 +969,48 @@ void aer_print_error(struct aer_err_info *info, int i)
return;
dev = info->dev[i];
- id = pci_dev_id(dev);
pci_dev_aer_stats_incr(dev, info);
trace_aer_event(pci_name(dev), (info->status & ~info->mask),
- info->severity, info->tlp_header_valid, &info->tlp, bus_type);
+ info->severity, info->tlp_header_valid &&
+ info->severity != AER_CORRECTABLE, &info->tlp,
+ bus_type);
+ /*
+ * For Advisory Non-Fatal Errors, record statistics and tracing
+ * even if ratelimited
+ */
if (!info->ratelimit_print[i])
- return;
+ goto anfe;
if (!info->status) {
- pci_err(dev, "%s Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n",
+ pci_err(dev, "%s Bus Error: severity=%s (Inaccessible)\n",
bus_type, aer_error_severity_string[info->severity]);
- goto out;
+ return;
}
- layer = AER_GET_LAYER_ERROR(info->severity, info->status);
- agent = AER_GET_AGENT(info->severity, info->status);
-
- aer_printk(level, dev, "%s Bus Error: severity=%s, type=%s, (%s)\n",
- bus_type, aer_error_severity_string[info->severity],
- aer_error_layer[layer], aer_agent_string[agent]);
+ aer_printk(level, dev, "%s Bus Error: severity=%s\n",
+ bus_type, aer_error_severity_string[info->severity]);
aer_printk(level, dev, " device [%04x:%04x] error status/mask=%08x/%08x\n",
dev->vendor, dev->device, info->status, info->mask);
__aer_print_error(dev, info);
- if (info->tlp_header_valid)
+ if (info->tlp_header_valid && info->severity != AER_CORRECTABLE)
pcie_print_tlp_log(dev, &info->tlp, level, dev_fmt(" "));
-out:
- if (info->id && info->error_dev_num > 1 && info->id == id)
- pci_err(dev, " Error of this Agent is reported first\n");
+anfe:
+ /* Recursive invocation for Advisory Non-Fatal Errors */
+ if (info->anfe_status && info->severity == AER_CORRECTABLE) {
+ info->severity = AER_NONFATAL;
+ info->status = info->anfe_status;
+ info->mask = 0;
+
+ aer_print_error(info, i);
+
+ info->severity = AER_CORRECTABLE;
+ }
}
#ifdef CONFIG_ACPI_APEI_PCIEAER
@@ -923,13 +1028,15 @@ int cper_severity_to_aer(int cper_severity)
EXPORT_SYMBOL_GPL(cper_severity_to_aer);
#endif
-void pci_print_aer(struct pci_dev *dev, int aer_severity,
- struct aer_capability_regs *aer)
+static void __pci_print_aer(struct pci_dev *dev, int aer_severity,
+ struct aer_capability_regs *aer,
+ bool ratelimit_print, const char *level)
{
- const char *bus_type;
- int layer, agent, tlp_header_valid = 0;
+ const char *bus_type, *sev;
+ int tlp_header_valid = 0;
u32 status, mask;
struct aer_err_info info = {
+ .level = level,
.severity = aer_severity,
.first_error = PCI_ERR_CAP_FEP(aer->cap_control),
};
@@ -937,12 +1044,13 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity,
if (aer_severity == AER_CORRECTABLE) {
status = aer->cor_status;
mask = aer->cor_mask;
- info.level = KERN_WARNING;
+ sev = "cor";
} else {
status = aer->uncor_status;
mask = aer->uncor_mask;
- info.level = KERN_ERR;
- tlp_header_valid = tlp_header_logged(status, aer->cap_control);
+ sev = "uncor";
+ tlp_header_valid = tlp_header_logged(status & ~mask,
+ aer->cap_control);
}
info.status = status;
@@ -955,17 +1063,17 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity,
trace_aer_event(pci_name(dev), (status & ~mask), aer_severity,
tlp_header_valid, &aer->header_log, bus_type);
- if (!aer_ratelimit(dev, info.severity))
- return;
-
- layer = AER_GET_LAYER_ERROR(aer_severity, status);
- agent = AER_GET_AGENT(aer_severity, status);
+ /*
+ * For Advisory Non-Fatal Errors, record statistics and tracing
+ * even if ratelimited
+ */
+ if (!ratelimit_print)
+ goto anfe;
- aer_printk(info.level, dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n",
- status, mask);
+ aer_printk(info.level, dev,
+ "aer_%s_status: 0x%08x, aer_%s_mask: 0x%08x\n",
+ sev, status, sev, mask);
__aer_print_error(dev, &info);
- aer_printk(info.level, dev, "aer_layer=%s, aer_agent=%s\n",
- aer_error_layer[layer], aer_agent_string[agent]);
if (aer_severity != AER_CORRECTABLE)
aer_printk(info.level, dev, "aer_uncor_severity: 0x%08x\n",
@@ -974,6 +1082,36 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity,
if (tlp_header_valid)
pcie_print_tlp_log(dev, &aer->header_log, info.level,
dev_fmt(" "));
+
+anfe:
+ /* Recursive invocation for Advisory Non-Fatal Errors */
+ if (aer_severity == AER_CORRECTABLE &&
+ info.status & ~info.mask & PCI_ERR_COR_ADV_NFAT) {
+ u32 anfe_status = aer_compute_anfe_status(PCI_EXP_DEVSTA_CED,
+ aer->uncor_status,
+ aer->uncor_mask,
+ aer->uncor_severity);
+ if (anfe_status) {
+ aer->uncor_status = anfe_status;
+ aer->uncor_mask = 0;
+ __pci_print_aer(dev, AER_NONFATAL, aer,
+ ratelimit_print, level);
+ }
+ }
+}
+
+void pci_print_aer(struct pci_dev *dev, int aer_severity,
+ struct aer_capability_regs *aer)
+{
+ /*
+ * Precalculate ratelimit counter and log level so that Advisory
+ * Non-Fatal Errors are treated like the accompanying Correctable Error
+ */
+ bool ratelimit_print = aer_ratelimit(dev, aer_severity);
+ const char *level = aer_severity == AER_CORRECTABLE ? KERN_WARNING
+ : KERN_ERR;
+
+ __pci_print_aer(dev, aer_severity, aer, ratelimit_print, level);
}
EXPORT_SYMBOL_GPL(pci_print_aer);
@@ -1176,9 +1314,14 @@ static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info)
* Correctable error does not need software intervention.
* No need to go through error recovery process.
*/
- if (aer)
+ if (aer) {
pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS,
info->status);
+ if (info->anfe_status)
+ pci_write_config_dword(dev,
+ aer + PCI_ERR_UNCOR_STATUS,
+ info->anfe_status);
+ }
if (pcie_aer_is_native(dev)) {
struct pci_driver *pdrv = dev->driver;
@@ -1281,6 +1424,27 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
EXPORT_SYMBOL_GPL(aer_recover_queue);
#endif
+static void aer_get_uncor_info(struct pci_dev *dev, struct aer_err_info *info,
+ u32 status)
+{
+ u16 aer = dev->aer_cap;
+ u32 aercc;
+
+ /* Get First Error Pointer */
+ pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc);
+ info->first_error = PCI_ERR_CAP_FEP(aercc);
+
+ /* Get TLP Prefix/Header Log */
+ if (tlp_header_logged(status, aercc)) {
+ info->tlp_header_valid = 1;
+ pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG,
+ aer + PCI_ERR_PREFIX_LOG,
+ aer_tlp_log_len(dev, aercc),
+ aercc & PCI_ERR_CAP_TLP_LOG_FLIT,
+ &info->tlp);
+ }
+}
+
/**
* aer_get_device_error_info - read error status from dev and store it to info
* @info: pointer to structure to store the error record
@@ -1294,7 +1458,6 @@ int aer_get_device_error_info(struct aer_err_info *info, int i)
{
struct pci_dev *dev;
int type, aer;
- u32 aercc;
if (i >= AER_MAX_MULTI_ERR_DEVICES)
return 0;
@@ -1305,6 +1468,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i)
/* Must reset in this function */
info->status = 0;
+ info->anfe_status = 0;
info->tlp_header_valid = 0;
info->is_cxl = pcie_is_cxl(dev);
@@ -1319,6 +1483,10 @@ int aer_get_device_error_info(struct aer_err_info *info, int i)
&info->mask);
if (!(info->status & ~info->mask))
return 0;
+ if (info->status & ~info->mask & PCI_ERR_COR_ADV_NFAT) {
+ info->anfe_status = aer_get_anfe_status(dev);
+ aer_get_uncor_info(dev, info, info->anfe_status);
+ }
} else if (type == PCI_EXP_TYPE_ROOT_PORT ||
type == PCI_EXP_TYPE_RC_EC ||
type == PCI_EXP_TYPE_DOWNSTREAM ||
@@ -1332,18 +1500,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i)
if (!(info->status & ~info->mask))
return 0;
- /* Get First Error Pointer */
- pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc);
- info->first_error = PCI_ERR_CAP_FEP(aercc);
-
- if (tlp_header_logged(info->status, aercc)) {
- info->tlp_header_valid = 1;
- pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG,
- aer + PCI_ERR_PREFIX_LOG,
- aer_tlp_log_len(dev, aercc),
- aercc & PCI_ERR_CAP_TLP_LOG_FLIT,
- &info->tlp);
- }
+ aer_get_uncor_info(dev, info, info->status & ~info->mask);
}
return 1;
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 172783e7f519..95ac34a34bd5 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -839,6 +839,49 @@ static void aspm_l1ss_init(struct pcie_link_state *link)
#define FLAG(x, y, d) (((x) & (PCIE_LINK_STATE_##y)) ? d : "")
+/* Configure the ASPM L1 substates. Caller must disable L1 first. */
+static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
+{
+ u32 val = 0;
+ struct pci_dev *child = link->downstream, *parent = link->pdev;
+
+ if (state & PCIE_LINK_STATE_L1_1)
+ val |= PCI_L1SS_CTL1_ASPM_L1_1;
+ if (state & PCIE_LINK_STATE_L1_2)
+ val |= PCI_L1SS_CTL1_ASPM_L1_2;
+ if (state & PCIE_LINK_STATE_L1_1_PCIPM)
+ val |= PCI_L1SS_CTL1_PCIPM_L1_1;
+ if (state & PCIE_LINK_STATE_L1_2_PCIPM)
+ val |= PCI_L1SS_CTL1_PCIPM_L1_2;
+
+ /*
+ * PCIe r6.2, sec 5.5.4, rules for enabling L1 PM Substates:
+ * - Clear L1.x enable bits at child first, then at parent
+ * - Set L1.x enable bits at parent first, then at child
+ * - ASPM/PCIPM L1.2 must be disabled while programming timing
+ * parameters
+ */
+
+ /* Disable all L1 substates */
+ pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1SS_MASK, 0);
+ pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1SS_MASK, 0);
+
+ /* Enable what we need to enable */
+ pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1SS_MASK, val);
+ pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1SS_MASK, val);
+}
+
+static bool pcie_link_has_aspm_override(const struct pcie_link_state *link,
+ const char *aspm)
+{
+ return (device_property_present(&link->pdev->dev, aspm) ||
+ device_property_present(&link->downstream->dev, aspm));
+}
+
static void pcie_aspm_override_default_link_state(struct pcie_link_state *link)
{
struct pci_dev *pdev = link->downstream;
@@ -846,6 +889,36 @@ static void pcie_aspm_override_default_link_state(struct pcie_link_state *link)
/* For devicetree platforms, enable L0s and L1 by default */
if (of_have_populated_dt()) {
+ bool no_l0s = pcie_link_has_aspm_override(link, "aspm-no-l0s");
+ bool no_l1 = pcie_link_has_aspm_override(link, "aspm-no-l1");
+ bool no_l1ss = pcie_link_has_aspm_override(link, "aspm-no-l1ss");
+
+ if (no_l0s) {
+ link->aspm_support &= ~PCIE_LINK_STATE_L0S;
+ link->aspm_default &= ~PCIE_LINK_STATE_L0S;
+ link->aspm_enabled &= ~PCIE_LINK_STATE_L0S;
+ }
+
+ /*
+ * Clear L1SS in hardware before updating aspm_support. Once
+ * aspm_capable is derived from aspm_support, pcie_config_aspm_link()
+ * skips pcie_config_aspm_l1ss() entirely via the aspm_capable guard,
+ * leaving firmware-enabled L1SS substates active in hardware.
+ * This applies equally when disabling L1 (which implies L1SS).
+ */
+ if ((no_l1 || no_l1ss) && (link->aspm_enabled & PCIE_LINK_STATE_L1SS))
+ pcie_config_aspm_l1ss(link, 0);
+
+ if (no_l1) {
+ link->aspm_support &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS);
+ link->aspm_default &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS);
+ link->aspm_enabled &= ~(PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L1SS);
+ } else if (no_l1ss) {
+ link->aspm_support &= ~PCIE_LINK_STATE_L1SS;
+ link->aspm_default &= ~PCIE_LINK_STATE_L1SS;
+ link->aspm_enabled &= ~PCIE_LINK_STATE_L1SS;
+ }
+
if (link->aspm_support & PCIE_LINK_STATE_L0S)
link->aspm_default |= PCIE_LINK_STATE_L0S;
if (link->aspm_support & PCIE_LINK_STATE_L1)
@@ -861,6 +934,7 @@ static void pcie_aspm_override_default_link_state(struct pcie_link_state *link)
static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
{
struct pci_dev *child = link->downstream, *parent = link->pdev;
+ struct pci_dev *fn;
u16 parent_lnkctl, child_lnkctl;
struct pci_bus *linkbus = parent->subordinate;
@@ -894,10 +968,11 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
/* Disable L0s/L1 before updating L1SS config */
if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) ||
FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) {
- pcie_capability_write_word(child, PCI_EXP_LNKCTL,
- child_lnkctl & ~PCI_EXP_LNKCTL_ASPMC);
- pcie_capability_write_word(parent, PCI_EXP_LNKCTL,
- parent_lnkctl & ~PCI_EXP_LNKCTL_ASPMC);
+ list_for_each_entry(fn, &linkbus->devices, bus_list)
+ pcie_capability_clear_and_set_word(fn, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPMC, 0);
+ pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPMC, 0);
}
/*
@@ -924,18 +999,34 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
aspm_l1ss_init(link);
- /* Restore L0s/L1 if they were enabled */
- if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) ||
- FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) {
- pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_lnkctl);
- pcie_capability_write_word(child, PCI_EXP_LNKCTL, child_lnkctl);
- }
-
/* Save default state */
link->aspm_default = link->aspm_enabled;
pcie_aspm_override_default_link_state(link);
+ /*
+ * Restore L0s/L1 if they were enabled, but don't restore any
+ * state a Devicetree override just disabled in aspm_support above.
+ */
+ if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) ||
+ FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) {
+ if (!(link->aspm_support & PCIE_LINK_STATE_L0S)) {
+ child_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L0S;
+ parent_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L0S;
+ }
+ if (!(link->aspm_support & PCIE_LINK_STATE_L1)) {
+ child_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L1;
+ parent_lnkctl &= ~PCI_EXP_LNKCTL_ASPM_L1;
+ }
+ pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPMC,
+ parent_lnkctl & PCI_EXP_LNKCTL_ASPMC);
+ list_for_each_entry(fn, &linkbus->devices, bus_list)
+ pcie_capability_clear_and_set_word(fn, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPMC,
+ child_lnkctl & PCI_EXP_LNKCTL_ASPMC);
+ }
+
/* Setup initial capable state. Will be updated later */
link->aspm_capable = link->aspm_support;
@@ -949,42 +1040,6 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
}
}
-/* Configure the ASPM L1 substates. Caller must disable L1 first. */
-static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
-{
- u32 val = 0;
- struct pci_dev *child = link->downstream, *parent = link->pdev;
-
- if (state & PCIE_LINK_STATE_L1_1)
- val |= PCI_L1SS_CTL1_ASPM_L1_1;
- if (state & PCIE_LINK_STATE_L1_2)
- val |= PCI_L1SS_CTL1_ASPM_L1_2;
- if (state & PCIE_LINK_STATE_L1_1_PCIPM)
- val |= PCI_L1SS_CTL1_PCIPM_L1_1;
- if (state & PCIE_LINK_STATE_L1_2_PCIPM)
- val |= PCI_L1SS_CTL1_PCIPM_L1_2;
-
- /*
- * PCIe r6.2, sec 5.5.4, rules for enabling L1 PM Substates:
- * - Clear L1.x enable bits at child first, then at parent
- * - Set L1.x enable bits at parent first, then at child
- * - ASPM/PCIPM L1.2 must be disabled while programming timing
- * parameters
- */
-
- /* Disable all L1 substates */
- pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1,
- PCI_L1SS_CTL1_L1SS_MASK, 0);
- pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
- PCI_L1SS_CTL1_L1SS_MASK, 0);
-
- /* Enable what we need to enable */
- pci_clear_and_set_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
- PCI_L1SS_CTL1_L1SS_MASK, val);
- pci_clear_and_set_config_dword(child, child->l1ss + PCI_L1SS_CTL1,
- PCI_L1SS_CTL1_L1SS_MASK, val);
-}
-
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
diff --git a/drivers/pci/pcie/bwctrl.c b/drivers/pci/pcie/bwctrl.c
index c4c8d260bf96..d48021bfd844 100644
--- a/drivers/pci/pcie/bwctrl.c
+++ b/drivers/pci/pcie/bwctrl.c
@@ -48,28 +48,6 @@ struct pcie_bwctrl_data {
/* Prevent port removal during Link Speed changes. */
static DECLARE_RWSEM(pcie_bwctrl_setspeed_rwsem);
-static bool pcie_valid_speed(enum pci_bus_speed speed)
-{
- return (speed >= PCIE_SPEED_2_5GT) && (speed <= PCIE_SPEED_64_0GT);
-}
-
-static u16 pci_bus_speed2lnkctl2(enum pci_bus_speed speed)
-{
- static const u8 speed_conv[] = {
- [PCIE_SPEED_2_5GT] = PCI_EXP_LNKCTL2_TLS_2_5GT,
- [PCIE_SPEED_5_0GT] = PCI_EXP_LNKCTL2_TLS_5_0GT,
- [PCIE_SPEED_8_0GT] = PCI_EXP_LNKCTL2_TLS_8_0GT,
- [PCIE_SPEED_16_0GT] = PCI_EXP_LNKCTL2_TLS_16_0GT,
- [PCIE_SPEED_32_0GT] = PCI_EXP_LNKCTL2_TLS_32_0GT,
- [PCIE_SPEED_64_0GT] = PCI_EXP_LNKCTL2_TLS_64_0GT,
- };
-
- if (WARN_ON_ONCE(!pcie_valid_speed(speed)))
- return 0;
-
- return speed_conv[speed];
-}
-
static inline u16 pcie_supported_speeds2target_speed(u8 supported_speeds)
{
return __fls(supported_speeds);
diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c
index bebe4bc111d7..d77403d8855b 100644
--- a/drivers/pci/pcie/err.c
+++ b/drivers/pci/pcie/err.c
@@ -256,11 +256,6 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
}
if (status == PCI_ERS_RESULT_NEED_RESET) {
- /*
- * TODO: Should call platform-specific
- * functions to reset slot before calling
- * drivers' slot_reset callbacks?
- */
status = PCI_ERS_RESULT_RECOVERED;
pci_dbg(bridge, "broadcast slot_reset message\n");
pci_walk_bridge(bridge, report_slot_reset, &status);
@@ -297,3 +292,4 @@ failed:
return status;
}
+EXPORT_SYMBOL_GPL(pcie_do_recovery);
diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c
index 2d6aa488fe7b..a9cbfc1d2bc7 100644
--- a/drivers/pci/pcie/portdrv.c
+++ b/drivers/pci/pcie/portdrv.c
@@ -264,7 +264,7 @@ static int get_port_device_capability(struct pci_dev *dev)
*/
if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
pci_aer_available() &&
- (pcie_ports_dpc_native || (services & PCIE_PORT_SERVICE_AER)))
+ (pcie_ports_dpc_native || host->native_aer))
services |= PCIE_PORT_SERVICE_DPC;
/* Enable bandwidth control if more than one speed is supported. */
@@ -330,8 +330,8 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
*/
static int pcie_port_device_register(struct pci_dev *dev)
{
- int status, capabilities, i, nr_service;
int irqs[PCIE_PORT_DEVICE_MAXSERVICES];
+ int status, capabilities, i;
/* Enable PCI Express port device */
status = pci_enable_device(dev);
@@ -355,29 +355,29 @@ static int pcie_port_device_register(struct pci_dev *dev)
if (status) {
capabilities &= PCIE_PORT_SERVICE_HP;
if (!capabilities)
- goto error_disable;
+ goto out;
}
/* Allocate child services if any */
- status = -ENODEV;
- nr_service = 0;
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
int service = 1 << i;
if (!(capabilities & service))
continue;
- if (!pcie_device_init(dev, service, irqs[i]))
- nr_service++;
+ if (pcie_device_init(dev, service, irqs[i]))
+ capabilities &= ~service;
}
- if (!nr_service)
- goto error_cleanup_irqs;
- return 0;
+out:
+ /*
+ * With no child services, we shouldn't need bus mastering or any IRQ
+ * vectors we allocated.
+ */
+ if (!capabilities) {
+ pci_free_irq_vectors(dev);
+ pci_clear_master(dev);
+ }
-error_cleanup_irqs:
- pci_free_irq_vectors(dev);
-error_disable:
- pci_disable_device(dev);
- return status;
+ return 0;
}
typedef int (*pcie_callback_t)(struct pcie_device *);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index dd0abbc63e18..27008e2ea5af 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2749,6 +2749,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
pci_init_capabilities(dev);
+ platform_pci_configure_wake(dev);
+
/*
* Add the device to our list of discovered devices
* and the bus list for fixup functions, etc.
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 71ad289fcb8e..f5fd860a0540 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -14,6 +14,8 @@
#include <linux/capability.h>
#include <linux/uaccess.h>
#include <linux/security.h>
+#include <linux/panic.h>
+#include <linux/sched.h>
#include <asm/byteorder.h>
#include "pci.h"
@@ -39,13 +41,16 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
* undefined locations (think of Intel PIIX4 as a typical example).
*/
- if (capable(CAP_SYS_ADMIN))
+ if (file_ns_capable(file, &init_user_ns, CAP_SYS_ADMIN))
size = dev->cfg_size;
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
else
size = 64;
+ if (!nbytes)
+ return 0;
+
if (pos >= size)
return 0;
if (nbytes >= size)
@@ -122,6 +127,15 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
if (ret)
return ret;
+ if (!nbytes)
+ return 0;
+
+ if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) {
+ pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x",
+ current->comm, pos);
+ add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+ }
+
if (pos >= size)
return 0;
if (nbytes >= size)
diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
index 1555e8a9b3ca..fd53a7db9875 100644
--- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
+++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
@@ -79,8 +79,7 @@ enum tc9563_pwrctrl_ports {
TC9563_USP,
TC9563_DSP1,
TC9563_DSP2,
- TC9563_DSP3,
- TC9563_ETHERNET,
+ TC9563_VDSP,
TC9563_MAX
};
@@ -108,6 +107,7 @@ struct tc9563_pwrctrl {
struct pci_pwrctrl pwrctrl;
struct regulator_bulk_data supplies[TC9563_PWRCTL_MAX_SUPPLY];
struct tc9563_pwrctrl_cfg cfg[TC9563_MAX];
+ struct tc9563_pwrctrl_cfg ep_cfg;
struct gpio_desc *reset_gpio;
struct i2c_adapter *adapter;
struct i2c_client *client;
@@ -240,12 +240,18 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563,
if (!cfg->disable_port)
return 0;
- if (port == TC9563_DSP1) {
+ switch (port) {
+ case TC9563_DSP1:
seq = dsp1_pwroff_seq;
len = ARRAY_SIZE(dsp1_pwroff_seq);
- } else {
+ break;
+ case TC9563_DSP2:
seq = dsp2_pwroff_seq;
len = ARRAY_SIZE(dsp2_pwroff_seq);
+ break;
+ default:
+ /* Only external downstream ports DSP1/DSP2 can be powered off */
+ return 0;
}
ret = tc9563_pwrctrl_i2c_bulk_write(tc9563->client, seq, len);
@@ -256,11 +262,11 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563,
ARRAY_SIZE(common_pwroff_seq));
}
-static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
- enum tc9563_pwrctrl_ports port,
- bool is_l1, u32 ns)
+static int tc9563_pwrctrl_set_port_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
+ enum tc9563_pwrctrl_ports port,
+ bool is_l1, u32 ns)
{
- u32 rd_val, units;
+ u32 units;
int ret;
if (ns < TC9563_L0S_L1_DELAY_UNIT_NS)
@@ -269,25 +275,6 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
/* convert to units of 256ns */
units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
- if (port == TC9563_ETHERNET) {
- ret = tc9563_pwrctrl_i2c_read(tc9563->client,
- TC9563_EMBEDDED_ETH_DELAY,
- &rd_val);
- if (ret)
- return ret;
-
- if (is_l1)
- rd_val = u32_replace_bits(rd_val, units,
- TC9563_ETH_L1_DELAY_MASK);
- else
- rd_val = u32_replace_bits(rd_val, units,
- TC9563_ETH_L0S_DELAY_MASK);
-
- return tc9563_pwrctrl_i2c_write(tc9563->client,
- TC9563_EMBEDDED_ETH_DELAY,
- rd_val);
- }
-
ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
BIT(port));
if (ret)
@@ -298,9 +285,38 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
units);
}
+static int tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
+ bool is_l1, u32 ns)
+{
+ u32 rd_val, units;
+ int ret;
+
+ if (ns < TC9563_L0S_L1_DELAY_UNIT_NS)
+ return 0;
+
+ /* convert to units of 256ns */
+ units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
+
+ ret = tc9563_pwrctrl_i2c_read(tc9563->client, TC9563_EMBEDDED_ETH_DELAY,
+ &rd_val);
+ if (ret)
+ return ret;
+
+ if (is_l1)
+ rd_val = u32_replace_bits(rd_val, units,
+ TC9563_ETH_L1_DELAY_MASK);
+ else
+ rd_val = u32_replace_bits(rd_val, units,
+ TC9563_ETH_L0S_DELAY_MASK);
+
+ return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_EMBEDDED_ETH_DELAY,
+ rd_val);
+}
+
static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
+ struct device *dev = tc9563->pwrctrl.dev;
u32 amp = tc9563->cfg[port].tx_amp;
int port_access;
@@ -320,6 +336,9 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563,
case TC9563_DSP2:
port_access = 0x8;
break;
+ case TC9563_VDSP:
+ dev_dbg(dev, "Tx amplitude tuning not supported for VDSP\n");
+ return 0;
default:
return -EINVAL;
}
@@ -338,6 +357,7 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
+ struct device *dev = tc9563->pwrctrl.dev;
int port_access, lane_access = 0x3;
u32 phy_rate = 0x21;
@@ -356,6 +376,9 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
port_access = 0x8;
lane_access = 0x1;
break;
+ case TC9563_VDSP:
+ dev_dbg(dev, "DFE tuning not supported for VDSP\n");
+ return 0;
default:
return -EINVAL;
}
@@ -386,11 +409,17 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563,
{TC9563_NFTS_2_5_GT, nfts[0]},
{TC9563_NFTS_5_GT, nfts[1]},
};
+ struct device *dev = tc9563->pwrctrl.dev;
int ret;
if (!nfts[0])
return 0;
+ if (port == TC9563_VDSP) {
+ dev_dbg(dev, "N_FTS tuning not supported for VDSP\n");
+ return 0;
+ }
+
ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
BIT(port));
if (ret)
@@ -415,11 +444,9 @@ static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl *tc9563,
return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val);
}
-static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl *tc9563,
- struct device_node *node,
- enum tc9563_pwrctrl_ports port)
+static int tc9563_pwrctrl_parse_device_dt(struct device_node *node,
+ struct tc9563_pwrctrl_cfg *cfg)
{
- struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
int ret;
/* Disable port if the status of the port is disabled. */
@@ -490,13 +517,13 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
goto power_off;
}
- ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, false, cfg->l0s_delay);
+ ret = tc9563_pwrctrl_set_port_l0s_l1_entry_delay(tc9563, i, false, cfg->l0s_delay);
if (ret) {
dev_err(dev, "Setting L0s entry delay failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, true, cfg->l1_delay);
+ ret = tc9563_pwrctrl_set_port_l0s_l1_entry_delay(tc9563, i, true, cfg->l1_delay);
if (ret) {
dev_err(dev, "Setting L1 entry delay failed\n");
goto power_off;
@@ -521,6 +548,21 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
}
}
+ /* Configure the integrated Ethernet MAC endpoint */
+ ret = tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(tc9563, false,
+ tc9563->ep_cfg.l0s_delay);
+ if (ret) {
+ dev_err(dev, "Setting Ethernet L0s entry delay failed\n");
+ goto power_off;
+ }
+
+ ret = tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(tc9563, true,
+ tc9563->ep_cfg.l1_delay);
+ if (ret) {
+ dev_err(dev, "Setting Ethernet L1 entry delay failed\n");
+ goto power_off;
+ }
+
ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, true);
if (!ret)
return 0;
@@ -548,7 +590,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
return dev_err_probe(dev, ret, "Failed to read i2c-parent property\n");
i2c_node = of_parse_phandle(dev->of_node, "i2c-parent", 0);
- tc9563->adapter = of_find_i2c_adapter_by_node(i2c_node);
+ tc9563->adapter = of_get_i2c_adapter_by_node(i2c_node);
of_node_put(i2c_node);
if (!tc9563->adapter)
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find I2C adapter\n");
@@ -556,7 +598,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
tc9563->client = i2c_new_dummy_device(tc9563->adapter, addr);
if (IS_ERR(tc9563->client)) {
dev_err(dev, "Failed to create I2C client\n");
- put_device(&tc9563->adapter->dev);
+ i2c_put_adapter(tc9563->adapter);
return PTR_ERR(tc9563->client);
}
@@ -578,8 +620,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
pci_pwrctrl_init(&tc9563->pwrctrl, dev);
- port = TC9563_USP;
- ret = tc9563_pwrctrl_parse_device_dt(tc9563, node, port);
+ ret = tc9563_pwrctrl_parse_device_dt(node, &tc9563->cfg[TC9563_USP]);
if (ret) {
dev_err(dev, "failed to parse device tree properties: %d\n", ret);
goto remove_i2c;
@@ -590,17 +631,26 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
* The first node represents DSP1, the second node represents DSP2,
* and so on.
*/
+ port = TC9563_USP;
for_each_child_of_node_scoped(node, child) {
- port++;
- ret = tc9563_pwrctrl_parse_device_dt(tc9563, child, port);
+ if (++port >= TC9563_MAX)
+ break;
+
+ ret = tc9563_pwrctrl_parse_device_dt(child, &tc9563->cfg[port]);
if (ret)
break;
- /* Embedded ethernet device are under DSP3 */
- if (port == TC9563_DSP3) {
- for_each_child_of_node_scoped(child, child1) {
- port++;
- ret = tc9563_pwrctrl_parse_device_dt(tc9563,
- child1, port);
+
+ /*
+ * The integrated Ethernet MAC Endpoint under VDSP is a single
+ * device whose functions share the same config registers.
+ */
+ if (port == TC9563_VDSP) {
+ struct device_node *eth __free(device_node) =
+ of_get_next_available_child(child, NULL);
+
+ if (eth) {
+ ret = tc9563_pwrctrl_parse_device_dt(eth,
+ &tc9563->ep_cfg);
if (ret)
break;
}
@@ -624,7 +674,7 @@ power_off:
tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
remove_i2c:
i2c_unregister_device(tc9563->client);
- put_device(&tc9563->adapter->dev);
+ i2c_put_adapter(tc9563->adapter);
return ret;
}
@@ -636,7 +686,7 @@ static void tc9563_pwrctrl_remove(struct platform_device *pdev)
tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
i2c_unregister_device(tc9563->client);
- put_device(&tc9563->adapter->dev);
+ i2c_put_adapter(tc9563->adapter);
}
static const struct of_device_id tc9563_pwrctrl_of_match[] = {
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index b09f27f7846f..de9bbccda21f 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2507,6 +2507,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f1, quirk_disable_aspm_l0s);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s);
+/* Realtek RTS525A generates a Replay Timer Timeout storm when L0s is enabled. */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x525a, quirk_disable_aspm_l0s);
+
static void quirk_disable_aspm_l0s_l1(struct pci_dev *dev)
{
pcie_aspm_remove_cap(dev,
@@ -5361,7 +5364,7 @@ static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev)
if (!(mpc & INTEL_MPC_REG_IRBNCE)) {
pci_info(dev, "Enabling MPC IRBNCE\n");
mpc |= INTEL_MPC_REG_IRBNCE;
- pci_write_config_word(dev, INTEL_MPC_REG, mpc);
+ pci_write_config_dword(dev, INTEL_MPC_REG, mpc);
}
}
@@ -6090,6 +6093,7 @@ SWITCHTEC_PCI100X_QUIRK(0x1003); /* PCI1003XG4 */
SWITCHTEC_PCI100X_QUIRK(0x1004); /* PCI1004XG4 */
SWITCHTEC_PCI100X_QUIRK(0x1005); /* PCI1005XG4 */
SWITCHTEC_PCI100X_QUIRK(0x1006); /* PCI1006XG4 */
+SWITCHTEC_PCI100X_QUIRK(0x1008); /* PCI1008XG4 */
/*
@@ -6263,6 +6267,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb404,
pci_fixup_pericom_acs_store_forward);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404,
pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2608,
+ pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2608,
+ pci_fixup_pericom_acs_store_forward);
static void nvidia_ion_ahci_fixup(struct pci_dev *pdev)
{
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index d8bffa21498a..e711ac1d4e38 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -34,6 +34,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
if (pci_dev_test_and_set_removed(dev))
return;
+ platform_pci_remove_wake(dev);
pci_doe_sysfs_teardown(dev);
pci_npem_remove(dev);
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index e3d3177fce54..34f8de551d58 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -245,8 +245,10 @@ static int match_pci_dev_by_id(struct device *dev, const void *data)
{
struct pci_dev *pdev = to_pci_dev(dev);
const struct pci_device_id *id = data;
+ struct pci_device_id dev_id;
- if (pci_match_one_device(id, pdev))
+ dev_id = pci_id_from_device(pdev);
+ if (pci_match_one_id(id, &dev_id))
return 1;
return 0;
}
@@ -416,9 +418,9 @@ EXPORT_SYMBOL(pci_get_class);
* @class: search for a PCI device with this base class code
* @from: Previous PCI device found in search, or %NULL for new search.
*
- * Iterates through the list of known PCI devices. If a PCI device is found
+ * Iterate through the list of known PCI devices. If a PCI device is found
* with a matching base class code, the reference count to the device is
- * incremented. See pci_match_one_device() to figure out how does this works.
+ * incremented. See pci_match_one_id() to figure out how this works.
* A new search is initiated by passing %NULL as the @from argument.
* Otherwise if @from is not %NULL, searches continue from next device on the
* global list. The reference count for @from is always decremented if it is
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index c0a949f2c995..e8c94aa1d3c1 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -1021,7 +1021,7 @@ static resource_size_t calculate_memsize(resource_size_t size,
resource_size_t children_add_size,
resource_size_t align)
{
- size = max(size, min_size) + children_add_size;
+ size = max(size + children_add_size, min_size);
return ALIGN(size, align);
}
diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c
index 41fc4b512708..5711aaa5df11 100644
--- a/drivers/pci/switch/switchtec.c
+++ b/drivers/pci/switch/switchtec.c
@@ -1874,6 +1874,7 @@ static const struct pci_device_id switchtec_pci_tbl[] = {
SWITCHTEC_PCI100X_DEVICE(0x1004, SWITCHTEC_GEN4), /* PCI1004 16XG4 */
SWITCHTEC_PCI100X_DEVICE(0x1005, SWITCHTEC_GEN4), /* PCI1005 16XG4 */
SWITCHTEC_PCI100X_DEVICE(0x1006, SWITCHTEC_GEN4), /* PCI1006 16XG4 */
+ SWITCHTEC_PCI100X_DEVICE(0x1008, SWITCHTEC_GEN4), /* PCI1008 16XG4 */
{0}
};
MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl);
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
index e893d5677241..9c9281222a0a 100644
--- a/drivers/scsi/nsp32.c
+++ b/drivers/scsi/nsp32.c
@@ -1470,7 +1470,7 @@ static int nsp32_show_info(struct seq_file *m, struct Scsi_Host *host)
(nsp32_read2(base, INDEX_REG) >> 8) & 0xff);
mode_reg = nsp32_index_read1(base, CHIP_MODE);
- model = data->pci_devid->driver_data;
+ model = data->model;
#ifdef CONFIG_PM
seq_printf(m, "Power Management: %s\n",
@@ -2907,8 +2907,8 @@ static int nsp32_eh_host_reset(struct scsi_cmnd *SCpnt)
*/
static int nsp32_getprom_param(nsp32_hw_data *data)
{
- int vendor = data->pci_devid->vendor;
- int device = data->pci_devid->device;
+ int vendor = data->Pci->vendor;
+ int device = data->Pci->device;
int ret, i;
int __maybe_unused val;
@@ -3340,7 +3340,7 @@ static int nsp32_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
data->Pci = pdev;
- data->pci_devid = id;
+ data->model = id->driver_data;
data->IrqNumber = pdev->irq;
data->BaseAddress = pci_resource_start(pdev, 0);
data->NumAddress = pci_resource_len (pdev, 0);
diff --git a/drivers/scsi/nsp32.h b/drivers/scsi/nsp32.h
index 924889f8bd37..9e65771cb592 100644
--- a/drivers/scsi/nsp32.h
+++ b/drivers/scsi/nsp32.h
@@ -564,10 +564,10 @@ typedef struct _nsp32_hw_data {
struct scsi_cmnd *CurrentSC;
- struct pci_dev *Pci;
- const struct pci_device_id *pci_devid;
- struct Scsi_Host *Host;
- spinlock_t Lock;
+ struct pci_dev *Pci;
+ int model;
+ struct Scsi_Host *Host;
+ spinlock_t Lock;
char info_str[100];
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index 29658c0ee71f..649fe8eafcfa 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -30,12 +30,18 @@ static inline void of_pci_check_probe_only(void) { }
#if IS_ENABLED(CONFIG_OF_IRQ)
int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
+void pci_configure_of_wake_gpio(struct pci_dev *dev);
+void pci_remove_of_wake_gpio(struct pci_dev *dev);
#else
static inline int
of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
{
return 0;
}
+
+static inline void pci_configure_of_wake_gpio(struct pci_dev *dev) { }
+
+static inline void pci_remove_of_wake_gpio(struct pci_dev *dev) { }
#endif
#endif
diff --git a/include/linux/pci-ecam.h b/include/linux/pci-ecam.h
index d930651473b4..044f67ced6ff 100644
--- a/include/linux/pci-ecam.h
+++ b/include/linux/pci-ecam.h
@@ -81,6 +81,9 @@ void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
/* default ECAM ops */
extern const struct pci_ecam_ops pci_generic_ecam_ops;
+/* default CAM ops */
+extern const struct pci_ecam_ops pci_generic_cam_ops;
+
#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
extern const struct pci_ecam_ops pci_32b_ops; /* 32-bit accesses only */
extern const struct pci_ecam_ops pci_32b_read_ops; /* 32-bit read only */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index cc64cd616522..d31a8d107b1e 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -591,6 +591,8 @@ struct pci_dev {
/* These methods index pci_reset_fn_methods[] */
u8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */
+ struct gpio_desc *wake; /* WAKE# GPIO */
+
#ifdef CONFIG_PCIE_TPH
u16 tph_cap; /* TPH capability offset */
u8 tph_mode; /* TPH mode */
@@ -650,6 +652,7 @@ struct pci_host_bridge {
void (*release_fn)(struct pci_host_bridge *);
int (*enable_device)(struct pci_host_bridge *bridge, struct pci_dev *dev);
void (*disable_device)(struct pci_host_bridge *bridge, struct pci_dev *dev);
+ int (*reset_root_port)(struct pci_host_bridge *bridge, struct pci_dev *dev);
void *release_data;
unsigned int ignore_reset_delay:1; /* For entire hierarchy */
unsigned int no_ext_tags:1; /* No Extended Tags */
@@ -983,6 +986,7 @@ struct module;
* function returns zero when the driver chooses to
* take "ownership" of the device or an error code
* (negative number) otherwise.
+ * The pci_device_id parameter is only valid during probe.
* The probe function always gets called from process
* context, so it can sleep.
* @remove: The remove() function gets called whenever a device
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 2f72956c864d..63774ae2bf71 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2898,6 +2898,7 @@
#define PCI_DEVICE_ID_INTEL_HDA_ICH8 0x284b
#define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850
#define PCI_DEVICE_ID_INTEL_VMD_28C0 0x28c0
+#define PCI_DEVICE_ID_INTEL_VMD_28C1 0x28c1
#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910
#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912
#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913
diff --git a/include/linux/pcie-dwc.h b/include/linux/pcie-dwc.h
index d3cd701a58e3..097e806c714c 100644
--- a/include/linux/pcie-dwc.h
+++ b/include/linux/pcie-dwc.h
@@ -11,6 +11,8 @@
#include <linux/pci_ids.h>
+#define PCI_VENDOR_ID_LECARC 0x0720
+
struct dwc_pcie_vsec_id {
u16 vendor_id;
u16 vsec_id;
@@ -34,6 +36,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
.vsec_id = 0x02, .vsec_rev = 0x4 },
{ .vendor_id = PCI_VENDOR_ID_SAMSUNG,
.vsec_id = 0x02, .vsec_rev = 0x4 },
+ { .vendor_id = PCI_VENDOR_ID_LECARC,
+ .vsec_id = 0x02, .vsec_rev = 0x4 },
{}
};