zoukankan      html  css  js  c++  java
  • Device Tree Linux --- 做个备份

    Device Tree Linux

     
     


    Top Device Tree page

    Device Tree Framework Source Code

    The device tree framework source code is located in drivers/of/.

    Code for manipulating the flattened device tree (FDT) is is scripts/dtc/libfdt.

    libfdt is imported from the external project maintained in

    Linux Man pages

    Linux man pages are a work in progress.

    The current version of the pages can be downloaded or built locally.

    Linux Gotchas

    disabled nodes

    Linux has widespread use of the "status" property to indicate that a node does not exist. This is used to create a generic .dtsi file that defines all of the potential components of a device which might contain many different functions. When the device is used in a system, many of the functions might not be routed to connectors or might not be usable due to needing pins that are multiplexed with other devices that are active. The convention is to set the "status" property of such functions to "disabled" in the .dtsi file, then the system .dts file that includes the .dtsi will change the "status" property of functions that should be enabled to "ok".

    Some kernel code does not check the node "status" property before trying to configure, probe, or enable it.

    Expect efforts to fix the kernel code to respect the "status" property.

    node name case sensitivity

    Node names are supposed to be case sensitive. The function that checks whether a node name matches a string may be defined by an architecture or may be the generic implementation. Due to some issues with old Macs the generic Linux implementation was case insensitive. There is an architecture defined version that is not case sensitive.

    Expect this to change so that the generic node name compare function is case sensitive, and only a few select sub-architectures will not be case sensitive.

    The move to a case sensitive compare began in v4.19 with commit f42b0e18f2e5cf34f73ef1b6327b49040b307a33 ("of: add node name compare helper functions"), which creates of_node_name_eq() and of_node_name_prefix(). Use of these functions is added in later commits.

    Note that dtc has had node name case sensitivity for a long time (or maybe even from the beginning).

    phandles

    phandle source property

    There are three distinct properties that may be the source of a node's phandle:

    • ibm,phandle
    • linux,phandle
    • phandle

    Property "linux,phandle" is obsolete. If it exists, its value should be equivalent to the value of property "phandle".

    Property "ibm,phandle" may be used in the IBM pSeries dynamic device tree.

    phandle access in kernel (by drivers)

    The "phandle" and "linux,phandle" properties may exist in device tree source and in the compiled Flattened Device Tree (FDT), aka "binary blob" or ".dtb". But the Linux kernel views the phandle values as device tree structure information instead of device tree data and thus does not store them as properties.

    This results in a special rule:

      DO NOT USE property access functions to access the properties:
      
        ibm,phandle
        linux,phandle
        phandle
      
      Note: other properties may contain phandle values.  It is proper
            to use property access functions to read these values.  Then the
            node containing the phandle value can be found via
            of_find_node_by_phandle(handle).
    
      Functions to access phandles are:
      
        of_find_node_by_phandle()
    
      Functions that indirectly access phandles are:
      
        of_for_each_phandle()
        of_parse_phandle_with_args()
        of_parse_phandle_with_fixed_args()
    

    Linux conventions

    • hex constants are lower case
      • use "0x" instead of "0X"
      • use a..f instead of A..F, eg 0xf instead of 0xF
    • node names
      • should begin with a character in the range 'a' to 'z', 'A' to 'Z'
      • unit-address does not have a leading "0x" (the number is assumed to be hexadecimal)
      • unit-address does not have leading zeros
      • use dash "-" instead of underscore "_"
    • label names
      • should begin with a character in the range 'a' to 'z', 'A' to 'Z'
      • should be lowercase
      • use underscore "_" instead of dash "-"
    • property names
      • should be lower case
      • should begin with a character in the range 'a' to 'z'
      • use dash "-" instead of underscore "_"
    Actual usage in Linux 4.11-rc1: arch/arm/boot/dts/*
    
    2512 unique property names
    1877 use dash "-"
     234 use underscore "_"
     100 use both dash "-" and underscore "_"
     501 use neither dash "-" nor underscore "_"
    
    • tuple data
      • property data that is a tuple is more readable when grouped into tuples
    preferred style:
    
      reg = <0x2c000 0x30>, <0x2d000 0x800>, <0x20000 0x1000>
    
    instead of:
    
      reg = <0x2c000 0x30 0x2d000 0x800 0x20000 0x1000>
    
    • maximum depth of device tree
      • #define FDT_MAX_DEPTH 64

    Linux Preferences

    Items in this section may be less firm or certain than items in "Linux Conventions".

    If you can document or explain why any item should move to "Linux Conventions" please email frowand.list at gmail.com".

    cpp

    The Linux kernel build system preprocesses dts files with cpp before passing them to dtc. The dtc compileralso has an "/include/" directive.

    • Files that are included via the "/include/" directive can not contain any cpp directives. This is because such files are not visible to cpp.
    • Although cpp can do conditional inclusion with features such as "#if" and "#ifdef" this is frowned upon.
    • Common use of cpp is for "#include" and simple "#defines" for symbolic names.
    • Bindings related include files are in include/dt-bindings/
    • More complex macros do exist, but are rare.
    Complex example 1
    
    include/dt-bindings/pinctrl/omap.h:
    #define OMAP_IOPAD_OFFSET(pa, offset)   (((pa) & 0xffff) - (offset))
    
    #define OMAP2420_CORE_IOPAD(pa, val)    OMAP_IOPAD_OFFSET((pa), 0x0030) (val)
    
    Complex example 2
    
    include/dt-bindings/input/input.h:
    
    #define MATRIX_KEY(row, col, code)      
            ((((row) & 0xFF) << 24) | (((col) & 0xFF) << 16) | ((code) & 0xFFFF))
    
    

    interrupts vs interrupts-extended

    Should I use 1) the "interrupts" property in association with the "interrupt-parent" property or 2) the "interrupts-extended" property?

    "Interrupts" is well understood and commonly used. It is ok to use that model.

    The main purpose of "interrupts-extended" is to allow one device to have multiple interrupts that are handled by different controllers, without introducing a (more complex) "interrupt-map" property in the parent.

    Linux vs ePAPR Version 1.1

    #address-cells and #size-cells

    ---  ePAPR
    
       2.3.5 #address-cells and #size-cells
          'The #address-cells and #size-cells properties are not
           inherited from ancestors in the device tree. They shall be
           explicitly defined.'
    
    ---  Linux
    
       #address-cells defaults to 2
       #size-cells defaults to 1
       dtc may warn that the default is being used.
    

    aliases node

    ---  ePAPR
    
       3.3 aliases node
          'A client program may use an alias property name to refer to
           a full device path as all or part of its string value.'
    
          'A client program, when considering a string as a device path,
           shall detect and use the alias.'
    
    ---  Linux
    
       of_find_node_by_path() and of_find_node_opts_by_path() check
       whether the path begins with "/".  If it does not, then the first
       element must be a property in the aliases node.  That first element
       is replaced with the value of the property in the aliases node.
    
       It is not known whether other code (such as drivers) is aware of
       the requirement to be aware of aliases.
    
       Linux also has a concept of numeric id derived from the property
       names in the aliases node.  All trailing digits in the property
       name are used to create the "id".  The "stem" is the property
       name, with the trailing digits removed.  For each property in
       the aliases node, the tuple of stem, id, and the device tree
       node pointer for the path in the property's value is added to
       the aliases_lookup list.  A driver can use of_alias_get_id()
       to retrieve the id for a given stem and device tree node
       pointer.
    
       This is commonly used to number devices, although the practice
       is controversial.
    

    chosen node

    ---  ePAPR
    
       3.5 chosen node
          
       The chosen node is not required.  If it was required, sections 3.1 and 3.5
       would explicitly state such (for example, section 3.6 CPUS Node Properties
       states 'A cpus node is required for all device trees.').
    
       The list of required nodes is listed in section 3.1:
    
       3.1 Base Device Node Types
    
          'The sections that follow specify the requirements for the base set of device nodes required in an
           ePAPR-compliant device tree.
    
           All device trees shall have a root node and the following nodes shall be present at the root of all
           device trees:
              • One cpus node
              • At least one memory node'
    
    ---  Linux
    
       The chosen node is required.
    

    device_type property

    ---  ePAPR
    
       2.3.11 device_type
          'The device_type property was used in IEEE 1275 to describe the device’s FCode
           programming model. Because ePAPR does not have FCode, new use of the property is
           deprecated, and it should be included only on cpu and memory nodes for compatibility with
           IEEE 1275–derived device trees.'
    
    ---  Linux
    
       Many Linux bindings have a device_type property.  Many of these bindings are not
       well documented.
    
       Linux source code accesses this property via for_each_node_by_type(),
       of_find_node_by_type(), and by open coding.  As of 4.10-rc3, the list
       of values searched for by for_each_node_by_type() and of_find_node_by_type()
       is:
    
          8042
          CBEA-Internal-Interrupt-Controller
          PowerPC-External-Interrupt-Presentation
          adb
          be
          cache
          cpm
          cpu
          display
          fcu
          fdc
          interrupt-controller
          ipic
          isa
          memory
          memory-controller
          mic-tm
          mpc8xx-pic
          nvram
          open-pic
          pci
          pcie-endpoint
          pervasive
          pic-router
          qe
          qeic
          serial
          smu
          soc
          spe
          tsi-bridge
    

    labels

    ---  ePAPR
    
       Appendix A Device Tree Source Format (version 1)
    
       Node and property definitions
    
          'Device tree nodes are defined with a node name and unit address with braces marking the start and
           end of the node definition. They may be preceded by a label.'
    
       There is no note as to whether a label is allowed on the root node.
    
    
    ---  Linux
    
       dtc does not allow a label on the root node.
    

    names

    Node name

    ---  ePAPR
    
       2.2.1 Node Names
          The node name follows the convention:
     
             node-name@unit-address
     
          Characters in node-name may be:
             0-9
             a-z
             A-Z
             ,._+-
    
    ---  Linux
    
       dtc allows characters in node-name to be:
          0-9
          a-z
          A-Z
          ,._+-
          *#?
    
      Discouraged:
          #?.+*_
    

    Node Name length

    --- ePAPR
    
    The node-name component shall be 1 to 31 characters in length.
    
    --- Linux
    
    The maximun length of the node-name component is not enforced.
    
    It has been proposed that the Devicetree Specification be
    updated with a maximum length that is consistent with current
    Linux kernel usage.
    

    Property name

    ---  ePAPR
    
       2.2.4.1 Property Names
          Characters in the property name may be:
             0-9
             a-z
             A-Z
             ,._+-?#
    
    ---  Linux
    
       dtc allows characters in property name to be:
          0-9
          a-z
          A-Z
          ,._+-?#
          *
    
       Discouraged:
          ?.+*_
          # is only recommended as the first character of a property name
    

    Label names

    ---  ePAPR
    
       No information on characters in label names.
     
    ---  Linux
    
       dtc allows characters in label name to be:
          0-9
          a-z
          A-Z
          _
       The label name may not begin with 0-9
    

    status property

    ---  ePAPR
    
       2.3.4 status
          'Valid values for status are: "okay", "disabled", "fail", "fail-sss".
           "Refer to the device binding for details on what disabled means for
           a given device.'
    
       3.7.1 General Properties of CPU nodes
          Valid values for status are: "okay", "disabled".
    
    ---  Linux
    
       A node is enabled if:
          status == "ok"
          status == "okay"
          status property does not exist
       Convention for disabling a node:
          status == "disabled"
       Not all code checks whether a node is disabled.  These cases are bugs.
    

    "ABI" stability

    Are device tree bindings stable?

    Well.... Let me put this open jar full of gasoline on the floor and step away for a little while. Oh, can I loan you a lighter or a book of matches?

    • Email thread: DT bindings as ABI [was: Do we have people interested in device tree janitoring / cleanup?]

    Bindings review process

    devicetree/bindings/submitting-patches.txt was created following discussion at the 2013 Kernel Summit.

    Email thread: [Ksummit-2013-discuss] [ATTEND] Handling of devicetree bindings

    The slides that Grant Likely presented at the Kernel Summit discussion were in an email attachment that the mail list stripped. Here are the slides: It's Broken! Fixing the DT binding process (PDF)

    Slides 18 and 19 of [PDF] "Device Tree, the Disaster so Far", ELC Europe 2013 by Mark Rutland.

    Support of different hardware versions in a single driver

    Examples of drivers that match more than one compatible string.

    This list is not an endorsement of any particular technique. It is instead a (partial) list of some existing code in the Linux kernel.

    The examples are not meant to capture each method entirely; they are instead meant to illustrate the basic concept.

    Hardware Version in struct of_device_id.data

    The hardware version is used throughout the driver to choose alternate actions.

    drivers/iommu/arm-smmu.c:

    static const struct of_device_id arm_smmu_of_match[] = {
            { .compatible = "arm,smmu-v1", .data = (void *)ARM_SMMU_V1 },
            { .compatible = "arm,smmu-v2", .data = (void *)ARM_SMMU_V2 },
            { .compatible = "arm,mmu-400", .data = (void *)ARM_SMMU_V1 },
            { .compatible = "arm,mmu-401", .data = (void *)ARM_SMMU_V1 },
            { .compatible = "arm,mmu-500", .data = (void *)ARM_SMMU_V2 },
            { },
    };
    MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
    
    static int arm_smmu_device_dt_probe(struct platform_device *pdev)
    {
            const struct of_device_id *of_id;
    
            of_id = of_match_node(arm_smmu_of_match, dev->of_node);
            smmu->version = (enum arm_smmu_arch_version)of_id->data;
    
            ...
    
            if (smmu->version > ARM_SMMU_V1) {
                    ...
            }
    }
    
    static struct platform_driver arm_smmu_driver = {
           .driver = {
                    .name           = "arm-smmu",
                    .of_match_table = of_match_ptr(arm_smmu_of_match),
            },
            .probe  = arm_smmu_device_dt_probe,
            .remove = arm_smmu_device_remove,
    };
    

    Function Call Table pointer in struct of_device_id.data

    The function call table is used throughout the driver to choose alternate actions.

    drivers/iio/adc/xilinx-xadc-core.c:

    static const struct xadc_ops xadc_zynq_ops = {
            .read = xadc_zynq_read_adc_reg,
            .write = xadc_zynq_write_adc_reg,
            .setup = xadc_zynq_setup,
            .get_dclk_rate = xadc_zynq_get_dclk_rate,
            .interrupt_handler = xadc_zynq_interrupt_handler,
            .threaded_interrupt_handler = xadc_zynq_threaded_interrupt_handler,
            .update_alarm = xadc_zynq_update_alarm,
    };
    
    static const struct of_device_id xadc_of_match_table[] = {
            { .compatible = "xlnx,zynq-xadc-1.00.a", (void *)&xadc_zynq_ops },
            { .compatible = "xlnx,axi-xadc-1.00.a", (void *)&xadc_axi_ops },
            { },
    };
    MODULE_DEVICE_TABLE(of, xadc_of_match_table);
    
    static int xadc_probe(struct platform_device *pdev)
    {
            const struct of_device_id *id;
            id = of_match_node(xadc_of_match_table, pdev->dev.of_node);
            ...
            indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*xadc));
            xadc = iio_priv(indio_dev);
            ...
            xadc->ops = id->data;
            ...
            ret = xadc->ops->setup(pdev, indio_dev, irq);
            ...
    }
    
    static struct platform_driver xadc_driver = {
            .probe = xadc_probe,
            .remove = xadc_remove,
            .driver = {
                    .name = "xadc",
                    .of_match_table = xadc_of_match_table,
            },
    };
    module_platform_driver(xadc_driver);
    

    Hardware Description pointer in struct of_device_id.data

    The hardware description data is used to configure the device.

    This struct pointed to by struct of_device_id.data in this example includes a function call table in addition to the hardware description fields.

    drivers/iio/adc/twl6030-gpadc.c:

    static const struct twl6030_gpadc_platform_data twl6030_pdata = {
            .iio_channels = twl6030_gpadc_iio_channels,
            .nchannels = TWL6030_GPADC_USED_CHANNELS,
            .ideal = twl6030_ideal,
            .start_conversion = twl6030_start_conversion,
            .channel_to_reg = twl6030_channel_to_reg,
            .calibrate = twl6030_calibration,
    };
    
    static const struct of_device_id of_twl6030_match_tbl[] = {
            {
                    .compatible = "ti,twl6030-gpadc",
                    .data = &twl6030_pdata,
            },
            {
                    .compatible = "ti,twl6032-gpadc",
                    .data = &twl6032_pdata,
            },
            { /* end */ }
    };
    
    static int twl6030_gpadc_probe(struct platform_device *pdev)
    {
            const struct of_device_id *match;
            const struct twl6030_gpadc_platform_data *pdata;
    
            match = of_match_device(of_twl6030_match_tbl, dev);
            pdata = match->data;
            indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc));
            gpadc = iio_priv(indio_dev);
            gpadc->pdata = pdata;
            platform_set_drvdata(pdev, indio_dev);
            ...
            ret = pdata->calibrate(gpadc);
            ...
            indio_dev->channels = pdata->iio_channels;
            indio_dev->num_channels = pdata->nchannels;
    }
    

    FDT built into kernel as data

    The Linux kernel build system has a rule to automagically place an FDT in the kernel image as a binary data object. To invoke the rule, add entries to the makefile of the form XXX.dtb.o to create a data object for devicetree source file XXX.dts.

    obj-y += my_system.dtb.o
    obj-$(CONFIG_XXX) += XXX.dtb.o
    
    # enable export of phandle symbols to be used by another overlay
    DTC_FLAGS_my_system += -@
    
    .PRECIOUS: 
            $(obj)/%.dtb.S 
            $(obj)/%.dtb
    

    The data object can be accessed in the kernel through extern declarations of pointers to the beginning and just past the end of the data:

    extern uint8_t __dtb_XXX_begin[];
    extern uint8_t __dtb_XXX_end[];
    

    SPECIAL CASE: devicetree source filename containing dash

    The name of the devicetree source file is embedded in the pointers to the binary data object in the kernel image. A file name containing a dash can not be embedded in the pointer names because dash ("-", aka hyphen) is not a valid character in a pointer name. To resolve this problem, the build system process will convert each dash ("-") in the file name to an underscore ("_") in the pointer names.

    This fixup is scheduled to be added in Linux version 4.16, 4.4.122, 4.9.88, 4.14.27, 4.15.10

    forward and backward dts compatibility

    Current practice is that

    • new kernels work with old devicetrees
    • old kernels may or may not work with new devicetrees

    This means that if a binding is modified in a non-compatible manner then the kernel implementation must still recognize the old binding until old devicetrees have been obsoleted and no longer exist.

    defaults and inheritance of #address-cells and #size-cells

    The standards used by Linux do not allow a default value for #address-cells and #size-cells. This also means that a node should not inherit the values from a predecessor in the tree.

    Contrary to the standards, the Linux implementation provides both default values and inheritance.

    of_n_addr_cells() will search up through the devicetree until a node with the #address-cells property is found. If none is found the default value of OF_ROOT_NODE_ADDR_CELLS_DEFAULT is returned.

    of_n_size_cells() will search up through the devicetree until a node with the #size-cells property is found. If none is found the default value of OF_ROOT_NODE_SIZE_CELLS_DEFAULT is returned.

    The values of OF_ROOT_NODE_ADDR_CELLS_DEFAULT and OF_ROOT_NODE_SIZE_CELLS_DEFAULT are normally 1, but can be changed to a difference value by architecture specific defines. Currently (Linux 4.17) overrides for sparc are 1 for OF_ROOT_NODE_SIZE_CELLS_DEFAULT and 2 for OF_ROOT_NODE_ADDR_CELLS_DEFAULT.

    ePAPR 1.1, section 2.3.5, page 24
    
    The #address-cells and #size-cells properties are not inherited from
    ancestors in the device tree. They shall be explicitly defined.
    

    The Linux kernel supports a default for #address-cells and #size-cells to avoid breaking old OF based platforms which did not set them.

    ... the default that Linux implements does not match the one defined by
    the IEE1275 spec, in order to accomodate certain broken device trees on
    certain machines (mostly Apple, I think).  The IEEE1275 default is
    simply 2, ...  These properties were never supposed to be inherited,
    but apparently some old firmwares didn't get the memo.
    
    reference:
      Date:   Thu, 19 Jul 2018 15:37:09 +1000
      From:   David Gibson <david@gibson.dropbear.id.au>
      Subject: Re: [PATCH] libfdt: fdt_address_cells() and fdt_size_cells()
      Message-ID: <20180719053709.GB18205@umbus.fritz.box>
      List-ID: <devicetree-compiler.vger.kernel.org>
    

    The dtc devicetree compiler will warn if #address-cells or #size-cells is missing. New devicetree source is expected to properly provide the values for #address-cells and #size-cells.

    /chosen node vs /firmware node

    The /chosen node is used by firmware to communicate boot time information to the Linux kernel.

    The /firmware node is used to describe the interface that the Linux kernel can use for run-time communication with the firmware. Examples of run-time communication include secure calls, a mailbox, IPC, and hypervisor calls.

    An exception to using the /firmware node for run-time communication with the firmware is if the firmware is accessed through a register based interface, in which case the firmware would be under simple-bus. [1]

    The specific bindings for the /firmware nodes on various platforms are documented in the same manner as other Linux bindings.

    [1] Rob Herring; aug 24, 2018; Re: Recognize "/firmware" node usage
    

    printk format string

    There are some printk() format specifiers to do complicated formatting of pointers to some structures. For devicetree, there is a family of specifiers for struct device_node.

    Documentation/core-api/printk-formats.rst contains a description of the format specifiers. Check printk-formats.rst for the current specification of the format specifiers.


    Excerpt from Linux 4.18 printk-formats.rst:

            %pOF[fnpPcCF]
    
    
    For printing kobject based structs (device nodes). Default behaviour is
    equivalent to %pOFf.
    
            - f - device node full_name
            - n - device node name
            - p - device node phandle
            - P - device node path spec (name + @unit)
            - F - device node flags
            - c - major compatible string
            - C - full compatible string
    
    The separator when using multiple arguments is ':'
    
    Examples::
    
            %pOF    /foo/bar@0                      - Node full name
            %pOFf   /foo/bar@0                      - Same as above
            %pOFfp  /foo/bar@0:10                   - Node full name + phandle
            %pOFfcF /foo/bar@0:foo,device:--P-      - Node full name +
                                                      major compatible string +
                                                      node flags
                                                            D - dynamic
                                                            d - detached
                                                            P - Populated
                                                            B - Populated bus
    

    Notice that multiple arguments can follow '%pOF'. This has the unfortunate side effect that the current implementation of decoding the format specifier with eat the character following the final argument if that character is not detected as a boundary between specifiers, such as a space or a '%'. For example, in this snprintf(), the 'T' is meant to be printed as a literal value, but is instead suppressed:

    csize = snprintf(str, len, "of:N%pOFnT%s", dev->of_node,
                     dev->of_node->type);
    
      For a node with name of 'name' and a null pointer for type,
        desired result:  of:NnameT<NULL>
        actual result:   of:Nname<NULL>
    
    A temporary workaround is to use a '%c' specifier:
    
    csize = snprintf(str, len, "of:N%pOFn%c%s", dev->of_node,
                     "T",
                     dev->of_node->type);
    

    boolean property

    The devicetree standards do not define a boolean type, but the Linux kernel API includes a function to determine whether a property is true or false:

    /**
     * of_property_read_bool - Findfrom a property
     * @np:         device node from which the property value is to be read.
     * @propname:   name of the property to be searched.
     *
     * Search for a property in a device node.
     * Returns true if the property exists false otherwise.
     */
    static inline bool of_property_read_bool(const struct device_node *np,
                                             const char *propname)
    {
    

    The return value of of_property_read_bool() is based on whether the property is present or not. The return value is not based on the value of the property. Thus the following confusing result:

    a_node {
            flag="false";
    }
    
    The result of of_property_read_bool(np, "flag") is true.
  • 相关阅读:
    golang 相关
    ES root用户启动失败can not run elasticsearch as root
    基于 Flink CDC + Hudi 湖仓一体方案实践
    数据平台上云
    多云趋势
    数果实时数仓探索
    宽表的设计
    数仓指标体系
    Hudi在医疗大数据的应用
    Hudi on Flink上手使用总结
  • 原文地址:https://www.cnblogs.com/lagujw/p/12410039.html
Copyright © 2011-2022 走看看