linux/drivers/platform/x86/intel_menlow.c
<<
>>
Prefs
   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, int 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);
 544
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.