/*
* arch/arm/mach-lpc32xx/clock.c
*
* Author: Kevin Wells <kevin.wells@nxp.com>
*
* Copyright (C) 2010 NXP Semiconductors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* LPC32xx clock management driver overview
*
* The LPC32XX contains a number of high level system clocks that can be
* generated from different sources. These system clocks are used to
* generate the CPU and bus rates and the individual peripheral clocks in
* the system. When Linux is started by the boot loader, the system
* clocks are already running. Stopping a system clock during normal
* Linux operation should never be attempted, as peripherals that require
* those clocks will quit working (ie, DRAM).
*
* The LPC32xx high level clock tree looks as follows. Clocks marked with
* an asterisk are always on and cannot be disabled. Clocks marked with
* an ampersand can only be disabled in CPU suspend mode. Clocks marked
* with a caret are always on if it is the selected clock for the SYSCLK
* source. The clock that isn't used for SYSCLK can be enabled and
* disabled normally.
* 32KHz oscillator*
* / | \
* RTC* PLL397^ TOUCH
* /
* Main oscillator^ /
* | \ /
* | SYSCLK&
* | \
* | \
* USB_PLL HCLK_PLL&
* | | |
* USB host/device PCLK& |
* | |
* Peripherals
*
* The CPU and chip bus rates are derived from the HCLK PLL, which can
* generate various clock rates up to 266MHz and beyond. The internal bus
* rates (PCLK and HCLK) are generated from dividers based on the HCLK
* PLL rate. HCLK can be a ratio of 1:1, 1:2, or 1:4 or HCLK PLL rate,
* while PCLK can be 1:1 to 1:32 of HCLK PLL rate. Most peripherals high
* level clocks are based on either HCLK or PCLK, but have their own
* dividers as part of the IP itself. Because of this, the system clock
* rates should not be changed.
*
* The HCLK PLL is clocked from SYSCLK, which can be derived from the
* main oscillator or PLL397. PLL397 generates a rate that is 397 times
* the 32KHz oscillator rate. The main oscillator runs at the selected
* oscillator/crystal rate on the mosc_in pin of the LPC32xx. This rate
* is normally 13MHz, but depends on the selection of external crystals
* or oscillators. If USB operation is required, the main oscillator must
* be used in the system.
*
* Switching SYSCLK between sources during normal Linux operation is not
* supported. SYSCLK is preset in the bootloader. Because of the
* complexities of clock management during clock frequency changes,
* there are some limitations to the clock driver explained below:
* - The PLL397 and main oscillator can be enabled and disabled by the
* clk_enable() and clk_disable() functions unless SYSCLK is based
* on that clock. This allows the other oscillator that isn't driving
* the HCLK PLL to be used as another system clock that can be routed
* to an external pin.
* - The muxed SYSCLK input and HCLK_PLL rate cannot be changed with
* this driver.
* - HCLK and PCLK rates cannot be changed as part of this driver.
* - Most peripherals have their own dividers are part of the peripheral
* block. Changing SYSCLK, HCLK PLL, HCLK, or PCLK sources or rates
* will also impact the individual peripheral rates.
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
#include <mach/hardware.h>
#include <asm/clkdev.h>
#include <mach/clkdev.h>
#include <mach/platform.h>
#include "clock.h"
#include "common.h"
static struct clk clk_armpll;
static struct clk clk_usbpll;
static DEFINE_MUTEX(clkm_lock);
/*
* Post divider values for PLLs based on selected register value
*/
static const u32 pll_postdivs[4] = {1, 2, 4, 8};
static unsigned long local_return_parent_rate(struct clk *clk)
{
/*
* If a clock has a rate of 0, then it inherits it's parent
* clock rate
*/
while (clk->rate == 0)
clk = clk->parent;
return clk->rate;
}
/* 32KHz clock has a fixed rate and is not stoppable */
static struct clk osc_32KHz = {
.rate = LPC32XX_CLOCK_OSC_FREQ,
.get_rate = local_return_parent_rate,
};
static int local_pll397_enable(struct clk *clk, int enable)
{
u32 reg;
unsigned long timeout = 1 + msecs_to_jiffies(10);
reg = __raw_readl(LPC32XX_CLKPWR_PLL397_CTRL);
if (enable == 0) {
reg |= LPC32XX_CLKPWR_SYSCTRL_PLL397_DIS;
__raw_writel(reg, LPC32XX_CLKPWR_PLL397_CTRL);
} else {
/* Enable PLL397 */
reg &= ~LPC32XX_CLKPWR_SYSCTRL_PLL397_DIS;
__raw_writel(reg, LPC32XX_CLKPWR_PLL397_CTRL);
/* Wait for PLL397 lock */
while (((__raw_readl(LPC32XX_CLKPWR_PLL397_CTRL) &
LPC32XX_CLKPWR_SYSCTRL_PLL397_STS) == 0) &&
(timeout > jiffies))
cpu_relax();
if ((__raw_readl(LPC32XX_CLKPWR_PLL397_CTRL) &
LPC32XX_CLKPWR_SYSCTRL_PLL397_STS) == 0)
return -ENODEV;
}
return 0;
}
static int local_oscmain_enable(struct clk *clk, int enable)
{
u32 reg;
unsigned long timeout = 1 + msecs_to_jiffies(10);
reg = __raw_readl(LPC32XX_CLKPWR_MAIN_OSC_CTRL);
if (enable == 0) {
reg |= LPC32XX_CLKPWR_MOSC_DISABLE;
__raw_writel(reg, LPC32XX_CLKPWR_MAIN_OSC_CTRL);
} else {
/* Enable main oscillator */
reg