Configuration

Hardware peripherals are attached to the CPU through a bus, such as the PCI bus.

During bootup, the BIOS (or equivalent platform startup software) discovers all of the peripherals attached to the PCI bus. Each peripheral is assigned resources (notably interrupt vectors, and address ranges for configuration registers).

The impact of this is that the actual resources assigned to each peripheral may be different across reboots. When the operating system software starts up, it enumerates the bus and starts drivers for all supported devices. The drivers then call PCI functions in order to obtain configuration information about their device(s) so that they can map registers and bind to interrupts.

Base address register

The Base Address Register (BAR) is a configuration register that exists on each PCI device. It's where the BIOS stores information about the device, such as the assigned interrupt vector and addresses of control registers. Other, device specific information, is stored there as well.

Call Pci::MapMmio() to cause the BAR register to be mapped into the driver host's address space:

#include <lib/device-protocol/pci.h>

zx_status_t Pci::MapMmio(uint32_t bar_id, uint32_t cache_policy,
                         std::optional<fdf::MmioBuffer>* mmio);

The ddk::Pci class is the interface drivers use to talk to the PCI bus.

The first parameter, bar_id, is the BAR register number, starting with 0.

The second parameter, cache_policy, determines the caching policy for access, and can take on the following values:

cache_policy value Meaning
ZX_CACHE_POLICY_CACHED use hardware caching
ZX_CACHE_POLICY_UNCACHED disable caching
ZX_CACHE_POLICY_UNCACHED_DEVICE disable caching, and treat as device memory
ZX_CACHE_POLICY_WRITE_COMBINING uncached with write combining

Note that ZX_CACHE_POLICY_UNCACHED_DEVICE is architecture dependent and may in fact be equivalent to ZX_CACHE_POLICY_UNCACHED on some architectures.

The last argument is an output parameter for the created buffer.

Reading and writing memory

Once the Pci::MapMmio() function returns with a valid result, you can access the BAR with through the MmioBuffer interface, for example:

#include <lib/device-protocol/pci.h>
#include <lib/mmio/mmio-buffer.h>

std::optional<fdf::MmioBuffer> mmio;
zx_status_t status = pci.MapMmio(0, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
if (status == ZX_OK) {
  mmio.Write32(0x1234, REGISTER_X);  // configure register X for deep sleep mode
}