• GPIO子系统(三)


    1,简述

    GPIO 资源是相对来说较为简单,而且比较通用(比如 LED 灯),而 Linux 的 GPIO 驱动属于 Linux Driver 中较为容易上手的部分,但是简单归简单,在 Linux 系统中,要使用 GPIO 资源,还是需要了解一些内容。

    Linux Kernel 中对 GPIO 资源进行了抽象,抽象出一个叫做 Gpiolib 的东东,这个东东作为 GPIO 资源的管理核心存在:

    中间层是 Gpiolib,用于管理系统中的 GPIO。Gpiolib 汇总了 GPIO 的通用操作,根据 GPIO 的特性,Gpiolib 对上(其他 Drivers)提供的一套统一通用的操作 GPIO 的软件接口,屏蔽了不同芯片的具体实现。对下,Gpiolib 提供了针对不同芯片操作的一套 framework,针对不同芯片,只需要实现 Specific Chip Driver ,然后使用 Gpiolib 提供的注册函数,将其挂接到 Gpiolib 上,这样就完成了这一套东西。

    对于其他驱动来说,比如 LED 灯驱动,就需要用到通用的 Gpiolib 的函数来进行 I/O 口的操作。

    2,Gpiolib相关数据结构分析

    先分析数据结构,Gpiolib 其实就是围绕几个数据结构在做文章,数据结构以及抽象层次清楚了,代码自然很快。

    数据结构主要定义在 include/linux/gpio/driver.h 和 /drivers/gpio/gpiolib.h 中

    首先看一个数据结构,叫 struct gpio_chip (include/linux/gpio/driver.h):

    1. struct gpio_chip {
    2. const char *label;
    3. struct gpio_device *gpiodev;
    4. struct device *parent;
    5. struct module *owner;
    6. int (*request)(struct gpio_chip *gc,
    7. unsigned int offset);
    8. void (*free)(struct gpio_chip *gc,
    9. unsigned int offset);
    10. int (*get_direction)(struct gpio_chip *gc,
    11. unsigned int offset);
    12. int (*direction_input)(struct gpio_chip *gc,
    13. unsigned int offset);
    14. int (*direction_output)(struct gpio_chip *gc,
    15. unsigned int offset, int value);
    16. int (*get)(struct gpio_chip *gc,
    17. unsigned int offset);
    18. int (*get_multiple)(struct gpio_chip *gc,
    19. unsigned long *mask,
    20. unsigned long *bits);
    21. void (*set)(struct gpio_chip *gc,
    22. unsigned int offset, int value);
    23. void (*set_multiple)(struct gpio_chip *gc,
    24. unsigned long *mask,
    25. unsigned long *bits);
    26. int (*set_config)(struct gpio_chip *gc,
    27. unsigned int offset,
    28. unsigned long config);
    29. int (*to_irq)(struct gpio_chip *gc,
    30. unsigned int offset);
    31. void (*dbg_show)(struct seq_file *s,
    32. struct gpio_chip *gc);
    33. int (*init_valid_mask)(struct gpio_chip *gc,
    34. unsigned long *valid_mask,
    35. unsigned int ngpios);
    36. int (*add_pin_ranges)(struct gpio_chip *gc);
    37. int base;
    38. u16 ngpio;
    39. const char *const *names;
    40. bool can_sleep;
    41. #if IS_ENABLED(CONFIG_GPIO_GENERIC)
    42. unsigned long (*read_reg)(void __iomem *reg);
    43. void (*write_reg)(void __iomem *reg, unsigned long data);
    44. bool be_bits;
    45. void __iomem *reg_dat;
    46. void __iomem *reg_set;
    47. void __iomem *reg_clr;
    48. void __iomem *reg_dir_out;
    49. void __iomem *reg_dir_in;
    50. bool bgpio_dir_unreadable;
    51. int bgpio_bits;
    52. spinlock_t bgpio_lock;
    53. unsigned long bgpio_data;
    54. unsigned long bgpio_dir;
    55. #endif /* CONFIG_GPIO_GENERIC */
    56. #ifdef CONFIG_GPIOLIB_IRQCHIP
    57. /*
    58. * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
    59. * to handle IRQs for most practical cases.
    60. */
    61. /**
    62. * @irq:
    63. *
    64. * Integrates interrupt chip functionality with the GPIO chip. Can be
    65. * used to handle IRQs for most practical cases.
    66. */
    67. struct gpio_irq_chip irq;
    68. #endif /* CONFIG_GPIOLIB_IRQCHIP */
    69. /**
    70. * @valid_mask:
    71. *
    72. * If not %NULL holds bitmask of GPIOs which are valid to be used
    73. * from the chip.
    74. */
    75. unsigned long *valid_mask;
    76. #if defined(CONFIG_OF_GPIO)
    77. /*
    78. * If CONFIG_OF is enabled, then all GPIO controllers described in the
    79. * device tree automatically may have an OF translation
    80. */
    81. /**
    82. * @of_node:
    83. *
    84. * Pointer to a device tree node representing this GPIO controller.
    85. */
    86. struct device_node *of_node;
    87. /**
    88. * @of_gpio_n_cells:
    89. *
    90. * Number of cells used to form the GPIO specifier.
    91. */
    92. unsigned int of_gpio_n_cells;
    93. /**
    94. * @of_xlate:
    95. *
    96. * Callback to translate a device tree GPIO specifier into a chip-
    97. * relative GPIO number and flags.
    98. */
    99. int (*of_xlate)(struct gpio_chip *gc,
    100. const struct of_phandle_args *gpiospec, u32 *flags);
    101. #endif /* CONFIG_OF_GPIO */
    102. ANDROID_KABI_RESERVE(1);
    103. ANDROID_KABI_RESERVE(2);
    104. };

    gpio_chip 这个数据结构一看,很多函数指针结构,明眼人秒懂,此结构是为了抽象 GPIO 的所有操作,同时适配不同芯片的一个 common 的结构,所以,这个结构是要开出去给其他芯片进行特定的操作赋值的,比如你是 Qcom 的芯片,那么你需要实现你的这些 gpio_chip 的内容。

    2.1 gpio_chip 结构

    一般的,在一个芯片中,针对所有的 I/O 口都会有配置,默认状态有些是 I/O 口全部默认 GPIO 输入(稳当)。一般芯片会提供管脚复用的功能(后期的 Linux 版本中,使用 pin control 来抽象),要使用 GPIO ,则首先需要配置他为 GPIO 功能,而不是其他的复用功能。

    而针对 GPIO 呢,有一些通用的特性,比如设置 GPIO 的方向,读 GPIO 的电平(输入的时候),写 GPIO 的电平(输出的时候),GPIO 作为外部中断输入,等等。

    gpio_chip 的抽象,其实是对 GPIO 一组 Bank 的抽象,通常在硬件上,一个芯片对 IO 口来说,分为了很多个 Bank,每个 Bank 分为了 N 组 GPIO。

    比如:1 个 SoC 将 I/O 分为了 4 个 Bank:

    1. Bank 1:GPIOA ~ GPIOB
    2. Bank 2:GPIOC ~ GPIOD
    3. Bank 3:GPIOE ~ GPIOF
    4. Bank 4:GPIOG ~ GPIOH

    然鹅,每个 Bank 都有 N 组寄存器来表示 GPIO 的操作,比如:

    Bank 1 中,针对 GPIO A:

    1. GPIOA_CFG 来表示对 GPIO A 的配置
    2. GPIOA_PULL 来表示对 GPIO A 的上下拉的配置
    3. GPIOA_DIR 来表示对 GPIO A 配置成为输入或者输出
    4. GPIOA_DATA 来表示 GPIO A 设置为输出的时候设置为高低或者输入的时候读高低

    当然,Bank 1 中 针对 GPIO B,也是一样的操作:

    1. GPIOB_CFG 来表示对 GPIO B 的配置
    2. GPIOB_PULL 来表示对 GPIO B 的上下拉的配置
    3. GPIOB_DIR 来表示对 GPIO B 配置成为输入或者输出
    4. GPIOB_DATA 来表示 GPIO B 设置为输出的时候设置为高低或者输入的时候读高低

    上面说的是一个 Bank 的情况,那么芯片有好几个 Bank,所以它们都是类似的,这里不在赘述。

    所以整体结构是如下所示(这里只是打个比方,有的芯片 Bank 很多,寄存器也很多):

    Linux Driver Gpiolib 对他们的抽象,使用 gpio_chip 对应了一组 Bank 描述,比如 Bank ·1,用一个 gpio_chip 来抽象:

    那么多个 Bank ,就用指针,或者数组来表示咯。当然这里可能说得有点不准确,gpio_chip 只是抽象了一组 Bank 的统一的接口而已。

    那么对于一颗芯片底层来说,需要根据芯片手册 Datasheet,来实现这些结构的接口。

    2.2 gpio_desc结构

    既然系统分为多个 Bank,每个 Bank 又由几组组成,那么每个 GPIO 实体就由一个 gpio_desc 来描述:

    1. struct gpio_desc {
    2. struct gpio_device *gdev;
    3. unsigned long flags;
    4. /* flag symbols are bit numbers */
    5. #define FLAG_REQUESTED 0
    6. #define FLAG_IS_OUT 1
    7. #define FLAG_EXPORT 2 /* protected by sysfs_lock */
    8. #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
    9. #define FLAG_ACTIVE_LOW 6 /* value has active low */
    10. #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
    11. #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
    12. #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
    13. #define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
    14. #define FLAG_IS_HOGGED 11 /* GPIO is hogged */
    15. #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
    16. #define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
    17. #define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
    18. #define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
    19. #define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
    20. #define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
    21. /* Connection label */
    22. const char *label;
    23. /* Name of the GPIO */
    24. const char *name;
    25. #ifdef CONFIG_OF_DYNAMIC
    26. struct device_node *hog;
    27. #endif
    28. #ifdef CONFIG_GPIO_CDEV
    29. /* debounce period in microseconds */
    30. unsigned int debounce_period_us;
    31. #endif
    32. };

    这个结构比较简单,可以看到,他包含了一个 gpio_device 的结构和 flag,以及 lable 和 name;

    gdev 指针指向了这个 gpio_desc 所属的 gpio_device(马上描述),flag 代表了这个 GPIO 的属性状态;

    看起来 gpio_chip 和 gpio_desc 应该是包含关系,但是 Kernel 中并没有直接将其两个结构联系上,而是通过另外一个结构将其联系在一起,这个结构就是 gpio_device。

    2.3 gpio_device结构

    gpio_device 应该算是大内总管了(最新的内核有,Linux 3 版本的内核没有这个),如果说 gpio_chip 是对一个 Bank 的 GPIO 的硬件的具体抽象的话,那么 gpio_device 就是软件层面上对一个 Bank 的 GPIO 进行管理的单元,它的数据结构是:

    1. struct gpio_device {
    2. int id;
    3. struct device dev;
    4. struct cdev chrdev;
    5. struct device *mockdev;
    6. struct module *owner;
    7. struct gpio_chip *chip;
    8. struct gpio_desc *descs;
    9. int base;
    10. u16 ngpio;
    11. const char *label;
    12. void *data;
    13. struct list_head list;
    14. struct blocking_notifier_head notifier;
    15. #ifdef CONFIG_PINCTRL
    16. /*
    17. * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
    18. * describe the actual pin range which they serve in an SoC. This
    19. * information would be used by pinctrl subsystem to configure
    20. * corresponding pins for gpio usage.
    21. */
    22. struct list_head pin_ranges;
    23. #endif
    24. };

    在这个 gpio_device 结构中,包含了 gpio_chip(对接芯片的操作集),gpio_desc(一些 GPIO 的描述);这个结构贯穿了整个 Gpiolib,因为 gpio_device 代表的是一个 Bank,一般的 GPIO 有多个 Bank,所以 Kernel 中,对这 gpio_device 的组织是由一个 gpio_devices 的链表构成(此处是多个 device,所以后面加了 s),在 gpiolib.c:

    LIST_HEAD(gpio_devices);

    2.4 gpio_chip/gpio_desc/gpio_device 结构体之间的关系

    3,Gpiolib对接芯片底层

    先聊聊 Gpiolib 是怎么对接到底层实际的驱动的。在前面的 2.1 部分讲过,底层需要对接的,其实对接的部分只有那些通用的操作,其实也就是 gpio_chip 这个玩意,所以,对接底层的部分,主要关心的是这个结构体,并且对这个结构体进行赋值的过程。

    在底层对接到 Gpiolib 的时候,主要是对 gpio_chip 进行实现,然后调用 gpiochip_add 的接口,向 Gpiolib 注册你的 GPIO 。

    实现的过程,主要是根据芯片手册,实现对应的 GPIO 的操作,也就是说,把寄存器操作编程成为函数,对接到这个 gpio_chip 结构体上。

    3.1 gpio_chip具体芯片操作函数的填充 - 一个gpio controller驱动gpio-pl061.c

    代码路径:drivers/gpio/gpio-pl061.c

    寄存器手册:http://access.ee.ntu.edu.tw/course/SOC%E5%AF%A6%E9%A9%97%E6%95%99%E6%9D%90/Version%203/Lab05_External%20IO%20Control/Doc/Ref/ddi0190_gpio_trm.pdf

    pl061是通过AMBA总线连接在SOC上的片上gpio controller,arm架构的芯片经过授权之后可以直接使用。

    部分寄存器描述:

    suspend/resume操作:

    1. #ifdef CONFIG_PM
    2. //suspend函数中保存当前所有gpio pin寄存器状态
    3. static int pl061_suspend(struct device *dev)
    4. {
    5. struct pl061 *pl061 = dev_get_drvdata(dev);
    6. int offset;
    7. pl061->csave_regs.gpio_data = 0;
    8. pl061->csave_regs.gpio_dir = readb(pl061->base + GPIODIR);
    9. pl061->csave_regs.gpio_is = readb(pl061->base + GPIOIS);
    10. pl061->csave_regs.gpio_ibe = readb(pl061->base + GPIOIBE);
    11. pl061->csave_regs.gpio_iev = readb(pl061->base + GPIOIEV);
    12. pl061->csave_regs.gpio_ie = readb(pl061->base + GPIOIE);
    13. for (offset = 0; offset < PL061_GPIO_NR; offset++) {
    14. if (pl061->csave_regs.gpio_dir & (BIT(offset)))
    15. pl061->csave_regs.gpio_data |=
    16. pl061_get_value(&pl061->gc, offset) << offset;
    17. }
    18. return 0;
    19. }
    20. //resume函数中将gpio pin的寄存器状态恢复
    21. static int pl061_resume(struct device *dev)
    22. {
    23. struct pl061 *pl061 = dev_get_drvdata(dev);
    24. int offset;
    25. for (offset = 0; offset < PL061_GPIO_NR; offset++) {
    26. if (pl061->csave_regs.gpio_dir & (BIT(offset)))
    27. pl061_direction_output(&pl061->gc, offset,
    28. pl061->csave_regs.gpio_data &
    29. (BIT(offset)));
    30. else
    31. pl061_direction_input(&pl061->gc, offset);
    32. }
    33. writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
    34. writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
    35. writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
    36. writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);
    37. return 0;
    38. }

    probe函数流程:

    1. static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
    2. {
    3. struct device *dev = &adev->dev;
    4. struct pl061 *pl061;
    5. struct gpio_irq_chip *girq;
    6. int ret, irq;
    7. pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL);
    8. if (pl061 == NULL)
    9. return -ENOMEM;
    10. pl061->base = devm_ioremap_resource(dev, &adev->res);
    11. if (IS_ERR(pl061->base))
    12. return PTR_ERR(pl061->base);
    13. //填充gpio_chip中的回调函数
    14. raw_spin_lock_init(&pl061->lock);
    15. pl061->gc.request = gpiochip_generic_request;
    16. pl061->gc.free = gpiochip_generic_free;
    17. pl061->gc.base = -1;
    18. pl061->gc.get_direction = pl061_get_direction;
    19. pl061->gc.direction_input = pl061_direction_input;
    20. pl061->gc.direction_output = pl061_direction_output;
    21. pl061->gc.get = pl061_get_value;
    22. pl061->gc.set = pl061_set_value;
    23. pl061->gc.ngpio = PL061_GPIO_NR; //8
    24. pl061->gc.label = dev_name(dev);
    25. pl061->gc.parent = dev;
    26. pl061->gc.owner = THIS_MODULE;
    27. /*
    28. * irq_chip support
    29. */
    30. pl061->irq_chip.name = dev_name(dev);
    31. pl061->irq_chip.irq_ack = pl061_irq_ack;
    32. pl061->irq_chip.irq_mask = pl061_irq_mask;
    33. pl061->irq_chip.irq_unmask = pl061_irq_unmask;
    34. pl061->irq_chip.irq_set_type = pl061_irq_type;
    35. pl061->irq_chip.irq_set_wake = pl061_irq_set_wake;
    36. writeb(0, pl061->base + GPIOIE); /* disable irqs */
    37. irq = adev->irq[0];
    38. if (!irq)
    39. dev_warn(&adev->dev, "IRQ support disabled\n");
    40. pl061->parent_irq = irq;
    41. girq = &pl061->gc.irq;
    42. girq->chip = &pl061->irq_chip;
    43. girq->parent_handler = pl061_irq_handler;
    44. girq->num_parents = 1;
    45. girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
    46. GFP_KERNEL);
    47. if (!girq->parents)
    48. return -ENOMEM;
    49. girq->parents[0] = irq;
    50. girq->default_type = IRQ_TYPE_NONE;
    51. girq->handler = handle_bad_irq;
    52. //注册gpio资源
    53. ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);
    54. if (ret)
    55. return ret;
    56. amba_set_drvdata(adev, pl061);
    57. dev_info(dev, "PL061 GPIO chip registered\n");
    58. return 0;
    59. }

    几个gpio_chip函数的实现 - pl061_get_direction

    1. static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
    2. {
    3. //per-instance data assigned by the driver, return gc->gpiodev->data
    4. struct pl061 *pl061 = gpiochip_get_data(gc);
    5. //读取GPIODIR寄存器判断是输入或者输出并返回结果
    6. if (readb(pl061->base + GPIODIR) & BIT(offset))
    7. return GPIO_LINE_DIRECTION_OUT;
    8. return GPIO_LINE_DIRECTION_IN;
    9. }

    几个gpio_chip函数的实现 -pl061_set_value

    1. static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
    2. {
    3. struct pl061 *pl061 = gpiochip_get_data(gc);
    4. //设置GPIODATA寄存器
    5. writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
    6. }
    3.2 gpiochip_add_data_with_key

    1)

    1. int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
    2. struct lock_class_key *lock_key,
    3. struct lock_class_key *request_key)
    4. {
    5. struct fwnode_handle *fwnode = gc->parent ? dev_fwnode(gc->parent) : NULL;
    6. unsigned long flags;
    7. int ret = 0;
    8. unsigned i;
    9. int base = gc->base;
    10. struct gpio_device *gdev;
    11. bool block_gpio_read = false;
    12. /*
    13. * First: allocate and populate the internal stat container, and
    14. * set up the struct device.
    15. */
    16. gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
    17. if (!gdev)
    18. return -ENOMEM;
    19. gdev->dev.bus = &gpio_bus_type;
    20. gdev->chip = gc;
    21. gc->gpiodev = gdev;
    22. if (gc->parent) {
    23. gdev->dev.parent = gc->parent;
    24. gdev->dev.of_node = gc->parent->of_node;
    25. }
    26. of_gpio_dev_init(gc, gdev);
    27. /*
    28. * Assign fwnode depending on the result of the previous calls,
    29. * if none of them succeed, assign it to the parent's one.
    30. */
    31. gdev->dev.fwnode = dev_fwnode(&gdev->dev) ?: fwnode;
    32. //给chip分配唯一的ID
    33. gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
    34. if (gdev->id < 0) {
    35. ret = gdev->id;
    36. goto err_free_gdev;
    37. }
    38. ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
    39. if (ret)
    40. goto err_free_ida;
    41. device_initialize(&gdev->dev);
    42. if (gc->parent && gc->parent->driver)
    43. gdev->owner = gc->parent->driver->owner;
    44. else if (gc->owner)
    45. /* TODO: remove chip->owner */
    46. gdev->owner = gc->owner;
    47. else
    48. gdev->owner = THIS_MODULE;

    因为传入的结构是 gpio_chip,他代表了是一个 Bank,但是并没有 gpio_device 的结构,所以,在这个函数中,首先分配一个 gpio_device 的结构,并将其结构体成员的 chip ,等等进行赋值,建立起相关的结构联系。

    2)

    1. gdev->descs = kcalloc(gc->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
    2. if (!gdev->descs) {
    3. ret = -ENOMEM;
    4. goto err_free_dev_name;
    5. }
    6. if (gc->ngpio == 0) {
    7. chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
    8. ret = -EINVAL;
    9. goto err_free_descs;
    10. }
    11. if (gc->ngpio > FASTPATH_NGPIO)
    12. chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
    13. gc->ngpio, FASTPATH_NGPIO);
    14. gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL);
    15. if (!gdev->label) {
    16. ret = -ENOMEM;
    17. goto err_free_descs;
    18. }
    19. gdev->ngpio = gc->ngpio;
    20. gdev->data = data;

    由于 1 个 Bank不仅仅只有一个 GPIO,所以 gpio_chip->ngpio 的结构表示了这个 Bank 一共的 GPIO 个数,每一个 GPIO 使用一个 gpio_desc 表示,所以,这里分配了 ngpio 个 descs;

     3)

    1. spin_lock_irqsave(&gpio_lock, flags);
    2. /*
    3. * TODO: this allocates a Linux GPIO number base in the global
    4. * GPIO numberspace for this chip. In the long run we want to
    5. * get *rid* of this numberspace and use only descriptors, but
    6. * it may be a pipe dream. It will not happen before we get rid
    7. * of the sysfs interface anyways.
    8. */
    9. if (base < 0) {
    10. base = gpiochip_find_base(gc->ngpio);
    11. if (base < 0) {
    12. ret = base;
    13. spin_unlock_irqrestore(&gpio_lock, flags);
    14. goto err_free_label;
    15. }
    16. /*
    17. * TODO: it should not be necessary to reflect the assigned
    18. * base outside of the GPIO subsystem. Go over drivers and
    19. * see if anyone makes use of this, else drop this and assign
    20. * a poison instead.
    21. */
    22. gc->base = base;
    23. }
    24. gdev->base = base;
    25. ret = gpiodev_add_to_list(gdev);
    26. if (ret) {
    27. spin_unlock_irqrestore(&gpio_lock, flags);
    28. goto err_free_label;
    29. }
    30. for (i = 0; i < gc->ngpio; i++)
    31. gdev->descs[i].gdev = gdev;
    32. spin_unlock_irqrestore(&gpio_lock, flags);
    33. BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier);
    34. #ifdef CONFIG_PINCTRL
    35. INIT_LIST_HEAD(&gdev->pin_ranges);
    36. #endif
    37. if (gc->names)
    38. ret = gpiochip_set_desc_names(gc);
    39. else
    40. ret = devprop_gpiochip_set_names(gc);
    41. if (ret)
    42. goto err_remove_from_list;
    43. ret = gpiochip_alloc_valid_mask(gc);
    44. if (ret)
    45. goto err_remove_from_list;
    46. ret = of_gpiochip_add(gc);
    47. if (ret)
    48. goto err_free_gpiochip_mask;
    49. ret = gpiochip_init_valid_mask(gc);
    50. if (ret)
    51. goto err_remove_of_chip;
    52. trace_android_vh_gpio_block_read(gdev, &block_gpio_read);
    53. if (!block_gpio_read) {
    54. for (i = 0; i < gc->ngpio; i++) {
    55. struct gpio_desc *desc = &gdev->descs[i];
    56. if (gc->get_direction && gpiochip_line_is_valid(gc, i)) {
    57. assign_bit(FLAG_IS_OUT,
    58. &desc->flags, !gc->get_direction(gc, i));
    59. } else {
    60. assign_bit(FLAG_IS_OUT,
    61. &desc->flags, !gc->direction_input);
    62. }
    63. }
    64. }
    65. ret = gpiochip_add_pin_ranges(gc);
    66. if (ret)
    67. goto err_remove_of_chip;
    68. acpi_gpiochip_add(gc);
    69. machine_gpiochip_add(gc);
    70. ret = gpiochip_irqchip_init_valid_mask(gc);
    71. if (ret)
    72. goto err_remove_acpi_chip;
    73. ret = gpiochip_irqchip_init_hw(gc);
    74. if (ret)
    75. goto err_remove_acpi_chip;
    76. ret = gpiochip_add_irqchip(gc, lock_key, request_key);
    77. if (ret)
    78. goto err_remove_irqchip_mask;
    79. /*
    80. * By first adding the chardev, and then adding the device,
    81. * we get a device node entry in sysfs under
    82. * /sys/bus/gpio/devices/gpiochipN/dev that can be used for
    83. * coldplug of device nodes and other udev business.
    84. * We can do this only if gpiolib has been initialized.
    85. * Otherwise, defer until later.
    86. */
    87. if (gpiolib_initialized) {
    88. ret = gpiochip_setup_dev(gdev);
    89. if (ret)
    90. goto err_remove_irqchip;
    91. }
    92. return 0;
    93. err_remove_irqchip:
    94. gpiochip_irqchip_remove(gc);
    95. err_remove_irqchip_mask:
    96. gpiochip_irqchip_free_valid_mask(gc);
    97. err_remove_acpi_chip:
    98. acpi_gpiochip_remove(gc);
    99. err_remove_of_chip:
    100. gpiochip_free_hogs(gc);
    101. of_gpiochip_remove(gc);
    102. err_free_gpiochip_mask:
    103. gpiochip_remove_pin_ranges(gc);
    104. gpiochip_free_valid_mask(gc);
    105. err_remove_from_list:
    106. spin_lock_irqsave(&gpio_lock, flags);
    107. list_del(&gdev->list);
    108. spin_unlock_irqrestore(&gpio_lock, flags);
    109. err_free_label:
    110. kfree_const(gdev->label);
    111. err_free_descs:
    112. kfree(gdev->descs);
    113. err_free_dev_name:
    114. kfree(dev_name(&gdev->dev));
    115. err_free_ida:
    116. ida_free(&gpio_ida, gdev->id);
    117. err_free_gdev:
    118. /* failures here can mean systems won't boot... */
    119. pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
    120. gdev->base, gdev->base + gdev->ngpio - 1,
    121. gc->label ? : "generic", ret);
    122. kfree(gdev);
    123. return ret;
    124. }
    125. EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key);

    base 代表了每个 Bank 的编号,将其赋值;然后通过 gpiodev_add_to_list(gdev) 将这个 gdev 挂到全局的 gpio_devices :

    1. /*
    2. * Add a new chip to the global chips list, keeping the list of chips sorted
    3. * by range(means [base, base + ngpio - 1]) order.
    4. *
    5. * Return -EBUSY if the new chip overlaps with some other chip's integer
    6. * space.
    7. */
    8. static int gpiodev_add_to_list(struct gpio_device *gdev)
    9. {
    10. struct gpio_device *prev, *next;
    11. if (list_empty(&gpio_devices)) {
    12. /* initial entry in list */
    13. list_add_tail(&gdev->list, &gpio_devices);
    14. return 0;
    15. }
    16. next = list_entry(gpio_devices.next, struct gpio_device, list);
    17. if (gdev->base + gdev->ngpio <= next->base) {
    18. /* add before first entry */
    19. list_add(&gdev->list, &gpio_devices);
    20. return 0;
    21. }
    22. prev = list_entry(gpio_devices.prev, struct gpio_device, list);
    23. if (prev->base + prev->ngpio <= gdev->base) {
    24. /* add behind last entry */
    25. list_add_tail(&gdev->list, &gpio_devices);
    26. return 0;
    27. }
    28. list_for_each_entry_safe(prev, next, &gpio_devices, list) {
    29. /* at the end of the list */
    30. if (&next->list == &gpio_devices)
    31. break;
    32. /* add between prev and next */
    33. if (prev->base + prev->ngpio <= gdev->base
    34. && gdev->base + gdev->ngpio <= next->base) {
    35. list_add(&gdev->list, &prev->list);
    36. return 0;
    37. }
    38. }
    39. dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chip\n");
    40. return -EBUSY;
    41. }

    接着就是设置一些 name 字段,配置中断之类的,初始化每个 desc[] 结构的 flags,最后调用:

    1. if (gpiolib_initialized) {
    2. ret = gpiochip_setup_dev(gdev);
    3. if (ret)
    4. goto err_remove_irqchip;
    5. }

    然后,不出意外的话,返回 0;

    这里说一下 gpiochip_setup_dev 调用,这个是在 Gpiolib init 的时候调用 gpiochip_setup_devs:

    1. static int __init gpiolib_dev_init(void)
    2. {
    3. int ret;
    4. /* Register GPIO sysfs bus */
    5. ret = bus_register(&gpio_bus_type);
    6. if (ret < 0) {
    7. pr_err("gpiolib: could not register GPIO bus type\n");
    8. return ret;
    9. }
    10. ret = driver_register(&gpio_stub_drv);
    11. if (ret < 0) {
    12. pr_err("gpiolib: could not register GPIO stub driver\n");
    13. bus_unregister(&gpio_bus_type);
    14. return ret;
    15. }
    16. ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME);
    17. if (ret < 0) {
    18. pr_err("gpiolib: failed to allocate char dev region\n");
    19. driver_unregister(&gpio_stub_drv);
    20. bus_unregister(&gpio_bus_type);
    21. return ret;
    22. }
    23. gpiolib_initialized = true;
    24. gpiochip_setup_devs();
    25. #if IS_ENABLED(CONFIG_OF_DYNAMIC) && IS_ENABLED(CONFIG_OF_GPIO)
    26. WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier));
    27. #endif /* CONFIG_OF_DYNAMIC && CONFIG_OF_GPIO */
    28. return ret;
    29. }
    30. core_initcall(gpiolib_dev_init);

    而这个 gpiochip_setup_devs 对每一个 gpio_devicecs 节点调用:gpiochip_setup_dev:

    1. static void gpiochip_setup_devs(void)
    2. {
    3. struct gpio_device *gdev;
    4. int ret;
    5. list_for_each_entry(gdev, &gpio_devices, list) {
    6. ret = gpiochip_setup_dev(gdev);
    7. if (ret)
    8. dev_err(&gdev->dev,
    9. "Failed to initialize gpio device (%d)\n", ret);
    10. }
    11. }

    最后到:

    1. static int gpiochip_setup_dev(struct gpio_device *gdev)
    2. {
    3. int ret;
    4. ret = gcdev_register(gdev, gpio_devt);
    5. if (ret)
    6. return ret;
    7. ret = gpiochip_sysfs_register(gdev);
    8. if (ret)
    9. goto err_remove_device;
    10. /* From this point, the .release() function cleans up gpio_device */
    11. gdev->dev.release = gpiodevice_release;
    12. dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base,
    13. gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic");
    14. return 0;
    15. err_remove_device:
    16. gcdev_unregister(gdev);
    17. return ret;
    18. }

    其实就是注册了字符设备,并且添加到了 sysfs;

    debug logs:

    1. char dev节点:/sys/bus/gpio/devices
    2. ls -l /sys/bus/gpio/devices
    3. total 0
    4. lrwxrwxrwx 1 root root 0 2023-09-21 06:52 gpiochip0 -> ../../../devices/platform/soc/f000000.pinctrl/gpiochip0
    5. lrwxrwxrwx 1 root root 0 2023-09-21 06:52 gpiochip1 -> ../../../devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-00/c42d000.qcom,spmi:qcom,pmk8350@0:pinctrl@b000/gpiochip1
    6. lrwxrwxrwx 1 root root 0 2023-09-21 06:52 gpiochip2 -> ../../../devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-02/c42d000.qcom,spmi:qcom,pm8350c@2:pinctrl@8800/gpiochip2
    7. lrwxrwxrwx 1 root root 0 2023-09-21 06:52 gpiochip3 -> ../../../devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-01/c42d000.qcom,spmi:qcom,pm7325@1:pinctrl@8800/gpiochip3
    8. lrwxrwxrwx 1 root root 0 2023-09-21 06:52 gpiochip4 -> ../../../devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-03/c42d000.qcom,spmi:qcom,pm8350b@3:pinctrl@8800/gpiochip4
    9. lrwxrwxrwx 1 root root 0 2023-09-21 06:52 gpiochip5 -> ../../../devices/platform/soc/soc:spf_core_platform/soc:spf_core_platform:lpi_pinctrl@3440000/gpiochip5
    10. sysfs节点 /sys/class/gpio
    11. ls -l /sys/class/gpio/
    12. total 0
    13. --w------- 1 root root 4096 2023-09-21 06:53 export
    14. lrwxrwxrwx 1 root root 0 2023-09-21 06:53 gpiochip287 -> ../../devices/platform/soc/soc:spf_core_platform/soc:spf_core_platform:lpi_pinctrl@3440000/gpio/gpiochip287
    15. lrwxrwxrwx 1 root root 0 2023-09-21 06:53 gpiochip310 -> ../../devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-03/c42d000.qcom,spmi:qcom,pm8350b@3:pinctrl@8800/gpio/gpiochip310
    16. lrwxrwxrwx 1 root root 0 2023-09-21 06:53 gpiochip318 -> ../../devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-01/c42d000.qcom,spmi:qcom,pm7325@1:pinctrl@8800/gpio/gpiochip318
    17. lrwxrwxrwx 1 root root 0 2023-09-21 06:53 gpiochip328 -> ../../devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-02/c42d000.qcom,spmi:qcom,pm8350c@2:pinctrl@8800/gpio/gpiochip328
    18. lrwxrwxrwx 1 root root 0 2023-09-21 06:53 gpiochip337 -> ../../devices/platform/soc/c42d000.qcom,spmi/spmi-0/0-00/c42d000.qcom,spmi:qcom,pmk8350@0:pinctrl@b000/gpio/gpiochip337
    19. lrwxrwxrwx 1 root root 0 2023-09-21 06:53 gpiochip341 -> ../../devices/platform/soc/f000000.pinctrl/gpio/gpiochip341
    20. --w------- 1 root root 4096 2023-09-21 06:53 unexport
    1. dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base, gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic");
    2. 05-23 16:18:33.460 1 1 E gpiochip0: (f000000.pinctrl): william_gpio added GPIO chardev (254:0)
    3. 05-23 16:18:33.460 1 1 E gpiochip0: william_gpio registered GPIOs 341 to 511 on f000000.pinctrl
    4. 05-23 16:18:35.347 357 357 E gpiochip1: (c42d000.qcom,spmi:qcom,pmk8350@0:pinctrl@b000): william_gpio added GPIO chardev (254:1)
    5. 05-23 16:18:35.347 357 357 E gpiochip1: william_gpio registered GPIOs 337 to 340 on c42d000.qcom,spmi:qcom,pmk8350@0:pinctrl@b000
    6. 05-23 16:18:35.351 357 357 E gpiochip2: (c42d000.qcom,spmi:qcom,pm8350c@2:pinctrl@8800): william_gpio added GPIO chardev (254:2)
    7. 05-23 16:18:35.351 357 357 E gpiochip2: william_gpio registered GPIOs 328 to 336 on c42d000.qcom,spmi:qcom,pm8350c@2:pinctrl@8800
    8. 05-23 16:18:35.361 357 357 E gpiochip3: (c42d000.qcom,spmi:qcom,pm7325@1:pinctrl@8800): william_gpio added GPIO chardev (254:3)
    9. 05-23 16:18:35.361 357 357 E gpiochip3: william_gpio registered GPIOs 318 to 327 on c42d000.qcom,spmi:qcom,pm7325@1:pinctrl@8800
    10. 05-23 16:18:35.362 357 357 E gpiochip4: (c42d000.qcom,spmi:qcom,pm8350b@3:pinctrl@8800): william_gpio added GPIO chardev (254:4)
    11. 05-23 16:18:35.362 357 357 E gpiochip4: william_gpio registered GPIOs 310 to 317 on c42d000.qcom,spmi:qcom,pm8350b@3:pinctrl@8800
    12. 09-21 01:52:25.519 758 758 E gpiochip5: (soc:spf_core_platform:lpi_pinctrl@3440000): william_gpio added GPIO chardev (254:5)
    13. 09-21 01:52:25.519 758 758 E gpiochip5: william_gpio registered GPIOs 287 to 309 on soc:spf_core_platform:lpi_pinctrl@3440000
    14. chip base ngpio gdev->chip->label
    15. gpiochip0 341 170 f000000.pinctrl
    16. gpiochip1 337 3 pmk8350@0:pinctrl@b000
    17. gpiochip2 328 8 pm8350c@2:pinctrl@8800
    18. gpiochip3 318 9 pm7325@1:pinctrl@8800
    19. gpiochip4 310 7 pm8350b@3:pinctrl@8800
    20. gpiochip5 287 22 lpi_pinctrl@3440000

    个人理解,因为不知道这个 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 向上提供的操作接口

    两种不同的gpio框架:

     * descriptor-based interface: 基于描述符的接口, 新框架, 官方推荐;

     * legacy integer-based interface: 基于整数的接口;

    4.1 基于描述符的gpio在dts中使用

    参考:

    Documentation/gpio/board.txt

    1. #include
    2. foo_device {
    3. compatible = "acme,foo";
    4. ...
    5. led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
    6. <&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
    7. <&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */
    8. power-gpio = <&gpio 18 GPIO_ACTIVE_LOW>;
    9. };
    10. struct gpio_desc *red, *green, *blue, *power;
    11. red = gpiod_get_index(dev, "led", 0);
    12. green = gpiod_get_index(dev, "led", 1);
    13. blue = gpiod_get_index(dev, "led", 2);
    14. power = gpiod_get(dev, "power");
    15. gpiod_direction_output(red, 1);
    16. gpiod_direction_output(green, 1);
    17. gpiod_direction_output(blue, 1);
    18. gpiod_direction_output(power, 1);
    19. gpiod_put(red); //释放gpio口;

    dts中gpio label的写法: name-gpios, 其中name是gpiod_get*()函数里的第二个参数;

    The led GPIOs will be active-high, while the power GPIO will be active-low;

    gpiod_is_active_low(power); 为true;

    4.2 基于整数的gpio在dts中使用
    1. device_node {
    2. ...
    3. gpio_name = <&tlmm 99 0>;
    4. ...
    5. }
    6. int gpio_99 = of_get_named_gpio_flags(dev->of_node, "gpio_name", 0, NULL);
    7. gpio_request(gpio_99, "gpio_name"); //通过gpio号申请gpio
    8. gpio_direction_output(gpio_99, 1); //设置gpio_99输出,初始值为1
    9. gpio_set_value(gpio_99, 0); //设置gpio_99值为0
    10. gpio_free(gpio_99);
    11. gpio_get_value(gpio_99, 0); //获取gpio_99的值

    这种方法目前最常用;

    gpio_request()流程:

    1. gpio_request()
    2. *desc = gpio_to_desc(gpio);
    3. gpiod_request(desc, label);
    4. gpiod_request_commit(desc, label);
    5. offset = gpio_chip_hwgpio(desc);
    6. gc->request(gc, offset);
    7. pl061->gc.request = gpiochip_generic_request;
    8. pinctrl_gpio_request(gc->gpiodev->base + offset); //drivers/pinctrl/core.c
    9. pin = gpio_to_pin(range, gpio);
    10. pinmux_request_gpio(pctldev, range, pin, gpio);
    11. pin_request(pctldev, pin, owner, range);
    12. ops->gpio_request_enable(pctldev, gpio_range, pin);

    可以看到GPIO子系统是通过pinctrl子系统来实现的。

    gpio_direction_output()流程:

    1. gpio_direction_output()
    2. gpiod_direction_output_raw(gpio_to_desc(gpio), value);
    3. gpiod_direction_output_raw_commit(desc, value);
    4. *gc = desc->gdev->chip;
    5. gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
    6. pl061->gc.direction_output = pl061_direction_output;
    4.3 Device-managed variants函数

    代码路径:drivers/gpio/gpiolib-devres.c

    1. struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
    2. enum gpiod_flags flags)
    3. struct gpio_desc *devm_gpiod_get_index(struct device *dev,
    4. const char *con_id,
    5. unsigned int idx,
    6. enum gpiod_flags flags)
    7. struct gpio_desc *devm_gpiod_get_optional(struct device *dev,
    8. const char *con_id,
    9. enum gpiod_flags flags)
    10. struct gpio_desc * devm_gpiod_get_index_optional(struct device *dev,
    11. const char *con_id,
    12. unsigned int index,
    13. enum gpiod_flags flags)

    释放gpio:

    1. void gpiod_put(struct gpio_desc *desc);
    2. void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
    4.4 新旧框架的相互转换
    1. gpio与gpio_desc结构体的相互转换:
    2. static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //gpiolib.c
    3. gpio_to_desc(unsigned gpio)
    4. &gpio_desc[gpio] //这里的gpio_desc是同名结构体的数组;
    5. int desc_to_gpio(const struct gpio_desc *desc) //gpiolib.c
    6. return desc - &gpio_desc[0];

    参考链接:

    Linux GPIO 驱动 (gpiolib)_devm_gpiochip_add_data-CSDN博客

    linux gpio学习笔记(gpiolib)_devm_gpiod_get_verdicty的博客-CSDN博客

  • 相关阅读:
    js 定时器 setInterval(图片的自动变换)
    C#,数值计算——插值和外推,双线性插值(Bilin_interp)的计算方法与源程序
    短视频seo矩阵系统源码开发搭建--代用户发布视频能力
    云原生Kubernetes:K8S概述
    Immutable 插件库的使用(数据深拷贝)
    Kubernetes 介绍
    十五、修改VGG16网络来适应自己的需求
    CentOS7安装Oracle完整教程(超级详细,亲测完美)
    Codeforces Round 924 (Div. 2)---->B. Equalize
    PostGreSQL:时间戳时区问题
  • 原文地址:https://blog.csdn.net/u011456016/article/details/133818303