1、修改设备树文件
在根节点/创建一个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);
}
嵌入式机器人公众号平台