RV1126 修改VOP的PLL

发布于 2024-08-14  109 次阅读


一、参考资料

  1. PLL硬件设计:《Rockchip RV1109&RV1126TRM V1.0 Part1》中的【Fig. 5-3 PLL Block Diagram】。
  2. PLL相关描述:《Rockchip Clock 开发指南》中的【2.2.1 PLL】,注意,该文档的表示和RV1126略有差异,仅作基本参考。
  3. Linux中的时钟配置:《Rockchip 时钟配置详细说明》,注意,目前的SDK下,该文档版本为1.0,是RK3399参考文档,仅作基本参考。

二、问题起因

  隔壁老哥想要将RV1126的dclk_vop修改为65MHz,以适配1024x768@60Hz的分辨率。但在使用命令echo 65000000 > /sys/kernel/debug/clk/dclk_vop/clk_rate设置的时候,得到的频率是:62526316。但是如果设置54MHz(1024x768@50)或者74.25MHz(1920x1080@60)都可以正常配置。

  因此我们有如下假设:

  1. 驱动是正常的,包括clk_change_rate()clk_round_rate()
  2. 芯片中从PLL到dclk_vop的时钟频率无法分配65MHz

三、验证问题

  我们使用cat /sys/kernel/debug/clk/clk_summary来查看当前的时钟树(这里只显示了一部分):

                                 enable  prepare  protect                                duty
   clock                          count    count    count        rate   accuracy phase  cycle
---------------------------------------------------------------------------------------------
    pll_hpll                          1        1        0  1400000000          0     0  50000
       hpll                           1        2        0  1400000000          0     0  50000
          clk_npu_np5                 0        0        0   186666667          0     0  50000
          clk_npu_div                 0        1        0   700000000          0     0  50000
             clk_core_npu             0        2        0   700000000          0     0  50000
                clk_core_npupvtm       0        0        0   700000000          0     0  50000
    pll_gpll                          1        1        0  1188000000          0     0  50000
       gpll                          14       36        0  1188000000          0     0  50000
          dclk_vop_div                1        1        0    62526316          0     0  50000
             dclk_vop_mux             1        1        0    62526316          0     0  50000
                dclk_vop              1        3        0    62526316          0     0  50000
             dclk_vop_fracdiv         0        0        0    62526316          0     0  50000

我们可以看到,dclk_vop使用的时钟源来自gpll,其时钟是1188000000。如果按照54MHz分频,其分频倍数为22;如果按照74.25MHz分配,其分频倍数为16;如果按照65MHz分频,其分频倍数为18.27,因为PLL out后的分频器只能按照整数倍分频,因此将其调整为19,得到62526315,和cat出来的数据差1Hz,属于误差范围。

四、解决问题

4.1 修改vop的时钟源

  我们翻阅dtsi和文档2可以发现,RV1126有以下几个PLL:

  1. APLL,CPU的时钟:一般只给CPU使用,因为CPU会变频,APLL会根据CPU要求的频率变化。
  2. DPLL,DDR的时钟:一般只给DDR使用,因为DDR会变频,DPLL会根据DDR要求的频率变化。
  3. GPLL,提供总线、外设时钟做备份:一般设置在594M或者1200M,保证基本的100、200、300、400M的时钟都有输出。
  4. CPLL,GMAC或者其他设备做备份:一般可能是400、500、800、1000M,或者是给Lcdc独占使用。
  5. NPLL(HPLL),给其他设备做备份:一般可能是1188M,或者给Lcdc独占使用。

因此,我们从时钟树中发现hpll的设备最少,我们将dclk_vop_div迁移至hpll之后倍频更容易更改。

  进一步的,如果我们不想要修改dclk_vop_div的时钟源,那么就需要保证当前时钟源gpll可以分频出65MHz、同时兼容1188000000。我们计算发现,我们需要PLL77.22GHz才能满足要求。我们通过文档1中的PLL架构可以计算出,其最大频率支持到38.4GHz,因此无法实现兼容。于是我们选择将dclk_vop_div的时钟源替换为hpll,计算发现,只需要将其时钟提升到18.2GHz即可满足兼容65MHz700MHz的需求。但在设备树中,其使用32bits的数据来存放时钟,因此最高上限只有4GHz多一些,因此我将其修改为1.82GHz,以避免PLL之后后分频器不够做到更高比例的分频。

  我们在rv1126.dtsi中做如下修改:

cru: clock-controller@ff490000 {
    compatible = "rockchip,rv1126-cru";
    reg = <0xff490000 0x1000>;
    rockchip,grf = <&grf>;
    #clock-cells = <1>;
    #reset-cells = <1>;

    assigned-clocks =
        <&pmucru CLK_RTC32K>, <&pmucru PLL_GPLL>,
        <&pmucru PCLK_PDPMU>, <&cru PLL_CPLL>,
        <&cru PLL_HPLL>, <&cru ARMCLK>,
        <&cru ACLK_PDBUS>, <&cru HCLK_PDBUS>,
        <&cru PCLK_PDBUS>, <&cru ACLK_PDPHP>,
        <&cru HCLK_PDPHP>, <&cru HCLK_PDAUDIO>,
        <&cru HCLK_PDCORE_NIU>;
    assigned-clock-rates =
        <32768>, <1188000000>,
        <100000000>, <500000000>,
        // <1400000000>, <600000000>,
        <1820000000>, <600000000>,
        <500000000>, <200000000>,
        <100000000>, <300000000>,
        <200000000>, <150000000>,
        <200000000>;
    assigned-clock-parents =
        <&pmucru CLK_OSC0_DIV32K>;
};

4.2 修改dclk_vop_div的时钟源

  经过我们在4.1中的分析,现在我们需要将dclk_vop_div的PLL修改为hpll。我们修改的文件是kernel/driver/clk/rockchip/clk-rv1126.c,首先添加仅有hpll的定义:

#ifndef CONFIG_ROCKCHIP_LOW_PERFORMANCE
PNAME(mux_gpll_usb480m_cpll_xin24m_p)   = { "gpll", "usb480m", "cpll", "xin24m" };
PNAME(mux_armclk_p)         = { "gpll", "cpll", "apll" };
PNAME(mux_gpll_cpll_dpll_p)     = { "gpll", "cpll", "dummy_dpll" };
PNAME(mux_gpll_cpll_p)          = { "gpll", "cpll" };
PNAME(mux_gpll_cpll_usb480m_xin24m_p)   = { "gpll", "cpll", "usb480m", "xin24m" };
PNAME(mux_cpll_gpll_p)          = { "cpll", "gpll" };
PNAME(mux_gpll_cpll_xin24m_p)       = { "gpll", "cpll", "xin24m" };
PNAME(mux_cpll_hpll_gpll_p)     = { "cpll", "hpll", "gpll" };
PNAME(mux_cpll_gpll_hpll_p)     = { "cpll", "gpll", "hpll" };
PNAME(mux_gpll_cpll_hpll_p)     = { "gpll", "cpll", "hpll" };
PNAME(mux_gpll_cpll_apll_hpll_p)    = { "gpll", "cpll", "dummy_apll", "hpll" };
// ADD BEGIN
PNAME(mux_hpll_p)   = { "hpll" };
// ADD END

接着我们将dclk_vop_div绑定的PLL设置为mux_hpll_p即可。

// Original: Bind to mux_gpll_cpll_p
COMPOSITE(DCLK_VOP_DIV, "dclk_vop_div", mux_gpll_cpll_p, 0,
        RV1126_CLKSEL_CON(47), 8, 1, MFLAGS, 0, 8, DFLAGS,
        RV1126_CLKGATE_CON(14), 11, GFLAGS),
// Modify: Bind to mux_hpll_p
COMPOSITE(DCLK_VOP_DIV, "dclk_vop_div", mux_hpll_p, 0,
        RV1126_CLKSEL_CON(47), 8, 1, MFLAGS, 0, 8, DFLAGS,
        RV1126_CLKGATE_CON(14), 11, GFLAGS),