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