这周主要对pinctrl子系统进行分析,该分析的基本上已经分析完成,唯一没有细说的估计就是gpio与pinctrl之间的关联了。本章即是pinctrl子系统分析的最后一章,本章我们主要实现一个虚拟的pinctrl device驱动,以便我们能够使用pinctrl子系统提供的接口,实现pinctrl device的驱动开发(本章实现的驱动代码可以在ubuntu18.04系统上正常运行)。
本篇文章的目的如下:
- 实现一个虚拟的pinctrl dev驱动,掌握pinctrl dev的驱动开发;
- 不需要借助开发板,即可完成pinctrl dev驱动开发及验证工作(我们既然分析内核各驱动子系统模块,要学习的就是他们的系统设计方法、硬件抽象等工作。本篇文章保证在没有硬件开发板的情况下,也可以进行pinctrl 子系统的驱动开发,其实大多数驱动工程师可能都不一定有开发pinctrl 子系统驱动的场景,pinctrl device驱动开发基本上是soc厂家实现的)。
本篇文章涉及的知识点:
- 需要知道platform device、driver的知识;
- 需要对sysfs有个大概的理解,我们通过sysfs子系统的属性文件,查看pin mux配置是否生效;
- 需要使用一个虚拟的gpio控制器驱动(在之前gpio专栏中已经实现,此处增加对pinctrl的支持),验证gpio相关的引脚配置功能;
- 需要使用一个虚拟控制器驱动,验证device与pinctr的绑定功能(此处我们使用之前在spi专栏中实现的虚拟spi控制器驱动,该驱动在此处基本上无需修改)。
本章的主要章节如下:
一、 virt soc pin描述
二、 virt pinctrl dev驱动实现
三、virt board pin描述及pinctrl maps注册
四、device与pinctrl的绑定
五、gpio与pinctrl子系统相关知识点说明
六、功能验证
一、Virt soc pin描述
既然pinctrl device是对soc pin controller的驱动程序,因此我们需要定义下我们虚拟的soc引脚定义。
如下图所示,本virt soc 提供32个pin,每一个pin支持4个可选状态。提供2个32bit寄存器描述该soc引脚复用信息,因为每个pin支持4个可选状态,因此使用2bits描述该pin的状态。因为只是一个虚拟的soc pin描述,因此此处仅定义了32个pin信息。
两个寄存器分别定义pinmux_reg0、pinmux_reg1,其中pin0使用pinmux_reg0的bit0、bit1描述其状态:00b表示gpio0;01b iic0_sdat。Pin1使用pinmux_reg0的bit2、bit3描述其状态:00b表示gpio1;10表示uart0_tx;
该soc可支持32个gpio、3个iic、2个uart、1个spi、2个can、1个nandflash的功能复用,而这些功能中存在着引脚复用。
二、 virt pinctrl dev驱动实现
前面的文章中,已经说明了pinctrl dev的驱动开发流程,此处再次说明一下:
主要包含如下几个步骤:
- 为该soc pin controller 实现platform device driver驱动,然后在该驱动的probe接口中实现如下功能:
- 定义struct pinctrl_desc类型的变量,并实现相应的成员变量的配置,包含支持的引脚描述、支持的引脚复用接口的赋值、支持的引脚配置接口的赋值、支持的group操作接口以及dt2map接口的赋值等;
- 调用pinctrl_register/devm_pinctrl_register完成pinctrl device的注册
- 定义该soc pin controller的group相关变量的添加(若使用自行定义的结构存储就自行实现,也可调用pinctrl_generic_add_group接口实现);
- 定义该soc pin controller的function相关变量的添加(若使用自行定义的结构存储就自行实现,也可调用pinctrl_generic_add_function接口实现);
Virt pinctrl dev数据结构
我们定义了三个数据结构,分别为struct virt_function_desc、struct virt_group_desc、struct virt_pinctrl,其中struct virt_function_desc是对一个function的描述,struct virt_group_desc是对一个group的描述,而struct virt_pinctrl则描述一个soc pin controller。
struct virt_function_desc
该数据结构描述一个function,包含function名称、该function所包含的group名称数组、group的个数、引脚复用的配置参数、引脚复用配置参数的掩码(针对我们的soc,mask为0x03(占用2位),而mux_val即为引脚复用配置值,如针对iic function,则其mux_val为0x01)
struct virt_group_desc
该数据结构描述一个group,包含group名称,该group包含的引脚个数、引脚id数组。
struct virt_pinctrl
该数据结构描述一个soc pin controller,包含:
- Struct pinctrl_dev类型的指针变量;
- 引脚复用寄存器(此处定义为pin_mux_reg,在实际的应用中,应是寄存器基地址的map,即reg_base变量,此处用pin_mux_reg替代);
- 该soc pin controller所包含的group信息;
- 该soc pin controller所包含的function信息
struct pinctrl_desc类型变量定义
如下是该soc pin controller对应的struct pinctrl_desc类型变量的定义,包含描述该soc pin controller的引脚信息的变量(virt_pins)、引脚复用操作接口(virt_pinmux_ops)、group获取相关的操作接口(virt_pinctrl_ops),此处我们没有实现引脚配置的操作接口,感兴趣的童鞋可自行实现。
Pinctrl device的注册
调用pinctrl_register/devm_pinctrl_register接口即完成virt soc controller 驱动的注册。
如下即为该virt pinctrl dev驱动对应的platform driver probe函数的实现,相对来说比较简单
在上面我们为该platform device注册了属性参数,主要用于读取引脚复用配置寄存器virt_pinctrl_ptr->pin_mux_reg的信息,定义如下:
三、virt board pin描述及pinctrl maps注册
上面说明soc pin controller 驱动的实现,下面我们说明virt board pin 描述及pinctrl maps的注册。由于在ubunt1804上测试,其内核是没有支持设备树的,因此我们通过定义struct pinctrl_map数组,并调用pinctrl_register_mappings实现baord相关的pinctrl maps注册。
因为仅是测试验证,此处我们仅描述spi0的pinctrl_map(若是正常的驱动,则需要描述本board所需要配置的所有pinctrl_map信息),我们的pinctrl_map,其对应的spi设备名称为virt_spi.0(spi master设备所对应的platform device的名称,因为spi master并没有使用设备与驱动绑定操作,因此此处不能是spi master对应device的名称)、virt_pinctrl_dev是我们上面定义的virt pinctrl dev对应的struct device类型变量的名称、spi0_group表示我们选择的virt soc pin controller的组名称、spi0_func表示我们选择的virt soc pin controller的function名称(对应最上面的引脚状态定义表格的内容)。
调用pinctrl_regiser_mappings后,则将该pinctrl_map注册到pinctrl_maps链表上。
若内核支持设备树,则需要在各自外设的的节点中增加针对pinctrl function、pinctrl group的描述即可。如下图时zynq-zc702的i2c0控制器的节点描述,通过pinctrl-names(描述该function的状态,包含default、idle、sleep等,在之前的文章中已经说明,需要了解的可查看之前的文章)、pinctrl-0(对应的的function定义)即可描述
四、device与pinctrl的绑定
在上面我们定义了针对spi0的pinctrl map,那什么时候才会配置spi0的引脚复用呢?我们在前面的《Linux pinctrl子系统分析之六 设备与pinctrl子系统的bind》文章中已经说明,当spi0对应的platform device、platform driver 匹配成功后,probe时进行设备与pinctrl子系统的绑定,并完成引脚的参数配置、复用配置操作。而在此次测试中,我们使用之前在《spi分析专栏》中实现的虚拟spi控制器驱动,完成虚拟spi控制器对应的platform device、platform driver的注册及绑定,从而完成针对spi0引脚的复用配置操作(虚拟spi控制器驱动实现就不再此处细说了)。
五、gpio与pinctrl子系统相关知识点说明
针对gpio的使用,一般也是需要进行引脚复用配置,如我们在此处定义的引脚状态表中,这32个引脚既可以作为gpio引脚、也可以作为不同控制器的引脚。而针对gpio控制器而言,和普通的设备引脚复用又有所不同,针对普通的设备而言,若作为设备引脚使用,则这些引脚均被设备使用(如iic0 sda、iic0 scl)。但是针对gpio控制器而言,如我们实现虚拟gpio控制器,其包含32个gpio引脚,但是由于引脚复用的关系,该gpio控制器中可能只有部分引脚可以作为gpio,因此针对gpio的引脚复用配置,pinctrl与gpio子系统做了兼容设置。
在调用gpio_request时,则会调用pinctrl 子系统提供的pin_request操作,通过pin_request确定该引脚是否已被其他模块使用(gpiochip_generic_request接口或者pinctrl_request_gpio、pinctrl_gpio_requeset)。而针对gpio与pinctrl,存在gpio引脚index与pinctrl pin index的转换工作,因此定义数据结构描述gpio引脚与pinctrl 引脚的转换;主要数据结构为struct gpio_pin_range、pinctrl_gpio_range,主要也就是gpio控制器的gpio base、num_gpio、pinctrl pin引脚的base index等信息。只需要在gpio_chip注册时,将struct gpio_pin_range类型的变量,添加到struct pinctrl_dev的成员变量链表gpio_ranges上即可。
本篇文章我们的虚拟gpio控制器驱动(该驱动是在之前《gpio专栏》中实现的,此处不再细述),增加实现了该功能。主要是在虚拟gpio控制器驱动对应platform driver probe中增加针对gpio range的注册代码,实现如下:
六、功能验证
- 首先将pinctrl device驱动注册到系统中:
- insmod ./images/virt_pinctrl_dev.ko;
- insmod ./images/pinctrl-virt0612.ko
执行完成以上工作后,即完成soc pinctrl dev、pinctrl map的注册,而我们的pinctrl device对应的platform device路径为/sys/devices/platform/virt_pinctrl_dev,我们可以在该目录下查看引脚复用寄存器的设置值。如下:
- 将spi controller 注册到系统中
- insmod ./images/virtual_spi_controller.ko
执行完成insmod后,查看寄存器的值
已经完成引脚复用的配置。
- 将gpio controller驱动注册到系统中
- insmod ./images/virt_gpio.ko
- insmod ./images/virt_gpio_dev.ko
测试验证下:
我们注册的gpio的base index为256,我们会发现能够设置gpio0(即256),但是不能设置gpio6(262),那是引脚6我们已经用作spi0 clk了。下面我们注销spi 0 controller:
注销spi0后,就可以使用gpio6了,那是在spi controller注销时,会调用pin_free释放该引脚,因此就可以将pin6作为gpio使用了。
以上就是本章的主要内容,我们实现了一个虚拟的pinctrl device驱动,且借助虚拟的spi控制器驱动、虚拟的gpio控制器驱动、sysfs的属性文件,完成了完整的模拟工作。希望对学习pinctrl子系统的童鞋有所帮助。(本篇文章涉及的所有代码,会放到gitee上,稍后会把链接放出来)
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/10347.html