20.6 GPIO驱动
在drivers/gpio下实现了通用的基于gpiolib的GPIO驱动,其中定义了一个通用的用于描述底层GPIO控制器的gpio_chip结构体,并要求具体的SoC实现gpio_chip结构体的成员函数,最后通过gpiochip_add()注册gpio_chip。GPIO驱动可以存在于drivers/gpio目录中,但是在GPIO兼有多种功能且需要复杂配置的情况下,GPIO的驱动部分往往直接移到drivers/pinctrl目录下并连同pinmux一起实现,而不存在于drivers/gpio目录中。
GPIO控制器的gpio_chip结构体封装了底层硬件的GPIO enable()/disable()等操作,其定义如代码清单20.15所示。
代码清单20.15 描述GPIO控制器的gpio_chip结构体
include/linux/gpio/driver.h
struct gpio_chip {
const char *label;
struct gpio_device *gpiodev;
struct device *parent;
struct module *owner;
int (*request)(struct gpio_chip *chip, unsigned offset);
void (*free)(struct gpio_chip *chip, unsigned offset);
int (*get_direction)(struct gpio_chip *chip, unsigned offset);
int (*direction_input)(struct gpio_chip *chip, unsigned offset);
int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
int (*get)(struct gpio_chip *chip, unsigned offset);
int (*get_multiple)(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits);
void (*set)(struct gpio_chip *chip, unsigned offset, int value);
void (*set_multiple)(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits);
int (*set_config)(struct gpio_chip *chip, unsigned offset, unsigned long config);
int (*to_irq)(struct gpio_chip *chip, unsigned offset);
void (*dbg_show)(struct seq_file *s, struct gpio_chip *chip);
int base;
u16 ngpio;
const char *const *names;
bool can_sleep;
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
unsigned long (*read_reg)(void __iomem *reg);
void (*write_reg)(void __iomem *reg, unsigned long data);
bool be_bits;
void __iomem *reg_dat;
void __iomem *reg_set;
void __iomem *reg_clr;
void __iomem *reg_dir;
int bgpio_bits;
spinlock_t bgpio_lock;
unsigned long bgpio_data;
unsigned long bgpio_dir;
#endif
#ifdef CONFIG_GPIOLIB_IRQCHIP
/*
* With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
* to handle IRQs for most practical cases.
*/
/**
* @irq:
*
* Integrates interrupt chip functionality with the GPIO chip. Can be
* used to handle IRQs for most practical cases.
*/
struct gpio_irq_chip irq;
#endif
#if defined(CONFIG_OF_GPIO)
/*
* If CONFIG_OF is enabled, then all GPIO controllers described in the
* device tree automatically may have an OF translation
*/
/**
* @of_node:
*
* Pointer to a device tree node representing this GPIO controller.
*/
struct device_node *of_node;
/**
* @of_gpio_n_cells:
*
* Number of cells used to form the GPIO specifier.
*/
unsigned int of_gpio_n_cells;
/**
* @of_xlate:
*
* Callback to translate a device tree GPIO specifier into a chip-
* relative GPIO number and flags.
*/
int (*of_xlate)(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
通过这层封装,每个具体的要用到GPIO的设备驱动都使用通用的GPIO API来操作GPIO,这些API主要用于GPIO的申请、释放和设置:
asm-generic/gpio.h
int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
int gpio_set_debounce(unsigned gpio, unsigned debounce);
int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
int gpio_request_array(const struct gpio *array, size_t num);
void gpio_free_array(const struct gpio *array, size_t num);
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label);
void devm_gpio_free(struct device *dev, unsigned int gpio);
注意:
内核中针对内存、IRQ、时钟、GPIO、pinctrl、Regulator都有以devm_开头的API,使用这部分API的时候,内核会有类似于Java的资源自动回收机制,在代码中进行出错处理时,无须释放相关的资源。
对于GPIO,内核会创建/sys节点/sys/class/gpio/gpioN/,通过这个节点可以echo值来改变GPIO的方向、设置并获取GPIO的值。
在拥有设备树支持的情况下,可以通过设备树来描述某GPIO控制器提供的GPIO引脚被具体设备使用的情况。在GPIO控制器对应的节点中,需定义#gpio-cells和gpio-controller属性,具体的设备节点则通过xxx-gpios属性来引用GPIO控制器节点及GPIO引脚。
如VEXPRESS电路板DT文件arch/arm/boot/dts/vexpress-v2m.dtsi中有如下GPIO控制器节点:
v2m_sysreg: sysreg@00000 {
compatible = "arm,vexpress-sysreg";
reg = <0x00000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
};
VEXPRESS电路板上的MMC控制器会使用该节点GPIO控制器提供的GPIO引脚,则具体的mmci@05000设备节点会通过-gpios属性引用GPIO:
mmci@05000 {
compatible = "arm,pl180", "arm,primecell";
reg = <0x05000 0x1000>;
interrupts = <9 10>;
cd-gpios = <&v2m_sysreg 0 0>;
wp-gpios = <&v2m_sysreg 1 0>;
…
};
备注:
其中cd-gpios用于SD/MMC卡的探测,而wp-gpios用于写保护,MMC主机控制器驱动会通过如下方法获取这两个GPIO,见于drivers/mmc/host/mmci.c:
static void mmci_dt_populate_generic_pdata(struct device_node *np, struct mmci_platform_data *pdata)
{
...
pdata->gpio_wp = of_get_named_gpio(np, "wp-gpios", 0);
pdata->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
...
}