Advertisement

Linux device driver development: The GPIO subsystem

John Madieu

September 23, 2018

John MadieuSeptember 23, 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 begun in Part 1

Adapted from Linux Device Drivers Development, by John Madieu.


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

The GPIO subsystem

From the hardware point of view, a GPIO is a functionality, a mode in which a pin can operate. From a software point of view, a GPIO is nothing but a digital line, which can operate as an input or output, and can have only two values: (1 for high or 0 for low). Kernel GPIO subsystems provide every function you can imagine to set up and handle GPIO line from within your driver:

  • Prior to using a GPIO from within the driver, one should claim it to the kernel. This is a way to take the ownership of the GPIO, preventing other drivers from accessing the same GPIO. After taking the ownership of the GPIO, one can:

    • Set the direction

    • Toggle its output state (driving line high or low) if used as output

    • Set the debounce-interval and read the state, if used as input. For GPIO lines mapped to IRQ, one can define at what edge/level the interrupt should be triggered, and register a handler that will be run whenever the interrupt occurs.

There are actually two different ways to deal with GPIO in the kernel, as follows:

  • The legacy and depreciated integer-based interface, where GPIOs are represented by integer

  • The new and recommended descriptor-based interface, where a GPIO is represented and described by an opaque structure, with a dedicated API

The integer-based GPIO interface: legacy

The integer-based interface is the most well-known. The GPIO is identified by an integer, which is used for every operation that needs to be performed on the GPIO. The following is the header that contains legacy GPIO access functions:

   #include <linux/gpio.h>

There are well known functions to handle GPIO in kernel.

Claiming and configuring the GPIO

One can allocate and take the ownership of a GPIO using the gpio_request() function:

   static int gpio_request(unsigned gpio, const char *label)

gpio represents the GPIO number we are interested in, and label is the label used by the kernel for the GPIO in sysfs, as we can see in /sys/kernel/debug/gpio. You have to check the value returned, where 0 mean success, and negative error code on error. Once done with the GPIO, it should be set free with the gpio_free() function:

   void gpio_free(unsigned int gpio)

If in doubt, one can use gpio_is_valid() function to check whether this GPIO number is valid on the system prior to allocate it:

   static bool gpio_is_valid(int number)

Once we own the GPIO, we can change its direction, depending on the need, and whether it should be an input or output, using the gpio_direction_input() or gpio_direction_output() functions:

   static int  gpio_direction_input(unsigned gpio)

   static int  gpio_direction_output(unsigned gpio, int value)

gpio is the GPIO number we need to set the direction. There is a second parameter when it comes to configuring the GPIO as output: value, which is the state the GPIO should be in once the output direction is effective. Here again, the return value is zero or a negative error number. These functions are internally mapped on top of lower level callback functions exposed by the driver of the GPIO controller that provides the GPIO we use. In the next Chapter 15, GPIO Controller Drivers - gpio_chip, dealing with GPIO controller drivers, we will see that a GPIO controller, through its struct gpio_chip structure, must expose a generic set of callback functions to use its GPIOs.

Some GPIO controllers offer the possibility to change the GPIO debounce-interval (this is only useful when the GPIO line is configured as input). This feature is platform-dependent. One can use int gpio_set_debounce() to achieve that:

   static int gpio_set_debounce(unsigned gpio, unsigned debounce)

where debounce is the debounce time in ms.

All the preceding functions should be called in a context that may sleep. It is a good practice to claim and configure GPIOs from within the driver's probe function.

Accessing the GPIO getting/setting the value

You should pay attention when accessing GPIO. In an atomic context, especially in an interrupt handler, one has to be sure the GPIO controller callback functions will not sleep. A well-designed controller driver should be able to inform other drivers (actually clients) whether call to its methods may sleep or not. This can be checked with gpio_cansleep() function.

None of the functions used to access GPIO return an error code. That is why you should pay attention and check return values during GPIO allocation and configuration.

Continue reading on page two >>

 

< Previous
Page 1 of 2
Next >

Loading comments...