How Not to Write x86 Platform Drivers – Darren Hart (Intel)

Darren is a core kernel guy, recently doing driver work for the MinnowBoard. This talk is about some of the mistakes he made, and some of the gaps. The main difference between core work and driver work is that core work is complex algorithms on simple primitives, while drivers are simple algorithms on a complex set of primitives and layers.

A platform driver in the kernel is a pseudo-bus driver that gets enumerated explicitly in board file or dts.

The MinnowBoard is a completely open design for an Atom CPU (cfr. beaglebone).  Like beaglebone, it has derivative works as a design goal; the daughter cards are called lures. It exports non-discoverable buses like I2C, SPI, CAN, GPIO. So this requires an in the field hardware description mechanism.

The chipset used has GPIOs (for buttons and LEDs; PCI enumerated), this is the first board to actually use them. Ethernet PHY doesn’t have a MAC address EEPROM. One of the GPIOs is the reset line of the PHY.

A board-file is a self-describing non-enumerated driver, that reserves GPIOs and binds bus devices. It’s loaded as a module. It checks itself using DMI to check if the UEFI really says it’s a MinnowBoard. This check is exported so it can be used in other drivers as well. It’s in drivers/platform/x86. Of course, it was rejected because the drivers/platform approach is just not nice, because it’s dangerous and may damage hardware. It also adds files that the kernel has to maintain, and affects independent drivers, e.g. pch_uart has to detect it’s a MinnowBoard.

For UART, the problem is that it’s a standard PCI device, but its clock is set by the UEFI so the device or the driver don’t know what the clock is. You could add an explicit check for MinnowBoard in the driver, but it’s not nice.

For the PHY, the driver has to be adapted to toggle the reset line, and also to set some flags to e.g. disable hibernate and set clock delay according to the board layout. To implement this, a PCI subsystem ID was used. This is an additional field in the PCI descriptor that identifies not the device, but how the device is integrated, so you can write your own VID/PID into it and detect that in the driver. In the PCI device table, a match on the subsystem ID is added that specifies a platform_init function to be called and sets generic flags for the clock delay and hibernation.

For the Ethernet MAC address: normally the driver reads it from EEPROM and writes it to the MAC address register in the PCI address space. So now the UEFI was modified to read a location in its own SPI flash and write that to the Ethernet’s PCI address. The driver tries to read EEPROM, fails, and just continues with the MAC address that is already there.

Darren discovered something obvious: if you can influence the board design, make sure they use devices that are well supported by the kernel’s drivers.

How can you identify and describe a device? VID/PID, read from EFI or ACPI or device tree. On x86, ACPI is more appropriate than device tree because it’s already there anyway. Ideally, there would be a single mechanism in the kernel to retrieve information either from ACPI or from DT. Preferably, a new board should add no files at all to the kernel => store in ACPI.

ACPI assigns device IDs to pseudo devices, making them enumerable. It can lso identify GPIO resources. What it can’t do is describe arbitrary device attributes. Some vendors invent their own blobs but it’s not standardized. So you can e.g. define the existence of GPIO resources, but not what to do with them (is it a button? which keycode?). ACPI has support for non-typed arrays called Package, which allows you to build a dictionary and you can create a mapping with this. So add a reserved method _PRP that returns “properties” of the board – the equivalent of a device tree (but not a tree). So you need a parsing function similar to of_*.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s