/* linux/arch/arm/mach-s3c2443/clock.c
*
* Copyright (c) 2007 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* S3C2443 Clock control support
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/sysdev.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/serial_core.h>
#include <asm/mach/map.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/arch/regs-s3c2443-clock.h>
#include <asm/plat-s3c24xx/s3c2443.h>
#include <asm/plat-s3c24xx/clock.h>
#include <asm/plat-s3c24xx/cpu.h>
/* We currently have to assume that the system is running
* from the XTPll input, and that all ***REFCLKs are being
* fed from it, as we cannot read the state of OM[4] from
* software.
*
* It would be possible for each board initialisation to
* set the correct muxing at initialisation
*/
static int s3c2443_clkcon_enable_h(struct clk *clk, int enable)
{
unsigned int clocks = clk->ctrlbit;
unsigned long clkcon;
clkcon = __raw_readl(S3C2443_HCLKCON);
if (enable)
clkcon |= clocks;
else
clkcon &= ~clocks;
__raw_writel(clkcon, S3C2443_HCLKCON);
return 0;
}
static int s3c2443_clkcon_enable_p(struct clk *clk, int enable)
{
unsigned int clocks = clk->ctrlbit;
unsigned long clkcon;
clkcon = __raw_readl(S3C2443_PCLKCON);
if (enable)
clkcon |= clocks;
else
clkcon &= ~clocks;
__raw_writel(clkcon, S3C2443_HCLKCON);
return 0;
}
static int s3c2443_clkcon_enable_s(struct clk *clk, int enable)
{
unsigned int clocks = clk->ctrlbit;
unsigned long clkcon;
clkcon = __raw_readl(S3C2443_SCLKCON);
if (enable)
clkcon |= clocks;
else
clkcon &= ~clocks;
__raw_writel(clkcon, S3C2443_SCLKCON);
return 0;
}
static unsigned long s3c2443_roundrate_clksrc(struct clk *clk,
unsigned long rate,
unsigned int max)
{
unsigned long parent_rate = clk_get_rate(clk->parent);
int div;
if (rate > parent_rate)
return parent_rate;
/* note, we remove the +/- 1 calculations as they cancel out */
div = (rate / parent_rate);
if (div < 1)
div = 1;
else if (div > max)
div = max;
return parent_rate / div;
}
static unsigned long s3c2443_roundrate_clksrc4(struct clk *clk,
unsigned long rate)
{
return s3c2443_roundrate_clksrc(clk, rate, 4);
}
static unsigned long s3c2443_roundrate_clksrc16(struct clk *clk,
unsigned long rate)
{
return s3c2443_roundrate_clksrc(clk, rate, 16);
}
static unsigned long s3c2443_roundrate_clksrc256(struct clk *clk,
unsigned long rate)
{
return s3c2443_roundrate_clksrc(clk, rate, 256);
}
/* clock selections */
/* CPU EXTCLK input */
static struct clk clk_ext = {
.name = "ext",
.id = -1,
};
static struct clk clk_mpllref = {
.name = "mpllref",
.parent = &clk_xtal,
.id = -1,
};
#if 0
static struct clk clk_mpll = {
.name = "mpll",
.parent = &clk_mpllref,
.id = -1,
};
#endif
static struct clk clk_epllref;
static struct clk clk_epll = {
.name = "epll",
.parent = &clk_epllref,
.id = -1,
};
static struct clk clk_i2s_ext = {
.name = "i2s-ext",
.id = -1,
};
static int s3c2443_setparent_epllref(struct clk *clk, struct clk *parent)
{
unsigned long clksrc = __raw_readl(S3C2443_CLKSRC);
clksrc &= ~S3C2443