Linux machine board file

From RidgeRun Developer Connection
Jump to: navigation, search

This wiki is an example to show how to add the necessary information to a Linux machine board file so that a Linux driver is associated with a chip that is always on the board. The Texas Instruments TPS65073 multi-function regulator, touch screen controller, and LED driver chip is used for this example.

TPS65073 chip overview

The TPS65073 is a multi-function device chip, so an MFD driver is used to coordinate access to the chip and route interrupts from the chip to the correct sub-driver.

The Linux files of interest include:

File Purpose
include/linux/mfd/tps6507x.h Multi-function device specific data structures used by board file
include/linux/regulator/tps6507x.h Regulator specific data structure used by board file
include/linux/input/tps6507x-ts.h Touch screen specific data structure used by board file
arch/arm/mach-davinci/board-*.c Machine/platform/board specific
drivers/mfd/tps6507x.c Multi-function (access control) driver
drivers/regulator/tps6507x-regulator.c Regulator sub-driver
drivers/input/touchscreen/tps6507x-ts.c Touch screen sub-driver
include/linux/leds/tps6507x.h LED specific data structure used by board file
Does not exist
Likely not needed
drivers/leds/tps6507x.c LED sub-driver
Not written yet

Kernel matching hardware with driver

The kernel needs to handle two different interactions between new hardware being discovered and new drivers being loaded:

  1. Hardware is known to the kernel (e.g. built onto the board) and the user loads the driver after boot (using insmod or modprobe).
  2. Kernel has initialized driver, (e.g. driver built into kernel or already loaded via insmod or modprobe) and user adds hardware (e.g. plugs in SD card or USB device).

How does the kernel connect the driver to the hardware in such a way that it works for both of these cases? The kernel keeps two lists - (a) all the known drivers and (b) all the known devices. Both lists are available from sysfs (/sys/modules and /sys/devices). When a device is connected, device_attach() in drivers/base/dd.c is called, which checks the list of drivers to see if any support the device. When a driver is loaded, driver_attach() in the same file is called, and in a similar way walks the list of devices to see if the driver can support that device. At the driver level, this process is called probing.

You can enable CONFIG_DEBUG_DRIVER to see all driver / device interaction in action.

Platform devices

If a device is built into the base hardware, it is called a platform device. The platform devices are exposed to the kernel (meaning get added to the kernel's list of known devices) via the board file for that specific hardware. For example, for the LeopardBoard 365, the board file is arch/arm/mach-davinci/board-dm365-leopard.c.

Kernel support for multi-function devices

Typically when a device driver writer creates code to support a particular device (chip), they assume their driver is the only code talking to the chip. If the chip supports more than one function, say touch screen and GPIOs, then we have two choices. Either one driver can be written to support all parts of the chip, or several sub-drivers can be written. If one driver is written, then where does it go? In the drivers/input/touchscreen directory or the drivers/gpio directory? This also effects where the user looks to enable a driver, etc. The driver is also more complex and intertwined if it supports more than one logical function.

Instead, the kernel supports a multi-function device (mfd) driver, where the mfd driver coordinates sub-driver probing, interrupt routine, and serializing interaction with the chip.

Extending the board file to support TPS6507x

Adding MFD information

The board file arch/arm/mach-davinci/board-dm365-leopard.c in our case) needs to inform the kernel about the existence of the MFD TSP6507x chip. The data structure used is defined in include/linux/mfd/tps6507x.h. For out example hardware design, that means the following is added to the board file:

#include <linux/i2c.h>
#include <linux/mfd/tps6507x.h>
static struct tps6507x_board tps_board = {
        .tps6507x_pmic_init_data = &tps65070_regulator_data[0],
        .tps6507x_ts_init_data = &tps6507x_touchscreen_data,
static struct i2c_board_info __initdata board_tps65070_info[] = {
                I2C_BOARD_INFO("tps6507x", 0x48),
                .platform_data = &tps_board,
        return i2c_register_board_info(1, board_tps65070_info,

Looking at the code from the bottom up, we start by informing the kernel about the MFD chip via the call to the i2c_register_board_info() function. This function is used to register all I2C chips. We pass a driver name tps6507x and in this case, since the I2C address is known to be 0x48, we tell the kernel where on the I2C bus to find the MFD chip. There is also regulator and touchscreen specific data, which we discuss below.

In the drivers/mfd/tps6507x.c source, you will find

static const struct i2c_device_id tps6507x_i2c_id[] = {
       { "tps6507x", 0 },
       { }
MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id);

This is how the driver provides the name tps6507x to the kernel. Part of the kernel probing logic matches the devices name, provided by struct i2c_board_info in this case, to the driver name, provided by struct i2c_device_id. When adding a device to the board file, the most common error for the probing function to fail is to have a mismatch in the name being used.