本节说明使用Pinctrl子系统时,要掌握的重要概念。
上节我们说到,我们通过Pinctrl子系统来控制设备的引脚,但实际上,大多数芯片都没有一个单独的Pinctrl。引脚的复用、配置等操作,实际上是在GPIO子系统内部实现的。
但是,我们可以把Pinctrl(pin controller)控制器抽象出来,当做一个软件概念,他主要实现两个作用:
- 引脚复用
- 引脚配置(上下拉电阻等等)
将配置GPIO的工作交给Pinctrl子系统。这样,需要配置GPIO时,调用Pinctrl子系统的函数即可。
怎么使用Pinctrl子系统呢?
以串口模块为例:
首先,我们在设备树文件中定义一个UART节点,因为UART节点是Pinctrl子系统的用户,所以在内核文档中,它也被叫做client device,寓意为这个节点是一个用户,它会使用Pinctrl子系统。
UART节点中分别定义了两种状态(state)—— 正常工作状态和休眠状态;这两种状态分别要配置哪些管脚(group);管脚分别要配置成什么功能(function)。
要怎么配置状态,引脚,功能呢?
复用节点
以下图为例,在client端,定义了一个用户设备device,这个用户设备有两种状态,分别是“default”和“sleep”。
这两种状态与之后的属性名称是一一对应的,其中-0表示第一种状态,-1表示第二种状态。即“default”对应pinctrl-0;“sleep”对应pinctrl-1。
而pinctrl-0和pinctrl-1分别等于一个节点,这个节点就是用来描述在这个状态下,应该配置哪些管脚,这些管脚应该配置成什么样。
那么,这些节点位于哪里呢?——就在Pinctrl中。
当使用“default”状态时,就通过Pinctrl子系统,将指定group的管脚配置为“uart0”功能;当使用“sleep”状态是,则将指定管脚配置为“gpio”功能。
因为这些节点,是将相同的管脚在不同的状态下,被复用为了不同的功能,所以在内核文档中,这些节点也被叫做复用节点(Generic pin multiplexing node)。
配置节点
另外,还有一个配置节点(Generic pin configuration node)。
与配置节点一样,当处于“default”状态时,配置为“uart0”功能;但是当处于“sleep”状态时,管脚被直接配置为了输出高电平,而不是某种function,这样的节点,在内核文档中就被叫做配置节点(Generic pin configuration node)。
总结
对于上面的配置,我们可以将左右分为两部分,左边是controller,右边是client。
在client中,配置不同的状态,给不同的状态指定不同的节点,而这些节点位于controller中,将哪些管脚配置为哪些功能或者状态。
对于client,它的格式通常都是固定的,但是对于controller,就没有统一的格式了。
虽然controller没有统一的格式,但是group,function,复用,配置,这些概念都是统一的。
举几个例子来看一下:
右边是client,它们的格式都是统一的,但是左边的controller,他们就没有什么统一的格式了。
代码中怎么引用Pinctrl?
这是透明的,我们的驱动基本不用管。当设备切换状态时,对应的pinctrl就会被调用。
比如在platform_device和platform_driver的枚举过程中,流程如下:
类似的,当系统休眠时,也会去设置该设备sleep状态对应的引脚,不需要我们自己去调用代码。
非要自己调用,也有函数(暂时不涉及,后续有需要再研究):
devm_pinctrl_get_select_default(struct device *dev); // 使用"default"状态的引脚
pinctrl_get_select(struct device *dev, const char *name); // 根据name选择某种状态的引脚
pinctrl_put(struct pinctrl *p); // 不再使用, 退出时调用