Embedded Linux device drivers: Discovering the hardware configuration

August 22, 2018

CHRIS.SIMMONDS_#1-August 22, 2018

Editor's Note: Embedded Linux has consistently ranked among the top operating systems used in embedded system design. With the rapid growth in interest in the Internet of Things (IoT), the ability of embedded Linux to serve multiple roles will prove vital in supporting diverse needs at each layer of the IoT application hierarchy. In turn, the ability of engineers to master embedded Linux systems will become critical for achieving rapid, reliable development of more sophisticated systems. In Mastering Embedded Linux Programming - Second Edition, author Chris Simmonds takes the reader on a detailed tour across the breadth and depth of this important operating system, using detailed examples to illustrate each key point.

Adapted from Mastering Embedded Linux Programming - Second Edition, by Chris Simmonds.

Chapter 9. Interfacing with Device Drivers (Continued)
By Chris Simmonds

Discovering the hardware configuration

The dummy driver demonstrates the structure of a device driver, but it lacks interaction with real hardware since it only manipulates memory structures. Device drivers are usually written to interact with hardware. Part of that is being able to discover the hardware in the first place, bearing in mind that it maybe at different addresses in different configurations.

In some cases, the hardware provides the information itself. Devices on a discoverable bus such as PCI or USB have a query mode, which returns resource requirements and a unique identifier. The kernel matches the identifier and possibly other characteristics with the device drivers, and marries them up.

However, most of the hardware blocks on an embedded board do not have such identifiers. You have to provide the information yourself in the form of a device tree or as C structures known as platform data.

In the standard driver model for Linux, device drivers register themselves with the appropriate subsystem: PCI, USB, open firmware (device tree), platform device, and so on. The registration includes an identifier and a callback function called a probe function that is called if there is a match between the ID of the hardware and the ID of the driver. For PCI and USB, the ID is based on the vendor and the product IDs of the devices; for device tree and platform devices, it is a name (an text string).

Device trees

I gave you an introduction to device trees in Chapter 3, All About Bootloaders. Here, I want to show you how the Linux device drivers hook up with this information.

As an example, I will use the ARM Versatile board, arch/arm/boot/dts/versatile- ab.dts, for which the Ethernet adapter is defined here:

  net@10010000 {
     compatible = "smsc,lan91c111";
     reg = <0x10010000 0x10000>;
     interrupts = <25>;

The platform data

In the absence of device tree support, there is a fallback method of describing hardware using C structures, known as the platform data.

Each piece of hardware is described by struct platform_device, which has a name and a pointer to an array of resources. The type of the resource is determined by flags, which include the following:

  • IORESOURCE_MEM: This is the physical address of a region of memory

  • IORESOURCE_IO: This is the physical address or port number of IO registers

  • IORESOURCE_IRQ: This is the interrupt number

Here is an example of the platform data for an Ethernet controller taken from arch/arm/mach-versatile/core.c, which has been edited for clarity:

  #define VERSATILE_ETH_BASE     0x10010000
   #define IRQ_ETH                25
   static struct resource smc91x_resources[] = {
     [0] = {
       .start          = VERSATILE_ETH_BASE,
       .end            = VERSATILE_ETH_BASE + SZ_64K - 1,
       .flags          = IORESOURCE_MEM,
[1] = {        .start          = IRQ_ETH,        .end            = IRQ_ETH,        .flags          = IORESOURCE_IRQ, }, 
};    static struct platform_device smc91x_device = {      .name           = "smc91x",      .id             = 0,      .num_resources  = ARRAY_SIZE(smc91x_resources),      .resource       = smc91x_resources, };

It has a memory area of 64 KB and an interrupt. The platform data has to be registered with the kernel, usually when the board is initialized:

  void __init versatile_init(void)
     [ ...]

Continue reading on page two >>


< Previous
Page 1 of 2
Next >

Loading comments...