aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2012-06-04 08:51:32 -0400
committerSascha Hauer <s.hauer@pengutronix.de>2012-06-04 09:23:47 -0400
commit9ca41bccc39f5ebbbb515dfadb2e0f912168c8bd (patch)
tree75ecfabaee9931b93deb1dd18fe5ebf582083d45 /arch/arm/mach-imx
parent6cc90d6de16532fe68b54ee6967894f7ca83affa (diff)
ARM i.MX pllv2: make round_rate accurate
in round_rate we made the assumption that we can set arbitrary frequencies and thus returned the input rate. This is not correct, for certain frequencies after setting a frequency with set_rate, recalc_rate will return different values. To fix this, introduce set_rate/recalc_rate functions which work on variables instead of registers directly. This way we can call these in round_rate to get the exact rate which we would get if we call set_rate with this value. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/arm/mach-imx')
-rw-r--r--arch/arm/mach-imx/clk-pllv2.c78
1 files changed, 54 insertions, 24 deletions
diff --git a/arch/arm/mach-imx/clk-pllv2.c b/arch/arm/mach-imx/clk-pllv2.c
index 1b0307195a6e..0440379e3628 100644
--- a/arch/arm/mach-imx/clk-pllv2.c
+++ b/arch/arm/mach-imx/clk-pllv2.c
@@ -74,24 +74,15 @@ struct clk_pllv2 {
74 void __iomem *base; 74 void __iomem *base;
75}; 75};
76 76
77static unsigned long clk_pllv2_recalc_rate(struct clk_hw *hw, 77static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate,
78 unsigned long parent_rate) 78 u32 dp_ctl, u32 dp_op, u32 dp_mfd, u32 dp_mfn)
79{ 79{
80 long mfi, mfn, mfd, pdf, ref_clk, mfn_abs; 80 long mfi, mfn, mfd, pdf, ref_clk, mfn_abs;
81 unsigned long dp_op, dp_mfd, dp_mfn, dp_ctl, dbl; 81 unsigned long dbl;
82 void __iomem *pllbase;
83 s64 temp; 82 s64 temp;
84 struct clk_pllv2 *pll = to_clk_pllv2(hw);
85
86 pllbase = pll->base;
87 83
88 dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
89 dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN; 84 dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN;
90 85
91 dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP);
92 dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD);
93 dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN);
94
95 pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK; 86 pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK;
96 mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET; 87 mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET;
97 mfi = (mfi <= 5) ? 5 : mfi; 88 mfi = (mfi <= 5) ? 5 : mfi;
@@ -117,18 +108,30 @@ static unsigned long clk_pllv2_recalc_rate(struct clk_hw *hw,
117 return temp; 108 return temp;
118} 109}
119 110
120static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate, 111static unsigned long clk_pllv2_recalc_rate(struct clk_hw *hw,
121 unsigned long parent_rate) 112 unsigned long parent_rate)
122{ 113{
114 u32 dp_op, dp_mfd, dp_mfn, dp_ctl;
115 void __iomem *pllbase;
123 struct clk_pllv2 *pll = to_clk_pllv2(hw); 116 struct clk_pllv2 *pll = to_clk_pllv2(hw);
117
118 pllbase = pll->base;
119
120 dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
121 dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP);
122 dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD);
123 dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN);
124
125 return __clk_pllv2_recalc_rate(parent_rate, dp_ctl, dp_op, dp_mfd, dp_mfn);
126}
127
128static int __clk_pllv2_set_rate(unsigned long rate, unsigned long parent_rate,
129 u32 *dp_op, u32 *dp_mfd, u32 *dp_mfn)
130{
124 u32 reg; 131 u32 reg;
125 void __iomem *pllbase;
126 long mfi, pdf, mfn, mfd = 999999; 132 long mfi, pdf, mfn, mfd = 999999;
127 s64 temp64; 133 s64 temp64;
128 unsigned long quad_parent_rate; 134 unsigned long quad_parent_rate;
129 unsigned long dp_ctl;
130
131 pllbase = pll->base;
132 135
133 quad_parent_rate = 4 * parent_rate; 136 quad_parent_rate = 4 * parent_rate;
134 pdf = mfi = -1; 137 pdf = mfi = -1;
@@ -138,18 +141,41 @@ static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate,
138 return -EINVAL; 141 return -EINVAL;
139 pdf--; 142 pdf--;
140 143
141 temp64 = rate * (pdf+1) - quad_parent_rate * mfi; 144 temp64 = rate * (pdf + 1) - quad_parent_rate * mfi;
142 do_div(temp64, quad_parent_rate/1000000); 145 do_div(temp64, quad_parent_rate / 1000000);
143 mfn = (long)temp64; 146 mfn = (long)temp64;
144 147
148 reg = mfi << 4 | pdf;
149
150 *dp_op = reg;
151 *dp_mfd = mfd;
152 *dp_mfn = mfn;
153
154 return 0;
155}
156
157static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate,
158 unsigned long parent_rate)
159{
160 struct clk_pllv2 *pll = to_clk_pllv2(hw);
161 void __iomem *pllbase;
162 u32 dp_ctl, dp_op, dp_mfd, dp_mfn;
163 int ret;
164
165 pllbase = pll->base;
166
167
168 ret = __clk_pllv2_set_rate(rate, parent_rate, &dp_op, &dp_mfd, &dp_mfn);
169 if (ret)
170 return ret;
171
145 dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); 172 dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
146 /* use dpdck0_2 */ 173 /* use dpdck0_2 */
147 __raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL); 174 __raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL);
148 175
149 reg = mfi << 4 | pdf; 176 __raw_writel(dp_op, pllbase + MXC_PLL_DP_OP);
150 __raw_writel(reg, pllbase + MXC_PLL_DP_OP); 177 __raw_writel(dp_mfd, pllbase + MXC_PLL_DP_MFD);
151 __raw_writel(mfd, pllbase + MXC_PLL_DP_MFD); 178 __raw_writel(dp_mfn, pllbase + MXC_PLL_DP_MFN);
152 __raw_writel(mfn, pllbase + MXC_PLL_DP_MFN);
153 179
154 return 0; 180 return 0;
155} 181}
@@ -157,7 +183,11 @@ static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate,
157static long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate, 183static long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate,
158 unsigned long *prate) 184 unsigned long *prate)
159{ 185{
160 return rate; 186 u32 dp_op, dp_mfd, dp_mfn;
187
188 __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn);
189 return __clk_pllv2_recalc_rate(*prate, MXC_PLL_DP_CTL_DPDCK0_2_EN,
190 dp_op, dp_mfd, dp_mfn);
161} 191}
162 192
163static int clk_pllv2_prepare(struct clk_hw *hw) 193static int clk_pllv2_prepare(struct clk_hw *hw)