----------------------------------------------------------------------------------------------------------------------------内核版本:linux 5.2.8根文件系统:busybox 1.25.0u-boot:2016.05----------------------------------------------------------------------------------------------------------------------------
一、修改设备树s3c2440.dtsi
s3c2440.dtsi设备树存放的是s3c2440这个SoC跟其他s3c24xx系列不同的一些硬件信息,如clock控制器、串口等等;
修改arch/arm/boot/dts/s3c2440.dtsi文件,将该文件中的2416全部替换成2440,同时移除s3c6410相关的节点,文件内容如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#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";
};
};
接着我们需要对该文件进行修改来适配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了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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;
}
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;