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