aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-ep93xx/clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-ep93xx/clock.c')
-rw-r--r--arch/arm/mach-ep93xx/clock.c217
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
37struct clk { 25struct 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
48static unsigned long get_uart_rate(struct clk *clk); 37static unsigned long get_uart_rate(struct clk *clk);
49 38
39static int set_keytchclk_rate(struct clk *clk, unsigned long rate);
40static int set_div_rate(struct clk *clk, unsigned long rate);
50 41
51static struct clk clk_uart1 = { 42static 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};
57static struct clk clk_uart2 = { 48static 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};
63static struct clk clk_uart3 = { 54static 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};
69static struct clk clk_pll1; 60static 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};
69static 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};
75static struct clk clk_pwm = {
76 .rate = EP93XX_EXT_CLK_RATE,
77};
78
79static 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 */
80static struct clk clk_m2p0 = { 87static 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
132static struct clk_lookup clocks[] = { 139static 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}
183EXPORT_SYMBOL(clk_disable); 197EXPORT_SYMBOL(clk_disable);
@@ -202,6 +216,121 @@ unsigned long clk_get_rate(struct clk *clk)
202} 216}
203EXPORT_SYMBOL(clk_get_rate); 217EXPORT_SYMBOL(clk_get_rate);
204 218
219static 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
247static 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
302static 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
325int 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}
332EXPORT_SYMBOL(clk_set_rate);
333
205 334
206static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 }; 335static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
207static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 }; 336static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };