Difference between revisions of "Linux Drivers Device Tree Guide"

From eLinux.org
Jump to: navigation, search
(add link to top device tree page)
(add Linux conventions)
Line 2: Line 2:
  
 
[[Device_Tree | Top Device Tree page]]
 
[[Device_Tree | 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 and property names
 +
** use "-" instead of "_"
 +
 +
* node names
 +
** 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
  
 
== Support of different hardware versions in a single driver ==
 
== Support of different hardware versions in a single driver ==

Revision as of 02:12, 20 June 2016


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 and property names
    • use "-" instead of "_"
  • node names
    • 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

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