【Linux基础系列之】gpio系统

来源:互联网 发布:恐怖黎明物品数据官网 编辑:程序博客网 时间:2024/05/17 04:10

  上一章介绍了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()函数的作用:

  1. 将chip添加到全局的gpio chip链表中,用于gpio chip冲突处理和gpio管理 ;
  2. 将该gpio chip对应的那段gpio都初始化 ;
  3. 初始化设备树相关的信息,用于后面的属性解析及向pinctrl子系统同步下;
  4. 导入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接口:

//请求一个/一组gpiogpio_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的中断handlerrequest_irq(gpio_to_irq(gpio_num)...)   //释放请求的一个或者一组gpiogpio_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     else1256         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的offset1332     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);