123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866 |
- /*
- * @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 */
- }
|