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