processor_idle.c
来自「linux 内核源代码」· C语言 代码 · 共 1,790 行 · 第 1/4 页
C
1,790 行
/* * 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, 2005 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 <linux/sched.h> /* need_resched() */#include <linux/latency.h>#include <linux/clockchips.h>#include <linux/cpuidle.h>/* * Include the apic definitions for x86 to have the APIC timer related defines * available also for UP (on SMP it gets magically included via linux/smp.h). * asm/acpi.h is not an option, as it would require more include magic. Also * creating an empty asm-ia64/apic.h would just trade pest vs. cholera. */#ifdef CONFIG_X86#include <asm/apic.h>#endif#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 _COMPONENT ACPI_PROCESSOR_COMPONENTACPI_MODULE_NAME("processor_idle");#define ACPI_PROCESSOR_FILE_POWER "power"#define US_TO_PM_TIMER_TICKS(t) ((t * (PM_TIMER_FREQUENCY/1000)) / 1000)#define PM_TIMER_TICK_NS (1000000000ULL/PM_TIMER_FREQUENCY)#ifndef CONFIG_CPU_IDLE#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) __read_mostly;#else#define C2_OVERHEAD 1 /* 1us */#define C3_OVERHEAD 1 /* 1us */#endif#define PM_TIMER_TICKS_TO_US(p) (((p) * 1000)/(PM_TIMER_FREQUENCY/1000))static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER;#ifdef CONFIG_CPU_IDLEmodule_param(max_cstate, uint, 0000);#elsemodule_param(max_cstate, uint, 0644);#endifstatic unsigned int nocst __read_mostly;module_param(nocst, uint, 0000);#ifndef CONFIG_CPU_IDLE/* * 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 __read_mostly = (HZ >= 800 ? 0xFFFFFFFF : ((1U << (HZ / 25)) - 1));module_param(bm_history, uint, 0644);static int acpi_processor_set_power_policy(struct acpi_processor *pr);#endif/* * 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(const 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;}/* Actually this shouldn't be __cpuinitdata, would be better to fix the callers to only run once -AK */static struct dmi_system_id __cpuinitdata processor_power_dmi_table[] = { { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET70WW")}, (void *)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW")}, (void *)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET43WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET45WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET47WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET50WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET52WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET55WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET56WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET59WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET61WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET62WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET64WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET65WW") }, (void*)1}, { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), DMI_MATCH(DMI_BIOS_VERSION,"1SET68WW") }, (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 u32 ticks_elapsed(u32 t1, u32 t2){ if (t2 >= t1) return (t2 - t1); else if (!(acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER)) return (((0x00FFFFFF - t1) + t2) & 0x00FFFFFF); else return ((0xFFFFFFFF - t1) + t2);}static inline u32 ticks_elapsed_in_us(u32 t1, u32 t2){ if (t2 >= t1) return PM_TIMER_TICKS_TO_US(t2 - t1); else if (!(acpi_gbl_FADT.flags & ACPI_FADT_32BIT_TIMER)) return PM_TIMER_TICKS_TO_US(((0x00FFFFFF - t1) + t2) & 0x00FFFFFF); else return PM_TIMER_TICKS_TO_US((0xFFFFFFFF - t1) + t2);}static void acpi_safe_halt(void){ current_thread_info()->status &= ~TS_POLLING; /* * TS_POLLING-cleared state must be visible before we * test NEED_RESCHED: */ smp_mb(); if (!need_resched()) safe_halt(); current_thread_info()->status |= TS_POLLING;}#ifndef CONFIG_CPU_IDLEstatic 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); 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); break; } pr->power.state = new; return;}static atomic_t c3_cpu_count;/* Common C-state entry for C2, C3, .. */static void acpi_cstate_enter(struct acpi_processor_cx *cstate){ if (cstate->space_id == ACPI_CSTATE_FFH) { /* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter(cstate); } else { int unused; /* IO port based C-state */ inb(cstate->address); /* Dummy wait op - must do something useless after P_LVL2 read because chipsets cannot guarantee that STPCLK# signal gets asserted in time to freeze execution properly. */ unused = inl(acpi_gbl_FADT.xpm_timer_block.address); }}#endif /* !CONFIG_CPU_IDLE */#ifdef ARCH_APICTIMER_STOPS_ON_C3/* * Some BIOS implementations switch to C3 in the published C2 state. * This seems to be a common problem on AMD boxen, but other vendors * are affected too. We pick the most conservative approach: we assume * that the local APIC stops in both C2 and C3. */static void acpi_timer_check_state(int state, struct acpi_processor *pr, struct acpi_processor_cx *cx){ struct acpi_processor_power *pwr = &pr->power; u8 type = local_apic_timer_c2_ok ? ACPI_STATE_C3 : ACPI_STATE_C2; /* * Check, if one of the previous states already marked the lapic * unstable */ if (pwr->timer_broadcast_on_state < state) return; if (cx->type >= type) pr->power.timer_broadcast_on_state = state;}static void acpi_propagate_timer_broadcast(struct acpi_processor *pr){ unsigned long reason; reason = pr->power.timer_broadcast_on_state < INT_MAX ? CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF; clockevents_notify(reason, &pr->id);}/* Power(C) State timer broadcast control */static void acpi_state_timer_broadcast(struct acpi_processor *pr, struct acpi_processor_cx *cx, int broadcast){ int state = cx - pr->power.states; if (state >= pr->power.timer_broadcast_on_state) { unsigned long reason; reason = broadcast ? CLOCK_EVT_NOTIFY_BROADCAST_ENTER : CLOCK_EVT_NOTIFY_BROADCAST_EXIT; clockevents_notify(reason, &pr->id); }}#elsestatic void acpi_timer_check_state(int state, struct acpi_processor *pr, struct acpi_processor_cx *cstate) { }static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) { }static void acpi_state_timer_broadcast(struct acpi_processor *pr, struct acpi_processor_cx *cx, int broadcast){}#endif/* * Suspend / resume control */static int acpi_idle_suspend;int acpi_processor_suspend(struct acpi_device * device, pm_message_t state){ acpi_idle_suspend = 1; return 0;}int acpi_processor_resume(struct acpi_device * device){ acpi_idle_suspend = 0; return 0;}#ifndef CONFIG_CPU_IDLEstatic 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; /* * Interrupts must be disabled during bus mastering calculations and * for C2/C3 transitions. */ local_irq_disable(); pr = processors[smp_processor_id()]; if (!pr) { local_irq_enable(); return; } /* * 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 || acpi_idle_suspend) { if (pm_idle_save) pm_idle_save(); else acpi_safe_halt(); return; } /* * 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 > 31) diff = 31; pr->power.bm_activity <<= diff; acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status); if (bm_status) { pr->power.bm_activity |= 0x1; acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); } /* * 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 |= 0x1; } pr->power.bm_check_timestamp = jiffies; /* * If bus mastering is or was active this jiffy, demote * to avoid a faulty transition. Note that the processor * won't enter a low-power state during this call (to this * function) 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 & 0x1) && cx->demotion.threshold.bm) { local_irq_enable(); next_state = cx->demotion.state; goto end;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?