您的位置:首页 > 运维架构 > Linux

【Linux基础系列之】gpio系统

2017-08-18 15:55 260 查看
  上一章介绍了pinctrl系统管理所有的物理pin脚,gpio也是pin脚的一种,所以需要某个gpio的时候就需要通过pinctrl把某个pin脚设置为gpio功能就即可;在pinctrl系统提供了操作pin脚的统一API接口,同时也可以用gpio的API来操作,在request gpio表明该pin没有被挪为他用之后,就可以设置该GPIO的输入输出,驱动能力,或者debounce功能等;

(一) GPIO 初始化

  pinctrl有讲到一个pinctrl可以管理着个gpio range链表,每个node管理着一定范围内的gpio,可以叫作gpio bank,我们把它实例话成一个设备,用gpio_chip来表示,下面为各个结构关系图:



通过gpiochip_add()将gpio chip添加到gpio子系统中:

299 int gpiochip_add(struct gpio_chip *chip)
300 {
301     unsigned long   flags;
302     int     status = 0;
303     unsigned    id;
304     int     base = chip->base; //这个chip处理的第一个gpio number;
305     struct gpio_desc *descs;
306
307     descs = kcalloc(chip->ngpio, sizeof(descs[0]), GFP_KERNEL);
308     if (!descs)
309         return -ENOMEM;
310
311     spin_lock_irqsave(&gpio_lock, flags);
312
313     if (base < 0) {
//如果没有指定base,那么需要基于该chip的gpio数量在系统支持的gpio范围里找一段区间给该chip;
//一般平台默认为512个gpio,通过扫描gpio_chips全局chip链表,分配一段free的gpio空间;
314         base = gpiochip_find_base(chip->ngpio); //ngpio为该chip处理的gpio个数;
315         if (base < 0) {
316             status = base;
317             spin_unlock_irqrestore(&gpio_lock, flags);
318             goto err_free_descs;
319         }
320         chip->base = base;
321     }
322  //到这里的时候,说明一切正常,把它加入到全局的gpiochip链表中去,注意,加入的时候会基于base排序;
323     status = gpiochip_add_to_list(chip);
324     if (status) {
325         spin_unlock_irqrestore(&gpio_lock, flags);
326         goto err_free_descs;
327     }
328     //如果加入成功,最后一步就是初始化该chip对应的那些gpio了;
329     for (id = 0; id < chip->ngpio; id++) {
330         struct gpio_desc *desc = &descs[id]; //每个gpio分配一个gpio_desc来描述;
331
332         desc->chip = chip;
333         //默认指定为输入为好,考虑到大多数情况下推挽式输出,防止漏电;
340         desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0;
341     }
342
343     chip->desc = descs;
344
345     spin_unlock_irqrestore(&gpio_lock, flags);
346
347 #ifdef CONFIG_PINCTRL
348     INIT_LIST_HEAD(&chip->pin_ranges);
349 #endif
350
351     if (!chip->owner && chip->dev && chip->dev->driver)
352         chip->owner = chip->dev->driver->owner;
353
354     status = gpiochip_set_desc_names(chip);
355     if (status)
356         goto err_remove_from_list;
357
358     status = of_gpiochip_add(chip); // 初始化设备树相关的信息;
359     if (status)
360         goto err_remove_chip;
361     //添加chip到ACPI(Advanced Configuration & Power Interface.)
362     acpi_gpiochip_add(chip);
363
364     status = gpiochip_sysfs_register(chip);// 将该gpiochip导出到sys,用于调试和应用层直接操作;
372     return 0;
373
391 }
392 EXPORT_SYMBOL_GPL(gpiochip_add);


422 int of_gpiochip_add(struct gpio_chip *chip)
423 {
424     int status;
425
426     if ((!chip->of_node) && (chip->dev))
427         chip->of_node = chip->dev->of_node;
428
429     if (!chip->of_node)
430         return 0;
431
432     if (!chip->of_xlate) {
433         chip->of_gpio_n_cells = 2;
434         chip->of_xlate = of_gpio_simple_xlate; //of_xlate用于解析设备树里gpio属性,不同的soc可能需要不同的解析方法;
435     }
436
437     status = of_gpiochip_add_pin_range(chip);//从dts解析pin range,一般没有定义gpio-ranges和gpio-ranges-group-names;都是用gpiochip_add_pin_range()来添加,下面在介绍;
438     if (status)
439         return status;
440
441     of_node_get(chip->of_node);//增加该节点引用计数
442
443     of_gpiochip_scan_gpios(chip);
444
445     return 0;
446 }


  总结gpiochip_add()函数的作用:

将chip添加到全局的gpio chip链表中,用于gpio chip冲突处理和gpio管理 ;

将该gpio chip对应的那段gpio都初始化 ;

初始化设备树相关的信息,用于后面的属性解析及向pinctrl子系统同步下;

导入sys目录;

  gpiochip_add_pin_range():

697 int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
698                unsigned int gpio_offset, unsigned int pin_offset,
699                unsigned int npins)
700 {
701     struct gpio_pin_range *pin_range;
702     int ret;
703
704     pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
709
710     /* Use local offset as range ID */
711     pin_range->range.id = gpio_offset;//GPIO chip ID
712     pin_range->range.gc = chip;//每个GPIO bank都是一个gpio chip,对应一个GPIO range
713     pin_range->range.name = chip->label;
714     pin_range->range.base = chip->base + gpio_offset;
715     pin_range->range.pin_base = pin_offset;//在线性映射的情况下,这是起始的pin base;
716     pin_range->range.npins = npins;//这个range有多少个GPIO引脚;
717     pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
718             &pin_range->range);
719     if (IS_ERR(pin_range->pctldev)) {
720         ret = PTR_ERR(pin_range->pctldev);
721         chip_err(chip, "could not create pin range\n");
722         kfree(pin_range);
723         return ret;
724     }
725     chip_dbg(chip, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
726          gpio_offset, gpio_offset + npins - 1,
727          pinctl_name,
728          pin_offset, pin_offset + npins - 1);
729
730     list_add_tail(&pin_range->node, &chip->pin_ranges);
731
732     return 0;
733 }
734 EXPORT_SYMBOL_GPL(gpiochip_add_pin_range);


  一个pin control device要管理的GPIO ID是分成区域的,每个区域用struct pinctrl_gpio_range来抽象,在pin controller初始化的时候,会调用pinctrl_add_gpio_range将每个GPIO bank表示的gpio range挂入到pin control device的range list中(gpio_ranges成员);

  当管脚控制子系统中GPIO特定函数被调用后,这些范围将用于查找合适的管脚控制器,通过检查且匹配管脚到所有控制器的管脚范围。当匹配到一个管脚控制器的处理范围时,GPIO-specific函数在此管脚控制器上被调用。

(二) GPIO API

  linux内核中的gpio接口目前有新旧两个版本,新的版本的接口是descriptor-based的,而旧的是integer-based的。旧的接口已出于兼容性的考虑仍被支持,但已不再建议使用。

legacy gpio API接口:

//请求一个/一组gpio
gpio_request/devm_gpio_request、gpio_request_one/devm_gpio_request_one、gpio_request_array

//设置gpio方向为输入/输出
gpio_direction_input或者gpio_direction_output

//如果gpio为输入,获取gpio值,如果gpio为输出,可以设置gpio高低电平
gpio_get_value、gpio_set_value

//将gpio转为对应的irq,然后注册该irq的中断handler
request_irq(gpio_to_irq(gpio_num)...)

//释放请求的一个或者一组gpio
gpio_free/devm_gpio_free、gpio_free_array


descriptor-based gpio API 操作借口:

#include <linux/gpio/consumer.h>
//需要include consumer.h;

//请求第一个/指定某一个gpio desc,该返回值用于后面的操作;
//gpiod_get和gpiod_get_index则是通过解析设备树信息,提取里面的gpio号,然后再转换;
gpiod_get/devm_gpiod_get、gpiod_get_index/devm_gpiod_get_index

//设置gpio方向为输入/输出;
gpiod_direction_input或者gpiod_get_direction

//将该gpio通过sys文件系统导出,应用层可以通过文件操作gpio;
gpiod_export

//如果gpio为输入,获取gpio值,如果gpio为输出,可以设置gpio高低电平;
gpiod_get_value或者gpiod_set_value

//将gpio转为对应的irq,然后注册该irq的中断handler;
request_irq(gpiod_to_irq(gpio_desc)...)

//释放请求的一个或者一组gpio;
gpiod_put/devm_gpiod_put


(1) gpiod_request

  下面分析gpiod_request():

934 int gpiod_request(struct gpio_desc *desc, const char *label)
935 {
936     int status = -EPROBE_DEFER;
937     struct gpio_chip *chip;
938     //检查desc是否有效,gpio_request会根据传入的gpio号在全局的desc里定位到desc;可以通过gpiod_find()获取desc;
939     if (!desc) {
940         pr_warn("%s: invalid GPIO\n", __func__);
941         return -EINVAL;
942     }
943
944     chip = desc->chip;//获取该desc的chip;
945     if (!chip)
946         goto done;
947
948     if (try_module_get(chip->owner)) {//该module是否有效;
949         status = __gpiod_request(desc, label);
950         if (status < 0)
951             module_put(chip->owner);
952     }
958     return status;
959 }


891 static int __gpiod_request(struct gpio_desc *desc, const char *label)
892 {
910
911     if (chip->request) {
912         /* chip->request may sleep */
913         spin_unlock_irqrestore(&gpio_lock, flags);
914         status = chip->request(chip, gpio_chip_hwgpio(desc));
915         spin_lock_irqsave(&gpio_lock, flags);
922     }
923     if (chip->get_direction) {
924         /* chip->get_direction may sleep */
925         spin_unlock_irqrestore(&gpio_lock, flags);
926         gpiod_get_direction(desc);//调用chip的get_direction;
927         spin_lock_irqsave(&gpio_lock, flags);
928     }
931     return status;
932 }


  request通常是直接调用gpiochip_generic_request() -> pinctrl_request_gpio()传入chip和gpio偏移:

570 int pinctrl_request_gpio(unsigned gpio)
571 {
572     struct pinctrl_dev *pctldev;
573     struct pinctrl_gpio_range *range;
574     int ret;
575     int pin;
576     //两个链表轮询先在全局pinctrldev_list列表获取一个pinctrl_dev然后根据gpio是否在这个range内,返回该pinctrl_dev和range;
577     ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
578     if (ret) {
579         if (pinctrl_ready_for_gpio_range(gpio))
580             ret = 0;
581         return ret;
582     }
583
584     mutex_lock(&pctldev->mutex);
585
586     /* Convert to the pin controllers number space */
587     pin = gpio_to_pin(range, gpio);
588
589     ret = pinmux_request_gpio(pctldev, range, pin, gpio);//作用主要有两个,第一个是在core driver中标记该pin已经用作GPIO了,这样,如果有模块后续request该资源,那么core driver可以拒绝不合理的要求。
//第二个就是调用底层pin controller driver的callback函数,进行底层寄存器相关的操作。
590
591     mutex_unlock(&pctldev->mutex);
592
593     return ret;
594 }


  pinmux_request_gpio() -> pin_request(pctldev, pin, owner, range) -> ops->gpio_request_enable()最终调用到上一章pinctrl所定义的pmxops; (ops = pctldev->desc->pmxops)

(2) gpiod_set_value()

  通过gpiod_set_value()可以来设置gpio的状态,传入一个参数gpio_desc,这在前面gpiochip_add()的时候会为每个GPIO创建一个描述符,并添加到全局变量gpio_desc数组中,其中flag表是该gpio的类型:

58 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
65 struct gpio_desc {
66     struct gpio_chip    *chip;
67     unsigned long       flags;
68 /* flag symbols are bit numbers */
69 #define FLAG_REQUESTED  0
70 #define FLAG_IS_OUT 1
71 #define FLAG_EXPORT 2   /* protected by sysfs_lock */
72 #define FLAG_SYSFS  3   /* exported via /sys/class/gpio/control */
73 #define FLAG_TRIG_FALL  4   /* trigger on falling edge */
74 #define FLAG_TRIG_RISE  5   /* trigger on rising edge */
75 #define FLAG_ACTIVE_LOW 6   /* value has active low */
76 #define FLAG_OPEN_DRAIN 7   /* Gpio is open drain type */
77 #define FLAG_OPEN_SOURCE 8  /* Gpio is open source type */
78 #define FLAG_USED_AS_IRQ 9  /* GPIO is connected to an IRQ */
79
80 #define ID_SHIFT    16  /* add new flags before this one */
81
82 #define GPIO_FLAGS_MASK     ((1 << ID_SHIFT) - 1)
83 #define GPIO_TRIGGER_MASK   (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
84
85     const char      *label;
86 };


  当我们需要设置一个gpio的状态的时候,就需要通过gpio号找到它对应的gpio_desc,可以通过gpio_to_desc()接口来实现;现在来看看gpiod_set_value()的实现:

1291 void gpiod_set_value(struct gpio_desc *desc, int value)
1292 {
1293     if (!desc)
1294         return;
1295     /* Should be using gpio_set_value_cansleep() */      //函数说明的很清楚,该函数调用的时候是不能睡眠的;
1296     WARN_ON(desc->chip->can_sleep);
1297     if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
1298         value = !value;
1299     _gpiod_set_raw_value(desc, value);
1300 }
1301 EXPORT_SYMBOL_GPL(gpiod_set_value);


1245 static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
1246 {
1247     struct gpio_chip    *chip;
1248
1249     chip = desc->chip;
1250     trace_gpio_value(desc_to_gpio(desc), 0, value);  //开漏模式
1251     if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
1252         _gpio_set_open_drain_value(desc, value);
1253     else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
1254         _gpio_set_open_source_value(desc, value);
1255     else
1256         chip->set(chip, gpio_chip_hwgpio(desc), value);
1257 }


  最终调用注册在gpio_chip的函数接口来实现,普通输出设置为高还是低通过set来设置,关于open drain和open source模式抽出时间在研究研究;

(三) GPIO 与中断

  GPIO系统用作普通io的同时也可以当作interrupt controller,是一个GPIO type的中断控制器,通过访问GPIO type的中断控制器的寄存器,通过访问GPIO type的中断控制器的寄存器,可以软件设置中断的disable和enable(mask和unmask),中断的触发方式等,但是并非所有的IO port都支持中断功能,可能某些处理器只有一两组GPIO有中断功能。

  gpio系统也作为一个中断控制器,所以也通过IRQCHIP_DECLARE来申明一个irqchipchip,并保存在全局的系统初始化的时候通过名字匹配,匹配之后完成相关的初始化:

IRQCHIP_DECLARE(8937_pinctrl, "qcom,msm8937-pinctrl", pinctrl_irq_dummy);


  通过gpiochip_irqchip_add()来添加irqchip,通过注册的接口来操作该gpio中断控制器:

791 static struct irq_chip msm_gpio_irq_chip = {
792     .name           = "msmgpio",
793     .irq_mask       = msm_gpio_irq_mask,
794     .irq_unmask     = msm_gpio_irq_unmask,
795     .irq_ack        = msm_gpio_irq_ack,
796     .irq_set_type   = msm_gpio_irq_set_type,
797     .irq_set_wake   = msm_gpio_irq_set_wake,
798 };


572 int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
573              struct irq_chip *irqchip,
574              unsigned int first_irq,
575              irq_flow_handler_t handler,
576              unsigned int type)
577 {
578     struct device_node *of_node;
579     unsigned int offset;
580     unsigned irq_base = 0;
581
589     of_node = gpiochip->dev->of_node;
590 #ifdef CONFIG_OF_GPIO
595     if (gpiochip->of_node)
596         of_node = gpiochip->of_node;
597 #endif
598     gpiochip->irqchip = irqchip;
599     gpiochip->irq_handler = handler;//注册flow interrupt 处理函数:handle_edge_irq,边沿触发类型;
600     gpiochip->irq_default_type = type;
601     gpiochip->to_irq = gpiochip_to_irq;//gpio num和irq number 转换函数;
602     gpiochip->irqdomain = irq_domain_add_simple(of_node,
603                     gpiochip->ngpio, first_irq,
604                     &gpiochip_domain_ops, gpiochip);//irq domain模块来管理IRQ number与HW interrupt ID的映射;并提供解析函数接口:gpiochip_domain_ops;
//first_irq为0,只通过__irq_domain_add来初始化并添加irq_domain链表;
609     irqchip->irq_request_resources = gpiochip_irq_reqres;
610     irqchip->irq_release_resources = gpiochip_irq_relres;
611
617     for (offset = 0; offset < gpiochip->ngpio; offset++) {
618         irq_base = irq_create_mapping(gpiochip->irqdomain, offset);//创建HW interrupt ID和IRQ number的映射关系,并为每个gpio number分配中断描述符;
619         if (offset == 0)
624             gpiochip->irq_base = irq_base;
625     }
626
627     acpi_gpiochip_request_interrupts(gpiochip);//for ACPI;
628
629     return 0;
630 }
631 EXPORT_SYMBOL_GPL(gpiochip_irqchip_add);


  关于具体中断系统的创建可以参考之前的文章 【Linux基础系列之】中断系统(1)-框架

  在具体驱动里面,我们需要某个gpio作为中断号,注册中断函数接口还是一样,通过request_threaded_irq来实现,需要的是用gpiod_to_irq来把gpio num转化成irq number,举个例子如下:

1323 int gpiod_to_irq(const struct gpio_desc *desc)
1324 {
1325     struct gpio_chip    *chip;
1326     int         offset;
1327
1328     if (!desc)
1329         return -EINVAL;
1330     chip = desc->chip;
1331     offset = gpio_chip_hwgpio(desc);//获取该gpio号对应于该chip的offset
1332     return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO;
//gpio号与中断号的对应关系是由chip driver处理,调用前面注册的to_irq函数;最终通过irq_domain的注册的映射关系来查找;
1333 }
1334 EXPORT_SYMBOL_GPL(gpiod_to_irq);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux gpio api