mfd: dbx500: Provide a more accurate smp_twd clock
authorMichel Jaouen <michel.jaouen@stericsson.com>
Fri, 31 Aug 2012 12:21:30 +0000 (14:21 +0200)
committerMike Turquette <mturquette@linaro.org>
Fri, 7 Sep 2012 00:57:21 +0000 (17:57 -0700)
The local timer clock is based on ARM subsystem clock. This patch
obtains a more exact value of that clock by reading PRCMU registers.
Using this increases the accuracy of the local timer events.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Rickard Andersson <rickard.andersson@stericsson.com>
Signed-off-by: Michel Jaouen <michel.jaouen@stericsson.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
drivers/mfd/db8500-prcmu.c
drivers/mfd/dbx500-prcmu-regs.h
include/linux/mfd/dbx500-prcmu.h

index 7040a0081130c93ce6b73145355abec0a8c571b8..6b37e2d6ed8f5dd579c9ba655373641463ccfe99 100644 (file)
@@ -418,6 +418,9 @@ static struct {
 
 static atomic_t ac_wake_req_state = ATOMIC_INIT(0);
 
+/* Functions definition */
+static void compute_armss_rate(void);
+
 /* Spinlocks */
 static DEFINE_SPINLOCK(prcmu_lock);
 static DEFINE_SPINLOCK(clkout_lock);
@@ -517,6 +520,7 @@ static struct dsiescclk dsiescclk[3] = {
        }
 };
 
+
 /*
 * Used by MCDE to setup all necessary PRCMU registers
 */
@@ -1013,6 +1017,7 @@ int db8500_prcmu_set_arm_opp(u8 opp)
                (mb1_transfer.ack.arm_opp != opp))
                r = -EIO;
 
+       compute_armss_rate();
        mutex_unlock(&mb1_transfer.lock);
 
        return r;
@@ -1612,6 +1617,7 @@ static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate,
        if ((branch == PLL_FIX) || ((branch == PLL_DIV) &&
                (val & PRCM_PLL_FREQ_DIV2EN) &&
                ((reg == PRCM_PLLSOC0_FREQ) ||
+                (reg == PRCM_PLLARM_FREQ) ||
                 (reg == PRCM_PLLDDR_FREQ))))
                div *= 2;
 
@@ -1661,6 +1667,39 @@ static unsigned long clock_rate(u8 clock)
        else
                return 0;
 }
+static unsigned long latest_armss_rate;
+static unsigned long armss_rate(void)
+{
+       return latest_armss_rate;
+}
+
+static void compute_armss_rate(void)
+{
+       u32 r;
+       unsigned long rate;
+
+       r = readl(PRCM_ARM_CHGCLKREQ);
+
+       if (r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ) {
+               /* External ARMCLKFIX clock */
+
+               rate = pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_FIX);
+
+               /* Check PRCM_ARM_CHGCLKREQ divider */
+               if (!(r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL))
+                       rate /= 2;
+
+               /* Check PRCM_ARMCLKFIX_MGT divider */
+               r = readl(PRCM_ARMCLKFIX_MGT);
+               r &= PRCM_CLK_MGT_CLKPLLDIV_MASK;
+               rate /= r;
+
+       } else {/* ARM PLL */
+               rate = pll_rate(PRCM_PLLARM_FREQ, ROOT_CLOCK_RATE, PLL_DIV);
+       }
+
+       latest_armss_rate = rate;
+}
 
 static unsigned long dsiclk_rate(u8 n)
 {
@@ -1707,6 +1746,8 @@ unsigned long prcmu_clock_rate(u8 clock)
                return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
        else if (clock == PRCMU_PLLSOC1)
                return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
+       else if (clock == PRCMU_ARMSS)
+               return armss_rate();
        else if (clock == PRCMU_PLLDDR)
                return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW);
        else if (clock == PRCMU_PLLDSI)
@@ -2693,6 +2734,7 @@ void __init db8500_prcmu_early_init(void)
                                         handle_simple_irq);
                set_irq_flags(irq, IRQF_VALID);
        }
+       compute_armss_rate();
 }
 
 static void __init init_prcm_registers(void)
index 23108a6e316782612eafda90cb2944b37a644e0e..79c76ebdba525c7e57970512aa6e02e7c7d28a88 100644 (file)
@@ -61,7 +61,8 @@
 #define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3   0x2
 
 #define PRCM_ARM_CHGCLKREQ     (_PRCMU_BASE + 0x114)
-#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ  0x1
+#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ  BIT(0)
+#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL     BIT(16)
 
 #define PRCM_PLLARM_ENABLE     (_PRCMU_BASE + 0x98)
 #define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE  0x1
 /* PRCMU clock/PLL/reset registers */
 #define PRCM_PLLSOC0_FREQ         (_PRCMU_BASE + 0x080)
 #define PRCM_PLLSOC1_FREQ         (_PRCMU_BASE + 0x084)
+#define PRCM_PLLARM_FREQ          (_PRCMU_BASE + 0x088)
 #define PRCM_PLLDDR_FREQ          (_PRCMU_BASE + 0x08C)
 #define PRCM_PLL_FREQ_D_SHIFT  0
 #define PRCM_PLL_FREQ_D_MASK   BITS(0, 7)
index 5b90e94399e1b2d8707885ee85be713b9288ab57..c410d99bd6678ea86dc7f704f0355a4e40517218 100644 (file)
@@ -136,6 +136,7 @@ enum prcmu_clock {
        PRCMU_TIMCLK,
        PRCMU_PLLSOC0,
        PRCMU_PLLSOC1,
+       PRCMU_ARMSS,
        PRCMU_PLLDDR,
        PRCMU_PLLDSI,
        PRCMU_DSI0CLK,