Legacy GPIO Interface
Kernel API
/* Device-tree helper */
int of_get_gpio(struct device_node *np, int idx);
/* Pin life-cycle */
int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
/* Direction */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
/* Value access */
int gpio_get_value(unsigned gpio);
void gpio_set_value(unsigned gpio, int value);
Device-tree snippet
leds_legacy: user_leds@0 {
compatible = "vendor,leds-legacy";
label = "user_leds";
/* Property must be named "gpios" */
gpios = <&gpioc 3 GPIO_ACTIVE_HIGH>,
<&gpioc 4 GPIO_ACTIVE_HIGH>,
<&gpioc 5 GPIO_ACTIVE_HIGH>;
};
Minimal driver
#define LED_COUNT 3
static int led_gpio[LED_COUNT];
static int legacy_led_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int i, ret;
for (i = 0; i < LED_COUNT; i++) {
led_gpio[i] = of_get_gpio(np, i);
if (led_gpio[i] < 0)
return led_gpio[i];
ret = devm_gpio_request(&pdev->dev, led_gpio[i], "user_led");
if (ret)
return ret;
gpio_direction_output(led_gpio[i], 0);
}
return 0;
}
static const struct of_device_id legacy_led_of_match[] = {
{ .compatible = "vendor,leds-legacy" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, legacy_led_of_match);
static struct platform_driver legacy_led_driver = {
.probe = legacy_led_probe,
.driver = {
.name = "legacy_leds",
.of_match_table = legacy_led_of_match,
},
};
module_platform_driver(legacy_led_driver);
MODULE_LICENSE("GPL");
Modern Descriptor Interface
Kernel API
/* Acquisition */
struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags);
struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id,
unsigned int idx, enum gpiod_flags flags);
/* Managed variants (auto-cleanup) */
struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags);
struct gpio_desc *devm_gpiod_get_index(struct device *dev, const char *con_id,
unsigned int idx, enum gpiod_flags flags);
/* Release (only for non-managed) */
void gpiod_put(struct gpio_desc *desc);
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
/* Direction & value */
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);
Device-tree flexibility
leds_new: user_leds@1 {
compatible = "vendor,leds-new";
label = "user_leds";
/* Any of these names are accepted */
led-gpios = <&gpioc 3 GPIO_ACTIVE_HIGH>,
<&gpioc 4 GPIO_ACTIVE_HIGH>,
<&gpioc 5 GPIO_ACTIVE_HIGH>;
};
Modern driver
#define LED_COUNT 3
static struct gpio_desc *led_desc[LED_COUNT];
static int new_led_probe(struct platform_device *pdev)
{
int i;
for (i = 0; i < LED_COUNT; i++) {
led_desc[i] = devm_gpiod_get_index(&pdev->dev, "led", i,
GPIOD_OUT_LOW);
if (IS_ERR(led_desc[i]))
return PTR_ERR(led_desc[i]);
}
return 0;
}
static const struct of_device_id new_led_of_match[] = {
{ .compatible = "vendor,leds-new" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, new_led_of_match);
static struct platform_driver new_led_driver = {
.probe = new_led_probe,
.driver = {
.name = "new_leds",
.of_match_table = new_led_of_match,
},
};
module_platform_driver(new_led_driver);
MODULE_LICENSE("GPL");
Migratino hints
- Replace
of_get_gpio() → devm_gpiod_get_index()
- Replace
gpio_request()/gpio_free() with managed variants
- Use
struct gpio_desc * instead of raw integers
- Property names in DT can now be
*-gpios, *-gpio, gpios, or gpio