完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
1、简述
GPIO资源是相对归来说简单,而且比较通用(LED灯),而Linux的GPIO驱动是Linux Driver中易上手的部分,简单简单简单,在Linux的系统中,要使用GPIO资源,还是需要了解一些内容。 Linux内核中对GPIO资源进行了抽象,抽象出一个叫做Gpiolib的东东,这个东东作为GPIO资源的管理核心存在: 中间层是Gpiolib,用于管理系统中的GPIO。Gpiolib汇总了GPIO的通用操作,根据GPIO的特性,Gpiolib对上(其他驱动)提供了特定的通用通用的操作GPIO的软件接口,屏蔽了不同的芯片的具体实现。这一套东西。 对于其他驱动来说,比如LED灯驱动,就需要用到通用的Gpiolib的函数来进行I / O口的操作。 2,Gpiolib数据相关分析结构 先分析数据结构,Gpiolib其实就是围绕几个数据结构在文章中,数据结构自然以及层次结构明确了,代码循环 数据结构主要定义在include/linux/gpio/ driver.h和/drivers/gpio/gpiolib.h中 首先看一个数据结构,叫struct gpio_chip (包括/linux/gpio/driver.h): gpio_chip这个数据结构一看,很多函数模块结构,明眼人秒懂,这个是为了通用GPIO的所有操作,同时适配不同结构芯片的一个通用的结构,所以,结构是要对外公开给其他芯片进行特定的的操作赋予的,比如你是 Qcom 的芯片,那么你需要实现你的这些 gpio_chip 的内容。 2.1 gpio_chip 结构 一般,在一个芯片中,针对所有的默认 I/O 口都有配置,默认状态是 I/O 口全部 GPIO 输入(稳当)。一般芯片会提供管脚伴侣的功能(功能)的Linux版本中,使用引脚控制来抽象),使用GPIO,那么首先需要配置他为GPIO功能,而不是其他的服用功能。 而针对GPIO呢,有一些通用的特性,比如设置GPIO的方向,读GPIO的数据(输入外部的),写GPIO的时候对输出的时候(输入输出的时候), gpio_chip的抽象,实际是GPIO一组银行的抽象,通常在硬件上,一个芯片对 IO 口说,分成了很多个 Bank,每个 Bank 又分成了 N 个组 GPIO。 例如:1 个 SoC 将 I/O 分成了 4 个 Bank: Bank 1:GPIOA ~ GPIOB Bank 2:GPIOC ~ GPIOD Bank 3:GPIOE ~ GPIOF Bank 4:GPIOG ~ GPIOH 然,每个 Bank 上方 N 组寄存器来表示 GPIO 的操作,例如: Bank 1 中,针对 GPIO A: GPIO_CFG 来表示对 GPIO A 的配置 GPIOA_PULL 来表示对 GPIO A 的上下拉或者的配置 GPIOA_DIR 来表示对 GPIO A 配置输入输入 GPIOA_DATA 来表示 GPIO A 设置为输出的时候设置为高低输入的时候读高低 当然,Bank 1 中针对 GPIO B,也是一样的操作: GPIO_CFG 来表示对 GPIO B 的配置 GPIOB_PULL 来表示对 GPIO B 的上下拉或者的配置 GPIOB_DIR 来表示对 GPIO B 配置输入 GPIOB_DATA 来表示 GPIO B 设置为输出的时候设置为高低输入的时候读高低 上面说的是一个银行的情况,那么芯片有好几个银行,所以它们都是类似的,这里不在赘述。 所以整体结构是如下所示(这里只是打个比方,有的芯片银行很多,寄存器也很多): Linux 驱动 Gpiolib 对他们的抽象,使用gpio_chip 对应了一组银行描述,例如银行·1,用一个 gpio_chip 来抽象: 那么几个银行,就用交通,或者数组来表示咯。当然这里可能得有点不准确,gpio_chip 单一了一个银行的接口的 故障。那么对于一颗芯片来说,需要根据芯片手册数据表,来实现这些结构的接口。 2.2 gpio_desc 结构 由系统分为多个银行,每家银行又由几组组成,就那么每个 GPIO 实体由一个 gpio_desc 来描述: gdev 探寻这个状态它本次的 gpio_desc 所属的 gpio_device(马上描述),标志代表了 GPIO 的属性; gpio_chip 和 gpio_desc 应该是有关系的,内核中并没有直接将两个结构上,可以通过另外一个结构将其联系在一起,这个结构就是gpio_device。 2.3 gpio_device结构 gpio_device应该算是大内总管了(最新的内核有,Linux 3的版本的内核没有这个),如果说gpio_chip是对一个银行的GPIO的硬件具体的抽象的话,那么gpio_device就是软件上对一个Bank的GPIO进行管理的单元,它的数据结构是: 在这个gpio_device结构中,包含了gpio_chip(对接芯片的操作集),gpio_desc(GPIO的描述);这个结构因为整个Gpiolib,gpio_device代表了一个Bank,一般的GPIO有多个Bank,所以Kernel中,对这 gpio_device 的组织是由一个gpio_devices的链表构成(这里是多个设备,所以又加了 s),在 gpiolib.c 中: LIST_HEAD(gpio_devices); 3、Gpiolib 那些对接讲讲 先聊的 Gpiolib 是对接到的特殊用途的驱动的。在前面的部分。1.2讲过,可能需要对接的,重点对接的部分只有通用的,其实也就是gpio_chip这个玩玩意,所以,对接底层的部分,主要关心的是这个结构体,并且对这个结构体进行赋值的。 在底层对接到Gpiolib的时候,主要是对gpio_chip进行实现,然后调用gpiochip_add的接口,向Gpiolib注册你实现的 GPIO 的过程,主要是根据芯片手册,实现 GPIO 的操作,对应把操作编程编程成为函数,对接到这个 gpio_chip 体上的结构。 打比方,对应 Exynos 4 说: 在gpio-exynos4.c 中: 定义好各个银行的基地址和 GPIO 的个数。以及支持中断的情况。 在他的初始化函数中: 调用了 xxx_gpiolib_add_xxxx 的函数,在一些列的调用中,就将 gpio_chip 结构体相关的成员进行了调用,并最终调用到了gpiochip_add函数,其注册到了内核的Gpiolib。将他们的流程是这样的,一些细节,可能是因为版本特性,内容有差异。 在较新的内核版本中,内核提供了devm的接口,用于管理使用的内存情况,那么这个gpiochip_add_data变为了: 。devm_gpiochip_add_data接下来我们看看这个注册GPIO的函数 3.1,注册GPIO资源(gpiochip_add) 前面也说过了,注册一个GPIO资源使用接口: INT gpiochip_add( struct gp_chip *chip) 结构是gpio芯片,就是一个Bank的描述,那么实际上,CPU的GPIO控制器有多个I/O Bank,那部分也需要内核管理的GPIO Bank部分,都需要调用这个接口,注册进内核(当然,你也可以野蛮的旁路); 在旧的内核中,对接的部分,是关键银行直接调用了gpiochip_add函数,新的内核支持补充数据,就是带芯片数据所以,新内核支持的注册接口是: gpiochip_add_data(chip, data) 他的定义是: #define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL, NULL) gpio_chip 的结构,data 是 void * data,合法数据。 为了原来以前的驱动,所以在新的结构中gpiochip_add的定义是: static inline int gpiochip_add(struct gpio_chip *chip){ return gpiochip_add_data(chip, NULL);}也就是不带数据的调用; 当然,Gpiolib也支持带devm的接口,管理其内存: 所以,可以看到,前前后的几个注册的接口,最后都可以到:gpiochip_add_data_with_key 下就来分析一下这个接口; 3.2、gpiochip_add_data_with_key 这个函数查看回顾,先是part1: 。。。。。。。。。。。。。。。。。。。。。。第 1 部分开始。。。。。。。。。。。。。。。。。。。。。。.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; 整数状态 = 0; 未签名的我;int base =chip-》base; 结构 gpio_device *gdev; /* * 首先:分配并填充内部统计容器,并 * 设置结构设备。*/ gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); 如果 (!gdev) 返回 -ENOMEM; gdev-》dev.bus = &gpio_bus_type; gdev-》芯片 = 芯片;芯片-》 gpiodev = gdev; if (chip-》parent) { gdev-》dev.parent = 芯片-》parent; gdev-》dev.of_node = 芯片-》父-》of_node;}#ifdef CONFIG_OF_GPIO /* 如果 gpiochip 具有指定的 OF 节点,则优先 */ if (chip-》of_node) gdev-》dev.of_node = chip-》of_node; else 芯片-》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; 转到 err_free_gdev; } dev_set_name(&gdev-》dev, ”gpiochip%d“, gdev-》id); device_initialize(&gdev-》dev); dev_set_drvdata(&gdev-》dev, gdev); if (chip-》parent &&chip-》parent-》driver) gdev-》owner =chip-》parent-》driver-》owner; 否则如果(芯片-》 owner) /* TODO: 移除chip-》owner */ gdev-》owner = chip-》owner; else gdev-》owner = THIS_MODULE;。。。。。。。。。。第 1 部分结束。。。。。。 。。。。。。。} 1 中,因为一部分的结构是 gpio_chip,他代表了一个银行,但并没有 gpio_device 的结构,所以,在这个函数中,首先分配一个 gpio_device 的结构,分配其结构体成员的芯片,等等进行创建,建立起相关的结构联系。 再看第二部分: 。。。。。。。第 2 部分开始。。。。。。。 gdev-》descs = kcalloc(chip-》ngpio, sizeof(gdev-》descs[ 0]), GFP_KERNEL); if (!gdev-》descs) { status = -ENOMEM; 转到 err_free_ida; } if (chip-》ngpio == 0) { chip_err(chip, ”试图插入一个带有零行的 GPIO 芯片n“); 状态 = -EINVAL; 转到 err_free_descs; } if (chip-》ngpio 》 FASTPATH_NGPIO) chip_warn(chip, ”line cnt %u is大于fast path cnt %un“,chip-》ngpio, FASTPATH_NGPIO); gdev-》label = kstrdup_const(chip-》label ?: ”unknown“, GFP_KERNEL); if (!gdev-》label) { status = -ENOMEM; 转到 err_free_descs; } gdev-》ngpio = 芯片-》 ngpio; gdev-》data = data;。。。。。。。。。。。。。。。.part 2 end.。。。。。。。 part 2 中,由于1个银行只有一个GPIO,所以 gpio_chip-》ngpio 的结构表示了这个 Bank 一共的 GPIO 个数,每一个 GPIO 使用一个 gpio_desc 表示,所以,这里分配了 ngpio 个 descs; 最后是第三部分: 。。。。。。。第 3 部分开始。。。。。。。 spin_lock_irqsave(&gpio_lock, flags); /* * TODO:这会在全局 * GPIO 编号空间中为此芯片分配一个 Linux GPIO 编号库。从长远来看,我们希望*摆脱*这个数字空间并只使用描述符,但这可能是一个白日梦。无论如何,在我们摆脱 * sysfs 接口之前不会发生这种情况。*/ if (base 《 0) { base = gpiochip_find_base(chip-》ngpio); 如果(基数 《 0){ 状态 = 基数;spin_unlock_irqrestore(&gpio_lock, flags); 转到 err_free_label; } /* * 去做:不必在 GPIO 子系统之外反映分配的 * 基数。检查驱动程序并*看看是否有人使用它,否则丢弃它并分配*毒药。*/ 芯片-》基数 = 基数;gdev-》base = base; 状态 = gpiodev_add_to_list(gdev); if (status) { spin_unlock_irqrestore(&gpio_lock, flags); 转到 err_free_label; } spin_unlock_irqrestore(&gpio_lock, flags); for (i = 0; i 《chip-》ngpio; i++) gdev-》descs 基地 = 基地; 状态 = gpiodev_add_to_list(gdev); if (status) { spin_unlock_irqrestore(&gpio_lock, flags); 转到 err_free_label; } spin_unlock_irqrestore(&gpio_lock, flags); for (i = 0; i 《chip-》ngpio; i++) gdev-》descs 基地 = 基地; 状态 = gpiodev_add_to_list(gdev); if (status) { spin_unlock_irqrestore(&gpio_lock, flags); 转到 err_free_label; } spin_unlock_irqrestore(&gpio_lock, flags); for (i = 0; i 《chip-》ngpio; i++) gdev-》descs.gdev = gdev;#ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev-》pin_ranges);#endif status = gpiochip_set_desc_names(chip); 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_remove_chip; status = of_gpiochip_add(chip); if (status) goto err_remove_chip; status = gpiochip_init_valid_mask(chip); if (status) goto err_remove_chip; for (i = 0; i 《 chip-》ngpio; i++) { struct gpio_desc *desc = &gdev-》descs; if (chip-》get_direction && gpiochip_line_is_valid(chip, i)) desc-》flags = !chip-》get_direction(chip, i) ? (1 《《 FLAG_IS_OUT) : 0; else desc-》flags = !chip-》direction_input ? (1 《《 FLAG_IS_OUT) : 0; } 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_chip; } return 0;err_remove_chip: acpi_gpiochip_remove(chip); gpiochip_free_hogs(chip); of_gpiochip_remove(chip); 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, %dn“, __func__, gdev-》base, gdev-》base + gdev-》ngpio - 1, chip-》label ? : ”generic“, status); kfree(gdev); return status;。。。。。。。。。。。。.part 3 end.。。。。。。。。。。。。 在 part 3 中,base 代表了每个 Bank 的编号,将其赋值;然后通过 gpiodev_add_to_list(gdev) 将这个 gdev 挂到全局的 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 chipn“); return -EBUSY;} 接着就是设置一些 name 字段,配置中断之类的,初始化每个 desc[] 结构的 flags,最后调用: if (gpiolib_initialized) { status = gpiochip_setup_dev(gdev); if (status) goto err_remove_chip; } return 0; 然后,不出意外的话,返回 0; 这里说一下 gpiochip_setup_dev 调用,这个是在 Gpiolib init 的时候调用 gpiochip_setup_devs: static int __init gpiolib_dev_init(void){ int ret; /* Register GPIO sysfs bus */ ret = bus_register(&gpio_bus_type); if (ret 《 0) { pr_err(”gpiolib: could not register GPIO bus typen“); return ret; } ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, ”gpiochip“); if (ret 《 0) { pr_err(”gpiolib: failed to allocate char dev regionn“); bus_unregister(&gpio_bus_type); } else { gpiolib_initialized = true; gpiochip_setup_devs(); } return ret;}core_initcall(gpiolib_dev_init); 而这个 gpiochip_setup_devs 对每一个 gpio_devicecs 节点调用:gpiochip_setup_dev: static void gpiochip_setup_devs(void){ struct gpio_device *gdev; int err; list_for_each_entry(gdev, &gpio_devices, list) { err = gpiochip_setup_dev(gdev); if (err) pr_err(”%s: Failed to initialize gpio device (%d)n“, dev_name(&gdev-》dev), err); }} 最后到: static int gpiochip_setup_dev(struct gpio_device *gdev){ int status; cdev_init(&gdev-》chrdev, &gpio_fileops); gdev-》chrdev.owner = THIS_MODULE; gdev-》dev.devt = MKDEV(MAJOR(gpio_devt), gdev-》id); status = cdev_device_add(&gdev-》chrdev, &gdev-》dev); if (status) return status; chip_dbg(gdev-》chip, ”added GPIO chardev (%d:%d)n“, MAJOR(gpio_devt), gdev-》id); status = gpiochip_sysfs_register(gdev); if (status) goto err_remove_device; /* From this point, the .release() function cleans up gpio_device */ gdev-》dev.release = gpiodevice_release; pr_debug(”%s: registered GPIOs %d to %d on device: %s (%s)n“, __func__, gdev-》base, gdev-》base + gdev-》ngpio - 1, dev_name(&gdev-》dev), gdev-》chip-》label ? : ”generic“); return 0;err_remove_device: cdev_device_del(&gdev-》chrdev, &gdev-》dev); return status;} 其实就是注册了字符设备,并且添加到了 sysfs; 个人理解,因为不知道这个 init 和我们的对接底层的驱动的 init 谁先执行到,所以用了一个变量 gpiolib_initialized 来表示当前的 Gpiolib 是不是已经完成了相关的字符设备的注册,如果是 Gpiolib 先去 init 的话,那么 gpiolib_initialized ture,芯片对接底层的部分错过 gpio_chip setup 的机会,所以需要重新调用这个 gpiochip_setup_dev 接口,反之 OK; 到这里,对接底层驱动的部分基本上 OK 了,小伙伴们需要按照自己芯片的 Specific 去做自己的 gpio_chip 结构并最终通过 gpiochip_add_data 添加到 Gpiolib 子系统中; 还有一点需要注意到的是,小伙伴们需要自行定义一些结构,来获得并表示自己 Bank 的虚拟地址等等,这样才能操作到实际的硬件寄存器; 4、Gpiolib 为其他驱动提供的 APIs 在对接底层完成后,Gpiolib 为其他的驱动提供了一些列的调用接口: 1、gpio_request:向内核申请 gpio 要使用 GPIO 首先应该向内核进行申请,返回 0,代表申请成功,可以进行后续操作 2、gpio_free: 对应 gpio_request,是使用完gpio以后把gpio释放掉 3、gpio_direction_input :设置 GPIO 为输入 4、gpio_direction_output:设置 GPIO 为输出 5、gpio_get_value :读取 GPIO 的值 6、gpio_set_value:设置 GPIO 口的值 4.1、gpio_request 其他驱动使用 GPIO 的时候呢,需要先调用这个接口,向 Gpiolib 进行申请 GPIO : int gpio_request(unsigned gpio, const char *label){ struct gpio_desc *desc = gpio_to_desc(gpio); /* Compatibility: assume unavailable ”valid“ GPIOs will appear later */ if (!desc && gpio_is_valid(gpio)) return -EPROBE_DEFER; return gpiod_request(desc, label);}EXPORT_SYMBOL_GPL(gpio_request); 他传入的参数有一个 gpio 和一个 label,gpio 参数是一个数字,代表着板子上 GPIO 的编号,什么叫编号呢?请注意,这里的编号,有别于 Datasheet 中的编号,要知道这个,我们先好好了解一下 gpio_chip-》base 的成员,打个比如,很多人都了解过三星的 2440 或者 6410 的芯片,对于他们的 GPIO 定义,我们来看看如何定义这个 gpio_chip-》base 成员的: static struct samsung_gpio_chip s3c64xx_gpios_4bit[] = {#ifdef CONFIG_ARCH_S3C64XX { .chip = { .base = S3C64XX_GPA(0), .ngpio = S3C64XX_GPIO_A_NR, .label = ”GPA“, }, }, { .chip = { .base = S3C64XX_GPB(0), .ngpio = S3C64XX_GPIO_B_NR, .label = ”GPB“, }, }, { .chip = { .base = S3C64XX_GPC(0), .ngpio = S3C64XX_GPIO_C_NR, .label = ”GPC“, },。。。。。。。。。。。。。。。。。。。。。。。} 这里我们看到的 .base 就是 gpio_chip-》base ,他别定义为 S3C64XX_GPA(0)、S3C64XX_GPB(0)、S3C64XX_GPC(0)。。。。。。他定义在: gpio_samsung.h /* GPIO bank sizes */#define S3C64XX_GPIO_A_NR (8)#define S3C64XX_GPIO_B_NR (7)#define S3C64XX_GPIO_C_NR (8)#define S3C64XX_GPIO_D_NR (5)#define S3C64XX_GPIO_E_NR (5)#define S3C64XX_GPIO_F_NR (16)#define S3C64XX_GPIO_G_NR (7)#define S3C64XX_GPIO_H_NR (10)#define S3C64XX_GPIO_I_NR (16)#define S3C64XX_GPIO_J_NR (12)#define S3C64XX_GPIO_K_NR (16)#define S3C64XX_GPIO_L_NR (15)#define S3C64XX_GPIO_M_NR (6)#define S3C64XX_GPIO_N_NR (16)#define S3C64XX_GPIO_O_NR (16)#define S3C64XX_GPIO_P_NR (15)#define S3C64XX_GPIO_Q_NR (9)/* GPIO bank numbes *//* CONFIG_S3C_GPIO_SPACE allows the user to select extra * space for debugging purposes so that any accidental * change from one gpio bank to another can be caught.*/#define S3C64XX_GPIO_NEXT(__gpio) ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)enum s3c_gpio_number { S3C64XX_GPIO_A_START = 0, S3C64XX_GPIO_B_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_A), S3C64XX_GPIO_C_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_B), S3C64XX_GPIO_D_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_C), S3C64XX_GPIO_E_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_D), S3C64XX_GPIO_F_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_E), S3C64XX_GPIO_G_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_F), S3C64XX_GPIO_H_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_G), S3C64XX_GPIO_I_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_H), S3C64XX_GPIO_J_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_I), S3C64XX_GPIO_K_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_J), S3C64XX_GPIO_L_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_K), S3C64XX_GPIO_M_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_L), S3C64XX_GPIO_N_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_M), S3C64XX_GPIO_O_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_N), S3C64XX_GPIO_P_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_O), S3C64XX_GPIO_Q_START = S3C64XX_GPIO_NEXT(S3C64XX_GPIO_P),};/* S3C64XX GPIO number definitions. */#define S3C64XX_GPA(_nr) (S3C64XX_GPIO_A_START + (_nr))#define S3C64XX_GPB(_nr) (S3C64XX_GPIO_B_START + (_nr))#define S3C64XX_GPC(_nr) (S3C64XX_GPIO_C_START + (_nr))#define S3C64XX_GPD(_nr) (S3C64XX_GPIO_D_START + (_nr))#define S3C64XX_GPE(_nr) (S3C64XX_GPIO_E_START + (_nr))#define S3C64XX_GPF(_nr) (S3C64XX_GPIO_F_START + (_nr))#define S3C64XX_GPG(_nr) (S3C64XX_GPIO_G_START + (_nr))#define S3C64XX_GPH(_nr) (S3C64XX_GPIO_H_START + (_nr))#define S3C64XX_GPI(_nr) (S3C64XX_GPIO_I_START + (_nr))#define S3C64XX_GPJ(_nr) (S3C64XX_GPIO_J_START + (_nr))#define S3C64XX_GPK(_nr) (S3C64XX_GPIO_K_START + (_nr))#define S3C64XX_GPL(_nr) (S3C64XX_GPIO_L_START + (_nr))#define S3C64XX_GPM(_nr) (S3C64XX_GPIO_M_START + (_nr))#define S3C64XX_GPN(_nr) (S3C64XX_GPIO_N_START + (_nr))#define S3C64XX_GPO(_nr) (S3C64XX_GPIO_O_START + (_nr))#define S3C64XX_GPP(_nr) (S3C64XX_GPIO_P_START + (_nr))#define S3C64XX_GPQ(_nr) (S3C64XX_GPIO_Q_START + (_nr)) 有上面的定义可知: Bank A base -》 S3C64XX_GPA(0) -》 0 Bank B base -》 S3C64XX_GPB(0) -》 S3C64XX_GPIO_A_START(=0) + S3C64XX_GPIO_A_NR(=8) Bank C base -》 S3C64XX_GPC(0) -》 S3C64XX_GPIO_B_START(8) + S3C64XX_GPIO_B_NR(=7) 。。。。。。。。。。。。。。。。。。。。 所以呢,所有的 GPIO 都是按照这个从 0 开始的顺序进行编号的,在这款芯片软件层的对接 Gpiolib 的实现上,每一个 Bank 的 base 被设计成为了上一个 GPIO 的起始位置,加上上一个 GPIO 的个数,最终得出,所有的 GPIO 都是按照顺序,在软件层面上,进行统一编号,这个 gpio_chip-》base 值,就是指的每个 Bank 的起始编号! 言归正传,gpio_request 函数中的这个 gpio 入参,就是指的这些被编号了的 GPIO 的号码,你要他,就要翻他的牌! 在 gpio_request 实现中,首先调用了 gpio_to_desc 去根据传入的 gpio 的牌子,去索引到 gpio_desc 结构(还记得么,一个 gpio_desc 表征了一个 GPIO 的实例) struct gpio_desc *gpio_to_desc(unsigned gpio){ struct gpio_device *gdev; unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(gdev, &gpio_devices, list) { 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 %dn“, gpio); return NULL;}EXPORT_SYMBOL_GPL(gpio_to_desc); 可以看到,其实就是遍历了 gpio_devices ,然后如果你传入的编号在这个 Bank 之内,OK,给你返回这个这个 desc 结构; 然后调用到 Gpiolib 的 gpiod_request: int gpiod_request(struct gpio_desc *desc, const char *label){ int status = -EPROBE_DEFER; struct gpio_device *gdev; VALIDATE_DESC(desc); gdev = desc-》gdev; if (try_module_get(gdev-》owner)) { status = gpiod_request_commit(desc, label); if (status 《 0) module_put(gdev-》owner); else get_device(&gdev-》dev); } if (status) gpiod_dbg(desc, ”%s: status %dn“, __func__, status); return status;} 走到 gpiod_request_commit: static int gpiod_request_commit(struct gpio_desc *desc, const char *label){ struct gpio_chip *chip = desc-》gdev-》chip; int status; unsigned long flags; unsigned offset; spin_lock_irqsave(&gpio_lock, flags); /* NOTE: gpio_request() can be called in early boot, * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ if (test_and_set_bit(FLAG_REQUESTED, &desc-》flags) == 0) { desc_set_label(desc, label ? : ”?“); status = 0; } else { status = -EBUSY; goto done; } if (chip-》request) { /* chip-》request may sleep */ spin_unlock_irqrestore(&gpio_lock, flags); offset = gpio_chip_hwgpio(desc); if (gpiochip_line_is_valid(chip, offset)) status = chip-》request(chip, offset); else status = -EINVAL; spin_lock_irqsave(&gpio_lock, flags); if (status 《 0) { desc_set_label(desc, NULL); clear_bit(FLAG_REQUESTED, &desc-》flags); goto done; } } if (chip-》get_direction) { /* chip-》get_direction may sleep */ spin_unlock_irqrestore(&gpio_lock, flags); gpiod_get_direction(desc); spin_lock_irqsave(&gpio_lock, flags); }done: spin_unlock_irqrestore(&gpio_lock, flags); return status;} 针对这 GPIO,如果没人去 FLAG_REQUESTED 的话,那么按照传入的 label 设置他的标签,在获得他的一些方向,等状态; 如果已经被 FLAG_REQUESTED 了的话,返回 -EBUSY,请求 GPIO 失败。 4.2、gpio_direction_input/gpio_direction_output 当 request 成功后,你就可以用你成功的这个 gpio 来搞事情了,如果要读 GPIO 状态,则需要配置成为 input,写的话,配置 output(点个 LED 灯); 这个两个函数很简单: static inline int gpio_direction_input(unsigned gpio){ return gpiod_direction_input(gpio_to_desc(gpio));}static inline int gpio_direction_output(unsigned gpio, int value){ return gpiod_direction_output_raw(gpio_to_desc(gpio), value);} 不在过多分析,其实就是根据传入的 gpio 的编号,也是转换成为了 desc 后,获得了 Bank 的 gpio_chip 结构,然后调用了挂接上去的 direction_input/direction_output 而已,我们看一个实际的底层实现: static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, unsigned int offset){ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); void __iomem *base = ourchip-》base; void __iomem *regcon = base; unsigned long con; if (offset 》 7) offset -= 8; else regcon -= 4; con = __raw_readl(regcon); con &= ~(0xf 《《 con_4bit_shift(offset)); __raw_writel(con, regcon); pr_debug(”%s: %p: CON %08lxn“, __func__, base, con); return 0;} static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, unsigned int offset, int value){ struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); void __iomem *base = ourchip-》base; void __iomem *regcon = base; unsigned long con; unsigned long dat; unsigned con_offset = offset; if (con_offset 》 7) con_offset -= 8; else regcon -= 4; con = __raw_readl(regcon); con &= ~(0xf 《《 con_4bit_shift(con_offset)); con |= 0x1 《《 con_4bit_shift(con_offset); dat = __raw_readl(base + GPIODAT_OFF); if (value) dat |= 1 《《 offset; else dat &= ~(1 《《 offset); __raw_writel(dat, base + GPIODAT_OFF); __raw_writel(con, regcon); __raw_writel(dat, base + GPIODAT_OFF); pr_debug(”%s: %p: CON %08lx, DAT %08lxn“, __func__, base, con, dat); return 0;} 4.3、gpiod_set_value/gpiod_get_value 读GPIO和写GPIO差不多,这里不在多说,一眼便知; |
|
|
|
只有小组成员才能发言,加入小组>>
4540个成员聚集在这个小组
加入小组3343 浏览 0 评论
航顺(HK)联合电子发烧友推出“近距离体验高性能Cortex-M3,免费申请价值288元评估板
4270 浏览 1 评论
4296 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-28 11:18 , Processed in 0.466981 second(s), Total 45, Slave 38 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号