Device Tree 基本笔录

流程

一张比较经典的流程图
在这里插入图片描述

dtc编译的命令

make dtbs

dtsi: 一个SOC可能会有不同的board/machine, 把公用部分或者多个machine共同的部分提炼成.dtsi(公共提炼), 差异部分放进.dts, 然后.dts中include相应的.dtsi文件即可, 如果有同一节点的不同设置, dts中的配置会覆盖.dtsi的配置(最后生效)

dtc: dtc是编译dts的工具,可以在Ubuntu系统上通过指令apt-get install device-tree-compiler安装dtc工具,在内核源码scripts/dtc路径下已经包含了dtc工具

dtb: dtb(Device Tree Blob),dts经过dtc编译之后会得到dtb文件,dtb通过Bootloader引导程序(如U-Boot)加载到内核。所以Bootloader需要支持设备树才行;Kernel也需要加入设备树的支持

目录

一些不在主线内核的可能有自己的目录, 其它的一般会在默认目录.
DT文件默认目录: arch/*/boot/dts, 其中*代表架构arm/arm64//mips/riscv/x86等, 如stm32mp157a-dk1相关的设备树为arch/arm/boot/dts目录下的:

# 倒着推
stm32mp157a-dk1.dts	# dk1板子独有的描述, 比stm32mp157c-dk2少了触摸屏/蓝牙/WiFi等的描述
	- stm32mp157.dtsi	# gpu 和 dsi(显示接口) 描述
		- stm32mp153.dtsi	# cpu1, arm-pmu, can 描述
			- stm32mp151.dtsi	# cpu0, arm-pmu, psci, intc, gpio/timer等外设, clocks, booster, mlahb...
				- dt-bindings/interrupt-controller/arm-gic.h
				- dt-bindings/clock/stm32mp1-clks.h
				- dt-bindings/reset/stm32mp1-resets.h
	- stm32mp15-pinctrl.dtsi	# PINMUX描述
		- dt-bindings/pinctrl/stm32-pinfunc.h
	- stm32mp15xxac-pinctrl.dtsi	# pinctrl, gpioa~gpioi, gpioz 描述
	- stm32mp15xx-dkx.dtsi	# dk1/dk2 板子的共有硬件描述
		- dt-bindings/gpio/gpio.h
		- dt-bindings/mfd/st,stpmic1.h

格式

Devicetree 节点是使用节点名称和单元地址(可选)定义的, 大括号标记节点定义的开始和结束. 它们前面可以有一个标签. 节点的基本格式:

[label:] node-name[@unit-address] {
  [properties definitions]
  [child nodes]
};

# []可选

# [label:], 标签, 1~31个字符(字母,数字,下划线_), 不得以数字开头
# 通过在label前加上&符号来创建引用, 如 interrupt-affinity = <&cpu0>, <&cpu1>;

# 以前定义的节点/标签/属性可能被删除
# /delete-node/ node-name;
# /delete-node/ &label;
# /delete-property/ property-name;

# 属性的常见类型
# text string: 双引号, 如 compatible = "arm,cortex-a7";
# cells, 尖括号, u32类型, 64bit值用两个32位cell表示, 如 clock-frequency = <0x00000001 0x00000000>;
# binary data, 方括号, 如 local-mac-address = [00 00 12 34 56 78]; 等效 [000012345678]

# phandle, 是properties handle的缩写么? 值是一种引用设备树中另一个节点的方法, 简单的理解为句柄

# 引用一个节点的完整路径, 用大括号括起来
# interrupt-parent = < &{/soc/interrupt-controller@40000} >;

# 标签也可能出现在属性值的任何组件之前或之后,或者在元胞数组的元胞之间,或者在字节串的字节之间
# reg = reglabel: <0 sizelabel: 0x1000000>;
# prop = [ab cd ef byte4: 00 ff fe];
# str = start: "string value" end: ;

# 支持 C 样式 (/* ... */) 和 C++ 样式 (//) 注释

# 通过指定从根节点到所有后代节点到所需节点的完整路径,可以唯一标识设备树中的节点
# 如 /cpus/cpu@0

# compatible = "fsl,mpc8641", "ns16550";
# 在此示例中,操作系统将首先尝试查找支持 fsl,mpc8641 的设备驱动程序
# 如果未找到驱动程序,它将尝试找到支持更通用的 ns16550 设备类型的驱动程序

提取

以STM32MP157为例, STM32 MPU的产品系列如下

在这里插入图片描述

SOC公共部分提取出来:

  • STM32MP157/153有两个Cortex-A7, STM32MP151有一个Cortex-A7, 所以可以把公共的一个Cortex-A7和所有公共的外设提取出来, 做成 stm32mp151.dtsi文件, 这个文件内容是最多的.

  • STM32MP157/153比151多了一个A7和CANFD, 可以把这个Cortex-A7以及公共外设(如CANFD等)提取出来, 做成 stm32mp153.dtsi, 这个里面是157/153都有, 151没有的

  • STM32MP157比153多了3D GPU-DSI, 所以, 多出来的gpu和dtsi就组成了 stm32mp157.dtsi, 这个里面是157独有的

板子(dk1和dk2)公共部分提取出来:

  • stm32mp15xx-dkx.dtsi
  • dk1的描述构成stm32mp157a-dk1.dts, dk2独有的(多出蓝牙/WiFi/DSI等)构成stm32mp157c-dk2.dts

描述

根据实例注释说明部分常见的设备树语法

/* ==============================================================================
 * stm32mp157x-dk1.dts
 * ============================================================================*/
/dts-v1/;	// 将文件标识为version 1的DTS, 没有此标记的将被dtc视为version 0

#include "stm32mp157.dtsi"	// 类似C语言, 展开
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxac-pinctrl.dtsi"
#include "stm32mp15xx-dkx.dtsi"

/ {	// 每个dts或dtsi文件由一个root根节点"/{};"组成
	// model 指定制造商和设备型号, 推荐格式"manufacturer, model"
	model = "STMicroelectronics STM32MP157A-DK1 Discovery Board";
	// compatible指定系统名称, 包括"manufacturer, model"形式的字符串, 避免命名空间冲突
	compatible = "st,stm32mp157a-dk1", "st,stm32mp157";

	aliases {	// 别名, 小写字母/数字/-
		ethernet0 = &ethernet0;
		serial0 = &uart4;
		serial1 = &usart3;
		serial2 = &uart7;
	};
	
	// chosen, 运行时选择或指定的参数, 控制台的输入输出等
    // bootargs: 指定引导参数, 如 bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
    // stdout-path: 指定引导控制台输出的设备
    // stdin-path: 指定引导控制台输入的设备
	chosen {
		stdout-path = "serial0:115200n8";	// serial0就是上面的&uart4, 115200-N-8
	};
};

/* ==============================================================================
 * stm32mp157x-dk1.dts 节选
 * ============================================================================*/
/ {
	// 基地址, 片选号 等绝对地址所占字长, 单位u32, 64bit系统为2?
	#address-cells = <1>;
	// 长度所占字长, 单位u32, 32bit系统为1, 64bit系统为2?
	#size-cells = <1>;

	cpus {
		#address-cells = <1>;
		// cpus的size-cells应为0, 子节点属性中不需要大小, 则CPU地址按0,1...
		#size-cells = <0>;

		cpu0: cpu@0 {
			compatible = "arm,cortex-a7";
			clock-frequency = <650000000>;	//650MHz
			device_type = "cpu";
			// reg 属性的第一个值必须包括[unit-address], 也就是 cpu@0 中的0
			reg = <0>;
		};
	};
	
	...
};

/* ==============================================================================
 * stm32mp157xx-dkx.dtsi 节选
 * ============================================================================*/
/ {
	memory@c0000000 {
		device_type = "memory";
		//起始地址0xc0000000, 大小0x20000000(512MB), 最大支持1GB内存
		reg = <0xc0000000 0x20000000>;	
	};
};

//一些外设的例子

//spi
spi1: spi@44004000 {
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = "st,stm32h7-spi";
	reg = <0x44004000 0x400>;
	interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&rcc SPI1_K>;
	resets = <&rcc SPI1_R>;
	dmas = <&dmamux1 37 0x400 0x05>,
	       <&dmamux1 38 0x400 0x05>;
	dma-names = "rx", "tx";
	status = "disabled";
};
//spi 使用示例
//cs-gpios表示用作芯片选择的 GPIO 列表, 如cs-gpios = <&gpioz 3 0>;
//dmas:默认情况下,为所有 SPI 实例指定DMA
//&spi1 {
//	pinctrl-names = "default", "sleep";
//	pinctrl-0 = <&spi1_pins_a>;	//引脚配置
//	pinctrl-1 = <&spi1_sleep_pins_a>;
//	status = "okay";
//
//   foo@0 {
//       compatible = "spi-foo";	//spi设备驱动程序的名称
//       reg = <0>; /* CS #0 */		//spi器件关联的gpio芯片的索引
//       spi-max-frequency = <10000000>;	//spi最大时钟速度Hz
//   };
//};

//adc
//IIO(工业 I/O)是用于模数转换器 (ADC)、数模转换器 (DAC) 和各种类型传感器的子系统。
//由ADC Linux驱动程序使用,该驱动程序在IIO框架中注册相关信息
//分辨率设置 assigned-resolution-bits = <12> 
adc: adc@48003000 {	//soc配置, 勿修改
	compatible = "st,stm32mp1-adc-core";
	reg = <0x48003000 0x400>;	//起始地址0x48003000, 大小0x400(1KB)
	interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
		     <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&rcc ADC12>, <&rcc ADC12_K>;
	clock-names = "bus", "adc";
	interrupt-controller;
	st,syscfg = <&syscfg>;
	#interrupt-cells = <1>;
	#address-cells = <1>;
	#size-cells = <0>;
	status = "disabled";	//以上是adc1和adc2的公共资源

	adc1: adc@0 {
		compatible = "st,stm32mp1-adc";
		#io-channel-cells = <1>;
		reg = <0x0>;
		interrupt-parent = <&adc>;
		interrupts = <0>;
		dmas = <&dmamux1 9 0x400 0x01>;
		dma-names = "rx";
		status = "disabled";
	};

	adc2: adc@100 {
		compatible = "st,stm32mp1-adc";
		#io-channel-cells = <1>;
		reg = <0x100>;
		interrupt-parent = <&adc>;
		interrupts = <1>;
		dmas = <&dmamux1 10 0x400 0x01>;
		dma-names = "rx";
		status = "disabled";
	};
};

&adc {	//板级配置
	pinctrl-names = "default";
	pinctrl-0 = <&adc12_ain_pins_a>, <&adc12_usb_cc_pins_a>;	//配置使用的引脚
	vdd-supply = <&vdd>;
	vdda-supply = <&vdd>;
	vref-supply = <&vrefbuf>;
	status = "disabled";
	adc1: adc@0 {
		/*
		 * Type-C USB_PWR_CC1 & USB_PWR_CC2 on in18 & in19.
		 * Use at least 5 * RC time, e.g. 5 * (Rp + Rd) * C:
		 * 5 * (56 + 47kOhms) * 5pF => 2.5us.
		 * Use arbitrary margin here (e.g. 5us).
		 */
		st,min-sample-time-nsecs = <5000>;	// 最小采样时间, 5us
		/* AIN connector, USB Type-C CC1 & CC2 */
		st,adc-channels = <0 1 6 13 18 19>;	//adc通道, 参见 stm32mp15-pinctrl.dtsi
		status = "okay";	//启用adc1
	};
	adc2: adc@100 {
		/* AIN connector, USB Type-C CC1 & CC2 */
		st,adc-channels = <0 1 2 6 18 19>;
		st,min-sample-time-nsecs = <5000>;
		status = "okay";	//启用adc2
	};
};

//canfd
m_can1: can@4400e000 {
	compatible = "bosch,m_can";	//博世的M_CAN控制器
	// message_ram 其实地址0x44011000, 大小0x1400(5KB)
	reg = <0x4400e000 0x400>, <0x44011000 0x1400>;
	reg-names = "m_can", "message_ram";
	interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>,
		     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
	interrupt-names = "int0", "int1";
	clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>;
	clock-names = "hclk", "cclk";
	bosch,mram-cfg = <0x0 0 0 32 0 0 2 2>;
	status = "disabled";
};
m_can2: can@4400f000 {
	compatible = "bosch,m_can";
	reg = <0x4400f000 0x400>, <0x44011000 0x2800>;	//10KB
	reg-names = "m_can", "message_ram";
	interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>,
		     <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
	interrupt-names = "int0", "int1";
	clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>;
	clock-names = "hclk", "cclk";
	bosch,mram-cfg = <0x1400 0 0 32 0 0 2 2>;
	status = "disabled";
};
 &m_can1 {	//用法举例
 	pinctrl-names = "default", "sleep";         /* configure pinctrl modes for m_can1 */
 	pinctrl-0 = <&m_can1_pins_a>;               /* configure m_can1_pins_a as default pinctrl configuration for m_can1 */
 	pinctrl-1 = <&m_can1_sleep_pins_a>;         /* configure m_can1_sleep_pins_a as sleep pinctrl configuration for m_can1 */
 	status = "okay";                            /* enable m_can1 */ 
 };

//eth
//address-bits, 指定 MAC 地址中的位数, 如果未指定, 默认48, 如address-bits = <48>;
//local-mac-address, 指定分配给包含此属性的节点所描述的网络设备的 MAC 地址, 如local-mac-address = [ 00 00 12 34 56 78 ];
//mac-address, 指定启动程序上次使用的 MAC 地址, 若与local-mac不同, 使用此值, 如mac-address = [ 01 02 03 04 05 06 ];
//max-frame-size, 指定物理接口可以发送和接收的最大数据包长度(以字节为单位), 如max-frame-size = <1518>;
//phy-connection-type, 指定以太网设备和物理层 (PHY) 设备之间的接口类型, 如phy-connection-type = "mii";
//https://wiki.stmicroelectronics.cn/stm32mpu/wiki/Ethernet_device_tree_configuration
//3.3.5 RGMII,ETH _CLK(无PHY晶体)上具有25MHz,来自PHY的CLK125(参考时钟(标准RGMII时钟名称)由RCC SoC内部时钟提供)
ethernet0: ethernet@5800a000 {
	compatible = "st,stm32mp1-dwmac", "snps,dwmac-4.20a";
	reg = <0x5800a000 0x2000>;	//起始地址0x5800a000, 大小0x2000(8KB)
	reg-names = "stmmaceth";
	interrupts-extended = <&intc GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
	interrupt-names = "macirq";
	clock-names = "stmmaceth",
		      "mac-clk-tx",
		      "mac-clk-rx",
		      "eth-ck",
		      "ethstp";
	clocks = <&rcc ETHMAC>,
		 <&rcc ETHTX>,
		 <&rcc ETHRX>,
		 <&rcc ETHCK_K>,
		 <&rcc ETHSTP>;
	st,syscon = <&syscfg 0x4>;
	snps,mixed-burst;
	snps,pbl = <2>;
	snps,en-tx-lpi-clockgating;
	snps,axi-config = <&stmmac_axi_config_0>;
	snps,tso;
	status = "disabled";
};

&ethernet0 {
	status = "okay";	//状态OK, 启动以太网
	pinctrl-0 = <&ethernet0_rgmii_pins_a>;	//见下面的pinmux映射(TX/RX/SMI接口)
	pinctrl-1 = <&ethernet0_rgmii_sleep_pins_a>;	//让rgmii所有引脚sleep
	pinctrl-names = "default", "sleep";
	phy-mode = "rgmii-id";	// rgmii, id 0
	max-speed = <1000>;	// max-speed: 指定设备支持的最大速度(Mbit/s)
	phy-handle = <&phy0>;	// phy-handle: 指定对表示连接到此以太网设备的物理层 (PHY) 设备的节点的引用

	mdio0 {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "snps,dwmac-mdio";	//drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c, mdio总会释放
		phy0: ethernet-phy@0 {
			reg = <0>;
		};
	};
};

ethernet0_rgmii_pins_a: rgmii-0 {	//和原理图的引脚对上
	pins1 {
		pinmux = <STM32_PINMUX('G', 5, AF11)>, /* ETH_RGMII_CLK125 */
			 <STM32_PINMUX('G', 4, AF11)>, /* ETH_RGMII_GTX_CLK */
			 <STM32_PINMUX('G', 13, AF11)>, /* ETH_RGMII_TXD0 */
			 <STM32_PINMUX('G', 14, AF11)>, /* ETH_RGMII_TXD1 */
			 <STM32_PINMUX('C', 2, AF11)>, /* ETH_RGMII_TXD2 */
			 <STM32_PINMUX('E', 2, AF11)>, /* ETH_RGMII_TXD3 */
			 <STM32_PINMUX('B', 11, AF11)>, /* ETH_RGMII_TX_CTL */
			 <STM32_PINMUX('C', 1, AF11)>; /* ETH_MDC */
		bias-disable;
		drive-push-pull;
		slew-rate = <2>;
	};
	pins2 {
		pinmux = <STM32_PINMUX('A', 2, AF11)>; /* ETH_MDIO */
		bias-disable;
		drive-push-pull;
		slew-rate = <0>;
	};
	pins3 {
		pinmux = <STM32_PINMUX('C', 4, AF11)>, /* ETH_RGMII_RXD0 */
			 <STM32_PINMUX('C', 5, AF11)>, /* ETH_RGMII_RXD1 */
			 <STM32_PINMUX('B', 0, AF11)>, /* ETH_RGMII_RXD2 */
			 <STM32_PINMUX('B', 1, AF11)>, /* ETH_RGMII_RXD3 */
			 <STM32_PINMUX('A', 1, AF11)>, /* ETH_RGMII_RX_CLK */
			 <STM32_PINMUX('A', 7, AF11)>; /* ETH_RGMII_RX_CTL */
		bias-disable;
	};
};

ethernet0_rgmii_sleep_pins_a: rgmii-sleep-0 {	// 让rgmii所有引脚sleep
	pins1 {
		pinmux = <STM32_PINMUX('G', 5, ANALOG)>, /* ETH_RGMII_CLK125 */
			 <STM32_PINMUX('G', 4, ANALOG)>, /* ETH_RGMII_GTX_CLK */
			 <STM32_PINMUX('G', 13, ANALOG)>, /* ETH_RGMII_TXD0 */
			 <STM32_PINMUX('G', 14, ANALOG)>, /* ETH_RGMII_TXD1 */
			 <STM32_PINMUX('C', 2, ANALOG)>, /* ETH_RGMII_TXD2 */
			 <STM32_PINMUX('E', 2, ANALOG)>, /* ETH_RGMII_TXD3 */
			 <STM32_PINMUX('B', 11, ANALOG)>, /* ETH_RGMII_TX_CTL */
			 <STM32_PINMUX('A', 2, ANALOG)>, /* ETH_MDIO */
			 <STM32_PINMUX('C', 1, ANALOG)>, /* ETH_MDC */
			 <STM32_PINMUX('C', 4, ANALOG)>, /* ETH_RGMII_RXD0 */
			 <STM32_PINMUX('C', 5, ANALOG)>, /* ETH_RGMII_RXD1 */
			 <STM32_PINMUX('B', 0, ANALOG)>, /* ETH_RGMII_RXD2 */
			 <STM32_PINMUX('B', 1, ANALOG)>, /* ETH_RGMII_RXD3 */
			 <STM32_PINMUX('A', 1, ANALOG)>, /* ETH_RGMII_RX_CLK */
			 <STM32_PINMUX('A', 7, ANALOG)>; /* ETH_RGMII_RX_CTL */
	};
};

compatable的值是和驱动里面的程序对的上的, 如mdio0的compatible = "snps,dwmac-mdio";, 在 drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c 里面

/**
 * stmmac_dt_phy - parse device-tree driver parameters to allocate PHY resources
 * @plat: driver data platform structure
 * @np: device tree node
 * @dev: device pointer
 * Description:
 * The mdio bus will be allocated in case of a phy transceiver is on board;
 * it will be NULL if the fixed-link is configured.
 * If there is the "snps,dwmac-mdio" sub-node the mdio will be allocated
 * in any case (for DSA, mdio must be registered even if fixed-link).
 * The table below sums the supported configurations:
 *	-------------------------------
 *	snps,phy-addr	|     Y
 *	-------------------------------
 *	phy-handle	|     Y
 *	-------------------------------
 *	fixed-link	|     N
 *	-------------------------------
 *	snps,dwmac-mdio	|
 *	  even if	|     Y
 *	fixed-link	|
 *	-------------------------------
 *
 * It returns 0 in case of success otherwise -ENODEV.
 */
static int stmmac_dt_phy(struct plat_stmmacenet_data *plat,
			 struct device_node *np, struct device *dev)
{
	bool mdio = !of_phy_is_fixed_link(np);
	static const struct of_device_id need_mdio_ids[] = {
		{ .compatible = "snps,dwc-qos-ethernet-4.10" },
		{},
	};

	if (of_match_node(need_mdio_ids, np)) {
		plat->mdio_node = of_get_child_by_name(np, "mdio");
	} else {
		/**
		 * If snps,dwmac-mdio is passed from DT, always register
		 * the MDIO
		 */
		for_each_child_of_node(np, plat->mdio_node) {
			if (of_device_is_compatible(plat->mdio_node,
						    "snps,dwmac-mdio"))
				break;
		}
	}

	if (plat->mdio_node) {
		dev_dbg(dev, "Found MDIO subnode\n");
		mdio = true;
	}

	if (mdio) {
		plat->mdio_bus_data =
			devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data),
				     GFP_KERNEL);
		if (!plat->mdio_bus_data)
			return -ENOMEM;

		plat->mdio_bus_data->needs_reset = true;
	}

	return 0;
}

整体内存映射

在这里插入图片描述
在设备树中的映射举例

// stm32mp151.dtsi
dma-ranges = <0x00000000 0x38000000 0x10000>,	// RETRAM 64KB映射
	     <0x10000000 0x10000000 0x60000>,	// MCU SRAM 384KB映射
	     <0x30000000 0x30000000 0x60000>;

还有中断映射等, 此处略…

参考

欢迎扫描二维码关注微信公众号, 及时获取最新文章:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值