Linux设备驱动--pinctrl子系统和gpio子系统

gpio和pinctrl的内核使用文档

  • gpio子系统

    \Documentation\devicetree\bindings\gpio\gpio.txt

  • pinctrl子系统

    \Documentation\devicetree\bindings\pinctrl\pinctrl-bindings.txt


pinctrl子系统

Linux中将芯片引脚的复用、配置抽出来,汇总了所有引脚的配置信息,做成pinctrl子系统给其他外设使用,而不用使用一个外设就要去查找寄存器一个一个配置。

imx6ull与pinctrl子系统相关节点

imx6ull.dtsi:

iomuxc: iomuxc@20e0000 {
    compatible = "fsl,imx6ul-iomuxc";
    reg = <0x20e0000 0x4000>;
};
  • compatible - 与pinctrl子系统的平台驱动匹配
  • reg - 引脚配置寄存器基地址

imx6ull-seeed-npi.dts:

&iomuxc {
	pinctrl-names = "default""init","sleep";
	pinctrl-0 = <&pinctrl_hog_1>;
	pinctrl-1 =<&xxx>;
	pinctrl-2 =<&yyy>;
...
	pinctrl_uart1: uart1grp {
		fsl,pins = <
			MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
			MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
		>;
	};
...
}

节点配置方式:

  • pinctrl-names :定义引脚状态
  • pinctrl-0 : 定义第0中状态(也就是pinctrl-names的default状态)使用到的引脚,可引用其他节点,这里引用了pinctrl_hog_1节点
  • pinctrl-1 : 定义第1中状态(也就是pinctrl-names的init状态)使用到的引脚…
  • pinctrl-x : 以此类推

每家厂商的芯片的pinctrl节点格式都不一样,imx6ull以fsl,pins作为标识结合imx6ull的pinctrl子系统使用。

对于pinctrl的引用驱动基本不用管,当设备切换状态时对应的pinctrl会被自动调用。

fsl,pins属性值:

fsl,pins = <
    MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX  0x1b0b1
//             宏         +                十六进制数
    MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX  0x1b0b1
>;

宏的路径:

imx6ull.dtsi ->#include "imx6ull-pinfunc.h"-> #include "imx6ul-pinfunc.h"
#ifndef __DTS_IMX6UL_PINFUNC_H
#define __DTS_IMX6UL_PINFUNC_H

/*
 * The pin function ID is a tuple of
 * <mux_reg conf_reg input_reg mux_mode input_val>
 */
#define MX6UL_PAD_BOOT_MODE0__GPIO5_IO10		0x0014 0x02a0 0x0000 5 0
#define MX6UL_PAD_BOOT_MODE1__GPIO5_IO11		0x0018 0x02a4 0x0000 5 0

#define MX6UL_PAD_SNVS_TAMPER0__GPIO5_IO00		0x001c 0x02a8 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01		0x0020 0x02ac 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02		0x0024 0x02b0 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER3__GPIO5_IO03		0x0028 0x02b4 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04		0x002c 0x02b8 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER5__GPIO5_IO05		0x0030 0x02bc 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER6__GPIO5_IO06		0x0034 0x02c0 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER7__GPIO5_IO07		0x0038 0x02c4 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER8__GPIO5_IO08		0x003c 0x02c8 0x0000 5 0
#define MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09		0x0040 0x02cc 0x0000 5 0

...
...

#endif /* __DTS_IMX6UL_PINFUNC_H */

宏5个数值对应的含义:

比如MX6UL_PAD_UART1_TX_DATA__GPIO1_IO16:


#define MX6UL_PAD_UART1_TX_DATA__GPIO1_IO16	0x0084 0x0310 0x0000 5 0

<mux_reg  conf_reg  input_reg  mux_mode  input_val>
 0x0084    0x0310    0x0000       5           0
  • mux_reg : 引脚复用设置寄存器地址相对及地址的偏移值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LgukIm36-1648092194087)(E:\My_Note\Linux\Linux驱动\Linux--pinctrl子系统和gpio子系统.assets\image-20210304152645420.png)]

  • conf_reg: 引脚属性设置寄存器相对基地址的偏移值
    在这里插入图片描述

  • input_reg:引脚输入设置寄存器。引脚需要输入功能时设置

  • mux_mode: 复用寄存器设置值,设置引脚复用,如i2c,spi

  • input_val : 输入寄存器的设置值

十六进制数:

属性寄存器设置值。因为pin作为不同复用为不同功能属性值就不一样,易变化,故需要独立设置。

使用pinctrl子系统初始化LED灯引脚

修改设备树:

1、在iomuxc节点添加LED控制引脚配置信息的子节点


pinctrl_rgb_led:rgb_led{
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO04__GPIO1_IO04    0x000010B1
        MX6UL_PAD_CSI_HSYNC__GPIO4_IO20     0x000010B1
        MX6UL_PAD_CSI_VSYNC__GPIO4_IO19     0x000010B1
     >;
};

2、rgb_led节点添加引脚状态:

	/*添加led节点*/
	rgb_led {
		#address-cells = <1>;
		#size-cells = <1>;
     	pinctrl-names = "default";         //在rgb_led节点添加的pinctrl信息
 		pinctrl-0 = <&pinctrl_rgb_led>;    //在rgb_led节点添加的pinctrl信息
		compatible = "fire,rgb_led";

		/*红灯节点*/
		ranges;
		rgb_led_red@0x020C406C{
			compatible = "fire,rgb_led_red";
			reg = <0x020C406C 0x00000004
			       0x020E006C 0x00000004
			       0x020E02F8 0x00000004
				   0x0209C000 0x00000004
			       0x0209C004 0x00000004>;
			status = "okay";
		};

		/*绿灯节点*/
		rgb_led_green@0x020C4074{
			compatible = "fire,rgb_led_green";
			reg = <0x020C4074 0x00000004
			       0x020E01E0 0x00000004
			       0x020E046C 0x00000004
				   0x020A8000 0x00000004
			       0x020A8004 0x00000004>;
			status = "okay";
		};

		/*蓝灯节点*/
		rgb_led_blue@0x020C4074{
			compatible = "fire,rgb_led_blue";
			reg = <0x020C4074 0x00000004
			       0x020E01DC 0x00000004
			       0x020E0468 0x00000004
				   0x020A8000 0x00000004
			       0x020A8004 0x00000004>;
			status = "okay";
		};
	};
};

imx6ull的pinctrl子系统在内核源码 drivers/pinctrl/freescale/pinctrl-imx6ul.c中实现,也是基于平台总线驱动


gpio子系统

如果pinctrl子系统将芯片的pin复用成最基本的功能gpio功能,就要用到gpio子系统。linux将pin的输入输出功能抽象成了gpio子系统,而不在像操作寄存器那样麻烦。

drivers/gpio/gpio-mxc.c 为imx6ull的gpio子系统文件

static struct platform_driver mxc_gpio_driver = { 
   .driver     = { 
       .name   = "gpio-mxc", 
       .of_match_table = mxc_gpio_dt_ids, 
   }, 
   .probe      = mxc_gpio_probe, 
   .id_table   = mxc_gpio_devtype, 
}; 

可以看出gpio子系统也是个平台总线设备驱动。

设备树总的gpio节点:

在imx6ull.dtsi设备文件中

gpio1: gpio@209c000 {
    compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
    reg = <0x209c000 0x4000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
    <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_GPIO1>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    gpio-ranges = <&iomuxc  0 23 10>, <&iomuxc 10 17 6>,
    <&iomuxc 16 33 16>;
};
  • interrupts:GPIO中断相关

  • clocks:GPIO外设时钟相关

  • gpio-controller:表示 gpio1 节点是个 GPIO 控制器

  • #gpio-cells:表示要用2个cell描述一个 GPIO引脚

  • interrupt-controller:表示 gpio1 节点是个中断控制器

  • #interrupt-cells:表示要用2个cell描述一个中断

  • gpio-ranges:gpio编号转换成pin编号

以此类似的还有gpio2~gpio5

在imx6ull-seeed-npi.dts设备树文件新增rgb_led节点,使用gpio子系统

gpio子系统相关api

1、申请gpio

int gpio_request(unsigned gpio,  const char *label) 
//gpio - gpio编号
//label - 给gpio设置标签名字

2、释放gpio

void gpio_free(unsigned gpio)   //不使用就释放

3、设置gpio为输入

int gpio_direction_input(unsigned gpio) 

4、设置gpio为输出

int gpio_direction_output(unsigned gpio, int value) 

5、读取gpio的输入值

#define gpio_get_value    __gpio_get_value 
int __gpio_get_value(unsigned gpio)

6、设置gpio的值

#define gpio_set_value    __gpio_set_value 
void __gpio_set_value(unsigned gpio, int value) 

7、获取gpio编号,也就是上面函数的形参gpio

int of_get_named_gpio(struct device_node *np, 
                      const char  *propname,     
						int    index) 

pinctrl子系统和gpio子系统的使用

1、在iomux节点中添加跟pinctrl相关的节点

 /* 添加 pinctrl节点 pinctrl子系统通过fsl,pins来找到pinctrl子系统要初始化的pin */
pinctrl_red_led: fire_red_led {   
		fsl,pins = <
			MX6UL_PAD_GPIO1_IO04__GPIO1_IO04    0x000010B1
		>;
};

2、在根节点添加led的节点

red_led {                         /* 我加的设备树红色led的设备树子节点 */
        #address-cells = <1>; 
        #size-cells = <1>;
        compatible = "red_led";
        pinctrl-names = "default";      /* 加上跟pinctrl子系统相关的属性 */
		pinctrl-0 = <&pinctrl_red_led>; /* 加上跟pinctrl子系统相关的属性*/
        red_led_gpio = <&gpio1 4 GPIO_ACTIVE_LOW>;    /* 添加跟gpio子系统相关的 */
        status = "okay";
//       reg = < 0x20C406C 0x04  /* CCM_CCGR1 */       /* 去掉相关的寄存器 */
//                0x20E006C 0x04  /* MUX_PAD_GPIO1_IO04 */
//                0x209C000 0x04  /* GPIO1_DR */
//               0x209C004 0x04  >;  /* GPIO1_GDIR */ 
    };

2、在驱动程序中的的使用

#define RED_LED_DTS_NODE   "/red_led"       //red_led设备树节点路径
#define RED_LED_GPIO_NAME  "red_led_gpio"   //red led 节点中 gpio子系统相关属性名

//设备树的匹配条件
static struct of_device_id dts_match_table[] = {
    {.compatible = "red_led", },                     //通过设备树来匹配
};


static int led_red_driver_probe(struct platform_device *dev)
{
    int err;
    int ret;
    struct device *tmpdev;

    led_dev.dev_node = of_find_node_by_path(RED_LED_DTS_NODE);   //找到red_led的设备树节点
    if (!led_dev.dev_node) {          
        printk("red led device node can not found!\r\n"); 
        return -EINVAL; 
    }

    led_dev.red_led_gpio = of_get_named_gpio(led_dev.dev_node, RED_LED_GPIO_NAME, 0);   //找到led的gpio的编号
    if ( led_dev.red_led_gpio < 0) {
        printk("red led gpio can not found!\r\n"); 
        return -EINVAL;
    }

    // ret = gpio_request(led_dev.red_led_gpio, RED_LED_GPIO_NAME);   //请求gpio
    // if (ret < 0) {
    //     printk("rcan not request gpio!\r\n"); 
    //     return -EINVAL;
    // }

    ret = alloc_chrdev_region(&led_dev.dev_no, 0, 1, "red_led");
	if (ret < 0) {
		pr_err("Error: failed to register mbochs_dev, err: %d\n", ret);
		return ret;
	}

	cdev_init(&led_dev.chrdev, &red_led_drv);

	cdev_add(&led_dev.chrdev, led_dev.dev_no, 1);

    led_dev.led_class = class_create(THIS_MODULE, "red_led_class");
	err = PTR_ERR(led_dev.led_class);
	if (IS_ERR(led_dev.led_class)) {
        goto failed1;
	}

    //在 /dev目录下创建red_led设备节点
    tmpdev = device_create(led_dev.led_class , NULL, led_dev.dev_no, NULL, "red_led"); 
    if (IS_ERR(tmpdev)) {
        ret = -EINVAL;
		goto failed2;
	}

   	printk(KERN_INFO"%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    
    return 0;
failed2:
    device_destroy(led_dev.led_class, led_dev.dev_no);
    class_destroy(led_dev.led_class);
failed1:
    cdev_del(&led_dev.chrdev);
	unregister_chrdev_region(led_dev.dev_no, 1);
    return ret;
}

int led_red_driver_remove(struct platform_device *dev)
{
    printk(KERN_INFO"driver remove %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    // gpio_free(led_dev.red_led_gpio);      //释放gpio
    device_destroy(led_dev.led_class, led_dev.dev_no);
	class_destroy(led_dev.led_class);
	unregister_chrdev_region(led_dev.dev_no, 1);
    cdev_del(&led_dev.chrdev);
     
    return 0;
}

static struct platform_driver red_led_platform_driver = {
      .probe = led_red_driver_probe,
      .remove = led_red_driver_remove,
      .driver = {
        .name = "fire-rgb-led",
        .owner = THIS_MODULE,
        .of_match_table = dts_match_table,         //通过设备树匹配
      },
};


static int __init red_led_driver_init(void)
{
    int ret;
    printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    ret = platform_driver_register(&red_led_platform_driver);   //注册platform驱动
    return ret;
}

static void __exit red_led_driver_exit(void)
{
    printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    platform_driver_unregister(&red_led_platform_driver);
}

使用gpio子系统操作led:

static int red_led_drv_open (struct inode *node, struct file *file)
{
    int ret;

    printk(KERN_INFO"open red led dev\n set gpio is ouput\n");

    ret = gpio_direction_output(led_dev.red_led_gpio, 1);    //设置gpio为输出,默认输出高电平
    return ret;
}


static ssize_t red_led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int err;
	char status;
	
	printk("write %s %s line %d\r\n", __FILE__, __FUNCTION__, __LINE__);

	err = copy_from_user(&status, buf, 1);

	if (status)      
    {  /* 点灯 */
        gpio_set_value(led_dev.red_led_gpio, 0);       //输出低电平
    }
    else                      
    {   /* 熄灯 */
        gpio_set_value(led_dev.red_led_gpio, 1);      //输出高电平
    }
	
	return 1;
}

static struct file_operations red_led_drv = {
	.owner	 = THIS_MODULE,
	.open    = red_led_drv_open,
	.write   = red_led_drv_write,
};

测试应用程序:

debian@npi:~/nfs_rootfs$ sudo ./led_test /dev/red_led on
debian@npi:~/nfs_rootfs$ sudo ./led_test /dev/red_led off
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

欲盖弥彰1314

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

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

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

打赏作者

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

抵扣说明:

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

余额充值