2 * @brief LPC18xx/43xx clock driver
\r
5 * Copyright(C) NXP Semiconductors, 2012
\r
6 * All rights reserved.
\r
9 * Software that is described herein is for illustrative purposes only
\r
10 * which provides customers with programming information regarding the
\r
11 * LPC products. This software is supplied "AS IS" without any warranties of
\r
12 * any kind, and NXP Semiconductors and its licenser disclaim any and
\r
13 * all warranties, express or implied, including all implied warranties of
\r
14 * merchantability, fitness for a particular purpose and non-infringement of
\r
15 * intellectual property rights. NXP Semiconductors assumes no responsibility
\r
16 * or liability for the use of the software, conveys no license or rights under any
\r
17 * patent, copyright, mask work right, or any other intellectual property rights in
\r
18 * or to any products. NXP Semiconductors reserves the right to make changes
\r
19 * in the software without notification. NXP Semiconductors also makes no
\r
20 * representation or warranty that such application will be suitable for the
\r
21 * specified use without further testing or modification.
\r
24 * Permission to use, copy, modify, and distribute this software and its
\r
25 * documentation is hereby granted, under NXP Semiconductors' and its
\r
26 * licensor's relevant copyrights in the software, without fee, provided that it
\r
27 * is used in conjunction with NXP Semiconductors microcontrollers. This
\r
28 * copyright, permission, and disclaimer notice must appear in all copies of
\r
34 /*****************************************************************************
\r
35 * Private types/enumerations/variables
\r
36 ****************************************************************************/
\r
38 /* Maps a peripheral clock to it's base clock */
\r
40 CHIP_CCU_CLK_T clkstart;
\r
41 CHIP_CCU_CLK_T clkend;
\r
42 CHIP_CGU_BASE_CLK_T clkbase;
\r
43 } CLK_PERIPH_TO_BASE_T;
\r
44 static const CLK_PERIPH_TO_BASE_T periph_to_base[] = {
\r
45 {CLK_APB3_BUS, CLK_APB3_CAN0, CLK_BASE_APB3},
\r
46 {CLK_APB1_BUS, CLK_APB1_CAN1, CLK_BASE_APB1},
\r
47 {CLK_SPIFI, CLK_SPIFI, CLK_BASE_SPIFI},
\r
48 {CLK_MX_BUS, CLK_MX_QEI, CLK_BASE_MX},
\r
50 #if defined(CHIP_LPC43XX)
\r
51 {CLK_PERIPH_BUS, CLK_PERIPH_SGPIO, CLK_BASE_PERIPH},
\r
53 {CLK_USB0, CLK_USB0, CLK_BASE_USB0},
\r
54 {CLK_USB1, CLK_USB1, CLK_BASE_USB1},
\r
55 #if defined(CHIP_LPC43XX)
\r
56 {CLK_SPI, CLK_SPI, CLK_BASE_SPI},
\r
57 {CLK_VADC, CLK_VADC, CLK_BASE_VADC},
\r
59 {CLK_APLL, CLK_APLL, CLK_BASE_APLL},
\r
60 {CLK_APB2_UART3, CLK_APB2_UART3, CLK_BASE_UART3},
\r
61 {CLK_APB2_UART2, CLK_APB2_UART2, CLK_BASE_UART2},
\r
62 {CLK_APB2_UART1, CLK_APB2_UART1, CLK_BASE_UART1},
\r
63 {CLK_APB2_UART0, CLK_APB2_UART0, CLK_BASE_UART0},
\r
64 {CLK_APB2_SSP1, CLK_APB2_SSP1, CLK_BASE_SSP1},
\r
65 {CLK_APB2_SSP0, CLK_APB2_SSP0, CLK_BASE_SSP0},
\r
66 {CLK_APB2_SDIO, CLK_APB2_SDIO, CLK_BASE_SDIO},
\r
67 {CLK_CCU2_LAST, CLK_CCU2_LAST, CLK_BASE_NONE}
\r
71 /*****************************************************************************
\r
72 * Public types/enumerations/variables
\r
73 ****************************************************************************/
\r
75 /*****************************************************************************
\r
77 ****************************************************************************/
\r
79 /* Test PLL input values for a specific frequency range */
\r
80 static uint32_t Chip_Clock_TestMainPLLMultiplier(uint32_t InputHz, uint32_t TestMult, uint32_t MinHz, uint32_t MaxHz)
\r
82 uint32_t TestHz = TestMult * InputHz;
\r
84 if ((TestHz < MinHz) || (TestHz > MAX_CLOCK_FREQ) || (TestHz > MaxHz)) {
\r
91 /* Returns clock rate out of a divider */
\r
92 static uint32_t Chip_Clock_GetDivRate(CHIP_CGU_CLKIN_T clock, CHIP_CGU_IDIV_T divider)
\r
94 CHIP_CGU_CLKIN_T input;
\r
97 input = Chip_Clock_GetDividerSource(divider);
\r
98 div = Chip_Clock_GetDividerDivisor(divider);
\r
99 return Chip_Clock_GetClockInputHz(input) / (div + 1);
\r
102 /* Finds the base clock for the peripheral clock */
\r
103 static CHIP_CGU_BASE_CLK_T Chip_Clock_FindBaseClock(CHIP_CCU_CLK_T clk)
\r
105 CHIP_CGU_BASE_CLK_T baseclk = CLK_BASE_NONE;
\r
108 while ((baseclk == CLK_BASE_NONE) && (periph_to_base[i].clkbase != baseclk)) {
\r
109 if ((clk >= periph_to_base[i].clkstart) && (clk <= periph_to_base[i].clkend)) {
\r
110 baseclk = periph_to_base[i].clkbase;
\r
120 /*****************************************************************************
\r
122 ****************************************************************************/
\r
124 /* Enables the crystal oscillator */
\r
125 void Chip_Clock_EnableCrystal(void)
\r
127 uint32_t OldCrystalConfig = LPC_CGU->XTAL_OSC_CTRL;
\r
129 /* Clear bypass mode */
\r
130 OldCrystalConfig &= (~2);
\r
131 if (OldCrystalConfig != LPC_CGU->XTAL_OSC_CTRL) {
\r
132 LPC_CGU->XTAL_OSC_CTRL = OldCrystalConfig;
\r
135 /* Enable crystal oscillator */
\r
136 OldCrystalConfig &= (~1);
\r
137 if (CRYSTAL_MAIN_FREQ_IN >= 20000000) {
\r
138 OldCrystalConfig |= 4; /* Set high frequency mode */
\r
141 LPC_CGU->XTAL_OSC_CTRL = OldCrystalConfig;
\r
144 /* Disables the crystal oscillator */
\r
145 void Chip_Clock_DisableCrystal(void)
\r
147 /* Disable crystal oscillator */
\r
148 LPC_CGU->XTAL_OSC_CTRL &= (~1);
\r
151 /* Configures the main PLL */
\r
152 uint32_t Chip_Clock_SetupMainPLLHz(CHIP_CGU_CLKIN_T Input, uint32_t MinHz, uint32_t DesiredHz, uint32_t MaxHz)
\r
154 uint32_t freqin = Chip_Clock_GetClockInputHz(Input);
\r
155 uint32_t Mult, LastMult, MultEnd;
\r
156 uint32_t freqout, freqout2;
\r
158 if (DesiredHz != 0xFFFFFFFF) {
\r
159 /* Test DesiredHz rounded down */
\r
160 Mult = DesiredHz / freqin;
\r
161 freqout = Chip_Clock_TestMainPLLMultiplier(freqin, Mult, MinHz, MaxHz);
\r
163 /* Test DesiredHz rounded up */
\r
165 freqout2 = Chip_Clock_TestMainPLLMultiplier(freqin, Mult, MinHz, MaxHz);
\r
167 if (freqout && !freqout2) { /* rounding up is no good? set first multiplier */
\r
169 return Chip_Clock_SetupMainPLLMult(Input, Mult);
\r
171 if (!freqout && freqout2) { /* didn't work until rounded up? set 2nd multiplier */
\r
172 return Chip_Clock_SetupMainPLLMult(Input, Mult);
\r
175 if (freqout && freqout2) { /* either multiplier okay? choose closer one */
\r
176 if ((DesiredHz - freqout) > (freqout2 - DesiredHz)) {
\r
178 return Chip_Clock_SetupMainPLLMult(Input, Mult);
\r
181 return Chip_Clock_SetupMainPLLMult(Input, Mult);
\r
186 /* Neither multiplier okay? Try to start at MinHz and increment.
\r
187 This should find the highest multiplier that is still good */
\r
188 Mult = MinHz / freqin;
\r
189 MultEnd = MaxHz / freqin;
\r
192 freqout = Chip_Clock_TestMainPLLMultiplier(freqin, Mult, MinHz, MaxHz);
\r
198 if (Mult >= MultEnd) {
\r
205 return Chip_Clock_SetupMainPLLMult(Input, LastMult);
\r
211 /* Directly set the PLL multipler */
\r
212 uint32_t Chip_Clock_SetupMainPLLMult(CHIP_CGU_CLKIN_T Input, uint32_t mult)
\r
214 uint32_t freq = Chip_Clock_GetClockInputHz(Input);
\r
215 uint32_t msel = 0, nsel = 0, psel = 0, pval = 1;
\r
216 uint32_t PLLReg = LPC_CGU->PLL1_CTRL;
\r
221 PLLReg &= ~(0x1F << 24);/* clear input source bits */
\r
222 PLLReg |= Input << 24; /* set input source bits to parameter */
\r
224 /* Clear other PLL input bits */
\r
225 PLLReg &= ~((1 << 6) | /* FBSEL */
\r
226 (1 << 1) | /* BYPASS */
\r
227 (1 << 7) | /* DIRECT */
\r
228 (0x03 << 8) | (0xFF << 16) | (0x03 << 12)); /* PSEL, MSEL, NSEL- divider ratios */
\r
230 if (freq < 156000000) {
\r
231 /* psel is encoded such that 0=1, 1=2, 2=4, 3=8 */
\r
232 while ((2 * (pval) * freq) < 156000000) {
\r
237 PLLReg |= (msel << 16) | (nsel << 12) | (psel << 8) | (1 << 6); /* dividers + FBSEL */
\r
239 else if (freq < 320000000) {
\r
240 PLLReg |= (msel << 16) | (nsel << 12) | (psel << 8) | (1 << 7) | (1 << 6); /* dividers + DIRECT + FBSEL */
\r
243 Chip_Clock_DisableMainPLL();
\r
246 LPC_CGU->PLL1_CTRL = PLLReg & ~(1 << 0);
\r
251 /* Returns the frequency of the main PLL */
\r
252 uint32_t Chip_Clock_GetMainPLLHz(void)
\r
254 uint32_t PLLReg = LPC_CGU->PLL1_CTRL;
\r
255 uint32_t freq = Chip_Clock_GetClockInputHz((CHIP_CGU_CLKIN_T) ((PLLReg >> 24) & 0xF));
\r
256 uint32_t msel, nsel, psel, direct, fbsel;
\r
258 const uint8_t ptab[] = {1, 2, 4, 8};
\r
261 if (!(LPC_CGU->PLL1_STAT & 1)) {
\r
265 msel = (PLLReg >> 16) & 0xFF;
\r
266 nsel = (PLLReg >> 12) & 0x3;
\r
267 psel = (PLLReg >> 8) & 0x3;
\r
268 direct = (PLLReg >> 7) & 0x1;
\r
269 fbsel = (PLLReg >> 6) & 0x1;
\r
275 if (direct || fbsel) {
\r
276 return m * (freq / n);
\r
279 return (m / (2 * p)) * (freq / n);
\r
282 /* Disables the main PLL */
\r
283 void Chip_Clock_DisableMainPLL(void)
\r
285 /* power down main PLL */
\r
286 LPC_CGU->PLL1_CTRL |= 1;
\r
289 /* Disables the main PLL */
\r
290 void Chip_Clock_EnableMainPLL(void)
\r
292 /* power down main PLL */
\r
293 LPC_CGU->PLL1_CTRL &= ~1;
\r
296 /* Returns the lock status of the main PLL */
\r
297 bool Chip_Clock_MainPLLLocked(void)
\r
299 /* Return true if locked */
\r
300 return (bool) (LPC_CGU->PLL1_STAT & 1);
\r
303 /* Sets up a CGU clock divider and it's input clock */
\r
304 void Chip_Clock_SetDivider(CHIP_CGU_IDIV_T Divider, CHIP_CGU_CLKIN_T Input, uint32_t Divisor)
\r
306 uint32_t reg = LPC_CGU->IDIV_CTRL[Divider];
\r
310 if (Input != CLKINPUT_PD) {
\r
311 /* Mask off bits that need to changes */
\r
312 reg &= ~((0x1F << 24) | 1 | (0xF << 2));
\r
314 /* Enable autoblocking, clear PD, and set clock source & divisor */
\r
315 LPC_CGU->IDIV_CTRL[Divider] = reg | (1 << 11) | (Input << 24) | (Divisor << 2);
\r
318 LPC_CGU->IDIV_CTRL[Divider] = reg | 1; /* Power down this divider */
\r
322 /* Gets a CGU clock divider source */
\r
323 CHIP_CGU_CLKIN_T Chip_Clock_GetDividerSource(CHIP_CGU_IDIV_T Divider)
\r
325 uint32_t reg = LPC_CGU->IDIV_CTRL[Divider];
\r
327 if (reg & 1) { /* divider is powered down */
\r
328 return CLKINPUT_PD;
\r
331 return (CHIP_CGU_CLKIN_T) ((reg >> 24) & 0x1F);
\r
334 /* Gets a CGU clock divider divisor */
\r
335 uint32_t Chip_Clock_GetDividerDivisor(CHIP_CGU_IDIV_T Divider)
\r
337 return (CHIP_CGU_CLKIN_T) ((LPC_CGU->IDIV_CTRL[Divider] >> 2) & 0xF);
\r
340 /* Returns the frequency of the specified input clock source */
\r
341 uint32_t Chip_Clock_GetClockInputHz(CHIP_CGU_CLKIN_T input)
\r
347 rate = CRYSTAL_32K_FREQ_IN;
\r
351 rate = CGU_IRC_FREQ;
\r
354 case CLKIN_ENET_RX:
\r
355 #if defined(USE_RMII)
\r
356 /* In RMII mode, this clock is not attached */
\r
358 /* MII mode requires 25MHz clock */
\r
363 case CLKIN_ENET_TX:
\r
364 #if defined(USE_RMII)
\r
365 /* MII mode requires 50MHz clock */
\r
368 /* MII mode requires 25MHz clock */
\r
374 #if defined(EXTERNAL_CLKIN_FREQ_IN)
\r
375 rate = EXTERNAL_CLKIN_FREQ_IN;
\r
377 /* Assume no clock in if a rate wasn't defined */
\r
381 case CLKIN_CRYSTAL:
\r
382 rate = CRYSTAL_MAIN_FREQ_IN;
\r
386 rate = CGU_USB_PLL_RATE;
\r
389 case CLKIN_AUDIOPLL:
\r
390 rate = CGU_AUDIO_PLL_RATE;
\r
393 case CLKIN_MAINPLL:
\r
394 rate = Chip_Clock_GetMainPLLHz();
\r
398 rate = Chip_Clock_GetDivRate(input, CLK_IDIV_A);
\r
402 rate = Chip_Clock_GetDivRate(input, CLK_IDIV_B);
\r
406 rate = Chip_Clock_GetDivRate(input, CLK_IDIV_C);
\r
410 rate = Chip_Clock_GetDivRate(input, CLK_IDIV_D);
\r
414 rate = Chip_Clock_GetDivRate(input, CLK_IDIV_E);
\r
428 /* Returns the frequency of the specified base clock source */
\r
429 uint32_t Chip_Clock_GetBaseClocktHz(CHIP_CGU_BASE_CLK_T clock)
\r
431 return Chip_Clock_GetClockInputHz(Chip_Clock_GetBaseClock(clock));
\r
434 /* Sets a CGU Base Clock clock source */
\r
435 void Chip_Clock_SetBaseClock(CHIP_CGU_BASE_CLK_T BaseClock, CHIP_CGU_CLKIN_T Input, bool autoblocken, bool powerdn)
\r
437 uint32_t reg = LPC_CGU->BASE_CLK[BaseClock];
\r
439 if (BaseClock < CLK_BASE_NONE) {
\r
440 if (Input != CLKINPUT_PD) {
\r
441 /* Mask off fields we plan to update */
\r
442 reg &= ~((0x1F << 24) | 1 | (1 << 11));
\r
451 /* Set clock source */
\r
452 reg |= (Input << 24);
\r
454 LPC_CGU->BASE_CLK[BaseClock] = reg;
\r
458 LPC_CGU->BASE_CLK[BaseClock] = reg | 1; /* Power down this base clock */
\r
462 /* Reads CGU Base Clock clock source information */
\r
463 void Chip_Clock_GetBaseClockOpts(CHIP_CGU_BASE_CLK_T BaseClock, CHIP_CGU_CLKIN_T *Input, bool *autoblocken,
\r
466 uint32_t reg = LPC_CGU->BASE_CLK[BaseClock];
\r
467 CHIP_CGU_CLKIN_T ClkIn = (CHIP_CGU_CLKIN_T) ((reg >> 24) & 0x1F );
\r
469 if (BaseClock < CLK_BASE_NONE) {
\r
472 *autoblocken = (reg & (1 << 11)) ? true : false;
\r
473 *powerdn = (reg & (1 << 0)) ? true : false;
\r
476 *Input = CLKINPUT_PD;
\r
478 *autoblocken = true;
\r
482 /*Enables a base clock source */
\r
483 void Chip_Clock_EnableBaseClock(CHIP_CGU_BASE_CLK_T BaseClock)
\r
485 if (BaseClock < CLK_BASE_NONE) {
\r
486 LPC_CGU->BASE_CLK[BaseClock] &= ~1;
\r
490 /* Disables a base clock source */
\r
491 void Chip_Clock_DisableBaseClock(CHIP_CGU_BASE_CLK_T BaseClock)
\r
493 if (BaseClock < CLK_BASE_NONE) {
\r
494 LPC_CGU->BASE_CLK[BaseClock] |= 1;
\r
498 /* Returns base clock enable state */
\r
499 bool Chip_Clock_IsBaseClockEnabled(CHIP_CGU_BASE_CLK_T BaseClock)
\r
503 if (BaseClock < CLK_BASE_NONE) {
\r
504 enabled = (bool) ((LPC_CGU->BASE_CLK[BaseClock] & 1) == 0);
\r
513 /* Gets a CGU Base Clock clock source */
\r
514 CHIP_CGU_CLKIN_T Chip_Clock_GetBaseClock(CHIP_CGU_BASE_CLK_T BaseClock)
\r
518 if (BaseClock >= CLK_BASE_NONE) {
\r
519 return CLKINPUT_PD;
\r
522 reg = LPC_CGU->BASE_CLK[BaseClock];
\r
524 /* base clock is powered down? */
\r
526 return CLKINPUT_PD;
\r
529 return (CHIP_CGU_CLKIN_T) ((reg >> 24) & 0x1F);
\r
532 /* Enables a peripheral clock and sets clock states */
\r
533 void Chip_Clock_EnableOpts(CHIP_CCU_CLK_T clk, bool autoen, bool wakeupen, int div)
\r
544 /* Not all clocks support a divider, but we won't check that here. Only
\r
545 dividers of 1 and 2 are allowed. Assume 1 if not 2 */
\r
550 /* Setup peripheral clock and start running */
\r
551 if (clk >= CLK_CCU2_START) {
\r
552 LPC_CCU2->CLKCCU[clk - CLK_CCU2_START].CFG = reg;
\r
555 LPC_CCU1->CLKCCU[clk].CFG = reg;
\r
559 /* Enables a peripheral clock */
\r
560 void Chip_Clock_Enable(CHIP_CCU_CLK_T clk)
\r
562 /* Start peripheral clock running */
\r
563 if (clk >= CLK_CCU2_START) {
\r
564 LPC_CCU2->CLKCCU[clk - CLK_CCU2_START].CFG |= 1;
\r
567 LPC_CCU1->CLKCCU[clk].CFG |= 1;
\r
571 /* Disables a peripheral clock */
\r
572 void Chip_Clock_Disable(CHIP_CCU_CLK_T clk)
\r
574 /* Stop peripheral clock */
\r
575 if (clk >= CLK_CCU2_START) {
\r
576 LPC_CCU2->CLKCCU[clk - CLK_CCU2_START].CFG &= ~1;
\r
579 LPC_CCU1->CLKCCU[clk].CFG &= ~1;
\r
584 * Disable all branch output clocks with wake up mechanism enabled.
\r
585 * Only the clocks with wake up mechanism enabled will be disabled &
\r
586 * power down sequence started
\r
588 void Chip_Clock_StartPowerDown(void)
\r
590 /* Set Power Down bit */
\r
596 * Enable all branch output clocks after the wake up event.
\r
597 * Only the clocks with wake up mechanism enabled will be enabled
\r
599 void Chip_Clock_ClearPowerDown(void)
\r
601 /* Clear Power Down bit */
\r
606 /* Returns a peripheral clock rate */
\r
607 uint32_t Chip_Clock_GetRate(CHIP_CCU_CLK_T clk)
\r
609 CHIP_CGU_BASE_CLK_T baseclk;
\r
610 uint32_t reg, div, rate;
\r
612 /* Get CCU config register for clock */
\r
613 if (clk >= CLK_CCU2_START) {
\r
614 reg = LPC_CCU2->CLKCCU[clk - CLK_CCU2_START].CFG;
\r
617 reg = LPC_CCU1->CLKCCU[clk].CFG;
\r
620 /* Is the clock enabled? */
\r
622 /* Get base clock for this peripheral clock */
\r
623 baseclk = Chip_Clock_FindBaseClock(clk);
\r
625 /* Get base clock rate */
\r
626 rate = Chip_Clock_GetBaseClocktHz(baseclk);
\r
628 /* Get divider for this clock */
\r
629 if (((reg >> 5) & 0x7) == 0) {
\r
633 div = 2;/* No other dividers supported */
\r
645 /* Sets up the audio or USB PLL */
\r
646 void Chip_Clock_SetupPLL(CHIP_CGU_CLKIN_T Input, CHIP_CGU_USB_AUDIO_PLL_T pllnum,
\r
647 const CGU_USBAUDIO_PLL_SETUP_T *pPLLSetup)
\r
649 uint32_t reg = pPLLSetup->ctrl | (Input << 24);
\r
650 /* Setup from passed values */
\r
651 LPC_CGU->PLL[pllnum].PLL_CTRL = reg;
\r
652 LPC_CGU->PLL[pllnum].PLL_MDIV = pPLLSetup->mdiv;
\r
653 LPC_CGU->PLL[pllnum].PLL_NP_DIV = pPLLSetup->ndiv;
\r
655 /* Fractional divider is for audio PLL only */
\r
656 if (pllnum == pllnum) {
\r
657 LPC_CGU->PLL0AUDIO_FRAC = pPLLSetup->fract;
\r
661 /* Enables the audio or USB PLL */
\r
662 void Chip_Clock_EnablePLL(CHIP_CGU_USB_AUDIO_PLL_T pllnum)
\r
664 LPC_CGU->PLL[pllnum].PLL_CTRL &= ~1;
\r
667 /* Disables the audio or USB PLL */
\r
668 void Chip_Clock_DisablePLL(CHIP_CGU_USB_AUDIO_PLL_T pllnum)
\r
670 LPC_CGU->PLL[pllnum].PLL_CTRL |= 1;
\r
673 /* Returns the PLL status */
\r
674 uint32_t Chip_Clock_GetPLLStatus(CHIP_CGU_USB_AUDIO_PLL_T pllnum)
\r
676 return LPC_CGU->PLL[pllnum].PLL_STAT;
\r