GPIO Consumer API
The GPIO consumer API allows drivers to request and use GPIO pins for various purposes like reset signals, enable pins, interrupts, and data lines.
Getting GPIOs
From Device Tree
device@10000000 {
compatible = "vendor,my-device";
reset-gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
enable-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
data-gpios = <&gpio0 7 0>,
<&gpio0 8 0>,
<&gpio0 9 0>,
<&gpio0 10 0>;
};
In Driver Code
#include <linux/gpio/consumer.h>
struct my_device {
struct gpio_desc *reset_gpio;
struct gpio_desc *enable_gpio;
struct gpio_descs *data_gpios;
};
static int my_probe(struct platform_device *pdev)
{
struct my_device *dev;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
/* Required GPIO */
dev->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(dev->reset_gpio))
return dev_err_probe(&pdev->dev, PTR_ERR(dev->reset_gpio),
"Failed to get reset GPIO\n");
/* Optional GPIO */
dev->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(dev->enable_gpio))
return PTR_ERR(dev->enable_gpio);
/* Array of GPIOs */
dev->data_gpios = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_OUT_LOW);
if (IS_ERR(dev->data_gpios))
return PTR_ERR(dev->data_gpios);
return 0;
}
GPIO Acquisition Functions
/* Single GPIO */
struct gpio_desc *devm_gpiod_get(struct device *dev,
const char *con_id,
enum gpiod_flags flags);
struct gpio_desc *devm_gpiod_get_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags);
/* By index (for multiple GPIOs with same name) */
struct gpio_desc *devm_gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags);
/* Array of GPIOs */
struct gpio_descs *devm_gpiod_get_array(struct device *dev,
const char *con_id,
enum gpiod_flags flags);
GPIO Flags
/* Direction flags */
GPIOD_IN /* Input */
GPIOD_OUT_LOW /* Output, initially deasserted */
GPIOD_OUT_HIGH /* Output, initially asserted */
GPIOD_OUT_LOW_OPEN_DRAIN /* Open drain, initially low */
GPIOD_OUT_HIGH_OPEN_DRAIN /* Open drain, initially high */
/* For devm_gpiod_get_optional */
GPIOD_ASIS /* Don't change direction */
Setting and Getting Values
Output GPIOs
/* Set logical value (handles active-low automatically) */
gpiod_set_value(gpio, 1); /* Assert (active) */
gpiod_set_value(gpio, 0); /* Deassert (inactive) */
/* Set with potential sleep (for I2C/SPI GPIO expanders) */
gpiod_set_value_cansleep(gpio, 1);
/* Set raw value (ignores active-low flag) */
gpiod_set_raw_value(gpio, 1);
Input GPIOs
/* Get logical value */
int value = gpiod_get_value(gpio);
/* Get with potential sleep */
int value = gpiod_get_value_cansleep(gpio);
/* Get raw value */
int value = gpiod_get_raw_value(gpio);
Bulk Operations
struct gpio_descs *gpios;
DECLARE_BITMAP(values, 8);
/* Set multiple GPIOs at once */
bitmap_zero(values, gpios->ndescs);
set_bit(0, values); /* GPIO 0 = high */
set_bit(2, values); /* GPIO 2 = high */
gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, values);
/* Get multiple GPIOs */
gpiod_get_array_value(gpios->ndescs, gpios->desc, gpios->info, values);
Direction Control
/* Change direction */
gpiod_direction_input(gpio);
gpiod_direction_output(gpio, initial_value);
/* Check current direction */
int dir = gpiod_get_direction(gpio);
/* 0 = output, 1 = input */
GPIO to IRQ
static int my_probe(struct platform_device *pdev)
{
struct gpio_desc *irq_gpio;
int irq;
/* Get GPIO configured as input */
irq_gpio = devm_gpiod_get(&pdev->dev, "irq", GPIOD_IN);
if (IS_ERR(irq_gpio))
return PTR_ERR(irq_gpio);
/* Get IRQ number */
irq = gpiod_to_irq(irq_gpio);
if (irq < 0)
return irq;
/* Request interrupt */
return devm_request_threaded_irq(&pdev->dev, irq,
NULL, my_irq_handler,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
"my-device", dev);
}
Reset Sequence Example
static int device_reset(struct my_device *dev)
{
if (!dev->reset_gpio)
return 0;
/* Assert reset */
gpiod_set_value_cansleep(dev->reset_gpio, 1);
/* Hold reset for 100us minimum */
usleep_range(100, 200);
/* Release reset */
gpiod_set_value_cansleep(dev->reset_gpio, 0);
/* Wait for device to initialize */
usleep_range(1000, 2000);
return 0;
}
Power Sequence Example
struct my_device {
struct gpio_desc *power_gpio;
struct gpio_desc *enable_gpio;
struct gpio_desc *reset_gpio;
};
static int device_power_on(struct my_device *dev)
{
/* Power on */
if (dev->power_gpio)
gpiod_set_value_cansleep(dev->power_gpio, 1);
usleep_range(1000, 2000); /* Power stabilization */
/* Enable */
if (dev->enable_gpio)
gpiod_set_value_cansleep(dev->enable_gpio, 1);
usleep_range(100, 200);
/* Release reset */
if (dev->reset_gpio) {
gpiod_set_value_cansleep(dev->reset_gpio, 1);
usleep_range(100, 200);
gpiod_set_value_cansleep(dev->reset_gpio, 0);
}
usleep_range(5000, 10000); /* Device startup */
return 0;
}
static void device_power_off(struct my_device *dev)
{
/* Assert reset */
if (dev->reset_gpio)
gpiod_set_value_cansleep(dev->reset_gpio, 1);
/* Disable */
if (dev->enable_gpio)
gpiod_set_value_cansleep(dev->enable_gpio, 0);
/* Power off */
if (dev->power_gpio)
gpiod_set_value_cansleep(dev->power_gpio, 0);
}
GPIO Information
/* Get GPIO label (from DT or request) */
const char *label = gpiod_get_label(gpio);
/* Check if GPIO is active low */
bool active_low = gpiod_is_active_low(gpio);
/* Get number of GPIOs in array */
int count = gpiod_count(dev, "data");
Debouncing
/* Set hardware debounce (if supported) */
int ret = gpiod_set_debounce(gpio, 1000); /* 1000us */
if (ret == -ENOTSUPP)
/* Use software debounce */
Complete Example
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
struct button_led_device {
struct device *dev;
struct gpio_desc *led_gpio;
struct gpio_desc *button_gpio;
int button_irq;
bool led_state;
};
static irqreturn_t button_irq_handler(int irq, void *data)
{
struct button_led_device *dev = data;
/* Toggle LED */
dev->led_state = !dev->led_state;
gpiod_set_value(dev->led_gpio, dev->led_state);
dev_info(dev->dev, "Button pressed, LED %s\n",
dev->led_state ? "on" : "off");
return IRQ_HANDLED;
}
static int button_led_probe(struct platform_device *pdev)
{
struct button_led_device *dev;
int ret;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->dev = &pdev->dev;
/* Get LED GPIO (output) */
dev->led_gpio = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
if (IS_ERR(dev->led_gpio))
return dev_err_probe(&pdev->dev, PTR_ERR(dev->led_gpio),
"Failed to get LED GPIO\n");
/* Get button GPIO (input) */
dev->button_gpio = devm_gpiod_get(&pdev->dev, "button", GPIOD_IN);
if (IS_ERR(dev->button_gpio))
return dev_err_probe(&pdev->dev, PTR_ERR(dev->button_gpio),
"Failed to get button GPIO\n");
/* Set up button debounce */
gpiod_set_debounce(dev->button_gpio, 5000); /* 5ms */
/* Get IRQ for button */
dev->button_irq = gpiod_to_irq(dev->button_gpio);
if (dev->button_irq < 0)
return dev->button_irq;
/* Request IRQ */
ret = devm_request_irq(&pdev->dev, dev->button_irq,
button_irq_handler,
IRQF_TRIGGER_FALLING,
"button", dev);
if (ret)
return ret;
platform_set_drvdata(pdev, dev);
dev_info(&pdev->dev, "Button-LED device initialized\n");
return 0;
}
static const struct of_device_id button_led_of_match[] = {
{ .compatible = "demo,button-led" },
{ }
};
MODULE_DEVICE_TABLE(of, button_led_of_match);
static struct platform_driver button_led_driver = {
.probe = button_led_probe,
.driver = {
.name = "button-led",
.of_match_table = button_led_of_match,
},
};
module_platform_driver(button_led_driver);
MODULE_LICENSE("GPL");
Device Tree for Example
button_led {
compatible = "demo,button-led";
led-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
button-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
};
Summary
- Use
devm_gpiod_get*()for automatic cleanup _optionalvariants for non-critical GPIOsgpiod_set_value()handles active-low automatically- Use
_cansleepvariants for I2C/SPI GPIO expanders gpiod_to_irq()converts GPIO to interrupt number- Follow proper power/reset sequences with timing
Next
Learn how to write a GPIO provider (gpio_chip).