程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

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

----------------------------------------------------------------------------------------------------------------------------
内核版本: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,同时移除s3c6410相关的节点,文件内容如下:

#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";
        };

        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";
        };
};
View Code

接着我们需要对该文件进行修改来适配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  }

展开后,其实就是定义了:

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,
};

定义一个名为__clk_of_table_s3c2410_irq的of_device_id结构体,并将其放置在内核的.__irqchip_of_table节(section)内。 

这样当设备树定义有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 consumer。它描述了使用哪一个clock provider中的哪一个时钟;

  • 每个时钟都分配了一个标识符,设备节点可以使用此标识符来指定它们使用的时钟。其中一些时钟仅在特定的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";  /* 时钟名,uart、clk_uart_baud2 */
        clocks = <&clocks PCLK_UART0>, <&clocks PCLK_UART0>;  /*  指定uart时钟来自PCLK_UART0; 指定clk_uart_baud2时钟来自PCLK_UART0
                                                       其中PCLK_UART0参考include/dt-bindings/clock/s3c2410.h或者这个clock控制器驱动的实现。 */
};

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

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

clocks-names:用于指定时钟名,调用devm_clk_get获取时钟时,可以传入该名字;比如在s3c24xx_serial_probe中,调用clk_get(&platdev->dev, "uart") 获取时钟;

另外,注意:如果时钟提供者将#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驱动移植-通用时钟框架子系统

void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,   // np为NULL、xti_f=12000000、current_soc=1
                                    int current_soc,
                                    void __iomem *base)
{
        ....

        /* Register external clocks only in non-dt cases */
        if (!np)
                s3c2410_common_clk_register_fixed_ext(ctx, xti_f);   // 注册通用的外部固定时钟,即注册晶振

        ...
}
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时钟的初始化。

void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,   // np为NULL、xti_f=12000000、current_soc=1
                                    int current_soc,
                                    void __iomem *base)
{
        ....

        /* Register external clocks only in non-dt cases */
        if (!np)  // 不为空,所以不会走这里   外部固定时钟的注册也是通过设备树实现的,后面介绍
                s3c2410_common_clk_register_fixed_ext(ctx, xti_f);   

        ...
}

1.6 pinctrl节点

s3c2440.dtsi文件pinctrl节点定义如下:

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

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

pinctrl_0: pinctrl@56000000 {
        reg = <0x56000000 0x1000>;

        wakeup-interrupt-controller {
                compatible = "samsung,s3c2410-wakeup-eint";
                interrupts = <0 0 0 3>,
                             <0 0 1 3>,
                             <0 0 2 3>,
                             <0 0 3 3>,
                             <0 0 4 4>,
                             <0 0 5 4>;
        };
};

在s3c2440-pinctrl.dtsi文件中定义了pin bank、以及pin group子节点:

&pinctrl_0 {
        /*
         * Pin banks
         */

        gpa: gpa {
                gpio-controller;
                #gpio-cells = <2>;
        };
        ....
}

1.7 串口节点

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

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

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

uart_2: serial@50008000 {
        compatible = "samsung,s3c2440-uart";
        clock-names = "uart","clk_uart_baud2";
        clocks = <&clocks PCLK_UART2>,<&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_get(&platdev->dev, "clk_uart_baud2")根据设备名称/时钟别名来查找并获取对时钟生产者的引用。其中:

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

因此uart_0 clk_get函数获取到的时钟为<&clocks PCLK_UART0>;uart_1 clk_get函数获取到的时钟为<&clocks PCLK_UART1>,uart_2 clk_get函数获取到的时钟为<&clocks PCLK_UART2>;

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

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

1.8 看门狗节点

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

watchdog: watchdog@53000000 {
        interrupts = <1 9 27 3>;
        clocks = <&clocks PCLK>;     /* PCLK_WDT修改为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.9 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")根据设备名称/时钟别名来查找并获取对时钟生产者的引用。其中:

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

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

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

1.10 i2c节点

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

i2c: i2c@54000000 {
        compatible = "samsung,s3c2440-i2c";
        clocks = <&clocks PCLK_I2C>;  /* PCLK_I2C0修改为PCLK_I2C */
        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;

1.11 新增nand节点

nand0: nand@4e000000 {
    compatible = "samsung,s3c2440-nand";
    reg = <0x4e000000 0x40>;
    interrupts = <0 0 24 3>;
    clocks = <&clocks HCLK_NAND>;
    clock-names = "nand";
    pinctrl-names = "default";
    pinctrl-0 = <&nand_pinctrl>;
    status = "disabled";
};

1.12 文件内容

修改之后,arch/arm/boot/dts/s3c2440.dtsi文件内容如下:

// SPDX-License-Identifier: GPL-2.0
/*
 * Samsung's S3C2440 SoC device tree source
 *
 * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
 */

#include <dt-bindings/clock/s3c2410.h>
#include "s3c24xx.dtsi"
#include "s3c2440-pinctrl.dtsi"

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

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


        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";
                clocks = <&clocks PCLK_UART0>,<&clocks PCLK_UART0>;
        };

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

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

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

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

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

        nand0: nand@4e000000 {
                compatible = "samsung,s3c2440-nand";
                reg = <0x4e000000 0x40>;
                interrupts = <0 0 24 3>;
                clocks = <&clocks HCLK_NAND>;
                clock-names = "nand";
                pinctrl-names = "default";
                pinctrl-0 = <&nand_pinctrl>;
                status = "disabled";
   };
};
View Code

二、修改设备树s3c2440-pinctrl.dtsi

由于s3c2440只有GPA~GPJ,因此将GPJ之后的引脚配置移除,比如gpl,得到文件内容如下:

// SPDX-License-Identifier: GPL-2.0
/*
 * Samsung S3C2416 pinctrl settings
 *
 * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
 */

#include <dt-bindings/pinctrl/samsung.h>

&pinctrl_0 {
    /*
     * Pin banks
     */

    gpa: gpa {
        gpio-controller;
        #gpio-cells = <2>;
    };

    gpb: gpb {
        gpio-controller;
        #gpio-cells = <2>;
    };

    gpc: gpc {
        gpio-controller;
        #gpio-cells = <2>;
    };

    gpd: gpd {
        gpio-controller;
        #gpio-cells = <2>;
    };

    gpe: gpe {
        gpio-controller;
        #gpio-cells = <2>;
    };

    gpf: gpf {
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
    };

    gpg: gpg {
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
    };

    gph: gph {
        gpio-controller;
        #gpio-cells = <2>;
    };

    gpj: gpj {
        gpio-controller;
        #gpio-cells = <2>;
    };/*
     * Pin groups
     */

    uart0_data: uart0-data {
        samsung,pins = "gph-0", "gph-1";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    uart0_fctl: uart0-fctl {
        samsung,pins = "gph-8", "gph-9";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    uart1_data: uart1-data {
        samsung,pins = "gph-2", "gph-3";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    uart1_fctl: uart1-fctl {
        samsung,pins = "gph-10", "gph-11";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    uart2_data: uart2-data {
        samsung,pins = "gph-4", "gph-5";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    uart2_fctl: uart2-fctl {
        samsung,pins = "gph-6", "gph-7";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    uart3_data: uart3-data {
        samsung,pins = "gph-6", "gph-7";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    extuart_clk: extuart-clk {
        samsung,pins = "gph-12";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    i2c0_bus: i2c0-bus {
        samsung,pins = "gpe-14", "gpe-15";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    spi0_bus: spi0-bus {
        samsung,pins = "gpe-11", "gpe-12", "gpe-13";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    sd0_clk: sd0-clk {
        samsung,pins = "gpe-5";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    sd0_cmd: sd0-cmd {
        samsung,pins = "gpe-6";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    sd0_bus1: sd0-bus1 {
        samsung,pins = "gpe-7";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    sd0_bus4: sd0-bus4 {
        samsung,pins = "gpe-8", "gpe-9", "gpe-10";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };
};
View Code

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

2.1 gpa节点

gpa: gpa {
    gpio-controller;
    #gpio-cells = <2>;
};

2.2 gpf节点

gpf: gpf {
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
};

以上设备树描述的是一个中断控制器节点gpf,它同时也是一个GPIO控制器。下面是各个属性的含义及作用:

  • gpio-controller:表示该节点是一个GPIO控制器;
  • #gpio-cells:指定使用几个cell来描述一个GPIO;这里设为2,即包含两个参数,第一个为 GPIO 编号,第二个是GPIO 的功能;
  • interrupt-controller:表示该节点是一个中断控制器节点;
  • #interrupt-cells:用来描述子节点中"interrupts"属性使用了几个cell才能确定所使用的中断。这里设为2,第一个表示硬件中断编号,第二个表示中断触发类型(比如上升沿触发、下降沿触发等)。

该中断控制器用于连接和处理系统内部或外部的中断信号。当有一个设备产生中断时,会将中断信号通过该中断控制器进行转发和处理。

同时,该节点也是一个GPIO控制器,可以控制一些GPIO引脚的输出或输入状态。这样可以实现对一些外设的控制或监测功能

例如,在某个GPIO引脚产生了按键按下或释放事件时,可以通过GPIO接口通知到CPU,CPU再进行相应的处理。

2.3 串口

2.3.1 uart0_data

串口0对应的引脚GPH2:TXD0,GPH3:RXD0,因此修改uart0_data节点为:

uart0_data: uart0-data {
    samsung,pins = "gph-2", "gph-3";
    samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};
2.3.2 uart0_fctl

移除uart0_fctl节点。

2.3.3 uart1_data

串口1对应的引脚GPH4:TXD1,GPH5:RXD1,因此修改uart1_data节点为:

uart1_data: uart1-data {
    samsung,pins = "gph-4", "gph-5";
    samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};
2.3.4 uart1_fctl

移除uart1_fctl节点。

2.3.5 uart2_data

串口2对应的引脚GPH6:TXD2,GPH7:RXD2,因此修改uart2_data节点为:

uart2_data: uart2-data {
    samsung,pins = "gph-6", "gph-7";
    samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};
2.3.6 uart2_fctl

移除uart2_fctl节点。

2.3.7 uart3_data

移除uart3_data节点。

2.4 extuart_clk

串口外部时钟输入引脚为gph8,因此修改extuart_clk节点为:

extuart_clk: extuart-clk {
    samsung,pins = "gph-8";
    samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

2.5 i2c0_bus

s3c2440这款SoC其内部只有一个I2C控制器,其中SCL连接GPE14引脚,SDA连接GPE15引脚。因此i2c0_bus节点如下:

i2c0_bus: i2c0-bus {
    samsung,pins = "gpe-14", "gpe-15";
    samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

2.6 spi0_bus

s3c2440这款SoC的SPI结构,其内部有两个SPI控制器:s3c2440 SPI相关引脚定义:

SPI SCLK MOSI MISO SS
SPI0 GPE13 GPE12 GPE11 GPG2
SPI1 GPG7 GPG6 GPG5 GPG3

因此spi0_bus节点如下:

spi0_bus: spi0-bus {
    samsung,pins = "gpe-11", "gpe-12", "gpe-13";
    samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
};

2.7 SD节点

s3c2440包含一个SD控制器,SD相关引脚定义:

SDDAT[3:0]

SD接收/发送数据

SDCMD

SDK接收应答/发送指令

SDCLK

SD时钟

GPE10~GPE7 GPE6 GPE5

因此SD相关节点如下:

    sd0_clk: sd0-clk {
        samsung,pins = "gpe-5";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    sd0_cmd: sd0-cmd {
        samsung,pins = "gpe-6";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    sd0_bus1: sd0-bus1 {
        samsung,pins = "gpe-7";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

    sd0_bus4: sd0-bus4 {
        samsung,pins = "gpe-8", "gpe-9", "gpe-10";
        samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
    };

2.8 新增nand节点

/*添加Nand Flash所用的管脚*/
nand_pinctrl: nand_pinctrl {
    samsung,pins = "gpa-17", "gpa-18", "gpa-19",
                 "gpa-20", "gpa-22";
    samsung,pin-function = <1>;
};

2.9 文件内容

修改之后,arch/arm/boot/dts/s3c2440-pinctrl.dtsi文件内容如下:

// SPDX-License-Identifier: GPL-2.0
/*
 * Samsung S3C2416 pinctrl settings
 *
 * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
 */

#include <dt-bindings/pinctrl/samsung.h>

&pinctrl_0 {
        /*
         * Pin banks
         */

        gpa: gpa {
                gpio-controller;
                #gpio-cells = <2>;
        };

        gpb: gpb {
                gpio-controller;
                #gpio-cells = <2>;
        };

        gpc: gpc {
                gpio-controller;
                #gpio-cells = <2>;
        };

        gpd: gpd {
                gpio-controller;
                #gpio-cells = <2>;
        };

        gpe: gpe {
                gpio-controller;
                #gpio-cells = <2>;
        };

        gpf: gpf {
                gpio-controller;
                #gpio-cells = <2>;
                interrupt-controller;
                #interrupt-cells = <2>;
        };

        gpg: gpg {
                gpio-controller;
                #gpio-cells = <2>;
                interrupt-controller;
                #interrupt-cells = <2>;
        };

        gph: gph {
                gpio-controller;
                #gpio-cells = <2>;
        };

        gpj: gpj {
                gpio-controller;
                #gpio-cells = <2>;
        };


        /*
         * Pin groups
         */

        uart0_data: uart0-data {
                samsung,pins = "gph-2", "gph-3";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        uart1_data: uart1-data {
                samsung,pins = "gph-4", "gph-5";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };


        uart2_data: uart2-data {
                samsung,pins = "gph-6", "gph-7";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        uart2_fctl: uart2-fctl {
                samsung,pins = "gph-6", "gph-7";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        extuart_clk: extuart-clk {
                samsung,pins = "gph-8";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        i2c0_bus: i2c0-bus {
                samsung,pins = "gpe-14", "gpe-15";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        spi0_bus: spi0-bus {
                samsung,pins = "gpe-11", "gpe-12", "gpe-13";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        sd0_clk: sd0-clk {
                samsung,pins = "gpe-5";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        sd0_cmd: sd0-cmd {
                samsung,pins = "gpe-6";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        sd0_bus1: sd0-bus1 {
                samsung,pins = "gpe-7";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        sd0_bus4: sd0-bus4 {
                samsung,pins = "gpe-8", "gpe-9", "gpe-10";
                samsung,pin-function = <EXYNOS_PIN_FUNC_2>;
        };

        /*添加Nand Flash所用的管脚*/
        nand_pinctrl: nand_pinctrl {
                samsung,pins = "gpa-17", "gpa-18", "gpa-19",
                     "gpa-20", "gpa-22";
               samsung,pin-function = <1>;
       };
};
View Code

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

修改s3c2440-smdk2440.dts设备树,将该文件中的2416全部替换成2440,同时移除&sdhci_0、&sdhci_1、&uart_3节点,文件内容如下:

/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";
};

&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>;
};

&watchdog {
        status = "okay";
};
View Code

接着我们需要对该文件进行修改来适配Mini2440开发板。

3.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>;
                };
        };

其中与clk注册相关的属性:

  • clock-cells:前面已经介绍过了,不重复介绍了;设置为0,表示对外输出一路时钟;
  • clocks-output-names:用于说明一个clk provider输出多路clock的名称,clock consumer的设备树节点中提供的clock provider的标识是一个index,通过这个index可以在clock-output-names属性值中找到对应的输出clock provider的名字;
  • clock-frequency:输出时钟的频率;

在linux内核drivers路径下搜索fixed-clock:

root@zhengyang:/work/sambashare/linux-5.2.8-dt/drivers# grep "fixed-clock" * -nR
clk/clk-fixed-rate.c:194:CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
clk/clk-fixed-rate.c:224:       { .compatible = "fixed-clock" },
clk/ti/clk.c:395:       { .compatible = "fixed-clock" },

定位到clk/clk-fixed-rate.c文件:

static struct clk *_of_fixed_clk_setup(struct device_node *node)
{
        struct clk *clk;
        const char *clk_name = node->name;
        u32 rate;
        u32 accuracy = 0;
        int ret;

        if (of_property_read_u32(node, "clock-frequency", &rate))  // rate = 12000000
                return ERR_PTR(-EIO);

        of_property_read_u32(node, "clock-accuracy", &accuracy);   // accuracy = 0

        of_property_read_string(node, "clock-output-names", &clk_name);  // clk_name = "xti"

        clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,  // 注册fixed rate clock
                                                    0, rate, accuracy);
        if (IS_ERR(clk))
                return clk;

        ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);  // 针对于设备树,注册of_clk_provider
        if (ret) {
                clk_unregister(clk);
                return ERR_PTR(ret);
        }

        return clk;
}

/**
 * of_fixed_clk_setup() - Setup function for simple fixed rate clock
 */
void __init of_fixed_clk_setup(struct device_node *node)
{
        _of_fixed_clk_setup(node);
}
CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);

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

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

of_fixed_clk_setup函数其内部调用_of_fixed_clk_setup注册fixed rate clock。

3.2 新增网卡节点

3.2.1 DM9000网卡硬件接线

linux驱动移植-DM9000网卡驱动介绍过Mini2440开发板的DM9000的接线原理图;

  • DM9000片选线是nLAN_CS(AEN)连接在s3c2440的nGCS4上;
  • DM9000 IRQ_LAN(INT)接的是s3c2440的ENT7(GPF7),用的外部中断7,这个中断用于接收数据时触发的,高电平有效;
  • DM9000只有一地址线CMD,地址线连接在s3c2440的ADDR2口上;
  • DM9000数据线SD0~SD15连接在s3c2440的LDATA0~LDATA15;

nGCS4对应的片选信号是0x20000000开头的,在0x20000000-0x28000000之间。所以当CPU发出0x20000000-0x28000000的物理地址数据时,DM9000就会被选中。

对于DM9000芯片来说,读写地址与数据使用的是同一组16个I/O引脚,DM9000芯片通过CMD引脚区分数据线传输的的是DM9000的寄存器地址,还是寄存器数据。

也就是说当CPU发出物理地址(0x20000000-0x28000000)| 0x4时,此时CPU再发出的数据线信号对于DM9000来说实际是地址信号。例如:

  • 当在地址0x20000000上读写数据时,表示读写的数据是DM9000的地址;
  • 当访问的地址0x20000004上读写数据时,表示读写的数据是DM9000的数据;
3.2.2 设备树节点

添加DM9000 网卡,以便于后面的驱动开发调试,需要引入头文件:

#include <dt-bindings/interrupt-controller/irq.h>

同时在根节点下新增子节点:

    /*添加DM9000 网卡,以便于后面的驱动开发调试*/
    srom-cs4@20000000 {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        reg = <0x20000000 0x8000000>;
        ranges;
        ethernet@20000000 {
            compatible = "davicom,dm9000";
            reg = <0x20000000 0x2 0x20000004 0x2>;
            interrupt-parent = <&gpf>;
            interrupts = <7 IRQ_TYPE_EDGE_RISING>;
            local-mac-address = [00 00 de ad be ef];
            davicom,no-eeprom;
        };
    };

以上设备树描述的是一个srom-cs4设备节点,它包含了一个以太网控制器子节点ethernet。下面是各个属性的含义:

srom-cs4:

  • compatible:表示设备类型名称,这里为 "simple-bus";
  • #address-cells:地址单元格数量,这里为 1,即使用1个u32数来表示地址;
  • #size-cells:大小单元格数量,这里为 1,即使用1个u32数来表示地址跨度;
  • reg:物理地址和大小,这里表示该设备在物理地址 0x20000000 处,共占用 0x8000000 个字节的空间;
  • ranges:空属性;

ethernet:

  • compatible:表示设备类型名称,这里为 "davicom,dm9000",说明该以太网控制器使用的是 DM9000 型号控制器
  • reg:该以太网控制器在地址 0x20000000 处,共占用 2 个字节的空间;地址 0x20000004 处,共占用 2 个字节的空间;
  • interrupt-parent:指定该设备产生中断时,需要向哪个中断控制器发送中断信号。这里指向了全局中断控制器gpf;
  • interrupts:定义该设备所连接的中断号和触发方式。这里它会使用第7号中断,中断触发方式为边沿触发;
  • local-mac-address:该以太网控制器的 MAC 地址,这里设置为 [00 00 de ad be ef]
  • davicom,no-eeprom:表示该设备没有 EEPROM 存储器;

在drivers/net/ethernet/davicom/dm9000.c中可以搜索关键字 “davicom,dm9000”;当platform设备和驱动匹配后,dm9000_probe函数被调用。具体参考博linux驱动移植-DM9000网卡驱动

3.3 串口

3.3.1 uart_0

修改uart_0节点为:

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

修改uart_1节点为:

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

修改uart_2节点为:

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

3.4 新增nand节点

&nand0 {
    status = "okay";
    nand,tacls = <0xa>;
    nand,twrph0 = <0x19>;
    nand,twrph1 = <0xa>;

    #address-cells = <1>;
    #size-cells = <1>;
    /*Nand Flash 分区*/
    partitions {
        /* MTD partition table */
        #address-cells = <1>;
        #size-cells = <1>;
        nr-chips = <1>;
        set-name = "Mini2440-0";

        partition@0 {
            label = "u-boot";
            reg = <0x0000000 0x00040000>;
            read-only;
        };

        partition@40000 {
            label = "params";
            reg = <0x00040000 0x00020000>;
            read-only;
        };

        partition@60000 {
            label = "kernel";
            reg = <0x00060000 0x00400000>;
            read-only;
        };

        partition@460000 {
            label = "rootfs";
            reg = <0x00460000 0x0>;
            /*read-only;*/ /*若rootfs为read-only,内核加载文件系统后,文件系统只能读不能写*/
        };
    };
};

3.5 新增i2c节点

&i2c {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c0_bus>;
};

3.6 文件内容

修改之后,arch/arm/boot/dts/s3c2440-smdk2440.dts文件内容如下:

// SPDX-License-Identifier: GPL-2.0
/*
 * SAMSUNG SMDK2440 board device tree source
 *
 * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
 */

/dts-v1/;
#include "s3c2440.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
/ {
        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>;
                };
        };
    /*添加DM9000 网卡,以便于后面的驱动开发调试*/
        srom-cs4@20000000 {
                compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
                reg = <0x20000000 0x8000000>;
                ranges;
                ethernet@20000000 {
                        compatible = "davicom,dm9000";
                        reg = <0x20000000 0x2 0x20000004 0x2>;
                        interrupt-parent = <&gpf>;
                        interrupts = <7 IRQ_TYPE_EDGE_RISING>;
                        local-mac-address = [00 00 de ad be ef];
                        davicom,no-eeprom;
                };
        };

};

&rtc {
        status = "okay";
};


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

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

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

&watchdog {
        status = "okay";
};

&nand0 {
    status = "okay";
    nand,tacls = <0xa>;
    nand,twrph0 = <0x19>;
    nand,twrph1 = <0xa>;

    #address-cells = <1>;
    #size-cells = <1>;
    /*Nand Flash 分区*/
    partitions {
        /* MTD partition table */
        #address-cells = <1>;
        #size-cells = <1>;
        nr-chips = <1>;
        set-name = "mini2440-0";

        partition@0 {
            label = "u-boot";
            reg = <0x0000000 0x00040000>;
            read-only;
        };

        partition@40000 {
            label = "params";
            reg = <0x00040000 0x00020000>;
            read-only;
        };

        partition@60000 {
            label = "kernel";
            reg = <0x00060000 0x00400000>;
            read-only;
        };

        partition@460000 {
            label = "rootfs";
            reg = <0x00460000 0x0>;
            /*read-only;*/ /*若rootfs为read-only,内核加载文件系统后,文件系统只能读不能写*/
        };
    };
};

&i2c {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c0_bus>;
};
View Code

四、测试

4.1 编译内核

在linux内核根目录下执行如下命令:

make  V=1 uImage

将uImage复制到tftp服务器路径下:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# cp /work/sambashare/linux-5.2.8-dt/arch/arm/boot/uImage /work/tftpboot/

4.2  编译dts

在linux内核根目录执行如下命令:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# make dtbs
  DTC     arch/arm/boot/dts/s3c2416-smdk2416.dtb
  DTC     arch/arm/boot/dts/s3c2440-smdk2440.dtb

编译设备树文件,把前面配置过的arch/arm/boot/dts里的dts文件编译成dtb文件。

将s3c2440-smdk2440.dtb复制到tftp服务器路径下:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# cp /work/sambashare/linux-5.2.8-dt/arch/arm/boot/dts/s3c2440-smdk2440.dtb /work/tftpboot/

4.3 启动内核

uboot启动后,将dtb下载到内存地址0x30001000中:

SMDK2440 # tftp 0x30001000 s3c2440-smdk2440.dtb

注意:我们可以修改uboot源码,扩展一个device_tree分区,然后将dtb文件存储在该分组取中。

然后将内核镜像加载到内存0x30008000地址,并烧录内核到Nand Flash:

SMDK2440 # tftp 30008000 uImage
SMDK2440 # nand erase.part kernel
SMDK2440 # nand write 30008000 kernel

然后可以使用如下命令启动内核:

SMDK2440 # bootm 0x30008000 - 0x30001000   // 无设备树时,直接bootm 0x30008000
//bootm  uImage地址  ramdisk地址  设备树镜像地址

内核启动打印信息:

SMDK2440 # bootm 0x30008000 - 0x30001000
## Booting kernel from Legacy Image at 30008000 ...
   Image Name:   Linux-5.2.8
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    3708432 Bytes = 3.5 MiB
   Load Address: 30008000
   Entry Point:  30008000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 30001000
   Booting using the fdt blob at 0x30001000
   Loading Kernel Image ... OK
   Loading Device Tree to 33afa000, end 33afe521 ... OK
Using machid 0xa8 from environment

Starting kernel ...

Booting Linux on physical CPU 0x0
Linux version 5.2.8 (root@zhengyang) (gcc version 4.8.3 20140320 (prerelease) (Sourcery CodeBench Lite 2014.05-29)) #11 Mon Apr 24 22:09:14 CST 2023
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c000717f
CPU: VIVT data cache, VIVT instruction cache
OF: fdt: Machine model: SMDK2440
printk: debug: ignoring loglevel setting.
printk: bootconsole [earlycon0] enabled
Memory policy: Data cache writeback
CPU S3C2440A (id 0x32440001)
On node 0 totalpages: 16384
  Normal zone: 128 pages used for memmap
  Normal zone: 0 pages reserved
  Normal zone: 16384 pages, LIFO batch:3
pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
pcpu-alloc: [0] 0
Built 1 zonelists, mobility grouping on.  Total pages: 16256
Kernel command line: noinitrd console=ttySAC1,115200  user_debug=31 root=/dev/nfs rw nfsroot=192.168.0.200:/work/nfs_root/rootfs ip=192.168.0.105:192.168.0.200:192.168.0.1:255.255.255.0::eth0:off ignore_loglevel earlyprintk
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 57176K/65536K available (5806K kernel code, 217K rwdata, 1260K rodata, 216K init, 197K bss, 8360K reserved, 0K cma-reserved)
NR_IRQS: 103
irq: clearing pending status 00000018
irq: clearing pending status 00000010
random: get_random_bytes called from start_kernel+0x25c/0x41c with crng_init=0
_get_rate: could not find clock xti
clock-names timers in index 0
sched_clock: 16 bits at 1000kHz, resolution 1000ns, wraps every 32767500ns
clocksource: samsung_clocksource_timer: mask: 0xffff max_cycles: 0xffff, max_idle_ns: 29163075 ns
Console: colour dummy device 80x30
Calibrating delay loop... 199.06 BogoMIPS (lpj=995328)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
CPU: Testing write buffer coherency: ok
Setting up static identity map for 0x30008200 - 0x30008258
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 256 (order: -1, 3072 bytes)
pinctrl core: initialized pinctrl subsystem
NET: Registered protocol family 16
DMA: preallocated 256 KiB pool for atomic coherent allocations
S3C Power Management, Copyright 2004 Simtec Electronics
S3C2440: Initialising architecture
SCSI subsystem initialized
libata version 3.00 loaded.
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
clock-names i2c in index 0
s3c-i2c 54000000.i2c: slave address 0x00
s3c-i2c 54000000.i2c: bus frequency set to 97 KHz
s3c-i2c 54000000.i2c: i2c-0: S3C I2C adapter
Advanced Linux Sound Architecture Driver Initialized.
clocksource: Switched to clocksource samsung_clocksource_timer
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 0, 4096 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
NetWinder Floating Point Emulator V0.97 (extended precision)
Initialise system trusted keyrings
workingset: timestamp_bits=30 max_order=14 bucket_order=0
NFS: Registering the id_resolver key type
Key type id_resolver registered
Key type id_legacy registered
jffs2: version 2.2. (NAND) (SUMMARY)  © 2001-2006 Red Hat, Inc.
romfs: ROMFS MTD (C) 2007 Red Hat, Inc.
Key type asymmetric registered
Asymmetric key parser 'x509' registered
io scheduler mq-deadline registered
io scheduler kyber registered
clock-names timers in index 0
clock-names pwm-tclk0 in index -61
clock-names pwm-tclk1 in index -61
Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
clock-names uart in index 0
clock-names clk_uart_baud2 in index 1
50000000.serial: ttySAC0 at MMIO 0x50000000 (irq = 32, base_baud = 0) is a S3C2440
clock-names clk_uart_baud2 in index 1
50004000.serial: ttySAC1 at MMIO 0x50004000 (irq = 35, base_baud = 0) is a S3C2440
clock-names clk_uart_baud2 in index 1
clock-names clk_uart_baud2 in index 1
printk: console [ttySAC1] enabled
printk: console [ttySAC1] enabled
printk: bootconsole [earlycon0] disabled
printk: bootconsole [earlycon0] disabled
clock-names uart in index 0
clock-names clk_uart_baud2 in index 1
50008000.serial: ttySAC2 at MMIO 0x50008000 (irq = 38, base_baud = 0) is a S3C2440
lp: driver loaded but no devices found
ppdev: user-space parallel port driver
brd: module loaded
loop: module loaded
clock-names nand in index 0
s3c24xx-nand 4e000000.nand: Tacls=1, 10ns Twrph0=5 50ns, Twrph1=2 20ns
nand: device found, Manufacturer ID: 0xec, Chip ID: 0xf1
nand: Samsung NAND 128MiB 3,3V 8-bit
nand: 128 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
s3c24xx-nand 4e000000.nand: ECC disabled
nand: NAND_ECC_NONE selected by board driver. This is not recommended!
s3c24xx-nand 4e000000.nand: Tacls=1, 10ns Twrph0=5 50ns, Twrph1=2 20ns
Scanning device for bad blocks
4 fixed-partitions partitions found on MTD device partitions
Creating 4 MTD partitions on "partitions":
0x000000000000-0x000000040000 : "u-boot"
0x000000040000-0x000000060000 : "params"
0x000000060000-0x000000460000 : "kernel"
0x000000460000-0x000008000000 : "rootfs"
s3c24xx-nand 4e000000.nand: Tacls=1, 10ns Twrph0=5 50ns, Twrph1=2 20ns
eth0: dm9000e at (ptrval),(ptrval) IRQ 7 MAC: 00:00:de:ad:be:ef (platform data)
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
ohci-s3c2410: OHCI S3C2410 driver
usbcore: registered new interface driver usbserial_generic
usbserial: USB Serial support registered for generic
usbcore: registered new interface driver ftdi_sio
usbserial: USB Serial support registered for FTDI USB Serial Device
usbcore: registered new interface driver pl2303
usbserial: USB Serial support registered for pl2303
clock-names rtc in index 0
s3c-rtc 57000000.rtc: rtc disabled, re-enabling
rtc rtc0: invalid alarm value: 1900-02-01T00:00:00
s3c-rtc 57000000.rtc: registered as rtc0
clock-names watchdog in index 0
s3c2410-wdt 53000000.watchdog: watchdog inactive, reset disabled, irq disabled
NET: Registered protocol family 10
Segment Routing with IPv6
sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
NET: Registered protocol family 17
Key type dns_resolver registered
Loading compiled-in X.509 certificates
s3c-rtc 57000000.rtc: setting system clock to 2023-03-29T13:28:39 UTC (1680096519)
dm9000 20000000.ethernet eth0: link up, 100Mbps, full-duplex, lpa 0x4DE1
IP-Config: Complete:
     device=eth0, hwaddr=00:00:de:ad:be:ef, ipaddr=192.168.0.105, mask=255.255.255.0, gw=192.168.0.1
     host=192.168.0.105, domain=, nis-domain=(none)
     bootserver=192.168.0.200, rootserver=192.168.0.200, rootpath=
ALSA device list:
  No soundcards found.
clock-names clk_uart_baud2 in index 1
VFS: Mounted root (nfs filesystem) on device 0:10.
Freeing unused kernel memory: 216K
This architecture does not have kernel memory protection.
Run /sbin/init as init process
random: fast init done
clock-names clk_uart_baud2 in index 1
clock-names clk_uart_baud2 in index 1

Please press Enter to activate this console.
View Code

需要注意的是输出日志中多次打印了类似如下的日志:

clock-names uart in index 0
clock-names clk_uart_baud2 in index 1

这个是我在devm_clk_get函数调用流程中加入了日志输出:

devm_clk_get(dev,con_id);
     hw = of_clk_get_hw(dev->of_node, 0, con_id);
             of_parse_clkspec(np, index, con_id, &clkspec);
                   index = of_property_match_string(np, "clock-names", con_id);

这里我修改了drivers/clk/clk.c文件中的of_parse_clkspec函数:

if (con_id){  
        index = of_property_match_string(np, "clock-names", con_id);
        pr_info("clock-names %s in index %d",con_id,index);
}

4.4 查看设备树

在/sys/firmware/fdt查看原始dtb文件:

[root@zy:/]# ls -l /sys/firmware/fdt
-r--------    1 0        0             8192 Mar 29 12:06 /sys/firmware/fdt
[root@zy:/]# hexdump -C /sys/firmware/fdt

/sys/firmware/devicetree以目录结构程现的dtb文件,根节点对应base目录, 每一个设备节点对应一个目录;

[root@zy:/]# ls -l /sys/firmware/devicetree/base/ -l
total 0
-r--r--r--    1 0        0                4 Mar 29 12:07 #address-cells
-r--r--r--    1 0        0                4 Mar 29 12:07 #size-cells
drwxr-xr-x    2 0        0                0 Mar 29 12:07 aliases
drwxr-xr-x    2 0        0                0 Mar 29 12:07 chosen
drwxr-xr-x    2 0        0                0 Mar 29 12:07 clock-controller@4c000000
drwxr-xr-x    3 0        0                0 Mar 29 12:07 clocks
-r--r--r--    1 0        0               33 Mar 29 12:07 compatible
drwxr-xr-x    3 0        0                0 Mar 29 12:07 cpus
drwxr-xr-x    2 0        0                0 Mar 29 12:07 i2c@54000000
drwxr-xr-x    2 0        0                0 Mar 29 12:07 interrupt-controller@4a000000
-r--r--r--    1 0        0                4 Mar 29 12:07 interrupt-parent
drwxr-xr-x    2 0        0                0 Mar 29 12:07 memory@30000000
-r--r--r--    1 0        0                9 Mar 29 12:07 model
-r--r--r--    1 0        0                1 Mar 29 12:07 name
drwxr-xr-x    3 0        0                0 Mar 29 12:06 nand@4e000000
drwxr-xr-x   24 0        0                0 Mar 29 12:07 pinctrl@56000000
drwxr-xr-x    2 0        0                0 Mar 29 12:07 rtc@57000000
drwxr-xr-x    2 0        0                0 Mar 29 12:07 serial@50000000
drwxr-xr-x    2 0        0                0 Mar 29 12:07 serial@50004000
drwxr-xr-x    2 0        0                0 Mar 29 12:07 serial@50008000
drwxr-xr-x    3 0        0                0 Mar 29 12:07 srom-cs4@20000000
drwxr-xr-x    2 0        0                0 Mar 29 12:07 timer@51000000
drwxr-xr-x    2 0        0                0 Mar 29 12:07 watchdog@53000000

设备节点每一个属性对应一个文件;

[root@zy:/]# ls -l /sys/firmware/devicetree/base/serial@50000000/
total 0
-r--r--r--    1 0        0               20 Mar 29 12:08 clock-names
-r--r--r--    1 0        0               16 Mar 29 12:08 clocks
-r--r--r--    1 0        0               21 Mar 29 12:08 compatible
-r--r--r--    1 0        0               32 Mar 29 12:08 interrupts
-r--r--r--    1 0        0                7 Mar 29 12:08 name
-r--r--r--    1 0        0                4 Mar 29 12:08 pinctrl-0
-r--r--r--    1 0        0                8 Mar 29 12:08 pinctrl-names
-r--r--r--    1 0        0                8 Mar 29 12:08 reg
-r--r--r--    1 0        0                5 Mar 29 12:08 status

比如查看compatible:

[root@zy:/]# cat /sys/firmware/devicetree/base/serial@50000000/compatible
samsung,s3c2440-uart

/sys/devices/platform系统中所有的platform_device, 有来自设备树的, 也有来有.c文件中注册的;

对于来自设备树的platform_device, 可以进 /sys/devices/platform/<设备名>/of_node查看它的设备树属性;

[root@zy:/]# ls /sys/devices/platform/50000000.serial/
driver/          modalias         power/           tty/
driver_override  of_node/         subsystem/       uevent
[root@zy:/]# ls /sys/devices/platform/50000000.serial/of_node/ -l
total 0
-r--r--r--    1 0        0               20 Mar 29 12:08 clock-names
-r--r--r--    1 0        0               16 Mar 29 12:08 clocks
-r--r--r--    1 0        0               21 Mar 29 12:08 compatible
-r--r--r--    1 0        0               32 Mar 29 12:08 interrupts
-r--r--r--    1 0        0                7 Mar 29 12:08 name
-r--r--r--    1 0        0                4 Mar 29 12:08 pinctrl-0
-r--r--r--    1 0        0                8 Mar 29 12:08 pinctrl-names
-r--r--r--    1 0        0                8 Mar 29 12:08 reg
-r--r--r--    1 0        0                5 Mar 29 12:08 status

/proc/device-tree 是链接文件, 指向 /sys/firmware/devicetree/base;

[root@zy:/]# ls -l /proc/device-tree
lrwxrwxrwx    1 0        0               29 Mar 29 12:11 /proc/device-tree -> /sys/firmware/devicetree/base

五、uboot FDT命令

uboot提供了fdt的相关命令:

    "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"

5.1 fdt addr

设置dtb文件在内存地址:

SMDK2440 # fdt addr 0x30001000

5.2 fdt print

利用ftd print命令输出rtc节点信息:

SMDK2440 # fdt print /rtc
rtc@57000000 {
        compatible = "samsung,s3c2410-rtc";
        reg = <0x57000000 0x00000100>;
        interrupts = <0x00000000 0x00000000 0x0000001e 0x00000003 0x00000000 0x00000000 0x00000008 0x00000003>;
        status = "okay";
        clocks = <0x00000002 0x0000001a>;
        clock-names = "rtc";
};

5.3 fdt get value

利用ftd get value输出rtc节点reg属性的值:

SMDK2440 # fdt get value reg /rtc reg
SMDK2440 # print reg
reg=0000005700010000

参考文章

[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

[12]Linux CommonClock Framework分析之三 驱动开发流程说明

[13]基于设备树的中断实现 (24x0平台)

[14]中断系统中的设备树__使用设备树描述按键中断

[15]GPIO+Pinctrl

[16]在设备树中pinctrl的简单使用

[17] tq2440_dt/arch/arm/boot/dts/s3c2440-tq2440-dt.dts

[18]tq2440_dt/arch/arm/boot/dts/s3c2440-pinctrl.dtsi

posted @ 2023-04-13 23:29  大奥特曼打小怪兽  阅读(573)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步