1 /* 2 * processor_thermal.c - Passive cooling submodule of 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> 6 * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> 7 * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> 8 * - Added processor hotplug support 9 * 10 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2 of the License, or (at 15 * your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, but 18 * WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License along 23 * with this program; if not, write to the Free Software Foundation, Inc., 24 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 25 * 26 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 27 */ 28 29 #include <linux/kernel.h> 30 #include <linux/module.h> 31 #include <linux/init.h> 32 #include <linux/cpufreq.h> 33 #include <linux/proc_fs.h> 34 #include <linux/seq_file.h> 35 36 #include <asm/uaccess.h> 37 38 #include <acpi/acpi_bus.h> 39 #include <acpi/processor.h> 40 #include <acpi/acpi_drivers.h> 41 42 #define ACPI_PROCESSOR_COMPONENT 0x01000000 43 #define ACPI_PROCESSOR_CLASS "processor" 44 #define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" 45 #define _COMPONENT ACPI_PROCESSOR_COMPONENT 46 ACPI_MODULE_NAME ("acpi_processor") 47 48 49 /* -------------------------------------------------------------------------- 50 Limit Interface 51 -------------------------------------------------------------------------- */ 52 53 static int 54 acpi_processor_apply_limit ( 55 struct acpi_processor* pr) 56 { 57 int result = 0; 58 u16 px = 0; 59 u16 tx = 0; 60 61 ACPI_FUNCTION_TRACE("acpi_processor_apply_limit"); 62 63 if (!pr) 64 return_VALUE(-EINVAL); 65 66 if (!pr->flags.limit) 67 return_VALUE(-ENODEV); 68 69 if (pr->flags.throttling) { 70 if (pr->limit.user.tx > tx) 71 tx = pr->limit.user.tx; 72 if (pr->limit.thermal.tx > tx) 73 tx = pr->limit.thermal.tx; 74 75 result = acpi_processor_set_throttling(pr, tx); 76 if (result) 77 goto end; 78 } 79 80 pr->limit.state.px = px; 81 pr->limit.state.tx = tx; 82 83 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d] limit set to (P%d:T%d)\n", 84 pr->id, 85 pr->limit.state.px, 86 pr->limit.state.tx)); 87 88 end: 89 if (result) 90 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to set limit\n")); 91 92 return_VALUE(result); 93 } 94 95 96 #ifdef CONFIG_CPU_FREQ 97 98 /* If a passive cooling situation is detected, primarily CPUfreq is used, as it 99 * offers (in most cases) voltage scaling in addition to frequency scaling, and 100 * thus a cubic (instead of linear) reduction of energy. Also, we allow for 101 * _any_ cpufreq driver and not only the acpi-cpufreq driver. 102 */ 103 104 static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; 105 static unsigned int acpi_thermal_cpufreq_is_init = 0; 106 107 108 static int cpu_has_cpufreq(unsigned int cpu) 109 { 110 struct cpufreq_policy policy; 111 if (!acpi_thermal_cpufreq_is_init) 112 return -ENODEV; 113 if (!cpufreq_get_policy(&policy, cpu)) 114 return -ENODEV; 115 return 0; 116 } 117 118 119 static int acpi_thermal_cpufreq_increase(unsigned int cpu) 120 { 121 if (!cpu_has_cpufreq(cpu)) 122 return -ENODEV; 123 124 if (cpufreq_thermal_reduction_pctg[cpu] < 60) { 125 cpufreq_thermal_reduction_pctg[cpu] += 20; 126 cpufreq_update_policy(cpu); 127 return 0; 128 } 129 130 return -ERANGE; 131 } 132 133 134 static int acpi_thermal_cpufreq_decrease(unsigned int cpu) 135 { 136 if (!cpu_has_cpufreq(cpu)) 137 return -ENODEV; 138 139 if (cpufreq_thermal_reduction_pctg[cpu] >= 20) { 140 cpufreq_thermal_reduction_pctg[cpu] -= 20; 141 cpufreq_update_policy(cpu); 142 return 0; 143 } 144 145 return -ERANGE; 146 } 147 148 149 static int acpi_thermal_cpufreq_notifier( 150 struct notifier_block *nb, 151 unsigned long event, 152 void *data) 153 { 154 struct cpufreq_policy *policy = data; 155 unsigned long max_freq = 0; 156 157 if (event != CPUFREQ_ADJUST) 158 goto out; 159 160 max_freq = (policy->cpuinfo.max_freq * (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100; 161 162 cpufreq_verify_within_limits(policy, 0, max_freq); 163 164 out: 165 return 0; 166 } 167 168 169 static struct notifier_block acpi_thermal_cpufreq_notifier_block = { 170 .notifier_call = acpi_thermal_cpufreq_notifier, 171 }; 172 173 174 void acpi_thermal_cpufreq_init(void) { 175 int i; 176 177 for (i=0; i<NR_CPUS; i++) 178 cpufreq_thermal_reduction_pctg[i] = 0; 179 180 i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); 181 if (!i) 182 acpi_thermal_cpufreq_is_init = 1; 183 } 184 185 void acpi_thermal_cpufreq_exit(void) { 186 if (acpi_thermal_cpufreq_is_init) 187 cpufreq_unregister_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); 188 189 acpi_thermal_cpufreq_is_init = 0; 190 } 191 192 #else /* ! CONFIG_CPU_FREQ */ 193 194 static int acpi_thermal_cpufreq_increase(unsigned int cpu) { return -ENODEV; } 195 static int acpi_thermal_cpufreq_decrease(unsigned int cpu) { return -ENODEV; } 196 197 198 #endif 199 200 201 int 202 acpi_processor_set_thermal_limit ( 203 acpi_handle handle, 204 int type) 205 { 206 int result = 0; 207 struct acpi_processor *pr = NULL; 208 struct acpi_device *device = NULL; 209 int tx = 0; 210 211 ACPI_FUNCTION_TRACE("acpi_processor_set_thermal_limit"); 212 213 if ((type < ACPI_PROCESSOR_LIMIT_NONE) 214 || (type > ACPI_PROCESSOR_LIMIT_DECREMENT)) 215 return_VALUE(-EINVAL); 216 217 result = acpi_bus_get_device(handle, &device); 218 if (result) 219 return_VALUE(result); 220 221 pr = (struct acpi_processor *) acpi_driver_data(device); 222 if (!pr) 223 return_VALUE(-ENODEV); 224 225 /* Thermal limits are always relative to the current Px/Tx state. */ 226 if (pr->flags.throttling) 227 pr->limit.thermal.tx = pr->throttling.state; 228 229 /* 230 * Our default policy is to only use throttling at the lowest 231 * performance state. 232 */ 233 234 tx = pr->limit.thermal.tx; 235 236 switch (type) { 237 238 case ACPI_PROCESSOR_LIMIT_NONE: 239 do { 240 result = acpi_thermal_cpufreq_decrease(pr->id); 241 } while (!result); 242 tx = 0; 243 break; 244 245 case ACPI_PROCESSOR_LIMIT_INCREMENT: 246 /* if going up: P-states first, T-states later */ 247 248 result = acpi_thermal_cpufreq_increase(pr->id); 249 if (!result) 250 goto end; 251 else if (result == -ERANGE) 252 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 253 "At maximum performance state\n")); 254 255 if (pr->flags.throttling) { 256 if (tx == (pr->throttling.state_count - 1)) 257 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 258 "At maximum throttling state\n")); 259 else 260 tx++; 261 } 262 break; 263 264 case ACPI_PROCESSOR_LIMIT_DECREMENT: 265 /* if going down: T-states first, P-states later */ 266 267 if (pr->flags.throttling) { 268 if (tx == 0) 269 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 270 "At minimum throttling state\n")); 271 else { 272 tx--; 273 goto end; 274 } 275 } 276 277 result = acpi_thermal_cpufreq_decrease(pr->id); 278 if (result == -ERANGE) 279 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 280 "At minimum performance state\n")); 281 282 break; 283 } 284 285 end: 286 if (pr->flags.throttling) { 287 pr->limit.thermal.px = 0; 288 pr->limit.thermal.tx = tx; 289 290 result = acpi_processor_apply_limit(pr); 291 if (result) 292 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, 293 "Unable to set thermal limit\n")); 294 295 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Thermal limit now (P%d:T%d)\n", 296 pr->limit.thermal.px, 297 pr->limit.thermal.tx)); 298 } else 299 result = 0; 300 301 return_VALUE(result); 302 } 303 304 305 int 306 acpi_processor_get_limit_info ( 307 struct acpi_processor *pr) 308 { 309 ACPI_FUNCTION_TRACE("acpi_processor_get_limit_info"); 310 311 if (!pr) 312 return_VALUE(-EINVAL); 313 314 if (pr->flags.throttling) 315 pr->flags.limit = 1; 316 317 return_VALUE(0); 318 } 319 320 321 /* /proc interface */ 322 323 static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) 324 { 325 struct acpi_processor *pr = (struct acpi_processor *)seq->private; 326 327 ACPI_FUNCTION_TRACE("acpi_processor_limit_seq_show"); 328 329 if (!pr) 330 goto end; 331 332 if (!pr->flags.limit) { 333 seq_puts(seq, "<not supported>\n"); 334 goto end; 335 } 336 337 seq_printf(seq, "active limit: P%d:T%d\n" 338 "user limit: P%d:T%d\n" 339 "thermal limit: P%d:T%d\n", 340 pr->limit.state.px, pr->limit.state.tx, 341 pr->limit.user.px, pr->limit.user.tx, 342 pr->limit.thermal.px, pr->limit.thermal.tx); 343 344 end: 345 return_VALUE(0); 346 } 347 348 static int acpi_processor_limit_open_fs(struct inode *inode, struct file *file) 349 { 350 return single_open(file, acpi_processor_limit_seq_show, 351 PDE(inode)->data); 352 } 353 354 ssize_t acpi_processor_write_limit ( 355 struct file *file, 356 const char __user *buffer, 357 size_t count, 358 loff_t *data) 359 { 360 int result = 0; 361 struct seq_file *m = (struct seq_file *)file->private_data; 362 struct acpi_processor *pr = (struct acpi_processor *)m->private; 363 char limit_string[25] = {'\0'}; 364 int px = 0; 365 int tx = 0; 366 367 ACPI_FUNCTION_TRACE("acpi_processor_write_limit"); 368 369 if (!pr || (count > sizeof(limit_string) - 1)) { 370 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument\n")); 371 return_VALUE(-EINVAL); 372 } 373 374 if (copy_from_user(limit_string, buffer, count)) { 375 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n")); 376 return_VALUE(-EFAULT); 377 } 378 379 limit_string[count] = '\0'; 380 381 if (sscanf(limit_string, "%d:%d", &px, &tx) != 2) { 382 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n")); 383 return_VALUE(-EINVAL); 384 } 385 386 if (pr->flags.throttling) { 387 if ((tx < 0) || (tx > (pr->throttling.state_count - 1))) { 388 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid tx\n")); 389 return_VALUE(-EINVAL); 390 } 391 pr->limit.user.tx = tx; 392 } 393 394 result = acpi_processor_apply_limit(pr); 395 396 return_VALUE(count); 397 } 398 399 400 struct file_operations acpi_processor_limit_fops = { 401 .open = acpi_processor_limit_open_fs, 402 .read = seq_read, 403 .llseek = seq_lseek, 404 .release = single_release, 405 }; 406 407