• pinctrl子系统 - pinctrl与gpio之间的耦合(六)


    1,gpio subsystem和pinctrl subsystem的关系

    作为软件工程师,我们期望的硬件设计应该如下图所示:

    GPIO的HW block应该和其他功能复用的block是对等关系的,它们共同输入到一个复用器block,这个block的寄存器控制哪一个功能电路目前是active的。pin configuration是全局的,不论哪种功能是active的,都可以针对pin进行电气特性的设定。这样的架构下,上图中红色边框的三个block是完全独立的HW block,其控制寄存器在SOC datasheet中应该是分成三个章节描述,同时,这些block的寄存器应该分别处于不同的地址区间。

    对于软件工程师,我们可以让pin control subsystem和GPIO subsystem完全独立,各自进行初始化,各自映射自己的寄存器地址空间,对于pin control subsystem而言,GPIO和其他的HW block没有什么不同,都是使用自己提供服务的一个软件模块而已。然而实际上SOC的设计并非总是向软件工程师期望的那样,有的SOC的设计框架图如下:

    这时候,GPIO block是alway active的,而红色边框的三个block是紧密的捆绑在一起,它们的寄存器占据了一个memory range(datasheet中用一个章节描述这三个block)。这时候,对于软件工程师来说就有些纠结了,本来不属于我的GPIO控制也被迫要参与进来。这时候,硬件寄存器的控制都是pin controller来处理,GPIO相关的操作都要经过pin controller driver,这时候,pin controller driver要作为GPIO driver的back-end出现。

    2,为什么是这种关系

    任何一个gpio chip,在使用GPIO的时候(通常是gpio subsystem的consumer申请GPIO资源的时候),都需要向系统的pinctrl subsystem申请管脚,并将管脚配置为GPIO功能。

    思路是简单、直接的,但实际操作起来,却有点棘手,下面以一个最简单的例子说明:

    假设某一个gpio chip只包括2个gpio,这两个gpio分别和uart进行功能复用。

    如果这两个管脚是同时控制的,要么是gpio,要么是uart,就很好处理了,按照pinctrl subsystem的精神,抽象出两个function:gpio和uart,gpio chip在使用gpio功能的时候,调用pinctrl set state,将它们切换为gpio即可。

    但是,如果这两个gpio可以分开控制(很多硬件都是这样的设计的),麻烦就出现了,每个gpio要单独抽象为一个function,因此我们可以抽象出3个function:gpio1、gpio2和uart。

    然后考虑一下一个包含32个gpio的chip(一般硬件的gpio bank都是32个),如果它们都可以单独控制,则会出现32个function。而系统又不止有一个chip,灾难就发生了,我们的device tree文件将会被一坨坨的gpio functions撑爆!

    规范是我们追求的目标,但有限度,不能让上面的事情发生,怎么办呢?在pinctrl subsystem的标准框架上打个洞,只要碰到这种情况,直接就走后门就是了。

    3,pinctrl中和gpio有关的后门

    后门是什么呢?其实很简单,参考下面的API:

    1. static inline int pinctrl_request_gpio(unsigned gpio) ;
    2. static inline void pinctrl_free_gpio(unsigned gpio) ;
    3. static inline int pinctrl_gpio_direction_input(unsigned gpio);
    4. static inline int pinctrl_gpio_direction_output(unsigned gpio);
    5. 当gpio driver需要使用某个管脚的时候,直接调用pinctrl_request_gpio,向pinctrl subsystem申请。
    6. pinctrl subsystem会维护一个gpio number到pin number的map,将gpio subsystem传来的gpio number转换为pin number之后,调用struct pinmux_ops中有关的回调函数即可:
    7. struct pinmux_ops {
    8. ...
    9. int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
    10. struct pinctrl_gpio_range *range,
    11. unsigned offset);
    12. void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
    13. struct pinctrl_gpio_range *range,
    14. unsigned offset);
    15. int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
    16. struct pinctrl_gpio_range *range,
    17. unsigned offset,
    18. bool input);
    19. };
    20. 对gpio driver来说,要做的事情就是提供gpio number到pin number的map。
    21. 而pinctrl subsystem呢,做自己分内的事情即可:管理系统的pin资源,并根据gpio subsystem的请求,将某些pin设置为GPIO功能。

    4,gpio range----gpio number到pin number的map

    4.1 gpio range

    那么,怎么提供gpio number和pin number的map呢?是通过一个名称为gpio range的数据结构(pinctrl subsystem提供的),如下:

    1. /* include/linux/pinctrl/pinctrl.h */
    2. /**
    3. * struct pinctrl_gpio_range - each pin controller can provide subranges of
    4. * the GPIO number space to be handled by the controller
    5. * @node: list node for internal use
    6. * @name: a name for the chip in this range
    7. * @id: an ID number for the chip in this range
    8. * @base: base offset of the GPIO range
    9. * @pin_base: base pin number of the GPIO range if pins == NULL
    10. * @pins: enumeration of pins in GPIO range or NULL
    11. * @npins: number of pins in the GPIO range, including the base number
    12. * @gc: an optional pointer to a gpio_chip
    13. */
    14. struct pinctrl_gpio_range {
    15. struct list_head node;
    16. const char *name;
    17. unsigned int id;
    18. unsigned int base;
    19. unsigned int pin_base;
    20. unsigned const *pins;
    21. unsigned int npins;
    22. struct gpio_chip *gc;
    23. };

    该数据结构很容易理解,总结来说,就是:gpio controller(gc)中的gpio(base)到gpio(base + npins - 1),和pin controller中的pin(pin_base)到pin(pin_base + npins - 1)是一一对应的。

    有了这个对应关系之后,pinctrl subsystem就可以将任意一个gpio controller中的gpio number转换为相应的pin controller中的pin number。

    4.2 gpio number到pin number的map,代码流程
    1. int msm_pinctrl_probe(struct platform_device *pdev, const struct msm_pinctrl_soc_data *soc_data)
    2. msm_gpio_init(pctrl);
    3. gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 0, 0, chip->ngpio);

    gpiochip_add_pin_range:

    1. int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
    2. unsigned int gpio_offset, unsigned int pin_offset,
    3. unsigned int npins)
    4. {
    5. struct gpio_pin_range *pin_range;
    6. struct gpio_device *gdev = gc->gpiodev;
    7. int ret;
    8. //分配 struct gpio_pin_range内存
    9. pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
    10. if (!pin_range) {
    11. chip_err(gc, "failed to allocate pin ranges\n");
    12. return -ENOMEM;
    13. }
    14. /* Use local offset as range ID */
    15. //初始化pin_range,资源来自于gpio_chip和gpio_device
    16. pin_range->range.id = gpio_offset;
    17. pin_range->range.gc = gc;
    18. pin_range->range.name = gc->label;
    19. pin_range->range.base = gdev->base + gpio_offset;
    20. pin_range->range.pin_base = pin_offset;
    21. pin_range->range.npins = npins;
    22. pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
    23. &pin_range->range);
    24. if (IS_ERR(pin_range->pctldev)) {
    25. ret = PTR_ERR(pin_range->pctldev);
    26. chip_err(gc, "could not create pin range\n");
    27. kfree(pin_range);
    28. return ret;
    29. }
    30. chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
    31. gpio_offset, gpio_offset + npins - 1,
    32. pinctl_name,
    33. pin_offset, pin_offset + npins - 1);
    34. //将pin_range node挂到gpio_deviced的pin_ranges链表
    35. list_add_tail(&pin_range->node, &gdev->pin_ranges);
    36. return 0;
    37. }

    pinctrl_find_and_add_gpio_range,register a GPIO range for a controller:

    1. struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
    2. struct pinctrl_gpio_range *range)
    3. {
    4. struct pinctrl_dev *pctldev;
    5. pctldev = get_pinctrl_dev_from_devname(devname);
    6. /*
    7. * If we can't find this device, let's assume that is because
    8. * it has not probed yet, so the driver trying to register this
    9. * range need to defer probing.
    10. */
    11. if (!pctldev) {
    12. return ERR_PTR(-EPROBE_DEFER);
    13. }
    14. pinctrl_add_gpio_range(pctldev, range);
    15. return pctldev;
    16. }
    17. void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev,
    18. struct pinctrl_gpio_range *range)
    19. {
    20. mutex_lock(&pctldev->mutex);
    21. //将pinctrl_gpio_range node挂到pinctrl_dev的gpio_ranges链表
    22. list_add_tail(&range->node, &pctldev->gpio_ranges);
    23. mutex_unlock(&pctldev->mutex);
    24. }
    4.3 gpio-ranges debug info
    1. static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
    2. {
    3. struct pinctrl_dev *pctldev = s->private;
    4. struct pinctrl_gpio_range *range;
    5. seq_puts(s, "GPIO ranges handled:\n");
    6. mutex_lock(&pctldev->mutex);
    7. /* Loop over the ranges */
    8. list_for_each_entry(range, &pctldev->gpio_ranges, node) {
    9. if (range->pins) {
    10. int a;
    11. seq_printf(s, "%u: %s GPIOS [%u - %u] PINS {",
    12. range->id, range->name,
    13. range->base, (range->base + range->npins - 1));
    14. for (a = 0; a < range->npins - 1; a++)
    15. seq_printf(s, "%u, ", range->pins[a]);
    16. seq_printf(s, "%u}\n", range->pins[a]);
    17. }
    18. else
    19. seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
    20. range->id, range->name,
    21. range->base, (range->base + range->npins - 1),
    22. range->pin_base,
    23. (range->pin_base + range->npins - 1));
    24. }
    25. mutex_unlock(&pctldev->mutex);
    26. return 0;
    27. }
    28. DEFINE_SHOW_ATTRIBUTE(pinctrl_gpioranges);
    1. cat /sys/kernel/debug/pinctrl/f000000.pinctrl/gpio-ranges
    2. GPIO ranges handled:
    3. 0: f000000.pinctrl GPIOS [341 - 511] PINS [0 - 170]

    5,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);
    13. .gpio_request_enable = msm_pinmux_request_gpio,
    14. msm_pinmux_set_mux(pctldev, g->funcs[pctrl->soc->gpio_func], offset);

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

    参考链接:

    linux内核中的GPIO系统之(5):gpio subsysem和pinctrl subsystem之间的耦合

  • 相关阅读:
    外汇天眼:这才是外汇维权的正确打开方式,还不get?
    table 滚动时固定表头
    2023常见自动化测试工具集合
    【图解计算机网络】从浏览器地址输入到网页显示的整个过程
    新功能&案例分享丨DolphinDB 与你相约上海,报名限时开放!
    【21】c++设计模式——>装饰模式
    函数的参数
    Google Earth Engine 教程——NDVI差异分析以及图像采集迭代分析
    MyBatis八股文
    如何设计大电流九线导电滑环
  • 原文地址:https://blog.csdn.net/u011456016/article/details/133853040