1. I2C Devices
1.1 I2C Controller
Description of the I2C bus controller node:
i2c0: i2c@10002000 {
compatible = "arm,versatile-i2c";
reg = <0x10002000 0x1000>;
interrupts = <0 30 4>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&osc24M>;
clock-frequency = <100000>; /* 100KHz standard mode */
status = "okay";
};
Key Attribute Descriptions:
<span>#address-cells = <1></span>: The I2C device address is represented by one cell<span>#size-cells = <0></span>: I2C devices have no size concept<span>clock-frequency</span>: I2C bus frequency (100K/400K/1M, etc.)
1.2 I2C Device Nodes
I2C devices as child nodes of the I2C controller:
EEPROM Device
i2c0: i2c@10002000 {
#address-cells = <1>;
#size-cells = <0>;
/* AT24C32 EEPROM */
eeprom: at24c32@50 {
compatible = "atmel,24c32";
reg = <0x50>; /* I2C device address */
pagesize = <32>; /* Page size 32 bytes */
size = <4096>; /* Total capacity 4KB */
address-width = <16>; /* Address width 16 bits */
};
};
Temperature Sensor
i2c0: i2c@10002000 {
#address-cells = <1>;
#size-cells = <0>;
/* LM75 Temperature Sensor */
temp_sensor: lm75@48 {
compatible = "national,lm75";
reg = <0x48>;
vs-supply = <&vdd_3v3>;
temperature-max = <85000>; /* Maximum temperature 85°C */
temperature-min = <-40000>; /* Minimum temperature -40°C */
};
};
RTC Clock Chip
i2c0: i2c@10002000 {
#address-cells = <1>;
#size-cells = <0>;
/* DS1307 RTC */
rtc: ds1307@68 {
compatible = "dallas,ds1307";
reg = <0x68>;
interrupt-parent = <&gpio0>;
interrupts = <20 IRQ_TYPE_EDGE_FALLING>;
};
};
2. SPI Devices
2.1 SPI Controller
Description of the SPI bus controller node:
spi0: spi@10004000 {
compatible = "arm,pl022", "arm,primecell";
reg = <0x10004000 0x1000>;
interrupts = <0 32 4>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&osc24M>, <&osc24M>;
clock-names = "spiclk", "apb_pclk";
status = "okay";
};
2.2 SPI Device Nodes
SPI devices as child nodes of the SPI controller, using<span>reg</span> to specify the chip select number:
SPI Flash
spi0: spi@10004000 {
#address-cells = <1>;
#size-cells = <0>;
/* SPI NOR Flash */
flash: m25p80@0 {
compatible = "jedec,spi-nor";
reg = <0>; /* Chip select 0 */
spi-max-frequency = <40000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "bootloader";
reg = <0x0 0x40000>;
read-only;
};
partition@40000 {
label = "kernel";
reg = <0x40000 0x300000>;
};
};
};
};
SPI ADC Device
spi0: spi@10004000 {
#address-cells = <1>;
#size-cells = <0>;
/* MCP3008 ADC */
adc: mcp3008@1 {
compatible = "microchip,mcp3008";
reg = <1>; /* Chip select 1 */
spi-max-frequency = <1000000>;
vref-supply = <&vdd_3v3>;
};
};
3. GPIO Devices
3.1 GPIO Controller
GPIO controller node:
gpio0: gpio@10015000 {
compatible = "arm,pl061", "arm,primecell";
reg = <0x10015000 0x1000>;
interrupts = <0 8 4>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
clocks = <&osc24M>;
clock-names = "apb_pclk";
};
Key Attributes:
<span>gpio-controller</span>: Identifies as a GPIO controller<span>#gpio-cells = <2></span>: GPIO references require two parameters (pin number, flags)<span>interrupt-controller</span>: Can act as an interrupt controller<span>#interrupt-cells = <2></span>: Interrupt references require two parameters (pin number, trigger type)
3.2 GPIO Usage Examples
GPIO LED
#include <dt-bindings/gpio/gpio.h>
leds {
compatible = "gpio-leds";
led0: led-0 {
label = "status";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
led1: led-1 {
label = "error";
gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
linux,default-trigger = "heartbeat";
};
};
GPIO Button
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
gpio_keys {
compatible = "gpio-keys";
button0: button-0 {
label = "User Button";
linux,code = <116>; /* KEY_POWER */
gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
gpio-key,wakeup;
};
button1: button-1 {
label = "Reset Button";
linux,code = <408>; /* KEY_RESTART */
gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
};
};
GPIO Expander Chip
i2c0: i2c@10002000 {
#address-cells = <1>;
#size-cells = <0>;
/* PCA9555 GPIO Expander */
gpio_expander: pca9555@20 {
compatible = "nxp,pca9555";
reg = <0x20>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio0>;
interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
};
};
/* Using expanded GPIO */
custom_device {
compatible = "vendor,custom";
enable-gpios = <&gpio_expander 0 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio_expander 1 GPIO_ACTIVE_LOW>;
};
4. PCIe Devices
4.1 PCIe Controller
PCIe controller node:
pcie: pcie@40000000 {
compatible = "vendor,pcie-controller";
reg = <0x40000000 0x1000000>, /* Configuration space */
<0x50000000 0x20000000>; /* I/O space */
reg-names = "config", "io";
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
ranges = <0x81000000 0x0 0x00000000 0x50000000 0x0 0x00010000 /* I/O */
0x82000000 0x0 0x60000000 0x60000000 0x0 0x10000000>; /* MEM */
interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &gic 0 0 0 88 4>,
<0x0 0x0 0x0 0x2 &gic 0 0 0 89 4>,
<0x0 0x0 0x0 0x3 &gic 0 0 0 90 4>,
<0x0 0x0 0x0 0x4 &gic 0 0 0 91 4>;
status = "okay";
};
Key Attribute Descriptions:
<span>#address-cells = <3></span>: PCI addresses require three cells (bus, device, function)<span>#size-cells = <2></span>: PCI sizes require two cells (64-bit)<span>ranges</span>: Defines PCI address space mapping<span>interrupt-map</span>: Defines interrupt routing
4.2 PCIe Devices
PCIe devices are usually auto-discovered by the kernel but can be configured via the device tree:
pcie: pcie@40000000 {
/* PCIe devices are automatically enumerated by the kernel */
/* Device tree can override specific device configurations */
};
/* PCIe device overrides (if supported) */
&pcie {
/* Some platforms support configuring PCIe devices via device tree */
};
5. Interrupt Configuration
5.1 Interrupt Controller
GIC Interrupt Controller
#include <dt-bindings/interrupt-controller/arm-gic.h>
gic: interrupt-controller@1e001000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x1e001000 0x1000>,
<0x1e000100 0x100>;
};
Interrupt Attribute Format:
<span><GIC_SPI interrupt-num trigger-type></span><span>GIC_SPI</span>: Shared peripheral interrupt<span>GIC_PPI</span>: Private peripheral interrupt- Trigger types:
<span>IRQ_TYPE_LEVEL_HIGH</span>,<span>IRQ_TYPE_EDGE_RISING</span>, etc.
GPIO Interrupt Controller
gpio0: gpio@10015000 {
compatible = "arm,pl061";
reg = <0x10015000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gic>;
interrupts = <0 8 4>;
};
5.2 Device Interrupt Configuration
Standard Interrupt Attributes
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
uart0: serial@10009000 {
compatible = "arm,pl011";
reg = <0x10009000 0x1000>;
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "uart";
};
Multiple Interrupts
device@20000000 {
compatible = "vendor,multi-irq-device";
reg = <0x20000000 0x1000>;
interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 51 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "main", "aux";
};
GPIO Interrupt
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
sensor@30000000 {
compatible = "vendor,sensor";
reg = <0x30000000 0x1000>;
interrupt-parent = <&gpio0>;
interrupts = <10 IRQ_TYPE_EDGE_FALLING>;
};
Interrupt Expansion
/* Using interrupts from GPIO expander */
i2c0: i2c@10002000 {
gpio_expander: pca9555@20 {
compatible = "nxp,pca9555";
reg = <0x20>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio0>;
interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
};
};
/* Device using expander interrupts */
sensor {
compatible = "vendor,sensor";
interrupt-parent = <&gpio_expander>;
interrupts = <5 IRQ_TYPE_EDGE_RISING>;
};
6. Comprehensive Example
The following is a comprehensive example that includes various devices:
/dts-v1/;
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "vendor,demo-board";
model = "Demo Board v1.0";
#address-cells = <1>;
#size-cells = <1>;
/* Interrupt Controller */
gic: interrupt-controller@1e001000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x1e001000 0x1000>,
<0x1e000100 0x100>;
};
/* GPIO Controller */
gpio0: gpio@10015000 {
compatible = "arm,pl061";
reg = <0x10015000 0x1000>;
interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
/* I2C Controller */
i2c0: i2c@10002000 {
compatible = "arm,versatile-i2c";
reg = <0x10002000 0x1000>;
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
clock-frequency = <100000>;
/* EEPROM */
eeprom@50 {
compatible = "atmel,24c32";
reg = <0x50>;
pagesize = <32>;
};
/* Temperature Sensor */
temp_sensor@48 {
compatible = "national,lm75";
reg = <0x48>;
};
};
/* SPI Controller */
spi0: spi@10004000 {
compatible = "arm,pl022";
reg = <0x10004000 0x1000>;
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
/* SPI Flash */
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <40000000>;
};
};
/* GPIO LED */
leds {
compatible = "gpio-leds";
led0: led-0 {
label = "status";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
};
/* GPIO Buttons */
gpio_keys {
compatible = "gpio-keys";
button0: button-0 {
label = "User Button";
linux,code = <116>;
gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
};
};
};
7. Summary
In this section, we learned about the device tree description methods for common devices:
Key Points:
- I2C Devices:
<span>#size-cells = <0></span>, device address is in<span>reg</span> - SPI Devices: Use
<span>reg</span>to specify chip select number,<span>spi-max-frequency</span>to set frequency - GPIO Devices: Require
<span>gpio-controller</span>and<span>#gpio-cells</span>attributes - PCIe Devices: Complex address mapping and interrupt routing
- Interrupt Configuration: Use standard interrupt attributes, support multi-level interrupts
Best Practices:
- Follow device binding specifications: Use standard
<span>compatible</span>strings - Correctly set address cells: Set
<span>#address-cells</span>and<span>#size-cells</span>according to bus type - Use header file constants: Define interrupt types, GPIO states, etc., through header files
- Organize nodes logically: Group by function, maintain clear hierarchy
- Add necessary comments: Explain device purpose and key parameters
Series Summary: Through this series of four articles, we have learned the basic concepts of device trees, compilation commands, syntax specifications, and common device descriptions. The device tree, as a standard method for hardware description, plays an important role in embedded Linux development. Mastering the use of device trees can greatly improve development efficiency and system portability.