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