Platform Bus

Introduction

The platform bus manages drivers for devices on SOC-based platforms. The platform bus is a driver itself (located at system/dev/bus/platform) and is started automatically by the device manager on platforms that need it. Currently the platform bus is used on all arm64 platforms. It is not used on x86 platforms, since ACPI performs a similar role on x86. On platforms that use it, the platform bus is represented in the device tree as /sys.

The platform bus serves the following purposes:

  • Creating devices for binding the drivers needed for a particular platform.

  • Abstracting away the platform specific details for drivers, such as MMIO addresses, IRQ and GPIO numbers, etc. so a single driver for a particular hardware device or IP can be used across multiple platforms.

  • Sandboxing, so drivers can access the MMIO ranges, IRQs, GPIOs, I2C addresses, etc. that they need, but nothing else.

Initialization

The platform bus driver is a generic driver that contains no specific information about the platform it is running on. To provide the platform specific logic for the platform bus, it loads a platform specific helper driver called the “board driver”.

When Zircon boots, the vid, pid and board_name are read from the BOOTDATA_PLATFORM_ID section in the bootdata. This is passed to the Platform Bus driver via the device argument list. The Platform Bus then adds a device with protocol ZX_PROTOCOL_PLATFORM_BUS with the BIND_PLATFORM_DEV_VID and BIND_PLATFORM_DEV_PID binding variables set to the vid and did from the bootdata information. The board driver then binds to this device.

The board driver uses the ZX_PROTOCOL_PLATFORM_BUS protocol (see system/ulib/ddk/include/ddk/protocol/platform-bus.h) to communicate with the platform bus driver. After the board driver initializes itself, it calls pbus_set_interface() to register itself with the platform bus. This tells the platform bus that the board driver is ready to go, and provides an interface the platform bus driver can use to communicate with the board driver.

Platform Devices

After the board driver calls pbus_set_interface() to register itself, it will then typically call pbus_device_add() one or more times to create devices for platform device drivers to bind to. Platform device drivers use the ZX_PROTOCOL_PLATFORM_DEV protocol (see system/ulib/ddk/include/ddk/protocol/platform-device.h) to communicate with the platform bus.

The platform device protocol is used by platform device drivers to access the resources they need to function. To allow development of platform independent drivers, these resources are accessed via device specific index rather than an a physical address or ID in the global name space. For example, if a driver needs two IRQs, they will be index zero and one rather than the actual IRQ numbers on the platform. Similarly, MMIO ranges are accessed via an index rather than the actual physical address. This allows us to write drivers that work across multiple platforms, where the basic functionality of the hardware is the same but details like MMIO addresses and IRQ and GPIO numbers may be different. The mapping from the indices used by the platform devices and the actual physical values is provided by the board driver.

Platform device drivers use pdev_map_mmio() to map MMIO regions and pdev_map_interrupt() to get interrupt handles for their IRQs. The platform device protocol also provides pdev_alloc_contig_vmo() and pdev_map_contig_vmo() to create contiguous VMOs for hardware that requires contiguous DMA buffers. (It is strongly encouraged to use regular non-contiguous VMOs whenever possible instead). Finally, the platform device protocol includes pdev_vmo_buffer_t, which is a helper to make it easy to work with VMOs for MMIO ranges and contiguous DMA regions.

Platform Bus Protocols

In addition to the ZX_PROTOCOL_PLATFORM_DEV platform device protocol, the platform bus can provide additional protocols to platform device drivers. These protocols are also accessed via the DDK device_get_protocol API. For example, the platform bus can provide the ZX_PROTOCOL_GPIO protocol for GPIOs and ZX_PROTOCOL_I2C for devices on an I2C bus. The implementation of these protocols live in the board driver. The board driver provides this via the pbus_interface_get_protocol call in the interface that the board driver registers with the platform bus. For these protocols, the resources are also accessed via an abstract index rather than raw value to insulate the platform device driver from the specifics of the platform.