diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-23 12:44:10 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-23 12:44:10 -0700 |
| commit | 570f7e331f5febb30f1384817463c7e42b65ca7d (patch) | |
| tree | 1b8b905be28128b36b991d69241768b99454772d | |
| parent | b6b019a1d9b90ac51174f0ca15b1338b61506bf9 (diff) | |
| parent | 9324becc398a1f9e67c91a3d652a7c56e5afc7f4 (diff) | |
| download | linux-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
...
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, + <ssm_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 = 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, ®); if (BUS_RESTRICT_CAP(reg)) { pci_read_config_word(dev, PCI_REG_VMCONFIG, ®); + 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(¬e->device->dev), + acpi_bus_generate_netlink_event("", dev_name(¬e->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 }, {} }; |
