/*
 * @brief LPC18xx/43xx clock driver
 *
 * @note
 * Copyright(C) NXP Semiconductors, 2012
 * 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
 ****************************************************************************/

/* Maps a peripheral clock to it's base clock */
typedef struct {
	CHIP_CCU_CLK_T clkstart;
	CHIP_CCU_CLK_T clkend;
	CHIP_CGU_BASE_CLK_T clkbase;
} CLK_PERIPH_TO_BASE_T;
static const CLK_PERIPH_TO_BASE_T periph_to_base[] = {
	{CLK_APB3_BUS, CLK_APB3_CAN0, CLK_BASE_APB3},
	{CLK_APB1_BUS, CLK_APB1_CAN1, CLK_BASE_APB1},
	{CLK_SPIFI, CLK_SPIFI, CLK_BASE_SPIFI},
	{CLK_MX_BUS, CLK_MX_QEI, CLK_BASE_MX},
#if defined(CHIP_LPC43XX)
	{CLK_PERIPH_BUS, CLK_PERIPH_SGPIO, CLK_BASE_PERIPH},
#endif
	{CLK_USB0, CLK_USB0, CLK_BASE_USB0},
	{CLK_USB1, CLK_USB1, CLK_BASE_USB1},
#if defined(CHIP_LPC43XX)
	{CLK_SPI, CLK_SPI, CLK_BASE_SPI},
	{CLK_ADCHS, CLK_ADCHS, CLK_BASE_ADCHS},
#endif
	{CLK_APLL, CLK_APLL, CLK_BASE_APLL},
	{CLK_APB2_UART3, CLK_APB2_UART3, CLK_BASE_UART3},
	{CLK_APB2_UART2, CLK_APB2_UART2, CLK_BASE_UART2},
	{CLK_APB0_UART1, CLK_APB0_UART1, CLK_BASE_UART1},
	{CLK_APB0_UART0, CLK_APB0_UART0, CLK_BASE_UART0},
	{CLK_APB2_SSP1, CLK_APB2_SSP1, CLK_BASE_SSP1},
	{CLK_APB0_SSP0, CLK_APB0_SSP0, CLK_BASE_SSP0},
	{CLK_APB2_SDIO, CLK_APB2_SDIO, CLK_BASE_SDIO},
	{CLK_CCU2_LAST, CLK_CCU2_LAST, CLK_BASE_NONE}
};

#define CRYSTAL_32K_FREQ_IN    (32 * 1024)

/* Variables to use audio and usb pll frequency */
static uint32_t audio_usb_pll_freq[CGU_AUDIO_PLL+1];

/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/

/*****************************************************************************
 * Private functions
 ****************************************************************************/
__STATIC_INLINE uint32_t ABS(int val)
{
	if (val < 0)
		return -val;
	return val;
}

static void pll_calc_divs(uint32_t freq, PLL_PARAM_T *ppll)
{

	uint32_t prev = freq;
	int n, m, p;

	/* When direct mode is set FBSEL should be a don't care */
	if (ppll->ctrl & (1 << 7)) {
		ppll->ctrl &= ~(1 << 6);
	}
	for (n = 1; n <= 4; n++) {
		for (p = 0; p < 4; p ++) {
			for (m = 1; m <= 256; m++) {
				uint32_t fcco, fout;
				if (ppll->ctrl & (1 << 6)) {
					fcco = ((m << (p + 1)) * ppll->fin) / n;
				} else {
					fcco = (m * ppll->fin) / n;
				}
				if (fcco < PLL_MIN_CCO_FREQ) continue;
				if (fcco > PLL_MAX_CCO_FREQ) break;
				if (ppll->ctrl & (1 << 7)) {
					fout = fcco;
				} else {
					fout = fcco >> (p + 1);
				}

				if (ABS(freq - fout) < prev) {
					ppll->nsel = n;
					ppll->psel = p + 1;
					ppll->msel = m;
					ppll->fout = fout;
					ppll->fcco = fcco;
					prev = ABS(freq - fout);
				}
			}
		}
	}
}

static void pll_get_frac(uint32_t freq, PLL_PARAM_T *ppll)
{
	int diff[3];
	PLL_PARAM_T pll[3] = {{0},{0},{0}};

	/* Try direct mode */
	pll[0].ctrl |= (1 << 7);
	pll[0].fin = ppll->fin;
	pll[0].srcin = ppll->srcin;
	pll_calc_divs(freq, &pll[0]);
	if (pll[0].fout == freq) {
		*ppll = pll[0];
		return ;
	}
	diff[0] = ABS(freq - pll[0].fout);

	/* Try non-Integer mode */
	pll[2].ctrl &= ~(1 << 6);			// need to set FBSEL to 0
	pll[2].fin = ppll->fin;
	pll[2].srcin = ppll->srcin;
	pll_calc_divs(freq, &pll[2]);
	if (pll[2].fout == freq) {
		*ppll = pll[2];
		return ;
	}
	diff[2] = ABS(freq - pll[2].fout);
	
	if (freq <= 110000000) {
		/* Try integer mode */
		pll[1].ctrl = (1 << 6);
		pll[1].fin = ppll->fin;
		pll_calc_divs(freq, &pll[1]);
		if (pll[1].fout == freq) {
			*ppll = pll[1];
			return ;
		}
	}
	diff[1] = ABS(freq - pll[1].fout);

	/* Find the min of 3 and return */
	if (diff[0] <= diff[1]) {
		if (diff[0] <= diff[2]) {
			*ppll = pll[0];
		} else {
			*ppll = pll[2];
		}
	} else {
		if (diff[1] <= diff[2]) {
			*ppll = pll[1];
		} else {
			*ppll = pll[2];
		}
	}
}

/* Test PLL input values for a specific frequency range */
static uint32_t Chip_Clock_TestMainPLLMultiplier(uint32_t InputHz, uint32_t TestMult, uint32_t MinHz, uint32_t MaxHz)
{
	uint32_t TestHz = TestMult * InputHz;

	if ((TestHz < MinHz) || (TestHz > MAX_CLOCK_FREQ) || (TestHz > MaxHz)) {
		TestHz = 0;
	}

	return TestHz;
}

/* Returns clock rate out of a divider */
static uint32_t Chip_Clock_GetDivRate(CHIP_CGU_CLKIN_T clock, CHIP_CGU_IDIV_T divider)
{
  (void) clock;
	CHIP_CGU_CLKIN_T input;
	uint32_t div;

	input = Chip_Clock_GetDividerSource(divider);
	div = Chip_Clock_GetDividerDivisor(divider);
	return Chip_Clock_GetClockInputHz(input) / (div + 1);
}

/* Finds the base clock for the peripheral clock */
static CHIP_CGU_BASE_CLK_T Chip_Clock_FindBaseClock(CHIP_CCU_CLK_T clk)
{
	CHIP_CGU_BASE_CLK_T baseclk = CLK_BASE_NONE;
	int i = 0;

	while ((baseclk == CLK_BASE_NONE) && (periph_to_base[i].clkbase != baseclk)) {
		if ((clk >= periph_to_base[i].clkstart) && (clk <= periph_to_base[i].clkend)) {
			baseclk = periph_to_base[i].clkbase;
		}
		else {
			i++;
		}
	}

	return baseclk;
}

/*****************************************************************************
 * Public functions
 ****************************************************************************/

/* Enables the crystal oscillator */
void Chip_Clock_EnableCrystal(void)
{
	volatile uint32_t delay = 1000;

	uint32_t OldCrystalConfig = LPC_CGU->XTAL_OSC_CTRL;

	/* Clear bypass mode */
	OldCrystalConfig &= (~2);
	if (OldCrystalConfig != LPC_CGU->XTAL_OSC_CTRL) {
		LPC_CGU->XTAL_OSC_CTRL = OldCrystalConfig;
	}

	/* Enable crystal oscillator */
	OldCrystalConfig &= (~1);
	if (OscRateIn >= 20000000) {
		OldCrystalConfig |= 4;	/* Set high frequency mode */

	}
	LPC_CGU->XTAL_OSC_CTRL = OldCrystalConfig;

	/* Delay for 250uSec */
	while(delay--) {}
}

/* Calculate the Main PLL div values */
int Chip_Clock_CalcMainPLLValue(uint32_t freq, PLL_PARAM_T *ppll)
{
	ppll->fin = Chip_Clock_GetClockInputHz(ppll->srcin);

	/* Do sanity check on frequency */
	if (freq > MAX_CLOCK_FREQ || freq < (PLL_MIN_CCO_FREQ / 16) || !ppll->fin) {
		return -1;
	}

	ppll->ctrl = 1 << 7; /* Enable direct mode [If possible] */
	ppll->nsel = 0;
	ppll->psel = 0;
	ppll->msel = freq / ppll->fin;

	if (freq < PLL_MIN_CCO_FREQ || ppll->msel * ppll->fin != freq) {
		pll_get_frac(freq, ppll);
		if (!ppll->nsel) {
			return -1;
		}
		ppll->nsel --;
	}

	if (ppll->msel == 0) {
		return - 1;
	}

	if (ppll->psel) {
		ppll->psel --;
	}

	ppll->msel --;

	return 0;
}

/* Disables the crystal oscillator */
void Chip_Clock_DisableCrystal(void)
{
	/* Disable crystal oscillator */
	LPC_CGU->XTAL_OSC_CTRL |= 1;
}

/* Configures the main PLL */
uint32_t Chip_Clock_SetupMainPLLHz(CHIP_CGU_CLKIN_T Input, uint32_t MinHz, uint32_t DesiredHz, uint32_t MaxHz)
{
	uint32_t freqin = Chip_Clock_GetClockInputHz(Input);
	uint32_t Mult, LastMult, MultEnd;
	uint32_t freqout, freqout2;

	if (DesiredHz != 0xFFFFFFFF) {
		/* Test DesiredHz rounded down */
		Mult = DesiredHz / freqin;
		freqout = Chip_Clock_TestMainPLLMultiplier(freqin, Mult, MinHz, MaxHz);

		/* Test DesiredHz rounded up */
		Mult++;
		freqout2 = Chip_Clock_TestMainPLLMultiplier(freqin, Mult, MinHz, MaxHz);

		if (freqout && !freqout2) {	/* rounding up is no good? set first multiplier */
			Mult--;
			return Chip_Clock_SetupMainPLLMult(Input, Mult);
		}
		if (!freqout && freqout2) {	/* didn't work until rounded up? set 2nd multiplier */
			return Chip_Clock_SetupMainPLLMult(Input, Mult);
		}

		if (freqout && freqout2) {	/* either multiplier okay? choose closer one */
			if ((DesiredHz - freqout) > (freqout2 - DesiredHz)) {
				Mult--;
				return Chip_Clock_SetupMainPLLMult(Input, Mult);
			}
			else {
				return Chip_Clock_SetupMainPLLMult(Input, Mult);
			}
		}
	}

	/* Neither multiplier okay? Try to start at MinHz and increment.
	   This should find the highest multiplier that is still good */
	Mult = MinHz / freqin;
	MultEnd = MaxHz / freqin;
	LastMult = 0;
	while (1) {
		freqout = Chip_Clock_TestMainPLLMultiplier(freqin, Mult, MinHz, MaxHz);

		if (freqout) {
			LastMult = Mult;
		}

		if (Mult >= MultEnd) {
			break;
		}
		Mult++;
	}

	if (LastMult) {
		return Chip_Clock_SetupMainPLLMult(Input, LastMult);
	}

	return 0;
}

/* Directly set the PLL multipler */
uint32_t Chip_Clock_SetupMainPLLMult(CHIP_CGU_CLKIN_T Input, uint32_t mult)
{
	volatile uint32_t delay = 250;
	uint32_t freq = Chip_Clock_GetClockInputHz(Input);
	uint32_t msel = 0, nsel = 0, psel = 0, pval = 1;
	uint32_t PLLReg = LPC_CGU->PLL1_CTRL;

	freq *= mult;
	msel = mult - 1;

	PLLReg &= ~(0x1F << 24);/* clear input source bits */
	PLLReg |= Input << 24;	/* set input source bits to parameter */

	/* Clear other PLL input bits */
	PLLReg &= ~((1 << 6) |	/* FBSEL */
				(1 << 1) |	/* BYPASS */
				(1 << 7) |	/* DIRECT */
				(0x03 << 8) | (0xFF << 16) | (0x03 << 12));	/* PSEL, MSEL, NSEL- divider ratios */
	
	PLLReg |= (1 << 11);		/* AUTOBLOCK */

	if (freq < 156000000) {
		/* psel is encoded such that 0=1, 1=2, 2=4, 3=8 */
		while ((2 * (pval) * freq) < 156000000) {
			psel++;
			pval *= 2;
		}

		PLLReg |= (msel << 16) | (nsel << 12) | (psel << 8) | (1 << 6);	/* dividers + FBSEL */
	}
	else if (freq < 320000000) {
		PLLReg |= (msel << 16) | (nsel << 12) | (psel << 8) | (1 << 7) | (1 << 6);	/* dividers + DIRECT + FBSEL */
	}
	else {
		Chip_Clock_DisableMainPLL();
		return 0;
	}
	LPC_CGU->PLL1_CTRL = PLLReg & ~(1 << 0);

	/* Wait for 50uSec */
	while(delay--) {}

	return freq;
}

/* Returns the frequency of the main PLL */
uint32_t Chip_Clock_GetMainPLLHz(void)
{
	uint32_t PLLReg = LPC_CGU->PLL1_CTRL;
	uint32_t freq = Chip_Clock_GetClockInputHz((CHIP_CGU_CLKIN_T) ((PLLReg >> 24) & 0xF));
	uint32_t msel, nsel, psel, direct, fbsel;
	uint32_t m, n, p;
	const uint8_t ptab[] = {1, 2, 4, 8};

	/* No lock? */
	if (!(LPC_CGU->PLL1_STAT & 1)) {
		return 0;
	}

	msel = (PLLReg >> 16) & 0xFF;
	nsel = (PLLReg >> 12) & 0x3;
	psel = (PLLReg >> 8) & 0x3;
	direct = (PLLReg >> 7) & 0x1;
	fbsel = (PLLReg >> 6) & 0x1;

	m = msel + 1;
	n = nsel + 1;
	p = ptab[psel];

	if (direct || fbsel) {
		return m * (freq / n);
	}

	return (m / (2 * p)) * (freq / n);
}

/* Sets up a CGU clock divider and it's input clock */
void Chip_Clock_SetDivider(CHIP_CGU_IDIV_T Divider, CHIP_CGU_CLKIN_T Input, uint32_t Divisor)
{
	uint32_t reg = LPC_CGU->IDIV_CTRL[Divider];

	Divisor--;

	if (Input != CLKINPUT_PD) {
		/* Mask off bits that need to changes */
		reg &= ~((0x1F << 24) | 1 | (CHIP_CGU_IDIV_MASK(Divider) << 2));

		/* Enable autoblocking, clear PD, and set clock source & divisor */
		LPC_CGU->IDIV_CTRL[Divider] = reg | (1 << 11) | (Input << 24) | (Divisor << 2);
	}
	else {
		LPC_CGU->IDIV_CTRL[Divider] = reg | 1;	/* Power down this divider */
	}
}

/* Gets a CGU clock divider source */
CHIP_CGU_CLKIN_T Chip_Clock_GetDividerSource(CHIP_CGU_IDIV_T Divider)
{
	uint32_t reg = LPC_CGU->IDIV_CTRL[Divider];

	if (reg & 1) {	/* divider is powered down */
		return CLKINPUT_PD;
	}

	return (CHIP_CGU_CLKIN_T) ((reg >> 24) & 0x1F);
}

/* Gets a CGU clock divider divisor */
uint32_t Chip_Clock_GetDividerDivisor(CHIP_CGU_IDIV_T Divider)
{
	return (CHIP_CGU_CLKIN_T) ((LPC_CGU->IDIV_CTRL[Divider] >> 2) & CHIP_CGU_IDIV_MASK(Divider));
}

/* Returns the frequency of the specified input clock source */
uint32_t Chip_Clock_GetClockInputHz(CHIP_CGU_CLKIN_T input)
{
	uint32_t rate = 0;

	switch (input) {
	case CLKIN_32K:
		rate = CRYSTAL_32K_FREQ_IN;
		break;

	case CLKIN_IRC:
		rate = CGU_IRC_FREQ;
		break;

	case CLKIN_ENET_RX:
		if ((LPC_CREG->CREG6 & 0x07) != 0x4) {
			/* MII mode requires 25MHz clock */
			rate = 25000000;
		}
		break;

	case CLKIN_ENET_TX:
		if ((LPC_CREG->CREG6 & 0x07) != 0x4) {
			rate = 25000000; /* MII uses 25 MHz */
		} else {
			rate = 50000000; /* RMII uses 50 MHz */
		}
		break;

	case CLKIN_CLKIN:
		rate = ExtRateIn;
		break;

	case CLKIN_CRYSTAL:
		rate = OscRateIn;
		break;

	case CLKIN_USBPLL:
		rate = audio_usb_pll_freq[CGU_USB_PLL];
		break;

	case CLKIN_AUDIOPLL:
		rate = audio_usb_pll_freq[CGU_AUDIO_PLL];
		break;

	case CLKIN_MAINPLL:
		rate = Chip_Clock_GetMainPLLHz();
		break;

	case CLKIN_IDIVA:
		rate = Chip_Clock_GetDivRate(input, CLK_IDIV_A);
		break;

	case CLKIN_IDIVB:
		rate = Chip_Clock_GetDivRate(input, CLK_IDIV_B);
		break;

	case CLKIN_IDIVC:
		rate = Chip_Clock_GetDivRate(input, CLK_IDIV_C);
		break;

	case CLKIN_IDIVD:
		rate = Chip_Clock_GetDivRate(input, CLK_IDIV_D);
		break;

	case CLKIN_IDIVE:
		rate = Chip_Clock_GetDivRate(input, CLK_IDIV_E);
		break;

	case CLKINPUT_PD:
		rate = 0;
		break;

	default:
		break;
	}

	return rate;
}

/* Returns the frequency of the specified base clock source */
uint32_t Chip_Clock_GetBaseClocktHz(CHIP_CGU_BASE_CLK_T clock)
{
	return Chip_Clock_GetClockInputHz(Chip_Clock_GetBaseClock(clock));
}

/* Sets a CGU Base Clock clock source */
void Chip_Clock_SetBaseClock(CHIP_CGU_BASE_CLK_T BaseClock, CHIP_CGU_CLKIN_T Input, bool autoblocken, bool powerdn)
{
	uint32_t reg = LPC_CGU->BASE_CLK[BaseClock];

	if (BaseClock < CLK_BASE_NONE) {
		if (Input != CLKINPUT_PD) {
			/* Mask off fields we plan to update */
			reg &= ~((0x1F << 24) | 1 | (1 << 11));

			if (autoblocken) {
				reg |= (1 << 11);
			}
			if (powerdn) {
				reg |= (1 << 0);
			}

			/* Set clock source */
			reg |= (Input << 24);

			LPC_CGU->BASE_CLK[BaseClock] = reg;
		}
	}
	else {
		LPC_CGU->BASE_CLK[BaseClock] = reg | 1;	/* Power down this base clock */
	}
}

/* Reads CGU Base Clock clock source information */
void Chip_Clock_GetBaseClockOpts(CHIP_CGU_BASE_CLK_T BaseClock, CHIP_CGU_CLKIN_T *Input, bool *autoblocken,
								 bool *powerdn)
{
	uint32_t reg = LPC_CGU->BASE_CLK[BaseClock];
	CHIP_CGU_CLKIN_T ClkIn = (CHIP_CGU_CLKIN_T) ((reg  >> 24) & 0x1F );

	if (BaseClock < CLK_BASE_NONE) {
		/* Get settings */
		*Input = ClkIn;
		*autoblocken = (reg & (1 << 11)) ? true : false;
		*powerdn = (reg & (1 << 0)) ? true : false;
	}
	else {
		*Input = CLKINPUT_PD;
		*powerdn = true;
		*autoblocken = true;
	}
}

/*Enables a base clock source */
void Chip_Clock_EnableBaseClock(CHIP_CGU_BASE_CLK_T BaseClock)
{
	if (BaseClock < CLK_BASE_NONE) {
		LPC_CGU->BASE_CLK[BaseClock] &= ~1;
	}
}

/* Disables a base clock source */
void Chip_Clock_DisableBaseClock(CHIP_CGU_BASE_CLK_T BaseClock)
{
	if (BaseClock < CLK_BASE_NONE) {
		LPC_CGU->BASE_CLK[BaseClock] |= 1;
	}
}

/* Returns base clock enable state */
bool Chip_Clock_IsBaseClockEnabled(CHIP_CGU_BASE_CLK_T BaseClock)
{
	bool enabled;

	if (BaseClock < CLK_BASE_NONE) {
		enabled = (bool) ((LPC_CGU->BASE_CLK[BaseClock] & 1) == 0);
	}
	else {
		enabled = false;
	}

	return enabled;
}

/* Gets a CGU Base Clock clock source */
CHIP_CGU_CLKIN_T Chip_Clock_GetBaseClock(CHIP_CGU_BASE_CLK_T BaseClock)
{
	uint32_t reg;

	if (BaseClock >= CLK_BASE_NONE) {
		return CLKINPUT_PD;
	}

	reg = LPC_CGU->BASE_CLK[BaseClock];

	/* base clock is powered down? */
	if (reg & 1) {
		return CLKINPUT_PD;
	}

	return (CHIP_CGU_CLKIN_T) ((reg >> 24) & 0x1F);
}

/* Enables a peripheral clock and sets clock states */
void Chip_Clock_EnableOpts(CHIP_CCU_CLK_T clk, bool autoen, bool wakeupen, int div)
{
	uint32_t reg = 1;

	if (autoen) {
		reg |= (1 << 1);
	}
	if (wakeupen) {
		reg |= (1 << 2);
	}

	/* Not all clocks support a divider, but we won't check that here. Only
	   dividers of 1 and 2 are allowed. Assume 1 if not 2 */
	if (div == 2) {
		reg |= (1 << 5);
	}

	/* Setup peripheral clock and start running */
	if (clk >= CLK_CCU2_START) {
		LPC_CCU2->CLKCCU[clk - CLK_CCU2_START].CFG = reg;
	}
	else {
		LPC_CCU1->CLKCCU[clk].CFG = reg;
	}
}

/* Enables a peripheral clock */
void Chip_Clock_Enable(CHIP_CCU_CLK_T clk)
{
	/* Start peripheral clock running */
	if (clk >= CLK_CCU2_START) {
		LPC_CCU2->CLKCCU[clk - CLK_CCU2_START].CFG |= 1;
	}
	else {
		LPC_CCU1->CLKCCU[clk].CFG |= 1;
	}
}

/* Enable RTC Clock */
void Chip_Clock_RTCEnable(void)
{
	LPC_CREG->CREG0 &= ~((1 << 3) | (1 << 2));	/* Reset 32Khz oscillator */
	LPC_CREG->CREG0 |= (1 << 1) | (1 << 0);	/* Enable 32 kHz & 1 kHz on osc32k and release reset */
}

/* Disables a peripheral clock */
void Chip_Clock_Disable(CHIP_CCU_CLK_T clk)
{
	/* Stop peripheral clock */
	if (clk >= CLK_CCU2_START) {
		LPC_CCU2->CLKCCU[clk - CLK_CCU2_START].CFG &= ~1;
	}
	else {
		LPC_CCU1->CLKCCU[clk].CFG &= ~1;
	}
}

/**
 * Disable all branch output clocks with wake up mechanism enabled.
 * Only the clocks with wake up mechanism enabled will be disabled &
 * power down sequence started
 */
void Chip_Clock_StartPowerDown(void)
{
	/* Set Power Down bit */
	LPC_CCU1->PM = 1;
	LPC_CCU2->PM = 1;
}

/**
 * Enable all branch output clocks after the wake up event.
 * Only the clocks with wake up mechanism enabled will be enabled
 */
void Chip_Clock_ClearPowerDown(void)
{
	/* Clear Power Down bit */
	LPC_CCU1->PM = 0;
	LPC_CCU2->PM = 0;
}

/* Returns a peripheral clock rate */
uint32_t Chip_Clock_GetRate(CHIP_CCU_CLK_T clk)
{
	CHIP_CGU_BASE_CLK_T baseclk;
	uint32_t reg, div, rate;

	/* Get CCU config register for clock */
	if (clk >= CLK_CCU2_START) {
		reg = LPC_CCU2->CLKCCU[clk - CLK_CCU2_START].CFG;
	}
	else {
		reg = LPC_CCU1->CLKCCU[clk].CFG;
	}

	/* Is the clock enabled? */
	if (reg & 1) {
		/* Get base clock for this peripheral clock */
		baseclk = Chip_Clock_FindBaseClock(clk);

		/* Get base clock rate */
		rate = Chip_Clock_GetBaseClocktHz(baseclk);

		/* Get divider for this clock */
		if (((reg >> 5) & 0x7) == 0) {
			div = 1;
		}
		else {
			div = 2;/* No other dividers supported */

		}
		rate = rate / div;
	}
	else {
		rate = 0;
	}

	return rate;
}

/* Get EMC Clock Rate */
uint32_t Chip_Clock_GetEMCRate(void)

{
	uint32_t ClkFreq;
	uint32_t EMCDiv;
	ClkFreq = Chip_Clock_GetRate(CLK_MX_EMC);

	/* EMC Divider readback at pos 27
	    TODO: just checked but dont mention in UM */
	EMCDiv = (LPC_CCU1->CLKCCU[CLK_MX_EMC_DIV].CFG >> 27) & 0x07;

	/* Check EMC Divider to get real EMC clock out */
	if ((EMCDiv == 1) && (LPC_CREG->CREG6 & (1 << 16))) {
		ClkFreq >>= 1;
	}
	return ClkFreq;
}

/* Sets up the audio or USB PLL */
void Chip_Clock_SetupPLL(CHIP_CGU_CLKIN_T Input, CHIP_CGU_USB_AUDIO_PLL_T pllnum,
						 const CGU_USBAUDIO_PLL_SETUP_T *pPLLSetup)
{
	uint32_t reg = pPLLSetup->ctrl | (Input << 24);

	/* Setup from passed values */
	LPC_CGU->PLL[pllnum].PLL_CTRL = reg;
	LPC_CGU->PLL[pllnum].PLL_MDIV = pPLLSetup->mdiv;
	LPC_CGU->PLL[pllnum].PLL_NP_DIV = pPLLSetup->ndiv;

	/* Fractional divider is for audio PLL only */
	if (pllnum == CGU_AUDIO_PLL) {
		LPC_CGU->PLL0AUDIO_FRAC = pPLLSetup->fract;
	}
	audio_usb_pll_freq[pllnum] = pPLLSetup->freq;
}

/* Enables the audio or USB PLL */
void Chip_Clock_EnablePLL(CHIP_CGU_USB_AUDIO_PLL_T pllnum)
{
	LPC_CGU->PLL[pllnum].PLL_CTRL &= ~1;
}

/* Disables the audio or USB PLL */
void Chip_Clock_DisablePLL(CHIP_CGU_USB_AUDIO_PLL_T pllnum)
{
	LPC_CGU->PLL[pllnum].PLL_CTRL |= 1;
}

/* Returns the PLL status */
uint32_t Chip_Clock_GetPLLStatus(CHIP_CGU_USB_AUDIO_PLL_T pllnum)
{
	return LPC_CGU->PLL[pllnum].PLL_STAT;
}






