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

被折叠的 条评论
为什么被折叠?



