设备树API
引子
首先看一个例子,也可参考linux设备树语法中的gpio示例。该示例选自openwrt的gpio-button-hotblug驱动。
设备树code:
gpio-keys-polled { compatible = "gpio-keys-polled"; #address-cells = <1>; #size-cells = <0>; poll-interval = <20>; bat { label = "bat"; gpios = <&gpio0 9 1>; linux,code = <0x211>; }; reset { label = "reset"; gpios = <&gpio0 10 1>; linux,code = <0x198>; }; mode { label = "mode"; gpios = <&gpio0 14 1>; linux,code = <0x100>; linux,input-type = <5>; }; };
驱动相关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 = 0; node = dev->of_node; if (!node) return NULL; nbuttons = of_get_child_count(node); if (nbuttons == 0) 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 + 1); 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, 0, &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 = 5; } if (pdata->nbuttons == 0) { 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, 1);
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设备树语法