Based on kernel version 7.3. Page generated on 2026-10-19 13:17 EDT.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 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/
|