diff options
-rw-r--r-- | arch/powerpc/platforms/powermac/cpufreq_64.c | 496 |
1 files changed, 449 insertions, 47 deletions
diff --git a/arch/powerpc/platforms/powermac/cpufreq_64.c b/arch/powerpc/platforms/powermac/cpufreq_64.c index 39150342c6f1..a4b50c4109c2 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_64.c +++ b/arch/powerpc/platforms/powermac/cpufreq_64.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <asm/cputable.h> | 28 | #include <asm/cputable.h> |
29 | #include <asm/time.h> | 29 | #include <asm/time.h> |
30 | #include <asm/smu.h> | 30 | #include <asm/smu.h> |
31 | #include <asm/pmac_pfunc.h> | ||
31 | 32 | ||
32 | #undef DEBUG | 33 | #undef DEBUG |
33 | 34 | ||
@@ -85,6 +86,10 @@ static u32 *g5_pmode_data; | |||
85 | static int g5_pmode_max; | 86 | static int g5_pmode_max; |
86 | static int g5_pmode_cur; | 87 | static int g5_pmode_cur; |
87 | 88 | ||
89 | static void (*g5_switch_volt)(int speed_mode); | ||
90 | static int (*g5_switch_freq)(int speed_mode); | ||
91 | static int (*g5_query_freq)(void); | ||
92 | |||
88 | static DECLARE_MUTEX(g5_switch_mutex); | 93 | static DECLARE_MUTEX(g5_switch_mutex); |
89 | 94 | ||
90 | 95 | ||
@@ -92,9 +97,11 @@ static struct smu_sdbp_fvt *g5_fvt_table; /* table of op. points */ | |||
92 | static int g5_fvt_count; /* number of op. points */ | 97 | static int g5_fvt_count; /* number of op. points */ |
93 | static int g5_fvt_cur; /* current op. point */ | 98 | static int g5_fvt_cur; /* current op. point */ |
94 | 99 | ||
95 | /* ----------------- real hardware interface */ | 100 | /* |
101 | * SMU based voltage switching for Neo2 platforms | ||
102 | */ | ||
96 | 103 | ||
97 | static void g5_switch_volt(int speed_mode) | 104 | static void g5_smu_switch_volt(int speed_mode) |
98 | { | 105 | { |
99 | struct smu_simple_cmd cmd; | 106 | struct smu_simple_cmd cmd; |
100 | 107 | ||
@@ -105,26 +112,57 @@ static void g5_switch_volt(int speed_mode) | |||
105 | wait_for_completion(&comp); | 112 | wait_for_completion(&comp); |
106 | } | 113 | } |
107 | 114 | ||
108 | static int g5_switch_freq(int speed_mode) | 115 | /* |
116 | * Platform function based voltage/vdnap switching for Neo2 | ||
117 | */ | ||
118 | |||
119 | static struct pmf_function *pfunc_set_vdnap0; | ||
120 | static struct pmf_function *pfunc_vdnap0_complete; | ||
121 | |||
122 | static void g5_vdnap_switch_volt(int speed_mode) | ||
109 | { | 123 | { |
110 | struct cpufreq_freqs freqs; | 124 | struct pmf_args args; |
111 | int to; | 125 | u32 slew, done = 0; |
126 | unsigned long timeout; | ||
112 | 127 | ||
113 | if (g5_pmode_cur == speed_mode) | 128 | slew = (speed_mode == CPUFREQ_LOW) ? 1 : 0; |
114 | return 0; | 129 | args.count = 1; |
130 | args.u[0].p = &slew; | ||
115 | 131 | ||
116 | down(&g5_switch_mutex); | 132 | pmf_call_one(pfunc_set_vdnap0, &args); |
117 | 133 | ||
118 | freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency; | 134 | /* It's an irq GPIO so we should be able to just block here, |
119 | freqs.new = g5_cpu_freqs[speed_mode].frequency; | 135 | * I'll do that later after I've properly tested the IRQ code for |
120 | freqs.cpu = 0; | 136 | * platform functions |
137 | */ | ||
138 | timeout = jiffies + HZ/10; | ||
139 | while(!time_after(jiffies, timeout)) { | ||
140 | args.count = 1; | ||
141 | args.u[0].p = &done; | ||
142 | pmf_call_one(pfunc_vdnap0_complete, &args); | ||
143 | if (done) | ||
144 | break; | ||
145 | msleep(1); | ||
146 | } | ||
147 | if (done == 0) | ||
148 | printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n"); | ||
149 | } | ||
121 | 150 | ||
122 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | 151 | |
152 | /* | ||
153 | * SCOM based frequency switching for 970FX rev3 | ||
154 | */ | ||
155 | static int g5_scom_switch_freq(int speed_mode) | ||
156 | { | ||
157 | unsigned long flags; | ||
158 | int to; | ||
123 | 159 | ||
124 | /* If frequency is going up, first ramp up the voltage */ | 160 | /* If frequency is going up, first ramp up the voltage */ |
125 | if (speed_mode < g5_pmode_cur) | 161 | if (speed_mode < g5_pmode_cur) |
126 | g5_switch_volt(speed_mode); | 162 | g5_switch_volt(speed_mode); |
127 | 163 | ||
164 | local_irq_save(flags); | ||
165 | |||
128 | /* Clear PCR high */ | 166 | /* Clear PCR high */ |
129 | scom970_write(SCOM_PCR, 0); | 167 | scom970_write(SCOM_PCR, 0); |
130 | /* Clear PCR low */ | 168 | /* Clear PCR low */ |
@@ -147,6 +185,8 @@ static int g5_switch_freq(int speed_mode) | |||
147 | udelay(100); | 185 | udelay(100); |
148 | } | 186 | } |
149 | 187 | ||
188 | local_irq_restore(flags); | ||
189 | |||
150 | /* If frequency is going down, last ramp the voltage */ | 190 | /* If frequency is going down, last ramp the voltage */ |
151 | if (speed_mode > g5_pmode_cur) | 191 | if (speed_mode > g5_pmode_cur) |
152 | g5_switch_volt(speed_mode); | 192 | g5_switch_volt(speed_mode); |
@@ -154,14 +194,10 @@ static int g5_switch_freq(int speed_mode) | |||
154 | g5_pmode_cur = speed_mode; | 194 | g5_pmode_cur = speed_mode; |
155 | ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul; | 195 | ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul; |
156 | 196 | ||
157 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
158 | |||
159 | up(&g5_switch_mutex); | ||
160 | |||
161 | return 0; | 197 | return 0; |
162 | } | 198 | } |
163 | 199 | ||
164 | static int g5_query_freq(void) | 200 | static int g5_scom_query_freq(void) |
165 | { | 201 | { |
166 | unsigned long psr = scom970_read(SCOM_PSR); | 202 | unsigned long psr = scom970_read(SCOM_PSR); |
167 | int i; | 203 | int i; |
@@ -173,7 +209,104 @@ static int g5_query_freq(void) | |||
173 | return i; | 209 | return i; |
174 | } | 210 | } |
175 | 211 | ||
176 | /* ----------------- cpufreq bookkeeping */ | 212 | /* |
213 | * Platform function based voltage switching for PowerMac7,2 & 7,3 | ||
214 | */ | ||
215 | |||
216 | static struct pmf_function *pfunc_cpu0_volt_high; | ||
217 | static struct pmf_function *pfunc_cpu0_volt_low; | ||
218 | static struct pmf_function *pfunc_cpu1_volt_high; | ||
219 | static struct pmf_function *pfunc_cpu1_volt_low; | ||
220 | |||
221 | static void g5_pfunc_switch_volt(int speed_mode) | ||
222 | { | ||
223 | if (speed_mode == CPUFREQ_HIGH) { | ||
224 | if (pfunc_cpu0_volt_high) | ||
225 | pmf_call_one(pfunc_cpu0_volt_high, NULL); | ||
226 | if (pfunc_cpu1_volt_high) | ||
227 | pmf_call_one(pfunc_cpu1_volt_high, NULL); | ||
228 | } else { | ||
229 | if (pfunc_cpu0_volt_low) | ||
230 | pmf_call_one(pfunc_cpu0_volt_low, NULL); | ||
231 | if (pfunc_cpu1_volt_low) | ||
232 | pmf_call_one(pfunc_cpu1_volt_low, NULL); | ||
233 | } | ||
234 | msleep(10); /* should be faster , to fix */ | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * Platform function based frequency switching for PowerMac7,2 & 7,3 | ||
239 | */ | ||
240 | |||
241 | static struct pmf_function *pfunc_cpu_setfreq_high; | ||
242 | static struct pmf_function *pfunc_cpu_setfreq_low; | ||
243 | static struct pmf_function *pfunc_cpu_getfreq; | ||
244 | static struct pmf_function *pfunc_slewing_done;; | ||
245 | |||
246 | static int g5_pfunc_switch_freq(int speed_mode) | ||
247 | { | ||
248 | struct pmf_args args; | ||
249 | u32 done = 0; | ||
250 | unsigned long timeout; | ||
251 | |||
252 | /* If frequency is going up, first ramp up the voltage */ | ||
253 | if (speed_mode < g5_pmode_cur) | ||
254 | g5_switch_volt(speed_mode); | ||
255 | |||
256 | /* Do it */ | ||
257 | if (speed_mode == CPUFREQ_HIGH) | ||
258 | pmf_call_one(pfunc_cpu_setfreq_high, NULL); | ||
259 | else | ||
260 | pmf_call_one(pfunc_cpu_setfreq_low, NULL); | ||
261 | |||
262 | /* It's an irq GPIO so we should be able to just block here, | ||
263 | * I'll do that later after I've properly tested the IRQ code for | ||
264 | * platform functions | ||
265 | */ | ||
266 | timeout = jiffies + HZ/10; | ||
267 | while(!time_after(jiffies, timeout)) { | ||
268 | args.count = 1; | ||
269 | args.u[0].p = &done; | ||
270 | pmf_call_one(pfunc_slewing_done, &args); | ||
271 | if (done) | ||
272 | break; | ||
273 | msleep(1); | ||
274 | } | ||
275 | if (done == 0) | ||
276 | printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n"); | ||
277 | |||
278 | /* If frequency is going down, last ramp the voltage */ | ||
279 | if (speed_mode > g5_pmode_cur) | ||
280 | g5_switch_volt(speed_mode); | ||
281 | |||
282 | g5_pmode_cur = speed_mode; | ||
283 | ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul; | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int g5_pfunc_query_freq(void) | ||
289 | { | ||
290 | struct pmf_args args; | ||
291 | u32 val = 0; | ||
292 | |||
293 | args.count = 1; | ||
294 | args.u[0].p = &val; | ||
295 | pmf_call_one(pfunc_cpu_getfreq, &args); | ||
296 | return val ? CPUFREQ_HIGH : CPUFREQ_LOW; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * Fake voltage switching for platforms with missing support | ||
301 | */ | ||
302 | |||
303 | static void g5_dummy_switch_volt(int speed_mode) | ||
304 | { | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * Common interface to the cpufreq core | ||
309 | */ | ||
177 | 310 | ||
178 | static int g5_cpufreq_verify(struct cpufreq_policy *policy) | 311 | static int g5_cpufreq_verify(struct cpufreq_policy *policy) |
179 | { | 312 | { |
@@ -183,13 +316,30 @@ static int g5_cpufreq_verify(struct cpufreq_policy *policy) | |||
183 | static int g5_cpufreq_target(struct cpufreq_policy *policy, | 316 | static int g5_cpufreq_target(struct cpufreq_policy *policy, |
184 | unsigned int target_freq, unsigned int relation) | 317 | unsigned int target_freq, unsigned int relation) |
185 | { | 318 | { |
186 | unsigned int newstate = 0; | 319 | unsigned int newstate = 0; |
320 | struct cpufreq_freqs freqs; | ||
321 | int rc; | ||
187 | 322 | ||
188 | if (cpufreq_frequency_table_target(policy, g5_cpu_freqs, | 323 | if (cpufreq_frequency_table_target(policy, g5_cpu_freqs, |
189 | target_freq, relation, &newstate)) | 324 | target_freq, relation, &newstate)) |
190 | return -EINVAL; | 325 | return -EINVAL; |
191 | 326 | ||
192 | return g5_switch_freq(newstate); | 327 | if (g5_pmode_cur == newstate) |
328 | return 0; | ||
329 | |||
330 | down(&g5_switch_mutex); | ||
331 | |||
332 | freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency; | ||
333 | freqs.new = g5_cpu_freqs[newstate].frequency; | ||
334 | freqs.cpu = 0; | ||
335 | |||
336 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
337 | rc = g5_switch_freq(newstate); | ||
338 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
339 | |||
340 | up(&g5_switch_mutex); | ||
341 | |||
342 | return rc; | ||
193 | } | 343 | } |
194 | 344 | ||
195 | static unsigned int g5_cpufreq_get_speed(unsigned int cpu) | 345 | static unsigned int g5_cpufreq_get_speed(unsigned int cpu) |
@@ -205,6 +355,7 @@ static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
205 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | 355 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; |
206 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | 356 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; |
207 | policy->cur = g5_cpu_freqs[g5_query_freq()].frequency; | 357 | policy->cur = g5_cpu_freqs[g5_query_freq()].frequency; |
358 | policy->cpus = cpu_possible_map; | ||
208 | cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu); | 359 | cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu); |
209 | 360 | ||
210 | return cpufreq_frequency_table_cpuinfo(policy, | 361 | return cpufreq_frequency_table_cpuinfo(policy, |
@@ -224,19 +375,39 @@ static struct cpufreq_driver g5_cpufreq_driver = { | |||
224 | }; | 375 | }; |
225 | 376 | ||
226 | 377 | ||
227 | static int __init g5_cpufreq_init(void) | 378 | static int __init g5_neo2_cpufreq_init(struct device_node *cpus) |
228 | { | 379 | { |
229 | struct device_node *cpunode; | 380 | struct device_node *cpunode; |
230 | unsigned int psize, ssize; | 381 | unsigned int psize, ssize; |
231 | struct smu_sdbp_header *shdr; | ||
232 | unsigned long max_freq; | 382 | unsigned long max_freq; |
233 | u32 *valp; | 383 | char *freq_method, *volt_method; |
384 | u32 *valp, pvr_hi; | ||
385 | int use_volts_vdnap = 0; | ||
386 | int use_volts_smu = 0; | ||
234 | int rc = -ENODEV; | 387 | int rc = -ENODEV; |
235 | 388 | ||
236 | /* Look for CPU and SMU nodes */ | 389 | /* Check supported platforms */ |
237 | cpunode = of_find_node_by_type(NULL, "cpu"); | 390 | if (machine_is_compatible("PowerMac8,1") || |
238 | if (!cpunode) { | 391 | machine_is_compatible("PowerMac8,2") || |
239 | DBG("No CPU node !\n"); | 392 | machine_is_compatible("PowerMac9,1")) |
393 | use_volts_smu = 1; | ||
394 | else if (machine_is_compatible("PowerMac11,2")) | ||
395 | use_volts_vdnap = 1; | ||
396 | else | ||
397 | return -ENODEV; | ||
398 | |||
399 | /* Get first CPU node */ | ||
400 | for (cpunode = NULL; | ||
401 | (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) { | ||
402 | u32 *reg = | ||
403 | (u32 *)get_property(cpunode, "reg", NULL); | ||
404 | if (reg == NULL || (*reg) != 0) | ||
405 | continue; | ||
406 | if (!strcmp(cpunode->type, "cpu")) | ||
407 | break; | ||
408 | } | ||
409 | if (cpunode == NULL) { | ||
410 | printk(KERN_ERR "cpufreq: Can't find any CPU 0 node\n"); | ||
240 | return -ENODEV; | 411 | return -ENODEV; |
241 | } | 412 | } |
242 | 413 | ||
@@ -246,8 +417,9 @@ static int __init g5_cpufreq_init(void) | |||
246 | DBG("No cpu-version property !\n"); | 417 | DBG("No cpu-version property !\n"); |
247 | goto bail_noprops; | 418 | goto bail_noprops; |
248 | } | 419 | } |
249 | if (((*valp) >> 16) != 0x3c) { | 420 | pvr_hi = (*valp) >> 16; |
250 | DBG("Wrong CPU version: %08x\n", *valp); | 421 | if (pvr_hi != 0x3c && pvr_hi != 0x44) { |
422 | printk(KERN_ERR "cpufreq: Unsupported CPU version\n"); | ||
251 | goto bail_noprops; | 423 | goto bail_noprops; |
252 | } | 424 | } |
253 | 425 | ||
@@ -259,18 +431,50 @@ static int __init g5_cpufreq_init(void) | |||
259 | } | 431 | } |
260 | g5_pmode_max = psize / sizeof(u32) - 1; | 432 | g5_pmode_max = psize / sizeof(u32) - 1; |
261 | 433 | ||
262 | /* Look for the FVT table */ | 434 | if (use_volts_smu) { |
263 | shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL); | 435 | struct smu_sdbp_header *shdr; |
264 | if (!shdr) | 436 | |
265 | goto bail_noprops; | 437 | /* Look for the FVT table */ |
266 | g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1]; | 438 | shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL); |
267 | ssize = (shdr->len * sizeof(u32)) - sizeof(struct smu_sdbp_header); | 439 | if (!shdr) |
268 | g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt); | 440 | goto bail_noprops; |
269 | g5_fvt_cur = 0; | 441 | g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1]; |
270 | 442 | ssize = (shdr->len * sizeof(u32)) - | |
271 | /* Sanity checking */ | 443 | sizeof(struct smu_sdbp_header); |
272 | if (g5_fvt_count < 1 || g5_pmode_max < 1) | 444 | g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt); |
273 | goto bail_noprops; | 445 | g5_fvt_cur = 0; |
446 | |||
447 | /* Sanity checking */ | ||
448 | if (g5_fvt_count < 1 || g5_pmode_max < 1) | ||
449 | goto bail_noprops; | ||
450 | |||
451 | g5_switch_volt = g5_smu_switch_volt; | ||
452 | volt_method = "SMU"; | ||
453 | } else if (use_volts_vdnap) { | ||
454 | struct device_node *root; | ||
455 | |||
456 | root = of_find_node_by_path("/"); | ||
457 | if (root == NULL) { | ||
458 | printk(KERN_ERR "cpufreq: Can't find root of " | ||
459 | "device tree\n"); | ||
460 | goto bail_noprops; | ||
461 | } | ||
462 | pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0"); | ||
463 | pfunc_vdnap0_complete = | ||
464 | pmf_find_function(root, "slewing-done"); | ||
465 | if (pfunc_set_vdnap0 == NULL || | ||
466 | pfunc_vdnap0_complete == NULL) { | ||
467 | printk(KERN_ERR "cpufreq: Can't find required " | ||
468 | "platform function\n"); | ||
469 | goto bail_noprops; | ||
470 | } | ||
471 | |||
472 | g5_switch_volt = g5_vdnap_switch_volt; | ||
473 | volt_method = "GPIO"; | ||
474 | } else { | ||
475 | g5_switch_volt = g5_dummy_switch_volt; | ||
476 | volt_method = "none"; | ||
477 | } | ||
274 | 478 | ||
275 | /* | 479 | /* |
276 | * From what I see, clock-frequency is always the maximal frequency. | 480 | * From what I see, clock-frequency is always the maximal frequency. |
@@ -286,19 +490,23 @@ static int __init g5_cpufreq_init(void) | |||
286 | g5_cpu_freqs[0].frequency = max_freq; | 490 | g5_cpu_freqs[0].frequency = max_freq; |
287 | g5_cpu_freqs[1].frequency = max_freq/2; | 491 | g5_cpu_freqs[1].frequency = max_freq/2; |
288 | 492 | ||
289 | /* Check current frequency */ | 493 | /* Set callbacks */ |
290 | g5_pmode_cur = g5_query_freq(); | 494 | g5_switch_freq = g5_scom_switch_freq; |
291 | if (g5_pmode_cur > 1) | 495 | g5_query_freq = g5_scom_query_freq; |
292 | /* We don't support anything but 1:1 and 1:2, fixup ... */ | 496 | freq_method = "SCOM"; |
293 | g5_pmode_cur = 1; | ||
294 | 497 | ||
295 | /* Force apply current frequency to make sure everything is in | 498 | /* Force apply current frequency to make sure everything is in |
296 | * sync (voltage is right for example). Firmware may leave us with | 499 | * sync (voltage is right for example). Firmware may leave us with |
297 | * a strange setting ... | 500 | * a strange setting ... |
298 | */ | 501 | */ |
299 | g5_switch_freq(g5_pmode_cur); | 502 | g5_switch_volt(CPUFREQ_HIGH); |
503 | msleep(10); | ||
504 | g5_pmode_cur = -1; | ||
505 | g5_switch_freq(g5_query_freq()); | ||
300 | 506 | ||
301 | printk(KERN_INFO "Registering G5 CPU frequency driver\n"); | 507 | printk(KERN_INFO "Registering G5 CPU frequency driver\n"); |
508 | printk(KERN_INFO "Frequency method: %s, Voltage method: %s\n", | ||
509 | freq_method, volt_method); | ||
302 | printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", | 510 | printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", |
303 | g5_cpu_freqs[1].frequency/1000, | 511 | g5_cpu_freqs[1].frequency/1000, |
304 | g5_cpu_freqs[0].frequency/1000, | 512 | g5_cpu_freqs[0].frequency/1000, |
@@ -317,6 +525,200 @@ static int __init g5_cpufreq_init(void) | |||
317 | return rc; | 525 | return rc; |
318 | } | 526 | } |
319 | 527 | ||
528 | static int __init g5_pm72_cpufreq_init(struct device_node *cpus) | ||
529 | { | ||
530 | struct device_node *cpuid = NULL, *hwclock = NULL, *cpunode = NULL; | ||
531 | u8 *eeprom = NULL; | ||
532 | u32 *valp; | ||
533 | u64 max_freq, min_freq, ih, il; | ||
534 | int has_volt = 1, rc = 0; | ||
535 | |||
536 | /* Get first CPU node */ | ||
537 | for (cpunode = NULL; | ||
538 | (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) { | ||
539 | if (!strcmp(cpunode->type, "cpu")) | ||
540 | break; | ||
541 | } | ||
542 | if (cpunode == NULL) { | ||
543 | printk(KERN_ERR "cpufreq: Can't find any CPU node\n"); | ||
544 | return -ENODEV; | ||
545 | } | ||
546 | |||
547 | /* Lookup the cpuid eeprom node */ | ||
548 | cpuid = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/cpuid@a0"); | ||
549 | if (cpuid != NULL) | ||
550 | eeprom = (u8 *)get_property(cpuid, "cpuid", NULL); | ||
551 | if (eeprom == NULL) { | ||
552 | printk(KERN_ERR "cpufreq: Can't find cpuid EEPROM !\n"); | ||
553 | rc = -ENODEV; | ||
554 | goto bail; | ||
555 | } | ||
556 | |||
557 | /* Lookup the i2c hwclock */ | ||
558 | for (hwclock = NULL; | ||
559 | (hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){ | ||
560 | char *loc = get_property(hwclock, "hwctrl-location", NULL); | ||
561 | if (loc == NULL) | ||
562 | continue; | ||
563 | if (strcmp(loc, "CPU CLOCK")) | ||
564 | continue; | ||
565 | if (!get_property(hwclock, "platform-get-frequency", NULL)) | ||
566 | continue; | ||
567 | break; | ||
568 | } | ||
569 | if (hwclock == NULL) { | ||
570 | printk(KERN_ERR "cpufreq: Can't find i2c clock chip !\n"); | ||
571 | rc = -ENODEV; | ||
572 | goto bail; | ||
573 | } | ||
574 | |||
575 | DBG("cpufreq: i2c clock chip found: %s\n", hwclock->full_name); | ||
576 | |||
577 | /* Now get all the platform functions */ | ||
578 | pfunc_cpu_getfreq = | ||
579 | pmf_find_function(hwclock, "get-frequency"); | ||
580 | pfunc_cpu_setfreq_high = | ||
581 | pmf_find_function(hwclock, "set-frequency-high"); | ||
582 | pfunc_cpu_setfreq_low = | ||
583 | pmf_find_function(hwclock, "set-frequency-low"); | ||
584 | pfunc_slewing_done = | ||
585 | pmf_find_function(hwclock, "slewing-done"); | ||
586 | pfunc_cpu0_volt_high = | ||
587 | pmf_find_function(hwclock, "set-voltage-high-0"); | ||
588 | pfunc_cpu0_volt_low = | ||
589 | pmf_find_function(hwclock, "set-voltage-low-0"); | ||
590 | pfunc_cpu1_volt_high = | ||
591 | pmf_find_function(hwclock, "set-voltage-high-1"); | ||
592 | pfunc_cpu1_volt_low = | ||
593 | pmf_find_function(hwclock, "set-voltage-low-1"); | ||
594 | |||
595 | /* Check we have minimum requirements */ | ||
596 | if (pfunc_cpu_getfreq == NULL || pfunc_cpu_setfreq_high == NULL || | ||
597 | pfunc_cpu_setfreq_low == NULL || pfunc_slewing_done == NULL) { | ||
598 | printk(KERN_ERR "cpufreq: Can't find platform functions !\n"); | ||
599 | rc = -ENODEV; | ||
600 | goto bail; | ||
601 | } | ||
602 | |||
603 | /* Check that we have complete sets */ | ||
604 | if (pfunc_cpu0_volt_high == NULL || pfunc_cpu0_volt_low == NULL) { | ||
605 | pmf_put_function(pfunc_cpu0_volt_high); | ||
606 | pmf_put_function(pfunc_cpu0_volt_low); | ||
607 | pfunc_cpu0_volt_high = pfunc_cpu0_volt_low = NULL; | ||
608 | has_volt = 0; | ||
609 | } | ||
610 | if (!has_volt || | ||
611 | pfunc_cpu1_volt_high == NULL || pfunc_cpu1_volt_low == NULL) { | ||
612 | pmf_put_function(pfunc_cpu1_volt_high); | ||
613 | pmf_put_function(pfunc_cpu1_volt_low); | ||
614 | pfunc_cpu1_volt_high = pfunc_cpu1_volt_low = NULL; | ||
615 | } | ||
616 | |||
617 | /* Note: The device tree also contains a "platform-set-values" | ||
618 | * function for which I haven't quite figured out the usage. It | ||
619 | * might have to be called on init and/or wakeup, I'm not too sure | ||
620 | * but things seem to work fine without it so far ... | ||
621 | */ | ||
622 | |||
623 | /* Get max frequency from device-tree */ | ||
624 | valp = (u32 *)get_property(cpunode, "clock-frequency", NULL); | ||
625 | if (!valp) { | ||
626 | printk(KERN_ERR "cpufreq: Can't find CPU frequency !\n"); | ||
627 | rc = -ENODEV; | ||
628 | goto bail; | ||
629 | } | ||
630 | |||
631 | max_freq = (*valp)/1000; | ||
632 | |||
633 | /* Now calculate reduced frequency by using the cpuid input freq | ||
634 | * ratio. This requires 64 bits math unless we are willing to lose | ||
635 | * some precision | ||
636 | */ | ||
637 | ih = *((u32 *)(eeprom + 0x10)); | ||
638 | il = *((u32 *)(eeprom + 0x20)); | ||
639 | min_freq = 0; | ||
640 | if (ih != 0 && il != 0) | ||
641 | min_freq = (max_freq * il) / ih; | ||
642 | |||
643 | /* Sanity check */ | ||
644 | if (min_freq >= max_freq || min_freq < 1000) { | ||
645 | printk(KERN_ERR "cpufreq: Can't calculate low frequency !\n"); | ||
646 | rc = -ENODEV; | ||
647 | goto bail; | ||
648 | } | ||
649 | g5_cpu_freqs[0].frequency = max_freq; | ||
650 | g5_cpu_freqs[1].frequency = min_freq; | ||
651 | |||
652 | /* Set callbacks */ | ||
653 | g5_switch_volt = g5_pfunc_switch_volt; | ||
654 | g5_switch_freq = g5_pfunc_switch_freq; | ||
655 | g5_query_freq = g5_pfunc_query_freq; | ||
656 | |||
657 | /* Force apply current frequency to make sure everything is in | ||
658 | * sync (voltage is right for example). Firmware may leave us with | ||
659 | * a strange setting ... | ||
660 | */ | ||
661 | g5_switch_volt(CPUFREQ_HIGH); | ||
662 | msleep(10); | ||
663 | g5_pmode_cur = -1; | ||
664 | g5_switch_freq(g5_query_freq()); | ||
665 | |||
666 | printk(KERN_INFO "Registering G5 CPU frequency driver\n"); | ||
667 | printk(KERN_INFO "Frequency method: i2c/pfunc, " | ||
668 | "Voltage method: %s\n", has_volt ? "i2c/pfunc" : "none"); | ||
669 | printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", | ||
670 | g5_cpu_freqs[1].frequency/1000, | ||
671 | g5_cpu_freqs[0].frequency/1000, | ||
672 | g5_cpu_freqs[g5_pmode_cur].frequency/1000); | ||
673 | |||
674 | rc = cpufreq_register_driver(&g5_cpufreq_driver); | ||
675 | bail: | ||
676 | if (rc != 0) { | ||
677 | pmf_put_function(pfunc_cpu_getfreq); | ||
678 | pmf_put_function(pfunc_cpu_setfreq_high); | ||
679 | pmf_put_function(pfunc_cpu_setfreq_low); | ||
680 | pmf_put_function(pfunc_slewing_done); | ||
681 | pmf_put_function(pfunc_cpu0_volt_high); | ||
682 | pmf_put_function(pfunc_cpu0_volt_low); | ||
683 | pmf_put_function(pfunc_cpu1_volt_high); | ||
684 | pmf_put_function(pfunc_cpu1_volt_low); | ||
685 | } | ||
686 | of_node_put(hwclock); | ||
687 | of_node_put(cpuid); | ||
688 | of_node_put(cpunode); | ||
689 | |||
690 | return rc; | ||
691 | } | ||
692 | |||
693 | static int __init g5_rm31_cpufreq_init(struct device_node *cpus) | ||
694 | { | ||
695 | /* NYI */ | ||
696 | return 0; | ||
697 | } | ||
698 | |||
699 | static int __init g5_cpufreq_init(void) | ||
700 | { | ||
701 | struct device_node *cpus; | ||
702 | int rc; | ||
703 | |||
704 | cpus = of_find_node_by_path("/cpus"); | ||
705 | if (cpus == NULL) { | ||
706 | DBG("No /cpus node !\n"); | ||
707 | return -ENODEV; | ||
708 | } | ||
709 | |||
710 | if (machine_is_compatible("PowerMac7,2") || | ||
711 | machine_is_compatible("PowerMac7,3")) | ||
712 | rc = g5_pm72_cpufreq_init(cpus); | ||
713 | else if (machine_is_compatible("RackMac3,1")) | ||
714 | rc = g5_rm31_cpufreq_init(cpus); | ||
715 | else | ||
716 | rc = g5_neo2_cpufreq_init(cpus); | ||
717 | |||
718 | of_node_put(cpus); | ||
719 | return rc; | ||
720 | } | ||
721 | |||
320 | module_init(g5_cpufreq_init); | 722 | module_init(g5_cpufreq_init); |
321 | 723 | ||
322 | 724 | ||