22 Jan 2018

RPi and TPM and DT Part 1 (Background)

The company for which I work put together a daughter-board for the Raspberry Pi that includes a TPM chip (among other things). This series of posts is a recap of what was required to get the TPM chip working with an OE/Yocto-generated image.

More generically, Hitex has also created a daughter-board for the Raspberry Pi that includes the same chip.

The TPM chip is this one from Infineon, the SLB9670. It uses an SPI interface.

Back in the glory days of the PC and the PC clones, the memory locations of a device's configuration registers were known a priori. In other words, given the example of a serial port: a serial port has 8 contiguous configuration registers, if your computer had a serial port, its first register would be at memory location 0x3f8 and it would be known as COM1. If you had a second serial port, its base address would be 0x2f8 and it would be COM2. Nobody would ever dream of putting something else at 0x3f8, and if your PC's OS probed 0x3f8 and didn't find anything, it could assume you didn't have a COM1.

Nowadays, such hard-coding of the I/O space would be considered silly. Some products need no serial ports, and others need dozens; setting aside a large block of I/O space for devices that may or may not be present isn't useful. There's no reason 0x3f8 has to be reserved for the first serial port. Additionally, buses such as PCI and USB allow devices to be placed anywhere in memory and can be configured and probed dynamically. However, not all devices are on such fancy buses. SPI and I2C devices, for example, have simpler buses that don't provide dynamic probing and configuration. But your drivers still need to know where in the memory map they are found.

One solution to map known device addresses for a given product to device drivers, without hard-coding or a priori knowledge, is to use a Device Tree (DT). Given a specific product with a set of devices at specific addresses (for that product) a DT can be created (for that product) to map these known addresses for the relevant drivers. This means every product, potentially, needs its own Device Tree (if DT is the solution being used). Device Trees are stored and maintained alongside the Linux kernel sources, but are usually flashed to a separate location than the kernel itself are not not part of the kernel blob itself (although there are options to make it so). When booted, the kernel is provided with the information it needs to find the Device Tree. In this way, the same kernel build could work among a set of products that have, roughly, the same peripherals but potentially at different addresses, by providing different DTs for each product.

Device Trees are not new technology. Their lineage can be traced back to Sun's OpenFirmware from the late 1980s. The Linux kernel started using OF/DT early on for various PowerPC boards (e.g. the PowerPC-based Apple Macs from the 1990s).

Device Trees are not the only way to solve the kernel's "how can I find my peripherals?" problem. Other solutions include ACPI and UEFI.

This is all well and good for boards whose peripherals are "hard-coded" to the board itself (either because they are part of the MCU, or because they are soldered to the board and their bus IDs are also hard-coded). But most embedded boards come with expansion headers that allow the user to plug in daughter-boards to add features (be they "shields" or "capes" or "hats" etc.). These expansion headers mostly expose a bunch of I2C, SPI, and various GPIO pins to the user. The resulting explosion of DT possibilities would be crazy to try to maintain in any repository if the goal was to try to track every combination of board with every combination of daughter-board. Therefore Device Tree Overlays came into existence.

A Device Tree Overlay is a small snippet of a Device Tree that can be maintained and loaded separately, but is processed and merged with the base Device Tree by the kernel when it boots. DT Overlays are not dynamic. The assumption is that in order to change a product's daughter-board, the product would have to be powered down, and while the power is applied the wiring is not being changed. Some daughter-boards are even getting so fancy as to include a discoverable ID that can be used by the kernel to find and load the overlay without user intervention!

In my specific case I have a Raspberry Pi 3, with a "dumb" daughter-board which includes a TPM chip that I want the Linux kernel to be able to find and use. The Linux kernel already includes a driver for this specific TPM chip [drivers/char/tpm/tpm_tis_spi.c], all I need to do is to help it find the chip by providing a correct DT Overlay telling the driver where to look (i.e. which SPI bus and chip select to use) for the hardware. I also need to adjust my kernel configuration to make sure the relevant parts were compiled and included to support this hardware.