引子

首先看一个例子,也可参考linux设备树语法中的gpio示例。该示例选自openwrt的gpio-button-hotblug驱动。

设备树code:

    gpio-keys-polled {
compatible = "gpio-keys-polled";
#address-cells = <>;
#size-cells = <>;
poll-interval = <>;
bat {
label = "bat";
gpios = <&gpio0 >;
linux,code = <0x211>;
};
reset {
label = "reset";
gpios = <&gpio0 >;
linux,code = <0x198>;
};
mode {
label = "mode";
gpios = <&gpio0 >;
linux,code = <0x100>;
linux,input-type = <>;
};
};

驱动相关code:

#ifdef CONFIG_OF
static struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
struct device_node *node, *pp;
struct gpio_keys_platform_data *pdata;
struct gpio_keys_button *button;
int error;
int nbuttons;
int i = ; node = dev->of_node;
if (!node)
return NULL; nbuttons = of_get_child_count(node);
if (nbuttons == )
return NULL; pdata = devm_kzalloc(dev, sizeof(*pdata) + nbuttons * (sizeof *button),
GFP_KERNEL);
if (!pdata) {
error = -ENOMEM;
goto err_out;
} pdata->buttons = (struct gpio_keys_button *)(pdata + );
pdata->nbuttons = nbuttons; pdata->rep = !!of_get_property(node, "autorepeat", NULL);
of_property_read_u32(node, "poll-interval", &pdata->poll_interval); for_each_child_of_node(node, pp) {
enum of_gpio_flags flags; if (!of_find_property(pp, "gpios", NULL)) {
pdata->nbuttons--;
dev_warn(dev, "Found button without gpios\n");
continue;
} button = &pdata->buttons[i++]; button->gpio = of_get_gpio_flags(pp, , &flags);
button->active_low = flags & OF_GPIO_ACTIVE_LOW; if (of_property_read_u32(pp, "linux,code", &button->code)) {
dev_err(dev, "Button without keycode: 0x%x\n",
button->gpio);
error = -EINVAL;
goto err_out;
} button->desc = of_get_property(pp, "label", NULL); if (of_property_read_u32(pp, "linux,input-type", &button->type))
button->type = EV_KEY; button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL); if (of_property_read_u32(pp, "debounce-interval",
&button->debounce_interval))
button->debounce_interval = ;
} if (pdata->nbuttons == ) {
error = -EINVAL;
goto err_out;
} return pdata; err_out:
return ERR_PTR(error);
} static struct of_device_id gpio_keys_of_match[] = {
{ .compatible = "gpio-keys", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_keys_of_match); static struct of_device_id gpio_keys_polled_of_match[] = {
{ .compatible = "gpio-keys-polled", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match); #else static inline struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device *dev)
{
return NULL;
}
#endif
static struct platform_driver gpio_keys_driver = {
.probe = gpio_keys_probe,
.remove = gpio_keys_remove,
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_keys_of_match),
},
}; static struct platform_driver gpio_keys_polled_driver = {
.probe = gpio_keys_polled_probe,
.remove = gpio_keys_remove,
.driver = {
.name = "gpio-keys-polled",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_keys_polled_of_match),
},
};
static int __init gpio_button_init(void)
{
int ret; ret = platform_driver_register(&gpio_keys_driver);
if (ret)
return ret; ret = platform_driver_register(&gpio_keys_polled_driver);
if (ret)
platform_driver_unregister(&gpio_keys_driver); return ret;
} static void __exit gpio_button_exit(void)
{
platform_driver_unregister(&gpio_keys_driver);
platform_driver_unregister(&gpio_keys_polled_driver);
} module_init(gpio_button_init);
module_exit(gpio_button_exit);

该驱动同时注册了两种设备驱动:gpio_keys_driver和gpio_keys_polled_driver,前者采用中断方式检测按键状态,后者通过轮询方式检测案件状态。

OF API

设备树API通常以of_开头,实现代码位于drivers/of目录下,drivers/of目录下文件如下:

address.c  fdt.c      of_mtd.c  of_pci_irq.c       pdt.c
base.c irq.c of_net.c of_private.h platform.c
device.c of_mdio.c of_pci.c of_reserved_mem.c selftest.c

include/linux/目录下头文件:

of_address.h       of_gpio.h          of_mdio.h          of_pdt.h
of_device.h of.h of_mtd.h of_platform.h
of_dma.h of_iommu.h of_net.h of_reserved_mem.h
of_fdt.h of_irq.h of_pci.h

0. 数据结构

of.h为基础头文件,包含相关数据结构的定义。

typedef u32 phandle;
typedef u32 ihandle; struct property {
char *name;
int length;
void *value;
struct property *next;
unsigned long _flags;
unsigned int unique_id;
};
struct device_node {
const char *name;
const char *type;
phandle phandle;
const char *full_name; struct property *properties;
struct property *deadprops; /* removed properties */
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
struct device_node *next; /* next device of same type */
struct device_node *allnext; /* next in list of all nodes */
struct proc_dir_entry *pde; /* this node's proc directory */
struct kref kref;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
}; #define MAX_PHANDLE_ARGS 8
struct of_phandle_args {
struct device_node *np;
int args_count;
uint32_t args[MAX_PHANDLE_ARGS];
};
#define of_match_ptr(_ptr) (_ptr)

1. 寻找节点

int of_device_is_compatible(const struct device_node *device,const char *compat);

判断设备结点的compatible 属性是否包含compat指定的字符串。当一个驱动支持2个或多个设备的时候,这些不同.dts文件中设备的compatible 属性都会进入驱动 OF匹配表。因此驱动可以透过Bootloader传递给内核的Device Tree中的真正结点的compatible 属性以确定究竟是哪一种设备,从而根据不同的设备类型进行不同的处理。

struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible);

根据compatible属性,获得设备结点。遍历Device Tree中所有的设备结点,看看哪个结点的类型、compatible属性与本函数的输入参数匹配,大多数情况下,from、type为NULL,则表示遍历所有节点。

2. 读取属性

int of_property_read_u8_array(const struct device_node *np,

                     const char *propname, u8 *out_values, size_t sz);

int of_property_read_u16_array(const struct device_node *np,

                      const char *propname, u16 *out_values, size_t sz);

int of_property_read_u32_array(const struct device_node *np,

                      const char *propname, u32 *out_values, size_t sz);

int of_property_read_u64(const struct device_node *np, const char

*propname, u64 *out_value);

读取设备结点np的属性名为propname,类型为8、16、32、64位整型数组的属性。对于32位处理器来讲,最常用的是of_property_read_u32_array()。

of_property_read_u32_array(np, "arm,data-latency", data, ARRAY_SIZE(data)); 

of_property_read_u32_array(np, propname, out_value, ); 

int of_property_read_string(struct device_node *np, const char

*propname, const char **out_string);

int of_property_read_string_index(struct device_node *np, const char

    *propname, int index, const char **output);

前者读取字符串属性,后者读取字符串数组属性中的第index个字符串。

static inline bool of_property_read_bool(const struct device_node *np,

                                         const char *propname);

如果设备结点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在。

3. 内存映射

void __iomem *of_iomap(struct device_node *node, int index);

通过设备结点直接进行设备内存区间的 ioremap(),index是内存段的索引。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情 况,index为0。采用Device Tree后,大量的设备驱动通过of_iomap()进行映射,而不再通过传统的ioremap。

4. 解析中断

unsigned int irq_of_parse_and_map(struct device_node *dev, int index);

透过Device Tree或者设备的中断号,实际上是从.dts中的interrupts属性解析出中断号。若设备使用了多个中断,index指定中断的索引号。

5. 获取与节点对应的platform_device

struct platform_device *of_find_device_by_node(struct device_node *np);

在拿到device_node的情况下,反向获取对应的platform_device。

在已知platform_device的情况下,想获取device_node,如下:

static int imx_gpio_probe (struct platform_device *op)
{
struct device_node *dn = op->dev.of_node;
...
}

参考:

1. linux设备树语法

2. ARM Linux 3.x的设备树(Device Tree)

最新文章

  1. java.sql.SQLException: 关闭的 Resultset: next
  2. 转 :meta name的含义:&lt;META http-equiv=Content-Type content=&quot;text/html; charset=gb2312&quot;&gt;
  3. 读jQuery源码 - Deferred
  4. DuiLib——xml配置项
  5. LeetCode 370. Range Addition (范围加法)$
  6. 彻底搞懂js原型与原型链
  7. C# 7.1中default关键字的新用法
  8. Java学习笔记之——自动装箱与拆箱
  9. luogu 1966 火柴排队 离散化+逆序对
  10. GetCheckProxy
  11. JS 处理Json数据事例
  12. DShow + OpenGL播放视屏
  13. ExifInterface针对照片的使用
  14. MD5类库(hex_md5)
  15. labelimg
  16. 54个提高PHP程序运行效率的方法(转载)
  17. a标签在实际工作中的应用
  18. JQuery学习四(过滤选择器)
  19. EasyUI combobox 加载JSON数据《一》
  20. Android中自动跳转

热门文章

  1. 导出pem证书给服务端Push Notification使用
  2. 上传ipa文件时报错 Your account already has a valid iOS distribution certificate
  3. HashMap源码-Basic hash bin node
  4. Bootstrap标签Tabs
  5. 设置html属性为disabled时flask后台获取数据失败
  6. 【JUnit4.10源码分析】6.1 排序和过滤
  7. 转: Linux与JVM的内存关系分析
  8. JAVA加解密 -- 对称加密算法与非对称加密算法
  9. 阿里云ECS linux通过iptables 配置SNAT代理网关,实现局域网上网
  10. ssh之&lt;context:component-scan base-package=&quot;com.xx&quot; /&gt;