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 {
	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)
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值