]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/acpi/ac.c
324b5a096effffc8110460df01336fb77e1894d2
[karo-tx-linux.git] / drivers / acpi / ac.c
1 /*
2  *  acpi_ac.c - ACPI AC Adapter Driver ($Revision: 27 $)
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/slab.h>
29 #include <linux/init.h>
30 #include <linux/types.h>
31 #include <linux/dmi.h>
32 #include <linux/delay.h>
33 #ifdef CONFIG_ACPI_PROCFS_POWER
34 #include <linux/proc_fs.h>
35 #include <linux/seq_file.h>
36 #endif
37 #include <linux/platform_device.h>
38 #include <linux/power_supply.h>
39 #include <acpi/acpi_bus.h>
40 #include <acpi/acpi_drivers.h>
41
42 #define PREFIX "ACPI: "
43
44 #define ACPI_AC_CLASS                   "ac_adapter"
45 #define ACPI_AC_DEVICE_NAME             "AC Adapter"
46 #define ACPI_AC_FILE_STATE              "state"
47 #define ACPI_AC_NOTIFY_STATUS           0x80
48 #define ACPI_AC_STATUS_OFFLINE          0x00
49 #define ACPI_AC_STATUS_ONLINE           0x01
50 #define ACPI_AC_STATUS_UNKNOWN          0xFF
51
52 #define _COMPONENT              ACPI_AC_COMPONENT
53 ACPI_MODULE_NAME("ac");
54
55 MODULE_AUTHOR("Paul Diefenbaugh");
56 MODULE_DESCRIPTION("ACPI AC Adapter Driver");
57 MODULE_LICENSE("GPL");
58
59 #ifdef CONFIG_ACPI_PROCFS_POWER
60 extern struct proc_dir_entry *acpi_lock_ac_dir(void);
61 extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
62 static int acpi_ac_open_fs(struct inode *inode, struct file *file);
63 #endif
64
65 static int ac_sleep_before_get_state_ms;
66
67 struct acpi_ac {
68         struct power_supply charger;
69         struct acpi_device *adev;
70         struct platform_device *pdev;
71         unsigned long long state;
72 };
73
74 #define to_acpi_ac(x) container_of(x, struct acpi_ac, charger)
75
76 #ifdef CONFIG_ACPI_PROCFS_POWER
77 static const struct file_operations acpi_ac_fops = {
78         .owner = THIS_MODULE,
79         .open = acpi_ac_open_fs,
80         .read = seq_read,
81         .llseek = seq_lseek,
82         .release = single_release,
83 };
84 #endif
85
86 /* --------------------------------------------------------------------------
87                                AC Adapter Management
88    -------------------------------------------------------------------------- */
89
90 static int acpi_ac_get_state(struct acpi_ac *ac)
91 {
92         acpi_status status;
93
94         status = acpi_evaluate_integer(ac->adev->handle, "_PSR", NULL,
95                                        &ac->state);
96         if (ACPI_FAILURE(status)) {
97                 ACPI_EXCEPTION((AE_INFO, status,
98                                 "Error reading AC Adapter state"));
99                 ac->state = ACPI_AC_STATUS_UNKNOWN;
100                 return -ENODEV;
101         }
102
103         return 0;
104 }
105
106 /* --------------------------------------------------------------------------
107                             sysfs I/F
108    -------------------------------------------------------------------------- */
109 static int get_ac_property(struct power_supply *psy,
110                            enum power_supply_property psp,
111                            union power_supply_propval *val)
112 {
113         struct acpi_ac *ac = to_acpi_ac(psy);
114
115         if (!ac)
116                 return -ENODEV;
117
118         if (acpi_ac_get_state(ac))
119                 return -ENODEV;
120
121         switch (psp) {
122         case POWER_SUPPLY_PROP_ONLINE:
123                 val->intval = ac->state;
124                 break;
125         default:
126                 return -EINVAL;
127         }
128         return 0;
129 }
130
131 static enum power_supply_property ac_props[] = {
132         POWER_SUPPLY_PROP_ONLINE,
133 };
134
135 #ifdef CONFIG_ACPI_PROCFS_POWER
136 /* --------------------------------------------------------------------------
137                               FS Interface (/proc)
138    -------------------------------------------------------------------------- */
139
140 static struct proc_dir_entry *acpi_ac_dir;
141
142 static int acpi_ac_seq_show(struct seq_file *seq, void *offset)
143 {
144         struct acpi_ac *ac = seq->private;
145
146
147         if (!ac)
148                 return 0;
149
150         if (acpi_ac_get_state(ac)) {
151                 seq_puts(seq, "ERROR: Unable to read AC Adapter state\n");
152                 return 0;
153         }
154
155         seq_puts(seq, "state:                   ");
156         switch (ac->state) {
157         case ACPI_AC_STATUS_OFFLINE:
158                 seq_puts(seq, "off-line\n");
159                 break;
160         case ACPI_AC_STATUS_ONLINE:
161                 seq_puts(seq, "on-line\n");
162                 break;
163         default:
164                 seq_puts(seq, "unknown\n");
165                 break;
166         }
167
168         return 0;
169 }
170
171 static int acpi_ac_open_fs(struct inode *inode, struct file *file)
172 {
173         return single_open(file, acpi_ac_seq_show, PDE_DATA(inode));
174 }
175
176 static int acpi_ac_add_fs(struct acpi_ac *ac)
177 {
178         struct proc_dir_entry *entry = NULL;
179
180         printk(KERN_WARNING PREFIX "Deprecated procfs I/F for AC is loaded,"
181                         " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n");
182         if (!acpi_device_dir(ac->adev)) {
183                 acpi_device_dir(ac->adev) =
184                         proc_mkdir(acpi_device_bid(ac->adev), acpi_ac_dir);
185                 if (!acpi_device_dir(ac->adev))
186                         return -ENODEV;
187         }
188
189         /* 'state' [R] */
190         entry = proc_create_data(ACPI_AC_FILE_STATE,
191                                  S_IRUGO, acpi_device_dir(ac->adev),
192                                  &acpi_ac_fops, ac);
193         if (!entry)
194                 return -ENODEV;
195         return 0;
196 }
197
198 static int acpi_ac_remove_fs(struct acpi_ac *ac)
199 {
200
201         if (acpi_device_dir(ac->adev)) {
202                 remove_proc_entry(ACPI_AC_FILE_STATE,
203                                   acpi_device_dir(ac->adev));
204                 remove_proc_entry(acpi_device_bid(ac->adev), acpi_ac_dir);
205                 acpi_device_dir(ac->adev) = NULL;
206         }
207
208         return 0;
209 }
210 #endif
211
212 /* --------------------------------------------------------------------------
213                                    Driver Model
214    -------------------------------------------------------------------------- */
215
216 static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data)
217 {
218         struct acpi_ac *ac = data;
219
220         if (!ac)
221                 return;
222
223         switch (event) {
224         default:
225                 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
226                                   "Unsupported event [0x%x]\n", event));
227         case ACPI_AC_NOTIFY_STATUS:
228         case ACPI_NOTIFY_BUS_CHECK:
229         case ACPI_NOTIFY_DEVICE_CHECK:
230                 /*
231                  * A buggy BIOS may notify AC first and then sleep for
232                  * a specific time before doing actual operations in the
233                  * EC event handler (_Qxx). This will cause the AC state
234                  * reported by the ACPI event to be incorrect, so wait for a
235                  * specific time for the EC event handler to make progress.
236                  */
237                 if (ac_sleep_before_get_state_ms > 0)
238                         msleep(ac_sleep_before_get_state_ms);
239
240                 acpi_ac_get_state(ac);
241                 acpi_bus_generate_netlink_event(ac->adev->pnp.device_class,
242                                                 dev_name(&ac->pdev->dev),
243                                                 event, (u32) ac->state);
244                 acpi_notifier_call_chain(ac->adev, event, (u32) ac->state);
245                 kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
246         }
247
248         return;
249 }
250
251 static int thinkpad_e530_quirk(const struct dmi_system_id *d)
252 {
253         ac_sleep_before_get_state_ms = 1000;
254         return 0;
255 }
256
257 static struct dmi_system_id ac_dmi_table[] = {
258         {
259         .callback = thinkpad_e530_quirk,
260         .ident = "thinkpad e530",
261         .matches = {
262                 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
263                 DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"),
264                 },
265         },
266         {},
267 };
268
269 static int acpi_ac_probe(struct platform_device *pdev)
270 {
271         int result = 0;
272         struct acpi_ac *ac = NULL;
273         struct acpi_device *adev;
274
275         if (!pdev)
276                 return -EINVAL;
277
278         result = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
279         if (result)
280                 return -ENODEV;
281
282         ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
283         if (!ac)
284                 return -ENOMEM;
285
286         strcpy(acpi_device_name(adev), ACPI_AC_DEVICE_NAME);
287         strcpy(acpi_device_class(adev), ACPI_AC_CLASS);
288         ac->adev = adev;
289         ac->pdev = pdev;
290         platform_set_drvdata(pdev, ac);
291
292         result = acpi_ac_get_state(ac);
293         if (result)
294                 goto end;
295
296 #ifdef CONFIG_ACPI_PROCFS_POWER
297         result = acpi_ac_add_fs(ac);
298         if (result)
299                 goto end;
300 #endif
301         ac->charger.name = acpi_device_bid(adev);
302         ac->charger.type = POWER_SUPPLY_TYPE_MAINS;
303         ac->charger.properties = ac_props;
304         ac->charger.num_properties = ARRAY_SIZE(ac_props);
305         ac->charger.get_property = get_ac_property;
306         result = power_supply_register(&pdev->dev, &ac->charger);
307         if (result)
308                 goto end;
309
310         result = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
311                         ACPI_DEVICE_NOTIFY, acpi_ac_notify_handler, ac);
312         if (result) {
313                 power_supply_unregister(&ac->charger);
314                 goto end;
315         }
316         printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
317                acpi_device_name(adev), acpi_device_bid(adev),
318                ac->state ? "on-line" : "off-line");
319
320       end:
321         if (result) {
322 #ifdef CONFIG_ACPI_PROCFS_POWER
323                 acpi_ac_remove_fs(ac);
324 #endif
325                 kfree(ac);
326         }
327
328         dmi_check_system(ac_dmi_table);
329         return result;
330 }
331
332 #ifdef CONFIG_PM_SLEEP
333 static int acpi_ac_resume(struct device *dev)
334 {
335         struct acpi_ac *ac;
336         unsigned old_state;
337
338         if (!dev)
339                 return -EINVAL;
340
341         ac = platform_get_drvdata(to_platform_device(dev));
342         if (!ac)
343                 return -EINVAL;
344
345         old_state = ac->state;
346         if (acpi_ac_get_state(ac))
347                 return 0;
348         if (old_state != ac->state)
349                 kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
350         return 0;
351 }
352 #endif
353 static SIMPLE_DEV_PM_OPS(acpi_ac_pm_ops, NULL, acpi_ac_resume);
354
355 static int acpi_ac_remove(struct platform_device *pdev)
356 {
357         struct acpi_ac *ac;
358
359         if (!pdev)
360                 return -EINVAL;
361
362         acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
363                         ACPI_DEVICE_NOTIFY, acpi_ac_notify_handler);
364
365         ac = platform_get_drvdata(pdev);
366         if (ac->charger.dev)
367                 power_supply_unregister(&ac->charger);
368
369 #ifdef CONFIG_ACPI_PROCFS_POWER
370         acpi_ac_remove_fs(ac);
371 #endif
372
373         kfree(ac);
374
375         return 0;
376 }
377
378 static const struct acpi_device_id acpi_ac_match[] = {
379         { "ACPI0003", 0 },
380         { }
381 };
382 MODULE_DEVICE_TABLE(acpi, acpi_ac_match);
383
384 static struct platform_driver acpi_ac_driver = {
385         .probe          = acpi_ac_probe,
386         .remove         = acpi_ac_remove,
387         .driver         = {
388                 .name   = "acpi-ac",
389                 .owner  = THIS_MODULE,
390                 .pm     = &acpi_ac_pm_ops,
391                 .acpi_match_table = ACPI_PTR(acpi_ac_match),
392         },
393 };
394
395 static int __init acpi_ac_init(void)
396 {
397         int result;
398
399         if (acpi_disabled)
400                 return -ENODEV;
401
402 #ifdef CONFIG_ACPI_PROCFS_POWER
403         acpi_ac_dir = acpi_lock_ac_dir();
404         if (!acpi_ac_dir)
405                 return -ENODEV;
406 #endif
407
408         result = platform_driver_register(&acpi_ac_driver);
409         if (result < 0) {
410 #ifdef CONFIG_ACPI_PROCFS_POWER
411                 acpi_unlock_ac_dir(acpi_ac_dir);
412 #endif
413                 return -ENODEV;
414         }
415
416         return 0;
417 }
418
419 static void __exit acpi_ac_exit(void)
420 {
421         platform_driver_unregister(&acpi_ac_driver);
422 #ifdef CONFIG_ACPI_PROCFS_POWER
423         acpi_unlock_ac_dir(acpi_ac_dir);
424 #endif
425 }
426 module_init(acpi_ac_init);
427 module_exit(acpi_ac_exit);