Linux驱动开发学习笔记【4】:自动创建设备节点+通过设备树获取寄存器信息的LED灯驱动

目录

前言

一、led设备树节点添加

二、led设备树节点信息获取


前言

在 正点原子阿尔法Linux开发板LED灯驱动开发(自动创建设备节点)中,虽然实现了功能,但是是直接定义了LED的GPIO寄存器地址,然后操作的寄存器,这种方法是不推荐使用的,而且在Linux中专门用了设备树来描述板子的设备信息,所以完全可以通过设备树来设置LED的GPIO寄存器信息,然后在LED的模块驱动中通过of函数获取设备树中LED节点的属性信息,以此来完成驱动编写

一、led设备树节点添加

在 imx6ull-alientek-emmc.dts 设备树的根节点下添加如下节点

/*led节点*/
alphaled{
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "atkalpha-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_fops.dtsled_open 中添加获取设备树节点信息,获取 reg 数据,然后通过直接操作寄存器,设置pin的复用功能、GPIO的电气属性、输入输出、默认电平

static int dtsled_open(struct inode *inode, struct file *file)
{
    int ret = 0, val;
    struct property *dtsled_prop = NULL;
    const char *str = NULL;
    uint32_t i, regdata[14];

    file->private_data = &dtsled;

    /*1、找到节点 alphaled,路径是 /alphaled */
    dtsled.nd = of_find_node_by_path("/alphaled");
    if (dtsled.nd == NULL){
        printk("alphaled node can not found!\r\n");
        return -EINVAL;
    } else {
        printk("alphaled node has been found!\r\n");
    }

    /*2、查找 兼容性属性*/
    dtsled_prop = of_find_property(dtsled.nd,"compatible",NULL);
    if (dtsled_prop == NULL){
        printk("compatible property find failed\r\n");
        return -EINVAL;
    } else {
        printk("compatible = %s\r\n", (char *)dtsled_prop->value);
    }
    /*3、查找 status 属性*/
    ret = of_property_read_string(dtsled.nd, "status", &str);
    if (ret < 0){
        printk("status read failed!\r\n");
        return -EINVAL;
    } 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 {
        printk("reg data:\r\n");
        for (i=0; i<10; i++){
            printk("%#X ", regdata[i]);
        }
        printk("\r\n");
    }

    /*初始化led,不能直接操作物理地址,需要地址重映射*/
    /*1、寄存器地址映射*/
#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);
    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

    /* 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);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_37845735/article/details/106976227