linux gpio学习笔记(gpiolib)
两种不同的gpio框架:1). descriptor-based interface:基于描述符的接口;2). legacy integer-based interface: 基于整数的接口;1). 基于描述符的gpio在dts中使用:参考:Documentation/gpio/board.txt#include <linux/gpio/consumer.h>foo_device {c
两种不同的gpio框架:
1). descriptor-based interface: 基于描述符的接口, 新框架, 官方推荐;
2). legacy integer-based interface: 基于整数的接口;
1). 基于描述符的gpio在dts中使用:
参考:
Documentation/gpio/board.txt
#include <linux/gpio/consumer.h>
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-gpio = <&gpio 18 GPIO_ACTIVE_LOW>;
};
struct gpio_desc *red, *green, *blue, *power;
red = gpiod_get_index(dev, "led", 0);
green = gpiod_get_index(dev, "led", 1);
blue = gpiod_get_index(dev, "led", 2);
power = gpiod_get(dev, "power");
gpiod_direction_output(red, 1);
gpiod_direction_output(green, 1);
gpiod_direction_output(blue, 1);
gpiod_direction_output(power, 1);
gpiod_put(red); //释放gpio口;
dts中gpio label的写法: name-gpios, 其中name是gpiod_get*()函数里的第二个参数;
The led GPIOs will be active-high, while the power GPIO will be active-low;
gpiod_is_active_low(power);为true;
2). 基于整数的gpio在dts中使用:
device_node {
...
gpio_name = <&tlmm 99 0>;
...
}
int gpio_99 = of_get_named_gpio_flags(dev->of_node, "gpio_name", 0, NULL);
gpio_request(gpio_99, "gpio_name"); //通过gpio号申请gpio
gpio_direction_output(gpio_99, 1); //设置gpio_99输出,初始值为1
gpio_set_value(gpio_99, 0); //设置gpio_99值为0
gpio_free(gpio_99);
gpio_get_value(gpio_99, 0); //获取gpio_99的值
这种方法目前最常用;
3). 基于描述符的gpio在非dts中使用:
//platform device:
#include <linux/gpio/machine.h>
struct gpiod_lookup_table gpios_table = {
.dev_id = "foo.0",
.table = {
GPIO_LOOKUP_IDX("gpio.0", 15, "led", 0, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("gpio.0", 16, "led", 1, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("gpio.0", 17, "led", 2, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP("gpio.0", 1, "power", GPIO_ACTIVE_LOW),
{ },
},
};
gpiod_add_lookup_table(&gpios_table);
//platform driver:
struct gpio_desc *red, *green, *blue, *power;
red = gpiod_get_index(dev, "led", 0);
green = gpiod_get_index(dev, "led", 1);
blue = gpiod_get_index(dev, "led", 2);
power = gpiod_get(dev, "power");
gpiod_direction_output(power, 1);
基于描述符的gpio的api函数:
参考: Documentation/gpio/consumer.txt
获取gpio口:
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);
flags: (可选参数, 可不带)
* GPIOD_ASIS or 0: gpio口没有初始化, gpio方向后续需使用专用函数设置;
* GPIOD_IN: gpio初始化为输入;
* GPIOD_OUT_LOW: gpio口初始化为输出, 且输出低电平;
* GPIOD_OUT_HIGH: gpio口初始化为输出, 且输出高电平;
返回值:
-ENOENT: 没有gpio口分配给device/function/index中三者之一;
other error: gpio口已分配, 但发生了其他的错误;
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)
返回值:
NULL: 代替-ENOENT, 没有gpio口可分配时;
Device-managed variants函数:
struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
struct gpio_desc * devm_gpiod_get_index_optional(struct device *dev,
const char *con_id,
unsigned int index,
enum gpiod_flags flags)
释放gpio:
void gpiod_put(struct gpio_desc *desc);
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
注意: gpio释放后一定不能再使用了;
设置gpio口方向:
如果gpiod_get*()函数没有设置方向, 需调用下面其中一个函数:
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
查询gpio口当前的方向:
int gpiod_get_direction(const struct gpio_desc *desc);
设置和获取gpio的值:
int gpiod_get_value(const struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);
gpio映射为中断:
int gpiod_to_irq(const struct gpio_desc *desc);
Active-low State and Raw GPIO Values
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);
int gpiod_is_active_low(const struct gpio_desc *desc);
新旧框架的相互转换:
gpio与gpio_desc结构体的相互转换:
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //gpiolib.c
gpio_to_desc(unsigned gpio)
&gpio_desc[gpio] //这里的gpio_desc是同名结构体的数组;
int desc_to_gpio(const struct gpio_desc *desc) //gpiolib.c
return desc - &gpio_desc[0];
关键结构体:
1). gpio控制器:
//kernel/msm-3.18/include/linux/gpio/driver.h
struct gpio_chip {
const char *label;
struct device *dev;
struct module *owner;
struct list_head list;
...
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 base; //该gpio控制器控制的第一个gpio编号, 非常重要;
u16 ngpio; //该gpio控制器包含的gpio数量;
struct gpio_desc *desc;
...
}
这个结构体表示一个抽象的gpio控制器, 各soc厂商负责去实现这个结构体:
820p上该结构体实现:
//pinctrl_msm.c
static struct gpio_chip msm_gpio_template = {
.direction_input = msm_gpio_direction_input,
.direction_output = msm_gpio_direction_output,
.get = msm_gpio_get,
.set = msm_gpio_set,
.request = msm_gpio_request,
.free = msm_gpio_free,
.dbg_show = msm_gpio_dbg_show,
};
msm_pinctrl_probe(pdev, soc_data)
pctrl->chip = msm_gpio_template
msm_gpio_init(pctrl)
ngpio = pctrl->soc->ngpios
chip = &pctrl->chip
chip->base = 0
chip->ngpio = ngpio
chip->label = dev_name(pctrl->dev)
gpiochip_add(&pctrl->chip) //注册gpio控制器;
它的gpio_chip驱动是放到pinctrl里写的;
看下这些回调函数在哪里被使用的?
gpio_direction_input(unsigned gpio) //kernel/msm-3.18/include/asm-generic/gpio.h
gpiod_direction_input(gpio_to_desc(gpio)) //gpiolib.c
struct gpio_chip *chip
chip = desc->chip
chip->direction_input(chip, gpio_chip_hwgpio(desc))
gpio_request(unsigned gpio, const char *label) //kernel/msm-3.18/drivers/gpio/gpiolib-legacy.c
gpiod_request(gpio_to_desc(gpio), label) //gpiolib.c
__gpiod_request(desc, label)
struct gpio_chip *chip = desc->chip
if (chip->request)
chip->request(chip, gpio_chip_hwgpio(desc))
即:msm_gpio_request(chip, offset) //pinctrl-msm.c
gpio = chip->base + offset
pinctrl_request_gpio(gpio) //core.c
pinctrl_get_device_gpio_range(gpio, &pctldev, &range)
pin = gpio_to_pin(range, gpio)
pinmux_request_gpio(pctldev, range, pin, gpio)
//不知为什么要获取gpio的方向:
if (chip->get_direction)
gpiod_get_direction(desc)
2). gpio描述符:
//kernel/msm-3.18/drivers/gpio/gpiolib.h
struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2 /* protected by sysfs_lock */
#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL 4 /* trigger on falling edge */
#define FLAG_TRIG_RISE 5 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 6 /* value has active low */
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#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))
const char *label;
};
这个结构体的作用是为了保存gpio口对应的gpio控制器(gpio_chip),
内核定义了一个数组: gpio_desc[ARCH_NR_GPIOS];
每个gpio口都有一个成员, 里面保存了它所对应的gpio_chip;
一个soc里可能有多个gpio_chip, gpio与gpio_chip是一对多的关系;
代码实现:
//gpiolib.c
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //全局数组
gpiochip_add(chip) //注册gpio_chip;
base = chip->base
if (base < 0)
base = gpiochip_find_base(chip->ngpio)
chip->base = base
gpiochip_add_to_list(chip)
//注意:chip里的desc数组下标不是从0开始的, 是从base开始的:
chip->desc = &gpio_desc[chip->base]
for (id = 0; id < chip->ngpio; id++) {
//desc就是全局数组里的每一项:
struct gpio_desc *desc = &chip->desc[id]
//将当前要注册的chip赋值给数组里对应那一项的chip变量:
desc->chip = chip
of_gpiochip_add(chip)
gpiochip_export(chip)
这样就将每个gpio对应的gpio_chip保存起来了;
为什么会有base<0的情况出现呢?
按理来说, soc厂家清楚自家芯片的gpio情况, 给它固定一个base很容易, 为什么不固定呢?
在820p上, 有24个gpio_chip, 但只有第一个chip_chip: “1010000.pinctrl”, 定义了base=0;
其他的base=-1, 就需要动态分配base:
gpiochip_find_base(ngpio) //gpiolib.c
base = ARCH_NR_GPIOS - ngpio
list_for_each_entry_reverse(chip, &gpio_chips, list)
if (chip->base + chip->ngpio <= base)
break;
else
base = chip->base - ngpio
return base;
动态分配base的方法很简单, 从最大值往下分配;
820p上ARCH_NR_GPIOS=1024;
如:
gpio_chip1: 1000-1024
gpio_chip2: 900 -999
gpio_chip3: 825 -899
…
1010000.pinctrl: 0-149 //这个是固定base的;
如果没有分配完, 149后面就会有未被使用的编号;
总结:
1). gpio_chip注册的时候, 将其放入链表, 后面查找时要用到;
2). 要操作gpio时, 先找到它的gpio_chip, 然后才能使用它的回调函数去操作gpio;
3). 找gpio_chip: 遍历gpio_chip链表, 按照规则去匹配; 然后根据gpio的序号找到对应的gpio_desc;
4). 然后再根据gpio_desc找到gpio_chip, 进而调用它的回调函数;
问题: 发现gpio_desc是个多余的东西, 我们可以在链表里找到gpio_chip, 然后调用它的回调函数, 为什么还用使用gpio_desc呢?
答: 猜测是兼容旧框架, 旧框架提供了gpio的整数, 这样可以找到gpio_chip;
问题: 在找gpio_chip的时候, 为什么要到链表里去匹配, 而不是在gpio_desc里根据下标直接找呢?这样不是更方便高效吗?
答: 要想通过gpio_desc直接找到gpio_chip, 必须要直到这个数组的下标,
而下标=base+hwnum, base这个变量保存在gpio_chip里面, 所以行不通;
源码分析:
1). gpiod_get_index() 用于获取gpio_desc:
#define gpiod_get_index(varargs...) __gpiod_get_index(varargs, 0) //consumer.h
__gpiod_get_index(dev, con_id, idx, flags) //gpio_lib.c
struct gpio_desc *desc = NULL
enum gpio_lookup_flags lookupflags = 0
desc = of_find_gpio(dev, con_id, idx, &lookupflags)
gpiod_request(desc, con_id)
return desc
of_find_gpio(dev, con_id, idx, flags) //gpiolib.c
static const char *suffixes[] = { "gpios", "gpio" }
snprintf(prop_name, 32, "%s-%s", con_id, suffixes[i])
desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, &of_flags) //gpiolib-of.c
of_parse_phandle_with_args(np, propname, "#gpio-cells", index, &gg_data.gpiospec)
gpiochip_find(&gg_data, of_gpiochip_find_and_xlate)
return gg_data.out_gpio
gpiochip_find(data, match()) //gpiolib.c
//在链表gpio_chips里面匹配:
list_for_each_entry(chip, &gpio_chips, list)
if (chip && match(chip, data))
break;
return chip;
传入的match函数是:
of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) //gpiolib-of.c
if ((gc->of_node != gg_data->gpiospec.np) ||
(gc->of_gpio_n_cells != gg_data->gpiospec.args_count) ||
(!gc->of_xlate))
return false;
ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags);
if (ret < 0)
return false;
//通过gpio_chip找到gpio_desc:
gg_data->out_gpio = gpiochip_get_desc(gc, ret)
return true;
看下上面的匹配原则:
1). 比较gpio_chip里的变量"of_node"和"of_gpio_n_cells"与提供的是否相等, 且of_xlate不能为空;
2). 执行函数of_xlate()来比较;
但是, 在820p的gpio_chip:1010000.pinctrl里, "of_gpio_n_cells"和"of_xlate"这两个变量没有直接赋值,
而log打印里, 他们都有值, 且对所有的gpio_chip, of_gpio_n_cells都等于2;
系统开机在创建gpio_chip的时候会调用:
of_gpiochip_add(chip) //gpiolib-of.c
if (!chip->of_xlate) {
chip->of_gpio_n_cells = 2;
chip->of_xlate = of_gpio_simple_xlate;
}
2). gpiod_direction_output() 用于设置gpio方向位输出, 且设置输出电压高低;
这个函数最后肯定会设置soc的寄存器, 即设置soc的gpio寄存器;
gpiod_direction_output(desc, value) //gpiolib.c
_gpiod_direction_output_raw(desc, value)
chip = desc->chip
chip->direction_output(chip, gpio_chip_hwgpio(desc), value)
即:msm_gpio_direction_output(chip, offset, value) //pinctrl-msm.c
//设置寄存器:
g = &pctrl->soc->groups[offset]
val = readl(pctrl->regs + g->io_reg)
writel(val, pctrl->regs + g->io_reg)
val = readl(pctrl->regs + g->ctl_reg)
writel(val, pctrl->regs + g->ctl_reg)
gpiod_direction_output(desc, value) //gpiolib.c
//如果是低电平有效, value取反:
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
_gpiod_direction_output_raw(desc, value)
更多推荐
所有评论(0)