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