精华内容
下载资源
问答
  • 在启用了Credential Guard或Device Guard的Windows 10主机上启动12.5版之前的VMware Workstation中的虚拟机时,将显示蓝色诊断屏幕(BSOD)。 会看到类似于以下内容的错误: VMware Workstation和Device / ...

    在启用了Credential Guard或Device Guard的Windows 10主机上启动12.5版之前的VMware Workstation中的虚拟机时,将显示蓝色诊断屏幕(BSOD)。
    会看到类似于以下内容的错误:
    VMware Workstation和Device / Credential Guard不兼容。禁用Device / Credential Guard后,可以运行VMware Workstation。

    原因:
    1、出现此问题的原因是Device Guard或Credential Guard与Workstation不兼容。

    2、Windows系统的Hyper-V不兼容导致。

    解决方法:

    有热心网友反馈:1909版本的win用不了很有可能是沙盒没关,把沙盒功能关了重启一下就好 (我没试过!)

    步骤一:禁用Device Guard或Credential Guard:

    1. 禁用用于启用Credential Guard的组策略设置。
      1. 在主机操作系统上,右键单击“开始” > “运行”,键入gpedit.msc,然后单击“ 确定”。本地组策略编辑器打开。
      2. 转至本地计算机策略 > 计算机配置 > 管理模板>系统 >Device Guard(或者是: 设备防护) > 启用基于虚拟化的安全性
      3. 选择已禁用
         
    2. 转到“ 控制面板” >“ 卸载程序” >“ 打开或关闭Windows功能”以关闭Hyper-V
    3. 选择不重启

    步骤二:通过命令关闭Hyper-V(控制面板关闭Hyper-V起不到决定性作用,要彻底关闭Hyper-V) 

            以管理员身份运行Windows Powershell (管理员)(Windows键+X)

            运行下面命令并重启电脑:

    bcdedit /set hypervisorlaunchtype off
    展开全文
  • ARM Linux 3.x的设备树(Device Tree)

    万次阅读 多人点赞 2013-01-01 17:32:36
    本文部分案例和文字英文原版来源于http://devicetree.org/Device_Tree_Usage 更多精华文章请扫描下方二维码关注Linux阅码场 1. ARM Device Tree起源 Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称...

    本文部分案例和文字英文原版来源于 http://devicetree.org/Device_Tree_Usage

    更多精华文章请扫描下方二维码关注Linux阅码场

    1.    ARM Device Tree起源

    Linus Torvalds在2011年3月17日的ARM Linux邮件列表宣称“this whole ARM thing is a f*cking pain in the ass”,引发ARM Linux社区的地震,随后ARM社区进行了一系列的重大修正。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。读者有兴趣可以统计下常见的s3c2410、s3c6410等板级目录,代码量在数万行。
    社区必须改变这种局面,于是PowerPC等其他体系架构下已经使用的Flattened Device Tree(FDT)进入ARM社区的视野。Device Tree是一种描述硬件的数据结构,它起源于 OpenFirmware (OF)。在Linux 2.6中,ARM架构的板极硬件细节过多地被硬编码在arch/arm/plat-xxx和arch/arm/mach-xxx,采用Device Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。
    Device Tree由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。在Device Tree中,可描述的信息包括(原先这些信息大多被hard code到kernel中):

    • CPU的数量和类别
    • 内存基地址和大小
    • 总线和桥
    • 外设连接
    • 中断控制器和中断使用情况
    • GPIO控制器和GPIO使用情况
    • Clock控制器和Clock使用情况

    它基本上就是画一棵电路板上CPU、总线、设备组成的树,Bootloader会将这棵树传递给内核,然后内核可以识别这棵树,并根据它展开出Linux内核中的platform_device、i2c_client、spi_device等设备,而这些设备用到的内存、IRQ等资源,也被传递给了内核,内核会将这些资源绑定给展开的相应的设备。

    2.    Device Tree组成和结构

    整个Device Tree牵涉面比较广,即增加了新的用于描述设备硬件信息的文本格式,又增加了编译这一文本的工具,同时Bootloader也需要支持将编译后的Device Tree传递给Linux内核。

    DTS (device tree source)

    .dts文件是一种ASCII 文本格式的Device Tree描述,此文本格式非常人性化,适合人类的阅读习惯。基本上,在ARM Linux在,一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts/目录。由于一个SoC可能对应多个machine(一个SoC可以对应多个产品和电路板),势必这些.dts文件需包含许多共同的部分,Linux内核为了简化,把SoC公用的部分或者多个machine共同的部分一般提炼为.dtsi,类似于C语言的头文件。其他的machine对应的.dts就include这个.dtsi。譬如,对于VEXPRESS而言,vexpress-v2m.dtsi就被vexpress-v2p-ca9.dts所引用, vexpress-v2p-ca9.dts有如下一行:
    /include/ "vexpress-v2m.dtsi"
    当然,和C语言的头文件类似,.dtsi也可以include其他的.dtsi,譬如几乎所有的ARM SoC的.dtsi都引用了skeleton.dtsi。
    .dts(或者其include的.dtsi)基本元素即为前文所述的结点和属性:

    / {
        node1 {
            a-string-property = "A string";
            a-string-list-property = "first string", "second string";
            a-byte-data-property = [0x01 0x23 0x34 0x56];
            child-node1 {
                first-child-property;
                second-child-property = <1>;
                a-string-property = "Hello, world";
            };
            child-node2 {
            };
        };
        node2 {
            an-empty-property;
            a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
            child-node1 {
            };
        };
    };

    上述.dts文件并没有什么真实的用途,但它基本表征了一个Device Tree源文件的结构:
    1个root结点"/";
    root结点下面含一系列子结点,本例中为"node1" 和 "node2";
    结点"node1"下又含有一系列子结点,本例中为"child-node1" 和 "child-node2";
    各结点都有一系列属性。这些属性可能为空,如" an-empty-property";可能为字符串,如"a-string-property";可能为字符串数组,如"a-string-list-property";可能为Cells(由u32整数组成),如"second-child-property",可能为二进制数,如"a-byte-data-property"。
    下面以一个最简单的machine为例来看如何写一个.dts文件。假设此machine的配置如下:
    1个双核ARM Cortex-A9 32位处理器;
    ARM的local bus上的内存映射区域分布了2个串口(分别位于0x101F1000 和 0x101F2000)、GPIO控制器(位于0x101F3000)、SPI控制器(位于0x10170000)、中断控制器(位于0x10140000)和一个external bus桥;
    External bus桥上又连接了SMC SMC91111 Ethernet(位于0x10100000)、I2C控制器(位于0x10160000)、64MB NOR Flash(位于0x30000000);
    External bus桥上连接的I2C控制器所对应的I2C总线上又连接了Maxim DS1338实时钟(I2C地址为0x58)。
    其对应的.dts文件为:

    / {
        compatible = "acme,coyotes-revenge";
        #address-cells = <1>;
        #size-cells = <1>;
        interrupt-parent = <&intc>;
    
        cpus {
            #address-cells = <1>;
            #size-cells = <0>;
            cpu@0 {
                compatible = "arm,cortex-a9";
                reg = <0>;
            };
            cpu@1 {
                compatible = "arm,cortex-a9";
                reg = <1>;
            };
        };
    
        serial@101f0000 {
            compatible = "arm,pl011";
            reg = <0x101f0000 0x1000 >;
            interrupts = < 1 0 >;
        };
    
        serial@101f2000 {
            compatible = "arm,pl011";
            reg = <0x101f2000 0x1000 >;
            interrupts = < 2 0 >;
        };
    
        gpio@101f3000 {
            compatible = "arm,pl061";
            reg = <0x101f3000 0x1000
                   0x101f4000 0x0010>;
            interrupts = < 3 0 >;
        };
    
        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 {
            #address-cells = <2>
            #size-cells = <1>;
            ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
                      1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
                      2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash
    
            ethernet@0,0 {
                compatible = "smc,smc91c111";
                reg = <0 0 0x1000>;
                interrupts = < 5 2 >;
            };
    
            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文件中,root结点"/"的compatible 属性compatible = "acme,coyotes-revenge";定义了系统的名称,它的组织形式为:<manufacturer>,<model>。Linux内核透过root结点"/"的compatible 属性即可判断它启动的是什么machine。
    在.dts文件的每个设备,都有一个compatible 属性,compatible属性用户驱动和设备的绑定。compatible 属性是一个字符串的列表,列表中的第一个字符串表征了结点代表的确切设备,形式为"<manufacturer>,<model>",其后的字符串表征可兼容的其他设备。可以说前面的是特指,后面的则涵盖更广的范围。如在arch/arm/boot/dts/vexpress-v2m.dtsi中的Flash结点:

    flash@0,00000000 {
         compatible = "arm,vexpress-flash", "cfi-flash";
         reg = <0 0x00000000 0x04000000>,
         <1 0x00000000 0x04000000>;
         bank-width = <4>;
     };

    compatible属性的第2个字符串"cfi-flash"明显比第1个字符串"arm,vexpress-flash"涵盖的范围更广。
    再比如,Freescale MPC8349 SoC含一个串口设备,它实现了国家半导体(National Semiconductor)的ns16550 寄存器接口。则MPC8349串口设备的compatible属性为compatible = "fsl,mpc8349-uart", "ns16550"。其中,fsl,mpc8349-uart指代了确切的设备, ns16550代表该设备与National Semiconductor 的16550 UART保持了寄存器兼容。
    接下来root结点"/"的cpus子结点下面又包含2个cpu子结点,描述了此machine上的2个CPU,并且二者的compatible 属性为"arm,cortex-a9"。
    注意cpus和cpus的2个cpu子结点的命名,它们遵循的组织形式为:<name>[@<unit-address>],<>中的内容是必选项,[]中的则为可选项。name是一个ASCII字符串,用于描述结点对应的设备类型,如3com Ethernet适配器对应的结点name宜为ethernet,而不是3com509。如果一个结点描述的设备有地址,则应该给出@unit-address。多个相同类型设备结点的name可以一样,只要unit-address不同即可,如本例中含有cpu@0、cpu@1以及serial@101f0000与serial@101f2000这样的同名结点。设备的unit-address地址也经常在其对应结点的reg属性中给出。ePAPR标准给出了结点命名的规范。
    可寻址的设备使用如下信息来在Device Tree中编码地址信息:

    •     reg
    •     #address-cells
    •     #size-cells

    其中reg的组织形式为reg = <address1 length1 [address2 length2] [address3 length3] ... >,其中的每一组address length表明了设备使用的一个地址范围。address为1个或多个32位的整型(即cell),而length则为cell的列表或者为空(若#size-cells = 0)。address 和 length 字段是可变长的,父结点的#address-cells和#size-cells分别决定了子结点的reg属性的address和length字段的长度。在本例中,root结点的#address-cells = <1>;和#size-cells = <1>;决定了serial、gpio、spi等结点的address和length字段的长度分别为1。cpus 结点的#address-cells = <1>;和#size-cells = <0>;决定了2个cpu子结点的address为1,而length为空,于是形成了2个cpu的reg = <0>;和reg = <1>;。external-bus结点的#address-cells = <2>和#size-cells = <1>;决定了其下的ethernet、i2c、flash的reg字段形如reg = <0 0 0x1000>;、reg = <1 0 0x1000>;和reg = <2 0 0x4000000>;。其中,address字段长度为0,开始的第一个cell(0、1、2)是对应的片选,第2个cell(0,0,0)是相对该片选的基地址,第3个cell(0x1000、0x1000、0x4000000)为length。特别要留意的是i2c结点中定义的 #address-cells = <1>;和#size-cells = <0>;又作用到了I2C总线上连接的RTC,它的address字段为0x58,是设备的I2C地址。
    root结点的子结点描述的是CPU的视图,因此root子结点的address区域就直接位于CPU的memory区域。但是,经过总线桥后的address往往需要经过转换才能对应的CPU的memory映射。external-bus的ranges属性定义了经过external-bus桥后的地址范围如何映射到CPU的memory区域。

            ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
                      1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
                      2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash

    ranges是地址转换表,其中的每个项目是一个子地址、父地址以及在子地址空间的大小的映射。映射表中的子地址、父地址分别采用子地址空间的#address-cells和父地址空间的#address-cells大小。对于本例而言,子地址空间的#address-cells为2,父地址空间的#address-cells值为1,因此0 0  0x10100000   0x10000的前2个cell为external-bus后片选0上偏移0,第3个cell表示external-bus后片选0上偏移0的地址空间被映射到CPU的0x10100000位置,第4个cell表示映射的大小为0x10000。ranges的后面2个项目的含义可以类推。
    Device Tree中还可以中断连接信息,对于中断控制器而言,它提供如下属性:
    interrupt-controller – 这个属性为空,中断控制器应该加上此属性表明自己的身份;
    #interrupt-cells – 与#address-cells 和 #size-cells相似,它表明连接此中断控制器的设备的interrupts属性的cell大小。
    在整个Device Tree中,与中断相关的属性还包括:
    interrupt-parent – 设备结点透过它来指定它所依附的中断控制器的phandle,当结点没有指定interrupt-parent 时,则从父级结点继承。对于本例而言,root结点指定了interrupt-parent = <&intc>;其对应于intc: interrupt-controller@10140000,而root结点的子结点并未指定interrupt-parent,因此它们都继承了intc,即位于0x10140000的中断控制器。
    interrupts – 用到了中断的设备结点透过它指定中断号、触发方法等,具体这个属性含有多少个cell,由它依附的中断控制器结点的#interrupt-cells属性决定。而具体每个cell又是什么含义,一般由驱动的实现决定,而且也会在Device Tree的binding文档中说明。譬如,对于ARM GIC中断控制器而言,#interrupt-cells为3,它3个cell的具体含义Documentation/devicetree/bindings/arm/gic.txt就有如下文字说明:

    01   The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
    02   interrupts.
    03
    04   The 2nd cell contains the interrupt number for the interrupt type.
    05   SPI interrupts are in the range [0-987].  PPI interrupts are in the
    06   range [0-15].
    07
    08   The 3rd cell is the flags, encoded as follows:
    09         bits[3:0] trigger type and level flags.
    10                 1 = low-to-high edge triggered
    11                 2 = high-to-low edge triggered
    12                 4 = active high level-sensitive
    13                 8 = active low level-sensitive
    14         bits[15:8] PPI interrupt cpu mask.  Each bit corresponds to each of
    15         the 8 possible cpus attached to the GIC.  A bit set to '1' indicated
    16         the interrupt is wired to that CPU.  Only valid for PPI interrupts.

    另外,值得注意的是,一个设备还可能用到多个中断号。对于ARM GIC而言,若某设备使用了SPI的168、169号2个中断,而言都是高电平触发,则该设备结点的interrupts属性可定义为:interrupts = <0 168 4>, <0 169 4>;
    除了中断以外,在ARM Linux中clock、GPIO、pinmux都可以透过.dts中的结点和属性进行描述。

    DTC (device tree compiler)

    将.dts编译为.dtb的工具。DTC的源代码位于内核的scripts/dtc目录,在Linux内核使能了Device Tree的情况下,编译内核的时候主机工具dtc会被编译出来,对应scripts/dtc/Makefile中的“hostprogs-y := dtc”这一hostprogs编译target。
    在Linux内核的arch/arm/boot/dts/Makefile中,描述了当某种SoC被选中后,哪些.dtb文件会被编译出来,如与VEXPRESS对应的.dtb包括:

    dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
            vexpress-v2p-ca9.dtb \
            vexpress-v2p-ca15-tc1.dtb \
            vexpress-v2p-ca15_a7.dtb \
            xenvm-4.2.dtb

    在Linux下,我们可以单独编译Device Tree文件。当我们在Linux内核下运行make dtbs时,若我们之前选择了ARCH_VEXPRESS,上述.dtb都会由对应的.dts编译出来。因为arch/arm/Makefile中含有一个dtbs编译target项目。

    Device Tree Blob (.dtb)

    .dtb是.dts被DTC编译后的二进制格式的Device Tree描述,可由Linux内核解析。通常在我们为电路板制作NAND、SD启动image时,会为.dtb文件单独留下一个很小的区域以存放之,之后bootloader在引导kernel的过程中,会先读取该.dtb到内存。

    Binding

    对于Device Tree中的结点和属性具体是如何来描述设备的硬件细节的,一般需要文档来进行讲解,文档的后缀名一般为.txt。这些文档位于内核的Documentation/devicetree/bindings目录,其下又分为很多子目录。

    Bootloader

    Uboot mainline 从 v1.1.3开始支持Device Tree,其对ARM的支持则是和ARM内核支持Device Tree同期完成。
    为了使能Device Tree,需要编译Uboot的时候在config文件中加入
    #define CONFIG_OF_LIBFDT
    在Uboot中,可以从NAND、SD或者TFTP等任意介质将.dtb读入内存,假设.dtb放入的内存地址为0x71000000,之后可在Uboot运行命令fdt addr命令设置.dtb的地址,如:
    U-Boot> fdt addr 0x71000000
    fdt的其他命令就变地可以使用,如fdt resize、fdt print等。
    对于ARM来讲,可以透过bootz kernel_addr initrd_address dtb_address的命令来启动内核,即dtb_address作为bootz或者bootm的最后一次参数,第一个参数为内核映像的地址,第二个参数为initrd的地址,若不存在initrd,可以用 -代替。

    3.    Device Tree引发的BSP和驱动变更

    有了Device Tree后,大量的板级信息都不再需要,譬如过去经常在arch/arm/plat-xxx和arch/arm/mach-xxx实施的如下事情:
    1.    注册platform_device,绑定resource,即内存、IRQ等板级信息。

    透过Device Tree后,形如

     

    90 static struct resource xxx_resources[] = {
    91         [0] = {
    92                 .start  = …,
    93                 .end    = …,
    94                 .flags  = IORESOURCE_MEM,
    95         },
    96         [1] = {
    97                 .start  = …,
    98                 .end    = …,
    99                 .flags  = IORESOURCE_IRQ,
    100         },
    101 };
    102
    103 static struct platform_device xxx_device = {
    104         .name           = "xxx",
    105         .id             = -1,
    106         .dev            = {
    107                                 .platform_data          = &xxx_data,
    108         },
    109         .resource       = xxx_resources,
    110         .num_resources  = ARRAY_SIZE(xxx_resources),
    111 };

    之类的platform_device代码都不再需要,其中platform_device会由kernel自动展开。而这些resource实际来源于.dts中设备结点的reg、interrupts属性。典型地,大多数总线都与“simple_bus”兼容,而在SoC对应的machine的.init_machine成员函数中,调用of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);即可自动展开所有的platform_device。譬如,假设我们有个XXX SoC,则可在arch/arm/mach-xxx/的板文件中透过如下方式展开.dts中的设备结点对应的platform_device:

     

     

    18 static struct of_device_id xxx_of_bus_ids[] __initdata = {
    19         { .compatible = "simple-bus", },
    20         {},
    21 };
    22
    23 void __init xxx_mach_init(void)
    24 {
    25         of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);
    26 }
    32
    33 #ifdef CONFIG_ARCH_XXX
    38
    39 DT_MACHINE_START(XXX_DT, "Generic XXX (Flattened Device Tree)")
    41         …
    45         .init_machine   = xxx_mach_init,
    46         …
    49 MACHINE_END
    50 #endif

     

     

     

    2.    注册i2c_board_info,指定IRQ等板级信息。

    形如

     

    145 static struct i2c_board_info __initdata afeb9260_i2c_devices[] = {
    146         {
    147                 I2C_BOARD_INFO("tlv320aic23", 0x1a),
    148         }, {
    149                 I2C_BOARD_INFO("fm3130", 0x68),
    150         }, {
    151                 I2C_BOARD_INFO("24c64", 0x50),
    152         },
    153 };

    之类的i2c_board_info代码,目前不再需要出现,现在只需要把tlv320aic23、fm3130、24c64这些设备结点填充作为相应的I2C controller结点的子结点即可,类似于前面的

     

     

          i2c@1,0 {
                compatible = "acme,a1234-i2c-bus";
                …
                rtc@58 {
                    compatible = "maxim,ds1338";
                    reg = <58>;
                    interrupts = < 7 3 >;
                };
            };

    Device Tree中的I2C client会透过I2C host驱动的probe()函数中调用of_i2c_register_devices(&i2c_dev->adapter);被自动展开。

     

    3.    注册spi_board_info,指定IRQ等板级信息。

    形如

     

    79 static struct spi_board_info afeb9260_spi_devices[] = {
    80         {       /* DataFlash chip */
    81                 .modalias       = "mtd_dataflash",
    82                 .chip_select    = 1,
    83                 .max_speed_hz   = 15 * 1000 * 1000,
    84                 .bus_num        = 0,
    85         },
    86 };

    之类的spi_board_info代码,目前不再需要出现,与I2C类似,现在只需要把mtd_dataflash之类的结点,作为SPI控制器的子结点即可,SPI host驱动的probe函数透过spi_register_master()注册master的时候,会自动展开依附于它的slave。

     

    4.    多个针对不同电路板的machine,以及相关的callback。

    过去,ARM Linux针对不同的电路板会建立由MACHINE_START和MACHINE_END包围起来的针对这个machine的一系列callback,譬如:

     

    373 MACHINE_START(VEXPRESS, "ARM-Versatile Express")
    374         .atag_offset    = 0x100,
    375         .smp            = smp_ops(vexpress_smp_ops),
    376         .map_io         = v2m_map_io,
    377         .init_early     = v2m_init_early,
    378         .init_irq       = v2m_init_irq,
    379         .timer          = &v2m_timer,
    380         .handle_irq     = gic_handle_irq,
    381         .init_machine   = v2m_init,
    382         .restart        = vexpress_restart,
    383 MACHINE_END

    这些不同的machine会有不同的MACHINE ID,Uboot在启动Linux内核时会将MACHINE ID存放在r1寄存器,Linux启动时会匹配Bootloader传递的MACHINE ID和MACHINE_START声明的MACHINE ID,然后执行相应machine的一系列初始化函数。

     

    引入Device Tree之后,MACHINE_START变更为DT_MACHINE_START,其中含有一个.dt_compat成员,用于表明相关的machine与.dts中root结点的compatible属性兼容关系。如果Bootloader传递给内核的Device Tree中root结点的compatible属性出现在某machine的.dt_compat表中,相关的machine就与对应的Device Tree匹配,从而引发这一machine的一系列初始化函数被执行。

     

    489 static const char * const v2m_dt_match[] __initconst = {
    490         "arm,vexpress",
    491         "xen,xenvm",
    492         NULL,
    493 };
    495 DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
    496         .dt_compat      = v2m_dt_match,
    497         .smp            = smp_ops(vexpress_smp_ops),
    498         .map_io         = v2m_dt_map_io,
    499         .init_early     = v2m_dt_init_early,
    500         .init_irq       = v2m_dt_init_irq,
    501         .timer          = &v2m_dt_timer,
    502         .init_machine   = v2m_dt_init,
    503         .handle_irq     = gic_handle_irq,
    504         .restart        = vexpress_restart,
    505 MACHINE_END

    Linux倡导针对多个SoC、多个电路板的通用DT machine,即一个DT machine的.dt_compat表含多个电路板.dts文件的root结点compatible属性字符串。之后,如果的电路板的初始化序列不一样,可以透过int of_machine_is_compatible(const char *compat) API判断具体的电路板是什么。

     

        譬如arch/arm/mach-exynos/mach-exynos5-dt.c的EXYNOS5_DT machine同时兼容"samsung,exynos5250"和"samsung,exynos5440":

     

    158 static char const *exynos5_dt_compat[] __initdata = {
    159         "samsung,exynos5250",
    160         "samsung,exynos5440",
    161         NULL
    162 };
    163
    177 DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")
    178         /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
    179         .init_irq       = exynos5_init_irq,
    180         .smp            = smp_ops(exynos_smp_ops),
    181         .map_io         = exynos5_dt_map_io,
    182         .handle_irq     = gic_handle_irq,
    183         .init_machine   = exynos5_dt_machine_init,
    184         .init_late      = exynos_init_late,
    185         .timer          = &exynos4_timer,
    186         .dt_compat      = exynos5_dt_compat,
    187         .restart        = exynos5_restart,
    188         .reserve        = exynos5_reserve,
    189 MACHINE_END

         它的.init_machine成员函数就针对不同的machine进行了不同的分支处理:

     

    126 static void __init exynos5_dt_machine_init(void)
    127 {
    128         …
    149
    150         if (of_machine_is_compatible("samsung,exynos5250"))
    151                 of_platform_populate(NULL, of_default_bus_match_table,
    152                                      exynos5250_auxdata_lookup, NULL);
    153         else if (of_machine_is_compatible("samsung,exynos5440"))
    154                 of_platform_populate(NULL, of_default_bus_match_table,
    155                                      exynos5440_auxdata_lookup, NULL);
    156 }

     

     

     

    使用Device Tree后,驱动需要与.dts中描述的设备结点进行匹配,从而引发驱动的probe()函数执行。对于platform_driver而言,需要添加一个OF匹配表,如前文的.dts文件的"acme,a1234-i2c-bus"兼容I2C控制器结点的OF匹配表可以是:

     

    436 static const struct of_device_id a1234_i2c_of_match[] = {
    437         { .compatible = "acme,a1234-i2c-bus ", },
    438         {},
    439 };
    440 MODULE_DEVICE_TABLE(of, a1234_i2c_of_match);
    441
    442 static struct platform_driver i2c_a1234_driver = {
    443         .driver = {
    444                 .name = "a1234-i2c-bus ",
    445                 .owner = THIS_MODULE,
    449                 .of_match_table = a1234_i2c_of_match,
    450         },
    451         .probe = i2c_a1234_probe,
    452         .remove = i2c_a1234_remove,
    453 };
    454 module_platform_driver(i2c_a1234_driver);

     

    对于I2C和SPI从设备而言,同样也可以透过of_match_table添加匹配的.dts中的相关结点的compatible属性,如sound/soc/codecs/wm8753.c中的:

     

    1533 static const struct of_device_id wm8753_of_match[] = {
    1534         { .compatible = "wlf,wm8753", },
    1535         { }
    1536 };
    1537 MODULE_DEVICE_TABLE(of, wm8753_of_match);
    1587 static struct spi_driver wm8753_spi_driver = {
    1588         .driver = {
    1589                 .name   = "wm8753",
    1590                 .owner  = THIS_MODULE,
    1591                 .of_match_table = wm8753_of_match,
    1592         },
    1593         .probe          = wm8753_spi_probe,
    1594         .remove         = wm8753_spi_remove,
    1595 };
    1640 static struct i2c_driver wm8753_i2c_driver = {
    1641         .driver = {
    1642                 .name = "wm8753",
    1643                 .owner = THIS_MODULE,
    1644                 .of_match_table = wm8753_of_match,
    1645         },
    1646         .probe =    wm8753_i2c_probe,
    1647         .remove =   wm8753_i2c_remove,
    1648         .id_table = wm8753_i2c_id,
    1649 };

    不过这边有一点需要提醒的是,I2C和SPI外设驱动和Device Tree中设备结点的compatible 属性还有一种弱式匹配方法,就是别名匹配。compatible 属性的组织形式为<manufacturer>,<model>,别名其实就是去掉compatible 属性中逗号前的manufacturer前缀。关于这一点,可查看drivers/spi/spi.c的源代码,函数spi_match_device()暴露了更多的细节,如果别名出现在设备spi_driver的id_table里面,或者别名与spi_driver的name字段相同,SPI设备和驱动都可以匹配上:

     

     

     

    90 static int spi_match_device(struct device *dev, struct device_driver *drv)
    91 {
    92         const struct spi_device *spi = to_spi_device(dev);
    93         const struct spi_driver *sdrv = to_spi_driver(drv);
    94
    95         /* Attempt an OF style match */
    96         if (of_driver_match_device(dev, drv))
    97                 return 1;
    98
    99         /* Then try ACPI */
    100         if (acpi_driver_match_device(dev, drv))
    101                 return 1;
    102
    103         if (sdrv->id_table)
    104                 return !!spi_match_id(sdrv->id_table, spi);
    105
    106         return strcmp(spi->modalias, drv->name) == 0;
    107 }
    71 static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
    72                                                 const struct spi_device *sdev)
    73 {
    74         while (id->name[0]) {
    75                 if (!strcmp(sdev->modalias, id->name))
    76                         return id;
    77                 id++;
    78         }
    79         return NULL;
    80 }

     

    4.    常用OF API

    在Linux的BSP和驱动代码中,还经常会使用到Linux中一组Device Tree的API,这些API通常被冠以of_前缀,它们的实现代码位于内核的drivers/of目录。这些常用的API包括:

    int of_device_is_compatible(const struct device_node *device,const char *compat);

    判断设备结点的compatible 属性是否包含compat指定的字符串。当一个驱动支持2个或多个设备的时候,这些不同.dts文件中设备的compatible 属性都会进入驱动 OF匹配表。因此驱动可以透过Bootloader传递给内核的Device Tree中的真正结点的compatible 属性以确定究竟是哪一种设备,从而根据不同的设备类型进行不同的处理。如drivers/pinctrl/pinctrl-sirf.c即兼容于"sirf,prima2-pinctrl",又兼容于"sirf,prima2-pinctrl",在驱动中就有相应分支处理:

     

    1682 if (of_device_is_compatible(np, "sirf,marco-pinctrl"))
    1683      is_marco = 1;

    struct device_node *of_find_compatible_node(struct device_node *from,

     

             const char *type, const char *compatible);

     

    根据compatible属性,获得设备结点。遍历Device Tree中所有的设备结点,看看哪个结点的类型、compatible属性与本函数的输入参数匹配,大多数情况下,from、type为NULL。

    int of_property_read_u8_array(const struct device_node *np,

                         const char *propname, u8 *out_values, size_t sz);

    int of_property_read_u16_array(const struct device_node *np,

                          const char *propname, u16 *out_values, size_t sz);

    int of_property_read_u32_array(const struct device_node *np,

                          const char *propname, u32 *out_values, size_t sz);

    int of_property_read_u64(const struct device_node *np, const char

    *propname, u64 *out_value);

    读取设备结点np的属性名为propname,类型为8、16、32、64位整型数组的属性。对于32位处理器来讲,最常用的是of_property_read_u32_array()。如在arch/arm/mm/cache-l2x0.c中,透过如下语句读取L2 cache的"arm,data-latency"属性:

     

    534         of_property_read_u32_array(np, "arm,data-latency",
    535                                    data, ARRAY_SIZE(data));

    在arch/arm/boot/dts/vexpress-v2p-ca9.dts中,含有"arm,data-latency"属性的L2 cache结点如下:

     

    137         L2: cache-controller@1e00a000 {
    138                 compatible = "arm,pl310-cache";
    139                 reg = <0x1e00a000 0x1000>;
    140                 interrupts = <0 43 4>;
    141                 cache-level = <2>;
    142                 arm,data-latency = <1 1 1>;
    143                 arm,tag-latency = <1 1 1>;
    144         }


    有些情况下,整形属性的长度可能为1,于是内核为了方便调用者,又在上述API的基础上封装出了更加简单的读单一整形属性的API,它们为int of_property_read_u8()、of_property_read_u16()等,实现于include/linux/of.h:

     

     

    513 static inline int of_property_read_u8(const struct device_node *np,
    514                                        const char *propname,
    515                                        u8 *out_value)
    516 {
    517         return of_property_read_u8_array(np, propname, out_value, 1);
    518 }
    519
    520 static inline int of_property_read_u16(const struct device_node *np,
    521                                        const char *propname,
    522                                        u16 *out_value)
    523 {
    524         return of_property_read_u16_array(np, propname, out_value, 1);
    525 }
    526
    527 static inline int of_property_read_u32(const struct device_node *np,
    528                                        const char *propname,
    529                                        u32 *out_value)
    530 {
    531         return of_property_read_u32_array(np, propname, out_value, 1);
    532 }


    int of_property_read_string(struct device_node *np, const char

     

    *propname, const char **out_string);

    int of_property_read_string_index(struct device_node *np, const char

        *propname, int index, const char **output);

    前者读取字符串属性,后者读取字符串数组属性中的第index个字符串。如drivers/clk/clk.c中的of_clk_get_parent_name()透过of_property_read_string_index()遍历clkspec结点的所有"clock-output-names"字符串数组属性。

     

    1759 const char *of_clk_get_parent_name(struct device_node *np, int index)
    1760 {
    1761         struct of_phandle_args clkspec;
    1762         const char *clk_name;
    1763         int rc;
    1764
    1765         if (index < 0)
    1766                 return NULL;
    1767
    1768         rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
    1769                                         &clkspec);
    1770         if (rc)
    1771                 return NULL;
    1772
    1773         if (of_property_read_string_index(clkspec.np, "clock-output-names",
    1774                                   clkspec.args_count ? clkspec.args[0] : 0,
    1775                                           &clk_name) < 0)
    1776                 clk_name = clkspec.np->name;
    1777
    1778         of_node_put(clkspec.np);
    1779         return clk_name;
    1780 }
    1781 EXPORT_SYMBOL_GPL(of_clk_get_parent_name);


    static inline bool of_property_read_bool(const struct device_node *np,

     

                                             const char *propname);

     

    如果设备结点np含有propname属性,则返回true,否则返回false。一般用于检查空属性是否存在。

     

    void __iomem *of_iomap(struct device_node *node, int index);

     

    通过设备结点直接进行设备内存区间的 ioremap(),index是内存段的索引。若设备结点的reg属性有多段,可通过index标示要ioremap的是哪一段,只有1段的情况,index为0。采用Device Tree后,大量的设备驱动通过of_iomap()进行映射,而不再通过传统的ioremap。

     

    unsigned int irq_of_parse_and_map(struct device_node *dev, int index);

    透过Device Tree或者设备的中断号,实际上是从.dts中的interrupts属性解析出中断号。若设备使用了多个中断,index指定中断的索引号。

     

    还有一些OF API,这里不一一列举,具体可参考include/linux/of.h头文件。

    5.    总结

    ARM社区一贯充斥的大量垃圾代码导致Linus盛怒,因此社区在2011年到2012年进行了大量的工作。ARM Linux开始围绕Device Tree展开,Device Tree有自己的独立的语法,它的源文件为.dts,编译后得到.dtb,Bootloader在引导Linux内核的时候会将.dtb地址告知内核。之后内核会展开Device Tree并创建和注册相关的设备,因此arch/arm/mach-xxx和arch/arm/plat-xxx中大量的用于注册platform、I2C、SPI板级信息的代码被删除,而驱动也以新的方式和.dts中定义的设备结点进行匹配。

    更多精华文章请扫描下方二维码关注Linux阅码场

    展开全文
  • samsung android usb device

    千次下载 热门讨论 2011-09-13 22:44:07
    samsung android usb device
  • wd ses device usb device 西部数据移动硬盘 驱动下载

    千次下载 热门讨论 2011-03-14 21:20:38
    wd ses device usb device 西部数据 移动硬盘 驱动 wd ses device usb device 西部数据 移动硬盘 驱动 wd ses device usb device 西部数据 移动硬盘 驱动
  • Device Tree 详解

    万次阅读 多人点赞 2018-03-20 20:47:12
    对于DeviceTree的来历和用处大部分人都已经非常了解了,DeviceTree发源于PowerPC架构,为了消除代码中冗余的各种device注册代码而产生的,现在已经成为了linux的通用机制。 DeviceTree的结构非常简单,由两种元素...

    1、DTS语法

    对于DeviceTree的来历和用处大部分人都已经非常了解了,DeviceTree发源于PowerPC架构,为了消除代码中冗余的各种device注册代码而产生的,现在已经成为了linux的通用机制。

    DeviceTree的结构非常简单,由两种元素组成:Node(节点)、Property(属性)。下图是一个真实的简单的DeviceTree树形结构图。

    这里写图片描述

    • Node节点。在DTS中使用一对花括号"node-name{}"来定义;
    • Property属性。在Node中使用"property-name=value"字符串来定义;
    / {
    	model = "mt6799";
    	compatible = "mediatek,mt6799";
    	interrupt-parent = <&gic>;
    	#address-cells = <2>;
    	#size-cells = <2>;
    
    	/* chosen */
    	chosen {
    		bootargs = "console=tty0 console=ttyMT0,921600n1 root=/dev/ram";
    	};
    }
    

    上述例子中定义了一个根节点"/"和一个子节点“chosen”,其他的字符串“model = “mt6799”;”、“compatible = “mediatek,mt6799”;”都是property。

    Node、Property的名字和值都是可以自定义的,没有太大限制。但是DeviceTree的标准还是预定义了一些标准的Node和Property,在标准Node和Property之间还定义了一些约束规则。关于这些描述在 The DeviceTree Specification官方spec中有详细描述。这里为了方便大家,还是重复做一些搬运。

    1.1、标准Property

    Property的格式为"property-name=value",其中value的取值类型如下:

    Property values:
    Value Description
    <empty> Value is empty. Used for conveying true-false information, when the presence of absence of the property itself is sufficiently descriptive.

    Property值为空,用property本身出现或者不出现来表示一个treue/false值。
    <u32> A 32-bit integer in big-endian format. Example: the 32-bit value 0x11223344 would be represented in memory as:
    address 11
    address+1 22
    address+2 33
    address+3 44

    32bit的值,用大端格式存储。
    <u64> Represents a 64-bit integer in big-endian format. Consists of two <u32> values where the first value contains the most significant bits of the integer and the second value contains the least significant bits.
    Example: the 64-bit value 0x1122334455667788 would be represented as two cells as: <0x11223344 0x55667788>.
    The value would be represented in memory as:
    address 11
    address+1 22
    address+2 33
    address+3 44
    address+4 55
    address+5 66
    address+6 77
    address+7 88

    64bit的值,用大端格式存储。
    <string> Strings are printable and null-terminated. Example: the string “hello” would be represented in memory as:
    address 68 'h'
    address+1 65 'e'
    address+2 6C 'l'
    address+3 6C 'l'
    address+4 6F 'o'
    address+5 00 '\0'

    字符串。
    <prop-encoded-array> Format is specific to the property. See the property definition.

    混合编码,自定义property的值。
    <phandle> A <u32> value. A phandle value is a way to reference another node in the devicetree.
    Any node that can be referenced defines a phandle property with a unique <u32> value. That number is used for the value of properties with a phandle value type.

    作为一个句柄指向一个Node,用来引用Node。
    <stringlist> A list of <string> values concatenated together.
    Example: The string list “hello”,”world” would be represented in memory as:
    address 68 'h'
    address+1 65 'e'
    address+2 6C 'l'
    address+3 6C 'l'
    address+4 6F 'o'
    address+5 00 '\0'
    address+6 77 'w'
    address+7 6f 'o'
    address+8 72 'r'
    address+9 6C 'l'
    address+10 64 'd'
    address+11 00 '\0'

    字符串数组。

    1.1.1、compatible

    • “compatible"属性通常用来device和driver的适配,推荐的格式为"manufacturer,model”。
    Property name: compatible
    Value type: <stringlist>
    Description: The compatible property value consists of one or more strings that define the specific programming model for the device. This list of strings should be used by a client program for device driver selection. The property value consists of a concatenated list of null terminated strings, from most specific to most general. They allow a device to express its compatibility with a family of similar devices, potentially allowing a single device driver to match against several devices.
    The recommended format is "manufacturer,model", where manufacturer is a string describing the name of the manufacturer (such as a stock ticker symbol), and model specifies the model number.

    Example: compatible = "fsl,mpc8641", "ns16550";

    In this example, an operating system would first try to locate a device driver that supported fsl,mpc8641. If a driver was not found, it would then try to locate a driver that supported the more general ns16550 device type.

    在这个例子中,device首先尝试去适配"fsl,mpc8641"driver,如果失败再去尝试适配"ns16550"driver。

    1.1.2、model

    • "model"属性只是简单的表示型号,root节点用其来传递值给machine_desc_str。
    Property name: model
    Value type: <stringlist>
    Description: The model property value is a <string> that specifies the manufacturer’s model number of the device.
    The recommended format is: "manufacturer,model", where manufacturer is a string describing the name of the manufacturer (such as a stock ticker symbol), and model specifies the model number.
    Example: model = "fsl,MPC8349EMITX";

    1.1.3、phandle

    • "phandle"属性通用一个唯一的id来标识一个Node,在property可以使用这个id来引用Node。
    Property name: phandle
    Value type: <u32>
    Description: The phandle property specifies a numerical identifier for a node that is unique within the devicetree. The phandle property value is used by other nodes that need to refer to the node associated with the property.
    Example: See the following devicetree excerpt:
    pic@10000000 {
    phandle = <1>;
    interrupt-controller;
    };

    A phandle value of 1 is defined. Another device node could reference the pic node with a phandle value of 1:
    another-device-node {
    interrupt-parent = <1>;
    };

    Node“pic@10000000”定义了一个phandle属性,这个phandle有唯一id = <1>,在property“interrupt-parent”通过<1>对Node“pic@10000000”进行引用。

    在DeviceTree中通过另一种方式进行phandle的定义和引用更加常见:

    • 定义一个“label:”来引用Node,在编译是系统会自动为node生成一个phandle属性。“cpu0"是一个label,用来引用node"cpu@0”:
    		cpu0: cpu@0 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a35";
    			reg = <0x000>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1248000000>;
    		};
    
    • 使用"&“来引用“label”,即是引用phandle。property"cpu"通过”&cpu0"来对node"cpu@0":
    		cpu-map {
    			cluster0 {
    				core0 {
    					cpu = <&cpu0>;
    				};
    
    
    				core1 {
    					cpu = <&cpu1>;
    				};
    
    				core2 {
    					cpu = <&cpu2>;
    				};
    
    				core3 {
    					cpu = <&cpu3>;
    				};
    
    			};
    

    1.1.4、#address-cells 、 #size-cells

    • "#address-cells, #size-cells"属性用来定义当前node的子node中"reg"属性的解析格式。
    Property name: #address-cells, #size-cells
    Value type: <u32>
    Description: The #address-cells and #size-cells properties may be used in any device node that has children in the devicetree hierarchy and describes how child device nodes should be addressed. The #address-cells property defines the number of <u32> cells used to encode the address field in a child node’s reg property. The #size-cells property defines the number of <u32> cells used to encode the size field in a child node’s reg property.
    The #address-cells and #size-cells properties are not inherited from ancestors in the devicetree. They shall be explicitly defined.
    A DTSpec-compliant boot program shall supply #address-cells and #size-cells on all nodes that have children.
    If missing, a client program should assume a default value of 2 for #address-cells, and a value of 1 for #size-cells.
    Example: See the following devicetree excerpt:
    soc {
    #address-cells = <1>;
    #size-cells = <1>;
        serial {
        compatible = "ns16550";
        reg = <0x4600 0x100>;
        clock-frequency = <0>;
        interrupts = <0xA 0x8>;
        interrupt-parent = <&ipic>;
        };
    };

    In this example, the #address-cells and #size-cells properties of the soc node are both set to 1. This setting specifies that one cell is required to represent an address and one cell is required to represent the size of nodes that are children of this node.
    The serial device reg property necessarily follows this specification set in the parent (soc) node—the address is represented by a single cell (0x4600), and the size is represented by a single cell (0x100).

    举例说明:

    • 1、如果node"soc"中"#address-cells=<1>"、"#size-cells=<1>",那么子node"serial"中"reg"属性的解析为“addr1 = 0x0, size1 = 0x100, addr2 = 0x0, size2 = 0x200”:
    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        serial {
            reg = <0x0 0x100 0x0 0x200>;
        }
    }
    
    • 2、如果node"soc"中"#address-cells=<2>"、"#size-cells=<2>",那么子node"serial"中"reg"属性的解析为“addr1 = 0x100, size1 = 0x200”:
    soc {
        #address-cells = <2>;
        #size-cells = <2>;
        serial {
            reg = <0x0 0x100 0x0 0x200>;
        }
    }
    
    • 3、如果node"soc"中"#address-cells=<2>"、"#size-cells=<0>",那么子node"serial"中"reg"属性的解析为“addr1 = 0x100, addr2 = 0x200”:
    soc {
        #address-cells = <2>;
        #size-cells = <0>;
        serial {
            reg = <0x0 0x100 0x0 0x200>;
        }
    }
    

    1.1.5、reg

    • “reg"属性解析出"address,length"数字,解析格式依据父节点的”#address-cells、#size-cells"定义。
    Property name: reg
    Value type: <prop-encoded-array> encoded as an arbitrary number of (address, length) pairs.
    Description: The reg property describes the address of the device’s resources within the address space defined by its parent bus. Most commonly this means the offsets and lengths of memory-mapped IO register blocks, but may have a different meaning on some bus types. Addresses in the address space defined by the root node are CPU real addresses.
    The value is a <prop-encoded-array>, composed of an arbitrary number of pairs of address and length,
    . The number of <u32> cells required to specify the address and length are bus-specific and are specified by the #address-cells and #size-cells properties in the parent of the device node. If the parent node specifies a value of 0 for #size-cells, the length field in the value of reg shall be omitted.
    Example: Suppose a device within a system-on-a-chip had two blocks of registers, a 32-byte block at offset 0x3000 in the SOC and a 256-byte block at offset 0xFE00. The reg property would be encoded as follows (assuming #address-cells and #size-cells values of 1):

    reg = <0x3000 0x20 0xFE00 0x100>;

    1.1.6、ranges

    • "ranges"属性用来做当前node和父node之间的地址映射,格式为(child-bus-address, parentbus-address, length)。其中child-bus-address的解析长度受当前node的#address-cells属性控制,parentbus-address的解析长度受父node的#address-cells属性控制length的解析长度受当前node的#size-cells属性控制。

    Property name: ranges
    Value type: <empty> or <prop-encoded-array> encoded as an arbitrary number of (child-bus-address, parentbus-address, length) triplets.
    Description: The ranges property provides a means of defining a mapping or translation between the address space of the bus (the child address space) and the address space of the bus node’s parent (the parent address space).
    The format of the value of the ranges property is an arbitrary number of triplets of (child-bus-address, parentbus-address, length)
    • The child-bus-address is a physical address within the child bus’ address space. The number of cells to represent the address is bus dependent and can be determined from the #address-cells of this node (the node in which the ranges property appears).
    • The parent-bus-address is a physical address within the parent bus’ address space. The number of cells to represent the parent address is bus dependent and can be determined from the #address-cells property of the node that defines the parent’s address space.
    • The length specifies the size of the range in the child’s address space. The number of cells to represent the size can be determined from the #size-cells of this node (the node in which the ranges property appears).
    If the property is defined with an <empty> value, it specifies that the parent and child address space is identical, and no address translation is required.
    If the property is not present in a bus node, it is assumed that no mapping exists between children of the node and the parent address space.
    Example: Address Translation Example:
    soc {
    compatible = "simple-bus";
    #address-cells = <1>;
    #size-cells = <1>;
    ranges = <0x0 0xe0000000 0x00100000>;
    serial {
    device_type = "serial";
    compatible = "ns16550";
    reg = <0x4600 0x100>;
    clock-frequency = <0>;
    interrupts = <0xA 0x8>;
    interrupt-parent = <&ipic>;
    };
    };

    The soc node specifies a ranges property of
    <0x0 0xe0000000 0x00100000>;

    This property value specifies that for an 1024KB range of address space, a child node addressed at physical 0x0 maps to a parent address of physical 0xe0000000. With this mapping, the serial device node can be addressed by a load or store at address 0xe0004600, an offset of 0x4600 (specified in reg) plus the 0xe0000000 mapping specified in ranges.

    1.1.7、interrupt property

    和中断相关的node可以分成3种:

    • “Interrupt Generating Devices”,中断发生设备,这种设备可以发生中断。
    • “Interrupt Controllers”,中断控制器,处理中断。
    • “Interrupt Nexus”,中断联结,路由中断给中断控制器。

    这里写图片描述

    1.1.7.1、Interrupt Generating Devices Property

    • "interrupts"属性用来定义设备的中断解析,根据其"interrupt-parent"node中定义的“#interrupt-cells”来解析。比如#interrupt-cells=2,那根据2个cells为单位来解析"interrupts"属性。

    Property name: interrupts
    Value type: <prop-encoded-array> encoded as arbitrary number of interrupt specifiers
    Description: The interrupts property of a device node defines the interrupt or interrupts that are generated by the device.
    The value of the interrupts property consists of an arbitrary number of interrupt specifiers. The format of an interrupt specifier is defined by the binding of the interrupt domain root.
    interrupts is overridden by the interrupts-extended property and normally only one or the other should be used.
    Example: A common definition of an interrupt specifier in an open PIC–compatible interrupt domain consists of two cells; an interrupt number and level/sense information. See the following example, which defines a single interrupt specifier, with an interrupt number of 0xA and level/sense encoding of 8.

    interrupts = <0xA 8>;
    • "interrupt-parent"属性用来制定当前设备的Interrupt Controllers/Interrupt Nexus,phandle指向对应的node。
    Property name: interrupt-parent Value type: <phandle> Description: Because the hierarchy of the nodes in the interrupt tree might not match the devicetree, the interrupt-parent property is available to make the definition of an interrupt parent explicit. The value is the phandle to the interrupt parent. If this property is missing from a device, its interrupt parent is assumed to be its devicetree parent.

    1.1.7.2、Interrupt Controllers Property

    • "#interrupt-cells"属性用来规定连接到该中断控制器上的设备的"interrupts"属性的解析长度。
    Property name: #interrupt-cells
    Value type: <u32>
    Description: The #interrupt-cells property defines the number of cells required to encode an interrupt specifier for an interrupt domain.
    • "interrupt-controller"属性用来声明当前node为中断控制器。
    Property name: interrupt-controller
    Value type: <empty>
    Description: The presence of an interrupt-controller property defines a node as an interrupt controller node.

    1.1.7.3、Interrupt Nexus Property

    • "#interrupt-cells"属性用来规定连接到该中断控制器上的设备的"interrupts"属性的解析长度。
    Property name: #interrupt-cells
    Value type: <u32>
    Description: The #interrupt-cells property defines the number of cells required to encode an interrupt specifier for an interrupt domain.
    • "interrupt-map"属性用来描述interrupt nexus设备对中断的路由。解析格式为5元素序列“child unit address, child interrupt specifier, interrupt-parent, parent unit address, parent interrupt specifier”。

    其中:

    “child unit address”的cells长度由子节点的“#address-cells”指定;
    “child interrupt specifier”的cells长度由子节点的“#interrupt-cells”指定;
    “interrupt-parent”phandle指向interrupt controller的引用;
    “parent unit address”的cells长度由父节点的“#address-cells”指定;
    “parent interrupt specifier”的cells长度由父节点的“#interrupt-cells”指定;

    Property name: interrupt-map
    Value type: <prop-encoded-array> encoded as an arbitrary number of interrupt mapping entries.
    Description: An interrupt-map is a property on a nexus node that bridges one interrupt domain with a set of parent interrupt domains and specifies how interrupt specifiers in the child domain are mapped to their respective parent domains.
    The interrupt map is a table where each row is a mapping entry consisting of five components: child unit address, child interrupt specifier, interrupt-parent, parent unit address, parent interrupt specifier.
    child unit address The unit address of the child node being mapped. The number of 32-bit cells required to specify this is described by the #address-cells property of the bus node on which the child is located.
    child interrupt specifier The interrupt specifier of the child node being mapped. The number of 32-bit cells required to specify this component is described by the #interrupt-cells property of this node—the nexus node containing the interrupt-map property.
    interrupt-parent A single <phandle> value that points to the interrupt parent to which the child domain is being mapped.
    parent unit address The unit address in the domain of the interrupt parent. The number of 32-bit cells required to specify this address is described by the #address-cells property of the node pointed to by the interrupt-parent field.
    parent interrupt specifier The interrupt specifier in the parent domain. The number of 32-bit cells required to specify this component is described by the #interrupt-cells property of the node pointed to by the interrupt-parent field.
    Lookups are performed on the interrupt mapping table by matching a unit-address/interrupt specifier pair against the child components in the interrupt-map. Because some fields in the unit interrupt specifier may not be relevant, a mask is applied before the lookup is done. This mask is defined in the interrupt-map-mask property (see section 2.4.3.2).

    举例:

    soc {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        open-pic {
            clock-frequency = <0>;
            interrupt-controller;
            #address-cells = <0>;
            #interrupt-cells = <2>;
        };
        pci {
            #interrupt-cells = <1>;
            #size-cells = <2>;
            #address-cells = <3>;
            interrupt-map-mask = <0xf800 0 0 7>;
            interrupt-map = <
            /* IDSEL 0x11 - PCI slot 1 */
            0x8800 0 0 1 &open-pic 2 1 /* INTA */
            0x8800 0 0 2 &open-pic 3 1 /* INTB */
            0x8800 0 0 3 &open-pic 4 1 /* INTC */
            0x8800 0 0 4 &open-pic 1 1 /* INTD */
            /* IDSEL 0x12 - PCI slot 2 */
            0x9000 0 0 1 &open-pic 3 1 /* INTA */
            0x9000 0 0 2 &open-pic 4 1 /* INTB */
            0x9000 0 0 3 &open-pic 1 1 /* INTC */
            0x9000 0 0 4 &open-pic 2 1 /* INTD */
            >;
        };
    };
    
    • For example, the first row of the interrupt-map table specifies the mapping for INTA of slot 1. The components of that row are shown here
        child unit address: 0x8800 0 0
        child interrupt specifier: 1
        interrupt parent: &open-pic
        parent unit address: (empty because #address-cells = <0> in the open-pic node)
        parent interrupt specifier: 2 1
    

    1.2、标准Node

    Node Name常常由两部分组成“node-name@unit-address”,主要是为了防止Node Name重复冲突:

    • "node-name"是node的名字;
    • "unit-address"是node中“reg”属性描述的开始地址;

    例如:"msdc@11240000"中node-name=“msdc”,unit-address=“11240000”。

    / {
    	model = "mt6799";
    	compatible = "mediatek,mt6799";
    	interrupt-parent = <&gic>;
    	#address-cells = <2>;
    	#size-cells = <2>;
    
    	msdc0:msdc@11240000 {
    		compatible = "mediatek,msdc";
    		reg = <0x0 0x11240000 0x0 0x10000
    		       0x0 0x10000e84 0x0 0x2>;    /* FPGA PWR_GPIO, PWR_GPIO_EO */
    		interrupts = <GIC_SPI 79 IRQ_TYPE_LEVEL_LOW>;
    	};
    

    下面主要介绍一下一些预先定义的标准Node。

    1.2.1、Root node

    每个DeviceTree只有一个根节点。根节点需要有以下必备属性:

    Root Node Properties
    Property Name Usage Value Type Definition
    #address-cells R <u32> Specifies the number of <u32> cells to represent the address in the reg property in children of root.
    #size-cells R <u32> Specifies the number of <u32> cells to represent the size in the reg property in children of root.
    model R <string> Specifies a string that uniquely identifies the model of the system board. The recommended format is “manufacturer,model-number”.
    compatible R <stringlist> Specifies a list of platform architectures with which this platform is compatible. This property can be used by operating systems in selecting platform specific code. The recommended form of the property value is:
    "manufacturer,model"

    For example:
    compatible = "fsl,mpc8572ds"

    1.2.2、/aliases node

    用来给一些绝对路径定义别名:

    aliases {
        serial0 = "/simple-bus@fe000000/serial@llc500";
        ethernet0 = "/simple-bus@fe000000/ethernet@31c000";
    };
    

    1.2.3、/memory node

    用来传递内存布局:

    /memory Node Properties
    Property Name Usage Value Type Definition
    device_type R <string> Value shall be “memory”
    reg R <prop-encoded-array> Consists of an arbitrary number of address and size pairs that specify the physical address and size of the memory ranges.
    initial-mapped-area O <prop-encoded-array> Specifies the address and size of the Initial Mapped Area Is a prop-encoded-array consisting of a triplet of (effective address, physical address, size). The effective and physical address shall each be 64-bit (<u64> value), and the size shall be 32-bits (<u32> value).

    举例:

    • RAM: starting address 0x0, length 0x80000000 (2GB)
    • RAM: starting address 0x100000000, length 0x100000000 (4GB)
    
    \ {
        #address-cells = <2>;
        #size-cells = <2>;
    
        memory@0 {
            device_type = "memory";
            reg = <0x000000000 0x00000000 0x00000000 0x80000000
            0x000000001 0x00000000 0x00000001 0x00000000>;
        };
    }
    
    

    1.2.4、/chosen node

    其中“bootargs”属性用来传递cmdline参数,“stdout-path”属性用来指定标准输出设备,“stdin-path”属性用来指定标准输入设备。

    /chosen Node Properties
    Property Name Usage Value Type Definition
    bootargs O <string> A string that specifies the boot arguments for the client program. The value could potentially be a null string if no boot arguments are required.
    stdout-path O <string> A string that specifies the full path to the node representing the device to be used for boot console output. If the character “:” is present in the value it terminates the path. The value may be an alias. If the stdin-path property is not specified, stdout-path should be assumed to define the input device.
    stdin-path O <string> A string that specifies the full path to the node representing the device to be used for boot console input. If the character “:” is present in the value it terminates the path. The value may be an alias.

    举例:

    	/* chosen */
    	chosen {
    		bootargs = "console=tty0 console=ttyMT0,921600n1 root=/dev/ram";
    	};
    

    1.2.5、/cpus node

    /cpus节点也是必须的,下面举个具体例子:

    	cpus {
    		#address-cells = <1>;
    		#size-cells = <0>;
    
    		cpu0: cpu@0 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a35";
    			reg = <0x000>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1248000000>;
    		};
    
    		cpu1: cpu@001 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a35";
    			reg = <0x001>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1248000000>;
    		};
    
    		cpu2: cpu@002 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a35";
    			reg = <0x002>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1248000000>;
    		};
    
    		cpu3: cpu@003 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a35";
    			reg = <0x003>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1248000000>;
    		};
    
    		cpu4: cpu@100 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a53";
    			reg = <0x100>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1378000000>;
    		};
    
    		cpu5: cpu@101 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a53";
    			reg = <0x101>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1378000000>;
    		};
    
    		cpu6: cpu@102 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a53";
    			reg = <0x102>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1378000000>;
    		};
    
    		cpu7: cpu@103 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a53";
    			reg = <0x103>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1378000000>;
    		};
    
    		cpu8: cpu@200 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a73";
    			reg = <0x200>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1638000000>;
    		};
    
    		cpu9: cpu@201 {
    			device_type = "cpu";
    			compatible = "arm,cortex-a73";
    			reg = <0x201>;
    			enable-method = "psci";
    			cpu-idle-states = <&LEGACY_MCDI &LEGACY_SODI &LEGACY_SODI3 &LEGACY_DPIDLE>,
    					  <&LEGACY_SUSPEND &MCDI &SODI &SODI3 &DPIDLE &SUSPEND>;
    			cpu-release-addr = <0x0 0x40000200>;
    			clock-frequency = <1638000000>;
    		};
    
    		cpu-map {
    			cluster0 {
    				core0 {
    					cpu = <&cpu0>;
    				};
    
    
    				core1 {
    					cpu = <&cpu1>;
    				};
    
    				core2 {
    					cpu = <&cpu2>;
    				};
    
    				core3 {
    					cpu = <&cpu3>;
    				};
    
    			};
    
    			cluster1 {
    				core0 {
    					cpu = <&cpu4>;
    				};
    
    				core1 {
    					cpu = <&cpu5>;
    				};
    
    				core2 {
    					cpu = <&cpu6>;
    				};
    
    				core3 {
    					cpu = <&cpu7>;
    				};
    
    			};
    
    			cluster2 {
    				core0 {
    					cpu = <&cpu8>;
    				};
    
    				core1 {
    					cpu = <&cpu9>;
    				};
    
    			};
    		};
    
    		idle-states {
    			entry-method = "arm,psci";
    
    			LEGACY_MCDI: legacy-mcdi {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x0000001>;
    				entry-latency-us = <600>;
    				exit-latency-us = <600>;
    				min-residency-us = <1200>;
    			};
    
    			LEGACY_SODI: legacy-sodi {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x0000002>;
    				entry-latency-us = <600>;
    				exit-latency-us = <600>;
    				min-residency-us = <1200>;
    			};
    
    			LEGACY_SODI3: legacy-sodi3 {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x0000003>;
    				entry-latency-us = <600>;
    				exit-latency-us = <600>;
    				min-residency-us = <1200>;
    			};
    
    			LEGACY_DPIDLE: legacy-dpidle {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x0000004>;
    				entry-latency-us = <600>;
    				exit-latency-us = <600>;
    				min-residency-us = <1200>;
    			};
    
    			LEGACY_SUSPEND: legacy-suspend {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x0000005>;
    				entry-latency-us = <600>;
    				exit-latency-us = <600>;
    				min-residency-us = <1200>;
    			};
    
    			MCDI: mcdi {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x0010001>;
    				entry-latency-us = <600>;
    				exit-latency-us = <600>;
    				min-residency-us = <1200>;
    			};
    
    			SODI: sodi {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x1010002>;
    				entry-latency-us = <800>;
    				exit-latency-us = <1000>;
    				min-residency-us = <2000>;
    			};
    
    			SODI3: sodi3 {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x1010003>;
    				entry-latency-us = <800>;
    				exit-latency-us = <1000>;
    				min-residency-us = <2000>;
    			};
    
    			DPIDLE: dpidle {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x1010004>;
    				entry-latency-us = <800>;
    				exit-latency-us = <1000>;
    				min-residency-us = <2000>;
    			};
    
    			SUSPEND: suspend {
    				compatible = "arm,idle-state";
    				arm,psci-suspend-param = <0x1010005>;
    				entry-latency-us = <800>;
    				exit-latency-us = <1000>;
    				min-residency-us = <2000>;
    			};
    
    		};
    	};
    

    2、DTB

    2.1、DTB的编译

    DTB(Devicetree Blob)是DTS的二进制文件格式,Kernel使用DTC工具将DTS源文件编译成DTB,bootloader再将DTB文件传递给Kernel解析。

    不遵守标准书写的DTS文件在编译的时候会报错。

    2.2、DTB的文件结构

    这里写图片描述

    DTB文件的结构如上图所示,主要在3部分:

    • struct ftd_header。文件头结构;
    • structure block。存放含Node和Property的Value;
    • strings block。存放Property的Name;把Property Name单独分为一个区域的原因是,有很多Property Name是重复的,单独一个区域可以使用指针引用,节约空间。

    dtb中的fdt_header的数据结构:

    struct fdt_header {
        uint32_t magic;
        uint32_t totalsize;
        uint32_t off_dt_struct;
        uint32_t off_dt_strings;
        uint32_t off_mem_rsvmap;
        uint32_t version;
        uint32_t last_comp_version;
        uint32_t boot_cpuid_phys;
        uint32_t size_dt_strings;
        uint32_t size_dt_struct;
    };
    

    dtb中node header的数据结构:

    struct fdt_node_header {
    	fdt32_t tag;
    	char name[0];   // node name 存放在structure block
    };
    

    dtb中property header的数据结构:

    struct fdt_property {
    	fdt32_t tag;
    	fdt32_t len;
    	fdt32_t nameoff;    // perperty name存放在strings block
    	char data[0];
    };
    

    整个文件使用5种token来分割出node和property:

    • FDT_BEGIN_NODE (0x00000001)
    • FDT_END_NODE (0x00000002)
    • FDT_PROP (0x00000003)
    • FDT_NOP (0x00000004)
    • FDT_END (0x00000009)

    可以使用hex编辑器来查看DTB文件的结构:

    这里写图片描述

    2.3、Bootloader对DTB的传递

    没有仔细去看

    3、Kernel解析

    3.1、DTB解析

    3.1.1 setup_machine_fdt()

    直接在dtb中解析根节点的一些属性和子节点给系统早期使用。

    • 解析"/"节点的model"属性给machine_desc赋值;
    • 解析"/chosen"node中的"bootargs"属性给boot_command_line;
    • 解析"/“节点的”#size-cells"、"#address-cells"属性;
    • 解析"/memory"node中的"reg"属性,并将memory区域加入到系统;
    start_kernel() -> setup_arch() -> setup_machine_fdt():
    ↓
    
    static void __init setup_machine_fdt(phys_addr_t dt_phys)
    {
        /* (1) 映射dtb内存,到使之可以访问 */
    	void *dt_virt = fixmap_remap_fdt(dt_phys);
    
        /* (2) 早期扫描device tree中的一些node和property */
    	if (!dt_virt || !early_init_dt_scan(dt_virt)) {
    		pr_crit("\n"
    			"Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
    			"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
    			"\nPlease check your bootloader.",
    			&dt_phys, dt_virt);
    
    		while (true)
    			cpu_relax();
    	}
    
        /* (3) 使用device tree中root node的"model/compatible"属性给machine_desc赋值 */
    	machine_desc_set(of_flat_dt_get_machine_name());
    	dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name());
    }
    ↓
    
    bool __init early_init_dt_scan(void *params)
    {
    	bool status;
    
        /* (2.1)校验dtb数据 */
    	status = early_init_dt_verify(params);
    	if (!status)
    		return false;
    
        /* (2.2) */
    	early_init_dt_scan_nodes();
    	return true;
    }
    ↓
    
    void __init early_init_dt_scan_nodes(void)
    {
        /* (2.2.1) 解析"/chosen"node中的"bootargs"属性 */
    	/* Retrieve various information from the /chosen node */
    	of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
    
        /* (2.2.2) 解析"/"node中的"#size-cells"、"#address-cells"属性 */
    	/* Initialize {size,address}-cells info */
    	of_scan_flat_dt(early_init_dt_scan_root, NULL);
    
        /* (2.2.3) 解析"/memory"node中的"reg"属性,并将memory区域加入到系统 */
    	/* Setup memory, calling early_init_dt_add_memory_arch */
    	of_scan_flat_dt(early_init_dt_scan_memory, NULL);
    }
    ↓
    
    early_init_dt_scan_memory() -> early_init_dt_add_memory_arch() -> memblock_add()
    

    3.1.2 unflatten_device_tree()

    将DTB完全解析为内核使用的的device_node、property结构:

    start_kernel() -> setup_arch() -> unflatten_device_tree():
    ↓
    
    void __init unflatten_device_tree(void)
    {
        /* (1) 解析dtb数据到kernel中 */
    	__unflatten_device_tree(initial_boot_params, &of_root,
    				early_init_dt_alloc_memory_arch);
    
        /* (2) 扫描"/aliases"、"/chosen"节点来进行一些预制值的配置 */
    	/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
    	of_alias_scan(early_init_dt_alloc_memory_arch);
    }
    ↓
    
    static void __unflatten_device_tree(const void *blob,
    			     struct device_node **mynodes,
    			     void * (*dt_alloc)(u64 size, u64 align))
    {
    	unsigned long size;
    	int start;
    	void *mem;
    
    	pr_debug(" -> unflatten_device_tree()\n");
    
    	if (!blob) {
    		pr_debug("No device tree pointer\n");
    		return;
    	}
    
    	pr_debug("Unflattening device tree:\n");
    	pr_debug("magic: %08x\n", fdt_magic(blob));
    	pr_debug("size: %08x\n", fdt_totalsize(blob));
    	pr_debug("version: %08x\n", fdt_version(blob));
    
    	if (fdt_check_header(blob)) {
    		pr_err("Invalid device tree blob header\n");
    		return;
    	}
    
        /* (1.1) 第一遍扫描,计算dtb解析需要的内存空间 */
    	/* First pass, scan for size */
    	start = 0;
    	size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
    	size = ALIGN(size, 4);
    
    	pr_debug("  size is %lx, allocating...\n", size);
    
        /* (1.2) 分配所需内存 */
    	/* Allocate memory for the expanded device tree */
    	mem = dt_alloc(size + 4, __alignof__(struct device_node));
    	memset(mem, 0, size);
    
    	*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);
    
    	pr_debug("  unflattening %p...\n", mem);
    
        /* (1.3)第二遍扫描,在分配的内存中创建device_node、property树形结构来存储dtb的解析 */
    	/* Second pass, do actual unflattening */
    	start = 0;
    	unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
    	if (be32_to_cpup(mem + size) != 0xdeadbeef)
    		pr_warning("End of tree marker overwritten: %08x\n",
    			   be32_to_cpup(mem + size));
    
    	pr_debug(" <- unflatten_device_tree()\n");
    }
    ↓
    
    static void * unflatten_dt_node(const void *blob,
    				void *mem,
    				int *poffset,
    				struct device_node *dad,
    				struct device_node **nodepp,
    				unsigned long fpsize,
    				bool dryrun)
    {
    	const __be32 *p;
    	struct device_node *np;
    	struct property *pp, **prev_pp = NULL;
    	const char *pathp;
    	unsigned int l, allocl;
    	static int depth;
    	int old_depth;
    	int offset;
    	int has_name = 0;
    	int new_format = 0;
    
        /* (1.1.1) 解析node,解析node中的name值 */
    	pathp = fdt_get_name(blob, *poffset, &l);
    	if (!pathp)
    		return mem;
    
    	allocl = ++l;	/* l 为当前路径的长度 */
    
    	/* version 0x10 has a more compact unit name here instead of the full
    	 * path. we accumulate the full path size using "fpsize", we'll rebuild
    	 * it later. We detect this because the first character of the name is
    	 * not '/'.
    	 */
    	if ((*pathp) != '/') {
    		new_format = 1;
    		if (fpsize == 0) {
    			/* root node: special case. fpsize accounts for path
    			 * plus terminating zero. root node only has '/', so
    			 * fpsize should be 2, but we want to avoid the first
    			 * level nodes to have two '/' so we use fpsize 1 here
    			 */
    			fpsize = 1;
    			allocl = 2;
    			l = 1;
    			pathp = "";
    		} else {
    			/* account for '/' and path size minus terminal 0
    			 * already in 'l'
    			 */
    			fpsize += l;	/* 当前full path的长度 = 上一次full path的长度 + 当前node nam的长度 l */
    			allocl = fpsize;
    		}
    	}
    
        /* 计算解析当前node节点需要的内存大小 = device_node + full path  */
    	np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
    				__alignof__(struct device_node));
        /*  dryrun = true,只进行长度计算
         dryrun = fasle,进行实际的赋值  */
    	if (!dryrun) {
    		char *fn;
    		of_node_init(np);
    		np->full_name = fn = ((char *)np) + sizeof(*np);	 /* device_node->full_name,指向device_node结构体的结尾 */ 
    		if (new_format) {
    			/* rebuild full path for new format */
    			if (dad && dad->parent) {
                     /* 先拷入上次的full name */
    				strcpy(fn, dad->full_name);
    #ifdef DEBUG
    				if ((strlen(fn) + l + 1) != allocl) {
    					pr_debug("%s: p: %d, l: %d, a: %d\n",
    						pathp, (int)strlen(fn),
    						l, allocl);
    				}
    #endif
    				fn += strlen(fn);
    			}
                /* 再加上 '/' */
    			*(fn++) = '/';
    		}
            /* 最后加上当前node的name */
    		memcpy(fn, pathp, l);
    
    		prev_pp = &np->properties;
            /* node和node之间树形结构的创建 */
    		if (dad != NULL) {	
    			np->parent = dad;
    			np->sibling = dad->child;
    			dad->child = np;
    		}
    	}
    
        /* (1.1.2) 解析node中的property */
    	/* process properties */
    	for (offset = fdt_first_property_offset(blob, *poffset);
    	     (offset >= 0);
    	     (offset = fdt_next_property_offset(blob, offset))) {
    		const char *pname;
    		u32 sz;
    
            /* 解析一个property:
              p:property中的data
              pname:property的name指针,实际存储位置在dt_strings区域中
             sz:property data的长度
            */
    		if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
    			offset = -FDT_ERR_INTERNAL;
    			break;
    		}
    
    		if (pname == NULL) {
    			pr_info("Can't find property name in list !\n");
    			break;
    		}
    		if (strcmp(pname, "name") == 0)
    			has_name = 1;
            /* 计算解析当前property的内存大小 = property */
    		pp = unflatten_dt_alloc(&mem, sizeof(struct property),
    					__alignof__(struct property));
            /* 实际的property赋值 */
    		if (!dryrun) {
    			/* We accept flattened tree phandles either in
    			 * ePAPR-style "phandle" properties, or the
    			 * legacy "linux,phandle" properties.  If both
    			 * appear and have different values, things
    			 * will get weird.  Don't do that. */
                /* 如果property为"phandle",设置父node的device_node->phandle为当前属性的值 */
    			if ((strcmp(pname, "phandle") == 0) ||
    			    (strcmp(pname, "linux,phandle") == 0)) {
    				if (np->phandle == 0)
    					np->phandle = be32_to_cpup(p);
    			}
    			/* And we process the "ibm,phandle" property
    			 * used in pSeries dynamic device tree
    			 * stuff */
    			if (strcmp(pname, "ibm,phandle") == 0)
    				np->phandle = be32_to_cpup(p);
                /* 给property的其他字段赋值:(DTB的空间没有释放,被property成员指针引用)
                property->name:指针指向dtb strings blcok区域中的属性name
                property->length:属性data的长度
                property->value:指针指向dtb stucture block区域中的属性data
                 */
    			pp->name = (char *)pname;
    			pp->length = sz;
    			pp->value = (__be32 *)p;
    			*prev_pp = pp;
    			prev_pp = &pp->next;
    		}
    	}
    	/* with version 0x10 we may not have the name property, recreate
    	 * it here from the unit name if absent
    	 */
    	if (!has_name) {
    		const char *p1 = pathp, *ps = pathp, *pa = NULL;
    		int sz;
    
    		while (*p1) {
    			if ((*p1) == '@')
    				pa = p1;
    			if ((*p1) == '/')
    				ps = p1 + 1;
    			p1++;
    		}
    		if (pa < ps)
    			pa = p1;
    		sz = (pa - ps) + 1;
    		pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
    					__alignof__(struct property));
    		if (!dryrun) {
    			pp->name = "name";
    			pp->length = sz;
    			pp->value = pp + 1;
    			*prev_pp = pp;
    			prev_pp = &pp->next;
    			memcpy(pp->value, ps, sz - 1);
    			((char *)pp->value)[sz - 1] = 0;
    			pr_debug("fixed up name for %s -> %s\n", pathp,
    				(char *)pp->value);
    		}
    	}
        /* 根据"name"、 "device_type"属性,来给device_node结构中的name、type成员赋值 */
    	if (!dryrun) {
    		*prev_pp = NULL;
    		np->name = of_get_property(np, "name", NULL);
    		np->type = of_get_property(np, "device_type", NULL);
    
    		if (!np->name)
    			np->name = "<NULL>";
    		if (!np->type)
    			np->type = "<NULL>";
    	}
    
        /* (1.1.3)如果还有子node的存在,递归解析 */
    	old_depth = depth;
    	*poffset = fdt_next_node(blob, *poffset, &depth);
    	if (depth < 0)
    		depth = 0;
    	while (*poffset > 0 && depth > old_depth)
    		mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
    					fpsize, dryrun);
    
    	if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
    		pr_err("unflatten: error %d processing FDT\n", *poffset);
    
    	/*
    	 * Reverse the child list. Some drivers assumes node order matches .dts
    	 * node order
    	 */
    	if (!dryrun && np->child) {
    		struct device_node *child = np->child;
    		np->child = NULL;
    		while (child) {
    			struct device_node *next = child->sibling;
    			child->sibling = np->child;
    			np->child = child;
    			child = next;
    		}
    	}
    
    	if (nodepp)
    		*nodepp = np;
    
    	return mem;
    }
    
    

    3.2、Device创建

    3.2.1 of_platform_populate()

    首先root节点下的第1级子节点创建成platform device。

    • 对root节点下的第1级子节点,如果有"compatible"属性创建对应platform device;
    • 如果"compatible"属性等于of_default_bus_match_table(“simple-bus”/“simple-mfd”/“arm,amba-bus”)中任意一种,继续对其子节点进行platform device创建。
    start_kernel() -> ... ->do_initcalls() -> arm64_device_init():
    ↓
    
    const struct of_device_id of_default_bus_match_table[] = {
    	{ .compatible = "simple-bus", },
    	{ .compatible = "simple-mfd", },
    #ifdef CONFIG_ARM_AMBA
    	{ .compatible = "arm,amba-bus", },
    #endif /* CONFIG_ARM_AMBA */
    	{} /* Empty terminated list */
    };
    
    static int __init arm64_device_init(void)
    {
    	if (of_have_populated_dt()) {
    		of_iommu_init();
    		of_platform_populate(NULL, of_default_bus_match_table,
    				     NULL, NULL);
    	} else if (acpi_disabled) {
    		pr_crit("Device tree not populated\n");
    	}
    	return 0;
    }
    ↓
    
    int of_platform_populate(struct device_node *root,
    			const struct of_device_id *matches,
    			const struct of_dev_auxdata *lookup,
    			struct device *parent)
    {
    	struct device_node *child;
    	int rc = 0;
    
        /* (1) 获取dts中的root node */
    	root = root ? of_node_get(root) : of_find_node_by_path("/");
    	if (!root)
    		return -EINVAL;
    
        /* (2) 对root node的child node进行platform device创建 */
    	for_each_child_of_node(root, child) {
    		rc = of_platform_bus_create(child, matches, lookup, parent, true);
    		if (rc) {
    			of_node_put(child);
    			break;
    		}
    	}
    	of_node_set_flag(root, OF_POPULATED_BUS);
    
    	of_node_put(root);
    	return rc;
    }
    ↓
    
    static int of_platform_bus_create(struct device_node *bus,
    				  const struct of_device_id *matches,
    				  const struct of_dev_auxdata *lookup,
    				  struct device *parent, bool strict)
    {
    	const struct of_dev_auxdata *auxdata;
    	struct device_node *child;
    	struct platform_device *dev;
    	const char *bus_id = NULL;
    	void *platform_data = NULL;
    	int rc = 0;
    
        /* (2.1) 确保要创建为platform device的node,拥有"compatible"属性 */
    	/* Make sure it has a compatible property */
    	if (strict && (!of_get_property(bus, "compatible", NULL))) {
    		pr_debug("%s() - skipping %s, no compatible prop\n",
    			 __func__, bus->full_name);
    		return 0;
    	}
    
    	auxdata = of_dev_lookup(lookup, bus);
    	if (auxdata) {
    		bus_id = auxdata->name;
    		platform_data = auxdata->platform_data;
    	}
    
    	if (of_device_is_compatible(bus, "arm,primecell")) {
    		/*
    		 * Don't return an error here to keep compatibility with older
    		 * device tree files.
    		 */
    		of_amba_device_create(bus, bus_id, platform_data, parent);
    		return 0;
    	}
    
        /* (2.2) 对当前的node创建platform device */
    	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
        /* (2.3) 根据of_default_bus_match_table,如果node中含有以下属性:
        compatible = "simple-bus"
        compatible = "simple-mfd"
        compatible = "arm,amba-bus"
        则继续对node的子node进行platform device创建
         */
    	if (!dev || !of_match_node(matches, bus))
    		return 0;
    
        /* (2.4) 递归对本node的child node进行platform device创建 */
    	for_each_child_of_node(bus, child) {
    		pr_debug("   create child: %s\n", child->full_name);
    		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
    		if (rc) {
    			of_node_put(child);
    			break;
    		}
    	}
    	of_node_set_flag(bus, OF_POPULATED_BUS);
    	return rc;
    }
    ↓
    
    static struct platform_device *of_platform_device_create_pdata(
    					struct device_node *np,
    					const char *bus_id,
    					void *platform_data,
    					struct device *parent)
    {
    	struct platform_device *dev;
    
    	if (!of_device_is_available(np) ||
    	    of_node_test_and_set_flag(np, OF_POPULATED))
    		return NULL;
    
        /* (2.2.1) 分配node对应的platform_device结构,
         并且解析node中的"reg"、"interrupts"属性,
        作为platform_device->resource */
    	dev = of_device_alloc(np, bus_id, parent);
    	if (!dev)
    		goto err_clear_flag;
    
        /* (2.2.2) device对应的bus为platform_bus_type  */
    	dev->dev.bus = &platform_bus_type;
    	dev->dev.platform_data = platform_data;
    	of_dma_configure(&dev->dev, dev->dev.of_node);
    	of_msi_configure(&dev->dev, dev->dev.of_node);
    
        /* (2.2.3) 注册platform_device->dev为标准的device */
    	if (of_device_add(dev) != 0) {
    		of_dma_deconfigure(&dev->dev);
    		platform_device_put(dev);
    		goto err_clear_flag;
    	}
    
    	return dev;
    
    err_clear_flag:
    	of_node_clear_flag(np, OF_POPULATED);
    	return NULL;
    }
    ↓
    
    struct platform_device *of_device_alloc(struct device_node *np,
    				  const char *bus_id,
    				  struct device *parent)
    {
    	struct platform_device *dev;
    	int rc, i, num_reg = 0, num_irq;
    	struct resource *res, temp_res;
    
        /* (2.2.1.1) 分配platform_device空间  */
    	dev = platform_device_alloc("", -1);
    	if (!dev)
    		return NULL;
    
        /* (2.2.1.2) 计算node中"reg"属性中"address、size"resource的个数  */
    	/* count the io and irq resources */
    	while (of_address_to_resource(np, num_reg, &temp_res) == 0)
    		num_reg++;
    	num_irq = of_irq_count(np); /* 计算node中"interrupts"属性中irq的个数 */
    
        /* (2.2.1.3) 给resource分配空间并解析值 */
    	/* Populate the resource table */
    	if (num_irq || num_reg) {
    		res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
    		if (!res) {
    			platform_device_put(dev);
    			return NULL;
    		}
    
    		dev->num_resources = num_reg + num_irq;
    		dev->resource = res;
    		for (i = 0; i < num_reg; i++, res++) {
    			rc = of_address_to_resource(np, i, res);
    			WARN_ON(rc);
    		}
    		if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
    			pr_debug("not all legacy IRQ resources mapped for %s\n",
    				 np->name);
    	}
    
        /* (2.2.1.4) 根据device能够找到of node  */
    	dev->dev.of_node = of_node_get(np);
    	dev->dev.parent = parent ? : &platform_bus;
    
        /* (2.2.1.5) 配置device的那么,基本命名规则为:
        dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s",
        				     (unsigned long long)addr, node->name,
        				     dev_name(dev))
        */
    	if (bus_id)
    		dev_set_name(&dev->dev, "%s", bus_id);
    	else
    		of_device_make_bus_id(&dev->dev);
    
    	return dev;
    }
    

    3.2.2 mt_i2c_driver

    因为第1级子节点会被注册成platform device,例如i2c/spi控制器,那么对应也需要注册platform driver。已i2c控制器驱动为例:

    • 控制器首先会创建对应platform driver,把adapter注册成i2c device;
    • 在adapter的probe过程中,会调用of_i2c_register_devices()函数遍历控制器下挂的i2c设备的DTS节点,并将其注册成i2c_client;
    drivers\i2c\busses\i2c-mtk.c:
    
    static const struct of_device_id mtk_i2c_of_match[] = {
    	{ .compatible = "mediatek,mt6735-i2c", .data = &mt6735_compat },
    	{ .compatible = "mediatek,mt6797-i2c", .data = &mt6797_compat },
    	{ .compatible = "mediatek,mt6757-i2c", .data = &mt6757_compat },
    	{ .compatible = "mediatek,mt6799-i2c", .data = &mt6799_compat },
    	{ .compatible = "mediatek,elbrus-i2c", .data = &elbrus_compat },
    	{},
    };
    
    static struct platform_driver mt_i2c_driver = {
    	.probe = mt_i2c_probe,
    	.remove = mt_i2c_remove,
    	.driver = {
    		.name = I2C_DRV_NAME,
    		.owner = THIS_MODULE,
    		.of_match_table = of_match_ptr(mtk_i2c_of_match),
    	},
    };
    
    static int mt_i2c_probe(struct platform_device *pdev)
    {
    	int ret = 0;
    	struct mt_i2c *i2c;
    	unsigned int clk_src_in_hz;
    	struct resource *res;
    	const struct of_device_id *of_id;
    
    	i2c = devm_kzalloc(&pdev->dev, sizeof(struct mt_i2c), GFP_KERNEL);
    	if (i2c == NULL)
    		return -ENOMEM;
    
    	ret = mt_i2c_parse_dt(pdev->dev.of_node, i2c);
    	if (ret)
    		return -EINVAL;
    
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    
    	i2c->base = devm_ioremap_resource(&pdev->dev, res);
    	if (IS_ERR(i2c->base))
    		return PTR_ERR(i2c->base);
    
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    
    	i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
    	if (IS_ERR(i2c->pdmabase))
    		return PTR_ERR(i2c->pdmabase);
    
    	i2c->irqnr = platform_get_irq(pdev, 0);
    	if (i2c->irqnr <= 0)
    		return -EINVAL;
    	init_waitqueue_head(&i2c->wait);
    
    	ret = devm_request_irq(&pdev->dev, i2c->irqnr, mt_i2c_irq,
    #ifdef CONFIG_MEIZU_BSP
    		IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
    #else
    		IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
    #endif /*CONFIG_MEIZU_BSP*/
    	if (ret < 0) {
    		dev_err(&pdev->dev,
    			"Request I2C IRQ %d fail\n", i2c->irqnr);
    		return ret;
    	}
    	of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
    	if (!of_id)
    		return -EINVAL;
    
    	i2c->dev_comp = of_id->data;
    	i2c->adap.dev.of_node = pdev->dev.of_node;
    	i2c->dev = &i2c->adap.dev;
    	i2c->adap.dev.parent = &pdev->dev;
    	i2c->adap.owner = THIS_MODULE;
    	i2c->adap.algo = &mt_i2c_algorithm;
    	i2c->adap.algo_data = NULL;
    	i2c->adap.timeout = 2 * HZ;
    	i2c->adap.retries = 1;
    	i2c->adap.nr = i2c->id;
    	spin_lock_init(&i2c->cg_lock);
    
    	if (i2c->dev_comp->dma_support == 2) {
    		if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(33))) {
    			dev_err(&pdev->dev, "dma_set_mask return error.\n");
    			return -EINVAL;
    		}
    	} else if (i2c->dev_comp->dma_support == 3) {
    		if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(36))) {
    			dev_err(&pdev->dev, "dma_set_mask return error.\n");
    			return -EINVAL;
    		}
    	}
    
    #if !defined(CONFIG_MT_I2C_FPGA_ENABLE)
    	i2c->clk_main = devm_clk_get(&pdev->dev, "main");
    	if (IS_ERR(i2c->clk_main)) {
    		dev_err(&pdev->dev, "cannot get main clock\n");
    		return PTR_ERR(i2c->clk_main);
    	}
    	i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
    	if (IS_ERR(i2c->clk_dma)) {
    		dev_err(&pdev->dev, "cannot get dma clock\n");
    		return PTR_ERR(i2c->clk_dma);
    	}
    	i2c->clk_arb = devm_clk_get(&pdev->dev, "arb");
    	if (IS_ERR(i2c->clk_arb))
    		i2c->clk_arb = NULL;
    	else
    		dev_dbg(&pdev->dev, "i2c%d has the relevant arbitrator clk.\n", i2c->id);
    #endif
    
    	if (i2c->have_pmic) {
    		i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
    		if (IS_ERR(i2c->clk_pmic)) {
    			dev_err(&pdev->dev, "cannot get pmic clock\n");
    			return PTR_ERR(i2c->clk_pmic);
    		}
    		clk_src_in_hz = clk_get_rate(i2c->clk_pmic) / i2c->clk_src_div;
    	} else {
    		clk_src_in_hz = clk_get_rate(i2c->clk_main) / i2c->clk_src_div;
    	}
    	dev_dbg(&pdev->dev, "clock source %p,clock src frequency %d\n",
    		i2c->clk_main, clk_src_in_hz);
    
    	strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
    	mutex_init(&i2c->i2c_mutex);
    	ret = i2c_set_speed(i2c, clk_src_in_hz);
    	if (ret) {
    		dev_err(&pdev->dev, "Failed to set the speed\n");
    		return -EINVAL;
    	}
    	ret = mt_i2c_clock_enable(i2c);
    	if (ret) {
    		dev_err(&pdev->dev, "clock enable failed!\n");
    		return ret;
    	}
    	mt_i2c_init_hw(i2c);
    	mt_i2c_clock_disable(i2c);
    	i2c->dma_buf.vaddr = dma_alloc_coherent(&pdev->dev,
    		PAGE_SIZE, &i2c->dma_buf.paddr, GFP_KERNEL);
    	if (i2c->dma_buf.vaddr == NULL) {
    		dev_err(&pdev->dev, "dma_alloc_coherent fail\n");
    		return -ENOMEM;
    	}
    	i2c_set_adapdata(&i2c->adap, i2c);
    	/* ret = i2c_add_adapter(&i2c->adap); */
    	ret = i2c_add_numbered_adapter(&i2c->adap);
    	if (ret) {
    		dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n");
    		free_i2c_dma_bufs(i2c);
    		return ret;
    	}
    	platform_set_drvdata(pdev, i2c);
    
    	if (!map_cg_regs(i2c))
    		pr_warn("Map cg regs successfully.\n");
    
    	return 0;
    }
    ↓
    
    i2c_add_numbered_adapter() -> __i2c_add_numbered_adapter() -> 
    ↓
    
    static int i2c_register_adapter(struct i2c_adapter *adap)
    {
    	int res = 0;
    
    	/* Can't register until after driver model init */
    	if (unlikely(WARN_ON(!i2c_bus_type.p))) {
    		res = -EAGAIN;
    		goto out_list;
    	}
    
    	/* Sanity checks */
    	if (unlikely(adap->name[0] == '\0')) {
    		pr_err("i2c-core: Attempt to register an adapter with "
    		       "no name!\n");
    		return -EINVAL;
    	}
    	if (unlikely(!adap->algo)) {
    		pr_err("i2c-core: Attempt to register adapter '%s' with "
    		       "no algo!\n", adap->name);
    		return -EINVAL;
    	}
    
    	rt_mutex_init(&adap->bus_lock);
    	mutex_init(&adap->userspace_clients_lock);
    	INIT_LIST_HEAD(&adap->userspace_clients);
    
    	/* Set default timeout to 1 second if not already set */
    	if (adap->timeout == 0)
    		adap->timeout = HZ;
    
        /* 注册adapter为i2c_bus上的device */
    	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
    	adap->dev.bus = &i2c_bus_type;
    	adap->dev.type = &i2c_adapter_type;
    	res = device_register(&adap->dev);
    	if (res)
    		goto out_list;
    
    	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
    
    	pm_runtime_no_callbacks(&adap->dev);
    
    #ifdef CONFIG_I2C_COMPAT
    	res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
    				       adap->dev.parent);
    	if (res)
    		dev_warn(&adap->dev,
    			 "Failed to create compatibility class link\n");
    #endif
    
    	/* bus recovery specific initialization */
    	if (adap->bus_recovery_info) {
    		struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
    
    		if (!bri->recover_bus) {
    			dev_err(&adap->dev, "No recover_bus() found, not using recovery\n");
    			adap->bus_recovery_info = NULL;
    			goto exit_recovery;
    		}
    
    		/* Generic GPIO recovery */
    		if (bri->recover_bus == i2c_generic_gpio_recovery) {
    			if (!gpio_is_valid(bri->scl_gpio)) {
    				dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n");
    				adap->bus_recovery_info = NULL;
    				goto exit_recovery;
    			}
    
    			if (gpio_is_valid(bri->sda_gpio))
    				bri->get_sda = get_sda_gpio_value;
    			else
    				bri->get_sda = NULL;
    
    			bri->get_scl = get_scl_gpio_value;
    			bri->set_scl = set_scl_gpio_value;
    		} else if (!bri->set_scl || !bri->get_scl) {
    			/* Generic SCL recovery */
    			dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n");
    			adap->bus_recovery_info = NULL;
    		}
    	}
    
    exit_recovery:
    	/* create pre-declared device nodes */
        /* 循环遍历adapter node下挂载的其他子node,注册成为i2c bus的device */
    	of_i2c_register_devices(adap);
    	acpi_i2c_register_devices(adap);
    	acpi_i2c_install_space_handler(adap);
    
    	if (adap->nr < __i2c_first_dynamic_bus_num)
    		i2c_scan_static_board_info(adap);
    
    	/* Notify drivers */
    	mutex_lock(&core_lock);
    	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
    	mutex_unlock(&core_lock);
    
    	return 0;
    
    out_list:
    	mutex_lock(&core_lock);
    	idr_remove(&i2c_adapter_idr, adap->nr);
    	mutex_unlock(&core_lock);
    	return res;
    }
    ↓
    
    static void of_i2c_register_devices(struct i2c_adapter *adap)
    {
    	struct device_node *node;
    
    	/* Only register child devices if the adapter has a node pointer set */
    	if (!adap->dev.of_node)
    		return;
    
    	dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
    
        /* 遍历adapter node下的子node,并创建标准的i2c bus的device */
    	for_each_available_child_of_node(adap->dev.of_node, node) {
    		if (of_node_test_and_set_flag(node, OF_POPULATED))
    			continue;
    		of_i2c_register_device(adap, node);
    	}
    }
    ↓
    
    static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
    						 struct device_node *node)
    {
    	struct i2c_client *result;
    	struct i2c_board_info info = {};
    	struct dev_archdata dev_ad = {};
    	const __be32 *addr_be;
    	u32 addr;
    	int len;
    
    	dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
    
    	if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
    		dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
    			node->full_name);
    		return ERR_PTR(-EINVAL);
    	}
    
    	addr_be = of_get_property(node, "reg", &len);
    	if (!addr_be || (len < sizeof(*addr_be))) {
    		dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
    			node->full_name);
    		return ERR_PTR(-EINVAL);
    	}
    
    	addr = be32_to_cpup(addr_be);
    	if (addr & I2C_TEN_BIT_ADDRESS) {
    		addr &= ~I2C_TEN_BIT_ADDRESS;
    		info.flags |= I2C_CLIENT_TEN;
    	}
    
    	if (addr & I2C_OWN_SLAVE_ADDRESS) {
    		addr &= ~I2C_OWN_SLAVE_ADDRESS;
    		info.flags |= I2C_CLIENT_SLAVE;
    	}
    
    	if (i2c_check_addr_validity(addr, info.flags)) {
    		dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
    			info.addr, node->full_name);
    		return ERR_PTR(-EINVAL);
    	}
    
    	info.addr = addr;
    	info.of_node = of_node_get(node);
    	info.archdata = &dev_ad;
    
    	if (of_get_property(node, "wakeup-source", NULL))
    		info.flags |= I2C_CLIENT_WAKE;
    
    	result = i2c_new_device(adap, &info);
    	if (result == NULL) {
    		dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
    			node->full_name);
    		of_node_put(node);
    		return ERR_PTR(-EINVAL);
    	}
    	return result;
    }
    

    3.2.1 mz_mag_driver

    具体的I2c设备驱动,在总线驱动使用of_i2c_register_devices()创建设备以后,就可以适配工作了。

    dts:

    arch\arm64\boot\dts\mediatek\mt6799.dtsi:
    	i2c1: i2c@11090000 {
    		compatible = "mediatek,mt6799-i2c";
    		id = <1>;
    		reg = <0 0x11090000 0 0x1000>,
    			<0 0x11000100 0 0x80>;
    		interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_LOW>;
    		clocks = <&pericfg CLK_PERICFG_RG_I2C1_BCLK>, <&pericfg CLK_PERICFG_RG_AP_DM>;
    		clock-names = "main", "dma";
    		clock-div = <5>;
    	};
    
    arch\arm64\boot\dts\mediatek\mz6799_6m_v2_2k_n.dtsi:
    &i2c1 {
    	apds9922:apds9922@53 {
    		compatible = "mediatek,apds9922";
    		interrupt-parent = <&eintc>;
    		interrupts = < 8 IRQ_TYPE_EDGE_FALLING>;
    		debounce = <8 0>;
    		gpio = < 8 >;
    		reg = <0x53>;
    		status = "okay";
    	};
    }
    

    driver:

    drivers\iio\magnetometer\mz_mag.c:
    static const struct i2c_device_id mz_mag_id[] = {
    	{"mediatek,mmc3530", 0 },
    	{"mediatek,akm09911", 1 },
    	{ }
    };
    
    static struct i2c_driver mz_mag_driver = {
    	.probe     = mz_mag_probe,
    	.id_table  = mz_mag_id,
    	.driver = {
    		.name  = MZ_MAG_DEV_NAME,
    		.owner = THIS_MODULE,
    		#ifdef CONFIG_OF
        	.of_match_table = of_match_ptr(msensor_of_match),
    		#endif
    	},
    };
    
    
    展开全文
  • torch.cuda.set_device(device)

    千次阅读 2020-05-07 11:47:33
    torch.cuda.set_device(device)[source] Sets the current device. Usage of this function is discouraged in favor of device. In most cases it’s better to use CUDA_VISIBLE_DEVICES environmental variable...

    torch.cuda.set_device(device)[source]

    Sets the current device.

    Usage of this function is discouraged in favor of device. In most cases it’s better to use CUDA_VISIBLE_DEVICES environmental variable.

    Parameters:

    • device (torch.device or int) – selected device. This function is a no-op if this argument is negative.

     

    展开全文
  • 自从上一次在实验室插电源时出现电压问题之后,我的笔记本启动就会出现no bootable device,觉得这个问题实在是会偶然性发生且网上并没有实际有效的回答解决,所以我在此以自己的亲身体验写下我自己的解决办法(因为...
  • 成功解决 gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: GeForce 94 目录 解决问题 解决思路 解决方法 解决问题 gpu_device.cc:1120] Creating TensorFlow ...
  • 虚拟机打开出错:VMware Workstation 与 Device/Credential Guard 不兼容.在禁用 Device/Credenti。。。。。。 这个时候我们需要去关掉几个功能 1、关闭Hyper-V 打开控制面板首页,找到“程序”,然后找到“启用...
  • Graphic Device

    千次阅读 2020-07-03 17:24:35
    Graphic Device菜单,switchable混合输出模式,另一个独立输出
  • Pytorch to(device

    万次阅读 多人点赞 2019-07-09 16:21:43
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model.to(device) 这两行代码放在读取数据之前。 mytensor = my_tensor.to(device) 这行代码的意思是将所有最开始读取数据时的tensor...
  • devicemapper

    千次阅读 2018-07-12 10:35:23
    1. Device mapper和thinprovison1) Devicemapper简介Devicemapper是内核中支持逻辑卷管理的通用设备映射机制,它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核架构,它包含三个重要的对象概念,...
  • 2、关掉 Device/Credenti “win+ R“打开运行,输入gpedit.msc 本地计算机策略 --> 计算机配置 --> 管理模板>系统 --> Device Guard 基于虚拟化的安全设置为“已禁用” 3、bcdedit /set hypervis
  • Device Install Service和device setup manager这两个服务启动后为什么老是自动关闭?
  • device_create和device_add区别

    千次阅读 2018-07-28 12:04:34
    在学习linux驱动的时候经常遇到device_create和device_add两个函数,因为这两个函数都是属于驱动底层的,所以平时很少关注。但最近准备写一个简单驱动框架练手,所以到底用那个来创建出相应的设备,所以准备学习一下...
  • struct device

    千次阅读 2017-03-03 00:57:18
    struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; /* initial name of the device */ struct device_type *type; struct mutex mutex;
  • win 10 系统1903版 VMware Workstation 与 Device/Credential Guard 不兼容.在禁用 Device/Credenti 查看win10系统版本号 查看 内核隔离 是否关闭 查看 Hyper-V 是否关闭 关闭 Device/Credenti 查看win10 系统...
  • 前言今天同事配置服务器发现vim文件的时候,竟然失败了,报了这个错误:No space left on device,故帮忙解决了一下。顺便记录一下。正文发现问题出现这个错误第一反应是空间满了。df -h 一看却发现还有挺多没有用 ...
  • Linux kernel的思路很简单:驱动开发,就是要开发指定的软件(driver)以驱动指定的设备(device),所以kernel就为设备和驱动它的driver定义了两个数据结构,分别是devicedevice_driver。因此本文将会围绕这两个...
  • 系统重装后no bootable device

    万次阅读 2019-09-03 14:28:46
    就在前两天因为自己把系统搞崩了,没办法只能重装系统,但是通过PE重装完后开机竟然报【no bootable device】,而且启动项里检测不到硬盘,当时就傻眼了,重装系统不会把硬盘搞坏了吧。 案当然不是,相信很多小...
  • Device device = new Device(); device.setKeyNum(""); device.setcodingNum(scanResult); 这几句代码什么意思啊 百度不出来怎么 安卓项目里 的
  • torch.device作用

    万次阅读 2019-07-18 17:35:58
    torch.device代表将torch.Tensor分配到的设备的对象。torch.device包含一个设备类型(‘cpu’或‘cuda’)和可选的设备序号。如果设备序号不存在,则为当前设备。如:torch.Tensor用设备构建‘cuda’的结果等同于...
  • VMware Workstation 与 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard 后,可以运行 VMware Workstation。 这是在用VMware Workstation 装 Ubuntu虚拟机时出现的问题,网上看了一些方法,均...
  • Apowersoft_Audio Device 一个是Apowersoft Audio Device Apowersoft_Audio Device 前往文件夹/Library/Extensions和/System/Library/Extensions 删除Apowersoft_AudioDevice.kext Apowersoft Audio Device ...
  • deviceQuery

    千次阅读 2015-10-14 13:36:25
    C:\ProgramData\NVIDIA Corporation\CUDA Samples\v7.5\1_Utilities\deviceQuery\../. ./bin/win64/Debug/deviceQuery.exe Starting...  CUDA Device Query (Runtime API) version (CUDART static linking)
  • Pytorch的to(device)用法

    万次阅读 2020-01-15 20:52:59
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model.to(device) 这两行代码放在读取数据之前。 mytensor = my_tensor.to(device) 这行代码的意思是将所有最开始读取数据时的...
  • meta name="viewport" content="width=device-width,initial-scale=1.0" 解释 <meta name="viewport" content="width=device-width,initial-scale=1.0"> content属性值 : width:可视区域的宽度,值可为...
  • 1 模型是否放到了CUDA上 model = model.to(device) 或 model = model.cuda(device) 2 输入数据是否放到了CUDA上 data = data.to(device) 或 data = data .cuda(device) 3 模型内部新建的张量是否放到了CUDA上 p = ...
  • 一键设置 DeviceAdmin/ProfileOwner/DeviceOwner 应用

    千次阅读 热门讨论 2018-12-03 15:36:53
    一键设置DeviceOwner 概述 Android提供了三种设备管理方案,Device Administration(设备管理员), ProfileOwner(配置文件所有者)和 DeviceOwner(设备所有者)。这三种管理方案对应三种等级的管理权限,相对的,等级越...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 313,351
精华内容 125,340
关键字:

device