X-006-UBOOT-pinctrl driver移植(Bubblegum-96平台)
作者:wowo 发布于:2016-7-9 21:47 分类:X Project
1. 前言
wowo觉得,在linux kernel新引入的众多子系统中,pinctrl subsystem是一个特别晦涩难懂的子系统,它所解决的问题,和它所引入的困扰,不相上下。在平时工作的过程中,年轻工程师问的最多的,就是在驱动中要怎么使用pinctrl?这样配置pinctrl到底是什么意思?等等。
对一个子系统来说,如果不能让它的使用者(consumer)很容易的理解和掌握,就宣告了它的失败。更不用说让它的提供者(provider)简单、快速地编写驱动程序了。
为什么会这样呢?通俗一点讲,就是“大炮打蚊子”。从技术的角度看,pinctrl是一个非常优秀的子系统,有着复杂而巧妙的封装和抽象,但它所要解决的问题,实际上非常简单、直白。这就造成了一个落差,从而带来了各种理解上的困惑。
正因为此,u-boot在照搬linux kernel pinctrl的同时,额外提供了一种简洁的方法。本文将借助“X Project” u-boot中pin control driver的移植过程,介绍、分析这种方法,并以此理解pinctrl的本质。
与此同时,我们会基于“X-005-UBOOT-device tree移植(Bubblegum-96平台)”,扩展对device tree的使用,以加深对device tree的理解和掌握。
2. pinctrl要解决的问题
有关pinctrl的基本概念,可参考本站的三篇文章(本文不再赘述了):
基于上面三篇文章,我们可以总结出如下要点:
1)CPU中有些管脚的功能和参数,是可配置的。例如D5和A8两个管脚(名称是随便写的),即可以配置成具有上拉功能的I2C2的SCL/SDA,也可以配置为UART5的RX/TX,又可以配置为两个正常的GPIO,等等。
2)这些可配置参数的组合,受限于具体CPU的功能,个数是有限的。这一点很重要。
3)某个产品的硬件设计确定下来之后,这些可配置的功能和参数,基本上都确定了下来了,很少会在运行时修改。例如,连接3G modem的UART2,由于物理连接的限制,它所在对应的管脚,以及这些管脚的参数,都已经固定了。这一点同样很重要。
因此,pinctrl的本质功能,可总结为:
产品(或平台)的硬件设计确定后,在某些运行状态下,某些管脚的功能和参数也已经确定。软件需要根据这些确定的信息,在需要的时候(通常是对应的driver初始化的时候),将这些管脚配置成所需的功能和参数(通常是通过寄存器配置)。
而本着抽象和封装的原则,我们不希望每个驱动工程师,在为各自负责的硬件模块编写驱动的时候,都要研究一下本平台的硬件设计,确定和该硬件模块有关的管脚分配情况(这在复杂的SOC系统中,是一项非常繁琐的事情)。因此希望pinctrl driver,能否将这些繁琐的事情,封装起来,以比较友好的方式,向各个设备驱动提供服务。
听着有点拗口(但我相信大家都理解),下面我以Bubblegum-96平台调试用的UART5(可参考“X-004-UBOOT-串口驱动移植(Bubblegum-96平台)”)为例,做进一步的说明:
由“X-004-UBOOT-串口驱动移植(Bubblegum-96平台)”的描述可知,Bubblegum-96平台中,UART5是一个很明确的功能,它的RX/TX两个信号,分别连接到S900的A7和D8两个管脚上。这两个管脚和GPIOA25、GPIOA27、SENS0_VSYNC、SENS0_HSYNC、PWM2等功能复用。
想要UART5正常工作,就需要把A7和D8两个管脚配置为UART_RX和UART_TX功能(通过MFP_CTL1寄存器的bit28:23)。如果没有pinctrl driver,这个配置的工作,就需要serial driver负责(就像“X-004-UBOOT-串口驱动移植(Bubblegum-96平台)”中所描述的那样)。但这显然不符合模块化的设计理念,因此需要pinctrl driver代劳。
pinctrl driver要做什么事情呢?
它要抽象出来一个UART5功能(function,例如“pinctrl_serial5”),并允许serial driver使用这个function。具体的配置工作,由pinctrl driver负责,serial driver只需要“拿来主义”即可。具体可参考后面章节的介绍。
3. 设计思路
基于上面的描述,我们将设计一个最简的pinctrl driver,思路如下:
1)基于具体的版型(这里是Bubblegum-96),以该版型所对应的硬件功能为单位,将管脚配置抽象出来。借助device tree描述,有如下的架构:
/* arch/arm/dts/s900-bubblegum.dts */
#include "s900-bubblegum-pinctrl.dtsi"
“s900-bubblegum.dts”是Bubblegum-96的硬件描述,包含了描述该版型下所有的管脚配置的“s900-bubblegum-pinctrl.dtsi”文件:
/* arch/arm/dts/s900-bubblegum-pinctrl.dtsi */
#include "s900.dtsi"
/ {
pinctrl@e01b0000 {
/* actions,pins =, ...*/
pinctrl_serial5: serial5 {
actions,pins = <0x0044 23="" 0x3f="" 0xc="">;
};
…
};
};
其中黄色背景所描述的dts节点,就是对应的硬件功能,本例为用于控制台的UART5。
2)每一个硬件功能,所对应的管脚配置,都由一系列的寄存器配置值表示,这些值最终会写入到pinctrl的寄存器中,使对应的硬件功能生效,如上面例子中红色背景的部分,格式说明如下。
actions,pins =
, ... reg,所要操作的寄存器值,是相对pinctrl寄存器(e01b0000)的一个偏移偏移;
offset,所要配置的value在寄存器中的偏移量(位置);
mask,所需要配置的value的mask。
value,所要配置的value。
例如,需要使能S900 SOC A7和D8管脚的UART5功能,需要将0xe01b0044(MFP_CTL1)的bit28:23配置为0xc,只需如下的配置即可:actions,pins = <0x0044 23="" 0x3f="" 0xc="">;
当然,如果需要多组寄存器配置,也是okay的,只要:actions,pins = <0x0044 23="" 0x3f="" 0xc="">, <0x00xx xx="" 0xxx="">, …;即可。
3)对应的硬件功能的driver,需要在自己的dts节点中,引用“管脚配置”(pinctrl_serial5),这样就可以在u-boot启动的时候,由设备模型调用pinctrl的接口,自动将寄存器配置生效。或者,在需要的时候,手动调用pinctrl的接口,将某个pinctrl配置项生效,如下:
/* arch/arm/dts/s900-bubblegum.dts */
/ {
…
serial5: serial@e012a000 {
u-boot,dm-pre-reloc;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_serial5>;
};
};
u-boot的设备模型,配合pinctrl core,会在设备枚举的时候,将设备中pinctrl-name为“default”或者pinctrl句柄为“pinctrl-0”的pinctrl配置项生效。
另外,我们可以指定多个pinctrl功能,并在driver中随意的使能/禁止相应的功能,这里不再详细介绍,需要使用的同学可自行揣摩。
4)基于该设计思路,pinctrl有关的开发任务可总结为:
a)硬件设计确定后,负责pinctrl driver的工程师,需要将各个硬件功能以及对应的寄存器配置,抽象出来,保存在“xxx-pinctrl.dtsi ”中。
b)其它设备驱动,在需要的时候,引用并使用“xxx-pinctrl.dtsi ”所抽象的功能。
4. 移植过程及说明
4.1 u-boot pinctrl子系统介绍
u-boot的pinctrl子系统和linux kernel的非常类似,因此也比较复杂,这里只列出一些要点(具体细节不再详细介绍):
1)基于PINCTRL uclass。
2)根据复杂度、代码量、标准fdt的支持等不同,提供全功能(CONFIG_PINCTRL_FULL)、通用功能(CONFIG_PINCTRL_GENERIC)等不同类型的功能。我们只使用全功能(CONFIG_PINCTRL_FULL),并且只使用其中的简单接口(set_state_simple)。
4.2 新建一个pinctrl驱动,并添加到u-boot的编译框架中
1)新建s900 soc的pinctrl驱动----pinctrl-owl.c(OWL可能是S900 soc所在的系列的代码,copy自Actions代码,大家不用太纠结)。
touch drivers/pinctrl/pinctrl-owl.c
2)修改u-boot pinctrl driver的Kconfig和Makefile文件,将该驱动添加进去
/* drivers/pinctrl/Kconfig */
@@ -141,6 +141,11 @@ config PIC32_PINCTRL
by a device tree node which contains both GPIO defintion and pin control
functions.
+config OWL_PINCTRL
+ bool "Actions pin control driver for OWL soc"
+ depends on DM
+ help
+ Support pin multiplexing control on Actions OWL SoCs.
endif
source "drivers/pinctrl/nxp/Kconfig"
/* drivers/pinctrl/Makefile */
@@ -11,3 +11,5 @@ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
obj-$(CONFIG_PIC32_PINCTRL) += pinctrl_pic32.o
+
+obj-$(CONFIG_OWL_PINCTRL) += pinctrl-owl.o
4.3 配置u-boot,使能pinctrl有关的配置项
/* configs/bubblegum_defconfig */
#
# Pin controllers
#
-# CONFIG_PINCTRL is not set
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+# CONFIG_ROCKCHIP_PINCTRL is not set
+# CONFIG_ROCKCHIP_3036_PINCTRL is not set
+CONFIG_OWL_PINCTRL=y
#
# Power
其中CONFIG_PINCTRL为pinctrl总开关。CONFIG_PINCTRL_FULL可以提供比较全面的pinctrl功能,暂时不用深究。CONFIG_OWL_PINCTRL使我们新加的pinctrl driver。
4.4 添加pinctrl device有关的device tree描述
在这里,我们将DTS文件拆分为三个:
s900.dtsi,SOC级别的文件,存放该SOC共同的内容,如serial controller、pinctrl controller等通用的配置。
s900-bubblegum-pinctrl.dtsi,Bubblegum-96平台所对应的pinctrl配置,具体可参考第3章的介绍。
s900-bubblegum.dts,Bubblegum-96平台的dts文件,存放该平台特有的内容。
对于pinctrl device来说,需要在s900.dtsi中定义基本的controller信息,如下:
+ pinctrl@e01b0000 {
+ compatible = "actions,s900-pinctrl";
+ reg = <0 0="" 0xe01b0000="" 0x1000="">;
+ };
然后在s900-bubblegum-pinctrl.dtsi中定义所有的pinctrl功能,以及相应的寄存器配置信息,如下:
+ pinctrl@e01b0000 {
+ /* actions,pins =, ...*/
+ pinctrl_serial5: serial5 {
+ actions,pins = <0x0044 23="" 0x3f="" 0xc="">;
+ };
…
+ };
最后,在中定义版型特有的配置:
+ pinctrl@e01b0000 {
+ u-boot,dm-pre-reloc;
+ };
4.5 编写pinctrl driver
要点包括(具体可参考“https://github.com/wowotechX/u-boot/blob/7340ec6450f5cfa8076dac59811ef2e459dc92b4/drivers/pinctrl/pinctrl-owl.c”):
1)基于pinctrl uclass,注册owl_pinctrl driver,并结合dts文件,添加对应的of_match table
static const struct udevice_id owl_pinctrl_match[] = {
{ .compatible = "actions,s900-pinctrl" },
{ /* sentinel */ }
};U_BOOT_DRIVER(owl_pinctrl) = {
.name = "owl_pinctrl",
.id = UCLASS_PINCTRL,
.of_match = owl_pinctrl_match,
.probe = owl_pinctrl_probe,
.ops = &owl_pinctrl_ops,
.flags = DM_FLAG_PRE_RELOC,
.priv_auto_alloc_size = sizeof(struct owl_pinctrl_priv),
};
2)提供probe(owl_pinctrl_probe)接口,以便获取pinctrl device的寄存器基地址,并保存在priv_auto_alloc_size 中(struct owl_pinctrl_priv)。
3)pinctrl uclass要求实现对应的struct pinctrl_ops结构,我们只实现其中最简单的一个----set_state_simple:
static const struct pinctrl_ops owl_pinctrl_ops = {
.set_state_simple = owl_pinctrl_set_state_simple,
};
4)set_state_simple的原型如下:
static int owl_pinctrl_set_state_simple(struct udevice *dev,
struct udevice *periph)
其中dev代表pinctrl device,periph代表引用了相应pinctrl功能的device(如本文中的serial device)。我们需要
在这个接口中,通过“pinctrl-0”关键字,找到该设备所引用的所有pinctrl节点的句柄,并以此获得对应的寄存器配置信息(通过"actions,pins"关键字),然后将配置信息写入到具体的寄存器即可,如下:
{
int entry, count, i;
uint32_t cell[20];
struct owl_pinctrl_priv *priv = dev_get_priv(dev);entry = fdtdec_lookup_phandle(gd->fdt_blob, periph->of_offset,
"pinctrl-0");
if (entry < 0)
return -1;count = fdtdec_get_int_array_count(gd->fdt_blob, entry, "actions,pins",
cell, ARRAY_SIZE(cell));
if (count < 4)
return -1;for (i = 0; i < count / 4; i += 4)
clrsetbits_le32(priv->base + cell[i],
cell[i + 2] << cell[i + 1],
cell[i + 3] << cell[i + 1]);return 0;
}
注1:当前的实现,只处理了pinctrl-0中第一个字段,后续可以增加多个字段的处理。
5. pinctrl的生效过程
正常情况下(如第4章中serial driver的例子),设备驱动在dts文件中引用对应的pinctrl配置后,设备模型会在设备枚举的时候,自动调用pinctrl提供的接口,将配置生效,具体过程可参考如下的流程(比较简单,因此不再详细分析,感兴趣的同学可以自己阅读源代码):
serial_init—>serial_find_console_or_panic(drivers/serial/serial-uclass.c)
device_probe(drivers/core/device.c)
pinctrl_select_state(dev, "default")
pinctrl_select_state_full—>pinctrl_select_state_simple(drivers/pinctrl/pinctrl-uclass.c)
ops->set_state_simple(pctldev, dev);(drivers/pinctrl/pinctrl-owl.c)
6. 总结
本文通过一个简单的实例,介绍了在u-boot中实现一个简单的pinctrl driver的步骤,以加深对pinctrl subsystem理解。与此同时,通过引入更多的device tree操作(例如分离出不同的DTS文件、通过DTS解析pinctrl配置信息等),也进一步加深了我们对device tree的理解。
最后,本文涉及到的示例代码,可以从如下链接获取:
https://github.com/wowotechX/u-boot/commit/7340ec6450f5cfa8076dac59811ef2e459dc92b4
原创文章,转发请注明出处。蜗窝科技,www.wowotech.net。
标签: subsystem u-boot porting pinctrl

评论:
2017-07-21 16:39
是的。
2、这个文件中需要列出那些设备有什么决定?
由硬件决定。
3、您说pin有几种功能是没有关系的,但是我们在区分功能的时候是什么依据?
你是指状态吗?“default”之类的?状态是根据工作状态,例如正常工作和suspend状态,有些设备可能需要不同的管脚配置。
2017-07-25 08:20
1、{ 2、这个文件中需要列出那些设备有什么决定?
由硬件决定。
}
例如:dts中一个设备对应了下面的这几个文件:
s3c2416.dtsi s3c2416-smdk2416.dts s3c2416-pinctrl.dtsi s3c24xx.dtsi
你说的硬件是不是就是s3c2416-smdk2416.dts这个文件中所列出的。
2、{您说pin有几种功能是没有关系的,但是我们在区分功能的时候是什么依据?
你是指状态吗?“default”之类的?状态是根据工作状态,例如正常工作和 suspend状态,有些设备可能需要不同的管脚配置。
}
我说的并不是状态,我们这个里面没有其他的什么状态,只有一个default状态。
我的意思是对于一个emmc这个设备为什么要分成这三个节点:
&emmc_p1_pmx_cfg
&emmc_p2_pmx_cfg
&emmc_p3_pmx_cfg
去描述,不能一个,两个..。这其中有什么依据。
2016-07-10 16:30
pinctrl@e01b0000 {
pinctrl_funcA: funcA {
...
};
pinctrl_funcB: funcB {
...
};
pinctrl_funcC: funcC {
...
};
...
};
然后在需要使用的DTS中,将它们都引用过来,如:
pinctrl-names = "funcA", "funcCB", "funcC";
pinctrl-0 = <&pinctrl_funcA>. <&pinctrl_funcB>. <&pinctrl_funcC>;
在driver中,就可以调用标准接口,切换这三个状态了。
pinctrl的设计思路,大体如此。
功能
最新评论
- wangjing
写得太好了 - wangjing
写得太好了! - DRAM
圖面都沒辦法顯示出來好像掛點了。 - Simbr
bus至少是不是还有个subsystem? - troy
@testtest:只要ldrex-modify-strex... - gh
Linux 内核在 sparse 内存模型基础上实现了vme...
文章分类
随机文章
文章存档
- 2025年4月(5)
- 2024年2月(1)
- 2023年5月(1)
- 2022年10月(1)
- 2022年8月(1)
- 2022年6月(1)
- 2022年5月(1)
- 2022年4月(2)
- 2022年2月(2)
- 2021年12月(1)
- 2021年11月(5)
- 2021年7月(1)
- 2021年6月(1)
- 2021年5月(3)
- 2020年3月(3)
- 2020年2月(2)
- 2020年1月(3)
- 2019年12月(3)
- 2019年5月(4)
- 2019年3月(1)
- 2019年1月(3)
- 2018年12月(2)
- 2018年11月(1)
- 2018年10月(2)
- 2018年8月(1)
- 2018年6月(1)
- 2018年5月(1)
- 2018年4月(7)
- 2018年2月(4)
- 2018年1月(5)
- 2017年12月(2)
- 2017年11月(2)
- 2017年10月(1)
- 2017年9月(5)
- 2017年8月(4)
- 2017年7月(4)
- 2017年6月(3)
- 2017年5月(3)
- 2017年4月(1)
- 2017年3月(8)
- 2017年2月(6)
- 2017年1月(5)
- 2016年12月(6)
- 2016年11月(11)
- 2016年10月(9)
- 2016年9月(6)
- 2016年8月(9)
- 2016年7月(5)
- 2016年6月(8)
- 2016年5月(8)
- 2016年4月(7)
- 2016年3月(5)
- 2016年2月(5)
- 2016年1月(6)
- 2015年12月(6)
- 2015年11月(9)
- 2015年10月(9)
- 2015年9月(4)
- 2015年8月(3)
- 2015年7月(7)
- 2015年6月(3)
- 2015年5月(6)
- 2015年4月(9)
- 2015年3月(9)
- 2015年2月(6)
- 2015年1月(6)
- 2014年12月(17)
- 2014年11月(8)
- 2014年10月(9)
- 2014年9月(7)
- 2014年8月(12)
- 2014年7月(6)
- 2014年6月(6)
- 2014年5月(9)
- 2014年4月(9)
- 2014年3月(7)
- 2014年2月(3)
- 2014年1月(4)
2017-07-20 16:11
1、在看这个文件的过程中发现它其实就是在配置管脚(复用寄存器)的复用功能!
2、这个文件中需要列出那些设备有什么决定?
3、您说pin有几种功能是没有关系的,但是我们在区分功能的时候是什么依据?
比如:
pmx0: pinconf@f8a21000 {
pinctrl-names = "default";
pinctrl-0 = <
&emmc_p1_pmx_cfg
&emmc_p2_pmx_cfg
&emmc_p3_pmx_cfg
****
>;
其中的属性值,在driver中会读取其中的内容吧!谢谢!