1. gpioX 目录下的节点属性介绍
/sys/class/gpio/gpio211
-- active_low // 获取dts中设置的flag,如果dts中配置的是FLAG_ACTIVE_LOW,则显示0
-- device
-- direction // gpio 输入输出方向(输入:in, 输出:out)
-- edge // 如果是中断,设置中断类型(上升沿、下降沿、高电平、低电平)
-- power // 供电相关
-- subsystem
-- uevent // uevent 通知相关
-- value // gpio 当前值(0、1)
2. gpiochipX 目录下节点属性介绍
/sys/class/gpio/gpiochip160
-- base // 当前chip的base 地址,用于计算gpio口号的
-- device
-- label // chip 的名字
-- ngpio // 当前chip 下可以有gpio 口的数量,当前chip 下的gpio号为base ~ base + ngpio
-- power
-- subsystem
-- uevent
3. 创建export 流程
bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c
static CLASS_ATTR_WO(export); //创建只写export属性
static struct attribute *gpio_class_attrs[] = {
&class_attr_export.attr, // 定义export 属性节点
&class_attr_unexport.attr, // 定义unexport 属性节点
NULL,
};
ATTRIBUTE_GROUPS(gpio_class);
static struct class gpio_class = {
.name = "gpio", // sys/class/gpio 目录名字
.owner = THIS_MODULE,
.class_groups = gpio_class_groups,
};
3.1 CLASS_ATTR_WO(export) 展开
struct class_attribute class_attr_export = {
.attr = { .name = __stringify(export), .mode = S_IWUSR },
.store = export_store,
}
3.2 ATTRIBUTE_GROUPS(gpio_class) 展开
static const struct attribute_group gpio_class_group = {
.attrs = gpio_class_attrs,
};
3.3 gpiolib-sysfs.c入口函数 gpiolib_sysfs_init 解析
bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c
static int __init gpiolib_sysfs_init(void)
{
int status;
unsigned long flags;
struct gpio_device *gdev;
status = class_register(&gpio_class); // 这个方法会创建sys/class/gpio 目录和export 和unexport 节点
if (status < 0)
return status;
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(gdev, &gpio_devices, list) { // 循环读取gpio_devices 链表,结果保存到gdev 中
if (gdev->mockdev)
continue;
spin_unlock_irqrestore(&gpio_lock, flags);
status = gpiochip_sysfs_register(gdev); // 注册gpiochipX 节点
spin_lock_irqsave(&gpio_lock, flags);
}
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}
postcore_initcall(gpiolib_sysfs_init);
4. 创建gpiochipX 流程
接着查看gpiochip_sysfs_register() 方法,分析创建gpioichipX 目录的流程
int gpiochip_sysfs_register(struct gpio_device *gdev)
{
struct device *dev;
struct device *parent;
struct gpio_chip *chip = gdev->chip;
if (!gpio_class.p)
return 0;
if (chip->parent)
parent = chip->parent;
else
parent = &gdev->dev;
// 会创建gpiochipX 目录和其下的base、lable、ngpio 等属性节点
dev = device_create_with_groups(&gpio_class, parent,
MKDEV(0, 0),
chip, gpiochip_groups,
"gpiochip%d", chip->base);
if (IS_ERR(dev))
return PTR_ERR(dev);
mutex_lock(&sysfs_lock);
gdev->mockdev = dev;
mutex_unlock(&sysfs_lock);
return 0;
}
static struct attribute *gpiochip_attrs[] = {
&dev_attr_base.attr, // 创建base 属性节点,代表当前chip 的base 地址
&dev_attr_label.attr, // 创建label 属性节点
&dev_attr_ngpio.attr, // 创建ngpio 属性节点,代表当前chip 下gpio 的数量
NULL,
};
ATTRIBUTE_GROUPS(gpiochip);
static ssize_t base_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct gpio_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", chip->base);
}
static DEVICE_ATTR_RO(base); // 这个会创建dev_attr_base 结构体,并且show 方法为base_show
static ssize_t 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_RO(label); // 这个会创建dev_attr_label 结构体,并且show 方法为label_show
static ssize_t ngpio_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const struct gpio_chip *chip = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", chip->ngpio);
}
static DEVICE_ATTR_RO(ngpio); // 这个会创建dev_attr_ngpio 结构体,并且show 方法为ngpio_show
device_create_with_groups方法会按照gpio_gevices 链表创建gpiochipX 目录
4.1 ATTRIBUTE_GROUPS (gpiochip)展开
static const struct attribute_group gpiochip_group = {
.attrs = gpiochip_attrs,
};
5. gpio_devices 链表初始化
接下来需要查看在哪儿初始化的gpio_devices 链表
bsp/kernel/kernel4.14/drivers/gpio/gpiolib.c
LIST_HEAD(gpio_devices);
static int gpiodev_add_to_list(struct gpio_device *gdev)
{
struct gpio_device *prev, *next;
if (list_empty(&gpio_devices)) {
/* initial entry in list */
list_add_tail(&gdev->list, &gpio_devices);
return 0;
}
next = list_entry(gpio_devices.next, struct gpio_device, list);
if (gdev->base + gdev->ngpio <= next->base) {
/* add before first entry */
list_add(&gdev->list, &gpio_devices);
return 0;
}
prev = list_entry(gpio_devices.prev, struct gpio_device, list);
if (prev->base + prev->ngpio <= gdev->base) {
/* add behind last entry */
list_add_tail(&gdev->list, &gpio_devices);
return 0;
}
list_for_each_entry_safe(prev, next, &gpio_devices, list) {
/* at the end of the list */
if (&next->list == &gpio_devices)
break;
/* add between prev and next */
if (prev->base + prev->ngpio <= gdev->base
&& gdev->base + gdev->ngpio <= next->base) {
list_add(&gdev->list, &prev->list);
return 0;
}
}
dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chip\n");
return -EBUSY;
}
int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
struct lock_class_key *key)
{
struct gpio_device *gdev;
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
gdev->dev.bus = &gpio_bus_type;
gdev->chip = chip;
chip->gpiodev = gdev;
...
if (chip->label)
gdev->label = kstrdup(chip->label, GFP_KERNEL);
else
gdev->label = kstrdup("unknown", GFP_KERNEL);
gdev->ngpio = chip->ngpio;
...
gdev->base = base;
...
status = gpiodev_add_to_list(gdev);
...
}
通过宏LIST_HEAD 初始化gpio_devices 链表,接着在方法gpiodev_add_to_list() 中通过list_add_tail() 添加数据到gpio_devices 链表中。
查看调用gpiochip_add_data_with_key()方法的地方
bsp/kernel/kernel4.14/include/linux/gpio/driver.h
#ifdef CONFIG_LOCKDEP
#define gpiochip_add_data(chip, data) ({ \
static struct lock_class_key key; \
gpiochip_add_data_with_key(chip, data, &key); \
})
#else
#define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL)
#endif
所以最终需要查看哪儿调用了gpiochip_add_data() 方法
5.1 gpiochip_add_data添加gpiochip 流程
比如我们看bsp/kernel/kernel4.14/drivers/gpio/gpio-sprd.c 驱动
bsp/kernel/kernel4.14/drivers/gpio/gpio-sprd.c
static int sprd_gpio_probe(struct platform_device *pdev)
{
...
struct sprd_gpio *sprd_gpio;
...
sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
...
sprd_gpio->irq = platform_get_irq(pdev, 0);
...
sprd_gpio->chip.label = dev_name(&pdev->dev);
sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
sprd_gpio->chip.base = -1;
sprd_gpio->chip.parent = &pdev->dev;
sprd_gpio->chip.of_node = pdev->dev.of_node;
sprd_gpio->chip.request = sprd_gpio_request;
sprd_gpio->chip.free = sprd_gpio_free;
sprd_gpio->chip.get = sprd_gpio_get;
sprd_gpio->chip.set = sprd_gpio_set;
sprd_gpio->chip.direction_input = sprd_gpio_direction_input;
sprd_gpio->chip.direction_output = sprd_gpio_direction_output;
...
ret = devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
...
}
在probe函数中调用了devm_gpiochip_add_data() 方法向gpio_devices 链表中添加了gpiochip 结构体。
还有两个probe 函数有调用devm_gpiochip_add_data() 方法
sprd_eic_probe
sprd_gpio_probe ----> gpiochip160
sprd_pmic_eic_probe ----> gpiochip144
6. export 节点介绍
在我们想控制某个gpio 口时,我们一般在代码中写如下代码,比如控制gpio11
#define USB_EN_GPIO 11
gpio_request(USB_EN_GPIO, "usb_en");
gpio_direction_output(USB_EN_GPIO, 1);
gpio_export(USB_EN_GPIO, false);
编译和烧录镜像后在sys/class/gpio 目录下生成gpio11 的目录,目录结构如下
-rw-r--r-- 0 0 4096 1970-01-01 01:38 active_low
-rw-r--r-- 0 0 4096 1970-01-01 01:38 direction
drwxr-xr-x 0 0 1970-01-01 00:00 power
lrwxrwxrwx 0 0 1970-01-01 01:38 subsystem -> ../../../../class/gpio
-rw-r--r-- 0 0 4096 1970-01-01 00:00 uevent
-rw-r--r-- 0 0 4096 1970-01-01 01:38 value
然后就可以通过写入value 和 direction 控制gpio 口
还有一种比较方便的方法就是通过sys/class/gpio/export 来实现创建gpioX,比如我们想操作gpio11 ,可以通过如下方法生成gpio11 目录
echo 11 > sys/class/gpio/export
之后会生成sys/class/gpio/gpio11 目录
注意: 如果当前gpio 已经有request 的地方还没有释放,我们再写入export 节点,不会再创建gpioX 目录。
7. export节点写入流程
当我们使用echo 向sys/class/gpio/export 节点写值时会调用export_store() 方法
bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c
static ssize_t export_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t len)
{
long gpio;
struct gpio_desc *desc;
int status;
status = kstrtol(buf, 0, &gpio);
if (status < 0)
goto done;
desc = gpio_to_valid_desc(gpio); // 判断gpio 是否可以使用, 并返回gpio_desc 结构体
/* reject invalid GPIOs */
if (!desc) {
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
return -EINVAL;
}
status = gpiod_request(desc, "sysfs"); // 申请当前gpio,命名为sysfs,如果当前gpio 在其他地方被申请使用,则申请会失败
if (status < 0) {
if (status == -EPROBE_DEFER)
status = -ENODEV;
goto done;
}
status = gpiod_export(desc, true); // export 出gpio口,创建gpioX 目录
if (status < 0)
gpiod_free(desc);
else
set_bit(FLAG_SYSFS, &desc->flags);
done:
if (status)
pr_debug("%s: status %d\n", __func__, status);
return status ? : len;
}
static CLASS_ATTR_WO(export);
最终会通过gpiod_export() 方法将gpio export出来
bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
struct gpio_chip *chip;
struct gpio_device *gdev;
struct gpiod_data *data;
unsigned long flags;
int status;
const char *ioname = NULL;
struct device *dev;
int offset;
...
gdev = desc->gdev;
chip = gdev->chip;
mutex_lock(&sysfs_lock);
...
spin_lock_irqsave(&gpio_lock, flags);
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
test_bit(FLAG_EXPORT, &desc->flags)) {
spin_unlock_irqrestore(&gpio_lock, flags);
gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
__func__,
test_bit(FLAG_REQUESTED, &desc->flags),
test_bit(FLAG_EXPORT, &desc->flags));
status = -EPERM;
goto err_unlock;
}
spin_unlock_irqrestore(&gpio_lock, flags);
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
status = -ENOMEM;
goto err_unlock;
}
data->desc = desc;
mutex_init(&data->mutex);
// 如果已经设置了输入和输出函数,则可以切换为输入和输出
if (chip->direction_input && chip->direction_output)
data->direction_can_change = direction_may_change;
else
data->direction_can_change = false;
offset = gpio_chip_hwgpio(desc);
if (chip->names && chip->names[offset])
ioname = chip->names[offset];
// 创建gpioX 目录及其属性节点
dev = device_create_with_groups(&gpio_class, &gdev->dev,
MKDEV(0, 0), data, gpio_groups,
ioname ? ioname : "gpio%u",
desc_to_gpio(desc));
if (IS_ERR(dev)) {
status = PTR_ERR(dev);
goto err_free_data;
}
set_bit(FLAG_EXPORT, &desc->flags);
mutex_unlock(&sysfs_lock);
return 0;
err_free_data:
kfree(data);
err_unlock:
mutex_unlock(&sysfs_lock);
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
return status;
}
最终会通过device_create_with_groups() 方法创建一个gpioX 的目录,里面有direction、edg、value、active_low等属性节点
bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c
static struct attribute *gpio_attrs[] = {
&dev_attr_direction.attr, // DEVICE_ATTR_RW(direction) 创建
&dev_attr_edge.attr, // DEVICE_ATTR_RW(edge) 创建
&dev_attr_value.attr, // DEVICE_ATTR_RW(value) 创建
&dev_attr_active_low.attr, // DEVICE_ATTR_RW(active_low) 创建
NULL,
};
static const struct attribute_group gpio_group = {
.attrs = gpio_attrs, // gpioX 目录下的属性信息
.is_visible = gpio_is_visible, // 控制哪些属性节点需要创建
};
static const struct attribute_group *gpio_groups[] = {
&gpio_group,
NULL
};
static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct gpiod_data *data = dev_get_drvdata(dev);
struct gpio_desc *desc = data->desc;
umode_t mode = attr->mode;
// 如果该gpio 有输入输出函数,则显示direction 属性节点
bool show_direction = data->direction_can_change;
if (attr == &dev_attr_direction.attr) {
if (!show_direction)
mode = 0;
} else if (attr == &dev_attr_edge.attr) {
// 如果该gpio 是中断模式,则创建edge 属性节点,用于设置中断条件(上拉、下拉、高、低触发中断)
if (gpiod_to_irq(desc) < 0)
mode = 0;
if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags))
mode = 0;
}
return mode;
}
7.1 gpio_to_valid_desc 方法
bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c
static struct gpio_desc *gpio_to_valid_desc(int gpio){
// gpio_is_valid: 如果gpio 大于0 返回 1
return gpio_is_valid(gpio) ? gpio_to_desc(gpio) : NULL;
}
bsp/kernel/kernel4.14/drivers/gpio/gpiolib.c
struct gpio_desc *gpio_to_desc(unsigned gpio)
{
struct gpio_device *gdev;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
// 循环遍历gpio_devices 链表,获取注册的gpiochip 结构体
list_for_each_entry(gdev, &gpio_devices, list) {
// 如果该gpio 属于某个gpiochip,则返回该chip 下的第gpio - gdev->base 个gpio,条件是(base < gpio < base + ngpio)
if (gdev->base <= gpio &&
gdev->base + gdev->ngpio > gpio) {
spin_unlock_irqrestore(&gpio_lock, flags);
return &gdev->descs[gpio - gdev->base];
}
}
spin_unlock_irqrestore(&gpio_lock, flags);
if (!gpio_is_valid(gpio))
WARN(1, "invalid GPIO %d\n", gpio);
return NULL;
}
从上述代码可以看到,通过gpio获取gpio_desc 的条件为gpio需要大于0并且属于某一个chipcode。
8. 创建gpiochip 流程
gpiochip 目录是通过gpiochip_add_data_with_key() 方法将gpiochip 添加到gpio_devices的,我们再接着分析gpiochip_add_data_with_key() 方法
bsp/kernel/kernel4.14/drivers/gpio/gpiolib.c
int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
struct lock_class_key *key)
{
...
if (base < 0) {
// 如果base值小于0,重新计算当前gpiochip 的base 地址
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); // 将gpiochip 添加到gpio_devices 链表中
if (status) {
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
spin_unlock_irqrestore(&gpio_lock, flags);
// 循环遍历当前chip 下所有的gpio
for (i = 0; i < chip->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
desc->gdev = gdev;
// 初始化当前gpio 的输入输出方向
if (chip->get_direction) {
/*
* If we have .get_direction, set up the initial
* direction flag from the hardware.
*/
int dir = chip->get_direction(chip, i);
if (!dir)
set_bit(FLAG_IS_OUT, &desc->flags);
// 如果当前chip 没有定义输入的函数,则将当前gpio设置为输出模式
} else if (!chip->direction_input) {
/*
* If the chip lacks the .direction_input callback
* we logically assume all lines are outputs.
*/
set_bit(FLAG_IS_OUT, &desc->flags);
}
}
...
}
在这里我们已经获取到了每个需要注册的gpiochip,需要判断chip的基地址,如果没有设置,需要重新计算chip 的base 地址
8.1 gpiochip_find_base 计算chip的base地址
bsp/kernel/kernel4.14/drivers/gpio/gpiolib.c
static int gpiochip_find_base(int ngpio)
{
struct gpio_device *gdev;
int base = ARCH_NR_GPIOS - ngpio; // 首先从512 开始逐减计算chip 的base 地址
// 反向循环遍历gpio_device,获取已经加入的gpiochip
// 找到一段空的区间,然后减去需要的长度即是当前chip的base 地址
list_for_each_entry_reverse(gdev, &gpio_devices, list) {
/* found a free space? */
if (gdev->base + gdev->ngpio <= base)
break;
else
/* nope, check the space right before the chip */
base = gdev->base - ngpio;
}
if (gpio_is_valid(base)) {
pr_debug("%s: found new base at %d\n", __func__, base);
return base;
} else {
pr_err("%s: cannot find free range\n", __func__);
return -ENOSPC;
}
}
从adb shell cat sys/kernel/debug/gpio 中的信息可以看到
gpiochip5: GPIOs 144-159, parent: platform/sc27xx-eic, sc27xx-eic:
gpio-144 ( |vbus ) in hi IRQ
gpio-145 ( |Power Key ) in hi IRQ
gpio-147 ( |button ) in lo IRQ
gpio-149 ( |detect_mic ) in lo
gpio-152 ( |detect_l ) in lo
gpio-153 ( |bat-detect ) in hi IRQ
gpio-154 ( |Volume Up Key ) in lo IRQ
gpio-156 ( |detect_h ) in lo
gpio-157 ( |detect_all ) in lo IRQ
gpiochip4: GPIOs 160-415, parent: platform/40280000.gpio, 40280000.gpio:
gpio-160 ( |ap_send_data ) in lo IRQ
gpio-165 ( |chip_en ) out hi
gpio-168 ( |avdd-gpios ) out lo
gpio-185 ( |wcn_power_on_1v2 ) out hi
gpio-186 ( |sdiohal_gpio ) in lo IRQ
gpio-192 ( |dvdd-gpios ) out lo
gpio-201 ( |reset-gpios ) out lo
gpio-204 ( |reset-gpios ) out lo
gpio-205 ( |reset ) out hi
gpio-206 ( |power-down-gpios ) out lo
gpio-207 ( |int_ap ) in lo IRQ
gpio-210 ( |reset ) out lo
gpio-211 ( |sysfs ) out hi
gpio-213 ( |magnetic_reset ) out hi
gpio-215 ( |acceleremter_INT1 ) in lo IRQ
gpio-236 ( |wcn_power_on ) out hi
gpio-238 ( |cd ) in hi IRQ
gpio-247 ( |otg_power_gpio ) out lo
gpio-248 ( |tp_power_gpio ) out hi
gpio-249 ( |flash-chip-en-gpios ) out hi
gpio-250 ( |power ) in hi IRQ
gpio-251 ( |avdd-gpios ) out lo
gpio-301 ( |flash-sync-gpios ) in lo
gpio-304 ( |? ) in hi IRQ
gpio-305 ( |? ) out hi
gpiochip3: GPIOs 416-439, parent: platform/402100c0.gpio, eic-sync:
gpiochip2: GPIOs 440-463, parent: platform/402100a0.gpio, eic-async:
gpiochip1: GPIOs 464-487, parent: platform/40210080.gpio, eic-latch:
gpiochip0: GPIOs 488-511, parent: platform/40210000.gpio, eic-debounce:
gpio-490 ( |Volume Down Key ) in hi IRQ
gpio-492 ( |id ) in hi IRQ
可以看到 eic-debounce 的chip base 地址为488 、40280000.gpio 的base 地址为160 ...... ,所以我们查看sys/class/gpio 下的目录为
- export
- gpiochip144
- gpiochip160
- gpiochip416
- gpiochip440
- gpiochip464
- gpiochip488
- unexport
由于eic-debounce 驱动最先被加载,所以它的base 地址最先分配,所以最大,加载顺序可以从串口log查看,驱动加载按顺序分别为
1. sprd_eic_probe
2. sprd_gpio_probe
3. sprd_pmic_eic_probe
9. 计算gpio 的实际地址
如果我们想在在shell 中控制gpio90 口,通过向sys/class/gpio/export 节点写入gpio 口 创建gpioX 目录,然后通过gpioX 下的属性节点控制gpio 口
9.1 gpio dts 配置
比如我们在dtsi中有如下配置
power-gpio = <&ap_gpio 90 GPIO_ACTIVE_LOW>;
9.2 驱动中使用gpio
这里将ap_gpio 90 口设置为上升沿和下降沿中断
desc->gpiop = devm_gpiod_get(&pdev->dev, "power", GPIOD_IN);
if (IS_ERR(desc->gpiop)) {
dev_err(&pdev->dev, "failed to get power detection GPIO\n");
return PTR_ERR(desc->gpiop);
}
irq_power = gpiod_to_irq(desc->gpiop);
if (irq_power < 0) {
dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n");
return irq_power;
}
ret = devm_request_threaded_irq(&pdev->dev, irq_power, NULL,
sc27xx_fgu_power_detection,
IRQF_ONESHOT | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"power-gpio", cm);
if (ret) {
dev_err(&pdev->dev, "failed to request IRQ\n");
return ret;
}
如果我们要向export 中写值,应该是写多少呢?答案是250
9.3 打印gpio 口使用情况
adb shell cat sys/kernel/debug/gpio
...
gpiochip4: GPIOs 160-415, parent: platform/40280000.gpio, 40280000.gpio:
gpio-160 ( |ap_send_data ) in lo IRQ
gpio-165 ( |chip_en ) out hi
gpio-168 ( |avdd-gpios ) out lo
gpio-185 ( |wcn_power_on_1v2 ) out hi
gpio-186 ( |sdiohal_gpio ) in lo IRQ
gpio-192 ( |dvdd-gpios ) out lo
gpio-201 ( |reset-gpios ) out lo
gpio-204 ( |reset-gpios ) out lo
gpio-205 ( |reset ) out hi
gpio-206 ( |power-down-gpios ) out lo
gpio-207 ( |int_ap ) in lo IRQ
gpio-210 ( |reset ) out lo
gpio-211 ( |sysfs ) out hi
gpio-213 ( |magnetic_reset ) out hi
gpio-215 ( |acceleremter_INT1 ) in lo IRQ
gpio-236 ( |wcn_power_on ) out hi
gpio-238 ( |cd ) in hi IRQ
gpio-247 ( |otg_power_gpio ) out lo
gpio-248 ( |tp_power_gpio ) out hi
gpio-249 ( |flash-chip-en-gpios ) out hi
gpio-250 ( |power ) in hi IRQ
gpio-251 ( |avdd-gpios ) out lo
gpio-301 ( |flash-sync-gpios ) in lo
gpio-304 ( |? ) in hi IRQ
gpio-305 ( |? ) out hi
...
从上可以看到gpio-250 的name 为 power,方向为输入, 当前为高电平,模式为IRQ 中断
为什么是gpio 250 呢,
我们在dts 中配置的是ap_gpio 90,因此我们需要操控的gpio base 地址应该是ap_gpio 的,然后offest 应该为90 ,而ap_gpio 的base 为160。
所以ap_gpio90 口的gpio 号为 base + offset = 160 + 90 = 250
9.4 ap_gpio base 地址查找
在源码中搜索ap_gpio,可以看到节点名应该是gpio@40280000,对应sys/kernel/debug/gpio 打印出来的值为40280000.gpio,所以ap_gpio 的base 地址为160
同理我们查看sys/class/gpio/gpiochip*/label 节点,可以看到gpiochip160/label 的值为40280000.gpio
ap_gpio: gpio@40280000 {
compatible = "sprd,sharkle-gpio";
reg = <0x40280000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
};
当前dts在sprd_gpio_probe()中使用了,并且在此方法中构建了gpiochip目录,可以查看此方法和 “8.1 gpiochip_find_base 计算chip的base地址“ 查看怎么计算的当前chip 的base 地址。