GPIO子系统架构分析(一)
GPIO子系统架构分析(二)
通过sysf操作gpio
gpio lib
gpio lib(drivers\gpio\gpiolib.c)中提供了一组不依赖于硬件平台的接口函数,理解其中的主要函数非常关键。gpio lib提供的主要函数如下。
(1) gpio的设置与gpio值的获取
void gpiod_set_value(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);
(2) 设置gpio方向的接口
int gpiod_direction_output(struct gpio_desc *desc, int value);
int gpiod_direction_input(struct gpio_desc *desc);
(3) gpio的申请与释放
int gpiod_request(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc);
(4) gpio控制器驱动的注册与注销
int gpiochip_add(struct gpio_chip *chip);
void gpiochip_remove(struct gpio_chip *chip);
gpio控制器驱动
对于用户而言,只需知道要操作的gpio引脚对应的编号,调用gpio lib提供的函数如gpiod_set_value等,即可操作gpio引脚。gpio_chip,表示一个gpio控制器,里面含有各种成员函数,用于操作gpio引脚。gpio lib提供的函数最终会调用到gpio_chip里面的函数来操作gpio引脚。
gpio控制器驱动的主要工作就是,设置好gpio_chip,然后调用gpio lib提供的gpiochip_add函数进行注册。
下面就来分析gpiochip_add函数,定义如下:
static inline int gpiochip_add(struct gpio_chip *chip)
{
return gpiochip_add_data(chip, NULL);
}
int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
unsigned long flags;
int status = 0;
unsigned i;
int base = chip->base;
struct gpio_device *gdev;
//申请一个gpio_device
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
if (!gdev)
return -ENOMEM;
//设置gpio_device
gdev->dev.bus = &gpio_bus_type;
gdev->chip = chip;
chip->gpiodev = gdev;
......
//根据gpio控制器的gpio个数,分配n个gpio_desc
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
if (!gdev->descs) {
status = -ENOMEM;
goto err_free_gdev;
}
......
gdev->ngpio = chip->ngpio;
gdev->data = data;
spin_lock_irqsave(&gpio_lock, flags);
/* gpio控制器的起始gpio号base可以由驱动自身设置,gpio号范围为:[base, base + gdev->ngpio)
这个范围不能与系统中的gpio号有所重复,如果不能确定base的话,将gpio_chip->base设为-1
由系统来选择一个合适的base,这个合适的base通过遍历gpio_devices链表计算得到
*/
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
......
chip->base = base;
}
gdev->base = base;
//将gpio_device插入全局链表gpio_devices
status = gpiodev_add_to_list(gdev);
......
spin_unlock_irqrestore(&gpio_lock, flags);
......
/* 在支持设备树的内核中,驱动想使用gpio,可以在设备节点描述需要使用的gpio
如在设备节点里描述属性gpios = <&gpio4 14 GPIO_ACTIVE_LOW>;
这个属性怎么解析得到对应的gpio_desc呢?由gpio_chip->of_xlate函数进行解析
如果未提供of_xlate函数,默认设置为of_gpio_simple_xlate
*/
status = of_gpiochip_add(chip);
......
//字符设备相关设置
status = gpiochip_setup_dev(gdev);
......
}
gpiochip_setup_dev:
static int gpiochip_setup_dev(struct gpio_device *gdev)
{
int status;
//初始化字符设备,file_operations设置为gpio_fileops
cdev_init(&gdev->chrdev, &gpio_fileops);
gdev->chrdev.owner = THIS_MODULE;
gdev->chrdev.kobj.parent = &gdev->dev.kobj;
gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
//注册字符设备
status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
......
status = device_add(&gdev->dev);
......
}
驱动程序中怎么使用gpio?
前面提到过,驱动程序可以在其设备节点里描述需要使用的gpio,之后中调用相关函数就可以获得对应的gpio_desc了。
现在主流的驱动程序,都是在设备树中,通过设备节点,描述硬件相关信息,在驱动程序中,读取设备节点的信息,注册驱动程序。要想通过设备树来使用gpio资源,其gpio控制器驱动程序要以这种方式来编写。
gpio控制器的设备节点必须有两项属性,gpio-controller和#gpio-cells,如下图:
gpio-controller属性,表明这是一个gpio控制器。#gpio-cells属性的值为n,表示用n项信息描述一个gpio引脚。
要描述一个gpio引脚,基本的信息是要知道这个gpio引脚接在哪个控制器上,控制器上的几号引脚。
说了那么多,还不如来个例子,如下图的设备节点,描述了两个gpio引脚:
cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;
/* &gpio4,引用了gpio4控制器设备节点,表示gpio接在gpio4控制器上,26一般表示引脚是在
gpio4这组引脚的第26号引脚,GPIO_ACTIVE_LOW表示引脚低电平有效
可以判断gpio4控制器设备节点的#gpio-cells = <2>,如果#gpio-cells = <1>
那么cs-gpios属性要变成:cs-gpios = <&gpio4 26>, <&gpio4 24>;
*/
//具体参考Documentation/devicetree/bindings/gpio/gpio.txt
下面就来分析在设备节点描述的gpio引脚信息如何解析成gpio号的吧。
驱动程序可以调用of_get_named_gpiod_flags函数获取gpio_desc ,函数定义如下:
struct of_phandle_args {
struct device_node *np;
int args_count;
uint32_t args[MAX_PHANDLE_ARGS];
};
/*参数说明:
np,设备节点
list_name,属性名,如cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW> ...; 则传入"cs-gpios"
index,获取哪个gpio号,如cs-gpios描述了多个gpio,0表示获取第一个,1表示...
*/
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *propname, int index, enum of_gpio_flags *flags)
{
struct of_phandle_args gpiospec;
struct gpio_chip *chip;
struct gpio_desc *desc;
int ret;
/* 如,解析<&gpio4 26 GPIO_ACTIVE_LOW>,解析结果就是:
gpio4控制器设备节点的device_node存入gpiospec.np,
gpiospec.args_count = 2,
gpiospec.args[0] = <26>,
gpiospec.args[1] = <GPIO_ACTIVE_LOW>
*/
ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
&gpiospec);
......
/* 遍历全局链表gpio_devices,调用of_gpiochip_match_node_and_xlate函数
主要是通过gpio4控制器设备节点的device_node找到对应的gpio_chip
*/
chip = of_find_gpiochip_by_xlate(&gpiospec);
......
//调用gpio_chip->of_xlate解析参数,默认的of_xlate函数为of_gpio_simple_xlate
desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
......
//返回gpio_desc
return desc;
}
of_xlate_and_get_gpiod_flags:
static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
struct of_phandle_args *gpiospec,
enum of_gpio_flags *flags)
{
int ret;
......
/* 调用gpio_chip->of_xlate,返回该gpio引脚在gpio控制器的offset
如gpio4_26,那么返回26
*/
ret = chip->of_xlate(chip, gpiospec, flags);
......
//通过offset在gpio控制器内找到对应的gpio_desc
return gpiochip_get_desc(chip, ret);
}
//默认的解析函数
int of_gpio_simple_xlate(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags)
{
......
if (flags)
*flags = gpiospec->args[1];
//即第一个参数,如<&gpio4 26 GPIO_ACTIVE_LOW>,返回26
return gpiospec->args[0];
}
//通过offset在gpio控制器内找到对应的gpio_desc
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
u16 hwnum)
{
struct gpio_device *gdev = chip->gpiodev;
if (hwnum >= gdev->ngpio)
return ERR_PTR(-EINVAL);
return &gdev->descs[hwnum];
}
在获取到gpio_desc后,并不是马上就可以使用了,使用之前需要申请,调用gpiod_request函数进行申请。