diff options
-rw-r--r-- | arch/arm/mach-omap2/cclock44xx_data.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.h | 10 | ||||
-rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 46 | ||||
-rw-r--r-- | arch/arm/mach-omap2/dpll44xx.c | 64 |
4 files changed, 99 insertions, 23 deletions
diff --git a/arch/arm/mach-omap2/cclock44xx_data.c b/arch/arm/mach-omap2/cclock44xx_data.c index 7c1ffe61ec1b..44ef54fa118b 100644 --- a/arch/arm/mach-omap2/cclock44xx_data.c +++ b/arch/arm/mach-omap2/cclock44xx_data.c | |||
@@ -124,6 +124,8 @@ static struct dpll_data dpll_abe_dd = { | |||
124 | .enable_mask = OMAP4430_DPLL_EN_MASK, | 124 | .enable_mask = OMAP4430_DPLL_EN_MASK, |
125 | .autoidle_mask = OMAP4430_AUTO_DPLL_MODE_MASK, | 125 | .autoidle_mask = OMAP4430_AUTO_DPLL_MODE_MASK, |
126 | .idlest_mask = OMAP4430_ST_DPLL_CLK_MASK, | 126 | .idlest_mask = OMAP4430_ST_DPLL_CLK_MASK, |
127 | .m4xen_mask = OMAP4430_DPLL_REGM4XEN_MASK, | ||
128 | .lpmode_mask = OMAP4430_DPLL_LPMODE_EN_MASK, | ||
127 | .max_multiplier = 2047, | 129 | .max_multiplier = 2047, |
128 | .max_divider = 128, | 130 | .max_divider = 128, |
129 | .min_divider = 1, | 131 | .min_divider = 1, |
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index 9917f793c3b6..b40204837bd7 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h | |||
@@ -195,6 +195,10 @@ struct clksel { | |||
195 | * @enable_mask: mask of the DPLL mode bitfield in @control_reg | 195 | * @enable_mask: mask of the DPLL mode bitfield in @control_reg |
196 | * @last_rounded_rate: cache of the last rate result of omap2_dpll_round_rate() | 196 | * @last_rounded_rate: cache of the last rate result of omap2_dpll_round_rate() |
197 | * @last_rounded_m: cache of the last M result of omap2_dpll_round_rate() | 197 | * @last_rounded_m: cache of the last M result of omap2_dpll_round_rate() |
198 | * @last_rounded_m4xen: cache of the last M4X result of | ||
199 | * omap4_dpll_regm4xen_round_rate() | ||
200 | * @last_rounded_lpmode: cache of the last lpmode result of | ||
201 | * omap4_dpll_lpmode_recalc() | ||
198 | * @max_multiplier: maximum valid non-bypass multiplier value (actual) | 202 | * @max_multiplier: maximum valid non-bypass multiplier value (actual) |
199 | * @last_rounded_n: cache of the last N result of omap2_dpll_round_rate() | 203 | * @last_rounded_n: cache of the last N result of omap2_dpll_round_rate() |
200 | * @min_divider: minimum valid non-bypass divider value (actual) | 204 | * @min_divider: minimum valid non-bypass divider value (actual) |
@@ -205,6 +209,8 @@ struct clksel { | |||
205 | * @autoidle_mask: mask of the DPLL autoidle mode bitfield in @autoidle_reg | 209 | * @autoidle_mask: mask of the DPLL autoidle mode bitfield in @autoidle_reg |
206 | * @freqsel_mask: mask of the DPLL jitter correction bitfield in @control_reg | 210 | * @freqsel_mask: mask of the DPLL jitter correction bitfield in @control_reg |
207 | * @idlest_mask: mask of the DPLL idle status bitfield in @idlest_reg | 211 | * @idlest_mask: mask of the DPLL idle status bitfield in @idlest_reg |
212 | * @lpmode_mask: mask of the DPLL low-power mode bitfield in @control_reg | ||
213 | * @m4xen_mask: mask of the DPLL M4X multiplier bitfield in @control_reg | ||
208 | * @auto_recal_bit: bitshift of the driftguard enable bit in @control_reg | 214 | * @auto_recal_bit: bitshift of the driftguard enable bit in @control_reg |
209 | * @recal_en_bit: bitshift of the PRM_IRQENABLE_* bit for recalibration IRQs | 215 | * @recal_en_bit: bitshift of the PRM_IRQENABLE_* bit for recalibration IRQs |
210 | * @recal_st_bit: bitshift of the PRM_IRQSTATUS_* bit for recalibration IRQs | 216 | * @recal_st_bit: bitshift of the PRM_IRQSTATUS_* bit for recalibration IRQs |
@@ -233,6 +239,8 @@ struct dpll_data { | |||
233 | u32 enable_mask; | 239 | u32 enable_mask; |
234 | unsigned long last_rounded_rate; | 240 | unsigned long last_rounded_rate; |
235 | u16 last_rounded_m; | 241 | u16 last_rounded_m; |
242 | u8 last_rounded_m4xen; | ||
243 | u8 last_rounded_lpmode; | ||
236 | u16 max_multiplier; | 244 | u16 max_multiplier; |
237 | u8 last_rounded_n; | 245 | u8 last_rounded_n; |
238 | u8 min_divider; | 246 | u8 min_divider; |
@@ -245,6 +253,8 @@ struct dpll_data { | |||
245 | u32 idlest_mask; | 253 | u32 idlest_mask; |
246 | u32 dco_mask; | 254 | u32 dco_mask; |
247 | u32 sddiv_mask; | 255 | u32 sddiv_mask; |
256 | u32 lpmode_mask; | ||
257 | u32 m4xen_mask; | ||
248 | u8 auto_recal_bit; | 258 | u8 auto_recal_bit; |
249 | u8 recal_en_bit; | 259 | u8 recal_en_bit; |
250 | u8 recal_st_bit; | 260 | u8 recal_st_bit; |
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index fafb28c0dcbc..2bb18838cba9 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c | |||
@@ -291,16 +291,13 @@ static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n) | |||
291 | 291 | ||
292 | /* | 292 | /* |
293 | * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly | 293 | * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly |
294 | * @clk: struct clk * of DPLL to set | 294 | * @clk: struct clk * of DPLL to set |
295 | * @m: DPLL multiplier to set | 295 | * @freqsel: FREQSEL value to set |
296 | * @n: DPLL divider to set | ||
297 | * @freqsel: FREQSEL value to set | ||
298 | * | 296 | * |
299 | * Program the DPLL with the supplied M, N values, and wait for the DPLL to | 297 | * Program the DPLL with the last M, N values calculated, and wait for |
300 | * lock.. Returns -EINVAL upon error, or 0 upon success. | 298 | * the DPLL to lock. Returns -EINVAL upon error, or 0 upon success. |
301 | */ | 299 | */ |
302 | static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 m, u8 n, | 300 | static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel) |
303 | u16 freqsel) | ||
304 | { | 301 | { |
305 | struct dpll_data *dd = clk->dpll_data; | 302 | struct dpll_data *dd = clk->dpll_data; |
306 | u8 dco, sd_div; | 303 | u8 dco, sd_div; |
@@ -323,23 +320,45 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 m, u8 n, | |||
323 | /* Set DPLL multiplier, divider */ | 320 | /* Set DPLL multiplier, divider */ |
324 | v = __raw_readl(dd->mult_div1_reg); | 321 | v = __raw_readl(dd->mult_div1_reg); |
325 | v &= ~(dd->mult_mask | dd->div1_mask); | 322 | v &= ~(dd->mult_mask | dd->div1_mask); |
326 | v |= m << __ffs(dd->mult_mask); | 323 | v |= dd->last_rounded_m << __ffs(dd->mult_mask); |
327 | v |= (n - 1) << __ffs(dd->div1_mask); | 324 | v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask); |
328 | 325 | ||
329 | /* Configure dco and sd_div for dplls that have these fields */ | 326 | /* Configure dco and sd_div for dplls that have these fields */ |
330 | if (dd->dco_mask) { | 327 | if (dd->dco_mask) { |
331 | _lookup_dco(clk, &dco, m, n); | 328 | _lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n); |
332 | v &= ~(dd->dco_mask); | 329 | v &= ~(dd->dco_mask); |
333 | v |= dco << __ffs(dd->dco_mask); | 330 | v |= dco << __ffs(dd->dco_mask); |
334 | } | 331 | } |
335 | if (dd->sddiv_mask) { | 332 | if (dd->sddiv_mask) { |
336 | _lookup_sddiv(clk, &sd_div, m, n); | 333 | _lookup_sddiv(clk, &sd_div, dd->last_rounded_m, |
334 | dd->last_rounded_n); | ||
337 | v &= ~(dd->sddiv_mask); | 335 | v &= ~(dd->sddiv_mask); |
338 | v |= sd_div << __ffs(dd->sddiv_mask); | 336 | v |= sd_div << __ffs(dd->sddiv_mask); |
339 | } | 337 | } |
340 | 338 | ||
341 | __raw_writel(v, dd->mult_div1_reg); | 339 | __raw_writel(v, dd->mult_div1_reg); |
342 | 340 | ||
341 | /* Set 4X multiplier and low-power mode */ | ||
342 | if (dd->m4xen_mask || dd->lpmode_mask) { | ||
343 | v = __raw_readl(dd->control_reg); | ||
344 | |||
345 | if (dd->m4xen_mask) { | ||
346 | if (dd->last_rounded_m4xen) | ||
347 | v |= dd->m4xen_mask; | ||
348 | else | ||
349 | v &= ~dd->m4xen_mask; | ||
350 | } | ||
351 | |||
352 | if (dd->lpmode_mask) { | ||
353 | if (dd->last_rounded_lpmode) | ||
354 | v |= dd->lpmode_mask; | ||
355 | else | ||
356 | v &= ~dd->lpmode_mask; | ||
357 | } | ||
358 | |||
359 | __raw_writel(v, dd->control_reg); | ||
360 | } | ||
361 | |||
343 | /* We let the clock framework set the other output dividers later */ | 362 | /* We let the clock framework set the other output dividers later */ |
344 | 363 | ||
345 | /* REVISIT: Set ramp-up delay? */ | 364 | /* REVISIT: Set ramp-up delay? */ |
@@ -492,8 +511,7 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, | |||
492 | pr_debug("%s: %s: set rate: locking rate to %lu.\n", | 511 | pr_debug("%s: %s: set rate: locking rate to %lu.\n", |
493 | __func__, __clk_get_name(hw->clk), rate); | 512 | __func__, __clk_get_name(hw->clk), rate); |
494 | 513 | ||
495 | ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, | 514 | ret = omap3_noncore_dpll_program(clk, freqsel); |
496 | dd->last_rounded_n, freqsel); | ||
497 | if (!ret) | 515 | if (!ret) |
498 | new_parent = dd->clk_ref; | 516 | new_parent = dd->clk_ref; |
499 | } | 517 | } |
diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c index d3326c474fdc..d28b0f726715 100644 --- a/arch/arm/mach-omap2/dpll44xx.c +++ b/arch/arm/mach-omap2/dpll44xx.c | |||
@@ -20,6 +20,15 @@ | |||
20 | #include "clock44xx.h" | 20 | #include "clock44xx.h" |
21 | #include "cm-regbits-44xx.h" | 21 | #include "cm-regbits-44xx.h" |
22 | 22 | ||
23 | /* | ||
24 | * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that | ||
25 | * can supported when using the DPLL low-power mode. Frequencies are | ||
26 | * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control, | ||
27 | * Status, and Low-Power Operation Mode". | ||
28 | */ | ||
29 | #define OMAP4_DPLL_LP_FINT_MAX 1000000 | ||
30 | #define OMAP4_DPLL_LP_FOUT_MAX 100000000 | ||
31 | |||
23 | /* Supported only on OMAP4 */ | 32 | /* Supported only on OMAP4 */ |
24 | int omap4_dpllmx_gatectrl_read(struct clk_hw_omap *clk) | 33 | int omap4_dpllmx_gatectrl_read(struct clk_hw_omap *clk) |
25 | { | 34 | { |
@@ -82,6 +91,31 @@ const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = { | |||
82 | }; | 91 | }; |
83 | 92 | ||
84 | /** | 93 | /** |
94 | * omap4_dpll_lpmode_recalc - compute DPLL low-power setting | ||
95 | * @dd: pointer to the dpll data structure | ||
96 | * | ||
97 | * Calculates if low-power mode can be enabled based upon the last | ||
98 | * multiplier and divider values calculated. If low-power mode can be | ||
99 | * enabled, then the bit to enable low-power mode is stored in the | ||
100 | * last_rounded_lpmode variable. This implementation is based upon the | ||
101 | * criteria for enabling low-power mode as described in the OMAP4430/60 | ||
102 | * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power | ||
103 | * Operation Mode". | ||
104 | */ | ||
105 | static void omap4_dpll_lpmode_recalc(struct dpll_data *dd) | ||
106 | { | ||
107 | long fint, fout; | ||
108 | |||
109 | fint = __clk_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1); | ||
110 | fout = fint * dd->last_rounded_m; | ||
111 | |||
112 | if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX)) | ||
113 | dd->last_rounded_lpmode = 1; | ||
114 | else | ||
115 | dd->last_rounded_lpmode = 0; | ||
116 | } | ||
117 | |||
118 | /** | ||
85 | * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit | 119 | * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit |
86 | * @clk: struct clk * of the DPLL to compute the rate for | 120 | * @clk: struct clk * of the DPLL to compute the rate for |
87 | * | 121 | * |
@@ -130,7 +164,6 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, | |||
130 | unsigned long *parent_rate) | 164 | unsigned long *parent_rate) |
131 | { | 165 | { |
132 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | 166 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); |
133 | u32 v; | ||
134 | struct dpll_data *dd; | 167 | struct dpll_data *dd; |
135 | long r; | 168 | long r; |
136 | 169 | ||
@@ -139,18 +172,31 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, | |||
139 | 172 | ||
140 | dd = clk->dpll_data; | 173 | dd = clk->dpll_data; |
141 | 174 | ||
142 | /* regm4xen adds a multiplier of 4 to DPLL calculations */ | 175 | dd->last_rounded_m4xen = 0; |
143 | v = __raw_readl(dd->control_reg) & OMAP4430_DPLL_REGM4XEN_MASK; | ||
144 | |||
145 | if (v) | ||
146 | target_rate = target_rate / OMAP4430_REGM4XEN_MULT; | ||
147 | 176 | ||
177 | /* | ||
178 | * First try to compute the DPLL configuration for | ||
179 | * target rate without using the 4X multiplier. | ||
180 | */ | ||
148 | r = omap2_dpll_round_rate(hw, target_rate, NULL); | 181 | r = omap2_dpll_round_rate(hw, target_rate, NULL); |
182 | if (r != ~0) | ||
183 | goto out; | ||
184 | |||
185 | /* | ||
186 | * If we did not find a valid DPLL configuration, try again, but | ||
187 | * this time see if using the 4X multiplier can help. Enabling the | ||
188 | * 4X multiplier is equivalent to dividing the target rate by 4. | ||
189 | */ | ||
190 | r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT, | ||
191 | NULL); | ||
149 | if (r == ~0) | 192 | if (r == ~0) |
150 | return r; | 193 | return r; |
151 | 194 | ||
152 | if (v) | 195 | dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; |
153 | clk->dpll_data->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; | 196 | dd->last_rounded_m4xen = 1; |
197 | |||
198 | out: | ||
199 | omap4_dpll_lpmode_recalc(dd); | ||
154 | 200 | ||
155 | return clk->dpll_data->last_rounded_rate; | 201 | return dd->last_rounded_rate; |
156 | } | 202 | } |