Linux的GPIO子系统解析 ( 一 ) 之 gpiolib.c
绪论
Linux中有非常多的子系统实现,甚至连LED都有相应的子系统,学好内核中的各种驱动相关的子系统,可以让我们写驱动更加高效,不用重复造轮子,尽量利用内核给我们提供的机制减轻工作量同时又达到高效可用。本章不涉及具体的函数代码,只涉及具体的函数的作用,代码需读者自行去查看
关于GPIO子系统库文件的gpiolib.c解析
drivers/gpio/gpiolib.c
gpio_desc结构体
其中涉及一个重要结构体 gpio_desc:
该结构体用来描述一个GPIO 我们主要关注其中包含的gpio_chip结构体
struct gpio_desc {
struct gpio_chip *chip; //每个GPIO对应一个gpio_chip结构体
unsigned long flags; //接下来是这个gpio_desc的一些标志描述的位定义
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0 //标志gpio是否已被请求
#define FLAG_IS_OUT 1 //标志该gpio的默认输出状态
#define FLAG_RESERVED 2 //标志gpio是否被保留 保留的gpio不能被申请
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL 5 /* 下降沿触发*/
#define FLAG_TRIG_RISE 6 /* 上升沿触发 */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
#define ID_SHIFT 16 /* add new flags before this one */
#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //为所有GPIO都定义一个gpio_desc数组
相应的ARCH_NR_GPIOS定义在你的arch/arm/你的架构/include/mach/gpio.h中定义
比如我用的芯片是EXYNOS4412 所以相应的目录就在 arch/arm/mach-exynos/include/mach/gpio.h中
具体整个宏展开后就是板子上最大的GPIO数量了,这里不做展开,有兴趣可以看看该头文件的定义就知道了
gpio_chip结构体
接着我们来看gpio_desc中的一个重要结构 gpio_chip 该结构包含了对gpio的各种操作方法,比如设置gpio的输入输出,设置gpio的输出值或者获取值等 是核心重点
struct gpio_chip {
const char *label; //名字
struct device *dev; //对应的设备结构体
struct module *owner; //所属模块 在驱动中经常出现的字段
int (*request)(struct gpio_chip *chip,
unsigned offset); //申请一个gpio的函数 主要用来防止gpio冲突使用
void (*free)(struct gpio_chip *chip,
unsigned offset);//用完了gpio后要调用free函数释放
int (*direction_input)(struct gpio_chip *chip,
unsigned offset); //设定GPIO为输入
int (*get)(struct gpio_chip *chip,
unsigned offset); //获取gpio的值
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value); //设定gpio为输出
int (*set_debounce)(struct gpio_chip *chip,
unsigned offset, unsigned debounce);//设置去抖动的时间 按键可能会用到
void (*set)(struct gpio_chip *chip,
unsigned offset, int value);//设置gpio的值
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int base; //所管辖的起始GPIO号
u16 ngpio; //所管辖的GPIO数量
const char *const *names;
unsigned can_sleep:1;
unsigned exported:1;
#if defined(CONFIG_OF_GPIO) //这里是为了设备树中定义的GPIO而准备的
/*
* If CONFIG_OF is enabled, then all GPIO controllers described in the
* device tree automatically may have an OF translation
*/
struct device_node *of_node; //对应设备树的GPIO节点
int of_gpio_n_cells; //gpio的个数
int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
const void *gpio_spec, u32 *flags); //定义设备树的信息转换函数
#endif
};
该库中所涉及的函数作用我简单说一下:
gpio_ensure_requested函数
int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset)
该函数用来确保gpio被申请,如果没有申请的话调用该函数会自动去申请,
并且会发出警告,并将GPIO描述符的label字段置为"[auto]"
gpio_to_chip函数
static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
获取相应GPIO的的gpio_chip结构体
gpiochip_find_base函数
static int gpiochip_find_base(int ngpio)
动态的在GPIO的描述符数组中寻找连续的ngpio个空闲GPIO,并返回该连续GPIO的第一个序号
主要是用于给那些拥有可热插拔GPIO的设备
gpiochip_reserve函数
int __init gpiochip_reserve(int start, int ngpio)
该函数用来将指定范围的GPIO设为不可动态分配,为什么要这么做呢?是因为对应的设备还没准备好
所以此时必须把这些IO给禁止掉,如果指定范围内有任何一个GPIO已经被设置为保留,该函数失败返回-EBUSY
还有一些函数只有在开启了 CONFIG_GPIO_SYSFS才会启用
主要是为了定义了一些show,store方法方便在文件系统中查看GPIO的信息状态
这里不解释
下面的所有函数都是导出函数 可被外部模块所使用 也就是提供给外部模块的接口
导出函数
gpiochip_add函数
int gpiochip_add(struct gpio_chip *chip)
注册一个gpio_chip,注册失败返回负数,成功返回0
该函数在引导过程中很早就被调用,以便GPIO能够自由使用
chip->dev必须在GPIO框架的arch_initcall()前注册好,否则sysfs的初始化会因为GPIO而失败
该函数实际就是根据chip所管辖的GPIO范围,将gpio描述数组中的相关gpio的chip字段设为该chip
接着将chip->ofnode设置为chip->dev->of_node 最后调用gpiochip_export将芯片信息导出到文件系统中
这一步只有有开SYSFS才会做 这里不分析 后面独立来分析
gpiochip_remove函数
int gpiochip_remove(struct gpio_chip *chip)
注销指定的gpio_chip 会同时减少对应ofnode的引用计数,实际就是把所管辖的GPIO的chip字段全部设为NULL
同时取消对SYSFS的导出
gpiochip_find函数
struct gpio_chip *gpiochip_find(void *data,
int (*match)(struct gpio_chip *chip, void *data))
用于定位特定的gpio_chip 用户需要自己构造一个match函数 之后gpiochip_find会对每一个gpio_chip调用该函数
成功匹配后会返回该chip
gpio_request函数
int gpio_request(unsigned gpio, const char *label)
请求注册一个GPIO,并设置gpio_desc的label和FLAG_REQUESETED位,最后再调用chip->request
gpio_request_one函数
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
该函数请求一个GPIO,并对其进行默认的GPIO方向设置
其实就是调用gpio_request再根据方向调用gpio_direction_xxxx
gpio_request_array函数
int gpio_request_array(const struct gpio *array, size_t num)
该函数为以下gpio数组循环调用gpio_request_one请求每个gpio
struct gpio {
unsigned gpio;
unsigned long flags;
const char *label;
};
gpio_free_array函数
void gpio_free_array(const struct gpio *array, size_t num)
为指定的gpio数组循环调用gpio_free
gpiochip_is_requested函数
const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
返回指定chip管辖的第offset个GPIO的label
gpio_direction_input / gpio_direction_output函数
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
最终通过对应的chip->direction_input/output来完成实际操作 如果对应gpio没有被request 也会调用chip->request
gpio_set_debounce函数
int gpio_set_debounce(unsigned gpio, unsigned debounce)
做一些参数的检查工作,最终调用chip->set_debounce(chip, gpio, debounce)
__gpio_get_value / __gpio_set_value 函数
int __gpio_get_value(unsigned gpio)
void __gpio_set_value(unsigned gpio, int value)
调用chip->get/set
__gpio_cansleep 函数
int __gpio_cansleep(unsigned gpio)
返回chip->can_sleep
__gpio_to_irq函数
int __gpio_to_irq(unsigned gpio)
返回gpio对应的中断irq数量
gpio_get_value_cansleep / gpio_set_value_cansleep函数
int gpio_get_value_cansleep(unsigned gpio)
void gpio_set_value_cansleep(unsigned gpio, int value)
调用后可能会发生休眠 取决于内核是否开启了CONFIG_PREEMPT_VOLUNTARY
最终会调用chip_get/set
如何去使用该GPIO库?
gpiolib库可以看出依赖于gpio_chip来提供最终的gpio操作,所以我们如果要用这个gpiolib,就要定义各chip,然后调用gpiochip_add来注册 之后就可以使用该gpiolib提供的gpio的操作函数了
一般如果linux支持你的板子的芯片的话,会有一个你的芯片专用的.c文件 在里面就会定义一系列的gpiochip 然后会调用gpio_add去注册
比如我的板子是exynos4412 在driver/gpio/gpio-samsung.c中就会有这些gpiochip的定义和注册。但是如果我们的芯片没有支持的话,你也可以自己定义gpiochip使得gpiolib库被支持。
我们的驱动程序要使用该库 需 #include <mach/gpio.h> 使用该库操作gpio,可省去我们繁琐的io映射及io配置 假设我们要使用该库在驱动中,我们可以这么做:
1. 添加头文件 #include <mach/gpio.h>
2. 调用gpio_direction_input / gpio_direction_input
3. 使用__gpio_get_value / __gpio_set_value / gpio_get_value_cansleep / gpio_set_value_cansleep 等函数操作gpio
读者可能好奇,难道我不用ioremap去映射寄存器就可以直接操作了吗?是的,其实kernel在初始化时已经对IO寄存器有了映射,而我们的gpiolib.c正是直接使用了这些已经映射好的地址,省去了我们自己去映射的那些琐碎麻烦。
GPIO库的流程示意图如下:
下一章我们将来解析gpio-samsung.c中是如何定义gpiochip 以及如何完成整个注册过程的!