Commit | Line | Data |
---|---|---|
1d81eedb LB |
1 | /* |
2 | * arch/arm/mach-ep93xx/clock.c | |
3 | * Clock control for Cirrus EP93xx chips. | |
4 | * | |
5 | * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or (at | |
10 | * your option) any later version. | |
11 | */ | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/clk.h> | |
15 | #include <linux/err.h> | |
51dd249e | 16 | #include <linux/module.h> |
1d81eedb | 17 | #include <linux/string.h> |
fced80c7 | 18 | #include <linux/io.h> |
ae696fd5 RK |
19 | |
20 | #include <asm/clkdev.h> | |
1d81eedb | 21 | #include <asm/div64.h> |
a09e64fb | 22 | #include <mach/hardware.h> |
1d81eedb | 23 | |
ff05c033 HS |
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 | ||
1d81eedb | 37 | struct clk { |
1d81eedb LB |
38 | unsigned long rate; |
39 | int users; | |
ff05c033 | 40 | int sw_locked; |
1d81eedb LB |
41 | u32 enable_reg; |
42 | u32 enable_mask; | |
ff05c033 HS |
43 | |
44 | unsigned long (*get_rate)(struct clk *clk); | |
1d81eedb LB |
45 | }; |
46 | ||
ff05c033 HS |
47 | |
48 | static unsigned long get_uart_rate(struct clk *clk); | |
49 | ||
50 | ||
51 | static struct clk clk_uart1 = { | |
52 | .sw_locked = 1, | |
53 | .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG, | |
54 | .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U1EN, | |
55 | .get_rate = get_uart_rate, | |
56 | }; | |
57 | static struct clk clk_uart2 = { | |
58 | .sw_locked = 1, | |
59 | .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG, | |
60 | .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U2EN, | |
61 | .get_rate = get_uart_rate, | |
62 | }; | |
63 | static struct clk clk_uart3 = { | |
64 | .sw_locked = 1, | |
65 | .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG, | |
66 | .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U3EN, | |
67 | .get_rate = get_uart_rate, | |
ed519ded | 68 | }; |
ae696fd5 RK |
69 | static struct clk clk_pll1; |
70 | static struct clk clk_f; | |
71 | static struct clk clk_h; | |
72 | static struct clk clk_p; | |
73 | static struct clk clk_pll2; | |
1d81eedb | 74 | static struct clk clk_usb_host = { |
40702432 HS |
75 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
76 | .enable_mask = EP93XX_SYSCON_PWRCNT_USH_EN, | |
1d81eedb LB |
77 | }; |
78 | ||
1c8daabe RM |
79 | /* DMA Clocks */ |
80 | static struct clk clk_m2p0 = { | |
40702432 HS |
81 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
82 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P0, | |
1c8daabe RM |
83 | }; |
84 | static struct clk clk_m2p1 = { | |
40702432 HS |
85 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
86 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P1, | |
1c8daabe RM |
87 | }; |
88 | static struct clk clk_m2p2 = { | |
40702432 HS |
89 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
90 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P2, | |
1c8daabe RM |
91 | }; |
92 | static struct clk clk_m2p3 = { | |
40702432 HS |
93 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
94 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P3, | |
1c8daabe RM |
95 | }; |
96 | static struct clk clk_m2p4 = { | |
40702432 HS |
97 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
98 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P4, | |
1c8daabe RM |
99 | }; |
100 | static struct clk clk_m2p5 = { | |
40702432 HS |
101 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
102 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P5, | |
1c8daabe RM |
103 | }; |
104 | static struct clk clk_m2p6 = { | |
40702432 HS |
105 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
106 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P6, | |
1c8daabe RM |
107 | }; |
108 | static struct clk clk_m2p7 = { | |
40702432 HS |
109 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
110 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P7, | |
1c8daabe RM |
111 | }; |
112 | static struct clk clk_m2p8 = { | |
40702432 HS |
113 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
114 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P8, | |
1c8daabe RM |
115 | }; |
116 | static struct clk clk_m2p9 = { | |
40702432 HS |
117 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
118 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2P9, | |
1c8daabe RM |
119 | }; |
120 | static struct clk clk_m2m0 = { | |
40702432 HS |
121 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
122 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2M0, | |
1c8daabe RM |
123 | }; |
124 | static struct clk clk_m2m1 = { | |
40702432 HS |
125 | .enable_reg = EP93XX_SYSCON_PWRCNT, |
126 | .enable_mask = EP93XX_SYSCON_PWRCNT_DMA_M2M1, | |
1c8daabe RM |
127 | }; |
128 | ||
ae696fd5 RK |
129 | #define INIT_CK(dev,con,ck) \ |
130 | { .dev_id = dev, .con_id = con, .clk = ck } | |
131 | ||
132 | static struct clk_lookup clocks[] = { | |
ff05c033 HS |
133 | INIT_CK("apb:uart1", NULL, &clk_uart1), |
134 | INIT_CK("apb:uart2", NULL, &clk_uart2), | |
135 | INIT_CK("apb:uart3", NULL, &clk_uart3), | |
ae696fd5 RK |
136 | INIT_CK(NULL, "pll1", &clk_pll1), |
137 | INIT_CK(NULL, "fclk", &clk_f), | |
138 | INIT_CK(NULL, "hclk", &clk_h), | |
139 | INIT_CK(NULL, "pclk", &clk_p), | |
140 | INIT_CK(NULL, "pll2", &clk_pll2), | |
e3a6d019 | 141 | INIT_CK("ep93xx-ohci", NULL, &clk_usb_host), |
1c8daabe RM |
142 | INIT_CK(NULL, "m2p0", &clk_m2p0), |
143 | INIT_CK(NULL, "m2p1", &clk_m2p1), | |
144 | INIT_CK(NULL, "m2p2", &clk_m2p2), | |
145 | INIT_CK(NULL, "m2p3", &clk_m2p3), | |
146 | INIT_CK(NULL, "m2p4", &clk_m2p4), | |
147 | INIT_CK(NULL, "m2p5", &clk_m2p5), | |
148 | INIT_CK(NULL, "m2p6", &clk_m2p6), | |
149 | INIT_CK(NULL, "m2p7", &clk_m2p7), | |
150 | INIT_CK(NULL, "m2p8", &clk_m2p8), | |
151 | INIT_CK(NULL, "m2p9", &clk_m2p9), | |
152 | INIT_CK(NULL, "m2m0", &clk_m2m0), | |
153 | INIT_CK(NULL, "m2m1", &clk_m2m1), | |
1d81eedb LB |
154 | }; |
155 | ||
1d81eedb LB |
156 | |
157 | int clk_enable(struct clk *clk) | |
158 | { | |
159 | if (!clk->users++ && clk->enable_reg) { | |
160 | u32 value; | |
161 | ||
162 | value = __raw_readl(clk->enable_reg); | |
ff05c033 HS |
163 | if (clk->sw_locked) |
164 | __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); | |
1d81eedb LB |
165 | __raw_writel(value | clk->enable_mask, clk->enable_reg); |
166 | } | |
167 | ||
168 | return 0; | |
169 | } | |
0c5d5b70 | 170 | EXPORT_SYMBOL(clk_enable); |
1d81eedb LB |
171 | |
172 | void clk_disable(struct clk *clk) | |
173 | { | |
174 | if (!--clk->users && clk->enable_reg) { | |
175 | u32 value; | |
176 | ||
177 | value = __raw_readl(clk->enable_reg); | |
ff05c033 HS |
178 | if (clk->sw_locked) |
179 | __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK); | |
1d81eedb LB |
180 | __raw_writel(value & ~clk->enable_mask, clk->enable_reg); |
181 | } | |
182 | } | |
0c5d5b70 | 183 | EXPORT_SYMBOL(clk_disable); |
1d81eedb | 184 | |
ff05c033 HS |
185 | static unsigned long get_uart_rate(struct clk *clk) |
186 | { | |
187 | u32 value; | |
188 | ||
ca8cbc83 MK |
189 | value = __raw_readl(EP93XX_SYSCON_PWRCNT); |
190 | if (value & EP93XX_SYSCON_PWRCNT_UARTBAUD) | |
ff05c033 HS |
191 | return EP93XX_EXT_CLK_RATE; |
192 | else | |
193 | return EP93XX_EXT_CLK_RATE / 2; | |
194 | } | |
195 | ||
1d81eedb LB |
196 | unsigned long clk_get_rate(struct clk *clk) |
197 | { | |
ff05c033 HS |
198 | if (clk->get_rate) |
199 | return clk->get_rate(clk); | |
200 | ||
1d81eedb LB |
201 | return clk->rate; |
202 | } | |
0c5d5b70 | 203 | EXPORT_SYMBOL(clk_get_rate); |
1d81eedb | 204 | |
1d81eedb LB |
205 | |
206 | 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 }; | |
208 | static char pclk_divisors[] = { 1, 2, 4, 8 }; | |
209 | ||
210 | /* | |
211 | * PLL rate = 14.7456 MHz * (X1FBD + 1) * (X2FBD + 1) / (X2IPD + 1) / 2^PS | |
212 | */ | |
213 | static unsigned long calc_pll_rate(u32 config_word) | |
214 | { | |
215 | unsigned long long rate; | |
216 | int i; | |
217 | ||
ff05c033 | 218 | rate = EP93XX_EXT_CLK_RATE; |
1d81eedb LB |
219 | rate *= ((config_word >> 11) & 0x1f) + 1; /* X1FBD */ |
220 | rate *= ((config_word >> 5) & 0x3f) + 1; /* X2FBD */ | |
221 | do_div(rate, (config_word & 0x1f) + 1); /* X2IPD */ | |
222 | for (i = 0; i < ((config_word >> 16) & 3); i++) /* PS */ | |
223 | rate >>= 1; | |
224 | ||
225 | return (unsigned long)rate; | |
226 | } | |
227 | ||
1c8daabe RM |
228 | static void __init ep93xx_dma_clock_init(void) |
229 | { | |
230 | clk_m2p0.rate = clk_h.rate; | |
231 | clk_m2p1.rate = clk_h.rate; | |
232 | clk_m2p2.rate = clk_h.rate; | |
233 | clk_m2p3.rate = clk_h.rate; | |
234 | clk_m2p4.rate = clk_h.rate; | |
235 | clk_m2p5.rate = clk_h.rate; | |
236 | clk_m2p6.rate = clk_h.rate; | |
237 | clk_m2p7.rate = clk_h.rate; | |
238 | clk_m2p8.rate = clk_h.rate; | |
239 | clk_m2p9.rate = clk_h.rate; | |
240 | clk_m2m0.rate = clk_h.rate; | |
241 | clk_m2m1.rate = clk_h.rate; | |
242 | } | |
243 | ||
51dd249e | 244 | static int __init ep93xx_clock_init(void) |
1d81eedb LB |
245 | { |
246 | u32 value; | |
ae696fd5 | 247 | int i; |
1d81eedb LB |
248 | |
249 | value = __raw_readl(EP93XX_SYSCON_CLOCK_SET1); | |
250 | if (!(value & 0x00800000)) { /* PLL1 bypassed? */ | |
ff05c033 | 251 | clk_pll1.rate = EP93XX_EXT_CLK_RATE; |
1d81eedb LB |
252 | } else { |
253 | clk_pll1.rate = calc_pll_rate(value); | |
254 | } | |
255 | clk_f.rate = clk_pll1.rate / fclk_divisors[(value >> 25) & 0x7]; | |
256 | clk_h.rate = clk_pll1.rate / hclk_divisors[(value >> 20) & 0x7]; | |
257 | clk_p.rate = clk_h.rate / pclk_divisors[(value >> 18) & 0x3]; | |
1c8daabe | 258 | ep93xx_dma_clock_init(); |
1d81eedb LB |
259 | |
260 | value = __raw_readl(EP93XX_SYSCON_CLOCK_SET2); | |
261 | if (!(value & 0x00080000)) { /* PLL2 bypassed? */ | |
ff05c033 | 262 | clk_pll2.rate = EP93XX_EXT_CLK_RATE; |
1d81eedb LB |
263 | } else if (value & 0x00040000) { /* PLL2 enabled? */ |
264 | clk_pll2.rate = calc_pll_rate(value); | |
265 | } else { | |
266 | clk_pll2.rate = 0; | |
267 | } | |
268 | clk_usb_host.rate = clk_pll2.rate / (((value >> 28) & 0xf) + 1); | |
269 | ||
270 | printk(KERN_INFO "ep93xx: PLL1 running at %ld MHz, PLL2 at %ld MHz\n", | |
271 | clk_pll1.rate / 1000000, clk_pll2.rate / 1000000); | |
272 | printk(KERN_INFO "ep93xx: FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n", | |
273 | clk_f.rate / 1000000, clk_h.rate / 1000000, | |
274 | clk_p.rate / 1000000); | |
51dd249e | 275 | |
ae696fd5 RK |
276 | for (i = 0; i < ARRAY_SIZE(clocks); i++) |
277 | clkdev_add(&clocks[i]); | |
51dd249e | 278 | return 0; |
1d81eedb | 279 | } |
51dd249e | 280 | arch_initcall(ep93xx_clock_init); |