zoukankan      html  css  js  c++  java
  • 设备树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
    ");
                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
    ",
                    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)

  • 相关阅读:
    变量的生命周期【CPP】
    cpp静态成员和普通成员
    MAVEN学习(二) Myeclipse简单maven项目搭建
    redis安装和部署
    MAVEN学习(三) Maven构建多模块项目
    redis常用客户端命令
    本地计算机如何连接阿里云Mysql数据库
    MAVEN学习(一) nexus私服
    Linux之系统目录结构
    .net开发常用的第三方开发组件
  • 原文地址:https://www.cnblogs.com/embedded-linux/p/6702042.html
Copyright © 2011-2022 走看看