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#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_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_cur_bandwidth(struct thermal_cooling_device *cdev,
  87                                    unsigned long *value)
  88{
  89        struct acpi_device *device = cdev->devdata;
  90        acpi_handle handle = device->handle;
  91        unsigned long long result;
  92        struct acpi_object_list arg_list;
  93        union acpi_object arg;
  94        acpi_status status = AE_OK;
  95
  96        arg_list.count = 1;
  97        arg_list.pointer = &arg;
  98        arg.type = ACPI_TYPE_INTEGER;
  99        arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
 100        status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
 101                                       &arg_list, &result);
 102        if (ACPI_FAILURE(status))
 103                return -EFAULT;
 104
 105        *value = result;
 106        return 0;
 107}
 108
 109static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
 110                                    unsigned long state)
 111{
 112        struct acpi_device *device = cdev->devdata;
 113        acpi_handle handle = device->handle;
 114        struct acpi_object_list arg_list;
 115        union acpi_object arg;
 116        acpi_status status;
 117        unsigned long long temp;
 118        unsigned long max_state;
 119
 120        if (memory_get_max_bandwidth(cdev, &max_state))
 121                return -EFAULT;
 122
 123        if (state > max_state)
 124                return -EINVAL;
 125
 126        arg_list.count = 1;
 127        arg_list.pointer = &arg;
 128        arg.type = ACPI_TYPE_INTEGER;
 129        arg.integer.value = state;
 130
 131        status =
 132            acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
 133                                  &temp);
 134
 135        printk(KERN_INFO
 136               "Bandwidth value was %ld: status is %d\n", state, status);
 137        if (ACPI_FAILURE(status))
 138                return -EFAULT;
 139
 140        return 0;
 141}
 142
 143static struct thermal_cooling_device_ops memory_cooling_ops = {
 144        .get_max_state = memory_get_max_bandwidth,
 145        .get_cur_state = memory_get_cur_bandwidth,
 146        .set_cur_state = memory_set_cur_bandwidth,
 147};
 148
 149/*
 150 * Memory Device Management
 151 */
 152static int intel_menlow_memory_add(struct acpi_device *device)
 153{
 154        int result = -ENODEV;
 155        acpi_status status = AE_OK;
 156        acpi_handle dummy;
 157        struct thermal_cooling_device *cdev;
 158
 159        if (!device)
 160                return -EINVAL;
 161
 162        status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
 163        if (ACPI_FAILURE(status))
 164                goto end;
 165
 166        status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
 167        if (ACPI_FAILURE(status))
 168                goto end;
 169
 170        cdev = thermal_cooling_device_register("Memory controller", device,
 171                                               &memory_cooling_ops);
 172        if (IS_ERR(cdev)) {
 173                result = PTR_ERR(cdev);
 174                goto end;
 175        }
 176
 177        device->driver_data = cdev;
 178        result = sysfs_create_link(&device->dev.kobj,
 179                                &cdev->device.kobj, "thermal_cooling");
 180        if (result)
 181                goto unregister;
 182
 183        result = sysfs_create_link(&cdev->device.kobj,
 184                                &device->dev.kobj, "device");
 185        if (result) {
 186                sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
 187                goto unregister;
 188        }
 189
 190 end:
 191        return result;
 192
 193 unregister:
 194        thermal_cooling_device_unregister(cdev);
 195        return result;
 196
 197}
 198
 199static int intel_menlow_memory_remove(struct acpi_device *device, int type)
 200{
 201        struct thermal_cooling_device *cdev = acpi_driver_data(device);
 202
 203        if (!device || !cdev)
 204                return -EINVAL;
 205
 206        sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
 207        sysfs_remove_link(&cdev->device.kobj, "device");
 208        thermal_cooling_device_unregister(cdev);
 209
 210        return 0;
 211}
 212
 213static const struct acpi_device_id intel_menlow_memory_ids[] = {
 214        {"INT0002", 0},
 215        {"", 0},
 216};
 217
 218static struct acpi_driver intel_menlow_memory_driver = {
 219        .name = "intel_menlow_thermal_control",
 220        .ids = intel_menlow_memory_ids,
 221        .ops = {
 222                .add = intel_menlow_memory_add,
 223                .remove = intel_menlow_memory_remove,
 224                },
 225};
 226
 227/*
 228 * Sensor control on menlow platform
 229 */
 230
 231#define THERMAL_AUX0 0
 232#define THERMAL_AUX1 1
 233#define GET_AUX0 "GAX0"
 234#define GET_AUX1 "GAX1"
 235#define SET_AUX0 "SAX0"
 236#define SET_AUX1 "SAX1"
 237
 238struct intel_menlow_attribute {
 239        struct device_attribute attr;
 240        struct device *device;
 241        acpi_handle handle;
 242        struct list_head node;
 243};
 244
 245static LIST_HEAD(intel_menlow_attr_list);
 246static DEFINE_MUTEX(intel_menlow_attr_lock);
 247
 248/*
 249 * sensor_get_auxtrip - get the current auxtrip value from sensor
 250 * @name: Thermalzone name
 251 * @auxtype : AUX0/AUX1
 252 * @buf: syfs buffer
 253 */
 254static int sensor_get_auxtrip(acpi_handle handle, int index,
 255                                                        unsigned long long *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, 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        unsigned long long 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, &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, &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        unsigned long long 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        unsigned long long 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 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 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);
 528
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.