一、linux驱动开发-2.2-设备树下的LED驱动

目录

一、前言

二、修改设备树文件

三、驱动程序编写


一、前言

       之前都是在.c文件中定义有关寄存器物理地址,然后使用io_remap函数进行内存映射,得到虚拟地址,实现操作寄存器对应的虚拟地址控制GPIO的目的。

       现在学了设备树,就用设备树向linux内核传递相关的寄存器物理地址,然后用of函数从设备树中获取需要的属性值,用获取到的属性值来初始化相关的IO。具体的步骤如下:

       ①、在.dts文件中创建相应的设备节点;

       ②、利用of函数获取设备树中相关属性;

       ③、使用获取到的属性值初始化用到的GPIO;

二、修改设备树文件

       打开开发板用到的设备树.dts文件,在根节点/末尾处加上如下代码:

miniled {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "alientek,mini";
		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 */
};

       address-cell和size-cell都为1,表示reg属性中起始地址占用一个字长,地址长度也占用一个字长。比如“0X020C406C 0X04”表示CCM_CCGR1寄存器首地址为0x020C406C,长度为4个字节。

       修改完后,输入make dtbs,得到.dtb文件,放在tftp路径下,就可以用新的dtb启动内核了。

       启动后进入/proc/devicetree目录下查看是否有“miniled”节点,然后进入“miniled”目录中查看看是否跟设置值一致。

三、驱动程序编写

        通过of函数获取设备树中属性数据,主要是获取reg中各寄存器物理地址值,然后利用ioremap获取虚拟地址进行操作;也可以获取设备节点后直接用of_iomap直接得到虚拟地址。

//设备结构体
struct dtsled_dev{
	dev_t devid;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	int major;
	int minor;
	struct device_node *nd;		//设备节点
};

//定义一个设备
struct dtsled_dev dtsled;  

①、获取设备节点

//methor1:通过节点名字查找节点
dtsled.nd = of_find_node_by_name(NULL, "/miniled");
if (dtsled.nd == NULL)
{
	printk("find %s fail\r\n", "miniled");
	return -EINVAL;
}

②、根据设备节点获取compatible、status属性内容

//查找指定属性,compatible
proper = of_find_property(dtsled.nd, "compatible", NULL);
if (proper == NULL)
{
	printk("can't find 'compatible'\r\n");
    return -EINVAL;
}
else
{
	printk("'compatible' value is %s\r\n", (char *)proper->value);
}
//查找指定属性,status
ret = of_property_read_string(dtsled.nd, "status", &str);
if (ret < 0)
{
	printk("read 'status' fail\r\n");
	return -EINVAL;
}
else
{
	printk("'status' value is %s\r\n", str);
}

③、内存映射

//查找指定属性值,reg
ret = of_property_read_u32_array(dtsled.nd, "reg", reg, 10);
if (ret < 0)
{
	printk("read 'reg' fail\r\n");
	return -EINVAL;
}
else
{
	for (i = 0; i < 10; i++)
		printk("reg[%d] = %x\r\n", i, reg[i]);
}

//methor1:根据寄存器地址映射
#if 0
    CCM_CCGR1 = ioremap(reg[0], reg[1]);
    SW_MUX_GPIO1_IO03 = ioremap(reg[2], reg[3]);	
    SW_PAD_GPIO1_IO03 = ioremap(reg[4], reg[5]);	
    GPIO1_DR = ioremap(reg[6], reg[7]);
    GPIO1_GDIR = ioremap(reg[8], reg[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

示例如下:

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>

#define DTSLED_CNT      1
#define DTSLED_NAME     "dtsled"

/* 地址映射后的虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

/* dtsled设备结构体 */
struct dtsled_dev{
    dev_t devid;            /* 设备号 */
    struct cdev cdev;       /* 字符设备 */
    struct class *class;     /* 类 */
    struct device *device;   /* 设备 */
    int major;              /* 主设备号 */
    int minor;              /* 次数备号 */
    struct devive_node *nd; /* 设备节点 */

};

struct dtsled_dev dtsled;   /* led设备 */

#define LEDOFF	0
#define LEDON   1

void led_switch(unsigned char sta)
{
	unsigned int val = 0;

	if (sta == LEDOFF){
		val = readl(GPIO1_DR);
		val |= 1 << 3;		/* bit3置1,关闭LED */
		writel(val, GPIO1_DR);
	} else if (sta == LEDON){
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);		/* bit3清零,打开LED */
		writel(val, GPIO1_DR);
	}
}

static int dtsled_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &dtsled; /* 设置私有数据 */
	return 0;
}

static ssize_t dtsled_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static ssize_t dtsled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    struct dtsled_dev *dev = (struct dtsled_dev*)filp->private_data;
    
    int retvalue;
	unsigned char databuf[1];

	retvalue = copy_from_user(databuf, buf ,cnt);
	if (retvalue < 0){
		return -EFAULT;
	}
	/*开灯关灯判断*/
	led_switch(databuf[0]);

    return 0;
}

static int dtsled_release(struct inode *inode, struct file *filp)
{
    struct dtsled_dev *dev = (struct dtsled_dev*)filp->private_data;

	return 0;
}

/* 设备操作函数 */
static struct file_operations dtsled_fops = {
	.owner = THIS_MODULE,
	.open = dtsled_open,
	.read = dtsled_read,
	.write = dtsled_write,
	.release = dtsled_release,
};

/* 入口 */
static int __init dtsled_init(void)
{
    int ret = 0;
    u32 regdata[10] = {0};
    u8 i = 0;
    unsigned int val = 0;

    /* 1、注册字符设备 */
    dtsled.major = 0;
    if (dtsled.major) {
        dtsled.devid = MKDEV(dtsled.major, 0);
        ret = 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);
    }

    if (ret < 0) {
        goto faile_devid;
    }

    /* 1、添加字符设备 */
    dtsled.cdev.owner = THIS_MODULE;
    cdev_init(&dtsled.cdev, &dtsled_fops);
    ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
    if (ret < 0) {
        goto fail_cdev;
    }

    /* 3、自动创建设备节点 */
    dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
    if (IS_ERR(dtsled.class)) {
        ret = PTR_ERR(dtsled.class);
        goto fail_class;
    }

    dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);
    if (IS_ERR(dtsled.device)) {
        ret = PTR_ERR(dtsled.device);
        goto fail_device;
    }

    /* 获取设备树信息 */
    dtsled.nd = of_find_node_by_path("/miniled");
    if (dtsled.nd == NULL) {
        ret = -EINVAL;
        goto fail_findnd;
    }
#if 0 
    /* 获取寄存器参数 */
    /* 获取数组 */
	ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
	if (ret < 0) {
		goto fail_rs;
	} else {
        printk("reg data:\r\n");
		for (i = 0; i< 10; i++)
			printk("%#X ", regdata[i]);
        printk("\r\n");
	}

    /* LED灯初始化 */
    /* 1、初始化LED灯,地址映射 */
   
    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]);
#endif
    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);
    
    val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	/* 先清除以前的配置bit26,27 */
	val |= 3 << 26;		/* bit26,27置1 */
	writel(val, IMX6U_CCM_CCGR1);

	writel(0x05, SW_MUX_GPIO1_IO03);	/* 设置复用 */
	writel(0X10B0, SW_PAD_GPIO1_IO03);	/* 设置电气属性 */

	val = readl(GPIO1_GDIR);
	val |= 1 << 3;		/* bit3置1,设置为输出 */
	writel(val, GPIO1_GDIR);

	val = readl(GPIO1_DR);
	val &= ~(1 << 3);		/* bit3清零,打开LED */
	writel(val, GPIO1_DR);

    return 0;

fail_rs:    
fail_findnd:
    device_destroy(dtsled.class, dtsled.devid);
fail_device:
    class_destroy(dtsled.class);
fail_class:
    cdev_del(&dtsled.cdev);
fail_cdev:
    unregister_chrdev_region(dtsled.devid, DTSLED_CNT);
faile_devid:
    return ret;
}

static void __exit dtsled_exit(void)
{
    unsigned int val = 0;

    val = readl(GPIO1_DR);
	val |= 1 << 3;		/* bit3置1,关闭LED */
	writel(val, GPIO1_DR);

    /* 1、取消地址映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

    /* 删除字符设备 */
    cdev_del(&dtsled.cdev);

    /* 释放设备号 */
    unregister_chrdev_region(dtsled.devid, DTSLED_CNT);

    /* 摧毁设备 */
    device_destroy(dtsled.class, dtsled.devid);

    /* 摧毁类 */
    class_destroy(dtsled.class);
}

/* 驱动注册与卸载 */
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangkai");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值