processor.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,497 行 · 第 1/5 页

C
2,497
字号
/* * acpi_processor.c - ACPI Processor Driver ($Revision: 71 $) * *  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> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * *  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. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *  TBD: *	1. Make # power states dynamic. *	2. Support duty_cycle values that span bit 4. *	3. Optimize by having scheduler determine business instead of *	   having us try to calculate it here. *	4. Need C1 timing -- must modify kernel (IRQ handler) to get this. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/pm.h>#include <linux/cpufreq.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <asm/io.h>#include <asm/system.h>#include <asm/delay.h>#include <asm/uaccess.h>#include <asm/processor.h>#include <asm/smp.h>#include <asm/acpi.h>#include <acpi/acpi_bus.h>#include <acpi/acpi_drivers.h>#include <acpi/processor.h>#define ACPI_PROCESSOR_COMPONENT	0x01000000#define ACPI_PROCESSOR_CLASS		"processor"#define ACPI_PROCESSOR_DRIVER_NAME	"ACPI Processor Driver"#define ACPI_PROCESSOR_DEVICE_NAME	"Processor"#define ACPI_PROCESSOR_FILE_INFO	"info"#define ACPI_PROCESSOR_FILE_POWER	"power"#define ACPI_PROCESSOR_FILE_THROTTLING	"throttling"#define ACPI_PROCESSOR_FILE_LIMIT	"limit"#define ACPI_PROCESSOR_FILE_PERFORMANCE	"performance"#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80#define ACPI_PROCESSOR_NOTIFY_POWER	0x81#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) */#define ACPI_PROCESSOR_LIMIT_USER	0#define ACPI_PROCESSOR_LIMIT_THERMAL	1#define _COMPONENT		ACPI_PROCESSOR_COMPONENTACPI_MODULE_NAME		("acpi_processor")MODULE_AUTHOR("Paul Diefenbaugh");MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME);MODULE_LICENSE("GPL");static int acpi_processor_add (struct acpi_device *device);static int acpi_processor_remove (struct acpi_device *device, int type);static int acpi_processor_info_open_fs(struct inode *inode, struct file *file);static int acpi_processor_throttling_open_fs(struct inode *inode, struct file *file);static int acpi_processor_power_open_fs(struct inode *inode, struct file *file);static int acpi_processor_limit_open_fs(struct inode *inode, struct file *file);static int acpi_processor_get_limit_info(struct acpi_processor *pr);static struct acpi_driver acpi_processor_driver = {	.name =		ACPI_PROCESSOR_DRIVER_NAME,	.class =	ACPI_PROCESSOR_CLASS,	.ids =		ACPI_PROCESSOR_HID,	.ops =		{				.add =		acpi_processor_add,				.remove =	acpi_processor_remove,			},};struct acpi_processor_errata {	u8			smp;	struct {		u8			throttle:1;		u8			fdma:1;		u8			reserved:6;		u32			bmisx;	}			piix4;};static struct file_operations acpi_processor_info_fops = {	.open 		= acpi_processor_info_open_fs,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= single_release,};static struct file_operations acpi_processor_power_fops = {	.open 		= acpi_processor_power_open_fs,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= single_release,};static struct file_operations acpi_processor_throttling_fops = {	.open 		= acpi_processor_throttling_open_fs,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= single_release,};static struct file_operations acpi_processor_limit_fops = {	.open 		= acpi_processor_limit_open_fs,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= single_release,};static struct acpi_processor	*processors[NR_CPUS];static struct acpi_processor_errata errata;static void (*pm_idle_save)(void);/* --------------------------------------------------------------------------                                Errata Handling   -------------------------------------------------------------------------- */intacpi_processor_errata_piix4 (	struct pci_dev		*dev){	u8			rev = 0;	u8			value1 = 0;	u8			value2 = 0;	ACPI_FUNCTION_TRACE("acpi_processor_errata_piix4");	if (!dev)		return_VALUE(-EINVAL);	/*	 * Note that 'dev' references the PIIX4 ACPI Controller.	 */	pci_read_config_byte(dev, PCI_REVISION_ID, &rev);	switch (rev) {	case 0:		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n"));		break;	case 1:		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n"));		break;	case 2:		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n"));		break;	case 3:		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n"));		break;	default:		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n"));		break;	}	switch (rev) {	case 0:		/* PIIX4 A-step */	case 1:		/* PIIX4 B-step */		/*		 * See specification changes #13 ("Manual Throttle Duty Cycle")		 * and #14 ("Enabling and Disabling Manual Throttle"), plus		 * erratum #5 ("STPCLK# Deassertion Time") from the January 		 * 2002 PIIX4 specification update.  Applies to only older 		 * PIIX4 models.		 */		errata.piix4.throttle = 1;	case 2:		/* PIIX4E */	case 3:		/* PIIX4M */		/*		 * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA 		 * Livelock") from the January 2002 PIIX4 specification update.		 * Applies to all PIIX4 models.		 */		/* 		 * BM-IDE		 * ------		 * Find the PIIX4 IDE Controller and get the Bus Master IDE 		 * Status register address.  We'll use this later to read 		 * each IDE controller's DMA status to make sure we catch all		 * DMA activity.		 */		dev = pci_find_subsys(PCI_VENDOR_ID_INTEL,		           PCI_DEVICE_ID_INTEL_82371AB,                            PCI_ANY_ID, PCI_ANY_ID, NULL);		if (dev)			errata.piix4.bmisx = pci_resource_start(dev, 4);		/* 		 * Type-F DMA		 * ----------		 * Find the PIIX4 ISA Controller and read the Motherboard		 * DMA controller's status to see if Type-F (Fast) DMA mode		 * is enabled (bit 7) on either channel.  Note that we'll 		 * disable C3 support if this is enabled, as some legacy 		 * devices won't operate well if fast DMA is disabled.		 */		dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, 			PCI_DEVICE_ID_INTEL_82371AB_0, 			PCI_ANY_ID, PCI_ANY_ID, NULL);		if (dev) {			pci_read_config_byte(dev, 0x76, &value1);			pci_read_config_byte(dev, 0x77, &value2);			if ((value1 & 0x80) || (value2 & 0x80))				errata.piix4.fdma = 1;		}		break;	}	if (errata.piix4.bmisx)		ACPI_DEBUG_PRINT((ACPI_DB_INFO, 			"Bus master activity detection (BM-IDE) erratum enabled\n"));	if (errata.piix4.fdma)		ACPI_DEBUG_PRINT((ACPI_DB_INFO, 			"Type-F DMA livelock erratum (C3 disabled)\n"));	return_VALUE(0);}intacpi_processor_errata (	struct acpi_processor	*pr){	int			result = 0;	struct pci_dev		*dev = NULL;	ACPI_FUNCTION_TRACE("acpi_processor_errata");	if (!pr)		return_VALUE(-EINVAL);	/*	 * PIIX4	 */	dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, 		PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, PCI_ANY_ID, NULL);	if (dev)		result = acpi_processor_errata_piix4(dev);	return_VALUE(result);}/* --------------------------------------------------------------------------                                Power Management   -------------------------------------------------------------------------- */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,	int			state){	if (!pr)		return;	pr->power.states[pr->power.state].promotion.count = 0;	pr->power.states[pr->power.state].demotion.count = 0;	/* Cleanup from old state. */	switch (pr->power.state) {	case ACPI_STATE_C3:		/* Disable bus master reload */		acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0, ACPI_MTX_DO_NOT_LOCK);		break;	}	/* Prepare to use new state. */	switch (state) {	case ACPI_STATE_C3:		/* Enable bus master reload */		acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1, ACPI_MTX_DO_NOT_LOCK);		break;	}	pr->power.state = state;	return;}static voidacpi_processor_idle (void){	struct acpi_processor	*pr = NULL;	struct acpi_processor_cx *cx = NULL;	int			next_state = 0;	int			sleep_ticks = 0;	u32			t1, t2 = 0;	pr = processors[smp_processor_id()];	if (!pr)		return;	/*	 * Interrupts must be disabled during bus mastering calculations and	 * for C2/C3 transitions.	 */	local_irq_disable();	cx = &(pr->power.states[pr->power.state]);	/*	 * Check BM Activity	 * -----------------	 * Check for bus mastering activity (if required), record, and check	 * for demotion.	 */	if (pr->flags.bm_check) {		u32		bm_status = 0;		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++;		}		/*		 * 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 (pr->power.state) {	case ACPI_STATE_C1:		/* Invoke C1. */		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(pr->power.states[ACPI_STATE_C2].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:		/* Disable bus master arbitration */		acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1, ACPI_MTX_DO_NOT_LOCK);		/* Get start time (ticks) */		t1 = inl(acpi_fadt.xpm_tmr_blk.address);		/* Invoke C3 */		inb(pr->power.states[ACPI_STATE_C3].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);		/* Enable bus master arbitration */		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.	 */	if (cx->promotion.state) {		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;			}		}	}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?