aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/dell-laptop.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/dell-laptop.c')
-rw-r--r--drivers/platform/x86/dell-laptop.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index bb77e18b3dd4..c608b1d33f4a 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -21,6 +21,7 @@
21#include <linux/err.h> 21#include <linux/err.h>
22#include <linux/dmi.h> 22#include <linux/dmi.h>
23#include <linux/io.h> 23#include <linux/io.h>
24#include <linux/rfkill.h>
24#include <linux/power_supply.h> 25#include <linux/power_supply.h>
25#include <linux/acpi.h> 26#include <linux/acpi.h>
26#include <linux/mm.h> 27#include <linux/mm.h>
@@ -89,6 +90,13 @@ static struct platform_driver platform_driver = {
89 90
90static struct platform_device *platform_device; 91static struct platform_device *platform_device;
91static struct backlight_device *dell_backlight_device; 92static struct backlight_device *dell_backlight_device;
93static struct rfkill *wifi_rfkill;
94static struct rfkill *bluetooth_rfkill;
95static struct rfkill *wwan_rfkill;
96static bool force_rfkill;
97
98module_param(force_rfkill, bool, 0444);
99MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
92 100
93static const struct dmi_system_id dell_device_table[] __initconst = { 101static const struct dmi_system_id dell_device_table[] __initconst = {
94 { 102 {
@@ -355,6 +363,108 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
355 return buffer; 363 return buffer;
356} 364}
357 365
366/* Derived from information in DellWirelessCtl.cpp:
367 Class 17, select 11 is radio control. It returns an array of 32-bit values.
368
369 Input byte 0 = 0: Wireless information
370
371 result[0]: return code
372 result[1]:
373 Bit 0: Hardware switch supported
374 Bit 1: Wifi locator supported
375 Bit 2: Wifi is supported
376 Bit 3: Bluetooth is supported
377 Bit 4: WWAN is supported
378 Bit 5: Wireless keyboard supported
379 Bits 6-7: Reserved
380 Bit 8: Wifi is installed
381 Bit 9: Bluetooth is installed
382 Bit 10: WWAN is installed
383 Bits 11-15: Reserved
384 Bit 16: Hardware switch is on
385 Bit 17: Wifi is blocked
386 Bit 18: Bluetooth is blocked
387 Bit 19: WWAN is blocked
388 Bits 20-31: Reserved
389 result[2]: NVRAM size in bytes
390 result[3]: NVRAM format version number
391
392 Input byte 0 = 2: Wireless switch configuration
393 result[0]: return code
394 result[1]:
395 Bit 0: Wifi controlled by switch
396 Bit 1: Bluetooth controlled by switch
397 Bit 2: WWAN controlled by switch
398 Bits 3-6: Reserved
399 Bit 7: Wireless switch config locked
400 Bit 8: Wifi locator enabled
401 Bits 9-14: Reserved
402 Bit 15: Wifi locator setting locked
403 Bits 16-31: Reserved
404*/
405
406static int dell_rfkill_set(void *data, bool blocked)
407{
408 int disable = blocked ? 1 : 0;
409 unsigned long radio = (unsigned long)data;
410 int hwswitch_bit = (unsigned long)data - 1;
411
412 get_buffer();
413 dell_send_request(buffer, 17, 11);
414
415 /* If the hardware switch controls this radio, and the hardware
416 switch is disabled, always disable the radio */
417 if ((hwswitch_state & BIT(hwswitch_bit)) &&
418 !(buffer->output[1] & BIT(16)))
419 disable = 1;
420
421 buffer->input[0] = (1 | (radio<<8) | (disable << 16));
422 dell_send_request(buffer, 17, 11);
423
424 release_buffer();
425 return 0;
426}
427
428/* Must be called with the buffer held */
429static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
430 int status)
431{
432 if (status & BIT(0)) {
433 /* Has hw-switch, sync sw_state to BIOS */
434 int block = rfkill_blocked(rfkill);
435 buffer->input[0] = (1 | (radio << 8) | (block << 16));
436 dell_send_request(buffer, 17, 11);
437 } else {
438 /* No hw-switch, sync BIOS state to sw_state */
439 rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
440 }
441}
442
443static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
444 int status)
445{
446 if (hwswitch_state & (BIT(radio - 1)))
447 rfkill_set_hw_state(rfkill, !(status & BIT(16)));
448}
449
450static void dell_rfkill_query(struct rfkill *rfkill, void *data)
451{
452 int status;
453
454 get_buffer();
455 dell_send_request(buffer, 17, 11);
456 status = buffer->output[1];
457
458 dell_rfkill_update_hw_state(rfkill, (unsigned long)data, status);
459
460 release_buffer();
461}
462
463static const struct rfkill_ops dell_rfkill_ops = {
464 .set_block = dell_rfkill_set,
465 .query = dell_rfkill_query,
466};
467
358static struct dentry *dell_laptop_dir; 468static struct dentry *dell_laptop_dir;
359 469
360static int dell_debugfs_show(struct seq_file *s, void *data) 470static int dell_debugfs_show(struct seq_file *s, void *data)
@@ -424,6 +534,136 @@ static const struct file_operations dell_debugfs_fops = {
424 .release = single_release, 534 .release = single_release,
425}; 535};
426 536
537static void dell_update_rfkill(struct work_struct *ignored)
538{
539 int status;
540
541 get_buffer();
542 dell_send_request(buffer, 17, 11);
543 status = buffer->output[1];
544
545 if (wifi_rfkill) {
546 dell_rfkill_update_hw_state(wifi_rfkill, 1, status);
547 dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
548 }
549 if (bluetooth_rfkill) {
550 dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status);
551 dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
552 }
553 if (wwan_rfkill) {
554 dell_rfkill_update_hw_state(wwan_rfkill, 3, status);
555 dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
556 }
557
558 release_buffer();
559}
560static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
561
562
563static int __init dell_setup_rfkill(void)
564{
565 int status;
566 int ret;
567 const char *product;
568
569 /*
570 * rfkill causes trouble on various non Latitudes, according to Dell
571 * actually testing the rfkill functionality is only done on Latitudes.
572 */
573 product = dmi_get_system_info(DMI_PRODUCT_NAME);
574 if (!force_rfkill && (!product || strncmp(product, "Latitude", 8)))
575 return 0;
576
577 get_buffer();
578 dell_send_request(buffer, 17, 11);
579 status = buffer->output[1];
580 buffer->input[0] = 0x2;
581 dell_send_request(buffer, 17, 11);
582 hwswitch_state = buffer->output[1];
583 release_buffer();
584
585 if (!(status & BIT(0))) {
586 if (force_rfkill) {
587 /* No hwsitch, clear all hw-controlled bits */
588 hwswitch_state &= ~7;
589 } else {
590 /* rfkill is only tested on laptops with a hwswitch */
591 return 0;
592 }
593 }
594
595 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
596 wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
597 RFKILL_TYPE_WLAN,
598 &dell_rfkill_ops, (void *) 1);
599 if (!wifi_rfkill) {
600 ret = -ENOMEM;
601 goto err_wifi;
602 }
603 ret = rfkill_register(wifi_rfkill);
604 if (ret)
605 goto err_wifi;
606 }
607
608 if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
609 bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
610 &platform_device->dev,
611 RFKILL_TYPE_BLUETOOTH,
612 &dell_rfkill_ops, (void *) 2);
613 if (!bluetooth_rfkill) {
614 ret = -ENOMEM;
615 goto err_bluetooth;
616 }
617 ret = rfkill_register(bluetooth_rfkill);
618 if (ret)
619 goto err_bluetooth;
620 }
621
622 if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
623 wwan_rfkill = rfkill_alloc("dell-wwan",
624 &platform_device->dev,
625 RFKILL_TYPE_WWAN,
626 &dell_rfkill_ops, (void *) 3);
627 if (!wwan_rfkill) {
628 ret = -ENOMEM;
629 goto err_wwan;
630 }
631 ret = rfkill_register(wwan_rfkill);
632 if (ret)
633 goto err_wwan;
634 }
635
636 return 0;
637err_wwan:
638 rfkill_destroy(wwan_rfkill);
639 if (bluetooth_rfkill)
640 rfkill_unregister(bluetooth_rfkill);
641err_bluetooth:
642 rfkill_destroy(bluetooth_rfkill);
643 if (wifi_rfkill)
644 rfkill_unregister(wifi_rfkill);
645err_wifi:
646 rfkill_destroy(wifi_rfkill);
647
648 return ret;
649}
650
651static void dell_cleanup_rfkill(void)
652{
653 if (wifi_rfkill) {
654 rfkill_unregister(wifi_rfkill);
655 rfkill_destroy(wifi_rfkill);
656 }
657 if (bluetooth_rfkill) {
658 rfkill_unregister(bluetooth_rfkill);
659 rfkill_destroy(bluetooth_rfkill);
660 }
661 if (wwan_rfkill) {
662 rfkill_unregister(wwan_rfkill);
663 rfkill_destroy(wwan_rfkill);
664 }
665}
666
427static int dell_send_intensity(struct backlight_device *bd) 667static int dell_send_intensity(struct backlight_device *bd)
428{ 668{
429 int ret = 0; 669 int ret = 0;
@@ -515,6 +755,30 @@ static void touchpad_led_exit(void)
515 led_classdev_unregister(&touchpad_led); 755 led_classdev_unregister(&touchpad_led);
516} 756}
517 757
758static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
759 struct serio *port)
760{
761 static bool extended;
762
763 if (str & 0x20)
764 return false;
765
766 if (unlikely(data == 0xe0)) {
767 extended = true;
768 return false;
769 } else if (unlikely(extended)) {
770 switch (data) {
771 case 0x8:
772 schedule_delayed_work(&dell_rfkill_work,
773 round_jiffies_relative(HZ / 4));
774 break;
775 }
776 extended = false;
777 }
778
779 return false;
780}
781
518static int __init dell_init(void) 782static int __init dell_init(void)
519{ 783{
520 int max_intensity = 0; 784 int max_intensity = 0;
@@ -557,10 +821,26 @@ static int __init dell_init(void)
557 } 821 }
558 buffer = page_address(bufferpage); 822 buffer = page_address(bufferpage);
559 823
824 ret = dell_setup_rfkill();
825
826 if (ret) {
827 pr_warn("Unable to setup rfkill\n");
828 goto fail_rfkill;
829 }
830
831 ret = i8042_install_filter(dell_laptop_i8042_filter);
832 if (ret) {
833 pr_warn("Unable to install key filter\n");
834 goto fail_filter;
835 }
836
560 if (quirks && quirks->touchpad_led) 837 if (quirks && quirks->touchpad_led)
561 touchpad_led_init(&platform_device->dev); 838 touchpad_led_init(&platform_device->dev);
562 839
563 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); 840 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
841 if (dell_laptop_dir != NULL)
842 debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
843 &dell_debugfs_fops);
564 844
565#ifdef CONFIG_ACPI 845#ifdef CONFIG_ACPI
566 /* In the event of an ACPI backlight being available, don't 846 /* In the event of an ACPI backlight being available, don't
@@ -603,6 +883,11 @@ static int __init dell_init(void)
603 return 0; 883 return 0;
604 884
605fail_backlight: 885fail_backlight:
886 i8042_remove_filter(dell_laptop_i8042_filter);
887 cancel_delayed_work_sync(&dell_rfkill_work);
888fail_filter:
889 dell_cleanup_rfkill();
890fail_rfkill:
606 free_page((unsigned long)bufferpage); 891 free_page((unsigned long)bufferpage);
607fail_buffer: 892fail_buffer:
608 platform_device_del(platform_device); 893 platform_device_del(platform_device);
@@ -620,7 +905,10 @@ static void __exit dell_exit(void)
620 debugfs_remove_recursive(dell_laptop_dir); 905 debugfs_remove_recursive(dell_laptop_dir);
621 if (quirks && quirks->touchpad_led) 906 if (quirks && quirks->touchpad_led)
622 touchpad_led_exit(); 907 touchpad_led_exit();
908 i8042_remove_filter(dell_laptop_i8042_filter);
909 cancel_delayed_work_sync(&dell_rfkill_work);
623 backlight_device_unregister(dell_backlight_device); 910 backlight_device_unregister(dell_backlight_device);
911 dell_cleanup_rfkill();
624 if (platform_device) { 912 if (platform_device) {
625 platform_device_unregister(platform_device); 913 platform_device_unregister(platform_device);
626 platform_driver_unregister(&platform_driver); 914 platform_driver_unregister(&platform_driver);