diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/arm/mach-sa1100/cpu-sa1110.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/arm/mach-sa1100/cpu-sa1110.c')
-rw-r--r-- | arch/arm/mach-sa1100/cpu-sa1110.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/arch/arm/mach-sa1100/cpu-sa1110.c b/arch/arm/mach-sa1100/cpu-sa1110.c new file mode 100644 index 000000000000..8d2a89a2ea01 --- /dev/null +++ b/arch/arm/mach-sa1100/cpu-sa1110.c | |||
@@ -0,0 +1,367 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-sa1100/cpu-sa1110.c | ||
3 | * | ||
4 | * Copyright (C) 2001 Russell King | ||
5 | * | ||
6 | * $Id: cpu-sa1110.c,v 1.9 2002/07/06 16:53:18 rmk Exp $ | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * Note: there are two erratas that apply to the SA1110 here: | ||
13 | * 7 - SDRAM auto-power-up failure (rev A0) | ||
14 | * 13 - Corruption of internal register reads/writes following | ||
15 | * SDRAM reads (rev A0, B0, B1) | ||
16 | * | ||
17 | * We ignore rev. A0 and B0 devices; I don't think they're worth supporting. | ||
18 | */ | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/cpufreq.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/init.h> | ||
25 | |||
26 | #include <asm/hardware.h> | ||
27 | #include <asm/mach-types.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/system.h> | ||
30 | |||
31 | #include "generic.h" | ||
32 | |||
33 | #undef DEBUG | ||
34 | |||
35 | static struct cpufreq_driver sa1110_driver; | ||
36 | |||
37 | struct sdram_params { | ||
38 | u_char rows; /* bits */ | ||
39 | u_char cas_latency; /* cycles */ | ||
40 | u_char tck; /* clock cycle time (ns) */ | ||
41 | u_char trcd; /* activate to r/w (ns) */ | ||
42 | u_char trp; /* precharge to activate (ns) */ | ||
43 | u_char twr; /* write recovery time (ns) */ | ||
44 | u_short refresh; /* refresh time for array (us) */ | ||
45 | }; | ||
46 | |||
47 | struct sdram_info { | ||
48 | u_int mdcnfg; | ||
49 | u_int mdrefr; | ||
50 | u_int mdcas[3]; | ||
51 | }; | ||
52 | |||
53 | static struct sdram_params tc59sm716_cl2_params __initdata = { | ||
54 | .rows = 12, | ||
55 | .tck = 10, | ||
56 | .trcd = 20, | ||
57 | .trp = 20, | ||
58 | .twr = 10, | ||
59 | .refresh = 64000, | ||
60 | .cas_latency = 2, | ||
61 | }; | ||
62 | |||
63 | static struct sdram_params tc59sm716_cl3_params __initdata = { | ||
64 | .rows = 12, | ||
65 | .tck = 8, | ||
66 | .trcd = 20, | ||
67 | .trp = 20, | ||
68 | .twr = 8, | ||
69 | .refresh = 64000, | ||
70 | .cas_latency = 3, | ||
71 | }; | ||
72 | |||
73 | static struct sdram_params samsung_k4s641632d_tc75 __initdata = { | ||
74 | .rows = 14, | ||
75 | .tck = 9, | ||
76 | .trcd = 27, | ||
77 | .trp = 20, | ||
78 | .twr = 9, | ||
79 | .refresh = 64000, | ||
80 | .cas_latency = 3, | ||
81 | }; | ||
82 | |||
83 | static struct sdram_params samsung_km416s4030ct __initdata = { | ||
84 | .rows = 13, | ||
85 | .tck = 8, | ||
86 | .trcd = 24, /* 3 CLKs */ | ||
87 | .trp = 24, /* 3 CLKs */ | ||
88 | .twr = 16, /* Trdl: 2 CLKs */ | ||
89 | .refresh = 64000, | ||
90 | .cas_latency = 3, | ||
91 | }; | ||
92 | |||
93 | static struct sdram_params wbond_w982516ah75l_cl3_params __initdata = { | ||
94 | .rows = 16, | ||
95 | .tck = 8, | ||
96 | .trcd = 20, | ||
97 | .trp = 20, | ||
98 | .twr = 8, | ||
99 | .refresh = 64000, | ||
100 | .cas_latency = 3, | ||
101 | }; | ||
102 | |||
103 | static struct sdram_params sdram_params; | ||
104 | |||
105 | /* | ||
106 | * Given a period in ns and frequency in khz, calculate the number of | ||
107 | * cycles of frequency in period. Note that we round up to the next | ||
108 | * cycle, even if we are only slightly over. | ||
109 | */ | ||
110 | static inline u_int ns_to_cycles(u_int ns, u_int khz) | ||
111 | { | ||
112 | return (ns * khz + 999999) / 1000000; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * Create the MDCAS register bit pattern. | ||
117 | */ | ||
118 | static inline void set_mdcas(u_int *mdcas, int delayed, u_int rcd) | ||
119 | { | ||
120 | u_int shift; | ||
121 | |||
122 | rcd = 2 * rcd - 1; | ||
123 | shift = delayed + 1 + rcd; | ||
124 | |||
125 | mdcas[0] = (1 << rcd) - 1; | ||
126 | mdcas[0] |= 0x55555555 << shift; | ||
127 | mdcas[1] = mdcas[2] = 0x55555555 << (shift & 1); | ||
128 | } | ||
129 | |||
130 | static void | ||
131 | sdram_calculate_timing(struct sdram_info *sd, u_int cpu_khz, | ||
132 | struct sdram_params *sdram) | ||
133 | { | ||
134 | u_int mem_khz, sd_khz, trp, twr; | ||
135 | |||
136 | mem_khz = cpu_khz / 2; | ||
137 | sd_khz = mem_khz; | ||
138 | |||
139 | /* | ||
140 | * If SDCLK would invalidate the SDRAM timings, | ||
141 | * run SDCLK at half speed. | ||
142 | * | ||
143 | * CPU steppings prior to B2 must either run the memory at | ||
144 | * half speed or use delayed read latching (errata 13). | ||
145 | */ | ||
146 | if ((ns_to_cycles(sdram->tck, sd_khz) > 1) || | ||
147 | (CPU_REVISION < CPU_SA1110_B2 && sd_khz < 62000)) | ||
148 | sd_khz /= 2; | ||
149 | |||
150 | sd->mdcnfg = MDCNFG & 0x007f007f; | ||
151 | |||
152 | twr = ns_to_cycles(sdram->twr, mem_khz); | ||
153 | |||
154 | /* trp should always be >1 */ | ||
155 | trp = ns_to_cycles(sdram->trp, mem_khz) - 1; | ||
156 | if (trp < 1) | ||
157 | trp = 1; | ||
158 | |||
159 | sd->mdcnfg |= trp << 8; | ||
160 | sd->mdcnfg |= trp << 24; | ||
161 | sd->mdcnfg |= sdram->cas_latency << 12; | ||
162 | sd->mdcnfg |= sdram->cas_latency << 28; | ||
163 | sd->mdcnfg |= twr << 14; | ||
164 | sd->mdcnfg |= twr << 30; | ||
165 | |||
166 | sd->mdrefr = MDREFR & 0xffbffff0; | ||
167 | sd->mdrefr |= 7; | ||
168 | |||
169 | if (sd_khz != mem_khz) | ||
170 | sd->mdrefr |= MDREFR_K1DB2; | ||
171 | |||
172 | /* initial number of '1's in MDCAS + 1 */ | ||
173 | set_mdcas(sd->mdcas, sd_khz >= 62000, ns_to_cycles(sdram->trcd, mem_khz)); | ||
174 | |||
175 | #ifdef DEBUG | ||
176 | printk("MDCNFG: %08x MDREFR: %08x MDCAS0: %08x MDCAS1: %08x MDCAS2: %08x\n", | ||
177 | sd->mdcnfg, sd->mdrefr, sd->mdcas[0], sd->mdcas[1], sd->mdcas[2]); | ||
178 | #endif | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * Set the SDRAM refresh rate. | ||
183 | */ | ||
184 | static inline void sdram_set_refresh(u_int dri) | ||
185 | { | ||
186 | MDREFR = (MDREFR & 0xffff000f) | (dri << 4); | ||
187 | (void) MDREFR; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Update the refresh period. We do this such that we always refresh | ||
192 | * the SDRAMs within their permissible period. The refresh period is | ||
193 | * always a multiple of the memory clock (fixed at cpu_clock / 2). | ||
194 | * | ||
195 | * FIXME: we don't currently take account of burst accesses here, | ||
196 | * but neither do Intels DM nor Angel. | ||
197 | */ | ||
198 | static void | ||
199 | sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram) | ||
200 | { | ||
201 | u_int ns_row = (sdram->refresh * 1000) >> sdram->rows; | ||
202 | u_int dri = ns_to_cycles(ns_row, cpu_khz / 2) / 32; | ||
203 | |||
204 | #ifdef DEBUG | ||
205 | mdelay(250); | ||
206 | printk("new dri value = %d\n", dri); | ||
207 | #endif | ||
208 | |||
209 | sdram_set_refresh(dri); | ||
210 | } | ||
211 | |||
212 | /* | ||
213 | * Ok, set the CPU frequency. | ||
214 | */ | ||
215 | static int sa1110_target(struct cpufreq_policy *policy, | ||
216 | unsigned int target_freq, | ||
217 | unsigned int relation) | ||
218 | { | ||
219 | struct sdram_params *sdram = &sdram_params; | ||
220 | struct cpufreq_freqs freqs; | ||
221 | struct sdram_info sd; | ||
222 | unsigned long flags; | ||
223 | unsigned int ppcr, unused; | ||
224 | |||
225 | switch(relation){ | ||
226 | case CPUFREQ_RELATION_L: | ||
227 | ppcr = sa11x0_freq_to_ppcr(target_freq); | ||
228 | if (sa11x0_ppcr_to_freq(ppcr) > policy->max) | ||
229 | ppcr--; | ||
230 | break; | ||
231 | case CPUFREQ_RELATION_H: | ||
232 | ppcr = sa11x0_freq_to_ppcr(target_freq); | ||
233 | if (ppcr && (sa11x0_ppcr_to_freq(ppcr) > target_freq) && | ||
234 | (sa11x0_ppcr_to_freq(ppcr-1) >= policy->min)) | ||
235 | ppcr--; | ||
236 | break; | ||
237 | default: | ||
238 | return -EINVAL; | ||
239 | } | ||
240 | |||
241 | freqs.old = sa11x0_getspeed(0); | ||
242 | freqs.new = sa11x0_ppcr_to_freq(ppcr); | ||
243 | freqs.cpu = 0; | ||
244 | |||
245 | sdram_calculate_timing(&sd, freqs.new, sdram); | ||
246 | |||
247 | #if 0 | ||
248 | /* | ||
249 | * These values are wrong according to the SA1110 documentation | ||
250 | * and errata, but they seem to work. Need to get a storage | ||
251 | * scope on to the SDRAM signals to work out why. | ||
252 | */ | ||
253 | if (policy->max < 147500) { | ||
254 | sd.mdrefr |= MDREFR_K1DB2; | ||
255 | sd.mdcas[0] = 0xaaaaaa7f; | ||
256 | } else { | ||
257 | sd.mdrefr &= ~MDREFR_K1DB2; | ||
258 | sd.mdcas[0] = 0xaaaaaa9f; | ||
259 | } | ||
260 | sd.mdcas[1] = 0xaaaaaaaa; | ||
261 | sd.mdcas[2] = 0xaaaaaaaa; | ||
262 | #endif | ||
263 | |||
264 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
265 | |||
266 | /* | ||
267 | * The clock could be going away for some time. Set the SDRAMs | ||
268 | * to refresh rapidly (every 64 memory clock cycles). To get | ||
269 | * through the whole array, we need to wait 262144 mclk cycles. | ||
270 | * We wait 20ms to be safe. | ||
271 | */ | ||
272 | sdram_set_refresh(2); | ||
273 | if (!irqs_disabled()) { | ||
274 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
275 | schedule_timeout(20 * HZ / 1000); | ||
276 | } else { | ||
277 | mdelay(20); | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Reprogram the DRAM timings with interrupts disabled, and | ||
282 | * ensure that we are doing this within a complete cache line. | ||
283 | * This means that we won't access SDRAM for the duration of | ||
284 | * the programming. | ||
285 | */ | ||
286 | local_irq_save(flags); | ||
287 | asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); | ||
288 | udelay(10); | ||
289 | __asm__ __volatile__(" \n\ | ||
290 | b 2f \n\ | ||
291 | .align 5 \n\ | ||
292 | 1: str %3, [%1, #0] @ MDCNFG \n\ | ||
293 | str %4, [%1, #28] @ MDREFR \n\ | ||
294 | str %5, [%1, #4] @ MDCAS0 \n\ | ||
295 | str %6, [%1, #8] @ MDCAS1 \n\ | ||
296 | str %7, [%1, #12] @ MDCAS2 \n\ | ||
297 | str %8, [%2, #0] @ PPCR \n\ | ||
298 | ldr %0, [%1, #0] \n\ | ||
299 | b 3f \n\ | ||
300 | 2: b 1b \n\ | ||
301 | 3: nop \n\ | ||
302 | nop" | ||
303 | : "=&r" (unused) | ||
304 | : "r" (&MDCNFG), "r" (&PPCR), "0" (sd.mdcnfg), | ||
305 | "r" (sd.mdrefr), "r" (sd.mdcas[0]), | ||
306 | "r" (sd.mdcas[1]), "r" (sd.mdcas[2]), "r" (ppcr)); | ||
307 | local_irq_restore(flags); | ||
308 | |||
309 | /* | ||
310 | * Now, return the SDRAM refresh back to normal. | ||
311 | */ | ||
312 | sdram_update_refresh(freqs.new, sdram); | ||
313 | |||
314 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
315 | |||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static int __init sa1110_cpu_init(struct cpufreq_policy *policy) | ||
320 | { | ||
321 | if (policy->cpu != 0) | ||
322 | return -EINVAL; | ||
323 | policy->cur = policy->min = policy->max = sa11x0_getspeed(0); | ||
324 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | ||
325 | policy->cpuinfo.min_freq = 59000; | ||
326 | policy->cpuinfo.max_freq = 287000; | ||
327 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static struct cpufreq_driver sa1110_driver = { | ||
332 | .flags = CPUFREQ_STICKY, | ||
333 | .verify = sa11x0_verify_speed, | ||
334 | .target = sa1110_target, | ||
335 | .get = sa11x0_getspeed, | ||
336 | .init = sa1110_cpu_init, | ||
337 | .name = "sa1110", | ||
338 | }; | ||
339 | |||
340 | static int __init sa1110_clk_init(void) | ||
341 | { | ||
342 | struct sdram_params *sdram = NULL; | ||
343 | |||
344 | if (machine_is_assabet()) | ||
345 | sdram = &tc59sm716_cl3_params; | ||
346 | |||
347 | if (machine_is_pt_system3()) | ||
348 | sdram = &samsung_k4s641632d_tc75; | ||
349 | |||
350 | if (machine_is_h3100()) | ||
351 | sdram = &samsung_km416s4030ct; | ||
352 | |||
353 | if (sdram) { | ||
354 | printk(KERN_DEBUG "SDRAM: tck: %d trcd: %d trp: %d" | ||
355 | " twr: %d refresh: %d cas_latency: %d\n", | ||
356 | sdram->tck, sdram->trcd, sdram->trp, | ||
357 | sdram->twr, sdram->refresh, sdram->cas_latency); | ||
358 | |||
359 | memcpy(&sdram_params, sdram, sizeof(sdram_params)); | ||
360 | |||
361 | return cpufreq_register_driver(&sa1110_driver); | ||
362 | } | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | arch_initcall(sa1110_clk_init); | ||