1075 lines · 27.3 KB · C
RawDownload
/* * processor_idle - idle state submodule to the ACPI processor driver * *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de> *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> *  			- Added processor hotplug support *  Copyright (C) 2005  Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> *  			- Added support for C3 on SMP * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or (at *  your option) any later version. * *  This program is distributed in the hope that it will be useful, but *  WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU *  General Public License for more details. * *  You should have received a copy of the GNU General Public License along *  with this program; if not, write to the Free Software Foundation, Inc., *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/cpufreq.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/acpi.h>#include <linux/dmi.h>#include <linux/moduleparam.h> #include <asm/io.h>#include <asm/uaccess.h> #include <acpi/acpi_bus.h>#include <acpi/processor.h> #define ACPI_PROCESSOR_COMPONENT        0x01000000#define ACPI_PROCESSOR_CLASS            "processor"#define ACPI_PROCESSOR_DRIVER_NAME      "ACPI Processor Driver"#define _COMPONENT              ACPI_PROCESSOR_COMPONENTACPI_MODULE_NAME                ("acpi_processor") #define ACPI_PROCESSOR_FILE_POWER	"power" #define US_TO_PM_TIMER_TICKS(t)		((t * (PM_TIMER_FREQUENCY/1000)) / 1000)#define C2_OVERHEAD			4	/* 1us (3.579 ticks per us) */#define C3_OVERHEAD			4	/* 1us (3.579 ticks per us) */ static void (*pm_idle_save)(void);module_param(max_cstate, uint, 0644); static unsigned int nocst = 0;module_param(nocst, uint, 0000); /* * bm_history -- bit-mask with a bit per jiffy of bus-master activity * 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms * 800 HZ: 0xFFFFFFFF: 32 jiffies = 40ms * 100 HZ: 0x0000000F: 4 jiffies = 40ms * reduce history for more aggressive entry into C3 */static unsigned int bm_history = (HZ >= 800 ? 0xFFFFFFFF : ((1U << (HZ / 25)) - 1));module_param(bm_history, uint, 0644);/* --------------------------------------------------------------------------                                Power Management   -------------------------------------------------------------------------- */ /* * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3. * For now disable this. Probably a bug somewhere else. * * To skip this limit, boot/load with a large max_cstate limit. */static int set_max_cstate(struct dmi_system_id *id){	if (max_cstate > ACPI_PROCESSOR_MAX_POWER)		return 0; 	printk(KERN_NOTICE PREFIX "%s detected - limiting to C%ld max_cstate."		" Override with \"processor.max_cstate=%d\"\n", id->ident,		(long)id->driver_data, ACPI_PROCESSOR_MAX_POWER + 1); 	max_cstate = (long)id->driver_data; 	return 0;}  static struct dmi_system_id __initdata processor_power_dmi_table[] = {	{ set_max_cstate, "IBM ThinkPad R40e", {	  DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),	  DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW") }, (void*)1},	{ set_max_cstate, "Medion 41700", {	  DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),	  DMI_MATCH(DMI_BIOS_VERSION,"R01-A1J") }, (void*)1},	{ set_max_cstate, "Clevo 5600D", {	  DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),	  DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307") },	  (void*)2},	{},};  static inline u32ticks_elapsed (	u32			t1,	u32			t2){	if (t2 >= t1)		return (t2 - t1);	else if (!acpi_fadt.tmr_val_ext)		return (((0x00FFFFFF - t1) + t2) & 0x00FFFFFF);	else		return ((0xFFFFFFFF - t1) + t2);}  static voidacpi_processor_power_activate (	struct acpi_processor	*pr,	struct acpi_processor_cx  *new){	struct acpi_processor_cx  *old; 	if (!pr || !new)		return; 	old = pr->power.state; 	if (old)		old->promotion.count = 0; 	new->demotion.count = 0; 	/* Cleanup from old state. */	if (old) {		switch (old->type) {		case ACPI_STATE_C3:			/* Disable bus master reload */			if (new->type != ACPI_STATE_C3 && pr->flags.bm_check)				acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0, ACPI_MTX_DO_NOT_LOCK);			break;		}	} 	/* Prepare to use new state. */	switch (new->type) {	case ACPI_STATE_C3:		/* Enable bus master reload */		if (old->type != ACPI_STATE_C3 && pr->flags.bm_check)			acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1, ACPI_MTX_DO_NOT_LOCK);		break;	} 	pr->power.state = new; 	return;}  static atomic_t 	c3_cpu_count;  static void acpi_processor_idle (void){	struct acpi_processor	*pr = NULL;	struct acpi_processor_cx *cx = NULL;	struct acpi_processor_cx *next_state = NULL;	int			sleep_ticks = 0;	u32			t1, t2 = 0; 	pr = processors[raw_smp_processor_id()];	if (!pr)		return; 	/*	 * Interrupts must be disabled during bus mastering calculations and	 * for C2/C3 transitions.	 */	local_irq_disable(); 	/*	 * Check whether we truly need to go idle, or should	 * reschedule:	 */	if (unlikely(need_resched())) {		local_irq_enable();		return;	} 	cx = pr->power.state;	if (!cx)		goto easy_out; 	/*	 * Check BM Activity	 * -----------------	 * Check for bus mastering activity (if required), record, and check	 * for demotion.	 */	if (pr->flags.bm_check) {		u32		bm_status = 0;		unsigned long	diff = jiffies - pr->power.bm_check_timestamp; 		if (diff > 32)			diff = 32; 		while (diff) {			/* if we didn't get called, assume there was busmaster activity */			diff--;			if (diff)				pr->power.bm_activity |= 0x1;			pr->power.bm_activity <<= 1;		} 		acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS,			&bm_status, ACPI_MTX_DO_NOT_LOCK);		if (bm_status) {			pr->power.bm_activity++;			acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS,				1, ACPI_MTX_DO_NOT_LOCK);		}		/*		 * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect		 * the true state of bus mastering activity; forcing us to		 * manually check the BMIDEA bit of each IDE channel.		 */		else if (errata.piix4.bmisx) {			if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01)				|| (inb_p(errata.piix4.bmisx + 0x0A) & 0x01))				pr->power.bm_activity++;		} 		pr->power.bm_check_timestamp = jiffies; 		/*		 * Apply bus mastering demotion policy.  Automatically demote		 * to avoid a faulty transition.  Note that the processor		 * won't enter a low-power state during this call (to this		 * funciton) but should upon the next.		 *		 * TBD: A better policy might be to fallback to the demotion		 *      state (use it for this quantum only) istead of		 *      demoting -- and rely on duration as our sole demotion		 *      qualification.  This may, however, introduce DMA		 *      issues (e.g. floppy DMA transfer overrun/underrun).		 */		if (pr->power.bm_activity & cx->demotion.threshold.bm) {			local_irq_enable();			next_state = cx->demotion.state;			goto end;		}	} 	cx->usage++; 	/*	 * Sleep:	 * ------	 * Invoke the current Cx state to put the processor to sleep.	 */	switch (cx->type) { 	case ACPI_STATE_C1:		/*		 * Invoke C1.		 * Use the appropriate idle routine, the one that would		 * be used without acpi C-states.		 */		if (pm_idle_save)			pm_idle_save();		else			safe_halt();		/*                 * TBD: Can't get time duration while in C1, as resumes		 *      go to an ISR rather than here.  Need to instrument		 *      base interrupt handler.		 */		sleep_ticks = 0xFFFFFFFF;		break; 	case ACPI_STATE_C2:		/* Get start time (ticks) */		t1 = inl(acpi_fadt.xpm_tmr_blk.address);		/* Invoke C2 */		inb(cx->address);		/* Dummy op - must do something useless after P_LVL2 read */		t2 = inl(acpi_fadt.xpm_tmr_blk.address);		/* Get end time (ticks) */		t2 = inl(acpi_fadt.xpm_tmr_blk.address);		/* Re-enable interrupts */		local_irq_enable();		/* Compute time (ticks) that we were actually asleep */		sleep_ticks = ticks_elapsed(t1, t2) - cx->latency_ticks - C2_OVERHEAD;		break; 	case ACPI_STATE_C3:				if (pr->flags.bm_check) {			if (atomic_inc_return(&c3_cpu_count) ==					num_online_cpus()) {				/*				 * All CPUs are trying to go to C3				 * Disable bus master arbitration				 */				acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1,					ACPI_MTX_DO_NOT_LOCK);			}		} else {			/* SMP with no shared cache... Invalidate cache  */			ACPI_FLUSH_CPU_CACHE();		}				/* Get start time (ticks) */		t1 = inl(acpi_fadt.xpm_tmr_blk.address);		/* Invoke C3 */		inb(cx->address);		/* Dummy op - must do something useless after P_LVL3 read */		t2 = inl(acpi_fadt.xpm_tmr_blk.address);		/* Get end time (ticks) */		t2 = inl(acpi_fadt.xpm_tmr_blk.address);		if (pr->flags.bm_check) {			/* Enable bus master arbitration */			atomic_dec(&c3_cpu_count);			acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0, ACPI_MTX_DO_NOT_LOCK);		} 		/* Re-enable interrupts */		local_irq_enable();		/* Compute time (ticks) that we were actually asleep */		sleep_ticks = ticks_elapsed(t1, t2) - cx->latency_ticks - C3_OVERHEAD;		break; 	default:		local_irq_enable();		return;	} 	next_state = pr->power.state; 	/*	 * Promotion?	 * ----------	 * Track the number of longs (time asleep is greater than threshold)	 * and promote when the count threshold is reached.  Note that bus	 * mastering activity may prevent promotions.	 * Do not promote above max_cstate.	 */	if (cx->promotion.state &&	    ((cx->promotion.state - pr->power.states) <= max_cstate)) {		if (sleep_ticks > cx->promotion.threshold.ticks) {			cx->promotion.count++; 			cx->demotion.count = 0;			if (cx->promotion.count >= cx->promotion.threshold.count) {				if (pr->flags.bm_check) {					if (!(pr->power.bm_activity & cx->promotion.threshold.bm)) {						next_state = cx->promotion.state;						goto end;					}				}				else {					next_state = cx->promotion.state;					goto end;				}			}		}	} 	/*	 * Demotion?	 * ---------	 * Track the number of shorts (time asleep is less than time threshold)	 * and demote when the usage threshold is reached.	 */	if (cx->demotion.state) {		if (sleep_ticks < cx->demotion.threshold.ticks) {			cx->demotion.count++;			cx->promotion.count = 0;			if (cx->demotion.count >= cx->demotion.threshold.count) {				next_state = cx->demotion.state;				goto end;			}		}	} end:	/*	 * Demote if current state exceeds max_cstate	 */	if ((pr->power.state - pr->power.states) > max_cstate) {		if (cx->demotion.state)			next_state = cx->demotion.state;	} 	/*	 * New Cx State?	 * -------------	 * If we're going to start using a new Cx state we must clean up	 * from the previous and prepare to use the new.	 */	if (next_state != pr->power.state)		acpi_processor_power_activate(pr, next_state); 	return;  easy_out:	/* do C1 instead of busy loop */	if (pm_idle_save)		pm_idle_save();	else		safe_halt();	return;}  static intacpi_processor_set_power_policy (	struct acpi_processor	*pr){	unsigned int i;	unsigned int state_is_set = 0;	struct acpi_processor_cx *lower = NULL;	struct acpi_processor_cx *higher = NULL;	struct acpi_processor_cx *cx;  	ACPI_FUNCTION_TRACE("acpi_processor_set_power_policy"); 	if (!pr)		return_VALUE(-EINVAL); 	/*	 * This function sets the default Cx state policy (OS idle handler).	 * Our scheme is to promote quickly to C2 but more conservatively	 * to C3.  We're favoring C2  for its characteristics of low latency	 * (quick response), good power savings, and ability to allow bus	 * mastering activity.  Note that the Cx state policy is completely	 * customizable and can be altered dynamically.	 */ 	/* startup state */	for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) {		cx = &pr->power.states[i];		if (!cx->valid)			continue; 		if (!state_is_set)			pr->power.state = cx;		state_is_set++;		break; 	} 	if (!state_is_set)		return_VALUE(-ENODEV); 	/* demotion */	for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) {		cx = &pr->power.states[i];		if (!cx->valid)			continue; 		if (lower) {			cx->demotion.state = lower;			cx->demotion.threshold.ticks = cx->latency_ticks;			cx->demotion.threshold.count = 1;			if (cx->type == ACPI_STATE_C3)				cx->demotion.threshold.bm = bm_history;		} 		lower = cx;	} 	/* promotion */	for (i = (ACPI_PROCESSOR_MAX_POWER - 1); i > 0; i--) {		cx = &pr->power.states[i];		if (!cx->valid)			continue; 		if (higher) {			cx->promotion.state  = higher;			cx->promotion.threshold.ticks = cx->latency_ticks;			if (cx->type >= ACPI_STATE_C2)				cx->promotion.threshold.count = 4;			else				cx->promotion.threshold.count = 10;			if (higher->type == ACPI_STATE_C3)				cx->promotion.threshold.bm = bm_history;		} 		higher = cx;	} 

Lines 1–500 of 1075.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t