blob: c753b4e4cb5ca9ede52e5e74131545168e0918a6 [file] [log] [blame]
// 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 <ddk/binding.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/clockimpl.h>
#include <ddk/protocol/platform/bus.h>
#include <ddk/protocol/platform/device.h>
#include <ddktl/pdev.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#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>
#include "msm8x53-clk-regs.h"
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)
},
};
} // namespace
zx_status_t Msm8x53Clk::Create(void* ctx, zx_device_t* parent) {
zx_status_t status;
std::unique_ptr<Msm8x53Clk> device(new Msm8x53Clk(parent));
status = device->Init();
if (status != ZX_OK) {
zxlogf(ERROR, "msm-clk: failed to initialize, st = %d\n", status);
return status;
}
status = device->DdkAdd(kMsmClkName);
if (status != ZX_OK) {
zxlogf(ERROR, "msm-clk: DdkAdd failed, st = %d\n", status);
return status;
}
// Intentially leak, devmgr owns the memory now.
__UNUSED auto* unused = device.release();
return ZX_OK;
}
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;
}
status = RegisterClockProtocol();
if (status != ZX_OK) {
zxlogf(ERROR, "msm-clk: failed to register clock impl protocol, st = %d\n", status);
return status;
}
return ZX_OK;
}
zx_status_t Msm8x53Clk::ClockImplEnable(uint32_t index) {
// Extract the index and the type of the clock from the argument.
const uint32_t clock_id = msm8x53::MsmClkIndex(index);
const msm8x53::msm_clk_type clock_type = msm8x53::MsmClkType(index);
switch (clock_type) {
case msm8x53::msm_clk_type::kGate:
return GateClockEnable(clock_id);
case msm8x53::msm_clk_type::kBranch:
return BranchClockEnable(clock_id);
case msm8x53::msm_clk_type::kVoter:
return VoterClockEnable(clock_id);
}
// Unimplemented clock type?
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Msm8x53Clk::ClockImplDisable(uint32_t index) {
// Extract the index and the type of the clock from the argument.
const uint32_t clock_id = msm8x53::MsmClkIndex(index);
const msm8x53::msm_clk_type clock_type = msm8x53::MsmClkType(index);
switch (clock_type) {
case msm8x53::msm_clk_type::kGate:
return GateClockDisable(clock_id);
case msm8x53::msm_clk_type::kBranch:
return BranchClockDisable(clock_id);
case msm8x53::msm_clk_type::kVoter:
return VoterClockDisable(clock_id);
}
// Unimplemented clock type?
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Msm8x53Clk::ClockImplRequestRate(uint32_t id, uint64_t hz) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Msm8x53Clk::AwaitBranchClock(AwaitBranchClockStatus status,
const uint32_t cbcr_reg) {
// In case the status check register and the clock control register cross
// a boundary.
hw_mb();
// clang-format off
constexpr uint32_t kReadyMask = 0xf0000000;
constexpr uint32_t kBranchEnableVal = 0x0;
constexpr uint32_t kBranchDisableVal = 0x80000000;
constexpr uint32_t kBranchNocFsmEnableVal = 0x20000000;
// clang-format on
constexpr uint32_t kMaxAttempts = 500;
for (uint32_t attempts = 0; attempts < kMaxAttempts; attempts++) {
const uint32_t val = mmio_->Read32(cbcr_reg) & kReadyMask;
switch (status) {
case AwaitBranchClockStatus::Enabled:
if ((val == kBranchEnableVal) || (val == kBranchNocFsmEnableVal)) {
return ZX_OK;
}
break;
case AwaitBranchClockStatus::Disabled:
if (val == kBranchDisableVal) {
return ZX_OK;
}
break;
}
zx_nanosleep(zx_deadline_after(ZX_USEC(1)));
}
return ZX_ERR_TIMED_OUT;
}
zx_status_t Msm8x53Clk::VoterClockEnable(uint32_t index) {
if (unlikely(index >= countof(kMsmClkVoters))) {
return ZX_ERR_OUT_OF_RANGE;
}
const struct clk::msm_clk_voter& clk = kMsmClkVoters[index];
lock_.Acquire();
mmio_->SetBits32(clk.bit, clk.vote_reg);
lock_.Release();
return AwaitBranchClock(AwaitBranchClockStatus::Enabled, clk.cbcr_reg);
}
zx_status_t Msm8x53Clk::VoterClockDisable(uint32_t index) {
if (unlikely(index >= countof(kMsmClkVoters))) {
return ZX_ERR_OUT_OF_RANGE;
}
const struct clk::msm_clk_voter& clk = kMsmClkVoters[index];
lock_.Acquire();
mmio_->ClearBits32(clk.bit, clk.vote_reg);
lock_.Release();
return ZX_OK;
}
zx_status_t Msm8x53Clk::BranchClockEnable(uint32_t index) {
if (unlikely(index >= countof(kMsmClkBranches))) {
return ZX_ERR_OUT_OF_RANGE;
}
const struct clk::msm_clk_branch& clk = kMsmClkBranches[index];
lock_.Acquire();
mmio_->SetBits32(kBranchEnable, clk.reg);
lock_.Release();
return AwaitBranchClock(AwaitBranchClockStatus::Enabled, clk.reg);
}
zx_status_t Msm8x53Clk::BranchClockDisable(uint32_t index) {
if (unlikely(index >= countof(kMsmClkBranches))) {
return ZX_ERR_OUT_OF_RANGE;
}
const struct msm_clk_branch& clk = kMsmClkBranches[index];
lock_.Acquire();
mmio_->ClearBits32(kBranchEnable, clk.reg);
lock_.Release();
return AwaitBranchClock(AwaitBranchClockStatus::Disabled, clk.reg);
}
zx_status_t Msm8x53Clk::GateClockEnable(uint32_t index) {
if (unlikely(index >= countof(kMsmClkGates))) {
return ZX_ERR_OUT_OF_RANGE;
}
const msm_clk_gate_t& clk = kMsmClkGates[index];
lock_.Acquire();
mmio_->SetBits32(clk.bit, clk.reg);
lock_.Release();
if (clk.delay_us) {
zx_nanosleep(zx_deadline_after(ZX_USEC(clk.delay_us)));
}
return ZX_OK;
}
zx_status_t Msm8x53Clk::GateClockDisable(uint32_t index) {
if (unlikely(index > countof(kMsmClkGates))) {
return ZX_ERR_OUT_OF_RANGE;
}
const msm_clk_gate_t& clk = kMsmClkGates[index];
lock_.Acquire();
mmio_->ClearBits32(clk.bit, clk.reg);
lock_.Release();
if (clk.delay_us) {
zx_nanosleep(zx_deadline_after(ZX_USEC(clk.delay_us)));
}
return ZX_OK;
}
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();
DdkRemove();
}
void Msm8x53Clk::DdkRelease() {
delete this;
}
zx_status_t Msm8x53Clk::RegisterClockProtocol() {
zx_status_t st;
ddk::PBusProtocolClient pbus(parent());
if (!pbus.is_valid()) {
return ZX_ERR_NO_RESOURCES;
}
clock_impl_protocol_t clk_proto = {
.ops = &clock_impl_protocol_ops_,
.ctx = this,
};
st = pbus.RegisterProtocol(ZX_PROTOCOL_CLOCK_IMPL, &clk_proto, sizeof(clk_proto));
if (st != ZX_OK) {
zxlogf(ERROR, "msm-clk: pbus_register_protocol failed, st = %d\n", st);
return st;
}
return ZX_OK;
}
} // namespace clk
static zx_driver_ops_t msm8x53_clk_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = clk::Msm8x53Clk::Create;
return ops;
}();
// clang-format off
ZIRCON_DRIVER_BEGIN(msm8x53_clk, msm8x53_clk_driver_ops, "zircon", "0.1", 3)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PDEV),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_QUALCOMM),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_QUALCOMM_CLOCK),
ZIRCON_DRIVER_END(msm8x53_clk)