06、GPIO子系统层次与数据结构
参考资料:
Linux 5.x内核文档
- Linux-5.4\Documentation\driver-api
- Linux-5.4\Documentation\devicetree\bindings\gpio\gpio.txt
Linux 4.x内核文档
- Linux-4.9.88\Documentation\gpio
- Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt
一、GPIO子系统的层次
1.1、GPIOLIB向上提供的接口对比
基于描述符的GPIO接口与基于整数的GPIO接口对比:
include/asm-generic/gpio.h
描述符 | 整数 |
---|---|
获得GPIO | 获得GPIO |
gpiod_get | gpio_request |
gpiod_get_index | |
gpiod_get_array | gpio_request_array |
devm_gpiod_get | |
devm_gpiod_get_index | |
devm_gpiod_get_array | |
设置方向 | |
gpiod_direction_input | gpio_direction_input |
gpiod_direction_output | gpio_direction_output |
读值、写值 | |
gpiod_get_value | gpio_get_value |
gpiod_set_value | gpio_set_value |
释放GPIO | |
gpio_free | gpio_free |
gpiod_put | gpio_free_array |
gpiod_put_array | |
devm_gpiod_put | |
devm_gpiod_put_array |
1.2、GPIOLIB向下提供的接口
GPIO控制器驱动编写其实就是三步走:
- 根据芯片有多少个GPIO控制器,就创建多少个gpio_chip结构体(比如芯片有3个GPIO控制器,就建3个结构体)
- 针对每个GPIO控制器的硬件寄存器,写出对应的操作函数(比如设置高低电平、读取状态这些功能)
- 把这些gpio_chip结构体一个个注册到系统里,用gpiochip_add这个接口完成注册
这个注册函数实际是调用gpiochip_add_data,参数就是你写好的gpio_chip结构体。如果有多个GPIO控制器,就需要像循环一样逐个调用这个函数,把每个控制器的信息都挂载到系统里。
1.2.1、gpiochip_add_data
gpiochip_add_data()
函数用于向内核注册一个GPIO控制器芯片(gpio_chip
),使其管理的GPIO引脚可以被系统使用。
/**
* gpiochip_add_data() - register a gpio_chip
* @gc: the chip to register, with gc->base initialized
* @data: driver-private data associated with this chip
*
* Context: potentially before irqs will work
*
* When gpiochip_add_data() is called very early during boot, so that GPIOs
* can be freely used, the gc->parent device must be registered before
* the gpio framework's arch_initcall(). Otherwise sysfs initialization
* for GPIOs will fail rudely.
*
* gpiochip_add_data() must only be called after gpiolib initialization,
* ie after core_initcall().
*
* If gc->base is negative, this requests dynamic assignment of
* a range of valid GPIOs.
*
* Returns:
* A negative errno if the chip can't be registered, such as because the
* gc->base is invalid or already associated with a different chip.
* Otherwise it returns zero as a success code.
*/
#ifdef CONFIG_LOCKDEP
#define gpiochip_add_data(gc, data) ({ \
static struct lock_class_key lock_key; \
static struct lock_class_key request_key; \
gpiochip_add_data_with_key(gc, data, &lock_key, \
&request_key); \
})
#define devm_gpiochip_add_data(dev, gc, data) ({ \
static struct lock_class_key lock_key; \
static struct lock_class_key request_key; \
devm_gpiochip_add_data_with_key(dev, gc, data, &lock_key, \
&request_key); \
})
#else
#define gpiochip_add_data(gc, data) gpiochip_add_data_with_key(gc, data, NULL, NULL)
#define devm_gpiochip_add_data(dev, gc, data) \
devm_gpiochip_add_data_with_key(dev, gc, data, NULL, NULL)
#endif /* CONFIG_LOCKDEP */
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
代码分为两部分,通过#ifdef CONFIG_LOCKDEP
条件编译控制:
当LOCKDEP(锁依赖检测)启用时:
- 定义了两个静态的锁类键(lock_key和request_key)
- 调用带有锁键参数的
gpiochip_add_data_with_key()
函数
当LOCKDEP未启用时:
- 直接调用
gpiochip_add_data_with_key()
函数 - 传递NULL作为锁键参数
- 直接调用
1.2.2、devm_gpiochip_add_data
GPIOLIB中提供了两种注册方式:
gpiochip_add_data()
- 普通版本devm_gpiochip_add_data()
- 设备资源管理版本(会自动处理资源释放)
其中gpiolib提供了带devm的接口,管理其内存,定义如下:
/**
* devm_gpiochip_add_data() - Resource manager gpiochip_add_data()
* @dev: pointer to the device that gpio_chip belongs to.
* @chip: the chip to register, with chip->base initialized
* @data: driver-private data associated with this chip
*
* Context: potentially before irqs will work
*
* The gpio chip automatically be released when the device is unbound.
*
* Returns:
* A negative errno if the chip can't be registered, such as because the
* chip->base is invalid or already associated with a different chip.
* Otherwise it returns zero as a success code.
*/
int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
void *data)
{
struct gpio_chip **ptr;
int ret;
ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = gpiochip_add_data(chip, data);
if (ret < 0) {
devres_free(ptr);
return ret;
}
*ptr = chip;
devres_add(dev, ptr);
return 0;
}
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
1.2.3、gpiochip_add_data_with_key
drivers/gpio/gpiolib.c
int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
struct lock_class_key *lock_key,
struct lock_class_key *request_key)
{
unsigned long flags;
int status = 0;
unsigned i;
int base = chip->base; // 当前GPIO控制器的第一个GPIO编号
struct gpio_device *gdev;
/*
* First: allocate and populate the internal stat container, and
* set up the struct device.
*/
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); // 分配一个gpio_device
if (!gdev)
return -ENOMEM;
gdev->dev.bus = &gpio_bus_type; // 设置bus
gdev->chip = chip; // 设置chip
chip->gpiodev = gdev; // 设置gpiodev
if (chip->parent) {
gdev->dev.parent = chip->parent;
gdev->dev.of_node = chip->parent->of_node;
}
#ifdef CONFIG_OF_GPIO
/* If the gpiochip has an assigned OF node this takes precedence */
if (chip->of_node)
gdev->dev.of_node = chip->of_node;
else
chip->of_node = gdev->dev.of_node;
#endif
gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
if (gdev->id < 0) {
status = gdev->id;
goto err_free_gdev;
}
dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); // 设置名称 goiochip%d
device_initialize(&gdev->dev); // 初始化dev
dev_set_drvdata(&gdev->dev, gdev); // 设置gdev->dev.driver_data = gdev
if (chip->parent && chip->parent->driver)
gdev->owner = chip->parent->driver->owner;
else if (chip->owner)
/* TODO: remove chip->owner */
gdev->owner = chip->owner;
else
gdev->owner = THIS_MODULE;
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL); // 根据一个GPIO控制器所拥有的GPIO数量,申请gpio_desc
if (!gdev->descs) {
status = -ENOMEM;
goto err_free_ida;
}
if (chip->ngpio == 0) {
chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
status = -EINVAL;
goto err_free_descs;
}
if (chip->ngpio > FASTPATH_NGPIO)
chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n",
chip->ngpio, FASTPATH_NGPIO);
gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) {
status = -ENOMEM;
goto err_free_descs;
}
gdev->ngpio = chip->ngpio;
gdev->data = data;
spin_lock_irqsave(&gpio_lock, flags); // 获取自旋锁 + 关中断
/*
* TODO: this allocates a Linux GPIO number base in the global
* GPIO numberspace for this chip. In the long run we want to
* get *rid* of this numberspace and use only descriptors, but
* it may be a pipe dream. It will not happen before we get rid
* of the sysfs interface anyways.
*/
if (base < 0) { // 未指定 动态分配
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
/*
* TODO: it should not be necessary to reflect the assigned
* base outside of the GPIO subsystem. Go over drivers and
* see if anyone makes use of this, else drop this and assign
* a poison instead.
*/
chip->base = base;
}
gdev->base = base;
status = gpiodev_add_to_list(gdev); // 将当前gdev添加到双向链表gpio_devices
if (status) {
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
spin_unlock_irqrestore(&gpio_lock, flags); // 释放自旋锁 + 开中断
for (i = 0; i < chip->ngpio; i++) // 设置每个gpio_desc所指向的gdev
gdev->descs[i].gdev = gdev;
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
status = gpiochip_set_desc_names(chip); // 设置每个gpio_desc的名字,值取值chip->names[i]
if (status)
goto err_remove_from_list;
status = gpiochip_irqchip_init_valid_mask(chip); // 中断配置
if (status)
goto err_remove_from_list;
status = gpiochip_alloc_valid_mask(chip);
if (status)
goto err_remove_irqchip_mask;
status = gpiochip_add_irqchip(chip, lock_key, request_key);
if (status)
goto err_free_gpiochip_mask;
status = of_gpiochip_add(chip);
if (status)
goto err_remove_chip;
status = gpiochip_init_valid_mask(chip);
if (status)
goto err_remove_of_chip;
for (i = 0; i < chip->ngpio; i++) { // 初始化每一个GPIO对应的gpio_desc
struct gpio_desc *desc = &gdev->descs[i];
if (chip->get_direction && gpiochip_line_is_valid(chip, i)) {
if (!chip->get_direction(chip, i)) // 0输出 进入
set_bit(FLAG_IS_OUT, &desc->flags); // 设置标志位为输出
else
clear_bit(FLAG_IS_OUT, &desc->flags); // 清除输出标志位
} else {
if (!chip->direction_input)
set_bit(FLAG_IS_OUT, &desc->flags);
else
clear_bit(FLAG_IS_OUT, &desc->flags);
}
}
acpi_gpiochip_add(chip);
machine_gpiochip_add(chip);
/*
* By first adding the chardev, and then adding the device,
* we get a device node entry in sysfs under
* /sys/bus/gpio/devices/gpiochipN/dev that can be used for
* coldplug of device nodes and other udev business.
* We can do this only if gpiolib has been initialized.
* Otherwise, defer until later.
*/
if (gpiolib_initialized) {
status = gpiochip_setup_dev(gdev);
if (status)
goto err_remove_acpi_chip;
}
return 0;
err_remove_acpi_chip:
acpi_gpiochip_remove(chip);
err_remove_of_chip:
gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
err_remove_chip:
gpiochip_irqchip_remove(chip);
err_free_gpiochip_mask:
gpiochip_free_valid_mask(chip);
err_remove_irqchip_mask:
gpiochip_irqchip_free_valid_mask(chip);
err_remove_from_list:
spin_lock_irqsave(&gpio_lock, flags);
list_del(&gdev->list);
spin_unlock_irqrestore(&gpio_lock, flags);
err_free_label:
kfree_const(gdev->label);
err_free_descs:
kfree(gdev->descs);
err_free_ida:
ida_simple_remove(&gpio_ida, gdev->id);
err_free_gdev:
/* failures here can mean systems won't boot... */
pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
gdev->base, gdev->base + gdev->ngpio - 1,
chip->label ? : "generic", status);
kfree(gdev);
return status;
}
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
这个函数的作用是向系统注册一个新的GPIO控制器,具体步骤如下:
- 创建设备结构
- 首先生成一个GPIO设备结构体(gpio_device),这是管理GPIO控制器的基础容器
- 设置设备参数
- 将设备结构与GPIO控制器芯片(chip)关联起来
- 根据控制器能管理的GPIO数量,分配对应的GPIO描述符数组(每个GPIO一个描述符)
- 编号分配
- 如果用户没有指定GPIO起始编号,系统会自动分配一组连续编号
- 确保每个GPIO编号在系统中是唯一的
- 注册设备
- 将新创建的GPIO设备添加到系统GPIO设备列表
- 添加时按编号范围排序(例如:20-30的控制器会排在31-40前面)
- 中断配置
- 设置控制器的中断处理相关参数
- 初始化GPIO描述符
为每个GPIO的描述符设置基本信息:
- 关联所属的GPIO设备
- 设置GPIO名称(如果有的话)
- 配置初始属性(如方向、驱动强度等)
整个过程就像给新加入的GPIO控制器办"身份证":先建立基本信息档案,分配固定编号,然后按编号顺序排入系统名单,最后配置具体功能参数。每个GPIO管脚都有独立的"身份卡片"(描述符)记录其状态和属性。
二、重要的3个核心数据结构
2.1 gpio_device
每个GPIO Controller用一个gpio_device来表示:
- 里面每一个gpio引脚用一个gpio_desc来表示
- gpio引脚的函数(引脚控制、中断相关),都放在gpio_chip里
/**
* struct gpio_device - internal state container for GPIO devices
* @id: numerical ID number for the GPIO chip
* @dev: the GPIO device struct
* @chrdev: character device for the GPIO device
* @mockdev: class device used by the deprecated sysfs interface (may be
* NULL)
* @owner: helps prevent removal of modules exporting active GPIOs
* @chip: pointer to the corresponding gpiochip, holding static
* data for this device
* @descs: array of ngpio descriptors.
* @ngpio: the number of GPIO lines on this GPIO device, equal to the size
* of the @descs array.
* @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned
* at device creation time.
* @label: a descriptive name for the GPIO device, such as the part number
* or name of the IP component in a System on Chip.
* @data: per-instance data assigned by the driver
* @list: links gpio_device:s together for traversal
*
* This state container holds most of the runtime variable data
* for a GPIO device and can hold references and live on after the
* GPIO chip has been removed, if it is still being used from
* userspace.
*/
struct gpio_device {
int id;
struct device dev;
struct cdev chrdev;
struct device *mockdev;
struct module *owner;
struct gpio_chip *chip;
struct gpio_desc *descs;
int base;
u16 ngpio;
const char *label;
void *data;
struct list_head list;
#ifdef CONFIG_PINCTRL
/*
* If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
* describe the actual pin range which they serve in an SoC. This
* information would be used by pinctrl subsystem to configure
* corresponding pins for gpio usage.
*/
struct list_head pin_ranges;
#endif
};
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
47
48
49
该结构体用于描述GPIO设备的信息,各参数含义如下:
int id:一个整数,用来唯一标识这个GPIO设备的编号。
struct device *dev:指向设备结构体的指针,记录与该GPIO设备关联的硬件设备信息。
struct cdev chrdev:字符设备结构体,用于实现与GPIO设备的字符设备交互(如文件操作)。
struct device *mockdev:指向模拟设备结构体的指针,用于保存GPIO设备的模拟设备信息(用于测试或模拟场景)。
struct module *owner:指向内核模块的指针,标记这个GPIO设备属于哪个内核模块。
struct gpio_chip *chip:指向GPIO芯片结构体的指针,表示控制该GPIO设备的硬件控制器。
struct gpio_desc *descs:指向GPIO描述符数组的指针,每个描述符记录一个GPIO引脚的详细状态和配置信息。
int base:整数,表示该GPIO设备起始的GPIO编号(通常用于标识一组GPIO的起始位置)。
u16 ngpio:16位无符号整数,记录该设备包含的GPIO引脚总数。
const char *label:字符串指针,保存GPIO设备的名称或标识标签。
void *data:通用数据指针,可以存放与GPIO设备相关的任何自定义数据。
struct list_head list:列表节点,用于将多个GPIO设备串联成链表(方便系统统一管理)。
struct list_head pin_ranges(仅在支持引脚控制时存在):列表节点,用于记录该GPIO控制器支持的引脚范围(当系统配置了引脚控制功能时使用)。
每个字段都对应着GPIO设备的不同管理或配置信息,共同构成了完整的GPIO设备描述。
2.2 gpio_chip
我们去使用GPIO子系统时,首先是获得某个引脚对应的gpio_desc。
gpio_device表示一个GPIO Controller,里面支持多个GPIO。
在gpio_device中有一个gpio_desc数组,每一引脚有一项gpio_desc。
struct gpio_chip {
const char *label;
struct gpio_device *gpiodev;
struct device *parent;
struct module *owner;
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 (*get)(struct gpio_chip *chip,
unsigned offset);
int (*get_multiple)(struct gpio_chip *chip,
unsigned long *mask,
unsigned long *bits);
void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
void (*set_multiple)(struct gpio_chip *chip,
unsigned long *mask,
unsigned long *bits);
int (*set_config)(struct gpio_chip *chip,
unsigned offset,
unsigned long config);
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int (*init_valid_mask)(struct gpio_chip *chip);
int base;
u16 ngpio;
const char *const *names;
bool can_sleep;
...
}
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
关键参数解释
label:控制器名称(如GPA、GPB),用于标识不同GPIO控制器。
gpiodev:指向内部管理结构,记录该控制器所有GPIO的状态和描述信息。
parent:可选参数,指向提供GPIO的设备(如芯片或扩展模块)。
request/free:
request
:分配GPIO前执行的操作(如开启电源/时钟)。free
:释放GPIO后执行的操作(如关闭电源/时钟)。
方向控制:
get_direction(offset)
:检测指定GPIO是输入(返回1)还是输出(返回0)。direction_input(offset)
:将GPIO设为输入模式。direction_output(offset)
:将GPIO设为输出模式。
电平操作:
get(offset)
:读取GPIO的当前电平(0或1)。set(offset, value)
:设置GPIO输出电平(0或1)。- 批量操作:
get_multiple
和set_multiple
可同时读/写一组GPIO。
基础参数:
base
:控制器内GPIO的起始编号(如设置为-1由系统自动分配)。ngpio
:控制器包含的GPIO总数。- 最后一个GPIO编号为
base + ngpio - 1
。
中断映射:
to_irq(offset)
:将GPIO引脚映射为中断号。
寄存器操作:
read_reg/write_reg
:通用寄存器读写函数。reg_dat
等寄存器地址定义了GPIO数据、方向等寄存器的位置。
关键细节
offset含义: 表示GPIO在控制器内的偏移量,例如:
offset=0
→ 对应编号为base
的GPIO。offset=1
→ 对应编号为base+1
的GPIO。
睡眠标志
**can_sleep**
: 若GPIO操作可能阻塞(如通过I2C访问扩展芯片),需设置此标志。此时中断需通过线程处理,避免死锁。寄存器与影子机制:
- 硬件寄存器通过
read_reg/write_reg
访问。 bgpio_data/direction
是寄存器的内存备份,用于安全修改位值。
- 硬件寄存器通过
注意事项
- 过时特性:
base
建议设为-1,让系统自动分配编号(旧代码可能固定编号,但新代码应避免)。 - 默认状态: GPIO默认通常为输入模式,需显式配置为输出才能控制电平。
:::
总结
该结构体抽象了一个GPIO控制器,管理其下的所有GPIO引脚。开发者通过设置方向、读写电平等操作,控制硬件引脚的输入输出行为。核心参数定义了GPIO的编号范围、操作方式及硬件交互细节。 :::
2.3 gpio_desc
gpio_desc结构体和中断系统中的irq_desc结构类似。就像irq_desc用来管理中断信号一样,gpio_desc专门用来管理GPIO引脚的信息。它的定义在drivers/gpio/gpiolib.h文件中,用于记录系统中每个具体GPIO引脚的详细状态和配置信息。
struct gpio_desc {
struct gpio_device *gdev;
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_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 FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
/* Connection label */
const char *label;
/* Name of the GPIO */
const char *name;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
其中部分参数含义如下:
gdev:指向了一个gpio_device;内核是通过这个指针将gpio_chip和gpio_desc连接起来的,这个也和中断子系统中的irq_data数据结构类似;
flags:GPIO属性标志,比如输出、上拉使能,开漏输出等;
lable:标签;
name:名称;
三、怎么编写GPIO Controller驱动程序
设备树:
驱动目录:gpio/gpio-rockchip.c
rockchip_gpio_probe
---rockchip_gpiolib_register
3.1、rockchip_gpiolib_register
static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank)
{
struct gpio_chip *gc;
int ret;
bank->gpio_chip = rockchip_gpiolib_chip;
gc = &bank->gpio_chip;
gc->base = bank->pin_base;
gc->ngpio = bank->nr_pins;
gc->label = bank->name;
gc->parent = bank->dev;
#ifdef CONFIG_OF_GPIO
gc->of_node = of_node_get(bank->of_node);
#endif
ret = gpiochip_add_data(gc, bank);
if (ret) {
dev_err(bank->dev, "failed to add gpiochip %s, %d\n", gc->label, ret);
return ret;
}
/*
* For DeviceTree-supported systems, the gpio core checks the
* pinctrl's device node for the "gpio-ranges" property.
* If it is present, it takes care of adding the pin ranges
* for the driver. In this case the driver can skip ahead.
*
* In order to remain compatible with older, existing DeviceTree
* files which don't set the "gpio-ranges" property or systems that
* utilize ACPI the driver has to call gpiochip_add_pin_range().
*/
if (!of_property_read_bool(bank->of_node, "gpio-ranges")) {
struct device_node *pctlnp = of_get_parent(bank->of_node);
struct pinctrl_dev *pctldev = NULL;
if (!pctlnp)
return -ENODATA;
pctldev = of_pinctrl_get(pctlnp);
if (!pctldev)
return -ENODEV;
ret = gpiochip_add_pin_range(gc, dev_name(pctldev->dev), 0, gc->base, gc->ngpio);
if (ret) {
dev_err(bank->dev, "Failed to add pin range\n");
goto fail;
}
}
ret = rockchip_interrupts_register(bank);
if (ret) {
dev_err(bank->dev, "failed to register interrupt, %d\n", ret);
goto fail;
}
return 0;
fail:
gpiochip_remove(&bank->gpio_chip);
return ret;
}
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63