RK3576时钟系统深度解析:从原理到实践,玩转SoC核心时钟!

电子说

1.4w人已加入

描述

时钟系统是 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 通信恢复正常。

七、时钟系统完整流程图

soc

八、总结

RK3576 时钟系统的核心优势是 “灵活”—— 多级 PLL + 丰富的选择器 / 分频器,能为不同外设定制时钟方案。开发者只要理清时钟树结构、遵循配置流程,就能兼顾 “性能、功耗、稳定性”。

最后给个调试小技巧:遇到时钟问题时,先用clk dump查看时钟状态,再结合寄存器读写定位问题,效率会大幅提升!

希望这篇深度解析能帮你玩转 RK3576 时钟系统,少踩坑、多提效~

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分