camera驱动开发 之 DTS
DTS/DTSI/DTC/DTB
dts
设备树源文件,描述某个 具体的设备板子 所用的硬件资源(如 SoC 型号、挂载的摄像头、LCD、I2C 地址等);类似“主函数”,最终会被编译成 .dtb 供内核使用
设备树的公共部分
dtsi
一个SoC可能对应多个machine,如果每个machine的设备树都写成一个完全独立的.dts文件,那么势必相当一些.dts文件有重复的部分,为了解决这个问题,Linux设备树目录把一个SoC公用的部分或者多个machine共同的部分提炼为相应的.dtsi文件。这样每个.dts就只有自己差异的部分,公有的部分只需要"include"相应的.dtsi文件, 这样就是整个设备树的管理更加有序。类似 C 的 .h 头文件,可以被多个 .dts 文件 #include。
dtc
编译器工具,负责将 .dts/.dtsi 编译成 .dtb;
dtc -I dts -O dtb -o rk3568-board.dtb rk3568-board.dts // -I dts:输入是设备树源文件;-O dtb:输出为设备树 blob(二进制);-o 指定输出文件。
dtb
编译生成的二进制文件,供内核在启动时读取;bootloader(如 U-Boot)会将 .dtb 加载到内存中;Linux 内核启动时读取 .dtb,根据描述来初始化硬件;.dtb 是最终给设备使用的配置文件。
+-------------+ +-------------+
| .dtsi |<----->| .dts |
| (通用部分) | | (板级配置) |
+-------------+ +-------------+
↓ ↓
======合并=======
↓
+---------------+
| dtc 编译器 |
+---------------+
↓
+---------------+
| .dtb 文件 |
+---------------+
↓
+---------------+
| bootloader 加载|
+---------------+
↓
+-------------------+
| 内核解析 device tree|
+-------------------+
↓
+-------------------------+
|驱动匹配(of_match_table)|
+-------------------------+
dts 语法结构
设备树是一个由节点和属性组成的简单树形结构。属性是键值对,节点可能同时包含属性和子节点。例如,下面是一个 .dts 格式的简单树形结构;
dts 示例注释
点击查看代码
/dts-v1/;
/ {
//compatible 指定系统名称。
//它包含一个形式为“<manufacturer>,<model>”的字符串;
//制造商:acme,型号:coyotes-revenge
compatible = "acme,coyotes-revenge";
#address-cells = <1>;
#size-cells = <1>;
//父节点:cpus,子节点:cpu@0,cpu@1;
//制造商:arm,型号:cortex-a9;说明是 ARM 的双核 Cortex A9 系统。
//在 cpus 节点中,#address-cells 设置为 1,#size-cells 设置为 0,
//这意味着子设备注册值是表示地址的单一 uint32,没有大小字段。
//在这种情况下,两个 CPU 的地址分别为 0 和 1。cpu 节点的 #size-cells 值为 0,
//因为每个 cpu 只分配一个地址。
cpus {
#address-cells = <1>;
#size-cells = <0>;
interrupt-parent = <&intc>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg = <1>;
};
};
//按照惯例,如果一个节点有一个 reg 属性,那么节点名称中就必须包含单元地址,
//也就是 reg 属性中的第一个地址值;serial@101F0000 reg = <0x101f0000 0x1000>;
//根节点的子节点已经在使用 CPU 的地址域,因此不需要任何明确的映射。
//例如,serial@101f0000 设备直接被分配了 0x101f0000 地址。
serial@101F0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >;
interrupts = < 1 0 >;
};
serial@101F2000 {
compatible = "arm,pl011";
reg = <0x101f2000 0x1000 >;
interrupts = < 1 0 >;
};
gpio@101F3000 {
compatible = "arm,pl061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
interrupts = < 3 0 >;
};
//interrupt controller 中断控制器;标签 “intc: ”已被添加到中断控制器节点中;
//每个设备都使用中断属性来指定不同的中断输入线。
intc: interrupt-controller@10140000 {
compatible = "arm,pl190";
reg = <0x10140000 0x1000 >;
interrupt-controller;
#interrupt-cells = <2>;
};
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
interrupts = < 4 0 >;
};
//external-bus 使用两个单元格来表示地址值;一个用于表示片选编号,另一个用于表示从片选基数开始的偏移量。
//长度字段#size-cells保留为一个单元格,因为只有地址的偏移部分需要有一个范围。
//因此,在本示例中,每个注册表reg 项包含 3 个单元格:片选编号、偏移量和长度。
//reg = <0 0 0x1000>; 片选 0 偏移量 0 长度0x1000;
//非根节点的直接子节点不使用 CPU 的地址域。为了获得内存映射地址,设备树必须指定如何将地址从一个域转换到另一个域
//range属性是一个地址转换列表某些总线(如外部芯片片选、多路桥接)地址不能直接被 CPU 访问;
//需要告知 Linux:子节点地址如何映射到系统地址空间;
//Linux 驱动在 probe 阶段用 of_translate_address() 解析 reg 时会使用 ranges;
external-bus {
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 // Chipselect 1, Ethernet 映射到0x10100000
1 0 0x10100000 0x10000 // Chipselect 2, i2c controller 映射到0x10100000
2 0 0x30000000 0x1000000>; // Chipselect 3, NOR Flash 映射到0x30000000
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};
//Non Memory Mapped Devices I2C
//子设备rtc不能被 CPU 直接访问。由父设备i2c的驱动程序将代表 CPU 执行间接访问。
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>;
interrupts = < 6 2 >;
rtc@58 {
compatible = "maxim,ds1338";
reg = <58>;
interrupts = < 7 3 >;
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
};
dts中断映射补充
与遵循设备树自然结构的地址范围转换不同,中断信号可以从机器中的任何设备发出并终止。与在设备树中自然表达的设备寻址不同,中断信号表达为独立于设备树的节点之间的连接。有四种属性可用于描述中断连接:
- interrupt-controller - 一个空属性,将节点声明为接收中断信号的设备;
-
interrupt-cells - 是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符,用来描述子节点中"interrupts"属性使用了父节点中的interrupts属性的具体的哪个值。一般,如果父节点的该属性的值是3,则子节点的interrupts一个cell的三个32bits整数值分别为:<中断域 中断 触发方式>,如果父节点的该属性是2,则是<中断 触发方式>
- interrupt-parent - 设备节点的一个属性,包含该节点所连接的中断控制器的指针。没有中断父节点属性的节点也可从其父节点继承该属性;
- interrupts -中断指定符,设备上的每个中断输出信号都有一个指定符;
点击查看代码
//interrupt-controller表示这个节点是一个中断控制器,需要注意的是,一个SoC中可能有不止一个中断控制器;
//#interrupt-cells的值是4,
//第0位:中断控制器编号:0表主中断控制器,1表示子中断控制器;
//第1位:表示子中断所属的主中断控制器的硬件中断号:对于子中断,这个参数才有意义;
//第2位:硬件中断编号:表示位于当前中断控制器的硬件中断号,比如EINT0~EINT3都是直接连接到主中断控制器的,
//所以其对应的中断控制器编号为0,硬件中断编号为0~3;
//第3位:中断类型:1为上升沿触发、2位下降沿触发、3位双边沿触发、4位高电平触发、8位低电平触发;
intc:interrupt-controller@4a000000 {
compatible = "samsung,s3c2410-irq";
reg = <0x4a000000 0x100>;
interrupt-controller;
#interrupt-cells = <4>;
};
//interrupts = <1 28 0 4> 描述的是带有子中断控制器,主中断硬件中断号为28,子中断硬件中断编号为0,高定平触发;
uart0: serial@50000000 {
compatible = "samsung,s3c2410-uart";
reg = <0x50000000 0x4000>;
interrupts = <1 28 0 4>, <1 28 1 4>;
status = "disabled";
};
camera部分示例
DTSI
点击查看代码
...
csi2_0: csi2_0@0xff118123 {
compatible = "TEST,csi2-0";
reg = <0xff118123 0x68>,
<0xff119123 0x290>,
<0xff11a123 0xd8>;
...
reg-names = "name1", "name2", "name3"...;
status = "disabled";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
};
port@1 {
reg = <1>;
csi2_0_out: endpoint {
remote-endpoint = <&adap0_in>;
};
};
};
};
adap0: adap0@ff11b123 {
compatible = "TEST,adap0";
reg = <0xff11b123 0xe4>;
reg-names = "adap";
status = "disabled";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
adap0_in: endpoint {
remote-endpoint = <&csi2_0_out>;
};
};
port@1 {
reg = <1>;
adap0_out: endpoint {
remote-endpoint = <&isp_virt0_in>;
};
};
};
};
isp: isp@ff100000 {
compatible = "TEST,isp";
reg = <0xff100000 0x715c>,
<0xff110000 0x8a4>,
<0xff111800 0x19c>,
<0xff111c00 0x180>,
...
reg-names = "ispname1", "isp2name", "isp3name",
"ispname4",..
//interrupt-parent = <&intc>;
interrupts = <0 159 1>,
<0 158 1>,
<0 157 1>,
<0 156 1>;
status = "disabled";
};
isp_virt0: isp-virt0 {
compatible = "TETS,isp-virt0";
isp = <&isp>;
// power-domains = <>;
memory-region = <&isp_reserved>;
status = "disabled";
port {
isp_virt0_in: endpoint {
remote-endpoint = <&adap0_out>;
};
};
};
...
DTS
点击查看代码
/ {
compatible = "TEST,a";
model = "TEST Meson a";
aliases {
serial0 = &uart_B;
i2c0 = &i2c0;
spi0 = &spifc;
tsensor0 = &tsensor;
};
memory@0 {
device_type = "memory";
linux,usable-memory = <0x0 0x80000000>;
};
...
}
...
&i2c0 {
status = "okay";
clock-frequency = <100000>;
imx290: sensor@1a {
compatible = "sony,imx290";
reg = <0x1a>;
clock-frequency = <37125000>;
port {
imx290_out: endpoint {
data-lanes = <0 1 2 3>;
link-frequencies = /bits/ 64 <222750000 148500000>;
remote-endpoint = <&csi2_0_in>;
};
};
};
};
&csi2_0 {
status = "okay";
ports {
port@0 {
csi2_0_in: endpoint {
remote-endpoint = <&imx290_out>;
clock-lanes = <4>;
data-lanes = <0 1 2 3>;
clock-noncontinuous;
};
};
};
};
&adap0 {
status = "okay";
};
&isp {
status = "okay";
};
&isp_virt0 {
status = "okay";
};
...
参考文档:
https://www.cnblogs.com/zyly/p/17266960.html
https://blog.csdn.net/qq_39400113/article/details/127939224
https://elinux.org/Device_Tree_Usage#Basic_Data_Format
https://www.cnblogs.com/xiaojiang1025/p/6131381.html
https://www.cnblogs.com/xinghuo123/p/12968942.html
https://blog.csdn.net/u012489236/article/details/97137007
点击查看代码
浙公网安备 33010602011771号