// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2022 Rockchip Electronics Co. Ltd. * * Author: Joseph Chen */ #include "cru_core.h" /********************* Private MACRO Definition ******************************/ #define REG_X4(i) ((i) * 4) #define PWRDOWN_SHIFT 13 #define PWRDOWN_MASK (1 << PWRDOWN_SHIFT) #define PLL_POSTDIV1_SHIFT 12 #define PLL_POSTDIV1_MASK (0x7 << PLL_POSTDIV1_SHIFT) #define PLL_FBDIV_SHIFT 0 #define PLL_FBDIV_MASK (0xfff << PLL_FBDIV_SHIFT) #define PLL_POSTDIV2_SHIFT 6 #define PLL_POSTDIV2_MASK (0x7 << PLL_POSTDIV2_SHIFT) #define PLL_REFDIV_SHIFT 0 #define PLL_REFDIV_MASK (0x3f << PLL_REFDIV_SHIFT) #define PLL_DSMPD_SHIFT 12 #define PLL_DSMPD_MASK (1 << PLL_DSMPD_SHIFT) #define PLL_FRAC_SHIFT 0 #define PLL_FRAC_MASK (0xffffff << PLL_FRAC_SHIFT) #define EXPONENT_OF_FRAC_PLL 24 #define RK_PLL_MODE_SLOW 0 #define RK_PLL_MODE_NORMAL 1 #define RK_PLL_MODE_DEEP 2 #define MHZ 1000000 #define PLL_GET_PLLMODE(val, shift, mask) \ (((uint32_t)(val) & mask) >> shift) #define PLL_GET_FBDIV(x) \ (((uint32_t)(x) & (PLL_FBDIV_MASK)) >> PLL_FBDIV_SHIFT) #define PLL_GET_REFDIV(x) \ (((uint32_t)(x) & (PLL_REFDIV_MASK)) >> PLL_REFDIV_SHIFT) #define PLL_GET_POSTDIV1(x) \ (((uint32_t)(x) & (PLL_POSTDIV1_MASK)) >> PLL_POSTDIV1_SHIFT) #define PLL_GET_POSTDIV2(x) \ (((uint32_t)(x) & (PLL_POSTDIV2_MASK)) >> PLL_POSTDIV2_SHIFT) #define PLL_GET_DSMPD(x) \ (((uint32_t)(x) & (PLL_DSMPD_MASK)) >> PLL_DSMPD_SHIFT) #define PLL_GET_FRAC(x) \ (((uint32_t)(x) & (PLL_FRAC_MASK)) >> PLL_FRAC_SHIFT) #define CRU_PLL_ROUND_UP_TO_KHZ(x) \ (HAL_DIV_ROUND_UP((x), 1000) * 1000) static struct hwclk g_hwclk_desc[HAL_HWCLK_MAX_NUM]; static struct PLL_CONFIG g_AutoTable; /********************* Private Function Definition ***************************/ uint32_t HAL_CRU_Read(struct hwclk *hw, uint32_t reg) { uint32_t val; int ret; ret = hw->xfer.read(hw->xfer.client, reg, &val); if (ret) { CRU_ERR("%s: read reg=0x%08x failed, ret=%d\n", hw->name, reg, ret); } CRU_DBGR("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val); return val; } uint32_t HAL_CRU_Write(struct hwclk *hw, uint32_t reg, uint32_t val) { int ret; CRU_DBGR("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val); ret = hw->xfer.write(hw->xfer.client, reg, val); if (ret) { CRU_ERR("%s: write reg=0x%08x, val=0x%08x failed, ret=%d\n", hw->name, reg, val, ret); } return ret; } uint32_t HAL_CRU_WriteMask(struct hwclk *hw, uint32_t reg, uint32_t msk, uint32_t val) { return HAL_CRU_Write(hw, reg, VAL_MASK_WE(msk, val)); } static int isBetterFreq(uint32_t now, uint32_t new, uint32_t best) { return (new <= now && new > best); } int HAL_CRU_FreqGetMux4(struct hwclk *hw, uint32_t freq, uint32_t freq0, uint32_t freq1, uint32_t freq2, uint32_t freq3) { uint32_t best = 0; if (isBetterFreq(freq, freq0, best)) { best = freq0; } if (isBetterFreq(freq, freq1, best)) { best = freq1; } if (isBetterFreq(freq, freq2, best)) { best = freq2; } if (isBetterFreq(freq, freq3, best)) { best = freq3; } if (best == freq0) { return 0; } else if (best == freq1) { return 1; } else if (best == freq2) { return 2; } else if (best == freq3) { return 3; } return HAL_INVAL; } int HAL_CRU_FreqGetMux3(struct hwclk *hw, uint32_t freq, uint32_t freq0, uint32_t freq1, uint32_t freq2) { return HAL_CRU_FreqGetMux4(hw, freq, freq0, freq1, freq2, freq2); } int HAL_CRU_FreqGetMux2(struct hwclk *hw, uint32_t freq, uint32_t freq0, uint32_t freq1) { return HAL_CRU_FreqGetMux4(hw, freq, freq0, freq1, freq1, freq1); } uint32_t HAL_CRU_MuxGetFreq4(struct hwclk *hw, uint32_t muxName, uint32_t freq0, uint32_t freq1, uint32_t freq2, uint32_t freq3) { switch (HAL_CRU_ClkGetMux(hw, muxName)) { case 0: return freq0; case 1: return freq1; case 2: return freq2; case 3: return freq3; } return HAL_INVAL; } uint32_t HAL_CRU_MuxGetFreq3(struct hwclk *hw, uint32_t muxName, uint32_t freq0, uint32_t freq1, uint32_t freq2) { return HAL_CRU_MuxGetFreq4(hw, muxName, freq0, freq1, freq2, freq2); } uint32_t HAL_CRU_MuxGetFreq2(struct hwclk *hw, uint32_t muxName, uint32_t freq0, uint32_t freq1) { return HAL_CRU_MuxGetFreq4(hw, muxName, freq0, freq1, freq1, freq1); } int HAL_CRU_RoundFreqGetMux4(struct hwclk *hw, uint32_t freq, uint32_t pFreq0, uint32_t pFreq1, uint32_t pFreq2, uint32_t pFreq3, uint32_t *pFreqOut) { uint32_t mux; if (pFreq3 && (pFreq3 % freq == 0)) { *pFreqOut = pFreq3; mux = 3; } else if (pFreq2 && (pFreq2 % freq == 0)) { *pFreqOut = pFreq2; mux = 2; } else if (pFreq1 % freq == 0) { *pFreqOut = pFreq1; mux = 1; } else { *pFreqOut = pFreq0; mux = 0; } return mux; } int HAL_CRU_RoundFreqGetMux3(struct hwclk *hw, uint32_t freq, uint32_t pFreq0, uint32_t pFreq1, uint32_t pFreq2, uint32_t *pFreqOut) { return HAL_CRU_RoundFreqGetMux4(hw, freq, pFreq0, pFreq1, pFreq2, 0, pFreqOut); } int HAL_CRU_RoundFreqGetMux2(struct hwclk *hw, uint32_t freq, uint32_t pFreq0, uint32_t pFreq1, uint32_t *pFreqOut) { return HAL_CRU_RoundFreqGetMux4(hw, freq, pFreq0, pFreq1, 0, 0, pFreqOut); } /******************************************************************************/ uint32_t HAL_CRU_GetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup) { uint32_t refDiv, fbDiv, postdDv1, postDiv2, frac, dsmpd; uint32_t mode = 0, rate = OSC_24M; mode = PLL_GET_PLLMODE(HAL_CRU_Read(hw, pSetup->modeOffset), pSetup->modeShift, pSetup->modeMask); switch (mode) { case RK_PLL_MODE_SLOW: rate = OSC_24M; break; case RK_PLL_MODE_NORMAL: postdDv1 = PLL_GET_POSTDIV1(HAL_CRU_Read(hw, pSetup->conOffset0)); fbDiv = PLL_GET_FBDIV(HAL_CRU_Read(hw, pSetup->conOffset0)); postDiv2 = PLL_GET_POSTDIV2(HAL_CRU_Read(hw, pSetup->conOffset1)); refDiv = PLL_GET_REFDIV(HAL_CRU_Read(hw, pSetup->conOffset1)); dsmpd = PLL_GET_DSMPD(HAL_CRU_Read(hw, pSetup->conOffset1)); frac = PLL_GET_FRAC(HAL_CRU_Read(hw, pSetup->conOffset2)); rate = (rate / refDiv) * fbDiv; if (dsmpd == 0) { uint64_t fracRate = OSC_24M; fracRate *= frac; fracRate = fracRate >> EXPONENT_OF_FRAC_PLL; fracRate = fracRate / refDiv; rate += fracRate; } rate = rate / (postdDv1 * postDiv2); rate = CRU_PLL_ROUND_UP_TO_KHZ(rate); break; case RK_PLL_MODE_DEEP: default: rate = 32768; break; } return rate; } static uint32_t CRU_Gcd(uint32_t m, uint32_t n) { int t; while (m > 0) { if (n > m) { t = m; m = n; n = t; } m -= n; } return n; } static uint64_t HAL_DivU64Rem(uint64_t numerator, uint32_t denominator, uint32_t *pRemainder) { uint64_t remainder = numerator; uint64_t b = denominator; uint64_t result; uint64_t d = 1; uint32_t high = numerator >> 32; result = 0; if (high >= denominator) { high /= denominator; result = (uint64_t)high << 32; remainder -= (uint64_t)(high * denominator) << 32; } while ((int64_t)b > 0 && b < remainder) { b = b + b; d = d + d; } do { if (remainder >= b) { remainder -= b; result += d; } b >>= 1; d >>= 1; } while (d); if (pRemainder) { *pRemainder = remainder; } return result; } static inline uint64_t HAL_DivU64(uint64_t numerator, uint32_t denominator) { return HAL_DivU64Rem(numerator, denominator, HAL_NULL); } static const struct PLL_CONFIG *CRU_PllSetByAuto(struct hwclk *hw, struct PLL_SETUP *pSetup, uint32_t finHz, uint32_t foutHz) { struct PLL_CONFIG *rateTable = &g_AutoTable; uint32_t refDiv, fbDiv, dsmpd; uint32_t postDiv1, postDiv2; uint32_t clkGcd = 0; uint64_t foutVco; uint64_t intVco; uint64_t frac64; CRU_DBGF("%s: %s: pll=%d, refdiv: [%u, %u], foutVco: [%u, %u], fout: [%u, %u]\n", __func__, hw->name, pSetup->id, pSetup->minRefdiv, pSetup->maxRefdiv, pSetup->minVco, pSetup->maxVco, pSetup->minFout, pSetup->maxFout); if (finHz == 0 || foutHz == 0 || foutHz == finHz) { return HAL_NULL; } if ((foutHz < pSetup->minFout) || (foutHz > pSetup->maxFout)) { return HAL_NULL; } for (postDiv1 = 1; postDiv1 <= 7; postDiv1++) { for (postDiv2 = 1; postDiv2 <= 7; postDiv2++) { if (postDiv1 < postDiv2) { CRU_DBGF("%s: %s: pll=%d, Invalid postDiv1(%u) < postDiv2(%u)\n", __func__, hw->name, pSetup->id, postDiv1, postDiv2); continue; } foutVco = (uint64_t)foutHz * postDiv1 * postDiv2; if ((foutVco < pSetup->minVco) || (foutVco > pSetup->maxVco)) { CRU_DBGF("%s: %s: pll=%d, Invalid foutVco(%llu), min_max[%u, %u]\n", __func__, hw->name, pSetup->id, foutVco, pSetup->minVco, pSetup->maxVco); continue; } intVco = foutVco / MHZ * MHZ; clkGcd = CRU_Gcd(finHz, intVco); refDiv = finHz / clkGcd; fbDiv = intVco / clkGcd; if ((refDiv < pSetup->minRefdiv) || (refDiv > pSetup->maxRefdiv)) { CRU_DBGF("%s: %s: pll=%d, Invalid refDiv(%u), min_max[%u, %u]\n", __func__, hw->name, pSetup->id, refDiv, pSetup->minRefdiv, pSetup->maxRefdiv); continue; } if (foutHz / MHZ * MHZ == foutHz) { dsmpd = 1; frac64 = 0; } else { frac64 = (foutVco % MHZ) * refDiv; frac64 = HAL_DivU64(frac64 << EXPONENT_OF_FRAC_PLL, OSC_24M); if (frac64 > 0) { dsmpd = 0; } else { dsmpd = 1; } } if (dsmpd && !(fbDiv >= 16 && fbDiv <= 2500)) { CRU_DBGF("%s: %s: pll=%d, Invalid fbDiv(%u) on int mode, min_max[16, 2500]\n", __func__, hw->name, pSetup->id, fbDiv); continue; } if (!dsmpd && !(fbDiv >= 20 && fbDiv <= 500)) { CRU_DBGF("%s: %s: pll=%d, Invalid fbDiv(%u) on frac mode, min_max[20, 500]\n", __func__, hw->name, pSetup->id, fbDiv); continue; } if (frac64 > 0x00ffffff) { CRU_DBGF("%s: %s: pll=%d, Invalid frac64(0x%llx) over max 0x00ffffff\n", __func__, hw->name, pSetup->id, frac64); continue; } rateTable->refDiv = refDiv; rateTable->fbDiv = fbDiv; rateTable->postDiv1 = postDiv1; rateTable->postDiv2 = postDiv2; rateTable->dsmpd = dsmpd; rateTable->frac = frac64; return rateTable; } } return HAL_NULL; } static const struct PLL_CONFIG *CRU_PllGetSettings(struct hwclk *hw, struct PLL_SETUP *pSetup, uint32_t rate) { const struct PLL_CONFIG *rateTable = pSetup->rateTable; if (!rateTable) { CRU_ERR("%s: %s: Unavailable pll=%d rate table\n", __func__, hw->name, pSetup->id); return HAL_NULL; } while (rateTable->rate) { if (rateTable->rate == rate) { return rateTable; } rateTable++; } rateTable = CRU_PllSetByAuto(hw, pSetup, OSC_24M, rate); if (!rateTable) { CRU_ERR("%s: %s: Unsupported pll=%d rate %d\n", __func__, hw->name, pSetup->id, rate); return HAL_NULL; } else { CRU_MSG("%s: Auto PLL=%d: (%d, %d, %d, %d, %d, %d, %d)\n", hw->name, pSetup->id, rate, rateTable->refDiv, rateTable->fbDiv, rateTable->postDiv1, rateTable->postDiv2, rateTable->dsmpd, rateTable->frac); } return rateTable; } /* * Force PLL into slow mode * Pll Power down * Pll Config fbDiv, refDiv, postdDv1, postDiv2, dsmpd, frac * Pll Power up * Waiting for pll lock * Force PLL into normal mode */ HAL_Status HAL_CRU_SetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup, uint32_t rate) { const struct PLL_CONFIG *pConfig; int delay = 2400; if (rate == HAL_CRU_GetPllFreq(hw, pSetup)) { return HAL_OK; } pConfig = CRU_PllGetSettings(hw, pSetup, rate); if (!pConfig) { return HAL_ERROR; } /* Force PLL into slow mode to ensure output stable clock */ HAL_CRU_WriteMask(hw, pSetup->modeOffset, pSetup->modeMask, RK_PLL_MODE_SLOW << pSetup->modeShift); /* Pll Power down */ HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 1 << PWRDOWN_SHIFT); /* Pll Config */ HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_POSTDIV2_MASK, pConfig->postDiv2 << PLL_POSTDIV2_SHIFT); HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_REFDIV_MASK, pConfig->refDiv << PLL_REFDIV_SHIFT); HAL_CRU_WriteMask(hw, pSetup->conOffset0, PLL_POSTDIV1_MASK, pConfig->postDiv1 << PLL_POSTDIV1_SHIFT); HAL_CRU_WriteMask(hw, pSetup->conOffset0, PLL_FBDIV_MASK, pConfig->fbDiv << PLL_FBDIV_SHIFT); if (pSetup->sscgEn) { HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_DSMPD_MASK, 0 << PLL_DSMPD_SHIFT); } else { HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_DSMPD_MASK, pConfig->dsmpd << PLL_DSMPD_SHIFT); } if (pConfig->frac) { HAL_CRU_Write(hw, pSetup->conOffset2, (HAL_CRU_Read(hw, pSetup->conOffset2) & 0xff000000) | pConfig->frac); } /* Pll Power up */ HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 0 << PWRDOWN_SHIFT); /* Waiting for pll lock */ while (delay > 0) { if (pSetup->stat0) { if (HAL_CRU_Read(hw, pSetup->stat0) & (1 << pSetup->lockShift)) { break; } } else { if (HAL_CRU_Read(hw, pSetup->conOffset1) & (1 << pSetup->lockShift)) { break; } } HAL_DelayUs(2); delay--; } if (delay == 0) { return HAL_TIMEOUT; } /* Force PLL into normal mode */ HAL_CRU_WriteMask(hw, pSetup->modeOffset, pSetup->modeMask, RK_PLL_MODE_NORMAL << pSetup->modeShift); return HAL_OK; } HAL_Status HAL_CRU_SetPllPowerUp(struct hwclk *hw, struct PLL_SETUP *pSetup) { int delay = 2400; /* Pll Power up */ HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 0 << PWRDOWN_SHIFT); /* Waiting for pll lock */ while (delay > 0) { if (pSetup->stat0) { if (HAL_CRU_Read(hw, pSetup->stat0) & (1 << pSetup->lockShift)) { break; } } else { if (HAL_CRU_Read(hw, pSetup->conOffset1) & (1 << pSetup->lockShift)) { break; } } HAL_DelayUs(1000); delay--; } if (delay == 0) { return HAL_TIMEOUT; } return HAL_OK; } HAL_Status HAL_CRU_SetPllPowerDown(struct hwclk *hw, struct PLL_SETUP *pSetup) { HAL_CRU_Write(hw, pSetup->conOffset1, VAL_MASK_WE(PWRDOWN_MASK, 1 << PWRDOWN_SHIFT)); return HAL_OK; } HAL_Check HAL_CRU_ClkIsEnabled(struct hwclk *hw, uint32_t clk) { uint32_t index = CLK_GATE_GET_REG_OFFSET(clk); uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk); return (HAL_Check)((HAL_CRU_Read(hw, hw->gate_con0 + REG_X4(index)) & (1 << shift)) >> shift); } HAL_Status HAL_CRU_ClkEnable(struct hwclk *hw, uint32_t clk) { uint32_t index = CLK_GATE_GET_REG_OFFSET(clk); uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk); uint32_t gid = 16 * index + shift; if (gid >= hw->num_gate) { CRU_ERR("%s: %s: clock(#%08x) is unsupported, gid=%d\n", __func__, hw->name, clk, gid); return HAL_INVAL; } if (hw->gate[gid].enable_count == 0) { CRU_DBGF("%s: %s: clock(#%08x), gid=%d\n", __func__, hw->name, clk, gid); HAL_CRU_Write(hw, hw->gate_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 0U << shift)); } hw->gate[gid].enable_count++; return HAL_OK; } HAL_Status HAL_CRU_ClkDisable(struct hwclk *hw, uint32_t clk) { uint32_t index = CLK_GATE_GET_REG_OFFSET(clk); uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk); uint32_t gid = 16 * index + shift; if (gid >= hw->num_gate) { CRU_ERR("%s: %s: clock(#%08x) is unsupported\n", __func__, hw->name, clk); return HAL_INVAL; } if (hw->gate[gid].enable_count == 0) { CRU_ERR("%s: %s: clock(#%08x) is already disabled\n", __func__, hw->name, clk); return HAL_OK; } hw->gate[gid].enable_count--; if (hw->gate[gid].enable_count == 0) { CRU_DBGF("%s: %s: clock(#%08x), gid=%d\n", __func__, hw->name, clk, gid); HAL_CRU_Write(hw, hw->gate_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 1U << shift)); } return HAL_OK; } HAL_Check HAL_CRU_ClkIsReset(struct hwclk *hw, uint32_t clk) { uint32_t index = CLK_GATE_GET_REG_OFFSET(clk); uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk); return (HAL_Check)((HAL_CRU_Read(hw, hw->softrst_con0 + REG_X4(index)) & (1 << shift)) >> shift); } HAL_Status HAL_CRU_ClkResetAssert(struct hwclk *hw, uint32_t clk) { uint32_t index = CLK_RESET_GET_REG_OFFSET(clk); uint32_t shift = CLK_RESET_GET_BITS_SHIFT(clk); HAL_CRU_Write(hw, hw->softrst_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 1U << shift)); return HAL_OK; } HAL_Status HAL_CRU_ClkResetDeassert(struct hwclk *hw, uint32_t clk) { uint32_t index = CLK_RESET_GET_REG_OFFSET(clk); uint32_t shift = CLK_RESET_GET_BITS_SHIFT(clk); HAL_CRU_Write(hw, hw->softrst_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 0U << shift)); return HAL_OK; } HAL_Status HAL_CRU_ClkSetDiv(struct hwclk *hw, uint32_t divName, uint32_t divValue) { uint32_t shift, mask, index; index = CLK_DIV_GET_REG_OFFSET(divName); shift = CLK_DIV_GET_BITS_SHIFT(divName); mask = CLK_DIV_GET_MASK(divName); if (divValue > mask) { divValue = mask; } CRU_DBGF("%s: %s: clock(#%08x), selcon%d=0x%08x, shift=%d, mask=0x%x, div=%d\n", __func__, hw->name, divName, index, hw->sel_con0 + REG_X4(index), shift, mask, divValue); HAL_CRU_Write(hw, hw->sel_con0 + REG_X4(index), VAL_MASK_WE(mask, (divValue - 1U) << shift)); return HAL_OK; } uint32_t HAL_CRU_ClkGetDiv(struct hwclk *hw, uint32_t divName) { uint32_t shift, mask, index, divValue; index = CLK_DIV_GET_REG_OFFSET(divName); shift = CLK_DIV_GET_BITS_SHIFT(divName); mask = CLK_DIV_GET_MASK(divName); divValue = ((HAL_CRU_Read(hw, hw->sel_con0 + REG_X4(index)) & mask) >> shift) + 1; return divValue; } HAL_Status HAL_CRU_ClkSetMux(struct hwclk *hw, uint32_t muxName, uint32_t muxValue) { uint32_t shift, mask, index; index = CLK_MUX_GET_REG_OFFSET(muxName); shift = CLK_MUX_GET_BITS_SHIFT(muxName); mask = CLK_MUX_GET_MASK(muxName); CRU_DBGF("%s: %s: clock(#%08x), selcon%d=0x%08x, shift=%d, mask=0x%x, mux=%d\n", __func__, hw->name, muxName, index, hw->sel_con0 + REG_X4(index), shift, mask, muxValue); HAL_CRU_Write(hw, hw->sel_con0 + REG_X4(index), VAL_MASK_WE(mask, muxValue << shift)); return HAL_OK; } uint32_t HAL_CRU_ClkGetMux(struct hwclk *hw, uint32_t muxName) { uint32_t shift, mask, index, muxValue; index = CLK_MUX_GET_REG_OFFSET(muxName); shift = CLK_MUX_GET_BITS_SHIFT(muxName); mask = CLK_MUX_GET_MASK(muxName); muxValue = (HAL_CRU_Read(hw, hw->sel_con0 + REG_X4(index)) & mask) >> shift; return muxValue; } HAL_Status HAL_CRU_ClkSetTestout(struct hwclk *hw, uint32_t clockName, uint32_t muxValue, uint32_t divValue) { if (hw->ops->clkInitTestout) { hw->ops->clkInitTestout(hw, clockName, muxValue, divValue); } return HAL_OK; } void HAL_CRU_ClkDumpTree(HAL_ClockType type) { struct hwclk *hw = &g_hwclk_desc[0]; struct clkTable *c; const char *parent; uint32_t clkMux, mux; uint32_t i, freq; for (i = 0; i < HAL_HWCLK_MAX_NUM; i++, hw++) { if (hw->type == CLK_UNDEF) { break; } if ((type != hw->type) && (type != CLK_ALL)) { continue; } if (!hw->ops->clkTable) { continue; } CRU_MSG(" ================== %s ==================\n", hw->name); CRU_MSG(" Name Rate:hz Parent\n"); CRU_MSG(" ====================================================\n"); for (c = hw->ops->clkTable; c->name; c++) { if (c->type == DUMP_INT) { if (c->numParents == 1) { mux = 0; } else { clkMux = CLK_GET_MUX(c->clk); mux = HAL_CRU_ClkGetMux(hw, clkMux); } freq = HAL_CRU_ClkGetFreq(hw, c->clk); parent = c->parents[mux]; } else if (c->type == DUMP_EXT) { freq = c->getFreq(hw, c->clk); parent = c->extParent ? c->extParent : "ext-in"; } else { continue; } CRU_MSG(" %-30s %10d %s\n", c->name, freq, parent); } CRU_MSG("\n"); } } HAL_Status HAL_CRU_SetGlbSrst(struct hwclk *hw, eCRU_GlbSrstType type) { if (type == GLB_SRST_FST) { HAL_CRU_Write(hw, hw->gbl_srst_fst, GLB_SRST_FST); } return HAL_INVAL; } uint32_t HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName) { uint32_t rate; rate = hw->ops->clkGetFreq(hw, clockName); CRU_DBGF("%s: %s: clock(#%08x) get rate: %d\n", __func__, hw->name, clockName, rate); return rate; } HAL_Status HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate) { HAL_Status ret; uint32_t now; CRU_DBGF("%s: %s: clock(#%08x) set rate: %d\n", __func__, hw->name, clockName, rate); ret = hw->ops->clkSetFreq(hw, clockName, rate); if (hw->flags & CLK_FLG_SET_RATE_VERIFY) { now = hw->ops->clkGetFreq(hw, clockName); if (rate != now) { CRU_MSG("%s: Set rate %d, but %d returned\n", hw->name, rate, now); ret = HAL_ERROR; } } return ret; } HAL_Status HAL_CRU_Init(void) { return HAL_OK; } struct hwclk *HAL_CRU_ClkGet(void *client) { struct hwclk *hw = &g_hwclk_desc[0]; int i; if (!client) { return HAL_NULL; } for (i = 0; i < HAL_HWCLK_MAX_NUM; i++, hw++) { if (hw->xfer.client == client) { return hw; } } return HAL_NULL; } struct hwclk *HAL_CRU_Register(struct xferclk xfer) { struct hwclk hw; int i, ret; if (!xfer.read || !xfer.write || !xfer.client || !xfer.name[0]) { return HAL_NULL; } if (xfer.type == CLK_UNDEF || xfer.type >= CLK_MAX) { return HAL_NULL; } /* registered ? */ for (i = 0; i < HAL_HWCLK_MAX_NUM; i++) { if (g_hwclk_desc[i].type == CLK_UNDEF) { break; } if (g_hwclk_desc[i].xfer.client == xfer.client) { return &g_hwclk_desc[i]; } } if (i >= HAL_HWCLK_MAX_NUM) { CRU_ERR("CLK: No more space for register\n"); return HAL_NULL; } memset(&hw, 0, sizeof(hw)); hw.type = xfer.type; hw.xfer = xfer; switch (xfer.type) { case CLK_RKX110: if (xfer.version == 0) { hw.ops = &rkx110_clk_ops; } else if (xfer.version == 1) { hw.ops = &rkx111_clk_ops; } break; case CLK_RKX120: if (xfer.version == 0) { hw.ops = &rkx120_clk_ops; } else if (xfer.version == 1) { hw.ops = &rkx121_clk_ops; } break; default: CRU_ERR("%s: Invalid type: %d\n", __func__, xfer.type); return HAL_NULL; } if (!hw.ops || !hw.ops->clkInit || !hw.ops->clkGetFreq || !hw.ops->clkSetFreq) { CRU_ERR("No available clkOps or .clkInit() or .clkGetFreq() or .clkSetFreq()\n"); return HAL_NULL; } HAL_MutexInit(&hw.lock); ret = hw.ops->clkInit(&hw, &xfer); if (ret) { CRU_ERR("%s: Init clock failed, ret=%d\n", hw.name, ret); return HAL_NULL; } if (!hw.num_gate) { CRU_ERR("No available .gate\n"); return HAL_NULL; } #if HAL_ENABLE_SET_RATE_VERIFY hw->flags |= CLK_FLG_SET_RATE_VERIFY; #endif g_hwclk_desc[i] = hw; CRU_MSG("CLK: register '%s' with client 0x%08lx successfully.\n", hw.name, (unsigned long)hw.xfer.client); #if DEBUG_CRU_INIT HAL_CRU_ClkDumpTree(xfer.type); #endif return &g_hwclk_desc[i]; }