diff --git a/zircon/system/dev/clk/msm8x53-clk/BUILD.gn b/zircon/system/dev/clk/msm8x53-clk/BUILD.gn
index e4cf2109a0d72acbf25e7b64bef793c32ca04770..4ba45f88a14a80f719befd629e984f92f9ec24fe 100644
--- a/zircon/system/dev/clk/msm8x53-clk/BUILD.gn
+++ b/zircon/system/dev/clk/msm8x53-clk/BUILD.gn
@@ -27,3 +27,35 @@ driver("msm8x53-clk") {
     "$zx/system/ulib/zxcpp",
   ]
 }
+
+test("msm8x53-clk-test") {
+  testonly = true
+  output_name = "msm8x53-clk-test"
+  sources = [
+    "msm8x53-clk.cpp",
+    "msm8x53-clk-test.cpp"
+  ]
+  deps = [
+    "$zx/system/banjo/ddk.protocol.clock",
+    "$zx/system/banjo/ddk.protocol.clockimpl",
+    "$zx/system/banjo/ddk.protocol.gpio",
+    "$zx/system/banjo/ddk.protocol.i2c",
+    "$zx/system/banjo/ddk.protocol.platform.bus",
+    "$zx/system/banjo/ddk.protocol.platform.device",
+    "$zx/system/dev/lib/mock-mmio-reg",
+    "$zx/system/dev/lib/mmio",
+    "$zx/system/dev/lib/msm8x53",
+    "$zx/system/fidl/fuchsia-hardware-clock:c",
+    "$zx/system/ulib/ddk",
+    "$zx/system/ulib/ddktl",
+    "$zx/system/ulib/driver",
+    "$zx/system/ulib/fbl",
+    "$zx/system/ulib/fidl",
+    "$zx/system/ulib/hwreg",
+    "$zx/system/ulib/sync",
+    "$zx/system/ulib/zircon",
+    "$zx/system/ulib/zx",
+    "$zx/system/ulib/zxcpp",
+    "$zx/system/ulib/zxtest",
+  ]
+}
diff --git a/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk-regs.h b/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk-regs.h
index 47a896402049ab02e1c64f0db0959f27f234786c..cda5d373c868ddd2390b399b4fdb39bcd0d19bf5 100644
--- a/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk-regs.h
+++ b/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk-regs.h
@@ -141,4 +141,764 @@ constexpr uint32_t kApssTcuAsyncCbcr = 0x12018;
 constexpr uint32_t kApcsClockBranchEnaVote = 0x45004;
 constexpr uint32_t kApcsSmmuClockBranchEnaVote = 0x4500C;
 
-} // namespace msm8x53
\ No newline at end of file
+// RCG Command Registers
+constexpr uint32_t kCamssTopAhbCmdRcgr = 0x5A000;
+constexpr uint32_t kCsi0CmdRcgr = 0x4E020;
+constexpr uint32_t kApssAhbCmdRcgr = 0x46000;
+constexpr uint32_t kCsi1CmdRcgr = 0x4F020;
+constexpr uint32_t kCsi2CmdRcgr = 0x3C020;
+constexpr uint32_t kVfe0CmdRcgr = 0x58000;
+constexpr uint32_t kGfx3dCmdRcgr = 0x59000;
+constexpr uint32_t kVcodec0CmdRcgr = 0x4C000;
+constexpr uint32_t kCppCmdRcgr = 0x58018;
+constexpr uint32_t kJpeg0CmdRcgr = 0x57000;
+constexpr uint32_t kMdpCmdRcgr = 0x4D014;
+constexpr uint32_t kPclk0CmdRcgr = 0x4D000;
+constexpr uint32_t kPclk1CmdRcgr = 0x4D0B8;
+constexpr uint32_t kUsb30MasterCmdRcgr = 0x3F00C;
+constexpr uint32_t kVfe1CmdRcgr = 0x58054;
+constexpr uint32_t kApc0VoltageDroopDetectorCmdRcgr = 0x78008;
+constexpr uint32_t kApc1VoltageDroopDetectorCmdRcgr = 0x79008;
+constexpr uint32_t kBlsp1Qup1I2cAppsCmdRcgr = 0x0200C;
+constexpr uint32_t kBlsp1Qup1SpiAppsCmdRcgr = 0x02024;
+constexpr uint32_t kBlsp1Qup2I2cAppsCmdRcgr = 0x03000;
+constexpr uint32_t kBlsp1Qup2SpiAppsCmdRcgr = 0x03014;
+constexpr uint32_t kBlsp1Qup3I2cAppsCmdRcgr = 0x04000;
+constexpr uint32_t kBlsp1Qup3SpiAppsCmdRcgr = 0x04024;
+constexpr uint32_t kBlsp1Qup4I2cAppsCmdRcgr = 0x05000;
+constexpr uint32_t kBlsp1Qup4SpiAppsCmdRcgr = 0x05024;
+constexpr uint32_t kBlsp1Uart1AppsCmdRcgr = 0x02044;
+constexpr uint32_t kBlsp1Uart2AppsCmdRcgr = 0x03034;
+constexpr uint32_t kBlsp2Qup1I2cAppsCmdRcgr = 0x0C00C;
+constexpr uint32_t kBlsp2Qup1SpiAppsCmdRcgr = 0x0C024;
+constexpr uint32_t kBlsp2Qup2I2cAppsCmdRcgr = 0x0D000;
+constexpr uint32_t kBlsp2Qup2SpiAppsCmdRcgr = 0x0D014;
+constexpr uint32_t kBlsp2Qup3I2cAppsCmdRcgr = 0x0F000;
+constexpr uint32_t kBlsp2Qup3SpiAppsCmdRcgr = 0x0F024;
+constexpr uint32_t kBlsp2Qup4I2cAppsCmdRcgr = 0x18000;
+constexpr uint32_t kBlsp2Qup4SpiAppsCmdRcgr = 0x18024;
+constexpr uint32_t kBlsp2Uart1AppsCmdRcgr = 0x0C044;
+constexpr uint32_t kBlsp2Uart2AppsCmdRcgr = 0x0D034;
+constexpr uint32_t kCciCmdRcgr = 0x51000;
+constexpr uint32_t kCsi0pCmdRcgr = 0x58084;
+constexpr uint32_t kCsi1pCmdRcgr = 0x58094;
+constexpr uint32_t kCsi2pCmdRcgr = 0x580A4;
+constexpr uint32_t kCamssGp0CmdRcgr = 0x54000;
+constexpr uint32_t kCamssGp1CmdRcgr = 0x55000;
+constexpr uint32_t kMclk0CmdRcgr = 0x52000;
+constexpr uint32_t kMclk1CmdRcgr = 0x53000;
+constexpr uint32_t kMclk2CmdRcgr = 0x5C000;
+constexpr uint32_t kMclk3CmdRcgr = 0x5E000;
+constexpr uint32_t kCsi0phytimerCmdRcgr = 0x4E000;
+constexpr uint32_t kCsi1phytimerCmdRcgr = 0x4F000;
+constexpr uint32_t kCsi2phytimerCmdRcgr = 0x4F05C;
+constexpr uint32_t kCryptoCmdRcgr = 0x16004;
+constexpr uint32_t kGp1CmdRcgr = 0x08004;
+constexpr uint32_t kGp2CmdRcgr = 0x09004;
+constexpr uint32_t kGp3CmdRcgr = 0x0A004;
+constexpr uint32_t kByte0CmdRcgr = 0x4D044;
+constexpr uint32_t kByte1CmdRcgr = 0x4D0B0;
+constexpr uint32_t kEsc0CmdRcgr = 0x4D05C;
+constexpr uint32_t kEsc1CmdRcgr = 0x4D0A8;
+constexpr uint32_t kVsyncCmdRcgr = 0x4D02C;
+constexpr uint32_t kPdm2CmdRcgr = 0x44010;
+constexpr uint32_t kRbcprGfxCmdRcgr = 0x3A00C;
+constexpr uint32_t kSdcc1AppsCmdRcgr = 0x42004;
+constexpr uint32_t kSdcc1IceCoreCmdRcgr = 0x5D000;
+constexpr uint32_t kSdcc2AppsCmdRcgr = 0x43004;
+constexpr uint32_t kUsb30MockUtmiCmdRcgr = 0x3F020;
+constexpr uint32_t kUsb3AuxCmdRcgr = 0x3F05C;
+
+// Mux Constants
+constexpr uint32_t kXoSrcVal = 0;
+constexpr uint32_t kXoASrcVal = 0;
+constexpr uint32_t kXoPipeSrcVal = 1;
+constexpr uint32_t kGpll0SrcVal = 1;
+constexpr uint32_t kGpll0MainSrcVal = 2;
+constexpr uint32_t kGpll0MainMockSrcVal = 3;
+constexpr uint32_t kGpll0MainDiv2Usb3SrcVal = 2;
+constexpr uint32_t kGpll0MainDiv2SrcVal = 4;
+constexpr uint32_t kGpll0MainDiv2CciSrcVal = 3;
+constexpr uint32_t kGpll0MainDiv2MmSrcVal = 5;
+constexpr uint32_t kGpll0MainDiv2AxiSrcVal = 6;
+constexpr uint32_t kGpll2SrcVal = 4;
+constexpr uint32_t kGpll2OutMainSrcVal = 5;
+constexpr uint32_t kGpll2VcodecSrcVal = 3;
+constexpr uint32_t kGpll3SrcVal = 2;
+constexpr uint32_t kGpll4SrcVal = 2;
+constexpr uint32_t kGpll4AuxSrcVal = 2;
+constexpr uint32_t kGpll4OutAuxSrcVal = 4;
+constexpr uint32_t kGpll6MainSrcVal = 1;
+constexpr uint32_t kGpll6SrcVal = 2;
+constexpr uint32_t kGpll6MainGfxSrcVal = 3;
+constexpr uint32_t kGpll6MainDiv2MockSrcVal = 2;
+constexpr uint32_t kGpll6MainDiv2SrcVal = 5;
+constexpr uint32_t kGpll6MainDiv2GfxSrcVal = 6;
+constexpr uint32_t kGpll6AuxSrcVal = 2;
+constexpr uint32_t kGpll6OutAuxSrcVal = 3;
+constexpr uint32_t kUsb3PipeSrcVal = 0;
+constexpr uint32_t kDsi0PhypllMmSrcVal = 1;
+constexpr uint32_t kDsi1PhypllMmSrcVal = 3;
+constexpr uint32_t kDsi0PhypllClkMmSrcVal = 3;
+constexpr uint32_t kDsi1PhypllClkMmSrcVal = 1;
+
+} // namespace msm8x53
+
+namespace clk {
+
+typedef struct msm_clk_gate {
+    uint32_t reg;
+    uint32_t bit;
+    uint32_t delay_us;
+} msm_clk_gate_t;
+
+struct msm_clk_branch {
+    uint32_t reg;
+};
+
+struct msm_clk_voter {
+    uint32_t cbcr_reg;
+    uint32_t vote_reg;
+    uint32_t bit;
+};
+
+enum class RcgDividerType {
+    HalfInteger,
+    Mnd
+};
+
+class RcgFrequencyTable {
+    static constexpr uint32_t kPredivMask = 0x1f;
+    static constexpr uint32_t kSrcMask = 0x7;
+    static constexpr uint32_t kSrcShift = 8;
+
+public:
+    constexpr RcgFrequencyTable(uint64_t rate, uint32_t m, uint32_t n, uint32_t d2, uint32_t parent)
+        : rate_(rate), m_(m), n_(n == 0 ? 0 : ~(n - m)), d_(~n), predev_parent_(((d2 - 1) & kPredivMask) | ((parent & kSrcMask) << kSrcShift)) {}
+
+    uint64_t rate() const { return rate_; }
+    uint32_t m() const { return m_; }
+    uint32_t n() const { return n_; }
+    uint32_t d() const { return d_; }
+    uint32_t predev_parent() const { return predev_parent_; }
+
+private:
+    const uint64_t rate_;
+    const uint32_t m_;
+    const uint32_t n_;
+    const uint32_t d_;
+    const uint32_t predev_parent_;
+};
+
+class MsmClkRcg {
+public:
+    constexpr MsmClkRcg(uint32_t reg, RcgDividerType type, const RcgFrequencyTable* table,
+                        size_t frequency_table_count, bool unsupported = false)
+        : cmd_rcgr_reg_(reg), type_(type), frequency_table_(table),
+          frequency_table_count_(frequency_table_count), unsupported_(unsupported) {}
+
+    static constexpr uint32_t kCmdOffset = 0x0;
+    uint32_t CmdReg() const { return cmd_rcgr_reg_ + kCmdOffset; }
+
+    static constexpr uint32_t kCfgOffset = 0x4;
+    uint32_t CfgReg() const { return cmd_rcgr_reg_ + kCfgOffset; }
+
+    static constexpr uint32_t kMOffset = 0x8;
+    uint32_t MReg() const { return cmd_rcgr_reg_ + kMOffset; }
+
+    static constexpr uint32_t kNOffset = 0xC;
+    uint32_t NReg() const { return cmd_rcgr_reg_ + kNOffset; }
+
+    static constexpr uint32_t KDOffset = 0x10;
+    uint32_t DReg() const { return cmd_rcgr_reg_ + KDOffset; }
+
+    RcgDividerType Type() const { return type_; }
+    const RcgFrequencyTable* Table() const { return frequency_table_; }
+    size_t TableCount() const { return frequency_table_count_; }
+    bool Unsupported() const { return unsupported_; }
+
+private:
+    const uint32_t cmd_rcgr_reg_;
+    const RcgDividerType type_;
+    const RcgFrequencyTable* frequency_table_;
+    const size_t frequency_table_count_;
+    const bool unsupported_;
+};
+
+namespace {
+
+class RcgClkCmd : public hwreg::RegisterBase<RcgClkCmd, uint32_t> {
+public:
+    DEF_BIT(0, cfg_update);
+    DEF_BIT(1, root_enable);
+    DEF_BIT(31, root_status);
+
+    static auto Read(uint32_t offset) {
+        return hwreg::RegisterAddr<RcgClkCmd>(offset);
+    }
+};
+
+constexpr msm_clk_gate_t kMsmClkGates[] = {
+    [msm8x53::MsmClkIndex(msm8x53::kQUsbRefClk)] = {.reg = 0x41030, .bit = 0, .delay_us = 0},
+    [msm8x53::MsmClkIndex(msm8x53::kUsbSSRefClk)] = {.reg = 0x5e07c, .bit = 0, .delay_us = 0},
+    [msm8x53::MsmClkIndex(msm8x53::kUsb3PipeClk)] = {.reg = 0x5e040, .bit = 0, .delay_us = 50},
+};
+
+constexpr uint32_t kBranchEnable = (0x1u << 0);
+constexpr struct msm_clk_branch kMsmClkBranches[] = {
+    [msm8x53::MsmClkIndex(msm8x53::kApc0DroopDetectorGpll0Clk)] = {
+        .reg = msm8x53::kApc0VoltageDroopDetectorGpll0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kApc1DroopDetectorGpll0Clk)] = {.reg = msm8x53::kApc1VoltageDroopDetectorGpll0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup1I2cAppsClk)] = {.reg = msm8x53::kBlsp1Qup1I2cAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup1SpiAppsClk)] = {.reg = msm8x53::kBlsp1Qup1SpiAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup2I2cAppsClk)] = {.reg = msm8x53::kBlsp1Qup2I2cAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup2SpiAppsClk)] = {.reg = msm8x53::kBlsp1Qup2SpiAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup3I2cAppsClk)] = {.reg = msm8x53::kBlsp1Qup3I2cAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup3SpiAppsClk)] = {.reg = msm8x53::kBlsp1Qup3SpiAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup4I2cAppsClk)] = {.reg = msm8x53::kBlsp1Qup4I2cAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup4SpiAppsClk)] = {.reg = msm8x53::kBlsp1Qup4SpiAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Uart1AppsClk)] = {.reg = msm8x53::kBlsp1Uart1AppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Uart2AppsClk)] = {.reg = msm8x53::kBlsp1Uart2AppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup1I2cAppsClk)] = {.reg = msm8x53::kBlsp2Qup1I2cAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup1SpiAppsClk)] = {.reg = msm8x53::kBlsp2Qup1SpiAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup2I2cAppsClk)] = {.reg = msm8x53::kBlsp2Qup2I2cAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup2SpiAppsClk)] = {.reg = msm8x53::kBlsp2Qup2SpiAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup3I2cAppsClk)] = {.reg = msm8x53::kBlsp2Qup3I2cAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup3SpiAppsClk)] = {.reg = msm8x53::kBlsp2Qup3SpiAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup4I2cAppsClk)] = {.reg = msm8x53::kBlsp2Qup4I2cAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup4SpiAppsClk)] = {.reg = msm8x53::kBlsp2Qup4SpiAppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Uart1AppsClk)] = {.reg = msm8x53::kBlsp2Uart1AppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Uart2AppsClk)] = {.reg = msm8x53::kBlsp2Uart2AppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBimcGpuClk)] = {.reg = msm8x53::kBimcGpuCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCciAhbClk)] = {.reg = msm8x53::kCamssCciAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCciClk)] = {.reg = msm8x53::kCamssCciCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCppAhbClk)] = {.reg = msm8x53::kCamssCppAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCppAxiClk)] = {.reg = msm8x53::kCamssCppAxiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCppClk)] = {.reg = msm8x53::kCamssCppCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0AhbClk)] = {.reg = msm8x53::kCamssCsi0AhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0Clk)] = {.reg = msm8x53::kCamssCsi0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0Csiphy3pClk)] = {.reg = msm8x53::kCamssCsi0Csiphy3pCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0phyClk)] = {.reg = msm8x53::kCamssCsi0phyCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0pixClk)] = {.reg = msm8x53::kCamssCsi0pixCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0rdiClk)] = {.reg = msm8x53::kCamssCsi0rdiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1AhbClk)] = {.reg = msm8x53::kCamssCsi1AhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1Clk)] = {.reg = msm8x53::kCamssCsi1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1Csiphy3pClk)] = {.reg = msm8x53::kCamssCsi1Csiphy3pCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1phyClk)] = {.reg = msm8x53::kCamssCsi1phyCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1pixClk)] = {.reg = msm8x53::kCamssCsi1pixCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1rdiClk)] = {.reg = msm8x53::kCamssCsi1rdiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2AhbClk)] = {.reg = msm8x53::kCamssCsi2AhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2Clk)] = {.reg = msm8x53::kCamssCsi2Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2Csiphy3pClk)] = {.reg = msm8x53::kCamssCsi2Csiphy3pCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2phyClk)] = {.reg = msm8x53::kCamssCsi2phyCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2pixClk)] = {.reg = msm8x53::kCamssCsi2pixCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2rdiClk)] = {.reg = msm8x53::kCamssCsi2rdiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsiVfe0Clk)] = {.reg = msm8x53::kCamssCsiVfe0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsiVfe1Clk)] = {.reg = msm8x53::kCamssCsiVfe1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssGp0Clk)] = {.reg = msm8x53::kCamssGp0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssGp1Clk)] = {.reg = msm8x53::kCamssGp1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssIspifAhbClk)] = {.reg = msm8x53::kCamssIspifAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssJpeg0Clk)] = {.reg = msm8x53::kCamssJpeg0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssJpegAhbClk)] = {.reg = msm8x53::kCamssJpegAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssJpegAxiClk)] = {.reg = msm8x53::kCamssJpegAxiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssMclk0Clk)] = {.reg = msm8x53::kCamssMclk0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssMclk1Clk)] = {.reg = msm8x53::kCamssMclk1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssMclk2Clk)] = {.reg = msm8x53::kCamssMclk2Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssMclk3Clk)] = {.reg = msm8x53::kCamssMclk3Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssMicroAhbClk)] = {.reg = msm8x53::kCamssMicroAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0phytimerClk)] = {.reg = msm8x53::kCamssCsi0phytimerCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1phytimerClk)] = {.reg = msm8x53::kCamssCsi1phytimerCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2phytimerClk)] = {.reg = msm8x53::kCamssCsi2phytimerCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssAhbClk)] = {.reg = msm8x53::kCamssAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssTopAhbClk)] = {.reg = msm8x53::kCamssTopAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssVfe0Clk)] = {.reg = msm8x53::kCamssVfe0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssVfeAhbClk)] = {.reg = msm8x53::kCamssVfeAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssVfeAxiClk)] = {.reg = msm8x53::kCamssVfeAxiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssVfe1AhbClk)] = {.reg = msm8x53::kCamssVfe1AhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssVfe1AxiClk)] = {.reg = msm8x53::kCamssVfe1AxiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kCamssVfe1Clk)] = {.reg = msm8x53::kCamssVfe1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kDccClk)] = {.reg = msm8x53::kDccCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kGp1Clk)] = {.reg = msm8x53::kGp1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kGp2Clk)] = {.reg = msm8x53::kGp2Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kGp3Clk)] = {.reg = msm8x53::kGp3Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssAhbClk)] = {.reg = msm8x53::kMdssAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssAxiClk)] = {.reg = msm8x53::kMdssAxiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssByte0Clk)] = {.reg = msm8x53::kMdssByte0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssByte1Clk)] = {.reg = msm8x53::kMdssByte1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssEsc0Clk)] = {.reg = msm8x53::kMdssEsc0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssEsc1Clk)] = {.reg = msm8x53::kMdssEsc1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssMdpClk)] = {.reg = msm8x53::kMdssMdpCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssPclk0Clk)] = {.reg = msm8x53::kMdssPclk0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssPclk1Clk)] = {.reg = msm8x53::kMdssPclk1Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMdssVsyncClk)] = {.reg = msm8x53::kMdssVsyncCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMssCfgAhbClk)] = {.reg = msm8x53::kMssCfgAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kMssQ6BimcAxiClk)] = {.reg = msm8x53::kMssQ6BimcAxiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kBimcGfxClk)] = {.reg = msm8x53::kBimcGfxCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kOxiliAhbClk)] = {.reg = msm8x53::kOxiliAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kOxiliAonClk)] = {.reg = msm8x53::kOxiliAonCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kOxiliGfx3dClk)] = {.reg = msm8x53::kOxiliGfx3dCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kOxiliTimerClk)] = {.reg = msm8x53::kOxiliTimerCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kPcnocUsb3AxiClk)] = {.reg = msm8x53::kPcnocUsb3AxiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kPdm2Clk)] = {.reg = msm8x53::kPdm2Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kPdmAhbClk)] = {.reg = msm8x53::kPdmAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kRbcprGfxClk)] = {.reg = msm8x53::kRbcprGfxCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kSdcc1AhbClk)] = {.reg = msm8x53::kSdcc1AhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kSdcc1AppsClk)] = {.reg = msm8x53::kSdcc1AppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kSdcc1IceCoreClk)] = {.reg = msm8x53::kSdcc1IceCoreCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kSdcc2AhbClk)] = {.reg = msm8x53::kSdcc2AhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kSdcc2AppsClk)] = {.reg = msm8x53::kSdcc2AppsCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kUsb30MasterClk)] = {.reg = msm8x53::kUsb30MasterCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kUsb30MockUtmiClk)] = {.reg = msm8x53::kUsb30MockUtmiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kUsb30SleepClk)] = {.reg = msm8x53::kUsb30SleepCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kUsb3AuxClk)] = {.reg = msm8x53::kUsb3AuxCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kUsbPhyCfgAhbClk)] = {.reg = msm8x53::kUsbPhyCfgAhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kVenus0AhbClk)] = {.reg = msm8x53::kVenus0AhbCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kVenus0AxiClk)] = {.reg = msm8x53::kVenus0AxiCbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kVenus0Core0Vcodec0Clk)] = {.reg = msm8x53::kVenus0Core0Vcodec0Cbcr},
+    [msm8x53::MsmClkIndex(msm8x53::kVenus0Vcodec0Clk)] = {.reg = msm8x53::kVenus0Vcodec0Cbcr},
+};
+
+constexpr RcgFrequencyTable kFtblCamssTopAhbClkSrc[] = {
+    RcgFrequencyTable(40000000, 0, 0, 20, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(80000000, 0, 0, 20, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi0ClkSrc[] = {
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2SrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(465000000, 0, 0, 4, msm8x53::kGpll2SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblApssAhbClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoASrcVal),
+    RcgFrequencyTable(25000000, 0, 0, 32, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(50000000, 0, 0, 32, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 16, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(133330000, 0, 0, 12, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi1ClkSrc[] = {
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2OutMainSrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(465000000, 0, 0, 4, msm8x53::kGpll2OutMainSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi2ClkSrc[] = {
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2OutMainSrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(465000000, 0, 0, 4, msm8x53::kGpll2OutMainSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblVfe0ClkSrc[] = {
+    RcgFrequencyTable(50000000, 0, 0, 16, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(133330000, 0, 0, 12, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(160000000, 0, 0, 10, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2SrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(465000000, 0, 0, 4, msm8x53::kGpll2SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblGfx3dClkSrc[] = {};
+
+__UNUSED constexpr RcgFrequencyTable kFtblGfx3dClkSrcSdm450[] = {};
+
+__UNUSED constexpr RcgFrequencyTable kFtblGfx3dClkSrcSdm632[] = {};
+
+constexpr RcgFrequencyTable kFtblVcodec0ClkSrc[] = {
+    RcgFrequencyTable(114290000, 0, 0, 7, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(228570000, 0, 0, 7, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2VcodecSrcVal),
+    RcgFrequencyTable(360000000, 0, 0, 6, msm8x53::kGpll6SrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(465000000, 0, 0, 4, msm8x53::kGpll2VcodecSrcVal),
+};
+
+__UNUSED constexpr RcgFrequencyTable kFtblVcodec0ClkSrc540MHz[] = {
+    RcgFrequencyTable(114290000, 0, 0, 7, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(228570000, 0, 0, 7, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2VcodecSrcVal),
+    RcgFrequencyTable(360000000, 0, 0, 6, msm8x53::kGpll6SrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(465000000, 0, 0, 4, msm8x53::kGpll2VcodecSrcVal),
+    RcgFrequencyTable(540000000, 0, 0, 4, msm8x53::kGpll6SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCppClkSrc[] = {
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(320000000, 0, 0, 5, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(465000000, 0, 0, 4, msm8x53::kGpll2SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblJpeg0ClkSrc[] = {
+    RcgFrequencyTable(66670000, 0, 0, 12, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(133330000, 0, 0, 12, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2OutMainSrcVal),
+    RcgFrequencyTable(320000000, 0, 0, 5, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblMdpClkSrc[] = {
+    RcgFrequencyTable(50000000, 0, 0, 16, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(80000000, 0, 0, 10, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(160000000, 0, 0, 5, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(320000000, 0, 0, 5, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblPclk0ClkSrc[] = {};
+
+constexpr RcgFrequencyTable kFtblPclk1ClkSrc[] = {};
+
+constexpr RcgFrequencyTable kFtblUsb30MasterClkSrc[] = {
+    RcgFrequencyTable(80000000, 0, 0, 10, msm8x53::kGpll0MainDiv2Usb3SrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 16, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(133330000, 0, 0, 12, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblVfe1ClkSrc[] = {
+    RcgFrequencyTable(50000000, 0, 0, 16, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(133330000, 0, 0, 12, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(160000000, 0, 0, 10, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2SrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(465000000, 0, 0, 4, msm8x53::kGpll2SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblApc0DroopDetectorClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(576000000, 0, 0, 4, msm8x53::kGpll4SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblApc1DroopDetectorClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(400000000, 0, 0, 4, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(576000000, 0, 0, 4, msm8x53::kGpll4SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblBlspI2cAppsClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(25000000, 0, 0, 32, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(50000000, 0, 0, 32, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblBlspSpiAppsClkSrc[] = {
+    RcgFrequencyTable(960000, 1, 2, 20, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(4800000, 0, 0, 8, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(9600000, 0, 0, 4, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(12500000, 1, 2, 32, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(16000000, 1, 5, 20, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(25000000, 1, 2, 32, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(50000000, 0, 0, 32, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblBlspUartAppsClkSrc[] = {
+    RcgFrequencyTable(3686400, 144, 15625, 2, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(7372800, 288, 15625, 2, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(14745600, 576, 15625, 2, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(16000000, 1, 5, 10, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(24000000, 3, 100, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(25000000, 1, 2, 32, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(32000000, 1, 25, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(40000000, 1, 20, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(46400000, 29, 500, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(48000000, 3, 50, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(51200000, 8, 125, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(56000000, 7, 100, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(58982400, 1152, 15625, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(60000000, 3, 40, 2, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(64000000, 2, 25, 2, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCciClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(37500000, 3, 32, 2, msm8x53::kGpll0MainDiv2CciSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi0pClkSrc[] = {
+    RcgFrequencyTable(66670000, 0, 0, 12, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(133330000, 0, 0, 12, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi1pClkSrc[] = {
+    RcgFrequencyTable(66670000, 0, 0, 12, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(133330000, 0, 0, 12, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi2pClkSrc[] = {
+    RcgFrequencyTable(66670000, 0, 0, 12, msm8x53::kGpll0MainDiv2MmSrcVal),
+    RcgFrequencyTable(133330000, 0, 0, 12, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(310000000, 0, 0, 6, msm8x53::kGpll2SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCamssGp0ClkSrc[] = {
+    RcgFrequencyTable(50000000, 0, 0, 16, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 16, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCamssGp1ClkSrc[] = {
+    RcgFrequencyTable(50000000, 0, 0, 16, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 16, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblMclk0ClkSrc[] = {
+    RcgFrequencyTable(24000000, 2, 45, 2, msm8x53::kGpll6MainDiv2SrcVal),
+    RcgFrequencyTable(33330000, 0, 0, 24, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(36610000, 2, 59, 2, msm8x53::kGpll6SrcVal),
+    RcgFrequencyTable(66667000, 0, 0, 24, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblMclk1ClkSrc[] = {
+    RcgFrequencyTable(24000000, 2, 45, 2, msm8x53::kGpll6MainDiv2SrcVal),
+    RcgFrequencyTable(33330000, 0, 0, 24, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(36610000, 2, 59, 2, msm8x53::kGpll6SrcVal),
+    RcgFrequencyTable(66667000, 0, 0, 24, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblMclk2ClkSrc[] = {
+    RcgFrequencyTable(24000000, 2, 45, 2, msm8x53::kGpll6MainDiv2SrcVal),
+    RcgFrequencyTable(33330000, 0, 0, 24, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(36610000, 2, 59, 2, msm8x53::kGpll6SrcVal),
+    RcgFrequencyTable(66667000, 0, 0, 24, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblMclk3ClkSrc[] = {
+    RcgFrequencyTable(24000000, 2, 45, 2, msm8x53::kGpll6MainDiv2SrcVal),
+    RcgFrequencyTable(33330000, 0, 0, 24, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(36610000, 2, 59, 2, msm8x53::kGpll6SrcVal),
+    RcgFrequencyTable(66667000, 0, 0, 24, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi0phytimerClkSrc[] = {
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi1phytimerClkSrc[] = {
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCsi2phytimerClkSrc[] = {
+    RcgFrequencyTable(100000000, 0, 0, 8, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(266670000, 0, 0, 6, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblCryptoClkSrc[] = {
+    RcgFrequencyTable(40000000, 0, 0, 20, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(80000000, 0, 0, 20, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 16, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(160000000, 0, 0, 10, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblGp1ClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblGp2ClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblGp3ClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblByte0ClkSrc[] = {};
+
+constexpr RcgFrequencyTable kFtblByte1ClkSrc[] = {};
+
+constexpr RcgFrequencyTable kFtblEsc0ClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblEsc1ClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblVsyncClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblPdm2ClkSrc[] = {
+    RcgFrequencyTable(32000000, 0, 0, 25, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(64000000, 0, 0, 25, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblRbcprGfxClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(50000000, 0, 0, 32, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblSdcc1AppsClkSrc[] = {
+    RcgFrequencyTable(144000, 3, 25, 32, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(400000, 1, 4, 24, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(20000000, 1, 4, 10, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(25000000, 0, 0, 32, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(50000000, 0, 0, 32, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 16, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(177770000, 0, 0, 9, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(192000000, 0, 0, 12, msm8x53::kGpll4SrcVal),
+    RcgFrequencyTable(384000000, 0, 0, 6, msm8x53::kGpll4SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblSdcc1IceCoreClkSrc[] = {
+    RcgFrequencyTable(80000000, 0, 0, 10, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(160000000, 0, 0, 10, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(270000000, 0, 0, 8, msm8x53::kGpll6SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblSdcc2AppsClkSrc[] = {
+    RcgFrequencyTable(144000, 3, 25, 32, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(400000, 1, 4, 24, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(20000000, 1, 4, 10, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(25000000, 0, 0, 32, msm8x53::kGpll0MainDiv2SrcVal),
+    RcgFrequencyTable(50000000, 0, 0, 32, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(100000000, 0, 0, 16, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(177770000, 0, 0, 9, msm8x53::kGpll0SrcVal),
+    RcgFrequencyTable(192000000, 0, 0, 12, msm8x53::kGpll4AuxSrcVal),
+    RcgFrequencyTable(200000000, 0, 0, 8, msm8x53::kGpll0SrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblUsb30MockUtmiClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+    RcgFrequencyTable(60000000, 1, 1, 18, msm8x53::kGpll6MainDiv2MockSrcVal),
+};
+
+constexpr RcgFrequencyTable kFtblUsb3AuxClkSrc[] = {
+    RcgFrequencyTable(19200000, 0, 0, 2, msm8x53::kXoSrcVal),
+};
+
+constexpr MsmClkRcg kMsmClkRcgs[] = {
+    [msm8x53::MsmClkIndex(msm8x53::kCamssTopAhbClkSrc)] = MsmClkRcg(msm8x53::kCamssTopAhbCmdRcgr, RcgDividerType::Mnd, kFtblCamssTopAhbClkSrc, countof(kFtblCamssTopAhbClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi0ClkSrc)] = MsmClkRcg(msm8x53::kCsi0CmdRcgr, RcgDividerType::HalfInteger, kFtblCsi0ClkSrc, countof(kFtblCsi0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kApssAhbClkSrc)] = MsmClkRcg(msm8x53::kApssAhbCmdRcgr, RcgDividerType::HalfInteger, kFtblApssAhbClkSrc, countof(kFtblApssAhbClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi1ClkSrc)] = MsmClkRcg(msm8x53::kCsi1CmdRcgr, RcgDividerType::HalfInteger, kFtblCsi1ClkSrc, countof(kFtblCsi1ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi2ClkSrc)] = MsmClkRcg(msm8x53::kCsi2CmdRcgr, RcgDividerType::HalfInteger, kFtblCsi2ClkSrc, countof(kFtblCsi2ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kVfe0ClkSrc)] = MsmClkRcg(msm8x53::kVfe0CmdRcgr, RcgDividerType::HalfInteger, kFtblVfe0ClkSrc, countof(kFtblVfe0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kGfx3dClkSrc)] = MsmClkRcg(msm8x53::kGfx3dCmdRcgr, RcgDividerType::HalfInteger, kFtblGfx3dClkSrc, countof(kFtblGfx3dClkSrc), true),
+    [msm8x53::MsmClkIndex(msm8x53::kVcodec0ClkSrc)] = MsmClkRcg(msm8x53::kVcodec0CmdRcgr, RcgDividerType::Mnd, kFtblVcodec0ClkSrc, countof(kFtblVcodec0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCppClkSrc)] = MsmClkRcg(msm8x53::kCppCmdRcgr, RcgDividerType::HalfInteger, kFtblCppClkSrc, countof(kFtblCppClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kJpeg0ClkSrc)] = MsmClkRcg(msm8x53::kJpeg0CmdRcgr, RcgDividerType::HalfInteger, kFtblJpeg0ClkSrc, countof(kFtblJpeg0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kMdpClkSrc)] = MsmClkRcg(msm8x53::kMdpCmdRcgr, RcgDividerType::HalfInteger, kFtblMdpClkSrc, countof(kFtblMdpClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kPclk0ClkSrc)] = MsmClkRcg(msm8x53::kPclk0CmdRcgr, RcgDividerType::Mnd, kFtblPclk0ClkSrc, countof(kFtblPclk0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kPclk1ClkSrc)] = MsmClkRcg(msm8x53::kPclk1CmdRcgr, RcgDividerType::Mnd, kFtblPclk1ClkSrc, countof(kFtblPclk1ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kUsb30MasterClkSrc)] = MsmClkRcg(msm8x53::kUsb30MasterCmdRcgr, RcgDividerType::Mnd, kFtblUsb30MasterClkSrc, countof(kFtblUsb30MasterClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kVfe1ClkSrc)] = MsmClkRcg(msm8x53::kVfe1CmdRcgr, RcgDividerType::HalfInteger, kFtblVfe1ClkSrc, countof(kFtblVfe1ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kApc0DroopDetectorClkSrc)] = MsmClkRcg(msm8x53::kApc0VoltageDroopDetectorCmdRcgr, RcgDividerType::HalfInteger, kFtblApc0DroopDetectorClkSrc, countof(kFtblApc0DroopDetectorClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kApc1DroopDetectorClkSrc)] = MsmClkRcg(msm8x53::kApc1VoltageDroopDetectorCmdRcgr, RcgDividerType::HalfInteger, kFtblApc1DroopDetectorClkSrc, countof(kFtblApc1DroopDetectorClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup1I2cAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Qup1I2cAppsCmdRcgr, RcgDividerType::HalfInteger, kFtblBlspI2cAppsClkSrc, countof(kFtblBlspI2cAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup1SpiAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Qup1SpiAppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspSpiAppsClkSrc, countof(kFtblBlspSpiAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup2I2cAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Qup2I2cAppsCmdRcgr, RcgDividerType::HalfInteger, kFtblBlspI2cAppsClkSrc, countof(kFtblBlspI2cAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup2SpiAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Qup2SpiAppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspSpiAppsClkSrc, countof(kFtblBlspSpiAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup3I2cAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Qup3I2cAppsCmdRcgr, RcgDividerType::HalfInteger, kFtblBlspI2cAppsClkSrc, countof(kFtblBlspI2cAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup3SpiAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Qup3SpiAppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspSpiAppsClkSrc, countof(kFtblBlspSpiAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup4I2cAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Qup4I2cAppsCmdRcgr, RcgDividerType::HalfInteger, kFtblBlspI2cAppsClkSrc, countof(kFtblBlspI2cAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup4SpiAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Qup4SpiAppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspSpiAppsClkSrc, countof(kFtblBlspSpiAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Uart1AppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Uart1AppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspUartAppsClkSrc, countof(kFtblBlspUartAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Uart2AppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp1Uart2AppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspUartAppsClkSrc, countof(kFtblBlspUartAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup1I2cAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Qup1I2cAppsCmdRcgr, RcgDividerType::HalfInteger, kFtblBlspI2cAppsClkSrc, countof(kFtblBlspI2cAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup1SpiAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Qup1SpiAppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspSpiAppsClkSrc, countof(kFtblBlspSpiAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup2I2cAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Qup2I2cAppsCmdRcgr, RcgDividerType::HalfInteger, kFtblBlspI2cAppsClkSrc, countof(kFtblBlspI2cAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup2SpiAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Qup2SpiAppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspSpiAppsClkSrc, countof(kFtblBlspSpiAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup3I2cAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Qup3I2cAppsCmdRcgr, RcgDividerType::HalfInteger, kFtblBlspI2cAppsClkSrc, countof(kFtblBlspI2cAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup3SpiAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Qup3SpiAppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspSpiAppsClkSrc, countof(kFtblBlspSpiAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup4I2cAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Qup4I2cAppsCmdRcgr, RcgDividerType::HalfInteger, kFtblBlspI2cAppsClkSrc, countof(kFtblBlspI2cAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup4SpiAppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Qup4SpiAppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspSpiAppsClkSrc, countof(kFtblBlspSpiAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Uart1AppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Uart1AppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspUartAppsClkSrc, countof(kFtblBlspUartAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Uart2AppsClkSrc)] = MsmClkRcg(msm8x53::kBlsp2Uart2AppsCmdRcgr, RcgDividerType::Mnd, kFtblBlspUartAppsClkSrc, countof(kFtblBlspUartAppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCciClkSrc)] = MsmClkRcg(msm8x53::kCciCmdRcgr, RcgDividerType::Mnd, kFtblCciClkSrc, countof(kFtblCciClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi0pClkSrc)] = MsmClkRcg(msm8x53::kCsi0pCmdRcgr, RcgDividerType::HalfInteger, kFtblCsi0pClkSrc, countof(kFtblCsi0pClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi1pClkSrc)] = MsmClkRcg(msm8x53::kCsi1pCmdRcgr, RcgDividerType::HalfInteger, kFtblCsi1pClkSrc, countof(kFtblCsi1pClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi2pClkSrc)] = MsmClkRcg(msm8x53::kCsi2pCmdRcgr, RcgDividerType::HalfInteger, kFtblCsi2pClkSrc, countof(kFtblCsi2pClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCamssGp0ClkSrc)] = MsmClkRcg(msm8x53::kCamssGp0CmdRcgr, RcgDividerType::Mnd, kFtblCamssGp0ClkSrc, countof(kFtblCamssGp0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCamssGp1ClkSrc)] = MsmClkRcg(msm8x53::kCamssGp1CmdRcgr, RcgDividerType::Mnd, kFtblCamssGp1ClkSrc, countof(kFtblCamssGp1ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kMclk0ClkSrc)] = MsmClkRcg(msm8x53::kMclk0CmdRcgr, RcgDividerType::Mnd, kFtblMclk0ClkSrc, countof(kFtblMclk0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kMclk1ClkSrc)] = MsmClkRcg(msm8x53::kMclk1CmdRcgr, RcgDividerType::Mnd, kFtblMclk1ClkSrc, countof(kFtblMclk1ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kMclk2ClkSrc)] = MsmClkRcg(msm8x53::kMclk2CmdRcgr, RcgDividerType::Mnd, kFtblMclk2ClkSrc, countof(kFtblMclk2ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kMclk3ClkSrc)] = MsmClkRcg(msm8x53::kMclk3CmdRcgr, RcgDividerType::Mnd, kFtblMclk3ClkSrc, countof(kFtblMclk3ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi0phytimerClkSrc)] = MsmClkRcg(msm8x53::kCsi0phytimerCmdRcgr, RcgDividerType::HalfInteger, kFtblCsi0phytimerClkSrc, countof(kFtblCsi0phytimerClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi1phytimerClkSrc)] = MsmClkRcg(msm8x53::kCsi1phytimerCmdRcgr, RcgDividerType::HalfInteger, kFtblCsi1phytimerClkSrc, countof(kFtblCsi1phytimerClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCsi2phytimerClkSrc)] = MsmClkRcg(msm8x53::kCsi2phytimerCmdRcgr, RcgDividerType::HalfInteger, kFtblCsi2phytimerClkSrc, countof(kFtblCsi2phytimerClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kCryptoClkSrc)] = MsmClkRcg(msm8x53::kCryptoCmdRcgr, RcgDividerType::HalfInteger, kFtblCryptoClkSrc, countof(kFtblCryptoClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kGp1ClkSrc)] = MsmClkRcg(msm8x53::kGp1CmdRcgr, RcgDividerType::Mnd, kFtblGp1ClkSrc, countof(kFtblGp1ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kGp2ClkSrc)] = MsmClkRcg(msm8x53::kGp2CmdRcgr, RcgDividerType::Mnd, kFtblGp2ClkSrc, countof(kFtblGp2ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kGp3ClkSrc)] = MsmClkRcg(msm8x53::kGp3CmdRcgr, RcgDividerType::Mnd, kFtblGp3ClkSrc, countof(kFtblGp3ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kByte0ClkSrc)] = MsmClkRcg(msm8x53::kByte0CmdRcgr, RcgDividerType::HalfInteger, kFtblByte0ClkSrc, countof(kFtblByte0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kByte1ClkSrc)] = MsmClkRcg(msm8x53::kByte1CmdRcgr, RcgDividerType::HalfInteger, kFtblByte1ClkSrc, countof(kFtblByte1ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kEsc0ClkSrc)] = MsmClkRcg(msm8x53::kEsc0CmdRcgr, RcgDividerType::HalfInteger, kFtblEsc0ClkSrc, countof(kFtblEsc0ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kEsc1ClkSrc)] = MsmClkRcg(msm8x53::kEsc1CmdRcgr, RcgDividerType::HalfInteger, kFtblEsc1ClkSrc, countof(kFtblEsc1ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kVsyncClkSrc)] = MsmClkRcg(msm8x53::kVsyncCmdRcgr, RcgDividerType::HalfInteger, kFtblVsyncClkSrc, countof(kFtblVsyncClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kPdm2ClkSrc)] = MsmClkRcg(msm8x53::kPdm2CmdRcgr, RcgDividerType::HalfInteger, kFtblPdm2ClkSrc, countof(kFtblPdm2ClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kRbcprGfxClkSrc)] = MsmClkRcg(msm8x53::kRbcprGfxCmdRcgr, RcgDividerType::HalfInteger, kFtblRbcprGfxClkSrc, countof(kFtblRbcprGfxClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kSdcc1AppsClkSrc)] = MsmClkRcg(msm8x53::kSdcc1AppsCmdRcgr, RcgDividerType::Mnd, kFtblSdcc1AppsClkSrc, countof(kFtblSdcc1AppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kSdcc1IceCoreClkSrc)] = MsmClkRcg(msm8x53::kSdcc1IceCoreCmdRcgr, RcgDividerType::Mnd, kFtblSdcc1IceCoreClkSrc, countof(kFtblSdcc1IceCoreClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kSdcc2AppsClkSrc)] = MsmClkRcg(msm8x53::kSdcc2AppsCmdRcgr, RcgDividerType::Mnd, kFtblSdcc2AppsClkSrc, countof(kFtblSdcc2AppsClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kUsb30MockUtmiClkSrc)] = MsmClkRcg(msm8x53::kUsb30MockUtmiCmdRcgr, RcgDividerType::Mnd, kFtblUsb30MockUtmiClkSrc, countof(kFtblUsb30MockUtmiClkSrc)),
+    [msm8x53::MsmClkIndex(msm8x53::kUsb3AuxClkSrc)] = MsmClkRcg(msm8x53::kUsb3AuxCmdRcgr, RcgDividerType::Mnd, kFtblUsb3AuxClkSrc, countof(kFtblUsb3AuxClkSrc)),
+};
+
+static_assert(msm8x53::kRcgClkCount == countof(kMsmClkRcgs),
+              "kRcgClkCount must match count of RCG clocks");
+
+constexpr struct msm_clk_voter kMsmClkVoters[] = {
+    [msm8x53::MsmClkIndex(msm8x53::kApssAhbClk)] = {
+        .cbcr_reg = msm8x53::kApssAhbCbcr,
+        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
+        .bit = (1 << 14)},
+    [msm8x53::MsmClkIndex(msm8x53::kApssAxiClk)] = {.cbcr_reg = msm8x53::kApssAxiCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 13)},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp1AhbClk)] = {.cbcr_reg = msm8x53::kBlsp1AhbCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 10)},
+    [msm8x53::MsmClkIndex(msm8x53::kBlsp2AhbClk)] = {.cbcr_reg = msm8x53::kBlsp2AhbCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 20)},
+    [msm8x53::MsmClkIndex(msm8x53::kBootRomAhbClk)] = {.cbcr_reg = msm8x53::kBootRomAhbCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 7)},
+    [msm8x53::MsmClkIndex(msm8x53::kCryptoAhbClk)] = {.cbcr_reg = msm8x53::kCryptoAhbCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 0)},
+    [msm8x53::MsmClkIndex(msm8x53::kCryptoAxiClk)] = {.cbcr_reg = msm8x53::kCryptoAxiCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 1)},
+    [msm8x53::MsmClkIndex(msm8x53::kCryptoClk)] = {.cbcr_reg = msm8x53::kCryptoCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 2)},
+    [msm8x53::MsmClkIndex(msm8x53::kQdssDapClk)] = {.cbcr_reg = msm8x53::kQdssDapCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 11)},
+    [msm8x53::MsmClkIndex(msm8x53::kPrngAhbClk)] = {.cbcr_reg = msm8x53::kPrngAhbCbcr, .vote_reg = msm8x53::kApcsClockBranchEnaVote, .bit = (1 << 8)},
+    [msm8x53::MsmClkIndex(msm8x53::kApssTcuAsyncClk)] = {.cbcr_reg = msm8x53::kApssTcuAsyncCbcr, .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote, .bit = (1 << 1)},
+    [msm8x53::MsmClkIndex(msm8x53::kCppTbuClk)] = {.cbcr_reg = msm8x53::kCppTbuCbcr, .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote, .bit = (1 << 14)},
+    [msm8x53::MsmClkIndex(msm8x53::kJpegTbuClk)] = {.cbcr_reg = msm8x53::kJpegTbuCbcr, .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote, .bit = (1 << 10)},
+    [msm8x53::MsmClkIndex(msm8x53::kMdpTbuClk)] = {.cbcr_reg = msm8x53::kMdpTbuCbcr, .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote, .bit = (1 << 4)},
+    [msm8x53::MsmClkIndex(msm8x53::kSmmuCfgClk)] = {.cbcr_reg = msm8x53::kSmmuCfgCbcr, .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote, .bit = (1 << 12)},
+    [msm8x53::MsmClkIndex(msm8x53::kVenusTbuClk)] = {.cbcr_reg = msm8x53::kVenusTbuCbcr, .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote, .bit = (1 << 5)},
+    [msm8x53::MsmClkIndex(msm8x53::kVfe1TbuClk)] = {.cbcr_reg = msm8x53::kVfe1TbuCbcr, .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote, .bit = (1 << 17)},
+    [msm8x53::MsmClkIndex(msm8x53::kVfeTbuClk)] = {.cbcr_reg = msm8x53::kVfeTbuCbcr, .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote, .bit = (1 << 9)},
+};
+
+}  // namespace
+
+}  // namespace clk
\ No newline at end of file
diff --git a/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk-test.cpp b/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk-test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..55c931596d034929267756bb08693e1d1a7e1766
--- /dev/null
+++ b/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk-test.cpp
@@ -0,0 +1,156 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "msm8x53-clk.h"
+
+#include <soc/msm8x53/msm8x53-clock.h>
+#include <mock-mmio-reg/mock-mmio-reg.h>
+
+#include "msm8x53-clk-regs.h"
+
+
+namespace clk {
+
+class Msm8x53ClkTest : public Msm8x53Clk {
+public:
+    Msm8x53ClkTest(ddk_mock::MockMmioRegRegion& mmio)
+        : Msm8x53Clk(nullptr, ddk::MmioBuffer(mmio.GetMmioBuffer())) {}
+};
+
+// Test MND divider path.
+TEST(ClkTest8x53, TestSetRcgMnd) {
+    auto cc_regs_arr = std::make_unique<ddk_mock::MockMmioReg[]>(msm8x53::kCcSize);
+    ddk_mock::MockMmioRegRegion cc_regs(cc_regs_arr.get(), sizeof(uint32_t), msm8x53::kCcSize);
+    Msm8x53ClkTest clk(cc_regs);
+
+    const MsmClkRcg& kTestClock =
+        kMsmClkRcgs[msm8x53::MsmClkIndex(msm8x53::kBlsp1Uart2AppsClkSrc)];
+    const RcgFrequencyTable kTestFrequency =
+        kTestClock.Table()[0];
+    cc_regs[kTestClock.MReg()].ExpectWrite(kTestFrequency.m());
+    cc_regs[kTestClock.NReg()].ExpectWrite(kTestFrequency.n());
+    cc_regs[kTestClock.DReg()].ExpectWrite(kTestFrequency.d());
+    cc_regs[kTestClock.CmdReg()].ExpectWrite(0x1);
+
+    // Invalid clock ID and bad rate.
+    zx_status_t st = clk.ClockImplRequestRate(0, 0);
+    EXPECT_NE(st, ZX_OK);
+
+    // Set a good clock and a bad rate.
+    constexpr uint64_t kBadClkRate = 1;
+    st = clk.ClockImplRequestRate(msm8x53::kBlsp1Uart2AppsClkSrc, kBadClkRate);
+    EXPECT_NE(st, ZX_OK);
+
+    // Try setting a clock that exists.
+    const uint64_t kGoodClkRate = kTestFrequency.rate();
+    st = clk.ClockImplRequestRate(msm8x53::kBlsp1Uart2AppsClkSrc, kGoodClkRate);
+    EXPECT_OK(st);
+
+    cc_regs.VerifyAll();
+}
+
+// Test Half Integer Divider path.
+TEST(ClkTest8x53, TestSetRcgHid) {
+    auto cc_regs_arr = std::make_unique<ddk_mock::MockMmioReg[]>(msm8x53::kCcSize);
+    ddk_mock::MockMmioRegRegion cc_regs(cc_regs_arr.get(), sizeof(uint32_t), msm8x53::kCcSize);
+    Msm8x53ClkTest clk(cc_regs);
+
+    const MsmClkRcg& kTestClock =
+        kMsmClkRcgs[msm8x53::MsmClkIndex(msm8x53::kCsi0pClkSrc)];
+    const RcgFrequencyTable kTestFrequency =
+        kTestClock.Table()[0];
+    cc_regs[msm8x53::kCsi0pCmdRcgr + 0x04].ExpectWrite(kTestFrequency.predev_parent());
+    cc_regs[msm8x53::kCsi0pCmdRcgr + 0x00].ExpectWrite(0x1);
+
+    // Set a good clock and a bad rate.
+    constexpr uint64_t kBadClkRate = 1;
+    zx_status_t st = clk.ClockImplRequestRate(msm8x53::kCsi0pClkSrc, kBadClkRate);
+    EXPECT_NE(st, ZX_OK);
+
+    // Try setting a clock that exists.
+    const uint64_t kGoodClkRate = kTestFrequency.rate();
+    st = clk.ClockImplRequestRate(msm8x53::kCsi0pClkSrc, kGoodClkRate);
+    EXPECT_OK(st);
+
+    cc_regs.VerifyAll();
+}
+
+TEST(ClkTest8x53, TestRcgEnableDisabe) {
+    auto cc_regs_arr = std::make_unique<ddk_mock::MockMmioReg[]>(msm8x53::kCcSize);
+    ddk_mock::MockMmioRegRegion cc_regs(cc_regs_arr.get(), sizeof(uint32_t), msm8x53::kCcSize);
+    Msm8x53ClkTest clk(cc_regs);
+
+    const uint32_t kTestClkId = msm8x53::kBlsp1Qup3SpiAppsClkSrc;
+    const MsmClkRcg& kTestClock =
+        kMsmClkRcgs[msm8x53::MsmClkIndex(kTestClkId)];
+    const RcgFrequencyTable kTestFrequency =
+        kTestClock.Table()[0];
+
+    // You are prohibited from enabling an RCG before setting the rate.
+    zx_status_t st = clk.ClockImplEnable(kTestClkId);
+    EXPECT_NE(st, ZX_OK);
+
+    // Okay, set a frequency and try again.
+    st = clk.ClockImplRequestRate(kTestClkId, kTestFrequency.rate());
+    EXPECT_OK(st);
+
+    // Try enabling the RCG again and make sure it works.
+    st = clk.ClockImplEnable(kTestClkId);
+    EXPECT_OK(st);
+
+    cc_regs[kTestClock.CmdReg()].ExpectWrite(((1 << 1)));
+}
+
+TEST(ClkTest8x53, TestGateClkEnableDisable) {
+    zx_status_t st;
+    auto cc_regs_arr = std::make_unique<ddk_mock::MockMmioReg[]>(msm8x53::kCcSize);
+    ddk_mock::MockMmioRegRegion cc_regs(cc_regs_arr.get(), sizeof(uint32_t), msm8x53::kCcSize);
+    Msm8x53ClkTest clk(cc_regs);
+
+    constexpr uint32_t kTestClkId = msm8x53::kUsb3PipeClk;
+
+    st = clk.ClockImplEnable(kTestClkId);
+    EXPECT_OK(st);
+
+    st = clk.ClockImplDisable(kTestClkId);
+    EXPECT_OK(st);
+}
+
+TEST(ClkTest8x53, TestBranchClkEnableDisable) {
+    zx_status_t st;
+    auto cc_regs_arr = std::make_unique<ddk_mock::MockMmioReg[]>(msm8x53::kCcSize);
+    ddk_mock::MockMmioRegRegion cc_regs(cc_regs_arr.get(), sizeof(uint32_t), msm8x53::kCcSize);
+    Msm8x53ClkTest clk(cc_regs);
+
+    constexpr uint32_t kTestClkId = msm8x53::kBlsp2Qup3I2cAppsClk;
+    constexpr uint32_t kTestClkIdx = msm8x53::MsmClkIndex(kTestClkId);
+    const struct clk::msm_clk_branch& branch = kMsmClkBranches[kTestClkIdx];
+
+    cc_regs[branch.reg].ExpectWrite(kBranchEnable).ExpectRead(0x0);
+    st = clk.ClockImplEnable(kTestClkId);
+    EXPECT_OK(st);
+
+    cc_regs[branch.reg].ExpectRead(0x0).ExpectWrite(0x0).ExpectRead(0x80000000);
+    st = clk.ClockImplDisable(kTestClkId);
+    EXPECT_OK(st);
+
+    cc_regs.VerifyAll();
+}
+
+TEST(ClkTest8x53, TestVoterClkEnableDisable) {
+    zx_status_t st;
+    auto cc_regs_arr = std::make_unique<ddk_mock::MockMmioReg[]>(msm8x53::kCcSize);
+    ddk_mock::MockMmioRegRegion cc_regs(cc_regs_arr.get(), sizeof(uint32_t), msm8x53::kCcSize);
+    Msm8x53ClkTest clk(cc_regs);
+
+    constexpr uint32_t kTestClkId = msm8x53::kBlsp2AhbClk;
+
+    st = clk.ClockImplEnable(kTestClkId);
+    EXPECT_OK(st);
+
+    st = clk.ClockImplDisable(kTestClkId);
+    EXPECT_OK(st);
+}
+
+} // namespace clk
diff --git a/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk.cpp b/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk.cpp
index c753b4e4cb5ca9ede52e5e74131545168e0918a6..1c94a6dab11364717ee0888a1d70bdf26a3c595c 100644
--- a/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk.cpp
+++ b/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk.cpp
@@ -16,7 +16,6 @@
 #include <fbl/unique_ptr.h>
 #include <fuchsia/hardware/clock/c/fidl.h>
 #include <hwreg/bitfields.h>
-#include <soc/msm8x53/msm8x53-clock.h>
 
 #include <ddktl/protocol/platform/bus.h>
 
@@ -24,253 +23,33 @@
 
 namespace clk {
 
-typedef struct msm_clk_gate {
-    uint32_t reg;
-    uint32_t bit;
-    uint32_t delay_us;
-} msm_clk_gate_t;
-
-struct msm_clk_branch {
-    uint32_t reg;
-};
-
-struct msm_clk_voter {
-    uint32_t cbcr_reg;
-    uint32_t vote_reg;
-    uint32_t bit;
-};
-
 namespace {
 
-const char kMsmClkName[] = "msm-clk";
-
-constexpr msm_clk_gate_t kMsmClkGates[] = {
-    [msm8x53::MsmClkIndex(msm8x53::kQUsbRefClk)] = {.reg = 0x41030, .bit = 0, .delay_us = 0},
-    [msm8x53::MsmClkIndex(msm8x53::kUsbSSRefClk)] = {.reg = 0x5e07c, .bit = 0, .delay_us = 0},
-    [msm8x53::MsmClkIndex(msm8x53::kUsb3PipeClk)] = {.reg = 0x5e040, .bit = 0, .delay_us = 50},
-};
-
-constexpr uint32_t kBranchEnable = (0x1u << 0);
-constexpr struct msm_clk_branch kMsmClkBranches[] = {
-    [msm8x53::MsmClkIndex(msm8x53::kApc0DroopDetectorGpll0Clk)] = {
-        .reg = msm8x53::kApc0VoltageDroopDetectorGpll0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kApc1DroopDetectorGpll0Clk)] = {
-        .reg = msm8x53::kApc1VoltageDroopDetectorGpll0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup1I2cAppsClk)] = {.reg = msm8x53::kBlsp1Qup1I2cAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup1SpiAppsClk)] = {.reg = msm8x53::kBlsp1Qup1SpiAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup2I2cAppsClk)] = {.reg = msm8x53::kBlsp1Qup2I2cAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup2SpiAppsClk)] = {.reg = msm8x53::kBlsp1Qup2SpiAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup3I2cAppsClk)] = {.reg = msm8x53::kBlsp1Qup3I2cAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup3SpiAppsClk)] = {.reg = msm8x53::kBlsp1Qup3SpiAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup4I2cAppsClk)] = {.reg = msm8x53::kBlsp1Qup4I2cAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Qup4SpiAppsClk)] = {.reg = msm8x53::kBlsp1Qup4SpiAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Uart1AppsClk)] = {.reg = msm8x53::kBlsp1Uart1AppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1Uart2AppsClk)] = {.reg = msm8x53::kBlsp1Uart2AppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup1I2cAppsClk)] = {.reg = msm8x53::kBlsp2Qup1I2cAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup1SpiAppsClk)] = {.reg = msm8x53::kBlsp2Qup1SpiAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup2I2cAppsClk)] = {.reg = msm8x53::kBlsp2Qup2I2cAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup2SpiAppsClk)] = {.reg = msm8x53::kBlsp2Qup2SpiAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup3I2cAppsClk)] = {.reg = msm8x53::kBlsp2Qup3I2cAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup3SpiAppsClk)] = {.reg = msm8x53::kBlsp2Qup3SpiAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup4I2cAppsClk)] = {.reg = msm8x53::kBlsp2Qup4I2cAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Qup4SpiAppsClk)] = {.reg = msm8x53::kBlsp2Qup4SpiAppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Uart1AppsClk)] = {.reg = msm8x53::kBlsp2Uart1AppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2Uart2AppsClk)] = {.reg = msm8x53::kBlsp2Uart2AppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBimcGpuClk)] = {.reg = msm8x53::kBimcGpuCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCciAhbClk)] = {.reg = msm8x53::kCamssCciAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCciClk)] = {.reg = msm8x53::kCamssCciCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCppAhbClk)] = {.reg = msm8x53::kCamssCppAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCppAxiClk)] = {.reg = msm8x53::kCamssCppAxiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCppClk)] = {.reg = msm8x53::kCamssCppCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0AhbClk)] = {.reg = msm8x53::kCamssCsi0AhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0Clk)] = {.reg = msm8x53::kCamssCsi0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0Csiphy3pClk)] = {
-        .reg = msm8x53::kCamssCsi0Csiphy3pCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0phyClk)] = {.reg = msm8x53::kCamssCsi0phyCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0pixClk)] = {.reg = msm8x53::kCamssCsi0pixCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0rdiClk)] = {.reg = msm8x53::kCamssCsi0rdiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1AhbClk)] = {.reg = msm8x53::kCamssCsi1AhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1Clk)] = {.reg = msm8x53::kCamssCsi1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1Csiphy3pClk)] = {
-        .reg = msm8x53::kCamssCsi1Csiphy3pCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1phyClk)] = {.reg = msm8x53::kCamssCsi1phyCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1pixClk)] = {.reg = msm8x53::kCamssCsi1pixCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1rdiClk)] = {.reg = msm8x53::kCamssCsi1rdiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2AhbClk)] = {.reg = msm8x53::kCamssCsi2AhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2Clk)] = {.reg = msm8x53::kCamssCsi2Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2Csiphy3pClk)] = {
-        .reg = msm8x53::kCamssCsi2Csiphy3pCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2phyClk)] = {.reg = msm8x53::kCamssCsi2phyCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2pixClk)] = {.reg = msm8x53::kCamssCsi2pixCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2rdiClk)] = {.reg = msm8x53::kCamssCsi2rdiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsiVfe0Clk)] = {.reg = msm8x53::kCamssCsiVfe0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsiVfe1Clk)] = {.reg = msm8x53::kCamssCsiVfe1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssGp0Clk)] = {.reg = msm8x53::kCamssGp0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssGp1Clk)] = {.reg = msm8x53::kCamssGp1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssIspifAhbClk)] = {.reg = msm8x53::kCamssIspifAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssJpeg0Clk)] = {.reg = msm8x53::kCamssJpeg0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssJpegAhbClk)] = {.reg = msm8x53::kCamssJpegAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssJpegAxiClk)] = {.reg = msm8x53::kCamssJpegAxiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssMclk0Clk)] = {.reg = msm8x53::kCamssMclk0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssMclk1Clk)] = {.reg = msm8x53::kCamssMclk1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssMclk2Clk)] = {.reg = msm8x53::kCamssMclk2Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssMclk3Clk)] = {.reg = msm8x53::kCamssMclk3Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssMicroAhbClk)] = {.reg = msm8x53::kCamssMicroAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi0phytimerClk)] = {
-        .reg = msm8x53::kCamssCsi0phytimerCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi1phytimerClk)] = {
-        .reg = msm8x53::kCamssCsi1phytimerCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssCsi2phytimerClk)] = {
-        .reg = msm8x53::kCamssCsi2phytimerCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssAhbClk)] = {.reg = msm8x53::kCamssAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssTopAhbClk)] = {.reg = msm8x53::kCamssTopAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssVfe0Clk)] = {.reg = msm8x53::kCamssVfe0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssVfeAhbClk)] = {.reg = msm8x53::kCamssVfeAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssVfeAxiClk)] = {.reg = msm8x53::kCamssVfeAxiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssVfe1AhbClk)] = {.reg = msm8x53::kCamssVfe1AhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssVfe1AxiClk)] = {.reg = msm8x53::kCamssVfe1AxiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kCamssVfe1Clk)] = {.reg = msm8x53::kCamssVfe1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kDccClk)] = {.reg = msm8x53::kDccCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kGp1Clk)] = {.reg = msm8x53::kGp1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kGp2Clk)] = {.reg = msm8x53::kGp2Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kGp3Clk)] = {.reg = msm8x53::kGp3Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssAhbClk)] = {.reg = msm8x53::kMdssAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssAxiClk)] = {.reg = msm8x53::kMdssAxiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssByte0Clk)] = {.reg = msm8x53::kMdssByte0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssByte1Clk)] = {.reg = msm8x53::kMdssByte1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssEsc0Clk)] = {.reg = msm8x53::kMdssEsc0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssEsc1Clk)] = {.reg = msm8x53::kMdssEsc1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssMdpClk)] = {.reg = msm8x53::kMdssMdpCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssPclk0Clk)] = {.reg = msm8x53::kMdssPclk0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssPclk1Clk)] = {.reg = msm8x53::kMdssPclk1Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMdssVsyncClk)] = {.reg = msm8x53::kMdssVsyncCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMssCfgAhbClk)] = {.reg = msm8x53::kMssCfgAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kMssQ6BimcAxiClk)] = {.reg = msm8x53::kMssQ6BimcAxiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kBimcGfxClk)] = {.reg = msm8x53::kBimcGfxCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kOxiliAhbClk)] = {.reg = msm8x53::kOxiliAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kOxiliAonClk)] = {.reg = msm8x53::kOxiliAonCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kOxiliGfx3dClk)] = {.reg = msm8x53::kOxiliGfx3dCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kOxiliTimerClk)] = {.reg = msm8x53::kOxiliTimerCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kPcnocUsb3AxiClk)] = {.reg = msm8x53::kPcnocUsb3AxiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kPdm2Clk)] = {.reg = msm8x53::kPdm2Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kPdmAhbClk)] = {.reg = msm8x53::kPdmAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kRbcprGfxClk)] = {.reg = msm8x53::kRbcprGfxCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kSdcc1AhbClk)] = {.reg = msm8x53::kSdcc1AhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kSdcc1AppsClk)] = {.reg = msm8x53::kSdcc1AppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kSdcc1IceCoreClk)] = {.reg = msm8x53::kSdcc1IceCoreCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kSdcc2AhbClk)] = {.reg = msm8x53::kSdcc2AhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kSdcc2AppsClk)] = {.reg = msm8x53::kSdcc2AppsCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kUsb30MasterClk)] = {.reg = msm8x53::kUsb30MasterCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kUsb30MockUtmiClk)] = {.reg = msm8x53::kUsb30MockUtmiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kUsb30SleepClk)] = {.reg = msm8x53::kUsb30SleepCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kUsb3AuxClk)] = {.reg = msm8x53::kUsb3AuxCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kUsbPhyCfgAhbClk)] = {.reg = msm8x53::kUsbPhyCfgAhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kVenus0AhbClk)] = {.reg = msm8x53::kVenus0AhbCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kVenus0AxiClk)] = {.reg = msm8x53::kVenus0AxiCbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kVenus0Core0Vcodec0Clk)] = {
-        .reg = msm8x53::kVenus0Core0Vcodec0Cbcr},
-    [msm8x53::MsmClkIndex(msm8x53::kVenus0Vcodec0Clk)] = {.reg = msm8x53::kVenus0Vcodec0Cbcr},
-};
-
-constexpr struct msm_clk_voter kMsmClkVoters[] = {
-    [msm8x53::MsmClkIndex(msm8x53::kApssAhbClk)] = {
-        .cbcr_reg = msm8x53::kApssAhbCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 14)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kApssAxiClk)] = {
-        .cbcr_reg = msm8x53::kApssAxiCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 13)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp1AhbClk)] = {
-        .cbcr_reg = msm8x53::kBlsp1AhbCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 10)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kBlsp2AhbClk)] = {
-        .cbcr_reg = msm8x53::kBlsp2AhbCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 20)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kBootRomAhbClk)] = {
-        .cbcr_reg = msm8x53::kBootRomAhbCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 7)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kCryptoAhbClk)] = {
-        .cbcr_reg = msm8x53::kCryptoAhbCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 0)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kCryptoAxiClk)] = {
-        .cbcr_reg = msm8x53::kCryptoAxiCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 1)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kCryptoClk)] = {
-        .cbcr_reg = msm8x53::kCryptoCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 2)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kQdssDapClk)] = {
-        .cbcr_reg = msm8x53::kQdssDapCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 11)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kPrngAhbClk)] = {
-        .cbcr_reg = msm8x53::kPrngAhbCbcr,
-        .vote_reg = msm8x53::kApcsClockBranchEnaVote,
-        .bit = (1 << 8)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kApssTcuAsyncClk)] = {
-        .cbcr_reg = msm8x53::kApssTcuAsyncCbcr,
-        .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote,
-        .bit = (1 << 1)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kCppTbuClk)] = {
-        .cbcr_reg = msm8x53::kCppTbuCbcr,
-        .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote,
-        .bit = (1 << 14)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kJpegTbuClk)] = {
-        .cbcr_reg = msm8x53::kJpegTbuCbcr,
-        .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote,
-        .bit = (1 << 10)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kMdpTbuClk)] = {
-        .cbcr_reg = msm8x53::kMdpTbuCbcr,
-        .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote,
-        .bit = (1 << 4)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kSmmuCfgClk)] = {
-        .cbcr_reg = msm8x53::kSmmuCfgCbcr,
-        .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote,
-        .bit = (1 << 12)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kVenusTbuClk)] = {
-        .cbcr_reg = msm8x53::kVenusTbuCbcr,
-        .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote,
-        .bit = (1 << 5)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kVfe1TbuClk)] = {
-        .cbcr_reg = msm8x53::kVfe1TbuCbcr,
-        .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote,
-        .bit = (1 << 17)
-    },
-    [msm8x53::MsmClkIndex(msm8x53::kVfeTbuClk)] = {
-        .cbcr_reg = msm8x53::kVfeTbuCbcr,
-        .vote_reg = msm8x53::kApcsSmmuClockBranchEnaVote,
-        .bit = (1 << 9)
-    },
-};
+constexpr char kMsmClkName[] = "msm-clk";
+constexpr uint32_t kRcgUpdateTimeoutUsec = 500;
+constexpr uint64_t kRcgRateUnset = 0;
+constexpr uint32_t kCfgRcgrDivMask = (0x1f << 0);
+constexpr uint32_t kCfgRcgrSrcSelMask = (0x7 << 8);
 
 } // namespace
 
 zx_status_t Msm8x53Clk::Create(void* ctx, zx_device_t* parent) {
     zx_status_t status;
 
-    std::unique_ptr<Msm8x53Clk> device(new Msm8x53Clk(parent));
+    ddk::PDev pdev(parent);
+    if (!pdev.is_valid()) {
+        zxlogf(ERROR, "msm-clk: failed to get pdev protocol\n");
+        return ZX_ERR_NO_RESOURCES;
+    }
+
+    std::optional<ddk::MmioBuffer> mmio;
+    status = pdev.MapMmio(0, &mmio);
+    if (status != ZX_OK) {
+        zxlogf(ERROR, "msm-clk: failed to map cc_base mmio, st = %d\n", status);
+        return status;
+    }
+
+    std::unique_ptr<Msm8x53Clk> device(new Msm8x53Clk(parent, *std::move(mmio)));
 
     status = device->Init();
     if (status != ZX_OK) {
@@ -291,19 +70,12 @@ zx_status_t Msm8x53Clk::Create(void* ctx, zx_device_t* parent) {
 }
 
 zx_status_t Msm8x53Clk::Init() {
-    ddk::PDev pdev(parent());
-    if (!pdev.is_valid()) {
-        zxlogf(ERROR, "msm-clk: failed to get pdev protocol\n");
-        return ZX_ERR_NO_RESOURCES;
-    }
-
-    zx_status_t status = pdev.MapMmio(0, &mmio_);
-    if (status != ZX_OK) {
-        zxlogf(ERROR, "msm-clk: failed to map cc_base mmio, st = %d\n", status);
-        return status;
+    fbl::AutoLock lock(&rcg_rates_lock_);
+    for (size_t i = 0; i < msm8x53::kRcgClkCount; i++) {
+        rcg_rates_[i] = kRcgRateUnset;
     }
 
-    status = RegisterClockProtocol();
+    zx_status_t status = RegisterClockProtocol();
     if (status != ZX_OK) {
         zxlogf(ERROR, "msm-clk: failed to register clock impl protocol, st = %d\n", status);
         return status;
@@ -324,6 +96,8 @@ zx_status_t Msm8x53Clk::ClockImplEnable(uint32_t index) {
         return BranchClockEnable(clock_id);
     case msm8x53::msm_clk_type::kVoter:
         return VoterClockEnable(clock_id);
+    case msm8x53::msm_clk_type::kRcg:
+        return RcgClockEnable(clock_id);
     }
 
     // Unimplemented clock type?
@@ -342,6 +116,8 @@ zx_status_t Msm8x53Clk::ClockImplDisable(uint32_t index) {
         return BranchClockDisable(clock_id);
     case msm8x53::msm_clk_type::kVoter:
         return VoterClockDisable(clock_id);
+    case msm8x53::msm_clk_type::kRcg:
+        return RcgClockDisable(clock_id);
     }
 
     // Unimplemented clock type?
@@ -349,11 +125,22 @@ zx_status_t Msm8x53Clk::ClockImplDisable(uint32_t index) {
 }
 
 zx_status_t Msm8x53Clk::ClockImplRequestRate(uint32_t id, uint64_t hz) {
+    const uint32_t index = msm8x53::MsmClkIndex(id);
+    const msm8x53::msm_clk_type clock_type = msm8x53::MsmClkType(id);
+
+    switch (clock_type) {
+    case msm8x53::msm_clk_type::kRcg: {
+        fbl::AutoLock rcg_rates_lock(&rcg_rates_lock_);
+        return RcgClockSetRate(index, hz);
+    }
+    default:
+        zxlogf(WARN, "msm_clk: unsupported clock type: %u\n", (uint16_t)clock_type);
+    }
+
     return ZX_ERR_NOT_SUPPORTED;
 }
 
-zx_status_t Msm8x53Clk::AwaitBranchClock(AwaitBranchClockStatus status,
-                                         const uint32_t cbcr_reg) {
+zx_status_t Msm8x53Clk::AwaitBranchClock(Toggle status, const uint32_t cbcr_reg) {
     // In case the status check register and the clock control register cross
     // a boundary.
     hw_mb();
@@ -367,15 +154,15 @@ zx_status_t Msm8x53Clk::AwaitBranchClock(AwaitBranchClockStatus status,
 
     constexpr uint32_t kMaxAttempts = 500;
     for (uint32_t attempts = 0; attempts < kMaxAttempts; attempts++) {
-        const uint32_t val = mmio_->Read32(cbcr_reg) & kReadyMask;
+        const uint32_t val = mmio_.Read32(cbcr_reg) & kReadyMask;
 
         switch (status) {
-        case AwaitBranchClockStatus::Enabled:
+        case Toggle::Enabled:
             if ((val == kBranchEnableVal) || (val == kBranchNocFsmEnableVal)) {
                 return ZX_OK;
             }
             break;
-        case AwaitBranchClockStatus::Disabled:
+        case Toggle::Disabled:
             if (val == kBranchDisableVal) {
                 return ZX_OK;
             }
@@ -396,10 +183,10 @@ zx_status_t Msm8x53Clk::VoterClockEnable(uint32_t index) {
     const struct clk::msm_clk_voter& clk = kMsmClkVoters[index];
 
     lock_.Acquire();
-    mmio_->SetBits32(clk.bit, clk.vote_reg);
+    mmio_.SetBits32(clk.bit, clk.vote_reg);
     lock_.Release();
 
-    return AwaitBranchClock(AwaitBranchClockStatus::Enabled, clk.cbcr_reg);
+    return AwaitBranchClock(Toggle::Enabled, clk.cbcr_reg);
 }
 
 zx_status_t Msm8x53Clk::VoterClockDisable(uint32_t index) {
@@ -410,7 +197,7 @@ zx_status_t Msm8x53Clk::VoterClockDisable(uint32_t index) {
     const struct clk::msm_clk_voter& clk = kMsmClkVoters[index];
 
     lock_.Acquire();
-    mmio_->ClearBits32(clk.bit, clk.vote_reg);
+    mmio_.ClearBits32(clk.bit, clk.vote_reg);
     lock_.Release();
 
     return ZX_OK;
@@ -424,10 +211,10 @@ zx_status_t Msm8x53Clk::BranchClockEnable(uint32_t index) {
     const struct clk::msm_clk_branch& clk = kMsmClkBranches[index];
 
     lock_.Acquire();
-    mmio_->SetBits32(kBranchEnable, clk.reg);
+    mmio_.SetBits32(kBranchEnable, clk.reg);
     lock_.Release();
 
-    return AwaitBranchClock(AwaitBranchClockStatus::Enabled, clk.reg);
+    return AwaitBranchClock(Toggle::Enabled, clk.reg);
 }
 
 zx_status_t Msm8x53Clk::BranchClockDisable(uint32_t index) {
@@ -438,10 +225,10 @@ zx_status_t Msm8x53Clk::BranchClockDisable(uint32_t index) {
     const struct msm_clk_branch& clk = kMsmClkBranches[index];
 
     lock_.Acquire();
-    mmio_->ClearBits32(kBranchEnable, clk.reg);
+    mmio_.ClearBits32(kBranchEnable, clk.reg);
     lock_.Release();
 
-    return AwaitBranchClock(AwaitBranchClockStatus::Disabled, clk.reg);
+    return AwaitBranchClock(Toggle::Disabled, clk.reg);
 }
 
 zx_status_t Msm8x53Clk::GateClockEnable(uint32_t index) {
@@ -452,7 +239,7 @@ zx_status_t Msm8x53Clk::GateClockEnable(uint32_t index) {
     const msm_clk_gate_t& clk = kMsmClkGates[index];
 
     lock_.Acquire();
-    mmio_->SetBits32(clk.bit, clk.reg);
+    mmio_.SetBits32((1u << clk.bit), clk.reg);
     lock_.Release();
 
     if (clk.delay_us) {
@@ -469,7 +256,7 @@ zx_status_t Msm8x53Clk::GateClockDisable(uint32_t index) {
     const msm_clk_gate_t& clk = kMsmClkGates[index];
 
     lock_.Acquire();
-    mmio_->ClearBits32(clk.bit, clk.reg);
+    mmio_.ClearBits32(clk.bit, clk.reg);
     lock_.Release();
 
     if (clk.delay_us) {
@@ -479,12 +266,193 @@ zx_status_t Msm8x53Clk::GateClockDisable(uint32_t index) {
     return ZX_OK;
 }
 
+zx_status_t Msm8x53Clk::RcgClockEnable(uint32_t index) {
+    if (unlikely(index > countof(kMsmClkRcgs))) {
+        return ZX_ERR_OUT_OF_RANGE;
+    }
+
+    const MsmClkRcg& clk = kMsmClkRcgs[index];
+
+    // Check to see if frequency has been set.
+    fbl::AutoLock lock(&rcg_rates_lock_);
+    if (rcg_rates_[index] == kRcgRateUnset) {
+        zxlogf(ERROR, "Attempted to enable RCG %u before setting rate\n", index);
+        return ZX_ERR_BAD_STATE;
+    }
+
+    zx_status_t st;
+
+    st = ToggleRcgForceEnable(clk.CmdReg(), Toggle::Enabled);
+    if (st != ZX_OK) {
+        return st;
+    }
+
+    st = RcgClockSetRate(index, rcg_rates_[index]);
+    if (st != ZX_OK) {
+        return st;
+    }
+
+    st = ToggleRcgForceEnable(clk.CmdReg(), Toggle::Disabled);
+    if (st != ZX_OK) {
+        return st;
+    }
+
+    return st;
+}
+
+zx_status_t Msm8x53Clk::RcgClockDisable(uint32_t index) {
+    // This is a NOP for all clocks that we support.
+    // It only needs to be implemented for clocks with non-local children.
+    return ZX_OK;
+}
+
+zx_status_t Msm8x53Clk::RcgClockSetRate(uint32_t index, uint64_t rate) {
+    if (unlikely(index >= countof(kMsmClkRcgs))) {
+        return ZX_ERR_OUT_OF_RANGE;
+    }
+
+    const MsmClkRcg& clk = kMsmClkRcgs[index];
+
+    // Clocks with non-local children or nonlocal control timeouts are
+    // currently unimplemented.
+    // Clocks with source frequencies that are not fixed are also currently
+    // unimplemented.
+    if (clk.Unsupported()) {
+        zxlogf(ERROR, "Attempted to set rate for clock %u which is currently "
+                      "unimplemented\n",
+               index);
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+
+    // Search for the requested frequency in the clock's frequency table.
+    const RcgFrequencyTable* table = nullptr;
+    for (size_t i = 0; i < clk.TableCount(); i++) {
+        if (rate == clk.Table()[i].rate()) {
+            table = &clk.Table()[i];
+            break;
+        }
+    }
+
+    if (table == nullptr) {
+        // This clock frequency is not supported.
+        zxlogf(WARN, "unsupported clock frequency, clk = %u, rate = %lu\n",
+               index, rate);
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+
+    { // Nested scope for scoped locking
+        fbl::AutoLock lock(&lock_);
+
+        switch (clk.Type()) {
+        case RcgDividerType::HalfInteger:
+            RcgSetRateHalfInteger(clk, table);
+            break;
+        case RcgDividerType::Mnd:
+            RcgSetRateMnd(clk, table);
+            break;
+        }
+    }
+
+    // Update the frequency that we have listed in the RCG table.
+    rcg_rates_[index] = rate;
+
+    return ZX_OK;
+}
+
+zx_status_t Msm8x53Clk::LatchRcgConfig(const MsmClkRcg& clk) {
+    // Whack the config update bit and wait for it to stabilize.
+    constexpr uint32_t kCmdRcgrConfigUpdateBit = (0x1 << 0);
+    mmio_.SetBits32(kCmdRcgrConfigUpdateBit, clk.CmdReg());
+
+    constexpr uint32_t kMaxAttempts = 500;
+    for (uint32_t i = 0; i < kMaxAttempts; i++) {
+        const uint32_t cmd_reg = mmio_.Read32(clk.CmdReg());
+
+        if ((cmd_reg & kCmdRcgrConfigUpdateBit) == 0) {
+            return ZX_OK;
+        }
+
+        zx_nanosleep(zx_deadline_after(ZX_USEC(1)));
+    }
+
+    zxlogf(WARN, "Failed to latch RCG config\n");
+    return ZX_ERR_TIMED_OUT;
+}
+
+zx_status_t Msm8x53Clk::RcgSetRateHalfInteger(const MsmClkRcg& clk, const RcgFrequencyTable* table) {
+    uint32_t val;
+
+    val = mmio_.Read32(clk.CfgReg());
+    val &= ~(kCfgRcgrDivMask | kCfgRcgrSrcSelMask);
+    val |= table->predev_parent();
+    mmio_.Write32(val, clk.CfgReg());
+
+    return LatchRcgConfig(clk);
+}
+
+zx_status_t Msm8x53Clk::RcgSetRateMnd(const MsmClkRcg& clk, const RcgFrequencyTable* table) {
+    uint32_t cfg = mmio_.Read32(clk.CfgReg());
+
+    constexpr uint32_t kMndModeMask = (0x3 << 12);
+    constexpr uint32_t kMndDualEdgeMode = (0x2 << 12);
+
+    mmio_.Write32(table->m(), clk.MReg());
+    mmio_.Write32(table->n(), clk.NReg());
+    mmio_.Write32(table->d(), clk.DReg());
+
+    cfg = mmio_.Read32(clk.CfgReg());
+    cfg &= ~(kCfgRcgrDivMask | kCfgRcgrSrcSelMask);
+    cfg |= table->predev_parent();
+
+    cfg &= ~kMndModeMask;
+    if (table->n() != 0) {
+        cfg |= kMndDualEdgeMode;
+    }
+    mmio_.Write32(cfg, clk.CfgReg());
+
+    return LatchRcgConfig(clk);
+}
+
+zx_status_t Msm8x53Clk::ToggleRcgForceEnable(uint32_t rcgr_cmd_offset, Toggle toggle) {
+    constexpr uint32_t kRcgForceDisableDelayUSeconds = 100;
+    constexpr uint32_t kRcgRootEnableBit = (1 << 1);
+    zx_status_t result = ZX_OK;
+
+    switch (toggle) {
+    case Toggle::Enabled:
+        lock_.Acquire();
+        mmio_.SetBits32(kRcgRootEnableBit, rcgr_cmd_offset);
+        result = AwaitRcgEnableLocked(rcgr_cmd_offset);
+        lock_.Release();
+        break;
+    case Toggle::Disabled:
+        lock_.Acquire();
+        mmio_.ClearBits32(kRcgRootEnableBit, rcgr_cmd_offset);
+        lock_.Release();
+        zx_nanosleep(zx_deadline_after(ZX_USEC(kRcgForceDisableDelayUSeconds)));
+        break;
+    }
+    return result;
+}
+
+zx_status_t Msm8x53Clk::AwaitRcgEnableLocked(uint32_t rcgr_cmd_offset) {
+    for (uint32_t i = 0; i < kRcgUpdateTimeoutUsec; i++) {
+        auto rcg_ctrl = RcgClkCmd::Read(rcgr_cmd_offset).ReadFrom(&mmio_);
+
+        if (rcg_ctrl.root_status() == 0) {
+            return ZX_OK;
+        }
+
+        zx_nanosleep(zx_deadline_after(ZX_USEC(1)));
+    }
+
+    return ZX_ERR_TIMED_OUT;
+}
+
 zx_status_t Msm8x53Clk::Bind() {
     return ZX_OK;
 }
 void Msm8x53Clk::DdkUnbind() {
-    // Hazard! Always acquire locks in the order that they were defined in the
-    // header.
     fbl::AutoLock lock(&lock_);
 
     mmio_.reset();
diff --git a/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk.h b/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk.h
index 45363f5e084b2e867bc0a7e2373845b9610c0bdc..9d87fcb600da6fc24ac6338210ddc6e384b8fe1c 100644
--- a/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk.h
+++ b/zircon/system/dev/clk/msm8x53-clk/msm8x53-clk.h
@@ -9,10 +9,15 @@
 #include <fbl/mutex.h>
 #include <fuchsia/hardware/clock/c/fidl.h>
 #include <lib/mmio/mmio.h>
+#include <soc/msm8x53/msm8x53-clock.h>
 #include <zircon/thread_annotations.h>
 
 namespace clk {
 
+// Fwd declarations
+class RcgFrequencyTable;
+class MsmClkRcg;
+
 class Msm8x53Clk;
 using DeviceType = ddk::Device<Msm8x53Clk, ddk::Unbindable>;
 
@@ -33,33 +38,48 @@ public:
     void DdkUnbind();
     void DdkRelease();
 
-private:
-    Msm8x53Clk(zx_device_t* parent)
-        : DeviceType(parent) {}
+// Protected for tests.
+protected:
+    Msm8x53Clk(zx_device_t* parent, ddk::MmioBuffer mmio)
+        : DeviceType(parent)
+        , mmio_(std::move(mmio)) {}
 
     zx_status_t RegisterClockProtocol();
 
+    enum class Toggle {
+        Enabled,
+        Disabled
+    };
+
     // Gate Clocks
     zx_status_t GateClockEnable(uint32_t index);
     zx_status_t GateClockDisable(uint32_t index);
 
+    // RCG Clocks
+    zx_status_t RcgClockEnable(uint32_t index);
+    zx_status_t RcgClockDisable(uint32_t index);
+    zx_status_t RcgClockSetRate(uint32_t index, uint64_t hz) __TA_REQUIRES(rcg_rates_lock_);
+    zx_status_t ToggleRcgForceEnable(uint32_t rcgr_cmd_offset, Toggle toggle);
+    zx_status_t AwaitRcgEnableLocked(uint32_t rcgr_cmd_offset) __TA_REQUIRES(lock_);
+    zx_status_t RcgSetRateMnd(const MsmClkRcg& clk, const RcgFrequencyTable* table);
+    zx_status_t RcgSetRateHalfInteger(const MsmClkRcg& clk, const RcgFrequencyTable* table);
+    zx_status_t LatchRcgConfig(const MsmClkRcg& clk);
+
     // Branch Clocks
     zx_status_t BranchClockEnable(uint32_t index);
     zx_status_t BranchClockDisable(uint32_t index);
-    enum class AwaitBranchClockStatus {
-        Enabled,
-        Disabled
-    };
     // Wait for a change to a particular branch clock to take effect.
-    zx_status_t AwaitBranchClock(AwaitBranchClockStatus s,
-                                 const uint32_t cbcr_reg);
+    zx_status_t AwaitBranchClock(Toggle s, const uint32_t cbcr_reg);
 
     // Voter Clocks
     zx_status_t VoterClockEnable(uint32_t index);
     zx_status_t VoterClockDisable(uint32_t index);
 
-    fbl::Mutex lock_;       // Lock guards mmio_.
-    std::optional<ddk::MmioBuffer> mmio_;
+    fbl::Mutex lock_; // Lock guards mmio_.
+    ddk::MmioBuffer mmio_;
+
+    fbl::Mutex rcg_rates_lock_;
+    uint64_t rcg_rates_[msm8x53::kRcgClkCount];
 };
 
 } // namespace clk
diff --git a/zircon/system/dev/lib/msm8x53/include/soc/msm8x53/msm8x53-clock.h b/zircon/system/dev/lib/msm8x53/include/soc/msm8x53/msm8x53-clock.h
index bf3080e4a04f8622777590d18a18188160fb89b0..5774ceb909fe952e9f269ed89602d2b873e9ed19 100644
--- a/zircon/system/dev/lib/msm8x53/include/soc/msm8x53/msm8x53-clock.h
+++ b/zircon/system/dev/lib/msm8x53/include/soc/msm8x53/msm8x53-clock.h
@@ -31,7 +31,7 @@ public:
 };
 
 // Root clock gating config register.
-class RCG_CFG: public hwreg::RegisterBase<RCG_CFG, uint32_t> {
+class RCG_CFG : public hwreg::RegisterBase<RCG_CFG, uint32_t> {
 public:
     DEF_FIELD(12, 11, mode);
     DEF_FIELD(8, 6, src_sel);
@@ -65,9 +65,10 @@ static constexpr uint32_t kCcBase = 0x1800000;
 static constexpr uint32_t kCcSize = 0x80000;
 
 enum class msm_clk_type : uint16_t {
-    kGate,
+    kGate = 0,
     kBranch,
     kVoter,
+    kRcg
 };
 
 // Create a clock ID based on a type and an index
@@ -224,4 +225,73 @@ constexpr uint32_t kVenusTbuClk = MsmClkId(15, msm_clk_type::kVoter);
 constexpr uint32_t kVfe1TbuClk = MsmClkId(16, msm_clk_type::kVoter);
 constexpr uint32_t kVfeTbuClk = MsmClkId(17, msm_clk_type::kVoter);
 
+// MSM RCG Gates
+constexpr uint32_t kCamssTopAhbClkSrc = MsmClkId(0, msm_clk_type::kRcg);
+constexpr uint32_t kCsi0ClkSrc = MsmClkId(1, msm_clk_type::kRcg);
+constexpr uint32_t kApssAhbClkSrc = MsmClkId(2, msm_clk_type::kRcg);
+constexpr uint32_t kCsi1ClkSrc = MsmClkId(3, msm_clk_type::kRcg);
+constexpr uint32_t kCsi2ClkSrc = MsmClkId(4, msm_clk_type::kRcg);
+constexpr uint32_t kVfe0ClkSrc = MsmClkId(5, msm_clk_type::kRcg);
+constexpr uint32_t kGfx3dClkSrc = MsmClkId(6, msm_clk_type::kRcg);
+constexpr uint32_t kVcodec0ClkSrc = MsmClkId(7, msm_clk_type::kRcg);
+constexpr uint32_t kCppClkSrc = MsmClkId(8, msm_clk_type::kRcg);
+constexpr uint32_t kJpeg0ClkSrc = MsmClkId(9, msm_clk_type::kRcg);
+constexpr uint32_t kMdpClkSrc = MsmClkId(10, msm_clk_type::kRcg);
+constexpr uint32_t kPclk0ClkSrc = MsmClkId(11, msm_clk_type::kRcg);
+constexpr uint32_t kPclk1ClkSrc = MsmClkId(12, msm_clk_type::kRcg);
+constexpr uint32_t kUsb30MasterClkSrc = MsmClkId(13, msm_clk_type::kRcg);
+constexpr uint32_t kVfe1ClkSrc = MsmClkId(14, msm_clk_type::kRcg);
+constexpr uint32_t kApc0DroopDetectorClkSrc = MsmClkId(15, msm_clk_type::kRcg);
+constexpr uint32_t kApc1DroopDetectorClkSrc = MsmClkId(16, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Qup1I2cAppsClkSrc = MsmClkId(17, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Qup1SpiAppsClkSrc = MsmClkId(18, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Qup2I2cAppsClkSrc = MsmClkId(19, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Qup2SpiAppsClkSrc = MsmClkId(20, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Qup3I2cAppsClkSrc = MsmClkId(21, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Qup3SpiAppsClkSrc = MsmClkId(22, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Qup4I2cAppsClkSrc = MsmClkId(23, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Qup4SpiAppsClkSrc = MsmClkId(24, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Uart1AppsClkSrc = MsmClkId(25, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp1Uart2AppsClkSrc = MsmClkId(26, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Qup1I2cAppsClkSrc = MsmClkId(27, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Qup1SpiAppsClkSrc = MsmClkId(28, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Qup2I2cAppsClkSrc = MsmClkId(29, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Qup2SpiAppsClkSrc = MsmClkId(30, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Qup3I2cAppsClkSrc = MsmClkId(31, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Qup3SpiAppsClkSrc = MsmClkId(32, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Qup4I2cAppsClkSrc = MsmClkId(33, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Qup4SpiAppsClkSrc = MsmClkId(34, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Uart1AppsClkSrc = MsmClkId(35, msm_clk_type::kRcg);
+constexpr uint32_t kBlsp2Uart2AppsClkSrc = MsmClkId(36, msm_clk_type::kRcg);
+constexpr uint32_t kCciClkSrc = MsmClkId(37, msm_clk_type::kRcg);
+constexpr uint32_t kCsi0pClkSrc = MsmClkId(38, msm_clk_type::kRcg);
+constexpr uint32_t kCsi1pClkSrc = MsmClkId(39, msm_clk_type::kRcg);
+constexpr uint32_t kCsi2pClkSrc = MsmClkId(40, msm_clk_type::kRcg);
+constexpr uint32_t kCamssGp0ClkSrc = MsmClkId(41, msm_clk_type::kRcg);
+constexpr uint32_t kCamssGp1ClkSrc = MsmClkId(42, msm_clk_type::kRcg);
+constexpr uint32_t kMclk0ClkSrc = MsmClkId(43, msm_clk_type::kRcg);
+constexpr uint32_t kMclk1ClkSrc = MsmClkId(44, msm_clk_type::kRcg);
+constexpr uint32_t kMclk2ClkSrc = MsmClkId(45, msm_clk_type::kRcg);
+constexpr uint32_t kMclk3ClkSrc = MsmClkId(46, msm_clk_type::kRcg);
+constexpr uint32_t kCsi0phytimerClkSrc = MsmClkId(47, msm_clk_type::kRcg);
+constexpr uint32_t kCsi1phytimerClkSrc = MsmClkId(48, msm_clk_type::kRcg);
+constexpr uint32_t kCsi2phytimerClkSrc = MsmClkId(49, msm_clk_type::kRcg);
+constexpr uint32_t kCryptoClkSrc = MsmClkId(50, msm_clk_type::kRcg);
+constexpr uint32_t kGp1ClkSrc = MsmClkId(51, msm_clk_type::kRcg);
+constexpr uint32_t kGp2ClkSrc = MsmClkId(52, msm_clk_type::kRcg);
+constexpr uint32_t kGp3ClkSrc = MsmClkId(53, msm_clk_type::kRcg);
+constexpr uint32_t kByte0ClkSrc = MsmClkId(54, msm_clk_type::kRcg);
+constexpr uint32_t kByte1ClkSrc = MsmClkId(55, msm_clk_type::kRcg);
+constexpr uint32_t kEsc0ClkSrc = MsmClkId(56, msm_clk_type::kRcg);
+constexpr uint32_t kEsc1ClkSrc = MsmClkId(57, msm_clk_type::kRcg);
+constexpr uint32_t kVsyncClkSrc = MsmClkId(58, msm_clk_type::kRcg);
+constexpr uint32_t kPdm2ClkSrc = MsmClkId(59, msm_clk_type::kRcg);
+constexpr uint32_t kRbcprGfxClkSrc = MsmClkId(60, msm_clk_type::kRcg);
+constexpr uint32_t kSdcc1AppsClkSrc = MsmClkId(61, msm_clk_type::kRcg);
+constexpr uint32_t kSdcc1IceCoreClkSrc = MsmClkId(62, msm_clk_type::kRcg);
+constexpr uint32_t kSdcc2AppsClkSrc = MsmClkId(63, msm_clk_type::kRcg);
+constexpr uint32_t kUsb30MockUtmiClkSrc = MsmClkId(64, msm_clk_type::kRcg);
+constexpr uint32_t kUsb3AuxClkSrc = MsmClkId(65, msm_clk_type::kRcg);
+constexpr uint32_t kRcgClkCount = 66;
+
 } // namespace msm8x53
diff --git a/zircon/system/utest/BUILD.gn b/zircon/system/utest/BUILD.gn
index 31e1717cbe65cd2a892d8b3b80032eb9b97a2148..22d672875ad62fe1df386234a6d805e30d986f81 100644
--- a/zircon/system/utest/BUILD.gn
+++ b/zircon/system/utest/BUILD.gn
@@ -38,6 +38,7 @@ if (current_cpu != "") {
       "$zx/system/dev/nand/ram-nand:ram-nand-test",
       "$zx/system/dev/nand/skip-block:skip-block-test",
       "$zx/system/dev/power/msm8x53-power:msm8x53-power-test",
+      "$zx/system/dev/clk/msm8x53-clk:msm8x53-clk-test",
       "$zx/system/dev/sysmem/sysmem:sysmem-unittest",
       "$zx/system/dev/test/usb",
       "$zx/system/dev/thermal/mtk-thermal:mtk-thermal-test",