Linux gpiolib简要分析

一、怎么用?

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/leds.h>
#include <asm/io.h>
#include <plat/map-base.h>
#include <plat/map-s5p.h>

#include <linux/gpio.h>

//使用静态的虚拟地址
#define GPL2CON	((volatile unsigned int *)(S5P_VA_GPIO2 + 0x100))
#define GPL2DAT ((volatile unsigned int *)(S5P_VA_GPIO2 + 0x104))

struct led_classdev led_dev1;
struct led_classdev led_dev2;

static void exynos_led_set(struct led_classdev *led_cdev,
			    enum led_brightness value)
{
	//writel(0x11111111, GPL2CON);

	printk(KERN_INFO "exynos_led_set successful!\n");
	if (value == LED_OFF)
	{
		//writel(0x11111111, GPL2CON);
		//writel((0<<0)|(0<<1), GPL2DAT);
		//writel(readl(GPL2DAT) | (1<<0), GPL2DAT);
		gpio_set_value(EXYNOS4_GPL2(0), 0);
	}
	else
	{
		//writel(readl(GPL2DAT) & ~(1<<0), GPL2DAT);
		gpio_set_value(EXYNOS4_GPL2(0), 1);
	}
	
}


static int __init exynos_led_driver_init(void)
{
	int ret = -1;
	
	ret = gpio_request_one(EXYNOS4_GPL2(0), GPIOF_OUT_INIT_LOW,
				"led_driver1");
	if (ret) {
		printk(KERN_ERR "gpio_request_one ERROR!\n");
		return ret;
	}
----------------------------------------------------------------------------
----------------------------------------------------------------------------
//LED框架部分
	led_dev1.name = "led_driver1";
	led_dev1.brightness = 0;
	led_dev1.brightness_set = exynos_led_set;
	
	ret = led_classdev_register(NULL, &led_dev1);
	if (ret < 0) {
		printk(KERN_ERR "led_classdev_register ERROR!\n");
		return ret;
	}
	
	led_dev2.name = "led_driver2";
	led_dev2.brightness = 0;
	led_dev2.brightness_set = exynos_led_set;
	
	ret = led_classdev_register(NULL, &led_dev2);
	if (ret < 0) {
		printk(KERN_ERR "led_classdev_register ERROR!\n");
		return ret;
	}
	
	gpio_set_value(EXYNOS4_GPL2(0), 0);
	return 0;
}

static void __exit exynos_led_driver_exit(void)
{
	gpio_set_value(EXYNOS4_GPL2(0), 0);
	led_classdev_unregister(&led_dev1);
	led_classdev_unregister(&led_dev2);
	gpio_free(EXYNOS4_GPL2(0));
}


module_init(exynos_led_driver_init);
module_exit(exynos_led_driver_exit);

MODULE_AUTHOR("zhou <549963118@qq.com>");
MODULE_DESCRIPTION("LED driver");
MODULE_LICENSE("GPL");

1、跟gpiolib相关的函数
gpio_request_one向内核申请你需要用到的GPIO,正如你要去旅馆住,你先得在app上预定一间房间一样。假如房间被人提前预定了,那你就不能下订。
gpio_set_value将数值写入端口对应的gpio寄存器。

2、用法
(1)申请你需要用到的GPIO,gpio口的定义在\arch\arm\mach-exynos\include\mach\gpio-exynos4.h
(2)读写操作

二、第一部分

1、开机时候,自动启动了哪些函数?

(1)iTop4412_Kernel_3.0\drivers\gpio\gpio-exynos4.c

static __init int exynos4_gpiolib_init(void)
{
	struct s3c_gpio_chip *chip;
	int i;
	int nr_chips;

	/* GPIO common part  */

	chip = exynos4_gpio_common_4bit;
	nr_chips = ARRAY_SIZE(exynos4_gpio_common_4bit);

	for (i = 0; i < nr_chips; i++, chip++) {
		if (chip->config == NULL)
			chip->config = &gpio_cfg;
		if (chip->base == NULL)
			pr_err("No allocation of base address for [common gpio]");
	}

	samsung_gpiolib_add_4bit_chips(exynos4_gpio_common_4bit, nr_chips);

	....//省略的是跟4412无关的代码
	return 0;
}
core_initcall(exynos4_gpiolib_init);

(2)iTop4412_Kernel_3.0\drivers\gpio\gpiolib.c

static int __init gpiolib_sysfs_init(void)
{
...
}
postcore_initcall(gpiolib_sysfs_init);

从这里可知,gpiolib启动分成两步走。

#define core_initcall(fn)               __define_initcall("1",fn,1) 
#define postcore_initcall(fn)           __define_initcall("2",fn,2)  

接着,从启动的顺序来看,exynos4_gpiolib_init最先执行,所以我们先分析这个函数。


2、s3c_gpio_chip结构体

这结构体在exynos4_gpiolib_init中被使用。
主要作用:用来描述一个端口(GPIOA,GPIOB…)

struct s3c_gpio_chip {
	struct gpio_chip	chip; //用来记录gpio的属性,gpio配置的方法,稍后分析gpio_chip结构体
	struct s3c_gpio_cfg	*config;//设置gpio的配置,上下拉
	struct s3c_gpio_pm	*pm;	//电源管理
	void __iomem		*base;	//GPIO端口寄存器组对应的基地址(虚拟地址)
	int			irq_base;
	int			group;
	unsigned int		eint_offset;
	spinlock_t		lock;
#ifdef CONFIG_PM
	u32			pm_save[4];
#endif
};

定义如下:static struct s3c_gpio_chip exynos4_gpio_common_4bit[]
//截取一小部分来分析
{
.base = S5P_VA_GPIO1,
.eint_offset = 0x0,
.group = 0,
.chip = {
.base = EXYNOS4_GPA0(0), //IO口编号的基准编号
.ngpio = EXYNOS4_GPIO_A0_NR, //这个端口IO的个数
.label = “GPA0”, //端口名字
},

它的IO口编号的基准编号如何计算???(往内追溯)
\arch\arm\mach-exynos\include\mach
#define EXYNOS4_GPA1(_nr) (EXYNOS4_GPIO_A1_START + (_nr))
↓↓
#define EXYNOS4_GPIO_A0_NR (8)
#define EXYNOS4_GPIO_A1_NR (6)
↓↓
#define EXYNOS4_GPIO_NEXT(__gpio)
((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
//CONFIG_S3C_GPIO_SPACE的值为0,在CONFIG开头的宏在.config文件中定义
↓↓
解析EXYNOS4_GPIO_A1:
((EXYNOS4_GPIO_A0_START) + (EXYNOS4_GPIO_A0_NR) + CONFIG_S3C_GPIO_SPACE + 1)
= 0 + 8 + 0 + 1
= 9

后面加了1,说明io的编号不连续,空了一个出来,用作隔离。(如图)

有些开发板的定义没有下面这三行,即没有指定GPIO端口的静态虚拟基地址,可能厂家希望我们用动态的方法映射
{
//.base = (S5P_VA_GPIO1 + 0x120),
//.eint_offset = 0x24,
//.group = 13,
.chip = {
.base = EXYNOS4210_GPE2(0),
.ngpio = EXYNOS4210_GPIO_E2_NR,
.label = “GPE2”,
},


3、gpio_chip结构体

这个结构体被s3c_gpio_chip继承,有必要好好看看它的内部。

struct gpio_chip {
	const char		*label;
	struct device		*dev;
	struct module		*owner;

	int			(*request)(struct gpio_chip *chip,
						unsigned offset);
	void			(*free)(struct gpio_chip *chip,
						unsigned offset);

	int			(*direction_input)(struct gpio_chip *chip,
						unsigned offset);
	int			(*get)(struct gpio_chip *chip,
						unsigned offset);
	int			(*direction_output)(struct gpio_chip *chip,
						unsigned offset, int value);
	int			(*set_debounce)(struct gpio_chip *chip,
						unsigned offset, unsigned debounce);

	void			(*set)(struct gpio_chip *chip,
						unsigned offset, int value);

	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;
	unsigned		can_sleep:1;
	unsigned		exported:1;

#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
	 */
	struct device_node *of_node;
	int of_gpio_n_cells;
	int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
		        const void *gpio_spec, u32 *flags);
#endif
}

这一大堆代码,其实是用来描述gpio(及管脚),但是一个端口有这么多管脚,怎么仅用一个结构体就把全部的gpio描述清楚呢?

方法很简单。

  • label记录GPIO端口的名字,注意是端口!!!
  • base记录的是 端口里的io口基准编号,如1 2 3 4 5 67…;GPA0的io基准编号是1, GPIOA1的io基准编号是8(注意,这里的base和s3c_gpio_chip的base是不一样)。
  • ngpio记录端口有多少个gpio。
  • 我们还看到一堆的函数指针,设计的思想跟面向对象一样。
  • 内核中,每个GPIO口(所有端口的GPIO)都有一个数字编号,最终形式 字符串(端口名)+数字的标识(IO口编号)。(如图)

4、再次回到exynos4_gpiolib_init

	chip = exynos4_gpio_common_4bit;
	nr_chips = ARRAY_SIZE(exynos4_gpio_common_4bit);

	for (i = 0; i < nr_chips; i++, chip++) {
		if (chip->config == NULL)
			chip->config = &gpio_cfg;
		if (chip->base == NULL)
			pr_err("No allocation of base address for [common gpio]");
	}

	samsung_gpiolib_add_4bit_chips(exynos4_gpio_common_4bit, nr_chips);

我们重点关注这一段代码。
在此提出一个疑问。samsung_gpiolib_add_4bit_chips为什么是4bit?因为一个IO用4位来描述。可以在芯片手册上找到。

  • 关于samsung_gpiolib_add_4bit_chips。它的作用是初始化s3c_gpio_chip结构体。
  • 追溯samsung_gpiolib_add_4bit_chips
samsung_gpiolib_add_4bit_chips		----------------\drivers\gpio\gpio-plat-samsung.c
	samsung_gpiolib_add_4bit
		samsung_gpiolib_4bit_input		//配置输入输出模式,传入struct gpio_chip *chip和unsigned int offset
					获取外层结构体的地址	to_s3c_gpio
					获取端口的基地址	void __iomem *base = ourchip->base;
					读改写
						con = __raw_readl(regcon);
						con &= ~(0xf << con_4bit_shift(offset));
							#define con_4bit_shift(__off) ((__off) * 4)	//4位描述一个IO口
						__raw_writel(con, regcon);
		samsung_gpiolib_4bit_output		
		
	s3c_gpiolib_add		------\drivers\gpio\gpio.c,244xx 这种两bit CON类型的操作方法
		s3c_gpiolib_input		
		s3c_gpiolib_output
		s3c_gpiolib_set
		s3c_gpiolib_get
		gpiochip_add//这函数作用是:将s3c_gpio_chip的chip的内容交给gpiolib框架里的gpio_desc,这里,相当于把gpio_desc初始化了。gpio_desc的作用下面提到。
			gpiochip_export	
				device_create	//在sys\class\gpio下创建相应gpio管脚的目录,目录名字事 gpiochip%d

关于gpiochip_export函数,里面有一段注释:很多系统很早就注册了支持SOC的gpio芯片,在驱动模型支持可用之前。在这些情况下,我们稍后将其导出到gpiolib_sysfs_init()中。在这里我们只验证gpio_class的_some_字段已经初始化。

这也意味着,虽然我们注册了,但是gpiolib_sysfs_init还没被调用,文件系统还没搞出来,暂时无法在sys\class\gpio整出一个目录,目前甚至连sys\class\gpio的目录也没有。
最终还是要在gpiolib_sysfs_init中完成全部的目录创建。


5、总结执行exynos4_gpiolib_init干了什么事

(1)将GPIO端口抽象出来,并且初始化各个端口和端口的gpio。
(2)注册gpio,暂时未能在文件系统中显示出来,等gpiolib_sysfs_init调用后才出现。




二、第二部分

1、开启gpiolib的sysfs功能

gpiolib为什么需要一个文件系统?因为这是linux设计的思想,同时,我们只需对文件操作,就能对寄存器进行读写。gpiolib的表现出来就是一个文件夹,里面保存了gpio,每一个gpio都是独立的。

可能你有点迷糊,端口和gpio有啥区别?端口是gpio的集合,比如GPIOA端口,一个端口可以有8个gpio。那什么gpio呢?不就是芯片上那一小根管脚嘛,这个我们可以肉眼可见。

通过make menuconfig,可以使能gpiolib的sysfs。
Device Drivers —> GPIO Support —> /sys/class/gpio/…(sysfs interface)

在linux源码根目录下,打开.config, 我们可以看到相应的宏被设置了。
在这里插入图片描述
在/driver/gpiolib.c中, 有相当多的 #ifdef CONFIG_GPIO_SYSFS的宏,通过以上的操作,我们就能正常使用gpiolib的函数。
在这里插入图片描述


2、开机自动执行gpiolib初始化的函数

这个函数是在exynos4_gpiolib_init被执行之后,才执行gpiolib_sysfs_init。

static int __init gpiolib_sysfs_init(void)
{
	int		status;
	unsigned long	flags;
	unsigned	gpio;

	status = class_register(&gpio_class);
	if (status < 0)
		return status;

	/* Scan and register the gpio_chips which registered very
	 * early (e.g. before the class_register above was called).
	 *
	 * We run before arch_initcall() so chip->dev nodes can have
	 * registered, and so arch_initcall() can always gpio_export().
	 */
	spin_lock_irqsave(&gpio_lock, flags);
	for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
		struct gpio_chip	*chip;

		chip = gpio_desc[gpio].chip;
		if (!chip || chip->exported)
			continue;

		spin_unlock_irqrestore(&gpio_lock, flags);
		status = gpiochip_export(chip);
		spin_lock_irqsave(&gpio_lock, flags);
	}
	spin_unlock_irqrestore(&gpio_lock, flags);


	return status;
}
postcore_initcall(gpiolib_sysfs_init);

#define postcore_initcall(fn) __define_initcall(“2”,fn,2)
由于这个函数被链接到内核的init段,所以开机时候gpiolib初始化函数被调用。
这个函数被调用,在开发板的/sys/class中相应创建了gpio目录。(如图)

同时,在初始化函数也创建了各个gpio的目录。通过遍历gpio_desc[gpio].chip,再由gpiochip_export实现。(如图)


3、gpio_desc结构体

既然gpio_desc在初始化函数出现了,那就接着分析以下它。

gpio_desc结构体描述如下:

struct gpio_desc {
	struct gpio_chip	*chip;
	unsigned long		flags;
	...
	const char		*label;
#endif
};
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

gpio_desc结构体什么时候被赋值?
只有通过gpiochip_add函数。而这个函数在运行exynos4_gpiolib_init时候被调用了。

其实,gpio的注册本质就是注册到gpio_desc这个结构体数组里,使用gpiochip_add函数。


4、gpiolib框架相关函数

  • gpiochip_add: 向内核注册gpio。在sys/class/gpio中创建文件
  • gpio_request:给驱动工程师使用。当需要使用gpio时,需要向内核申请,这样的好处是可以防止一个gpio在多处地方被使用,从而产生冲突。
  • gpio_free:用完这个gpio,就得释放,腾出gpio资源,免得其它驱动申请失败
  • gpio_request_one/gpio_request_array:调用gpio_request+gpio的输入输出配置,最终调用的是chip->direction_input和chip->direction_output 也就是s3c_gpiolib_input和s3c_gpiolib_output来配置gpio的模式
  • gpiochip_is_requested:判断是否已经申请了gpio

5、gpiolib内部自己使用的一些代码

(1)attribute部分

attribute的作用是将gpio以文件的形式表现出来。需要将CONFIG_GPIO_SYSFS宏打开。

在gpio相应目录下,有些文件不可写,因为对应的attribute属性只有show,没有store方法。并且,文件权限也不许去写。

/*
 * /sys/class/gpio/gpiochipN/
 *   /base ... matching gpio_chip.base (N)
 *   /label ... matching gpio_chip.label
 *   /ngpio ... matching gpio_chip.ngpio
 */
static ssize_t chip_label_show(struct device *dev,
			       struct device_attribute *attr, char *buf)
{
	const struct gpio_chip	*chip = dev_get_drvdata(dev);

	return sprintf(buf, "%s\n", chip->label ? : "");
}
static DEVICE_ATTR(label, 0444, chip_label_show, NULL);

先看注释,说明是在gpiochipN目录下创建的文件
label文件,只有show的方法,所以不能写。
DEVICE_ATTR可以在sysfs中添加“文件”

并不是所有的attribute的变成一个文件,关键是看这段代码有没有被调用。
postcore_initcall(gpiolib_sysfs_init); 将这个函数放在初始化代码段中,这也是开机自启动的一种方式。

再次捋清脉络:
gpiolib_sysfs_init
遍历chip,判断chip是否为空或者是否被创建

gpiochip_export

device_create生成一个设备文件 “gpiochip%d”
sysfs_create_group(&dev->kobj,&gpiochip_attr_group); 在gpiochip%d目录下创建很多个attribute文件

static const struct attribute *gpiochip_attrs[] = {
&dev_attr_base.attr,
&dev_attr_label.attr,
&dev_attr_ngpio.attr,
NULL,
};

static const struct attribute_group gpiochip_attr_group = {
.attrs = (struct attribute **) gpiochip_attrs,
};

为什么能看见attribute,因为被调用了。


继续分析,另外一个attribute_group
static const struct attribute *gpio_attrs[] = {
&dev_attr_value.attr,
&dev_attr_active_low.attr,
NULL,
};

static const struct attribute_group gpio_attr_group = {
.attrs = (struct attribute **) gpio_attrs,
};

这个组的属性不能自动被调用,在哪被调用呢?

往下跟踪
static struct class_attribute gpio_class_attrs[] = {
__ATTR(export, 0200, NULL, export_store),
__ATTR(unexport, 0200, NULL, unexport_store),
__ATTR_NULL,
};

export_store
----gpio_export
----------gpio_attr_group

export_store这个操作是跟类相关,
在class/gpio中,是export文件。echo 1 > export时,调用export_store,这个功能是在类的目录下创建一个只写的文件目录。
在调用export_store,调用了gpio_export

device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),desc, ioname ? ioname : “gpio%u”, gpio);
也就是在类的目录下创建,gpio%u为名字的目录。
sysfs_create_group(&dev->kobj,&gpio_attr_group); 在gpio%u目录下,创建对应的属性

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值