Linux device driver development: The GPIO interface and device tree

John Madieu

October 22, 2018

John MadieuOctober 22, 2018

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 [<name>-]gpios], with <name> being the purpose of this GPIO for the device. Keep in mind this rule is a must for descriptor-based interfaces, and becomes <name>-gpios (note the absence of square brackets, meaning that the <name> prefix is mandatory):

  gpio1: gpio1 {
       #gpio-cells = <2>;
   gpio2: gpio2 {
       #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 <linux/of_gpio.h>

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 [<name>-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 gpio\n");
       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 gpio\n");
       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);
     * 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,
                            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);
    pr_info("End of the world\n");
    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_AUTHOR("John Madieu <>");

Continue reading on page two, GPIO mapping to IRQ >>



< Previous
Page 1 of 2
Next >

Loading comments...