完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1. 时钟系统结构
rockchip的时钟系统代码位于drivers/clk/rockchip,目录整体结构如下: ├── rockchip │ ├── clk.c---------------时钟系统注册 │ ├── clk-cpu.c-----------CPU调频 │ ├── clk-ddr.c-----------DDR调频 │ ├── clk-half-divider.c--二分频 │ ├── clk-inverter.c------极性翻转 │ ├── clk-mmc-phase.c-----mmc相位 │ ├── clk-muxgrf.c--------时钟复用 │ ├── clk-pll.c-----------PLL配置 │ ├── clk-pvtm.c----------电压温度检测调频 │ ├── clk-rk3399.c--------RK3399时钟核心初始化、PLL初始化、使能控制、分频等 │ ├── clk-rockchip.c------RK系列芯片时钟 └────────────────────────────────────────────────────── 从上面的文件结构中我们可以看到,rockchip的时钟系统基本包含了CCF框架提到的时钟使能、复用、分频、倍频等功能。RK3399的时钟系统主体是clk-rk3399.c这个文件。 2. 初始化方式 kernel中的时钟初始化代码位于驱动初始化这个层面,相较于CPU初始化而言,它是比较靠后的。在此之前,CPU及基本外设的工作时钟,由bootrom或者U-Boot及其他bootloader配置,有的CPU在时钟系统完全工作之前是工作于低频模式,而有的CPU则上电解复位之后便工作于高频模式,不可一概而论。 说到kernel中的时钟系统初始化,就不得不提CLK_OF_DECLARE这个宏定义,在时钟驱动中通过它来声明初始化函数,其实现原理如下图所示: 3. RK3399时钟系统实现 rockchip为全系列芯片提供了统一的时钟初始化API,这一点非常值得我们借鉴同时也是很好的一种代码架构模式。RK3399的时钟初始化涉及两部分内容,分别是cru和pmu,二者原理相通,本文以cru初始化为例。具体包括: 上图中涉及到的CCF文件在前文已经描述过:
3.1 核心初始化 RK3399基于数据结构rockchip_clk_provider描述整体的时钟结构,以此代表clock provider。 struct rockchip_clk_provider { void __iomem *reg_base; struct clk_onecell_data clk_data; struct device_node *cru_node; struct regmap *grf; struct regmap *pmugrf; spinlock_t lock; }; 在设备树rk3399.dtsi文件中以grf: syscon@ff770000和pmugrf: syscon@ff320000定义了grf和pmugrf,包含了时钟控制相关的寄存器。 通过rockchip_clk_init()例化数据结构struct rockchip_clk_provider: ctx->reg_base = base; ctx->clk_data.clks = clk_table; ctx->clk_data.clk_num = nr_clks; //---CLK数量,共497路 ctx->cru_node = np; //---设备树中的cru结点 ctx->grf = ERR_PTR(-EPROBE_DEFER); //---设备树中的grf结点 ctx->pmugrf = ERR_PTR(-EPROBE_DEFER); //---设备树中的pmugrf结点 spin_lock_init(&ctx->lock); ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node,"rockchip,grf"); ctx->pmugrf = syscon_regmap_lookup_by_phandle(ctx->cru_node,"rockchip,pmugrf"); 3.2 PLL初始化 RK3399芯片内部包含了8个PLL,分别是LPLL, BPLL, CPLL, GPLL, NPLL, VPLL, VPLL, PPLL,如下图所示: 在代码中使用数据结构struct rockchip_pll_clock描述RK3399的PLL: struct rockchip_pll_clock { unsigned int id; const char *name; const char *const *parent_names; u8 num_parents; unsigned long flags; int con_offset; int mode_offset; int mode_shift; int lock_shift; enum rockchip_pll_type type; u8 pll_flags; struct rockchip_pll_rate_table *rate_table; }; PLL的配置在程序中进行固化,如下: static struct rockchip_pll_clock rk3399_pll_clks[] __initdata = { [lpll] = PLL(pll_rk3399, PLL_APLLL, "lpll", mux_pll_p, 0, RK3399_PLL_CON(0),RK3399_PLL_CON(3), 8, 31, 0, rk3399_pll_rates), [bpll] = PLL(pll_rk3399, PLL_APLLB, "bpll", mux_pll_p, 0, RK3399_PLL_CON(8),RK3399_PLL_CON(11), 8, 31, 0, rk3399_pll_rates), [dpll] = PLL(pll_rk3399, PLL_DPLL, "dpll", mux_pll_p, 0, RK3399_PLL_CON(16),RK3399_PLL_CON(19), 8, 31, 0, NULL), #ifdef RK3399_TWO_PLL_FOR_VOP [cpll] = PLL(pll_rk3399, PLL_CPLL, "cpll", mux_pll_p, 0, RK3399_PLL_CON(24),RK3399_PLL_CON(27), 8, 31, 0, rk3399_pll_rates), #else [cpll] = PLL(pll_rk3399, PLL_CPLL, "cpll", mux_pll_p, 0, RK3399_PLL_CON(24),RK3399_PLL_CON(27), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates), #endif [gpll] = PLL(pll_rk3399, PLL_GPLL, "gpll", mux_pll_p, 0, RK3399_PLL_CON(32),RK3399_PLL_CON(35), 8, 31, 0, rk3399_pll_rates), [npll] = PLL(pll_rk3399, PLL_NPLL, "npll", mux_pll_p, 0, RK3399_PLL_CON(40),RK3399_PLL_CON(43), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates), [vpll] = PLL(pll_rk3399, PLL_VPLL, "vpll", mux_pll_p, 0, RK3399_PLL_CON(48),RK3399_PLL_CON(51), 8, 31, 0, rk3399_vpll_rates), }; 通过rockchip_clk_register_pll()函数初始化PLL,主要是例化数据结构struct rockchip_clk_pll并完成时钟的注册,重点包含如下几个方面: pll->pll_mux_ops = &clk_mux_ops; //--PLL复用 init.ops = &rockchip_rk3399_pll_clk_ops;//--PLL控制 pll->hw.init = &init; //--PLL数据 pll->type = pll_type; //--PLL类型 pll->ctx = ctx; //--关联时钟核心 上面的rockchip_rk3399_pll_clk_ops数据结构包含了PLL一系列的控制函数,如: static const struct clk_ops rockchip_rk3399_pll_clk_ops = { .recalc_rate = rockchip_rk3399_pll_recalc_rate, .round_rate = rockchip_pll_round_rate, .set_rate = rockchip_rk3399_pll_set_rate, .enable = rockchip_rk3399_pll_enable, .disable = rockchip_rk3399_pll_disable, .is_enabled = rockchip_rk3399_pll_is_enabled, .init = rockchip_rk3399_pll_init, }; 3.2 复用/分频/GATE 这部分的初始化内容不限于标题所述,只是这3部分内容更为重要而已。 在代码中通过数据结构rockchip_clk_branch描述RK3399时钟系统的控制功能,如下: struct rockchip_clk_branch { unsigned int id; enum rockchip_clk_branch_type branch_type; const char *name; const char *const *parent_names; u8 num_parents; unsigned long flags; ... }; 其相关配置在程序中固化,基本涵盖了cru时钟单元的所有输出时钟控制信息。 static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { GATE(SCLK_USB2PHY0_REF, "clk_u***2phy0_ref", "xin24m", CLK_IGNORE_UNUSED,RK3399_CLKGATE_CON(6), 5, GFLAGS), GATE(SCLK_USB2PHY1_REF, "clk_u***2phy1_ref", "xin24m", CLK_IGNORE_UNUSED,RK3399_CLKGATE_CON(6), 6, GFLAGS), GATE(SCLK_USBPHY0_480M_SRC, "clk_u***phy0_480m_src", "clk_u***phy0_480m", 0,RK3399_CLKGATE_CON(13), 12, GFLAGS), GATE(SCLK_USBPHY1_480M_SRC, "clk_u***phy1_480m_src", "clk_u***phy1_480m", 0,RK3399_CLKGATE_CON(13), 12, GFLAGS), MUX(0, "clk_u***phy_480m", mux_u***phy_480m_p, 0,RK3399_CLKSEL_CON(14), 6, 1, MFLAGS), ... } 通过函数rockchip_clk_register_branches()完成进行各功能的例化并完成时钟注册。 rockchip_clk_register_branches(ctx, rk3399_clk_branches, ARRAY_SIZE(rk3399_clk_branches)); 3.3 其他时钟控制项 - Tips 1 以上所有的时钟完成注册后,会将时钟信息更新进RK3399的时钟信息表,如下: void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx, struct clk *clk, unsigned int id) { if (ctx->clk_data.clks && id) ctx->clk_data.clks[id] = clk; } - Tips 2 若希望上电之后某些时钟常开,可以配置数据结构rk3399_cru_critical_clocks,例如: static const char *const rk3399_cru_critical_clocks[] __initconst = { "aclk_u***3_noc", "aclk_gmac_noc", "pclk_gmac_noc", "pclk_center_main_noc", "aclk_cci_noc0", "aclk_cci_noc1", "clk_dbg_noc", ... } Tips 3 打印RK3399的时钟树(信息很多),可以查看当前环境下所有的时钟信息,例如: [root@rk3399:/]# cat /sys/kernel/debug/clk/clk_summary clock enable_cnt prepare_cnt rate accuracy phase ---------------------------------------------------------------------------------------- rk808-clkout2 0 0 32768 0 0 xin32k 0 0 32768 0 0 CLK_CAMERA_24MHZ 0 0 24000000 0 0 ap6256_lpo_clk 2 2 32768 0 0 clkin_gmac 1 1 125000000 0 0 clk_rmii_src 4 4 125000000 0 0 clk_rmii_tx 2 2 125000000 0 0 clk_rmii_rx 1 1 125000000 0 0 clk_mac_ref 1 1 125000000 0 0 clk_mac_refout 1 1 125000000 0 0 dummy_vpll 0 0 0 0 0 dummy_cpll 0 0 0 0 0 clk_test_pre 0 0 0 0 0 clk_test 0 0 0 0 0 clk_test_frac 0 0 0 0 0 clk_cifout_src 0 0 0 0 0 clk_testout2_pll_src 0 0 0 0 0 clk_testout1_pll_src 0 0 0 0 0 ...... |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
基于米尔瑞芯微RK3576核心板/开发板的人脸疲劳检测应用方案
498 浏览 0 评论
768 浏览 1 评论
667 浏览 1 评论
1893 浏览 1 评论
3138 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-21 18:21 , Processed in 0.396511 second(s), Total 42, Slave 34 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号