設備樹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設備樹語法

2. ARM Linux 3.x的設備樹(Device Tree)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM