1、修改设备树文件

在根节点/创建一个LED的子节点

嵌入式实践教程--设备树下的LED驱动开发_设备号2、驱动编写

(1)入口函数

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

/* 获取设备树中的属性数据 */

//1、获取根节点下的设备节点 muggle_led
dtsled.nd = of_find_node_by_path("/muggle_led");

//判断获得的节点是否正确
if(dtsled.nd == NULL)
{
printk("muggle-led node can not found!\r\n");
return -EINVAL;
}
else
{
printk("muggle-led node has been found!\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 read success: %s\r\n", str);
}

//4、获取reg属性内容
ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
if(ret < 0 )
{
printk("reg 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
//寄存器地址映射
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); //映射reg的数据
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

//使能GPIO1时钟
val = readl(IMX6U_CCM_CCGR1); //读取数值
val &= ~(3<<26); //引脚清0
val |= (3<<26); //设置新值
writel(val, IMX6U_CCM_CCGR1); //写入数值

//设置GPIO_IO03的复用功能,将其复用为GPIO_IO03最后设置IO属性
writel(5, SW_MUX_GPIO1_IO03);
writel(5, SW_PAD_GPIO1_IO03);

//4、设置GPIO_GDIR输出功能
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);


/* 注册字符设备驱动 */
//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.class);
}
return 0;

}

(1.1)是遍历设备树的根节点,获取​​compatible​​、​​status​​、​​reg​​属性,获取到​​reg​​中引脚的地址数据,然后​​of_iomap​​映射备用。

(1.2)使能GPIO引脚,写入数据。

(1.3)注册字符设备驱动:创建设备号、初始化​​cdev​​、添加​​cdev​​时加入​​dtsled_fops​​结构体、创建一个类最后创建设备

(1.4)编写​​dtsled_fops​​中​​open​​、​​read​​、​​write​​和​​release​​函数

(1.4.1)​​open​​函数主要实现私有数据的设置

(1.4.2)​​write​​函数从用户空间获取数据,操作LED关停

(2)出口函数

static void __exit led_exit(void)
{
//取消映射
iounmap(IMX6U_CCM_CCGR1);
iounmap(SW_MUX_GPIO1_IO03);
iounmap(SW_PAD_GPIO1_IO03);
iounmap(GPIO1_DR);
iounmap(GPIO1_GDIR);

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

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

嵌入式机器人公众号平台

嵌入式实践教程--设备树下的LED驱动开发_驱动程序_02