Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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