diff options
Diffstat (limited to 'arch/arm/mach-ep93xx/clock.c')
-rw-r--r-- | arch/arm/mach-ep93xx/clock.c | 217 |
1 files changed, 173 insertions, 44 deletions
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c index 6c4c1633ed12..dda19cd76194 100644 --- a/arch/arm/mach-ep93xx/clock.c +++ b/arch/arm/mach-ep93xx/clock.c | |||
@@ -22,48 +22,39 @@ | |||
22 | #include <mach/hardware.h> | 22 | #include <mach/hardware.h> |
23 | 23 | ||
24 | 24 | ||
25 | /* | ||
26 | * The EP93xx has two external crystal oscillators. To generate the | ||
27 | * required high-frequency clocks, the processor uses two phase-locked- | ||
28 | * loops (PLLs) to multiply the incoming external clock signal to much | ||
29 | * higher frequencies that are then divided down by programmable dividers | ||
30 | * to produce the needed clocks. The PLLs operate independently of one | ||
31 | * another. | ||
32 | */ | ||
33 | #define EP93XX_EXT_CLK_RATE 14745600 | ||
34 | #define EP93XX_EXT_RTC_RATE 32768 | ||
35 | |||
36 | |||
37 | struct clk { | 25 | struct clk { |
38 | unsigned long rate; | 26 | unsigned long rate; |
39 | int users; | 27 | int users; |
40 | int sw_locked; | 28 | int sw_locked; |
41 | u32 enable_reg; | 29 | void __iomem *enable_reg; |
42 | u32 enable_mask; | 30 | u32 enable_mask; |
43 | 31 | ||
44 | unsigned long (*get_rate)(struct clk *clk); | 32 | unsigned long (*get_rate)(struct clk *clk); |
33 | int (*set_rate)(struct clk *clk, unsigned long rate); | ||
45 | }; | 34 | }; |
46 | 35 | ||
47 | 36 | ||
48 | static unsigned long get_uart_rate(struct clk *clk); | 37 | static unsigned long get_uart_rate(struct clk *clk); |
49 | 38 | ||
39 | static int set_keytchclk_rate(struct clk *clk, unsigned long rate); | ||
40 | static int set_div_rate(struct clk *clk, unsigned long rate); | ||
50 | 41 | ||
51 | static struct clk clk_uart1 = { | 42 | static struct clk clk_uart1 = { |
52 | .sw_locked = 1, | 43 | .sw_locked = 1, |
53 | .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG, | 44 | .enable_reg = EP93XX_SYSCON_DEVCFG, |
54 | .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U1EN, | 45 | .enable_mask = EP93XX_SYSCON_DEVCFG_U1EN, |
55 | .get_rate = get_uart_rate, | 46 | .get_rate = get_uart_rate, |
56 | }; | 47 | }; |
57 | static struct clk clk_uart2 = { | 48 | static struct clk clk_uart2 = { |
58 | .sw_locked = 1, | 49 | .sw_locked = 1, |
59 | .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG, | 50 | .enable_reg = EP93XX_SYSCON_DEVCFG, |
60 | .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U2EN, | 51 | .enable_mask = EP93XX_SYSCON_DEVCFG_U2EN, |
61 | .get_rate = get_uart_rate, | 52 | .get_rate = get_uart_rate, |
62 | }; | 53 | }; |
63 | static struct clk clk_uart3 = { | 54 | static struct clk clk_uart3 = { |
64 | .sw_locked = 1, | 55 | .sw_locked = 1, |
65 | .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG, | 56 | .enable_reg = EP93XX_SYSCON_DEVCFG, |
66 | .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U3EN, | 57 | .enable_mask = EP93XX_SYSCON_DEVCFG_U3EN, |
67 | .get_rate = get_uart_rate, | 58 | .get_rate = get_uart_rate, |
68 | }; | 59 | }; |
69 | static struct clk clk_pll1; | 60 | static struct clk clk_pll1; |
@@ -75,6 +66,22 @@ static struct clk clk_usb_host = { | |||
75 | .enable_reg = EP93XX_SYSCON_PWRCNT, | 66 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
76 | .enable_mask = EP93XX_SYSCON_PWRCNT_USH_EN, | 67 | .enable_mask = EP93XX_SYSCON_PWRCNT_USH_EN, |
77 | }; | 68 | }; |
69 | static struct clk clk_keypad = { | ||
70 | .sw_locked = 1, | ||
71 | .enable_reg = EP93XX_SYSCON_KEYTCHCLKDIV, | ||
72 | .enable_mask = EP93XX_SYSCON_KEYTCHCLKDIV_KEN, | ||
73 | .set_rate = set_keytchclk_rate, | ||
74 | }; | ||
75 | static struct clk clk_pwm = { | ||
76 | .rate = EP93XX_EXT_CLK_RATE, | ||
77 | }; | ||
78 | |||
79 | static struct clk clk_video = { | ||
80 | .sw_locked = 1, | ||
81 | .enable_reg = EP93XX_SYSCON_VIDCLKDIV, | ||
82 | .enable_mask = EP93XX_SYSCON_CLKDIV_ENABLE, | ||
83 | .set_rate = set_div_rate, | ||
84 | }; | ||
78 | 85 | ||
79 | /* DMA Clocks */ | 86 | /* DMA Clocks */ |
80 | static struct clk clk_m2p0 = { | 87 | static struct clk clk_m2p0 = { |
@@ -130,27 +137,30 @@ static struct clk clk_m2m1 = { | |||
130 | { .dev_id = dev, .con_id = con, .clk = ck } | 137 | { .dev_id = dev, .con_id = con, .clk = ck } |
131 | 138 | ||
132 | static struct clk_lookup clocks[] = { | 139 | static struct clk_lookup clocks[] = { |
133 | INIT_CK("apb:uart1", NULL, &clk_uart1), | 140 | INIT_CK("apb:uart1", NULL, &clk_uart1), |
134 | INIT_CK("apb:uart2", NULL, &clk_uart2), | 141 | INIT_CK("apb:uart2", NULL, &clk_uart2), |
135 | INIT_CK("apb:uart3", NULL, &clk_uart3), | 142 | INIT_CK("apb:uart3", NULL, &clk_uart3), |
136 | INIT_CK(NULL, "pll1", &clk_pll1), | 143 | INIT_CK(NULL, "pll1", &clk_pll1), |
137 | INIT_CK(NULL, "fclk", &clk_f), | 144 | INIT_CK(NULL, "fclk", &clk_f), |
138 | INIT_CK(NULL, "hclk", &clk_h), | 145 | INIT_CK(NULL, "hclk", &clk_h), |
139 | INIT_CK(NULL, "pclk", &clk_p), | 146 | INIT_CK(NULL, "pclk", &clk_p), |
140 | INIT_CK(NULL, "pll2", &clk_pll2), | 147 | INIT_CK(NULL, "pll2", &clk_pll2), |
141 | INIT_CK("ep93xx-ohci", NULL, &clk_usb_host), | 148 | INIT_CK("ep93xx-ohci", NULL, &clk_usb_host), |
142 | INIT_CK(NULL, "m2p0", &clk_m2p0), | 149 | INIT_CK("ep93xx-keypad", NULL, &clk_keypad), |
143 | INIT_CK(NULL, "m2p1", &clk_m2p1), | 150 | INIT_CK("ep93xx-fb", NULL, &clk_video), |
144 | INIT_CK(NULL, "m2p2", &clk_m2p2), | 151 | INIT_CK(NULL, "pwm_clk", &clk_pwm), |
145 | INIT_CK(NULL, "m2p3", &clk_m2p3), | 152 | INIT_CK(NULL, "m2p0", &clk_m2p0), |
146 | INIT_CK(NULL, "m2p4", &clk_m2p4), | 153 | INIT_CK(NULL, "m2p1", &clk_m2p1), |
147 | INIT_CK(NULL, "m2p5", &clk_m2p5), | 154 | INIT_CK(NULL, "m2p2", &clk_m2p2), |
148 | INIT_CK(NULL, "m2p6", &clk_m2p6), | 155 | INIT_CK(NULL, "m2p3", &clk_m2p3), |
149 | INIT_CK(NULL, "m2p7", &clk_m2p7), | 156 | INIT_CK(NULL, "m2p4", &clk_m2p4), |
150 | INIT_CK(NULL, "m2p8", &clk_m2p8), | 157 | INIT_CK(NULL, "m2p5", &clk_m2p5), |
151 | INIT_CK(NULL, "m2p9", &clk_m2p9), | 158 | INIT_CK(NULL, "m2p6", &clk_m2p6), |
152 | INIT_CK(NULL, "m2m0", &clk_m2m0), | 159 | INIT_CK(NULL, "m2p7", &clk_m2p7), |
153 | INIT_CK(NULL, "m2m1", &clk_m2m1), | 160 | INIT_CK(NULL, "m2p8", &clk_m2p8), |
161 | INIT_CK(NULL, "m2p9", &clk_m2p9), | ||
162 | INIT_CK(NULL, "m2m0", &clk_m2m0), | ||
163 | INIT_CK(NULL, "m2m1", &clk_m2m1), | ||
154 | }; | 164 | }; |
155 | 165 | ||
156 | 166 | ||
@@ -160,9 +170,11 @@ int clk_enable(struct clk *clk) | |||
160 | u32 value; | 170 | u32 value; |
161 | 171 | ||
162 | value = __raw_readl(clk->enable_reg); | 172 | value = __raw_readl(clk->enable_reg); |
173 | value |= clk->enable_mask; | ||
163 | if (clk->sw_locked) | 174 | if (clk->sw_locked) |
164 | __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); | 175 | ep93xx_syscon_swlocked_write(value, clk->enable_reg); |
165 | __raw_writel(value | clk->enable_mask, clk->enable_reg); | 176 | else |
177 | __raw_writel(value, clk->enable_reg); | ||
166 | } | 178 | } |
167 | 179 | ||
168 | return 0; | 180 | return 0; |
@@ -175,9 +187,11 @@ void clk_disable(struct clk *clk) | |||
175 | u32 value; | 187 | u32 value; |
176 | 188 | ||
177 | value = __raw_readl(clk->enable_reg); | 189 | value = __raw_readl(clk->enable_reg); |
190 | value &= ~clk->enable_mask; | ||
178 | if (clk->sw_locked) | 191 | if (clk->sw_locked) |
179 | __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); | 192 | ep93xx_syscon_swlocked_write(value, clk->enable_reg); |
180 | __raw_writel(value & ~clk->enable_mask, clk->enable_reg); | 193 | else |
194 | __raw_writel(value, clk->enable_reg); | ||
181 | } | 195 | } |
182 | } | 196 | } |
183 | EXPORT_SYMBOL(clk_disable); | 197 | EXPORT_SYMBOL(clk_disable); |
@@ -202,6 +216,121 @@ unsigned long clk_get_rate(struct clk *clk) | |||
202 | } | 216 | } |
203 | EXPORT_SYMBOL(clk_get_rate); | 217 | EXPORT_SYMBOL(clk_get_rate); |
204 | 218 | ||
219 | static int set_keytchclk_rate(struct clk *clk, unsigned long rate) | ||
220 | { | ||
221 | u32 val; | ||
222 | u32 div_bit; | ||
223 | |||
224 | val = __raw_readl(clk->enable_reg); | ||
225 | |||
226 | /* | ||
227 | * The Key Matrix and ADC clocks are configured using the same | ||
228 | * System Controller register. The clock used will be either | ||
229 | * 1/4 or 1/16 the external clock rate depending on the | ||
230 | * EP93XX_SYSCON_KEYTCHCLKDIV_KDIV/EP93XX_SYSCON_KEYTCHCLKDIV_ADIV | ||
231 | * bit being set or cleared. | ||
232 | */ | ||
233 | div_bit = clk->enable_mask >> 15; | ||
234 | |||
235 | if (rate == EP93XX_KEYTCHCLK_DIV4) | ||
236 | val |= div_bit; | ||
237 | else if (rate == EP93XX_KEYTCHCLK_DIV16) | ||
238 | val &= ~div_bit; | ||
239 | else | ||
240 | return -EINVAL; | ||
241 | |||
242 | ep93xx_syscon_swlocked_write(val, clk->enable_reg); | ||
243 | clk->rate = rate; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static unsigned long calc_clk_div(unsigned long rate, int *psel, int *esel, | ||
248 | int *pdiv, int *div) | ||
249 | { | ||
250 | unsigned long max_rate, best_rate = 0, | ||
251 | actual_rate = 0, mclk_rate = 0, rate_err = -1; | ||
252 | int i, found = 0, __div = 0, __pdiv = 0; | ||
253 | |||
254 | /* Don't exceed the maximum rate */ | ||
255 | max_rate = max(max(clk_pll1.rate / 4, clk_pll2.rate / 4), | ||
256 | (unsigned long)EP93XX_EXT_CLK_RATE / 4); | ||
257 | rate = min(rate, max_rate); | ||
258 | |||
259 | /* | ||
260 | * Try the two pll's and the external clock | ||
261 | * Because the valid predividers are 2, 2.5 and 3, we multiply | ||
262 | * all the clocks by 2 to avoid floating point math. | ||
263 | * | ||
264 | * This is based on the algorithm in the ep93xx raster guide: | ||
265 | * http://be-a-maverick.com/en/pubs/appNote/AN269REV1.pdf | ||
266 | * | ||
267 | */ | ||
268 | for (i = 0; i < 3; i++) { | ||
269 | if (i == 0) | ||
270 | mclk_rate = EP93XX_EXT_CLK_RATE * 2; | ||
271 | else if (i == 1) | ||
272 | mclk_rate = clk_pll1.rate * 2; | ||
273 | else if (i == 2) | ||
274 | mclk_rate = clk_pll2.rate * 2; | ||
275 | |||
276 | /* Try each predivider value */ | ||
277 | for (__pdiv = 4; __pdiv <= 6; __pdiv++) { | ||
278 | __div = mclk_rate / (rate * __pdiv); | ||
279 | if (__div < 2 || __div > 127) | ||
280 | continue; | ||
281 | |||
282 | actual_rate = mclk_rate / (__pdiv * __div); | ||
283 | |||
284 | if (!found || abs(actual_rate - rate) < rate_err) { | ||
285 | *pdiv = __pdiv - 3; | ||
286 | *div = __div; | ||
287 | *psel = (i == 2); | ||
288 | *esel = (i != 0); | ||
289 | best_rate = actual_rate; | ||
290 | rate_err = abs(actual_rate - rate); | ||
291 | found = 1; | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | |||
296 | if (!found) | ||
297 | return 0; | ||
298 | |||
299 | return best_rate; | ||
300 | } | ||
301 | |||
302 | static int set_div_rate(struct clk *clk, unsigned long rate) | ||
303 | { | ||
304 | unsigned long actual_rate; | ||
305 | int psel = 0, esel = 0, pdiv = 0, div = 0; | ||
306 | u32 val; | ||
307 | |||
308 | actual_rate = calc_clk_div(rate, &psel, &esel, &pdiv, &div); | ||
309 | if (actual_rate == 0) | ||
310 | return -EINVAL; | ||
311 | clk->rate = actual_rate; | ||
312 | |||
313 | /* Clear the esel, psel, pdiv and div bits */ | ||
314 | val = __raw_readl(clk->enable_reg); | ||
315 | val &= ~0x7fff; | ||
316 | |||
317 | /* Set the new esel, psel, pdiv and div bits for the new clock rate */ | ||
318 | val |= (esel ? EP93XX_SYSCON_CLKDIV_ESEL : 0) | | ||
319 | (psel ? EP93XX_SYSCON_CLKDIV_PSEL : 0) | | ||
320 | (pdiv << EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | div; | ||
321 | ep93xx_syscon_swlocked_write(val, clk->enable_reg); | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | int clk_set_rate(struct clk *clk, unsigned long rate) | ||
326 | { | ||
327 | if (clk->set_rate) | ||
328 | return clk->set_rate(clk, rate); | ||
329 | |||
330 | return -EINVAL; | ||
331 | } | ||
332 | EXPORT_SYMBOL(clk_set_rate); | ||
333 | |||
205 | 334 | ||
206 | static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 }; | 335 | static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 }; |
207 | static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 }; | 336 | static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 }; |