RK3576时钟系统深度解析:从原理到实践,玩转SoC核心时钟! 电子说
时钟系统是 SoC 的 “心脏”,为所有外设和核心部件提供稳定、精准的时钟信号,直接决定了芯片的性能、功耗与稳定性。RK3576 作为瑞芯微主流的中高端 SoC,其时钟系统设计灵活且复杂,今天我们就从概念到代码、从逻辑到数据流,全方位拆解 RK3576 的时钟系统,帮开发者彻底搞懂这一核心模块!
一、概念分析
1. 时钟系统基本概念
时钟系统的核心术语是理解 RK3576 时钟架构的基础,先理清这几个关键概念:
•PLL(锁相环):时钟系统的核心,将 24MHz/26MHz 晶振输入的参考时钟倍频到更高频率,为不同模块提供高频时钟源。
•时钟树:从 PLL 输出开始,通过分频器、选择器层层分配时钟到各外设的层级结构,是时钟分配的 “脉络”。
•时钟源:提供基础时钟的设备,最典型的就是晶振(OSC)。
•时钟域:芯片中使用相同 / 相关时钟的功能模块集合,不同域可独立配置时钟,兼顾性能与功耗。
2. RK3576 时钟架构
RK3576 采用多级时钟架构,覆盖核心、总线、外设、显示等全场景,主要分为四类:
•核心 PLL:包含 BPLL、LPLL、VPLL、AUPLL、CPLL、GPLL、PPLL 等,为不同功能域提供专属高频时钟;
•总线时钟:ACLK_BUS_ROOT、HCLK_BUS_ROOT、PCLK_BUS_ROOT 等,支撑总线数据传输的时钟;
•外设时钟:为 I2C、SPI、PWM、ADC、MMC 等外设提供工作时钟;
•显示时钟:专门为 VOP(视频输出处理)模块设计的时钟,保障显示输出的稳定性。
二、代码分析
RK3576 的时钟驱动代码核心位于u-bootdriversclkrockchipclk_rk3576.c(约 2750 行),我们从核心数据结构、操作函数、外设时钟处理三个维度拆解。
1. 核心数据结构
PLL 是时钟系统的核心,代码中通过专用结构体定义 PLL 速率表和时钟配置:
// PLL时钟表static struct rockchip_pll_rate_table rk3576_24m_pll_rates[] = { /* _mhz, _p, _m, _s, _k */ RK3588_PLL_RATE(1500000000, 2, 250, 1, 0), // ... 更多速率配置};// PLL时钟结构static struct rockchip_pll_clock rk3576_pll_clks[] = { [BPLL] = PLL(pll_rk3588, PLL_BPLL, RK3576_PLL_CON(0), RK3576_BPLL_MODE_CON0, 0, 15, 0, rk3576_24m_pll_rates), // ... 更多PLL配置};
2. 主要操作函数
时钟驱动的核心是“获取速率” 和 “设置速率”,代码通过switch-case匹配不同时钟 ID,调用专属处理函数:
时钟速率获取
static ulong rk3576_clk_get_rate(struct clk *clk) { struct rk3576_clk_priv *priv = dev_get_priv(clk->dev); ulong rate = 0;
// 根据时钟ID调用相应的获取函数 switch (clk->id) { case PLL_LPLL: rate = rockchip_pll_get_rate(&rk3576_pll_clks[LPLL], priv->cru, LPLL); priv->lpll_hz = rate; break; case ACLK_BUS_ROOT: case HCLK_BUS_ROOT: case PCLK_BUS_ROOT: rate = rk3576_bus_get_clk(priv, clk->id); break; // ... 更多时钟类型 } return rate;}
时钟速率设置
static ulong rk3576_clk_set_rate(struct clk *clk, ulong rate) { struct rk3576_clk_priv *priv = dev_get_priv(clk->dev); ulong ret = 0;
// 根据时钟ID调用相应的设置函数 switch (clk->id) { case PLL_CPLL: ret = rockchip_pll_set_rate(&rk3576_pll_clks[CPLL], priv->cru, CPLL, rate); priv->cpll_hz = rockchip_pll_get_rate(&rk3576_pll_clks[CPLL], priv->cru, CPLL); break; // ... 更多时钟类型 } return ret;}
3. 外设时钟处理
以常用的 I2C 时钟为例,代码通过读取寄存器确定时钟源,再返回对应速率:
static ulong rk3576_i2c_get_clk(struct rk3576_clk_priv *priv, ulong clk_id) { struct rk3576_cru *cru = priv->cru; u32 sel, con; ulong rate;
// 根据I2C实例读取相应的寄存器 switch (clk_id) { case CLK_I2C0: con = readl(&cru->pmuclksel_con[6]); sel = (con & CLK_I2C0_SEL_MASK) >> CLK_I2C0_SEL_SHIFT; break; // ... 更多I2C实例 }
// 根据选择的时钟源返回相应的速率 if (sel == CLK_I2C_SEL_200M) rate = 200 * MHz; else if (sel == CLK_I2C_SEL_100M) rate = 100 * MHz; else if (sel == CLK_I2C_SEL_50M) rate = 50 * MHz; else rate = OSC_HZ;
return rate;}
三、逻辑分析
1. 时钟初始化流程
RK3576 时钟驱动的初始化遵循 “加载 - 绑定 - 初始化 - 探测 - 时钟配置” 的流程,一步都不能少:
1.驱动加载:通过U_BOOT_DRIVER注册时钟驱动;
2.设备绑定:rk3576_clk_bind函数绑定相关设备;
3.平台数据初始化:rk3576_clk_ofdata_to_platdata获取硬件寄存器地址;
4.驱动探测:rk3576_clk_probe函数完成驱动初始化;
5.时钟初始化:rk3576_clk_init设置默认 PLL 频率。
2. 时钟配置逻辑
时钟配置的核心是“选源 - 分频 - 使能”,逻辑如下:
1.PLL 配置:根据目标频率,选择 P、M、S、K 参数(倍频系数);
2.时钟源选择:为外设匹配最合适的时钟源(如 PLL 输出 / 晶振);
3.分频配置:计算并设置分频系数,将时钟降到外设所需频率;
4.时钟使能:确认时钟信号正确输出到外设。
3. 时钟速率计算
时钟分频分为“整数分频” 和 “分数分频”,对应两种计算方式:
•整数分频:DIV_TO_RATE(input_rate, div) = input_rate / (div + 1);
•分数分频:通过rational_best_approximation函数计算最佳分数近似值,适配更精细的频率需求。
四、数据流走向
RK3576 的时钟信号从 “源头” 到 “外设”,遵循固定的流向,可概括为 5 步:
1.晶振输入:24MHz/26MHz 参考时钟(时钟系统的 “起点”);
2.PLL 倍频:PLL 将参考时钟倍频到高频(如 BPLL 可达 1.8GHz);
3.时钟选择:时钟选择器从多个 PLL 输出中选取出目标时钟源;
4.时钟分频:分频器将高频时钟降到外设可承受的频率;
5.外设使用:处理后的时钟信号输入到 I2C/SPI/ 显示等外设。
简化流程图:
晶振(24MHz) → PLL倍频 → 时钟选择器 → 分频器 → 外设
五、开发者需要注意的事项
调试 / 配置 RK3576 时钟时,6 个关键点直接影响系统稳定性,务必牢记:
1.时钟依赖关系:部分外设时钟依赖特定 PLL / 总线时钟,修改前需梳理依赖链;
2.频率限制:每个外设都有工作频率范围(如 I2C 通常≤400KHz),超出范围会导致功能异常;
3.功耗考虑:高频时钟 = 高功耗,按需配置频率,平衡性能与功耗;
4.稳定性:时钟频率突变易导致系统崩溃,需逐步调整;
5.寄存器操作:直接写时钟寄存器风险高,错误配置可能让芯片“变砖”;
6.SCMI 接口:若通过 SCMI 管理时钟,需严格遵循 SCMI 协议规范。
六、调试案例
以“我 2C 时钟配置异常导致通信失败” 为例,手把手教你定位问题:
问题描述
I2C 总线无响应,通信完全失败。
调试步骤
1.检查时钟是否使能:用clk dump命令查看 I2C 时钟状态:
=> clk dumpCLK: (uboot. arm: enter 1800000 KHz, init 1800000 KHz, kernel 0N/A) bpll 1800000 KHz lpll 1200000 KHz vpll 5940000 KHz ... clk_i2c0 100000 KHz
2.读取时钟配置寄存器:确认 I2C 时钟源选择的寄存器值:
// 读取I2C0时钟配置con = readl(&cru->pmuclksel_con[6]);sel = (con & CLK_I2C0_SEL_MASK) >> CLK_I2C0_SEL_SHIFT;
3.验证时钟源:确认选择的时钟源是否匹配预期:
if (sel == CLK_I2C_SEL_200M) rate = 200 * MHz;else if (sel == CLK_I2C_SEL_100M) rate = 100 * MHz;
4.调整时钟频率:若频率不匹配,重新配置:
rk3576_i2c_set_clk(priv, CLK_I2C0, 100000); // 设置为100KHz
解决方案
定位到“I2C 时钟源选择错误”,重新配置为 100MHz 时钟源并正确分频,I2C 通信恢复正常。
七、时钟系统完整流程图

八、总结
RK3576 时钟系统的核心优势是 “灵活”—— 多级 PLL + 丰富的选择器 / 分频器,能为不同外设定制时钟方案。开发者只要理清时钟树结构、遵循配置流程,就能兼顾 “性能、功耗、稳定性”。
最后给个调试小技巧:遇到时钟问题时,先用clk dump查看时钟状态,再结合寄存器读写定位问题,效率会大幅提升!
希望这篇深度解析能帮你玩转 RK3576 时钟系统,少踩坑、多提效~
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !