pmac_cpufreq.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 579 行 · 第 1/2 页
C
579 行
/* * arch/ppc/platforms/pmac_cpufreq.c * * Copyright (C) 2002 - 2004 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. * */#include <linux/config.h>#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/i2c.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>/* 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);extern void openpic_suspend(struct sys_device *sysdev, u32 state);extern void openpic_resume(struct sys_device *sysdev);extern void enable_kernel_altivec(void);extern void enable_kernel_fp(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;/* * Different models uses different mecanisms to switch the frequency */static int (*set_speed_proc)(int low_speed);/* * Some definitions used by the various speedprocs */static u32 voltage_gpio;static u32 frequency_gpio;static u32 slew_done_gpio;#define PMAC_CPU_LOW_SPEED 1#define PMAC_CPU_HIGH_SPEED 0/* There are only two frequency states for each processor. Values * are in kHz for the time being. */#define CPUFREQ_HIGH PMAC_CPU_HIGH_SPEED#define CPUFREQ_LOW PMAC_CPU_LOW_SPEEDstatic struct cpufreq_frequency_table pmac_cpu_freqs[] = { {CPUFREQ_HIGH, 0}, {CPUFREQ_LOW, 0}, {0, CPUFREQ_TABLE_END},};static inline void wakeup_decrementer(void){ set_dec(tb_ticks_per_jiffy); /* No currently-supported powerbook has a 601, * so use get_tbl, not native */ last_jiffy_stamp(0) = tb_last_stamp = get_tbl();}#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 __pmac cpu_750fx_cpu_speed(int low_speed){#ifdef DEBUG_FREQ printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));#endif#ifdef CONFIG_6xx low_choose_750fx_pll(low_speed);#endif#ifdef DEBUG_FREQ printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); debug_calc_bogomips();#endif return 0;}/* Switch CPU speed using DFS */static int __pmac 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); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/1000); } else { /* ramping down, enable aack delay first */ pmac_call_feature(PMAC_FTR_AACK_DELAY_ENABLE, NULL, 1, 0); } /* set frequency */ low_choose_7447a_dfs(low_speed); if (low_speed == 1) { /* ramping down, set voltage last */ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/1000); } else { /* ramping up, disable aack delay last */ pmac_call_feature(PMAC_FTR_AACK_DELAY_ENABLE, NULL, 0, 0); } return 0;}/* Switch CPU speed using slewing GPIOs */static int __pmac gpios_set_cpu_speed(int low_speed){ int gpio; /* 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 */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/100); } /* Set frequency */ pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, low_speed ? 0x04 : 0x05); udelay(200); do { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0); } while((gpio & 0x02) == 0); /* 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 */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/100); }#ifdef DEBUG_FREQ debug_calc_bogomips();#endif return 0;}/* Switch CPU speed under PMU control */static int __pmac pmu_set_cpu_speed(int low_speed){ struct adb_request req; unsigned long save_l2cr; unsigned long save_l3cr; preempt_disable();#ifdef DEBUG_FREQ printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1));#endif /* Disable all interrupt sources on openpic */ openpic_suspend(NULL, 1); /* Make sure the decrementer won't interrupt us */ asm volatile("mtdec %0" : : "r" (0x7fffffff)); /* Make sure any pending DEC interrupt occuring 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_disable(); /* Giveup the FPU & vec */ enable_kernel_fp();#ifdef CONFIG_ALTIVEC if (cur_cpu_spec[0]->cpu_features & 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) */ if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) _set_L3CR(save_l3cr & 0x7fffffff); if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) _set_L2CR(save_l2cr & 0x7fffffff); /* 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, 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 */ openpic_resume(NULL); /* Let interrupts flow again ... */ local_irq_enable();
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?