引子
首先看一個例子,也可參考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設備樹語法
