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
  ...
This commit is contained in:
Linus Torvalds
2026-08-23 12:44:10 -07:00
110 changed files with 4242 additions and 1054 deletions
@@ -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.
+1 -1
View File
@@ -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
+1
View File
@@ -7,4 +7,5 @@ PCI Native Host Bridge and Endpoint Drivers
.. toctree::
:maxdepth: 2
pci-controller-drivers
rcar-pcie-firmware
@@ -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/
@@ -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 |
+----------------+-----------------+-----------------+-----------------------------+
@@ -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 {
@@ -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:
@@ -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:
@@ -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;
};
};
};
@@ -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>;
};
};
};
@@ -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
@@ -66,6 +66,9 @@ properties:
max-link-speed:
maximum: 4
msi-parent:
maxItems: 1
num-lanes:
maximum: 4
@@ -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>,
@@ -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
@@ -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>;
@@ -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>;
@@ -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>;
};
@@ -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>;
+4 -2
View File
@@ -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
+16 -16
View File
@@ -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;
+9
View File
@@ -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
+2 -7
View File
@@ -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)
+3 -3
View File
@@ -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[] = {
+4 -22
View File
@@ -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",
},
{ }
};
+72 -236
View File
@@ -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"),
{ }
};
+1
View File
@@ -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);
-1
View File
@@ -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);
-1
View File
@@ -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;
+17 -7
View File
@@ -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
{
}
};
+31 -7
View File
@@ -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);
+4 -7
View File
@@ -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,
+10 -2
View File
@@ -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)"
+1
View File
@@ -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
+11 -2
View File
@@ -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
+1
View File
@@ -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
+2 -2
View File
@@ -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 = {
@@ -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);
}
@@ -0,0 +1,263 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Cadence PCIe controller debugfs driver
*
* Copyright (C) 2026 Hans Zhang <18255117159@163.com>
*/
#include <linux/bitfield.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "pcie-cadence.h"
#define CDNS_DEBUGFS_BUF_MAX 128
static const char *cdns_pcie_lga_ltssm_status_string(enum cdns_pcie_lga_ltssm ltssm)
{
const char *str;
switch (ltssm) {
#define CDNS_PCIE_LGA_LTSSM_NAME(n) case n: str = #n; break
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_DETECT_QUIET);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_DETECT_ACTIVE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_POLLING_ACTIVE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_POLLING_COMPLIANCE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_POLLING_CONFIGURATION);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LINKWIDTH_START);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LINKWIDTH_ACCEPT);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LANENUM_ACCEPT);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_LANENUM_WAIT);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_COMPLETE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_CONFIGURATION_IDLE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_RCVRLOCK);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_SPEED);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_RCVRCFG);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_IDLE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L0);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RX_L0S_ENTRY);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RX_L0S_IDLE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RX_L0S_FTS);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_TX_L0S_ENTRY);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_TX_L0S_IDLE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_TX_L0S_FTS);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L1_ENTRY);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L1_IDLE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L2_IDLE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_L2_TRANSMITWAKE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_DISABLED);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_ENTRY_MASTER);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_ACTIVE_MASTER);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_EXIT_MASTER);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_ENTRY_SLAVE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_ACTIVE_SLAVE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_LOOPBACK_EXIT_SLAVE);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_HOT_RESET);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_0);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_1);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_2);
CDNS_PCIE_LGA_LTSSM_NAME(CDNS_PCIE_LGA_LTSSM_RECOVERY_EQUALIZATION_PHASE_3);
default:
str = "CDNS_PCIE_LGA_LTSSM_UNKNOWN";
break;
}
return str + strlen("CDNS_PCIE_LGA_LTSSM_");
}
static const char *cdns_pcie_hpa_ltssm_status_string(enum cdns_pcie_hpa_ltssm ltssm)
{
const char *str;
switch (ltssm) {
#define CDNS_PCIE_HPA_LTSSM_NAME(n) case n: str = #n; break
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_QUIET);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_QUIET_ENTRY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DETECT_ACTIVE_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RCVR_DETECTED_ST);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RCVR_DETECTED_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_ACTIVE_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_CONFIG_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_RC_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_ACC_RC);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_RC);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_RC_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_RC);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_START_EP_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LW_ACC_EP);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_EP);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_WAIT_EP_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_EP);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_LANENUM_ACC_EP_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_COMPLETE_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_IDLE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_CONFIG_IDLE_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DUMMY_STATE_4);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0_STATE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_LOCK);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_LOCK_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_CFG);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_RCVR_CFG_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_IDLE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_IDLE_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_4);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_5);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_6);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_DISABLE_LINK_7);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_HOT_RESET);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_HOT_RESET_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_HOT_RESET_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_HOT_RESET_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_ENTRY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_4);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L0S_5);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_WAIT_FOR_LINK_TX);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_FTS_ENTRY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_FTS_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_FTS_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_ST);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_TX_ELEC_IDLE_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_SPEED_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_4);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_5);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_6);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_7);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_POLLING_COMPLIANCE_GEN23_8);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ENTRY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ENTRY_FROM_RECOVERY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_EXIT_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_EXIT);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_4);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_GEN2_5);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_SLAVE_ACTIVE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_ENTRY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_4);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_IDLE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L1_EXIT);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_ENTRY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_4);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_5);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_L2_IDLE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_3);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_4);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_5);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ENTRY_FROM_RECOVERY);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_ACTIVE);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_LOOPBACK_MASTER_EXIT_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE0);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE2_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE2_2);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE3_1);
CDNS_PCIE_HPA_LTSSM_NAME(CDNS_PCIE_HPA_LTSSM_RECOVERY_EQUALIZATION_PHASE3_2);
default:
str = "CDNS_PCIE_HPA_LTSSM_UNKNOWN";
break;
}
return str + strlen("CDNS_PCIE_HPA_LTSSM_");
}
static int ltssm_status_show(struct seq_file *s, void *v)
{
struct cdns_pcie *pci = s->private;
enum cdns_pcie_lga_ltssm lga_ltssm;
enum cdns_pcie_hpa_ltssm hpa_ltssm;
const char *str_ltssm;
u32 val;
if (pci->is_hpa) {
val = cdns_pcie_hpa_readl(pci, REG_BANK_IP_REG,
CDNS_PCIE_HPA_PHY_DBG_STS_REG0);
hpa_ltssm = FIELD_GET(CDNS_PCIE_HPA_LTSSM_STATUS_MASK, val);
str_ltssm = cdns_pcie_hpa_ltssm_status_string(hpa_ltssm);
} else {
val = cdns_pcie_readl(pci, CDNS_PCIE_LM_BASE);
lga_ltssm = FIELD_GET(CDNS_PCIE_LGA_LTSSM_STATUS_MASK, val);
str_ltssm = cdns_pcie_lga_ltssm_status_string(lga_ltssm);
}
seq_printf(s, "%s (0x%02x)\n", str_ltssm,
pci->is_hpa ? hpa_ltssm : lga_ltssm);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(ltssm_status);
static void cdns_pcie_ltssm_debugfs_init(struct cdns_pcie *pci, struct dentry *dir)
{
debugfs_create_file("ltssm_status", 0444, dir, pci,
&ltssm_status_fops);
}
void cdns_pcie_debugfs_deinit(struct cdns_pcie *pci)
{
if (!pci->debug_dir)
return;
debugfs_remove_recursive(pci->debug_dir);
}
EXPORT_SYMBOL_GPL(cdns_pcie_debugfs_deinit);
void cdns_pcie_debugfs_init(struct cdns_pcie *pci)
{
char dirname[CDNS_DEBUGFS_BUF_MAX];
struct device *dev = pci->dev;
/* Create main directory for each platform driver. */
snprintf(dirname, CDNS_DEBUGFS_BUF_MAX, "cdns_pcie_%s", dev_name(dev));
pci->debug_dir = debugfs_create_dir(dirname, NULL);
cdns_pcie_ltssm_debugfs_init(pci, pci->debug_dir);
}
EXPORT_SYMBOL_GPL(cdns_pcie_debugfs_init);
@@ -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:
@@ -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;
@@ -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);
@@ -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);
@@ -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
@@ -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 = {
@@ -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 */
+5 -2
View File
@@ -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.
+3 -1
View File
@@ -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)
+47 -25
View File
@@ -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,
+8 -2
View File
@@ -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);
}
@@ -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,
+2 -2
View File
@@ -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);
+3 -1
View File
@@ -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,
@@ -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);
@@ -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().
+9 -19
View File
@@ -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);
}
+3 -2
View File
@@ -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;
};
+134 -4
View File
@@ -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,
};
+3 -1
View File
@@ -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,
+188 -6
View File
@@ -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 },
+117 -7
View File
@@ -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)
@@ -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,
+8 -8
View File
@@ -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);
}
+79 -8
View File
@@ -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;
return dw_pcie_suspend_noirq(pci);
/*
* 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 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");
@@ -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");
@@ -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)
+3 -6
View File
@@ -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;
}
+40 -24
View File
@@ -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");
+1
View File
@@ -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);
+1 -10
View File
@@ -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 },
+1 -2
View File
@@ -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);
}
+1 -4
View File
@@ -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;
+4 -4
View File
@@ -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");
+154
View File
@@ -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 },
+2 -2
View File
@@ -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);
}
+216 -17
View File
@@ -180,6 +180,16 @@
/* Timeouts experimentally determined */
#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
@@ -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,
},
{}
};
+467
View File
@@ -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");
+2 -7
View File
@@ -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");
+42 -10
View File
@@ -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);
+20 -12
View File
@@ -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);
}
+168 -35
View File
@@ -37,6 +37,12 @@
#define MB2_SHADOW_OFFSET 0x2000
#define MB2_SHADOW_SIZE 16
/* DMR BAR4 register offsets */
#define SHADOW_MEMBAR1_28C1 0x2818 /* MEMBAR1 physical address */
#define SHADOW_MEMBAR2_28C1 0x2820 /* MEMBAR2 physical address */
#define BASE_ID_REG_28C1 0x2840
#define MEMBAR2_OFFSET_28C1 0x30d0
enum vmd_features {
/*
* Device may contain registers which hint the physical location of the
@@ -77,6 +83,15 @@ enum vmd_features {
* proper power management of the SoC.
*/
VMD_FEAT_BIOS_PM_QUIRK = (1 << 5),
/*
* Newer VMD with device ID 0x28C1 has unique settings compared to its
* predecessor where BIOS enumerates the entire VMD device tree and
* stores respective configurations including bus start range and
* shadow registers in VMD MMIO space in VMD BAR4/BAR5, otherwise
* referred to as MEMBAR2 or MSI-X BAR.
*/
VMD_FEAT_USE_BIOS_INFO = (1 << 6),
};
#define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */
@@ -142,6 +157,7 @@ struct vmd_dev {
u8 first_vec;
char *name;
int instance;
unsigned long features;
};
static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
@@ -366,6 +382,9 @@ static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool enable)
{
u16 reg;
if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO))
return;
pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, &reg);
reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) :
(reg | VMCONFIG_MSI_REMAP);
@@ -389,11 +408,22 @@ static void vmd_remove_irq_domain(struct vmd_dev *vmd)
}
}
static unsigned int vmd_bus_to_ecam(struct vmd_dev *vmd, unsigned int busnr)
{
if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO))
return busnr;
return busnr - vmd->busn_start;
}
static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
unsigned int devfn, int reg, int len)
{
unsigned int busnr_ecam = bus->number - vmd->busn_start;
u32 offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg);
unsigned int busnr_ecam;
u32 offset;
busnr_ecam = vmd_bus_to_ecam(vmd, bus->number);
offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg);
if (offset + len >= resource_size(&vmd->dev->resource[VMD_CFGBAR]))
return NULL;
@@ -518,22 +548,37 @@ static inline void vmd_acpi_begin(void) { }
static inline void vmd_acpi_end(void) { }
#endif /* CONFIG_ACPI */
static resource_size_t vmd_cfgbar_ecam_space(struct vmd_dev *vmd)
{
resource_size_t cfgbar_buses;
unsigned int ecam_start;
cfgbar_buses = resource_size(&vmd->dev->resource[VMD_CFGBAR]) >> 20;
ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[0].start);
if (ecam_start >= cfgbar_buses)
return 0;
return cfgbar_buses - ecam_start;
}
static void vmd_domain_reset(struct vmd_dev *vmd)
{
u16 bus, max_buses = resource_size(&vmd->resources[0]);
u8 dev, functions, fn, hdr_type;
unsigned int ecam_bus;
char __iomem *base;
max_buses = min_t(u16, max_buses, vmd_cfgbar_ecam_space(vmd));
for (bus = 0; bus < max_buses; bus++) {
ecam_bus = vmd_bus_to_ecam(vmd, vmd->resources[0].start + bus);
for (dev = 0; dev < 32; dev++) {
base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus,
base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus,
PCI_DEVFN(dev, 0), 0);
hdr_type = readb(base + PCI_HEADER_TYPE);
functions = (hdr_type & PCI_HEADER_TYPE_MFD) ? 8 : 1;
for (fn = 0; fn < functions; fn++) {
base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus,
base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus,
PCI_DEVFN(dev, fn), 0);
hdr_type = readb(base + PCI_HEADER_TYPE) &
@@ -640,6 +685,8 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
pci_read_config_word(dev, PCI_REG_VMCAP, &reg);
if (BUS_RESTRICT_CAP(reg)) {
pci_read_config_word(dev, PCI_REG_VMCONFIG, &reg);
if (PCI_POSSIBLE_ERROR(reg))
return -ENODEV;
switch (BUS_RESTRICT_CFG(reg)) {
case 0:
@@ -648,6 +695,7 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
case 1:
vmd->busn_start = 128;
break;
case 3:
case 2:
vmd->busn_start = 224;
break;
@@ -661,6 +709,46 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
return 0;
}
static int vmd_get_bus_info_from_bar4(struct vmd_dev *vmd,
resource_size_t *offset1,
resource_size_t *offset2)
{
u64 phys1, phys2, bar4_2840;
void __iomem *bar4;
u32 base_id;
u8 base_bus;
bar4 = pci_ioremap_bar(vmd->dev, 4);
if (!bar4)
return -ENOMEM;
/* Read shadow registers for MEMBAR1 and MEMBAR2 physical addresses */
phys1 = readq(bar4 + SHADOW_MEMBAR1_28C1);
phys2 = readq(bar4 + SHADOW_MEMBAR2_28C1);
/*
* Read and set bus start number from Base ID register. 24-bit Base ID
* register is part of 64-bit shadowed reqid hide range register and
* holds segment, bus, device and function.
*/
bar4_2840 = readq(bar4 + BASE_ID_REG_28C1);
base_id = bar4_2840 & 0xFFFFFF;
base_bus = base_id >> 8;
vmd->busn_start = base_bus;
/* Calculate offsets like vmd_get_phys_offsets() does */
if (phys1)
*offset1 = vmd->dev->resource[VMD_MEMBAR1].start -
(phys1 & PCI_BASE_ADDRESS_MEM_MASK);
if (phys2)
*offset2 = vmd->dev->resource[VMD_MEMBAR2].start -
(phys2 & PCI_BASE_ADDRESS_MEM_MASK);
pci_iounmap(vmd->dev, bar4);
return 0;
}
static irqreturn_t vmd_irq(int irq, void *data)
{
struct vmd_irq_list *irqs = data;
@@ -711,6 +799,52 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd)
return 0;
}
static int vmd_prepare_offsets_and_bus(struct vmd_dev *vmd,
unsigned long features,
resource_size_t *membar2_offset,
resource_size_t *offset1,
resource_size_t *offset2)
{
int ret;
/*
* Shadow registers may exist in certain VMD device IDs which allow
* guests to correctly assign host physical addresses to the root ports
* and child devices. These registers will either return the host value
* or 0, depending on an enable bit in the VMD device.
*
* For certain VMD devices (i.e. 0x28C1), BIOS places device info
* in BAR4 shadow registers to determine the base bus number and memory
* offsets.
*/
if (features & VMD_FEAT_USE_BIOS_INFO) {
*membar2_offset = MEMBAR2_OFFSET_28C1;
ret = vmd_get_bus_info_from_bar4(vmd, offset1, offset2);
if (ret)
return ret;
} else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) {
*membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE;
ret = vmd_get_phys_offsets(vmd, true, offset1, offset2);
if (ret)
return ret;
} else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) {
ret = vmd_get_phys_offsets(vmd, false, offset1, offset2);
if (ret)
return ret;
}
/*
* Certain VMD devices may have a root port configuration option which
* limits the bus range to between 0-127, 128-255, or 224-255.
*/
if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) {
ret = vmd_get_bus_number_start(vmd);
if (ret)
return ret;
}
return 0;
}
/*
* Since VMD is an aperture to regular PCIe root ports, only allow it to
* control features that the OS is allowed to control on the physical PCI bus.
@@ -780,42 +914,25 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
LIST_HEAD(resources);
resource_size_t offset[2] = {0};
resource_size_t membar2_offset = 0x2000;
resource_size_t busn_end;
struct pci_bus *child;
struct pci_dev *dev;
bool vmd_in_guest;
int ret;
/*
* Shadow registers may exist in certain VMD device ids which allow
* guests to correctly assign host physical addresses to the root ports
* and child devices. These registers will either return the host value
* or 0, depending on an enable bit in the VMD device.
*/
if (features & VMD_FEAT_HAS_MEMBAR_SHADOW) {
membar2_offset = MB2_SHADOW_OFFSET + MB2_SHADOW_SIZE;
ret = vmd_get_phys_offsets(vmd, true, &offset[0], &offset[1]);
if (ret)
return ret;
} else if (features & VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP) {
ret = vmd_get_phys_offsets(vmd, false, &offset[0], &offset[1]);
if (ret)
return ret;
}
/*
* Certain VMD devices may have a root port configuration option which
* limits the bus range to between 0-127, 128-255, or 224-255
*/
if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) {
ret = vmd_get_bus_number_start(vmd);
if (ret)
return ret;
}
ret = vmd_prepare_offsets_and_bus(vmd, features, &membar2_offset,
&offset[0], &offset[1]);
if (ret)
return ret;
/* Do not let resource[0] end go out of bounds */
res = &vmd->dev->resource[VMD_CFGBAR];
busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1;
busn_end = min_t(resource_size_t, busn_end, 0xff);
vmd->resources[0] = (struct resource) {
.name = "VMD CFGBAR",
.start = vmd->busn_start,
.end = vmd->busn_start + (resource_size(res) >> 20) - 1,
.end = busn_end,
.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
};
@@ -862,14 +979,16 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
.parent = res,
};
/* Non-zero offset means guest/direct assign view. */
vmd_in_guest = offset[0] || offset[1];
/*
* Currently MSI remapping must be enabled in guest passthrough mode
* due to some missing interrupt remapping plumbing. This is probably
* acceptable because the guest is usually CPU-limited and MSI
* remapping doesn't become a performance bottleneck.
*/
if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) ||
offset[0] || offset[1]) {
if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || vmd_in_guest) {
ret = vmd_alloc_irqs(vmd);
if (ret)
return ret;
@@ -910,8 +1029,13 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
return -ENODEV;
}
vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
to_pci_host_bridge(vmd->bus->bridge));
/*
* Don't copy _OSC control flags from root bridge if running in a VM, as
* they don't reflect the physical root bridge capabilities.
*/
if (!vmd_in_guest)
vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
to_pci_host_bridge(vmd->bus->bridge));
vmd_attach_resources(vmd);
if (vmd->irq_domain)
@@ -998,6 +1122,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
vmd->dev = dev;
vmd->sysdata.domain = PCI_DOMAIN_NR_NOT_SET;
vmd->features = features;
vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL);
if (vmd->instance < 0)
return vmd->instance;
@@ -1114,6 +1239,10 @@ static const struct pci_device_id vmd_ids[] = {
.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
VMD_FEAT_HAS_BUS_RESTRICTIONS |
VMD_FEAT_CAN_BYPASS_MSI_REMAP,},
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_28C1),
.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
VMD_FEAT_CAN_BYPASS_MSI_REMAP |
VMD_FEAT_USE_BIOS_INFO,},
{PCI_VDEVICE(INTEL, 0x467f),
.driver_data = VMD_FEATS_CLIENT,},
{PCI_VDEVICE(INTEL, 0x4c3d),
@@ -1132,6 +1261,10 @@ static const struct pci_device_id vmd_ids[] = {
.driver_data = VMD_FEATS_CLIENT,},
{PCI_VDEVICE(INTEL, 0xb07f),
.driver_data = VMD_FEATS_CLIENT,},
{PCI_VDEVICE(INTEL, 0xd70b),
.driver_data = VMD_FEATS_CLIENT,},
{PCI_VDEVICE(INTEL, 0xd73b),
.driver_data = VMD_FEATS_CLIENT,},
{0,}
};
MODULE_DEVICE_TABLE(pci, vmd_ids);
+2 -2
View File
@@ -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;
}
+13
View File
@@ -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 = {
+1 -2
View File
@@ -258,8 +258,7 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
if (subevent == 0x80) {
pr_debug("%s: generating bus event\n", __func__);
acpi_bus_generate_netlink_event(note->device->pnp.device_class,
dev_name(&note->device->dev),
acpi_bus_generate_netlink_event("", dev_name(&note->device->dev),
note->event, detail);
} else
note->event = event;
+79
View File
@@ -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,
+5
View File
@@ -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},
{}
};
+2 -2
View File
@@ -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;
}
+108 -84
View File
@@ -29,6 +29,47 @@ struct pci_dynid {
struct pci_device_id id;
};
/**
* 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
@@ -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;
struct pci_device_id id = {
.vendor = vendor,
.device = device,
.subvendor = subvendor,
.subdevice = subdevice,
.class = class,
.class_mask = class_mask,
.driver_data = driver_data,
};
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);
return do_pci_add_dynid(drv, &id, false);
}
EXPORT_SYMBOL_GPL(pci_add_dynid);
@@ -90,6 +123,31 @@ static void pci_free_dynids(struct pci_driver *drv)
spin_unlock(&drv->dynids.lock);
}
/**
* 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
@@ -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;
+111 -66
View File
@@ -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,
};
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_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,
};
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,
};
+29 -4
View File
@@ -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)
{
+59 -11
View File
@@ -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 struct pci_device_id pci_id_from_device(const struct pci_dev *dev)
{
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_device(const struct pci_device_id *id, const struct pci_dev *dev)
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->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))
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;
};
+216 -59
View File
@@ -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;
/*
* For Advisory Non-Fatal Errors, record statistics and tracing
* even if ratelimited
*/
if (!ratelimit_print)
goto anfe;
layer = AER_GET_LAYER_ERROR(aer_severity, status);
agent = AER_GET_AGENT(aer_severity, status);
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;
+165 -110
View File
@@ -839,116 +839,6 @@ static void aspm_l1ss_init(struct pcie_link_state *link)
#define FLAG(x, y, d) (((x) & (PCIE_LINK_STATE_##y)) ? d : "")
static void pcie_aspm_override_default_link_state(struct pcie_link_state *link)
{
struct pci_dev *pdev = link->downstream;
u32 override;
/* For devicetree platforms, enable L0s and L1 by default */
if (of_have_populated_dt()) {
if (link->aspm_support & PCIE_LINK_STATE_L0S)
link->aspm_default |= PCIE_LINK_STATE_L0S;
if (link->aspm_support & PCIE_LINK_STATE_L1)
link->aspm_default |= PCIE_LINK_STATE_L1;
override = link->aspm_default & ~link->aspm_enabled;
if (override)
pci_info(pdev, "ASPM: default states%s%s\n",
FLAG(override, L0S, " L0s"),
FLAG(override, L1, " L1"));
}
}
static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
{
struct pci_dev *child = link->downstream, *parent = link->pdev;
u16 parent_lnkctl, child_lnkctl;
struct pci_bus *linkbus = parent->subordinate;
if (blacklist) {
/* Set enabled/disable so that we will disable ASPM later */
link->aspm_enabled = PCIE_LINK_STATE_ASPM_ALL;
link->aspm_disable = PCIE_LINK_STATE_ASPM_ALL;
return;
}
/*
* If ASPM not supported, don't mess with the clocks and link,
* bail out now.
*/
if (!(parent->aspm_l0s_support && child->aspm_l0s_support) &&
!(parent->aspm_l1_support && child->aspm_l1_support))
return;
/* Configure common clock before checking latencies */
pcie_aspm_configure_common_clock(link);
/*
* Re-read upstream/downstream components' register state after
* clock configuration. L0s & L1 exit latencies in the otherwise
* read-only Link Capabilities may change depending on common clock
* configuration (PCIe r5.0, sec 7.5.3.6).
*/
pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_lnkctl);
pcie_capability_read_word(child, PCI_EXP_LNKCTL, &child_lnkctl);
/* 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);
}
/*
* Setup L0s state
*
* Note that we must not enable L0s in either direction on a
* given link unless components on both sides of the link each
* support L0s.
*/
if (parent->aspm_l0s_support && child->aspm_l0s_support)
link->aspm_support |= PCIE_LINK_STATE_L0S;
if (child_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S)
link->aspm_enabled |= PCIE_LINK_STATE_L0S_UP;
if (parent_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S)
link->aspm_enabled |= PCIE_LINK_STATE_L0S_DW;
/* Setup L1 state */
if (parent->aspm_l1_support && child->aspm_l1_support)
link->aspm_support |= PCIE_LINK_STATE_L1;
if (parent_lnkctl & child_lnkctl & PCI_EXP_LNKCTL_ASPM_L1)
link->aspm_enabled |= PCIE_LINK_STATE_L1;
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);
/* Setup initial capable state. Will be updated later */
link->aspm_capable = link->aspm_support;
/* Get and check endpoint acceptable latencies */
list_for_each_entry(child, &linkbus->devices, bus_list) {
if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT &&
pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END)
continue;
pcie_aspm_check_latency(child);
}
}
/* Configure the ASPM L1 substates. Caller must disable L1 first. */
static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
{
@@ -985,6 +875,171 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
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;
u32 override;
/* 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)
link->aspm_default |= PCIE_LINK_STATE_L1;
override = link->aspm_default & ~link->aspm_enabled;
if (override)
pci_info(pdev, "ASPM: default states%s%s\n",
FLAG(override, L0S, " L0s"),
FLAG(override, L1, " L1"));
}
}
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;
if (blacklist) {
/* Set enabled/disable so that we will disable ASPM later */
link->aspm_enabled = PCIE_LINK_STATE_ASPM_ALL;
link->aspm_disable = PCIE_LINK_STATE_ASPM_ALL;
return;
}
/*
* If ASPM not supported, don't mess with the clocks and link,
* bail out now.
*/
if (!(parent->aspm_l0s_support && child->aspm_l0s_support) &&
!(parent->aspm_l1_support && child->aspm_l1_support))
return;
/* Configure common clock before checking latencies */
pcie_aspm_configure_common_clock(link);
/*
* Re-read upstream/downstream components' register state after
* clock configuration. L0s & L1 exit latencies in the otherwise
* read-only Link Capabilities may change depending on common clock
* configuration (PCIe r5.0, sec 7.5.3.6).
*/
pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &parent_lnkctl);
pcie_capability_read_word(child, PCI_EXP_LNKCTL, &child_lnkctl);
/* Disable L0s/L1 before updating L1SS config */
if (FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) ||
FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl)) {
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);
}
/*
* Setup L0s state
*
* Note that we must not enable L0s in either direction on a
* given link unless components on both sides of the link each
* support L0s.
*/
if (parent->aspm_l0s_support && child->aspm_l0s_support)
link->aspm_support |= PCIE_LINK_STATE_L0S;
if (child_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S)
link->aspm_enabled |= PCIE_LINK_STATE_L0S_UP;
if (parent_lnkctl & PCI_EXP_LNKCTL_ASPM_L0S)
link->aspm_enabled |= PCIE_LINK_STATE_L0S_DW;
/* Setup L1 state */
if (parent->aspm_l1_support && child->aspm_l1_support)
link->aspm_support |= PCIE_LINK_STATE_L1;
if (parent_lnkctl & child_lnkctl & PCI_EXP_LNKCTL_ASPM_L1)
link->aspm_enabled |= PCIE_LINK_STATE_L1;
aspm_l1ss_init(link);
/* 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;
/* Get and check endpoint acceptable latencies */
list_for_each_entry(child, &linkbus->devices, bus_list) {
if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT &&
pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END)
continue;
pcie_aspm_check_latency(child);
}
}
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
-22
View File
@@ -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);
+1 -5
View File
@@ -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);
+15 -15
View File
@@ -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;
}
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);
}
if (!nr_service)
goto error_cleanup_irqs;
return 0;
error_cleanup_irqs:
pci_free_irq_vectors(dev);
error_disable:
pci_disable_device(dev);
return status;
}
typedef int (*pcie_callback_t)(struct pcie_device *);
+2
View File
@@ -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.
+15 -1
View File
@@ -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)
+97 -47
View File
@@ -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[] = {
+9 -1
View File
@@ -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)
{
+1
View File
@@ -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);

Some files were not shown because too many files have changed in this diff Show More