diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2012-06-04 08:51:32 -0400 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2012-06-04 09:23:47 -0400 |
commit | 9ca41bccc39f5ebbbb515dfadb2e0f912168c8bd (patch) | |
tree | 75ecfabaee9931b93deb1dd18fe5ebf582083d45 /arch/arm/mach-imx | |
parent | 6cc90d6de16532fe68b54ee6967894f7ca83affa (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.c | 78 |
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 | ||
77 | static unsigned long clk_pllv2_recalc_rate(struct clk_hw *hw, | 77 | static 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 | ||
120 | static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate, | 111 | static 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 | |||
128 | static 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 | |||
157 | static 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, | |||
157 | static long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate, | 183 | static 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 | ||
163 | static int clk_pllv2_prepare(struct clk_hw *hw) | 193 | static int clk_pllv2_prepare(struct clk_hw *hw) |