Kernel doc: General Purpose Input/Output (GPIO)
GPIO Driver Interface
Controller Drivers: gpio_chip
struct gpio_chip 抽象 gpio controller。
gpiochip_add_data() or devm_gpiochip_add_data()接口用来注册 gpio controller。
gpiochip_remove()释放 gpio controller。
gpiochip_is_request()在 gpio controller driver 中用于检测某个 gpio 是否被其他
chip 占用,没占用返回 NULL,占用返回 request 时传入的 string。
GPIO electrical configuration
gpio_chip 的.set_config回调用于设置:
- Debouncing
- Single-ended modes (open drain/open source)
- Pull up and pull down resistor enablement
这些属性可以在 dts 中指定,include/dt-bindings/gpio/gpio.h GPIO_PUSH_PULL
GPIO_LINE_OPEN_SOURCE GPIO_OPEN_DRAIN …
可以设置为gpiochip_generic_config()会调用到pinctrl_gpio_set_config()->
ops->pin_config_set
GPIO drivers providing IRQs
gpiod_to_irq(); // 传入 gpio_desc,返回 gpio 的 irq number(软件映射的,不是 irq hw id)
gpio_chip_hwgpio();
gc->to_irq();
rts_gpio_to_irq();
irq_linear_revmap();
Cascaded GPIO irqchips
- CHAINED CASCADED GPIO IRQCHIPS:挺多 soc 上是这种做法,
打开
CONFIG_GPIOLIB_IRQCHIP设置 girq->parent_handler。 gpio controller 注册过程中通过irq_set_chained_handler设置中断处理函数, 因此在中断处理函数中需要 chained_irq_enter,chained_irq_exit。 相当于级联中断处理器的做法。
static irqreturn_t foo_gpio_irq(int irq, void *data) /// 中断处理函数
chained_irq_enter(...);
generic_handle_irq(...);
chained_irq_exit(...);
- GENERIC CHAINED GPIO IRQCHIPS:rts3917 是这种做法, 通过reuqest_irq进入的 rts_irq_handler 中断处理函数。 发现的每一个 gpio 都进入 generic_handle_irq, 最后会到各自 irq_desc 中通过 request_irq 的中断处理函数。
static irqreturn_t rts_irq_handler(int irq, void *dev_id)
for each detected GPIO IRQ
generic_handle_irq(...);
- NESTED THREADED GPIO IRQCHIPS:gpio expander 的做法,不深究。
Infrastructure helpers for GPIO irqchips
GPIO 子系统有针对中断的一套框架,Kconfig 为GPIOLIB_IRQCHIP,
rts 没有用到就不分析了。可以看文档中具体的解释。
注意一点,方法一:如果 parent_handler
赋值了girq->parent_handler = ftgpio_gpio_irq_handler
应该就是上面CHAINED CASCADED GPIO IRQCHIPS的做法。
gpiochip_add_data();
gpiochip_add_data_with_key();
gpiochip_add_irqchip();
irq_set_chained_handler_and_data(gc->irq.parents[i], gc->irq.parent_handler, data);
方法二:girq->parent_handler = NULL,直接在 driver 中devm_request_threaded_irq
对应GENERIC CHAINED GPIO IRQCHIPS的做法。
GPIO Descriptor Consumer Interface

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);
// return NULL 如果没有 request 到 GPIO
struct gpio_desc *gpiod_get_optional(struct device *dev, const char *con_id, enum gpiod_flags flags);
GPIO Mappings
device tree
foo_device {
compatible = "acme,foo";
...
led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
<&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
<&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */
power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
};
对应
struct gpio_desc *red, *green, *blue, *power;
red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH);
green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH);
blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH);
power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);
gpiod_set_value 设置的值是“逻辑值”,不一定等于物理值。
Sysfs 接口
/sys/class/gpio/
echo 19 > export
/sys/class/gpio/gpioN/
/sys/kernel/debug/gpio 可以看哪些 gpio 被申请了
Reference
GPIO 推挽和开漏输出:https://zhuanlan.zhihu.com/p/41942876