Linux Drivers Device Tree Guide

From eLinux.org
Revision as of 09:12, 21 June 2016 by Frowand (talk | contribs) (Linux conventions: property name convention is not same as node name)
Jump to: navigation, search


Top Device Tree page

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
    • use "-" instead of "_"
    • unit-address does not have a leading "0x" (the number is assumed to be hexadecimal)
    • unit-address does not have a leading zeros
  • 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.
  • 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

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

  The functions of_n_addr_cells() and of_n_size_cells() search up the tree until
  a parent is found with the 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)

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