aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/imx/clk-pllv4.c72
1 files changed, 63 insertions, 9 deletions
diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c
index d38bc9f87c1d..d7e62c3620d3 100644
--- a/drivers/clk/imx/clk-pllv4.c
+++ b/drivers/clk/imx/clk-pllv4.c
@@ -30,6 +30,9 @@
30/* PLL Denominator Register (xPLLDENOM) */ 30/* PLL Denominator Register (xPLLDENOM) */
31#define PLL_DENOM_OFFSET 0x14 31#define PLL_DENOM_OFFSET 0x14
32 32
33#define MAX_MFD 0x3fffffff
34#define DEFAULT_MFD 1000000
35
33struct clk_pllv4 { 36struct clk_pllv4 {
34 struct clk_hw hw; 37 struct clk_hw hw;
35 void __iomem *base; 38 void __iomem *base;
@@ -64,13 +67,20 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
64 unsigned long parent_rate) 67 unsigned long parent_rate)
65{ 68{
66 struct clk_pllv4 *pll = to_clk_pllv4(hw); 69 struct clk_pllv4 *pll = to_clk_pllv4(hw);
67 u32 div; 70 u32 mult, mfn, mfd;
71 u64 temp64;
72
73 mult = readl_relaxed(pll->base + PLL_CFG_OFFSET);
74 mult &= BM_PLL_MULT;
75 mult >>= BP_PLL_MULT;
68 76
69 div = readl_relaxed(pll->base + PLL_CFG_OFFSET); 77 mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET);
70 div &= BM_PLL_MULT; 78 mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET);
71 div >>= BP_PLL_MULT; 79 temp64 = parent_rate;
80 temp64 *= mfn;
81 do_div(temp64, mfd);
72 82
73 return parent_rate * div; 83 return (parent_rate * mult) + (u32)temp64;
74} 84}
75 85
76static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, 86static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -78,14 +88,46 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
78{ 88{
79 unsigned long parent_rate = *prate; 89 unsigned long parent_rate = *prate;
80 unsigned long round_rate, i; 90 unsigned long round_rate, i;
91 u32 mfn, mfd = DEFAULT_MFD;
92 bool found = false;
93 u64 temp64;
81 94
82 for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { 95 for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
83 round_rate = parent_rate * pllv4_mult_table[i]; 96 round_rate = parent_rate * pllv4_mult_table[i];
84 if (rate >= round_rate) 97 if (rate >= round_rate) {
85 return round_rate; 98 found = true;
99 break;
100 }
101 }
102
103 if (!found) {
104 pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
105 clk_hw_get_name(hw), rate, parent_rate);
106 return 0;
86 } 107 }
87 108
88 return round_rate; 109 if (parent_rate <= MAX_MFD)
110 mfd = parent_rate;
111
112 temp64 = (u64)(rate - round_rate);
113 temp64 *= mfd;
114 do_div(temp64, parent_rate);
115 mfn = temp64;
116
117 /*
118 * NOTE: The value of numerator must always be configured to be
119 * less than the value of the denominator. If we can't get a proper
120 * pair of mfn/mfd, we simply return the round_rate without using
121 * the frac part.
122 */
123 if (mfn >= mfd)
124 return round_rate;
125
126 temp64 = (u64)parent_rate;
127 temp64 *= mfn;
128 do_div(temp64, mfd);
129
130 return round_rate + (u32)temp64;
89} 131}
90 132
91static bool clk_pllv4_is_valid_mult(unsigned int mult) 133static bool clk_pllv4_is_valid_mult(unsigned int mult)
@@ -105,18 +147,30 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
105 unsigned long parent_rate) 147 unsigned long parent_rate)
106{ 148{
107 struct clk_pllv4 *pll = to_clk_pllv4(hw); 149 struct clk_pllv4 *pll = to_clk_pllv4(hw);
108 u32 val, mult; 150 u32 val, mult, mfn, mfd = DEFAULT_MFD;
151 u64 temp64;
109 152
110 mult = rate / parent_rate; 153 mult = rate / parent_rate;
111 154
112 if (!clk_pllv4_is_valid_mult(mult)) 155 if (!clk_pllv4_is_valid_mult(mult))
113 return -EINVAL; 156 return -EINVAL;
114 157
158 if (parent_rate <= MAX_MFD)
159 mfd = parent_rate;
160
161 temp64 = (u64)(rate - mult * parent_rate);
162 temp64 *= mfd;
163 do_div(temp64, parent_rate);
164 mfn = temp64;
165
115 val = readl_relaxed(pll->base + PLL_CFG_OFFSET); 166 val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
116 val &= ~BM_PLL_MULT; 167 val &= ~BM_PLL_MULT;
117 val |= mult << BP_PLL_MULT; 168 val |= mult << BP_PLL_MULT;
118 writel_relaxed(val, pll->base + PLL_CFG_OFFSET); 169 writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
119 170
171 writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
172 writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
173
120 return 0; 174 return 0;
121} 175}
122 176