作为软件工程师,我们期望的硬件设计应该如下图所示:
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出现。
任何一个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的标准框架上打个洞,只要碰到这种情况,直接就走后门就是了。
后门是什么呢?其实很简单,参考下面的API:
- static inline int pinctrl_request_gpio(unsigned gpio) ;
- static inline void pinctrl_free_gpio(unsigned gpio) ;
- static inline int pinctrl_gpio_direction_input(unsigned gpio);
- static inline int pinctrl_gpio_direction_output(unsigned gpio);
-
-
- 当gpio driver需要使用某个管脚的时候,直接调用pinctrl_request_gpio,向pinctrl subsystem申请。
- pinctrl subsystem会维护一个gpio number到pin number的map,将gpio subsystem传来的gpio number转换为pin number之后,调用struct pinmux_ops中有关的回调函数即可:
- struct pinmux_ops {
- ...
- int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned offset);
- void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned offset);
- int (*gpio_set_direction) (struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range,
- unsigned offset,
- bool input);
- };
- 对gpio driver来说,要做的事情就是提供gpio number到pin number的map。
- 而pinctrl subsystem呢,做自己分内的事情即可:管理系统的pin资源,并根据gpio subsystem的请求,将某些pin设置为GPIO功能。
那么,怎么提供gpio number和pin number的map呢?是通过一个名称为gpio range的数据结构(pinctrl subsystem提供的),如下:
- /* include/linux/pinctrl/pinctrl.h */
-
- /**
- * struct pinctrl_gpio_range - each pin controller can provide subranges of
- * the GPIO number space to be handled by the controller
- * @node: list node for internal use
- * @name: a name for the chip in this range
- * @id: an ID number for the chip in this range
- * @base: base offset of the GPIO range
- * @pin_base: base pin number of the GPIO range if pins == NULL
- * @pins: enumeration of pins in GPIO range or NULL
- * @npins: number of pins in the GPIO range, including the base number
- * @gc: an optional pointer to a gpio_chip
- */
- struct pinctrl_gpio_range {
- struct list_head node;
- const char *name;
- unsigned int id;
- unsigned int base;
- unsigned int pin_base;
- unsigned const *pins;
- unsigned int npins;
- struct gpio_chip *gc;
- };
该数据结构很容易理解,总结来说,就是: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。
- int msm_pinctrl_probe(struct platform_device *pdev, const struct msm_pinctrl_soc_data *soc_data)
- msm_gpio_init(pctrl);
- gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), 0, 0, chip->ngpio);
gpiochip_add_pin_range:
- int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
- unsigned int gpio_offset, unsigned int pin_offset,
- unsigned int npins)
- {
- struct gpio_pin_range *pin_range;
- struct gpio_device *gdev = gc->gpiodev;
- int ret;
-
- //分配 struct gpio_pin_range内存
- pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
- if (!pin_range) {
- chip_err(gc, "failed to allocate pin ranges\n");
- return -ENOMEM;
- }
-
- /* Use local offset as range ID */
- //初始化pin_range,资源来自于gpio_chip和gpio_device
- pin_range->range.id = gpio_offset;
- pin_range->range.gc = gc;
- pin_range->range.name = gc->label;
- pin_range->range.base = gdev->base + gpio_offset;
- pin_range->range.pin_base = pin_offset;
- pin_range->range.npins = npins;
- pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
- &pin_range->range);
- if (IS_ERR(pin_range->pctldev)) {
- ret = PTR_ERR(pin_range->pctldev);
- chip_err(gc, "could not create pin range\n");
- kfree(pin_range);
- return ret;
- }
- chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
- gpio_offset, gpio_offset + npins - 1,
- pinctl_name,
- pin_offset, pin_offset + npins - 1);
-
- //将pin_range node挂到gpio_deviced的pin_ranges链表
- list_add_tail(&pin_range->node, &gdev->pin_ranges);
-
- return 0;
- }
pinctrl_find_and_add_gpio_range,register a GPIO range for a controller:
- struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname,
- struct pinctrl_gpio_range *range)
- {
- struct pinctrl_dev *pctldev;
-
- pctldev = get_pinctrl_dev_from_devname(devname);
-
- /*
- * If we can't find this device, let's assume that is because
- * it has not probed yet, so the driver trying to register this
- * range need to defer probing.
- */
- if (!pctldev) {
- return ERR_PTR(-EPROBE_DEFER);
- }
- pinctrl_add_gpio_range(pctldev, range);
-
- return pctldev;
- }
-
- void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev,
- struct pinctrl_gpio_range *range)
- {
- mutex_lock(&pctldev->mutex);
- //将pinctrl_gpio_range node挂到pinctrl_dev的gpio_ranges链表
- list_add_tail(&range->node, &pctldev->gpio_ranges);
- mutex_unlock(&pctldev->mutex);
- }
- static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
- {
- struct pinctrl_dev *pctldev = s->private;
- struct pinctrl_gpio_range *range;
-
- seq_puts(s, "GPIO ranges handled:\n");
-
- mutex_lock(&pctldev->mutex);
-
- /* Loop over the ranges */
- list_for_each_entry(range, &pctldev->gpio_ranges, node) {
- if (range->pins) {
- int a;
- seq_printf(s, "%u: %s GPIOS [%u - %u] PINS {",
- range->id, range->name,
- range->base, (range->base + range->npins - 1));
- for (a = 0; a < range->npins - 1; a++)
- seq_printf(s, "%u, ", range->pins[a]);
- seq_printf(s, "%u}\n", range->pins[a]);
- }
- else
- seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
- range->id, range->name,
- range->base, (range->base + range->npins - 1),
- range->pin_base,
- (range->pin_base + range->npins - 1));
- }
-
- mutex_unlock(&pctldev->mutex);
-
- return 0;
- }
- DEFINE_SHOW_ATTRIBUTE(pinctrl_gpioranges);
- cat /sys/kernel/debug/pinctrl/f000000.pinctrl/gpio-ranges
-
- GPIO ranges handled:
- 0: f000000.pinctrl GPIOS [341 - 511] PINS [0 - 170]
- gpio_request()
- *desc = gpio_to_desc(gpio);
- gpiod_request(desc, label);
- gpiod_request_commit(desc, label);
- offset = gpio_chip_hwgpio(desc);
- gc->request(gc, offset);
- pl061->gc.request = gpiochip_generic_request;
- pinctrl_gpio_request(gc->gpiodev->base + offset); //drivers/pinctrl/core.c
- pin = gpio_to_pin(range, gpio);
- pinmux_request_gpio(pctldev, range, pin, gpio);
- pin_request(pctldev, pin, owner, range);
- ops->gpio_request_enable(pctldev, gpio_range, pin);
- .gpio_request_enable = msm_pinmux_request_gpio,
- msm_pinmux_set_mux(pctldev, g->funcs[pctrl->soc->gpio_func], offset);
可以看到GPIO子系统是通过pinctrl子系统来实现的。
参考链接: