顯示具有 Linux - kernel - DT 標籤的文章。 顯示所有文章
顯示具有 Linux - kernel - DT 標籤的文章。 顯示所有文章

2020年2月29日 星期六

Linux Kernel(17.2)- Common Device Tree API


properties的value可以是empty或是以下資料型態:
  • Text strings (null terminated) are represented with double quotes: string-property = "a string";
  • 'Cells' are 32 bit unsigned integers delimited by angle brackets: cell-property = <0xbeef 123 0xabcd1234>;
  • Binary data is delimited with square brackets: binary-property = [0x01 0x23 0x45 0x67];
  • Data of differing representations can be concatenated together using a comma: mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
  • Commas are also used to create lists of strings: string-list = "red fish", "blue fish";

這章節介紹幾個API,用於存取這些常見的資料型態,會以下的DTS的內容進行parse
/ {
  node1 {
    compatible = "brook,dts-test";
    a-string-property = "A string";
    a-string-list-property = "first string", "second string";
    // hex is implied in byte arrays. no '0x' prefix is required
    a-byte-data-property = [01 23 34 56];
    child-node1 {
      first-child-property;
      second-child-property = <1>;
      a-string-property = "Hello, world";
    };
    child-node2 {
    };
  };
  
  node2 {
    compatible = "brook,dts-test";
    an-empty-property;
    a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
    child-node@1 {
    };
    child-node@2 {
    };
  };
};


  • of_match_device(): Sanity check for device that device is matching with the node
  • of_property_read_string(): To read string property
  • of_property_count_strings(): Find and return the number of strings from a multiple strings property.
  • of_property_read_string_index(): Read a string with index from string-list property.
  • of_find_property(): Find and return the property pointer by giving named
  • of_property_for_each_u32(): A macro to iterate the property to get all values
  • of_property_read_bool(): Returns true if the property exist false otherwise
  • of_find_node_by_name(): Find a node by its "name" property
  • for_each_child_of_node(): Traverse all child device node for current device node

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>

static struct of_device_id brook_dt_id[] = {
    {
     .compatible = "brook,dts-test",
    },
    {}
};

MODULE_DEVICE_TABLE(of, brook_dt_id);

static int brook_dts_probe(struct platform_device *pdev)
{
    const struct of_device_id *of_id;
    struct device_node *node, *child;
    struct property *pp;
    int i, ret, of_cnt, len;
    const char *str;
    const __be32 *cur;
    u32 val;

    // sanity check of_match_device() allows to get the matching entry
    of_id = of_match_device(brook_dt_id, &pdev->dev);
    if (!of_id) {
        pr_err("%s: of_id is NULL\n", __func__);
        return -1;
    }

    node = pdev->dev.of_node;
    // To read string property
    ret = of_property_read_string(node, "a-string-property", &str);
    if (!ret) {
        printk("a-string-property: %s\n", str);
    } else {
        printk("no a-string-property\n");
    }

    // Find and return the number of strings from a multiple strings property.
    of_cnt = of_property_count_strings(node, "a-string-list-property");
    if (of_cnt) {
        for (i = 0; i < of_cnt; i++) {
            ret = of_property_read_string_index(node, "a-string-list-property", i, &str);
            if (!ret) {
                printk("a-string-list-property[%d]: %s\n", i, str);
            }
        }
    }

    // to find aproperty named if arg2
    pp = of_find_property(node, "a-byte-data-property", &len);
    if (pp) {
        u8 *u8p = pp->value;
        printk("a-byte-data-property: len:%d, pp->len:%d", len, pp->length);
        for (i = 0; i < len; i++) {
            printk("a-byte-data-property:[%d] = %02x\n", i, u8p[i]);
        }
    }

    // to find aproperty named if arg2
    of_property_for_each_u32(node, "a-cell-property", pp, cur, val) {
        printk("a-cell-property: %04x/%04x\n", *cur, val);
    }

    // Returns true if the property exist false otherwise
    if (of_property_read_bool(node, "an-empty-property")) {
        printk("has an-empty-property\n");
    } else {
        printk("no an-empty-property\n");
    }

    // Traverse all child device node for current device node
    for_each_child_of_node(node, child) {
        printk("child name: %s\n", child->name);
    }

    // Find a node by its "name" property
    child = of_find_node_by_name(node, "child-node1");
    if (child) {
        printk("%s has a child %s\n", node->name, child->name);
    }

    return 0;
}

static int brook_dts_remove(struct platform_device *pdev)
{
    return 0;
}

static struct platform_driver brook_dts_platform_driver = {
    .probe = brook_dts_probe,
    .remove = brook_dts_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "brook-dts",
        .of_match_table = brook_dt_id,
    },
};


static int __init brook_init_module(void)
{
    return platform_driver_register(&brook_dts_platform_driver);
}

static void __exit brook_exit_module(void)
{
    platform_driver_unregister(&brook_dts_platform_driver);
}

module_init(brook_init_module);
module_exit(brook_exit_module);
MODULE_LICENSE("GPL");

執行結果如下:
/ # insmod dts.ko
a-string-property: A string
a-string-list-property[0]: first string
a-string-list-property[1]: second string
a-byte-data-property: len:4, pp->len:4
a-byte-data-property:[0] = 01
a-byte-data-property:[1] = 23
a-byte-data-property:[2] = 34
a-byte-data-property:[3] = 56
no an-empty-property
child name: child-node1
child name: child-node2
node1 has a child child-node1
no a-string-property
a-cell-property: 1000000/0001
a-cell-property: 2000000/0002
a-cell-property: 3000000/0003
a-cell-property: 4000000/0004
has an-empty-property
child name: child-node
child name: child-node


以下摘錄http://www.myexception.cn/linux-unix/1910031.html, linux下devicetree中常用的of函數
linux下devicetree中常用的of函数
从device_node中获取信息:

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);

从设备结点np中读取属性名为propname,类型为8、16、32、位整型数组的属性值,并放入out_values,sz指明了要读取的个数。


static inline int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value) 

static inline int of_property_read_u16(const struct device_node *np,const char *propname,u8 *out_value) 

static inline int of_property_read_u32(const struct device_node *np,const char *propname,u8 *out_value) 

从设备结点np中读取属性名为propname,类型为8、16、32位的属性值,并放入out_values。实际上这里调用的就是sz为1的XXX_array函数。

 

int of_property_read_u32_index(const struct device_node *np,const char*propname,u32 index, u32 *out_value)

从设备结点np中读取属性名为propname的属性值中第index个u32数值给out_value

 

int of_property_read_u64(conststruct device_node *np, const char *propname,u64 *out_value)

从设备结点np中读取属性名为propname,类型为64位的属性值,并放入out_values

 

int of_property_read_string(struct device_node *np, const char *propname,const char**out_string)

从设备结点np中读取属性名为propname的字符串型属性值

 

int of_property_read_string_index(struct device_node *np, const char *propname,intindex, const char **output)

从设备结点np中读取属性名为propname的字符串型属性值数组中的第index个字符串

 

int of_property_count_strings(struct device_node *np, const char *propname)

从设备结点np中读取属性名为propname的字符串型属性值的个数

 

unsigned int irq_of_parse_and_map(struct device_node *dev, int index)

从设备节点dev中读取第index个irq号

 

int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)

从设备节点dev中读取第index个irq号,并填充一个irq资源结构体

 

int of_irq_count(struct device_node *dev)

获取设备节点dev的irq个数


static inline bool of_property_read_bool(const struct device_node *np,const char *propname);

如果设备结点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在。

 

struct property* of_find_property(const struct device_node *np,const char *name,int *lenp)

根据name参数,在指定的设备结点np中查找匹配的property,并返回这个property

 

const void * of_get_property(const struct device_node *np, const char *name,int *lenp)

根据name参数,在指定的设备结点np中查找匹配的property,并返回这个property的属性值


struct device_node* of_get_parent(const struct device_node *node)

获得node节点的父节点的device node


int of_device_is_compatible(const struct device_node *device,const char *compat);

判断设备结点device的compatible属性是否包含compat指定的字符串


从of_allnodes中查找信息:

struct device_node* of_find_node_by_path(const char *path)
根据路径参数,在全局链表of_allnodes中,查找匹配的device_node


struct device_node* of_find_node_by_name(struct device_node *from,const char *name)
则根据name在全局链表of_allnodes中查找匹配的device_node,若from=NULL表示从头开始查找


struct device_node* of_find_node_by_type(struct device_node *from,const char *type)

根据设备类型在全局链表of_allnodes中查找匹配的device_node


struct device_node * of_find_compatible_node(struct device_node *from, const char*type, const char,*compatible);

根据compatible的属性值在全局链表of_allnodes中查找匹配的device_node,大多数情况下,from、type为NULL。

 

struct device_node* of_find_node_with_property(struct device_node *from,const char *prop_name)

根据节点属性的name在全局链表of_allnodes中查找匹配的device_node

 

struct device_node* of_find_node_by_phandle(phandle handle)

根据phandle在全局链表of_allnodes中查找匹配的device_node

 

杂:

void __iomem* of_iomap(struct device_node *node, int index);

通过设备结点直接进行设备内存区间的 ioremap(),index是内存段的索引。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况,index为0

 

unsigned long __init of_get_flat_dt_root(void)

用来查找在dtb中的根节点,好像返回的都是0


int of_alias_get_id(struct device_node *np, const char *stem)

获取节点np对应的aliasid号

 

struct device_node* of_node_get(struct device_node *node)

void of_node_put(struct device_node *node)

device node计数增加/减少


const struct of_device_id* of_match_node(const struct of_device_id *matches,const struct device_node*node)

将matches数组中of_device_id结构的name和type与device node的compatible和type匹配,返回匹配度最高的of_device_id结构


platform_device和resource相关:

int of_address_to_resource(struct device_node *dev, int index,struct resource *r)

根据设备节点dev的reg属性值,填充资源结构体r。Index参数指明了使用reg属性中第几个属性值,一般设置为0,表示第一个。


struct platform_device* of_device_alloc(struct device_node *np,const char *bus_id,struct device *parent)

根据device node,bus_id以及父节点创建该设备的platform_device结构,同时会初始化它的resource成员。

 

int of_platform_bus_probe(struct device_node *root,const struct of_device_id *matches,struct device *parent)

遍历of_allnodes中的节点挂接到of_platform_bus_type总线上,由于此时of_platform_bus_type总线上还没有驱动,所以此时不进行匹配

 

int of_platform_populate(struct device_node *root,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent)

遍历of_allnodes中的所有节点,生成并初始化所以节点的platform_device结构



struct platform_device* of_find_device_by_node(struct device_node *np)

根据device_node查找返回该设备对应的platform_device结构

    參考資料:
  • http://www.myexception.cn/linux-unix/1910031.html, linux下devicetree中常用的of函數
  • https://saurabhsengarblog.wordpress.com/2015/11/28/device-tree-tutorial-arm/, Device Tree Tutorial (ARM)
  • https://www.cnblogs.com/xiaojiang1025/p/6368260.html, Linux内核 设备树操作常用API





2019年8月18日 星期日

Linux Kernel(17.2)- Device Tree and Platform Device


如同Linux Kernel(15.3)- The Linux usage model for device tree data所描述,init_machine()透過呼叫of_platform_populate()建構platform device。
kernel v3.5
|-->> msm8x60_dt_init(void) @arch/arm/mach-msm/board-msm8x60.c
  |--> of_platform_populate() @drivers/of/platform.c
    |--> of_platform_bus_create() @drivers/of/platform.c
      /* Make sure it has a compatible property */ 
          那些具有compatible的node都會被轉成platform device
      if (strict && (!of_get_property(bus, "compatible", NULL))) {
        pr_debug("%s() - skipping %s, no compatible prop\n",
                 __func__, bus->full_name);
        return 0;
      }

      ...
      for_each_child_of_node(bus, child) {
        pr_debug("create child: %s\n", child->full_name);
        rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
        if (rc) {
          of_node_put(child);
          break;
        }
      }


接著會在註冊driver時, 呼叫of_driver_match_device()進行match
kernel v3.5
|--> platform_driver_register() @drivers/base/platform.c 
  |--> driver_register() @drivers/base/driver.c
    |--> bus_add_driver() @drivers/base/bus.c /* 建立sysfs file node 與 attr */  
      |--> driver_attach() @drivers/base/dd.c 
        |--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) @bus.c  
          |--> __driver_attach @drivers/base/dd.c
            |--> driver_match_device(drv, dev) @base.h
              |--> platform_match(); @drivers/base/platform.c
                if (of_driver_match_device(dev, drv))
                  return 1;

          |--> __driver_attach @drivers/base/dd.c
            if (!dev->driver)
              driver_probe_device(drv, dev);
            |--> driver_probe_device @drivers/base/dd.c
              ret = really_probe(dev, drv);
              |--> really_probe @drivers/base/dd.c
                if (dev->bus->probe) {
                  ret = dev->bus->probe(dev);

                if (ret)
                  goto probe_failed;
                } else if (drv->probe) {
                  ret = drv->probe(dev);
                  if (ret)
                    goto probe_failed;
                }

----- @include/linux/of_device.h -----
of_driver_match_device(struct device *dev, const struct device_driver *drv) 
{
    return of_match_device(drv->of_match_table, dev) != NULL;
}

----- @drivers/of/device.c -----
const struct of_device_id *of_match_device(const struct of_device_id *matches,
        const struct device *dev)
{
    if ((!matches) || (!dev->of_node))
        return NULL;
    return of_match_node(matches, dev->of_node);
}

----- @drivers/of/base.c -----
const struct of_device_id *of_match_node(const struct of_device_id *matches,
      const struct device_node *node)
{
    if (!matches)
        return NULL;

    while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
        int match = 1;
        if (matches->name[0])
            match &= node->name && !strcmp(matches->name, node->name);
        if (matches->type[0])
            match &= node->type && !strcmp(matches->type, node->type);
        if (matches->compatible[0])
            match &= of_device_is_compatible(node, matches->compatible);
        if (match)
            return matches;
        matches++;
    }
    return NULL;
}


修改一下DTS驗證一下platform device
/ {
    node1 {
        compatible = "brook,dts1";
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
};


/ # ls /sys/bus/platform/devices
10000000.sysreg
10002000.i2c
...
node1
...

/ # cd /sys/bus/platform/devices/node1/
/sys/devices/platform/node1 # ls
driver_override  of_node          subsystem
modalias         power            uevent
/sys/devices/platform/node1 # ls of_node/
a-byte-data-property    child-node1             name
a-string-list-property  child-node2
a-string-property       compatible
/sys/devices/platform/node1 # ftpget 192.168.1.1 /tmp/brook_modules.ko /home/brook/my_driver/brook_modules.ko
/sys/devices/platform/node1 # insmod /tmp/brook_modules.ko
brook_modules: loading out-of-tree module taints kernel.
brook_init(#55)
brook_probe(#21)


基本上與傳統的platform device driver的差異是:
  1. device由DTS的compatible產生,無須呼叫platform_device_register()註冊device
  2. 需在platform_driver.driver.of_match_table中掛上of_device_id[],裡面的compatible會與DTS的compatible進行比對
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include <linux/of.h>
#include <linux/of_device.h>

MODULE_AUTHOR("Brook");
MODULE_DESCRIPTION("Kernel module for demo");
MODULE_LICENSE("GPL");

#define DEVNAME "brook"

static struct platform_device brook_device = {
    .name = DEVNAME,
};

static int brook_probe(struct platform_device *pdev)
{
    pr_info("%s(#%d)\n", __func__, __LINE__);
    return 0;
}

static int brook_remove(struct platform_device *pdev)
{
    pr_info("%s(#%d)\n", __func__, __LINE__);
    return 0;
}

static struct of_device_id brook_dt_ids[] = {
    {
        .compatible = "brook,dts1",
    }, {
        .compatible = "brook,dts2",
    }, {
    }
};

MODULE_DEVICE_TABLE(of, brook_dt_ids);

static struct platform_driver brook_driver = {
    .driver = {
        .name = DEVNAME,
        .owner = THIS_MODULE,
        .of_match_table = brook_dt_ids,
    },
    .probe = brook_probe,
    .remove = brook_remove,
};

static int __init brook_init(void)
{
    int ret;
    pr_info("%s(#%d)\n", __func__, __LINE__);

    ret = platform_driver_register(&brook_driver);
    if (ret) {
        dev_err(&(brook_device.dev),
                "%s(#%d): platform_driver_register fail(%d)\n", __func__,
                __LINE__, ret);
    }
    return ret;
}
module_init(brook_init);

static void __exit brook_exit(void)
{
    dev_info(&(brook_device.dev), "%s(#%d)\n", __func__, __LINE__);
    platform_driver_unregister(&brook_driver);
}
module_exit(brook_exit);


    參考資料:
  • http://wiki.dreamrunner.org/public_html/Embedded-System/Linux-Device-Tree.html, Linux Device tree
  • Linux Kernel(15.3)- The Linux usage model for device tree data
  • http://wiki.100ask.org/%E7%AC%AC%E4%B8%89%E8%AF%BE:%E5%86%85%E6%A0%B8%E5%AF%B9%E8%AE%BE%E5%A4%87%E6%A0%91%E7%9A%84%E5%A4%84%E7%90%86, 内核对设备树的处理




Linux Kernel(17.1)- Basic Device Tree syntax


這篇會介紹一下Device Tree的基本資料型態,並透過觀察/sys/firmware/devicetree/讓你更貼近一下DT的資料結構,下面範例是在vexpress-v2p-ca9.dts中include "brook.dtsi",然後在"brook.dtsi"撰寫DT的基本語法。DT的每個node,可包含零個以上的properties或child node。
brook@vista:~/qemu/linux-arm$ vim arch/arm/boot/dts/vexpress-v2p-ca9.dts
 #include "vexpress-v2m.dtsi"
 #include "brook.dtsi" /* add this line */

brook@vista:~/qemu/linux-arm$ vim arch/arm/boot/dts/brook.dtsi 

/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
        // [@], 
        //  is a simple ascii string and can be up to 31 characters in length.
        child-node@1 {
        };
        child-node@2 {
        };
    };
};


brook@vista:~/qemu/linux-arm$ export ARCH=arm
brook@vista:~/qemu/linux-arm$ export CROSS_COMPILE=arm-linux-gnueabihf-
brook@vista:~/qemu/linux-arm$ export PATH=/opt/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin:$PATH
brook@vista:~/qemu/linux-arm$ make dtbs


properties的value可以是empty或是以下資料型態:
  • Text strings (null terminated) are represented with double quotes: string-property = "a string";
  • 'Cells' are 32 bit unsigned integers delimited by angle brackets: cell-property = <0xbeef 123 0xabcd1234>;
  • Binary data is delimited with square brackets: binary-property = [0x01 0x23 0x45 0x67];
  • Data of differing representations can be concatenated together using a comma: mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
  • Commas are also used to create lists of strings: string-list = "red fish", "blue fish";


brook@vista:~/qemu/linux-arm# cd ..
brook@vista:~/qemu$ qemu-system-arm -M vexpress-a9 -m 512M -kernel ./linux-arm/arch/arm/boot/zImage -dtb ./linux-arm/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -initrd ./initrd-arm.img -nographic -append "console=ttyAMA0"

----- boot to VM -----

Please press Enter to activate this console.
/ # ls /sys/firmware/
devicetree  fdt
/ # ls /sys/firmware/devicetree/
base
/ # ls /sys/firmware/devicetree/base/ #根目錄(/sys/firmware/devicetree/base/) 多了node1與node2
/ # ls /sys/firmware/devicetree/base/
#address-cells                 model
#size-cells                    name
aliases                        node1
arm,hbi                        node2
arm,vexpress,site              pmu
cache-controller@1e00a000      reserved-memory
chosen                         scu@1e000000
clcd@10020000                  smb@4000000
compatible                     timer@100e4000
cpus                           timer@1e000600
dcc                            virtio_mmio@10013000
hsb@e0000000                   virtio_mmio@10013200
interrupt-controller@1e001000  virtio_mmio@10013400
interrupt-parent               virtio_mmio@10013600
memory-controller@100e0000     watchdog@100e5000
memory-controller@100e1000     watchdog@1e000620
memory@60000000

/ # find /sys/firmware/devicetree/base/node1
/sys/firmware/devicetree/base/node1
/sys/firmware/devicetree/base/node1/child-node2
/sys/firmware/devicetree/base/node1/child-node2/name
/sys/firmware/devicetree/base/node1/a-string-property
/sys/firmware/devicetree/base/node1/a-string-list-property
/sys/firmware/devicetree/base/node1/a-byte-data-property
/sys/firmware/devicetree/base/node1/name
/sys/firmware/devicetree/base/node1/child-node1
/sys/firmware/devicetree/base/node1/child-node1/first-child-property
/sys/firmware/devicetree/base/node1/child-node1/second-child-property
/sys/firmware/devicetree/base/node1/child-node1/a-string-property
/sys/firmware/devicetree/base/node1/child-node1/name


/ # find /sys/firmware/devicetree/base/node2
/sys/firmware/devicetree/base/node2
/sys/firmware/devicetree/base/node2/child-node@1
/sys/firmware/devicetree/base/node2/child-node@1/name
/sys/firmware/devicetree/base/node2/child-node@2
/sys/firmware/devicetree/base/node2/child-node@2/name
/sys/firmware/devicetree/base/node2/a-cell-property
/sys/firmware/devicetree/base/node2/name
/sys/firmware/devicetree/base/node2/an-empty-property


/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-string-property
41 20 73 74 72 69 6E 67  A string
00

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-string-list-property
66 69 72 73 74 20 73 74  first st
72 69 6E 67 00 73 65 63  ringsec
6F 6E 64 20 73 74 72 69  ond stri
6E 67 00                 ng
strings lists中的element是"0"分隔

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/a-byte-data-property
01 23 34 56              #4V
byte-data如其名,每個值大小就是一個byte

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/name
6E 6F 64 65 31 00        node1

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/first-child-property
empty

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/second-child-property
00 00 00 01

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node1/child-node1/a-string-property
48 65 6C 6C 6F 2C 20 77  Hello, w
6F 72 6C 64 00           orld

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/a-cell-property
00 00 00 01 00 00 00 02
00 00 00 03 00 00 00 04
cell每個value大小為32byte

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/child-node@1/name
63 68 69 6C 64 2D 6E 6F  child-no
64 65 00                 de

/ # hexdump -e '8/1 "%02X ""\t"" "' -e '8/1 "%c""\n"' /sys/firmware/devicetree/base/node2/child-node@2/name
63 68 69 6C 64 2D 6E 6F  child-no
64 65 00                 de


    參考資料:
  • https://elinux.org/Device_Tree_Usage, Device Tree Usage
  • https://elinux.org/images/f/f9/Petazzoni-device-tree-dummies_0.pdf, device tree dumies
  • https://blog.csdn.net/RadianceBlau/article/details/70800076, Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)




Linux Kernel(17)- Device Tree


Device tree是一個用來描述硬體的資料結構,包含了CPU、Memory、bus與周邊,DT改變了原本kernel的hard-code(table),改由bootloader傳入DTB(Device Tree Blob)給kernel。這個由SPARC-based開始的Open Firmware project於是慢慢地推廣到Arm, x86, MicroBlaze, PowerPC等平台。
這個有趣的故事可以讀一下Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)

摘錄故事部分如下:
在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,
比如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的
platform_data,这些板级细节代码对内核来讲只不过是垃圾代码。而采用Device Tree后,许多硬件的细节
可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。
每次正式的linux kernel release之后都会有两周的merge window,在这个窗口期间,kernel各个部分的
维护者都会提交各自的patch,将自己测试稳定的代码请求并入kernel main line。每到这个时候,Linus
就会比较繁忙,他需要从各个内核维护者的分支上取得最新代码并merge到自己的kernel source tree中。
Tony Lindgren,内核OMAP development tree的维护者,发送了一个邮件给Linus,请求提交OMAP平台
代码修改,并给出了一些细节描述:
       1)简单介绍本次改动
       2)关于如何解决merge conficts。有些git mergetool就可以处理,不能处理的,
         给出了详细介绍和解决方案。
一切都很平常,也给出了足够的信息,然而,正是这个pull request引发了一场针对ARM linux的内核代码
的争论。我相信Linus一定是对ARM相关的代码早就不爽了,ARM的merge工作量较大倒在其次,主要是他认为
ARM很多的代码都是垃圾,代码里面有若干愚蠢的table,而多个人在维护这个table,从而导致了冲突。
因此,在处理完OMAP的pull request之后(Linus并非针对OMAP平台,只是Tony Lindgren撞在枪口上了)
,他发出了怒吼:
     Gaah.Guys, this whole ARM thing is a f*cking pain in the ass.
 
之后经过一些讨论,对ARM平台的相关code做出如下相关规范调整,这个也正是引入DTS的原因。
1、ARM的核心代码仍然保存在arch/arm目录下
2、ARM SoC core architecture code保存在arch/arm目录下
3、ARM SOC的周边外设模块的驱动保存在drivers目录下
4、ARM SOC的特定代码在arch/arm/mach-xxx目录下
5、ARM SOC board specific的代码被移除,由DeviceTree机制来负责传递硬件拓扑和硬件资源信息。
本质上,Device Tree改变了原来用hardcode方式将HW 配置信息嵌入到内核代码的方法,改用bootloader
传递一个DB的形式。
 ———————————————— 
版权声明:本文为CSDN博主「RadianceBlau」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/RadianceBlau/article/details/70800076

bootloader傳入DTB(Device Tree Blob),而DTB是由Device Tree Source透過DTC編成的binary data,關係概略如下:

DTC相關用法可以參考dtc - Device Tree Compiler
DTS語法會在後面章節介紹,基本上,DTS只描述那些無法動態偵測的設備

    參考資料
  • https://blog.csdn.net/RadianceBlau/article/details/70800076, Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇)
  • https://en.wikipedia.org/wiki/Device_tree, Device tree
  • dtc - Device Tree Compiler




2018年1月28日 星期日

dtc - Device Tree Compiler


從dtc manpage可以簡略瞭解一下DTC(Device Tree Compiler)功能, 就是將device-tree的format轉成另一種format, 其input格式有三種,
  1. dts - device tree source text
  2. dtb - device tree blob
  3. fs - /proc/device-tree style directory

而output格式也有三種,
  1. dts - device tree source text
  2. dtb - device tree blob
  3. asm - assembler source

而dtc支援的phandle可支援三種
  1. legacy - "linux,phandle" properties only
  2. epapr - "phandle" properties only
  3. both - Both "linux,phandle" and "phandle" properties

根據Device Tree Mysteries,
what is the reason for having a phandle? 
It is really just a hack to get around the fact that device tree does 
not have a pointer data type. 
It is a way to reference 
"that node over there that is related to this node for some reason". 
簡單來說, phandler就是pointer data type, 用個例子說明
pic@10000000 {
    phandle = < 1 >;
};

A phandle value of 1 is defined. 
Another device node could reference the pic node with a
phandle value of 1:

uart@20000000 {
    interrupt-parent = > 1 >;
};
"phandle = < 1 >"的"1"是一個隨意的unit32數值, 只要不衝突即可, 但是這並不是很好記, 所以DTC貼心的可以用lable來建立phandle,如下範例
PIC_3: pic@10000000 {
    interrupt-controller;
};

uart@20000000 {
    interrupt-parent = < &PIC_3 >;
};
這裡的"&"是告訴DTC後面接一個字串, 是個phandle參考到某個lable, 然後DTC就會幫user建立unit32的數值, 建立出phandle. 除此之外也可以使用full path來取代lable, 如下範例
/{
    soc {
        PIC_3: pic@10000000 {
            interrupt-controller;
        };
    };

    uart@20000000 {
        interrupt-parent = < &PIC_3 >;
    };

    uart@30000000 {
        interrupt-parent = < &{/soc/pic@10000000} >;
    };
};

下面再用一個例子同時說明DTC與phandle
brook@vista:~/dts$ cat test_phandle.dts
/dts-v1/;
/{
    soc {
        PIC_3: pic@10000000 {
           interrupt-controller;
        };
    };

    uart@20000000 {
        interrupt-parent = < &PIC_3 >;
    };

    uart@30000000 {
        interrupt-parent = < &{/soc/pic@10000000} >;
    };
};
brook@vista:~/dts$ dtc -O dtb -I dts test_phandle.dts > test_phandle.dtb
brook@vista:~/dts$ dtc -O dts -I dtb test_phandle.dtb
/dts-v1/;

/ {

        soc {

                pic@10000000 {
                        interrupt-controller;
                        linux,phandle = <0x1>;
                        phandle = <0x1>;
                };
        };

        uart@20000000 {
                interrupt-parent = <0x1>;
        };

        uart@30000000 {
                interrupt-parent = <0x1>;
        };
};

這篇文章主要簡單講解利用dtc來轉換dtb與dts, 順帶說明一下phandle是一種pointer data type, 與其基本原理



熱門文章