Device Tree Custom Properties Guide
Free Linux Kernel Development Course — Device Tree Chapter, Lecture 8
Device tree custom properties are how you attach board-specific configuration to a Linux device driver without touching a single line of driver source code. Every real-world platform driver eventually needs to read something that is not a standard resource — a sample rate, a calibration string, a feature flag — and that value has to come from a device tree custom property. In this lecture, part of our free Linux kernel development course, we build an original character-free platform driver on kernel 6.x that reads string, integer, array, and boolean device tree custom properties, and we explain exactly what the kernel is doing under the hood.
Keywords Covered In This Lecture
of_property_read_string
of_property_read_u32_array
of_property_read_bool
free linux device drivers course
What You Will Learn
- Why device tree custom properties exist and how they differ from standard properties like
regorinterrupts - The four practical data types you will use for device tree custom properties: string, integer (cell), array, and boolean
- Modern kernel 6.x APIs to read each type safely, including error handling
- How to write, build, load, and test an original platform driver that parses device tree custom properties
- Common mistakes students make when defining or reading device tree custom properties
Prerequisites
- Completion of the earlier Device Tree lectures in this free Linux kernel development course (node naming, reg property, phandles)
- A basic platform driver you can build and load (covered in the Platform Device Drivers chapter)
- A Linux host with kernel headers installed, or a QEMU / Raspberry Pi target running kernel 6.x
What Are Device Tree Custom Properties
A device tree describes hardware as a tree of nodes, and each node carries a set of properties. Some properties are standardized — reg, compatible, interrupts — and the kernel’s core device tree layer already knows how to interpret them. Everything else is a device tree custom property: a name and value pair that only your driver understands. Vendors prefix these names with a short vendor tag, such as ep,sample-rate, so that two unrelated vendors never collide on the same property name. This convention is not enforced by the compiler, only by community discipline, so following it matters when you publish a binding.
Node With Device Tree Custom Properties
| sensorhub@40 | compatible = “ep,dtprop-demo”; |
| reg = <0x40>; | |
| device-label | = “sensor-hub-01”; (string) |
| device-tags | = “primary”, “calibrated”; (string list) |
| sample-rate | = <1000>; (single cell) |
| channel-ids | = <0x10 0x20 0x30 0x40>; (cell array) |
| low-power-mode; | (boolean, no value) |
Device Tree Custom Properties Data Types
In practice a device tree custom property will almost always fall into one of the types below. Knowing the type up front decides which kernel API you must call.
| Type | DTS Syntax | Reader API (kernel 6.x) |
|---|---|---|
| String | prop = “text”; | of_property_read_string() |
| String list | prop = “a”, “b”; | of_property_read_string_index() |
| Single integer (cell) | prop = <100>; | of_property_read_u32() |
| Integer array | prop = <1 2 3>; | of_property_read_u32_array() |
| Boolean | prop; | of_property_read_bool() |
| Byte array | prop = [01 02 03]; | of_property_read_u8_array() |
Reading String Type Device Tree Custom Properties
The kernel gives you a pointer directly into the flattened device tree blob’s string table, so you never allocate or free memory for it. The function returns 0 on success and a negative error code — typically -EINVAL — if the property is missing.
const char *label;
int ret;
ret = of_property_read_string(np, "device-label", &label);
if (ret) {
dev_err(dev, "device-label property missing\n");
return ret;
}
dev_info(dev, "device-label = %s\n", label);
When a property holds a list of strings, such as device-tags above, read each entry by index instead:
const char *tag;
of_property_read_string_index(np, "device-tags", 0, &tag);
dev_info(dev, "first tag = %s\n", tag);
of_property_read_string_index(np, "device-tags", 1, &tag);
dev_info(dev, "second tag = %s\n", tag);
Reading Integer Type Device Tree Custom Properties
A single-cell integer property is read with of_property_read_u32(). Remember that every cell in a device tree is a 32-bit unsigned value, so a property meant to hold a signed number still needs to be cast carefully in your driver.
u32 rate;
if (of_property_read_u32(np, "sample-rate", &rate)) {
dev_warn(dev, "sample-rate not set, using default\n");
rate = 500;
}
dev_info(dev, "sample-rate = %u\n", rate);
For a list of cells, such as channel-ids, use of_property_read_u32_array() and pass the exact number of cells you expect:
u32 channels[4];
if (of_property_read_u32_array(np, "channel-ids", channels, 4)) {
dev_err(dev, "channel-ids must have exactly 4 entries\n");
return -EINVAL;
}
for (int i = 0; i < 4; i++)
dev_info(dev, "channel[%d] = 0x%x\n", i, channels[i]);
If you do not know the array length ahead of time, call of_property_count_u32_elems() first to find out how many cells are present, then allocate a buffer of that size before reading.
Reading Boolean Device Tree Custom Properties
A boolean device tree custom property carries no value at all — its presence means true, and its absence means false. of_property_read_bool() is still the correct call for a property that is genuinely boolean, like low-power-mode above.
bool low_power = of_property_read_bool(np, "low-power-mode");
dev_info(dev, "low power mode: %s\n", low_power ? "enabled" : "disabled");
Modernization note: recent kernel releases warn if you call of_property_read_bool() on a property that is not meant to be boolean, just to test whether it exists. For a plain presence check on any property type, use of_property_present() instead. Reserve of_property_read_bool() for properties defined as true boolean flags in your binding.
Complete Original Driver: ep_dtprop_demo
Below is a full, original platform driver written for this course that reads every device tree custom property type from the node shown earlier. It is not copied from any book — it is a fresh example built for kernel 6.x.
// ep_dtprop_demo.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
static int ep_dtprop_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
const char *label, *tag;
u32 rate, channels[4];
bool low_power;
if (of_property_read_string(np, "device-label", &label))
return -EINVAL;
of_property_read_string_index(np, "device-tags", 0, &tag);
if (of_property_read_u32(np, "sample-rate", &rate))
rate = 500;
if (of_property_read_u32_array(np, "channel-ids", channels, 4))
return -EINVAL;
low_power = of_property_read_bool(np, "low-power-mode");
dev_info(dev, "label=%s tag=%s rate=%u ch0=0x%x low_power=%d\n",
label, tag, rate, channels[0], low_power);
return 0;
}
static void ep_dtprop_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "ep_dtprop_demo removed\n");
}
static const struct of_device_id ep_dtprop_ids[] = {
{ .compatible = "ep,dtprop-demo" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_dtprop_ids);
static struct platform_driver ep_dtprop_driver = {
.probe = ep_dtprop_probe,
.remove = ep_dtprop_remove,
.driver = {
.name = "ep_dtprop_demo",
.of_match_table = ep_dtprop_ids,
},
};
module_platform_driver(ep_dtprop_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Original demo driver for device tree custom properties");
Matching Device Tree Node
sensorhub@40 {
compatible = "ep,dtprop-demo";
reg = <0x40>;
device-label = "sensor-hub-01";
device-tags = "primary", "calibrated";
sample-rate = <1000>;
channel-ids = <0x10 0x20 0x30 0x40>;
low-power-mode;
};
Build And Load
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_dtprop_demo.ko
dmesg | tail -5
Expected Output
ep_dtprop_demo sensorhub@40: label=sensor-hub-01 tag=primary rate=1000 ch0=0x10 low_power=0
If you add low-power-mode; to the node and rebuild the device tree blob, the same log line will show low_power=1 without any change to the driver source — that is the entire point of device tree custom properties: hardware description changes, driver logic does not.
Common Mistakes With Device Tree Custom Properties
| Mistake | Symptom | Fix |
|---|---|---|
| Wrong array length passed to of_property_read_u32_array() | Function returns -EOVERFLOW or reads garbage | Count elements first with of_property_count_u32_elems() |
| Treating a cell property as a string | Garbled or crashing dev_info() output | Match the DTS syntax to the correct reader function |
| Forgetting to check the return value | Driver silently uses uninitialized memory | Always check the int return of every of_property_read_* call |
| No vendor prefix on a custom property name | Naming collision with another vendor’s binding | Always prefix, e.g. ep,sample-rate |
Best Practices For Device Tree Custom Properties
- Document every device tree custom property in a YAML or text binding so other developers know the expected type and units
- Always provide a sane default in the driver when a property is optional
- Keep property names lowercase with hyphens, matching existing kernel convention
- Never encode signed or floating-point semantics silently inside a plain u32 cell — document the encoding
Real-World Use Cases
Sensor drivers use device tree custom properties for calibration offsets and sampling intervals. Display drivers use them for panel timings that are not covered by the standard panel-timing bindings. Audio codecs use them for gain tables. In every case, the same four reader functions covered in this lecture do the job.
Performance And Security Considerations
Property reads happen only during probe, so they have no runtime performance cost. From a security standpoint, always bound-check array lengths before reading, since a malformed device tree blob — for example from a bootloader that was tampered with — could otherwise cause an out-of-bounds read if you trust an attacker-supplied count.
Summary And Key Takeaways
- Device tree custom properties let you configure a driver from hardware description alone
- Four reader families cover almost every case: string, u32, array, and boolean
- Always check return values and provide defaults for optional properties
- Prefer of_property_present() over of_property_read_bool() for plain presence checks on non-boolean properties
Conclusion
Device tree custom properties are the bridge between a fixed piece of hardware and a driver that adapts to it without recompilation. Once you are comfortable with the four reader APIs covered here, you can describe almost any board-specific configuration your hardware needs. In the next lecture of this free Linux kernel development course, we extend this to sub-nodes, so a single device tree node can describe several related child components at once.
Frequently Asked Questions
What is a device tree custom property?
It is any property in a device tree node that is not part of the kernel’s standard set (reg, compatible, interrupts, and so on) and is defined and interpreted only by your own driver.
Do I need a vendor prefix for device tree custom properties?
It is strongly recommended. A vendor prefix such as ep, prevents your property name from colliding with another vendor’s binding of the same name.
What happens if of_property_read_u32() cannot find the property?
It returns -EINVAL and leaves your output variable untouched, so you should always supply a default value in that branch.
Is of_property_read_bool() still valid on kernel 6.x?
Yes, for properties that are genuinely boolean by design. It is only discouraged when used purely to test the presence of a non-boolean property, where of_property_present() is now preferred.
Can a device tree custom property hold a negative number?
Not directly. Cells are unsigned 32-bit values, so a negative number has to be encoded and decoded by convention in your driver, such as two’s complement casting.
How do I read a property whose array length is unknown at compile time?
Call of_property_count_u32_elems() first to get the element count, allocate a buffer of that size, then call of_property_read_u32_array().
Where should I document my device tree custom properties?
In a devicetree bindings document alongside your driver, describing each property’s name, type, unit, and whether it is required or optional.
Continue The Free Linux Kernel Development Course
Next up: parsing device tree sub-nodes in an original multi-partition driver.
