Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / clk / pistachio / clk-pll.c
index e17dada..7e8daab 100644 (file)
 #define MIN_OUTPUT_FRAC                        12000000UL
 #define MAX_OUTPUT_FRAC                        1600000000UL
 
+/* Fractional PLL operating modes */
+enum pll_mode {
+       PLL_MODE_FRAC,
+       PLL_MODE_INT,
+};
+
 struct pistachio_clk_pll {
        struct clk_hw hw;
        void __iomem *base;
@@ -88,12 +94,10 @@ static inline void pll_lock(struct pistachio_clk_pll *pll)
                cpu_relax();
 }
 
-static inline u32 do_div_round_closest(u64 dividend, u32 divisor)
+static inline u64 do_div_round_closest(u64 dividend, u64 divisor)
 {
        dividend += divisor / 2;
-       do_div(dividend, divisor);
-
-       return dividend;
+       return div64_u64(dividend, divisor);
 }
 
 static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw)
@@ -101,6 +105,29 @@ static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw)
        return container_of(hw, struct pistachio_clk_pll, hw);
 }
 
+static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw)
+{
+       struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+       u32 val;
+
+       val = pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_DSMPD;
+       return val ? PLL_MODE_INT : PLL_MODE_FRAC;
+}
+
+static inline void pll_frac_set_mode(struct clk_hw *hw, enum pll_mode mode)
+{
+       struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
+       u32 val;
+
+       val = pll_readl(pll, PLL_CTRL3);
+       if (mode == PLL_MODE_INT)
+               val |= PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD;
+       else
+               val &= ~(PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD);
+
+       pll_writel(pll, val, PLL_CTRL3);
+}
+
 static struct pistachio_pll_rate_table *
 pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref,
               unsigned long fout)
@@ -136,8 +163,7 @@ static int pll_gf40lp_frac_enable(struct clk_hw *hw)
        u32 val;
 
        val = pll_readl(pll, PLL_CTRL3);
-       val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_DACPD |
-                PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD |
+       val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD |
                 PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD);
        pll_writel(pll, val, PLL_CTRL3);
 
@@ -173,8 +199,8 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
        struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
        struct pistachio_pll_rate_table *params;
        int enabled = pll_gf40lp_frac_is_enabled(hw);
-       u32 val, vco, old_postdiv1, old_postdiv2;
-       const char *name = __clk_get_name(hw->clk);
+       u64 val, vco, old_postdiv1, old_postdiv2;
+       const char *name = clk_hw_get_name(hw);
 
        if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC)
                return -EINVAL;
@@ -183,17 +209,21 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
        if (!params || !params->refdiv)
                return -EINVAL;
 
-       vco = params->fref * params->fbdiv / params->refdiv;
+       /* calculate vco */
+       vco = params->fref;
+       vco *= (params->fbdiv << 24) + params->frac;
+       vco = div64_u64(vco, params->refdiv << 24);
+
        if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC)
-               pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco,
+               pr_warn("%s: VCO %llu is out of range %lu..%lu\n", name, vco,
                        MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC);
 
-       val = params->fref / params->refdiv;
+       val = div64_u64(params->fref, params->refdiv);
        if (val < MIN_PFD)
-               pr_warn("%s: PFD %u is too low (min %lu)\n",
+               pr_warn("%s: PFD %llu is too low (min %lu)\n",
                        name, val, MIN_PFD);
        if (val > vco / 16)
-               pr_warn("%s: PFD %u is too high (max %u)\n",
+               pr_warn("%s: PFD %llu is too high (max %llu)\n",
                        name, val, vco / 16);
 
        val = pll_readl(pll, PLL_CTRL1);
@@ -227,6 +257,12 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate,
                (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT);
        pll_writel(pll, val, PLL_CTRL2);
 
+       /* set operating mode */
+       if (params->frac)
+               pll_frac_set_mode(hw, PLL_MODE_FRAC);
+       else
+               pll_frac_set_mode(hw, PLL_MODE_INT);
+
        if (enabled)
                pll_lock(pll);
 
@@ -237,8 +273,7 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw,
                                                 unsigned long parent_rate)
 {
        struct pistachio_clk_pll *pll = to_pistachio_pll(hw);
-       u32 val, prediv, fbdiv, frac, postdiv1, postdiv2;
-       u64 rate = parent_rate;
+       u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate;
 
        val = pll_readl(pll, PLL_CTRL1);
        prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK;
@@ -251,7 +286,13 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw,
                PLL_FRAC_CTRL2_POSTDIV2_MASK;
        frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK;
 
-       rate *= (fbdiv << 24) + frac;
+       /* get operating mode (int/frac) and calculate rate accordingly */
+       rate = parent_rate;
+       if (pll_frac_get_mode(hw) == PLL_MODE_FRAC)
+               rate *= (fbdiv << 24) + frac;
+       else
+               rate *= (fbdiv << 24);
+
        rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24);
 
        return rate;
@@ -279,7 +320,7 @@ static int pll_gf40lp_laint_enable(struct clk_hw *hw)
        u32 val;
 
        val = pll_readl(pll, PLL_CTRL1);
-       val &= ~(PLL_INT_CTRL1_PD | PLL_INT_CTRL1_DSMPD |
+       val &= ~(PLL_INT_CTRL1_PD |
                 PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD);
        pll_writel(pll, val, PLL_CTRL1);
 
@@ -316,7 +357,7 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,
        struct pistachio_pll_rate_table *params;
        int enabled = pll_gf40lp_laint_is_enabled(hw);
        u32 val, vco, old_postdiv1, old_postdiv2;
-       const char *name = __clk_get_name(hw->clk);
+       const char *name = clk_hw_get_name(hw);
 
        if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA)
                return -EINVAL;
@@ -325,12 +366,12 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate,
        if (!params || !params->refdiv)
                return -EINVAL;
 
-       vco = params->fref * params->fbdiv / params->refdiv;
+       vco = div_u64(params->fref * params->fbdiv, params->refdiv);
        if (vco < MIN_VCO_LA || vco > MAX_VCO_LA)
                pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco,
                        MIN_VCO_LA, MAX_VCO_LA);
 
-       val = params->fref / params->refdiv;
+       val = div_u64(params->fref, params->refdiv);
        if (val < MIN_PFD)
                pr_warn("%s: PFD %u is too low (min %lu)\n",
                        name, val, MIN_PFD);