/* linux/arch/arm/plat-s5pc1xx/s5pc100-clock.c
*
* Copyright 2009 Samsung Electronics, Co.
* Byungho Min <bhmin@samsung.com>
*
* S5PC100 based common clock support
*
* Based on plat-s3c64xx/s3c6400-clock.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#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/clk.h>
#include <linux/sysdev.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <mach/map.h>
#include <plat/cpu-freq.h>
#include <plat/regs-clock.h>
#include <plat/clock.h>
#include <plat/cpu.h>
#include <plat/pll.h>
#include <plat/devs.h>
#include <plat/s5pc100.h>
/* fin_apll, fin_mpll and fin_epll are all the same clock, which we call
* ext_xtal_mux for want of an actual name from the manual.
*/
static struct clk clk_ext_xtal_mux = {
.name = "ext_xtal",
.id = -1,
};
#define clk_fin_apll clk_ext_xtal_mux
#define clk_fin_mpll clk_ext_xtal_mux
#define clk_fin_epll clk_ext_xtal_mux
#define clk_fin_hpll clk_ext_xtal_mux
#define clk_fout_mpll clk_mpll
#define clk_vclk_54m clk_54m
struct clk_sources {
unsigned int nr_sources;
struct clk **sources;
};
struct clksrc_clk {
struct clk clk;
unsigned int mask;
unsigned int shift;
struct clk_sources *sources;
unsigned int divider_shift;
void __iomem *reg_divider;
void __iomem *reg_source;
};
/* APLL */
static struct clk clk_fout_apll = {
.name = "fout_apll",
.id = -1,
.rate = 27000000,
};
static struct clk *clk_src_apll_list[] = {
[0] = &clk_fin_apll,
[1] = &clk_fout_apll,
};
static struct clk_sources clk_src_apll = {
.sources = clk_src_apll_list,
.nr_sources = ARRAY_SIZE(clk_src_apll_list),
};
static struct clksrc_clk clk_mout_apll = {
.clk = {
.name = "mout_apll",
.id = -1,
},
.shift = S5PC100_CLKSRC0_APLL_SHIFT,
.mask = S5PC100_CLKSRC0_APLL_MASK,
.sources = &clk_src_apll,
.reg_source = S5PC100_CLKSRC0,
};
static unsigned long s5pc100_clk_dout_apll_get_rate(struct clk *clk)
{
unsigned long rate = clk_get_rate(clk->parent);
unsigned int ratio;
ratio = __raw_readl(S5PC100_CLKDIV0) & S5PC100_CLKDIV0_APLL_MASK;
ratio >>= S5PC100_CLKDIV0_APLL_SHIFT;
return rate / (ratio + 1);
}
static struct clk clk_dout_apll = {
.name = "dout_apll",
.id = -1,
.parent = &clk_mout_apll.clk,
.get_rate = s5pc100_clk_dout_apll_get_rate,
};
static unsigned long s5pc100_clk_arm_get_rate(struct clk *clk)
{
unsigned long rate = clk_get_rate(clk->parent);
unsigned int ratio;
ratio = __raw_readl(S5PC100_CLKDIV0) & S5PC100_CLKDIV0_ARM_MASK;
ratio >>= S5PC100_CLKDIV0_ARM_SHIFT;
return rate / (ratio + 1);
}
static unsigned long s5pc100_clk_arm_round_rate(struct clk *clk,
unsigned long rate)
{
unsigned long parent = clk_get_rate(clk->parent);
u32 div;
if (parent < rate)
return rate;
div = (parent / rate) -