Linux驱动-GPIO-动态切换引脚复用

Linux驱动-GPIO-动态切换引脚复用


前言

动态切换 引脚复用

一、思考

pinctrl-names = “default”; 默认default 值:默认复用

Linux驱动-GPIO基本函数api 中,了解了GPIO的基本 函数.这里配置的设备树,如下;

  my_gpio:gpio1_a0{
        compatible =  "mygpio";
        my-gpios =  <&gpio1  RK_PA0  GPIO_ACTIVE_HIGH>;
	    pinctrl-names  =  "default";
	    pinctrl-0  =  <&my_gpio_ctrl>;
	  };
	  
	  
		mygpio{
		my_gpio_ctrl:my-gpio-ctrl {
			rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};  

核心点: pinctrl-names 值为default , 那么系统解析设备树,自动复用pinctrl-0 下面的 my_gpio_ctrl 标签配置,配置的是gpio 功能。 也就是说 default 情况下默认复用对应的功能。

GPIO Pin-Control 动态切换

pinctrl-names = "myled1", "sleep";
Linux驱动-GPIO子系统与pinctrl子系统相结合 中,我们理解的知识是:
pinctrl-names 配置了具体的值的时候,需要驱动动态配置复用功能。 通过pinctrl_get 方法拿到了pinctrl 实例,通过 pinctrl_lookup_state 方法 查找状态,通过 pinctrl_select_state 方法,
配置状态到硬件,也就是pin脚复用。 这里讲的其实属于GPIO Pin-Control动态切换
对应的设备树,举例人如下;

/ {
	model = "Rockchip RK3568 EVB1 DDR4 V10 Board";
	compatible = "rockchip,rk3568-evb1-ddr4-v10", "rockchip,rk3568";

....................................
my_gpio:gpio1_a0 {
    compatible = "mygpio";
	my-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_HIGH>;
	pinctrl-names = "myled1";
	pinctrl-0 = <&my_gpio_ctrl>;		
   };
};

&pinctrl {
 .......................
  	mygpio{
		my_gpio_ctrl:my-gpio-ctrl {
			rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
..................................
};

pinctrl-names = “urt”, “i2c”; GPIO 动态引脚复用

上面已经讲到了GPIO Pin-Control 动态切换,现在让pin脚复用不同的功能,比如 GPIO、I2C-SDA、UART-TX 复用功能 ,那么可以在驱动里面根据传参,动态复用到sleep 功能。 或者说 默认是gpio 功能,如何复用到I2C 功能,其实道理是一模一样的。 让驱动程序接收参数,然后动态切换复用功能、同步到硬件。

举例,设备树如下:

 my_gpio:gpio1_a0 {
      compatible = "mygpio";
      my-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "mygpio_func1", "mygpio_func2";
        pinctrl-0 = <&mygpio_ctrl>;
        pinctrl-1 = <&i2c3_sda>;
    };
	
	
	
    mygpio_func1 {
        mygpio_ctrl: my-gpio-ctrl {
            rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
        };
    };  
 
    mygpio_func2 {
        i2c3_sda: i2c3_sda {
            rockchip,pins = <1 RK_PA0 1 &pcfg_pull_none>;
        };
    };     	

二、参考资料

Linux驱动-GPIO基本函数api
Linux驱动-GPIO子系统与pinctrl子系统相结合
实现动态切换引脚复用功能

关于系统属性创建参考:
驱动-设备模型kobject实现属性文件读写终篇 关注:sysfs_create_file、sysfs_create_group 方法
驱动-注册自己的总线并创建属性文件 关注方法 bus_create_file
创建sysfs节点之device_create_file、sysfs_create_group
设备注册流程分析实验

这里核心目的是为了把之前kobject 模型知识点回顾一下,如何创建属性文件:device_create_file

三、核心概念理解

首先,我们简单区分一下这两个紧密相关的概念:

GPIO 动态引脚复用

  • 是什么:一个物理引脚在系统运行过程中,可以根据软件配置,在不同的时间扮演不同的角色(如 GPIO、I2C-SDA、UART-TX 等)。

  • 底层机制:通常由芯片内部的 引脚控制器 管理,通过配置多路复用器来选择引脚的功能。

GPIO Pin-Control 动态切换

  • 是什么:在 Linux 内核中,pinctrl 子系统负责管理引脚的复用和电气属性(如上拉/下拉、驱动强度等)。动态切换 就是指在驱动运行时,通过 pinctrl 子系统来改变这些设置。

  • 实现方式:在设备树中为同一个设备定义多个 pinctrl 状态(如 default, sleep, idle),然后在驱动代码中根据需要切换状态。

简单来说,动态引脚复用是目的,而 pinctrl 动态切换是在 Linux 系统中实现这个目的的主要手段。

四、技术实现简介(以 Linux 为例)

在 Linux 设备树中,你会看到这样的定义:

设备树配置

// 在设备节点外定义引脚状态

&iomuxc {
    pinctrl_uart2_default: uart2grp_default {
        fsl,pins = <
            MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x1b0b1
            MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x1b0b1
        >;
    };

    pinctrl_uart2_sleep: uart2grp_sleep {
        fsl,pins = <
            // 将TX/RX设置为GPIO输入,并使能内部上拉,以防悬空
            MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x4001b8b1
            MX6UL_PAD_UART2_RX_DATA__GPIO1_IO21 0x4001b8b1
        >;
    };
};

// 在设备节点中引用这些状态
&uart2 {
    pinctrl-names = "default", "sleep"; // 定义状态名称
    pinctrl-0 = <&pinctrl_uart2_default>;
    pinctrl-1 = <&pinctrl_uart2_sleep>;
    status = "okay";
};

在驱动代码中,切换状态

// 获取pinctrl状态
struct pinctrl *pinctrl = devm_pinctrl_get(dev);
struct pinctrl_state *default_state = pinctrl_lookup_state(pinctrl, "default");
struct pinctrl_state *sleep_state = pinctrl_lookup_state(pinctrl, "sleep");

// 切换到睡眠状态
pinctrl_select_state(pinctrl, sleep_state);

// ... 系统睡眠 ...

// 切换回默认状态
pinctrl_select_state(pinctrl, default_state);

五、具体实验

实验参考:用 将原理图中的一个引脚复用为gpio功能 中的 GPIO1 A0 来实验:原理图、版框图、复用关系参考 之前的文章来并实验。 配置设备树,去掉默认的引用,不要和自定义的设备树节点引用冲突。
在这里插入图片描述

设备树修改

 // pin脚配置
      mygpio_func1 {
        mygpio_ctrl: my-gpio-ctrl {
            rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
        };
    };  
 
    mygpio_func2 {
        i2c3_sda: i2c3_sda {
            rockchip,pins = <1 RK_PA0 1 &pcfg_pull_none>;
        };
    };     

根节点设备数配置


  my_gpio:gpio1_a0 {
      compatible = "mygpio";
      my-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "mygpio_func1", "mygpio_func2";
        pinctrl-0 = <&mygpio_ctrl>;
        pinctrl-1 = <&i2c3_sda>;
    };


kernel/arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi 看到了GPIO pin脚,如下:

<1 RK_PA1 1 &pcfg_pull_none_smt>,

在这里插入图片描述
那么就把i2c3m0_xfer 的引用去掉
就继续检索 i2c3m0_xfer 这个值,发现在rk3568.dtsi文件中,去掉对应的引用,如下:
在这里插入图片描述

驱动实验源码

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio.h>
#include <linux/device.h>
struct pinctrl *gpio_pinctrl;          // GPIO pinctrl 实例指针
struct pinctrl_state *func1_state;     // 功能1状态
struct pinctrl_state *func2_state;     // 功能2状态
int ret;

ssize_t selectmux_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    unsigned long select;
    select = simple_strtoul(buf, NULL, 10);
    if (select == 1) {
        pinctrl_select_state(gpio_pinctrl, func1_state);     // 选择功能1状态
    } else if (select == 0) {
        pinctrl_select_state(gpio_pinctrl, func2_state);     // 选择功能2状态
    }
    return count;
}
DEVICE_ATTR_WO(selectmux);       // 定义可写的设备属性 selectmux

int pinctrl_get_and_lookstate(struct device *dev)
{
    gpio_pinctrl = pinctrl_get(dev);    // 获取GPIO pinctrl实例
    if (IS_ERR(gpio_pinctrl)) {
        printk("pinctrl_get is error\n");
        return -1;
    }

    func1_state = pinctrl_lookup_state(gpio_pinctrl, "mygpio_func1");    // 查找功能1状态
    if (IS_ERR(func1_state)) {
        printk("pinctrl_lookup_state is error\n");
        return -2;
    }

    func2_state = pinctrl_lookup_state(gpio_pinctrl, "mygpio_func2");    // 查找功能2状态
    if (IS_ERR(func2_state)) {
        printk("pinctrl_lookup_state is error\n");
        return -2;
    }

    return 0;
}

// 平台设备初始化函数
static int my_platform_probe(struct platform_device *dev)
{
    printk("This is mydriver_probe\n");
    pinctrl_get_and_lookstate(&dev->dev);     // 获取并查找GPIO pinctrl实例和状态
    device_create_file(&dev->dev, &dev_attr_selectmux);    // 在设备上创建属性文件
    return 0;
}

// 平台设备的移除函数
static int my_platform_remove(struct platform_device *pdev)
{
    printk(KERN_INFO "my_platform_remove: Removing platform device\n");

    // 清理设备特定的操作
    // ...

    return 0;
}


const struct of_device_id of_match_table_id[]  = {
	{.compatible="mygpio"},
};

// 定义平台驱动结构体
static struct platform_driver my_platform_driver = {
    .probe = my_platform_probe,
    .remove = my_platform_remove,
    .driver = {
        .name = "my_platform_device",
        .owner = THIS_MODULE,
		.of_match_table =  of_match_table_id,
    },
};

// 模块初始化函数
static int __init my_platform_driver_init(void)
{
    int ret;

    // 注册平台驱动
    ret = platform_driver_register(&my_platform_driver);
    if (ret) {
        printk(KERN_ERR "Failed to register platform driver\n");
        return ret;
    }

    printk(KERN_INFO "my_platform_driver: Platform driver initialized\n");

    return 0;
}

// 模块退出函数
static void __exit my_platform_driver_exit(void)
{
    // 注销平台驱动
    platform_driver_unregister(&my_platform_driver);

    printk(KERN_INFO "my_platform_driver: Platform driver exited\n");
}

module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("fangchen");

测试验证

默认情况下查看设备模型中属性-GPIO引脚复用情况

编译room后,刷固件,然后准备这个驱动文件,实验参考通过:Linux驱动-GPIO子系统与pinctrl子系统相结合 来查看引脚复用:

cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins | grep 32

通过写属性来更改 设备属性值。

刷固件后,查看模型,如下: 默认是没有我们的属性的,那是因为驱动还没创建,还没有生成属性文件。
在这里插入图片描述

查看GPIO引脚复用情况,如下:默认情况下是没有设置的。

cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins | grep 32

在这里插入图片描述

加载驱动,再次查看引脚复用和设备属性

加载驱动,如下:
在这里插入图片描述
查看设备模型属性文件:
在这里插入图片描述

GPIO 复用情况如下:还是没有复用的
在这里插入图片描述

给系统属性selectmux 写0 ,再次查看引脚复用和设备属性

在这里插入图片描述

给系统属性selectmux 写1 ,再次查看引脚复用和设备属性

在这里插入图片描述

总结

  • 这里测试了 引脚复用功能具体实验
  • 这里了用了一千的知识点,给设备对象写一个属性,通过属性来判断。当然也可以通过其它方法与驱动通信即可,实现驱动传参来进行公引脚复用功能
  • 对于 无符号传参,用到一个方法,可以自行了解:select = simple_strtoul(buf, NULL, 10);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值