设备树下Led驱动实验-读设备树中设备节点的新方法

一.  简介

前面已经完成 从设备树文件读取 Led设备节点信息,Led的 IO初始化工作,最终实现Led灯的打开与关闭。

其中,从设备树文件读取 Led设备节点的属性 reg值,具体读取 (IMX6ULL芯片上)寄存器的地址。然后将读取的寄存器的物理地址转换为虚拟地址,这部分代码的实现,可以通过直接内存映射来实现,具体直接调用 of_iomap()函数即可完成。

二.  读取设备树中设备节点信息的新方法

1. 直接内存映射

of_iomap() 函数用于直接内存映射。of_iomap()函数的头文件为:

#include <linux/of_address.h>

注意: of_iomap()函数获取的 reg属性!!!

前面我们会通过 ioremap() 函数,来完成物理地址到虚拟地址的映射,采用设备树以后,就可以直接通过 of_iomap() 函数来获取内存地址所对应的虚拟地址, 而不需要使用 ioremap() 函数了。

当然了,你也可以使用 ioremap 函数来完成物理地址到虚拟地址的内存映射,只是在采用设备树以后,大部分的驱动都使用 of_iomap 函数了。

of_iomap() 函数本质上也是将 reg 属性中地址信息转换为虚拟地址,如果 reg 属性有多段的话,可以通过 index 参数指定要完成内存映射的是哪一段。

of_iomap 函数原型如下:
void __iomem *of_iomap(struct device_node *np, int index)

函数参数和返回值含义如下:

np:设备节点。

index:reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话, index 就设置为 0。

返回值:经过内存映射后的虚拟内存首地址,如果返回 NULL,表示内存映射失败。

函数参数 index说明:地址+地址长度,算是一个 index。例如,reg属性值如下:

#address-cells = <1>;
#size-cells = <1>;
reg = < 0X020C406C 0x04  /*CCM_CCGR1_BASE  */
        0X020E0068 0x04  /*SW_MUX_GPIO1_IO03_BASE */
        0X020E02F4 0x04  /*SW_PAD_GPIO1_IO03_BASE */
        0X0209C004 0x04  /*GPIO1_GDIR_BASE */
        0X0209C000 0x04>; /*GPIO1_DR_BASE */

如果 reg属性值如上所示,则 第3行的 index为 0;第4 行的 index为1;依次类推。

2.   代码实现

这里代码与前面驱动的区别,只是 dtsled.c文件中的驱动入口函数 dtsled_init()函数的差别。

调用 of_iomap()函数之前,需要向 dtsled.c文件添加头文件:

#include <linux/of_address.h>

dtsled_init() 函数实现如下:
/*驱动入口函数 */
static int __init dtsled_init(void)
{
    int ret = 0;
    u32 reg_value = 0;

    /*1. 读取设备树文件中寄存器地址 */
    dtsled.dev_node = of_find_node_by_path("/alpha_led");
    if(NULL == dtsled.dev_node)
    {
        printk("find dev-node failed!\n");
        ret = -EINVAL;
        goto apply_devid_failed;
    }
    //读取reg中的地址,将物理地址映射为虚拟地址
    IMX6ULL_CCM_CCGR1 = of_iomap(dtsled.dev_node, 0);
    SW_MUX_GPIO1_IO03 = of_iomap(dtsled.dev_node, 1);
    SW_PAD_GPIO01_IO03 = of_iomap(dtsled.dev_node, 2);
    GPIO1_GDIR = of_iomap(dtsled.dev_node, 3);
    GPIO1_DR = of_iomap(dtsled.dev_node, 4);

    /*3. Led设备IO初始化 */
    //使能时钟
    reg_value = readl(IMX6ULL_CCM_CCGR1);
    reg_value &= ~(3 << 26);
    reg_value |= (3 << 26);
    writel(reg_value, IMX6ULL_CCM_CCGR1);
    //复用为GPIO功能
    writel(0X05, SW_MUX_GPIO1_IO03);
    //配置电气特性
    writel(0X10B0, SW_PAD_GPIO01_IO03);
    //设置为输出
    reg_value = readl(GPIO1_GDIR);
    reg_value &= ~(1 << 3);
    reg_value |= (1 << 3);
    writel(reg_value, GPIO1_GDIR);
    
    //关闭Led灯
    reg_value = readl(GPIO1_DR);
    reg_value |= (1 << 3); //置1,关闭Led
    writel(reg_value, GPIO1_DR);

    dtsled.major = 0;
    /*1. 申请设备号 */
    if(dtsled.major) //给定设备号
    {
        dtsled.devid = MKDEV(dtsled.major, 0);
        ret = register_chrdev_region(dtsled.devid, DEV_CNT, DEV_NAME);
    }
    else //向内核申请设备号
    {
        ret = alloc_chrdev_region(&dtsled.devid, 0, DEV_CNT, DEV_NAME);
        dtsled.major = MAJOR(dtsled.devid);
        dtsled.minor = MINOR(dtsled.devid);
        printk("dtsled.major: %d\n", dtsled.major);
        printk("dtsled.minor: %d\n", dtsled.minor);
    }
    if(ret < 0)
    {
        printk("apply_dev_numbers failed!\n");
        goto apply_devid_failed;        
    }

    /*2. 添加字符设备 */
    dtsled.cdev.owner = THIS_MODULE;
    cdev_init(&dtsled.cdev, &dtsled_fops);
    ret = cdev_add(&dtsled.cdev, dtsled.devid, DEV_CNT);
    if(ret < 0)
    {
        printk("cdev-add failed!\n");
        goto cdev_add_failed;
    }

    /*3. 自动创建设备节点 */
    //创建类
    dtsled.class = class_create(THIS_MODULE, DEV_NAME);
    if (IS_ERR(dtsled.class)) {
        printk("class_create failed!\n");
		ret = PTR_ERR(dtsled.class);
		goto auto_class_create_failed;
	}
    //创建设备 
    dtsled.dev = device_create(dtsled.class, NULL, dtsled.devid, NULL, DEV_NAME);
    if (IS_ERR(dtsled.dev)) {
        printk("device_create failed!\n");
		ret = PTR_ERR(dtsled.dev);
		goto auto_create_dev_failed;
	}

    return 0;

auto_create_dev_failed:
    class_destroy(dtsled.class);
auto_class_create_failed:
    cdev_del(&dtsled.cdev);
cdev_add_failed:
    unregister_chrdev_region(dtsled.devid, DEV_CNT);
apply_devid_failed:
    return ret;
}

三.  编译驱动并测试

进入 5_dtsled工程目录下,重新编译驱动代码:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/5_dtsled.c$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/5_dtsled.c modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
  Building modules, stage 2.
  MODPOST 1 modules
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/5_dtsled.c$ 

可以看出,驱动代码已经编译通过。

下面就是对驱动进行测试。测试方法与之前的一样,也是使用应用程序调用的方式来控制 Led灯的亮灭:

设备树下Led驱动实验-Led驱动加载测试-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值