Linux 驱动开发基础(3):pinctrl 子系统
Linux 驱动开发基础详解3pinctrl 子系统前情回顾在上一节的学习中我们编写了基于设备树的 LED 驱动。但大家可能会发现我们依然像裸机开发那样通过直接获取并操作 GPIO 相关的底层寄存器来实现驱动。然而Linux 是一个庞大而高度抽象的系统这种“原始”的寄存器硬编码方式不仅繁琐而且违背了 Linux 驱动“分离与分层”的设计思想。为此Linux 内核引入了pinctrl和gpio子系统。本篇将聚焦于pinctrl子系统。01 学习重点在掌握了设备树基本语法后本节我们需要搞清以下两个核心问题为什么需要 pinctrl 子系统它到底负责干什么如何在设备树Device Tree中正确使用和配置 pinctrl02 告别裸机引入 pinctrl传统的“裸机式”配置方式回顾上一节 LED-GPIO 的初始化过程我们通常需要做三件事查地址在设备树节点中设置reg属性填入相关的寄存器物理地址。配引脚PIN初始化复用MUX寄存器和电气属性PAD寄存器设置引脚的复用功能如设为 GPIO、上下拉、速度等。配 GPIO初始化 GPIO 控制寄存器设置输入/输出方向及电高等。对于绝大多数 32 位 SOC 而言引脚支持多路复用Multiplexing。如果每个驱动都自己去查手册、算地址、配寄存器代码将极度冗余且难以维护。pinctrl 子系统的使命为了将开发者从繁琐的寄存器配置中解放出来Linux 推出了pinctrl子系统。它的核心工作内容只有三个获取 PIN 信息从设备树中读取引脚配置。设置复用功能MUX根据信息将引脚配置为特定的功能如 I2C、SPI、GPIO。设置电气特性PAD配置引脚的上/下拉、驱动能力、速度等。优势引入pinctrl后驱动开发者只需要在设备树中按规则写好 PIN 的属性底层的寄存器读写全由内核pinctrl驱动自动配置03 实战在设备树中配置 pinctrl我们以 NXP 的I.MX6ULL芯片为例实现在设备树.dtsi文件中配置 pinctrl。1. 找到 IOMUXC 节点在 I.MX 系列芯片中引脚由IOMUXC外设管理。在设备树中我们需要找到对应的iomuxc节点iomuxc: iomuxc020e0000 { /* 内核会根据 compatible 匹配对应的 pinctrl 驱动文件 */ compatible fsl,imx6ul-iomuxc; /* IOMUXC 外设的寄存器基地址为 0x020e0000长度为 0x4000 */ reg 0x020e0000 0x4000; };2. 追加 PIN 配置信息我们需要配置UART1_RTS_B这个引脚将其复用为GPIO1_IO19。我们需要在iomuxc节点下添加特定的子节点iomuxc { pinctrl-names default; pinctrl-0 pinctrl_hog_1; imx6ul-evk { /* 自定义的节点名称常以驱动名或功能命名 */ pinctrl_hog_1: hoggrp-1 { /* fsl,pins 是核心属性里面存放具体的引脚配置 */ fsl,pins /* 格式宏定义复用配置 自定义数值电气特性配置 */ MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058 ; }; }; };⚠️注意解析在fsl,pins属性中每一行代表一个引脚的配置由两部分组成前半部分宏MX6UL_PAD_UART1_RTS_B__GPIO1_IO19决定引脚的复用功能。后半部分数值0x17059决定引脚的电气特性由用户查手册自行计算填写用于配置上/下拉、驱动能力等。04 深度解析宏定义像MX6UL_PAD_UART1_RTS_B__GPIO1_IO19这么长的宏到底代表了什么呢设备树是支持 C 语言#include语法的这个宏定义在内核的.h头文件中如imx6ul-pinfunc.h。宏定义源码在头文件关于UART1_RTS_B的复用宏多达 8 个代表它可以复用为 8 种不同的功能/* 格式: mux_reg conf_reg input_reg mux_mode input_val */#defineMX6UL_PAD_UART1_RTS_B__UART1_DCE_RTS0x00900x031C0x06200x00x3#defineMX6UL_PAD_UART1_RTS_B__UART1_DTE_CTS0x00900x031C0x00000x00x0#defineMX6UL_PAD_UART1_RTS_B__ENET1_TX_ER0x00900x031C0x00000x10x0#defineMX6UL_PAD_UART1_RTS_B__USDHC1_CD_B0x00900x031C0x06680x20x1#defineMX6UL_PAD_UART1_RTS_B__CSI_DATA050x00900x031C0x04CC0x30x1#defineMX6UL_PAD_UART1_RTS_B__ENET2_1588_EVENT1_OUT0x00900x031C0x00000x40x0/* ⚠️这是我们用到的宏将其复用为 GPIO1_IO19 ⚠️ */#defineMX6UL_PAD_UART1_RTS_B__GPIO1_IO190x00900x031C0x00000x50x0#defineMX6UL_PAD_UART1_RTS_B__USDHC2_CD_B0x00900x031C0x06740x80x25个十六进制数宏定义后面跟着的0x0090 0x031C 0x0000 0x5 0x0这 5 个数字正是内核pinctrl驱动用来操作底层寄存器的方式。它们对应mux_reg conf_reg input_reg mux_mode input_val具体含义如下表参数本例数值含义说明实际物理地址计算 (基地址0x020e0000)mux_reg0x0090复用寄存器偏移地址0x020e0000 0x0090 0x020e0090(指向 MUX_CTL 寄存器)conf_reg0x031C电气特性寄存器偏移地址0x020e0000 0x031C 0x020e031C(指向 PAD_CTL 寄存器)input_reg0x0000输入寄存器偏移地址有些外设没有此寄存器如做 GPIO 时故填 0 无效。mux_mode0x5复用寄存器mux_reg要写入的值将底层寄存器0x020e0090的值配置为0x5即可复用为 GPIO。input_val0x0输入寄存器要写入的值在本例中无效。配置完成现在结合我们在设备树中写的那行代码MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059相当于我们向pinctrl子系统下达了如下指令“把偏移地址为0x0090的寄存器值写为0x5搞定复用然后把偏移地址为0x031C的寄存器值写为0x17059搞定电气特性。”底层的fsl,imx6ul-pinctrl驱动拿到这些参数后会自动完成所有寄存器的映射和写入完全不需要我们在驱动 C 代码中写一行ioremap映射05 小结通过本节学习我们掌握了 pinctrl 子系统的工作原理。后续驱动开发中配置引脚的标准方式步骤操作内容方式1打开内核提供的引脚复用头文件.h找到目标引脚的复用宏定义。查阅源码2查阅 SOC 参考手册计算出需要的电气特性值如0x17059。查阅手册3在设备树.dts的iomuxc节点下新建子节点在fsl,pins中填入宏定义 电气特性值。设备树修改4驱动程序加载时内核 pinctrl 自动接管并完成寄存器初始化。内核自动处理搞定了引脚的复用和上下拉pinctrl这只是第一步。下一节我们将学习如何使用GPIO 子系统来真正控制这个被配置好的引脚输出高低电平或读取输入状态。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2443146.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!