Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] ppc64: SMU based macs cpufreq support

CPU freq support using 970FX powertune facility for iMac G5 and SMU
based single CPU desktop.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>

authored by

Benjamin Herrenschmidt and committed by
Paul Mackerras
4350147a a82765b6

+572 -25
+8
arch/powerpc/Kconfig
··· 404 404 this currently includes some models of iBook & Titanium 405 405 PowerBook. 406 406 407 + config CPU_FREQ_PMAC64 408 + bool "Support for some Apple G5s" 409 + depends on CPU_FREQ && PMAC_SMU && PPC64 410 + select CPU_FREQ_TABLE 411 + help 412 + This adds support for frequency switching on Apple iMac G5, 413 + and some of the more recent desktop G5 machines as well. 414 + 407 415 config PPC601_SYNC_FIX 408 416 bool "Workarounds for PPC601 bugs" 409 417 depends on 6xx && (PPC_PREP || PPC_PMAC)
+70
arch/powerpc/kernel/misc_64.S
··· 604 604 #endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */ 605 605 606 606 /* 607 + * SCOM access functions for 970 (FX only for now) 608 + * 609 + * unsigned long scom970_read(unsigned int address); 610 + * void scom970_write(unsigned int address, unsigned long value); 611 + * 612 + * The address passed in is the 24 bits register address. This code 613 + * is 970 specific and will not check the status bits, so you should 614 + * know what you are doing. 615 + */ 616 + _GLOBAL(scom970_read) 617 + /* interrupts off */ 618 + mfmsr r4 619 + ori r0,r4,MSR_EE 620 + xori r0,r0,MSR_EE 621 + mtmsrd r0,1 622 + 623 + /* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits 624 + * (including parity). On current CPUs they must be 0'd, 625 + * and finally or in RW bit 626 + */ 627 + rlwinm r3,r3,8,0,15 628 + ori r3,r3,0x8000 629 + 630 + /* do the actual scom read */ 631 + sync 632 + mtspr SPRN_SCOMC,r3 633 + isync 634 + mfspr r3,SPRN_SCOMD 635 + isync 636 + mfspr r0,SPRN_SCOMC 637 + isync 638 + 639 + /* XXX: fixup result on some buggy 970's (ouch ! we lost a bit, bah 640 + * that's the best we can do). Not implemented yet as we don't use 641 + * the scom on any of the bogus CPUs yet, but may have to be done 642 + * ultimately 643 + */ 644 + 645 + /* restore interrupts */ 646 + mtmsrd r4,1 647 + blr 648 + 649 + 650 + _GLOBAL(scom970_write) 651 + /* interrupts off */ 652 + mfmsr r5 653 + ori r0,r5,MSR_EE 654 + xori r0,r0,MSR_EE 655 + mtmsrd r0,1 656 + 657 + /* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits 658 + * (including parity). On current CPUs they must be 0'd. 659 + */ 660 + 661 + rlwinm r3,r3,8,0,15 662 + 663 + sync 664 + mtspr SPRN_SCOMD,r4 /* write data */ 665 + isync 666 + mtspr SPRN_SCOMC,r3 /* write command */ 667 + isync 668 + mfspr 3,SPRN_SCOMC 669 + isync 670 + 671 + /* restore interrupts */ 672 + mtmsrd r5,1 673 + blr 674 + 675 + 676 + /* 607 677 * Create a kernel thread 608 678 * kernel_thread(fn, arg, flags) 609 679 */
+2 -1
arch/powerpc/platforms/powermac/Makefile
··· 1 1 obj-y += pic.o setup.o time.o feature.o pci.o \ 2 2 sleep.o low_i2c.o cache.o 3 3 obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o 4 - obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq.o 4 + obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o 5 + obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o 5 6 obj-$(CONFIG_NVRAM) += nvram.o 6 7 # ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff 7 8 obj-$(CONFIG_PPC64) += nvram.o
+9 -8
arch/powerpc/platforms/powermac/cpufreq.c arch/powerpc/platforms/powermac/cpufreq_32.c
··· 397 397 unsigned int relation) 398 398 { 399 399 unsigned int newstate = 0; 400 + int rc; 400 401 401 402 if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs, 402 403 target_freq, relation, &newstate)) 403 404 return -EINVAL; 404 405 405 - return do_set_cpu_speed(newstate, 1); 406 - } 406 + rc = do_set_cpu_speed(newstate, 1); 407 407 408 - unsigned int pmac_get_one_cpufreq(int i) 409 - { 410 - /* Supports only one CPU for now */ 411 - return (i == 0) ? cur_freq : 0; 408 + ppc_proc_freq = cur_freq * 1000ul; 409 + return rc; 412 410 } 413 411 414 412 static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) ··· 462 464 /* If we resume, first check if we have a get() function */ 463 465 if (get_speed_proc) 464 466 cur_freq = get_speed_proc(); 465 - else 467 + else) 466 468 cur_freq = 0; 467 469 468 470 /* We don't, hrm... we don't really know our speed here, best ··· 471 473 */ 472 474 do_set_cpu_speed(sleep_freq == low_freq ? 473 475 CPUFREQ_LOW : CPUFREQ_HIGH, 0); 476 + 477 + ppc_proc_freq = cur_freq * 1000ul; 474 478 475 479 no_schedule = 0; 476 480 return 0; ··· 547 547 */ 548 548 if (low_freq < 98000000) 549 549 low_freq = 101000000; 550 - 550 + 551 551 /* Convert those to CPU core clocks */ 552 552 low_freq = (low_freq * (*ratio)) / 2000; 553 553 hi_freq = (hi_freq * (*ratio)) / 2000; ··· 714 714 715 715 pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; 716 716 pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; 717 + ppc_proc_freq = cur_freq * 1000ul; 717 718 718 719 printk(KERN_INFO "Registering PowerMac CPU frequency driver\n"); 719 720 printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n",
+323
arch/powerpc/platforms/powermac/cpufreq_64.c
··· 1 + /* 2 + * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org> 3 + * and Markus Demleitner <msdemlei@cl.uni-heidelberg.de> 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License version 2 as 7 + * published by the Free Software Foundation. 8 + * 9 + * This driver adds basic cpufreq support for SMU & 970FX based G5 Macs, 10 + * that is iMac G5 and latest single CPU desktop. 11 + */ 12 + 13 + #include <linux/config.h> 14 + #include <linux/module.h> 15 + #include <linux/types.h> 16 + #include <linux/errno.h> 17 + #include <linux/kernel.h> 18 + #include <linux/delay.h> 19 + #include <linux/sched.h> 20 + #include <linux/slab.h> 21 + #include <linux/cpufreq.h> 22 + #include <linux/init.h> 23 + #include <linux/completion.h> 24 + #include <asm/prom.h> 25 + #include <asm/machdep.h> 26 + #include <asm/irq.h> 27 + #include <asm/sections.h> 28 + #include <asm/cputable.h> 29 + #include <asm/time.h> 30 + #include <asm/smu.h> 31 + 32 + #undef DEBUG 33 + 34 + #ifdef DEBUG 35 + #define DBG(fmt...) printk(fmt) 36 + #else 37 + #define DBG(fmt...) 38 + #endif 39 + 40 + /* see 970FX user manual */ 41 + 42 + #define SCOM_PCR 0x0aa001 /* PCR scom addr */ 43 + 44 + #define PCR_HILO_SELECT 0x80000000U /* 1 = PCR, 0 = PCRH */ 45 + #define PCR_SPEED_FULL 0x00000000U /* 1:1 speed value */ 46 + #define PCR_SPEED_HALF 0x00020000U /* 1:2 speed value */ 47 + #define PCR_SPEED_QUARTER 0x00040000U /* 1:4 speed value */ 48 + #define PCR_SPEED_MASK 0x000e0000U /* speed mask */ 49 + #define PCR_SPEED_SHIFT 17 50 + #define PCR_FREQ_REQ_VALID 0x00010000U /* freq request valid */ 51 + #define PCR_VOLT_REQ_VALID 0x00008000U /* volt request valid */ 52 + #define PCR_TARGET_TIME_MASK 0x00006000U /* target time */ 53 + #define PCR_STATLAT_MASK 0x00001f00U /* STATLAT value */ 54 + #define PCR_SNOOPLAT_MASK 0x000000f0U /* SNOOPLAT value */ 55 + #define PCR_SNOOPACC_MASK 0x0000000fU /* SNOOPACC value */ 56 + 57 + #define SCOM_PSR 0x408001 /* PSR scom addr */ 58 + /* warning: PSR is a 64 bits register */ 59 + #define PSR_CMD_RECEIVED 0x2000000000000000U /* command received */ 60 + #define PSR_CMD_COMPLETED 0x1000000000000000U /* command completed */ 61 + #define PSR_CUR_SPEED_MASK 0x0300000000000000U /* current speed */ 62 + #define PSR_CUR_SPEED_SHIFT (56) 63 + 64 + /* 65 + * The G5 only supports two frequencies (Quarter speed is not supported) 66 + */ 67 + #define CPUFREQ_HIGH 0 68 + #define CPUFREQ_LOW 1 69 + 70 + static struct cpufreq_frequency_table g5_cpu_freqs[] = { 71 + {CPUFREQ_HIGH, 0}, 72 + {CPUFREQ_LOW, 0}, 73 + {0, CPUFREQ_TABLE_END}, 74 + }; 75 + 76 + static struct freq_attr* g5_cpu_freqs_attr[] = { 77 + &cpufreq_freq_attr_scaling_available_freqs, 78 + NULL, 79 + }; 80 + 81 + /* Power mode data is an array of the 32 bits PCR values to use for 82 + * the various frequencies, retreived from the device-tree 83 + */ 84 + static u32 *g5_pmode_data; 85 + static int g5_pmode_max; 86 + static int g5_pmode_cur; 87 + 88 + static DECLARE_MUTEX(g5_switch_mutex); 89 + 90 + 91 + static struct smu_sdbp_fvt *g5_fvt_table; /* table of op. points */ 92 + static int g5_fvt_count; /* number of op. points */ 93 + static int g5_fvt_cur; /* current op. point */ 94 + 95 + /* ----------------- real hardware interface */ 96 + 97 + static void g5_switch_volt(int speed_mode) 98 + { 99 + struct smu_simple_cmd cmd; 100 + 101 + DECLARE_COMPLETION(comp); 102 + smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, smu_done_complete, 103 + &comp, 'V', 'S', 'L', 'E', 'W', 104 + 0xff, g5_fvt_cur+1, speed_mode); 105 + wait_for_completion(&comp); 106 + } 107 + 108 + static int g5_switch_freq(int speed_mode) 109 + { 110 + struct cpufreq_freqs freqs; 111 + int to; 112 + 113 + if (g5_pmode_cur == speed_mode) 114 + return 0; 115 + 116 + down(&g5_switch_mutex); 117 + 118 + freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency; 119 + freqs.new = g5_cpu_freqs[speed_mode].frequency; 120 + freqs.cpu = 0; 121 + 122 + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 123 + 124 + /* If frequency is going up, first ramp up the voltage */ 125 + if (speed_mode < g5_pmode_cur) 126 + g5_switch_volt(speed_mode); 127 + 128 + /* Clear PCR high */ 129 + scom970_write(SCOM_PCR, 0); 130 + /* Clear PCR low */ 131 + scom970_write(SCOM_PCR, PCR_HILO_SELECT | 0); 132 + /* Set PCR low */ 133 + scom970_write(SCOM_PCR, PCR_HILO_SELECT | 134 + g5_pmode_data[speed_mode]); 135 + 136 + /* Wait for completion */ 137 + for (to = 0; to < 10; to++) { 138 + unsigned long psr = scom970_read(SCOM_PSR); 139 + 140 + if ((psr & PSR_CMD_RECEIVED) == 0 && 141 + (((psr >> PSR_CUR_SPEED_SHIFT) ^ 142 + (g5_pmode_data[speed_mode] >> PCR_SPEED_SHIFT)) & 0x3) 143 + == 0) 144 + break; 145 + if (psr & PSR_CMD_COMPLETED) 146 + break; 147 + udelay(100); 148 + } 149 + 150 + /* If frequency is going down, last ramp the voltage */ 151 + if (speed_mode > g5_pmode_cur) 152 + g5_switch_volt(speed_mode); 153 + 154 + g5_pmode_cur = speed_mode; 155 + ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul; 156 + 157 + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 158 + 159 + up(&g5_switch_mutex); 160 + 161 + return 0; 162 + } 163 + 164 + static int g5_query_freq(void) 165 + { 166 + unsigned long psr = scom970_read(SCOM_PSR); 167 + int i; 168 + 169 + for (i = 0; i <= g5_pmode_max; i++) 170 + if ((((psr >> PSR_CUR_SPEED_SHIFT) ^ 171 + (g5_pmode_data[i] >> PCR_SPEED_SHIFT)) & 0x3) == 0) 172 + break; 173 + return i; 174 + } 175 + 176 + /* ----------------- cpufreq bookkeeping */ 177 + 178 + static int g5_cpufreq_verify(struct cpufreq_policy *policy) 179 + { 180 + return cpufreq_frequency_table_verify(policy, g5_cpu_freqs); 181 + } 182 + 183 + static int g5_cpufreq_target(struct cpufreq_policy *policy, 184 + unsigned int target_freq, unsigned int relation) 185 + { 186 + unsigned int newstate = 0; 187 + 188 + if (cpufreq_frequency_table_target(policy, g5_cpu_freqs, 189 + target_freq, relation, &newstate)) 190 + return -EINVAL; 191 + 192 + return g5_switch_freq(newstate); 193 + } 194 + 195 + static unsigned int g5_cpufreq_get_speed(unsigned int cpu) 196 + { 197 + return g5_cpu_freqs[g5_pmode_cur].frequency; 198 + } 199 + 200 + static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy) 201 + { 202 + if (policy->cpu != 0) 203 + return -ENODEV; 204 + 205 + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; 206 + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; 207 + policy->cur = g5_cpu_freqs[g5_query_freq()].frequency; 208 + cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu); 209 + 210 + return cpufreq_frequency_table_cpuinfo(policy, 211 + g5_cpu_freqs); 212 + } 213 + 214 + 215 + static struct cpufreq_driver g5_cpufreq_driver = { 216 + .name = "powermac", 217 + .owner = THIS_MODULE, 218 + .flags = CPUFREQ_CONST_LOOPS, 219 + .init = g5_cpufreq_cpu_init, 220 + .verify = g5_cpufreq_verify, 221 + .target = g5_cpufreq_target, 222 + .get = g5_cpufreq_get_speed, 223 + .attr = g5_cpu_freqs_attr, 224 + }; 225 + 226 + 227 + static int __init g5_cpufreq_init(void) 228 + { 229 + struct device_node *cpunode; 230 + unsigned int psize, ssize; 231 + struct smu_sdbp_header *shdr; 232 + unsigned long max_freq; 233 + u32 *valp; 234 + int rc = -ENODEV; 235 + 236 + /* Look for CPU and SMU nodes */ 237 + cpunode = of_find_node_by_type(NULL, "cpu"); 238 + if (!cpunode) { 239 + DBG("No CPU node !\n"); 240 + return -ENODEV; 241 + } 242 + 243 + /* Check 970FX for now */ 244 + valp = (u32 *)get_property(cpunode, "cpu-version", NULL); 245 + if (!valp) { 246 + DBG("No cpu-version property !\n"); 247 + goto bail_noprops; 248 + } 249 + if (((*valp) >> 16) != 0x3c) { 250 + DBG("Wrong CPU version: %08x\n", *valp); 251 + goto bail_noprops; 252 + } 253 + 254 + /* Look for the powertune data in the device-tree */ 255 + g5_pmode_data = (u32 *)get_property(cpunode, "power-mode-data",&psize); 256 + if (!g5_pmode_data) { 257 + DBG("No power-mode-data !\n"); 258 + goto bail_noprops; 259 + } 260 + g5_pmode_max = psize / sizeof(u32) - 1; 261 + 262 + /* Look for the FVT table */ 263 + shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL); 264 + if (!shdr) 265 + goto bail_noprops; 266 + g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1]; 267 + ssize = (shdr->len * sizeof(u32)) - sizeof(struct smu_sdbp_header); 268 + g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt); 269 + g5_fvt_cur = 0; 270 + 271 + /* Sanity checking */ 272 + if (g5_fvt_count < 1 || g5_pmode_max < 1) 273 + goto bail_noprops; 274 + 275 + /* 276 + * From what I see, clock-frequency is always the maximal frequency. 277 + * The current driver can not slew sysclk yet, so we really only deal 278 + * with powertune steps for now. We also only implement full freq and 279 + * half freq in this version. So far, I haven't yet seen a machine 280 + * supporting anything else. 281 + */ 282 + valp = (u32 *)get_property(cpunode, "clock-frequency", NULL); 283 + if (!valp) 284 + return -ENODEV; 285 + max_freq = (*valp)/1000; 286 + g5_cpu_freqs[0].frequency = max_freq; 287 + g5_cpu_freqs[1].frequency = max_freq/2; 288 + 289 + /* Check current frequency */ 290 + g5_pmode_cur = g5_query_freq(); 291 + if (g5_pmode_cur > 1) 292 + /* We don't support anything but 1:1 and 1:2, fixup ... */ 293 + g5_pmode_cur = 1; 294 + 295 + /* Force apply current frequency to make sure everything is in 296 + * sync (voltage is right for example). Firmware may leave us with 297 + * a strange setting ... 298 + */ 299 + g5_switch_freq(g5_pmode_cur); 300 + 301 + printk(KERN_INFO "Registering G5 CPU frequency driver\n"); 302 + printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", 303 + g5_cpu_freqs[1].frequency/1000, 304 + g5_cpu_freqs[0].frequency/1000, 305 + g5_cpu_freqs[g5_pmode_cur].frequency/1000); 306 + 307 + rc = cpufreq_register_driver(&g5_cpufreq_driver); 308 + 309 + /* We keep the CPU node on hold... hopefully, Apple G5 don't have 310 + * hotplug CPU with a dynamic device-tree ... 311 + */ 312 + return rc; 313 + 314 + bail_noprops: 315 + of_node_put(cpunode); 316 + 317 + return rc; 318 + } 319 + 320 + module_init(g5_cpufreq_init); 321 + 322 + 323 + MODULE_LICENSE("GPL");
-13
arch/powerpc/platforms/powermac/setup.c
··· 193 193 pmac_newworld ? "NewWorld" : "OldWorld"); 194 194 } 195 195 196 - static void pmac_show_percpuinfo(struct seq_file *m, int i) 197 - { 198 - #ifdef CONFIG_CPU_FREQ_PMAC 199 - extern unsigned int pmac_get_one_cpufreq(int i); 200 - unsigned int freq = pmac_get_one_cpufreq(i); 201 - if (freq != 0) { 202 - seq_printf(m, "clock\t\t: %dMHz\n", freq/1000); 203 - return; 204 - } 205 - #endif /* CONFIG_CPU_FREQ_PMAC */ 206 - } 207 - 208 196 #ifndef CONFIG_ADB_CUDA 209 197 int find_via_cuda(void) 210 198 { ··· 755 767 .setup_arch = pmac_setup_arch, 756 768 .init_early = pmac_init_early, 757 769 .show_cpuinfo = pmac_show_cpuinfo, 758 - .show_percpuinfo = pmac_show_percpuinfo, 759 770 .init_IRQ = pmac_pic_init, 760 771 .get_irq = mpic_get_irq, /* changed later */ 761 772 .pcibios_fixup = pmac_pcibios_fixup,
+10
arch/ppc64/Kconfig
··· 173 173 support. As of this writing the exact hardware interface is 174 174 strongly in flux, so no good recommendation can be made. 175 175 176 + source "drivers/cpufreq/Kconfig" 177 + 178 + config CPU_FREQ_PMAC64 179 + bool "Support for some Apple G5s" 180 + depends on CPU_FREQ && PMAC_SMU && PPC64 181 + select CPU_FREQ_TABLE 182 + help 183 + This adds support for frequency switching on Apple iMac G5, 184 + and some of the more recent desktop G5 machines as well. 185 + 176 186 config IBMVIO 177 187 depends on PPC_PSERIES || PPC_ISERIES 178 188 bool
+71 -1
arch/ppc64/kernel/misc.S
··· 560 560 isync 561 561 blr 562 562 563 - /* 563 + /* 564 564 * Do an IO access in real mode 565 565 */ 566 566 _GLOBAL(real_writeb) ··· 591 591 isync 592 592 blr 593 593 #endif /* defined(CONFIG_PPC_PMAC) || defined(CONFIG_PPC_MAPLE) */ 594 + 595 + /* 596 + * SCOM access functions for 970 (FX only for now) 597 + * 598 + * unsigned long scom970_read(unsigned int address); 599 + * void scom970_write(unsigned int address, unsigned long value); 600 + * 601 + * The address passed in is the 24 bits register address. This code 602 + * is 970 specific and will not check the status bits, so you should 603 + * know what you are doing. 604 + */ 605 + _GLOBAL(scom970_read) 606 + /* interrupts off */ 607 + mfmsr r4 608 + ori r0,r4,MSR_EE 609 + xori r0,r0,MSR_EE 610 + mtmsrd r0,1 611 + 612 + /* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits 613 + * (including parity). On current CPUs they must be 0'd, 614 + * and finally or in RW bit 615 + */ 616 + rlwinm r3,r3,8,0,15 617 + ori r3,r3,0x8000 618 + 619 + /* do the actual scom read */ 620 + sync 621 + mtspr SPRN_SCOMC,r3 622 + isync 623 + mfspr r3,SPRN_SCOMD 624 + isync 625 + mfspr r0,SPRN_SCOMC 626 + isync 627 + 628 + /* XXX: fixup result on some buggy 970's (ouch ! we lost a bit, bah 629 + * that's the best we can do). Not implemented yet as we don't use 630 + * the scom on any of the bogus CPUs yet, but may have to be done 631 + * ultimately 632 + */ 633 + 634 + /* restore interrupts */ 635 + mtmsrd r4,1 636 + blr 637 + 638 + 639 + _GLOBAL(scom970_write) 640 + /* interrupts off */ 641 + mfmsr r5 642 + ori r0,r5,MSR_EE 643 + xori r0,r0,MSR_EE 644 + mtmsrd r0,1 645 + 646 + /* rotate 24 bits SCOM address 8 bits left and mask out it's low 8 bits 647 + * (including parity). On current CPUs they must be 0'd. 648 + */ 649 + 650 + rlwinm r3,r3,8,0,15 651 + 652 + sync 653 + mtspr SPRN_SCOMD,r4 /* write data */ 654 + isync 655 + mtspr SPRN_SCOMC,r3 /* write command */ 656 + isync 657 + mfspr 3,SPRN_SCOMC 658 + isync 659 + 660 + /* restore interrupts */ 661 + mtmsrd r5,1 662 + blr 663 + 594 664 595 665 /* 596 666 * Create a kernel thread
+12
drivers/macintosh/smu.c
··· 845 845 return 0; 846 846 } 847 847 848 + struct smu_sdbp_header *smu_get_sdb_partition(int id, unsigned int *size) 849 + { 850 + char pname[32]; 851 + 852 + if (!smu) 853 + return NULL; 854 + 855 + sprintf(pname, "sdb-partition-%02x", id); 856 + return (struct smu_sdbp_header *)get_property(smu->of_node, 857 + pname, size); 858 + } 859 + EXPORT_SYMBOL(smu_get_sdb_partition); 848 860 849 861 850 862 /*
+8 -1
include/asm-powerpc/reg.h
··· 396 396 #define SPRN_VRSAVE 0x100 /* Vector Register Save Register */ 397 397 #define SPRN_XER 0x001 /* Fixed Point Exception Register */ 398 398 399 + #define SPRN_SCOMC 0x114 /* SCOM Access Control */ 400 + #define SPRN_SCOMD 0x115 /* SCOM Access DATA */ 401 + 399 402 /* Performance monitor SPRs */ 400 403 #ifdef CONFIG_PPC64 401 404 #define SPRN_MMCR0 795 ··· 597 594 mtspr(SPRN_CTRLT, ctrl); 598 595 } 599 596 } 600 - #endif 597 + 598 + extern unsigned long scom970_read(unsigned int address); 599 + extern void scom970_write(unsigned int address, unsigned long value); 600 + 601 + #endif /* CONFIG_PPC64 */ 601 602 602 603 #define __get_SP() ({unsigned long sp; \ 603 604 asm volatile("mr %0,1": "=r" (sp)); sp;})
+59 -1
include/asm-powerpc/smu.h
··· 144 144 * - lenght 8 ("VSLEWxyz") has 3 additional bytes appended, and is 145 145 * used to set the voltage slewing point. The SMU replies with "DONE" 146 146 * I yet have to figure out their exact meaning of those 3 bytes in 147 - * both cases. 147 + * both cases. They seem to be: 148 + * x = processor mask 149 + * y = op. point index 150 + * z = processor freq. step index 151 + * I haven't yet decyphered result codes 148 152 * 149 153 */ 150 154 #define SMU_CMD_POWER_COMMAND 0xaa ··· 336 332 337 333 338 334 #endif /* __KERNEL__ */ 335 + 336 + 337 + /* 338 + * - SMU "sdb" partitions informations - 339 + */ 340 + 341 + 342 + /* 343 + * Partition header format 344 + */ 345 + struct smu_sdbp_header { 346 + __u8 id; 347 + __u8 len; 348 + __u8 version; 349 + __u8 flags; 350 + }; 351 + 352 + /* 353 + * 32 bits integers are usually encoded with 2x16 bits swapped, 354 + * this demangles them 355 + */ 356 + #define SMU_U32_MIX(x) ((((x) << 16) & 0xffff0000u) | (((x) >> 16) & 0xffffu)) 357 + 358 + /* This is the definition of the SMU sdb-partition-0x12 table (called 359 + * CPU F/V/T operating points in Darwin). The definition for all those 360 + * SMU tables should be moved to some separate file 361 + */ 362 + #define SMU_SDB_FVT_ID 0x12 363 + 364 + struct smu_sdbp_fvt { 365 + __u32 sysclk; /* Base SysClk frequency in Hz for 366 + * this operating point 367 + */ 368 + __u8 pad; 369 + __u8 maxtemp; /* Max temp. supported by this 370 + * operating point 371 + */ 372 + 373 + __u16 volts[3]; /* CPU core voltage for the 3 374 + * PowerTune modes, a mode with 375 + * 0V = not supported. 376 + */ 377 + }; 378 + 379 + #ifdef __KERNEL__ 380 + /* 381 + * This returns the pointer to an SMU "sdb" partition data or NULL 382 + * if not found. The data format is described below 383 + */ 384 + extern struct smu_sdbp_header *smu_get_sdb_partition(int id, 385 + unsigned int *size); 386 + 387 + #endif /* __KERNEL__ */ 388 + 339 389 340 390 /* 341 391 * - Userland interface -