目录
前言
在 正点原子阿尔法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;
}