最全Linux驱动开发八股文(七)
你好,我是拉依达。
这是我的Linux驱动开发八股文详细解析系列。
本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。
全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。
现重新对内容进行整理,希望可以帮助到更多学习嵌入式的同学。
【下面是拉依达推荐学习相关专栏:】
一、Linux驱动学习专栏:拉依达的Linux驱动八股文 - 牛客网
二、Linux应用学习专栏:拉依达的Linux应用八股文 - 牛客网
【我的嵌入式学习和校招经验】 拉依达的嵌入式学习和秋招经验-CSDN博客
嵌入式学习规划/就业经验指导,可私信咨询
———————————————————————————————————————————————————
七、pinctrl子系统
Linux 内核提供了 pinctrl 子系统和 gpio 子系统用于 GPIO 驱动。
7.1 pinctrl 子系统主要工作内容:
- 获取设备树中 pin 信息,管理系统中所有的可以控制的 pin, 在系统初始化的时候, 枚举所有可以控制的 pin, 并标识这些 pin
- 根据获取到的 pin 信息来设置 pin 的复用功能,对于 SOC 而言, 其引脚除了配置成普通的 GPIO 之外,若干个引脚还可以组成一个 pin group, 形成特定的功能
- 根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
开发时只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成。
7.2 pinctrl的设备树设置
在设备树里面创建一个节点来描述 PIN 的配置信息。pinctrl 子系统一般在iomuxc子节点下,所有需要配置用户自己的pinctrl需要在该节点下添加。 例
iomuxc: iomuxc@020e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
.......
>;
};
......
}
- compatible 属性值为“fsl,imx6ul-iomuxc” ,
- pinctrl_hog_1 子节点所使用的 PIN 配置信息,如UART1_RTS_B 的配置信息
- UART1_RTS_B 这个 PIN 是作为 SD 卡的检测引脚
- MX6UL_PAD_UART1_RTS_B__GPIO1_IO19,这是一个宏定义,表 示 将 UART1_RTS_B 这个 IO 复用为 GPIO1_IO19(复用属性)
- 此宏定义后面跟着 5 个数字,0x0090 0x031C 0x0000 0x5 0x0,含义是<mux_reg conf_reg input_reg mux_mode input_val>
- 0x17059 就是 conf_reg 寄存器值 , 设置一个 IO 的上/下拉、驱动能力和速度(电气属性)
7.3 设备树中添加pinctrl模板
- 添加pinctrl设备结点 同一个外设的 PIN 都放到一个节点里面,在 iomuxc 节点中下添加“pinctrl_test”节点。节点前缀一定要为“pinctrl_”。
设备树是通过属性来保存信息的,因此需要添加一个属性,属性名字一定要为** fsl,pins **
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
......
pinctrl_led: ledgrp{
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0
>;
};
......
};
- 添加具体设备节点,调用pinctrl信息
在根节点“/”下创建 LED 灯节点,节点名为“gpioled” 只需要关注gpioled设备节点下的pinctrl-names 和 pinctrl-0 两条语句, 这两句就是引用在 iomuxc 中配置的 pinctrl 节点
test {
pinctrl-names = "default", "wake up";
pinctrl-0 = <&pinctrl_test>;
pinctrl-1 = <&pinctrl_test_2>;
/* 其他节点内容 */
};
-
pinctrl-names = "default", "wake up"; 设备的状态, 可以有多个状态, default 为状态 0, wake up 为状态 1。
-
pinctrl-0 = <&pinctrl_test>;第 0 个状态所对应的引脚配置, 也就是 default 状态对应的引脚在 pin controller 里面定义好的节点 pinctrl_test里面的管脚配置。
-
pinctrl-1 = <&pinctrl_test_2>;第 1 个状态所对应的引脚配置, 也就是 wake up 状态对应的引脚在 pin controller 里面定义好的节点 pinctrl_test_2里面的管脚配置。
例
gpioled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "songwei-gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
- pinctrl-names = "default"; 设备的状态, 可以有多个状态, default 为状态 0。
- pinctrl-0 属性设置 LED 灯所使用的 PIN 对应的 pinctrl 节点
八、GPIO子系统
当使用 pinctrl 子系统将引脚的复用设置为 GPIO,可以使用 GPIO 子系统来操作GPIO
8.1 GPIO子系统工作内容
通过 GPIO 子系统功能要实现:
- 引脚功能的配置(设置为 GPIO,GPIO 的方向, 输入输出模式,读取/设置 GPIO 的值)
- 实现软硬件的分离(分离出硬件差异, 有厂商提供的底层支持; 软件分层。 驱动只需要调用接口 API 即可操作 GPIO)
- iommu 内存管理(直接调用宏即可操作 GPIO)
8.2 GPIO子系统设备树设置
在具体设备节点中添加GPIO信息
gpioled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "songwei-gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
//gpio信息
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
- led-gpio 属性指定了 LED 灯所使用的 GPIO,在这里就是 GPIO1 的 IO03,低电平有效。
- 稍后编写驱动程序的时候会获取 led-gpio 属性的内容来得到 GPIO 编号,因为 gpio 子系统的 API 操作函数需要 GPIO 编号
8.3 API函数
-
gpio_request
gpio_request 函数用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request进行申请。
int gpio_request(unsigned gpio, const char *label)
- gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信 息,此函数会返回这个 GPIO 的标号
- label:给 gpio 设置个名字。
- 返回值: 0,申请成功;其他值,申请失败。
-
gpio_free 如果不使用某个 GPIO ,需要调用 gpio_free 函数进行释放。
void gpio_free(unsigned gpio); // gpio:要释放的 gpio 标号。
-
gpio_direction_input 设置某个 GPIO 为输入
int gpio_direction_input(unsigned gpio) //gpio:要设置为输入的 GPIO 标号。
-
gpio_direction_output 设置某个 GPIO 为输出,并且设置默认输出值。
int gpio_direction_output(unsigned gpio, int value)
- gpio:要设置为输出的 GPIO 标号。
- value: GPIO 默认输出值。
- 返回值: 0,设置成功;负值,设置失败
-
gpio_get_value 获取某个 GPIO 的值(0 或 1)
#define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio)
- gpio:要获取的 GPIO 标号。
- 返回值: 非负值,得到的 GPIO 值;负值,获取失败
-
gpio_set_value 设置某个 GPIO 的值
#define gpio_set_value __gpio_set_value void __gpio_set_value(unsigned gpio, int value)
- gpio:要设置的 GPIO 标号。
- value: 要设置的值
8.4 GPIO相关OF函数
-
of_gpio_named_count 获取设备树某个属性里面定义了几个GPIO 信息。
int of_gpio_named_count(struct device_node *np, const char *propname)
- np:设备节点。
- propname:要统计的 GPIO 属性。
- 返回值: 正值,统计到的 GPIO 数量;负值,失败
-
of_gpio_count 此函数统计的是gpios属性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的GPIO 信息
int of_gpio_count(struct device_node *np)
- np:设备节点。
- 返回值: 正值,统计到的 GPIO 数量;负值,失败
-
of_get_named_gpio 获取 GPIO 编号,在Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号。
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
- np:设备节点。
- propname:包含要获取 GPIO 信息的属性名。
- index: GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。
- 返回值: 正值,获取到的 GPIO 编号;负值,失败。
8.5 pinctrl和gpio子系统使用程序框架
int ret = 0;
/* 1 、获取设备节点:alphaled */
gpio_led.nd = of_find_node_by_path("/gpioled");
if(gpio_led.nd == NULL)
{
printk("songwei_led node can not found!\r\n");
return -EINVAL;
}else
{
printk("songwei_led node has been found!\r\n");
}
/* 2、 获取设备树中的 gpio 属性,得到 LED 所使用的 LED 编号 */
gpio_led.led_gpio = of_get_named_gpio(gpio_led.nd,"led-gpio",0);
if(gpio_led.led_gpio < 0)
{
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpio_led.led_gpio);
/* 3、设置 GPIO1_IO03 为输出,并且输出高电平,默认关闭 LED 灯 */
ret = gpio_direction_output(gpio_led.led_gpio, 1);
if(ret < 0)
{
printk("can't set gpio!\r\n");
}
#嵌入式##校招##八股文##Linux##linux驱动#你好,我是拉依达。 这是我的Linux驱动开发八股文详细解析系列。 本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。 全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。 现在我重新对内容进行整理,已专栏的形式发布在牛客上,希望可以帮助到更多学习嵌入式的同学。