ideapad: define cfg bits and create sysfs node for cfg
[linux-2.6.git] / drivers / platform / x86 / ideapad-laptop.c
1 /*
2  *  ideapad-laptop.c - Lenovo IdeaPad ACPI Extras
3  *
4  *  Copyright © 2010 Intel Corporation
5  *  Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  *  02110-1301, USA.
21  */
22
23 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
25 #include <linux/kernel.h>
26 #include <linux/module.h>
27 #include <linux/init.h>
28 #include <linux/types.h>
29 #include <acpi/acpi_bus.h>
30 #include <acpi/acpi_drivers.h>
31 #include <linux/rfkill.h>
32 #include <linux/platform_device.h>
33 #include <linux/input.h>
34 #include <linux/input/sparse-keymap.h>
35
36 #define IDEAPAD_RFKILL_DEV_NUM  (3)
37
38 #define CFG_BT_BIT      (16)
39 #define CFG_3G_BIT      (17)
40 #define CFG_WIFI_BIT    (18)
41
42 struct ideapad_private {
43         struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
44         struct platform_device *platform_device;
45         struct input_dev *inputdev;
46         unsigned long cfg;
47 };
48
49 static acpi_handle ideapad_handle;
50 static bool no_bt_rfkill;
51 module_param(no_bt_rfkill, bool, 0444);
52 MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
53
54 /*
55  * ACPI Helpers
56  */
57 #define IDEAPAD_EC_TIMEOUT (100) /* in ms */
58
59 static int read_method_int(acpi_handle handle, const char *method, int *val)
60 {
61         acpi_status status;
62         unsigned long long result;
63
64         status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
65         if (ACPI_FAILURE(status)) {
66                 *val = -1;
67                 return -1;
68         } else {
69                 *val = result;
70                 return 0;
71         }
72 }
73
74 static int method_vpcr(acpi_handle handle, int cmd, int *ret)
75 {
76         acpi_status status;
77         unsigned long long result;
78         struct acpi_object_list params;
79         union acpi_object in_obj;
80
81         params.count = 1;
82         params.pointer = &in_obj;
83         in_obj.type = ACPI_TYPE_INTEGER;
84         in_obj.integer.value = cmd;
85
86         status = acpi_evaluate_integer(handle, "VPCR", &params, &result);
87
88         if (ACPI_FAILURE(status)) {
89                 *ret = -1;
90                 return -1;
91         } else {
92                 *ret = result;
93                 return 0;
94         }
95 }
96
97 static int method_vpcw(acpi_handle handle, int cmd, int data)
98 {
99         struct acpi_object_list params;
100         union acpi_object in_obj[2];
101         acpi_status status;
102
103         params.count = 2;
104         params.pointer = in_obj;
105         in_obj[0].type = ACPI_TYPE_INTEGER;
106         in_obj[0].integer.value = cmd;
107         in_obj[1].type = ACPI_TYPE_INTEGER;
108         in_obj[1].integer.value = data;
109
110         status = acpi_evaluate_object(handle, "VPCW", &params, NULL);
111         if (status != AE_OK)
112                 return -1;
113         return 0;
114 }
115
116 static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data)
117 {
118         int val;
119         unsigned long int end_jiffies;
120
121         if (method_vpcw(handle, 1, cmd))
122                 return -1;
123
124         for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
125              time_before(jiffies, end_jiffies);) {
126                 schedule();
127                 if (method_vpcr(handle, 1, &val))
128                         return -1;
129                 if (val == 0) {
130                         if (method_vpcr(handle, 0, &val))
131                                 return -1;
132                         *data = val;
133                         return 0;
134                 }
135         }
136         pr_err("timeout in read_ec_cmd\n");
137         return -1;
138 }
139
140 static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
141 {
142         int val;
143         unsigned long int end_jiffies;
144
145         if (method_vpcw(handle, 0, data))
146                 return -1;
147         if (method_vpcw(handle, 1, cmd))
148                 return -1;
149
150         for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1;
151              time_before(jiffies, end_jiffies);) {
152                 schedule();
153                 if (method_vpcr(handle, 1, &val))
154                         return -1;
155                 if (val == 0)
156                         return 0;
157         }
158         pr_err("timeout in write_ec_cmd\n");
159         return -1;
160 }
161
162 /*
163  * sysfs
164  */
165 static ssize_t show_ideapad_cam(struct device *dev,
166                                 struct device_attribute *attr,
167                                 char *buf)
168 {
169         unsigned long result;
170
171         if (read_ec_data(ideapad_handle, 0x1D, &result))
172                 return sprintf(buf, "-1\n");
173         return sprintf(buf, "%lu\n", result);
174 }
175
176 static ssize_t store_ideapad_cam(struct device *dev,
177                                  struct device_attribute *attr,
178                                  const char *buf, size_t count)
179 {
180         int ret, state;
181
182         if (!count)
183                 return 0;
184         if (sscanf(buf, "%i", &state) != 1)
185                 return -EINVAL;
186         ret = write_ec_cmd(ideapad_handle, 0x1E, state);
187         if (ret < 0)
188                 return ret;
189         return count;
190 }
191
192 static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
193
194 static ssize_t show_ideapad_cfg(struct device *dev,
195                                 struct device_attribute *attr,
196                                 char *buf)
197 {
198         struct ideapad_private *priv = dev_get_drvdata(dev);
199
200         return sprintf(buf, "0x%.8lX\n", priv->cfg);
201 }
202
203 static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
204
205 static struct attribute *ideapad_attributes[] = {
206         &dev_attr_camera_power.attr,
207         &dev_attr_cfg.attr,
208         NULL
209 };
210
211 static struct attribute_group ideapad_attribute_group = {
212         .attrs = ideapad_attributes
213 };
214
215 /*
216  * Rfkill
217  */
218 struct ideapad_rfk_data {
219         char *name;
220         int cfgbit;
221         int opcode;
222         int type;
223 };
224
225 const struct ideapad_rfk_data ideapad_rfk_data[] = {
226         { "ideapad_wlan",      CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN },
227         { "ideapad_bluetooth", CFG_BT_BIT,   0x17, RFKILL_TYPE_BLUETOOTH },
228         { "ideapad_3g",        CFG_3G_BIT,   0x20, RFKILL_TYPE_WWAN },
229 };
230
231 static int ideapad_rfk_set(void *data, bool blocked)
232 {
233         unsigned long opcode = (unsigned long)data;
234
235         return write_ec_cmd(ideapad_handle, opcode, !blocked);
236 }
237
238 static struct rfkill_ops ideapad_rfk_ops = {
239         .set_block = ideapad_rfk_set,
240 };
241
242 static void ideapad_sync_rfk_state(struct acpi_device *adevice)
243 {
244         struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
245         unsigned long hw_blocked;
246         int i;
247
248         if (read_ec_data(ideapad_handle, 0x23, &hw_blocked))
249                 return;
250         hw_blocked = !hw_blocked;
251
252         for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
253                 if (priv->rfk[i])
254                         rfkill_set_hw_state(priv->rfk[i], hw_blocked);
255 }
256
257 static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
258                                              int dev)
259 {
260         struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
261         int ret;
262         unsigned long sw_blocked;
263
264         if (no_bt_rfkill &&
265             (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
266                 /* Force to enable bluetooth when no_bt_rfkill=1 */
267                 write_ec_cmd(ideapad_handle,
268                              ideapad_rfk_data[dev].opcode, 1);
269                 return 0;
270         }
271
272         priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
273                                       ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
274                                       (void *)(long)dev);
275         if (!priv->rfk[dev])
276                 return -ENOMEM;
277
278         if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1,
279                          &sw_blocked)) {
280                 rfkill_init_sw_state(priv->rfk[dev], 0);
281         } else {
282                 sw_blocked = !sw_blocked;
283                 rfkill_init_sw_state(priv->rfk[dev], sw_blocked);
284         }
285
286         ret = rfkill_register(priv->rfk[dev]);
287         if (ret) {
288                 rfkill_destroy(priv->rfk[dev]);
289                 return ret;
290         }
291         return 0;
292 }
293
294 static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
295                                                 int dev)
296 {
297         struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
298
299         if (!priv->rfk[dev])
300                 return;
301
302         rfkill_unregister(priv->rfk[dev]);
303         rfkill_destroy(priv->rfk[dev]);
304 }
305
306 /*
307  * Platform device
308  */
309 static int __devinit ideapad_platform_init(struct ideapad_private *priv)
310 {
311         int result;
312
313         priv->platform_device = platform_device_alloc("ideapad", -1);
314         if (!priv->platform_device)
315                 return -ENOMEM;
316         platform_set_drvdata(priv->platform_device, priv);
317
318         result = platform_device_add(priv->platform_device);
319         if (result)
320                 goto fail_platform_device;
321
322         result = sysfs_create_group(&priv->platform_device->dev.kobj,
323                                     &ideapad_attribute_group);
324         if (result)
325                 goto fail_sysfs;
326         return 0;
327
328 fail_sysfs:
329         platform_device_del(priv->platform_device);
330 fail_platform_device:
331         platform_device_put(priv->platform_device);
332         return result;
333 }
334
335 static void ideapad_platform_exit(struct ideapad_private *priv)
336 {
337         sysfs_remove_group(&priv->platform_device->dev.kobj,
338                            &ideapad_attribute_group);
339         platform_device_unregister(priv->platform_device);
340 }
341
342 /*
343  * input device
344  */
345 static const struct key_entry ideapad_keymap[] = {
346         { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } },
347         { KE_KEY, 0x0D, { KEY_WLAN } },
348         { KE_END, 0 },
349 };
350
351 static int __devinit ideapad_input_init(struct ideapad_private *priv)
352 {
353         struct input_dev *inputdev;
354         int error;
355
356         inputdev = input_allocate_device();
357         if (!inputdev) {
358                 pr_info("Unable to allocate input device\n");
359                 return -ENOMEM;
360         }
361
362         inputdev->name = "Ideapad extra buttons";
363         inputdev->phys = "ideapad/input0";
364         inputdev->id.bustype = BUS_HOST;
365         inputdev->dev.parent = &priv->platform_device->dev;
366
367         error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL);
368         if (error) {
369                 pr_err("Unable to setup input device keymap\n");
370                 goto err_free_dev;
371         }
372
373         error = input_register_device(inputdev);
374         if (error) {
375                 pr_err("Unable to register input device\n");
376                 goto err_free_keymap;
377         }
378
379         priv->inputdev = inputdev;
380         return 0;
381
382 err_free_keymap:
383         sparse_keymap_free(inputdev);
384 err_free_dev:
385         input_free_device(inputdev);
386         return error;
387 }
388
389 static void __devexit ideapad_input_exit(struct ideapad_private *priv)
390 {
391         sparse_keymap_free(priv->inputdev);
392         input_unregister_device(priv->inputdev);
393         priv->inputdev = NULL;
394 }
395
396 static void ideapad_input_report(struct ideapad_private *priv,
397                                  unsigned long scancode)
398 {
399         sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
400 }
401
402 /*
403  * module init/exit
404  */
405 static const struct acpi_device_id ideapad_device_ids[] = {
406         { "VPC2004", 0},
407         { "", 0},
408 };
409 MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
410
411 static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
412 {
413         int ret, i;
414         unsigned long cfg;
415         struct ideapad_private *priv;
416
417         if (read_method_int(adevice->handle, "_CFG", (int *)&cfg))
418                 return -ENODEV;
419
420         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
421         if (!priv)
422                 return -ENOMEM;
423         dev_set_drvdata(&adevice->dev, priv);
424         ideapad_handle = adevice->handle;
425         priv->cfg = cfg;
426
427         ret = ideapad_platform_init(priv);
428         if (ret)
429                 goto platform_failed;
430
431         ret = ideapad_input_init(priv);
432         if (ret)
433                 goto input_failed;
434
435         for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
436                 if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg))
437                         ideapad_register_rfkill(adevice, i);
438                 else
439                         priv->rfk[i] = NULL;
440         }
441         ideapad_sync_rfk_state(adevice);
442
443         return 0;
444
445 input_failed:
446         ideapad_platform_exit(priv);
447 platform_failed:
448         kfree(priv);
449         return ret;
450 }
451
452 static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
453 {
454         struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
455         int i;
456
457         for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
458                 ideapad_unregister_rfkill(adevice, i);
459         ideapad_input_exit(priv);
460         ideapad_platform_exit(priv);
461         dev_set_drvdata(&adevice->dev, NULL);
462         kfree(priv);
463
464         return 0;
465 }
466
467 static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
468 {
469         struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
470         acpi_handle handle = adevice->handle;
471         unsigned long vpc1, vpc2, vpc_bit;
472
473         if (read_ec_data(handle, 0x10, &vpc1))
474                 return;
475         if (read_ec_data(handle, 0x1A, &vpc2))
476                 return;
477
478         vpc1 = (vpc2 << 8) | vpc1;
479         for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
480                 if (test_bit(vpc_bit, &vpc1)) {
481                         if (vpc_bit == 9)
482                                 ideapad_sync_rfk_state(adevice);
483                         else if (vpc_bit == 4)
484                                 read_ec_data(handle, 0x12, &vpc2);
485                         else
486                                 ideapad_input_report(priv, vpc_bit);
487                 }
488         }
489 }
490
491 static struct acpi_driver ideapad_acpi_driver = {
492         .name = "ideapad_acpi",
493         .class = "IdeaPad",
494         .ids = ideapad_device_ids,
495         .ops.add = ideapad_acpi_add,
496         .ops.remove = ideapad_acpi_remove,
497         .ops.notify = ideapad_acpi_notify,
498         .owner = THIS_MODULE,
499 };
500
501 static int __init ideapad_acpi_module_init(void)
502 {
503         return acpi_bus_register_driver(&ideapad_acpi_driver);
504 }
505
506 static void __exit ideapad_acpi_module_exit(void)
507 {
508         acpi_bus_unregister_driver(&ideapad_acpi_driver);
509 }
510
511 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
512 MODULE_DESCRIPTION("IdeaPad ACPI Extras");
513 MODULE_LICENSE("GPL");
514
515 module_init(ideapad_acpi_module_init);
516 module_exit(ideapad_acpi_module_exit);