|
@@ -133,6 +133,65 @@ static struct clksrc_clk clk_mout_mpll = {
|
|
.sources = &clk_src_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)
|
|
static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk)
|
|
{
|
|
{
|
|
unsigned long rate = clk_get_rate(clk->parent);
|
|
unsigned long rate = clk_get_rate(clk->parent);
|
|
@@ -665,14 +724,29 @@ static struct clk *clks[] __initdata = {
|
|
&clk_audio1.clk,
|
|
&clk_audio1.clk,
|
|
&clk_irda.clk,
|
|
&clk_irda.clk,
|
|
&clk_camif.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;
|
|
struct clk *clkp;
|
|
int ret;
|
|
int ret;
|
|
int ptr;
|
|
int ptr;
|
|
|
|
|
|
|
|
+ armclk_mask = armclk_divlimit;
|
|
|
|
+
|
|
for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
|
|
for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
|
|
clkp = clks[ptr];
|
|
clkp = clks[ptr];
|
|
ret = s3c24xx_register_clock(clkp);
|
|
ret = s3c24xx_register_clock(clkp);
|