文章目录
一、怎么用?
#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目录下,创建对应的属性