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:
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功能。
4,gpio range----gpio number到pin number的map
4.1 gpio range
那么,怎么提供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。
4.2 gpio number到pin number的map,代码流程
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);
}
4.3 gpio-ranges debug info
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]
5,gpio_request()流程
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子系统来实现的。
参考链接: