引子
首先看一個例子,也可參考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;
...
}