162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org> 462306a36Sopenharmony_ci * Copyright (C) 2004 John Steele Scott <toojays@toojays.net> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * TODO: Need a big cleanup here. Basically, we need to have different 762306a36Sopenharmony_ci * cpufreq_driver structures for the different type of HW instead of the 862306a36Sopenharmony_ci * current mess. We also need to better deal with the detection of the 962306a36Sopenharmony_ci * type of machine. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> 2062306a36Sopenharmony_ci#include <linux/adb.h> 2162306a36Sopenharmony_ci#include <linux/pmu.h> 2262306a36Sopenharmony_ci#include <linux/cpufreq.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/device.h> 2562306a36Sopenharmony_ci#include <linux/hardirq.h> 2662306a36Sopenharmony_ci#include <linux/of.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <asm/machdep.h> 2962306a36Sopenharmony_ci#include <asm/irq.h> 3062306a36Sopenharmony_ci#include <asm/pmac_feature.h> 3162306a36Sopenharmony_ci#include <asm/mmu_context.h> 3262306a36Sopenharmony_ci#include <asm/sections.h> 3362306a36Sopenharmony_ci#include <asm/cputable.h> 3462306a36Sopenharmony_ci#include <asm/time.h> 3562306a36Sopenharmony_ci#include <asm/mpic.h> 3662306a36Sopenharmony_ci#include <asm/keylargo.h> 3762306a36Sopenharmony_ci#include <asm/switch_to.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* WARNING !!! This will cause calibrate_delay() to be called, 4062306a36Sopenharmony_ci * but this is an __init function ! So you MUST go edit 4162306a36Sopenharmony_ci * init/main.c to make it non-init before enabling DEBUG_FREQ 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci#undef DEBUG_FREQ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciextern void low_choose_7447a_dfs(int dfs); 4662306a36Sopenharmony_ciextern void low_choose_750fx_pll(int pll); 4762306a36Sopenharmony_ciextern void low_sleep_handler(void); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * Currently, PowerMac cpufreq supports only high & low frequencies 5162306a36Sopenharmony_ci * that are set by the firmware 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistatic unsigned int low_freq; 5462306a36Sopenharmony_cistatic unsigned int hi_freq; 5562306a36Sopenharmony_cistatic unsigned int cur_freq; 5662306a36Sopenharmony_cistatic unsigned int sleep_freq; 5762306a36Sopenharmony_cistatic unsigned long transition_latency; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * Different models uses different mechanisms to switch the frequency 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic int (*set_speed_proc)(int low_speed); 6362306a36Sopenharmony_cistatic unsigned int (*get_speed_proc)(void); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * Some definitions used by the various speedprocs 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistatic u32 voltage_gpio; 6962306a36Sopenharmony_cistatic u32 frequency_gpio; 7062306a36Sopenharmony_cistatic u32 slew_done_gpio; 7162306a36Sopenharmony_cistatic int no_schedule; 7262306a36Sopenharmony_cistatic int has_cpu_l2lve; 7362306a36Sopenharmony_cistatic int is_pmu_based; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* There are only two frequency states for each processor. Values 7662306a36Sopenharmony_ci * are in kHz for the time being. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci#define CPUFREQ_HIGH 0 7962306a36Sopenharmony_ci#define CPUFREQ_LOW 1 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic struct cpufreq_frequency_table pmac_cpu_freqs[] = { 8262306a36Sopenharmony_ci {0, CPUFREQ_HIGH, 0}, 8362306a36Sopenharmony_ci {0, CPUFREQ_LOW, 0}, 8462306a36Sopenharmony_ci {0, 0, CPUFREQ_TABLE_END}, 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline void local_delay(unsigned long ms) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci if (no_schedule) 9062306a36Sopenharmony_ci mdelay(ms); 9162306a36Sopenharmony_ci else 9262306a36Sopenharmony_ci msleep(ms); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#ifdef DEBUG_FREQ 9662306a36Sopenharmony_cistatic inline void debug_calc_bogomips(void) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci /* This will cause a recalc of bogomips and display the 9962306a36Sopenharmony_ci * result. We backup/restore the value to avoid affecting the 10062306a36Sopenharmony_ci * core cpufreq framework's own calculation. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci unsigned long save_lpj = loops_per_jiffy; 10362306a36Sopenharmony_ci calibrate_delay(); 10462306a36Sopenharmony_ci loops_per_jiffy = save_lpj; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci#endif /* DEBUG_FREQ */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Switch CPU speed under 750FX CPU control 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic int cpu_750fx_cpu_speed(int low_speed) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci u32 hid2; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (low_speed == 0) { 11562306a36Sopenharmony_ci /* ramping up, set voltage first */ 11662306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); 11762306a36Sopenharmony_ci /* Make sure we sleep for at least 1ms */ 11862306a36Sopenharmony_ci local_delay(10); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* tweak L2 for high voltage */ 12162306a36Sopenharmony_ci if (has_cpu_l2lve) { 12262306a36Sopenharmony_ci hid2 = mfspr(SPRN_HID2); 12362306a36Sopenharmony_ci hid2 &= ~0x2000; 12462306a36Sopenharmony_ci mtspr(SPRN_HID2, hid2); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_32 12862306a36Sopenharmony_ci low_choose_750fx_pll(low_speed); 12962306a36Sopenharmony_ci#endif 13062306a36Sopenharmony_ci if (low_speed == 1) { 13162306a36Sopenharmony_ci /* tweak L2 for low voltage */ 13262306a36Sopenharmony_ci if (has_cpu_l2lve) { 13362306a36Sopenharmony_ci hid2 = mfspr(SPRN_HID2); 13462306a36Sopenharmony_ci hid2 |= 0x2000; 13562306a36Sopenharmony_ci mtspr(SPRN_HID2, hid2); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* ramping down, set voltage last */ 13962306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); 14062306a36Sopenharmony_ci local_delay(10); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic unsigned int cpu_750fx_get_cpu_speed(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci if (mfspr(SPRN_HID1) & HID1_PS) 14962306a36Sopenharmony_ci return low_freq; 15062306a36Sopenharmony_ci else 15162306a36Sopenharmony_ci return hi_freq; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* Switch CPU speed using DFS */ 15562306a36Sopenharmony_cistatic int dfs_set_cpu_speed(int low_speed) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (low_speed == 0) { 15862306a36Sopenharmony_ci /* ramping up, set voltage first */ 15962306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); 16062306a36Sopenharmony_ci /* Make sure we sleep for at least 1ms */ 16162306a36Sopenharmony_ci local_delay(1); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* set frequency */ 16562306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_32 16662306a36Sopenharmony_ci low_choose_7447a_dfs(low_speed); 16762306a36Sopenharmony_ci#endif 16862306a36Sopenharmony_ci udelay(100); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (low_speed == 1) { 17162306a36Sopenharmony_ci /* ramping down, set voltage last */ 17262306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); 17362306a36Sopenharmony_ci local_delay(1); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic unsigned int dfs_get_cpu_speed(void) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci if (mfspr(SPRN_HID1) & HID1_DFS) 18262306a36Sopenharmony_ci return low_freq; 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci return hi_freq; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* Switch CPU speed using slewing GPIOs 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_cistatic int gpios_set_cpu_speed(int low_speed) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int gpio, timeout = 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* If ramping up, set voltage first */ 19562306a36Sopenharmony_ci if (low_speed == 0) { 19662306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); 19762306a36Sopenharmony_ci /* Delay is way too big but it's ok, we schedule */ 19862306a36Sopenharmony_ci local_delay(10); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Set frequency */ 20262306a36Sopenharmony_ci gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); 20362306a36Sopenharmony_ci if (low_speed == ((gpio & 0x01) == 0)) 20462306a36Sopenharmony_ci goto skip; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, 20762306a36Sopenharmony_ci low_speed ? 0x04 : 0x05); 20862306a36Sopenharmony_ci udelay(200); 20962306a36Sopenharmony_ci do { 21062306a36Sopenharmony_ci if (++timeout > 100) 21162306a36Sopenharmony_ci break; 21262306a36Sopenharmony_ci local_delay(1); 21362306a36Sopenharmony_ci gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0); 21462306a36Sopenharmony_ci } while((gpio & 0x02) == 0); 21562306a36Sopenharmony_ci skip: 21662306a36Sopenharmony_ci /* If ramping down, set voltage last */ 21762306a36Sopenharmony_ci if (low_speed == 1) { 21862306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); 21962306a36Sopenharmony_ci /* Delay is way too big but it's ok, we schedule */ 22062306a36Sopenharmony_ci local_delay(10); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci#ifdef DEBUG_FREQ 22462306a36Sopenharmony_ci debug_calc_bogomips(); 22562306a36Sopenharmony_ci#endif 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* Switch CPU speed under PMU control 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistatic int pmu_set_cpu_speed(int low_speed) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct adb_request req; 23562306a36Sopenharmony_ci unsigned long save_l2cr; 23662306a36Sopenharmony_ci unsigned long save_l3cr; 23762306a36Sopenharmony_ci unsigned int pic_prio; 23862306a36Sopenharmony_ci unsigned long flags; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci preempt_disable(); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci#ifdef DEBUG_FREQ 24362306a36Sopenharmony_ci printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); 24462306a36Sopenharmony_ci#endif 24562306a36Sopenharmony_ci pmu_suspend(); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Disable all interrupt sources on openpic */ 24862306a36Sopenharmony_ci pic_prio = mpic_cpu_get_priority(); 24962306a36Sopenharmony_ci mpic_cpu_set_priority(0xf); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Make sure the decrementer won't interrupt us */ 25262306a36Sopenharmony_ci asm volatile("mtdec %0" : : "r" (0x7fffffff)); 25362306a36Sopenharmony_ci /* Make sure any pending DEC interrupt occurring while we did 25462306a36Sopenharmony_ci * the above didn't re-enable the DEC */ 25562306a36Sopenharmony_ci mb(); 25662306a36Sopenharmony_ci asm volatile("mtdec %0" : : "r" (0x7fffffff)); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* We can now disable MSR_EE */ 25962306a36Sopenharmony_ci local_irq_save(flags); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Giveup the FPU & vec */ 26262306a36Sopenharmony_ci enable_kernel_fp(); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci#ifdef CONFIG_ALTIVEC 26562306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_ALTIVEC)) 26662306a36Sopenharmony_ci enable_kernel_altivec(); 26762306a36Sopenharmony_ci#endif /* CONFIG_ALTIVEC */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Save & disable L2 and L3 caches */ 27062306a36Sopenharmony_ci save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ 27162306a36Sopenharmony_ci save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Send the new speed command. My assumption is that this command 27462306a36Sopenharmony_ci * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed); 27762306a36Sopenharmony_ci while (!req.complete) 27862306a36Sopenharmony_ci pmu_poll(); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Prepare the northbridge for the speed transition */ 28162306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Call low level code to backup CPU state and recover from 28462306a36Sopenharmony_ci * hardware reset 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci low_sleep_handler(); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Restore the northbridge */ 28962306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Restore L2 cache */ 29262306a36Sopenharmony_ci if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) 29362306a36Sopenharmony_ci _set_L2CR(save_l2cr); 29462306a36Sopenharmony_ci /* Restore L3 cache */ 29562306a36Sopenharmony_ci if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) 29662306a36Sopenharmony_ci _set_L3CR(save_l3cr); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Restore userland MMU context */ 29962306a36Sopenharmony_ci switch_mmu_context(NULL, current->active_mm, NULL); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci#ifdef DEBUG_FREQ 30262306a36Sopenharmony_ci printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); 30362306a36Sopenharmony_ci#endif 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Restore low level PMU operations */ 30662306a36Sopenharmony_ci pmu_unlock(); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * Restore decrementer; we'll take a decrementer interrupt 31062306a36Sopenharmony_ci * as soon as interrupts are re-enabled and the generic 31162306a36Sopenharmony_ci * clockevents code will reprogram it with the right value. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci set_dec(1); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* Restore interrupts */ 31662306a36Sopenharmony_ci mpic_cpu_set_priority(pic_prio); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Let interrupts flow again ... */ 31962306a36Sopenharmony_ci local_irq_restore(flags); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci#ifdef DEBUG_FREQ 32262306a36Sopenharmony_ci debug_calc_bogomips(); 32362306a36Sopenharmony_ci#endif 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci pmu_resume(); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci preempt_enable(); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int do_set_cpu_speed(struct cpufreq_policy *policy, int speed_mode) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci unsigned long l3cr; 33562306a36Sopenharmony_ci static unsigned long prev_l3cr; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (speed_mode == CPUFREQ_LOW && 33862306a36Sopenharmony_ci cpu_has_feature(CPU_FTR_L3CR)) { 33962306a36Sopenharmony_ci l3cr = _get_L3CR(); 34062306a36Sopenharmony_ci if (l3cr & L3CR_L3E) { 34162306a36Sopenharmony_ci prev_l3cr = l3cr; 34262306a36Sopenharmony_ci _set_L3CR(0); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci set_speed_proc(speed_mode == CPUFREQ_LOW); 34662306a36Sopenharmony_ci if (speed_mode == CPUFREQ_HIGH && 34762306a36Sopenharmony_ci cpu_has_feature(CPU_FTR_L3CR)) { 34862306a36Sopenharmony_ci l3cr = _get_L3CR(); 34962306a36Sopenharmony_ci if ((prev_l3cr & L3CR_L3E) && l3cr != prev_l3cr) 35062306a36Sopenharmony_ci _set_L3CR(prev_l3cr); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci cur_freq = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic unsigned int pmac_cpufreq_get_speed(unsigned int cpu) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci return cur_freq; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int pmac_cpufreq_target( struct cpufreq_policy *policy, 36362306a36Sopenharmony_ci unsigned int index) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int rc; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci rc = do_set_cpu_speed(policy, index); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ppc_proc_freq = cur_freq * 1000ul; 37062306a36Sopenharmony_ci return rc; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci cpufreq_generic_init(policy, pmac_cpu_freqs, transition_latency); 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic u32 read_gpio(struct device_node *np) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci const u32 *reg = of_get_property(np, "reg", NULL); 38262306a36Sopenharmony_ci u32 offset; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (reg == NULL) 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci /* That works for all keylargos but shall be fixed properly 38762306a36Sopenharmony_ci * some day... The problem is that it seems we can't rely 38862306a36Sopenharmony_ci * on the "reg" property of the GPIO nodes, they are either 38962306a36Sopenharmony_ci * relative to the base of KeyLargo or to the base of the 39062306a36Sopenharmony_ci * GPIO space, and the device-tree doesn't help. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci offset = *reg; 39362306a36Sopenharmony_ci if (offset < KEYLARGO_GPIO_LEVELS0) 39462306a36Sopenharmony_ci offset += KEYLARGO_GPIO_LEVELS0; 39562306a36Sopenharmony_ci return offset; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int pmac_cpufreq_suspend(struct cpufreq_policy *policy) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci /* Ok, this could be made a bit smarter, but let's be robust for now. We 40162306a36Sopenharmony_ci * always force a speed change to high speed before sleep, to make sure 40262306a36Sopenharmony_ci * we have appropriate voltage and/or bus speed for the wakeup process, 40362306a36Sopenharmony_ci * and to make sure our loops_per_jiffies are "good enough", that is will 40462306a36Sopenharmony_ci * not cause too short delays if we sleep in low speed and wake in high 40562306a36Sopenharmony_ci * speed.. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci no_schedule = 1; 40862306a36Sopenharmony_ci sleep_freq = cur_freq; 40962306a36Sopenharmony_ci if (cur_freq == low_freq && !is_pmu_based) 41062306a36Sopenharmony_ci do_set_cpu_speed(policy, CPUFREQ_HIGH); 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int pmac_cpufreq_resume(struct cpufreq_policy *policy) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci /* If we resume, first check if we have a get() function */ 41762306a36Sopenharmony_ci if (get_speed_proc) 41862306a36Sopenharmony_ci cur_freq = get_speed_proc(); 41962306a36Sopenharmony_ci else 42062306a36Sopenharmony_ci cur_freq = 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* We don't, hrm... we don't really know our speed here, best 42362306a36Sopenharmony_ci * is that we force a switch to whatever it was, which is 42462306a36Sopenharmony_ci * probably high speed due to our suspend() routine 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci do_set_cpu_speed(policy, sleep_freq == low_freq ? 42762306a36Sopenharmony_ci CPUFREQ_LOW : CPUFREQ_HIGH); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci ppc_proc_freq = cur_freq * 1000ul; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci no_schedule = 0; 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic struct cpufreq_driver pmac_cpufreq_driver = { 43662306a36Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 43762306a36Sopenharmony_ci .target_index = pmac_cpufreq_target, 43862306a36Sopenharmony_ci .get = pmac_cpufreq_get_speed, 43962306a36Sopenharmony_ci .init = pmac_cpufreq_cpu_init, 44062306a36Sopenharmony_ci .suspend = pmac_cpufreq_suspend, 44162306a36Sopenharmony_ci .resume = pmac_cpufreq_resume, 44262306a36Sopenharmony_ci .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, 44362306a36Sopenharmony_ci .attr = cpufreq_generic_attr, 44462306a36Sopenharmony_ci .name = "powermac", 44562306a36Sopenharmony_ci}; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct device_node *volt_gpio_np = of_find_node_by_name(NULL, 45162306a36Sopenharmony_ci "voltage-gpio"); 45262306a36Sopenharmony_ci struct device_node *freq_gpio_np = of_find_node_by_name(NULL, 45362306a36Sopenharmony_ci "frequency-gpio"); 45462306a36Sopenharmony_ci struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL, 45562306a36Sopenharmony_ci "slewing-done"); 45662306a36Sopenharmony_ci const u32 *value; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * Check to see if it's GPIO driven or PMU only 46062306a36Sopenharmony_ci * 46162306a36Sopenharmony_ci * The way we extract the GPIO address is slightly hackish, but it 46262306a36Sopenharmony_ci * works well enough for now. We need to abstract the whole GPIO 46362306a36Sopenharmony_ci * stuff sooner or later anyway 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (volt_gpio_np) 46762306a36Sopenharmony_ci voltage_gpio = read_gpio(volt_gpio_np); 46862306a36Sopenharmony_ci if (freq_gpio_np) 46962306a36Sopenharmony_ci frequency_gpio = read_gpio(freq_gpio_np); 47062306a36Sopenharmony_ci if (slew_done_gpio_np) 47162306a36Sopenharmony_ci slew_done_gpio = read_gpio(slew_done_gpio_np); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci of_node_put(volt_gpio_np); 47462306a36Sopenharmony_ci of_node_put(freq_gpio_np); 47562306a36Sopenharmony_ci of_node_put(slew_done_gpio_np); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* If we use the frequency GPIOs, calculate the min/max speeds based 47862306a36Sopenharmony_ci * on the bus frequencies 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci if (frequency_gpio && slew_done_gpio) { 48162306a36Sopenharmony_ci int lenp, rc; 48262306a36Sopenharmony_ci const u32 *freqs, *ratio; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci freqs = of_get_property(cpunode, "bus-frequencies", &lenp); 48562306a36Sopenharmony_ci lenp /= sizeof(u32); 48662306a36Sopenharmony_ci if (freqs == NULL || lenp != 2) { 48762306a36Sopenharmony_ci pr_err("bus-frequencies incorrect or missing\n"); 48862306a36Sopenharmony_ci return 1; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci ratio = of_get_property(cpunode, "processor-to-bus-ratio*2", 49162306a36Sopenharmony_ci NULL); 49262306a36Sopenharmony_ci if (ratio == NULL) { 49362306a36Sopenharmony_ci pr_err("processor-to-bus-ratio*2 missing\n"); 49462306a36Sopenharmony_ci return 1; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* Get the min/max bus frequencies */ 49862306a36Sopenharmony_ci low_freq = min(freqs[0], freqs[1]); 49962306a36Sopenharmony_ci hi_freq = max(freqs[0], freqs[1]); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Grrrr.. It _seems_ that the device-tree is lying on the low bus 50262306a36Sopenharmony_ci * frequency, it claims it to be around 84Mhz on some models while 50362306a36Sopenharmony_ci * it appears to be approx. 101Mhz on all. Let's hack around here... 50462306a36Sopenharmony_ci * fortunately, we don't need to be too precise 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci if (low_freq < 98000000) 50762306a36Sopenharmony_ci low_freq = 101000000; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* Convert those to CPU core clocks */ 51062306a36Sopenharmony_ci low_freq = (low_freq * (*ratio)) / 2000; 51162306a36Sopenharmony_ci hi_freq = (hi_freq * (*ratio)) / 2000; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* Now we get the frequencies, we read the GPIO to see what is out current 51462306a36Sopenharmony_ci * speed 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci rc = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); 51762306a36Sopenharmony_ci cur_freq = (rc & 0x01) ? hi_freq : low_freq; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci set_speed_proc = gpios_set_cpu_speed; 52062306a36Sopenharmony_ci return 1; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* If we use the PMU, look for the min & max frequencies in the 52462306a36Sopenharmony_ci * device-tree 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci value = of_get_property(cpunode, "min-clock-frequency", NULL); 52762306a36Sopenharmony_ci if (!value) 52862306a36Sopenharmony_ci return 1; 52962306a36Sopenharmony_ci low_freq = (*value) / 1000; 53062306a36Sopenharmony_ci /* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree 53162306a36Sopenharmony_ci * here */ 53262306a36Sopenharmony_ci if (low_freq < 100000) 53362306a36Sopenharmony_ci low_freq *= 10; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci value = of_get_property(cpunode, "max-clock-frequency", NULL); 53662306a36Sopenharmony_ci if (!value) 53762306a36Sopenharmony_ci return 1; 53862306a36Sopenharmony_ci hi_freq = (*value) / 1000; 53962306a36Sopenharmony_ci set_speed_proc = pmu_set_cpu_speed; 54062306a36Sopenharmony_ci is_pmu_based = 1; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int pmac_cpufreq_init_7447A(struct device_node *cpunode) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct device_node *volt_gpio_np; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!of_property_read_bool(cpunode, "dynamic-power-step")) 55062306a36Sopenharmony_ci return 1; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); 55362306a36Sopenharmony_ci if (volt_gpio_np) 55462306a36Sopenharmony_ci voltage_gpio = read_gpio(volt_gpio_np); 55562306a36Sopenharmony_ci of_node_put(volt_gpio_np); 55662306a36Sopenharmony_ci if (!voltage_gpio){ 55762306a36Sopenharmony_ci pr_err("missing cpu-vcore-select gpio\n"); 55862306a36Sopenharmony_ci return 1; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* OF only reports the high frequency */ 56262306a36Sopenharmony_ci hi_freq = cur_freq; 56362306a36Sopenharmony_ci low_freq = cur_freq/2; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Read actual frequency from CPU */ 56662306a36Sopenharmony_ci cur_freq = dfs_get_cpu_speed(); 56762306a36Sopenharmony_ci set_speed_proc = dfs_set_cpu_speed; 56862306a36Sopenharmony_ci get_speed_proc = dfs_get_cpu_speed; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int pmac_cpufreq_init_750FX(struct device_node *cpunode) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct device_node *volt_gpio_np; 57662306a36Sopenharmony_ci u32 pvr; 57762306a36Sopenharmony_ci const u32 *value; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!of_property_read_bool(cpunode, "dynamic-power-step")) 58062306a36Sopenharmony_ci return 1; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci hi_freq = cur_freq; 58362306a36Sopenharmony_ci value = of_get_property(cpunode, "reduced-clock-frequency", NULL); 58462306a36Sopenharmony_ci if (!value) 58562306a36Sopenharmony_ci return 1; 58662306a36Sopenharmony_ci low_freq = (*value) / 1000; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); 58962306a36Sopenharmony_ci if (volt_gpio_np) 59062306a36Sopenharmony_ci voltage_gpio = read_gpio(volt_gpio_np); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci of_node_put(volt_gpio_np); 59362306a36Sopenharmony_ci pvr = mfspr(SPRN_PVR); 59462306a36Sopenharmony_ci has_cpu_l2lve = !((pvr & 0xf00) == 0x100); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci set_speed_proc = cpu_750fx_cpu_speed; 59762306a36Sopenharmony_ci get_speed_proc = cpu_750fx_get_cpu_speed; 59862306a36Sopenharmony_ci cur_freq = cpu_750fx_get_cpu_speed(); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci/* Currently, we support the following machines: 60462306a36Sopenharmony_ci * 60562306a36Sopenharmony_ci * - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz) 60662306a36Sopenharmony_ci * - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz) 60762306a36Sopenharmony_ci * - Titanium PowerBook 400 (PMU based, 300Mhz & 400Mhz) 60862306a36Sopenharmony_ci * - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz) 60962306a36Sopenharmony_ci * - iBook2 500/600 (PMU based, 400Mhz & 500/600Mhz) 61062306a36Sopenharmony_ci * - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage) 61162306a36Sopenharmony_ci * - Recent MacRISC3 laptops 61262306a36Sopenharmony_ci * - All new machines with 7447A CPUs 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_cistatic int __init pmac_cpufreq_setup(void) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct device_node *cpunode; 61762306a36Sopenharmony_ci const u32 *value; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (strstr(boot_command_line, "nocpufreq")) 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* Get first CPU node */ 62362306a36Sopenharmony_ci cpunode = of_cpu_device_node_get(0); 62462306a36Sopenharmony_ci if (!cpunode) 62562306a36Sopenharmony_ci goto out; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* Get current cpu clock freq */ 62862306a36Sopenharmony_ci value = of_get_property(cpunode, "clock-frequency", NULL); 62962306a36Sopenharmony_ci if (!value) 63062306a36Sopenharmony_ci goto out; 63162306a36Sopenharmony_ci cur_freq = (*value) / 1000; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Check for 7447A based MacRISC3 */ 63462306a36Sopenharmony_ci if (of_machine_is_compatible("MacRISC3") && 63562306a36Sopenharmony_ci of_property_read_bool(cpunode, "dynamic-power-step") && 63662306a36Sopenharmony_ci PVR_VER(mfspr(SPRN_PVR)) == 0x8003) { 63762306a36Sopenharmony_ci pmac_cpufreq_init_7447A(cpunode); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* Allow dynamic switching */ 64062306a36Sopenharmony_ci transition_latency = 8000000; 64162306a36Sopenharmony_ci pmac_cpufreq_driver.flags &= ~CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING; 64262306a36Sopenharmony_ci /* Check for other MacRISC3 machines */ 64362306a36Sopenharmony_ci } else if (of_machine_is_compatible("PowerBook3,4") || 64462306a36Sopenharmony_ci of_machine_is_compatible("PowerBook3,5") || 64562306a36Sopenharmony_ci of_machine_is_compatible("MacRISC3")) { 64662306a36Sopenharmony_ci pmac_cpufreq_init_MacRISC3(cpunode); 64762306a36Sopenharmony_ci /* Else check for iBook2 500/600 */ 64862306a36Sopenharmony_ci } else if (of_machine_is_compatible("PowerBook4,1")) { 64962306a36Sopenharmony_ci hi_freq = cur_freq; 65062306a36Sopenharmony_ci low_freq = 400000; 65162306a36Sopenharmony_ci set_speed_proc = pmu_set_cpu_speed; 65262306a36Sopenharmony_ci is_pmu_based = 1; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci /* Else check for TiPb 550 */ 65562306a36Sopenharmony_ci else if (of_machine_is_compatible("PowerBook3,3") && cur_freq == 550000) { 65662306a36Sopenharmony_ci hi_freq = cur_freq; 65762306a36Sopenharmony_ci low_freq = 500000; 65862306a36Sopenharmony_ci set_speed_proc = pmu_set_cpu_speed; 65962306a36Sopenharmony_ci is_pmu_based = 1; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci /* Else check for TiPb 400 & 500 */ 66262306a36Sopenharmony_ci else if (of_machine_is_compatible("PowerBook3,2")) { 66362306a36Sopenharmony_ci /* We only know about the 400 MHz and the 500Mhz model 66462306a36Sopenharmony_ci * they both have 300 MHz as low frequency 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_ci if (cur_freq < 350000 || cur_freq > 550000) 66762306a36Sopenharmony_ci goto out; 66862306a36Sopenharmony_ci hi_freq = cur_freq; 66962306a36Sopenharmony_ci low_freq = 300000; 67062306a36Sopenharmony_ci set_speed_proc = pmu_set_cpu_speed; 67162306a36Sopenharmony_ci is_pmu_based = 1; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci /* Else check for 750FX */ 67462306a36Sopenharmony_ci else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000) 67562306a36Sopenharmony_ci pmac_cpufreq_init_750FX(cpunode); 67662306a36Sopenharmony_ciout: 67762306a36Sopenharmony_ci of_node_put(cpunode); 67862306a36Sopenharmony_ci if (set_speed_proc == NULL) 67962306a36Sopenharmony_ci return -ENODEV; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; 68262306a36Sopenharmony_ci pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; 68362306a36Sopenharmony_ci ppc_proc_freq = cur_freq * 1000ul; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci pr_info("Registering PowerMac CPU frequency driver\n"); 68662306a36Sopenharmony_ci pr_info("Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n", 68762306a36Sopenharmony_ci low_freq/1000, hi_freq/1000, cur_freq/1000); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return cpufreq_register_driver(&pmac_cpufreq_driver); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cimodule_init(pmac_cpufreq_setup); 69362306a36Sopenharmony_ci 694