Linux device driver development: The GPIO interface and device tree

Editor's Note: The embedded Linux kernel already play a vital role in embedded systems and stands to grow in importance in serving the diverse requirements of the Internet of Things (IoT). In turn, device drivers provide the critical link between applications and IoT devices themselves. In Linux Device Drivers Development, author John Madieu offers a comprehensive look at development of these drivers, combining detailed explanation with plenty of code samples. 

This excerpt, Chapter 14 from the book, focuses pin control and GPIOs — an area of particular importance to embedded systems developers looking to interact with custom hardware devices. This installment continues this excerpt from Part 1Part 2 and Part 3.

Adapted from Linux Device Drivers Development, by John Madieu.


Chapter 14. Pin Control and GPIO Subsystem (Continued)
By John Madieu

The GPIO interface and the device tree

Whatever interface one needs to use GPIO for, how to specify GPIOs depends on the controller providing them, especially regarding its #gpio-cells property, which determines the number of cells used for a GPIO specifier. A GPIO specifier contains at least the controller phandle, and one or more argument, where the number of arguments on #gpio-cells property of the controller that provides the GPIO. The first cell is generally the GPIO offset number on the controller, and the second represents the GPIO flags.

GPIO properties should be named [-]gpios], with being the purpose of this GPIO for the device. Keep in mind this rule is a must for descriptor-based interfaces, and becomes -gpios (note the absence of square brackets, meaning that the prefix is mandatory):

  gpio1: gpio1 {       gpio-controller;       #gpio-cells = <2>;   };   gpio2: gpio2 {       gpio-controller;       #gpio-cells = <1>;   };   [...]   cs-gpios = <&gpio1 17 0>,              <&gpio2 2>;              <0>, /* holes are permitted, means no GPIO 2 */              <&gpio1 17 0>;   reset-gpios = <&gpio1 30 0>;   cd-gpios = <&gpio2 10>;

In the preceding sample, CS GPIOs contain both controller-1 and controller-2 GPIOs. If one does not need to specify a GPIO at a given index in the list, one can use <0>. The reset GPIO has two cells (two arguments after the controller phandle), whereas CD GPIO has only one cell. You can see how meaningful the names are that I gave to my GPIO specifier.

The legacy integer-based interface and device tree

This interface relies on the following header:

   #include

There are two functions you should remember when you need to support DT from within your driver using legacy integer-based interfaces; these are of_get_named_gpio() and of_get_named_gpio_count():

<span style="font-family: 'andale mono', monospace;">   int of_get_named_gpio(struct device_node *np,                         const char *propname, int index)   int of_get_named_gpio_count(struct device_node *np,                         const char* propname)</span style="font-family: 'andale mono', monospace;">

Given a device node, the former returns the GPIO number of the property *propname at index position. The second just returns the number of GPIOs specified in the property:

  int n_gpios = of_get_named_gpio_count(dev.of_node,                                       "cs-gpios"); /* return 4 */   int second_gpio = of_get_named_gpio(dev.of_node, "cs-gpio", 1);   int rst_gpio = of_get_named_gpio("reset-gpio", 0);   gpio_request(second_gpio, "my-gpio);

There are drivers still supporting the old specifier, where GPIO properties are named [-gpio] or gpios . In that case, one should use unnamed API versions, by means of of_get_gpio() and of_gpio_count():

  int of_gpio_count(struct device_node *np)   int of_get_gpio(struct device_node *np, int index)

The DT node would look like:

  my_node@addr {       compatible = "[...]";       gpios = <&gpio1 2 0>, /* INT */               <&gpio1 5 0>; /* RST */       [...]   };

The code in the driver would look like this:

  struct device_node *np = dev->of_node;   if (!np)       return ERR_PTR(-ENOENT);   int n_gpios = of_gpio_count(); /* Will return 2 */   int gpio_int = of_get_gpio(np, 0);   if (!gpio_is_valid(gpio_int)) {       dev_err(dev, "failed to get interrupt gpion");       return ERR_PTR(-EINVAL);   }   gpio_rst = of_get_gpio(np, 1);   if (!gpio_is_valid(pdata->gpio_rst)) {       dev_err(dev, "failed to get reset gpion");       return ERR_PTR(-EINVAL);   }

One can summarize this by rewriting the first driver (the one for integer-based interfaces), in order to comply with the platform drivers structure, and use DT API:

#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/platform_device.h>      /* For platform devices */#include <linux/interrupt.h>            /* For IRQ */#include <linux/gpio.h>                 /* For Legacy integer based GPIO */#include <linux/of_gpio.h>              /* For of_gpio* functions */#include <linux/of.h>                   /* For DT*//* * Let us consider the node bellow * *    foo_device { *       compatible = "packt,gpio-legacy-sample"; *       led-gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>, // red *                   <&gpio2 16 GPIO_ACTIVE_HIGH>, // green * *       btn1-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; *       btn2-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>; *   }; */static unsigned int gpio_red, gpio_green, gpio_btn1, gpio_btn2;static int irq;static irqreturn_t btn1_pushed_irq_handler(int irq, void *dev_id){    int state;    /* read the button value and change the led state */    state = gpio_get_value(gpio_btn2);    gpio_set_value(gpio_red, state);    gpio_set_value(gpio_green, state);    pr_info("gpio_btn1 interrupt: Interrupt! gpio_btn2 state is %d)n", state);    return IRQ_HANDLED;}static const struct of_device_id gpio_dt_ids[] = {    { .compatible = "packt,gpio-legacy-sample", },    { /* sentinel */ }};static int my_pdrv_probe (struct platform_device *pdev){    int retval;    struct device_node *np = pdev->dev.of_node;    if (!np)        return -ENOENT;    gpio_red = of_get_named_gpio(np, "led", 0);    gpio_green = of_get_named_gpio(np, "led", 1);    gpio_btn1 = of_get_named_gpio(np, "btn1", 0);    gpio_btn2 = of_get_named_gpio(np, "btn2", 0);    gpio_request(gpio_green, "green-led");    gpio_request(gpio_red, "red-led");    gpio_request(gpio_btn1, "button-1");    gpio_request(gpio_btn2, "button-2");    /*     * Configure Button GPIOs as input     *     * After this, one can call gpio_set_debounce()     * only if the controller has the feature     *     * For example, to debounce  a button with a delay of 200ms     *  gpio_set_debounce(gpio_btn1, 200);     */    gpio_direction_input(gpio_btn1);    gpio_direction_input(gpio_btn2);    /*     * Set LED GPIOs as output, with their initial values set to 0     */    gpio_direction_output(gpio_red, 0);    gpio_direction_output(gpio_green, 0);    irq = gpio_to_irq(gpio_btn1);    retval = request_threaded_irq(irq, NULL,                            btn1_pushed_irq_handler,                            IRQF_TRIGGER_LOW | IRQF_ONESHOT,                            "gpio-legacy-sample", NULL);    pr_info("Hello world!n");    return 0;}static int my_pdrv_remove(struct platform_device *pdev){    free_irq(irq, NULL);    gpio_free(gpio_red);    gpio_free(gpio_green);    gpio_free(gpio_btn1);    gpio_free(gpio_btn2);    pr_info("End of the worldn");    return 0;}static struct platform_driver mypdrv = {    .probe      = my_pdrv_probe,    .remove     = my_pdrv_remove,    .driver     = {        .name     = "gpio_legacy_sample",        .of_match_table = of_match_ptr(gpio_dt_ids),         .owner    = THIS_MODULE,    },};module_platform_driver(mypdrv);MODULE_AUTHOR("John Madieu <john.madieu@gmail.com>");MODULE_LICENSE("GPL");

GPIO mapping to IRQ in the device tree

One can easily map GPIO to IRQ in the device tree. Two properties are used to specify an interrupt:

  • interrupt-parent : This is the GPIO controller for GPIO

  • interrupts : This is the interrupts specifier list

This applies to legacy and descriptor-based interface. The IRQ specifier depends on the #interrupt-cell property of the GPIO controller providing this GPIO. #interrupt- cell determine the number of cells used when specifying the interrupt. Generally, the first cell represents the GPIO number to map to an IRQ and the second cell represents what level/edge should trigger the interrupt. In any case, interrupt specifier always depends on its parent (the one which has the interrupt-controller set), so refer to its binding documentation in the kernel source:

  gpio4: gpio4 {       gpio-controller;       #gpio-cells = <2>;       interrupt-controller;       #interrupt-cells = <2>;};   my_label: node@0 {       reg = <0>;       spi-max-frequency = <1000000>;       interrupt-parent = <&gpio4>;       interrupts = <29 IRQ_TYPE_LEVEL_LOW>;};

There are two solutions for obtaining the corresponding IRQ:

  1. Your device sits on a known bus (I2C or SPI) : The IRQ mapping will be done for you, and made available either through the struct i2c_client or struct spi_device structure given to your probe() function (by means of i2c_client.irq or spi_device.irq ).

  2. Your device sits on the pseudo-platform bus : The probe() function will be given a struct platform_device , on which you can call platform_get_irq():

int platform_get_irq(struct platform_device *dev, unsigned int num);

Feel free to have a look at Chapter 6, The Concept of Device Tree .

GPIO and sysfs

The sysfs GPIO interface lets people manage and control GPIOs through sets or files. It is located under /sys/class/gpio . The device model is heavily used here, and there are three kinds of entries available:

  • /sys/class/gpio/ : This is where everything begins. This directory contains two special files, export and unexport :

    • export : This allow us to ask the kernel to export control of a given GPIO to user space by writing its number to this file. Example: echo 21 > export will create a GPIO21 node for GPIO #21, if that's not requested by kernel code.

    • unexport : This reverses the effect of exporting to user space. Example: echo 21 > unexport will remove any GPIO21 node exported using the export file.

  • /sys/class/gpio/gpioN/: This directory corresponds to the GPIO number N (where N is global to the system, not relative to the chip), exported either using the export file, or from within the kernel. For example: /sys/class/gpio/gpio42/ (for GPIO #42) with the following read/write attributes:

    • The direction file is used to get/set GPIO direction. Allowed values are either in or out This value may normally be written. Writing as out defaults to initializing the value as low. To ensure glitch-free operation, low and high values may be written to configure the GPIO as an output with that initial value. This attribute will not exist if the kernel code has exported this GPIO, disabling direction (see the gpiod_export() or gpio_export() function).

    • The value attribute lets us get/set the state of the GPIO line, depending on the direction, input or output. If the GPIO is configured as an output, any non-zero value written will be treated as HIGH state. If configured as an output, writing 0 will set the output low, whereas 1 will set the output high. If the pin can be configured as an interrupt-generating lines and if it has been configured to generate, one can call the poll(2) system call on that file and poll(2) will return whenever the interrupt was triggered. Using poll(2) wil require setting the events POLLPRI and POLLERR . If one uses select(2) instead, one should set the file descriptor in exceptfds . After poll(2) returns, either lseek(2) to the beginning of the sysfs file and read the new value or close the file and re-open it to read the value. It is the same principle as we discussed regarding the pollable sysfs attribute.

    • edge determines the signal edge that will let the poll() or select() function return. Allowed values are none , rising , falling , or both . This file is readable/writable, and exists only if the pin can be configured as an interrupt-generating input pin. active_low reads as either 0 (false) or 1 (true). Writing any non- zero value will invert the value attribute for both reading and writing. Existing and subsequent poll(2) support configurations through the edge attribute for rising and falling edges will follow this setting. The relevant function from kernel to set this value is gpio_sysf_set_active_low().

Exporting a GPIO from kernel code

Apart from using /sys/class/gpio/export file to export a GPIO to user space, one can use functions like gpio_export (for legacy interface) or gpioD_export (the new interface) from the kernel code in order to explicitly manage export of GPIOs which have already been requested using gpio_request() or gpiod_get():

  int gpio_export(unsigned gpio, bool direction_may_change);   int gpiod_export(struct gpio_desc *desc, bool direction_may_change);

The direction_may_change parameter decides if one can change the signal direction from input to output and vice versa. The reverse operations from kernel are gpio_unexport() or gpiod_unexport():

  void gpio_unexport(unsigned gpio); /* Integer-based interface */   void gpiod_unexport(struct gpio_desc *desc) /* Descriptor-based */

Once exported, one can use gpio_export_link() (or gpiod_export_link() for descriptor-based interfaces) in order to create symbolic links from elsewhere in sysfs, which will point to the GPIO sysfs node. Drivers can use this to provide the interface under their own device in sysfs with a descriptive name:

  int gpio_export_link(struct device *dev, const char *name,                         unsigned gpio)   int gpiod_export_link(struct device *dev, const char *name,                         struct gpio_desc *desc)

One could use this in the probe() function for descriptor-based interfaces as follows: static struct gpio_desc *red, *green, *btn1, *btn2;

  static int my_pdrv_probe (struct platform_device *pdev)   {       [...]       red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_LOW);       green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_LOW);       gpiod_export(&pdev->dev, "Green_LED", green);       gpiod_export(&pdev->dev, "Red_LED", red);          [...]       return 0;}

For integer-based interfaces, the code would look like this:

  static int my_pdrv_probe (struct platform_device *pdev)   {[...]       gpio_red = of_get_named_gpio(np, "led", 0);       gpio_green = of_get_named_gpio(np, "led", 1);       [...]       int gpio_export_link(&pdev->dev, "Green_LED", gpio_green)       int gpio_export_link(&pdev->dev, "Red_LED", gpio_red)       return 0;}

Summary

Dealing with a GPIO from within the kernel is an easy task, as shown in this chapter. Both legacy and new interfaces are discussed, giving the possibility to choose the one that fits your needs, in order to write enhanced GPIO drivers. You'll be able to handle IRQs mapped to GPIOs. The next chapter will deal with the chip that provides and exposes GPIO lines, known as the GPIO controller.

This final installment concludes this excerpt.

Reprinted with permission from Packt Publishing. Copyright © 2017 Packt Publishing


John Madieu is an embedded Linux and kernel engineer living in France, in Paris. His main activities consist of developing drivers and Board Support Packages (BSP) for companies in domains such as automation, transport, healthcare, energy, and the military. John works at EXPEMB, a French company that is a pioneer in electronical board design based on computer-on-module, and in embedded Linux solutions. He is an open source and embedded systems enthusiast, convinced that it is only by sharing knowledge that one learns more.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.