实现一个驱动读取自己定义的设备树
1 设备树读写接口
头文件
#include <linux/of.h> //节点、属性基础接口
#include <linux/of_address.h> //reg物理地址
#include <linux/of_irq.h> //中断
#include <linux/of_gpio.h> //GPIO
#include <linux/of_clk.h> //时钟
#include <linux/of_pinctrl.h> //引脚配置
#include <linux/of_property.h>
查找/遍历设备节点
| of_find_node_by_path() | 按照设备树全路径查找节点 |
| of_find_node_by_name() | 按节点 name 查找 |
| of_find_node_by_type() | 按 device‑type 属性查找 |
| of_find_compatible_node() | 根据 compatible 匹配节点 |
| of_get_child_by_name() | 获取指定名字的子节点 |
| of_next_child_node() | 循环遍历所有子节点 |
| of_node_put() | 节点引用计数释放(查到节点必须调用) |
| of_device_is_available() | 判断节点 status = "okay" 是否启用 |
读取节点属性(最常用)
| of_property_read_bool(node, "xxx") | 布尔属性,存在即返回 true |
| of_property_read_u8() | 读取 uint8 属性 |
| of_property_read_u16() | 读取 uint16 |
| of_property_read_u32() | 读取单个 32 位整型 |
| of_property_read_u64() | 读取 64 位整型 |
| of_property_read_u32_array() | 读取 u32 数组 |
| of_property_read_string() | 读取字符串属性 |
| of_property_read_string_array() | 读取字符串数组 |
| of_property_match_string() | 匹配字符串列表索引 |
| of_find_property() | 获取 property 原始结构体 |
reg物理内存地址操作
| of_address_to_resource() | reg 转为 struct‑resource |
| of_iomap() | 物理地址映射成内核虚拟地址 |
| of_iounmap() | 解除地址映射 |
| of_address_in_resource() | 检查地址是否落在 reg 区间 |
中断资源
| irq_of_parse_and_map() | 解析 interrupts 属性、映射中断号 |
| of_irq_get() | 获取节点第 n 个中断 |
| of_irq_to_resource() | 中断转为 resource 结构体 |
phandle引用节点
| of_parse_phandle() | 解析 phandle,拿到被引用节点 |
| of_parse_phandle_with_args() | 解析带参数的 phandle(中断控制器常用) |
此外还有一些,就不全部列了。。
2 一个例子
实验是基于树莓派,主要是环境方便。
2.1 设备树
/*
* Custom Device Tree Overlay for Raspberry Pi DT experiment
* Defines a platform device node "tom,mydevice" with various property
* types that a driver can parse: string, u32 array, u64, GPIO, IRQ, clock.
*/
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2712";
fragment@0 {
target-path = "/";
__overlay__ {
tom_mydevice: tom,mydevice {
compatible = "tom,mydevice";
status = "okay";
/* string properties */
device-name = "tom-demo-device";
description = "A demo DT node for parsing practice";
/* u32 scalar */
version = <0x00010002>;
/* u32 array */
channel-ids = <1 2 3 4 8>;
/* u64 (as two 32-bit cells, big-endian) */
big-counter = <0x00000001 0x11223344>;
/* GPIO pin number (real BCM GPIO 17) */
led-gpio = <&gpio 17 0>;
/* GPIO handled via gpios prop */
gpios = <&gpio 22 0>, <&gpio 23 0>;
/* interrupts on a free IRQ line (10) */
interrupts = <10>;
interrupt-parent = <&gpio>;
/* clock reference: use the system's 108M clock */
clocks = <&clk_108MHz>;
clock-names = "sysclk";
/* child node for nested parsing demo */
child-config {
#address-cells = <1>;
#size-cells = <1>;
label = "child-1";
reg = <0x0 0x1000>;
};
};
};
};
__overrides__ {
mydevice-enabled = <&tom_mydevice>,"status";
};
};
2.2 驱动
// tom_device_driver.c
// Platform driver that parses properties from the "tom,mydevice"
// device tree node defined in tom-device.dts overlay.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Tom");
MODULE_DESCRIPTION("Parse custom device tree properties");
static int tom_device_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
const char *name, *desc;
u32 version;
u32 channels[8];
int nch, i;
u64 big_counter;
u32 counter_hi, counter_lo;
int gpio_led, ret;
struct gpio_desc *led_gpio;
struct clk *clk;
dev_info(dev, "=== Tom Device DT probe ===\\n");
if (!np) {
dev_err(dev, "No OF node!\\n");
return -ENODEV;
}
dev_info(dev, "OF node full name: %s\\n", np->full_name);
/* 1. String properties */
ret = of_property_read_string(np, "device-name", &name);
if (!ret)
dev_info(dev, "device-name = %s\\n", name);
else
dev_err(dev, "read device-name failed: %d\\n", ret);
ret = of_property_read_string(np, "description", &desc);
if (!ret)
dev_info(dev, "description = %s\\n", desc);
/* 2. u32 scalar */
ret = of_property_read_u32(np, "version", &version);
if (!ret)
dev_info(dev, "version = 0x%08x (%u)\\n", version, version);
/* 3. u32 array */
nch = of_property_read_variable_u32_array(np, "channel-ids", channels,
0, ARRAY_SIZE(channels));
if (nch > 0) {
dev_info(dev, "channel-ids count = %d:", nch);
for (i = 0; i < nch; i++)
pr_cont(" %u", channels[i]);
pr_cont("\\n");
} else {
dev_err(dev, "read channel-ids failed: %d\\n", nch);
}
/* 4. u64 property (two cells) */
ret = of_property_read_u32_index(np, "big-counter", 0, &counter_hi);
if (ret == 0)
ret = of_property_read_u32_index(np, "big-counter", 1, &counter_lo);
if (!ret) {
big_counter = ((u64)counter_hi << 32) | counter_lo;
dev_info(dev, "big-counter = 0x%016llx\\n", big_counter);
} else {
dev_err(dev, "read big-counter failed: %d\\n", ret);
}
/* 5. GPIO via of_get_named_gpio */
gpio_led = of_get_named_gpio(np, "led-gpio", 0);
if (gpio_is_valid(gpio_led))
dev_info(dev, "led-gpio = GPIO %d\\n", gpio_led);
else
dev_err(dev, "led-gpio invalid: %d\\n", gpio_led);
/* 6. GPIO via devm_gpiod_get_optional (gpios property) */
led_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_ASIS);
if (IS_ERR(led_gpio)) {
dev_err(dev, "gpiod get failed: %ld\\n", PTR_ERR(led_gpio));
} else if (led_gpio) {
int gpionum = desc_to_gpio(led_gpio);
dev_info(dev, "gpios[0] desc -> GPIO %d\\n", gpionum);
} else {
dev_info(dev, "no gpios property found\\n");
}
/* 7. Interrupt */
{
int irq = of_irq_get(np, 0);
if (irq >= 0)
dev_info(dev, "interrupts[0] -> irq = %d\\n", irq);
else
dev_info(dev, "no irq (%d)\\n", irq);
}
/* 8. Clock */
clk = devm_clk_get(dev, "sysclk");
if (IS_ERR(clk)) {
dev_info(dev, "clock sysclk not available: %ld\\n", PTR_ERR(clk));
} else {
unsigned long rate = clk_get_rate(clk);
dev_info(dev, "clock sysclk rate = %lu Hz\\n", rate);
}
/* 9. Child node */
{
struct device_node *child;
child = of_get_child_by_name(np, "child-config");
if (child) {
const char *clabel;
u32 caddr;
if (!of_property_read_string(child, "label", &clabel))
dev_info(dev, "child-config label = %s\\n", clabel);
if (!of_property_read_u32_index(child, "reg", 1, &caddr))
dev_info(dev, "child-config reg[1] = 0x%x\\n", caddr);
of_node_put(child);
} else {
dev_info(dev, "no child-config node\\n");
}
}
/* 10. Show full raw property dump via /sys nodes */
dev_info(dev, "=== probe complete ===\\n");
return 0;
}
static void tom_device_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "Tom device removed\\n");
}
static const struct of_device_id tom_device_of_match[] = {
{ .compatible = "tom,mydevice" },
{ }
};
MODULE_DEVICE_TABLE(of, tom_device_of_match);
static struct platform_driver tom_device_driver = {
.probe = tom_device_probe,
.remove = tom_device_remove,
.driver = {
.name = "tom_device",
.of_match_table = tom_device_of_match,
},
};
module_platform_driver(tom_device_driver);
2.3 运行
运行方法:
编译:dtc -@ -I dts -O dtb -o tom-device.dtbo tom-device.dts(-@ 保留符号)。
之后编译驱动并insmod。
运行结果:
tom_device tom,mydevice: device-name = tom-demo-device
tom_device tom,mydevice: description = A demo DT node for parsing practice
tom_device tom,mydevice: version = 0x00010002 (65538)
tom_device tom,mydevice: channel-ids count = 5:
tom_device tom,mydevice: big-counter = 0x0000000111223344
tom_device tom,mydevice: led-gpio = GPIO 588
tom_device tom,mydevice: gpios[0] desc -> GPIO 593
tom_device tom,mydevice: no irq (-75)
tom_device tom,mydevice: clock sysclk rate = 108000000 Hz
tom_device tom,mydevice: child-config label = child-1
tom_device tom,mydevice: child-config reg[1] = 0x1000

网硕互联帮助中心



评论前必须登录!
注册