# virt_pinctrl_dev **Repository Path**: jerry_chg/virt_pinctrl_dev ## Basic Information - **Project Name**: virt_pinctrl_dev - **Description**: 实现一个虚拟的pinctrl device驱动 - **Primary Language**: C - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 7 - **Created**: 2020-06-13 - **Last Updated**: 2025-07-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # virt_pinctrl_dev #### 介绍 这周主要对pinctrl子系统进行分析,该分析的基本上已经分析完成,唯一没有细说的估计就是gpio与pinctrl之间的关联了。本章即是pinctrl子系统分析的最后一章,本章我们主要实现一个虚拟的pinctrl device驱动,以便我们能够使用pinctrl子系统提供的接口,实现pinctrl device的驱动开发(本章实现的驱动代码可以在ubuntu18.04系统上正常运行)。 本篇文章的目的如下: 1. 实现一个虚拟的pinctrl dev驱动,掌握pinctrl dev的驱动开发; 2. 不需要借助开发板,即可完成pinctrl dev驱动开发及验证工作(我们既然分析内核各驱动子系统模块,要学习的就是他们的系统设计方法、硬件抽象等工作。本篇文章保证在没有硬件开发板的情况下,也可以进行pinctrl 子系统的驱动开发,其实大多数驱动工程师可能都不一定有开发pinctrl 子系统驱动的场景,pinctrl device驱动开发基本上是soc厂家实现的)。 本篇文章涉及的知识点: 1. 需要知道platform device、driver的知识; 2. 需要对sysfs有个大概的理解,我们通过sysfs子系统的属性文件,查看pin mux配置是否生效; 3. 需要使用一个虚拟的gpio控制器驱动(在之前gpio专栏中已经实现,此处增加对pinctrl的支持),验证gpio相关的引脚配置功能; 4. 需要使用一个虚拟控制器驱动,验证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的驱动开发流程,此处再次说明一下: 主要包含如下几个步骤: 1. 为该soc pin controller 实现platform device driver驱动,然后在该驱动的probe接口中实现如下功能: a. 定义struct pinctrl_desc类型的变量,并实现相应的成员变量的配置,包含支持的引脚描述、支持的引脚复用接口的赋值、支持的引脚配置接口的赋值、支持的group操作接口以及dt2map接口的赋值等; b. 调用pinctrl_register/devm_pinctrl_register完成pinctrl device的注册 c. 定义该soc pin controller的group相关变量的添加(若使用自行定义的结构存储就自行实现,也可调用pinctrl_generic_add_group接口实现); d. 定义该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_function_desc { const char *name; const char * const *group_names; int num_group_names; u32 mux_val; u32 mux_mask; }; struct virt_group_desc 该数据结构描述一个group,包含group名称,该group包含的引脚个数、引脚id数组。 struct virt_group_desc{ const char *name; const unsigned int *pins; const unsigned int npins; }; struct virt_pinctrl 该数据结构描述一个soc pin controller,包含: 1. Struct pinctrl_dev类型的指针变量; 2. 引脚复用寄存器(此处定义为pin_mux_reg,在实际的应用中,应是寄存器基地址的map,即reg_base变量,此处用pin_mux_reg替代); 3. 该soc pin controller所包含的group信息; 4. 该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),此处我们没有实现引脚配置的操作接口,感兴趣的童鞋可自行实现。 struct virt_pinctrl { struct pinctrl_dev *pctrl; u32 pin_mux_reg[2]; const struct virt_group_desc *groups; unsigned int ngroups; const struct virt_function_desc *funcs; unsigned int nfuncs; spinlock_t lock; /*当是具体的pinctrl device driver,需要获取寄存器地址,以便对寄存器地址进行配置*/ void __iomem *reg_base; }; Pinctrl device的注册 调用pinctrl_register/devm_pinctrl_register接口即完成virt soc controller 驱动的注册。 如下即为该virt pinctrl dev驱动对应的platform driver probe函数的实现,相对来说比较简单 static int virt_pinctrl_platform_probe(struct platform_device *platform_dev) { int ret = 0; struct virt_pinctrl *virt_pinctrl_ptr; #if 0 struct resource *res; #endif virt_pinctrl_ptr = devm_kzalloc(&platform_dev->dev, sizeof(*virt_pinctrl_ptr), GFP_KERNEL); if (!virt_pinctrl_ptr) return -ENOMEM; #if 0 res = platform_get_resource(platform_dev, IORESOURCE_MEM, 0); virt_pinctrl_ptr->reg_base = devm_ioremap_resource(&platform_dev->dev, res); if (IS_ERR(pctrl->base)) return PTR_ERR(pctrl->base); #endif virt_pinctrl_ptr->groups = virt0612_pinctrl_groups; virt_pinctrl_ptr->ngroups = ARRAY_SIZE(virt0612_pinctrl_groups); virt_pinctrl_ptr->funcs = virt0612_pinmux_functions; virt_pinctrl_ptr->nfuncs = ARRAY_SIZE(virt0612_pinmux_functions); spin_lock_init(&virt_pinctrl_ptr->lock); printk("%s:%d\n", __FUNCTION__, __LINE__); virt_pinctrl_ptr->pctrl = pinctrl_register(&virt0612_desc, &platform_dev->dev, virt_pinctrl_ptr); if (IS_ERR(virt_pinctrl_ptr->pctrl)) { pr_err("could not register virt0612 pin driver\n"); return ret; } sysfs_create_group(&platform_dev->dev.kobj, &virt_pinctrl_attr_group); platform_set_drvdata(platform_dev, virt_pinctrl_ptr); return 0; } 在上面我们为该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的注册代码,实现如下: if (pdata->pinctrl_dev_name) { ret = gpiochip_add_pin_range(&virt_chip_ptr->chip, pdata->pinctrl_dev_name, 0, pdata->pin_base, virt_chip_ptr->chip.ngpio); if (ret < 0) dev_warn(dev, "failed to add pin range\n"); } 六、功能验证 1. 首先将pinctrl device驱动注册到系统中: a. insmod ./images/virt_pinctrl_dev.ko; b. insmod ./images/pinctrl-virt0612.ko 执行完成以上工作后,即完成soc pinctrl dev、pinctrl map的注册,而我们的pinctrl device对应的platform device路径为/sys/devices/platform/virt_pinctrl_dev,我们可以在该目录下查看引脚复用寄存器的设置值。如下: 2. 将spi controller 注册到系统中 a. insmod ./images/virtual_spi_controller.ko 执行完成insmod后,查看寄存器的值 已经完成引脚复用的配置。 3. 将gpio controller驱动注册到系统中 a. insmod ./images/virt_gpio.ko b. 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子系统的童鞋有所帮助。 实现一个虚拟的pinctrl device驱动 #### 安装教程 1. make 2. make install 执行以上命令后,则在images目录下存在生成的ko文件 #### 使用说明 1. 指向source ./insmod.sh脚本,即完成测试驱动注册