Linux内核中的GPIO驱动

时间:2021-07-06 10:35:58

本文参考linux官方文档https://www.kernel.org/doc/Documentation/gpio/consumer.txt进行编写。

头文件

需要首先包含头文件

#include <linux/gpio/consumer.h>

获取&释放

使用一下两个函数获取GPIO设备,多个设备时需要附带index参数。函数返回一个GPIO描述符,或一个错误编码,可以使用IS_ERR()进行检查:

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)

或者也可以使用如下两个函数获取可用设备:

struct gpio_desc *gpiod_get_optional(struct device *dev,
                         const char *con_id,
                         enum gpiod_flags flags)

struct gpio_desc *gpiod_get_index_optional(struct device *dev,
                           const char *con_id,
                           unsigned int index,
                           enum gpiod_flags flags)

使用如下函数同时获取多个设备:

struct gpio_descs *gpiod_get_array(struct device *dev,
                       const char *con_id,
                       enum gpiod_flags flags)

该函数返回一个GPIO描述序列:

struct gpio_descs {
    unsigned int ndescs;
    struct gpio_desc *desc[];
}

一个GPIO描述符可以使用如下函数释放:

void gpiod_put(struct gpio_desc *desc)
void gpiod_put_array(struct gpio_descs *descs)

需要注意GPIO描述符被释放后不可再使用,而且不允许使用第一个函数来释放通过序列获取得到GPIO描述符。

使用GPIO

  1. 设置方向(Input | Output)
    使用如下函数设置一个设备的方向

    int gpiod_direction_input(struct gpio_desc *desc)
    int gpiod_direction_output(struct gpio_desc *desc, int value)

    使用如下函数检查一个设备的方向:
    int gpiod_get_direction(const struct gpio_desc *desc)

    函数返回GPIOF_DIR_IN或者GPIOF_DIR_OUT
  2. 访问
    访问分为两种,一种是通过储存器读写实现的,这种操作属于原子操作,不需要等待,所以可以在中断处理程序中使用:

    int gpiod_get_value(const struct gpio_desc *desc);
    void gpiod_set_value(struct gpio_desc *desc, int value);
    

    还有一种访问必须通过消息总线比如I2C或者SPI,这种访问需要在总线访问队列中等待,所以可能进入睡眠,此类访问不能出现在IRQ handler。
    可以使用如下函数分辨这些设备:

    int gpiod_cansleep(const struct gpio_desc *desc)

    使用如下函数读写:

    int gpiod_get_value_cansleep(const struct gpio_desc *desc)
    void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
  3. active-low & raw value
    有些设备采用低电平有效的方式输出逻辑信号。此时低电平输出1,高电平输出0。此时可以通过访问raw_value的方式来访问实际电路上的值,与逻辑处理无关:

    int gpiod_get_raw_value(const struct gpio_desc *desc)
    void gpiod_set_raw_value(struct gpio_desc *desc, int value)
    int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
    void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
    int gpiod_direction_output_raw(struct gpio_desc *desc, int value)

    逻辑关系汇总如下:
    Function (example) active-low property physical line
    gpiod_set_raw_value(desc, 0); don’t care low
    gpiod_set_raw_value(desc, 1); don’t care high
    gpiod_set_value(desc, 0); default (active-high) low
    gpiod_set_value(desc, 1); default (active-high) high
    gpiod_set_value(desc, 0); active-low high
    gpiod_set_value(desc, 1); active-low low

    可以使用如下函数判断一个设备是否是低电平有效的设备。

    int gpiod_is_active_low(const struct gpio_desc *desc)
  4. 设置多个输出
    使用如下函数设置一组设备的输出值

    void gpiod_set_array_value(unsigned int array_size,
    struct gpio_desc **desc_array,
    int *value_array)
    void gpiod_set_raw_array_value(unsigned int array_size,
    struct gpio_desc **desc_array,
    int *value_array)
    void gpiod_set_array_value_cansleep(unsigned int array_size,
    struct gpio_desc **desc_array,
    int *value_array)
    void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
    struct gpio_desc **desc_array,
    int *value_array)

    以上函数均可配套gpiod_get_array()使用
  5. 映射到IRQ
    使用如下函数获取一个GPIO设备对应的IRQ中断号

    int gpiod_to_irq(const struct gpio_desc *desc)

    返回值时一个IRQ number,或者一个负数的错误代码。得到的中断号可以传递给函数request_irq(),free_irq().
  6. ACPI
    在ACPI系统上,GPIO设备在GpioIo()/GpioInt()从_CRS中获得的资源列表中被描述,这些资源不能在系统GPIO中被使用。
  7. 兼容旧版本
    旧的GPIO系统使用基于标号的结构而不是基于描述符。可以使用如下两个函数进行相互转换:

    int desc_to_gpio(const struct gpio_desc *desc)
    struct gpio_desc *gpio_to_desc(unsigned gpio)

    注意不能使用一套API的方法释放另一套API获取的设备