dts文件分析---以ov5640为例,修改dts文件使ov5640使用第二个IPU
2019-07-12 13:48发布
生成海报
在ARMlinux中,每一个.dts文件都对应一个ARM的machine,这些文件都放在arch/arm/boot/dts文件夹中。同时,对于每一个SoC可能对应多个machine,这些dts文件中会包含许多共同的部分,所以就有了.dtsi文件。这个.dtsi文件类似C语言中的头文件,在其他的.dts文件中可以通过#include”xxxx.dtsi“
来包含这些头文件。
因为我们的开发板是imx6q-sabersd,所以首先查看对应的imx6q-sabresd.dts文件,有如下内容:
#include "imx6q.dtsi"
#include "imx6qdl-sabresd.dtsi"
/ {
model = "Freescale i.MX6 Quad SABRE Smart Device Board";
compatible = "fsl,imx6q-sabresd", "fsl,imx6q";
};
在.dts文件的每个设备,都有一个compatible属性,compatible属性用于用户驱动和设备的绑定。compatible属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,形式为",",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。
上述.dts文件中,root结点"/"的compatible属性compatible=
"fsl,imx6q-sabresd","fsl,imx6q";定义了系统的名称。Linux内核透过root结点"/"的compatible属性即可判断它启动的是什么machine。
但是在这个文件中,只有少量的设备,对于其他cpu等设备的描述信息都没有,肯定不是全部的设备信息,这时候就能看到这个文件中包含的其他文件,cpu等信息肯定是在其他地方描述的。
每个文件中都会有一个根结点/“,在编译设备文件的时候,编译器会将这些文件都放在同一个”/”结点下面,这样就不会担心各个文件中的根结点会重复,同时,编译器也会对不同文件中相同结点下的信息进行合并处理,在后面再具体分析。由于每个文件中都有一个根结点,这样就更方便我们书写这个设备树文件。
继续查看imx6q.dtsi文件:
/ {
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <0>;
next-level-cache = <&L2>;
operating-points = <
/* kHz uV */
1200000 1275000
996000 1250000
852000 1250000
792000 1175000
396000 975000
>;
fsl,soc-operating-points = <
/* ARM kHz SOC-PU uV */
1200000 1275000
996000 1250000
852000 1250000
792000 1175000
396000 1175000
>;
clock-latency = <61036>; /* two CLK32 periods */
clocks = <&clks IMX6QDL_CLK_ARM>,
<&clks IMX6QDL_CLK_PLL2_PFD2_396M>,
<&clks IMX6QDL_CLK_STEP>,
<&clks IMX6QDL_CLK_PLL1_SW>,
<&clks IMX6QDL_CLK_PLL1_SYS>,
<&clks IMX6QDL_PLL1_BYPASS>,
<&clks IMX6QDL_CLK_PLL1>,
<&clks IMX6QDL_PLL1_BYPASS_SRC> ;
clock-names = "arm", "pll2_pfd2_396m", "step",
"pll1_sw", "pll1_sys", "pll1_bypass", "pll1", "pll1_bypass_src";
arm-supply = <®_arm>;
pu-supply = <®_pu>;
soc-supply = <®_soc>;
};
cpu@1 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <1>;
next-level-cache = <&L2>;
};
cpu@2 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <2>;
next-level-cache = <&L2>;
};
cpu@3 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <3>;
next-level-cache = <&L2>;
};
};
这个文件中包含1个root结点"/";root结点下面含一系列子结点,本例中为"cpus";结点”cpus”下又有一个子结点”cpu0“,结点"cpu0"下又含有一系列子结点,为"cpu0","cpu1",”cpu2”和”cpu3”;
各结点都有一系列属性。这些属性可能为空,可能为字符串,可能为字符串数组,也可能为Cells(由u32整数组成)。
注意cpus和cpus的2个cpu子结点的命名,它们遵循的组织形式为:[@],<>中的内容是必选项,[]中的则为可选项。name是一个ASCII字符串,用于描述结点对应的设备类型,如3comEthernet适配器对应的结点name宜为ethernet,而不是3com509。如果一个结点描述的设备有地址,则应该给出@unit-address。多个相同类型设备结点的name可以一样,只要unit-address不同即可,如本例中含有cpu@0、cpu@1以及serial@101f0000与serial@101f2000这样的同名结点。设备的unit-address地址也经常在其对应结点的reg属性中给出。ePAPR标准给出了结点命名的规范。
下面来看cpus结点的信息:
#address-cells = <1>;
#size-cells = <0>;
首先前面的#代表是数字,对于这个cells的概念必须理解:一个cell代表一个u32整数,而这个address-cells代表子结点的address所包含的cells数目(即是由几个u32所组成的)。同样,size-cells代表子结点的size所包含的cells数目(即是由几个u32所组成的)。这两个概念与子结点的reg信息息息相关,也就是说,如果子结点中包含了reg信息的话,这个reg的表示方法是通过父结点的address-cells和size-cells来指定的。
(一)可寻址的设备使用如下信息来在DeviceTree中编码地址信息:
-
reg
-
#address-cells
-
#size-cells
其中reg的组织形式为reg=
,其中的每一组addresslength表明了设备使用的一个地址范围。address为1个或多个32位的整型(即cell),而length则为cell的列表或者为空(若#size-cells=
0)。address和length字段是可变长的,父结点的#address-cells和#size-cells分别决定了子结点的reg属性的address和length字段的长度。
以上面的信息为例:
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@1 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <1>;
next-level-cache = <&L2>;
};
cpu@2 {
compatible = "arm,cortex-a9";
device_type = "cpu";
reg = <2>;
next-level-cache = <&L2>;
};
};
cpus结点中指定了#address-cells=
<1>和#size-cells=
<0>,决定了4个cpu子结点的address为1,而length为空,于是形成了4个cpu的reg=
<0>; reg = <1>; reg = <2>;和reg=
<3>;。
再来看两个例子:
soc {
#address-cells = <1>;
#size-cells = <1>;
timer@00a00600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0x00a00600 0x20>;
};
ipu2: ipu@02800000 {
compatible = "fsl,imx6q-ipu";
reg = <0x02800000 0x400000>;
};
};
soc结点中指定了#address-cells=
<1>和#size-cells=
<1>,决定了下面timer子结点的address为1,length为1。
External-bus {
#address-cells = <2>
#size-cells = <1>;
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
interrupts = < 5 2 >;
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
external-bus结点中指定了#address-cells=
<2>和#size-cells=<1>,决定了下面ethernet子结点和flash子结点的address为2,length为1。同时需要注意的是,在这里数字的表示都是大端模式,如果address用两个cells来表示,那么想要知道这个外设的地址,就直接将两个cells里面的地址连接起来即可。
(二)DeviceTree中还可以表示中断连接信息,对于中断控制器而言,它提供如下属性:
interrupt-controller--- 这个属性为空,中断控制器应该加上此属性表明自己的身份;
#interrupt-cells与#address-cells和#size-cells相似,它表明连接此中断控制器的设备的interrupts属性的cell大小。
intc: interrupt-controller@00a01000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
#address-cells = <1>;
#size-cells = <1>;
interrupt-controller;
reg = <0x00a01000 0x1000>,
<0x00a00100 0x100>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
interrupt-parent = <&intc>;
ipu1: ipu@02400000 {
interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>,
<0 5 IRQ_TYPE_LEVEL_HIGH>;
};
};
在整个DeviceTree中,与中断相关的属性还包括:
interrupt-parent--- 设备结点透过它来指定它所依附的中断控制器的phandle,当结点没有指定interrupt-parent时,则从父结点继承。对于本例而言,soc结点指定了interrupt-parent=
<&intc>;其对应于intc:interrupt-controller@00a01000,而soc结点的子结点ipu1并未指定interrupt-parent,因此它们都继承了intc,即位于0x00a01000的中断控制器。
interrupts---用到了中断的设备结点通过它指定中断号、触发方法等,具体这个属性含有多少个cell,由它依附的中断控制器结点的#interrupt-cells属性决定。而具体每个cell又是什么含义,一般由驱动的实现决定,而且也会在DeviceTree的binding文档中说明。
对于其它的有关dts文件的信息,查看ePAPR标准。
下面是关于imx6q-sabresd开发板有关ipu信息的描述:
在imx6qdl.dtsi文件中:
mipi_csi: mipi_csi@021dc000 { /* MIPI-CSI */
compatible = "fsl,imx6q-mipi-csi2";
reg = <0x021dc000 0x4000>;
interrupts = <0 100 0x04>, <0 101 0x04>;
clocks = <&clks IMX6QDL_CLK_HSI_TX>,
<&clks IMX6QDL_CLK_EMI_SEL>,
<&clks IMX6QDL_CLK_VIDEO_27M>;
/* Note: clks 138 is hsi_tx, however, the dphy_c
* hsi_tx and pll_refclk use the same clk gate.
* In current clk driver, open/close clk gate do
* use hsi_tx for a temporary debug purpose.
*/
clock-names = "dphy_clk", "pixel_clk", "cfg_clk";
status = "disabled";
};
ipu1: ipu@02400000 {
compatible = "fsl,imx6q-ipu";
reg = <0x02400000 0x400000>;
interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>,
<0 5 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_IPU1>,
<&clks IMX6QDL_CLK_IPU1_DI0>, <&clks IMX6QDL_CLK_IPU1_DI1>,
<&clks IMX6QDL_CLK_IPU1_DI0_SEL>, <&clks IMX6QDL_CLK_IPU1_DI1_SEL>,
<&clks IMX6QDL_CLK_LDB_DI0>, <&clks IMX6QDL_CLK_LDB_DI1>;
clock-names = "bus",
"di0", "di1",
"di0_sel", "di1_sel",
"ldb_di0", "ldb_di1";
resets = <&src 2>;
bypass_reset = <0>;
};
在imx6q.dtsi文件中:
ipu2: ipu@02800000 {
compatible = "fsl,imx6q-ipu";
reg = <0x02800000 0x400000>;
interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>,
<0 7 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_IPU2>,
<&clks IMX6QDL_CLK_IPU2_DI0>, <&clks IMX6QDL_CLK_IPU2_DI1>,
<&clks IMX6QDL_CLK_IPU2_DI0_SEL>, <&clks IMX6QDL_CLK_IPU2_DI1_SEL>,
<&clks IMX6QDL_CLK_LDB_DI0>, <&clks IMX6QDL_CLK_LDB_DI1>;
clock-names = "bus",
"di0", "di1",
"di0_sel", "di1_sel",
"ldb_di0", "ldb_di1";
resets = <&src 4>;
bypass_reset = <0>;
};
在imx6qdl-sabresd.dtsi文件中:
v4l2_cap_0 {
compatible = "fsl,imx6q-v4l2-capture";
ipu_id = <0>;
csi_id = <0>;
mclk_source = <0>;
status = "okay";
};
v4l2_cap_1 {
compatible = "fsl,imx6q-v4l2-capture";
ipu_id = <0>;
csi_id = <1>;
mclk_source = <0>;
status = "okay";
};
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
ov564x: ov564x@3c {
compatible = "ovti,ov564x";
reg = <0x3c>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ipu1_2>;
clocks = <&clks IMX6QDL_CLK_CKO>;
clock-names = "csi_mclk";
DOVDD-supply = <&vgen4_reg>; /* 1.8v */
AVDD-supply = <&vgen3_reg>; /* 2.8v, on rev C board is VGEN3,
on rev B board is VGEN5 */
DVDD-supply = <&vgen2_reg>; /* 1.5v*/
pwn-gpios = <&gpio1 16 1>; /* active low: SD1_DAT0 */
rst-gpios = <&gpio1 17 0>; /* active high: SD1_DAT1 */
csi_id = <0>;
mclk = <24000000>;
mclk_source = <0>;
};
};
&i2c2 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
ov564x_mipi: ov564x_mipi@3c { /* i2c2 driver */
compatible = "ovti,ov564x_mipi";
reg = <0x3c>;
clocks = <&clks 201>;
clock-names = "csi_mclk";
DOVDD-supply = <&vgen4_reg>; /* 1.8v */
AVDD-supply = <&vgen3_reg>; /* 2.8v, rev C board is VGEN3
rev B board is VGEN5 */
DVDD-supply = <&vgen2_reg>; /* 1.5v*/
pwn-gpios = <&gpio1 19 1>; /* active low: SD1_CLK */
rst-gpios = <&gpio1 20 0>; /* active high: SD1_DAT2 */
csi_id = <1>;
mclk = <24000000>;
mclk_source = <0>;
};
};
&mipi_csi {
status = "okay";
ipu_id = <0>;
csi_id = <1>;
v_channel = <0>;
lanes = <2>;
};
在mxc_v4l2_capture.c文件中,
static struct platform_driver mxc_v4l2_driver = {
.driver = {
.name = "mxc_v4l2_capture",
.owner = THIS_MODULE,
.of_match_table = mxc_v4l2_dt_ids,
},
.id_table = imx_v4l2_devtype,
.probe = mxc_v4l2_probe,
.remove = mxc_v4l2_remove,
.suspend = mxc_v4l2_suspend,
.resume = mxc_v4l2_resume,
.shutdown = NULL,
};
static const struct of_device_id mxc_v4l2_dt_ids[] = {
{
.compatible = "fsl,imx6q-v4l2-capture",
.data = &imx_v4l2_devtype[IMX6_V4L2],
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, mxc_v4l2_dt_ids);
当内核中有匹配的驱动与设备的时候,就会调用mxc_v4l2_probe函数。是否匹配,在内核中是通过match函数的:
xxxxxxxxxxxxxxx
这个match函数中首先匹配of设备树类型的:匹配的就是这个compatible变量。向上搜索这个"fsl,imx6q-v4l2-capture",可以发现,在dts类文件中,匹配的是v4l2_cap_0和v4l2_cap_1。当这两个设备注册到内核中的时候,就会调用两次mxc_v4l2_probe函数。而在这个mxc_v4l2_probe函数中
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮