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