linux设备树-linux内核设备树移植(二)

发布时间 2023-04-13 23:36:17作者: 大奥特曼打小怪兽

----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8
根文件系统:busybox 1.25.0
u-boot:2016.05
----------------------------------------------------------------------------------------------------------------------------

一、修改设备树s3c2440.dtsi

s3c2440.dtsi设备树存放的是s3c2440这个SoC跟其他s3c24xx系列不同的一些硬件信息,如clock控制器、串口等等;

修改arch/arm/boot/dts/s3c2440.dtsi文件,将该文件中的2416全部替换成2440,替换后文件如下:

#include <dt-bindings/clock/s3c2443.h>  /* 注意 */
#include "s3c24xx.dtsi"
#include "s3c2440-pinctrl.dtsi"

/ {
        model = "Samsung S3C2440 SoC";
        compatible = "samsung,s3c2440","samsung,mini2440";

        aliases {
                serial3 = &uart_3;
        };

        cpus {
                cpu {
                        compatible = "arm,arm926ej-s";
                };
        };

        interrupt-controller@4a000000 {
                compatible = "samsung,s3c2440-irq";
        };

        clocks: clock-controller@4c000000 {
                compatible = "samsung,s3c2440-clock";
                reg = <0x4c000000 0x40>;
                #clock-cells = <1>;
        };

        pinctrl@56000000 {
                compatible = "samsung,s3c2440-pinctrl";
        };

        timer@51000000 {
                clocks = <&clocks PCLK_PWM>;
                clock-names = "timers";
        };

        uart_0: serial@50000000 {
                compatible = "samsung,s3c2440-uart";
                clock-names = "uart", "clk_uart_baud2",
                                "clk_uart_baud3";
                clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>,
                                <&clocks SCLK_UART>;
        };

        uart_1: serial@50004000 {
                compatible = "samsung,s3c2440-uart";
                clock-names = "uart", "clk_uart_baud2",
                                "clk_uart_baud3";
                clocks = <&clocks PCLK_UART1>, <&clocks PCLK_UART1>,
                                <&clocks SCLK_UART>;
        };

        uart_2: serial@50008000 {
                compatible = "samsung,s3c2440-uart";
                clock-names = "uart", "clk_uart_baud2",
                                "clk_uart_baud3";
                clocks = <&clocks PCLK_UART2>, <&clocks PCLK_UART2>,
                                <&clocks SCLK_UART>;
        };
        uart_3: serial@5000c000 {
                compatible = "samsung,s3c2440-uart";
                reg = <0x5000C000 0x4000>;
                interrupts = <1 18 24 4>, <1 18 25 4>;
                clock-names = "uart", "clk_uart_baud2",
                                "clk_uart_baud3";
                clocks = <&clocks PCLK_UART3>, <&clocks PCLK_UART3>,
                                <&clocks SCLK_UART>;
                status = "disabled";
        };

        sdhci_1: sdhci@4ac00000 {
                compatible = "samsung,s3c6410-sdhci";
                reg = <0x4AC00000 0x100>;
                interrupts = <0 0 21 3>;
                clock-names = "hsmmc", "mmc_busclk.0",
                                "mmc_busclk.2";
                clocks = <&clocks HCLK_HSMMC0>, <&clocks HCLK_HSMMC0>,
                                <&clocks MUX_HSMMC0>;
                status = "disabled";
        };

        sdhci_0: sdhci@4a800000 {
                compatible = "samsung,s3c6410-sdhci";
                reg = <0x4A800000 0x100>;
                interrupts = <0 0 20 3>;
                clock-names = "hsmmc", "mmc_busclk.0",
                                "mmc_busclk.2";
                clocks = <&clocks HCLK_HSMMC1>, <&clocks HCLK_HSMMC1>,
                                <&clocks MUX_HSMMC1>;
                status = "disabled";
        };

        watchdog: watchdog@53000000 {
                interrupts = <1 9 27 3>;
                clocks = <&clocks PCLK_WDT>;
                clock-names = "watchdog";
        };

        rtc: rtc@57000000 {
                compatible = "samsung,s3c2440-rtc";
                clocks = <&clocks PCLK_RTC>;
                clock-names = "rtc";
        };

        i2c@54000000 {
                compatible = "samsung,s3c2440-i2c";
                clocks = <&clocks PCLK_I2C0>;
                clock-names = "i2c";
        };
};

接着我们需要对该文件进行修改来适配s3c2440。

1.1 修改头文件

修改时钟编号相关宏头文件:

#include <dt-bindings/clock/s3c2443.h>

修改为:

#include <dt-bindings/clock/s3c2410.h>

1.2 修改根节点compatible 

compatible需要与mach-smdk2440-dt.c文件dt_compat数组数组的compatible匹配,修改为:

        model = "Samsung S3C2440 SoC";
        compatible = "samsung,s3c2440","samsung,mini2440";

1.3 cpus节点

由于s3c2440内核为arm920t,因此修改cpus为:

        cpus {
                cpu {
                        compatible = "arm,arm920t";
                };
        };

1.4 中断控制器节点

移除s3c2440.dtsi中中断控制器节点,在父级设备树s3c24xx.dtsi文件中定义有:

intc:interrupt-controller@4a000000 {
     compatible = "samsung,s3c2410-irq";
     reg = <0x4a000000 0x100>;
     interrupt-controller;
     #interrupt-cells = <4>;
};

中断控制器节点在上一片博客已经介绍过了,这里不重复介绍了。

关于中断控制器这部分可以参考内核文档:

  • Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt;
  • Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml;
  • Documentation/devicetree/bindings/interrupt-controller/interrupts.txt;
1.4.1 未使用设备树

在内核移植不使用设备树的时候,在arch/arm/mach-s3c24xx/mach-smdk2440.c文件:

MACHINE_START(S3C2440, "SMDK2440")
        /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
        .atag_offset    = 0x100,

        .init_irq       = s3c2440_init_irq,
        .map_io         = smdk2440_map_io,
        .init_machine   = smdk2440_machine_init,
        .init_time      = smdk2440_init_time,
MACHINE_END

void __init s3c2440_init_irq(void)
{
        pr_info("S3C2440: IRQ Support\n");

#ifdef CONFIG_FIQ
        init_FIQ(FIQ_START);
#endif

        s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL,    // 初始化32个主中断源相关的中断
                                        0x4a000000);
        if (IS_ERR(s3c_intc[0])) {
                pr_err("irq: could not create main interrupt controller\n");
                return;
        }

        s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);      // 初始化外部中断相关的中断、外部中断4~7、8~23分别对应主中断源中的的EINT4~7、EINT8~23
        s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0],         // 初始化带有子中断的内部中断相关的中断
                                        s3c_intc[0], 0x4a000018);
}

这里直接调用s3c24xx_init_intc进行中断控制器初始化,函数位于drivers/irqchip/irq-s3c24xx.c,具体可以参考linux驱动移植-中断子系统执行流程

1.4.2 使用设备树

在linux内核根路径下搜索samsung,s3c2410-irq:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# grep "samsung,s3c2410-irq" * -nR
drivers/irqchip/irq-s3c24xx.c:1307:IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);

定位到drivers/irqchip/irq-s3c24xx.c文件:

static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = {
        {
                .name = "intc",
                .offset = 0,
        }, {
                .name = "subintc",
                .offset = 0x18,
                .parent = &s3c_intc[0],
        }
};

int __init s3c2410_init_intc_of(struct device_node *np,
                        struct device_node *interrupt_parent)
{
        return s3c_init_intc_of(np, interrupt_parent,
                                s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
}
IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);

IRQCHIP_DECLARE的作用如下所述:用IRQCHIP_DECLARE声明兼容的中断控制器驱动,并将其与初始化函数关联。

其中IRQCHIP_DECLARE宏定义在include/linux/irqchip.h中:

#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
 
#define OF_DECLARE_2(table, name, compat, fn) \ 
        _OF_DECLARE(table, name, compat, fn, of_init_fn_2)
 
#define _OF_DECLARE(table, name, compat, fn, fn_type)            \ 
    static const struct of_device_id __of_table_##name        \ 
        __used __section(__##table##_of_table)            \ 
         = { .compatible = compat,                \ 
             .data = (fn == (fn_type)NULL) ? fn : fn  }

这一段需要借助内核编译的lds文件来解读,其中传入了参数给编译器来确定变量的存放位置,其实就是定义了:

static const struct of_device_id __clk_of_table_s3c2410_irq \
__used__section(__irqchip_of_table) \
= {
  .compatible = "samsung,s3c2410-irq",
  .data = s3c2410_init_intc_of,
};

通过IRQCHIP_DECLARE来定义相应的of_device_id,并且要把相应的驱动初始化函数fn的地址(即s3c2410_init_intc_of)传给data。

这样当设备树定义有compatible = "samsung,s3c2410-irq"时,匹配到相应的设备时就会直接调用驱动初始化函数s3c2410_init_intc_of了。

static int __init s3c_init_intc_of(struct device_node *np,
                        struct device_node *interrupt_parent,
                        struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl)
{
        struct s3c_irq_intc *intc;
        struct s3c24xx_irq_of_ctrl *ctrl;
        struct irq_domain *domain;
        void __iomem *reg_base;
        int i;

        reg_base = of_iomap(np, 0);
        if (!reg_base) {
                pr_err("irq-s3c24xx: could not map irq registers\n");
                return -EINVAL;
        }

        domain = irq_domain_add_linear(np, num_ctrl * 32,
                                                     &s3c24xx_irq_ops_of, NULL);
        if (!domain) {
                pr_err("irq: could not create irq-domain\n");
                return -EINVAL;
        }

        for (i = 0; i < num_ctrl; i++) {
                ctrl = &s3c_ctrl[i];

                pr_debug("irq: found controller %s\n", ctrl->name);

                intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
                if (!intc)
                        return -ENOMEM;

                intc->domain = domain;
                intc->irqs = kcalloc(32, sizeof(struct s3c_irq_data),
                                     GFP_KERNEL);
                if (!intc->irqs) {
                        kfree(intc);
                        return -ENOMEM;
                }

                if (ctrl->parent) {
                        intc->reg_pending = reg_base + ctrl->offset;
                        intc->reg_mask = reg_base + ctrl->offset + 0x4;

                        if (*(ctrl->parent)) {
                                intc->parent = *(ctrl->parent);
                        } else {
                                pr_warn("irq: parent of %s missing\n",
                                        ctrl->name);
                                kfree(intc->irqs);
                                kfree(intc);
                                continue;
                        }
                } else {
                        intc->reg_pending = reg_base + ctrl->offset;
                        intc->reg_mask = reg_base + ctrl->offset + 0x08;
                        intc->reg_intpnd = reg_base + ctrl->offset + 0x10;
                }

                s3c24xx_clear_intc(intc);
                s3c_intc[i] = intc;
        }

        set_handle_irq(s3c24xx_handle_irq);

        return 0;
}
View Code

1.5 时钟控制器节点

s3c2440.dtsi中定义了时钟控制器节点,在内核文档中称之为"Clock providers":

        clocks: clock-controller@4c000000 {
                compatible = "samsung,s3c2440-clock";
                reg = <0x4c000000 0x40>;
                #clock-cells = <1>;
        };

时钟提供者节点必须有#clock-cells属性,说明该节点是clock provider。它有2种取值:

  • #clock-cells = <0>,只有一个时钟输出;
  • #clock-cells = <1>,有多个时钟输出;

设备需要时钟时,它是"Clock consumers"。它描述了使用哪一个"Clock providers"中的哪一个时钟;

  • 每个时钟都分配了一个标识符,设备节点可以使用此标识符来指定它们使用的时钟。其中一些时钟仅在特定的SoC上可用;
  • 所有可用的时钟都定义为预处理器宏并位于dt-bindings/clock/s3c2410.h头文件中,可以在设备树源代码中使用;

比如使用时钟控制器生成的时钟的UART控制器节点:

        serial@50004000 {
                compatible = "samsung,s3c2440-uart";
                reg = <0x50004000 0x4000>;
                interrupts = <1 23 3 4>, <1 23 4 4>;
                clock-names = "uart", "clk_uart_baud2";  /*在s3c24xx_serial_probe中,调用clk_get(&platdev->dev, "uart")根据clock-names来查找并获取对时钟生产者的引用 */
                clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>;  /* 使用clocks即clock-controller@4c000000中的PCLK_UART0、
其中PCLK_UART0参考include/dt-bindings/clock/s3c2410.h或者这个clock控制器驱动的实现。 */
};

时钟使用者(clock consumer)的node节点必须有clocks属性,说明该节点是clock consumer。clocks属性由2部分组成:phandle、clock-specifier。

  •  phandle是@节点名称(例如上个示例中的@clokcks);
  •  clock-specifier怎么理解呢?一个时钟控制器可以控制很多时钟硬件(例如5种基本时钟:fixed-rate、fixed-factor、gate、mux、divider),每种时钟硬件都有对应的ID号。clock-specifier就是这个ID号。例如,s3c2440的时钟硬件编号已经在include/dt-bindings/clock/s3c2410.h中声明了;

另外,注意:如果时钟提供者将#clock-cells指定为“0”,则仅会出现该对中的phandle部分。

关于clock这部分可以参考内核文档:

  • Documentation/devicetree/bindings/clock/samsung,s3c2410-clock.txt;
  • Documentation/devicetree/bindings/clock/clock-bindings.txt;
1.5.1 未使用设备树

在内核移植不使用设备树的时候,在arch/arm/mach-s3c24xx/mach-smdk2440.c文件:

MACHINE_START(S3C2440, "SMDK2440")
        /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
        .atag_offset    = 0x100,

        .init_irq       = s3c2440_init_irq,
        .map_io         = smdk2440_map_io,
        .init_machine   = smdk2440_machine_init,
        .init_time      = smdk2440_init_time,
MACHINE_END

static void __init smdk2440_init_time(void)
{
        s3c2440_init_clocks(12000000);
        samsung_timer_init();
}

直接调用s3c2440_init_clocks初始化linux内核的时钟,晶振频率为12MHz;

void __init s3c2440_init_clocks(int xtal)
{
        s3c2410_common_clk_init(NULL, xtal, 1, S3C24XX_VA_CLKPWR);    // 1对应的枚举变量S3C2440
}

这里直接调用s3c2410_common_clk_init进行s3c2440时钟的初始化,具体可以参考linux驱动移植-通用时钟框架子系统

1.5.2 使用设备树

在linux内核根路径下搜索samsung,s3c2440-clock:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# grep "s3c2440-clock" * -nR
Documentation/devicetree/bindings/clock/samsung,s3c2410-clock.txt:11:  - "samsung,s3c2440-clock" - controller compatible with S3C2440 SoC.
drivers/clk/samsung/clk-s3c2410.c:437:CLK_OF_DECLARE(s3c2440_clk, "samsung,s3c2440-clock", s3c2440_clk_init);

定位到drivers/clk/samsung/clk-s3c2410.c文件:

static void __init s3c2440_clk_init(struct device_node *np)
{
        s3c2410_common_clk_init(np, 0, S3C2440, NULL);
}
CLK_OF_DECLARE(s3c2440_clk, "samsung,s3c2440-clock", s3c2440_clk_init);

CLK_OF_DECLARE的作用如下所述:用CLK_OF_DECLARE声明兼容的时钟驱动,并将其与初始化函数关联。

其中CLK_OF_DECLARE宏定义在include/linux/clk-provider.h中:

#define CLK_OF_DECLARE(name, compat, fn) \
static const struct of_device_id __clk_of_table_##name \
__used __section(__clk_of_table) \
= { .compatible = compat, .data = fn };

这一段需要借助内核编译的lds文件来解读,其中传入了参数给编译器来确定变量的存放位置,其实就是定义了:

static const struct of_device_id __clk_of_table_s3c2440_clk \
__used__section(__clk_of_table) \
= {
  .compatible = "samsung,s3c2440-clock",
  .data = s3c2440_clk_init,
};

通过CLK_OF_DECLARE来定义相应的of_device_id,并且要把相应的驱动初始化函数fn的地址(即s3c2440_clk_init)传给data。

这样当设备树定义有compatible = "samsung,s3c2440-clock"时,匹配到相应的设备时就会直接调用驱动初始化函数s3c2440_clk_init了。

s3c2440_clk_init函数其内部调用s3c2410_common_clk_init进行进行s3c2440时钟的初始化。

1.6 串口节点

s3c2440只有三个串口,因此需要修改s3c2440.dtsi文件中定义的串口节点uart_0、uart_1、uart_2,同时删除uart_3节点:

        uart_0: serial@50000000 {
                compatible = "samsung,s3c2440-uart";   // 对应驱动定义在drivers/tty/serial/samsung.c
                clock-names = "uart";
                clocks = <&clocks PCLK_UART0>;
        };

        uart_1: serial@50004000 {
                compatible = "samsung,s3c2440-uart";
                clock-names = "uart";
                clocks = <&clocks PCLK_UART1>;
        };

        uart_2: serial@50008000 {
                compatible = "samsung,s3c2440-uart";
                clock-names = "uart";
                clocks = <&clocks PCLK_UART2>;
        };

在父级设备树s3c24xx.dtsi文件中定义有:

        uart0: serial@50000000 {
                compatible = "samsung,s3c2410-uart";
                reg = <0x50000000 0x4000>;
                interrupts = <1 28 0 4>, <1 28 1 4>;
                status = "disabled";
        };

        uart1: serial@50004000 {
                compatible = "samsung,s3c2410-uart";
                reg = <0x50004000 0x4000>;
                interrupts = <1 23 3 4>, <1 23 4 4>;
                status = "disabled";
        };

        uart2: serial@50008000 {
                compatible = "samsung,s3c2410-uart";
                reg = <0x50008000 0x4000>;
                interrupts = <1 15 6 4>, <1 15 7 4>;
                status = "disabled";
        };

串口节点属性在上一片博客已经介绍过了,这里不重复介绍了。

在drivers/tty/serial/samsung.c中可以匹配关键字 “samsung,s3c2440-uart”;当platform设备和驱动匹配后,s3c24xx_serial_probe函数被调用。

在s3c24xx_serial_probe函数中,调用clk_get(&platdev->dev, "uart")根据设备名称/时钟别名来查找并获取对时钟生产者的引用,获取时钟生产者的clk结构体。其中:

  • dev是时钟使用者设备(device for clock consumer);
  • "uart"是串口时钟别名;

关于串口这部分可以参考内核文档:

  • Documentation/devicetree/bindings/serial/samsung_uart.txt;

1.7 看门狗节点

修改s3c2440.dtsi中看门狗节点信息:

        watchdog: watchdog@53000000 {
                interrupts = <1 9 27 3>;
                clocks = <&clocks PCLK>;     /* 修改为PCLK */
                clock-names = "watchdog";
        };

在父级设备树s3c24xx.dtsi文件中定义有:

        watchdog@53000000 {
                compatible = "samsung,s3c2410-wdt";
                reg = <0x53000000 0x100>;
                interrupts = <0 0 9 3>;
                status = "disabled";
        };

从S3C2440芯片手册上看,看门狗的时钟直接接到PCLK上,没有加任何开关,所以这里clocks修改为PCLK

关于watchdog这部分可以参考内核文档:

  • Documentation/devicetree/bindings/watchdog/samsung-wdt.txt;

1.8 rtc节点

修改s3c2440.dtsi中rtc节点信息:

        rtc: rtc@57000000 {
                compatible = "samsung,s3c2140-rtc";   // 对应驱动对应在 drivers/rtc/rtc-s3c.c
                clocks = <&clocks PCLK_RTC>;
                clock-names = "rtc";
        };

在父级设备树s3c24xx.dtsi文件中定义有:

        rtc@57000000 {
                compatible = "samsung,s3c2410-rtc";
                reg = <0x57000000 0x100>;
                interrupts = <0 0 30 3>, <0 0 8 3>;
                status = "disabled";
        };

在drivers/rtc/rtc-s3c.c中可以匹配关键字 “samsung,s3c2410-rtc”;当platform设备和驱动匹配后,s3c_rtc_probe函数被调用。

在s3c_rtc_probe函数中,调用devm_clk_get(&pdev->dev, "rtc")根据设备名称/时钟别名来查找并获取对时钟生产者的引用,获取时钟生产者的clk结构体。其中:

  • dev是时钟使用者设备(device for clock consumer);
  • "rtc"是rtc时钟别名;

关于rtc这部分可以参考内核文档:

  • Documentation/devicetree/bindings/rtc/rtc.txt;
  • Documentation/devicetree/bindings/rtc/s3c-rtc.txt;

1.9 i2c节点

修改s3c2440.dtsi中i2c节点信息:

        i2c@54000000 {
                compatible = "samsung,s3c2440-i2c";
                clocks = <&clocks PCLK_I2C0>;
                clock-names = "i2c";
        };

在父级设备树s3c24xx.dtsi文件中定义有:

        i2c@54000000 {
                compatible = "samsung,s3c2410-i2c";
                reg = <0x54000000 0x100>;
                interrupts = <0 0 27 3>;
                #address-cells = <1>;
                #size-cells = <0>;
                status = "disabled";
        };

关于i2c这部分可以参考内核文档:

  • Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt;

二、修改设备树s3c2440-smdk2440.dts

修改s3c2440-smdk2440.dts设备树,同时将该文件中的2416全部替换成2440:

/dts-v1/;
#include "s3c2440.dtsi"

/ {
        model = "SMDK2440";
        compatible = "samsung,s3c2440","samsung,mini2440";

        memory@30000000 {
                device_type = "memory";
                reg =  <0x30000000 0x4000000>;
        };

        clocks {
                compatible = "simple-bus";
                #address-cells = <1>;
                #size-cells = <0>;

                xti: xti@0 {
                        compatible = "fixed-clock";
                        reg = <0>;
                        clock-frequency = <12000000>;
                        clock-output-names = "xti";
                        #clock-cells = <0>;
                };
        };
};

&rtc {
        status = "okay";
};

&sdhci_0 {
        pinctrl-names = "default";
        pinctrl-0 = <&sd1_clk>, <&sd1_cmd>,
                        <&sd1_bus1>, <&sd1_bus4>;
        bus-width = <4>;
        broken-cd;
        status = "okay";
};

&sdhci_1 {
        pinctrl-names = "default";
        pinctrl-0 = <&sd0_clk>, <&sd0_cmd>,
                        <&sd0_bus1>, <&sd0_bus4>;
        bus-width = <4>;
        cd-gpios = <&gpf 1 0>;
        cd-inverted;
        status = "okay";
};

&uart_0 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&uart0_data>, <&uart0_fctl>;
};

&uart_1 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&uart1_data>, <&uart1_fctl>;
};

&uart_2 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&uart2_data>;
};

&uart_3 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&uart3_data>;
};

&watchdog {
        status = "okay";
};

2.1 fixed-clock时钟配置

晶振设备节点xti定义如下:

 clocks {
                compatible = "simple-bus";
                #address-cells = <1>;
                #size-cells = <0>;

                xti: xti@0 {
                        compatible = "fixed-clock";
                        reg = <0>;
                        clock-frequency = <12000000>;
                        clock-output-names = "xti";
                        #clock-cells = <0>;
                };
        };

根据compatible可以找到对应的驱动,驱动程序将晶振的频率记录下来,以后作为计算的基准。

三、uboot FDT命令

uboot提供了fdt的相关命令:

"addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>\n"
    "fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n"
    "fdt resize [<extrasize>]            - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n"
    "fdt print  <path> [<prop>]          - Recursive print starting at <path>\n"
    "fdt list   <path> [<prop>]          - Print one level starting at <path>\n"
    "fdt get value <var> <path> <prop>   - Get <property> and store in <var>\n"
    "fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>\n"
    "fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>\n"
    "fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>\n"
    "fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n"
    "fdt mknode <path> <node>            - Create a new node after <path>\n"
    "fdt rm     <path> [<prop>]          - Delete the node or <property>\n"
    "fdt header                          - Display header info\n"
    "fdt bootcpu <id>                    - Set boot cpuid\n"
    "fdt memory <addr> <size>            - Add/Update memory node\n"
    "fdt rsvmem print                    - Show current mem reserves\n"
    "fdt rsvmem add <addr> <size>        - Add a mem reserve\n"
    "fdt rsvmem delete <index>           - Delete a mem reserves\n"
    "fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree\n"
    "                                        <start>/<end> - initrd start/end addr\n"

参考文章

[1]linux设备驱动(19)设备树详解3-u-boot传输dts

[2]linux设备驱动(20)设备树详解4-kernel解析dts

[3]linux设备驱动(21)设备树详解5-dts的应用

[4]第四课:u-boot对设备树的支持

[5][uboot] (番外篇)uboot之fdt介绍

[6]内核解析U-boot传入的machid

[7]在JZ2440上移植设备树---下:内核和设备树移植

[8]讓TQ2440也用上設備樹(1) 

[9]TQ2440设备树

[10]Exynos4412的Linux5.4.174时钟驱动开发(五)——时钟设备树的修改方法

[11]在linux4.15 移植设备树到JZ2440