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 e896ec4302f45fdaf2fc78aec0093eca5478fe28 527 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 acpi_driver_data(device) = cdev; 179 result = sysfs_create_link(&device->dev.kobj, 180 &cdev->device.kobj, "thermal_cooling"); 181 if (result) 182 goto unregister; 183 184 result = sysfs_create_link(&cdev->device.kobj, 185 &device->dev.kobj, "device"); 186 if (result) { 187 sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); 188 goto unregister; 189 } 190 191 end: 192 return result; 193 194 unregister: 195 thermal_cooling_device_unregister(cdev); 196 return result; 197 198} 199 200static int intel_menlow_memory_remove(struct acpi_device *device, int type) 201{ 202 struct thermal_cooling_device *cdev = acpi_driver_data(device); 203 204 if (!device || !cdev) 205 return -EINVAL; 206 207 sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); 208 sysfs_remove_link(&cdev->device.kobj, "device"); 209 thermal_cooling_device_unregister(cdev); 210 211 return 0; 212} 213 214static const struct acpi_device_id intel_menlow_memory_ids[] = { 215 {"INT0002", 0}, 216 {"", 0}, 217}; 218 219static struct acpi_driver intel_menlow_memory_driver = { 220 .name = "intel_menlow_thermal_control", 221 .ids = intel_menlow_memory_ids, 222 .ops = { 223 .add = intel_menlow_memory_add, 224 .remove = intel_menlow_memory_remove, 225 }, 226}; 227 228/* 229 * Sensor control on menlow platform 230 */ 231 232#define THERMAL_AUX0 0 233#define THERMAL_AUX1 1 234#define GET_AUX0 "GAX0" 235#define GET_AUX1 "GAX1" 236#define SET_AUX0 "SAX0" 237#define SET_AUX1 "SAX1" 238 239struct intel_menlow_attribute { 240 struct device_attribute attr; 241 struct device *device; 242 acpi_handle handle; 243 struct list_head node; 244}; 245 246static LIST_HEAD(intel_menlow_attr_list); 247static DEFINE_MUTEX(intel_menlow_attr_lock); 248 249/* 250 * sensor_get_auxtrip - get the current auxtrip value from sensor 251 * @name: Thermalzone name 252 * @auxtype : AUX0/AUX1 253 * @buf: syfs buffer 254 */ 255static int sensor_get_auxtrip(acpi_handle handle, int index, int *value) 256{ 257 acpi_status status; 258 259 if ((index != 0 && index != 1) || !value) 260 return -EINVAL; 261 262 status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0, 263 NULL, (unsigned long *)value); 264 if (ACPI_FAILURE(status)) 265 return -EIO; 266 267 return 0; 268} 269 270/* 271 * sensor_set_auxtrip - set the new auxtrip value to sensor 272 * @name: Thermalzone name 273 * @auxtype : AUX0/AUX1 274 * @buf: syfs buffer 275 */ 276static int sensor_set_auxtrip(acpi_handle handle, int index, int value) 277{ 278 acpi_status status; 279 union acpi_object arg = { 280 ACPI_TYPE_INTEGER 281 }; 282 struct acpi_object_list args = { 283 1, &arg 284 }; 285 int temp; 286 287 if (index != 0 && index != 1) 288 return -EINVAL; 289 290 status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1, 291 NULL, (unsigned long *)&temp); 292 if (ACPI_FAILURE(status)) 293 return -EIO; 294 if ((index && value < temp) || (!index && value > temp)) 295 return -EINVAL; 296 297 arg.integer.value = value; 298 status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0, 299 &args, (unsigned long *)&temp); 300 if (ACPI_FAILURE(status)) 301 return -EIO; 302 303 /* do we need to check the return value of SAX0/SAX1 ? */ 304 305 return 0; 306} 307 308#define to_intel_menlow_attr(_attr) \ 309 container_of(_attr, struct intel_menlow_attribute, attr) 310 311static ssize_t aux0_show(struct device *dev, 312 struct device_attribute *dev_attr, char *buf) 313{ 314 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); 315 int value; 316 int result; 317 318 result = sensor_get_auxtrip(attr->handle, 0, &value); 319 320 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value)); 321} 322 323static ssize_t aux1_show(struct device *dev, 324 struct device_attribute *dev_attr, char *buf) 325{ 326 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); 327 int value; 328 int result; 329 330 result = sensor_get_auxtrip(attr->handle, 1, &value); 331 332 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value)); 333} 334 335static ssize_t aux0_store(struct device *dev, 336 struct device_attribute *dev_attr, 337 const char *buf, size_t count) 338{ 339 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); 340 int value; 341 int result; 342 343 /*Sanity check; should be a positive integer */ 344 if (!sscanf(buf, "%d", &value)) 345 return -EINVAL; 346 347 if (value < 0) 348 return -EINVAL; 349 350 result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value)); 351 return result ? result : count; 352} 353 354static ssize_t aux1_store(struct device *dev, 355 struct device_attribute *dev_attr, 356 const char *buf, size_t count) 357{ 358 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); 359 int value; 360 int result; 361 362 /*Sanity check; should be a positive integer */ 363 if (!sscanf(buf, "%d", &value)) 364 return -EINVAL; 365 366 if (value < 0) 367 return -EINVAL; 368 369 result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value)); 370 return result ? result : count; 371} 372 373/* BIOS can enable/disable the thermal user application in dabney platform */ 374#define BIOS_ENABLED "\\_TZ.GSTS" 375static ssize_t bios_enabled_show(struct device *dev, 376 struct device_attribute *attr, char *buf) 377{ 378 acpi_status status; 379 unsigned long bios_enabled; 380 381 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled); 382 if (ACPI_FAILURE(status)) 383 return -ENODEV; 384 385 return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled"); 386} 387 388static int intel_menlow_add_one_attribute(char *name, int mode, void *show, 389 void *store, struct device *dev, 390 acpi_handle handle) 391{ 392 struct intel_menlow_attribute *attr; 393 int result; 394 395 attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL); 396 if (!attr) 397 return -ENOMEM; 398 399 attr->attr.attr.name = name; 400 attr->attr.attr.mode = mode; 401 attr->attr.show = show; 402 attr->attr.store = store; 403 attr->device = dev; 404 attr->handle = handle; 405 406 result = device_create_file(dev, &attr->attr); 407 if (result) 408 return result; 409 410 mutex_lock(&intel_menlow_attr_lock); 411 list_add_tail(&attr->node, &intel_menlow_attr_list); 412 mutex_unlock(&intel_menlow_attr_lock); 413 414 return 0; 415} 416 417static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl, 418 void *context, void **rv) 419{ 420 acpi_status status; 421 acpi_handle dummy; 422 struct thermal_zone_device *thermal; 423 int result; 424 425 result = acpi_bus_get_private_data(handle, (void **)&thermal); 426 if (result) 427 return 0; 428 429 /* _TZ must have the AUX0/1 methods */ 430 status = acpi_get_handle(handle, GET_AUX0, &dummy); 431 if (ACPI_FAILURE(status)) 432 goto not_found; 433 434 status = acpi_get_handle(handle, SET_AUX0, &dummy); 435 if (ACPI_FAILURE(status)) 436 goto not_found; 437 438 result = intel_menlow_add_one_attribute("aux0", 0644, 439 aux0_show, aux0_store, 440 &thermal->device, handle); 441 if (result) 442 return AE_ERROR; 443 444 status = acpi_get_handle(handle, GET_AUX1, &dummy); 445 if (ACPI_FAILURE(status)) 446 goto not_found; 447 448 status = acpi_get_handle(handle, SET_AUX1, &dummy); 449 if (ACPI_FAILURE(status)) 450 goto not_found; 451 452 result = intel_menlow_add_one_attribute("aux1", 0644, 453 aux1_show, aux1_store, 454 &thermal->device, handle); 455 if (result) 456 return AE_ERROR; 457 458 /* 459 * create the "dabney_enabled" attribute which means the user app 460 * should be loaded or not 461 */ 462 463 result = intel_menlow_add_one_attribute("bios_enabled", 0444, 464 bios_enabled_show, NULL, 465 &thermal->device, handle); 466 if (result) 467 return AE_ERROR; 468 469 not_found: 470 if (status == AE_NOT_FOUND) 471 return AE_OK; 472 else 473 return status; 474} 475 476static void intel_menlow_unregister_sensor(void) 477{ 478 struct intel_menlow_attribute *pos, *next; 479 480 mutex_lock(&intel_menlow_attr_lock); 481 list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) { 482 list_del(&pos->node); 483 device_remove_file(pos->device, &pos->attr); 484 kfree(pos); 485 } 486 mutex_unlock(&intel_menlow_attr_lock); 487 488 return; 489} 490 491static int __init intel_menlow_module_init(void) 492{ 493 int result = -ENODEV; 494 acpi_status status; 495 unsigned long enable; 496 497 if (acpi_disabled) 498 return result; 499 500 /* Looking for the \_TZ.GSTS method */ 501 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable); 502 if (ACPI_FAILURE(status) || !enable) 503 return -ENODEV; 504 505 /* Looking for ACPI device MEM0 with hardware id INT0002 */ 506 result = acpi_bus_register_driver(&intel_menlow_memory_driver); 507 if (result) 508 return result; 509 510 /* Looking for sensors in each ACPI thermal zone */ 511 status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT, 512 ACPI_UINT32_MAX, 513 intel_menlow_register_sensor, NULL, NULL); 514 if (ACPI_FAILURE(status)) 515 return -ENODEV; 516 517 return 0; 518} 519 520static void __exit intel_menlow_module_exit(void) 521{ 522 acpi_bus_unregister_driver(&intel_menlow_memory_driver); 523 intel_menlow_unregister_sensor(); 524} 525 526module_init(intel_menlow_module_init); 527module_exit(intel_menlow_module_exit);