18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org> 48c2ecf20Sopenharmony_ci * Copyright (C) 2004 John Steele Scott <toojays@toojays.net> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * TODO: Need a big cleanup here. Basically, we need to have different 78c2ecf20Sopenharmony_ci * cpufreq_driver structures for the different type of HW instead of the 88c2ecf20Sopenharmony_ci * current mess. We also need to better deal with the detection of the 98c2ecf20Sopenharmony_ci * type of machine. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/sched.h> 208c2ecf20Sopenharmony_ci#include <linux/adb.h> 218c2ecf20Sopenharmony_ci#include <linux/pmu.h> 228c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/device.h> 258c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 268c2ecf20Sopenharmony_ci#include <linux/of_device.h> 278c2ecf20Sopenharmony_ci#include <asm/prom.h> 288c2ecf20Sopenharmony_ci#include <asm/machdep.h> 298c2ecf20Sopenharmony_ci#include <asm/irq.h> 308c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h> 318c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 328c2ecf20Sopenharmony_ci#include <asm/sections.h> 338c2ecf20Sopenharmony_ci#include <asm/cputable.h> 348c2ecf20Sopenharmony_ci#include <asm/time.h> 358c2ecf20Sopenharmony_ci#include <asm/mpic.h> 368c2ecf20Sopenharmony_ci#include <asm/keylargo.h> 378c2ecf20Sopenharmony_ci#include <asm/switch_to.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* WARNING !!! This will cause calibrate_delay() to be called, 408c2ecf20Sopenharmony_ci * but this is an __init function ! So you MUST go edit 418c2ecf20Sopenharmony_ci * init/main.c to make it non-init before enabling DEBUG_FREQ 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci#undef DEBUG_FREQ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciextern void low_choose_7447a_dfs(int dfs); 468c2ecf20Sopenharmony_ciextern void low_choose_750fx_pll(int pll); 478c2ecf20Sopenharmony_ciextern void low_sleep_handler(void); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* 508c2ecf20Sopenharmony_ci * Currently, PowerMac cpufreq supports only high & low frequencies 518c2ecf20Sopenharmony_ci * that are set by the firmware 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistatic unsigned int low_freq; 548c2ecf20Sopenharmony_cistatic unsigned int hi_freq; 558c2ecf20Sopenharmony_cistatic unsigned int cur_freq; 568c2ecf20Sopenharmony_cistatic unsigned int sleep_freq; 578c2ecf20Sopenharmony_cistatic unsigned long transition_latency; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * Different models uses different mechanisms to switch the frequency 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic int (*set_speed_proc)(int low_speed); 638c2ecf20Sopenharmony_cistatic unsigned int (*get_speed_proc)(void); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * Some definitions used by the various speedprocs 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic u32 voltage_gpio; 698c2ecf20Sopenharmony_cistatic u32 frequency_gpio; 708c2ecf20Sopenharmony_cistatic u32 slew_done_gpio; 718c2ecf20Sopenharmony_cistatic int no_schedule; 728c2ecf20Sopenharmony_cistatic int has_cpu_l2lve; 738c2ecf20Sopenharmony_cistatic int is_pmu_based; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* There are only two frequency states for each processor. Values 768c2ecf20Sopenharmony_ci * are in kHz for the time being. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci#define CPUFREQ_HIGH 0 798c2ecf20Sopenharmony_ci#define CPUFREQ_LOW 1 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic struct cpufreq_frequency_table pmac_cpu_freqs[] = { 828c2ecf20Sopenharmony_ci {0, CPUFREQ_HIGH, 0}, 838c2ecf20Sopenharmony_ci {0, CPUFREQ_LOW, 0}, 848c2ecf20Sopenharmony_ci {0, 0, CPUFREQ_TABLE_END}, 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic inline void local_delay(unsigned long ms) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci if (no_schedule) 908c2ecf20Sopenharmony_ci mdelay(ms); 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci msleep(ms); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#ifdef DEBUG_FREQ 968c2ecf20Sopenharmony_cistatic inline void debug_calc_bogomips(void) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci /* This will cause a recalc of bogomips and display the 998c2ecf20Sopenharmony_ci * result. We backup/restore the value to avoid affecting the 1008c2ecf20Sopenharmony_ci * core cpufreq framework's own calculation. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci unsigned long save_lpj = loops_per_jiffy; 1038c2ecf20Sopenharmony_ci calibrate_delay(); 1048c2ecf20Sopenharmony_ci loops_per_jiffy = save_lpj; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci#endif /* DEBUG_FREQ */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* Switch CPU speed under 750FX CPU control 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic int cpu_750fx_cpu_speed(int low_speed) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci u32 hid2; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (low_speed == 0) { 1158c2ecf20Sopenharmony_ci /* ramping up, set voltage first */ 1168c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); 1178c2ecf20Sopenharmony_ci /* Make sure we sleep for at least 1ms */ 1188c2ecf20Sopenharmony_ci local_delay(10); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* tweak L2 for high voltage */ 1218c2ecf20Sopenharmony_ci if (has_cpu_l2lve) { 1228c2ecf20Sopenharmony_ci hid2 = mfspr(SPRN_HID2); 1238c2ecf20Sopenharmony_ci hid2 &= ~0x2000; 1248c2ecf20Sopenharmony_ci mtspr(SPRN_HID2, hid2); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_32 1288c2ecf20Sopenharmony_ci low_choose_750fx_pll(low_speed); 1298c2ecf20Sopenharmony_ci#endif 1308c2ecf20Sopenharmony_ci if (low_speed == 1) { 1318c2ecf20Sopenharmony_ci /* tweak L2 for low voltage */ 1328c2ecf20Sopenharmony_ci if (has_cpu_l2lve) { 1338c2ecf20Sopenharmony_ci hid2 = mfspr(SPRN_HID2); 1348c2ecf20Sopenharmony_ci hid2 |= 0x2000; 1358c2ecf20Sopenharmony_ci mtspr(SPRN_HID2, hid2); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* ramping down, set voltage last */ 1398c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); 1408c2ecf20Sopenharmony_ci local_delay(10); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic unsigned int cpu_750fx_get_cpu_speed(void) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci if (mfspr(SPRN_HID1) & HID1_PS) 1498c2ecf20Sopenharmony_ci return low_freq; 1508c2ecf20Sopenharmony_ci else 1518c2ecf20Sopenharmony_ci return hi_freq; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* Switch CPU speed using DFS */ 1558c2ecf20Sopenharmony_cistatic int dfs_set_cpu_speed(int low_speed) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci if (low_speed == 0) { 1588c2ecf20Sopenharmony_ci /* ramping up, set voltage first */ 1598c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); 1608c2ecf20Sopenharmony_ci /* Make sure we sleep for at least 1ms */ 1618c2ecf20Sopenharmony_ci local_delay(1); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* set frequency */ 1658c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_32 1668c2ecf20Sopenharmony_ci low_choose_7447a_dfs(low_speed); 1678c2ecf20Sopenharmony_ci#endif 1688c2ecf20Sopenharmony_ci udelay(100); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (low_speed == 1) { 1718c2ecf20Sopenharmony_ci /* ramping down, set voltage last */ 1728c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); 1738c2ecf20Sopenharmony_ci local_delay(1); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic unsigned int dfs_get_cpu_speed(void) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci if (mfspr(SPRN_HID1) & HID1_DFS) 1828c2ecf20Sopenharmony_ci return low_freq; 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci return hi_freq; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* Switch CPU speed using slewing GPIOs 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic int gpios_set_cpu_speed(int low_speed) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci int gpio, timeout = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* If ramping up, set voltage first */ 1958c2ecf20Sopenharmony_ci if (low_speed == 0) { 1968c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); 1978c2ecf20Sopenharmony_ci /* Delay is way too big but it's ok, we schedule */ 1988c2ecf20Sopenharmony_ci local_delay(10); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Set frequency */ 2028c2ecf20Sopenharmony_ci gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); 2038c2ecf20Sopenharmony_ci if (low_speed == ((gpio & 0x01) == 0)) 2048c2ecf20Sopenharmony_ci goto skip; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, 2078c2ecf20Sopenharmony_ci low_speed ? 0x04 : 0x05); 2088c2ecf20Sopenharmony_ci udelay(200); 2098c2ecf20Sopenharmony_ci do { 2108c2ecf20Sopenharmony_ci if (++timeout > 100) 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci local_delay(1); 2138c2ecf20Sopenharmony_ci gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0); 2148c2ecf20Sopenharmony_ci } while((gpio & 0x02) == 0); 2158c2ecf20Sopenharmony_ci skip: 2168c2ecf20Sopenharmony_ci /* If ramping down, set voltage last */ 2178c2ecf20Sopenharmony_ci if (low_speed == 1) { 2188c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); 2198c2ecf20Sopenharmony_ci /* Delay is way too big but it's ok, we schedule */ 2208c2ecf20Sopenharmony_ci local_delay(10); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#ifdef DEBUG_FREQ 2248c2ecf20Sopenharmony_ci debug_calc_bogomips(); 2258c2ecf20Sopenharmony_ci#endif 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* Switch CPU speed under PMU control 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_cistatic int pmu_set_cpu_speed(int low_speed) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct adb_request req; 2358c2ecf20Sopenharmony_ci unsigned long save_l2cr; 2368c2ecf20Sopenharmony_ci unsigned long save_l3cr; 2378c2ecf20Sopenharmony_ci unsigned int pic_prio; 2388c2ecf20Sopenharmony_ci unsigned long flags; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci preempt_disable(); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci#ifdef DEBUG_FREQ 2438c2ecf20Sopenharmony_ci printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); 2448c2ecf20Sopenharmony_ci#endif 2458c2ecf20Sopenharmony_ci pmu_suspend(); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Disable all interrupt sources on openpic */ 2488c2ecf20Sopenharmony_ci pic_prio = mpic_cpu_get_priority(); 2498c2ecf20Sopenharmony_ci mpic_cpu_set_priority(0xf); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Make sure the decrementer won't interrupt us */ 2528c2ecf20Sopenharmony_ci asm volatile("mtdec %0" : : "r" (0x7fffffff)); 2538c2ecf20Sopenharmony_ci /* Make sure any pending DEC interrupt occurring while we did 2548c2ecf20Sopenharmony_ci * the above didn't re-enable the DEC */ 2558c2ecf20Sopenharmony_ci mb(); 2568c2ecf20Sopenharmony_ci asm volatile("mtdec %0" : : "r" (0x7fffffff)); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* We can now disable MSR_EE */ 2598c2ecf20Sopenharmony_ci local_irq_save(flags); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* Giveup the FPU & vec */ 2628c2ecf20Sopenharmony_ci enable_kernel_fp(); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci#ifdef CONFIG_ALTIVEC 2658c2ecf20Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ALTIVEC)) 2668c2ecf20Sopenharmony_ci enable_kernel_altivec(); 2678c2ecf20Sopenharmony_ci#endif /* CONFIG_ALTIVEC */ 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Save & disable L2 and L3 caches */ 2708c2ecf20Sopenharmony_ci save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ 2718c2ecf20Sopenharmony_ci save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Send the new speed command. My assumption is that this command 2748c2ecf20Sopenharmony_ci * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed); 2778c2ecf20Sopenharmony_ci while (!req.complete) 2788c2ecf20Sopenharmony_ci pmu_poll(); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Prepare the northbridge for the speed transition */ 2818c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Call low level code to backup CPU state and recover from 2848c2ecf20Sopenharmony_ci * hardware reset 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci low_sleep_handler(); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Restore the northbridge */ 2898c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Restore L2 cache */ 2928c2ecf20Sopenharmony_ci if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) 2938c2ecf20Sopenharmony_ci _set_L2CR(save_l2cr); 2948c2ecf20Sopenharmony_ci /* Restore L3 cache */ 2958c2ecf20Sopenharmony_ci if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) 2968c2ecf20Sopenharmony_ci _set_L3CR(save_l3cr); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Restore userland MMU context */ 2998c2ecf20Sopenharmony_ci switch_mmu_context(NULL, current->active_mm, NULL); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci#ifdef DEBUG_FREQ 3028c2ecf20Sopenharmony_ci printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); 3038c2ecf20Sopenharmony_ci#endif 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Restore low level PMU operations */ 3068c2ecf20Sopenharmony_ci pmu_unlock(); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * Restore decrementer; we'll take a decrementer interrupt 3108c2ecf20Sopenharmony_ci * as soon as interrupts are re-enabled and the generic 3118c2ecf20Sopenharmony_ci * clockevents code will reprogram it with the right value. 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci set_dec(1); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Restore interrupts */ 3168c2ecf20Sopenharmony_ci mpic_cpu_set_priority(pic_prio); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Let interrupts flow again ... */ 3198c2ecf20Sopenharmony_ci local_irq_restore(flags); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci#ifdef DEBUG_FREQ 3228c2ecf20Sopenharmony_ci debug_calc_bogomips(); 3238c2ecf20Sopenharmony_ci#endif 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci pmu_resume(); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci preempt_enable(); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int do_set_cpu_speed(struct cpufreq_policy *policy, int speed_mode) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci unsigned long l3cr; 3358c2ecf20Sopenharmony_ci static unsigned long prev_l3cr; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (speed_mode == CPUFREQ_LOW && 3388c2ecf20Sopenharmony_ci cpu_has_feature(CPU_FTR_L3CR)) { 3398c2ecf20Sopenharmony_ci l3cr = _get_L3CR(); 3408c2ecf20Sopenharmony_ci if (l3cr & L3CR_L3E) { 3418c2ecf20Sopenharmony_ci prev_l3cr = l3cr; 3428c2ecf20Sopenharmony_ci _set_L3CR(0); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci set_speed_proc(speed_mode == CPUFREQ_LOW); 3468c2ecf20Sopenharmony_ci if (speed_mode == CPUFREQ_HIGH && 3478c2ecf20Sopenharmony_ci cpu_has_feature(CPU_FTR_L3CR)) { 3488c2ecf20Sopenharmony_ci l3cr = _get_L3CR(); 3498c2ecf20Sopenharmony_ci if ((prev_l3cr & L3CR_L3E) && l3cr != prev_l3cr) 3508c2ecf20Sopenharmony_ci _set_L3CR(prev_l3cr); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci cur_freq = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic unsigned int pmac_cpufreq_get_speed(unsigned int cpu) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci return cur_freq; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int pmac_cpufreq_target( struct cpufreq_policy *policy, 3638c2ecf20Sopenharmony_ci unsigned int index) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci int rc; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci rc = do_set_cpu_speed(policy, index); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ppc_proc_freq = cur_freq * 1000ul; 3708c2ecf20Sopenharmony_ci return rc; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci cpufreq_generic_init(policy, pmac_cpu_freqs, transition_latency); 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic u32 read_gpio(struct device_node *np) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci const u32 *reg = of_get_property(np, "reg", NULL); 3828c2ecf20Sopenharmony_ci u32 offset; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (reg == NULL) 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci /* That works for all keylargos but shall be fixed properly 3878c2ecf20Sopenharmony_ci * some day... The problem is that it seems we can't rely 3888c2ecf20Sopenharmony_ci * on the "reg" property of the GPIO nodes, they are either 3898c2ecf20Sopenharmony_ci * relative to the base of KeyLargo or to the base of the 3908c2ecf20Sopenharmony_ci * GPIO space, and the device-tree doesn't help. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci offset = *reg; 3938c2ecf20Sopenharmony_ci if (offset < KEYLARGO_GPIO_LEVELS0) 3948c2ecf20Sopenharmony_ci offset += KEYLARGO_GPIO_LEVELS0; 3958c2ecf20Sopenharmony_ci return offset; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic int pmac_cpufreq_suspend(struct cpufreq_policy *policy) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci /* Ok, this could be made a bit smarter, but let's be robust for now. We 4018c2ecf20Sopenharmony_ci * always force a speed change to high speed before sleep, to make sure 4028c2ecf20Sopenharmony_ci * we have appropriate voltage and/or bus speed for the wakeup process, 4038c2ecf20Sopenharmony_ci * and to make sure our loops_per_jiffies are "good enough", that is will 4048c2ecf20Sopenharmony_ci * not cause too short delays if we sleep in low speed and wake in high 4058c2ecf20Sopenharmony_ci * speed.. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci no_schedule = 1; 4088c2ecf20Sopenharmony_ci sleep_freq = cur_freq; 4098c2ecf20Sopenharmony_ci if (cur_freq == low_freq && !is_pmu_based) 4108c2ecf20Sopenharmony_ci do_set_cpu_speed(policy, CPUFREQ_HIGH); 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int pmac_cpufreq_resume(struct cpufreq_policy *policy) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci /* If we resume, first check if we have a get() function */ 4178c2ecf20Sopenharmony_ci if (get_speed_proc) 4188c2ecf20Sopenharmony_ci cur_freq = get_speed_proc(); 4198c2ecf20Sopenharmony_ci else 4208c2ecf20Sopenharmony_ci cur_freq = 0; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* We don't, hrm... we don't really know our speed here, best 4238c2ecf20Sopenharmony_ci * is that we force a switch to whatever it was, which is 4248c2ecf20Sopenharmony_ci * probably high speed due to our suspend() routine 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci do_set_cpu_speed(policy, sleep_freq == low_freq ? 4278c2ecf20Sopenharmony_ci CPUFREQ_LOW : CPUFREQ_HIGH); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci ppc_proc_freq = cur_freq * 1000ul; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci no_schedule = 0; 4328c2ecf20Sopenharmony_ci return 0; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic struct cpufreq_driver pmac_cpufreq_driver = { 4368c2ecf20Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 4378c2ecf20Sopenharmony_ci .target_index = pmac_cpufreq_target, 4388c2ecf20Sopenharmony_ci .get = pmac_cpufreq_get_speed, 4398c2ecf20Sopenharmony_ci .init = pmac_cpufreq_cpu_init, 4408c2ecf20Sopenharmony_ci .suspend = pmac_cpufreq_suspend, 4418c2ecf20Sopenharmony_ci .resume = pmac_cpufreq_resume, 4428c2ecf20Sopenharmony_ci .flags = CPUFREQ_PM_NO_WARN | 4438c2ecf20Sopenharmony_ci CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, 4448c2ecf20Sopenharmony_ci .attr = cpufreq_generic_attr, 4458c2ecf20Sopenharmony_ci .name = "powermac", 4468c2ecf20Sopenharmony_ci}; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct device_node *volt_gpio_np = of_find_node_by_name(NULL, 4528c2ecf20Sopenharmony_ci "voltage-gpio"); 4538c2ecf20Sopenharmony_ci struct device_node *freq_gpio_np = of_find_node_by_name(NULL, 4548c2ecf20Sopenharmony_ci "frequency-gpio"); 4558c2ecf20Sopenharmony_ci struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL, 4568c2ecf20Sopenharmony_ci "slewing-done"); 4578c2ecf20Sopenharmony_ci const u32 *value; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * Check to see if it's GPIO driven or PMU only 4618c2ecf20Sopenharmony_ci * 4628c2ecf20Sopenharmony_ci * The way we extract the GPIO address is slightly hackish, but it 4638c2ecf20Sopenharmony_ci * works well enough for now. We need to abstract the whole GPIO 4648c2ecf20Sopenharmony_ci * stuff sooner or later anyway 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (volt_gpio_np) 4688c2ecf20Sopenharmony_ci voltage_gpio = read_gpio(volt_gpio_np); 4698c2ecf20Sopenharmony_ci if (freq_gpio_np) 4708c2ecf20Sopenharmony_ci frequency_gpio = read_gpio(freq_gpio_np); 4718c2ecf20Sopenharmony_ci if (slew_done_gpio_np) 4728c2ecf20Sopenharmony_ci slew_done_gpio = read_gpio(slew_done_gpio_np); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci of_node_put(volt_gpio_np); 4758c2ecf20Sopenharmony_ci of_node_put(freq_gpio_np); 4768c2ecf20Sopenharmony_ci of_node_put(slew_done_gpio_np); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* If we use the frequency GPIOs, calculate the min/max speeds based 4798c2ecf20Sopenharmony_ci * on the bus frequencies 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci if (frequency_gpio && slew_done_gpio) { 4828c2ecf20Sopenharmony_ci int lenp, rc; 4838c2ecf20Sopenharmony_ci const u32 *freqs, *ratio; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci freqs = of_get_property(cpunode, "bus-frequencies", &lenp); 4868c2ecf20Sopenharmony_ci lenp /= sizeof(u32); 4878c2ecf20Sopenharmony_ci if (freqs == NULL || lenp != 2) { 4888c2ecf20Sopenharmony_ci pr_err("bus-frequencies incorrect or missing\n"); 4898c2ecf20Sopenharmony_ci return 1; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci ratio = of_get_property(cpunode, "processor-to-bus-ratio*2", 4928c2ecf20Sopenharmony_ci NULL); 4938c2ecf20Sopenharmony_ci if (ratio == NULL) { 4948c2ecf20Sopenharmony_ci pr_err("processor-to-bus-ratio*2 missing\n"); 4958c2ecf20Sopenharmony_ci return 1; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Get the min/max bus frequencies */ 4998c2ecf20Sopenharmony_ci low_freq = min(freqs[0], freqs[1]); 5008c2ecf20Sopenharmony_ci hi_freq = max(freqs[0], freqs[1]); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Grrrr.. It _seems_ that the device-tree is lying on the low bus 5038c2ecf20Sopenharmony_ci * frequency, it claims it to be around 84Mhz on some models while 5048c2ecf20Sopenharmony_ci * it appears to be approx. 101Mhz on all. Let's hack around here... 5058c2ecf20Sopenharmony_ci * fortunately, we don't need to be too precise 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci if (low_freq < 98000000) 5088c2ecf20Sopenharmony_ci low_freq = 101000000; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* Convert those to CPU core clocks */ 5118c2ecf20Sopenharmony_ci low_freq = (low_freq * (*ratio)) / 2000; 5128c2ecf20Sopenharmony_ci hi_freq = (hi_freq * (*ratio)) / 2000; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Now we get the frequencies, we read the GPIO to see what is out current 5158c2ecf20Sopenharmony_ci * speed 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci rc = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); 5188c2ecf20Sopenharmony_ci cur_freq = (rc & 0x01) ? hi_freq : low_freq; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci set_speed_proc = gpios_set_cpu_speed; 5218c2ecf20Sopenharmony_ci return 1; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* If we use the PMU, look for the min & max frequencies in the 5258c2ecf20Sopenharmony_ci * device-tree 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci value = of_get_property(cpunode, "min-clock-frequency", NULL); 5288c2ecf20Sopenharmony_ci if (!value) 5298c2ecf20Sopenharmony_ci return 1; 5308c2ecf20Sopenharmony_ci low_freq = (*value) / 1000; 5318c2ecf20Sopenharmony_ci /* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree 5328c2ecf20Sopenharmony_ci * here */ 5338c2ecf20Sopenharmony_ci if (low_freq < 100000) 5348c2ecf20Sopenharmony_ci low_freq *= 10; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci value = of_get_property(cpunode, "max-clock-frequency", NULL); 5378c2ecf20Sopenharmony_ci if (!value) 5388c2ecf20Sopenharmony_ci return 1; 5398c2ecf20Sopenharmony_ci hi_freq = (*value) / 1000; 5408c2ecf20Sopenharmony_ci set_speed_proc = pmu_set_cpu_speed; 5418c2ecf20Sopenharmony_ci is_pmu_based = 1; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return 0; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic int pmac_cpufreq_init_7447A(struct device_node *cpunode) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct device_node *volt_gpio_np; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL) 5518c2ecf20Sopenharmony_ci return 1; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); 5548c2ecf20Sopenharmony_ci if (volt_gpio_np) 5558c2ecf20Sopenharmony_ci voltage_gpio = read_gpio(volt_gpio_np); 5568c2ecf20Sopenharmony_ci of_node_put(volt_gpio_np); 5578c2ecf20Sopenharmony_ci if (!voltage_gpio){ 5588c2ecf20Sopenharmony_ci pr_err("missing cpu-vcore-select gpio\n"); 5598c2ecf20Sopenharmony_ci return 1; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* OF only reports the high frequency */ 5638c2ecf20Sopenharmony_ci hi_freq = cur_freq; 5648c2ecf20Sopenharmony_ci low_freq = cur_freq/2; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Read actual frequency from CPU */ 5678c2ecf20Sopenharmony_ci cur_freq = dfs_get_cpu_speed(); 5688c2ecf20Sopenharmony_ci set_speed_proc = dfs_set_cpu_speed; 5698c2ecf20Sopenharmony_ci get_speed_proc = dfs_get_cpu_speed; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int pmac_cpufreq_init_750FX(struct device_node *cpunode) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct device_node *volt_gpio_np; 5778c2ecf20Sopenharmony_ci u32 pvr; 5788c2ecf20Sopenharmony_ci const u32 *value; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL) 5818c2ecf20Sopenharmony_ci return 1; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci hi_freq = cur_freq; 5848c2ecf20Sopenharmony_ci value = of_get_property(cpunode, "reduced-clock-frequency", NULL); 5858c2ecf20Sopenharmony_ci if (!value) 5868c2ecf20Sopenharmony_ci return 1; 5878c2ecf20Sopenharmony_ci low_freq = (*value) / 1000; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); 5908c2ecf20Sopenharmony_ci if (volt_gpio_np) 5918c2ecf20Sopenharmony_ci voltage_gpio = read_gpio(volt_gpio_np); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci of_node_put(volt_gpio_np); 5948c2ecf20Sopenharmony_ci pvr = mfspr(SPRN_PVR); 5958c2ecf20Sopenharmony_ci has_cpu_l2lve = !((pvr & 0xf00) == 0x100); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci set_speed_proc = cpu_750fx_cpu_speed; 5988c2ecf20Sopenharmony_ci get_speed_proc = cpu_750fx_get_cpu_speed; 5998c2ecf20Sopenharmony_ci cur_freq = cpu_750fx_get_cpu_speed(); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return 0; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/* Currently, we support the following machines: 6058c2ecf20Sopenharmony_ci * 6068c2ecf20Sopenharmony_ci * - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz) 6078c2ecf20Sopenharmony_ci * - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz) 6088c2ecf20Sopenharmony_ci * - Titanium PowerBook 400 (PMU based, 300Mhz & 400Mhz) 6098c2ecf20Sopenharmony_ci * - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz) 6108c2ecf20Sopenharmony_ci * - iBook2 500/600 (PMU based, 400Mhz & 500/600Mhz) 6118c2ecf20Sopenharmony_ci * - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage) 6128c2ecf20Sopenharmony_ci * - Recent MacRISC3 laptops 6138c2ecf20Sopenharmony_ci * - All new machines with 7447A CPUs 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_cistatic int __init pmac_cpufreq_setup(void) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct device_node *cpunode; 6188c2ecf20Sopenharmony_ci const u32 *value; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (strstr(boot_command_line, "nocpufreq")) 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* Get first CPU node */ 6248c2ecf20Sopenharmony_ci cpunode = of_cpu_device_node_get(0); 6258c2ecf20Sopenharmony_ci if (!cpunode) 6268c2ecf20Sopenharmony_ci goto out; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* Get current cpu clock freq */ 6298c2ecf20Sopenharmony_ci value = of_get_property(cpunode, "clock-frequency", NULL); 6308c2ecf20Sopenharmony_ci if (!value) 6318c2ecf20Sopenharmony_ci goto out; 6328c2ecf20Sopenharmony_ci cur_freq = (*value) / 1000; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* Check for 7447A based MacRISC3 */ 6358c2ecf20Sopenharmony_ci if (of_machine_is_compatible("MacRISC3") && 6368c2ecf20Sopenharmony_ci of_get_property(cpunode, "dynamic-power-step", NULL) && 6378c2ecf20Sopenharmony_ci PVR_VER(mfspr(SPRN_PVR)) == 0x8003) { 6388c2ecf20Sopenharmony_ci pmac_cpufreq_init_7447A(cpunode); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* Allow dynamic switching */ 6418c2ecf20Sopenharmony_ci transition_latency = 8000000; 6428c2ecf20Sopenharmony_ci pmac_cpufreq_driver.flags &= ~CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING; 6438c2ecf20Sopenharmony_ci /* Check for other MacRISC3 machines */ 6448c2ecf20Sopenharmony_ci } else if (of_machine_is_compatible("PowerBook3,4") || 6458c2ecf20Sopenharmony_ci of_machine_is_compatible("PowerBook3,5") || 6468c2ecf20Sopenharmony_ci of_machine_is_compatible("MacRISC3")) { 6478c2ecf20Sopenharmony_ci pmac_cpufreq_init_MacRISC3(cpunode); 6488c2ecf20Sopenharmony_ci /* Else check for iBook2 500/600 */ 6498c2ecf20Sopenharmony_ci } else if (of_machine_is_compatible("PowerBook4,1")) { 6508c2ecf20Sopenharmony_ci hi_freq = cur_freq; 6518c2ecf20Sopenharmony_ci low_freq = 400000; 6528c2ecf20Sopenharmony_ci set_speed_proc = pmu_set_cpu_speed; 6538c2ecf20Sopenharmony_ci is_pmu_based = 1; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci /* Else check for TiPb 550 */ 6568c2ecf20Sopenharmony_ci else if (of_machine_is_compatible("PowerBook3,3") && cur_freq == 550000) { 6578c2ecf20Sopenharmony_ci hi_freq = cur_freq; 6588c2ecf20Sopenharmony_ci low_freq = 500000; 6598c2ecf20Sopenharmony_ci set_speed_proc = pmu_set_cpu_speed; 6608c2ecf20Sopenharmony_ci is_pmu_based = 1; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci /* Else check for TiPb 400 & 500 */ 6638c2ecf20Sopenharmony_ci else if (of_machine_is_compatible("PowerBook3,2")) { 6648c2ecf20Sopenharmony_ci /* We only know about the 400 MHz and the 500Mhz model 6658c2ecf20Sopenharmony_ci * they both have 300 MHz as low frequency 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_ci if (cur_freq < 350000 || cur_freq > 550000) 6688c2ecf20Sopenharmony_ci goto out; 6698c2ecf20Sopenharmony_ci hi_freq = cur_freq; 6708c2ecf20Sopenharmony_ci low_freq = 300000; 6718c2ecf20Sopenharmony_ci set_speed_proc = pmu_set_cpu_speed; 6728c2ecf20Sopenharmony_ci is_pmu_based = 1; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci /* Else check for 750FX */ 6758c2ecf20Sopenharmony_ci else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000) 6768c2ecf20Sopenharmony_ci pmac_cpufreq_init_750FX(cpunode); 6778c2ecf20Sopenharmony_ciout: 6788c2ecf20Sopenharmony_ci of_node_put(cpunode); 6798c2ecf20Sopenharmony_ci if (set_speed_proc == NULL) 6808c2ecf20Sopenharmony_ci return -ENODEV; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; 6838c2ecf20Sopenharmony_ci pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; 6848c2ecf20Sopenharmony_ci ppc_proc_freq = cur_freq * 1000ul; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci pr_info("Registering PowerMac CPU frequency driver\n"); 6878c2ecf20Sopenharmony_ci pr_info("Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n", 6888c2ecf20Sopenharmony_ci low_freq/1000, hi_freq/1000, cur_freq/1000); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return cpufreq_register_driver(&pmac_cpufreq_driver); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cimodule_init(pmac_cpufreq_setup); 6948c2ecf20Sopenharmony_ci 695