]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - arch/arm/cpu/arm926ejs/mxs/clock.c
upgrade to upstream version 2013.07
[karo-tx-uboot.git] / arch / arm / cpu / arm926ejs / mxs / clock.c
index e9d8800f8c1b30f26d3598bdbb18d14732949638..8b5ad018ea7d3000c093f14c2090ccc810ceb650 100644 (file)
 #define MXC_SSPCLK_MAX MXC_SSPCLK3
 #endif
 
-static uint32_t mxs_get_pclk(void)
+static struct mxs_clkctrl_regs *clkctrl_regs = (void *)MXS_CLKCTRL_BASE;
+
+static uint32_t get_frac_clk(uint32_t refclk, uint32_t div, uint32_t _mask)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
+       uint32_t mask = (_mask + 1) >> 1;
+       uint32_t acc = div;
+       int period = 0;
+       int mult = 0;
+
+       if (div & mask)
+               return 0;
+
+       do {
+               acc += div;
+               if (acc & mask) {
+                       acc &= ~mask;
+                       mult++;
+               }
+               period++;
+       } while (acc != div);
 
+       return refclk * mult / period;
+}
+
+static uint32_t mxs_get_pclk(void)
+{
        uint32_t clkctrl, clkseq, div;
        uint8_t clkfrac, frac;
 
        clkctrl = readl(&clkctrl_regs->hw_clkctrl_cpu);
 
-       /* No support of fractional divider calculation */
+       div = clkctrl & CLKCTRL_CPU_DIV_CPU_MASK;
+       clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]);
+       frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
+       clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
+
        if (clkctrl &
                (CLKCTRL_CPU_DIV_XTAL_FRAC_EN | CLKCTRL_CPU_DIV_CPU_FRAC_EN)) {
-               return 0;
+               uint32_t refclk, mask;
+
+               if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
+                       refclk = XTAL_FREQ_MHZ;
+                       mask = CLKCTRL_CPU_DIV_XTAL_MASK >>
+                               CLKCTRL_CPU_DIV_XTAL_OFFSET;
+                       div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
+                               CLKCTRL_CPU_DIV_XTAL_OFFSET;
+               } else {
+                       refclk = PLL_FREQ_MHZ * PLL_FREQ_COEF / frac;
+                       mask = CLKCTRL_CPU_DIV_CPU_MASK;
+               }
+               return get_frac_clk(refclk, div, mask);
        }
 
-       clkseq = readl(&clkctrl_regs->hw_clkctrl_clkseq);
-
        /* XTAL Path */
        if (clkseq & CLKCTRL_CLKSEQ_BYPASS_CPU) {
                div = (clkctrl & CLKCTRL_CPU_DIV_XTAL_MASK) >>
@@ -60,35 +95,26 @@ static uint32_t mxs_get_pclk(void)
        }
 
        /* REF Path */
-       clkfrac = readb(&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]);
-       frac = clkfrac & CLKCTRL_FRAC_FRAC_MASK;
-       div = clkctrl & CLKCTRL_CPU_DIV_CPU_MASK;
        return (PLL_FREQ_MHZ * PLL_FREQ_COEF / frac) / div;
 }
 
 static uint32_t mxs_get_hclk(void)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
-
        uint32_t div;
        uint32_t clkctrl;
+       uint32_t refclk = mxs_get_pclk();
 
        clkctrl = readl(&clkctrl_regs->hw_clkctrl_hbus);
+       div = clkctrl & CLKCTRL_HBUS_DIV_MASK;
 
-       /* No support of fractional divider calculation */
        if (clkctrl & CLKCTRL_HBUS_DIV_FRAC_EN)
-               return 0;
+               return get_frac_clk(refclk, div, CLKCTRL_HBUS_DIV_MASK);
 
-       div = clkctrl & CLKCTRL_HBUS_DIV_MASK;
-       return mxs_get_pclk() / div;
+       return refclk / div;
 }
 
 static uint32_t mxs_get_emiclk(void)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
-
        uint32_t clkctrl, clkseq, div;
        uint8_t clkfrac, frac;
 
@@ -111,8 +137,6 @@ static uint32_t mxs_get_emiclk(void)
 
 static uint32_t mxs_get_gpmiclk(void)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
 #if defined(CONFIG_MX23)
        uint8_t *reg =
                &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU];
@@ -144,8 +168,6 @@ static uint32_t mxs_get_gpmiclk(void)
  */
 void mxs_set_ioclk(enum mxs_ioclock io, uint32_t freq)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
        uint32_t div;
        int io_reg;
 
@@ -177,14 +199,14 @@ void mxs_set_ioclk(enum mxs_ioclock io, uint32_t freq)
  */
 static uint32_t mxs_get_ioclk(enum mxs_ioclock io)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
        uint8_t ret;
        int io_reg;
 
-       if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1))
+       if ((io < MXC_IOCLK0) || (io > MXC_IOCLK1)) {
+               printf("%s: IO clock selector %u out of range %u..%u\n",
+                       __func__, io, MXC_IOCLK0, MXC_IOCLK1);
                return 0;
-
+       }
        io_reg = CLKCTRL_FRAC0_IO0 - io;        /* Register order is reversed */
 
        ret = readb(&clkctrl_regs->hw_clkctrl_frac0[io_reg]) &
@@ -198,8 +220,6 @@ static uint32_t mxs_get_ioclk(enum mxs_ioclock io)
  */
 void mxs_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
        uint32_t clk, clkreg;
 
        if (ssp > MXC_SSPCLK_MAX)
@@ -242,9 +262,7 @@ void mxs_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal)
  */
 static uint32_t mxs_get_sspclk(enum mxs_sspclock ssp)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
-       uint32_t clkreg;
+       uint32_t *clkreg;
        uint32_t clk, tmp;
 
        if (ssp > MXC_SSPCLK_MAX)
@@ -254,11 +272,10 @@ static uint32_t mxs_get_sspclk(enum mxs_sspclock ssp)
        if (tmp & (CLKCTRL_CLKSEQ_BYPASS_SSP0 << ssp))
                return XTAL_FREQ_KHZ;
 
-       clkreg = (uint32_t)(&clkctrl_regs->hw_clkctrl_ssp0) +
-                       (ssp * sizeof(struct mxs_register_32));
+       clkreg = &clkctrl_regs->hw_clkctrl_ssp0 +
+                       ssp * sizeof(struct mxs_register_32);
 
        tmp = readl(clkreg) & CLKCTRL_SSP_DIV_MASK;
-
        if (tmp == 0)
                return 0;
 
@@ -311,8 +328,6 @@ void mxs_set_ssp_busclock(unsigned int bus, uint32_t freq)
 
 void mxs_set_lcdclk(uint32_t freq)
 {
-       struct mxs_clkctrl_regs *clkctrl_regs =
-               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
        uint32_t fp, x, k_rest, k_best, x_best, tk;
        int32_t k_best_l = 999, k_best_t = 0, x_best_l = 0xff, x_best_t = 0xff;
 
@@ -402,6 +417,21 @@ void mxs_set_lcdclk(uint32_t freq)
 #endif
 }
 
+static uint32_t mxs_get_xbus_clk(void)
+{
+       uint32_t div;
+       uint32_t clkctrl;
+       uint32_t refclk = mxs_get_pclk();
+
+       clkctrl = readl(&clkctrl_regs->hw_clkctrl_xbus);
+       div = clkctrl & CLKCTRL_XBUS_DIV_MASK;
+
+       if (clkctrl & CLKCTRL_XBUS_DIV_FRAC_EN)
+               return get_frac_clk(refclk, div, CLKCTRL_XBUS_DIV_MASK);
+
+       return refclk / div;
+}
+
 uint32_t mxc_get_clock(enum mxc_clock clk)
 {
        switch (clk) {
@@ -430,6 +460,10 @@ uint32_t mxc_get_clock(enum mxc_clock clk)
        case MXC_SSP3_CLK:
                return mxs_get_sspclk(MXC_SSPCLK3);
 #endif
+       case MXC_XBUS_CLK:
+               return mxs_get_xbus_clk() * 1000000;
+       default:
+               printf("Invalid clock selector %u\n", clk);
        }
 
        return 0;