rtt-f030/bsp/lpc5410x/Libraries/lpc_chip/chip_5410x/pll_5410x.c

867 lines
28 KiB
C

/*
* @brief LPC5410X PLL driver
*
* @note
* Copyright(C) NXP Semiconductors, 2014
* All rights reserved.
*
* @par
* Software that is described herein is for illustrative purposes only
* which provides customers with programming information regarding the
* LPC products. This software is supplied "AS IS" without any warranties of
* any kind, and NXP Semiconductors and its licenser disclaim any and
* all warranties, express or implied, including all implied warranties of
* merchantability, fitness for a particular purpose and non-infringement of
* intellectual property rights. NXP Semiconductors assumes no responsibility
* or liability for the use of the software, conveys no license or rights under any
* patent, copyright, mask work right, or any other intellectual property rights in
* or to any products. NXP Semiconductors reserves the right to make changes
* in the software without notification. NXP Semiconductors also makes no
* representation or warranty that such application will be suitable for the
* specified use without further testing or modification.
*
* @par
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, under NXP Semiconductors' and its
* licensor's relevant copyrights in the software, without fee, provided that it
* is used in conjunction with NXP Semiconductors microcontrollers. This
* copyright, permission, and disclaimer notice must appear in all copies of
* this code.
*/
#include "chip.h"
/*****************************************************************************
* Private types/enumerations/variables
****************************************************************************/
#define NVALMAX (0x100)
#define PVALMAX (0x20)
#define MVALMAX (0x8000)
/* SYS PLL related bit fields */
#define SYS_PLL_SELR(d) (((d) & 0xf) << 0) /*!< Bandwidth select R value */
#define SYS_PLL_SELI(d) (((d) & 0x3f) << 4) /*!< Bandwidth select I value */
#define SYS_PLL_SELP(d) (((d) & 0x1f) << 10) /*!< Bandwidth select P value */
#define SYS_PLL_BYPASS (1 << 15) /*!< Enable PLL bypass */
#define SYS_PLL_BYPASSCCODIV2 (1 << 16) /*!< Enable bypass of extra divider by 2 */
#define SYS_PLL_UPLIMOFF (1 << 17) /*!< Enable spread spectrum/fractional mode */
#define SYS_PLL_BANDSEL (1 << 18) /*!< Enable MDEC control */
#define SYS_PLL_DIRECTI (1 << 19) /*!< PLL0 direct input enable */
#define SYS_PLL_DIRECTO (1 << 20) /*!< PLL0 direct output enable */
// #define FRAC_BITS_SELI (8) // For retaining fractions in divisions
#define PLL_SSCG0_MDEC_VAL_P (0) // MDEC is in bits 16 downto 0
#define PLL_SSCG0_MDEC_VAL_M (0x1FFFFUL << PLL_SSCG0_MDEC_VAL_P) // NDEC is in bits 9 downto 0
#define PLL_NDEC_VAL_P (0) // NDEC is in bits 9:0
#define PLL_NDEC_VAL_M (0x3FFUL << PLL_NDEC_VAL_P)
#define PLL_PDEC_VAL_P (0) // PDEC is in bits 6:0
#define PLL_PDEC_VAL_M (0x3FFUL << PLL_PDEC_VAL_P)
#define PLL_MIN_CCO_FREQ_MHZ (75000000)
#define PLL_MAX_CCO_FREQ_MHZ (150000000)
#define PLL_LOWER_IN_LIMIT (4000) /*!< Minimum PLL input rate */
#define PLL_MIN_IN_SSMODE (2000000)
#define PLL_MAX_IN_SSMODE (4000000)
// Middle of the range values for spread-spectrum
#define PLL_SSCG_MF_FREQ_VALUE 4
#define PLL_SSCG_MC_COMP_VALUE 2
#define PLL_SSCG_MR_DEPTH_VALUE 4
#define PLL_SSCG_DITHER_VALUE 0
// pll SYSPLLCTRL Bits
#define SYSCON_SYSPLLCTRL_SELR_P 0
#define SYSCON_SYSPLLCTRL_SELR_M (0xFUL << SYSCON_SYSPLLCTRL_SELR_P)
#define SYSCON_SYSPLLCTRL_SELI_P 4
#define SYSCON_SYSPLLCTRL_SELI_M (0x3FUL << SYSCON_SYSPLLCTRL_SELI_P)
#define SYSCON_SYSPLLCTRL_SELP_P 10
#define SYSCON_SYSPLLCTRL_SELP_M (0x1FUL << SYSCON_SYSPLLCTRL_SELP_P)
#define SYSCON_SYSPLLCTRL_BYPASS_P 15 // sys_pll150_ctrl
#define SYSCON_SYSPLLCTRL_BYPASS (1UL << SYSCON_SYSPLLCTRL_BYPASS_P)
#define SYSCON_SYSPLLCTRL_BYPASS_FBDIV2_P 16
#define SYSCON_SYSPLLCTRL_BYPASS_FBDIV2 (1UL << SYSCON_SYSPLLCTRL_BYPASS_FBDIV2_P)
#define SYSCON_SYSPLLCTRL_UPLIMOFF_P 17
#define SYSCON_SYSPLLCTRL_UPLIMOFF (1UL << SYSCON_SYSPLLCTRL_UPLIMOFF_P)
#define SYSCON_SYSPLLCTRL_BANDSEL_SSCGREG_N_P 18
#define SYSCON_SYSPLLCTRL_BANDSEL_SSCGREG_N (1UL << SYSCON_SYSPLLCTRL_BANDSEL_SSCGREG_N_P)
#define SYSCON_SYSPLLCTRL_DIRECTI_P 19
#define SYSCON_SYSPLLCTRL_DIRECTI (1UL << SYSCON_SYSPLLCTRL_DIRECTI_P)
#define SYSCON_SYSPLLCTRL_DIRECTO_P 20
#define SYSCON_SYSPLLCTRL_DIRECTO (1UL << SYSCON_SYSPLLCTRL_DIRECTO_P)
#define SYSCON_SYSPLLSTAT_LOCK_P 0
#define SYSCON_SYSPLLSTAT_LOCK (1UL << SYSCON_SYSPLLSTAT_LOCK_P)
#define PLL_CTRL_BYPASS_P 15 // sys_pll150_ctrl
#define PLL_CTRL_BYPASS_FBDIV2_P 16
#define PLL_CTRL_UPLIMOFF_P 17
#define PLL_CTRL_BANDSEL_SSCGREG_N_P 18
#define PLL_CTRL_DIRECTI_P 19
#define PLL_CTRL_DIRECTO_P 20
#define PLL_CTRL_BYPASS (1 << PLL_CTRL_BYPASS_P)
#define PLL_CTRL_DIRECTI (1 << PLL_CTRL_DIRECTI_P)
#define PLL_CTRL_DIRECTO (1 << PLL_CTRL_DIRECTO_P)
#define PLL_CTRL_UPLIMOFF (1 << PLL_CTRL_UPLIMOFF_P)
#define PLL_CTRL_BANDSEL_SSCGREG_N (1 << PLL_CTRL_BANDSEL_SSCGREG_N_P)
#define PLL_CTRL_BYPASS_FBDIV2 (1 << PLL_CTRL_BYPASS_FBDIV2_P)
// SSCG control[0]
// #define PLL_SSCG0_MDEC_VAL_P 0 // MDEC is in bits 16 downto 0
#define PLL_SSCG0_MREQ_P 17
#define PLL_SSCG0_SEL_EXT_SSCG_N_P 18
#define PLL_SSCG0_SEL_EXT_SSCG_N (1 << PLL_SSCG0_SEL_EXT_SSCG_N_P)
#define PLL_SSCG0_MREQ (1 << PLL_SSCG0_MREQ_P)
// SSCG control[1]
#define PLL_SSCG1_MD_REQ_P 19
#define PLL_SSCG1_MOD_PD_SSCGCLK_N_P 28
#define PLL_SSCG1_DITHER_P 29
#define PLL_SSCG1_MOD_PD_SSCGCLK_N (1 << PLL_SSCG1_MOD_PD_SSCGCLK_N_P)
#define PLL_SSCG1_DITHER (1 << PLL_SSCG1_DITHER_P)
#define PLL_SSCG1_MD_REQ (1 << PLL_SSCG1_MD_REQ_P)
// PLL NDEC reg
#define PLL_NDEC_VAL_SET(value) (((unsigned long) (value) << PLL_NDEC_VAL_P) & PLL_NDEC_VAL_M)
#define PLL_NDEC_NREQ_P 10
#define PLL_NDEC_NREQ (1 << PLL_NDEC_NREQ_P)
// PLL PDEC reg
#define PLL_PDEC_VAL_SET(value) (((unsigned long) (value) << PLL_PDEC_VAL_P) & PLL_PDEC_VAL_M)
#define PLL_PDEC_PREQ_P 7
#define PLL_PDEC_PREQ (1 << PLL_PDEC_PREQ_P)
// SSCG control[0]
#define PLL_SSCG0_MDEC_VAL_SET(value) (((unsigned long) (value) << PLL_SSCG0_MDEC_VAL_P) & PLL_SSCG0_MDEC_VAL_M)
#define PLL_SSCG0_MREQ_P 17
#define PLL_SSCG0_MREQ (1 << PLL_SSCG0_MREQ_P)
#define PLL_SSCG0_SEL_EXT_SSCG_N_P 18
#define PLL_SSCG0_SEL_EXT_SSCG_N (1 << PLL_SSCG0_SEL_EXT_SSCG_N_P)
// SSCG control[1]
#define PLL_SSCG1_MD_FRACT_P 0
#define PLL_SSCG1_MD_INT_P 11
#define PLL_SSCG1_MF_P 20
#define PLL_SSCG1_MC_P 26
#define PLL_SSCG1_MR_P 23
#define PLL_SSCG1_MD_FRACT_M (0x7FFUL << PLL_SSCG1_MD_FRACT_P)
#define PLL_SSCG1_MD_INT_M (0xFFUL << PLL_SSCG1_MD_INT_P)
#define PLL_SSCG1_MF_M (0x7UL << PLL_SSCG1_MF_P)
#define PLL_SSCG1_MC_M (0x3UL << PLL_SSCG1_MC_P)
#define PLL_SSCG1_MR_M (0x7UL << PLL_SSCG1_MR_P)
#define PLL_SSCG1_MD_FRACT_SET(value) (((unsigned long) (value) << \
PLL_SSCG1_MD_FRACT_P) & PLL_SSCG1_MD_FRACT_M)
#define PLL_SSCG1_MD_INT_SET(value) (((unsigned long) (value) << \
PLL_SSCG1_MD_INT_P) & PLL_SSCG1_MD_INT_M)
// #define PLL_SSCG1_MF_SET(value) (((unsigned long) (value) << \
// // PLL_SSCG1_MF_P) & PLL_SSCG1_MF_M)
// #define PLL_SSCG1_MC_SET(value) (((unsigned long) (value) << \
// // PLL_SSCG1_MC_P) & PLL_SSCG1_MC_M)
// #define PLL_SSCG1_MR_SET(value) (((unsigned long) (value) << \
// // PLL_SSCG1_MR_P) & PLL_SSCG1_MR_M)
// Middle of the range values for spread-spectrum
#define PLL0_SSCG_MF_FREQ_VALUE 4
#define PLL0_SSCG_MC_COMP_VALUE 2
#define PLL0_SSCG_MR_DEPTH_VALUE 4
#define PLL0_SSCG_DITHER_VALUE 0
#define PLL_MAX_N_DIV 0x100
/* Saved value of PLL output rate, computed whenever needed to save run-time
computation on each call to retrive the PLL rate. */
static uint32_t curPllRate;
/*****************************************************************************
* Public types/enumerations/variables
****************************************************************************/
/*****************************************************************************
* Private functions
****************************************************************************/
/* Find encoded NDEC value for raw N value, max N = NVALMAX */
static uint32_t pllEncodeN(uint32_t N)
{
uint32_t x, i;
/* Find NDec */
switch (N) {
case 0:
x = 0xFFF;
break;
case 1:
x = 0x302;
break;
case 2:
x = 0x202;
break;
default:
x = 0x080;
for (i = N; i <= NVALMAX; i++) {
x = (((x ^ (x >> 2) ^ (x >> 3) ^ (x >> 4)) & 1) << 7) | ((x >> 1) & 0x7F);
}
break;
}
return x & (PLL_NDEC_VAL_M >> PLL_NDEC_VAL_P);
}
/* Find decoded N value for raw NDEC value */
static uint32_t pllDecodeN(uint32_t NDEC)
{
uint32_t n, x, i;
/* Find NDec */
switch (NDEC) {
case 0xFFF:
n = 0;
break;
case 0x302:
n = 1;
break;
case 0x202:
n = 2;
break;
default:
x = 0x080;
n = 0xFFFFFFFF;
for (i = NVALMAX; ((i >= 3) && (n == 0xFFFFFFFF)); i--) {
x = (((x ^ (x >> 2) ^ (x >> 3) ^ (x >> 4)) & 1) << 7) | ((x >> 1) & 0x7F);
if ((x & (PLL_NDEC_VAL_M >> PLL_NDEC_VAL_P)) == NDEC) {
/* Decoded value of NDEC */
n = i;
}
}
break;
}
return n;
}
/* Find encoded PDEC value for raw P value, max P = PVALMAX */
static uint32_t pllEncodeP(uint32_t P)
{
uint32_t x, i;
/* Find PDec */
switch (P) {
case 0:
x = 0xFF;
break;
case 1:
x = 0x62;
break;
case 2:
x = 0x42;
break;
default:
x = 0x10;
for (i = P; i <= PVALMAX; i++) {
x = (((x ^ (x >> 2)) & 1) << 4) | ((x >> 1) & 0xF);
}
break;
}
return x & (PLL_PDEC_VAL_M >> PLL_PDEC_VAL_P);
}
/* Find decoded P value for raw PDEC value */
static uint32_t pllDecodeP(uint32_t PDEC)
{
uint32_t p, x, i;
/* Find PDec */
switch (PDEC) {
case 0xFF:
p = 0;
break;
case 0x62:
p = 1;
break;
case 0x42:
p = 2;
break;
default:
x = 0x10;
p = 0xFFFFFFFF;
for (i = PVALMAX; ((i >= 3) && (p == 0xFFFFFFFF)); i--) {
x = (((x ^ (x >> 2)) & 1) << 4) | ((x >> 1) & 0xF);
if ((x & (PLL_PDEC_VAL_M >> PLL_PDEC_VAL_P)) == PDEC) {
/* Decoded value of PDEC */
p = i;
}
}
break;
}
return p;
}
/* Find encoded MDEC value for raw M value, max M = MVALMAX */
static uint32_t pllEncodeM(uint32_t M)
{
uint32_t i, x;
/* Find MDec */
switch (M) {
case 0:
x = 0xFFFFF;
break;
case 1:
x = 0x18003;
break;
case 2:
x = 0x10003;
break;
default:
x = 0x04000;
for (i = M; i <= MVALMAX; i++) {
x = (((x ^ (x >> 1)) & 1) << 14) | ((x >> 1) & 0x3FFF);
}
break;
}
return x & (PLL_SSCG0_MDEC_VAL_M >> PLL_SSCG0_MDEC_VAL_P);
}
/* Find decoded M value for raw MDEC value */
static uint32_t pllDecodeM(uint32_t MDEC)
{
uint32_t m, i, x;
/* Find MDec */
switch (MDEC) {
case 0xFFFFF:
m = 0;
break;
case 0x18003:
m = 1;
break;
case 0x10003:
m = 2;
break;
default:
x = 0x04000;
m = 0xFFFFFFFF;
for (i = MVALMAX; ((i >= 3) && (m == 0xFFFFFFFF)); i--) {
x = (((x ^ (x >> 1)) & 1) << 14) | ((x >> 1) & 0x3FFF);
if ((x & (PLL_SSCG0_MDEC_VAL_M >> PLL_SSCG0_MDEC_VAL_P)) == MDEC) {
/* Decoded value of MDEC */
m = i;
}
}
break;
}
return m;
}
/* Find SELP, SELI, and SELR values for raw M value, max M = MVALMAX */
static void pllFindSel(uint32_t M, bool bypassFBDIV2, uint32_t *pSelP, uint32_t *pSelI, uint32_t *pSelR)
{
/* Bypass divider? */
if (bypassFBDIV2) {
M = M / 2;
}
/* bandwidth: compute selP from Multiplier */
if (M < 60) {
*pSelP = (M >> 1) + 1;
}
else {
*pSelP = PVALMAX - 1;
}
/* bandwidth: compute selI from Multiplier */
if (M > 16384) {
*pSelI = 1;
}
else if (M > 8192) {
*pSelI = 2;
}
else if (M > 2048) {
*pSelI = 4;
}
else if (M >= 501) {
*pSelI = 8;
}
else if (M >= 60) {
*pSelI = 4 * (1024 / (M + 9));
}
else {
*pSelI = (M & 0x3C) + 4;
}
if (*pSelI > (SYSCON_SYSPLLCTRL_SELI_M >> SYSCON_SYSPLLCTRL_SELI_P)) {
*pSelI = (SYSCON_SYSPLLCTRL_SELI_M >> SYSCON_SYSPLLCTRL_SELI_P);
}
*pSelR = 0;
}
/* Get predivider (N) from PLL NDEC setting */
uint32_t findPllPreDiv(uint32_t ctrlReg, uint32_t nDecReg)
{
uint32_t preDiv = 1;
/* Direct input is not used? */
if ((ctrlReg & SYSCON_SYSPLLCTRL_DIRECTI) == 0) {
/* Decode NDEC value to get (N) pre divider */
preDiv = pllDecodeN(nDecReg & 0x3FF);
if (preDiv == 0) {
preDiv = 1;
}
}
/* Adjusted by 1, directi is used to bypass */
return preDiv;
}
/* Get postdivider (P) from PLL PDEC setting */
uint32_t findPllPostDiv(uint32_t ctrlReg, uint32_t pDecReg)
{
uint32_t postDiv = 1;
/* Direct input is not used? */
if ((ctrlReg & SYS_PLL_DIRECTO) == 0) {
/* Decode PDEC value to get (P) post divider */
postDiv = 2 * pllDecodeP(pDecReg & 0x7F);
if (postDiv == 0) {
postDiv = 2;
}
}
/* Adjusted by 1, directo is used to bypass */
return postDiv;
}
/* Get multiplier (M) from PLL MDEC and BYPASS_FBDIV2 settings */
uint32_t findPllMMult(uint32_t ctrlReg, uint32_t mDecReg)
{
uint32_t mMult = 1;
/* Decode MDEC value to get (M) multiplier */
mMult = pllDecodeM(mDecReg & 0x1FFFF);
/* Extra divided by 2 needed? */
if ((ctrlReg & SYSCON_SYSPLLCTRL_BYPASS_FBDIV2) == 0) {
mMult = mMult >> 1;
}
if (mMult == 0) {
mMult = 1;
}
return mMult;
}
static uint32_t FindGreatestCommonDivisor(uint32_t m, uint32_t n)
{
uint32_t tmp;
while (n != 0) {
tmp = n;
n = m % n;
m = tmp;
}
return m;
}
/* Set PLL output based on desired output rate */
static PLL_ERROR_T Chip_Clock_GetPllConfig(uint32_t finHz, uint32_t foutHz, PLL_SETUP_T *pSetup,
bool useFeedbackDiv2, bool useSS)
{
uint32_t nDivOutHz, fccoHz, multFccoDiv;
uint32_t pllPreDivider, pllMultiplier, pllBypassFBDIV2, pllPostDivider;
uint32_t pllDirectInput, pllDirectOutput;
uint32_t pllSelP, pllSelI, pllSelR, bandsel, uplimoff;
/* Baseline parameters (no input or output dividers) */
pllPreDivider = 1; /* 1 implies pre-divider will be disabled */
pllPostDivider = 0; /* 0 implies post-divider will be disabled */
pllDirectOutput = 1;
if (useFeedbackDiv2) {
/* Using feedback divider for M, so disable bypass */
pllBypassFBDIV2 = 0;
}
else {
pllBypassFBDIV2 = 1;
}
multFccoDiv = (2 - pllBypassFBDIV2);
/* Verify output rate parameter */
if (foutHz > PLL_MAX_CCO_FREQ_MHZ) {
/* Maximum PLL output with post divider=1 cannot go above this frequency */
return PLL_ERROR_OUTPUT_TOO_HIGH;
}
if (foutHz < (PLL_MIN_CCO_FREQ_MHZ / (PVALMAX << 1))) {
/* Minmum PLL output with maximum post divider cannot go below this frequency */
return PLL_ERROR_OUTPUT_TOO_LOW;
}
/* If using SS mode, input clock needs to be between 2MHz and 4MHz */
if (useSS) {
/* Verify input rate parameter */
if (finHz < PLL_MIN_IN_SSMODE) {
/* Input clock into the PLL cannot be lower than this */
return PLL_ERROR_INPUT_TOO_LOW;
}
/* PLL input in SS mode must be under 4MHz */
pllPreDivider = finHz / ((PLL_MIN_IN_SSMODE + PLL_MAX_IN_SSMODE) / 2);
if (pllPreDivider > NVALMAX) {
return PLL_ERROR_INPUT_TOO_HIGH;
}
}
else {
/* Verify input rate parameter */
if (finHz < PLL_LOWER_IN_LIMIT) {
/* Input clock into the PLL cannot be lower than this */
return PLL_ERROR_INPUT_TOO_LOW;
}
}
/* Find the optimal CCO frequency for the output and input that
will keep it inside the PLL CCO range. This may require
tweaking the post-divider for the PLL. */
fccoHz = foutHz;
while (fccoHz < PLL_MIN_CCO_FREQ_MHZ) {
/* CCO output is less than minimum CCO range, so the CCO output
needs to be bumped up and the post-divider is used to bring
the PLL output back down. */
pllPostDivider++;
if (pllPostDivider > PVALMAX) {
return PLL_ERROR_OUTSIDE_INTLIMIT;
}
/* Target CCO goes up, PLL output goes down */
fccoHz = foutHz * (pllPostDivider * 2);
pllDirectOutput = 0;
}
/* Determine if a pre-divider is needed to get the best frequency */
if ((finHz > PLL_LOWER_IN_LIMIT) && (fccoHz >= finHz) && (useSS == false)) {
uint32_t a = FindGreatestCommonDivisor(fccoHz, (multFccoDiv * finHz));
if (a > 20000) {
a = (multFccoDiv * finHz) / a;
if ((a != 0) && (a < PLL_MAX_N_DIV)) {
pllPreDivider = a;
}
}
}
/* Bypass pre-divider hardware if pre-divider is 1 */
if (pllPreDivider > 1) {
pllDirectInput = 0;
}
else {
pllDirectInput = 1;
}
/* Determine PLL multipler */
nDivOutHz = (finHz / pllPreDivider);
pllMultiplier = (fccoHz / nDivOutHz) / multFccoDiv;
/* Find optimal values for filter */
if (useSS == false) {
/* Will bumping up M by 1 get us closer to the desired CCO frequency? */
if ((nDivOutHz * ((multFccoDiv * pllMultiplier * 2) + 1)) < (fccoHz * 2)) {
pllMultiplier++;
}
/* Setup filtering */
pllFindSel(pllMultiplier, pllBypassFBDIV2, &pllSelP, &pllSelI, &pllSelR);
bandsel = 1;
uplimoff = 0;
/* Get encoded value for M (mult) and use manual filter, disable SS mode */
pSetup->SYSPLLSSCTRL[0] = (PLL_SSCG0_MDEC_VAL_SET(pllEncodeM(pllMultiplier)) |
(1 << PLL_SSCG0_SEL_EXT_SSCG_N_P));
/* Power down SSC, not used */
pSetup->SYSPLLSSCTRL[1] = PLL_SSCG1_MOD_PD_SSCGCLK_N;
}
else {
uint64_t fc;
/* Filtering will be handled by SSC */
pllSelR = pllSelI = pllSelP = 0;
bandsel = 0;
uplimoff = 1;
/* The PLL multiplier will get very close and slightly under the
desired target frequency. A small fractional component can be
added to fine tune the frequency upwards to the target. */
fc = ((uint64_t) (fccoHz % (multFccoDiv * nDivOutHz)) << 11) / (multFccoDiv * nDivOutHz);
/* MDEC set by SSC */
pSetup->SYSPLLSSCTRL[0] = 0;
/* Set multiplier */
pSetup->SYSPLLSSCTRL[1] = PLL_SSCG1_MD_INT_SET(pllMultiplier) |
PLL_SSCG1_MD_FRACT_SET((uint32_t) fc);
}
/* Get encoded values for N (prediv) and P (postdiv) */
pSetup->SYSPLLNDEC = PLL_NDEC_VAL_SET(pllEncodeN(pllPreDivider));
pSetup->SYSPLLPDEC = PLL_PDEC_VAL_SET(pllEncodeP(pllPostDivider));
/* PLL control */
pSetup->SYSPLLCTRL =
(pllSelR << SYSCON_SYSPLLCTRL_SELR_P) | /* Filter coefficient */
(pllSelI << SYSCON_SYSPLLCTRL_SELI_P) | /* Filter coefficient */
(pllSelP << SYSCON_SYSPLLCTRL_SELP_P) | /* Filter coefficient */
(0 << SYSCON_SYSPLLCTRL_BYPASS_P) | /* PLL bypass mode disabled */
(pllBypassFBDIV2 << SYSCON_SYSPLLCTRL_BYPASS_FBDIV2_P) | /* Extra M / 2 divider? */
(uplimoff << SYSCON_SYSPLLCTRL_UPLIMOFF_P) | /* SS/fractional mode disabled */
(bandsel << SYSCON_SYSPLLCTRL_BANDSEL_SSCGREG_N_P) | /* Manual bandwidth selection enabled */
(pllDirectInput << SYSCON_SYSPLLCTRL_DIRECTI_P) | /* Bypass pre-divider? */
(pllDirectOutput << SYSCON_SYSPLLCTRL_DIRECTO_P); /* Bypass post-divider? */
return PLL_ERROR_SUCCESS;
}
/* Update local PLL rate variable */
static void Chip_Clock_GetSystemPLLOutFromSetupUpdate(PLL_SETUP_T *pSetup)
{
curPllRate = Chip_Clock_GetSystemPLLOutFromSetup(pSetup);
}
/*****************************************************************************
* Public functions
****************************************************************************/
/* Return System PLL input clock rate */
uint32_t Chip_Clock_GetSystemPLLInClockRate(void)
{
uint32_t clkRate = 0;
switch ((CHIP_SYSCON_PLLCLKSRC_T) (LPC_SYSCON->SYSPLLCLKSEL & 0x3)) {
case SYSCON_PLLCLKSRC_IRC:
clkRate = Chip_Clock_GetIntOscRate();
break;
case SYSCON_PLLCLKSRC_CLKIN:
clkRate = Chip_Clock_GetExtClockInRate();
break;
case SYSCON_PLLCLKSRC_WDTOSC:
clkRate = Chip_Clock_GetWDTOSCRate();
break;
case SYSCON_PLLCLKSRC_RTC:
clkRate = Chip_Clock_GetRTCOscRate();
break;
}
return clkRate;
}
/* Return System PLL output clock rate from setup structure */
uint32_t Chip_Clock_GetSystemPLLOutFromSetup(PLL_SETUP_T *pSetup)
{
uint32_t prediv, postdiv, mMult, inPllRate;
uint64_t workRate;
inPllRate = Chip_Clock_GetSystemPLLInClockRate();
if ((pSetup->SYSPLLCTRL & SYSCON_SYSPLLCTRL_BYPASS_P) == 0) {
/* PLL is not in bypass mode, get pre-divider, post-divider, and M divider */
prediv = findPllPreDiv(pSetup->SYSPLLCTRL, pSetup->SYSPLLNDEC);
postdiv = findPllPostDiv(pSetup->SYSPLLCTRL, pSetup->SYSPLLPDEC);
/* Adjust input clock */
inPllRate = inPllRate / prediv;
/* If using the SS, use the multiplier */
if (pSetup->SYSPLLSSCTRL[1] & PLL_SSCG1_MOD_PD_SSCGCLK_N) {
/* MDEC used for rate */
mMult = findPllMMult(pSetup->SYSPLLCTRL, pSetup->SYSPLLSSCTRL[0]);
workRate = (uint64_t) inPllRate * (uint64_t) mMult;
}
else {
uint64_t fract;
/* SS multipler used for rate */
mMult = (pSetup->SYSPLLSSCTRL[1] & PLL_SSCG1_MD_INT_M) >> PLL_SSCG1_MD_INT_P;
workRate = (uint64_t) inPllRate * (uint64_t) mMult;
/* Adjust by fractional */
fract = (uint64_t) (pSetup->SYSPLLSSCTRL[1] & PLL_SSCG1_MD_FRACT_M) >> PLL_SSCG1_MD_FRACT_P;
workRate = workRate + ((inPllRate * fract) / 0x7FF);
}
workRate = workRate / ((uint64_t) postdiv);
}
else {
/* In bypass mode */
workRate = (uint64_t) inPllRate;
}
return (uint32_t) workRate;
}
/* Return System PLL output clock rate */
uint32_t Chip_Clock_GetSystemPLLOutClockRate(bool recompute)
{
PLL_SETUP_T Setup;
uint32_t rate;
if ((recompute) || (curPllRate == 0)) {
Setup.SYSPLLCTRL = LPC_SYSCON->SYSPLLCTRL;
Setup.SYSPLLNDEC = LPC_SYSCON->SYSPLLNDEC;
Setup.SYSPLLPDEC = LPC_SYSCON->SYSPLLPDEC;
Setup.SYSPLLSSCTRL[0] = LPC_SYSCON->SYSPLLSSCTRL[0];
Setup.SYSPLLSSCTRL[1] = LPC_SYSCON->SYSPLLSSCTRL[1];
Chip_Clock_GetSystemPLLOutFromSetupUpdate(&Setup);
}
rate = curPllRate;
return rate;
}
/* Enables and disables PLL bypass mode */
void Chip_Clock_SetBypassPLL(bool bypass)
{
if (bypass) {
LPC_SYSCON->SYSPLLCTRL |= SYSCON_SYSPLLCTRL_BYPASS_P;
}
else {
LPC_SYSCON->SYSPLLCTRL &= ~SYSCON_SYSPLLCTRL_BYPASS_P;
}
}
/* Set PLL output based on the passed PLL setup data */
PLL_ERROR_T Chip_Clock_SetupPLLData(PLL_CONFIG_T *pControl, PLL_SETUP_T *pSetup)
{
uint32_t inRate;
bool useSS = (bool) ((pControl->flags & PLL_CONFIGFLAG_FORCENOFRACT) == 0);
PLL_ERROR_T pllError;
/* Determine input rate for the PLL */
if ((pControl->flags & PLL_CONFIGFLAG_USEINRATE) != 0) {
inRate = pControl->InputRate;
}
else {
inRate = Chip_Clock_GetSystemPLLInClockRate();
}
/* PLL flag options */
pllError = Chip_Clock_GetPllConfig(inRate, pControl->desiredRate, pSetup, false, useSS);
if ((useSS) && (pllError == PLL_ERROR_SUCCESS)) {
/* If using SS mode, then some tweaks are made to the generated setup */
pSetup->SYSPLLSSCTRL[1] |= (uint32_t) pControl->ss_mf | (uint32_t) pControl->ss_mr |
(uint32_t) pControl->ss_mc;
if (pControl->mfDither) {
pSetup->SYSPLLSSCTRL[1] |= PLL_SSCG1_DITHER;
}
}
return pllError;
}
/* Set PLL output from PLL setup structure */
PLL_ERROR_T Chip_Clock_SetupSystemPLLPrec(PLL_SETUP_T *pSetup)
{
/* Power off PLL during setup changes */
Chip_SYSCON_PowerDown(SYSCON_PDRUNCFG_PD_SYS_PLL);
/* Write PLL setup data */
LPC_SYSCON->SYSPLLCTRL = pSetup->SYSPLLCTRL;
LPC_SYSCON->SYSPLLNDEC = pSetup->SYSPLLNDEC;
LPC_SYSCON->SYSPLLNDEC = pSetup->SYSPLLNDEC | PLL_NDEC_NREQ;/* latch */
LPC_SYSCON->SYSPLLPDEC = pSetup->SYSPLLPDEC;
LPC_SYSCON->SYSPLLPDEC = pSetup->SYSPLLPDEC | PLL_PDEC_PREQ;/* latch */
LPC_SYSCON->SYSPLLSSCTRL[0] = pSetup->SYSPLLSSCTRL[0];
LPC_SYSCON->SYSPLLSSCTRL[0] = pSetup->SYSPLLSSCTRL[0] | PLL_SSCG0_MREQ; /* latch */
LPC_SYSCON->SYSPLLSSCTRL[1] = pSetup->SYSPLLSSCTRL[1];
LPC_SYSCON->SYSPLLSSCTRL[1] = pSetup->SYSPLLSSCTRL[1] | PLL_SSCG1_MD_REQ; /* latch */
/* Flags for lock or power on */
if ((pSetup->flags & (PLL_SETUPFLAG_POWERUP | PLL_SETUPFLAG_WAITLOCK)) != 0) {
Chip_SYSCON_PowerUp(SYSCON_PDRUNCFG_PD_SYS_PLL);
}
if ((pSetup->flags & PLL_SETUPFLAG_WAITLOCK) != 0) {
while (Chip_Clock_IsSystemPLLLocked() == false) {}
}
/* Update current programmed PLL rate var */
Chip_Clock_GetSystemPLLOutFromSetupUpdate(pSetup);
/* System voltage adjustment, occurs prior to setting main system clock */
if ((pSetup->flags & PLL_SETUPFLAG_ADGVOLT) != 0) {
Chip_POWER_SetVoltage(POWER_LOW_POWER_MODE, curPllRate);
}
return PLL_ERROR_SUCCESS;
}
/* Set System PLL clock based on the input frequency and multiplier */
void Chip_Clock_SetupSystemPLL(uint32_t multiply_by, uint32_t input_freq)
{
uint32_t cco_freq = input_freq * multiply_by;
uint32_t pdec = 1;
uint32_t selr;
uint32_t seli;
uint32_t selp;
uint32_t mdec, ndec;
uint32_t directo = SYS_PLL_DIRECTO;
while (cco_freq < 75000000) {
multiply_by <<= 1; /* double value in each iteration */
pdec <<= 1; /* correspondingly double pdec to cancel effect of double msel */
cco_freq = input_freq * multiply_by;
}
selr = 0;
seli = (multiply_by & 0x3c) + 4;
selp = (multiply_by >> 1) + 1;
if (pdec > 1) {
directo = 0; /* use post divider */
pdec = pdec / 2; /* Account for minus 1 encoding */
/* Translate P value */
pdec = (pdec == 1) ? 0x62 : /* 1 * 2 */
(pdec == 2) ? 0x42 : /* 2 * 2 */
(pdec == 4) ? 0x02 : /* 4 * 2 */
(pdec == 8) ? 0x0b : /* 8 * 2 */
(pdec == 16) ? 0x11 : /* 16 * 2 */
(pdec == 32) ? 0x08 : 0x08; /* 32 * 2 */
}
/* Only support values of 2 to 16 (to keep driver simple) */
mdec = 0x7fff >> (16 - (multiply_by - 1));
ndec = 0x202; /* pre divide by 2 (hardcoded) */
LPC_SYSCON->SYSPLLCTRL = SYS_PLL_BANDSEL | directo | (selr << SYSCON_SYSPLLCTRL_SELR_P) |
(seli << SYSCON_SYSPLLCTRL_SELI_P) | (selp << SYSCON_SYSPLLCTRL_SELP_P);
LPC_SYSCON->SYSPLLPDEC = pdec | (1 << 7); /* set Pdec value and assert preq */
LPC_SYSCON->SYSPLLNDEC = ndec | (1 << 10); /* set Pdec value and assert preq */
LPC_SYSCON->SYSPLLSSCTRL[0] = (1 << 18) | (1 << 17) | mdec; /* select non sscg MDEC value, assert mreq and select mdec value */
}