]> err.no Git - linux-2.6/blobdiff - arch/arm/plat-s3c24xx/clock.c
[ARM] 4967/1: Adds functions to set clkout rate for Samsung S3C2410
[linux-2.6] / arch / arm / plat-s3c24xx / clock.c
index d3dc03a7383a0379c0f467fe082948df85931913..d84167fb33b1fa69df54602a925ffc2f573ed008 100644 (file)
@@ -172,6 +172,15 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
        if (IS_ERR(clk))
                return -EINVAL;
 
+       /* We do not default just do a clk->rate = rate as
+        * the clock may have been made this way by choice.
+        */
+
+       WARN_ON(clk->set_rate == NULL);
+
+       if (clk->set_rate == NULL)
+               return -EINVAL;
+
        mutex_lock(&clocks_mutex);
        ret = (clk->set_rate)(clk, rate);
        mutex_unlock(&clocks_mutex);
@@ -213,6 +222,12 @@ EXPORT_SYMBOL(clk_set_parent);
 
 /* base clocks */
 
+static int clk_default_setrate(struct clk *clk, unsigned long rate)
+{
+       clk->rate = rate;
+       return 0;
+}
+
 struct clk clk_xtal = {
        .name           = "xtal",
        .id             = -1,
@@ -224,6 +239,7 @@ struct clk clk_xtal = {
 struct clk clk_mpll = {
        .name           = "mpll",
        .id             = -1,
+       .set_rate       = clk_default_setrate,
 };
 
 struct clk clk_upll = {
@@ -239,6 +255,7 @@ struct clk clk_f = {
        .rate           = 0,
        .parent         = &clk_mpll,
        .ctrlbit        = 0,
+       .set_rate       = clk_default_setrate,
 };
 
 struct clk clk_h = {
@@ -247,6 +264,7 @@ struct clk clk_h = {
        .rate           = 0,
        .parent         = NULL,
        .ctrlbit        = 0,
+       .set_rate       = clk_default_setrate,
 };
 
 struct clk clk_p = {
@@ -255,6 +273,7 @@ struct clk clk_p = {
        .rate           = 0,
        .parent         = NULL,
        .ctrlbit        = 0,
+       .set_rate       = clk_default_setrate,
 };
 
 struct clk clk_usb_bus = {
@@ -313,6 +332,58 @@ static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent)
        return 0;
 }
 
+static unsigned long s3c24xx_calc_div(struct clk *clk, unsigned long rate)
+{
+       unsigned long div;
+
+       if ((rate == 0) || !clk->parent)
+               return 0;
+
+       div = clk_get_rate(clk->parent) / rate;
+       if (div < 2)
+               div = 2;
+       else if (div > 16)
+               div = 16;
+
+       return div;
+}
+
+static unsigned long s3c24xx_round_dclk_rate(struct clk *clk,
+       unsigned long rate)
+{
+       unsigned long div = s3c24xx_calc_div(clk, rate);
+
+       if (div == 0)
+               return 0;
+
+       return clk_get_rate(clk->parent) / div;
+}
+
+static int s3c24xx_set_dclk_rate(struct clk *clk, unsigned long rate)
+{
+       unsigned long mask, data, div = s3c24xx_calc_div(clk, rate);
+
+       if (div == 0)
+               return -EINVAL;
+
+       if (clk == &s3c24xx_dclk0) {
+               mask = S3C2410_DCLKCON_DCLK0_DIV_MASK |
+                       S3C2410_DCLKCON_DCLK0_CMP_MASK;
+               data = S3C2410_DCLKCON_DCLK0_DIV(div) |
+                       S3C2410_DCLKCON_DCLK0_CMP((div + 1) / 2);
+       } else if (clk == &s3c24xx_dclk1) {
+               mask = S3C2410_DCLKCON_DCLK1_DIV_MASK |
+                       S3C2410_DCLKCON_DCLK1_CMP_MASK;
+               data = S3C2410_DCLKCON_DCLK1_DIV(div) |
+                       S3C2410_DCLKCON_DCLK1_CMP((div + 1) / 2);
+       } else
+               return -EINVAL;
+
+       clk->rate = clk_get_rate(clk->parent) / div;
+       __raw_writel(((__raw_readl(S3C24XX_DCLKCON) & ~mask) | data),
+               S3C24XX_DCLKCON);
+       return clk->rate;
+}
 
 static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent)
 {
@@ -359,6 +430,8 @@ struct clk s3c24xx_dclk0 = {
        .ctrlbit        = S3C2410_DCLKCON_DCLK0EN,
        .enable         = s3c24xx_dclk_enable,
        .set_parent     = s3c24xx_dclk_setparent,
+       .set_rate       = s3c24xx_set_dclk_rate,
+       .round_rate     = s3c24xx_round_dclk_rate,
 };
 
 struct clk s3c24xx_dclk1 = {
@@ -367,6 +440,8 @@ struct clk s3c24xx_dclk1 = {
        .ctrlbit        = S3C2410_DCLKCON_DCLK0EN,
        .enable         = s3c24xx_dclk_enable,
        .set_parent     = s3c24xx_dclk_setparent,
+       .set_rate       = s3c24xx_set_dclk_rate,
+       .round_rate     = s3c24xx_round_dclk_rate,
 };
 
 struct clk s3c24xx_clkout0 = {
@@ -404,6 +479,18 @@ int s3c24xx_register_clock(struct clk *clk)
        return 0;
 }
 
+int s3c24xx_register_clocks(struct clk **clks, int nr_clks)
+{
+       int fails = 0;
+
+       for (; nr_clks > 0; nr_clks--, clks++) {
+               if (s3c24xx_register_clock(*clks) < 0)
+                       fails++;
+       }
+
+       return fails;
+}
+
 /* initalise all the clocks */
 
 int __init s3c24xx_setup_clocks(unsigned long xtal,