4000 [uboot] (番外篇)uboot dm-gpio使用方法以及工作流程
2017-11-24 17:38
381 查看
[uboot] uboot流程系列:
[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)
[project X] tiny210(s5pv210)从存储设备加载代码到DDR
[uboot] (第一章)uboot流程——概述
[uboot] (第二章)uboot流程——uboot-spl编译流程
[uboot] (第三章)uboot流程——uboot-spl代码流程
[uboot] (第四章)uboot流程——uboot编译流程
[uboot] (第五章)uboot流程——uboot启动流程
[uboot] (番外篇)global_data介绍
[uboot] (番外篇)uboot relocation介绍
[uboot] (番外篇)uboot 驱动模型
建议先看《[uboot]
(番外篇)uboot 驱动模型》
=============================================================================================================
dm-gpio
建议先参考《[uboot] (番外篇)uboot 驱动模型》搞懂uboot下的驱动模型。
如下:
gpio core
也在gpio uclass中实现,
主要是为上层提供接口
从dts中获取GPIO属性
从gpio uclass的设备链表中获取到相应的udevice设备,并使用其操作集
gpio uclass
链接属于该uclass的所有gpio udevice设备
为gpio udevice的driver提供统一的操作集接口
bank和gpio
有些平台上,将某些使用同一组寄存器的gpio构成一个bank,例如三星的s5pv210
当然,并不是所有平台都有bank的概念,例如高通,高通的GPIO都有自己独立的寄存器,因此,可以将高通当成只有一个bank
gpio udevice
一个bank对应一个gpio udevice,用bank中的偏移来表示具体的GPIO号
gpio udevice的driver就会根据bank以及offset对相应的寄存器上的相应的bit进行操作。
这里先简单介绍一下dm下gpio的工作原理
一个bank对应一个udevice,udevice中私有数据中存放着该bank的信息,比如相应寄存器地址等等
上层用gpio_desc描述符来描述一个GPIO,其中包括该GPIO所属的udevice、在bank内的偏移、以及标志位。
上层通过调用gpio core的接口从dtsi获取到GPIO属性对应的gpio_desc描述符
上层使用gpio_desc描述符来作为调用gpio core的操作接口的参数
gpio core从gpio_desc描述符提取udevice,并调用其driver中对应的操作集,以bank内的偏移作为其参数(这样driver就能判断出是哪个GPIO了)
driver中提取udevice的私有数据中的bank信息,并进行相应的操作
这里简单的以tiny210的led_1的闪烁为例子来介绍一下GPIO的使用。
虽然uboot中已经实现了led的uclass,但是我们这里先不使用这个uclass,只是简单的实现通过dm-gpio来控制一个led。
s5pv210的GPIO的地址空间
参考《S5PV210_UM_REV1.1》2.1.2 SPECIAL FUNCTION REGISTER MAP,
s5pv210的GPIO的地址空间为0xE020_0000开始的1000B的区域
在tiny210上,有四个led,低电平为亮,高电平为灭
GPJ2_0 -> led_1
GPJ2_1 -> led_2
GPJ2_2 -> led_3
GPJ2_3 -> led_4
GPJ2这个bank的寄存器
对应控制寄存器 0xE020_0280
对应数据寄存器 0xE020_0284
这里只说明GPIO引用的节点,不说明gpio控制器的节点,gpio控制器的节点在后面driver中进行说明
arch/arm/dts/s5pv210-tiny210.dts
2
3
4
5
6
7
注意,这里<&gpj2 0 0>取决于driver是如何对这个属性进行转化的。并不是所有平台都是一样的。
我们在board_late_init中来实现GPIO闪烁的功能
board/samsung/tiny210/board.c
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
在dtsi中将需要的GPIO以一定的格式在属性中描述,如下:
led1-gpio = <&gpj2 0 0>;
在驱动中调用gpio_request_by_name_nodev接口或者其他接口从dtsi节点中获取对应GPIO属性的gpio_desc描述符
调用dm_gpio_is_valid判断该gpio_desc是否可用
调用gpio core的dm操作接口,以gpio_desc为参数,实现控制GPIO的目的
后面介绍dm-gpio的实现
建议先参考《[uboot] (番外篇)uboot 驱动模型》搞懂uboot下的驱动模型。
在gpio uclass中实现了两部分,
gpio uclass driver:gpio uclass驱动,提供gpio udevice绑定到uclass的设备链表之前和之后的一些操作,以及udevice的driver的操作集规范。
gpio core:为上层提供GPIO的操作接口
(1)CONFIG_DM
dm-gpio是基于uboot的DM实现的,关于uboot的DM可以参考《[uboot]
(番外篇)uboot 驱动模型》。
所以需要先使能DM,也就是打开CONFIG_DM宏。
在configs/tiny210_defconfig中定义了如下:
(2)打开CONFIG_DM_GPIO宏
在configs/tiny210_defconfig中定义了如下:
(1)gpio_desc
前面也说过了,上层是通过gpio_desc来和gpio core进行交互的。
2
3
4
5
offset对应bank内的偏移,通过offset+bank的gpio_base可以得到具体的GPIO号(用于兼容老版本的方式)。
调用uclass的接口可以从dts直接获得对应GPIO的描述符。DM框架下对GPIO的使用推荐使用这种方法。
(2)gpio_dev_priv
每个gpio uclass设备链表下的udevice都有一部分属于uclass的私有数据。udevice->uclass_priv。
都有一个这样的数据结构,提供给gpio uclass使用,让gpio uclass知道这个udevice中的gpio信息和情况。
2
3
4
5
6
(3)标准dm-gpio的操作集合
2
3
4
5
6
7
8
9
10
11
12
13
注意都是以udevice和bank内的偏移为参数,驱动中可以根据udevice获取到对应bank的信息以及私有数据,然后根据偏移就可以确认出到底是哪个GPIO了。
DM模型中gpio模块的uclass。定义模块加载前和加载后的操作函数等等。
2
3
4
5
6
7
8
(1)gpio_post_probe
gpio_post_probe会在gpio udevice probe之后被调用,实现如下:
2
3
4
5
6
7
8
9
10
主要负责通过gpio uclass为上层提供接口,
(1)接口说明
其中既提供了兼容老版本的接口,也提供了DM框架下的接口
DM框架下的接口
注意,外部通过gpio_desc 来描述一个GPIO,所以这些接口都是以gpio_desc作为参数
gpio_request_by_name
int gpio_request_by_name(struct udevice *dev, const char *list_name, int index, struct gpio_desc *desc, int flags)
通过对应的udevice找到其dtsi节点中属性名为list_name的GPIO属性并转化为gpio_desc,并且request。
gpio_request_by_name_nodev
int gpio_request_by_name_nodev(const void *blob, int node, const char *list_name, int index, struct gpio_desc *desc, int flags)
通过对应的dtsi节点中属性名为list_name的GPIO属性并转化为gpio_desc,并且request。
dm_gpio_request
int dm_gpio_request(struct gpio_desc *desc, const char *label)
申请gpio_desc描述的GPIO
dm_gpio_get_value
int dm_gpio_get_value(const struct gpio_desc *desc)
获取gpio_desc描述的GPIO的值
dm_gpio_set_value
int dm_gpio_set_value(const struct gpio_desc *desc, int value)
设置gpio_desc描述的GPIO的值
dm_gpio_set_dir_flags
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
设置gpio_desc描述的GPIO的输入输出方向,带标志
dm_gpio_set_dir
int dm_gpio_set_dir(struct gpio_desc *desc)
设置gpio_desc描述的GPIO的输入输出方向
dm_gpio_is_valid
static inline bool dm_gpio_is_valid(const struct gpio_desc *desc)
判断gpio_desc是否可用
老接口:
这些接口是为了兼容老版本的接口,注意,但是最终还是会调用DM框架下的接口
gpio_request
int gpio_request(unsigned gpio, const char *label)
申请一个GPIO
gpio_direction_input
int gpio_direction_input(unsigned gpio)
设置某个GPIO为输入
gpio_direction_output
int gpio_direction_output(unsigned gpio, int value)
设置某个GPIO为输出
gpio_get_value
int gpio_get_value(unsigned gpio)
获取某个GPIO上的值
gpio_set_value
int gpio_set_value(unsigned gpio, int value)
设置GPIO的值
(2)举例:dm_gpio_set_value
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(3)举例:gpio_set_value
2
3
4
5
6
7
8
9
10
11
12
所有驱动都有自己的方式来解析gpio_desc,这里我们以s5pv210为例:
不同driver可能差异较大。这里只作为一个参考。
drivers/gpios5p_gpio.c
所有bank都有对应的udevice,因此在绑定的过程都需要创建udevice中关于这个bank的私有数据。
s5pv210下这个私有数据是:
2
3
4
5
6
7
8
9
其size刚好是一个bank的占有的寄存器的地址空间长度。并且意义也和寄存器中的对应。
另外,s5pv210并不会为每个bank都创建一个匹配节点(注意是匹配节点而不是节点,节点还是会用的,匹配节点是指带有compitable属性的节点),而是将所有bank的节点都放在一个匹配节点下,而driver会在bind这个匹配节点的时候,去probe这个节点下面的bank节点。而匹配节点是不做probe操作的。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
注意这里的bank是有顺序的,必须和硬件上bank的顺序完全一致。
具体成员的意义参考《[uboot] (番外篇)uboot 驱动模型》
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
对应udevice绑定到uclass上所做的操作
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
用于给gpio uclass提供udevice对应的bank操作集,必须使用dm_gpio_ops来进行定义
2
3
4
5
6
7
8
在这些操作方法中,最终会直接操作到GPIO对应的寄存器上去。
以exynos_gpio_set_value为例:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意,上述还提供了转化方式exynos_gpio_xlate,负责解析dtsi的GPIO属性。所以,dtsi的GPIO的属性的格式就是取决于这里是怎么解析的。
[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)
[project X] tiny210(s5pv210)从存储设备加载代码到DDR
[uboot] (第一章)uboot流程——概述
[uboot] (第二章)uboot流程——uboot-spl编译流程
[uboot] (第三章)uboot流程——uboot-spl代码流程
[uboot] (第四章)uboot流程——uboot编译流程
[uboot] (第五章)uboot流程——uboot启动流程
[uboot] (番外篇)global_data介绍
[uboot] (番外篇)uboot relocation介绍
[uboot] (番外篇)uboot 驱动模型
建议先看《[uboot]
(番外篇)uboot 驱动模型》
=============================================================================================================
dm-gpio
一、dm-gpio架构
建议先参考《[uboot] (番外篇)uboot 驱动模型》搞懂uboot下的驱动模型。
1、dm-gpio架构图
如下:
2、说明
gpio core 也在gpio uclass中实现,
主要是为上层提供接口
从dts中获取GPIO属性
从gpio uclass的设备链表中获取到相应的udevice设备,并使用其操作集
gpio uclass
链接属于该uclass的所有gpio udevice设备
为gpio udevice的driver提供统一的操作集接口
bank和gpio
有些平台上,将某些使用同一组寄存器的gpio构成一个bank,例如三星的s5pv210
当然,并不是所有平台都有bank的概念,例如高通,高通的GPIO都有自己独立的寄存器,因此,可以将高通当成只有一个bank
gpio udevice
一个bank对应一个gpio udevice,用bank中的偏移来表示具体的GPIO号
gpio udevice的driver就会根据bank以及offset对相应的寄存器上的相应的bit进行操作。
3、原理介绍
这里先简单介绍一下dm下gpio的工作原理一个bank对应一个udevice,udevice中私有数据中存放着该bank的信息,比如相应寄存器地址等等
上层用gpio_desc描述符来描述一个GPIO,其中包括该GPIO所属的udevice、在bank内的偏移、以及标志位。
上层通过调用gpio core的接口从dtsi获取到GPIO属性对应的gpio_desc描述符
上层使用gpio_desc描述符来作为调用gpio core的操作接口的参数
gpio core从gpio_desc描述符提取udevice,并调用其driver中对应的操作集,以bank内的偏移作为其参数(这样driver就能判断出是哪个GPIO了)
driver中提取udevice的私有数据中的bank信息,并进行相应的操作
二、dm-gpio的使用示例
这里简单的以tiny210的led_1的闪烁为例子来介绍一下GPIO的使用。 虽然uboot中已经实现了led的uclass,但是我们这里先不使用这个uclass,只是简单的实现通过dm-gpio来控制一个led。
0、硬件简单说明(具体去参考原理图吧)
s5pv210的GPIO的地址空间 参考《S5PV210_UM_REV1.1》2.1.2 SPECIAL FUNCTION REGISTER MAP,
s5pv210的GPIO的地址空间为0xE020_0000开始的1000B的区域
在tiny210上,有四个led,低电平为亮,高电平为灭
GPJ2_0 -> led_1
GPJ2_1 -> led_2
GPJ2_2 -> led_3
GPJ2_3 -> led_4
GPJ2这个bank的寄存器
对应控制寄存器 0xE020_0280
对应数据寄存器 0xE020_0284
1、dtsi中的定义
这里只说明GPIO引用的节点,不说明gpio控制器的节点,gpio控制器的节点在后面driver中进行说明 arch/arm/dts/s5pv210-tiny210.dts
led_test { compatible = "dm-gp 20000 io,test"; led1-gpio = <&gpj2 0 0>; // 属性"led1-gpio",表示gpj2 bank的第0个gpio,0是值标志 led2-gpio = <&gpj2 1 0>; // gpj2 bank的第1个gpio,0是值标志 led3-gpio = <&gpj2 2 0>; // gpj2 bank的第2个gpio,0是值标志 led4-gpio = <&gpj2 3 0>; // gpj2 bank的第3个gpio,0是值标志 };1
2
3
4
5
6
7
注意,这里<&gpj2 0 0>取决于driver是如何对这个属性进行转化的。并不是所有平台都是一样的。
2、驱动中从节点中获取一个GPIO
我们在board_late_init中来实现GPIO闪烁的功能 board/samsung/tiny210/board.c
int board_late_init(void) { struct gpio_desc led1_gpio; int node; node = fdt_node_offset_by_compatible(gd->fdt_blob, 0, "dm-gpio,test"); // 注意,我们并没有使用dm模型的driver来匹配compatible = "dm-gpio,test"的节点,所以这里直接获取compatible = "dm-gpio,test"的节点,存放在node中 if (node < 0) { printf("===========================Don't find dm-gpio,test node\n"); return -1; } gpio_request_by_name_nodev(gd->fdt_blob, node, "led1-gpio", 0, &led1_gpio, GPIOD_IS_OUT); // 调用gpio_request_by_name_nodev来获取node节点中的"led1-gpio"属性,并转化为gpio_desc描述符,标志为输出 if (dm_gpio_is_valid(&led1_gpio))// 判断对应gpio_desc是否可用 { printf("===========================Get led1-gpio\n"); while(1) { dm_gpio_set_value(&led1_gpio, 0); // 调用dm_gpio_set_value将led1_gpio设置输出低电平 mdelay(1000); dm_gpio_set_value(&led1_gpio, 1); // 调用dm_gpio_set_value将led1_gpio设置输出高电平 mdelay(1000); } } else { printf("===========================Can't get led1-gpio\n"); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
3、简单说明
在dtsi中将需要的GPIO以一定的格式在属性中描述,如下: led1-gpio = <&gpj2 0 0>;
在驱动中调用gpio_request_by_name_nodev接口或者其他接口从dtsi节点中获取对应GPIO属性的gpio_desc描述符
调用dm_gpio_is_valid判断该gpio_desc是否可用
调用gpio core的dm操作接口,以gpio_desc为参数,实现控制GPIO的目的
后面介绍dm-gpio的实现
三、gpio uclass
建议先参考《[uboot] (番外篇)uboot 驱动模型》搞懂uboot下的驱动模型。 在gpio uclass中实现了两部分,
gpio uclass driver:gpio uclass驱动,提供gpio udevice绑定到uclass的设备链表之前和之后的一些操作,以及udevice的driver的操作集规范。
gpio core:为上层提供GPIO的操作接口
1、需要打开的宏
(1)CONFIG_DM dm-gpio是基于uboot的DM实现的,关于uboot的DM可以参考《[uboot]
(番外篇)uboot 驱动模型》。
所以需要先使能DM,也就是打开CONFIG_DM宏。
在configs/tiny210_defconfig中定义了如下:
CONFIG_DM=y1
(2)打开CONFIG_DM_GPIO宏
在configs/tiny210_defconfig中定义了如下:
CONFIG_DM_GPIO=y1
2、结构体说明
(1)gpio_desc 前面也说过了,上层是通过gpio_desc来和gpio core进行交互的。
struct gpio_desc { struct udevice *dev; /* Device, NULL for invalid GPIO */ //对应bank的udevice设备 unsigned long flags; uint offset; /* GPIO offset within the device */ // 该GPIO在bank内的偏移 };1
2
3
4
5
offset对应bank内的偏移,通过offset+bank的gpio_base可以得到具体的GPIO号(用于兼容老版本的方式)。
调用uclass的接口可以从dts直接获得对应GPIO的描述符。DM框架下对GPIO的使用推荐使用这种方法。
(2)gpio_dev_priv
每个gpio uclass设备链表下的udevice都有一部分属于uclass的私有数据。udevice->uclass_priv。
都有一个这样的数据结构,提供给gpio uclass使用,让gpio uclass知道这个udevice中的gpio信息和情况。
struct gpio_dev_priv { const char *bank_name; // udevice的名字,也是bank名称 unsigned gpio_count; // 这个bank中的GPIO数量 unsigned gpio_base; // 每个bank的gpio都占有gpio uclass的一部分连续的gpio空间,gpio_base则表示该bank的第一个GPIO号。 char **name; // 阵列,每个gpio被request之后,都会有自己的label名称,存储在这里,可以防止GPIO request冲突 };1
2
3
4
5
6
(3)标准dm-gpio的操作集合
struct dm_gpio_ops { int (*request)(struct udevice *dev, unsigned offset, const char *label); int (*free)(struct udevice *dev, unsigned offset); int (*direction_input)(struct udevice *dev, unsigned offset); int (*direction_output)(struct udevice *dev, unsigned offset, int value); int (*get_value)(struct udevice *dev, unsigned offset); int (*set_value)(struct udevice *dev, unsigned offset, int value); int (*get_function)(struct udevice *dev, unsigned offset); /* 这里具体看注释 */ int (*xlate)(struct udevice *dev, struct gpio_desc *desc, struct fdtdec_phandle_args *args); };1
2
3
4
5
6
7
8
9
10
11
12
13
注意都是以udevice和bank内的偏移为参数,驱动中可以根据udevice获取到对应bank的信息以及私有数据,然后根据偏移就可以确认出到底是哪个GPIO了。
3、gpio uclass driver
DM模型中gpio模块的uclass。定义模块加载前和加载后的操作函数等等。UCLASS_DRIVER(gpio) = { .id = UCLASS_GPIO, .name = "gpio", .flags = DM_UC_FLAG_SEQ_ALIAS, .post_probe = gpio_post_probe, .pre_remove = gpio_pre_remove, .per_device_auto_alloc_size = sizeof(struct gpio_dev_priv), };1
2
3
4
5
6
7
8
(1)gpio_post_probe
gpio_post_probe会在gpio udevice probe之后被调用,实现如下:
static int gpio_post_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); // 获取刚probe的udevice的提供给uclass使用私有数据 uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *)); // 为私有数据中的GPIO label分配空间 if (!uc_priv->name) return -ENOMEM; return gpio_renumber(NULL); // 重新分配GPIO空间 }1
2
3
4
5
6
7
8
9
10
4、gpio core
主要负责通过gpio uclass为上层提供接口,(1)接口说明
其中既提供了兼容老版本的接口,也提供了DM框架下的接口
DM框架下的接口
注意,外部通过gpio_desc 来描述一个GPIO,所以这些接口都是以gpio_desc作为参数
gpio_request_by_name
int gpio_request_by_name(struct udevice *dev, const char *list_name, int index, struct gpio_desc *desc, int flags)
通过对应的udevice找到其dtsi节点中属性名为list_name的GPIO属性并转化为gpio_desc,并且request。
gpio_request_by_name_nodev
int gpio_request_by_name_nodev(const void *blob, int node, const char *list_name, int index, struct gpio_desc *desc, int flags)
通过对应的dtsi节点中属性名为list_name的GPIO属性并转化为gpio_desc,并且request。
dm_gpio_request
int dm_gpio_request(struct gpio_desc *desc, const char *label)
申请gpio_desc描述的GPIO
dm_gpio_get_value
int dm_gpio_get_value(const struct gpio_desc *desc)
获取gpio_desc描述的GPIO的值
dm_gpio_set_value
int dm_gpio_set_value(const struct gpio_desc *desc, int value)
设置gpio_desc描述的GPIO的值
dm_gpio_set_dir_flags
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
设置gpio_desc描述的GPIO的输入输出方向,带标志
dm_gpio_set_dir
int dm_gpio_set_dir(struct gpio_desc *desc)
设置gpio_desc描述的GPIO的输入输出方向
dm_gpio_is_valid
static inline bool dm_gpio_is_valid(const struct gpio_desc *desc)
判断gpio_desc是否可用
老接口:
这些接口是为了兼容老版本的接口,注意,但是最终还是会调用DM框架下的接口
gpio_request
int gpio_request(unsigned gpio, const char *label)
申请一个GPIO
gpio_direction_input
int gpio_direction_input(unsigned gpio)
设置某个GPIO为输入
gpio_direction_output
int gpio_direction_output(unsigned gpio, int value)
设置某个GPIO为输出
gpio_get_value
int gpio_get_value(unsigned gpio)
获取某个GPIO上的值
gpio_set_value
int gpio_set_value(unsigned gpio, int value)
设置GPIO的值
(2)举例:dm_gpio_set_value
int dm_gpio_set_value(const struct gpio_desc *desc, int value) { int ret; ret = check_reserved(desc, "set_value"); // 判断这个GPIO是否已经被request了,必须是request之后才能被使用 if (ret) return ret; if (desc->flags & GPIOD_ACTIVE_LOW) value = !value; gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value); // 先获取对应udevice的driver,也就是对应bank的drvier的操作集中的set_value方法, // 然后以udevice、bank内的偏移,电平值为参数传入到set_value方法 // 剩下的就是driver中的事了,driver中需要能根据udevice和bank内偏移操作到对应的GPIO上去 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(3)举例:gpio_set_value
int gpio_set_value(unsigned gpio, int value) { struct gpio_desc desc; int ret; ret = gpio_to_device(gpio, &desc); // 将GPIO转化为对应的gpio_desc描述符 // 前面我们也说了,所有bank都占用uclass的gpio空间的一部分、并且是连续的不冲突的,因此,uclass core可以将对应的gpio转化为gpio_desc描述符 // 具体实现自己看代码 if (ret) return ret; return dm_gpio_set_value(&desc, value); }1
2
3
4
5
6
7
8
9
10
11
12
四、gpio driver
所有驱动都有自己的方式来解析gpio_desc,这里我们以s5pv210为例: 不同driver可能差异较大。这里只作为一个参考。
drivers/gpios5p_gpio.c
0、重点说明
所有bank都有对应的udevice,因此在绑定的过程都需要创建udevice中关于这个bank的私有数据。 s5pv210下这个私有数据是:
struct s5p_gpio_bank { unsigned int con; unsigned int dat; unsigned int pull; unsigned int drv; unsigned int pdn_con; unsigned int pdn_pull; unsigned char res1[8]; };1
2
3
4
5
6
7
8
9
其size刚好是一个bank的占有的寄存器的地址空间长度。并且意义也和寄存器中的对应。
另外,s5pv210并不会为每个bank都创建一个匹配节点(注意是匹配节点而不是节点,节点还是会用的,匹配节点是指带有compitable属性的节点),而是将所有bank的节点都放在一个匹配节点下,而driver会在bind这个匹配节点的时候,去probe这个节点下面的bank节点。而匹配节点是不做probe操作的。
1、dtsi中的定义
/{ pinctrl: pinctrl@e0200000 { compatible = "samsung,s5pc110-pinctrl"; // 只有这个节点被匹配到,但是在这个节点bind的过程中,会去为子节点的bank创建对应udevice并且绑定到gpio uclass上,然后probe子节点 reg = <0xe0200000 0x1000>; #address-cells = <1>; #size-cells = <1>; gpa0: gpa0 { gpio-controller; #gpio-cells = <2>; }; gpa1: gpa1 { gpio-controller; #gpio-cells = <2>; }; gpb: gpb { gpio-controller; #gpio-cells = <2>; }; gpc0: gpc0 { gpio-controller; #gpio-cells = <2>; }; // 后面的bank这里不写了。。。 }; };1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
注意这里的bank是有顺序的,必须和硬件上bank的顺序完全一致。
2、定义一个driver
具体成员的意义参考《[uboot] (番外篇)uboot 驱动模型》U_BOOT_DRIVER(gpio_exynos) = { .name = "gpio_exynos", .id = UCLASS_GPIO, // 定义所属uclass的id .of_match = exynos_gpio_ids, .bind = gpio_exynos_bind, .probe = gpio_exynos_probe, .priv_auto_alloc_size = sizeof(struct exynos_bank_info), .ops = &gpio_exynos_ops, };1
2
3
4
5
6
7
8
9
3、实现exynos_gpio_ids
static const struct udevice_id exynos_gpio_ids[] = { { .compatible = "samsung,s5pc100-pinctrl" }, { .compatible = "samsung,s5pc110-pinctrl" }, { .compatible = "samsung,exynos4210-pinctrl" }, { .compatible = "samsung,exynos4x12-pinctrl" }, { .compatible = "samsung,exynos5250-pinctrl" }, { .compatible = "samsung,exynos5420-pinctrl" }, { } };1
2
3
4
5
6
7
8
9
4、实现gpio_exynos_bind
对应udevice绑定到uclass上所做的操作static int gpio_exynos_bind(struct udevice *parent) { struct exynos_gpio_platdata *plat = parent->platdata; struct s5p_gpio_bank *bank, *base; const void *blob = gd->fdt_blob; int node; /* If this is a child device, there is nothing to do here */ if (plat) return 0; // 只有匹配的节点,也就是所有bank节点的父节点会做这个操作 base = (struct s5p_gpio_bank *)dev_get_addr(parent); // 获取gpio寄存器的基地址 for (node = fdt_first_subnode(blob, parent->of_offset), bank = base; node > 0; node = fdt_next_subnode(blob, node), bank++) { // 遍历所有子节点 struct exynos_gpio_platdata *plat; struct udevice *dev; fdt_addr_t reg; int ret; if (!fdtdec_get_bool(blob, node, "gpio-controller")) // 判断是否有"gpio-controller"属性,也就是是否一个bank continue; plat = calloc(1, sizeof(*plat)); // 分配平台数据 if (!plat) return -ENOMEM; plat->bank_name = fdt_get_name(blob, node, NULL); // 设置平台数据的部分值 ret = device_bind(parent, parent->driver, // 绑定子设备,这里会为bank创建udevice,并且绑定到gpio uclass中 plat->bank_name, plat, -1, &dev); if (ret) return ret; dev->of_offset = node; // 设备bank对应的udevice的node reg = dev_get_addr(dev); if (reg != FDT_ADDR_T_NONE) bank = (struct s5p_gpio_bank *)((ulong)base + reg); // 所有bank的物理空间是以此排列的,并且长度都是一样的。 plat->bank = bank; // 设置bank的物理地址 debug("dev at %p: %s\n", bank, plat->bank_name); } return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
5、实现gpio_exynos_probe
static int gpio_exynos_probe(struct udevice *dev) { struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct exynos_bank_info *priv = dev->priv; struct exynos_gpio_platdata *plat = dev->platdata; /* Only child devices have ports */ if (!plat) return 0; // 只有bank节点会做probe操作,而bank的父节点则不会做probe操作 priv->bank = plat->bank; // 设置bank的私有数据 uc_priv->gpio_count = GPIO_PER_BANK; // 设置提供给uclass使用的私有数据gpio_dev_priv uc_priv->bank_name = plat->bank_name; // 设置提供给uclass使用的私有数据gpio_dev_priv return 0; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
6、实现操作集
用于给gpio uclass提供udevice对应的bank操作集,必须使用dm_gpio_ops来进行定义static const struct dm_gpio_ops gpio_exynos_ops = { .direction_input = exynos_gpio_direction_input, .direction_output = exynos_gpio_direction_output, .get_value = exynos_gpio_get_value, .set_value = exynos_gpio_set_value, .get_function = exynos_gpio_get_function, .xlate = exynos_gpio_xlate, };1
2
3
4
5
6
7
8
在这些操作方法中,最终会直接操作到GPIO对应的寄存器上去。
以exynos_gpio_set_value为例:
static int exynos_gpio_set_value(struct udevice *dev, unsigned offset, int value) // 以bank对应的udevice,以及bank内的偏移为参数 { struct exynos_bank_info *state = dev_get_priv(dev); // 获取平台私有数据,state->bank则是对应bank的寄存器地址 s5p_gpio_set_value(state->bank, offset, value); return 0; } static void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) { unsigned int value; value = readl(&bank->dat); // 读bank中的data寄存器 value &= ~DAT_MASK(gpio); if (en) value |= DAT_SET(gpio); writel(value, &bank->dat); // 写入 }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意,上述还提供了转化方式exynos_gpio_xlate,负责解析dtsi的GPIO属性。所以,dtsi的GPIO的属性的格式就是取决于这里是怎么解析的。
相关文章推荐
- [uboot] (番外篇)uboot dm-gpio使用方法以及工作流程
- 4000 Oracle游标的使用方法以及使用for循环实现游标的原理
- [uboot] (番外篇)uboot串口&console&stdio设备工作流程
- Asp.net MVC的Model Binder工作流程以及扩展方法(1)
- 再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结
- 再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结
- Android之mediarecorder中的方法以及工作流程的过程
- 使用Intent调用其他活动的方法以及运作流程
- [uboot] (番外篇)uboot串口&console&stdio设备工作流程
- 再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结
- mediarecorder中的方法以及工作流程的过程
- 通达OA工作流程-使用方法
- 再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结
- Spring Boot (番外篇:使用idea创建springboot项目的两种方法)
- 再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结
- linux下vim中文乱码的解决方法以及vim多字节编码机制的工作流程
- 4000 webstorm中提升工作效率方法之快捷键使用
- Linux管理工作,实例讲解工作中使用ssh证书登录的实际流程,讲解ssh证书登录的配置原理,基于配置原理,解决实际工作中,windows下使用SecureCRT证书登录的各种问题,以及实现hadoo
- spr 4000 ingmvc 属性放数据库中解决方法 以及 @Controller 中 使用@vlaue无法注入属性值问题详解
- 再谈STM32的CAN过滤器-bxCAN的过滤器的4种工作模式以及使用方法总结