从设备树中获得相关的属性值

本文介绍了如何在Linux内核中利用设备树获取寄存器地址,创建设备节点,以及如何编写设备驱动程序,如IMX6ULL平台上的EMMC设备,通过设备树属性初始化GPIO控制LED。

1. 概述

使用设备树来向linux内核传递相关的寄存器的物理地址,linux驱动文件使用of函数,从设备树获得所需要的属性值,然后使用获得的属性值来进行初始化相关的IO

    1.在imx6ull-alientek-emmc.dts 文件中创建线管的设备节点

    2.编写设备驱动,从设备树中获得相关的属性值

    3.使用从设备树获得的有关属性值来初始化LED所使用的IO

2. 设备树节点 

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

3. 代码 dts_led.c


#define DTS_LED_CNT   1          /*设备个数*/
#define DTS_LED_NAME  "dts_led"  /*名字*/

#define LEDON   0 
#define LEDOFF  1 

/* 映射后的寄存器虚拟地址指针 */
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;

struct dts_led_dev{
    dev_t dev;                  /*设备号*/
    struct cdev cdev;           /*cdev*/
    struct class *class ; 
    struct device *device;      /*设备*/
    int major   ;               /*主设备号*/
    int minor   ;               /*次设备号*/
    struct device_node *nd ;    /*设备节点*/
} ; 

struct dts_led_dev dts_led ;  /*led 设备*/

static void led_switch(u8 state)
{
    u32 val = 0 ; 
    if(state == LEDON)
    {
        val = readl(GPIO1_DR) ; 
        val &= ~(1 << 3) ; 
        wiretel(val ,GPIO1_DR) ;
    }
    elseif(state == LEDOFF)
    {
        val  = readl(GPIO1_DR) ; 
        val |=(1 << 3) ; 
        wiretel(val ,GPIO1_DR) ;
    }
}
/*
* @description:打开设备
* @param-inode:传递给驱动的inode
* @param-filp :设备文件,file结构体有个叫private_data的成员变量 
                   一般在open的时候将private_data指向设备结构体
* @return     :0 成功;其他 失败
*/
static int dts_led_open(struct inode *inode , struct file *filp)
{
    filp->private_data = &dts_led ; 
    return 0 ; 
}
/*
* @description:从设备读取数据
* @param-filp : 要打开的设备驱动文件
* @param-buf  : 返回给用户空间的数据缓冲区
* @param-count: 要读取的数据长度
* @param-offt : 相对于文件首地址的偏移
* @return     : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t dts_led_read(struct file *filp , char __user *buf , size_t count
                            , loff_t *offt)
{
    return 0 ; 
}
/*
* @description: 向设备写数据
* @param-filp :设备驱动文件,表示打开的文件描述符
* @param-buf  :要写入的数据
* @param-count:要写入的数据长度
* @param-offt :相对于文件首地址的偏移
* @return     :写入的字节数,如果为负值,表示写入失败
*/

static ssize_t dts_led_write(struct file *filp , char __user *buf , size_t count
                            , loff_t *offt)
{   
    int value = 0 ;
    unsigned char databuf[1] ; 
    unsigned led_state ; 
    value = copy_from_user(databuf,buf,count) ; 
    if(value< 0 )
    {
        printk("kernel write failed! \r\n")
        return -EFAULT ; 
    }
    led_state = databuf[0] ; /*获取状态值*/

    if(led_state == LEDON)
    {
        led_switch(LEDON) ;
    }
    elseif(led_state == LEDOFF)
    {
        led_switch(LEDOFF) ;
    }
    return 0 ; 
}
/*
* @description: 关闭释放文件
* @param-inode:传递给驱动的inode
* @param-filp :设备驱动文件,表示打开的文件描述符
* @return     :0 成功 负数表示失败
*/
static int dts_led_release(struct inode *inode , struct file *filp)
{
    return 0 ; 
}

/*设备操作结构体*/
struct file_operations dts_led_fops = {
    .owner      = THIS_MODULE,
    .open       = dts_led_open,
    .write      = dts_led_write,
    .read       = dts_led_read,
    .release    = dts_led_release,
};
/*
* @description  : 模块入口函数
* @param        : none
* @return       : none
*/
static int __init led_init(void)
{
     u32 val = 0 ; 
     int ret ;
     u32 reg_data[14] ; 
     const char *str ; 
     struct property *proper ;

    /*获取设备树的属性数据*/
    /*1. 获取设备节点: alphaled*/
    dts_led.nd = of_find_node_by_path("/alphaled");
    if(dts_led.nd == NULL)
    {
        printk("alphaled node not found !  \r\n") ;
        return -EINVAL ; 
    }
    else{
        printk("alphaled node has been found ! \r\n ") ; 
        
    }

    /*2.获取compatible 属性内容*/
    proper = of_find_property(des_led.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(dts_led.nd , "status" , &str) ;
    if(ret < 0)
    {
        printk("status read failed \r\n") ; 
    }else 
    {
        printk("status = %s\r\n", str) ;
    }

    /*4.获取reg属性的内容*/ 
    ret = of_property_read_u32_array(dts_led.nd , "reg" , reg_data, 10) ; 
    if(ret <0 ) 
    {
        printk("reg read failed \r\n") ; 
    }else
    {
        u8 i = 0 ;
        printk("reg data: \r\n");
        for(i ; i < 10 ; i++)
        {
            printk("%#x" ,reg_data[i]);
        }printk("\r\n") ; 

    }
    /*初始化LED*/
    /*1.寄存器地址映射*/
    #if 0 
    IMX6U_CCM_CCGR1  = ioremap(CCM_CCGR1_BASE , 4) ; 
    SW_MUX_GPIO1_IO03= ioremap(SW_MUX_GPIO1_IO03_BASE , 4) ;
    SW_PAD_GPIO1_IO03= ioremap(SW_PAD_GPIO1_IO03_BASE , 4) ;
    GPIO1_DR         = ioremap(GPIO1_DR_BASE , 4) ;
    GPIO1_GDIR       = ioremap(GPIO1_GDIR_BASE , 4) ;          
    #else 
    IMX6U_CCM_CCGR1  = of_iomap(dts_led.nd , 0 ) ;  
    SW_MUX_GPIO1_IO03= of_iomap(dts_led.nd , 1 ) ;  
    SW_PAD_GPIO1_IO03= of_iomap(dts_led.nd , 2 ) ;  
    GPIO1_DR         = of_iomap(dts_led.nd , 3 ) ;  
    GPIO1_GDIR       = of_iomap(dts_led.nd , 4 ) ;   
    #endif 

    /*2.时能时钟*/
    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) ;

    /*4.寄存器SW_PAD_GPIO1_IO03设置为IO属性*/
    writel(0x10B0,SW_PAD_GPIO1_IO03) ;

    /*5.设置GPIO1_IO03为输出功能*/
    val = readl(GPIO1_GDIR) ; 
    val &= ~(1 << 3 ) ; 
    val |= (1 << 3 ) ; 
    writel(val,GPIO1_GDIR) ;

    /*6.默认关闭LED*/
    val = readl(GPIO1_DR) ;
    val |= (1 << 3) ;
    writel(val,GPIO1_DR) ;


    /*注册字符设备驱动*/
    /*1.创建设备号*/
    if(dts_led.major) /*如果定义了设备号*/
    {
        dts_led.devid = MKDEV(dts_led.major , 0) ;
        register_chrdev_region(dts_led.devid , 
        DTS_LED_CNT , DTS_LED_NAME) ;
    }
    else
    {
        alloc_chrdev_region(&dts_led.devid , 0 , DTS_LED_CNT
        , DTS_LED_NAME) ; /*申请设备号*/
        dts_led.major = MAJOR(dts_led.devid) ;/*获得分配号的主设备号*/
        dts_led.minor = MINOR(dts_led.devid) ;/*获得分配号的次设备号*/
    }
    printk("dts_led major = %d , minor = %d \r\n",dts_led.major
    ,  dts_led.minor) ; 

    /*2.初始化cdev*/
    dts_led.cdev.owner = THIS_MODULE ;
    cdev_init(&dts_led.cdev ,  dts_led.devid , DTS_LED_CNT) ; 
    /*3.添加一个cdev*/
    cdev_add(dts_led.cdev , dts_led.devid , DTS_LED_CNT) ;

    /*4.创建类*/
    dts_led.class = class_create(THIS_MODULE , DTS_LED_NAME) ;
    if(IS_ERR(dts_led.class))
    {
        return PTR_ERR(dts_led.class) ; 
    }

    /*5.创建设备*/
    nwdchrled.device = device_create(dts_led.class ,NULL, 
        dts_led.devid , NULL , DTS_LED_NAME) ;
    if(IS_ERR(nwdchrled.device))
    {
        return PTR_ERR(dts_led.device) ; 
    }
}

/*
* @description  : 模块卸载函数
* @param-none   : none
* @return       : none
*/
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(&dts_led.cdev) ;  /*删除cdev*/
    unregister_chrdev_region(dts_led.devid , DTS_LED_CNT) ;/*注销设备号*/

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

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值