一、使用设备树驱动led
主要是为了熟悉设备树中获取自己想要的节点信息,在dtbs文件中自定义一个节点,然后使用设备树相关API获取该节点里面的led寄存器值,驱动在之前的led不变,变得只是获取设备树节点信息而已。
1.1、修改设备树
自己定义一个节点,关键节点是键值是reg,这个值里面代表需要操作的led寄存器地址的相关地址和值。
my_gpio{
compatible = "my_led";
gpio-name = "GPIO103";
#address-cells = <1>;
#size-cells = <1>;
reg = < 0x020C406C 0x04 //时钟
0x020E0068 0x04 //复用
0x020E02F4 0x04 //电器特性
0x0209C000 0x04 //数据寄存器
0x0209C004 0x04 >; //方向寄存器
};
1.2、查看嵌入式linux板子是否存在着自己自定义的节点
将其编译,重新启动linux,进入设备树相关目录,如下图,我们自定义的节点也显示在终端上,下一步,我们尝试将其reg的值获取出来。
1.3、读取自定义设备树节点键值
代码如下,其实一开始我这边调试使用五个数值,发现其实他是包含进去了
void dtbs_test(void)
{
u32 data_tmp[5];
unsigned char i = 0;
disofled_dev.node = of_find_node_by_path("/my_gpio");
if (!disofled_dev.node) {
printk("No node found\r\rn");
return;
}
printk("already get node\r\n");
if (of_property_read_u32_array(disofled_dev.node, "reg", data_tmp, 5) == 0) {
printk("reg:\r\n");
for (i = 0; i < 5; i++) {
printk("%#x ",data_tmp[i]);
}
printk("\r\n");
}
}
获取到的结果为:
读取代码最终为:
void dtbs_test(void)
{
u32 data_tmp[10];
unsigned char i = 0;
disofled_dev.node = of_find_node_by_path("/my_gpio");
if (!disofled_dev.node) {
printk("No node found\r\rn");
return;
}
printk("already get node\r\n");
if (of_property_read_u32_array(disofled_dev.node, "reg", data_tmp, 10) == 0) {
printk("reg:\r\n");
for (i = 0; i < 10; i++) {
printk("%#x ",data_tmp[i]);
}
printk("\r\n");
}
}
实际获取到的reg值为:
这也和我们一开始自定义的值一样,所以我们就获取到设备树中自定义的值,接下来就是整体驱动了。
1.4、最终代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#define DISOFLED_NAME "disofled"
#define LED_ON 0x00
#define LED_OFF 0x01
struct disofled{
int major; /* default to dynamic major */
dev_t devid;
struct cdev cdev; /* use 1 cdev for all pins */
struct class *class;
struct device *device;
struct device_node *node;
u32 reg_data[10];
};
struct disofled disofled_dev;
static void __iomem *ccm_ccgr1;
static void __iomem *sw_mux_gpio_io03;
static void __iomem *sw_pad_gpio_io03;
static void __iomem *gpio1_dr;
static void __iomem *gpio1_gdir;
//地址映射申请
void disofled_ioremap(void)
{
ccm_ccgr1 = ioremap(disofled_dev.reg_data[0],4);
sw_mux_gpio_io03 = ioremap(disofled_dev.reg_data[2],4);
sw_pad_gpio_io03 = ioremap(disofled_dev.reg_data[4],4);
gpio1_dr = ioremap(disofled_dev.reg_data[6],4);
gpio1_gdir = ioremap(disofled_dev.reg_data[8],4);
}
//地址映射释放
void disofled_iounmap(void)
{
iounmap(ccm_ccgr1);
iounmap(sw_mux_gpio_io03);
iounmap(sw_pad_gpio_io03);
iounmap(gpio1_dr);
iounmap(gpio1_gdir);
}
void led_switch(unsigned char stat)
{
unsigned int tmp = 0;
if(stat == LED_ON){
tmp = 0;
tmp &= ~(1<<3);
writel(tmp,gpio1_dr);
}
else if(stat == LED_OFF){
tmp = 0;
tmp |= (1<<3);
writel(tmp,gpio1_dr);
}
}
void disofled_led_init(void)
{
unsigned int val = 0;
//初始化GPIO的时钟
val = readl(ccm_ccgr1);
val &= ~(3 << 26);
val |= (3 << 26);
writel(val,ccm_ccgr1);
writel(0x05,sw_mux_gpio_io03);
writel(0x10B0,sw_pad_gpio_io03);
writel(0x08,gpio1_gdir);
led_switch(LED_ON);
}
ssize_t disofled_write(struct file *file, const char __user *data,size_t len, loff_t *ppos)
{
unsigned char write_data = 0;
if(copy_from_user(&write_data,data,1))
return -EFAULT;
if(write_data == 1){
led_switch(LED_ON);
}
else if(write_data == 0){
led_switch(LED_OFF);
}
return 0;
}
static const struct file_operations disofled_fileops = {
.owner = THIS_MODULE,
.write = disofled_write,
};
int dtbs_gpio_get(void)
{
unsigned char i = 0;
disofled_dev.node = of_find_node_by_path("/my_gpio");
if (!disofled_dev.node) {
printk("No node found\r\rn");
return -1;
}
printk("already get node\r\n");
if (of_property_read_u32_array(disofled_dev.node, "reg", disofled_dev.reg_data, 10) == 0) {
printk("reg:\r\n");
for (i = 0; i < 10; i++) {
printk("%#x ",disofled_dev.reg_data[i]);
}
printk("\r\n");
}
return 0;
}
static int __init disof_init(void)
{
int ret = 0;
disofled_dev.major = 0;
if(disofled_dev.major){
}
if (disofled_dev.major) {
disofled_dev.devid = MKDEV(disofled_dev.major, 0);
ret = register_chrdev_region(disofled_dev.devid, 1, DISOFLED_NAME);
} else {
ret = alloc_chrdev_region(&disofled_dev.devid, 0, 1, DISOFLED_NAME);
disofled_dev.major = MAJOR(disofled_dev.devid);
}
if(ret < 0){
printk("chrdev err\r\n");
goto CHRDEV_ERR;
}
printk("major:%#x\r\n",disofled_dev.major);
cdev_init(&disofled_dev.cdev, &disofled_fileops);
ret = cdev_add(&disofled_dev.cdev, disofled_dev.devid, 1);
if(ret < 0){
printk("cdev add err \r\n");
goto CDEV_ADD;
}
disofled_dev.class = class_create(THIS_MODULE, DISOFLED_NAME);
if (IS_ERR(disofled_dev.class)) {
ret = PTR_ERR(disofled_dev.class);
goto CDEV_ADD;
}
disofled_dev.device = device_create(disofled_dev.class, NULL, MKDEV(disofled_dev.major, 0), NULL,DISOFLED_NAME);
if (IS_ERR(disofled_dev.device)) {
ret = PTR_ERR(disofled_dev.device);
printk("device create err\r\n");
goto DEVICE_CREATE;
}
printk("init module ok\r\n");
ret = dtbs_gpio_get();
if(ret < 0){
printk("get node data err\r\n");
goto NODE_ERR;
}
disofled_ioremap();
disofled_led_init();
return 0;
NODE_ERR:
device_destroy(disofled_dev.class, disofled_dev.devid);
DEVICE_CREATE:
class_destroy(disofled_dev.class); //卸载类
CDEV_ADD:
unregister_chrdev_region(MKDEV(disofled_dev.major, 0), 1);
CHRDEV_ERR:
return ret;
}
static void __exit disofled_exit(void)
{
device_destroy(disofled_dev.class, disofled_dev.devid);
class_destroy(disofled_dev.class); //卸载类
cdev_del(&disofled_dev.cdev);
unregister_chrdev_region(MKDEV(disofled_dev.major, 0), 1);
led_switch(LED_OFF);
disofled_iounmap();
}
module_init(disof_init);
module_exit(disofled_exit);
MODULE_AUTHOR("gale");
MODULE_LICENSE("GPL");
经过测试,达到自己想要的结果:
二、个人理解总结
从创建一个设备树节点到获取和实现自己的驱动,这个过程很简单,但是也加深自己对于驱动和设备树的一些关系理解。