linux/drivers/acpi/fan.c
<<
>>
Prefs
   1/*
   2 *  acpi_fan.c - ACPI Fan Driver ($Revision: 29 $)
   3 *
   4 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
   5 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
   6 *
   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; either version 2 of the License, or (at
  12 *  your option) any later version.
  13 *
  14 *  This program is distributed in the hope that it will be useful, but
  15 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 *  General Public License for more details.
  18 *
  19 *  You should have received a copy of the GNU General Public License along
  20 *  with this program; if not, write to the Free Software Foundation, Inc.,
  21 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  22 *
  23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  24 */
  25
  26#include <linux/kernel.h>
  27#include <linux/module.h>
  28#include <linux/init.h>
  29#include <linux/types.h>
  30#include <asm/uaccess.h>
  31#include <linux/thermal.h>
  32#include <acpi/acpi_bus.h>
  33#include <acpi/acpi_drivers.h>
  34
  35#define PREFIX "ACPI: "
  36
  37#define ACPI_FAN_CLASS                  "fan"
  38#define ACPI_FAN_FILE_STATE             "state"
  39
  40#define _COMPONENT              ACPI_FAN_COMPONENT
  41ACPI_MODULE_NAME("fan");
  42
  43MODULE_AUTHOR("Paul Diefenbaugh");
  44MODULE_DESCRIPTION("ACPI Fan Driver");
  45MODULE_LICENSE("GPL");
  46
  47static int acpi_fan_add(struct acpi_device *device);
  48static int acpi_fan_remove(struct acpi_device *device, int type);
  49static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state);
  50static int acpi_fan_resume(struct acpi_device *device);
  51
  52static const struct acpi_device_id fan_device_ids[] = {
  53        {"PNP0C0B", 0},
  54        {"", 0},
  55};
  56MODULE_DEVICE_TABLE(acpi, fan_device_ids);
  57
  58static struct acpi_driver acpi_fan_driver = {
  59        .name = "fan",
  60        .class = ACPI_FAN_CLASS,
  61        .ids = fan_device_ids,
  62        .ops = {
  63                .add = acpi_fan_add,
  64                .remove = acpi_fan_remove,
  65                .suspend = acpi_fan_suspend,
  66                .resume = acpi_fan_resume,
  67                },
  68};
  69
  70/* thermal cooling device callbacks */
  71static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
  72                             *state)
  73{
  74        /* ACPI fan device only support two states: ON/OFF */
  75        *state = 1;
  76        return 0;
  77}
  78
  79static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
  80                             *state)
  81{
  82        struct acpi_device *device = cdev->devdata;
  83        int result;
  84        int acpi_state;
  85
  86        if (!device)
  87                return -EINVAL;
  88
  89        result = acpi_bus_update_power(device->handle, &acpi_state);
  90        if (result)
  91                return result;
  92
  93        *state = (acpi_state == ACPI_STATE_D3 ? 0 :
  94                 (acpi_state == ACPI_STATE_D0 ? 1 : -1));
  95        return 0;
  96}
  97
  98static int
  99fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 100{
 101        struct acpi_device *device = cdev->devdata;
 102        int result;
 103
 104        if (!device || (state != 0 && state != 1))
 105                return -EINVAL;
 106
 107        result = acpi_bus_set_power(device->handle,
 108                                state ? ACPI_STATE_D0 : ACPI_STATE_D3);
 109
 110        return result;
 111}
 112
 113static struct thermal_cooling_device_ops fan_cooling_ops = {
 114        .get_max_state = fan_get_max_state,
 115        .get_cur_state = fan_get_cur_state,
 116        .set_cur_state = fan_set_cur_state,
 117};
 118
 119/* --------------------------------------------------------------------------
 120                                 Driver Interface
 121   -------------------------------------------------------------------------- */
 122
 123static int acpi_fan_add(struct acpi_device *device)
 124{
 125        int result = 0;
 126        struct thermal_cooling_device *cdev;
 127
 128        if (!device)
 129                return -EINVAL;
 130
 131        strcpy(acpi_device_name(device), "Fan");
 132        strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
 133
 134        result = acpi_bus_update_power(device->handle, NULL);
 135        if (result) {
 136                printk(KERN_ERR PREFIX "Setting initial power state\n");
 137                goto end;
 138        }
 139
 140        cdev = thermal_cooling_device_register("Fan", device,
 141                                                &fan_cooling_ops);
 142        if (IS_ERR(cdev)) {
 143                result = PTR_ERR(cdev);
 144                goto end;
 145        }
 146
 147        dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id);
 148
 149        device->driver_data = cdev;
 150        result = sysfs_create_link(&device->dev.kobj,
 151                                   &cdev->device.kobj,
 152                                   "thermal_cooling");
 153        if (result)
 154                dev_err(&device->dev, "Failed to create sysfs link "
 155                        "'thermal_cooling'\n");
 156
 157        result = sysfs_create_link(&cdev->device.kobj,
 158                                   &device->dev.kobj,
 159                                   "device");
 160        if (result)
 161                dev_err(&device->dev, "Failed to create sysfs link "
 162                        "'device'\n");
 163
 164        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
 165               acpi_device_name(device), acpi_device_bid(device),
 166               !device->power.state ? "on" : "off");
 167
 168      end:
 169        return result;
 170}
 171
 172static int acpi_fan_remove(struct acpi_device *device, int type)
 173{
 174        struct thermal_cooling_device *cdev = acpi_driver_data(device);
 175
 176        if (!device || !cdev)
 177                return -EINVAL;
 178
 179        sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
 180        sysfs_remove_link(&cdev->device.kobj, "device");
 181        thermal_cooling_device_unregister(cdev);
 182
 183        return 0;
 184}
 185
 186static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state)
 187{
 188        if (!device)
 189                return -EINVAL;
 190
 191        acpi_bus_set_power(device->handle, ACPI_STATE_D0);
 192
 193        return AE_OK;
 194}
 195
 196static int acpi_fan_resume(struct acpi_device *device)
 197{
 198        int result;
 199
 200        if (!device)
 201                return -EINVAL;
 202
 203        result = acpi_bus_update_power(device->handle, NULL);
 204        if (result)
 205                printk(KERN_ERR PREFIX "Error updating fan power state\n");
 206
 207        return result;
 208}
 209
 210static int __init acpi_fan_init(void)
 211{
 212        int result = 0;
 213
 214        result = acpi_bus_register_driver(&acpi_fan_driver);
 215        if (result < 0)
 216                return -ENODEV;
 217
 218        return 0;
 219}
 220
 221static void __exit acpi_fan_exit(void)
 222{
 223
 224        acpi_bus_unregister_driver(&acpi_fan_driver);
 225
 226        return;
 227}
 228
 229module_init(acpi_fan_init);
 230module_exit(acpi_fan_exit);
 231