/* SPDX-License-Identifier: BSD-3-Clause */
|
/*
|
* Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd.
|
*/
|
#include "Include/Library/CruLib.h"
|
#include <Library/BaseLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/TimerLib.h>
|
|
/** @addtogroup RK_HAL_Driver
|
* @{
|
*/
|
|
/** @addtogroup CRU
|
* @{
|
*/
|
|
/** @defgroup CRU_How_To_Use How To Use
|
* @{
|
|
The CRU driver can be used as follows:
|
|
- Invoke cru functions to set clk rate, enable or disable clk, reset clk in each device.
|
- The gate and soft reset id is include register offset and shift information:
|
|
con_offset: id /16
|
shift: id %16
|
|
- The mux and div id is include register offset, shift, mask information:
|
|
[15:0]: con
|
[23:16]: shift
|
[31:24]: width
|
|
- CRU driver is just responsible for passing simple command data, And
|
the usecount is the user's responsibility. Protection the usecount at the driver layer.
|
- More details refer to APIs' descriptions as below.
|
|
@} */
|
|
/** @defgroup CRU_Private_Definition Private Definition
|
* @{
|
*/
|
/********************* Private MACRO Definition ******************************/
|
#define PWRDOWN_SHIT 13
|
#define PWRDOWN_MASK 1 << PWRDOWN_SHIT
|
#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 PLLCON0_M_SHIFT 0
|
#define PLLCON0_M_MASK 0x3ff << PLLCON0_M_SHIFT
|
#define PLLCON1_P_SHIFT 0
|
#define PLLCON1_P_MASK 0x3f << PLLCON1_P_SHIFT
|
#define PLLCON1_S_SHIFT 6
|
#define PLLCON1_S_MASK 0x7 << PLLCON1_S_SHIFT
|
#define PLLCON2_K_SHIFT 0
|
#define PLLCON2_K_MASK 0xffff << PLLCON2_K_SHIFT
|
#define PLLCON1_PWRDOWN BIT(13)
|
#define PLLCON6_LOCK_STATUS BIT(15)
|
|
#define MIN_FOUTVCO_FREQ (800 * MHZ)
|
#define MAX_FOUTVCO_FREQ (2000 * MHZ)
|
#define MIN_FOUT_FREQ (24 * MHZ)
|
#define MAX_FOUT_FREQ (1600 * MHZ)
|
|
#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 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), KHZ) * KHZ)
|
|
/********************* Private Structure Definition **************************/
|
static struct PLL_CONFIG g_rockchipAutoTable;
|
|
/********************* Private Variable Definition ***************************/
|
|
/********************* Private Function Definition ***************************/
|
|
/** Calculate the greatest common divisor */
|
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;
|
}
|
|
/**
|
* @brief Rockchip pll clk set postdiv.
|
* @param foutHz: output freq
|
* @param *postDiv1: pll postDiv1
|
* @param *postDiv2: pll postDiv2
|
* @param *foutVco: pll vco
|
* @return HAL_Status.
|
* How to calculate the PLL:
|
* Formulas also embedded within the fractional PLL Verilog model:
|
* If DSMPD = 1 (DSM is disabled, "integer mode")
|
* FOUTVCO = FREF / REFDIV * FBDIV
|
* FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
|
* Where:
|
* FOUTVCO = fractional PLL non-divided output frequency
|
* FOUTPOSTDIV = fractional PLL divided output frequency
|
* (output of second post divider)
|
*/
|
static HAL_Status CRU_PllSetPostDiv(uint32_t foutHz, uint32_t *postDiv1,
|
uint32_t *postDiv2, uint32_t *foutVco)
|
{
|
uint32_t freq;
|
|
if (foutHz < MIN_FOUTVCO_FREQ) {
|
for (*postDiv1 = 1; *postDiv1 <= 7; (*postDiv1)++) {
|
for (*postDiv2 = 1; *postDiv2 <= 7; (*postDiv2)++) {
|
freq = foutHz * (*postDiv1) * (*postDiv2);
|
if (freq >= MIN_FOUTVCO_FREQ && freq <= MAX_FOUTVCO_FREQ) {
|
*foutVco = freq;
|
|
return HAL_OK;
|
}
|
}
|
}
|
|
return HAL_ERROR;
|
} else {
|
*postDiv1 = 1;
|
*postDiv2 = 1;
|
|
return HAL_OK;
|
}
|
}
|
|
/**
|
* @brief Get pll parameter by auto.
|
* @param finHz: pll intput freq
|
* @param foutHz: pll output freq
|
* @return struct PLL_CONFIG.
|
* How to calculate the PLL:
|
* Formulas also embedded within the fractional PLL Verilog model:
|
* If DSMPD = 1 (DSM is disabled, "integer mode")
|
* FOUTVCO = FREF / REFDIV * FBDIV
|
* FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
|
* Where:
|
* FOUTVCO = fractional PLL non-divided output frequency
|
* FOUTPOSTDIV = fractional PLL divided output frequency
|
* (output of second post divider)
|
* FREF = fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
|
* REFDIV = fractional PLL input reference clock divider
|
* FBDIV = Integer value programmed into feedback divide
|
*/
|
static const struct PLL_CONFIG *CRU_PllSetByAuto(uint32_t finHz,
|
uint32_t foutHz)
|
{
|
struct PLL_CONFIG *rateTable = &g_rockchipAutoTable;
|
uint32_t foutVco = foutHz;
|
uint64_t fin64, frac64;
|
uint32_t postDiv1, postDiv2;
|
uint32_t clkGcd = 0;
|
HAL_Status error;
|
|
if (finHz == 0 || foutHz == 0 || foutHz == finHz) {
|
return NULL;
|
}
|
|
error = CRU_PllSetPostDiv(foutHz, &postDiv1, &postDiv2, &foutVco);
|
if (error) {
|
return NULL;
|
}
|
rateTable->postDiv1 = postDiv1;
|
rateTable->postDiv2 = postDiv2;
|
rateTable->dsmpd = 1;
|
|
if (finHz / MHZ * MHZ == finHz && foutHz / MHZ * MHZ == foutHz) {
|
finHz /= MHZ;
|
foutVco /= MHZ;
|
clkGcd = CRU_Gcd(finHz, foutVco);
|
rateTable->refDiv = finHz / clkGcd;
|
rateTable->fbDiv = foutVco / clkGcd;
|
|
rateTable->frac = 0;
|
} else if (finHz / MHZ * MHZ != finHz) {
|
clkGcd = foutHz / finHz;
|
rateTable->fbDiv = clkGcd;
|
rateTable->refDiv = 1;
|
rateTable->postDiv1 = 1;
|
rateTable->postDiv2 = 1;
|
|
fin64 = foutHz * rateTable->refDiv;
|
fin64 = HAL_DivU64(fin64 << EXPONENT_OF_FRAC_PLL, finHz);
|
frac64 = rateTable->fbDiv;
|
frac64 = rateTable->fbDiv << EXPONENT_OF_FRAC_PLL;
|
frac64 = fin64 - frac64;
|
rateTable->frac = frac64;
|
if (rateTable->frac > 0) {
|
rateTable->dsmpd = 0;
|
}
|
} else {
|
clkGcd = CRU_Gcd(finHz / MHZ, foutVco / MHZ);
|
rateTable->refDiv = finHz / MHZ / clkGcd;
|
rateTable->fbDiv = foutVco / MHZ / clkGcd;
|
rateTable->frac = 0;
|
|
frac64 = (foutVco % MHZ);
|
fin64 = finHz;
|
frac64 = frac64 * rateTable->refDiv;
|
frac64 = HAL_DivU64(frac64 << EXPONENT_OF_FRAC_PLL, fin64);
|
rateTable->frac = frac64;
|
if (rateTable->frac > 0) {
|
rateTable->dsmpd = 0;
|
}
|
}
|
|
return rateTable;
|
}
|
|
/**
|
* @brief Get pll parameter by rateTable.
|
* @param *pSetup: struct PLL_SETUP struct, Contains PLL register parameters
|
* @param rate: pll target rate.
|
* @return struct PLL_CONFIG.
|
* How to calculate the PLL:
|
* Look up the rateTable to get the PLL config parameter
|
*/
|
static const struct PLL_CONFIG *CRU_PllGetSettings(struct PLL_SETUP *pSetup,
|
uint32_t rate)
|
{
|
const struct PLL_CONFIG *rateTable = pSetup->rateTable;
|
|
if (rateTable == NULL) {
|
return CRU_PllSetByAuto(PLL_INPUT_OSC_RATE, rate);
|
}
|
|
while (rateTable->rate) {
|
if (rateTable->rate == rate) {
|
break;
|
}
|
rateTable++;
|
}
|
if (rateTable->rate != rate) {
|
return CRU_PllSetByAuto(PLL_INPUT_OSC_RATE, rate);
|
} else {
|
return rateTable;
|
}
|
}
|
/** @} */
|
/********************* Public Function Definition ****************************/
|
|
/** @defgroup CRU_Exported_Functions_Group5 Other Functions
|
* @attention these APIs allow direct use in the HAL layer.
|
* @{
|
*/
|
|
/**
|
* @brief Get pll freq.
|
* @param *pSetup: struct PLL_SETUP struct,Contains PLL register parameters
|
* @return pll rate.
|
* How to calculate the PLL:
|
* Formulas also embedded within the fractional PLL Verilog model:
|
* If DSMPD = 1 (DSM is disabled, "integer mode")
|
* FOUTVCO = FREF / REFDIV * FBDIV
|
* FOUT = FOUTVCO / POSTDIV1 / POSTDIV2
|
* If DSMPD = 0 (DSM is enabled, "fractional mode")
|
* FOUTVCO = (FREF / REFDIV) * (FBDIV + FRAC / (2^24))
|
* FOUTPOSTDIV = FOUTVCO / (POSTDIV1*POSTDIV2)
|
* FOUT = FOUTVCO / POSTDIV1 / POSTDIV2
|
*/
|
uint32_t HAL_CRU_GetPllFreq(struct PLL_SETUP *pSetup)
|
{
|
uint32_t refDiv, fbDiv, postdDv1, postDiv2, frac, dsmpd;
|
uint32_t mode = 0, rate = PLL_INPUT_OSC_RATE;
|
|
mode = PLL_GET_PLLMODE(READ_REG(*(pSetup->modeOffset)), pSetup->modeShift,
|
pSetup->modeMask);
|
|
switch (mode) {
|
case RK_PLL_MODE_SLOW:
|
rate = PLL_INPUT_OSC_RATE;
|
break;
|
case RK_PLL_MODE_NORMAL:
|
postdDv1 = PLL_GET_POSTDIV1(READ_REG(*(pSetup->conOffset0)));
|
fbDiv = PLL_GET_FBDIV(READ_REG(*(pSetup->conOffset0)));
|
postDiv2 = PLL_GET_POSTDIV2(READ_REG(*(pSetup->conOffset1)));
|
refDiv = PLL_GET_REFDIV(READ_REG(*(pSetup->conOffset1)));
|
dsmpd = PLL_GET_DSMPD(READ_REG(*(pSetup->conOffset1)));
|
frac = PLL_GET_FRAC(READ_REG(*(pSetup->conOffset2)));
|
rate = (rate / refDiv) * fbDiv;
|
if (dsmpd == 0) {
|
uint64_t fracRate = PLL_INPUT_OSC_RATE;
|
|
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;
|
}
|
|
/**
|
* @brief Set pll freq.
|
* @param *pSetup: struct PLL_SETUP struct,Contains PLL register parameters
|
* @param rate: pll set rate
|
* @return HAL_Status.
|
* How to calculate the PLL:
|
* 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 PLL_SETUP *pSetup, uint32_t rate)
|
{
|
const struct PLL_CONFIG *pConfig;
|
int delay = 2400;
|
|
if (rate == HAL_CRU_GetPllFreq(pSetup)) {
|
return HAL_OK;
|
} else if (rate < MIN_FOUT_FREQ) {
|
return HAL_INVAL;
|
} else if (rate > MAX_FOUT_FREQ) {
|
return HAL_INVAL;
|
}
|
|
pConfig = CRU_PllGetSettings(pSetup, rate);
|
if (!pConfig) {
|
return HAL_ERROR;
|
}
|
|
/* Force PLL into slow mode to ensure output stable clock */
|
WRITE_REG_MASK_WE(*(pSetup->modeOffset), pSetup->modeMask, RK_PLL_MODE_SLOW << pSetup->modeShift);
|
|
/* Pll Power down */
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PWRDOWN_MASK, 1 << PWRDOWN_SHIT);
|
|
/* Pll Config */
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PLL_POSTDIV2_MASK, pConfig->postDiv2 << PLL_POSTDIV2_SHIFT);
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PLL_REFDIV_MASK, pConfig->refDiv << PLL_REFDIV_SHIFT);
|
WRITE_REG_MASK_WE(*(pSetup->conOffset0), PLL_POSTDIV1_MASK, pConfig->postDiv1 << PLL_POSTDIV1_SHIFT);
|
WRITE_REG_MASK_WE(*(pSetup->conOffset0), PLL_FBDIV_MASK, pConfig->fbDiv << PLL_FBDIV_SHIFT);
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PLL_DSMPD_MASK, pConfig->dsmpd << PLL_DSMPD_SHIFT);
|
|
if (pConfig->frac) {
|
WRITE_REG(*(pSetup->conOffset2), (READ_REG(*(pSetup->conOffset2)) & 0xff000000) | pConfig->frac);
|
}
|
|
/* Pll Power up */
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PWRDOWN_MASK, 0 << PWRDOWN_SHIT);
|
|
/* Waiting for pll lock */
|
while (delay > 0) {
|
if (pSetup->stat0) {
|
if (READ_REG(*(pSetup->stat0)) & (1 << pSetup->lockShift)) {
|
break;
|
}
|
} else {
|
if (READ_REG(*(pSetup->conOffset1)) & (1 << pSetup->lockShift)) {
|
break;
|
}
|
}
|
HAL_CPUDelayUs(1000);
|
delay--;
|
}
|
if (delay == 0) {
|
return HAL_TIMEOUT;
|
}
|
|
/* Force PLL into normal mode */
|
WRITE_REG_MASK_WE(*(pSetup->modeOffset), pSetup->modeMask, RK_PLL_MODE_NORMAL << pSetup->modeShift);
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief Get pll v1 freq(RK3588).
|
* @param *pSetup: struct PLL_SETUP struct,Contains PLL register parameters
|
* @return pll rate.
|
* How to calculate the PLL:
|
* Formulas also embedded within the fractional PLL Verilog model:
|
* If K = 0 (DSM is disabled, "integer mode")
|
* FOUTVCO = FREF / P * M
|
* FOUT = FOUTVCO / 2^S
|
* If K > 0 (DSM is enabled, "fractional mode")
|
* FOUTVCO = (FREF / P) * (M + K / 65536)
|
* FOUT = FOUTVCO / 2^S
|
*/
|
uint32_t HAL_CRU_GetPllV1Freq(struct PLL_SETUP *pSetup)
|
{
|
uint32_t m, p, s, k;
|
uint64_t rate = PLL_INPUT_OSC_RATE;
|
uint32_t mode = 0;
|
|
if (pSetup->modeMask)
|
mode = PLL_GET_PLLMODE(READ_REG(*(pSetup->modeOffset)), pSetup->modeShift,
|
pSetup->modeMask);
|
else
|
mode = RK_PLL_MODE_NORMAL;
|
|
switch (mode) {
|
case RK_PLL_MODE_SLOW:
|
rate = PLL_INPUT_OSC_RATE;
|
break;
|
case RK_PLL_MODE_NORMAL:
|
m = (READ_REG(*(pSetup->conOffset0)) & PLLCON0_M_MASK) >> PLLCON0_M_SHIFT;
|
p = (READ_REG(*(pSetup->conOffset1)) & PLLCON1_P_MASK) >> PLLCON1_P_SHIFT;
|
s = (READ_REG(*(pSetup->conOffset1)) & PLLCON1_S_MASK) >> PLLCON1_S_SHIFT;
|
k = (READ_REG(*(pSetup->conOffset2)) & PLLCON2_K_MASK) >> PLLCON2_K_SHIFT;
|
|
rate *= m;
|
rate = rate / p;
|
if (k) {
|
/* fractional mode */
|
uint64_t frac = PLL_INPUT_OSC_RATE / p;
|
|
frac *= k;
|
frac = frac / 65536;
|
rate += frac;
|
}
|
rate = rate >> s;
|
|
break;
|
case RK_PLL_MODE_DEEP:
|
default:
|
rate = 32768;
|
break;
|
}
|
|
return rate;
|
}
|
|
/**
|
* @brief Set pll v1 freq(RK3588).
|
* @param *pSetup: struct PLL_SETUP struct,Contains PLL register parameters
|
* @param rate: pll set rate
|
* @return HAL_Status.
|
* How to calculate the PLL:
|
* Force PLL into slow mode
|
* Pll Power down
|
* Pll Config M, P, S, K
|
* Pll Power up
|
* Waiting for pll lock
|
* Force PLL into normal mode
|
*/
|
HAL_Status HAL_CRU_SetPllV1Freq(struct PLL_SETUP *pSetup, uint32_t rate)
|
{
|
const struct PLL_CONFIG *pConfig;
|
int delay = 24000000;
|
|
if (rate == HAL_CRU_GetPllV1Freq(pSetup)) {
|
return HAL_OK;
|
} else if (rate < MIN_FOUT_FREQ) {
|
return HAL_INVAL;
|
} else if (rate > MAX_FOUT_FREQ) {
|
return HAL_INVAL;
|
}
|
|
pConfig = CRU_PllGetSettings(pSetup, rate);
|
if (!pConfig) {
|
return HAL_ERROR;
|
}
|
|
/* Force PLL into slow mode to ensure output stable clock */
|
if (pSetup->modeMask)
|
WRITE_REG_MASK_WE(*(pSetup->modeOffset), pSetup->modeMask, RK_PLL_MODE_SLOW << pSetup->modeShift);
|
|
/* Pll Power down */
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PWRDOWN_MASK, 1 << PWRDOWN_SHIT);
|
|
/* Pll Config */
|
WRITE_REG_MASK_WE(*(pSetup->conOffset0), PLLCON0_M_MASK, pConfig->m << PLLCON0_M_SHIFT);
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PLLCON1_P_MASK, pConfig->p << PLLCON1_P_SHIFT);
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PLLCON1_S_MASK, pConfig->s << PLLCON1_S_SHIFT);
|
if (pConfig->k)
|
WRITE_REG_MASK_WE(*(pSetup->conOffset2), PLLCON2_K_MASK, pConfig->k << PLLCON2_K_SHIFT);
|
|
/* Pll Power up */
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PWRDOWN_MASK, 0 << PWRDOWN_SHIT);
|
|
/* Waiting for pll lock */
|
while (delay > 0) {
|
if (READ_REG(*(pSetup->conOffset6)) & (1 << pSetup->lockShift)) {
|
break;
|
}
|
delay--;
|
}
|
if (delay == 0) {
|
return HAL_TIMEOUT;
|
}
|
|
/* Force PLL into normal mode */
|
if (pSetup->modeMask)
|
WRITE_REG_MASK_WE(*(pSetup->modeOffset), pSetup->modeMask, RK_PLL_MODE_NORMAL << pSetup->modeShift);
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief Set pll power up.
|
* @param *pSetup: struct PLL_SETUP struct,Contains PLL register parameters
|
* @return HAL_Status.
|
*/
|
HAL_Status HAL_CRU_SetPllPowerUp(struct PLL_SETUP *pSetup)
|
{
|
int delay = 2400;
|
|
/* Pll Power up */
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PWRDOWN_MASK, 0 << PWRDOWN_SHIT);
|
|
/* Waiting for pll lock */
|
while (delay > 0) {
|
if (pSetup->stat0) {
|
if (READ_REG(*(pSetup->stat0)) & (1 << pSetup->lockShift)) {
|
break;
|
}
|
} else {
|
if (READ_REG(*(pSetup->conOffset1)) & (1 << pSetup->lockShift)) {
|
break;
|
}
|
}
|
HAL_CPUDelayUs(1000);
|
delay--;
|
}
|
if (delay == 0) {
|
return HAL_TIMEOUT;
|
}
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief Set pll power down.
|
* @param *pSetup: struct PLL_SETUP struct,Contains PLL register parameters
|
* @return HAL_Status.
|
*/
|
HAL_Status HAL_CRU_SetPllPowerDown(struct PLL_SETUP *pSetup)
|
{
|
/* Pll Power down */
|
WRITE_REG_MASK_WE(*(pSetup->conOffset1), PWRDOWN_MASK, 1 << PWRDOWN_SHIT);
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief IP Clock is Enabled API
|
* @param clk: clk gate id
|
* @return HAL_Check
|
*/
|
HAL_Check HAL_CRU_ClkIsEnabled(uint32_t clk)
|
{
|
uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
|
uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
|
HAL_Check ret;
|
|
#ifdef CRU_GATE_CON_CNT
|
if (index < CRU_GATE_CON_CNT) {
|
ret = (HAL_Check)(!((CRU->CRU_CLKGATE_CON[index] & (1 << shift)) >> shift));
|
} else {
|
#ifdef PMUCRU_BASE
|
ret = (HAL_Check)(!((PMUCRU->CRU_CLKGATE_CON[index - CRU_GATE_CON_CNT] & (1 << shift)) >> shift));
|
#else
|
ret = (HAL_Check)(!((CRU->PMU_CLKGATE_CON[index - CRU_GATE_CON_CNT] & (1 << shift)) >> shift));
|
#endif
|
}
|
#else
|
ret = (HAL_Check)(!((CRU->CRU_CLKGATE_CON[index] & (1 << shift)) >> shift));
|
#endif
|
|
return ret;
|
}
|
|
/**
|
* @brief IP Clock Enable API
|
* @param clk: clk gate id
|
* @return NONE
|
*/
|
HAL_Status HAL_CRU_ClkEnable(uint32_t clk)
|
{
|
uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
|
uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
|
|
#ifdef CRU_GATE_CON_CNT
|
if (index < CRU_GATE_CON_CNT) {
|
CRU->CRU_CLKGATE_CON[index] = VAL_MASK_WE(1U << shift, 0U << shift);
|
} else {
|
#ifdef PMUCRU_BASE
|
PMUCRU->CRU_CLKGATE_CON[index - CRU_GATE_CON_CNT] = VAL_MASK_WE(1U << shift, 0U << shift);
|
#else
|
CRU->PMU_CLKGATE_CON[index - CRU_GATE_CON_CNT] = VAL_MASK_WE(1U << shift, 0U << shift);
|
#endif
|
}
|
#else
|
CRU->CRU_CLKGATE_CON[index] = VAL_MASK_WE(1U << shift, 0U << shift);
|
#endif
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief IP Clock Disabled API
|
* @param clk: clk gate id
|
* @return NONE
|
*/
|
HAL_Status HAL_CRU_ClkDisable(uint32_t clk)
|
{
|
uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
|
uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
|
|
#ifdef CRU_GATE_CON_CNT
|
if (index < CRU_GATE_CON_CNT) {
|
CRU->CRU_CLKGATE_CON[index] = VAL_MASK_WE(1U << shift, 1U << shift);
|
} else {
|
#ifdef PMUCRU_BASE
|
PMUCRU->CRU_CLKGATE_CON[index - CRU_GATE_CON_CNT] = VAL_MASK_WE(1U << shift, 1U << shift);
|
#else
|
CRU->PMU_CLKGATE_CON[index - CRU_GATE_CON_CNT] = VAL_MASK_WE(1U << shift, 1U << shift);
|
#endif
|
}
|
#else
|
CRU->CRU_CLKGATE_CON[index] = VAL_MASK_WE(1U << shift, 1U << shift);
|
#endif
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief IP Clock is reset API
|
* @param clk: clk reset id
|
* @return HAL_Check
|
*/
|
HAL_Check HAL_CRU_ClkIsReset(uint32_t clk)
|
{
|
uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
|
uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
|
HAL_Check ret;
|
|
#ifdef CRU_SRST_CON_CNT
|
if (index < CRU_SRST_CON_CNT) {
|
ret = (HAL_Check)((CRU->CRU_CLKGATE_CON[index] & (1 << shift)) >> shift);
|
} else {
|
ret = (HAL_Check)((PMUCRU->CRU_CLKGATE_CON[index - CRU_SRST_CON_CNT] & (1 << shift)) >> shift);
|
}
|
#else
|
ret = (HAL_Check)((CRU->CRU_CLKGATE_CON[index] & (1 << shift)) >> shift);
|
#endif
|
|
return ret;
|
}
|
|
/**
|
* @brief IP Clock Reset Assert API
|
* @param clk: clk reset id
|
* @return NONE
|
*/
|
HAL_Status HAL_CRU_ClkResetAssert(uint32_t clk)
|
{
|
uint32_t index = CLK_RESET_GET_REG_OFFSET(clk);
|
uint32_t shift = CLK_RESET_GET_BITS_SHIFT(clk);
|
|
HAL_ASSERT(shift < 16);
|
#ifdef CRU_SRST_CON_CNT
|
if (index < CRU_SRST_CON_CNT) {
|
CRU->CRU_SOFTRST_CON[index] = VAL_MASK_WE(1U << shift, 1U << shift);
|
} else {
|
PMUCRU->CRU_SOFTRST_CON[index - CRU_SRST_CON_CNT] = VAL_MASK_WE(1U << shift, 1U << shift);
|
}
|
#else
|
CRU->CRU_SOFTRST_CON[index] = VAL_MASK_WE(1U << shift, 1U << shift);
|
#endif
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief IP Clock Reset Deassert API
|
* @param clk: clk reset id
|
* @return NONE
|
*/
|
HAL_Status HAL_CRU_ClkResetDeassert(uint32_t clk)
|
{
|
uint32_t index = CLK_RESET_GET_REG_OFFSET(clk);
|
uint32_t shift = CLK_RESET_GET_BITS_SHIFT(clk);
|
|
HAL_ASSERT(shift < 16);
|
#ifdef CRU_SRST_CON_CNT
|
if (index < CRU_SRST_CON_CNT) {
|
CRU->CRU_SOFTRST_CON[index] = VAL_MASK_WE(1U << shift, 0U << shift);
|
} else {
|
PMUCRU->CRU_SOFTRST_CON[index - CRU_SRST_CON_CNT] = VAL_MASK_WE(1U << shift, 0U << shift);
|
}
|
#else
|
CRU->CRU_SOFTRST_CON[index] = VAL_MASK_WE(1U << shift, 0U << shift);
|
#endif
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief IP Clock set div API
|
* @param divName: div id(Contains div offset, shift, mask information)
|
* @param divValue: div value
|
* @return NONE
|
*/
|
HAL_Status HAL_CRU_ClkSetDiv(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);
|
HAL_ASSERT(shift < 16);
|
mask = CLK_DIV_GET_MASK(divName);
|
if (divValue > mask) {
|
divValue = mask;
|
}
|
|
#ifdef CRU_CLK_DIV_CON_CNT
|
if (index < CRU_CLK_DIV_CON_CNT) {
|
CRU->CRU_CLKSEL_CON[index] = VAL_MASK_WE(mask, (divValue - 1U) << shift);
|
} else {
|
#ifdef PMUCRU_BASE
|
PMUCRU->CRU_CLKSEL_CON[index - CRU_CLK_DIV_CON_CNT] = VAL_MASK_WE(mask, (divValue - 1U) << shift);
|
#else
|
CRU->PMU_CLKSEL_CON[index - CRU_CLK_DIV_CON_CNT] = VAL_MASK_WE(mask, (divValue - 1U) << shift);
|
#endif
|
}
|
#else
|
CRU->CRU_CLKSEL_CON[index] = VAL_MASK_WE(mask, (divValue - 1U) << shift);
|
#endif
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief IP Clock get div API
|
* @param divName: div id(Contains div offset, shift, mask information)
|
* @return div value
|
*/
|
uint32_t HAL_CRU_ClkGetDiv(uint32_t divName)
|
{
|
uint32_t shift, mask, index, divValue;
|
|
index = CLK_DIV_GET_REG_OFFSET(divName);
|
shift = CLK_DIV_GET_BITS_SHIFT(divName);
|
HAL_ASSERT(shift < 16);
|
mask = CLK_DIV_GET_MASK(divName);
|
|
#ifdef CRU_CLK_DIV_CON_CNT
|
if (index < CRU_CLK_DIV_CON_CNT) {
|
divValue = ((((CRU->CRU_CLKSEL_CON[index]) & mask) >> shift) + 1);
|
} else {
|
#ifdef PMUCRU_BASE
|
divValue = ((((PMUCRU->CRU_CLKSEL_CON[index - CRU_CLK_DIV_CON_CNT]) & mask) >> shift) + 1);
|
#else
|
divValue = ((((CRU->PMU_CLKSEL_CON[index - CRU_CLK_DIV_CON_CNT]) & mask) >> shift) + 1);
|
#endif
|
}
|
#else
|
divValue = ((((CRU->CRU_CLKSEL_CON[index]) & mask) >> shift) + 1);
|
#endif
|
|
return divValue;
|
}
|
|
/**
|
* @brief IP Clock set mux API
|
* @param muxName: mux id(Contains mux offset, shift, mask information)
|
* @param muxValue: mux value
|
* @return NONE
|
*/
|
HAL_SECTION_SRAM_CODE
|
HAL_Status HAL_CRU_ClkSetMux(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);
|
HAL_ASSERT(shift < 16);
|
mask = CLK_MUX_GET_MASK(muxName);
|
|
#ifdef CRU_CLK_SEL_CON_CNT
|
if (index < CRU_CLK_DIV_CON_CNT) {
|
CRU->CRU_CLKSEL_CON[index] = VAL_MASK_WE(mask, muxValue << shift);
|
} else {
|
#ifdef PMUCRU_BASE
|
PMUCRU->CRU_CLKSEL_CON[index - CRU_CLK_SEL_CON_CNT] = VAL_MASK_WE(mask, muxValue << shift);
|
#else
|
CRU->PMU_CLKSEL_CON[index - CRU_CLK_SEL_CON_CNT] = VAL_MASK_WE(mask, muxValue << shift);
|
#endif
|
}
|
#else
|
CRU->CRU_CLKSEL_CON[index] = VAL_MASK_WE(mask, muxValue << shift);
|
#endif
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief IP Clock get mux API
|
* @param muxName: mux id(Contains mux offset, shift, mask information)
|
* @return mux value
|
*/
|
HAL_SECTION_SRAM_CODE
|
uint32_t HAL_CRU_ClkGetMux(uint32_t muxName)
|
{
|
uint32_t shift, mask, index, muxValue;
|
|
index = CLK_MUX_GET_REG_OFFSET(muxName);
|
shift = CLK_MUX_GET_BITS_SHIFT(muxName);
|
HAL_ASSERT(shift < 16);
|
mask = CLK_MUX_GET_MASK(muxName);
|
|
#ifdef CRU_CLK_SEL_CON_CNT
|
if (index < CRU_CLK_SEL_CON_CNT) {
|
muxValue = ((CRU->CRU_CLKSEL_CON[index] & mask) >> shift);
|
} else {
|
#ifdef PMUCRU_BASE
|
muxValue = ((PMUCRU->CRU_CLKSEL_CON[index - CRU_CLK_SEL_CON_CNT] & mask) >> shift);
|
#else
|
muxValue = ((CRU->PMU_CLKSEL_CON[index - CRU_CLK_SEL_CON_CNT] & mask) >> shift);
|
#endif
|
}
|
#else
|
muxValue = ((CRU->CRU_CLKSEL_CON[index] & mask) >> shift);
|
#endif
|
|
return muxValue;
|
}
|
|
/**
|
* @brief Get frac div config.
|
* @param rateOut: clk out rate.
|
* @param rate: clk src rate.
|
* @param numerator: the returned numerator.
|
* @param denominator: the returned denominator.
|
* @return HAL_Status.
|
*/
|
HAL_Status HAL_CRU_FracdivGetConfig(uint32_t rateOut, uint32_t rate,
|
uint32_t *numerator,
|
uint32_t *denominator)
|
{
|
uint32_t gcdVal;
|
|
gcdVal = CRU_Gcd(rate, rateOut);
|
if (!gcdVal) {
|
return HAL_ERROR;
|
}
|
|
*numerator = rateOut / gcdVal;
|
*denominator = rate / gcdVal;
|
|
if (*numerator < 4) {
|
*numerator *= 4;
|
*denominator *= 4;
|
}
|
if (*numerator > 0xffff || *denominator > 0xffff) {
|
return HAL_INVAL;
|
}
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief Get Np5 best div.
|
* @param clockName: clk id.
|
* @param rate: clk rate.
|
* @param pRate: clk parent rate
|
* @param bestdiv: the returned bestdiv.
|
* @return HAL_Status.
|
*/
|
HAL_Status HAL_CRU_ClkNp5BestDiv(eCLOCK_Name clockName, uint32_t rate, uint32_t pRate, uint32_t *bestdiv)
|
{
|
uint32_t div = CLK_GET_DIV(clockName);
|
uint32_t maxDiv = CLK_DIV_GET_MASK(div);
|
uint32_t i;
|
|
for (i = 0; i < maxDiv; i++) {
|
if (((pRate * 2) == (rate * (i * 2 + 3)))) {
|
*bestdiv = i;
|
|
return HAL_OK;
|
}
|
}
|
|
return HAL_ERROR;
|
}
|
|
/**
|
* @brief vop dclk enable.
|
* @param gateId: gate id
|
* @return HAL_Status.
|
* @attention these APIs allow direct use in the HAL layer.
|
*/
|
__WEAK HAL_Status HAL_CRU_VopDclkEnable(uint32_t gateId)
|
{
|
HAL_CRU_ClkEnable(gateId);
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief vop dclk disable.
|
* @param gateId: gate id
|
* @return HAL_Status.
|
* @attention these APIs allow direct use in the HAL layer.
|
*/
|
__WEAK HAL_Status HAL_CRU_VopDclkDisable(uint32_t gateId)
|
{
|
HAL_CRU_ClkDisable(gateId);
|
|
return HAL_OK;
|
}
|
|
/**
|
* @brief assert CRU global software reset.
|
* @param type: global software reset type.
|
* @return HAL_INVAL if the SoC does not support.
|
*/
|
HAL_Status HAL_CRU_SetGlbSrst(eCRU_GlbSrstType type)
|
{
|
#ifdef CRU_GLB_SRST_FST_VALUE_OFFSET
|
if (type == GLB_SRST_FST) {
|
CRU->GLB_SRST_FST_VALUE = GLB_SRST_FST;
|
}
|
#endif
|
#ifdef CRU_GLB_SRST_SND_VALUE_OFFSET
|
if (type == GLB_SRST_SND) {
|
CRU->GLB_SRST_SND_VALUE = GLB_SRST_SND;
|
}
|
#endif
|
|
return HAL_INVAL;
|
}
|
|
/** @} */
|
|
/** @} */
|
|
/** @} */
|