跳到主要内容

Linux GPIO 子系统

GPIO 子系统

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:

从硬件的角度来看,GPIO 是一种功能,一种引脚可以操作的模式。 从软件的角度来看,GPIO 只不过是一条数字线,可以作为输入或输出操作,并且只能有两个值:(1 表示高或 0 表示低)。 内核 GPIO 子系统提供了您可以想象的所有功能,可以在您的驱动程序中设置和处理 GPIO 线:

  • 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

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 GPIOgetting/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.

In atomic context

There are GPIO controllers that can be accessed and managed through simple memory read/write operations. These are generally embedded in the SoC, and do not need to sleep. gpio_cansleep() will always return false for those controllers. For such GPIOs, you can get/set their value from within an IRQ handler, using the well-known gpio_get_value() or gpio_set_value() , depending on the GPIO line being configured as input or output :

static int gpio_get_value(unsigned gpio)
void gpio_set_value(unsigned int gpio, int value);

gpio_get_value() should be used when the GPIO is configured as input (using gpio_direction_input() ), and return the actual value (state) of the GPIO. On the other hand, gpio_set_value() will affect the value of the GPIO, which should have been configured as an output using gpio_direction_output() . For both function, value can be considered as Boolean , where zero means low, and non-zero value mean high.

In a non-atomic context (that may sleep)

On the other hand, there are GPIO controllers wired on buses such as SPI and I2C. Since functions accessing those buses may lead to sleep, the gpio_cansleep() function should always return true (it is up to the GPIO controller to take of returning true). In this case, you should not access those GPIOs from within the IRQ handled, at least not in the top half (the hard IRQ). Moreover, the accessors you have to use as your general-purpose access should be suffixed with _cansleep .

static int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);

They behave exactly like accessors without the _cansleep() name suffix, with the only difference being that they prevent the kernel from printing warnings when the GPIOs are accessed.

GPIOs mapped to IRQ

Input GPIOs can often be used as IRQ signals. Such IRQs can be edge-triggered or level- triggered. The configuration depends on your needs. The GPIO controller is responsible for providing the mapping between the GPIO and its IRQ. One can use goio_to_irq() to map a given GPIO number to its IRQ number:

int gpio_to_irq(unsigned gpio);

The return value is the IRQ number, on which one can call request_irq() (or the threaded version request_threaded_irq()) in order to register a handler for this IRQ:

static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
return IRQ_HANDLED;
}

int gpio_int = of_get_gpio(np, 0);
int irq_num = gpio_to_irq(gpio_int);
int error = devm_request_threaded_irq(&client->dev, irq_num, NULL, my_interrupt_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, input_dev->name, my_data_struct);
if (error) {
dev_err(&client->dev, "irq %d requested failed, %dn", client->irq, error);
return error;
}

Putting it all together

The following code is a summary putting into practice all the concepts discussed regarding integer-based interfaces. This driver manages four GPIOs: two buttons (btn1 and btn2), and two LEDs (green and red). Btn1 is mapped to an IRQ, and whenever its state changes to LOW, the state of btn2 is applied to LEDs. For example, if the state of btn1 goes LOW while btn2 is high, GREEN and RED led will be driven to HIGH:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h> /* For Legacy integer based GPIO */
#include <linux/interrupt.h> /* For IRQ */

/* * Please choose values that are free on your system */
static unsigned int GPIO_LED_RED = 49;
static unsigned int GPIO_BTN1 = 115;
static unsigned int GPIO_BTN2 = 116;
static unsigned int GPIO_LED_GREEN = 120;
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_LED_RED, state);
gpio_set_value(GPIO_LED_GREEN, state);
pr_info("GPIO_BTN1 interrupt: Interrupt! GPIO_BTN2 state is %d)n", state);
return IRQ_HANDLED;
}

static int __init hellowolrd_init(void)
{
int retval;
/*
* One could have checked whether the GPIO is valid on the controller or not,
* using gpio_is_valid() function.
* Ex:
* if (!gpio_is_valid(GPIO_LED_RED)) {
* pr_infor("Invalid Red LEDn");
* return -ENODEV;
* }
*/
gpio_request(GPIO_LED_GREEN, "green-led");
gpio_request(GPIO_LED_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_LED_RED, 0);
gpio_direction_output(GPIO_LED_GREEN, 0);
irq = gpio_to_irq(GPIO_BTN1);
retval = request_threaded_irq(irq, NULL, btn1_pushed_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "device-name", NULL);
pr_info("Hello world!n");
return 0;
}

static void __exit hellowolrd_exit(void)
{
free_irq(irq, NULL);
gpio_free(GPIO_LED_RED);
gpio_free(GPIO_LED_GREEN);
gpio_free(GPIO_BTN1);
gpio_free(GPIO_BTN2);
pr_info("End of the worldn");
}
module_init(hellowolrd_init);
module_exit(hellowolrd_exit);
MODULE_AUTHOR("John Madieu <john.madieu@gmail.com>");
MODULE_LICENSE("GPL");

The next installment will discuss the descriptor-based GPIO interface.