Linux 设备驱动程序开发:引脚控制子系统
编者注:嵌入式 Linux 内核已经在嵌入式系统中发挥着至关重要的作用,并且在满足物联网 (IoT) 的各种需求方面的重要性日益增加。反过来,设备驱动程序提供了应用程序和物联网设备本身之间的关键链接。在 Linux 设备驱动程序开发中,作者 John Madieu 全面介绍了这些驱动程序的开发,结合详细解释和大量代码示例。
这本书的第 14 章摘录重点介绍了引脚控制和 GPIO——对于希望与自定义硬件设备交互的嵌入式系统开发人员来说,这是一个特别重要的领域。本节选的第一部分介绍了引脚控制子系统。
改编自 Linux 设备驱动程序开发,作者:John Madieu。
第 14 章。引脚控制和 GPIO 子系统
作者:John Madieu
大多数嵌入式 Linux 驱动程序和内核工程师使用 GPIO 编写或使用引脚多路复用。通过引脚,我的意思是组件的输出线。 SoC确实是复用pins的,也就是说一个pin可能有几个功能,比如arch/arm/boot/dts/imx6dl-pinfunc.h中的MX6QDL_PAD_SD3_DAT1可以是SD3数据线1,UART1的cts/rts,Flexcan2的Rx,或者普通GPIO。
选择引脚工作模式的机制称为引脚复用。负责的系统称为引脚控制器。在本章的第二部分,我们将讨论通用输入输出 (GPIO ),这是一个引脚可以操作的特殊功能(模式)。
在本章中,我们将:
遍历引脚控制子系统,看看如何在 DT 中声明它们的节点
探索传统的基于整数的 GPIO 接口,以及新的基于描述符的接口 API
处理 GPIO 映射到 IRQ
处理专用于 GPIO 的 sysfs 接口
引脚控制子系统
引脚控制 (控制 ) 子系统允许管理引脚复用。在DT中,需要引脚以某种方式复用的设备必须声明它们需要的引脚控制配置。
pinctrl 子系统提供:
引脚复用,允许将同一引脚重用于不同目的,例如一个引脚作为 UART TX 引脚、GPIO 线或恒指数据线。多路复用会影响引脚组或单个引脚。
引脚配置,应用引脚的电子特性,例如上拉、下拉、驱动强度、去抖动周期等。
本书的目的仅限于使用引脚控制器驱动程序导出的函数,而不是如何编写引脚控制器驱动程序。
Pinctrl 和设备树
pinctrl 只不过是一种收集引脚(不仅仅是 GPIO)并将它们传递给驱动程序的方法。引脚控制器驱动程序负责解析 DT 中的引脚描述并将其配置应用到芯片中。驱动程序通常需要一组两个嵌套的节点来描述一组引脚配置。第一个节点描述组的功能(组将用于什么目的),第二个节点保存引脚配置。
如何在 DT 中分配引脚组在很大程度上取决于平台,因此也依赖于引脚控制器驱动程序。每个引脚控制状态都有一个整数 ID,从 0 开始并且是连续的。可以使用 name 属性,该属性将映射到 ID 之上,以便相同的名称始终指向相同的 ID。
每个客户端设备自己的绑定决定了必须在其 DT 节点中定义的状态集,以及是否定义必须提供的状态 ID 集,或者是否定义必须提供的状态名称集。在任何情况下,都可以通过两个属性将引脚配置节点分配给设备:
pinctrl-
:这允许提供设备特定状态所需的 pinctrl 配置列表。它是一个 phandles 列表,每个 phandles 都指向一个 pin 配置节点。这些引用的引脚配置节点必须是它们配置的引脚控制器的子节点。此列表中可能存在多个条目,以便可以配置多个引脚控制器,或者可以从单个引脚控制器的多个节点构建状态,每个节点都对整体配置做出贡献。 pinctrl-name :这允许为列表中的每个状态命名。列表条目 0 定义整数状态 ID 0 的名称,状态 ID 1 的列表条目 1,依此类推。状态 ID 0 通常被命名为 default .标准化状态列表可以在 include/linux/pinctrl/pinctrl-state.h 中找到。
以下是DT的摘录,显示了一些设备节点,以及它们的引脚控制节点:
在前面的示例中,引脚配置以
MX6QDL_PAD_DISP0_DAT15__GPIO5_IO09 0x80000000
MX6QDL_PAD_DISP0_DAT15__GPIO5_IO09代表引脚功能,本例为GPIO,0x80000000代表引脚设置。对于这一行,
MX6QDL_PAD_EIM_D25__UART3_RX_DATA 0x1b0b1
MX6QDL_PAD_EIM_D25__UART3_RX_DATA代表引脚功能,是UART3的RX线,0x1b0b1代表设置。
pin 函数是一个宏,其值仅对 pin 控制器驱动程序有意义。这些通常在位于 arch//boot/dts/ 的头文件中定义。例如,如果使用具有 i.MX6 四核 (ARM) 的 UDOO 四核,则引脚功能头文件将为 arch/arm/boot/dts/imx6q-pinfunc.h 。以下是GPIO5控制器第五行对应的宏:
#define MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x19c 0x4b0 0x000 0x5 0x0
这些前面的节点是从相应的驱动程序特定节点调用的。此外,这些引脚是在相应的驱动程序初始化期间配置的。在选择引脚组状态之前,必须首先使用 pinctrl_get() 函数获取引脚控制,调用 pinctrl_lookup_state() 以检查请求的状态是否存在,最后调用 pinctrl_select_state() 应用状态。>
以下示例展示了如何获取 pincontrol 并应用其默认配置:
通常在驱动程序初始化期间执行这些步骤。这段代码的合适位置可以在probe()函数内。
pinctrl_select_state() 内部调用 pinmux_enable_setting() ,它依次调用 pin 控制节点中每个 pin 上的 pin_request()。
可以使用 pinctrl_put() 函数释放引脚控制。可以使用 API 的资源管理版本。也就是说,可以使用 pinctrl_get_select(),给定要选择的状态的名称,以配置 pinmux。该函数在include/linux/pinctrl/consumer.h中定义如下:
静态结构 pinctrl *pinctrl_get_select(struct device *dev,
const char *name)
其中 *name 是 pinctrl-name 属性中写入的状态名称。如果状态的名称是 default ,则可以调用 pinctr_get_select_default() 函数,该函数是 pinctl_get_select() 的包装器:
静态结构 pinctrl * pinctrl_get_select_default(
struct device *dev)
{
return pinctrl_get_select(dev, DEFAFA_CTRL_STATE);
}
让我们在特定于板的 dts 文件 (am335x-evm.dts) 中看到一个真实示例:
dcan1:d_can@481d0000 {
status =“好的”;
pinctrl-names =“默认”;
pinctrl-0 =<&d_can1_pins>;
};
并在相应的驱动程序中:
pinctrl =devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl))
dev_warn(&pdev->dev,“引脚未从驱动程序配置”);
当设备被探测到时,引脚控制核心会自动为我们声明默认的 pinctrl 状态。如果定义了一个init状态,pinctrl核心会在probe()函数之前自动将pinctrl设置为这个状态,然后在probe()之后切换到默认状态(除非驱动程序已经明确改变了状态)。
下一部分将讨论 GPIO 子系统。
经 Packt Publishing 许可转载。版权所有 © 2017 Packt Publishing
John Madieu 是一位住在法国巴黎的嵌入式 Linux 和内核工程师。他的主要活动包括为自动化、运输、医疗保健、能源和军事等领域的公司开发驱动程序和董事会支持包 (BSP)。 John 在 EXPEMB 工作,这是一家法国公司,该公司是基于计算机模块的电子板设计和嵌入式 Linux 解决方案的先驱。他是一位开源和嵌入式系统爱好者,坚信只有分享知识,才能学到更多。
物联网技术