Platform drivers, Device Tree, and ACPI

Compare platform discovery through Device Tree and ACPI descriptions.

16 min readChapter 58 of 168Intermediate → advanced
In this chapter

Before you start#

Read the driver model and sysfs. The baseline is upstream Linux v6.12, a historical teaching version. Firmware descriptions, address translation, interrupt controllers and device availability are platform- and configuration-specific. The lab inventories firmware interfaces without modifying them or printing machine identifiers.

Learning goals#

Explain platform-device resources and matching, decode a simple Device Tree address, distinguish OF population from driver probe, and compare ACPI enumeration and resource descriptions without assuming either firmware system exists everywhere.

Source walkthrough#

Platform drivers, Device Tree, and ACPI
Device Tree
OF nodes describe hardware and providers; population precedes matching/probe.
ACPI
Namespace IDs, resources, properties and methods follow ACPI enumeration.
Platform probe
Resources, IRQ and suppliers must be checked; failures can defer or unwind.
Conceptual model. Conditional Linux v6.12 source model; illustrative ownership sequence, not a captured hardware execution.

A platform_device represents a device on the platform bus; it embeds a generic device and can carry resources such as memory ranges and interrupts. A platform_driver supplies callbacks and optional match tables. A platform device can be created from firmware or another kernel path. The platform bus is a software abstraction, not a claim that a device lacks a physical bus or that every platform device came from Device Tree. A resource describes an address or interrupt assignment; a CPU virtual mapping and a DMA address are separate things.

The v6.12 platform_match, GPL-2.0, checks several matching mechanisms. With no driver override, its first two firmware branches are:

c
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

An OF match succeeds first when applicable; otherwise ACPI matching is attempted. The function then checks a platform id table or falls back to a name comparison. An explicit driver_override takes precedence in this function. A positive match only permits probing; it does not prove that resources, clocks or interrupts are usable. Matching rules and firmware association depend on actual device and driver registration, not on the existence of a string alone.

platform_probe applies OF clock defaults and attaches a PM domain where applicable before invoking the platform driver's callback. Its callback/error branch, Linux v6.12, GPL-2.0, is:

c
	if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			dev_pm_domain_detach(_dev, true);
	}

The bus calls the driver's probe only when it exists; a nonzero result triggers PM-domain detach on this path. The surrounding function handles earlier errors and driver-specific restrictions. The general driver core then handles the returned status and its own cleanup. -EPROBE_DEFER indicates a dependency may be satisfied later; a missing mandatory resource can instead be a permanent error. A driver must not turn all errors into deferral or retain a pointer to a managed mapping after failed probe cleanup.

The full sequence before the callback is in drivers/base/platform.c, Linux v6.12, GPL-2.0 (surrounding code omitted):

c
	ret = of_clk_set_defaults(_dev->of_node, false);
	if (ret < 0)
		return ret;

	ret = dev_pm_domain_attach(_dev, true);
	if (ret)
		goto out;

	if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			dev_pm_domain_detach(_dev, true);
	}

Clock defaults run first, then PM-domain attach; a failed probe detaches the PM domain and returns its error. The prevent_deferred_probe branch just below the excerpt, which turns deferral into an error for drivers that set the flag, and the platform_probe_fail guard at the top of the function are not shown.

Population, match and probe
Population, match and probe1. OF population to Platform bus: creates a platform_device from an eligible DT node. 2. Platform bus to platform_match: match: OF compatible, then ACPI, then id table or name. 3. platform_match to Platform bus (return): nonzero match: probe may run. 4. Platform bus to platform_probe: call platform_probe. 5. platform_probe to Driver probe: of_clk_set_defaults, PM domain attach, then drv->probe. 6. Driver probe to platform_probe (return): nonzero: PM domain detached and error returned.OF populationPlatform busplatform_matchplatform_probeDriver probecreates aplatform_device froman eligible DT nodematch: OFcompatible, thenACPI, then id tableor namenonzero match: probemay runcall platform_probeof_clk_set_defaults,PM domain attach,then drv->probenonzero: PM domaindetached and errorreturned
  1. OF populationPlatform bus

    creates a platform_device from an eligible DT node

  2. Platform busplatform_match

    match: OF compatible, then ACPI, then id table or name

  3. platform_matchPlatform bus

    Return. nonzero match: probe may run

  4. Platform busplatform_probe

    call platform_probe

  5. platform_probeDriver probe

    of_clk_set_defaults, PM domain attach, then drv->probe

  6. Driver probeplatform_probe

    Return. nonzero: PM domain detached and error returned

Time runs downward. Dashed arrows show returns.

Read the text version
  1. OF population → Platform bus: creates a platform_device from an eligible DT node
  2. Platform bus → platform_match: match: OF compatible, then ACPI, then id table or name
  3. platform_match → Platform bus (return): nonzero match: probe may run
  4. Platform bus → platform_probe: call platform_probe
  5. platform_probe → Driver probe: of_clk_set_defaults, PM domain attach, then drv->probe
  6. Driver probe → platform_probe (return): nonzero: PM domain detached and error returned
Source map. Source map of Linux v6.12 platform matching and probe order, simplified. Not a captured trace.

Device Tree: a description, not a driver#

A flattened Device Tree (FDT) is a binary description passed to the kernel; the kernel expands it into OF nodes and properties. A node's compatible strings identify hardware programming models for matching. status can mark nodes unavailable under OF rules. A node describes hardware and relationships; it is not a driver binary and cannot make unsupported hardware work without a suitable enabled driver. Firmware, bootloader changes, overlays and kernel configuration affect the resulting tree. of_platform_populate walks eligible nodes and creates platform devices under its rules; individual drivers later parse resources and properties. Population and successful driver binding are separate stages.

reg encodes address and size cells according to the parent bus's #address-cells and #size-cells. Cells in the flattened format are big-endian 32-bit quantities. ranges can translate a child-bus address into a parent-bus address; an empty or absent property has bus-specific meaning and must not be guessed. A phandle refers to another node, for example a clock, reset controller, regulator or interrupt controller. Interrupt properties are interpreted by the relevant interrupt parent and domain; the firmware interrupt specifier is not automatically the Linux virtual IRQ number. reserved-memory describes regions with special reservation/use semantics under the actual binding. See drivers/of/address.c, drivers/of/irq.c, and the Device Tree usage model.

For a synthetic parent with #address-cells = <2> and #size-cells = <1>, suppose reg = <0x00000000 0x10001000 0x00001000>. The two address cells form child address 0x0000000010001000; the size is 0x1000 bytes. If an explicitly defined parent ranges maps child 0x10000000 to parent 0x40000000 for a range covering this register, the translated parent address is 0x40001000, with the same size. The exact ranges encoding depends on both buses' cell counts; this arithmetic is illustrative, not a decoded machine. A separate interrupt specifier such as hardware line 5 is interpreted by its interrupt controller/domain and may map to an allocated Linux IRQ number such as 42. The numbers are hypothetical; no universal 5 -> 42 rule exists.

Clock, reset and regulator phandles establish dependencies on providers. A provider not yet registered can cause -EPROBE_DEFER; a malformed property, invalid resource or unsupported compatible can fail for another reason. A driver must release partially acquired resources on failure and use the correct mapping and IRQ APIs. A DT node can be present with status = "disabled" and therefore not produce an active device through a given population path. Checking a compatible string alone is not evidence that the corresponding hardware is powered, mapped, or safe to access.

A real UART example and its branches#

The v6.12 amba-pl011.c has distinct AMBA and SBSA platform probe paths. In the SBSA platform path, sbsa_uart_probe obtains an IRQ using platform_get_irq, obtains a memory resource, and calls pl011_setup_port. That helper calls devm_ioremap_resource and returns the encoded error on failure before filling the UART port's mapping fields. The AMBA pl011_probe separately calls devm_clk_get and receives an IRQ from the AMBA device. These are related paths in the same driver file, not one invented universal sequence where the SBSA branch necessarily calls devm_clk_get.

If the SBSA memory resource is unavailable or the managed mapping fails, registration must not continue as though registers were mapped. If platform_get_irq returns a negative error, including a possible deferral, that path returns the error rather than treating it as an IRQ number. In the AMBA path, a missing or deferred clock from devm_clk_get similarly returns an error pointer. Managed resources provide cleanup at the appropriate probe lifecycle, but the serial subsystem and driver still own port registration and removal responsibilities. Do not perform MMIO merely because firmware lists an address; a resource claim, mapped access, power and driver contract are required.

ACPI: namespace, resources, and methods#

ACPI firmware provides tables and a namespace of objects; devices can carry hardware IDs (_HID) or compatible IDs (_CID) for matching. _CRS describes current resources under the relevant ACPI rules, _DSD can provide device properties, and _PRT can describe PCI interrupt routing. AML methods can express operations including power-related behavior. The kernel enumerates and interprets these structures through ACPI subsystem paths, rather than processing a DT compatible string as though it were an ACPI ID. See drivers/acpi/scan.c, drivers/acpi/resource.c, and drivers/acpi/property.c.

ACPI and DT can describe related resource concepts but their enumeration and power models are not interchangeable. Some devices are enumerated by PCI while ACPI supplies companion information; other ACPI objects lead to platform devices. A namespace entry does not automatically mean a driver bound successfully. Firmware can be incomplete or incorrect, and AML executes according to the configured kernel interpreter and platform policy. Treat firmware-provided resources as inputs requiring kernel validation and driver checks, not as proof of trustworthy hardware or of safe arbitrary register access. x86 systems commonly use ACPI and may expose no Device Tree; arm64 systems can use either according to platform firmware.

Probe failure and resource lifetime#

Imagine an OF-described UART node whose reg range translates successfully but whose clock provider has not registered. OF population creates a platform device; bus matching finds a compatible driver; probe asks for its clock and receives -EPROBE_DEFER. The driver returns that error after unwinding its acquired state according to its APIs. The driver core records a deferred attempt and may retry after a supplier becomes ready. The device remains present but is not yet successfully bound. If instead the reg property is malformed and the resource cannot be formed, an error can be permanent until the description or configuration is fixed. These are hypothetical states, not observations from the lab.

IRQ mapping also has lifecycle rules. platform_get_irq obtains a Linux IRQ through the resource/firmware path; the driver must request it through an appropriate API before expecting callbacks. A number alone is not a registered handler. If the driver has already registered a handler and a later probe step fails, cleanup must prevent an interrupt from running against freed private data. Managed IRQ release can help with resource ownership, but driver-specific device interrupt masking, work cancellation and DMA quiescence still matter. IRQ domains, shared lines and architecture-specific interrupt controllers mean that raw firmware specifiers should not be compared directly with a userspace IRQ number without the mapping context.

Turning firmware cells into resources#

Device Tree properties are byte strings with encodings defined by the specification and the binding. A cell is four bytes in big-endian order; converting it on a little-endian CPU is required before treating it as a native integer. The parent bus defines the number of cells in a child reg address and size, while a ranges entry uses the child and parent bus formats required by those buses. An address can require translation through multiple ancestors. The arithmetic example assumes an explicitly covering range; if no range covers the child address, the kernel cannot simply invent the same parent address. Always inspect the binding and translation helper for the actual bus.

The byte table below encodes the text's example reg value as three big-endian cells. It is a synthetic encoding, not a read of a real Device Tree blob.

The example reg value in bytes

Synthetic teaching data, not a device capture.

Select a field to connect its position, bytes, and meaning.

12 bytes in file order. Offsets start at zero.

Selected: Child address, high cell. Upper 32 bits of the child address in this example; zero..

Read the byte table
OffsetFieldHex bytesInterpretation
0Child address, high cell00 00 00 00Upper 32 bits of the child address in this example; zero.
4Child address, low cell10 00 10 00Big-endian 0x10001000; with the high cell the address is 0x0000000010001000.
8Size cell00 00 10 00Big-endian 0x00001000, a 4 KiB window under #size-cells = 1.
Synthetic teaching data. Synthetic teaching data: the text example reg value, encoded big-endian with #address-cells = 2 and #size-cells = 1. Not a decoded machine Device Tree.

An interrupt specifier is likewise a sequence of controller-defined cells, often including a hardware interrupt number and trigger information. interrupt-parent or an inherited relationship identifies the controller; an interrupt domain translates that firmware description into Linux's IRQ representation. The example value 42 is only a hypothetical result. A domain can be absent or not ready, and a driver must check negative error returns before requesting a handler. Mapping an IRQ does not power the device, enable the controller, or install a handler by itself.

A phandle creates a dependency edge, not a guarantee that the supplier driver has already probed. Clock, regulator and reset providers can appear later; a consumer can defer and be retried. reserved-memory can describe memory that should not be treated as ordinary general-purpose RAM, but the exact handling comes from the binding and kernel implementation. A reserved region is not automatically a device DMA mapping; the device still needs an appropriate DMA address and ownership protocol. These distinctions prevent a common error in which a firmware address is copied directly into a device descriptor without applying the DMA API.

Follow the UART failure paths#

In the SBSA branch of amba-pl011.c, firmware data can supply the current baud rate when an OF node exists. The driver obtains a free port slot and managed private allocation, calls platform_get_irq, and returns a negative result rather than storing it as a usable IRQ. It then obtains a memory resource and calls pl011_setup_port, where devm_ioremap_resource checks and maps the resource; an error pointer is propagated. Finally, successful setup records driver data and registers the UART port. Any later failure has to account for which serial-core state was registered as well as the managed mapping. This is a real conditional path in the cited file, not a claim that all UARTs use SBSA.

The IRQ check in that branch is the first failure point, and it is short enough to quote. In drivers/tty/serial/amba-pl011.c, Linux v6.12, GPL-2.0+ (surrounding code omitted):

c
	ret = platform_get_irq(pdev, 0);
	if (ret < 0)
		return ret;
	uap->port.irq	= ret;

A negative result, including a deferral, is returned as is and never stored as the port's IRQ. The excerpt does not show the managed private allocation before it or the memory-resource lookup after it.

The helper that the branch calls maps the register window and returns the error from the managed mapping:

c
static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap,
			    struct resource *mmiobase, int index)
{
	void __iomem *base;
	int ret;

	base = devm_ioremap_resource(dev, mmiobase);
	if (IS_ERR(base))
		return PTR_ERR(base);

devm_ioremap_resource either yields a usable __iomem base or an error pointer; the helper turns that into an integer return before it copies the base into the port. Registration of the port is outside this excerpt.

The separate AMBA pl011_probe allocates its private state, calls devm_clk_get, and uses an IRQ supplied on the AMBA device. Its clock error can include a deferral from a provider. The OF and ACPI matching mechanisms can lead to different enumeration paths, so do not splice the clock call from the AMBA function into a claimed SBSA call chain. Both paths illustrate a common ownership question: the driver must not use registers without the resource, mapping, power and clock prerequisites required for that particular hardware.

Firmware trust and interpretation#

ACPI _HID and _CID identify a device model for matching, while _CRS describes resources and _DSD can provide properties. _PRT relates to PCI interrupt routing, and AML methods can implement firmware-controlled operations. These are not equivalent to executing a kernel driver callback, even though the kernel interprets firmware data and methods while enumerating devices. A companion ACPI object can describe properties for a device enumerated by another bus. Missing or malformed firmware data can prevent binding or lead to quirks; visible ACPI tables alone do not prove that every described device is operational.

A read-only inventory should report presence, absence or permission failure without assuming an architecture. An x86 guest may expose ACPI and no Device Tree; an arm64 board can use DT or ACPI according to its boot environment. A container can hide both firmware trees while still using devices mediated by the host. Enumerating directory entries is deliberately narrower than decoding a vendor table or trusting its contents. Never alter an active host's firmware tables or boot arguments to perform this lesson.

Matching does not establish a working device#

A firmware node, platform device and bound driver describe three different stages. A Device Tree node can exist in the live tree but be disabled, malformed, or outside the set populated by a particular bus. A platform device can be registered with no matching driver. A matching driver can return -EPROBE_DEFER while a supplier is missing or fail permanently because an address, IRQ or binding property is invalid. Finally, a successful probe can still be followed by a runtime error or removal. A read-only directory count establishes none of those transitions by itself.

ACPI has analogous but not identical distinctions: a namespace object can exist without producing the platform device or binding that a student expects. Its IDs and resources must be interpreted with the relevant enumeration and driver rules, and AML methods can interact with power management. Kernel configuration can omit an interpreter or driver even when firmware provides data. These possibilities are reasons to record the platform and check source and logs, not to overwrite firmware tables or force a host driver to bind.

Practice#

Source-only route:

sh
rg -n -A30 'static int platform_match\(' "$KERNEL_SRC/drivers/base/platform.c"
rg -n -A38 'static int platform_probe\(' "$KERNEL_SRC/drivers/base/platform.c"
rg -n -A35 'int of_platform_populate\(' "$KERNEL_SRC/drivers/of/platform.c"
rg -n -A35 'static int sbsa_uart_probe\(' "$KERNEL_SRC/drivers/tty/serial/amba-pl011.c"
rg -n -A15 'static int pl011_setup_port\(' "$KERNEL_SRC/drivers/tty/serial/amba-pl011.c"

Verify a v6.12 checkout. Mark matching order, PM detach, DT population, the SBSA IRQ and mapping, and the separate AMBA clock acquisition. Calculate the synthetic reg translation and distinguish an interrupt specifier from a Linux IRQ.

Read-only firmware inventory, without dumping device identifiers or table contents:

sh
python3 - <<'PY'
from pathlib import Path
for name, path in [('DT /proc', '/proc/device-tree'),
                   ('DT sysfs', '/sys/firmware/devicetree/base'),
                   ('ACPI tables', '/sys/firmware/acpi/tables')]:
    p = Path(path)
    try:
        print(name, 'present' if p.is_dir() else 'not present')
        if p.is_dir(): print('  visible entries:', len(list(p.iterdir())))
    except OSError as e: print('  unavailable:', e.strerror)
PY

A missing DT on x86, missing ACPI on a DT-only platform, permission restriction or both absent in a container is an expected possible result. A visible table directory does not prove that any particular driver bound. No firmware table is overridden or read in full. Deliver the count/absence, synthetic address arithmetic and the two distinct UART probe branches.

Knowledge check#

  1. In platform_match, what is tried before the platform id table when no override is set?
  2. What does the synthetic reg value describe before and after the stated range translation?
  3. Why is an interrupt specifier not automatically a Linux IRQ number?
  4. What does platform_probe do if the driver's probe callback fails after PM-domain attachment?
  5. Why must the SBSA and AMBA PL011 probe paths be described separately?

Answers#

  1. OF matching and then ACPI matching are attempted before the platform id table and name fallback.
  2. It describes a child-bus range beginning at 0x10001000, length 0x1000; under the stated mapping its parent address begins at 0x40001000.
  3. Its controller-specific cells are interpreted by an interrupt domain, which maps them to a Linux IRQ under platform-specific rules.
  4. The shown branch detaches the PM domain and returns the failure through the surrounding path.
  5. The SBSA platform branch uses platform_get_irq and the setup helper; the AMBA branch obtains its clock and IRQ through its own path. Combining them would invent a call sequence.

Checkpoint#

Save the firmware inventory, cell arithmetic and UART probe/error diagram. Pass when you distinguish population, match and probe; identify the provider-deferral case; and explain why an MMIO resource is not a DMA address or safe raw pointer. Re-read the cited functions and correct the diagram as needed. This is practice, not certification.

Sources#

A little further beneath the surface.

Check your understanding with the checkpoint. Marking a chapter as read saves a bookmark, not a proficiency assessment.