led设备树驱动

设备树

DTC将设备树源码DTS编译成DTB。
每一个厂家的设备树写法都有不同之处,是一种ASCII文本文件,我们也只需在厂家提供的设备树文件上修改即可。

标准属性

compatible 属性

字符串

compatible = "arm,cortex-a7";
compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";

compatible 一般用于驱动和设备匹配。

model 属性

描述设备模块信息

model = "wm8960-audio";

status 属性

描述设备状态

描述
“okay”表明设备是可操作的。
“disabled”表明设备当前是不可操作的,但是在未来可以变为可操作的,比如热插拔设备插入以后。至于 disabled 的具体含义还要看设备的绑定文档。
“fail”表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。
“fail-sss”含义和“fail”相同,后面的 sss 部分是检测到的错误内容。

reg 属性

32 位无符号整数

reg = <address1 length1 address2 length2 address3 length3……>
reg = <0x02020000 0x4000>;

此 reg 属性中 address=0x02020000,length=0x4000。查阅《I.MX6ULL 参考手册》可知,I.MX6ULL 的 UART1 寄存器首地址为 0x02020000,但是 UART1 的地址长度(范围)并没 有 0x4000 这么多,这里我们重点是获取 UART1 寄存器首地址。

#address-cells 和#size-cells 属性

#address-cells 表明 address 这个数据所占用的字长,
#size-cells 表明 length 这个数据所占用的字长

ranges 属性

描述
child-bus-address子总线地址空间的物理地址,由父节点的#address-cells 确定此物理地址所占用的字长。
parent-bus-address父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长。
length子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长。

如果 ranges 属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换, 对于我们所使用的 I.MX6ULL来说,子地址空间和父地址空间完全相同

设备树查找匹配设备

在这里插入图片描述

向节点追加或修改内容

多个“/”根节点会合并。

label: node-name@unit-address
cpu0:cpu@0

可以直接通过&label 来访问这个节点,比如通过&cpu0 就可以访问“cpu@0”这个节点

&cpu0{  
/* 要追加或修改的内容 */
};

led设备树驱动

修改设备树文件

在根节点中创建设备节点

/* flynnsin led不使用pinctrl 和 gpio子系统设备树节点 */
	alphaled {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "flynnsin-led";
		status = "okay";
		reg = <	0X020C406C 0X04 	/* CCM_CCGR1_BASE */
				0X020E0068 0X04 	/* SW_MUX_GPIO1_IO03_BASE */
				0X020E02F4 0X04 	/* SW_PAD_GPIO1_IO03_BASE */
				0X0209C000 0X04 	/* GPIO1_DR_BASE */
				0X0209C004 0X04 >; 	/* GPIO1_GDIR_BASE */
	};

编写led驱动

创建结构体

/* dtsled设备结构体 */
struct dtsled_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	**struct device_node	*nd; /* 设备节点 */**
};

/* 获取设备树中的属性数据 */
	/* 1、获取设备节点:alphaled */
	/* 寻找设备树是否有该节点 */
	dtsled.nd = of_find_node_by_path("/alphaled");
	if(dtsled.nd == NULL) {
		printk("alphaled node nost find!\r\n");
		return -EINVAL;
	} else {
		printk("alphaled node find!\r\n");
	}

	/* 2、获取compatible属性内容 */
	/* 匹配设备树节点,也就是通过比对compatible属性 */
	proper = of_find_property(dtsled.nd, "compatible", NULL);
	if(proper == NULL) {
		printk("compatible property find failed\r\n");
	} else {
		printk("compatible = %s\r\n", (char*)proper->value);
	}

	/* 3、获取status属性内容 */
	/* 看该节点是否使能 */
	ret = of_property_read_string(dtsled.nd, "status", &str);
	if(ret < 0){
		printk("status read failed!\r\n");
	} else {
		printk("status = %s\r\n",str);
	}

	/* 4、获取reg属性内容 */
	/* 读取里面寄存器地址及字节 */
	ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
	if(ret < 0) {
		printk("reg property read failed!\r\n");
	} else {
		u8 i = 0;
		printk("reg data:\r\n");
		for(i = 0; i < 10; i++)
			printk("%#X ", regdata[i]);
		printk("\r\n");
	}

	/* 初始化LED */
	/* 两种方式映射寄存器地址 */
#if 0
	/* 1、寄存器地址映射 */
	IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
	SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
  	SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
	GPIO1_DR = ioremap(regdata[6], regdata[7]);
	GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
#else
	IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
	SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
  	SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
	GPIO1_DR = of_iomap(dtsled.nd, 3);
	GPIO1_GDIR = of_iomap(dtsled.nd, 4);
#endif

pinctrl 和 gpio 子系统led设备树驱动

对于linux里实现了很多子系统来方便驱动开发
在这里插入图片描述

修改设备树文件

添加 pinctrl 节点

在 iomuxc 节点添加pinctrl节点信息

/* flynnsin led pinctrl节点*/
		pinctrl_led: ledgrp	{
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0
			>;
		};
linux\arch\arm\boot\dts\imx6ul-pinfunc.h
#define MX6UL_PAD_GPIO1_IO03__GPIO1_IO03                          0x0068 0x02F4 0x0000 0x5 0x0

<mux_reg conf_reg input_reg mux_mode input_val>

在这里插入图片描述

/* 2、使能GPIO1时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	/* 清楚以前的设置 */
	val |= (3 << 26);	/* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);

	/* 3、设置GPIO1_IO03的复用功能,将其复用为
	 *    GPIO1_IO03,最后设置IO属性。
	 */
	writel(5, SW_MUX_GPIO1_IO03);
	
	/*寄存器SW_PAD_GPIO1_IO03设置IO属性
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
	 */
	writel(0x10B0, SW_PAD_GPIO1_IO03);

	/* 4、设置GPIO1_IO03为输出功能 */
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	/* 清除以前的设置 */
	val |= (1 << 3);	/* 设置为输出 */
	writel(val, GPIO1_GDIR);

	/* 5、默认关闭LED */
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);

是不是非常熟悉,除了使能时钟,时钟可能在别的地方使能了,设置复用,对应设置mux_reg 寄存器值(也就是模式)mux_mode 为5,然后就是设置电气特性0x10B0。

添加设备节点

也就是包含pinctal节点,设置端口及电平有效值。

/* flynnsin led 使用子系统设备节点 */
	gpioled	{
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "flynnsin-led";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led>;
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

检查 PIN 是否被其他外设使用

这一点非常重要!!!,把别人实现的注释掉。

pinctrl_tsc: tscgrp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO01__GPIO1_IO01	0xb0
				MX6UL_PAD_GPIO1_IO02__GPIO1_IO02	0xb0
				/* MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0xb0 */
				MX6UL_PAD_GPIO1_IO04__GPIO1_IO04	0xb0
			>;
		};
&tsc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_tsc>;
/* 	xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; */
	measure-delay-time = <0xffff>;
	pre-charge-time = <0xfff>;
	status = "okay";
};

编写led驱动

创建结构体

/* gpioled设备结构体 */
struct gpioled_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
	struct device_node	*nd; /* 设备节点 */
	int led_gpio;			/* led所使用的GPIO编号		*/
};

获取设备树节点数据


	/* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
	gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
	if(gpioled.led_gpio < 0) {
		printk("can't get led-gpio");
		return -EINVAL;
	}
	printk("led-gpio num = %d\r\n", gpioled.led_gpio);

	/* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
	ret = gpio_direction_output(gpioled.led_gpio, 1);
	if(ret < 0) {
		printk("can't set gpio!\r\n");
	}

操作gpio子系统

struct gpioled_dev *dev = filp->private_data;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	ledstat = databuf[0];		/* 获取状态值 */

	if(ledstat == LEDON) {	
		gpio_set_value(dev->led_gpio, 0);	/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		gpio_set_value(dev->led_gpio, 1);	/* 关闭LED灯 */
	}
	return 0;

测试

/proc # cd device-tree/
/sys/firmware/devicetree/base # ls
#address-cells                 interrupt-controller@00a01000
#size-cells                    memory
aliases                        model
**alphaled**                       name
backlight                      pxp_v4l2
chosen                         regulators
clocks                         reserved-memory
compatible                     soc
cpus                           sound
**gpioled**                        spi4

可以看到这两个节点。

测试没有子系统驱动

/lib/modules/4.1.15 # modprobe dtsled.ko
modprobe: module dtsled.ko not found in modules.dep
/lib/modules/4.1.15 # depmod
/lib/modules/4.1.15 # modprobe dtsled.ko
alphaled node find!
compatible = flynnsin-led
status = okay
reg data:
0X20C406C 0X4 0X20E0068 0X4 0X20E02F4 0X4 0X209C000 0X4 0X209C004 0X4
dtsled major=249,minor=0
/lib/modules/4.1.15 # ls /dev/dtsled -l
crw-rw----    1 0        0         249,   0 Jan  1 01:27 /dev/dtsled
/lib/modules/4.1.15 # ./ledapp /dev/dtsled 1
/lib/modules/4.1.15 # ./ledapp /dev/dtsled 0
/lib/modules/4.1.15 # rmmod dtsled
/lib/modules/4.1.15 # lsmod
Module                  Size  Used by    Tainted: G

测试子系统驱动
只有系统第一次启动测试需要输入depmod。

/lib/modules/4.1.15 # modprobe gpioled.ko
gpioled node find!
led-gpio num = 3
gpioled major=249,minor=0
/lib/modules/4.1.15 # ls /dev/gpioled -l
crw-rw----    1 0        0         249,   0 Jan  1 01:31 /dev/gpioled
/lib/modules/4.1.15 # ./ledapp /dev/dtsled 1
file /dev/dtsled open failed!
/lib/modules/4.1.15 # ./ledapp /dev/gpioled  1
/lib/modules/4.1.15 # ./ledapp /dev/gpioled  0
/lib/modules/4.1.15 # rmmod gpioled.ko
/lib/modules/4.1.15 # lsmod
Module                  Size  Used by    Tainted: G

修改驱动查找设备树节点名字

gpioled.nd = of_find_node_by_path("/flynnsinled");
	if(gpioled.nd == NULL) {
		printk("flynnsinled node not find!\r\n");
		return -EINVAL;
	} else {
		printk("flynnsinled node find!\r\n");
	}

编译测试

flynnsinled node not find!
flynnsinled node not find!
modprobe: can't load module gpioled.ko (gpioled.ko): Invalid argument

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值