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);
  49
  50static const struct acpi_device_id fan_device_ids[] = {
  51        {"PNP0C0B", 0},
  52        {"", 0},
  53};
  54MODULE_DEVICE_TABLE(acpi, fan_device_ids);
  55
  56#ifdef CONFIG_PM_SLEEP
  57static int acpi_fan_suspend(struct device *dev);
  58static int acpi_fan_resume(struct device *dev);
  59#endif
  60static SIMPLE_DEV_PM_OPS(acpi_fan_pm, acpi_fan_suspend, acpi_fan_resume);
  61
  62static struct acpi_driver acpi_fan_driver = {
  63        .name = "fan",
  64        .class = ACPI_FAN_CLASS,
  65        .ids = fan_device_ids,
  66        .ops = {
  67                .add = acpi_fan_add,
  68                .remove = acpi_fan_remove,
  69                },
  70        .drv.pm = &acpi_fan_pm,
  71};
  72
  73/* thermal cooling device callbacks */
  74static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
  75                             *state)
  76{
  77        /* ACPI fan device only support two states: ON/OFF */
  78        *state = 1;
  79        return 0;
  80}
  81
  82static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
  83                             *state)
  84{
  85        struct acpi_device *device = cdev->devdata;
  86        int result;
  87        int acpi_state;
  88
  89        if (!device)
  90                return -EINVAL;
  91
  92        result = acpi_bus_update_power(device->handle, &acpi_state);
  93        if (result)
  94                return result;
  95
  96        *state = (acpi_state == ACPI_STATE_D3 ? 0 :
  97                 (acpi_state == ACPI_STATE_D0 ? 1 : -1));
  98        return 0;
  99}
 100
 101static int
 102fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 103{
 104        struct acpi_device *device = cdev->devdata;
 105        int result;
 106
 107        if (!device || (state != 0 && state != 1))
 108                return -EINVAL;
 109
 110        result = acpi_bus_set_power(device->handle,
 111                                state ? ACPI_STATE_D0 : ACPI_STATE_D3);
 112
 113        return result;
 114}
 115
 116static const struct thermal_cooling_device_ops fan_cooling_ops = {
 117        .get_max_state = fan_get_max_state,
 118        .get_cur_state = fan_get_cur_state,
 119        .set_cur_state = fan_set_cur_state,
 120};
 121
 122/* --------------------------------------------------------------------------
 123                                 Driver Interface
 124   -------------------------------------------------------------------------- */
 125
 126static int acpi_fan_add(struct acpi_device *device)
 127{
 128        int result = 0;
 129        struct thermal_cooling_device *cdev;
 130
 131        if (!device)
 132                return -EINVAL;
 133
 134        strcpy(acpi_device_name(device), "Fan");
 135        strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
 136
 137        result = acpi_bus_update_power(device->handle, NULL);
 138        if (result) {
 139                printk(KERN_ERR PREFIX "Setting initial power state\n");
 140                goto end;
 141        }
 142
 143        cdev = thermal_cooling_device_register("Fan", device,
 144                                                &fan_cooling_ops);
 145        if (IS_ERR(cdev)) {
 146                result = PTR_ERR(cdev);
 147                goto end;
 148        }
 149
 150        dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id);
 151
 152        device->driver_data = cdev;
 153        result = sysfs_create_link(&device->dev.kobj,
 154                                   &cdev->device.kobj,
 155                                   "thermal_cooling");
 156        if (result)
 157                dev_err(&device->dev, "Failed to create sysfs link "
 158                        "'thermal_cooling'\n");
 159
 160        result = sysfs_create_link(&cdev->device.kobj,
 161                                   &device->dev.kobj,
 162                                   "device");
 163        if (result)
 164                dev_err(&device->dev, "Failed to create sysfs link "
 165                        "'device'\n");
 166
 167        printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
 168               acpi_device_name(device), acpi_device_bid(device),
 169               !device->power.state ? "on" : "off");
 170
 171      end:
 172        return result;
 173}
 174
 175static int acpi_fan_remove(struct acpi_device *device)
 176{
 177        struct thermal_cooling_device *cdev = acpi_driver_data(device);
 178
 179        if (!device || !cdev)
 180                return -EINVAL;
 181
 182        sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
 183        sysfs_remove_link(&cdev->device.kobj, "device");
 184        thermal_cooling_device_unregister(cdev);
 185
 186        return 0;
 187}
 188
 189#ifdef CONFIG_PM_SLEEP
 190static int acpi_fan_suspend(struct device *dev)
 191{
 192        if (!dev)
 193                return -EINVAL;
 194
 195        acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0);
 196
 197        return AE_OK;
 198}
 199
 200static int acpi_fan_resume(struct device *dev)
 201{
 202        int result;
 203
 204        if (!dev)
 205                return -EINVAL;
 206
 207        result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL);
 208        if (result)
 209                printk(KERN_ERR PREFIX "Error updating fan power state\n");
 210
 211        return result;
 212}
 213#endif
 214
 215module_acpi_driver(acpi_fan_driver);
 216