📄 cpufreq_32.c
字号:
/* * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org> * Copyright (C) 2004 John Steele Scott <toojays@toojays.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * TODO: Need a big cleanup here. Basically, we need to have different * cpufreq_driver structures for the different type of HW instead of the * current mess. We also need to better deal with the detection of the * type of machine. * */#include <linux/module.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/adb.h>#include <linux/pmu.h>#include <linux/slab.h>#include <linux/cpufreq.h>#include <linux/init.h>#include <linux/sysdev.h>#include <linux/hardirq.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/irq.h>#include <asm/pmac_feature.h>#include <asm/mmu_context.h>#include <asm/sections.h>#include <asm/cputable.h>#include <asm/time.h>#include <asm/system.h>#include <asm/mpic.h>#include <asm/keylargo.h>/* WARNING !!! This will cause calibrate_delay() to be called, * but this is an __init function ! So you MUST go edit * init/main.c to make it non-init before enabling DEBUG_FREQ */#undef DEBUG_FREQ/* * There is a problem with the core cpufreq code on SMP kernels, * it won't recalculate the Bogomips properly */#ifdef CONFIG_SMP#warning "WARNING, CPUFREQ not recommended on SMP kernels"#endifextern void low_choose_7447a_dfs(int dfs);extern void low_choose_750fx_pll(int pll);extern void low_sleep_handler(void);/* * Currently, PowerMac cpufreq supports only high & low frequencies * that are set by the firmware */static unsigned int low_freq;static unsigned int hi_freq;static unsigned int cur_freq;static unsigned int sleep_freq;/* * Different models uses different mechanisms to switch the frequency */static int (*set_speed_proc)(int low_speed);static unsigned int (*get_speed_proc)(void);/* * Some definitions used by the various speedprocs */static u32 voltage_gpio;static u32 frequency_gpio;static u32 slew_done_gpio;static int no_schedule;static int has_cpu_l2lve;static int is_pmu_based;/* There are only two frequency states for each processor. Values * are in kHz for the time being. */#define CPUFREQ_HIGH 0#define CPUFREQ_LOW 1static struct cpufreq_frequency_table pmac_cpu_freqs[] = { {CPUFREQ_HIGH, 0}, {CPUFREQ_LOW, 0}, {0, CPUFREQ_TABLE_END},};static struct freq_attr* pmac_cpu_freqs_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL,};static inline void local_delay(unsigned long ms){ if (no_schedule) mdelay(ms); else msleep(ms);}#ifdef DEBUG_FREQstatic inline void debug_calc_bogomips(void){ /* This will cause a recalc of bogomips and display the * result. We backup/restore the value to avoid affecting the * core cpufreq framework's own calculation. */ extern void calibrate_delay(void); unsigned long save_lpj = loops_per_jiffy; calibrate_delay(); loops_per_jiffy = save_lpj;}#endif /* DEBUG_FREQ *//* Switch CPU speed under 750FX CPU control */static int cpu_750fx_cpu_speed(int low_speed){ u32 hid2; if (low_speed == 0) { /* ramping up, set voltage first */ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); /* Make sure we sleep for at least 1ms */ local_delay(10); /* tweak L2 for high voltage */ if (has_cpu_l2lve) { hid2 = mfspr(SPRN_HID2); hid2 &= ~0x2000; mtspr(SPRN_HID2, hid2); } }#ifdef CONFIG_6xx low_choose_750fx_pll(low_speed);#endif if (low_speed == 1) { /* tweak L2 for low voltage */ if (has_cpu_l2lve) { hid2 = mfspr(SPRN_HID2); hid2 |= 0x2000; mtspr(SPRN_HID2, hid2); } /* ramping down, set voltage last */ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); local_delay(10); } return 0;}static unsigned int cpu_750fx_get_cpu_speed(void){ if (mfspr(SPRN_HID1) & HID1_PS) return low_freq; else return hi_freq;}/* Switch CPU speed using DFS */static int dfs_set_cpu_speed(int low_speed){ if (low_speed == 0) { /* ramping up, set voltage first */ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); /* Make sure we sleep for at least 1ms */ local_delay(1); } /* set frequency */#ifdef CONFIG_6xx low_choose_7447a_dfs(low_speed);#endif udelay(100); if (low_speed == 1) { /* ramping down, set voltage last */ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); local_delay(1); } return 0;}static unsigned int dfs_get_cpu_speed(void){ if (mfspr(SPRN_HID1) & HID1_DFS) return low_freq; else return hi_freq;}/* Switch CPU speed using slewing GPIOs */static int gpios_set_cpu_speed(int low_speed){ int gpio, timeout = 0; /* If ramping up, set voltage first */ if (low_speed == 0) { pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); /* Delay is way too big but it's ok, we schedule */ local_delay(10); } /* Set frequency */ gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); if (low_speed == ((gpio & 0x01) == 0)) goto skip; pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, low_speed ? 0x04 : 0x05); udelay(200); do { if (++timeout > 100) break; local_delay(1); gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0); } while((gpio & 0x02) == 0); skip: /* If ramping down, set voltage last */ if (low_speed == 1) { pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); /* Delay is way too big but it's ok, we schedule */ local_delay(10); }#ifdef DEBUG_FREQ debug_calc_bogomips();#endif return 0;}/* Switch CPU speed under PMU control */static int pmu_set_cpu_speed(int low_speed){ struct adb_request req; unsigned long save_l2cr; unsigned long save_l3cr; unsigned int pic_prio; unsigned long flags; preempt_disable();#ifdef DEBUG_FREQ printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));#endif pmu_suspend(); /* Disable all interrupt sources on openpic */ pic_prio = mpic_cpu_get_priority(); mpic_cpu_set_priority(0xf); /* Make sure the decrementer won't interrupt us */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* Make sure any pending DEC interrupt occurring while we did * the above didn't re-enable the DEC */ mb(); asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* We can now disable MSR_EE */ local_irq_save(flags); /* Giveup the FPU & vec */ enable_kernel_fp();#ifdef CONFIG_ALTIVEC if (cpu_has_feature(CPU_FTR_ALTIVEC)) enable_kernel_altivec();#endif /* CONFIG_ALTIVEC */ /* Save & disable L2 and L3 caches */ save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ /* Send the new speed command. My assumption is that this command * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep */ pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed); while (!req.complete) pmu_poll(); /* Prepare the northbridge for the speed transition */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1); /* Call low level code to backup CPU state and recover from * hardware reset */ low_sleep_handler(); /* Restore the northbridge */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0); /* Restore L2 cache */ if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) _set_L2CR(save_l2cr); /* Restore L3 cache */ if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) _set_L3CR(save_l3cr); /* Restore userland MMU context */ set_context(current->active_mm->context.id, current->active_mm->pgd);#ifdef DEBUG_FREQ printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1));#endif /* Restore low level PMU operations */ pmu_unlock(); /* Restore decrementer */ wakeup_decrementer(); /* Restore interrupts */ mpic_cpu_set_priority(pic_prio); /* Let interrupts flow again ... */ local_irq_restore(flags);#ifdef DEBUG_FREQ debug_calc_bogomips();#endif pmu_resume(); preempt_enable(); return 0;}static int do_set_cpu_speed(int speed_mode, int notify){ struct cpufreq_freqs freqs; unsigned long l3cr; static unsigned long prev_l3cr; freqs.old = cur_freq; freqs.new = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq; freqs.cpu = smp_processor_id(); if (freqs.old == freqs.new) return 0; if (notify) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); if (speed_mode == CPUFREQ_LOW && cpu_has_feature(CPU_FTR_L3CR)) { l3cr = _get_L3CR(); if (l3cr & L3CR_L3E) { prev_l3cr = l3cr;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -