100ask_imx6ull使用寄存器设备树方式亮点led

一、开发环境

		1、100ask_imx6ull开发板
		2、mint19.1开发环境
		3、适用windows下的NFS进行挂载测试

二、硬件原理

100ask_imx6ull使用寄存器设备树方式亮点led_第1张图片

LED的管脚为GPIO5_IO03。
当GPIO5_IO03为高电平时,LED熄灭;当GPIO5_IO03为低电平时,LED被点亮。

三、在arch/arm/boot/dts/100ask_imx6ull-14x14.dts的根节点下添加led设备信息。

		/*seedling 2020/03/18*/
	seedling_led {
			compatible = "imx6ull,led";
			#address-cells = <1>;
			#size-cells = <1>;
			status = "okay";
			reg = < 0X020C406C 0X04 		/*CCM_CCGR1_BASE*/
					0X02290014 0X04 		/*SW_MUX_GPIO5_IO03_BASE*/
					0X020E02B4 0X04 		/*SW_PAD_GPIO5_IO03_BASE*/
					0X020AC000 0X04 		/*GPIO5_DR_BASE*/
					0X020AC004 0X04 >;		/*GPIO5_GDIR_BASE*/
	};

四、驱动测试源码

dtsled.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/***************************************************************
文件名		: dtsled.c
版本	   	: V1.0
描述	   	: LED驱动文件。
***************************************************************/
#define DTSLED_CNT			1		  	/* 设备号个数 */
#define DTSLED_NAME			"dtsled"	/* 名字 */
#define LEDOFF 					0			/* 关灯 */
#define LEDON 					1			/* 开灯 */

/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO5_IO03;
static void __iomem *SW_PAD_GPIO5_IO03;
static void __iomem *GPIO5_DR;
static void __iomem *GPIO5_GDIR;

/* 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; /* 设备节点 */
};

struct dtsled_dev dtsled;	/* led设备 */

/*
 * @description		: LED打开/关闭
 * @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED
 * @return 			: 无
 */
void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO5_DR);
		val &= ~(1 << 3);	
		writel(val, GPIO5_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO5_DR);
		val|= (1 << 3);	
		writel(val, GPIO5_DR);
	}	
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &dtsled; /* 设置私有数据 */
	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;

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

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

	if(ledstat == LEDON) {	
		led_switch(LEDON);		/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);	/* 关闭LED灯 */
	}
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备操作函数 */
static struct file_operations dtsled_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};

static int __init led_init(void)
{
	u32 val = 0;
	int ret;
	u32 regdata[14];
	const char *str;
	struct property *proper;

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

	/* 2、获取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_GPIO5_IO03 = ioremap(regdata[2], regdata[3]);
  	SW_PAD_GPIO5_IO03 = ioremap(regdata[4], regdata[5]);
	GPIO5_DR = ioremap(regdata[6], regdata[7]);
	GPIO5_GDIR = ioremap(regdata[8], regdata[9]);
#else
	IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
	SW_MUX_GPIO5_IO03 = of_iomap(dtsled.nd, 1);
  	SW_PAD_GPIO5_IO03 = of_iomap(dtsled.nd, 2);
	GPIO5_DR = of_iomap(dtsled.nd, 3);
	GPIO5_GDIR = of_iomap(dtsled.nd, 4);
#endif

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

	/* 3、设置GPIO5_IO03的复用功能,将其复用为
	 *    GPIO5_IO03,最后设置IO属性。
	 */
	writel(5, SW_MUX_GPIO5_IO03);
	
	/*寄存器SW_PAD_GPIO5_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_GPIO5_IO03);

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

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

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (dtsled.major) {		/*  定义了设备号 */
		dtsled.devid = MKDEV(dtsled.major, 0);
		register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
	} else {						/* 没有定义设备号 */
		alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME);	/* 申请设备号 */
		dtsled.major = MAJOR(dtsled.devid);	/* 获取分配号的主设备号 */
		dtsled.minor = MINOR(dtsled.devid);	/* 获取分配号的次设备号 */
	}
	printk("dtsled major=%d,minor=%d\r\n",dtsled.major, dtsled.minor);	
	
	/* 2、初始化cdev */
	dtsled.cdev.owner = THIS_MODULE;
	cdev_init(&dtsled.cdev, &dtsled_fops);
	
	/* 3、添加一个cdev */
	cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);

	/* 4、创建类 */
	dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
	if (IS_ERR(dtsled.class)) {
		return PTR_ERR(dtsled.class);
	}

	/* 5、创建设备 */
	dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
	if (IS_ERR(dtsled.device)) {
		return PTR_ERR(dtsled.device);
	}
	
	return 0;
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO5_IO03);
	iounmap(SW_PAD_GPIO5_IO03);
	iounmap(GPIO5_DR);
	iounmap(GPIO5_GDIR);

	/* 注销字符设备驱动 */
	cdev_del(&dtsled.cdev);/*  删除cdev */
	unregister_chrdev_region(dtsled.devid, DTSLED_CNT); /* 注销设备号 */

	device_destroy(dtsled.class, dtsled.devid);
	class_destroy(dtsled.class);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("seedling");


五、应用层测试程序

ledApp.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

/***************************************************************
文件名		: ledApp.c
版本	   	: V1.0
描述	   	: 测试APP。
其他	   	: 无
使用方法	 :./ledApp   0 关闭LED
		     ./ledApp     1 打开LED		
***************************************************************/

#define LEDOFF 	0
#define LEDON 	1
#define LED_NODE "/dev/dtsled"
/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	int fd, retvalue;
	unsigned char databuf[1];
	
	if(argc != 2){
		printf("Error Usage!!!!!!\r\n");
		return -1;
	}


	/* 打开led驱动 */
	fd = open(LED_NODE, O_RDWR);
	if(fd < 0){
		printf("file %s open failed!\r\n", LED_NODE);
		return -1;
	}

	databuf[0] = atoi(argv[1]);	/* 要执行的操作:打开或关闭 */

	/* 向/dev/led文件写入数据 */
	retvalue = write(fd, databuf, sizeof(databuf));
	if(retvalue < 0){
		printf("LED Control Failed!\r\n");
		close(fd);
		return -1;
	}

	retvalue = close(fd); /* 关闭文件 */
	if(retvalue < 0){
		printf("file %s close failed!\r\n", LED_NODE);
		return -1;
	}
    
	return 0;
}

六、Makefile

KERNELDIR := /home/mint/imx6/100ask_imx6ull-sdk/Linux-4.9.88/
CURRENT_PATH := $(shell pwd)

obj-m := dtsled.o
APP_NAME := ledApp

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
	rm -rf $(APP_NAME)
install:
	arm-linux-gnueabihf-gcc $(APP_NAME).c -o  $(APP_NAME)
	cp $(APP_NAME)  *.ko  ~/nfs

七、将新的设备树烧写至开发板

八、测试

1、加载驱动

	insmod dtsled.ko
	dmesg -c
	

100ask_imx6ull使用寄存器设备树方式亮点led_第2张图片

2、查看设备树信息

 cd /proc/device-tree/seedling_led/
 cat name
 cat compatible
 cat status

100ask_imx6ull使用寄存器设备树方式亮点led_第3张图片

3、运行应用程序

						点灯:./ledApp 1
						灭灯:./ledApp 0

100ask_imx6ull使用寄存器设备树方式亮点led_第4张图片

你可能感兴趣的:(IMX.6)