RK3568 vs NUC970:
1.1 NUC970:平台设备+平台数据模式(传统BSP模式)
设计架构分析
// NUC970 BSP典型设计模式(2.6.x内核时代) // 平台设备定义(硬编码在板级文件) // arch/arm/mach-nuc970/mach-nuc970.c /* 1. 平台设备结构体定义(硬件设备静态描述) */ static struct platform_device nuc970_uart0_device = { .name = "nuc970-uart", // 驱动匹配名称 .id = 0, // 设备ID .dev = { .platform_data = &nuc970_uart0_data, // 平台数据指针 .dma_mask = &uart_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), }, .resource = nuc970_uart0_resources, // 硬件资源数组 .num_resources = ARRAY_SIZE(nuc970_uart0_resources), }; /* 2. 平台数据结构体(硬件参数配置) */ static struct nuc970_uart_platform_data nuc970_uart0_data = { .hw_flow_ctrl = 1, // 硬件流控使能 .baud_rate = 115200, // 默认波特率 .data_bits = 8, // 数据位 .parity = NUC970_UART_PARITY_NONE,// 校验位 .stop_bits = 1, // 停止位 .fifo_size = 16, // FIFO深度 .irq_flags = IRQF_SHARED, // 中断标志 .dma_enable = 0, // DMA禁用 }; /* 3. 硬件资源定义(寄存器、中断、DMA通道) */ static struct resource nuc970_uart0_resources[] = { [0] = { .start = NUC970_PA_UART0, // 物理地址起始 .end = NUC970_PA_UART0 + 0xFF, // 物理地址结束 .flags = IORESOURCE_MEM, // 内存资源 }, [1] = { .start = NUC970_IRQ_UART0, // 中断号 .end = NUC970_IRQ_UART0, .flags = IORESOURCE_IRQ, // 中断资源 }, [2] = { .start = NUC970_DMA_UART0_RX, // DMA通道 .end = NUC970_DMA_UART0_TX, .flags = IORESOURCE_DMA, // DMA资源 }, }; /* 4. 板级初始化函数(大量硬编码配置) */ static void __init nuc970_board_init(void) { int ret; /* 注册所有平台设备 */ platform_device_register(&nuc970_uart0_device); platform_device_register(&nuc970_uart1_device); platform_device_register(&nuc970_eth_device); platform_device_register(&nuc970_i2c_device); platform_device_register(&nuc970_spi_device); /* GPIO复用配置(硬编码) */ writel(readl(NUC970_SYS_GPA_MFP) | 0x0000000F, NUC970_SYS_GPA_MFP); // UART0引脚复用 /* 时钟配置(硬编码) */ writel((readl(NUC970_CLK_PCLKEN0) | (1 << 5)), NUC970_CLK_PCLKEN0); // 使能UART0时钟 /* 中断控制器配置 */ writel(0x1F, NUC970_AIC_MECR); // 使能所有中断 /* 电源管理配置 */ nuc970_power_init(); } /* 5. 机器描述符(连接板级与通用内核) */ MACHINE_START(NUC970, "NUC970") .atag_offset = 0x100, .map_io = nuc970_map_io, .init_irq = nuc970_init_irq, .init_time = nuc970_timer_init, .init_machine = nuc970_board_init, // 关键:板级初始化 .restart = nuc970_restart, MACHINE_END资源管理精度分析
静态资源绑定问题:
// 问题1:资源地址硬编码,无法适应不同内存布局 #define NUC970_PA_UART0 0xB8000000 // 物理地址固定 #define NUC970_IRQ_UART0 32 // 中断号固定 // 问题2:设备间依赖关系隐式存在 // 以下依赖关系隐藏在板级初始化代码中: // 1. GPIO必须在UART之前配置 // 2. 时钟必须在设备使能前配置 // 3. 电源域需要在设备注册前初始化 // 问题3:配置数据与驱动代码混合 static int nuc970_uart_probe(struct platform_device *pdev) { // 从平台数据获取配置 struct nuc970_uart_platform_data *pdata = pdev->dev.platform_data; // 混合了:1.通用驱动逻辑 2.芯片特定配置 3.板级特定配置 if (pdata->hw_flow_ctrl) { // 硬件流控配置(芯片相关) write_register(UART_FCR, pdata->fifo_size); } // 引脚复用配置(板级相关) configure_pin_mux(pdev->id); // 需要知道具体引脚 return 0; }数据流向分析:
启动时资源管理流程: Bootloader → 内核 → machine_desc.init_machine() → platform_device_register() ↓ 驱动加载:platform_driver.probe() ← 匹配platform_device.name ↓ 获取资源:platform_get_resource() ← platform_device.resource[] ↓ 获取配置:dev_get_platdata() ← platform_device.dev.platform_data ↓ 硬件操作:ioremap() + request_irq() + 其他硬件配置
设计缺陷深度分析:
编译时绑定:所有硬件配置在编译时确定
// 问题:同一驱动无法适应不同硬件变种 #ifdef BOARD_VERSION_A .baud_rate = 115200, #elif defined(BOARD_VERSION_B) .baud_rate = 921600, #else .baud_rate = 115200, #endif
冗余代码:每个板级文件重复相似配置
// nuc970_evb.c 和 nuc970_dev.c 中都有: static struct platform_device nuc970_uart0_device = { // 90%内容相同,10%不同 };运行时不可变:系统启动后无法动态调整配置
// 无法实现的功能: // 1. 运行时切换UART引脚映射 // 2. 动态调整中断优先级 // 3. 热插拔设备重新配置
1.2 RK3568:设备树+通用驱动模式(现代BSP模式)
设计架构分析
// RK3568设备树描述(硬件配置与代码分离) // 1. SoC级定义(芯片通用特性) // arch/arm64/boot/dts/rockchip/rk3568.dtsi / { compatible = "rockchip,rk3568"; // 中断控制器(GIC) gic: interrupt-controller@fd400000 { compatible = "arm,gic-v3"; reg = <0x0 0xfd400000 0x0 0x10000>, // GICD <0x0 0xfd460000 0x0 0x80000>; // GICR interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>; interrupt-controller; #interrupt-cells = <3>; }; // UART控制器定义 uart0: serial@fdd50000 { compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart"; reg = <0x0 0xfdd50000 0x0 0x100>; interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>; clock-names = "baudclk", "apb_pclk"; reg-shift = <2>; reg-io-width = <4>; dmas = <&dmac0 0>, <&dmac0 1>; dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&uart0_xfer>; status = "disabled"; // 默认禁用,由板级启用 }; // 引脚控制器(每个引脚可软件配置) pinctrl: pinctrl { compatible = "rockchip,rk3568-pinctrl"; uart0 { uart0_xfer: uart0-xfer { rockchip,pins = <0 RK_PC2 1 &pcfg_pull_up>, <0 RK_PC3 1 &pcfg_pull_up>; }; uart0_cts: uart0-cts { rockchip,pins = <0 RK_PC1 1 &pcfg_pull_none>; }; uart0_rts: uart0-rts { rockchip,pins = <0 RK_PC0 1 &pcfg_pull_none>; }; }; }; }; // 2. 板级定义(具体产品配置) // arch/arm64/boot/dts/rockchip/rk3568-evb.dts #include "rk3568.dtsi" // 包含SoC定义 / { model = "Rockchip RK3568 EVB"; compatible = "rockchip,rk3568-evb", "rockchip,rk3568"; // 内存配置(板级特定) memory@a0000000 { device_type = "memory"; reg = <0x0 0xa0000000 0x0 0x08000000>; // 128MB }; // 启用UART0(控制台) &uart0 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; }; // GPIO按键配置 gpio-keys { compatible = "gpio-keys"; power { label = "Power"; gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>; linux,code = <KEY_POWER>; }; }; };设备树驱动的资源管理机制
// 通用串口驱动(不包含板级特定代码) // drivers/tty/serial/8250/8250_dw.c static int dw8250_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); // 1. 从设备树获取通用参数 uart.port.iotype = UPIO_MEM32; uart.port.regshift = device_property_read_u32(&pdev->dev, "reg-shift", &val) ? 2 : val; uart.port.type = PORT_16550A; // 2. 获取时钟(通过时钟框架) uart.port.clk = devm_clk_get(&pdev->dev, "baudclk"); clk_prepare_enable(uart.port.clk); // 3. 获取DMA配置 if (device_property_read_bool(&pdev->dev, "dma-supported")) { setup_dma(pdev, &uart); } // 4. 获取引脚控制 uart.port.pinctrl = devm_pinctrl_get(&pdev->dev); pinctrl_select_state(uart.port.pinctrl, pinctrl_lookup_state("default")); // 5. 注册串口(完全通用,无板级特定代码) return serial8250_register_8250_port(&uart); } // 设备树到平台设备的转换流程 // drivers/of/platform.c static int of_platform_device_create_pdata(struct device_node *np, const char *bus_id, void *platform_data, struct device *parent) { struct platform_device *pdev; // 1. 分配平台设备 pdev = platform_device_alloc("", PLATFORM_DEVID_NONE); // 2. 从设备树填充资源 ret = of_address_to_resource(np, 0, &res); pdev->num_resources = of_irq_to_resource_table(np, res, 1); // 3. 设置设备树节点 pdev->dev.of_node = of_node_get(np); pdev->dev.fwnode = &np->fwnode; // 4. 注册到平台总线 ret = platform_device_add(pdev); return 0; }资源管理精度对比分析
设备树的精度控制优势:
层次化资源描述:
// 三层精度控制: // 第一层:芯片级(rk3568.dtsi)- 硬件能力 uart0: serial@fdd50000 { compatible = "rockchip,rk3568-uart"; reg = <0x0 0xfdd50000 0x0 0x100>; // 固定寄存器映射 interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; // 固定中断 }; // 第二层:板级(rk3568-evb.dts)- 硬件连接 &uart0 { status = "okay"; // 启用此设备 pinctrl-0 = <&uart0_xfer>; // 引脚配置 }; // 第三层:系统级(用户配置)- 运行参数 // 可通过sysfs动态调整: // echo 921600 > /sys/devices/platform/fdd50000.serial/tty/ttyS0/speed动态资源发现与管理:
// 运行时资源解析(内核of模块) int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_args) { // 解析设备树中的中断描述 // 支持:1.简单中断号 2.扩展中断说明符 3.级联中断控制器 } // 引脚控制动态配置 int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) { // 运行时切换引脚功能 // 可实现:1.睡眠状态省电配置 2.不同模式引脚复用 }数据流向树形分析:
设备树驱动数据流: 设备树源文件(.dts) → 编译(.dtb) → Bootloader传递 → 内核解析 ↓ of_platform_populate()遍历设备树节点 ↓ 为每个节点创建platform_device ↓ platform_device注册到平台总线 ↓ 驱动probe()通过of_match_table匹配 ↓ 驱动从device->of_node读取配置 ↓ 通用驱动逻辑 + 设备树配置 = 完整硬件操作
1.3 为什么这么设计:架构演进的内在逻辑
技术演进驱动力
NUC970模式的时代背景:
硬件相对简单:外设固定,引脚复用选项少
产品形态单一:单板计算机,硬件变种有限
内核版本限制:Linux 2.6时代,设备树支持不完善
开发工具链局限:需要简单直接的配置方式
RK3568模式的现代需求:
硬件复杂度爆炸:多核、多电源域、动态频率调节
产品多样性:从消费电子到工业控制的各种变种
软件生态要求:Android、多种Linux发行版支持
开发效率需求:快速原型、OTA更新、配置热调整
设计哲学对比
NUC970:代码中心化设计
// 设计哲学:一切在代码中配置 // 优点:直接、简单、编译时检查 // 缺点:僵化、冗余、难以维护 // 像一本硬编码的说明书: // "第32页:UART0使用引脚PA.0和PA.1,别用错了!"
RK3568:数据驱动设计
// 设计哲学:配置与代码分离 // 优点:灵活、可重用、运行时可变 // 缺点:解析开销、需要工具链支持 // 像一个结构化的数据库: // "UART控制器:地址0xfdd50000,中断116,时钟源bus_uart0" // "引脚复用:模式1=UART,模式2=SPI,模式3=I2C"
精度控制机制对比
| 维度 | NUC970(平台数据) | RK3568(设备树) | 精度提升 |
|---|---|---|---|
| 时间精度 | 编译时确定 | 启动时解析,运行时调整 | 支持动态重配置 |
| 空间精度 | 全局唯一配置 | 层次化覆盖机制 | 支持多板级变种 |
| 接口精度 | 硬编码接口 | 标准属性定义 | 跨平台兼容性 |
| 依赖精度 | 隐式顺序依赖 | 显式引用关系 | 清晰依赖管理 |
// RK3568的精度控制示例: // 1. 时钟依赖精度 cru: clock-controller@fdd20000 { #clock-cells = <1>; // 每个时钟一个cell #reset-cells = <1>; // 每个复位一个cell // 精确描述时钟关系 assigned-clocks = <&cru PLL_GPLL>, <&cru ACLK_BUS>; assigned-clock-rates = <1200000000>, <500000000>; }; // 2. 电源依赖精度 power: power-controller { #power-domain-cells = <1>; // 电源域依赖关系 pd_npu: power-domain@RK3568_PD_NPU { #power-domain-cells = <0>; // 依赖总线电源域 power-domains = <&power RK3568_PD_BUS>; }; }; // 3. 中断路由精度 gic: interrupt-controller { #interrupt-cells = <3>; // <类型 SPI/PPI 中断号 标志> // 明确的中断映射 interrupt-map = <0 0 0 1 &gic 0 GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>; };设计优势深度分析
RK3568设备树模式的核心优势:
单一二进制支持多硬件:
# 同一内核镜像,不同设备树 rk3568-evb.dtb # 评估板配置 rk3568-robot.dtb # 机器人控制板 rk3568-iot.dtb # IoT网关配置 rk3568-tvbox.dtb # 电视盒子配置 # 启动时选择设备树 bootcmd=load mmc 0:1 0x1000000 rk3568-evb.dtb; bootz 0x2000000 - 0x1000000
硬件配置版本化管理:
// 设备树支持覆盖和补丁 #include "rk3568.dtsi" #include "rk3568-evb-v11.dtsi" // 硬件版本11 #include "rk3568-camera-v2.dtsi" // 摄像头模块v2 #include "rk3568-display-fix.dtsi" // 显示问题修复
运行时配置验证:
// 设备树参数验证机制 static int rk3568_uart_validate_dt(struct device_node *np) { // 检查必需属性 if (!of_find_property(np, "reg", NULL)) { dev_err(dev, "missing 'reg' property\n"); return -EINVAL; } // 验证时钟配置 if (!of_device_is_compatible(np, "snps,dw-apb-uart")) { dev_warn(dev, "using fallback compatible\n"); } // 验证引脚配置 if (of_property_read_bool(np, "cts-flow-control")) { if (!of_find_property(np, "cts-gpios", NULL)) { dev_err(dev, "CTS flow control requires cts-gpios\n"); return -EINVAL; } } return 0; }下一代BSP演进方向:
从RK3568的设备树模式,我们可以看到向更高级抽象的演进趋势:
ACPI-like高级配置:更丰富的电源管理、热管理描述
硬件描述语言:使用SystemC或类似语言描述硬件
动态硬件发现:类似PC的PCIe枚举,完全动态配置
安全硬件描述:TEE、安全启动等安全特性的一体化描述
第一部分总结:
从NUC970的平台设备+平台数据到RK3568的设备树+通用驱动,是嵌入式Linux BSP的一次革命性演进:
根本转变:从"在代码中描述硬件"到"用数据描述硬件,用代码驱动硬件"
精度提升:设备树提供了更细粒度、更结构化、更可验证的硬件描述能力
生态影响:设备树成为ARM Linux的事实标准,实现了驱动与板级代码的彻底分离
这种演进不是简单的技术迭代,而是嵌入式系统复杂度管理范式的根本改变。它反映了从"单一产品"思维向"平台化、可配置、可重用"思维的转变,为现代复杂的SoC设计提供了可持续的软件支持基础。