| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * processor_idle - idle state submodule to the ACPI processor driver | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> | 
|  | 5 | *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> | 
| Dominik Brodowski | c5ab81c | 2006-06-24 19:37:00 -0400 | [diff] [blame] | 6 | *  Copyright (C) 2004, 2005 Dominik Brodowski <linux@brodo.de> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7 | *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> | 
|  | 8 | *  			- Added processor hotplug support | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 9 | *  Copyright (C) 2005  Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | 
|  | 10 | *  			- Added support for C3 on SMP | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 11 | * | 
|  | 12 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
|  | 13 | * | 
|  | 14 | *  This program is free software; you can redistribute it and/or modify | 
|  | 15 | *  it under the terms of the GNU General Public License as published by | 
|  | 16 | *  the Free Software Foundation; either version 2 of the License, or (at | 
|  | 17 | *  your option) any later version. | 
|  | 18 | * | 
|  | 19 | *  This program is distributed in the hope that it will be useful, but | 
|  | 20 | *  WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 21 | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | 22 | *  General Public License for more details. | 
|  | 23 | * | 
|  | 24 | *  You should have received a copy of the GNU General Public License along | 
|  | 25 | *  with this program; if not, write to the Free Software Foundation, Inc., | 
|  | 26 | *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | 
|  | 27 | * | 
|  | 28 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
|  | 29 | */ | 
|  | 30 |  | 
|  | 31 | #include <linux/kernel.h> | 
|  | 32 | #include <linux/module.h> | 
|  | 33 | #include <linux/init.h> | 
|  | 34 | #include <linux/cpufreq.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 35 | #include <linux/slab.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 | #include <linux/acpi.h> | 
|  | 37 | #include <linux/dmi.h> | 
|  | 38 | #include <linux/moduleparam.h> | 
| Tim Schmielau | 4e57b68 | 2005-10-30 15:03:48 -0800 | [diff] [blame] | 39 | #include <linux/sched.h>	/* need_resched() */ | 
| Jean Pihet | e8db0be | 2011-08-25 15:35:03 +0200 | [diff] [blame] | 40 | #include <linux/pm_qos.h> | 
| Thomas Gleixner | e9e2cdb | 2007-02-16 01:28:04 -0800 | [diff] [blame] | 41 | #include <linux/clockchips.h> | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 42 | #include <linux/cpuidle.h> | 
| Russell King | ba84be2 | 2009-01-06 14:41:07 -0800 | [diff] [blame] | 43 | #include <linux/irqflags.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 |  | 
| Thomas Gleixner | 3434933 | 2007-02-16 01:27:54 -0800 | [diff] [blame] | 45 | /* | 
|  | 46 | * Include the apic definitions for x86 to have the APIC timer related defines | 
|  | 47 | * available also for UP (on SMP it gets magically included via linux/smp.h). | 
|  | 48 | * asm/acpi.h is not an option, as it would require more include magic. Also | 
|  | 49 | * creating an empty asm-ia64/apic.h would just trade pest vs. cholera. | 
|  | 50 | */ | 
|  | 51 | #ifdef CONFIG_X86 | 
|  | 52 | #include <asm/apic.h> | 
|  | 53 | #endif | 
|  | 54 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 | #include <asm/io.h> | 
|  | 56 | #include <asm/uaccess.h> | 
|  | 57 |  | 
|  | 58 | #include <acpi/acpi_bus.h> | 
|  | 59 | #include <acpi/processor.h> | 
| Zhao Yakui | c1e3b37 | 2008-06-24 17:58:53 +0800 | [diff] [blame] | 60 | #include <asm/processor.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 61 |  | 
| Len Brown | a192a95 | 2009-07-28 16:45:54 -0400 | [diff] [blame] | 62 | #define PREFIX "ACPI: " | 
|  | 63 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 | #define ACPI_PROCESSOR_CLASS            "processor" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 | #define _COMPONENT              ACPI_PROCESSOR_COMPONENT | 
| Len Brown | f52fd66 | 2007-02-12 22:42:12 -0500 | [diff] [blame] | 66 | ACPI_MODULE_NAME("processor_idle"); | 
| Ingo Molnar | 2aa44d0 | 2007-08-23 15:18:02 +0200 | [diff] [blame] | 67 | #define PM_TIMER_TICK_NS		(1000000000ULL/PM_TIMER_FREQUENCY) | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 68 | #define C2_OVERHEAD			1	/* 1us */ | 
|  | 69 | #define C3_OVERHEAD			1	/* 1us */ | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 70 | #define PM_TIMER_TICKS_TO_US(p)		(((p) * 1000)/(PM_TIMER_FREQUENCY/1000)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 71 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 72 | static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER; | 
|  | 73 | module_param(max_cstate, uint, 0000); | 
| Andreas Mohr | b683505 | 2006-04-27 05:25:00 -0400 | [diff] [blame] | 74 | static unsigned int nocst __read_mostly; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 75 | module_param(nocst, uint, 0000); | 
| Len Brown | d3e7e99 | 2010-07-22 17:23:10 -0400 | [diff] [blame] | 76 | static int bm_check_disable __read_mostly; | 
|  | 77 | module_param(bm_check_disable, uint, 0000); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 |  | 
| Len Brown | 25de571 | 2007-12-14 00:24:15 -0500 | [diff] [blame] | 79 | static unsigned int latency_factor __read_mostly = 2; | 
| Len Brown | 4963f62 | 2007-12-13 23:50:45 -0500 | [diff] [blame] | 80 | module_param(latency_factor, uint, 0644); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 81 |  | 
| Thomas Renninger | d189604 | 2010-11-03 17:06:14 +0100 | [diff] [blame] | 82 | static int disabled_by_idle_boot_param(void) | 
|  | 83 | { | 
|  | 84 | return boot_option_idle_override == IDLE_POLL || | 
|  | 85 | boot_option_idle_override == IDLE_FORCE_MWAIT || | 
|  | 86 | boot_option_idle_override == IDLE_HALT; | 
|  | 87 | } | 
|  | 88 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 | /* | 
|  | 90 | * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3. | 
|  | 91 | * For now disable this. Probably a bug somewhere else. | 
|  | 92 | * | 
|  | 93 | * To skip this limit, boot/load with a large max_cstate limit. | 
|  | 94 | */ | 
| Jeff Garzik | 1855256 | 2007-10-03 15:15:40 -0400 | [diff] [blame] | 95 | static int set_max_cstate(const struct dmi_system_id *id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 | { | 
|  | 97 | if (max_cstate > ACPI_PROCESSOR_MAX_POWER) | 
|  | 98 | return 0; | 
|  | 99 |  | 
| Len Brown | 3d35600 | 2005-08-03 00:22:52 -0400 | [diff] [blame] | 100 | printk(KERN_NOTICE PREFIX "%s detected - limiting to C%ld max_cstate." | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 101 | " Override with \"processor.max_cstate=%d\"\n", id->ident, | 
|  | 102 | (long)id->driver_data, ACPI_PROCESSOR_MAX_POWER + 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 |  | 
| Len Brown | 3d35600 | 2005-08-03 00:22:52 -0400 | [diff] [blame] | 104 | max_cstate = (long)id->driver_data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 |  | 
|  | 106 | return 0; | 
|  | 107 | } | 
|  | 108 |  | 
| Ashok Raj | 7ded568 | 2006-02-03 21:51:23 +0100 | [diff] [blame] | 109 | /* Actually this shouldn't be __cpuinitdata, would be better to fix the | 
|  | 110 | callers to only run once -AK */ | 
|  | 111 | static struct dmi_system_id __cpuinitdata processor_power_dmi_table[] = { | 
| Thomas Rosner | 876c184 | 2006-01-06 01:31:00 -0500 | [diff] [blame] | 112 | { set_max_cstate, "Clevo 5600D", { | 
|  | 113 | DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), | 
|  | 114 | DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")}, | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 115 | (void *)2}, | 
| Arjan van de Ven | 370d5cd | 2010-01-27 15:25:39 -0800 | [diff] [blame] | 116 | { set_max_cstate, "Pavilion zv5000", { | 
|  | 117 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | 
|  | 118 | DMI_MATCH(DMI_PRODUCT_NAME,"Pavilion zv5000 (DS502A#ABA)")}, | 
|  | 119 | (void *)1}, | 
|  | 120 | { set_max_cstate, "Asus L8400B", { | 
|  | 121 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), | 
|  | 122 | DMI_MATCH(DMI_PRODUCT_NAME,"L8400B series Notebook PC")}, | 
|  | 123 | (void *)1}, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 124 | {}, | 
|  | 125 | }; | 
|  | 126 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 127 |  | 
| venkatesh.pallipadi@intel.com | 2e90665 | 2008-01-31 17:35:03 -0800 | [diff] [blame] | 128 | /* | 
|  | 129 | * Callers should disable interrupts before the call and enable | 
|  | 130 | * interrupts after return. | 
|  | 131 | */ | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 132 | static void acpi_safe_halt(void) | 
|  | 133 | { | 
|  | 134 | current_thread_info()->status &= ~TS_POLLING; | 
|  | 135 | /* | 
|  | 136 | * TS_POLLING-cleared state must be visible before we | 
|  | 137 | * test NEED_RESCHED: | 
|  | 138 | */ | 
|  | 139 | smp_mb(); | 
| Venki Pallipadi | 71e93d1 | 2008-03-13 17:18:19 -0700 | [diff] [blame] | 140 | if (!need_resched()) { | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 141 | safe_halt(); | 
| Venki Pallipadi | 71e93d1 | 2008-03-13 17:18:19 -0700 | [diff] [blame] | 142 | local_irq_disable(); | 
|  | 143 | } | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 144 | current_thread_info()->status |= TS_POLLING; | 
|  | 145 | } | 
|  | 146 |  | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 147 | #ifdef ARCH_APICTIMER_STOPS_ON_C3 | 
|  | 148 |  | 
|  | 149 | /* | 
|  | 150 | * Some BIOS implementations switch to C3 in the published C2 state. | 
| Linus Torvalds | 296d93c | 2007-03-23 08:03:47 -0700 | [diff] [blame] | 151 | * This seems to be a common problem on AMD boxen, but other vendors | 
|  | 152 | * are affected too. We pick the most conservative approach: we assume | 
|  | 153 | * that the local APIC stops in both C2 and C3. | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 154 | */ | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 155 | static void lapic_timer_check_state(int state, struct acpi_processor *pr, | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 156 | struct acpi_processor_cx *cx) | 
|  | 157 | { | 
|  | 158 | struct acpi_processor_power *pwr = &pr->power; | 
| Thomas Gleixner | e585bef | 2007-03-23 16:08:01 +0100 | [diff] [blame] | 159 | u8 type = local_apic_timer_c2_ok ? ACPI_STATE_C3 : ACPI_STATE_C2; | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 160 |  | 
| Venkatesh Pallipadi | db954b5 | 2009-04-06 18:51:29 -0700 | [diff] [blame] | 161 | if (cpu_has(&cpu_data(pr->id), X86_FEATURE_ARAT)) | 
|  | 162 | return; | 
|  | 163 |  | 
| Len Brown | 02c68a0 | 2011-04-01 16:59:53 -0400 | [diff] [blame] | 164 | if (amd_e400_c1e_detected) | 
| Shaohua Li | 87ad57b | 2009-05-19 16:09:42 +0800 | [diff] [blame] | 165 | type = ACPI_STATE_C1; | 
|  | 166 |  | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 167 | /* | 
|  | 168 | * Check, if one of the previous states already marked the lapic | 
|  | 169 | * unstable | 
|  | 170 | */ | 
|  | 171 | if (pwr->timer_broadcast_on_state < state) | 
|  | 172 | return; | 
|  | 173 |  | 
| Thomas Gleixner | e585bef | 2007-03-23 16:08:01 +0100 | [diff] [blame] | 174 | if (cx->type >= type) | 
| Linus Torvalds | 296d93c | 2007-03-23 08:03:47 -0700 | [diff] [blame] | 175 | pr->power.timer_broadcast_on_state = state; | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 176 | } | 
|  | 177 |  | 
| Hidetoshi Seto | 918aae4 | 2009-12-14 17:10:06 +0900 | [diff] [blame] | 178 | static void __lapic_timer_propagate_broadcast(void *arg) | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 179 | { | 
| Suresh Siddha | f833bab | 2009-08-17 14:34:59 -0700 | [diff] [blame] | 180 | struct acpi_processor *pr = (struct acpi_processor *) arg; | 
| Thomas Gleixner | e9e2cdb | 2007-02-16 01:28:04 -0800 | [diff] [blame] | 181 | unsigned long reason; | 
|  | 182 |  | 
|  | 183 | reason = pr->power.timer_broadcast_on_state < INT_MAX ? | 
|  | 184 | CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF; | 
|  | 185 |  | 
|  | 186 | clockevents_notify(reason, &pr->id); | 
| Thomas Gleixner | e9e2cdb | 2007-02-16 01:28:04 -0800 | [diff] [blame] | 187 | } | 
|  | 188 |  | 
| Hidetoshi Seto | 918aae4 | 2009-12-14 17:10:06 +0900 | [diff] [blame] | 189 | static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) | 
|  | 190 | { | 
|  | 191 | smp_call_function_single(pr->id, __lapic_timer_propagate_broadcast, | 
|  | 192 | (void *)pr, 1); | 
|  | 193 | } | 
|  | 194 |  | 
| Thomas Gleixner | e9e2cdb | 2007-02-16 01:28:04 -0800 | [diff] [blame] | 195 | /* Power(C) State timer broadcast control */ | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 196 | static void lapic_timer_state_broadcast(struct acpi_processor *pr, | 
| Thomas Gleixner | e9e2cdb | 2007-02-16 01:28:04 -0800 | [diff] [blame] | 197 | struct acpi_processor_cx *cx, | 
|  | 198 | int broadcast) | 
|  | 199 | { | 
| Thomas Gleixner | e9e2cdb | 2007-02-16 01:28:04 -0800 | [diff] [blame] | 200 | int state = cx - pr->power.states; | 
|  | 201 |  | 
|  | 202 | if (state >= pr->power.timer_broadcast_on_state) { | 
|  | 203 | unsigned long reason; | 
|  | 204 |  | 
|  | 205 | reason = broadcast ?  CLOCK_EVT_NOTIFY_BROADCAST_ENTER : | 
|  | 206 | CLOCK_EVT_NOTIFY_BROADCAST_EXIT; | 
|  | 207 | clockevents_notify(reason, &pr->id); | 
|  | 208 | } | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 209 | } | 
|  | 210 |  | 
|  | 211 | #else | 
|  | 212 |  | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 213 | static void lapic_timer_check_state(int state, struct acpi_processor *pr, | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 214 | struct acpi_processor_cx *cstate) { } | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 215 | static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { } | 
|  | 216 | static void lapic_timer_state_broadcast(struct acpi_processor *pr, | 
| Thomas Gleixner | e9e2cdb | 2007-02-16 01:28:04 -0800 | [diff] [blame] | 217 | struct acpi_processor_cx *cx, | 
|  | 218 | int broadcast) | 
|  | 219 | { | 
|  | 220 | } | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 221 |  | 
|  | 222 | #endif | 
|  | 223 |  | 
| Thomas Gleixner | b04e7bd | 2007-09-22 22:29:05 +0000 | [diff] [blame] | 224 | /* | 
|  | 225 | * Suspend / resume control | 
|  | 226 | */ | 
| Len Brown | 815ab0f | 2009-05-07 22:19:45 -0400 | [diff] [blame] | 227 | static u32 saved_bm_rld; | 
|  | 228 |  | 
|  | 229 | static void acpi_idle_bm_rld_save(void) | 
|  | 230 | { | 
|  | 231 | acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &saved_bm_rld); | 
|  | 232 | } | 
|  | 233 | static void acpi_idle_bm_rld_restore(void) | 
|  | 234 | { | 
|  | 235 | u32 resumed_bm_rld; | 
|  | 236 |  | 
|  | 237 | acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &resumed_bm_rld); | 
|  | 238 |  | 
|  | 239 | if (resumed_bm_rld != saved_bm_rld) | 
|  | 240 | acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); | 
|  | 241 | } | 
| Thomas Gleixner | b04e7bd | 2007-09-22 22:29:05 +0000 | [diff] [blame] | 242 |  | 
|  | 243 | int acpi_processor_suspend(struct acpi_device * device, pm_message_t state) | 
|  | 244 | { | 
| Len Brown | 815ab0f | 2009-05-07 22:19:45 -0400 | [diff] [blame] | 245 | acpi_idle_bm_rld_save(); | 
| Thomas Gleixner | b04e7bd | 2007-09-22 22:29:05 +0000 | [diff] [blame] | 246 | return 0; | 
|  | 247 | } | 
|  | 248 |  | 
|  | 249 | int acpi_processor_resume(struct acpi_device * device) | 
|  | 250 | { | 
| Len Brown | 815ab0f | 2009-05-07 22:19:45 -0400 | [diff] [blame] | 251 | acpi_idle_bm_rld_restore(); | 
| Thomas Gleixner | b04e7bd | 2007-09-22 22:29:05 +0000 | [diff] [blame] | 252 | return 0; | 
|  | 253 | } | 
|  | 254 |  | 
| John Stultz | 592913e | 2010-07-13 17:56:20 -0700 | [diff] [blame] | 255 | #if defined(CONFIG_X86) | 
| Len Brown | 520daf7 | 2009-05-14 17:27:38 -0400 | [diff] [blame] | 256 | static void tsc_check_state(int state) | 
| Andi Kleen | ddb25f9 | 2008-01-30 13:32:41 +0100 | [diff] [blame] | 257 | { | 
|  | 258 | switch (boot_cpu_data.x86_vendor) { | 
|  | 259 | case X86_VENDOR_AMD: | 
| Venki Pallipadi | 40fb171 | 2008-11-17 16:11:37 -0800 | [diff] [blame] | 260 | case X86_VENDOR_INTEL: | 
| Andi Kleen | ddb25f9 | 2008-01-30 13:32:41 +0100 | [diff] [blame] | 261 | /* | 
|  | 262 | * AMD Fam10h TSC will tick in all | 
|  | 263 | * C/P/S0/S1 states when this bit is set. | 
|  | 264 | */ | 
| Venki Pallipadi | 40fb171 | 2008-11-17 16:11:37 -0800 | [diff] [blame] | 265 | if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) | 
| Len Brown | 520daf7 | 2009-05-14 17:27:38 -0400 | [diff] [blame] | 266 | return; | 
| Venki Pallipadi | 40fb171 | 2008-11-17 16:11:37 -0800 | [diff] [blame] | 267 |  | 
| Andi Kleen | ddb25f9 | 2008-01-30 13:32:41 +0100 | [diff] [blame] | 268 | /*FALL THROUGH*/ | 
| Andi Kleen | ddb25f9 | 2008-01-30 13:32:41 +0100 | [diff] [blame] | 269 | default: | 
| Len Brown | 520daf7 | 2009-05-14 17:27:38 -0400 | [diff] [blame] | 270 | /* TSC could halt in idle, so notify users */ | 
|  | 271 | if (state > ACPI_STATE_C1) | 
|  | 272 | mark_tsc_unstable("TSC halts in idle"); | 
| Andi Kleen | ddb25f9 | 2008-01-30 13:32:41 +0100 | [diff] [blame] | 273 | } | 
|  | 274 | } | 
| Len Brown | 520daf7 | 2009-05-14 17:27:38 -0400 | [diff] [blame] | 275 | #else | 
|  | 276 | static void tsc_check_state(int state) { return; } | 
| Andi Kleen | ddb25f9 | 2008-01-30 13:32:41 +0100 | [diff] [blame] | 277 | #endif | 
|  | 278 |  | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 279 | static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 280 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 281 |  | 
|  | 282 | if (!pr) | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 283 | return -EINVAL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 284 |  | 
|  | 285 | if (!pr->pblk) | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 286 | return -ENODEV; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 287 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 288 | /* if info is obtained from pblk/fadt, type equals state */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 289 | pr->power.states[ACPI_STATE_C2].type = ACPI_STATE_C2; | 
|  | 290 | pr->power.states[ACPI_STATE_C3].type = ACPI_STATE_C3; | 
|  | 291 |  | 
| Venkatesh Pallipadi | 4c03355 | 2005-09-15 12:20:00 -0400 | [diff] [blame] | 292 | #ifndef CONFIG_HOTPLUG_CPU | 
|  | 293 | /* | 
|  | 294 | * Check for P_LVL2_UP flag before entering C2 and above on | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 295 | * an SMP system. | 
| Venkatesh Pallipadi | 4c03355 | 2005-09-15 12:20:00 -0400 | [diff] [blame] | 296 | */ | 
| Alexey Starikovskiy | ad71860 | 2007-02-02 19:48:19 +0300 | [diff] [blame] | 297 | if ((num_online_cpus() > 1) && | 
| Alexey Starikovskiy | cee324b | 2007-02-02 19:48:22 +0300 | [diff] [blame] | 298 | !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 299 | return -ENODEV; | 
| Venkatesh Pallipadi | 4c03355 | 2005-09-15 12:20:00 -0400 | [diff] [blame] | 300 | #endif | 
|  | 301 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | /* determine C2 and C3 address from pblk */ | 
|  | 303 | pr->power.states[ACPI_STATE_C2].address = pr->pblk + 4; | 
|  | 304 | pr->power.states[ACPI_STATE_C3].address = pr->pblk + 5; | 
|  | 305 |  | 
|  | 306 | /* determine latencies from FADT */ | 
| Alexey Starikovskiy | cee324b | 2007-02-02 19:48:22 +0300 | [diff] [blame] | 307 | pr->power.states[ACPI_STATE_C2].latency = acpi_gbl_FADT.C2latency; | 
|  | 308 | pr->power.states[ACPI_STATE_C3].latency = acpi_gbl_FADT.C3latency; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 309 |  | 
| Len Brown | 5d76b6f | 2010-01-19 22:41:14 -0500 | [diff] [blame] | 310 | /* | 
|  | 311 | * FADT specified C2 latency must be less than or equal to | 
|  | 312 | * 100 microseconds. | 
|  | 313 | */ | 
|  | 314 | if (acpi_gbl_FADT.C2latency > ACPI_PROCESSOR_MAX_C2_LATENCY) { | 
|  | 315 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 
|  | 316 | "C2 latency too large [%d]\n", acpi_gbl_FADT.C2latency)); | 
|  | 317 | /* invalidate C2 */ | 
|  | 318 | pr->power.states[ACPI_STATE_C2].address = 0; | 
|  | 319 | } | 
|  | 320 |  | 
| Len Brown | a6d72c1 | 2010-01-19 23:10:04 -0500 | [diff] [blame] | 321 | /* | 
|  | 322 | * FADT supplied C3 latency must be less than or equal to | 
|  | 323 | * 1000 microseconds. | 
|  | 324 | */ | 
|  | 325 | if (acpi_gbl_FADT.C3latency > ACPI_PROCESSOR_MAX_C3_LATENCY) { | 
|  | 326 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 
|  | 327 | "C3 latency too large [%d]\n", acpi_gbl_FADT.C3latency)); | 
|  | 328 | /* invalidate C3 */ | 
|  | 329 | pr->power.states[ACPI_STATE_C3].address = 0; | 
|  | 330 | } | 
|  | 331 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 
|  | 333 | "lvl2[0x%08x] lvl3[0x%08x]\n", | 
|  | 334 | pr->power.states[ACPI_STATE_C2].address, | 
|  | 335 | pr->power.states[ACPI_STATE_C3].address)); | 
|  | 336 |  | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 337 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 | } | 
|  | 339 |  | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 340 | static int acpi_processor_get_power_info_default(struct acpi_processor *pr) | 
| Venkatesh Pallipadi | acf05f4 | 2005-03-31 23:23:15 -0500 | [diff] [blame] | 341 | { | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 342 | if (!pr->power.states[ACPI_STATE_C1].valid) { | 
|  | 343 | /* set the first C-State to C1 */ | 
|  | 344 | /* all processors need to support C1 */ | 
|  | 345 | pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; | 
|  | 346 | pr->power.states[ACPI_STATE_C1].valid = 1; | 
| Venkatesh Pallipadi | 0fda6b4 | 2008-04-09 21:31:46 -0400 | [diff] [blame] | 347 | pr->power.states[ACPI_STATE_C1].entry_method = ACPI_CSTATE_HALT; | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 348 | } | 
|  | 349 | /* the C0 state only exists as a filler in our array */ | 
| Venkatesh Pallipadi | acf05f4 | 2005-03-31 23:23:15 -0500 | [diff] [blame] | 350 | pr->power.states[ACPI_STATE_C0].valid = 1; | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 351 | return 0; | 
| Venkatesh Pallipadi | acf05f4 | 2005-03-31 23:23:15 -0500 | [diff] [blame] | 352 | } | 
|  | 353 |  | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 354 | static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 355 | { | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 356 | acpi_status status = 0; | 
| Lin Ming | 439913f | 2010-01-28 10:53:19 +0800 | [diff] [blame] | 357 | u64 count; | 
| Janosch Machowinski | cf82478 | 2005-08-20 08:02:00 -0400 | [diff] [blame] | 358 | int current_count; | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 359 | int i; | 
|  | 360 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 
|  | 361 | union acpi_object *cst; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 362 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 363 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 364 | if (nocst) | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 365 | return -ENODEV; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 366 |  | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 367 | current_count = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 368 |  | 
|  | 369 | status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer); | 
|  | 370 | if (ACPI_FAILURE(status)) { | 
|  | 371 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _CST, giving up\n")); | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 372 | return -ENODEV; | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 373 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 374 |  | 
| Jan Engelhardt | 50dd096 | 2006-10-01 00:28:50 +0200 | [diff] [blame] | 375 | cst = buffer.pointer; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 376 |  | 
|  | 377 | /* There must be at least 2 elements */ | 
|  | 378 | if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) { | 
| Len Brown | 6468463 | 2006-06-26 23:41:38 -0400 | [diff] [blame] | 379 | printk(KERN_ERR PREFIX "not enough elements in _CST\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 380 | status = -EFAULT; | 
|  | 381 | goto end; | 
|  | 382 | } | 
|  | 383 |  | 
|  | 384 | count = cst->package.elements[0].integer.value; | 
|  | 385 |  | 
|  | 386 | /* Validate number of power states. */ | 
|  | 387 | if (count < 1 || count != cst->package.count - 1) { | 
| Len Brown | 6468463 | 2006-06-26 23:41:38 -0400 | [diff] [blame] | 388 | printk(KERN_ERR PREFIX "count given by _CST is not valid\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 389 | status = -EFAULT; | 
|  | 390 | goto end; | 
|  | 391 | } | 
|  | 392 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 393 | /* Tell driver that at least _CST is supported. */ | 
|  | 394 | pr->flags.has_cst = 1; | 
|  | 395 |  | 
|  | 396 | for (i = 1; i <= count; i++) { | 
|  | 397 | union acpi_object *element; | 
|  | 398 | union acpi_object *obj; | 
|  | 399 | struct acpi_power_register *reg; | 
|  | 400 | struct acpi_processor_cx cx; | 
|  | 401 |  | 
|  | 402 | memset(&cx, 0, sizeof(cx)); | 
|  | 403 |  | 
| Jan Engelhardt | 50dd096 | 2006-10-01 00:28:50 +0200 | [diff] [blame] | 404 | element = &(cst->package.elements[i]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 405 | if (element->type != ACPI_TYPE_PACKAGE) | 
|  | 406 | continue; | 
|  | 407 |  | 
|  | 408 | if (element->package.count != 4) | 
|  | 409 | continue; | 
|  | 410 |  | 
| Jan Engelhardt | 50dd096 | 2006-10-01 00:28:50 +0200 | [diff] [blame] | 411 | obj = &(element->package.elements[0]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 412 |  | 
|  | 413 | if (obj->type != ACPI_TYPE_BUFFER) | 
|  | 414 | continue; | 
|  | 415 |  | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 416 | reg = (struct acpi_power_register *)obj->buffer.pointer; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 417 |  | 
|  | 418 | if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 419 | (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 420 | continue; | 
|  | 421 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 422 | /* There should be an easy way to extract an integer... */ | 
| Jan Engelhardt | 50dd096 | 2006-10-01 00:28:50 +0200 | [diff] [blame] | 423 | obj = &(element->package.elements[1]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 424 | if (obj->type != ACPI_TYPE_INTEGER) | 
|  | 425 | continue; | 
|  | 426 |  | 
|  | 427 | cx.type = obj->integer.value; | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 428 | /* | 
|  | 429 | * Some buggy BIOSes won't list C1 in _CST - | 
|  | 430 | * Let acpi_processor_get_power_info_default() handle them later | 
|  | 431 | */ | 
|  | 432 | if (i == 1 && cx.type != ACPI_STATE_C1) | 
|  | 433 | current_count++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 434 |  | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 435 | cx.address = reg->address; | 
|  | 436 | cx.index = current_count + 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 437 |  | 
| venkatesh.pallipadi@intel.com | bc71bec | 2008-01-31 17:35:04 -0800 | [diff] [blame] | 438 | cx.entry_method = ACPI_CSTATE_SYSTEMIO; | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 439 | if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { | 
|  | 440 | if (acpi_processor_ffh_cstate_probe | 
|  | 441 | (pr->id, &cx, reg) == 0) { | 
| venkatesh.pallipadi@intel.com | bc71bec | 2008-01-31 17:35:04 -0800 | [diff] [blame] | 442 | cx.entry_method = ACPI_CSTATE_FFH; | 
|  | 443 | } else if (cx.type == ACPI_STATE_C1) { | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 444 | /* | 
|  | 445 | * C1 is a special case where FIXED_HARDWARE | 
|  | 446 | * can be handled in non-MWAIT way as well. | 
|  | 447 | * In that case, save this _CST entry info. | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 448 | * Otherwise, ignore this info and continue. | 
|  | 449 | */ | 
| venkatesh.pallipadi@intel.com | bc71bec | 2008-01-31 17:35:04 -0800 | [diff] [blame] | 450 | cx.entry_method = ACPI_CSTATE_HALT; | 
| Venkatesh Pallipadi | 4fcb2fc | 2008-02-11 17:46:31 -0800 | [diff] [blame] | 451 | snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT"); | 
| venkatesh.pallipadi@intel.com | bc71bec | 2008-01-31 17:35:04 -0800 | [diff] [blame] | 452 | } else { | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 453 | continue; | 
|  | 454 | } | 
| Zhao Yakui | da5e09a | 2008-06-24 18:01:09 +0800 | [diff] [blame] | 455 | if (cx.type == ACPI_STATE_C1 && | 
| Thomas Renninger | d189604 | 2010-11-03 17:06:14 +0100 | [diff] [blame] | 456 | (boot_option_idle_override == IDLE_NOMWAIT)) { | 
| Zhao Yakui | c1e3b37 | 2008-06-24 17:58:53 +0800 | [diff] [blame] | 457 | /* | 
|  | 458 | * In most cases the C1 space_id obtained from | 
|  | 459 | * _CST object is FIXED_HARDWARE access mode. | 
|  | 460 | * But when the option of idle=halt is added, | 
|  | 461 | * the entry_method type should be changed from | 
|  | 462 | * CSTATE_FFH to CSTATE_HALT. | 
| Zhao Yakui | da5e09a | 2008-06-24 18:01:09 +0800 | [diff] [blame] | 463 | * When the option of idle=nomwait is added, | 
|  | 464 | * the C1 entry_method type should be | 
|  | 465 | * CSTATE_HALT. | 
| Zhao Yakui | c1e3b37 | 2008-06-24 17:58:53 +0800 | [diff] [blame] | 466 | */ | 
|  | 467 | cx.entry_method = ACPI_CSTATE_HALT; | 
|  | 468 | snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT"); | 
|  | 469 | } | 
| Venkatesh Pallipadi | 4fcb2fc | 2008-02-11 17:46:31 -0800 | [diff] [blame] | 470 | } else { | 
|  | 471 | snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI IOPORT 0x%x", | 
|  | 472 | cx.address); | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 473 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 474 |  | 
| Venkatesh Pallipadi | 0fda6b4 | 2008-04-09 21:31:46 -0400 | [diff] [blame] | 475 | if (cx.type == ACPI_STATE_C1) { | 
|  | 476 | cx.valid = 1; | 
|  | 477 | } | 
| Venkatesh Pallipadi | 4fcb2fc | 2008-02-11 17:46:31 -0800 | [diff] [blame] | 478 |  | 
| Jan Engelhardt | 50dd096 | 2006-10-01 00:28:50 +0200 | [diff] [blame] | 479 | obj = &(element->package.elements[2]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 480 | if (obj->type != ACPI_TYPE_INTEGER) | 
|  | 481 | continue; | 
|  | 482 |  | 
|  | 483 | cx.latency = obj->integer.value; | 
|  | 484 |  | 
| Jan Engelhardt | 50dd096 | 2006-10-01 00:28:50 +0200 | [diff] [blame] | 485 | obj = &(element->package.elements[3]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 486 | if (obj->type != ACPI_TYPE_INTEGER) | 
|  | 487 | continue; | 
|  | 488 |  | 
|  | 489 | cx.power = obj->integer.value; | 
|  | 490 |  | 
| Janosch Machowinski | cf82478 | 2005-08-20 08:02:00 -0400 | [diff] [blame] | 491 | current_count++; | 
|  | 492 | memcpy(&(pr->power.states[current_count]), &cx, sizeof(cx)); | 
|  | 493 |  | 
|  | 494 | /* | 
|  | 495 | * We support total ACPI_PROCESSOR_MAX_POWER - 1 | 
|  | 496 | * (From 1 through ACPI_PROCESSOR_MAX_POWER - 1) | 
|  | 497 | */ | 
|  | 498 | if (current_count >= (ACPI_PROCESSOR_MAX_POWER - 1)) { | 
|  | 499 | printk(KERN_WARNING | 
|  | 500 | "Limiting number of power states to max (%d)\n", | 
|  | 501 | ACPI_PROCESSOR_MAX_POWER); | 
|  | 502 | printk(KERN_WARNING | 
|  | 503 | "Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); | 
|  | 504 | break; | 
|  | 505 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 506 | } | 
|  | 507 |  | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 508 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d power states\n", | 
| Janosch Machowinski | cf82478 | 2005-08-20 08:02:00 -0400 | [diff] [blame] | 509 | current_count)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 510 |  | 
|  | 511 | /* Validate number of power states discovered */ | 
| Janosch Machowinski | cf82478 | 2005-08-20 08:02:00 -0400 | [diff] [blame] | 512 | if (current_count < 2) | 
| Venkatesh Pallipadi | 6d93c64 | 2005-09-15 12:19:00 -0400 | [diff] [blame] | 513 | status = -EFAULT; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 514 |  | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 515 | end: | 
| Len Brown | 02438d8 | 2006-06-30 03:19:10 -0400 | [diff] [blame] | 516 | kfree(buffer.pointer); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 517 |  | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 518 | return status; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 519 | } | 
|  | 520 |  | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 521 | static void acpi_processor_power_verify_c3(struct acpi_processor *pr, | 
|  | 522 | struct acpi_processor_cx *cx) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 523 | { | 
| Pallipadi, Venkatesh | ee1ca48 | 2009-05-21 17:09:10 -0700 | [diff] [blame] | 524 | static int bm_check_flag = -1; | 
|  | 525 | static int bm_control_flag = -1; | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 526 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 527 |  | 
|  | 528 | if (!cx->address) | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 529 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 530 |  | 
|  | 531 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 532 | * PIIX4 Erratum #18: We don't support C3 when Type-F (fast) | 
|  | 533 | * DMA transfers are used by any ISA device to avoid livelock. | 
|  | 534 | * Note that we could disable Type-F DMA (as recommended by | 
|  | 535 | * the erratum), but this is known to disrupt certain ISA | 
|  | 536 | * devices thus we take the conservative approach. | 
|  | 537 | */ | 
|  | 538 | else if (errata.piix4.fdma) { | 
|  | 539 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 540 | "C3 not supported on PIIX4 with Type-F DMA\n")); | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 541 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 542 | } | 
|  | 543 |  | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 544 | /* All the logic here assumes flags.bm_check is same across all CPUs */ | 
| Pallipadi, Venkatesh | ee1ca48 | 2009-05-21 17:09:10 -0700 | [diff] [blame] | 545 | if (bm_check_flag == -1) { | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 546 | /* Determine whether bm_check is needed based on CPU  */ | 
|  | 547 | acpi_processor_power_init_bm_check(&(pr->flags), pr->id); | 
|  | 548 | bm_check_flag = pr->flags.bm_check; | 
| Pallipadi, Venkatesh | ee1ca48 | 2009-05-21 17:09:10 -0700 | [diff] [blame] | 549 | bm_control_flag = pr->flags.bm_control; | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 550 | } else { | 
|  | 551 | pr->flags.bm_check = bm_check_flag; | 
| Pallipadi, Venkatesh | ee1ca48 | 2009-05-21 17:09:10 -0700 | [diff] [blame] | 552 | pr->flags.bm_control = bm_control_flag; | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 553 | } | 
|  | 554 |  | 
|  | 555 | if (pr->flags.bm_check) { | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 556 | if (!pr->flags.bm_control) { | 
| Venki Pallipadi | ed3110e | 2007-07-31 12:04:31 -0700 | [diff] [blame] | 557 | if (pr->flags.has_cst != 1) { | 
|  | 558 | /* bus mastering control is necessary */ | 
|  | 559 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 
|  | 560 | "C3 support requires BM control\n")); | 
|  | 561 | return; | 
|  | 562 | } else { | 
|  | 563 | /* Here we enter C3 without bus mastering */ | 
|  | 564 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 
|  | 565 | "C3 support without BM control\n")); | 
|  | 566 | } | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 567 | } | 
|  | 568 | } else { | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 569 | /* | 
|  | 570 | * WBINVD should be set in fadt, for C3 state to be | 
|  | 571 | * supported on when bm_check is not required. | 
|  | 572 | */ | 
| Alexey Starikovskiy | cee324b | 2007-02-02 19:48:22 +0300 | [diff] [blame] | 573 | if (!(acpi_gbl_FADT.flags & ACPI_FADT_WBINVD)) { | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 574 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 575 | "Cache invalidation should work properly" | 
|  | 576 | " for C3 to be enabled on SMP systems\n")); | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 577 | return; | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 578 | } | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 579 | } | 
|  | 580 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 581 | /* | 
|  | 582 | * Otherwise we've met all of our C3 requirements. | 
|  | 583 | * Normalize the C3 latency to expidite policy.  Enable | 
|  | 584 | * checking of bus mastering status (bm_check) so we can | 
|  | 585 | * use this in our C3 policy | 
|  | 586 | */ | 
|  | 587 | cx->valid = 1; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 588 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 589 | cx->latency_ticks = cx->latency; | 
| Len Brown | 31878dd | 2009-01-28 18:28:09 -0500 | [diff] [blame] | 590 | /* | 
|  | 591 | * On older chipsets, BM_RLD needs to be set | 
|  | 592 | * in order for Bus Master activity to wake the | 
|  | 593 | * system from C3.  Newer chipsets handle DMA | 
|  | 594 | * during C3 automatically and BM_RLD is a NOP. | 
|  | 595 | * In either case, the proper way to | 
|  | 596 | * handle BM_RLD is to set it and leave it set. | 
|  | 597 | */ | 
| Bob Moore | 50ffba1 | 2009-02-23 15:02:07 +0800 | [diff] [blame] | 598 | acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 599 |  | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 600 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 601 | } | 
|  | 602 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 603 | static int acpi_processor_power_verify(struct acpi_processor *pr) | 
|  | 604 | { | 
|  | 605 | unsigned int i; | 
|  | 606 | unsigned int working = 0; | 
| Venkatesh Pallipadi | 6eb0a0f | 2006-01-11 22:44:21 +0100 | [diff] [blame] | 607 |  | 
| Thomas Gleixner | 169a0ab | 2007-02-16 01:27:55 -0800 | [diff] [blame] | 608 | pr->power.timer_broadcast_on_state = INT_MAX; | 
| Venkatesh Pallipadi | 6eb0a0f | 2006-01-11 22:44:21 +0100 | [diff] [blame] | 609 |  | 
| Len Brown | a0bf284 | 2009-05-15 01:29:31 -0400 | [diff] [blame] | 610 | for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 611 | struct acpi_processor_cx *cx = &pr->power.states[i]; | 
|  | 612 |  | 
|  | 613 | switch (cx->type) { | 
|  | 614 | case ACPI_STATE_C1: | 
|  | 615 | cx->valid = 1; | 
|  | 616 | break; | 
|  | 617 |  | 
|  | 618 | case ACPI_STATE_C2: | 
| Len Brown | d22edd2 | 2010-01-19 23:29:09 -0500 | [diff] [blame] | 619 | if (!cx->address) | 
|  | 620 | break; | 
|  | 621 | cx->valid = 1; | 
|  | 622 | cx->latency_ticks = cx->latency; /* Normalize latency */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 623 | break; | 
|  | 624 |  | 
|  | 625 | case ACPI_STATE_C3: | 
|  | 626 | acpi_processor_power_verify_c3(pr, cx); | 
|  | 627 | break; | 
|  | 628 | } | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 629 | if (!cx->valid) | 
|  | 630 | continue; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 631 |  | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 632 | lapic_timer_check_state(i, pr, cx); | 
|  | 633 | tsc_check_state(cx->type); | 
|  | 634 | working++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 635 | } | 
|  | 636 |  | 
| Hidetoshi Seto | 918aae4 | 2009-12-14 17:10:06 +0900 | [diff] [blame] | 637 | lapic_timer_propagate_broadcast(pr); | 
| Andi Kleen | bd66334 | 2006-03-25 16:31:07 +0100 | [diff] [blame] | 638 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 639 | return (working); | 
|  | 640 | } | 
|  | 641 |  | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 642 | static int acpi_processor_get_power_info(struct acpi_processor *pr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 643 | { | 
|  | 644 | unsigned int i; | 
|  | 645 | int result; | 
|  | 646 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 647 |  | 
|  | 648 | /* NOTE: the idle thread may not be running while calling | 
|  | 649 | * this function */ | 
|  | 650 |  | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 651 | /* Zero initialize all the C-states info. */ | 
|  | 652 | memset(pr->power.states, 0, sizeof(pr->power.states)); | 
|  | 653 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 654 | result = acpi_processor_get_power_info_cst(pr); | 
| Venkatesh Pallipadi | 6d93c64 | 2005-09-15 12:19:00 -0400 | [diff] [blame] | 655 | if (result == -ENODEV) | 
| Darrick J. Wong | c5a114f | 2006-10-19 23:28:28 -0700 | [diff] [blame] | 656 | result = acpi_processor_get_power_info_fadt(pr); | 
| Venkatesh Pallipadi | 6d93c64 | 2005-09-15 12:19:00 -0400 | [diff] [blame] | 657 |  | 
| Venkatesh Pallipadi | 991528d | 2006-09-25 16:28:13 -0700 | [diff] [blame] | 658 | if (result) | 
|  | 659 | return result; | 
|  | 660 |  | 
|  | 661 | acpi_processor_get_power_info_default(pr); | 
|  | 662 |  | 
| Janosch Machowinski | cf82478 | 2005-08-20 08:02:00 -0400 | [diff] [blame] | 663 | pr->power.count = acpi_processor_power_verify(pr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 664 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 665 | /* | 
|  | 666 | * if one state of type C2 or C3 is available, mark this | 
|  | 667 | * CPU as being "idle manageable" | 
|  | 668 | */ | 
|  | 669 | for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { | 
| Venkatesh Pallipadi | acf05f4 | 2005-03-31 23:23:15 -0500 | [diff] [blame] | 670 | if (pr->power.states[i].valid) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 671 | pr->power.count = i; | 
| Linus Torvalds | 2203d6e | 2005-11-18 07:29:51 -0800 | [diff] [blame] | 672 | if (pr->power.states[i].type >= ACPI_STATE_C2) | 
|  | 673 | pr->flags.power = 1; | 
| Venkatesh Pallipadi | acf05f4 | 2005-03-31 23:23:15 -0500 | [diff] [blame] | 674 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 675 | } | 
|  | 676 |  | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 677 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 678 | } | 
|  | 679 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 680 | /** | 
|  | 681 | * acpi_idle_bm_check - checks if bus master activity was detected | 
|  | 682 | */ | 
|  | 683 | static int acpi_idle_bm_check(void) | 
|  | 684 | { | 
|  | 685 | u32 bm_status = 0; | 
|  | 686 |  | 
| Len Brown | d3e7e99 | 2010-07-22 17:23:10 -0400 | [diff] [blame] | 687 | if (bm_check_disable) | 
|  | 688 | return 0; | 
|  | 689 |  | 
| Bob Moore | 50ffba1 | 2009-02-23 15:02:07 +0800 | [diff] [blame] | 690 | acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, &bm_status); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 691 | if (bm_status) | 
| Bob Moore | 50ffba1 | 2009-02-23 15:02:07 +0800 | [diff] [blame] | 692 | acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, 1); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 693 | /* | 
|  | 694 | * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect | 
|  | 695 | * the true state of bus mastering activity; forcing us to | 
|  | 696 | * manually check the BMIDEA bit of each IDE channel. | 
|  | 697 | */ | 
|  | 698 | else if (errata.piix4.bmisx) { | 
|  | 699 | if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01) | 
|  | 700 | || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01)) | 
|  | 701 | bm_status = 1; | 
|  | 702 | } | 
|  | 703 | return bm_status; | 
|  | 704 | } | 
|  | 705 |  | 
|  | 706 | /** | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 707 | * acpi_idle_do_entry - a helper function that does C2 and C3 type entry | 
|  | 708 | * @cx: cstate data | 
| venkatesh.pallipadi@intel.com | bc71bec | 2008-01-31 17:35:04 -0800 | [diff] [blame] | 709 | * | 
|  | 710 | * Caller disables interrupt before call and enables interrupt after return. | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 711 | */ | 
|  | 712 | static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) | 
|  | 713 | { | 
| Steven Rostedt | dcf3099 | 2008-07-25 18:00:42 -0400 | [diff] [blame] | 714 | /* Don't trace irqs off for idle */ | 
|  | 715 | stop_critical_timings(); | 
| venkatesh.pallipadi@intel.com | bc71bec | 2008-01-31 17:35:04 -0800 | [diff] [blame] | 716 | if (cx->entry_method == ACPI_CSTATE_FFH) { | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 717 | /* Call into architectural FFH based C-state */ | 
|  | 718 | acpi_processor_ffh_cstate_enter(cx); | 
| venkatesh.pallipadi@intel.com | bc71bec | 2008-01-31 17:35:04 -0800 | [diff] [blame] | 719 | } else if (cx->entry_method == ACPI_CSTATE_HALT) { | 
|  | 720 | acpi_safe_halt(); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 721 | } else { | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 722 | /* IO port based C-state */ | 
|  | 723 | inb(cx->address); | 
|  | 724 | /* Dummy wait op - must do something useless after P_LVL2 read | 
|  | 725 | because chipsets cannot guarantee that STPCLK# signal | 
|  | 726 | gets asserted in time to freeze execution properly. */ | 
| Andi Kleen | cfa806f | 2010-07-20 15:18:36 -0700 | [diff] [blame] | 727 | inl(acpi_gbl_FADT.xpm_timer_block.address); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 728 | } | 
| Steven Rostedt | dcf3099 | 2008-07-25 18:00:42 -0400 | [diff] [blame] | 729 | start_critical_timings(); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 730 | } | 
|  | 731 |  | 
|  | 732 | /** | 
|  | 733 | * acpi_idle_enter_c1 - enters an ACPI C1 state-type | 
|  | 734 | * @dev: the target CPU | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 735 | * @drv: cpuidle driver containing cpuidle state info | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 736 | * @index: index of target state | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 737 | * | 
|  | 738 | * This is equivalent to the HALT instruction. | 
|  | 739 | */ | 
|  | 740 | static int acpi_idle_enter_c1(struct cpuidle_device *dev, | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 741 | struct cpuidle_driver *drv, int index) | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 742 | { | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 743 | ktime_t  kt1, kt2; | 
|  | 744 | s64 idle_time; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 745 | struct acpi_processor *pr; | 
| Deepthi Dharwar | 4202735 | 2011-10-28 16:20:33 +0530 | [diff] [blame] | 746 | struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; | 
|  | 747 | struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage); | 
| venkatesh.pallipadi@intel.com | 9b12e18 | 2008-01-31 17:35:05 -0800 | [diff] [blame] | 748 |  | 
| Christoph Lameter | 4a6f4fe | 2010-12-06 11:16:24 -0600 | [diff] [blame] | 749 | pr = __this_cpu_read(processors); | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 750 | dev->last_residency = 0; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 751 |  | 
|  | 752 | if (unlikely(!pr)) | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 753 | return -EINVAL; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 754 |  | 
| venkatesh.pallipadi@intel.com | 2e90665 | 2008-01-31 17:35:03 -0800 | [diff] [blame] | 755 | local_irq_disable(); | 
| Venkatesh Pallipadi | b077fba | 2008-02-11 15:20:27 -0800 | [diff] [blame] | 756 |  | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 757 | lapic_timer_state_broadcast(pr, cx, 1); | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 758 | kt1 = ktime_get_real(); | 
| venkatesh.pallipadi@intel.com | bc71bec | 2008-01-31 17:35:04 -0800 | [diff] [blame] | 759 | acpi_idle_do_entry(cx); | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 760 | kt2 = ktime_get_real(); | 
|  | 761 | idle_time =  ktime_to_us(ktime_sub(kt2, kt1)); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 762 |  | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 763 | /* Update device last_residency*/ | 
|  | 764 | dev->last_residency = (int)idle_time; | 
|  | 765 |  | 
| venkatesh.pallipadi@intel.com | 2e90665 | 2008-01-31 17:35:03 -0800 | [diff] [blame] | 766 | local_irq_enable(); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 767 | cx->usage++; | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 768 | lapic_timer_state_broadcast(pr, cx, 0); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 769 |  | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 770 | return index; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 771 | } | 
|  | 772 |  | 
|  | 773 | /** | 
|  | 774 | * acpi_idle_enter_simple - enters an ACPI state without BM handling | 
|  | 775 | * @dev: the target CPU | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 776 | * @drv: cpuidle driver with cpuidle state information | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 777 | * @index: the index of suggested state | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 778 | */ | 
|  | 779 | static int acpi_idle_enter_simple(struct cpuidle_device *dev, | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 780 | struct cpuidle_driver *drv, int index) | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 781 | { | 
|  | 782 | struct acpi_processor *pr; | 
| Deepthi Dharwar | 4202735 | 2011-10-28 16:20:33 +0530 | [diff] [blame] | 783 | struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; | 
|  | 784 | struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage); | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 785 | ktime_t  kt1, kt2; | 
| Venkatesh Pallipadi | 2da513f | 2010-04-22 16:48:33 -0700 | [diff] [blame] | 786 | s64 idle_time_ns; | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 787 | s64 idle_time; | 
| Venkatesh Pallipadi | 5062911 | 2007-11-19 19:49:00 -0500 | [diff] [blame] | 788 |  | 
| Christoph Lameter | 4a6f4fe | 2010-12-06 11:16:24 -0600 | [diff] [blame] | 789 | pr = __this_cpu_read(processors); | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 790 | dev->last_residency = 0; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 791 |  | 
|  | 792 | if (unlikely(!pr)) | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 793 | return -EINVAL; | 
| Len Brown | e196441 | 2007-10-04 01:23:47 -0400 | [diff] [blame] | 794 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 795 | local_irq_disable(); | 
| Len Brown | 02cf4f9 | 2010-05-24 14:27:44 -0400 | [diff] [blame] | 796 |  | 
| Pallipadi, Venkatesh | d306ebc | 2010-02-10 10:35:31 -0800 | [diff] [blame] | 797 | if (cx->entry_method != ACPI_CSTATE_FFH) { | 
|  | 798 | current_thread_info()->status &= ~TS_POLLING; | 
|  | 799 | /* | 
|  | 800 | * TS_POLLING-cleared state must be visible before we test | 
|  | 801 | * NEED_RESCHED: | 
|  | 802 | */ | 
|  | 803 | smp_mb(); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 804 |  | 
| Len Brown | 02cf4f9 | 2010-05-24 14:27:44 -0400 | [diff] [blame] | 805 | if (unlikely(need_resched())) { | 
|  | 806 | current_thread_info()->status |= TS_POLLING; | 
|  | 807 | local_irq_enable(); | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 808 | return -EINVAL; | 
| Len Brown | 02cf4f9 | 2010-05-24 14:27:44 -0400 | [diff] [blame] | 809 | } | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 810 | } | 
|  | 811 |  | 
| Thomas Gleixner | e17bcb4 | 2007-12-07 19:16:17 +0100 | [diff] [blame] | 812 | /* | 
|  | 813 | * Must be done before busmaster disable as we might need to | 
|  | 814 | * access HPET ! | 
|  | 815 | */ | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 816 | lapic_timer_state_broadcast(pr, cx, 1); | 
| Thomas Gleixner | e17bcb4 | 2007-12-07 19:16:17 +0100 | [diff] [blame] | 817 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 818 | if (cx->type == ACPI_STATE_C3) | 
|  | 819 | ACPI_FLUSH_CPU_CACHE(); | 
|  | 820 |  | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 821 | kt1 = ktime_get_real(); | 
| Venkatesh Pallipadi | 5062911 | 2007-11-19 19:49:00 -0500 | [diff] [blame] | 822 | /* Tell the scheduler that we are going deep-idle: */ | 
|  | 823 | sched_clock_idle_sleep_event(); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 824 | acpi_idle_do_entry(cx); | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 825 | kt2 = ktime_get_real(); | 
| Venkatesh Pallipadi | 2da513f | 2010-04-22 16:48:33 -0700 | [diff] [blame] | 826 | idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1)); | 
|  | 827 | idle_time = idle_time_ns; | 
|  | 828 | do_div(idle_time, NSEC_PER_USEC); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 829 |  | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 830 | /* Update device last_residency*/ | 
|  | 831 | dev->last_residency = (int)idle_time; | 
|  | 832 |  | 
| Venkatesh Pallipadi | 5062911 | 2007-11-19 19:49:00 -0500 | [diff] [blame] | 833 | /* Tell the scheduler how much we idled: */ | 
| Venkatesh Pallipadi | 2da513f | 2010-04-22 16:48:33 -0700 | [diff] [blame] | 834 | sched_clock_idle_wakeup_event(idle_time_ns); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 835 |  | 
|  | 836 | local_irq_enable(); | 
| Len Brown | 02cf4f9 | 2010-05-24 14:27:44 -0400 | [diff] [blame] | 837 | if (cx->entry_method != ACPI_CSTATE_FFH) | 
|  | 838 | current_thread_info()->status |= TS_POLLING; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 839 |  | 
|  | 840 | cx->usage++; | 
|  | 841 |  | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 842 | lapic_timer_state_broadcast(pr, cx, 0); | 
| Venkatesh Pallipadi | bceefad | 2010-06-02 10:01:09 -0700 | [diff] [blame] | 843 | cx->time += idle_time; | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 844 | return index; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 845 | } | 
|  | 846 |  | 
|  | 847 | static int c3_cpu_count; | 
| Thomas Gleixner | e12f65f | 2009-07-25 18:14:37 +0200 | [diff] [blame] | 848 | static DEFINE_RAW_SPINLOCK(c3_lock); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 849 |  | 
|  | 850 | /** | 
|  | 851 | * acpi_idle_enter_bm - enters C3 with proper BM handling | 
|  | 852 | * @dev: the target CPU | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 853 | * @drv: cpuidle driver containing state data | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 854 | * @index: the index of suggested state | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 855 | * | 
|  | 856 | * If BM is detected, the deepest non-C3 idle state is entered instead. | 
|  | 857 | */ | 
|  | 858 | static int acpi_idle_enter_bm(struct cpuidle_device *dev, | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 859 | struct cpuidle_driver *drv, int index) | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 860 | { | 
|  | 861 | struct acpi_processor *pr; | 
| Deepthi Dharwar | 4202735 | 2011-10-28 16:20:33 +0530 | [diff] [blame] | 862 | struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; | 
|  | 863 | struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage); | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 864 | ktime_t  kt1, kt2; | 
| Venkatesh Pallipadi | 2da513f | 2010-04-22 16:48:33 -0700 | [diff] [blame] | 865 | s64 idle_time_ns; | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 866 | s64 idle_time; | 
| alex.shi | ff69f2b | 2009-03-04 11:55:26 -0800 | [diff] [blame] | 867 |  | 
| Venkatesh Pallipadi | 5062911 | 2007-11-19 19:49:00 -0500 | [diff] [blame] | 868 |  | 
| Christoph Lameter | 4a6f4fe | 2010-12-06 11:16:24 -0600 | [diff] [blame] | 869 | pr = __this_cpu_read(processors); | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 870 | dev->last_residency = 0; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 871 |  | 
|  | 872 | if (unlikely(!pr)) | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 873 | return -EINVAL; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 874 |  | 
| Len Brown | 718be4a | 2010-07-22 16:54:27 -0400 | [diff] [blame] | 875 | if (!cx->bm_sts_skip && acpi_idle_bm_check()) { | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 876 | if (drv->safe_state_index >= 0) { | 
|  | 877 | return drv->states[drv->safe_state_index].enter(dev, | 
|  | 878 | drv, drv->safe_state_index); | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 879 | } else { | 
| venkatesh.pallipadi@intel.com | 2e90665 | 2008-01-31 17:35:03 -0800 | [diff] [blame] | 880 | local_irq_disable(); | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 881 | acpi_safe_halt(); | 
| venkatesh.pallipadi@intel.com | 2e90665 | 2008-01-31 17:35:03 -0800 | [diff] [blame] | 882 | local_irq_enable(); | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 883 | return -EINVAL; | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 884 | } | 
|  | 885 | } | 
|  | 886 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 887 | local_irq_disable(); | 
| Len Brown | 02cf4f9 | 2010-05-24 14:27:44 -0400 | [diff] [blame] | 888 |  | 
| Pallipadi, Venkatesh | d306ebc | 2010-02-10 10:35:31 -0800 | [diff] [blame] | 889 | if (cx->entry_method != ACPI_CSTATE_FFH) { | 
|  | 890 | current_thread_info()->status &= ~TS_POLLING; | 
|  | 891 | /* | 
|  | 892 | * TS_POLLING-cleared state must be visible before we test | 
|  | 893 | * NEED_RESCHED: | 
|  | 894 | */ | 
|  | 895 | smp_mb(); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 896 |  | 
| Len Brown | 02cf4f9 | 2010-05-24 14:27:44 -0400 | [diff] [blame] | 897 | if (unlikely(need_resched())) { | 
|  | 898 | current_thread_info()->status |= TS_POLLING; | 
|  | 899 | local_irq_enable(); | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 900 | return -EINVAL; | 
| Len Brown | 02cf4f9 | 2010-05-24 14:27:44 -0400 | [diff] [blame] | 901 | } | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 902 | } | 
|  | 903 |  | 
| Venki Pallipadi | 996520c | 2008-03-24 14:24:10 -0700 | [diff] [blame] | 904 | acpi_unlazy_tlb(smp_processor_id()); | 
|  | 905 |  | 
| Venkatesh Pallipadi | 5062911 | 2007-11-19 19:49:00 -0500 | [diff] [blame] | 906 | /* Tell the scheduler that we are going deep-idle: */ | 
|  | 907 | sched_clock_idle_sleep_event(); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 908 | /* | 
|  | 909 | * Must be done before busmaster disable as we might need to | 
|  | 910 | * access HPET ! | 
|  | 911 | */ | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 912 | lapic_timer_state_broadcast(pr, cx, 1); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 913 |  | 
| Len Brown | f461dde | 2009-04-23 18:59:43 -0400 | [diff] [blame] | 914 | kt1 = ktime_get_real(); | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 915 | /* | 
|  | 916 | * disable bus master | 
|  | 917 | * bm_check implies we need ARB_DIS | 
|  | 918 | * !bm_check implies we need cache flush | 
|  | 919 | * bm_control implies whether we can do ARB_DIS | 
|  | 920 | * | 
|  | 921 | * That leaves a case where bm_check is set and bm_control is | 
|  | 922 | * not set. In that case we cannot do much, we enter C3 | 
|  | 923 | * without doing anything. | 
|  | 924 | */ | 
|  | 925 | if (pr->flags.bm_check && pr->flags.bm_control) { | 
| Thomas Gleixner | e12f65f | 2009-07-25 18:14:37 +0200 | [diff] [blame] | 926 | raw_spin_lock(&c3_lock); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 927 | c3_cpu_count++; | 
|  | 928 | /* Disable bus master arbitration when all CPUs are in C3 */ | 
|  | 929 | if (c3_cpu_count == num_online_cpus()) | 
| Bob Moore | 50ffba1 | 2009-02-23 15:02:07 +0800 | [diff] [blame] | 930 | acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1); | 
| Thomas Gleixner | e12f65f | 2009-07-25 18:14:37 +0200 | [diff] [blame] | 931 | raw_spin_unlock(&c3_lock); | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 932 | } else if (!pr->flags.bm_check) { | 
|  | 933 | ACPI_FLUSH_CPU_CACHE(); | 
|  | 934 | } | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 935 |  | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 936 | acpi_idle_do_entry(cx); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 937 |  | 
| Venkatesh Pallipadi | ddc081a | 2007-11-19 21:43:22 -0500 | [diff] [blame] | 938 | /* Re-enable bus master arbitration */ | 
|  | 939 | if (pr->flags.bm_check && pr->flags.bm_control) { | 
| Thomas Gleixner | e12f65f | 2009-07-25 18:14:37 +0200 | [diff] [blame] | 940 | raw_spin_lock(&c3_lock); | 
| Bob Moore | 50ffba1 | 2009-02-23 15:02:07 +0800 | [diff] [blame] | 941 | acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 942 | c3_cpu_count--; | 
| Thomas Gleixner | e12f65f | 2009-07-25 18:14:37 +0200 | [diff] [blame] | 943 | raw_spin_unlock(&c3_lock); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 944 | } | 
| Len Brown | f461dde | 2009-04-23 18:59:43 -0400 | [diff] [blame] | 945 | kt2 = ktime_get_real(); | 
| Zhao Yakui | 157317b | 2010-06-02 11:04:09 +0800 | [diff] [blame] | 946 | idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1)); | 
| Venkatesh Pallipadi | 2da513f | 2010-04-22 16:48:33 -0700 | [diff] [blame] | 947 | idle_time = idle_time_ns; | 
|  | 948 | do_div(idle_time, NSEC_PER_USEC); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 949 |  | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 950 | /* Update device last_residency*/ | 
|  | 951 | dev->last_residency = (int)idle_time; | 
|  | 952 |  | 
| Venkatesh Pallipadi | 5062911 | 2007-11-19 19:49:00 -0500 | [diff] [blame] | 953 | /* Tell the scheduler how much we idled: */ | 
| Venkatesh Pallipadi | 2da513f | 2010-04-22 16:48:33 -0700 | [diff] [blame] | 954 | sched_clock_idle_wakeup_event(idle_time_ns); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 955 |  | 
|  | 956 | local_irq_enable(); | 
| Len Brown | 02cf4f9 | 2010-05-24 14:27:44 -0400 | [diff] [blame] | 957 | if (cx->entry_method != ACPI_CSTATE_FFH) | 
|  | 958 | current_thread_info()->status |= TS_POLLING; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 959 |  | 
|  | 960 | cx->usage++; | 
|  | 961 |  | 
| Len Brown | 7e275cc | 2009-05-15 02:08:44 -0400 | [diff] [blame] | 962 | lapic_timer_state_broadcast(pr, cx, 0); | 
| Venkatesh Pallipadi | bceefad | 2010-06-02 10:01:09 -0700 | [diff] [blame] | 963 | cx->time += idle_time; | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 964 | return index; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 965 | } | 
|  | 966 |  | 
|  | 967 | struct cpuidle_driver acpi_idle_driver = { | 
|  | 968 | .name =		"acpi_idle", | 
|  | 969 | .owner =	THIS_MODULE, | 
|  | 970 | }; | 
|  | 971 |  | 
|  | 972 | /** | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 973 | * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE | 
|  | 974 | * device i.e. per-cpu data | 
|  | 975 | * | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 976 | * @pr: the ACPI processor | 
|  | 977 | */ | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 978 | static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr) | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 979 | { | 
| venkatesh.pallipadi@intel.com | 9a0b841 | 2008-01-31 17:35:06 -0800 | [diff] [blame] | 980 | int i, count = CPUIDLE_DRIVER_STATE_START; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 981 | struct acpi_processor_cx *cx; | 
| Deepthi Dharwar | 4202735 | 2011-10-28 16:20:33 +0530 | [diff] [blame] | 982 | struct cpuidle_state_usage *state_usage; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 983 | struct cpuidle_device *dev = &pr->power.dev; | 
|  | 984 |  | 
|  | 985 | if (!pr->flags.power_setup_done) | 
|  | 986 | return -EINVAL; | 
|  | 987 |  | 
|  | 988 | if (pr->flags.power == 0) { | 
|  | 989 | return -EINVAL; | 
|  | 990 | } | 
|  | 991 |  | 
| Venkatesh Pallipadi | dcb84f3 | 2008-05-19 19:09:27 -0400 | [diff] [blame] | 992 | dev->cpu = pr->id; | 
| Venkatesh Pallipadi | 4fcb2fc | 2008-02-11 17:46:31 -0800 | [diff] [blame] | 993 |  | 
| Len Brown | 615dfd9 | 2009-04-23 23:21:29 -0400 | [diff] [blame] | 994 | if (max_cstate == 0) | 
|  | 995 | max_cstate = 1; | 
|  | 996 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 997 | for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { | 
|  | 998 | cx = &pr->power.states[i]; | 
| Deepthi Dharwar | 4202735 | 2011-10-28 16:20:33 +0530 | [diff] [blame] | 999 | state_usage = &dev->states_usage[count]; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1000 |  | 
|  | 1001 | if (!cx->valid) | 
|  | 1002 | continue; | 
|  | 1003 |  | 
|  | 1004 | #ifdef CONFIG_HOTPLUG_CPU | 
|  | 1005 | if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && | 
|  | 1006 | !pr->flags.has_cst && | 
|  | 1007 | !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) | 
|  | 1008 | continue; | 
|  | 1009 | #endif | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1010 |  | 
| Deepthi Dharwar | 4202735 | 2011-10-28 16:20:33 +0530 | [diff] [blame] | 1011 | cpuidle_set_statedata(state_usage, cx); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1012 |  | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1013 | count++; | 
|  | 1014 | if (count == CPUIDLE_STATE_MAX) | 
|  | 1015 | break; | 
|  | 1016 | } | 
|  | 1017 |  | 
|  | 1018 | dev->state_count = count; | 
|  | 1019 |  | 
|  | 1020 | if (!count) | 
|  | 1021 | return -EINVAL; | 
|  | 1022 |  | 
|  | 1023 | return 0; | 
|  | 1024 | } | 
|  | 1025 |  | 
|  | 1026 | /** | 
|  | 1027 | * acpi_processor_setup_cpuidle states- prepares and configures cpuidle | 
|  | 1028 | * global state data i.e. idle routines | 
|  | 1029 | * | 
|  | 1030 | * @pr: the ACPI processor | 
|  | 1031 | */ | 
|  | 1032 | static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) | 
|  | 1033 | { | 
|  | 1034 | int i, count = CPUIDLE_DRIVER_STATE_START; | 
|  | 1035 | struct acpi_processor_cx *cx; | 
|  | 1036 | struct cpuidle_state *state; | 
|  | 1037 | struct cpuidle_driver *drv = &acpi_idle_driver; | 
|  | 1038 |  | 
|  | 1039 | if (!pr->flags.power_setup_done) | 
|  | 1040 | return -EINVAL; | 
|  | 1041 |  | 
|  | 1042 | if (pr->flags.power == 0) | 
|  | 1043 | return -EINVAL; | 
|  | 1044 |  | 
|  | 1045 | drv->safe_state_index = -1; | 
|  | 1046 | for (i = 0; i < CPUIDLE_STATE_MAX; i++) { | 
|  | 1047 | drv->states[i].name[0] = '\0'; | 
|  | 1048 | drv->states[i].desc[0] = '\0'; | 
|  | 1049 | } | 
|  | 1050 |  | 
|  | 1051 | if (max_cstate == 0) | 
|  | 1052 | max_cstate = 1; | 
|  | 1053 |  | 
|  | 1054 | for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { | 
|  | 1055 | cx = &pr->power.states[i]; | 
|  | 1056 |  | 
|  | 1057 | if (!cx->valid) | 
|  | 1058 | continue; | 
|  | 1059 |  | 
|  | 1060 | #ifdef CONFIG_HOTPLUG_CPU | 
|  | 1061 | if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && | 
|  | 1062 | !pr->flags.has_cst && | 
|  | 1063 | !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) | 
|  | 1064 | continue; | 
|  | 1065 | #endif | 
|  | 1066 |  | 
|  | 1067 | state = &drv->states[count]; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1068 | snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i); | 
| Venkatesh Pallipadi | 4fcb2fc | 2008-02-11 17:46:31 -0800 | [diff] [blame] | 1069 | strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1070 | state->exit_latency = cx->latency; | 
| Len Brown | 4963f62 | 2007-12-13 23:50:45 -0500 | [diff] [blame] | 1071 | state->target_residency = cx->latency * latency_factor; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1072 |  | 
|  | 1073 | state->flags = 0; | 
|  | 1074 | switch (cx->type) { | 
|  | 1075 | case ACPI_STATE_C1: | 
| Venki Pallipadi | 8e92b66 | 2008-02-29 10:24:32 -0800 | [diff] [blame] | 1076 | if (cx->entry_method == ACPI_CSTATE_FFH) | 
|  | 1077 | state->flags |= CPUIDLE_FLAG_TIME_VALID; | 
|  | 1078 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1079 | state->enter = acpi_idle_enter_c1; | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1080 | drv->safe_state_index = count; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1081 | break; | 
|  | 1082 |  | 
|  | 1083 | case ACPI_STATE_C2: | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1084 | state->flags |= CPUIDLE_FLAG_TIME_VALID; | 
|  | 1085 | state->enter = acpi_idle_enter_simple; | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1086 | drv->safe_state_index = count; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1087 | break; | 
|  | 1088 |  | 
|  | 1089 | case ACPI_STATE_C3: | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1090 | state->flags |= CPUIDLE_FLAG_TIME_VALID; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1091 | state->enter = pr->flags.bm_check ? | 
|  | 1092 | acpi_idle_enter_bm : | 
|  | 1093 | acpi_idle_enter_simple; | 
|  | 1094 | break; | 
|  | 1095 | } | 
|  | 1096 |  | 
|  | 1097 | count++; | 
| venkatesh.pallipadi@intel.com | 9a0b841 | 2008-01-31 17:35:06 -0800 | [diff] [blame] | 1098 | if (count == CPUIDLE_STATE_MAX) | 
|  | 1099 | break; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1100 | } | 
|  | 1101 |  | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1102 | drv->state_count = count; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1103 |  | 
|  | 1104 | if (!count) | 
|  | 1105 | return -EINVAL; | 
|  | 1106 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1107 | return 0; | 
|  | 1108 | } | 
|  | 1109 |  | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1110 | int acpi_processor_hotplug(struct acpi_processor *pr) | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1111 | { | 
| Venkatesh Pallipadi | dcb84f3 | 2008-05-19 19:09:27 -0400 | [diff] [blame] | 1112 | int ret = 0; | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1113 |  | 
| Thomas Renninger | d189604 | 2010-11-03 17:06:14 +0100 | [diff] [blame] | 1114 | if (disabled_by_idle_boot_param()) | 
| Venkatesh Pallipadi | 36a9135 | 2008-04-30 13:57:15 -0400 | [diff] [blame] | 1115 | return 0; | 
|  | 1116 |  | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1117 | if (!pr) | 
|  | 1118 | return -EINVAL; | 
|  | 1119 |  | 
|  | 1120 | if (nocst) { | 
|  | 1121 | return -ENODEV; | 
|  | 1122 | } | 
|  | 1123 |  | 
|  | 1124 | if (!pr->flags.power_setup_done) | 
|  | 1125 | return -ENODEV; | 
|  | 1126 |  | 
|  | 1127 | cpuidle_pause_and_lock(); | 
|  | 1128 | cpuidle_disable_device(&pr->power.dev); | 
|  | 1129 | acpi_processor_get_power_info(pr); | 
| Venkatesh Pallipadi | dcb84f3 | 2008-05-19 19:09:27 -0400 | [diff] [blame] | 1130 | if (pr->flags.power) { | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1131 | acpi_processor_setup_cpuidle_cx(pr); | 
| Venkatesh Pallipadi | dcb84f3 | 2008-05-19 19:09:27 -0400 | [diff] [blame] | 1132 | ret = cpuidle_enable_device(&pr->power.dev); | 
|  | 1133 | } | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1134 | cpuidle_resume_and_unlock(); | 
|  | 1135 |  | 
|  | 1136 | return ret; | 
|  | 1137 | } | 
|  | 1138 |  | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1139 | int acpi_processor_cst_has_changed(struct acpi_processor *pr) | 
|  | 1140 | { | 
|  | 1141 | int cpu; | 
|  | 1142 | struct acpi_processor *_pr; | 
|  | 1143 |  | 
|  | 1144 | if (disabled_by_idle_boot_param()) | 
|  | 1145 | return 0; | 
|  | 1146 |  | 
|  | 1147 | if (!pr) | 
|  | 1148 | return -EINVAL; | 
|  | 1149 |  | 
|  | 1150 | if (nocst) | 
|  | 1151 | return -ENODEV; | 
|  | 1152 |  | 
|  | 1153 | if (!pr->flags.power_setup_done) | 
|  | 1154 | return -ENODEV; | 
|  | 1155 |  | 
|  | 1156 | /* | 
|  | 1157 | * FIXME:  Design the ACPI notification to make it once per | 
|  | 1158 | * system instead of once per-cpu.  This condition is a hack | 
|  | 1159 | * to make the code that updates C-States be called once. | 
|  | 1160 | */ | 
|  | 1161 |  | 
|  | 1162 | if (smp_processor_id() == 0 && | 
|  | 1163 | cpuidle_get_driver() == &acpi_idle_driver) { | 
|  | 1164 |  | 
|  | 1165 | cpuidle_pause_and_lock(); | 
|  | 1166 | /* Protect against cpu-hotplug */ | 
|  | 1167 | get_online_cpus(); | 
|  | 1168 |  | 
|  | 1169 | /* Disable all cpuidle devices */ | 
|  | 1170 | for_each_online_cpu(cpu) { | 
|  | 1171 | _pr = per_cpu(processors, cpu); | 
|  | 1172 | if (!_pr || !_pr->flags.power_setup_done) | 
|  | 1173 | continue; | 
|  | 1174 | cpuidle_disable_device(&_pr->power.dev); | 
|  | 1175 | } | 
|  | 1176 |  | 
|  | 1177 | /* Populate Updated C-state information */ | 
|  | 1178 | acpi_processor_setup_cpuidle_states(pr); | 
|  | 1179 |  | 
|  | 1180 | /* Enable all cpuidle devices */ | 
|  | 1181 | for_each_online_cpu(cpu) { | 
|  | 1182 | _pr = per_cpu(processors, cpu); | 
|  | 1183 | if (!_pr || !_pr->flags.power_setup_done) | 
|  | 1184 | continue; | 
|  | 1185 | acpi_processor_get_power_info(_pr); | 
|  | 1186 | if (_pr->flags.power) { | 
|  | 1187 | acpi_processor_setup_cpuidle_cx(_pr); | 
|  | 1188 | cpuidle_enable_device(&_pr->power.dev); | 
|  | 1189 | } | 
|  | 1190 | } | 
|  | 1191 | put_online_cpus(); | 
|  | 1192 | cpuidle_resume_and_unlock(); | 
|  | 1193 | } | 
|  | 1194 |  | 
|  | 1195 | return 0; | 
|  | 1196 | } | 
|  | 1197 |  | 
|  | 1198 | static int acpi_processor_registered; | 
|  | 1199 |  | 
| Pierre Ossman | 7af8b66 | 2006-10-10 14:20:31 -0700 | [diff] [blame] | 1200 | int __cpuinit acpi_processor_power_init(struct acpi_processor *pr, | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 1201 | struct acpi_device *device) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1202 | { | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 1203 | acpi_status status = 0; | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1204 | int retval; | 
| Andreas Mohr | b683505 | 2006-04-27 05:25:00 -0400 | [diff] [blame] | 1205 | static int first_run; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1206 |  | 
| Thomas Renninger | d189604 | 2010-11-03 17:06:14 +0100 | [diff] [blame] | 1207 | if (disabled_by_idle_boot_param()) | 
| Venkatesh Pallipadi | 36a9135 | 2008-04-30 13:57:15 -0400 | [diff] [blame] | 1208 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1209 |  | 
|  | 1210 | if (!first_run) { | 
|  | 1211 | dmi_check_system(processor_power_dmi_table); | 
| Alexey Starikovskiy | c1c3063 | 2007-11-26 20:42:19 +0100 | [diff] [blame] | 1212 | max_cstate = acpi_processor_cstate_check(max_cstate); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1213 | if (max_cstate < ACPI_C_STATES_MAX) | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 1214 | printk(KERN_NOTICE | 
|  | 1215 | "ACPI: processor limited to max C-state %d\n", | 
|  | 1216 | max_cstate); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1217 | first_run++; | 
|  | 1218 | } | 
|  | 1219 |  | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 1220 | if (!pr) | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 1221 | return -EINVAL; | 
| Venkatesh Pallipadi | 02df8b9 | 2005-04-15 15:07:10 -0400 | [diff] [blame] | 1222 |  | 
| Alexey Starikovskiy | cee324b | 2007-02-02 19:48:22 +0300 | [diff] [blame] | 1223 | if (acpi_gbl_FADT.cst_control && !nocst) { | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 1224 | status = | 
| Alexey Starikovskiy | cee324b | 2007-02-02 19:48:22 +0300 | [diff] [blame] | 1225 | acpi_os_write_port(acpi_gbl_FADT.smi_command, acpi_gbl_FADT.cst_control, 8); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1226 | if (ACPI_FAILURE(status)) { | 
| Thomas Renninger | a6fc672 | 2006-06-26 23:58:43 -0400 | [diff] [blame] | 1227 | ACPI_EXCEPTION((AE_INFO, status, | 
|  | 1228 | "Notifying BIOS of _CST ability failed")); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1229 | } | 
|  | 1230 | } | 
|  | 1231 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1232 | acpi_processor_get_power_info(pr); | 
| Len Brown | 4f86d3a | 2007-10-03 18:58:00 -0400 | [diff] [blame] | 1233 | pr->flags.power_setup_done = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1234 |  | 
|  | 1235 | /* | 
|  | 1236 | * Install the idle handler if processor power management is supported. | 
|  | 1237 | * Note that we use previously set idle handler will be used on | 
|  | 1238 | * platforms that only support C1. | 
|  | 1239 | */ | 
| Venkatesh Pallipadi | 36a9135 | 2008-04-30 13:57:15 -0400 | [diff] [blame] | 1240 | if (pr->flags.power) { | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1241 | /* Register acpi_idle_driver if not already registered */ | 
|  | 1242 | if (!acpi_processor_registered) { | 
|  | 1243 | acpi_processor_setup_cpuidle_states(pr); | 
|  | 1244 | retval = cpuidle_register_driver(&acpi_idle_driver); | 
|  | 1245 | if (retval) | 
|  | 1246 | return retval; | 
|  | 1247 | printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n", | 
|  | 1248 | acpi_idle_driver.name); | 
|  | 1249 | } | 
|  | 1250 | /* Register per-cpu cpuidle_device. Cpuidle driver | 
|  | 1251 | * must already be registered before registering device | 
|  | 1252 | */ | 
|  | 1253 | acpi_processor_setup_cpuidle_cx(pr); | 
|  | 1254 | retval = cpuidle_register_device(&pr->power.dev); | 
|  | 1255 | if (retval) { | 
|  | 1256 | if (acpi_processor_registered == 0) | 
|  | 1257 | cpuidle_unregister_driver(&acpi_idle_driver); | 
|  | 1258 | return retval; | 
|  | 1259 | } | 
|  | 1260 | acpi_processor_registered++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1261 | } | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 1262 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1263 | } | 
|  | 1264 |  | 
| Len Brown | 4be44fc | 2005-08-05 00:44:28 -0400 | [diff] [blame] | 1265 | int acpi_processor_power_exit(struct acpi_processor *pr, | 
|  | 1266 | struct acpi_device *device) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1267 | { | 
| Thomas Renninger | d189604 | 2010-11-03 17:06:14 +0100 | [diff] [blame] | 1268 | if (disabled_by_idle_boot_param()) | 
| Venkatesh Pallipadi | 36a9135 | 2008-04-30 13:57:15 -0400 | [diff] [blame] | 1269 | return 0; | 
|  | 1270 |  | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1271 | if (pr->flags.power) { | 
|  | 1272 | cpuidle_unregister_device(&pr->power.dev); | 
|  | 1273 | acpi_processor_registered--; | 
|  | 1274 | if (acpi_processor_registered == 0) | 
|  | 1275 | cpuidle_unregister_driver(&acpi_idle_driver); | 
|  | 1276 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1277 |  | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 1278 | pr->flags.power_setup_done = 0; | 
| Patrick Mochel | d550d98 | 2006-06-27 00:41:40 -0400 | [diff] [blame] | 1279 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1280 | } |