上篇文章里,直接define使用LED GPIO的寄存器物理地址用于初始化和读写。如果用设备树,则驱动程序从设备树里去获取LED GPIO的寄存器物理地址。那么就要在设备树文件中指出这个要使用的的寄存器物理地址及其范围(范围表明多个寄存器)。
1、 在linux-4.19-rc3/arch/arm/boot/dts/文件夹下增加jz2440.dts
/dts-v1/;
/ {
model = "SMDK24440";
compatible = "samsung,smdk2440";
#address-cells = <1>;
#size-cells = <1>;
memory@30000000 {
device_type = "memory";
reg = <0x30000000 0x4000000>;
};
leds {
compatible = "jz2440_led";
status = "okay";
reg = <0x56000050 0x04>,<0x56000054 0x04>;
};
};
理论就不多说了,有兴趣可以去看下相关文章。看看下面注意事项就行,其它暂无关紧要。
注意:(1)根节点“/”必须要有compatible属性,代替不使用设备树时的machid,用于内核启动后匹配这块板子或者SoC;如不匹配,kernel无法启动。
(2)leds子节点compatible属性,用于内核启动后匹配leds设备的驱动,获取这里的reg资源。
(3)根节点“/”里面的xxx{}都是子节点;根节点“/”里面的#address-cells = <1>,#size-cells = <1>表明leds子节点的reg属性值<0x56000050 0x04>,0x56000050是起始地址,0x04是地址范围。
2、字符设备驱动
static void __iomem *gpfcon;
static void __iomem *gpfdat;
static int leds_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t leds_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
unsigned char readbuf[100];
copy_to_user(buf, readbuf, cnt);
return 0;
}
static ssize_t leds_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
unsigned char writebuf[100];
copy_from_user(writebuf, buf, cnt);
return 0;
}
static int leds_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations leds_fops = {
.owner = THIS_MODULE,
.open = leds_open,
.read = leds_read,
.write = leds_write,
.release = leds_release,
};
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *nd;
static int __init led_init(void)
{
nd = of_find_node_by_path("/leds");
gpfcon = of_iomap(nd, 0);
gpfdat = of_iomap(nd, 1);
val = readl(gpfcon);
val &= ~(3<<(4*2) | 3<<(5*2) | 3<<(6*2));
val |= 1<<(4*2)| 1<<(5*2)| 1<<(6*2);
writel(val, gpfcon);
alloc_chrdev_region(&devid, 0, 1, "leds");
cdev.owner = THIS_MODULE;
cdev_init(&cdev, &leds_fops);
cdev_add(&cdev, devid, 1);
class = class_create(THIS_MODULE, "leds");
device = device_create(class, NULL, devid, NULL, "leds");
return 0;
}
static void __exit led_exit(void)
{
iounmap(gpfcon);
iounmap(gpfdat);
cdev_del(&cdev);
unregister_chrdev_region(devid, 1);
device_destroy(class, devid);
class_destroy(class);
return 0;
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
和不使用设备树相比,就是改一下nd = of_find_node_by_path("/leds");gpfcon = of_iomap(nd, 0);gpfdat = of_iomap(nd, 1);
3、有个坑,如果设备树leds子节点reg写法为:reg = <0x56000050 0x04 0x56000054 0x04>;
则驱动函数要使用
of_property_read_u32_array(nd, "reg", regdata, 4);
gpfcon = ioremap(regdata[0], regdata[1]);
gpfdat = iordatemap(regdata[2], regdata[3]);
4、在linux-4.19-rc3目录下,make dtbs不成功的话,直接指明make jz2440.dtb应该能行,呵呵。然后重新烧录:
tftp 0x30000000 jz2440.dtb
nand erase.part device_tree
nand write.jffs2 0x30000000 device_tree $filesize