• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/acpi/
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/proc_fs.h>
31#include <linux/seq_file.h>
32#include <asm/uaccess.h>
33#include <linux/thermal.h>
34#include <acpi/acpi_bus.h>
35#include <acpi/acpi_drivers.h>
36
37#define PREFIX "ACPI: "
38
39#define ACPI_FAN_CLASS			"fan"
40#define ACPI_FAN_FILE_STATE		"state"
41
42#define _COMPONENT		ACPI_FAN_COMPONENT
43ACPI_MODULE_NAME("fan");
44
45MODULE_AUTHOR("Paul Diefenbaugh");
46MODULE_DESCRIPTION("ACPI Fan Driver");
47MODULE_LICENSE("GPL");
48
49static int acpi_fan_add(struct acpi_device *device);
50static int acpi_fan_remove(struct acpi_device *device, int type);
51static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state);
52static int acpi_fan_resume(struct acpi_device *device);
53
54static const struct acpi_device_id fan_device_ids[] = {
55	{"PNP0C0B", 0},
56	{"", 0},
57};
58MODULE_DEVICE_TABLE(acpi, fan_device_ids);
59
60static 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		.suspend = acpi_fan_suspend,
68		.resume = acpi_fan_resume,
69		},
70};
71
72/* thermal cooling device callbacks */
73static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
74			     *state)
75{
76	/* ACPI fan device only support two states: ON/OFF */
77	*state = 1;
78	return 0;
79}
80
81static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
82			     *state)
83{
84	struct acpi_device *device = cdev->devdata;
85	int result;
86	int acpi_state;
87
88	if (!device)
89		return -EINVAL;
90
91	result = acpi_bus_get_power(device->handle, &acpi_state);
92	if (result)
93		return result;
94
95	*state = (acpi_state == ACPI_STATE_D3 ? 0 :
96		 (acpi_state == ACPI_STATE_D0 ? 1 : -1));
97	return 0;
98}
99
100static int
101fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
102{
103	struct acpi_device *device = cdev->devdata;
104	int result;
105
106	if (!device || (state != 0 && state != 1))
107		return -EINVAL;
108
109	result = acpi_bus_set_power(device->handle,
110				state ? ACPI_STATE_D0 : ACPI_STATE_D3);
111
112	return result;
113}
114
115static struct thermal_cooling_device_ops fan_cooling_ops = {
116	.get_max_state = fan_get_max_state,
117	.get_cur_state = fan_get_cur_state,
118	.set_cur_state = fan_set_cur_state,
119};
120
121/* --------------------------------------------------------------------------
122                              FS Interface (/proc)
123   -------------------------------------------------------------------------- */
124#ifdef CONFIG_ACPI_PROCFS
125
126static struct proc_dir_entry *acpi_fan_dir;
127
128static int acpi_fan_read_state(struct seq_file *seq, void *offset)
129{
130	struct acpi_device *device = seq->private;
131	int state = 0;
132
133
134	if (device) {
135		if (acpi_bus_get_power(device->handle, &state))
136			seq_printf(seq, "status:                  ERROR\n");
137		else
138			seq_printf(seq, "status:                  %s\n",
139				   !state ? "on" : "off");
140	}
141	return 0;
142}
143
144static int acpi_fan_state_open_fs(struct inode *inode, struct file *file)
145{
146	return single_open(file, acpi_fan_read_state, PDE(inode)->data);
147}
148
149static ssize_t
150acpi_fan_write_state(struct file *file, const char __user * buffer,
151		     size_t count, loff_t * ppos)
152{
153	int result = 0;
154	struct seq_file *m = file->private_data;
155	struct acpi_device *device = m->private;
156	char state_string[3] = { '\0' };
157
158	if (count > sizeof(state_string) - 1)
159		return -EINVAL;
160
161	if (copy_from_user(state_string, buffer, count))
162		return -EFAULT;
163
164	state_string[count] = '\0';
165	if ((state_string[0] < '0') || (state_string[0] > '3'))
166		return -EINVAL;
167	if (state_string[1] == '\n')
168		state_string[1] = '\0';
169	if (state_string[1] != '\0')
170		return -EINVAL;
171
172	result = acpi_bus_set_power(device->handle,
173				    simple_strtoul(state_string, NULL, 0));
174	if (result)
175		return result;
176
177	return count;
178}
179
180static const struct file_operations acpi_fan_state_ops = {
181	.open = acpi_fan_state_open_fs,
182	.read = seq_read,
183	.write = acpi_fan_write_state,
184	.llseek = seq_lseek,
185	.release = single_release,
186	.owner = THIS_MODULE,
187};
188
189static int acpi_fan_add_fs(struct acpi_device *device)
190{
191	struct proc_dir_entry *entry = NULL;
192
193
194	if (!device)
195		return -EINVAL;
196
197	if (!acpi_device_dir(device)) {
198		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
199						     acpi_fan_dir);
200		if (!acpi_device_dir(device))
201			return -ENODEV;
202	}
203
204	/* 'status' [R/W] */
205	entry = proc_create_data(ACPI_FAN_FILE_STATE,
206				 S_IFREG | S_IRUGO | S_IWUSR,
207				 acpi_device_dir(device),
208				 &acpi_fan_state_ops,
209				 device);
210	if (!entry)
211		return -ENODEV;
212	return 0;
213}
214
215static int acpi_fan_remove_fs(struct acpi_device *device)
216{
217
218	if (acpi_device_dir(device)) {
219		remove_proc_entry(ACPI_FAN_FILE_STATE, acpi_device_dir(device));
220		remove_proc_entry(acpi_device_bid(device), acpi_fan_dir);
221		acpi_device_dir(device) = NULL;
222	}
223
224	return 0;
225}
226#else
227static int acpi_fan_add_fs(struct acpi_device *device)
228{
229	return 0;
230}
231
232static int acpi_fan_remove_fs(struct acpi_device *device)
233{
234	return 0;
235}
236#endif
237/* --------------------------------------------------------------------------
238                                 Driver Interface
239   -------------------------------------------------------------------------- */
240
241static int acpi_fan_add(struct acpi_device *device)
242{
243	int result = 0;
244	int state = 0;
245	struct thermal_cooling_device *cdev;
246
247	if (!device)
248		return -EINVAL;
249
250	strcpy(acpi_device_name(device), "Fan");
251	strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
252
253	result = acpi_bus_get_power(device->handle, &state);
254	if (result) {
255		printk(KERN_ERR PREFIX "Reading power state\n");
256		goto end;
257	}
258
259	device->flags.force_power_state = 1;
260	acpi_bus_set_power(device->handle, state);
261	device->flags.force_power_state = 0;
262
263	cdev = thermal_cooling_device_register("Fan", device,
264						&fan_cooling_ops);
265	if (IS_ERR(cdev)) {
266		result = PTR_ERR(cdev);
267		goto end;
268	}
269
270	dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id);
271
272	device->driver_data = cdev;
273	result = sysfs_create_link(&device->dev.kobj,
274				   &cdev->device.kobj,
275				   "thermal_cooling");
276	if (result)
277		dev_err(&device->dev, "Failed to create sysfs link "
278			"'thermal_cooling'\n");
279
280	result = sysfs_create_link(&cdev->device.kobj,
281				   &device->dev.kobj,
282				   "device");
283	if (result)
284		dev_err(&device->dev, "Failed to create sysfs link "
285			"'device'\n");
286
287	result = acpi_fan_add_fs(device);
288	if (result)
289		goto end;
290
291	printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
292	       acpi_device_name(device), acpi_device_bid(device),
293	       !device->power.state ? "on" : "off");
294
295      end:
296	return result;
297}
298
299static int acpi_fan_remove(struct acpi_device *device, int type)
300{
301	struct thermal_cooling_device *cdev = acpi_driver_data(device);
302
303	if (!device || !cdev)
304		return -EINVAL;
305
306	acpi_fan_remove_fs(device);
307	sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
308	sysfs_remove_link(&cdev->device.kobj, "device");
309	thermal_cooling_device_unregister(cdev);
310
311	return 0;
312}
313
314static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state)
315{
316	if (!device)
317		return -EINVAL;
318
319	acpi_bus_set_power(device->handle, ACPI_STATE_D0);
320
321	return AE_OK;
322}
323
324static int acpi_fan_resume(struct acpi_device *device)
325{
326	int result = 0;
327	int power_state = 0;
328
329	if (!device)
330		return -EINVAL;
331
332	result = acpi_bus_get_power(device->handle, &power_state);
333	if (result) {
334		printk(KERN_ERR PREFIX
335				  "Error reading fan power state\n");
336		return result;
337	}
338
339	device->flags.force_power_state = 1;
340	acpi_bus_set_power(device->handle, power_state);
341	device->flags.force_power_state = 0;
342
343	return result;
344}
345
346static int __init acpi_fan_init(void)
347{
348	int result = 0;
349
350#ifdef CONFIG_ACPI_PROCFS
351	acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir);
352	if (!acpi_fan_dir)
353		return -ENODEV;
354#endif
355
356	result = acpi_bus_register_driver(&acpi_fan_driver);
357	if (result < 0) {
358#ifdef CONFIG_ACPI_PROCFS
359		remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
360#endif
361		return -ENODEV;
362	}
363
364	return 0;
365}
366
367static void __exit acpi_fan_exit(void)
368{
369
370	acpi_bus_unregister_driver(&acpi_fan_driver);
371
372#ifdef CONFIG_ACPI_PROCFS
373	remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir);
374#endif
375
376	return;
377}
378
379module_init(acpi_fan_init);
380module_exit(acpi_fan_exit);
381