最全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 子系统来完成。

alt

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模板

  1. 添加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
						>;
				};
		......
	};
  1. 添加具体设备节点,调用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函数

  1. 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,申请成功;其他值,申请失败。
  2. gpio_free 如果不使用某个 GPIO ,需要调用 gpio_free 函数进行释放。

    void gpio_free(unsigned gpio);  // gpio:要释放的 gpio 标号。
    
  3. gpio_direction_input 设置某个 GPIO 为输入

    int gpio_direction_input(unsigned gpio) //gpio:要设置为输入的 GPIO 标号。
    
  4. gpio_direction_output 设置某个 GPIO 为输出,并且设置默认输出值。

    int gpio_direction_output(unsigned gpio, int value)
    
    • gpio:要设置为输出的 GPIO 标号。
    • value: GPIO 默认输出值。
    • 返回值: 0,设置成功;负值,设置失败
  5. gpio_get_value 获取某个 GPIO 的值(0 或 1)

    #define gpio_get_value __gpio_get_value
    int __gpio_get_value(unsigned gpio)
    
    • gpio:要获取的 GPIO 标号。
    • 返回值: 非负值,得到的 GPIO 值;负值,获取失败
  6. 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函数

  1. of_gpio_named_count 获取设备树某个属性里面定义了几个GPIO 信息。

    int of_gpio_named_count(struct device_node *np, const char *propname)
    
    • np:设备节点。
    • propname:要统计的 GPIO 属性。
    • 返回值: 正值,统计到的 GPIO 数量;负值,失败
  2. of_gpio_count 此函数统计的是gpios属性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的GPIO 信息

    int of_gpio_count(struct device_node *np)
    
    • np:设备节点。
    • 返回值: 正值,统计到的 GPIO 数量;负值,失败
  3. 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驱动八股文 文章被收录于专栏

你好,我是拉依达。 这是我的Linux驱动开发八股文详细解析系列。 本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。 全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。 现在我重新对内容进行整理,已专栏的形式发布在牛客上,希望可以帮助到更多学习嵌入式的同学。

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务