How is required information passed to device drivers in Linux kernel

Posted on November 14, 2014
Tags:
by Sanchayan Maity

First the basic is how do you provide information like memory addresses from which the driver will do the register read/writes? Now, depending on the kernel version, this can be different. The older kernels and drivers used the platform architecture to specify/get this information. New kernels and drivers rely on device trees for this information.

So let me show how this is done for both.

static struct resource foo_resource[] = {
[0] = {
      .start  = foo_BASE_TEG,         /* address */
      .end    = foo_BASE_TEG + 0xff,  /* data */
      .flags  = IORESOURCE_MEM,
      },
[1] = {
      /* interrupt assigned during initialisation */
      .flags  = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
      }
};

static struct foo_platform_data foo_platdata = {
      .osc_freq = 24000000
};

static struct platform_device foo_device = {
     .name = "foo_platform",
     .id   = 0,
     .num_resources  = ARRAY_SIZE(foo_resource),
     .resource       = foo_resource,
     .dev            = {
               .platform_data = &foo_platdata,
         }
};

Something like above will be specified in a board file for the hardware. The resource structure specifies the memory address with .start and .end specifier. This in turn is passed in the platform_device structure. When your driver loads at kernel boot up, in init() the platform driver register function matches the name specified in the driver structure and uses the data passed from platform device above. Or if a probe() is directly called by the kernel on boot up, the platform_device pointer passed to the probe call, can be used to retrieve the platform data. The oscillator frequency was specified in the platform data above, but, any such data can be specified and then accessed in the driver. If pdev is the pointer in the probe call, the platform data will be accessible with pdev->dev.platform_data.The pointer to the resource structure can be had with a call to platform_get_resource. Once the resouce structure pointer is available, an ioremap call will return the address to be used from that point onwards, which will be assigned to an iomem variable. Any writes or read you from here on will be based off the memory address you got in the iomem variable above. The readl and writel functions are used for the ARM architecture to read or write to registers. Now you may not note this functions directly in a driver, as a lot many times the drivers are build around functionality provided by a subsystem, but, ultimately in the back end these functions will be used.

Have a look at the “can” related structures here

http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm/mach-tegra/board-colibri_t20.c?h=tegra

and then have a look at the driver code below, especially the probe() call

http://git.toradex.com/cgit/linux-toradex.git/tree/drivers/mtd/maps/tegra_nor.c?h=tegra

In recent kernels, device trees are used. For example, an ADC peripheral have a device tree node specification as below

adc0: adc@4003b000
{
    compatible = "fsl,vf610-adc";
    reg = <0x4003b000 0x1000>;
    interrupts = <0 53 0x04>;
    clocks = <&clks VF610_CLK_ADC0>;
    clock-names = "adc";
    status = "disabled";
    #io-channel-cells = <1>;
};

So, 0x4003b000 is the starting address of the peripheral. Have a look here http://lxr.free-electrons.com/source/drivers/iio/adc/vf610_adc.c. The of_device_id matches against the name specified in the node and uses the information from the node. Have a look at the driver, probe() function especially and this is pretty simple. Shows how the memory information is read in from the node and used further. Also, one can clearly see the readl() and writel() calls. Do note that however not all drivers will use readl and writely directly, but, have several cascaded pointer calls which will ultimately result in a call to readl or writel. The data in a device tree node can be retrieved with device tree node helper functions.