Merge branch 'fix' of git://git.kernel.org/pub/scm/linux/kernel/git/ycmiao/pxa-linux-2.6
[pandora-kernel.git] / arch / arm / mach-ux500 / clock.c
index 1b2c989..fe84b90 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <asm/clkdev.h>
 
+#include <plat/mtu.h>
 #include <mach/hardware.h>
 #include "clock.h"
 
@@ -59,6 +60,9 @@
 #define PRCM_DMACLK_MGT                0x074
 #define PRCM_B2R2CLK_MGT       0x078
 #define PRCM_TVCLK_MGT         0x07C
+#define PRCM_TCR               0x1C8
+#define PRCM_TCR_STOPPED       (1 << 16)
+#define PRCM_TCR_DOZE_MODE     (1 << 17)
 #define PRCM_UNIPROCLK_MGT     0x278
 #define PRCM_SSPCLK_MGT                0x280
 #define PRCM_RNGCLK_MGT                0x284
@@ -120,10 +124,95 @@ void clk_disable(struct clk *clk)
 }
 EXPORT_SYMBOL(clk_disable);
 
+/*
+ * The MTU has a separate, rather complex muxing setup
+ * with alternative parents (peripheral cluster or
+ * ULP or fixed 32768 Hz) depending on settings
+ */
+static unsigned long clk_mtu_get_rate(struct clk *clk)
+{
+       void __iomem *addr = __io_address(U8500_PRCMU_BASE)
+               + PRCM_TCR;
+       u32 tcr = readl(addr);
+       int mtu = (int) clk->data;
+       /*
+        * One of these is selected eventually
+        * TODO: Replace the constant with a reference
+        * to the ULP source once this is modeled.
+        */
+       unsigned long clk32k = 32768;
+       unsigned long mturate;
+       unsigned long retclk;
+
+       /* Get the rate from the parent as a default */
+       if (clk->parent_periph)
+               mturate = clk_get_rate(clk->parent_periph);
+       else if (clk->parent_cluster)
+               mturate = clk_get_rate(clk->parent_cluster);
+       else
+               /* We need to be connected SOMEWHERE */
+               BUG();
+
+       /*
+        * Are we in doze mode?
+        * In this mode the parent peripheral or the fixed 32768 Hz
+        * clock is fed into the block.
+        */
+       if (!(tcr & PRCM_TCR_DOZE_MODE)) {
+               /*
+                * Here we're using the clock input from the APE ULP
+                * clock domain. But first: are the timers stopped?
+                */
+               if (tcr & PRCM_TCR_STOPPED) {
+                       clk32k = 0;
+                       mturate = 0;
+               } else {
+                       /* Else default mode: 0 and 2.4 MHz */
+                       clk32k = 0;
+                       if (cpu_is_u5500())
+                               /* DB5500 divides by 8 */
+                               mturate /= 8;
+                       else if (cpu_is_u8500ed()) {
+                               /*
+                                * This clocking setting must not be used
+                                * in the ED chip, it is simply not
+                                * connected anywhere!
+                                */
+                               mturate = 0;
+                               BUG();
+                       } else
+                               /*
+                                * In this mode the ulp38m4 clock is divided
+                                * by a factor 16, on the DB8500 typically
+                                * 38400000 / 16 ~ 2.4 MHz.
+                                * TODO: Replace the constant with a reference
+                                * to the ULP source once this is modeled.
+                                */
+                               mturate = 38400000 / 16;
+               }
+       }
+
+       /* Return the clock selected for this MTU */
+       if (tcr & (1 << mtu))
+               retclk = clk32k;
+       else
+               retclk = mturate;
+
+       pr_info("MTU%d clock rate: %lu Hz\n", mtu, retclk);
+       return retclk;
+}
+
 unsigned long clk_get_rate(struct clk *clk)
 {
        unsigned long rate;
 
+       /*
+        * If there is a custom getrate callback for this clock,
+        * it will take precedence.
+        */
+       if (clk->get_rate)
+               return clk->get_rate(clk);
+
        if (clk->ops && clk->ops->get_rate)
                return clk->ops->get_rate(clk);
 
@@ -341,8 +430,9 @@ static DEFINE_PRCC_CLK(5, usb_v1,   0,  0, NULL);
 
 /* Peripheral Cluster #6 */
 
-static DEFINE_PRCC_CLK(6, mtu1_v1,     8, -1, NULL);
-static DEFINE_PRCC_CLK(6, mtu0_v1,     7, -1, NULL);
+/* MTU ID in data */
+static DEFINE_PRCC_CLK_CUSTOM(6, mtu1_v1, 8, -1, NULL, clk_mtu_get_rate, 1);
+static DEFINE_PRCC_CLK_CUSTOM(6, mtu0_v1, 7, -1, NULL, clk_mtu_get_rate, 0);
 static DEFINE_PRCC_CLK(6, cfgreg_v1,   6,  6, NULL);
 static DEFINE_PRCC_CLK(6, dmc_ed,      6,  6, NULL);
 static DEFINE_PRCC_CLK(6, hash1,       5, -1, NULL);
@@ -357,8 +447,9 @@ static DEFINE_PRCC_CLK(6, rng_v1,   0,  0, &clk_rngclk);
 /* Peripheral Cluster #7 */
 
 static DEFINE_PRCC_CLK(7, tzpc0_ed,    4, -1, NULL);
-static DEFINE_PRCC_CLK(7, mtu1_ed,     3, -1, NULL);
-static DEFINE_PRCC_CLK(7, mtu0_ed,     2, -1, NULL);
+/* MTU ID in data */
+static DEFINE_PRCC_CLK_CUSTOM(7, mtu1_ed, 3, -1, NULL, clk_mtu_get_rate, 1);
+static DEFINE_PRCC_CLK_CUSTOM(7, mtu0_ed, 2, -1, NULL, clk_mtu_get_rate, 0);
 static DEFINE_PRCC_CLK(7, wdg_ed,      1, -1, NULL);
 static DEFINE_PRCC_CLK(7, cfgreg_ed,   0, -1, NULL);
 
@@ -411,7 +502,7 @@ static struct clk_lookup u8500_common_clks[] = {
        CLK(apetraceclk,        "apetrace",     NULL),
        CLK(mcdeclk,    "mcde",         NULL),
        CLK(ipi2clk,    "ipi2",         NULL),
-       CLK(dmaclk,     "dma40",        NULL),
+       CLK(dmaclk,     "dma40.0",      NULL),
        CLK(b2r2clk,    "b2r2",         NULL),
        CLK(tvclk,      "tv",           NULL),
 };
@@ -503,15 +594,17 @@ static struct clk_lookup u8500_v1_clks[] = {
        CLK(uiccclk,    "uicc",         NULL),
 };
 
-static int __init clk_init(void)
+int __init clk_init(void)
 {
        if (cpu_is_u8500ed()) {
                clk_prcmu_ops.enable = clk_prcmu_ed_enable;
                clk_prcmu_ops.disable = clk_prcmu_ed_disable;
+               clk_per6clk.rate = 100000000;
        } else if (cpu_is_u5500()) {
                /* Clock tree for U5500 not implemented yet */
                clk_prcc_ops.enable = clk_prcc_ops.disable = NULL;
                clk_prcmu_ops.enable = clk_prcmu_ops.disable = NULL;
+               clk_per6clk.rate = 26000000;
        }
 
        clkdev_add_table(u8500_common_clks, ARRAY_SIZE(u8500_common_clks));
@@ -522,4 +615,3 @@ static int __init clk_init(void)
 
        return 0;
 }
-arch_initcall(clk_init);