Merge Linus's tree into staging-next
[zen-stable.git] / drivers / platform / x86 / dell-laptop.c
blobd3841de6a8cf199ec08b24bd85f50a0af0490d37
1 /*
2 * Driver for Dell laptop extras
4 * Copyright (c) Red Hat <mjg@redhat.com>
6 * Based on documentation in the libsmbios package, Copyright (C) 2005 Dell
7 * Inc.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <linux/init.h>
19 #include <linux/platform_device.h>
20 #include <linux/backlight.h>
21 #include <linux/err.h>
22 #include <linux/dmi.h>
23 #include <linux/io.h>
24 #include <linux/rfkill.h>
25 #include <linux/power_supply.h>
26 #include <linux/acpi.h>
27 #include <linux/mm.h>
28 #include <linux/i8042.h>
29 #include <linux/slab.h>
30 #include <linux/debugfs.h>
31 #include <linux/seq_file.h>
32 #include "../../firmware/dcdbas.h"
34 #define BRIGHTNESS_TOKEN 0x7d
36 /* This structure will be modified by the firmware when we enter
37 * system management mode, hence the volatiles */
39 struct calling_interface_buffer {
40 u16 class;
41 u16 select;
42 volatile u32 input[4];
43 volatile u32 output[4];
44 } __packed;
46 struct calling_interface_token {
47 u16 tokenID;
48 u16 location;
49 union {
50 u16 value;
51 u16 stringlength;
55 struct calling_interface_structure {
56 struct dmi_header header;
57 u16 cmdIOAddress;
58 u8 cmdIOCode;
59 u32 supportedCmds;
60 struct calling_interface_token tokens[];
61 } __packed;
63 static int da_command_address;
64 static int da_command_code;
65 static int da_num_tokens;
66 static struct calling_interface_token *da_tokens;
68 static struct platform_driver platform_driver = {
69 .driver = {
70 .name = "dell-laptop",
71 .owner = THIS_MODULE,
75 static struct platform_device *platform_device;
76 static struct backlight_device *dell_backlight_device;
77 static struct rfkill *wifi_rfkill;
78 static struct rfkill *bluetooth_rfkill;
79 static struct rfkill *wwan_rfkill;
81 static const struct dmi_system_id __initdata dell_device_table[] = {
83 .ident = "Dell laptop",
84 .matches = {
85 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
86 DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
90 .matches = {
91 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
92 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
96 .ident = "Dell Computer Corporation",
97 .matches = {
98 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
99 DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
105 static struct dmi_system_id __devinitdata dell_blacklist[] = {
106 /* Supported by compal-laptop */
108 .ident = "Dell Mini 9",
109 .matches = {
110 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
111 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
115 .ident = "Dell Mini 10",
116 .matches = {
117 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
118 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
122 .ident = "Dell Mini 10v",
123 .matches = {
124 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
125 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
129 .ident = "Dell Mini 1012",
130 .matches = {
131 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
132 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
136 .ident = "Dell Inspiron 11z",
137 .matches = {
138 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
139 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
143 .ident = "Dell Mini 12",
144 .matches = {
145 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
146 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
152 static struct calling_interface_buffer *buffer;
153 static struct page *bufferpage;
154 static DEFINE_MUTEX(buffer_mutex);
156 static int hwswitch_state;
158 static void get_buffer(void)
160 mutex_lock(&buffer_mutex);
161 memset(buffer, 0, sizeof(struct calling_interface_buffer));
164 static void release_buffer(void)
166 mutex_unlock(&buffer_mutex);
169 static void __init parse_da_table(const struct dmi_header *dm)
171 /* Final token is a terminator, so we don't want to copy it */
172 int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
173 struct calling_interface_structure *table =
174 container_of(dm, struct calling_interface_structure, header);
176 /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
177 6 bytes of entry */
179 if (dm->length < 17)
180 return;
182 da_command_address = table->cmdIOAddress;
183 da_command_code = table->cmdIOCode;
185 da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
186 sizeof(struct calling_interface_token),
187 GFP_KERNEL);
189 if (!da_tokens)
190 return;
192 memcpy(da_tokens+da_num_tokens, table->tokens,
193 sizeof(struct calling_interface_token) * tokens);
195 da_num_tokens += tokens;
198 static void __init find_tokens(const struct dmi_header *dm, void *dummy)
200 switch (dm->type) {
201 case 0xd4: /* Indexed IO */
202 break;
203 case 0xd5: /* Protected Area Type 1 */
204 break;
205 case 0xd6: /* Protected Area Type 2 */
206 break;
207 case 0xda: /* Calling interface */
208 parse_da_table(dm);
209 break;
213 static int find_token_location(int tokenid)
215 int i;
216 for (i = 0; i < da_num_tokens; i++) {
217 if (da_tokens[i].tokenID == tokenid)
218 return da_tokens[i].location;
221 return -1;
224 static struct calling_interface_buffer *
225 dell_send_request(struct calling_interface_buffer *buffer, int class,
226 int select)
228 struct smi_cmd command;
230 command.magic = SMI_CMD_MAGIC;
231 command.command_address = da_command_address;
232 command.command_code = da_command_code;
233 command.ebx = virt_to_phys(buffer);
234 command.ecx = 0x42534931;
236 buffer->class = class;
237 buffer->select = select;
239 dcdbas_smi_request(&command);
241 return buffer;
244 /* Derived from information in DellWirelessCtl.cpp:
245 Class 17, select 11 is radio control. It returns an array of 32-bit values.
247 Input byte 0 = 0: Wireless information
249 result[0]: return code
250 result[1]:
251 Bit 0: Hardware switch supported
252 Bit 1: Wifi locator supported
253 Bit 2: Wifi is supported
254 Bit 3: Bluetooth is supported
255 Bit 4: WWAN is supported
256 Bit 5: Wireless keyboard supported
257 Bits 6-7: Reserved
258 Bit 8: Wifi is installed
259 Bit 9: Bluetooth is installed
260 Bit 10: WWAN is installed
261 Bits 11-15: Reserved
262 Bit 16: Hardware switch is on
263 Bit 17: Wifi is blocked
264 Bit 18: Bluetooth is blocked
265 Bit 19: WWAN is blocked
266 Bits 20-31: Reserved
267 result[2]: NVRAM size in bytes
268 result[3]: NVRAM format version number
270 Input byte 0 = 2: Wireless switch configuration
271 result[0]: return code
272 result[1]:
273 Bit 0: Wifi controlled by switch
274 Bit 1: Bluetooth controlled by switch
275 Bit 2: WWAN controlled by switch
276 Bits 3-6: Reserved
277 Bit 7: Wireless switch config locked
278 Bit 8: Wifi locator enabled
279 Bits 9-14: Reserved
280 Bit 15: Wifi locator setting locked
281 Bits 16-31: Reserved
284 static int dell_rfkill_set(void *data, bool blocked)
286 int disable = blocked ? 1 : 0;
287 unsigned long radio = (unsigned long)data;
288 int hwswitch_bit = (unsigned long)data - 1;
289 int ret = 0;
291 get_buffer();
292 dell_send_request(buffer, 17, 11);
294 /* If the hardware switch controls this radio, and the hardware
295 switch is disabled, don't allow changing the software state.
296 If the hardware switch is reported as not supported, always
297 fire the SMI to toggle the killswitch. */
298 if ((hwswitch_state & BIT(hwswitch_bit)) &&
299 !(buffer->output[1] & BIT(16)) &&
300 (buffer->output[1] & BIT(0))) {
301 ret = -EINVAL;
302 goto out;
305 buffer->input[0] = (1 | (radio<<8) | (disable << 16));
306 dell_send_request(buffer, 17, 11);
308 out:
309 release_buffer();
310 return ret;
313 static void dell_rfkill_query(struct rfkill *rfkill, void *data)
315 int status;
316 int bit = (unsigned long)data + 16;
317 int hwswitch_bit = (unsigned long)data - 1;
319 get_buffer();
320 dell_send_request(buffer, 17, 11);
321 status = buffer->output[1];
322 release_buffer();
324 rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
326 if (hwswitch_state & (BIT(hwswitch_bit)))
327 rfkill_set_hw_state(rfkill, !(status & BIT(16)));
330 static const struct rfkill_ops dell_rfkill_ops = {
331 .set_block = dell_rfkill_set,
332 .query = dell_rfkill_query,
335 static struct dentry *dell_laptop_dir;
337 static int dell_debugfs_show(struct seq_file *s, void *data)
339 int status;
341 get_buffer();
342 dell_send_request(buffer, 17, 11);
343 status = buffer->output[1];
344 release_buffer();
346 seq_printf(s, "status:\t0x%X\n", status);
347 seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
348 status & BIT(0));
349 seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
350 (status & BIT(1)) >> 1);
351 seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
352 (status & BIT(2)) >> 2);
353 seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
354 (status & BIT(3)) >> 3);
355 seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
356 (status & BIT(4)) >> 4);
357 seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
358 (status & BIT(5)) >> 5);
359 seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
360 (status & BIT(8)) >> 8);
361 seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
362 (status & BIT(9)) >> 9);
363 seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
364 (status & BIT(10)) >> 10);
365 seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
366 (status & BIT(16)) >> 16);
367 seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
368 (status & BIT(17)) >> 17);
369 seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
370 (status & BIT(18)) >> 18);
371 seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
372 (status & BIT(19)) >> 19);
374 seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
375 seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
376 hwswitch_state & BIT(0));
377 seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
378 (hwswitch_state & BIT(1)) >> 1);
379 seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
380 (hwswitch_state & BIT(2)) >> 2);
381 seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
382 (hwswitch_state & BIT(7)) >> 7);
383 seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
384 (hwswitch_state & BIT(8)) >> 8);
385 seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
386 (hwswitch_state & BIT(15)) >> 15);
388 return 0;
391 static int dell_debugfs_open(struct inode *inode, struct file *file)
393 return single_open(file, dell_debugfs_show, inode->i_private);
396 static const struct file_operations dell_debugfs_fops = {
397 .owner = THIS_MODULE,
398 .open = dell_debugfs_open,
399 .read = seq_read,
400 .llseek = seq_lseek,
401 .release = single_release,
404 static void dell_update_rfkill(struct work_struct *ignored)
406 int status;
408 get_buffer();
409 dell_send_request(buffer, 17, 11);
410 status = buffer->output[1];
411 release_buffer();
413 /* if hardware rfkill is not supported, set it explicitly */
414 if (!(status & BIT(0))) {
415 if (wifi_rfkill)
416 dell_rfkill_set((void *)1, !((status & BIT(17)) >> 17));
417 if (bluetooth_rfkill)
418 dell_rfkill_set((void *)2, !((status & BIT(18)) >> 18));
419 if (wwan_rfkill)
420 dell_rfkill_set((void *)3, !((status & BIT(19)) >> 19));
423 if (wifi_rfkill)
424 dell_rfkill_query(wifi_rfkill, (void *)1);
425 if (bluetooth_rfkill)
426 dell_rfkill_query(bluetooth_rfkill, (void *)2);
427 if (wwan_rfkill)
428 dell_rfkill_query(wwan_rfkill, (void *)3);
430 static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
433 static int __init dell_setup_rfkill(void)
435 int status;
436 int ret;
438 if (dmi_check_system(dell_blacklist)) {
439 pr_info("Blacklisted hardware detected - not enabling rfkill\n");
440 return 0;
443 get_buffer();
444 dell_send_request(buffer, 17, 11);
445 status = buffer->output[1];
446 buffer->input[0] = 0x2;
447 dell_send_request(buffer, 17, 11);
448 hwswitch_state = buffer->output[1];
449 release_buffer();
451 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
452 wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
453 RFKILL_TYPE_WLAN,
454 &dell_rfkill_ops, (void *) 1);
455 if (!wifi_rfkill) {
456 ret = -ENOMEM;
457 goto err_wifi;
459 ret = rfkill_register(wifi_rfkill);
460 if (ret)
461 goto err_wifi;
464 if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
465 bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
466 &platform_device->dev,
467 RFKILL_TYPE_BLUETOOTH,
468 &dell_rfkill_ops, (void *) 2);
469 if (!bluetooth_rfkill) {
470 ret = -ENOMEM;
471 goto err_bluetooth;
473 ret = rfkill_register(bluetooth_rfkill);
474 if (ret)
475 goto err_bluetooth;
478 if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
479 wwan_rfkill = rfkill_alloc("dell-wwan",
480 &platform_device->dev,
481 RFKILL_TYPE_WWAN,
482 &dell_rfkill_ops, (void *) 3);
483 if (!wwan_rfkill) {
484 ret = -ENOMEM;
485 goto err_wwan;
487 ret = rfkill_register(wwan_rfkill);
488 if (ret)
489 goto err_wwan;
492 return 0;
493 err_wwan:
494 rfkill_destroy(wwan_rfkill);
495 if (bluetooth_rfkill)
496 rfkill_unregister(bluetooth_rfkill);
497 err_bluetooth:
498 rfkill_destroy(bluetooth_rfkill);
499 if (wifi_rfkill)
500 rfkill_unregister(wifi_rfkill);
501 err_wifi:
502 rfkill_destroy(wifi_rfkill);
504 return ret;
507 static void dell_cleanup_rfkill(void)
509 if (wifi_rfkill) {
510 rfkill_unregister(wifi_rfkill);
511 rfkill_destroy(wifi_rfkill);
513 if (bluetooth_rfkill) {
514 rfkill_unregister(bluetooth_rfkill);
515 rfkill_destroy(bluetooth_rfkill);
517 if (wwan_rfkill) {
518 rfkill_unregister(wwan_rfkill);
519 rfkill_destroy(wwan_rfkill);
523 static int dell_send_intensity(struct backlight_device *bd)
525 int ret = 0;
527 get_buffer();
528 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
529 buffer->input[1] = bd->props.brightness;
531 if (buffer->input[0] == -1) {
532 ret = -ENODEV;
533 goto out;
536 if (power_supply_is_system_supplied() > 0)
537 dell_send_request(buffer, 1, 2);
538 else
539 dell_send_request(buffer, 1, 1);
541 out:
542 release_buffer();
543 return 0;
546 static int dell_get_intensity(struct backlight_device *bd)
548 int ret = 0;
550 get_buffer();
551 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
553 if (buffer->input[0] == -1) {
554 ret = -ENODEV;
555 goto out;
558 if (power_supply_is_system_supplied() > 0)
559 dell_send_request(buffer, 0, 2);
560 else
561 dell_send_request(buffer, 0, 1);
563 out:
564 release_buffer();
565 if (ret)
566 return ret;
567 return buffer->output[1];
570 static const struct backlight_ops dell_ops = {
571 .get_brightness = dell_get_intensity,
572 .update_status = dell_send_intensity,
575 static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
576 struct serio *port)
578 static bool extended;
580 if (str & 0x20)
581 return false;
583 if (unlikely(data == 0xe0)) {
584 extended = true;
585 return false;
586 } else if (unlikely(extended)) {
587 switch (data) {
588 case 0x8:
589 schedule_delayed_work(&dell_rfkill_work,
590 round_jiffies_relative(HZ));
591 break;
593 extended = false;
596 return false;
599 static int __init dell_init(void)
601 int max_intensity = 0;
602 int ret;
604 if (!dmi_check_system(dell_device_table))
605 return -ENODEV;
607 dmi_walk(find_tokens, NULL);
609 if (!da_tokens) {
610 pr_info("Unable to find dmi tokens\n");
611 return -ENODEV;
614 ret = platform_driver_register(&platform_driver);
615 if (ret)
616 goto fail_platform_driver;
617 platform_device = platform_device_alloc("dell-laptop", -1);
618 if (!platform_device) {
619 ret = -ENOMEM;
620 goto fail_platform_device1;
622 ret = platform_device_add(platform_device);
623 if (ret)
624 goto fail_platform_device2;
627 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
628 * is passed to SMI handler.
630 bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32);
632 if (!bufferpage)
633 goto fail_buffer;
634 buffer = page_address(bufferpage);
635 mutex_init(&buffer_mutex);
637 ret = dell_setup_rfkill();
639 if (ret) {
640 pr_warn("Unable to setup rfkill\n");
641 goto fail_rfkill;
644 ret = i8042_install_filter(dell_laptop_i8042_filter);
645 if (ret) {
646 pr_warn("Unable to install key filter\n");
647 goto fail_filter;
650 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
651 if (dell_laptop_dir != NULL)
652 debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
653 &dell_debugfs_fops);
655 #ifdef CONFIG_ACPI
656 /* In the event of an ACPI backlight being available, don't
657 * register the platform controller.
659 if (acpi_video_backlight_support())
660 return 0;
661 #endif
663 get_buffer();
664 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
665 if (buffer->input[0] != -1) {
666 dell_send_request(buffer, 0, 2);
667 max_intensity = buffer->output[3];
669 release_buffer();
671 if (max_intensity) {
672 struct backlight_properties props;
673 memset(&props, 0, sizeof(struct backlight_properties));
674 props.type = BACKLIGHT_PLATFORM;
675 props.max_brightness = max_intensity;
676 dell_backlight_device = backlight_device_register("dell_backlight",
677 &platform_device->dev,
678 NULL,
679 &dell_ops,
680 &props);
682 if (IS_ERR(dell_backlight_device)) {
683 ret = PTR_ERR(dell_backlight_device);
684 dell_backlight_device = NULL;
685 goto fail_backlight;
688 dell_backlight_device->props.brightness =
689 dell_get_intensity(dell_backlight_device);
690 backlight_update_status(dell_backlight_device);
693 return 0;
695 fail_backlight:
696 i8042_remove_filter(dell_laptop_i8042_filter);
697 cancel_delayed_work_sync(&dell_rfkill_work);
698 fail_filter:
699 dell_cleanup_rfkill();
700 fail_rfkill:
701 free_page((unsigned long)bufferpage);
702 fail_buffer:
703 platform_device_del(platform_device);
704 fail_platform_device2:
705 platform_device_put(platform_device);
706 fail_platform_device1:
707 platform_driver_unregister(&platform_driver);
708 fail_platform_driver:
709 kfree(da_tokens);
710 return ret;
713 static void __exit dell_exit(void)
715 debugfs_remove_recursive(dell_laptop_dir);
716 i8042_remove_filter(dell_laptop_i8042_filter);
717 cancel_delayed_work_sync(&dell_rfkill_work);
718 backlight_device_unregister(dell_backlight_device);
719 dell_cleanup_rfkill();
720 if (platform_device) {
721 platform_device_unregister(platform_device);
722 platform_driver_unregister(&platform_driver);
724 kfree(da_tokens);
725 free_page((unsigned long)buffer);
728 module_init(dell_init);
729 module_exit(dell_exit);
731 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
732 MODULE_DESCRIPTION("Dell laptop driver");
733 MODULE_LICENSE("GPL");
734 MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
735 MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*");
736 MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");