Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.7-rc2 543 lines 14 kB view raw
1/* 2 * intel_menlow.c - Intel menlow Driver for thermal management extension 3 * 4 * Copyright (C) 2008 Intel Corp 5 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> 6 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; version 2 of the License. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 * 22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 23 * 24 * This driver creates the sys I/F for programming the sensors. 25 * It also implements the driver for intel menlow memory controller (hardware 26 * id is INT0002) which makes use of the platform specific ACPI methods 27 * to get/set bandwidth. 28 */ 29 30#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 31 32#include <linux/kernel.h> 33#include <linux/module.h> 34#include <linux/init.h> 35#include <linux/slab.h> 36#include <linux/types.h> 37#include <linux/pci.h> 38#include <linux/pm.h> 39 40#include <linux/thermal.h> 41#include <acpi/acpi_bus.h> 42#include <acpi/acpi_drivers.h> 43 44MODULE_AUTHOR("Thomas Sujith"); 45MODULE_AUTHOR("Zhang Rui"); 46MODULE_DESCRIPTION("Intel Menlow platform specific driver"); 47MODULE_LICENSE("GPL"); 48 49/* 50 * Memory controller device control 51 */ 52 53#define MEMORY_GET_BANDWIDTH "GTHS" 54#define MEMORY_SET_BANDWIDTH "STHS" 55#define MEMORY_ARG_CUR_BANDWIDTH 1 56#define MEMORY_ARG_MAX_BANDWIDTH 0 57 58static void intel_menlow_unregister_sensor(void); 59 60/* 61 * GTHS returning 'n' would mean that [0,n-1] states are supported 62 * In that case max_cstate would be n-1 63 * GTHS returning '0' would mean that no bandwidth control states are supported 64 */ 65static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev, 66 unsigned long *max_state) 67{ 68 struct acpi_device *device = cdev->devdata; 69 acpi_handle handle = device->handle; 70 unsigned long long value; 71 struct acpi_object_list arg_list; 72 union acpi_object arg; 73 acpi_status status = AE_OK; 74 75 arg_list.count = 1; 76 arg_list.pointer = &arg; 77 arg.type = ACPI_TYPE_INTEGER; 78 arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH; 79 status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH, 80 &arg_list, &value); 81 if (ACPI_FAILURE(status)) 82 return -EFAULT; 83 84 if (!value) 85 return -EINVAL; 86 87 *max_state = value - 1; 88 return 0; 89} 90 91static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev, 92 unsigned long *value) 93{ 94 struct acpi_device *device = cdev->devdata; 95 acpi_handle handle = device->handle; 96 unsigned long long result; 97 struct acpi_object_list arg_list; 98 union acpi_object arg; 99 acpi_status status = AE_OK; 100 101 arg_list.count = 1; 102 arg_list.pointer = &arg; 103 arg.type = ACPI_TYPE_INTEGER; 104 arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH; 105 status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH, 106 &arg_list, &result); 107 if (ACPI_FAILURE(status)) 108 return -EFAULT; 109 110 *value = result; 111 return 0; 112} 113 114static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, 115 unsigned long state) 116{ 117 struct acpi_device *device = cdev->devdata; 118 acpi_handle handle = device->handle; 119 struct acpi_object_list arg_list; 120 union acpi_object arg; 121 acpi_status status; 122 unsigned long long temp; 123 unsigned long max_state; 124 125 if (memory_get_max_bandwidth(cdev, &max_state)) 126 return -EFAULT; 127 128 if (state > max_state) 129 return -EINVAL; 130 131 arg_list.count = 1; 132 arg_list.pointer = &arg; 133 arg.type = ACPI_TYPE_INTEGER; 134 arg.integer.value = state; 135 136 status = 137 acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list, 138 &temp); 139 140 pr_info("Bandwidth value was %ld: status is %d\n", state, status); 141 if (ACPI_FAILURE(status)) 142 return -EFAULT; 143 144 return 0; 145} 146 147static struct thermal_cooling_device_ops memory_cooling_ops = { 148 .get_max_state = memory_get_max_bandwidth, 149 .get_cur_state = memory_get_cur_bandwidth, 150 .set_cur_state = memory_set_cur_bandwidth, 151}; 152 153/* 154 * Memory Device Management 155 */ 156static int intel_menlow_memory_add(struct acpi_device *device) 157{ 158 int result = -ENODEV; 159 acpi_status status = AE_OK; 160 acpi_handle dummy; 161 struct thermal_cooling_device *cdev; 162 163 if (!device) 164 return -EINVAL; 165 166 status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy); 167 if (ACPI_FAILURE(status)) 168 goto end; 169 170 status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy); 171 if (ACPI_FAILURE(status)) 172 goto end; 173 174 cdev = thermal_cooling_device_register("Memory controller", device, 175 &memory_cooling_ops); 176 if (IS_ERR(cdev)) { 177 result = PTR_ERR(cdev); 178 goto end; 179 } 180 181 device->driver_data = cdev; 182 result = sysfs_create_link(&device->dev.kobj, 183 &cdev->device.kobj, "thermal_cooling"); 184 if (result) 185 goto unregister; 186 187 result = sysfs_create_link(&cdev->device.kobj, 188 &device->dev.kobj, "device"); 189 if (result) { 190 sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); 191 goto unregister; 192 } 193 194 end: 195 return result; 196 197 unregister: 198 thermal_cooling_device_unregister(cdev); 199 return result; 200 201} 202 203static int intel_menlow_memory_remove(struct acpi_device *device, int type) 204{ 205 struct thermal_cooling_device *cdev = acpi_driver_data(device); 206 207 if (!device || !cdev) 208 return -EINVAL; 209 210 sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); 211 sysfs_remove_link(&cdev->device.kobj, "device"); 212 thermal_cooling_device_unregister(cdev); 213 214 return 0; 215} 216 217static const struct acpi_device_id intel_menlow_memory_ids[] = { 218 {"INT0002", 0}, 219 {"", 0}, 220}; 221 222static struct acpi_driver intel_menlow_memory_driver = { 223 .name = "intel_menlow_thermal_control", 224 .ids = intel_menlow_memory_ids, 225 .ops = { 226 .add = intel_menlow_memory_add, 227 .remove = intel_menlow_memory_remove, 228 }, 229}; 230 231/* 232 * Sensor control on menlow platform 233 */ 234 235#define THERMAL_AUX0 0 236#define THERMAL_AUX1 1 237#define GET_AUX0 "GAX0" 238#define GET_AUX1 "GAX1" 239#define SET_AUX0 "SAX0" 240#define SET_AUX1 "SAX1" 241 242struct intel_menlow_attribute { 243 struct device_attribute attr; 244 struct device *device; 245 acpi_handle handle; 246 struct list_head node; 247}; 248 249static LIST_HEAD(intel_menlow_attr_list); 250static DEFINE_MUTEX(intel_menlow_attr_lock); 251 252/* 253 * sensor_get_auxtrip - get the current auxtrip value from sensor 254 * @name: Thermalzone name 255 * @auxtype : AUX0/AUX1 256 * @buf: syfs buffer 257 */ 258static int sensor_get_auxtrip(acpi_handle handle, int index, 259 unsigned long long *value) 260{ 261 acpi_status status; 262 263 if ((index != 0 && index != 1) || !value) 264 return -EINVAL; 265 266 status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0, 267 NULL, value); 268 if (ACPI_FAILURE(status)) 269 return -EIO; 270 271 return 0; 272} 273 274/* 275 * sensor_set_auxtrip - set the new auxtrip value to sensor 276 * @name: Thermalzone name 277 * @auxtype : AUX0/AUX1 278 * @buf: syfs buffer 279 */ 280static int sensor_set_auxtrip(acpi_handle handle, int index, int value) 281{ 282 acpi_status status; 283 union acpi_object arg = { 284 ACPI_TYPE_INTEGER 285 }; 286 struct acpi_object_list args = { 287 1, &arg 288 }; 289 unsigned long long temp; 290 291 if (index != 0 && index != 1) 292 return -EINVAL; 293 294 status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1, 295 NULL, &temp); 296 if (ACPI_FAILURE(status)) 297 return -EIO; 298 if ((index && value < temp) || (!index && value > temp)) 299 return -EINVAL; 300 301 arg.integer.value = value; 302 status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0, 303 &args, &temp); 304 if (ACPI_FAILURE(status)) 305 return -EIO; 306 307 /* do we need to check the return value of SAX0/SAX1 ? */ 308 309 return 0; 310} 311 312#define to_intel_menlow_attr(_attr) \ 313 container_of(_attr, struct intel_menlow_attribute, attr) 314 315static ssize_t aux0_show(struct device *dev, 316 struct device_attribute *dev_attr, char *buf) 317{ 318 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); 319 unsigned long long value; 320 int result; 321 322 result = sensor_get_auxtrip(attr->handle, 0, &value); 323 324 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value)); 325} 326 327static ssize_t aux1_show(struct device *dev, 328 struct device_attribute *dev_attr, char *buf) 329{ 330 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); 331 unsigned long long value; 332 int result; 333 334 result = sensor_get_auxtrip(attr->handle, 1, &value); 335 336 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value)); 337} 338 339static ssize_t aux0_store(struct device *dev, 340 struct device_attribute *dev_attr, 341 const char *buf, size_t count) 342{ 343 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); 344 int value; 345 int result; 346 347 /*Sanity check; should be a positive integer */ 348 if (!sscanf(buf, "%d", &value)) 349 return -EINVAL; 350 351 if (value < 0) 352 return -EINVAL; 353 354 result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value)); 355 return result ? result : count; 356} 357 358static ssize_t aux1_store(struct device *dev, 359 struct device_attribute *dev_attr, 360 const char *buf, size_t count) 361{ 362 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); 363 int value; 364 int result; 365 366 /*Sanity check; should be a positive integer */ 367 if (!sscanf(buf, "%d", &value)) 368 return -EINVAL; 369 370 if (value < 0) 371 return -EINVAL; 372 373 result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value)); 374 return result ? result : count; 375} 376 377/* BIOS can enable/disable the thermal user application in dabney platform */ 378#define BIOS_ENABLED "\\_TZ.GSTS" 379static ssize_t bios_enabled_show(struct device *dev, 380 struct device_attribute *attr, char *buf) 381{ 382 acpi_status status; 383 unsigned long long bios_enabled; 384 385 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled); 386 if (ACPI_FAILURE(status)) 387 return -ENODEV; 388 389 return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled"); 390} 391 392static int intel_menlow_add_one_attribute(char *name, umode_t mode, void *show, 393 void *store, struct device *dev, 394 acpi_handle handle) 395{ 396 struct intel_menlow_attribute *attr; 397 int result; 398 399 attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL); 400 if (!attr) 401 return -ENOMEM; 402 403 sysfs_attr_init(&attr->attr.attr); /* That is consistent naming :D */ 404 attr->attr.attr.name = name; 405 attr->attr.attr.mode = mode; 406 attr->attr.show = show; 407 attr->attr.store = store; 408 attr->device = dev; 409 attr->handle = handle; 410 411 result = device_create_file(dev, &attr->attr); 412 if (result) { 413 kfree(attr); 414 return result; 415 } 416 417 mutex_lock(&intel_menlow_attr_lock); 418 list_add_tail(&attr->node, &intel_menlow_attr_list); 419 mutex_unlock(&intel_menlow_attr_lock); 420 421 return 0; 422} 423 424static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl, 425 void *context, void **rv) 426{ 427 acpi_status status; 428 acpi_handle dummy; 429 struct thermal_zone_device *thermal; 430 int result; 431 432 result = acpi_bus_get_private_data(handle, (void **)&thermal); 433 if (result) 434 return 0; 435 436 /* _TZ must have the AUX0/1 methods */ 437 status = acpi_get_handle(handle, GET_AUX0, &dummy); 438 if (ACPI_FAILURE(status)) 439 return (status == AE_NOT_FOUND) ? AE_OK : status; 440 441 status = acpi_get_handle(handle, SET_AUX0, &dummy); 442 if (ACPI_FAILURE(status)) 443 return (status == AE_NOT_FOUND) ? AE_OK : status; 444 445 result = intel_menlow_add_one_attribute("aux0", 0644, 446 aux0_show, aux0_store, 447 &thermal->device, handle); 448 if (result) 449 return AE_ERROR; 450 451 status = acpi_get_handle(handle, GET_AUX1, &dummy); 452 if (ACPI_FAILURE(status)) 453 goto aux1_not_found; 454 455 status = acpi_get_handle(handle, SET_AUX1, &dummy); 456 if (ACPI_FAILURE(status)) 457 goto aux1_not_found; 458 459 result = intel_menlow_add_one_attribute("aux1", 0644, 460 aux1_show, aux1_store, 461 &thermal->device, handle); 462 if (result) { 463 intel_menlow_unregister_sensor(); 464 return AE_ERROR; 465 } 466 467 /* 468 * create the "dabney_enabled" attribute which means the user app 469 * should be loaded or not 470 */ 471 472 result = intel_menlow_add_one_attribute("bios_enabled", 0444, 473 bios_enabled_show, NULL, 474 &thermal->device, handle); 475 if (result) { 476 intel_menlow_unregister_sensor(); 477 return AE_ERROR; 478 } 479 480 return AE_OK; 481 482 aux1_not_found: 483 if (status == AE_NOT_FOUND) 484 return AE_OK; 485 486 intel_menlow_unregister_sensor(); 487 return status; 488} 489 490static void intel_menlow_unregister_sensor(void) 491{ 492 struct intel_menlow_attribute *pos, *next; 493 494 mutex_lock(&intel_menlow_attr_lock); 495 list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) { 496 list_del(&pos->node); 497 device_remove_file(pos->device, &pos->attr); 498 kfree(pos); 499 } 500 mutex_unlock(&intel_menlow_attr_lock); 501 502 return; 503} 504 505static int __init intel_menlow_module_init(void) 506{ 507 int result = -ENODEV; 508 acpi_status status; 509 unsigned long long enable; 510 511 if (acpi_disabled) 512 return result; 513 514 /* Looking for the \_TZ.GSTS method */ 515 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable); 516 if (ACPI_FAILURE(status) || !enable) 517 return -ENODEV; 518 519 /* Looking for ACPI device MEM0 with hardware id INT0002 */ 520 result = acpi_bus_register_driver(&intel_menlow_memory_driver); 521 if (result) 522 return result; 523 524 /* Looking for sensors in each ACPI thermal zone */ 525 status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT, 526 ACPI_UINT32_MAX, 527 intel_menlow_register_sensor, NULL, NULL, NULL); 528 if (ACPI_FAILURE(status)) { 529 acpi_bus_unregister_driver(&intel_menlow_memory_driver); 530 return -ENODEV; 531 } 532 533 return 0; 534} 535 536static void __exit intel_menlow_module_exit(void) 537{ 538 acpi_bus_unregister_driver(&intel_menlow_memory_driver); 539 intel_menlow_unregister_sensor(); 540} 541 542module_init(intel_menlow_module_init); 543module_exit(intel_menlow_module_exit);