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 + -
显示快捷键?