summaryrefslogtreecommitdiff
path: root/arch/arm/plat-s3c64xx
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2009-05-02 13:48:53 +0100
committerBen Dooks <ben-linux@fluff.org>2009-05-07 11:46:41 +0100
commit496a3f0927197ca155a604478bf6e7db3eca3bbd (patch)
tree8f0f68a8f2627743af393b17c5d0fd613f967578 /arch/arm/plat-s3c64xx
parent8f1ecf1d965f4e1842b32af5dd40f3d7b6d407ed (diff)
[ARM] S3C64XX: Add ARM clock
Add ARM clock to provide 'arm' from the APLL to the ARM core. Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/plat-s3c64xx')
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/s3c6400.h2
-rw-r--r--arch/arm/plat-s3c64xx/s3c6400-clock.c76
2 files changed, 76 insertions, 2 deletions
diff --git a/arch/arm/plat-s3c64xx/include/plat/s3c6400.h b/arch/arm/plat-s3c64xx/include/plat/s3c6400.h
index e12296666d46..11f2e1e119b0 100644
--- a/arch/arm/plat-s3c64xx/include/plat/s3c6400.h
+++ b/arch/arm/plat-s3c64xx/include/plat/s3c6400.h
@@ -15,7 +15,7 @@
/* Common init code for S3C6400 related SoCs */
extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no);
-extern void s3c6400_register_clocks(void);
+extern void s3c6400_register_clocks(unsigned armclk_divlimit);
extern void s3c6400_setup_clocks(void);
#ifdef CONFIG_CPU_S3C6400
diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c
index 96fa9ea4d5bc..1debc1f9f987 100644
--- a/arch/arm/plat-s3c64xx/s3c6400-clock.c
+++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c
@@ -133,6 +133,65 @@ static struct clksrc_clk clk_mout_mpll = {
.sources = &clk_src_mpll,
};
+static unsigned int armclk_mask;
+
+static unsigned long s3c64xx_clk_arm_get_rate(struct clk *clk)
+{
+ unsigned long rate = clk_get_rate(clk->parent);
+ u32 clkdiv;
+
+ /* divisor mask starts at bit0, so no need to shift */
+ clkdiv = __raw_readl(S3C_CLK_DIV0) & armclk_mask;
+
+ return rate / (clkdiv + 1);
+}
+
+static unsigned long s3c64xx_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) - 1;
+ if (div > armclk_mask)
+ div = armclk_mask;
+
+ return parent / (div + 1);
+}
+
+static int s3c64xx_clk_arm_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long parent = clk_get_rate(clk->parent);
+ u32 div;
+ u32 val;
+
+ if (rate < parent / (armclk_mask + 1))
+ return -EINVAL;
+
+ rate = clk_round_rate(clk, rate);
+ div = clk_get_rate(clk->parent) / rate;
+
+ val = __raw_readl(S3C_CLK_DIV0);
+ val &= armclk_mask;
+ val |= (div - 1);
+ __raw_writel(val, S3C_CLK_DIV0);
+
+ return 0;
+
+}
+
+static struct clk clk_arm = {
+ .name = "armclk",
+ .id = -1,
+ .parent = &clk_mout_apll.clk,
+ .get_rate = s3c64xx_clk_arm_get_rate,
+ .set_rate = s3c64xx_clk_arm_set_rate,
+ .round_rate = s3c64xx_clk_arm_round_rate,
+};
+
static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk)
{
unsigned long rate = clk_get_rate(clk->parent);
@@ -665,14 +724,29 @@ static struct clk *clks[] __initdata = {
&clk_audio1.clk,
&clk_irda.clk,
&clk_camif.clk,
+ &clk_arm,
};
-void __init s3c6400_register_clocks(void)
+/**
+ * s3c6400_register_clocks - register clocks for s3c6400 and above
+ * @armclk_divlimit: Divisor mask for ARMCLK
+ *
+ * Register the clocks for the S3C6400 and above SoC range, such
+ * as ARMCLK and the clocks which have divider chains attached.
+ *
+ * This call does not setup the clocks, which is left to the
+ * s3c6400_setup_clocks() call which may be needed by the cpufreq
+ * or resume code to re-set the clocks if the bootloader has changed
+ * them.
+ */
+void __init s3c6400_register_clocks(unsigned armclk_divlimit)
{
struct clk *clkp;
int ret;
int ptr;
+ armclk_mask = armclk_divlimit;
+
for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
clkp = clks[ptr];
ret = s3c24xx_register_clock(clkp);