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