diff options
Diffstat (limited to 'drivers/platform')
27 files changed, 2298 insertions, 3334 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 15dbd8cc445f..2a262f5c5c0c 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -26,6 +26,10 @@ config ACER_WMI | |||
26 | depends on RFKILL || RFKILL = n | 26 | depends on RFKILL || RFKILL = n |
27 | depends on ACPI_WMI | 27 | depends on ACPI_WMI |
28 | select INPUT_SPARSEKMAP | 28 | select INPUT_SPARSEKMAP |
29 | # Acer WMI depends on ACPI_VIDEO when ACPI is enabled | ||
30 | # but for select to work, need to select ACPI_VIDEO's dependencies, ick | ||
31 | select VIDEO_OUTPUT_CONTROL if ACPI | ||
32 | select ACPI_VIDEO if ACPI | ||
29 | ---help--- | 33 | ---help--- |
30 | This is a driver for newer Acer (and Wistron) laptops. It adds | 34 | This is a driver for newer Acer (and Wistron) laptops. It adds |
31 | wireless radio and bluetooth control, and on some laptops, | 35 | wireless radio and bluetooth control, and on some laptops, |
@@ -54,7 +58,6 @@ config ACERHDF | |||
54 | config ASUS_LAPTOP | 58 | config ASUS_LAPTOP |
55 | tristate "Asus Laptop Extras" | 59 | tristate "Asus Laptop Extras" |
56 | depends on ACPI | 60 | depends on ACPI |
57 | depends on !ACPI_ASUS | ||
58 | select LEDS_CLASS | 61 | select LEDS_CLASS |
59 | select NEW_LEDS | 62 | select NEW_LEDS |
60 | select BACKLIGHT_CLASS_DEVICE | 63 | select BACKLIGHT_CLASS_DEVICE |
@@ -460,10 +463,9 @@ config INTEL_MENLOW | |||
460 | If unsure, say N. | 463 | If unsure, say N. |
461 | 464 | ||
462 | config EEEPC_LAPTOP | 465 | config EEEPC_LAPTOP |
463 | tristate "Eee PC Hotkey Driver (EXPERIMENTAL)" | 466 | tristate "Eee PC Hotkey Driver" |
464 | depends on ACPI | 467 | depends on ACPI |
465 | depends on INPUT | 468 | depends on INPUT |
466 | depends on EXPERIMENTAL | ||
467 | depends on RFKILL || RFKILL = n | 469 | depends on RFKILL || RFKILL = n |
468 | depends on HOTPLUG_PCI | 470 | depends on HOTPLUG_PCI |
469 | select BACKLIGHT_CLASS_DEVICE | 471 | select BACKLIGHT_CLASS_DEVICE |
@@ -482,11 +484,10 @@ config EEEPC_LAPTOP | |||
482 | doesn't work on your Eee PC, try eeepc-wmi instead. | 484 | doesn't work on your Eee PC, try eeepc-wmi instead. |
483 | 485 | ||
484 | config ASUS_WMI | 486 | config ASUS_WMI |
485 | tristate "ASUS WMI Driver (EXPERIMENTAL)" | 487 | tristate "ASUS WMI Driver" |
486 | depends on ACPI_WMI | 488 | depends on ACPI_WMI |
487 | depends on INPUT | 489 | depends on INPUT |
488 | depends on HWMON | 490 | depends on HWMON |
489 | depends on EXPERIMENTAL | ||
490 | depends on BACKLIGHT_CLASS_DEVICE | 491 | depends on BACKLIGHT_CLASS_DEVICE |
491 | depends on RFKILL || RFKILL = n | 492 | depends on RFKILL || RFKILL = n |
492 | depends on HOTPLUG_PCI | 493 | depends on HOTPLUG_PCI |
@@ -501,7 +502,7 @@ config ASUS_WMI | |||
501 | be called asus-wmi. | 502 | be called asus-wmi. |
502 | 503 | ||
503 | config ASUS_NB_WMI | 504 | config ASUS_NB_WMI |
504 | tristate "Asus Notebook WMI Driver (EXPERIMENTAL)" | 505 | tristate "Asus Notebook WMI Driver" |
505 | depends on ASUS_WMI | 506 | depends on ASUS_WMI |
506 | ---help--- | 507 | ---help--- |
507 | This is a driver for newer Asus notebooks. It adds extra features | 508 | This is a driver for newer Asus notebooks. It adds extra features |
@@ -514,7 +515,7 @@ config ASUS_NB_WMI | |||
514 | here. | 515 | here. |
515 | 516 | ||
516 | config EEEPC_WMI | 517 | config EEEPC_WMI |
517 | tristate "Eee PC WMI Driver (EXPERIMENTAL)" | 518 | tristate "Eee PC WMI Driver" |
518 | depends on ASUS_WMI | 519 | depends on ASUS_WMI |
519 | ---help--- | 520 | ---help--- |
520 | This is a driver for newer Eee PC laptops. It adds extra features | 521 | This is a driver for newer Eee PC laptops. It adds extra features |
@@ -559,38 +560,6 @@ config MSI_WMI | |||
559 | To compile this driver as a module, choose M here: the module will | 560 | To compile this driver as a module, choose M here: the module will |
560 | be called msi-wmi. | 561 | be called msi-wmi. |
561 | 562 | ||
562 | config ACPI_ASUS | ||
563 | tristate "ASUS/Medion Laptop Extras (DEPRECATED)" | ||
564 | depends on ACPI | ||
565 | select BACKLIGHT_CLASS_DEVICE | ||
566 | ---help--- | ||
567 | This driver provides support for extra features of ACPI-compatible | ||
568 | ASUS laptops. As some of Medion laptops are made by ASUS, it may also | ||
569 | support some Medion laptops (such as 9675 for example). It makes all | ||
570 | the extra buttons generate standard ACPI events that go through | ||
571 | /proc/acpi/events, and (on some models) adds support for changing the | ||
572 | display brightness and output, switching the LCD backlight on and off, | ||
573 | and most importantly, allows you to blink those fancy LEDs intended | ||
574 | for reporting mail and wireless status. | ||
575 | |||
576 | Note: display switching code is currently considered EXPERIMENTAL, | ||
577 | toying with these values may even lock your machine. | ||
578 | |||
579 | All settings are changed via /proc/acpi/asus directory entries. Owner | ||
580 | and group for these entries can be set with asus_uid and asus_gid | ||
581 | parameters. | ||
582 | |||
583 | More information and a userspace daemon for handling the extra buttons | ||
584 | at <http://acpi4asus.sf.net>. | ||
585 | |||
586 | If you have an ACPI-compatible ASUS laptop, say Y or M here. This | ||
587 | driver is still under development, so if your laptop is unsupported or | ||
588 | something works not quite as expected, please use the mailing list | ||
589 | available on the above page (acpi4asus-user@lists.sourceforge.net). | ||
590 | |||
591 | NOTE: This driver is deprecated and will probably be removed soon, | ||
592 | use asus-laptop instead. | ||
593 | |||
594 | config TOPSTAR_LAPTOP | 563 | config TOPSTAR_LAPTOP |
595 | tristate "Topstar Laptop Extras" | 564 | tristate "Topstar Laptop Extras" |
596 | depends on ACPI | 565 | depends on ACPI |
@@ -604,6 +573,7 @@ config TOPSTAR_LAPTOP | |||
604 | config ACPI_TOSHIBA | 573 | config ACPI_TOSHIBA |
605 | tristate "Toshiba Laptop Extras" | 574 | tristate "Toshiba Laptop Extras" |
606 | depends on ACPI | 575 | depends on ACPI |
576 | depends on ACPI_WMI | ||
607 | select LEDS_CLASS | 577 | select LEDS_CLASS |
608 | select NEW_LEDS | 578 | select NEW_LEDS |
609 | depends on BACKLIGHT_CLASS_DEVICE | 579 | depends on BACKLIGHT_CLASS_DEVICE |
@@ -696,33 +666,11 @@ config INTEL_MID_POWER_BUTTON | |||
696 | 666 | ||
697 | config INTEL_MFLD_THERMAL | 667 | config INTEL_MFLD_THERMAL |
698 | tristate "Thermal driver for Intel Medfield platform" | 668 | tristate "Thermal driver for Intel Medfield platform" |
699 | depends on INTEL_SCU_IPC && THERMAL | 669 | depends on MFD_INTEL_MSIC && THERMAL |
700 | help | 670 | help |
701 | Say Y here to enable thermal driver support for the Intel Medfield | 671 | Say Y here to enable thermal driver support for the Intel Medfield |
702 | platform. | 672 | platform. |
703 | 673 | ||
704 | config RAR_REGISTER | ||
705 | bool "Restricted Access Region Register Driver" | ||
706 | depends on PCI && X86_MRST | ||
707 | default n | ||
708 | ---help--- | ||
709 | This driver allows other kernel drivers access to the | ||
710 | contents of the restricted access region control registers. | ||
711 | |||
712 | The restricted access region control registers | ||
713 | (rar_registers) are used to pass address and | ||
714 | locking information on restricted access regions | ||
715 | to other drivers that use restricted access regions. | ||
716 | |||
717 | The restricted access regions are regions of memory | ||
718 | on the Intel MID Platform that are not accessible to | ||
719 | the x86 processor, but are accessible to dedicated | ||
720 | processors on board peripheral devices. | ||
721 | |||
722 | The purpose of the restricted access regions is to | ||
723 | protect sensitive data from compromise by unauthorized | ||
724 | programs running on the x86 processor. | ||
725 | |||
726 | config INTEL_IPS | 674 | config INTEL_IPS |
727 | tristate "Intel Intelligent Power Sharing" | 675 | tristate "Intel Intelligent Power Sharing" |
728 | depends on ACPI | 676 | depends on ACPI |
@@ -768,13 +716,18 @@ config XO15_EBOOK | |||
768 | 716 | ||
769 | config SAMSUNG_LAPTOP | 717 | config SAMSUNG_LAPTOP |
770 | tristate "Samsung Laptop driver" | 718 | tristate "Samsung Laptop driver" |
771 | depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86 | 719 | depends on X86 |
720 | depends on RFKILL || RFKILL = n | ||
721 | depends on BACKLIGHT_CLASS_DEVICE | ||
722 | select LEDS_CLASS | ||
723 | select NEW_LEDS | ||
772 | ---help--- | 724 | ---help--- |
773 | This module implements a driver for a wide range of different | 725 | This module implements a driver for a wide range of different |
774 | Samsung laptops. It offers control over the different | 726 | Samsung laptops. It offers control over the different |
775 | function keys, wireless LED, LCD backlight level, and | 727 | function keys, wireless LED, LCD backlight level. |
776 | sometimes provides a "performance_control" sysfs file to allow | 728 | |
777 | the performance level of the laptop to be changed. | 729 | It may also provide some sysfs files described in |
730 | <file:Documentation/ABI/testing/sysfs-platform-samsung-laptop> | ||
778 | 731 | ||
779 | To compile this driver as a module, choose M here: the module | 732 | To compile this driver as a module, choose M here: the module |
780 | will be called samsung-laptop. | 733 | will be called samsung-laptop. |
@@ -803,4 +756,14 @@ config SAMSUNG_Q10 | |||
803 | This driver provides support for backlight control on Samsung Q10 | 756 | This driver provides support for backlight control on Samsung Q10 |
804 | and related laptops, including Dell Latitude X200. | 757 | and related laptops, including Dell Latitude X200. |
805 | 758 | ||
759 | config APPLE_GMUX | ||
760 | tristate "Apple Gmux Driver" | ||
761 | depends on PNP | ||
762 | select BACKLIGHT_CLASS_DEVICE | ||
763 | ---help--- | ||
764 | This driver provides support for the gmux device found on many | ||
765 | Apple laptops, which controls the display mux for the hybrid | ||
766 | graphics as well as the backlight. Currently only backlight | ||
767 | control is supported by the driver. | ||
768 | |||
806 | endif # X86_PLATFORM_DEVICES | 769 | endif # X86_PLATFORM_DEVICES |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d328f21e9fdd..bf7e4f935b17 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -29,14 +29,16 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o | |||
29 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o | 29 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o |
30 | obj-$(CONFIG_ACPI_WMI) += wmi.o | 30 | obj-$(CONFIG_ACPI_WMI) += wmi.o |
31 | obj-$(CONFIG_MSI_WMI) += msi-wmi.o | 31 | obj-$(CONFIG_MSI_WMI) += msi-wmi.o |
32 | obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o | ||
33 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | 32 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o |
33 | |||
34 | # toshiba_acpi must link after wmi to ensure that wmi devices are found | ||
35 | # before toshiba_acpi initializes | ||
34 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 36 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
37 | |||
35 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o | 38 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o |
36 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o | 39 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o |
37 | obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o | 40 | obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o |
38 | obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o | 41 | obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o |
39 | obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o | ||
40 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o | 42 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o |
41 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o | 43 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o |
42 | obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o | 44 | obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o |
@@ -47,3 +49,4 @@ obj-$(CONFIG_MXM_WMI) += mxm-wmi.o | |||
47 | obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o | 49 | obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o |
48 | obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o | 50 | obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o |
49 | obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o | 51 | obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o |
52 | obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o | ||
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 1e5290b5396d..c1a3fd8e1243 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <linux/input/sparse-keymap.h> | 43 | #include <linux/input/sparse-keymap.h> |
44 | 44 | ||
45 | #include <acpi/acpi_drivers.h> | 45 | #include <acpi/acpi_drivers.h> |
46 | #include <acpi/video.h> | ||
46 | 47 | ||
47 | MODULE_AUTHOR("Carlos Corbacho"); | 48 | MODULE_AUTHOR("Carlos Corbacho"); |
48 | MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); | 49 | MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); |
@@ -105,13 +106,19 @@ static const struct key_entry acer_wmi_keymap[] = { | |||
105 | {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ | 106 | {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ |
106 | {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ | 107 | {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ |
107 | {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ | 108 | {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ |
109 | {KE_KEY, 0x29, {KEY_PROG3} }, /* P_Key for TM8372 */ | ||
108 | {KE_IGNORE, 0x41, {KEY_MUTE} }, | 110 | {KE_IGNORE, 0x41, {KEY_MUTE} }, |
109 | {KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} }, | 111 | {KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} }, |
112 | {KE_IGNORE, 0x4d, {KEY_PREVIOUSSONG} }, | ||
110 | {KE_IGNORE, 0x43, {KEY_NEXTSONG} }, | 113 | {KE_IGNORE, 0x43, {KEY_NEXTSONG} }, |
114 | {KE_IGNORE, 0x4e, {KEY_NEXTSONG} }, | ||
111 | {KE_IGNORE, 0x44, {KEY_PLAYPAUSE} }, | 115 | {KE_IGNORE, 0x44, {KEY_PLAYPAUSE} }, |
116 | {KE_IGNORE, 0x4f, {KEY_PLAYPAUSE} }, | ||
112 | {KE_IGNORE, 0x45, {KEY_STOP} }, | 117 | {KE_IGNORE, 0x45, {KEY_STOP} }, |
118 | {KE_IGNORE, 0x50, {KEY_STOP} }, | ||
113 | {KE_IGNORE, 0x48, {KEY_VOLUMEUP} }, | 119 | {KE_IGNORE, 0x48, {KEY_VOLUMEUP} }, |
114 | {KE_IGNORE, 0x49, {KEY_VOLUMEDOWN} }, | 120 | {KE_IGNORE, 0x49, {KEY_VOLUMEDOWN} }, |
121 | {KE_IGNORE, 0x4a, {KEY_VOLUMEDOWN} }, | ||
115 | {KE_IGNORE, 0x61, {KEY_SWITCHVIDEOMODE} }, | 122 | {KE_IGNORE, 0x61, {KEY_SWITCHVIDEOMODE} }, |
116 | {KE_IGNORE, 0x62, {KEY_BRIGHTNESSUP} }, | 123 | {KE_IGNORE, 0x62, {KEY_BRIGHTNESSUP} }, |
117 | {KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} }, | 124 | {KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} }, |
@@ -153,7 +160,14 @@ struct lm_return_value { | |||
153 | u16 reserved; | 160 | u16 reserved; |
154 | } __attribute__((packed)); | 161 | } __attribute__((packed)); |
155 | 162 | ||
156 | struct wmid3_gds_input_param { /* Get Device Status input parameter */ | 163 | struct wmid3_gds_set_input_param { /* Set Device Status input parameter */ |
164 | u8 function_num; /* Function Number */ | ||
165 | u8 hotkey_number; /* Hotkey Number */ | ||
166 | u16 devices; /* Set Device */ | ||
167 | u8 volume_value; /* Volume Value */ | ||
168 | } __attribute__((packed)); | ||
169 | |||
170 | struct wmid3_gds_get_input_param { /* Get Device Status input parameter */ | ||
157 | u8 function_num; /* Function Number */ | 171 | u8 function_num; /* Function Number */ |
158 | u8 hotkey_number; /* Hotkey Number */ | 172 | u8 hotkey_number; /* Hotkey Number */ |
159 | u16 devices; /* Get Device */ | 173 | u16 devices; /* Get Device */ |
@@ -171,6 +185,11 @@ struct hotkey_function_type_aa { | |||
171 | u8 length; | 185 | u8 length; |
172 | u16 handle; | 186 | u16 handle; |
173 | u16 commun_func_bitmap; | 187 | u16 commun_func_bitmap; |
188 | u16 application_func_bitmap; | ||
189 | u16 media_func_bitmap; | ||
190 | u16 display_func_bitmap; | ||
191 | u16 others_func_bitmap; | ||
192 | u8 commun_fn_key_number; | ||
174 | } __attribute__((packed)); | 193 | } __attribute__((packed)); |
175 | 194 | ||
176 | /* | 195 | /* |
@@ -207,6 +226,7 @@ static int force_series; | |||
207 | static bool ec_raw_mode; | 226 | static bool ec_raw_mode; |
208 | static bool has_type_aa; | 227 | static bool has_type_aa; |
209 | static u16 commun_func_bitmap; | 228 | static u16 commun_func_bitmap; |
229 | static u8 commun_fn_key_number; | ||
210 | 230 | ||
211 | module_param(mailled, int, 0444); | 231 | module_param(mailled, int, 0444); |
212 | module_param(brightness, int, 0444); | 232 | module_param(brightness, int, 0444); |
@@ -468,6 +488,15 @@ static struct dmi_system_id acer_quirks[] = { | |||
468 | }, | 488 | }, |
469 | { | 489 | { |
470 | .callback = dmi_matched, | 490 | .callback = dmi_matched, |
491 | .ident = "Lenovo Ideapad S205 (Brazos)", | ||
492 | .matches = { | ||
493 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | ||
494 | DMI_MATCH(DMI_PRODUCT_NAME, "Brazos"), | ||
495 | }, | ||
496 | .driver_data = &quirk_lenovo_ideapad_s205, | ||
497 | }, | ||
498 | { | ||
499 | .callback = dmi_matched, | ||
471 | .ident = "Lenovo 3000 N200", | 500 | .ident = "Lenovo 3000 N200", |
472 | .matches = { | 501 | .matches = { |
473 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | 502 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
@@ -478,6 +507,25 @@ static struct dmi_system_id acer_quirks[] = { | |||
478 | {} | 507 | {} |
479 | }; | 508 | }; |
480 | 509 | ||
510 | static int video_set_backlight_video_vendor(const struct dmi_system_id *d) | ||
511 | { | ||
512 | interface->capability &= ~ACER_CAP_BRIGHTNESS; | ||
513 | pr_info("Brightness must be controlled by generic video driver\n"); | ||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | static const struct dmi_system_id video_vendor_dmi_table[] = { | ||
518 | { | ||
519 | .callback = video_set_backlight_video_vendor, | ||
520 | .ident = "Acer TravelMate 4750", | ||
521 | .matches = { | ||
522 | DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), | ||
523 | DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"), | ||
524 | }, | ||
525 | }, | ||
526 | {} | ||
527 | }; | ||
528 | |||
481 | /* Find which quirks are needed for a particular vendor/ model pair */ | 529 | /* Find which quirks are needed for a particular vendor/ model pair */ |
482 | static void find_quirks(void) | 530 | static void find_quirks(void) |
483 | { | 531 | { |
@@ -536,8 +584,7 @@ struct acpi_buffer *result) | |||
536 | return status; | 584 | return status; |
537 | } | 585 | } |
538 | 586 | ||
539 | static acpi_status AMW0_get_u32(u32 *value, u32 cap, | 587 | static acpi_status AMW0_get_u32(u32 *value, u32 cap) |
540 | struct wmi_interface *iface) | ||
541 | { | 588 | { |
542 | int err; | 589 | int err; |
543 | u8 result; | 590 | u8 result; |
@@ -607,7 +654,7 @@ struct wmi_interface *iface) | |||
607 | return AE_OK; | 654 | return AE_OK; |
608 | } | 655 | } |
609 | 656 | ||
610 | static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface) | 657 | static acpi_status AMW0_set_u32(u32 value, u32 cap) |
611 | { | 658 | { |
612 | struct wmab_args args; | 659 | struct wmab_args args; |
613 | 660 | ||
@@ -692,6 +739,7 @@ static const struct acpi_device_id norfkill_ids[] = { | |||
692 | { "VPC2004", 0}, | 739 | { "VPC2004", 0}, |
693 | { "IBM0068", 0}, | 740 | { "IBM0068", 0}, |
694 | { "LEN0068", 0}, | 741 | { "LEN0068", 0}, |
742 | { "SNY5001", 0}, /* sony-laptop in charge */ | ||
695 | { "", 0}, | 743 | { "", 0}, |
696 | }; | 744 | }; |
697 | 745 | ||
@@ -827,8 +875,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out) | |||
827 | return status; | 875 | return status; |
828 | } | 876 | } |
829 | 877 | ||
830 | static acpi_status WMID_get_u32(u32 *value, u32 cap, | 878 | static acpi_status WMID_get_u32(u32 *value, u32 cap) |
831 | struct wmi_interface *iface) | ||
832 | { | 879 | { |
833 | acpi_status status; | 880 | acpi_status status; |
834 | u8 tmp; | 881 | u8 tmp; |
@@ -864,7 +911,7 @@ struct wmi_interface *iface) | |||
864 | return status; | 911 | return status; |
865 | } | 912 | } |
866 | 913 | ||
867 | static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) | 914 | static acpi_status WMID_set_u32(u32 value, u32 cap) |
868 | { | 915 | { |
869 | u32 method_id = 0; | 916 | u32 method_id = 0; |
870 | char param; | 917 | char param; |
@@ -912,13 +959,13 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device) | |||
912 | struct wmid3_gds_return_value return_value; | 959 | struct wmid3_gds_return_value return_value; |
913 | acpi_status status; | 960 | acpi_status status; |
914 | union acpi_object *obj; | 961 | union acpi_object *obj; |
915 | struct wmid3_gds_input_param params = { | 962 | struct wmid3_gds_get_input_param params = { |
916 | .function_num = 0x1, | 963 | .function_num = 0x1, |
917 | .hotkey_number = 0x01, | 964 | .hotkey_number = commun_fn_key_number, |
918 | .devices = device, | 965 | .devices = device, |
919 | }; | 966 | }; |
920 | struct acpi_buffer input = { | 967 | struct acpi_buffer input = { |
921 | sizeof(struct wmid3_gds_input_param), | 968 | sizeof(struct wmid3_gds_get_input_param), |
922 | ¶ms | 969 | ¶ms |
923 | }; | 970 | }; |
924 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 971 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
@@ -981,19 +1028,28 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) | |||
981 | acpi_status status; | 1028 | acpi_status status; |
982 | union acpi_object *obj; | 1029 | union acpi_object *obj; |
983 | u16 devices; | 1030 | u16 devices; |
984 | struct wmid3_gds_input_param params = { | 1031 | struct wmid3_gds_get_input_param get_params = { |
985 | .function_num = 0x1, | 1032 | .function_num = 0x1, |
986 | .hotkey_number = 0x01, | 1033 | .hotkey_number = commun_fn_key_number, |
987 | .devices = commun_func_bitmap, | 1034 | .devices = commun_func_bitmap, |
988 | }; | 1035 | }; |
989 | struct acpi_buffer input = { | 1036 | struct acpi_buffer get_input = { |
990 | sizeof(struct wmid3_gds_input_param), | 1037 | sizeof(struct wmid3_gds_get_input_param), |
991 | ¶ms | 1038 | &get_params |
1039 | }; | ||
1040 | struct wmid3_gds_set_input_param set_params = { | ||
1041 | .function_num = 0x2, | ||
1042 | .hotkey_number = commun_fn_key_number, | ||
1043 | .devices = commun_func_bitmap, | ||
1044 | }; | ||
1045 | struct acpi_buffer set_input = { | ||
1046 | sizeof(struct wmid3_gds_set_input_param), | ||
1047 | &set_params | ||
992 | }; | 1048 | }; |
993 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 1049 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
994 | struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL }; | 1050 | struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL }; |
995 | 1051 | ||
996 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | 1052 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &get_input, &output); |
997 | if (ACPI_FAILURE(status)) | 1053 | if (ACPI_FAILURE(status)) |
998 | return status; | 1054 | return status; |
999 | 1055 | ||
@@ -1006,7 +1062,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) | |||
1006 | return AE_ERROR; | 1062 | return AE_ERROR; |
1007 | } | 1063 | } |
1008 | if (obj->buffer.length != 8) { | 1064 | if (obj->buffer.length != 8) { |
1009 | pr_warning("Unknown buffer length %d\n", obj->buffer.length); | 1065 | pr_warn("Unknown buffer length %d\n", obj->buffer.length); |
1010 | kfree(obj); | 1066 | kfree(obj); |
1011 | return AE_ERROR; | 1067 | return AE_ERROR; |
1012 | } | 1068 | } |
@@ -1015,18 +1071,16 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) | |||
1015 | kfree(obj); | 1071 | kfree(obj); |
1016 | 1072 | ||
1017 | if (return_value.error_code || return_value.ec_return_value) { | 1073 | if (return_value.error_code || return_value.ec_return_value) { |
1018 | pr_warning("Get Current Device Status failed: " | 1074 | pr_warn("Get Current Device Status failed: 0x%x - 0x%x\n", |
1019 | "0x%x - 0x%x\n", return_value.error_code, | 1075 | return_value.error_code, |
1020 | return_value.ec_return_value); | 1076 | return_value.ec_return_value); |
1021 | return status; | 1077 | return status; |
1022 | } | 1078 | } |
1023 | 1079 | ||
1024 | devices = return_value.devices; | 1080 | devices = return_value.devices; |
1025 | params.function_num = 0x2; | 1081 | set_params.devices = (value) ? (devices | device) : (devices & ~device); |
1026 | params.hotkey_number = 0x01; | ||
1027 | params.devices = (value) ? (devices | device) : (devices & ~device); | ||
1028 | 1082 | ||
1029 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2); | 1083 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &set_input, &output2); |
1030 | if (ACPI_FAILURE(status)) | 1084 | if (ACPI_FAILURE(status)) |
1031 | return status; | 1085 | return status; |
1032 | 1086 | ||
@@ -1039,7 +1093,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) | |||
1039 | return AE_ERROR; | 1093 | return AE_ERROR; |
1040 | } | 1094 | } |
1041 | if (obj->buffer.length != 4) { | 1095 | if (obj->buffer.length != 4) { |
1042 | pr_warning("Unknown buffer length %d\n", obj->buffer.length); | 1096 | pr_warn("Unknown buffer length %d\n", obj->buffer.length); |
1043 | kfree(obj); | 1097 | kfree(obj); |
1044 | return AE_ERROR; | 1098 | return AE_ERROR; |
1045 | } | 1099 | } |
@@ -1048,8 +1102,8 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) | |||
1048 | kfree(obj); | 1102 | kfree(obj); |
1049 | 1103 | ||
1050 | if (return_value.error_code || return_value.ec_return_value) | 1104 | if (return_value.error_code || return_value.ec_return_value) |
1051 | pr_warning("Set Device Status failed: " | 1105 | pr_warn("Set Device Status failed: 0x%x - 0x%x\n", |
1052 | "0x%x - 0x%x\n", return_value.error_code, | 1106 | return_value.error_code, |
1053 | return_value.ec_return_value); | 1107 | return_value.ec_return_value); |
1054 | 1108 | ||
1055 | return status; | 1109 | return status; |
@@ -1096,6 +1150,8 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) | |||
1096 | interface->capability |= ACER_CAP_THREEG; | 1150 | interface->capability |= ACER_CAP_THREEG; |
1097 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) | 1151 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) |
1098 | interface->capability |= ACER_CAP_BLUETOOTH; | 1152 | interface->capability |= ACER_CAP_BLUETOOTH; |
1153 | |||
1154 | commun_fn_key_number = type_aa->commun_fn_key_number; | ||
1099 | } | 1155 | } |
1100 | 1156 | ||
1101 | static acpi_status WMID_set_capabilities(void) | 1157 | static acpi_status WMID_set_capabilities(void) |
@@ -1154,15 +1210,15 @@ static acpi_status get_u32(u32 *value, u32 cap) | |||
1154 | 1210 | ||
1155 | switch (interface->type) { | 1211 | switch (interface->type) { |
1156 | case ACER_AMW0: | 1212 | case ACER_AMW0: |
1157 | status = AMW0_get_u32(value, cap, interface); | 1213 | status = AMW0_get_u32(value, cap); |
1158 | break; | 1214 | break; |
1159 | case ACER_AMW0_V2: | 1215 | case ACER_AMW0_V2: |
1160 | if (cap == ACER_CAP_MAILLED) { | 1216 | if (cap == ACER_CAP_MAILLED) { |
1161 | status = AMW0_get_u32(value, cap, interface); | 1217 | status = AMW0_get_u32(value, cap); |
1162 | break; | 1218 | break; |
1163 | } | 1219 | } |
1164 | case ACER_WMID: | 1220 | case ACER_WMID: |
1165 | status = WMID_get_u32(value, cap, interface); | 1221 | status = WMID_get_u32(value, cap); |
1166 | break; | 1222 | break; |
1167 | case ACER_WMID_v2: | 1223 | case ACER_WMID_v2: |
1168 | if (cap & (ACER_CAP_WIRELESS | | 1224 | if (cap & (ACER_CAP_WIRELESS | |
@@ -1170,7 +1226,7 @@ static acpi_status get_u32(u32 *value, u32 cap) | |||
1170 | ACER_CAP_THREEG)) | 1226 | ACER_CAP_THREEG)) |
1171 | status = wmid_v2_get_u32(value, cap); | 1227 | status = wmid_v2_get_u32(value, cap); |
1172 | else if (wmi_has_guid(WMID_GUID2)) | 1228 | else if (wmi_has_guid(WMID_GUID2)) |
1173 | status = WMID_get_u32(value, cap, interface); | 1229 | status = WMID_get_u32(value, cap); |
1174 | break; | 1230 | break; |
1175 | } | 1231 | } |
1176 | 1232 | ||
@@ -1184,10 +1240,10 @@ static acpi_status set_u32(u32 value, u32 cap) | |||
1184 | if (interface->capability & cap) { | 1240 | if (interface->capability & cap) { |
1185 | switch (interface->type) { | 1241 | switch (interface->type) { |
1186 | case ACER_AMW0: | 1242 | case ACER_AMW0: |
1187 | return AMW0_set_u32(value, cap, interface); | 1243 | return AMW0_set_u32(value, cap); |
1188 | case ACER_AMW0_V2: | 1244 | case ACER_AMW0_V2: |
1189 | if (cap == ACER_CAP_MAILLED) | 1245 | if (cap == ACER_CAP_MAILLED) |
1190 | return AMW0_set_u32(value, cap, interface); | 1246 | return AMW0_set_u32(value, cap); |
1191 | 1247 | ||
1192 | /* | 1248 | /* |
1193 | * On some models, some WMID methods don't toggle | 1249 | * On some models, some WMID methods don't toggle |
@@ -1197,21 +1253,21 @@ static acpi_status set_u32(u32 value, u32 cap) | |||
1197 | */ | 1253 | */ |
1198 | if (cap == ACER_CAP_WIRELESS || | 1254 | if (cap == ACER_CAP_WIRELESS || |
1199 | cap == ACER_CAP_BLUETOOTH) { | 1255 | cap == ACER_CAP_BLUETOOTH) { |
1200 | status = WMID_set_u32(value, cap, interface); | 1256 | status = WMID_set_u32(value, cap); |
1201 | if (ACPI_FAILURE(status)) | 1257 | if (ACPI_FAILURE(status)) |
1202 | return status; | 1258 | return status; |
1203 | 1259 | ||
1204 | return AMW0_set_u32(value, cap, interface); | 1260 | return AMW0_set_u32(value, cap); |
1205 | } | 1261 | } |
1206 | case ACER_WMID: | 1262 | case ACER_WMID: |
1207 | return WMID_set_u32(value, cap, interface); | 1263 | return WMID_set_u32(value, cap); |
1208 | case ACER_WMID_v2: | 1264 | case ACER_WMID_v2: |
1209 | if (cap & (ACER_CAP_WIRELESS | | 1265 | if (cap & (ACER_CAP_WIRELESS | |
1210 | ACER_CAP_BLUETOOTH | | 1266 | ACER_CAP_BLUETOOTH | |
1211 | ACER_CAP_THREEG)) | 1267 | ACER_CAP_THREEG)) |
1212 | return wmid_v2_set_u32(value, cap); | 1268 | return wmid_v2_set_u32(value, cap); |
1213 | else if (wmi_has_guid(WMID_GUID2)) | 1269 | else if (wmi_has_guid(WMID_GUID2)) |
1214 | return WMID_set_u32(value, cap, interface); | 1270 | return WMID_set_u32(value, cap); |
1215 | default: | 1271 | default: |
1216 | return AE_BAD_PARAMETER; | 1272 | return AE_BAD_PARAMETER; |
1217 | } | 1273 | } |
@@ -1488,8 +1544,8 @@ static ssize_t show_bool_threeg(struct device *dev, | |||
1488 | u32 result; \ | 1544 | u32 result; \ |
1489 | acpi_status status; | 1545 | acpi_status status; |
1490 | 1546 | ||
1491 | pr_info("This threeg sysfs will be removed in 2012" | 1547 | pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n", |
1492 | " - used by: %s\n", current->comm); | 1548 | current->comm); |
1493 | status = get_u32(&result, ACER_CAP_THREEG); | 1549 | status = get_u32(&result, ACER_CAP_THREEG); |
1494 | if (ACPI_SUCCESS(status)) | 1550 | if (ACPI_SUCCESS(status)) |
1495 | return sprintf(buf, "%u\n", result); | 1551 | return sprintf(buf, "%u\n", result); |
@@ -1501,8 +1557,8 @@ static ssize_t set_bool_threeg(struct device *dev, | |||
1501 | { | 1557 | { |
1502 | u32 tmp = simple_strtoul(buf, NULL, 10); | 1558 | u32 tmp = simple_strtoul(buf, NULL, 10); |
1503 | acpi_status status = set_u32(tmp, ACER_CAP_THREEG); | 1559 | acpi_status status = set_u32(tmp, ACER_CAP_THREEG); |
1504 | pr_info("This threeg sysfs will be removed in 2012" | 1560 | pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n", |
1505 | " - used by: %s\n", current->comm); | 1561 | current->comm); |
1506 | if (ACPI_FAILURE(status)) | 1562 | if (ACPI_FAILURE(status)) |
1507 | return -EINVAL; | 1563 | return -EINVAL; |
1508 | return count; | 1564 | return count; |
@@ -1513,8 +1569,8 @@ static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg, | |||
1513 | static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | 1569 | static ssize_t show_interface(struct device *dev, struct device_attribute *attr, |
1514 | char *buf) | 1570 | char *buf) |
1515 | { | 1571 | { |
1516 | pr_info("This interface sysfs will be removed in 2012" | 1572 | pr_info("This interface sysfs will be removed in 2012 - used by: %s\n", |
1517 | " - used by: %s\n", current->comm); | 1573 | current->comm); |
1518 | switch (interface->type) { | 1574 | switch (interface->type) { |
1519 | case ACER_AMW0: | 1575 | case ACER_AMW0: |
1520 | return sprintf(buf, "AMW0\n"); | 1576 | return sprintf(buf, "AMW0\n"); |
@@ -1981,9 +2037,13 @@ static int __init acer_wmi_init(void) | |||
1981 | set_quirks(); | 2037 | set_quirks(); |
1982 | 2038 | ||
1983 | if (acpi_video_backlight_support()) { | 2039 | if (acpi_video_backlight_support()) { |
1984 | interface->capability &= ~ACER_CAP_BRIGHTNESS; | 2040 | if (dmi_check_system(video_vendor_dmi_table)) { |
1985 | pr_info("Brightness must be controlled by " | 2041 | acpi_video_unregister(); |
1986 | "generic video driver\n"); | 2042 | } else { |
2043 | interface->capability &= ~ACER_CAP_BRIGHTNESS; | ||
2044 | pr_info("Brightness must be controlled by " | ||
2045 | "acpi video driver\n"); | ||
2046 | } | ||
1987 | } | 2047 | } |
1988 | 2048 | ||
1989 | if (wmi_has_guid(WMID_GUID3)) { | 2049 | if (wmi_has_guid(WMID_GUID3)) { |
@@ -2008,7 +2068,7 @@ static int __init acer_wmi_init(void) | |||
2008 | 2068 | ||
2009 | err = platform_driver_register(&acer_platform_driver); | 2069 | err = platform_driver_register(&acer_platform_driver); |
2010 | if (err) { | 2070 | if (err) { |
2011 | pr_err("Unable to register platform driver.\n"); | 2071 | pr_err("Unable to register platform driver\n"); |
2012 | goto error_platform_register; | 2072 | goto error_platform_register; |
2013 | } | 2073 | } |
2014 | 2074 | ||
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 760c6d7624fe..bc8384c6f3eb 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c | |||
@@ -244,12 +244,11 @@ static void acerhdf_change_fanstate(int state) | |||
244 | unsigned char cmd; | 244 | unsigned char cmd; |
245 | 245 | ||
246 | if (verbose) | 246 | if (verbose) |
247 | pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ? | 247 | pr_notice("fan %s\n", state == ACERHDF_FAN_OFF ? "OFF" : "ON"); |
248 | "OFF" : "ON"); | ||
249 | 248 | ||
250 | if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) { | 249 | if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) { |
251 | pr_err("invalid fan state %d requested, setting to auto!\n", | 250 | pr_err("invalid fan state %d requested, setting to auto!\n", |
252 | state); | 251 | state); |
253 | state = ACERHDF_FAN_AUTO; | 252 | state = ACERHDF_FAN_AUTO; |
254 | } | 253 | } |
255 | 254 | ||
@@ -264,19 +263,18 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal) | |||
264 | { | 263 | { |
265 | if (fanon > ACERHDF_MAX_FANON) { | 264 | if (fanon > ACERHDF_MAX_FANON) { |
266 | pr_err("fanon temperature too high, set to %d\n", | 265 | pr_err("fanon temperature too high, set to %d\n", |
267 | ACERHDF_MAX_FANON); | 266 | ACERHDF_MAX_FANON); |
268 | fanon = ACERHDF_MAX_FANON; | 267 | fanon = ACERHDF_MAX_FANON; |
269 | } | 268 | } |
270 | 269 | ||
271 | if (kernelmode && prev_interval != interval) { | 270 | if (kernelmode && prev_interval != interval) { |
272 | if (interval > ACERHDF_MAX_INTERVAL) { | 271 | if (interval > ACERHDF_MAX_INTERVAL) { |
273 | pr_err("interval too high, set to %d\n", | 272 | pr_err("interval too high, set to %d\n", |
274 | ACERHDF_MAX_INTERVAL); | 273 | ACERHDF_MAX_INTERVAL); |
275 | interval = ACERHDF_MAX_INTERVAL; | 274 | interval = ACERHDF_MAX_INTERVAL; |
276 | } | 275 | } |
277 | if (verbose) | 276 | if (verbose) |
278 | pr_notice("interval changed to: %d\n", | 277 | pr_notice("interval changed to: %d\n", interval); |
279 | interval); | ||
280 | thermal->polling_delay = interval*1000; | 278 | thermal->polling_delay = interval*1000; |
281 | prev_interval = interval; | 279 | prev_interval = interval; |
282 | } | 280 | } |
@@ -587,8 +585,8 @@ static int acerhdf_check_hardware(void) | |||
587 | } | 585 | } |
588 | 586 | ||
589 | if (!bios_cfg) { | 587 | if (!bios_cfg) { |
590 | pr_err("unknown (unsupported) BIOS version %s/%s/%s, " | 588 | pr_err("unknown (unsupported) BIOS version %s/%s/%s, please report, aborting!\n", |
591 | "please report, aborting!\n", vendor, product, version); | 589 | vendor, product, version); |
592 | return -EINVAL; | 590 | return -EINVAL; |
593 | } | 591 | } |
594 | 592 | ||
@@ -598,8 +596,7 @@ static int acerhdf_check_hardware(void) | |||
598 | */ | 596 | */ |
599 | if (!kernelmode) { | 597 | if (!kernelmode) { |
600 | pr_notice("Fan control off, to enable do:\n"); | 598 | pr_notice("Fan control off, to enable do:\n"); |
601 | pr_notice("echo -n \"enabled\" > " | 599 | pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zone0/mode\n"); |
602 | "/sys/class/thermal/thermal_zone0/mode\n"); | ||
603 | } | 600 | } |
604 | 601 | ||
605 | return 0; | 602 | return 0; |
diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c index 19170bb7700b..a514bf66fdd7 100644 --- a/drivers/platform/x86/amilo-rfkill.c +++ b/drivers/platform/x86/amilo-rfkill.c | |||
@@ -97,9 +97,12 @@ static struct rfkill *amilo_rfkill_dev; | |||
97 | 97 | ||
98 | static int __devinit amilo_rfkill_probe(struct platform_device *device) | 98 | static int __devinit amilo_rfkill_probe(struct platform_device *device) |
99 | { | 99 | { |
100 | int rc; | ||
100 | const struct dmi_system_id *system_id = | 101 | const struct dmi_system_id *system_id = |
101 | dmi_first_match(amilo_rfkill_id_table); | 102 | dmi_first_match(amilo_rfkill_id_table); |
102 | int rc; | 103 | |
104 | if (!system_id) | ||
105 | return -ENXIO; | ||
103 | 106 | ||
104 | amilo_rfkill_dev = rfkill_alloc(KBUILD_MODNAME, &device->dev, | 107 | amilo_rfkill_dev = rfkill_alloc(KBUILD_MODNAME, &device->dev, |
105 | RFKILL_TYPE_WLAN, | 108 | RFKILL_TYPE_WLAN, |
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c new file mode 100644 index 000000000000..8a582bdfdc76 --- /dev/null +++ b/drivers/platform/x86/apple-gmux.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Gmux driver for Apple laptops | ||
3 | * | ||
4 | * Copyright (C) Canonical Ltd. <seth.forshee@canonical.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/backlight.h> | ||
17 | #include <linux/acpi.h> | ||
18 | #include <linux/pnp.h> | ||
19 | #include <linux/apple_bl.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <acpi/video.h> | ||
22 | #include <asm/io.h> | ||
23 | |||
24 | struct apple_gmux_data { | ||
25 | unsigned long iostart; | ||
26 | unsigned long iolen; | ||
27 | |||
28 | struct backlight_device *bdev; | ||
29 | }; | ||
30 | |||
31 | /* | ||
32 | * gmux port offsets. Many of these are not yet used, but may be in the | ||
33 | * future, and it's useful to have them documented here anyhow. | ||
34 | */ | ||
35 | #define GMUX_PORT_VERSION_MAJOR 0x04 | ||
36 | #define GMUX_PORT_VERSION_MINOR 0x05 | ||
37 | #define GMUX_PORT_VERSION_RELEASE 0x06 | ||
38 | #define GMUX_PORT_SWITCH_DISPLAY 0x10 | ||
39 | #define GMUX_PORT_SWITCH_GET_DISPLAY 0x11 | ||
40 | #define GMUX_PORT_INTERRUPT_ENABLE 0x14 | ||
41 | #define GMUX_PORT_INTERRUPT_STATUS 0x16 | ||
42 | #define GMUX_PORT_SWITCH_DDC 0x28 | ||
43 | #define GMUX_PORT_SWITCH_EXTERNAL 0x40 | ||
44 | #define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41 | ||
45 | #define GMUX_PORT_DISCRETE_POWER 0x50 | ||
46 | #define GMUX_PORT_MAX_BRIGHTNESS 0x70 | ||
47 | #define GMUX_PORT_BRIGHTNESS 0x74 | ||
48 | |||
49 | #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) | ||
50 | |||
51 | #define GMUX_INTERRUPT_ENABLE 0xff | ||
52 | #define GMUX_INTERRUPT_DISABLE 0x00 | ||
53 | |||
54 | #define GMUX_INTERRUPT_STATUS_ACTIVE 0 | ||
55 | #define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0) | ||
56 | #define GMUX_INTERRUPT_STATUS_POWER (1 << 2) | ||
57 | #define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3) | ||
58 | |||
59 | #define GMUX_BRIGHTNESS_MASK 0x00ffffff | ||
60 | #define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK | ||
61 | |||
62 | static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) | ||
63 | { | ||
64 | return inb(gmux_data->iostart + port); | ||
65 | } | ||
66 | |||
67 | static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port, | ||
68 | u8 val) | ||
69 | { | ||
70 | outb(val, gmux_data->iostart + port); | ||
71 | } | ||
72 | |||
73 | static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port) | ||
74 | { | ||
75 | return inl(gmux_data->iostart + port); | ||
76 | } | ||
77 | |||
78 | static int gmux_get_brightness(struct backlight_device *bd) | ||
79 | { | ||
80 | struct apple_gmux_data *gmux_data = bl_get_data(bd); | ||
81 | return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) & | ||
82 | GMUX_BRIGHTNESS_MASK; | ||
83 | } | ||
84 | |||
85 | static int gmux_update_status(struct backlight_device *bd) | ||
86 | { | ||
87 | struct apple_gmux_data *gmux_data = bl_get_data(bd); | ||
88 | u32 brightness = bd->props.brightness; | ||
89 | |||
90 | /* | ||
91 | * Older gmux versions require writing out lower bytes first then | ||
92 | * setting the upper byte to 0 to flush the values. Newer versions | ||
93 | * accept a single u32 write, but the old method also works, so we | ||
94 | * just use the old method for all gmux versions. | ||
95 | */ | ||
96 | gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness); | ||
97 | gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8); | ||
98 | gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16); | ||
99 | gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static const struct backlight_ops gmux_bl_ops = { | ||
105 | .get_brightness = gmux_get_brightness, | ||
106 | .update_status = gmux_update_status, | ||
107 | }; | ||
108 | |||
109 | static int __devinit gmux_probe(struct pnp_dev *pnp, | ||
110 | const struct pnp_device_id *id) | ||
111 | { | ||
112 | struct apple_gmux_data *gmux_data; | ||
113 | struct resource *res; | ||
114 | struct backlight_properties props; | ||
115 | struct backlight_device *bdev; | ||
116 | u8 ver_major, ver_minor, ver_release; | ||
117 | int ret = -ENXIO; | ||
118 | |||
119 | gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL); | ||
120 | if (!gmux_data) | ||
121 | return -ENOMEM; | ||
122 | pnp_set_drvdata(pnp, gmux_data); | ||
123 | |||
124 | res = pnp_get_resource(pnp, IORESOURCE_IO, 0); | ||
125 | if (!res) { | ||
126 | pr_err("Failed to find gmux I/O resource\n"); | ||
127 | goto err_free; | ||
128 | } | ||
129 | |||
130 | gmux_data->iostart = res->start; | ||
131 | gmux_data->iolen = res->end - res->start; | ||
132 | |||
133 | if (gmux_data->iolen < GMUX_MIN_IO_LEN) { | ||
134 | pr_err("gmux I/O region too small (%lu < %u)\n", | ||
135 | gmux_data->iolen, GMUX_MIN_IO_LEN); | ||
136 | goto err_free; | ||
137 | } | ||
138 | |||
139 | if (!request_region(gmux_data->iostart, gmux_data->iolen, | ||
140 | "Apple gmux")) { | ||
141 | pr_err("gmux I/O already in use\n"); | ||
142 | goto err_free; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * On some machines the gmux is in ACPI even thought the machine | ||
147 | * doesn't really have a gmux. Check for invalid version information | ||
148 | * to detect this. | ||
149 | */ | ||
150 | ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR); | ||
151 | ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR); | ||
152 | ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE); | ||
153 | if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { | ||
154 | pr_info("gmux device not present\n"); | ||
155 | ret = -ENODEV; | ||
156 | goto err_release; | ||
157 | } | ||
158 | |||
159 | pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor, | ||
160 | ver_release); | ||
161 | |||
162 | memset(&props, 0, sizeof(props)); | ||
163 | props.type = BACKLIGHT_PLATFORM; | ||
164 | props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS); | ||
165 | |||
166 | /* | ||
167 | * Currently it's assumed that the maximum brightness is less than | ||
168 | * 2^24 for compatibility with old gmux versions. Cap the max | ||
169 | * brightness at this value, but print a warning if the hardware | ||
170 | * reports something higher so that it can be fixed. | ||
171 | */ | ||
172 | if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS)) | ||
173 | props.max_brightness = GMUX_MAX_BRIGHTNESS; | ||
174 | |||
175 | bdev = backlight_device_register("gmux_backlight", &pnp->dev, | ||
176 | gmux_data, &gmux_bl_ops, &props); | ||
177 | if (IS_ERR(bdev)) { | ||
178 | ret = PTR_ERR(bdev); | ||
179 | goto err_release; | ||
180 | } | ||
181 | |||
182 | gmux_data->bdev = bdev; | ||
183 | bdev->props.brightness = gmux_get_brightness(bdev); | ||
184 | backlight_update_status(bdev); | ||
185 | |||
186 | /* | ||
187 | * The backlight situation on Macs is complicated. If the gmux is | ||
188 | * present it's the best choice, because it always works for | ||
189 | * backlight control and supports more levels than other options. | ||
190 | * Disable the other backlight choices. | ||
191 | */ | ||
192 | acpi_video_unregister(); | ||
193 | apple_bl_unregister(); | ||
194 | |||
195 | return 0; | ||
196 | |||
197 | err_release: | ||
198 | release_region(gmux_data->iostart, gmux_data->iolen); | ||
199 | err_free: | ||
200 | kfree(gmux_data); | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | static void __devexit gmux_remove(struct pnp_dev *pnp) | ||
205 | { | ||
206 | struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); | ||
207 | |||
208 | backlight_device_unregister(gmux_data->bdev); | ||
209 | release_region(gmux_data->iostart, gmux_data->iolen); | ||
210 | kfree(gmux_data); | ||
211 | |||
212 | acpi_video_register(); | ||
213 | apple_bl_register(); | ||
214 | } | ||
215 | |||
216 | static const struct pnp_device_id gmux_device_ids[] = { | ||
217 | {"APP000B", 0}, | ||
218 | {"", 0} | ||
219 | }; | ||
220 | |||
221 | static struct pnp_driver gmux_pnp_driver = { | ||
222 | .name = "apple-gmux", | ||
223 | .probe = gmux_probe, | ||
224 | .remove = __devexit_p(gmux_remove), | ||
225 | .id_table = gmux_device_ids, | ||
226 | }; | ||
227 | |||
228 | static int __init apple_gmux_init(void) | ||
229 | { | ||
230 | return pnp_register_driver(&gmux_pnp_driver); | ||
231 | } | ||
232 | |||
233 | static void __exit apple_gmux_exit(void) | ||
234 | { | ||
235 | pnp_unregister_driver(&gmux_pnp_driver); | ||
236 | } | ||
237 | |||
238 | module_init(apple_gmux_init); | ||
239 | module_exit(apple_gmux_exit); | ||
240 | |||
241 | MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>"); | ||
242 | MODULE_DESCRIPTION("Apple Gmux Driver"); | ||
243 | MODULE_LICENSE("GPL"); | ||
244 | MODULE_DEVICE_TABLE(pnp, gmux_device_ids); | ||
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b7944f903886..e38f91be0b10 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -81,6 +81,19 @@ static uint wapf = 1; | |||
81 | module_param(wapf, uint, 0444); | 81 | module_param(wapf, uint, 0444); |
82 | MODULE_PARM_DESC(wapf, "WAPF value"); | 82 | MODULE_PARM_DESC(wapf, "WAPF value"); |
83 | 83 | ||
84 | static char *wled_type = "unknown"; | ||
85 | static char *bled_type = "unknown"; | ||
86 | |||
87 | module_param(wled_type, charp, 0444); | ||
88 | MODULE_PARM_DESC(wlan_status, "Set the wled type on boot " | ||
89 | "(unknown, led or rfkill). " | ||
90 | "default is unknown"); | ||
91 | |||
92 | module_param(bled_type, charp, 0444); | ||
93 | MODULE_PARM_DESC(bled_type, "Set the bled type on boot " | ||
94 | "(unknown, led or rfkill). " | ||
95 | "default is unknown"); | ||
96 | |||
84 | static int wlan_status = 1; | 97 | static int wlan_status = 1; |
85 | static int bluetooth_status = 1; | 98 | static int bluetooth_status = 1; |
86 | static int wimax_status = -1; | 99 | static int wimax_status = -1; |
@@ -137,6 +150,11 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot " | |||
137 | #define WM_RSTS 0x08 /* internal wimax */ | 150 | #define WM_RSTS 0x08 /* internal wimax */ |
138 | #define WW_RSTS 0x20 /* internal wwan */ | 151 | #define WW_RSTS 0x20 /* internal wwan */ |
139 | 152 | ||
153 | /* WLED and BLED type */ | ||
154 | #define TYPE_UNKNOWN 0 | ||
155 | #define TYPE_LED 1 | ||
156 | #define TYPE_RFKILL 2 | ||
157 | |||
140 | /* LED */ | 158 | /* LED */ |
141 | #define METHOD_MLED "MLED" | 159 | #define METHOD_MLED "MLED" |
142 | #define METHOD_TLED "TLED" | 160 | #define METHOD_TLED "TLED" |
@@ -218,8 +236,9 @@ struct asus_led { | |||
218 | /* | 236 | /* |
219 | * Same thing for rfkill | 237 | * Same thing for rfkill |
220 | */ | 238 | */ |
221 | struct asus_pega_rfkill { | 239 | struct asus_rfkill { |
222 | int control_id; /* type of control. Maps to PEGA_* values */ | 240 | /* type of control. Maps to PEGA_* values or *_RSTS */ |
241 | int control_id; | ||
223 | struct rfkill *rfkill; | 242 | struct rfkill *rfkill; |
224 | struct asus_laptop *asus; | 243 | struct asus_laptop *asus; |
225 | }; | 244 | }; |
@@ -240,6 +259,8 @@ struct asus_laptop { | |||
240 | struct key_entry *keymap; | 259 | struct key_entry *keymap; |
241 | struct input_polled_dev *pega_accel_poll; | 260 | struct input_polled_dev *pega_accel_poll; |
242 | 261 | ||
262 | struct asus_led wled; | ||
263 | struct asus_led bled; | ||
243 | struct asus_led mled; | 264 | struct asus_led mled; |
244 | struct asus_led tled; | 265 | struct asus_led tled; |
245 | struct asus_led rled; | 266 | struct asus_led rled; |
@@ -248,6 +269,8 @@ struct asus_laptop { | |||
248 | struct asus_led kled; | 269 | struct asus_led kled; |
249 | struct workqueue_struct *led_workqueue; | 270 | struct workqueue_struct *led_workqueue; |
250 | 271 | ||
272 | int wled_type; | ||
273 | int bled_type; | ||
251 | int wireless_status; | 274 | int wireless_status; |
252 | bool have_rsts; | 275 | bool have_rsts; |
253 | bool is_pega_lucid; | 276 | bool is_pega_lucid; |
@@ -256,11 +279,11 @@ struct asus_laptop { | |||
256 | int pega_acc_y; | 279 | int pega_acc_y; |
257 | int pega_acc_z; | 280 | int pega_acc_z; |
258 | 281 | ||
259 | struct rfkill *gps_rfkill; | 282 | struct asus_rfkill wlan; |
260 | 283 | struct asus_rfkill bluetooth; | |
261 | struct asus_pega_rfkill wlanrfk; | 284 | struct asus_rfkill wwan; |
262 | struct asus_pega_rfkill btrfk; | 285 | struct asus_rfkill wimax; |
263 | struct asus_pega_rfkill wwanrfk; | 286 | struct asus_rfkill gps; |
264 | 287 | ||
265 | acpi_handle handle; /* the handle of the hotk device */ | 288 | acpi_handle handle; /* the handle of the hotk device */ |
266 | u32 ledd_status; /* status of the LED display */ | 289 | u32 ledd_status; /* status of the LED display */ |
@@ -274,6 +297,7 @@ static const struct key_entry asus_keymap[] = { | |||
274 | {KE_KEY, 0x02, { KEY_SCREENLOCK } }, | 297 | {KE_KEY, 0x02, { KEY_SCREENLOCK } }, |
275 | {KE_KEY, 0x05, { KEY_WLAN } }, | 298 | {KE_KEY, 0x05, { KEY_WLAN } }, |
276 | {KE_KEY, 0x08, { KEY_F13 } }, | 299 | {KE_KEY, 0x08, { KEY_F13 } }, |
300 | {KE_KEY, 0x09, { KEY_PROG2 } }, /* Dock */ | ||
277 | {KE_KEY, 0x17, { KEY_ZOOM } }, | 301 | {KE_KEY, 0x17, { KEY_ZOOM } }, |
278 | {KE_KEY, 0x1f, { KEY_BATTERY } }, | 302 | {KE_KEY, 0x1f, { KEY_BATTERY } }, |
279 | /* End of Lenovo SL Specific keycodes */ | 303 | /* End of Lenovo SL Specific keycodes */ |
@@ -299,6 +323,8 @@ static const struct key_entry asus_keymap[] = { | |||
299 | {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, | 323 | {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, |
300 | {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, | 324 | {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, |
301 | {KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */ | 325 | {KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */ |
326 | {KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */ | ||
327 | {KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */ | ||
302 | {KE_KEY, 0x7E, { KEY_BLUETOOTH } }, | 328 | {KE_KEY, 0x7E, { KEY_BLUETOOTH } }, |
303 | {KE_KEY, 0x7D, { KEY_BLUETOOTH } }, | 329 | {KE_KEY, 0x7D, { KEY_BLUETOOTH } }, |
304 | {KE_KEY, 0x82, { KEY_CAMERA } }, | 330 | {KE_KEY, 0x82, { KEY_CAMERA } }, |
@@ -601,6 +627,10 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev) | |||
601 | 627 | ||
602 | static void asus_led_exit(struct asus_laptop *asus) | 628 | static void asus_led_exit(struct asus_laptop *asus) |
603 | { | 629 | { |
630 | if (!IS_ERR_OR_NULL(asus->wled.led.dev)) | ||
631 | led_classdev_unregister(&asus->wled.led); | ||
632 | if (!IS_ERR_OR_NULL(asus->bled.led.dev)) | ||
633 | led_classdev_unregister(&asus->bled.led); | ||
604 | if (!IS_ERR_OR_NULL(asus->mled.led.dev)) | 634 | if (!IS_ERR_OR_NULL(asus->mled.led.dev)) |
605 | led_classdev_unregister(&asus->mled.led); | 635 | led_classdev_unregister(&asus->mled.led); |
606 | if (!IS_ERR_OR_NULL(asus->tled.led.dev)) | 636 | if (!IS_ERR_OR_NULL(asus->tled.led.dev)) |
@@ -642,7 +672,7 @@ static int asus_led_register(struct asus_laptop *asus, | |||
642 | 672 | ||
643 | static int asus_led_init(struct asus_laptop *asus) | 673 | static int asus_led_init(struct asus_laptop *asus) |
644 | { | 674 | { |
645 | int r; | 675 | int r = 0; |
646 | 676 | ||
647 | /* | 677 | /* |
648 | * The Pegatron Lucid has no physical leds, but all methods are | 678 | * The Pegatron Lucid has no physical leds, but all methods are |
@@ -661,6 +691,16 @@ static int asus_led_init(struct asus_laptop *asus) | |||
661 | if (!asus->led_workqueue) | 691 | if (!asus->led_workqueue) |
662 | return -ENOMEM; | 692 | return -ENOMEM; |
663 | 693 | ||
694 | if (asus->wled_type == TYPE_LED) | ||
695 | r = asus_led_register(asus, &asus->wled, "asus::wlan", | ||
696 | METHOD_WLAN); | ||
697 | if (r) | ||
698 | goto error; | ||
699 | if (asus->bled_type == TYPE_LED) | ||
700 | r = asus_led_register(asus, &asus->bled, "asus::bluetooth", | ||
701 | METHOD_BLUETOOTH); | ||
702 | if (r) | ||
703 | goto error; | ||
664 | r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED); | 704 | r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED); |
665 | if (r) | 705 | if (r) |
666 | goto error; | 706 | goto error; |
@@ -963,7 +1003,7 @@ static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, | |||
963 | return sysfs_acpi_set(asus, buf, count, METHOD_WLAN); | 1003 | return sysfs_acpi_set(asus, buf, count, METHOD_WLAN); |
964 | } | 1004 | } |
965 | 1005 | ||
966 | /* | 1006 | /*e |
967 | * Bluetooth | 1007 | * Bluetooth |
968 | */ | 1008 | */ |
969 | static int asus_bluetooth_set(struct asus_laptop *asus, int status) | 1009 | static int asus_bluetooth_set(struct asus_laptop *asus, int status) |
@@ -1228,7 +1268,7 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr, | |||
1228 | ret = asus_gps_switch(asus, !!value); | 1268 | ret = asus_gps_switch(asus, !!value); |
1229 | if (ret) | 1269 | if (ret) |
1230 | return ret; | 1270 | return ret; |
1231 | rfkill_set_sw_state(asus->gps_rfkill, !value); | 1271 | rfkill_set_sw_state(asus->gps.rfkill, !value); |
1232 | return rv; | 1272 | return rv; |
1233 | } | 1273 | } |
1234 | 1274 | ||
@@ -1246,90 +1286,139 @@ static const struct rfkill_ops asus_gps_rfkill_ops = { | |||
1246 | .set_block = asus_gps_rfkill_set, | 1286 | .set_block = asus_gps_rfkill_set, |
1247 | }; | 1287 | }; |
1248 | 1288 | ||
1289 | static int asus_rfkill_set(void *data, bool blocked) | ||
1290 | { | ||
1291 | struct asus_rfkill *rfk = data; | ||
1292 | struct asus_laptop *asus = rfk->asus; | ||
1293 | |||
1294 | if (rfk->control_id == WL_RSTS) | ||
1295 | return asus_wlan_set(asus, !blocked); | ||
1296 | else if (rfk->control_id == BT_RSTS) | ||
1297 | return asus_bluetooth_set(asus, !blocked); | ||
1298 | else if (rfk->control_id == WM_RSTS) | ||
1299 | return asus_wimax_set(asus, !blocked); | ||
1300 | else if (rfk->control_id == WW_RSTS) | ||
1301 | return asus_wwan_set(asus, !blocked); | ||
1302 | |||
1303 | return -EINVAL; | ||
1304 | } | ||
1305 | |||
1306 | static const struct rfkill_ops asus_rfkill_ops = { | ||
1307 | .set_block = asus_rfkill_set, | ||
1308 | }; | ||
1309 | |||
1310 | static void asus_rfkill_terminate(struct asus_rfkill *rfk) | ||
1311 | { | ||
1312 | if (!rfk->rfkill) | ||
1313 | return ; | ||
1314 | |||
1315 | rfkill_unregister(rfk->rfkill); | ||
1316 | rfkill_destroy(rfk->rfkill); | ||
1317 | rfk->rfkill = NULL; | ||
1318 | } | ||
1319 | |||
1249 | static void asus_rfkill_exit(struct asus_laptop *asus) | 1320 | static void asus_rfkill_exit(struct asus_laptop *asus) |
1250 | { | 1321 | { |
1251 | if (asus->gps_rfkill) { | 1322 | asus_rfkill_terminate(&asus->wwan); |
1252 | rfkill_unregister(asus->gps_rfkill); | 1323 | asus_rfkill_terminate(&asus->bluetooth); |
1253 | rfkill_destroy(asus->gps_rfkill); | 1324 | asus_rfkill_terminate(&asus->wlan); |
1254 | asus->gps_rfkill = NULL; | 1325 | asus_rfkill_terminate(&asus->gps); |
1255 | } | ||
1256 | } | 1326 | } |
1257 | 1327 | ||
1258 | static int asus_rfkill_init(struct asus_laptop *asus) | 1328 | static int asus_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk, |
1329 | const char *name, int control_id, int type, | ||
1330 | const struct rfkill_ops *ops) | ||
1259 | { | 1331 | { |
1260 | int result; | 1332 | int result; |
1261 | 1333 | ||
1262 | if (acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) || | 1334 | rfk->control_id = control_id; |
1263 | acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) || | 1335 | rfk->asus = asus; |
1264 | acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) | 1336 | rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev, |
1265 | return 0; | 1337 | type, ops, rfk); |
1266 | 1338 | if (!rfk->rfkill) | |
1267 | asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, | ||
1268 | RFKILL_TYPE_GPS, | ||
1269 | &asus_gps_rfkill_ops, asus); | ||
1270 | if (!asus->gps_rfkill) | ||
1271 | return -EINVAL; | 1339 | return -EINVAL; |
1272 | 1340 | ||
1273 | result = rfkill_register(asus->gps_rfkill); | 1341 | result = rfkill_register(rfk->rfkill); |
1274 | if (result) { | 1342 | if (result) { |
1275 | rfkill_destroy(asus->gps_rfkill); | 1343 | rfkill_destroy(rfk->rfkill); |
1276 | asus->gps_rfkill = NULL; | 1344 | rfk->rfkill = NULL; |
1277 | } | 1345 | } |
1278 | 1346 | ||
1279 | return result; | 1347 | return result; |
1280 | } | 1348 | } |
1281 | 1349 | ||
1282 | static int pega_rfkill_set(void *data, bool blocked) | 1350 | static int asus_rfkill_init(struct asus_laptop *asus) |
1283 | { | 1351 | { |
1284 | struct asus_pega_rfkill *pega_rfk = data; | 1352 | int result = 0; |
1285 | 1353 | ||
1286 | int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked); | 1354 | if (asus->is_pega_lucid) |
1287 | pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret); | 1355 | return -ENODEV; |
1288 | 1356 | ||
1289 | return ret; | 1357 | if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && |
1290 | } | 1358 | !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && |
1359 | !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) | ||
1360 | result = asus_rfkill_setup(asus, &asus->gps, "asus-gps", | ||
1361 | -1, RFKILL_TYPE_GPS, | ||
1362 | &asus_gps_rfkill_ops); | ||
1363 | if (result) | ||
1364 | goto exit; | ||
1291 | 1365 | ||
1292 | static const struct rfkill_ops pega_rfkill_ops = { | ||
1293 | .set_block = pega_rfkill_set, | ||
1294 | }; | ||
1295 | 1366 | ||
1296 | static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk) | 1367 | if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL) && |
1297 | { | 1368 | asus->wled_type == TYPE_RFKILL) |
1298 | pr_warn("Terminating %d\n", pega_rfk->control_id); | 1369 | result = asus_rfkill_setup(asus, &asus->wlan, "asus-wlan", |
1299 | if (pega_rfk->rfkill) { | 1370 | WL_RSTS, RFKILL_TYPE_WLAN, |
1300 | rfkill_unregister(pega_rfk->rfkill); | 1371 | &asus_rfkill_ops); |
1301 | rfkill_destroy(pega_rfk->rfkill); | 1372 | if (result) |
1302 | pega_rfk->rfkill = NULL; | 1373 | goto exit; |
1303 | } | ||
1304 | } | ||
1305 | 1374 | ||
1306 | static void pega_rfkill_exit(struct asus_laptop *asus) | 1375 | if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL) && |
1307 | { | 1376 | asus->bled_type == TYPE_RFKILL) |
1308 | pega_rfkill_terminate(&asus->wwanrfk); | 1377 | result = asus_rfkill_setup(asus, &asus->bluetooth, |
1309 | pega_rfkill_terminate(&asus->btrfk); | 1378 | "asus-bluetooth", BT_RSTS, |
1310 | pega_rfkill_terminate(&asus->wlanrfk); | 1379 | RFKILL_TYPE_BLUETOOTH, |
1380 | &asus_rfkill_ops); | ||
1381 | if (result) | ||
1382 | goto exit; | ||
1383 | |||
1384 | if (!acpi_check_handle(asus->handle, METHOD_WWAN, NULL)) | ||
1385 | result = asus_rfkill_setup(asus, &asus->wwan, "asus-wwan", | ||
1386 | WW_RSTS, RFKILL_TYPE_WWAN, | ||
1387 | &asus_rfkill_ops); | ||
1388 | if (result) | ||
1389 | goto exit; | ||
1390 | |||
1391 | if (!acpi_check_handle(asus->handle, METHOD_WIMAX, NULL)) | ||
1392 | result = asus_rfkill_setup(asus, &asus->wimax, "asus-wimax", | ||
1393 | WM_RSTS, RFKILL_TYPE_WIMAX, | ||
1394 | &asus_rfkill_ops); | ||
1395 | if (result) | ||
1396 | goto exit; | ||
1397 | |||
1398 | exit: | ||
1399 | if (result) | ||
1400 | asus_rfkill_exit(asus); | ||
1401 | |||
1402 | return result; | ||
1311 | } | 1403 | } |
1312 | 1404 | ||
1313 | static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk, | 1405 | static int pega_rfkill_set(void *data, bool blocked) |
1314 | const char *name, int controlid, int rfkill_type) | ||
1315 | { | 1406 | { |
1316 | int result; | 1407 | struct asus_rfkill *rfk = data; |
1317 | 1408 | ||
1318 | pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type); | 1409 | int ret = asus_pega_lucid_set(rfk->asus, rfk->control_id, !blocked); |
1319 | pega_rfk->control_id = controlid; | 1410 | return ret; |
1320 | pega_rfk->asus = asus; | 1411 | } |
1321 | pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev, | ||
1322 | rfkill_type, &pega_rfkill_ops, pega_rfk); | ||
1323 | if (!pega_rfk->rfkill) | ||
1324 | return -EINVAL; | ||
1325 | 1412 | ||
1326 | result = rfkill_register(pega_rfk->rfkill); | 1413 | static const struct rfkill_ops pega_rfkill_ops = { |
1327 | if (result) { | 1414 | .set_block = pega_rfkill_set, |
1328 | rfkill_destroy(pega_rfk->rfkill); | 1415 | }; |
1329 | pega_rfk->rfkill = NULL; | ||
1330 | } | ||
1331 | 1416 | ||
1332 | return result; | 1417 | static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk, |
1418 | const char *name, int controlid, int rfkill_type) | ||
1419 | { | ||
1420 | return asus_rfkill_setup(asus, rfk, name, controlid, rfkill_type, | ||
1421 | &pega_rfkill_ops); | ||
1333 | } | 1422 | } |
1334 | 1423 | ||
1335 | static int pega_rfkill_init(struct asus_laptop *asus) | 1424 | static int pega_rfkill_init(struct asus_laptop *asus) |
@@ -1339,22 +1428,22 @@ static int pega_rfkill_init(struct asus_laptop *asus) | |||
1339 | if(!asus->is_pega_lucid) | 1428 | if(!asus->is_pega_lucid) |
1340 | return -ENODEV; | 1429 | return -ENODEV; |
1341 | 1430 | ||
1342 | ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN); | 1431 | ret = pega_rfkill_setup(asus, &asus->wlan, "pega-wlan", |
1343 | if(ret) | 1432 | PEGA_WLAN, RFKILL_TYPE_WLAN); |
1344 | return ret; | ||
1345 | ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH); | ||
1346 | if(ret) | 1433 | if(ret) |
1347 | goto err_btrfk; | 1434 | goto exit; |
1348 | ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN); | 1435 | |
1436 | ret = pega_rfkill_setup(asus, &asus->bluetooth, "pega-bt", | ||
1437 | PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH); | ||
1349 | if(ret) | 1438 | if(ret) |
1350 | goto err_wwanrfk; | 1439 | goto exit; |
1351 | 1440 | ||
1352 | pr_warn("Pega rfkill init succeeded\n"); | 1441 | ret = pega_rfkill_setup(asus, &asus->wwan, "pega-wwan", |
1353 | return 0; | 1442 | PEGA_WWAN, RFKILL_TYPE_WWAN); |
1354 | err_wwanrfk: | 1443 | |
1355 | pega_rfkill_terminate(&asus->btrfk); | 1444 | exit: |
1356 | err_btrfk: | 1445 | if (ret) |
1357 | pega_rfkill_terminate(&asus->wlanrfk); | 1446 | asus_rfkill_exit(asus); |
1358 | 1447 | ||
1359 | return ret; | 1448 | return ret; |
1360 | } | 1449 | } |
@@ -1364,8 +1453,10 @@ err_btrfk: | |||
1364 | */ | 1453 | */ |
1365 | static void asus_input_notify(struct asus_laptop *asus, int event) | 1454 | static void asus_input_notify(struct asus_laptop *asus, int event) |
1366 | { | 1455 | { |
1367 | if (asus->inputdev) | 1456 | if (!asus->inputdev) |
1368 | sparse_keymap_report_event(asus->inputdev, event, 1, true); | 1457 | return ; |
1458 | if (!sparse_keymap_report_event(asus->inputdev, event, 1, true)) | ||
1459 | pr_info("Unknown key %x pressed\n", event); | ||
1369 | } | 1460 | } |
1370 | 1461 | ||
1371 | static int asus_input_init(struct asus_laptop *asus) | 1462 | static int asus_input_init(struct asus_laptop *asus) |
@@ -1375,7 +1466,7 @@ static int asus_input_init(struct asus_laptop *asus) | |||
1375 | 1466 | ||
1376 | input = input_allocate_device(); | 1467 | input = input_allocate_device(); |
1377 | if (!input) { | 1468 | if (!input) { |
1378 | pr_info("Unable to allocate input device\n"); | 1469 | pr_warn("Unable to allocate input device\n"); |
1379 | return -ENOMEM; | 1470 | return -ENOMEM; |
1380 | } | 1471 | } |
1381 | input->name = "Asus Laptop extra buttons"; | 1472 | input->name = "Asus Laptop extra buttons"; |
@@ -1390,7 +1481,7 @@ static int asus_input_init(struct asus_laptop *asus) | |||
1390 | } | 1481 | } |
1391 | error = input_register_device(input); | 1482 | error = input_register_device(input); |
1392 | if (error) { | 1483 | if (error) { |
1393 | pr_info("Unable to register input device\n"); | 1484 | pr_warn("Unable to register input device\n"); |
1394 | goto err_free_keymap; | 1485 | goto err_free_keymap; |
1395 | } | 1486 | } |
1396 | 1487 | ||
@@ -1688,7 +1779,16 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) | |||
1688 | if (result) | 1779 | if (result) |
1689 | return result; | 1780 | return result; |
1690 | 1781 | ||
1691 | /* WLED and BLED are on by default */ | 1782 | if (!strcmp(bled_type, "led")) |
1783 | asus->bled_type = TYPE_LED; | ||
1784 | else if (!strcmp(bled_type, "rfkill")) | ||
1785 | asus->bled_type = TYPE_RFKILL; | ||
1786 | |||
1787 | if (!strcmp(wled_type, "led")) | ||
1788 | asus->wled_type = TYPE_LED; | ||
1789 | else if (!strcmp(wled_type, "rfkill")) | ||
1790 | asus->wled_type = TYPE_RFKILL; | ||
1791 | |||
1692 | if (bluetooth_status >= 0) | 1792 | if (bluetooth_status >= 0) |
1693 | asus_bluetooth_set(asus, !!bluetooth_status); | 1793 | asus_bluetooth_set(asus, !!bluetooth_status); |
1694 | 1794 | ||
@@ -1786,7 +1886,7 @@ static int __devinit asus_acpi_add(struct acpi_device *device) | |||
1786 | goto fail_led; | 1886 | goto fail_led; |
1787 | 1887 | ||
1788 | result = asus_rfkill_init(asus); | 1888 | result = asus_rfkill_init(asus); |
1789 | if (result) | 1889 | if (result && result != -ENODEV) |
1790 | goto fail_rfkill; | 1890 | goto fail_rfkill; |
1791 | 1891 | ||
1792 | result = pega_accel_init(asus); | 1892 | result = pega_accel_init(asus); |
@@ -1828,7 +1928,6 @@ static int asus_acpi_remove(struct acpi_device *device, int type) | |||
1828 | asus_led_exit(asus); | 1928 | asus_led_exit(asus); |
1829 | asus_input_exit(asus); | 1929 | asus_input_exit(asus); |
1830 | pega_accel_exit(asus); | 1930 | pega_accel_exit(asus); |
1831 | pega_rfkill_exit(asus); | ||
1832 | asus_platform_exit(asus); | 1931 | asus_platform_exit(asus); |
1833 | 1932 | ||
1834 | kfree(asus->name); | 1933 | kfree(asus->name); |
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b0859d4183e8..99a30b513137 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/init.h> | 25 | #include <linux/init.h> |
26 | #include <linux/input.h> | 26 | #include <linux/input.h> |
27 | #include <linux/input/sparse-keymap.h> | 27 | #include <linux/input/sparse-keymap.h> |
28 | #include <linux/fb.h> | ||
28 | 29 | ||
29 | #include "asus-wmi.h" | 30 | #include "asus-wmi.h" |
30 | 31 | ||
@@ -51,9 +52,14 @@ static uint wapf; | |||
51 | module_param(wapf, uint, 0444); | 52 | module_param(wapf, uint, 0444); |
52 | MODULE_PARM_DESC(wapf, "WAPF value"); | 53 | MODULE_PARM_DESC(wapf, "WAPF value"); |
53 | 54 | ||
55 | static struct quirk_entry quirk_asus_unknown = { | ||
56 | }; | ||
57 | |||
54 | static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) | 58 | static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) |
55 | { | 59 | { |
56 | driver->wapf = wapf; | 60 | driver->quirks = &quirk_asus_unknown; |
61 | driver->quirks->wapf = wapf; | ||
62 | driver->panel_power = FB_BLANK_UNBLANK; | ||
57 | } | 63 | } |
58 | 64 | ||
59 | static const struct key_entry asus_nb_wmi_keymap[] = { | 65 | static const struct key_entry asus_nb_wmi_keymap[] = { |
@@ -70,6 +76,8 @@ static const struct key_entry asus_nb_wmi_keymap[] = { | |||
70 | { KE_KEY, 0x50, { KEY_EMAIL } }, | 76 | { KE_KEY, 0x50, { KEY_EMAIL } }, |
71 | { KE_KEY, 0x51, { KEY_WWW } }, | 77 | { KE_KEY, 0x51, { KEY_WWW } }, |
72 | { KE_KEY, 0x55, { KEY_CALC } }, | 78 | { KE_KEY, 0x55, { KEY_CALC } }, |
79 | { KE_IGNORE, 0x57, }, /* Battery mode */ | ||
80 | { KE_IGNORE, 0x58, }, /* AC mode */ | ||
73 | { KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */ | 81 | { KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */ |
74 | { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */ | 82 | { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */ |
75 | { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */ | 83 | { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */ |
@@ -99,7 +107,7 @@ static struct asus_wmi_driver asus_nb_wmi_driver = { | |||
99 | .keymap = asus_nb_wmi_keymap, | 107 | .keymap = asus_nb_wmi_keymap, |
100 | .input_name = "Asus WMI hotkeys", | 108 | .input_name = "Asus WMI hotkeys", |
101 | .input_phys = ASUS_NB_WMI_FILE "/input0", | 109 | .input_phys = ASUS_NB_WMI_FILE "/input0", |
102 | .quirks = asus_nb_wmi_quirks, | 110 | .detect_quirks = asus_nb_wmi_quirks, |
103 | }; | 111 | }; |
104 | 112 | ||
105 | 113 | ||
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 72d731c21d45..77aadde5281c 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c | |||
@@ -411,7 +411,7 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) | |||
411 | 411 | ||
412 | if (retval >= 0) { | 412 | if (retval >= 0) { |
413 | if (level) | 413 | if (level) |
414 | *level = retval & 0x80 ? retval & 0x7F : 0; | 414 | *level = retval & 0x7F; |
415 | if (env) | 415 | if (env) |
416 | *env = (retval >> 8) & 0x7F; | 416 | *env = (retval >> 8) & 0x7F; |
417 | retval = 0; | 417 | retval = 0; |
@@ -571,7 +571,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) | |||
571 | } else { | 571 | } else { |
572 | dev = pci_get_slot(bus, 0); | 572 | dev = pci_get_slot(bus, 0); |
573 | if (dev) { | 573 | if (dev) { |
574 | pci_remove_bus_device(dev); | 574 | pci_stop_and_remove_bus_device(dev); |
575 | pci_dev_put(dev); | 575 | pci_dev_put(dev); |
576 | } | 576 | } |
577 | } | 577 | } |
@@ -784,7 +784,8 @@ static int asus_new_rfkill(struct asus_wmi *asus, | |||
784 | arfkill->dev_id = dev_id; | 784 | arfkill->dev_id = dev_id; |
785 | arfkill->asus = asus; | 785 | arfkill->asus = asus; |
786 | 786 | ||
787 | if (dev_id == ASUS_WMI_DEVID_WLAN && asus->driver->hotplug_wireless) | 787 | if (dev_id == ASUS_WMI_DEVID_WLAN && |
788 | asus->driver->quirks->hotplug_wireless) | ||
788 | *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, | 789 | *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, |
789 | &asus_rfkill_wlan_ops, arfkill); | 790 | &asus_rfkill_wlan_ops, arfkill); |
790 | else | 791 | else |
@@ -895,7 +896,7 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus) | |||
895 | if (result && result != -ENODEV) | 896 | if (result && result != -ENODEV) |
896 | goto exit; | 897 | goto exit; |
897 | 898 | ||
898 | if (!asus->driver->hotplug_wireless) | 899 | if (!asus->driver->quirks->hotplug_wireless) |
899 | goto exit; | 900 | goto exit; |
900 | 901 | ||
901 | result = asus_setup_pci_hotplug(asus); | 902 | result = asus_setup_pci_hotplug(asus); |
@@ -1075,7 +1076,12 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) | |||
1075 | */ | 1076 | */ |
1076 | static int read_backlight_power(struct asus_wmi *asus) | 1077 | static int read_backlight_power(struct asus_wmi *asus) |
1077 | { | 1078 | { |
1078 | int ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BACKLIGHT); | 1079 | int ret; |
1080 | if (asus->driver->quirks->store_backlight_power) | ||
1081 | ret = !asus->driver->panel_power; | ||
1082 | else | ||
1083 | ret = asus_wmi_get_devstate_simple(asus, | ||
1084 | ASUS_WMI_DEVID_BACKLIGHT); | ||
1079 | 1085 | ||
1080 | if (ret < 0) | 1086 | if (ret < 0) |
1081 | return ret; | 1087 | return ret; |
@@ -1116,26 +1122,51 @@ static int read_brightness(struct backlight_device *bd) | |||
1116 | return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; | 1122 | return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; |
1117 | } | 1123 | } |
1118 | 1124 | ||
1119 | static int update_bl_status(struct backlight_device *bd) | 1125 | static u32 get_scalar_command(struct backlight_device *bd) |
1120 | { | 1126 | { |
1121 | struct asus_wmi *asus = bl_get_data(bd); | 1127 | struct asus_wmi *asus = bl_get_data(bd); |
1122 | u32 ctrl_param; | 1128 | u32 ctrl_param = 0; |
1123 | int power, err; | ||
1124 | 1129 | ||
1125 | ctrl_param = bd->props.brightness; | 1130 | if ((asus->driver->brightness < bd->props.brightness) || |
1131 | bd->props.brightness == bd->props.max_brightness) | ||
1132 | ctrl_param = 0x00008001; | ||
1133 | else if ((asus->driver->brightness > bd->props.brightness) || | ||
1134 | bd->props.brightness == 0) | ||
1135 | ctrl_param = 0x00008000; | ||
1126 | 1136 | ||
1127 | err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, | 1137 | asus->driver->brightness = bd->props.brightness; |
1128 | ctrl_param, NULL); | ||
1129 | 1138 | ||
1130 | if (err < 0) | 1139 | return ctrl_param; |
1131 | return err; | 1140 | } |
1141 | |||
1142 | static int update_bl_status(struct backlight_device *bd) | ||
1143 | { | ||
1144 | struct asus_wmi *asus = bl_get_data(bd); | ||
1145 | u32 ctrl_param; | ||
1146 | int power, err = 0; | ||
1132 | 1147 | ||
1133 | power = read_backlight_power(asus); | 1148 | power = read_backlight_power(asus); |
1134 | if (power != -ENODEV && bd->props.power != power) { | 1149 | if (power != -ENODEV && bd->props.power != power) { |
1135 | ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); | 1150 | ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); |
1136 | err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, | 1151 | err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, |
1137 | ctrl_param, NULL); | 1152 | ctrl_param, NULL); |
1153 | if (asus->driver->quirks->store_backlight_power) | ||
1154 | asus->driver->panel_power = bd->props.power; | ||
1155 | |||
1156 | /* When using scalar brightness, updating the brightness | ||
1157 | * will mess with the backlight power */ | ||
1158 | if (asus->driver->quirks->scalar_panel_brightness) | ||
1159 | return err; | ||
1138 | } | 1160 | } |
1161 | |||
1162 | if (asus->driver->quirks->scalar_panel_brightness) | ||
1163 | ctrl_param = get_scalar_command(bd); | ||
1164 | else | ||
1165 | ctrl_param = bd->props.brightness; | ||
1166 | |||
1167 | err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, | ||
1168 | ctrl_param, NULL); | ||
1169 | |||
1139 | return err; | 1170 | return err; |
1140 | } | 1171 | } |
1141 | 1172 | ||
@@ -1196,10 +1227,15 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) | |||
1196 | 1227 | ||
1197 | asus->backlight_device = bd; | 1228 | asus->backlight_device = bd; |
1198 | 1229 | ||
1230 | if (asus->driver->quirks->store_backlight_power) | ||
1231 | asus->driver->panel_power = power; | ||
1232 | |||
1199 | bd->props.brightness = read_brightness(bd); | 1233 | bd->props.brightness = read_brightness(bd); |
1200 | bd->props.power = power; | 1234 | bd->props.power = power; |
1201 | backlight_update_status(bd); | 1235 | backlight_update_status(bd); |
1202 | 1236 | ||
1237 | asus->driver->brightness = bd->props.brightness; | ||
1238 | |||
1203 | return 0; | 1239 | return 0; |
1204 | } | 1240 | } |
1205 | 1241 | ||
@@ -1441,9 +1477,9 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) | |||
1441 | 1477 | ||
1442 | /* CWAP allow to define the behavior of the Fn+F2 key, | 1478 | /* CWAP allow to define the behavior of the Fn+F2 key, |
1443 | * this method doesn't seems to be present on Eee PCs */ | 1479 | * this method doesn't seems to be present on Eee PCs */ |
1444 | if (asus->driver->wapf >= 0) | 1480 | if (asus->driver->quirks->wapf >= 0) |
1445 | asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP, | 1481 | asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP, |
1446 | asus->driver->wapf, NULL); | 1482 | asus->driver->quirks->wapf, NULL); |
1447 | 1483 | ||
1448 | return asus_wmi_sysfs_init(asus->platform_device); | 1484 | return asus_wmi_sysfs_init(asus->platform_device); |
1449 | } | 1485 | } |
@@ -1622,8 +1658,8 @@ static int asus_wmi_add(struct platform_device *pdev) | |||
1622 | wdrv->platform_device = pdev; | 1658 | wdrv->platform_device = pdev; |
1623 | platform_set_drvdata(asus->platform_device, asus); | 1659 | platform_set_drvdata(asus->platform_device, asus); |
1624 | 1660 | ||
1625 | if (wdrv->quirks) | 1661 | if (wdrv->detect_quirks) |
1626 | wdrv->quirks(asus->driver); | 1662 | wdrv->detect_quirks(asus->driver); |
1627 | 1663 | ||
1628 | err = asus_wmi_platform_init(asus); | 1664 | err = asus_wmi_platform_init(asus); |
1629 | if (err) | 1665 | if (err) |
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 8147c10161cc..d43b66742004 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h | |||
@@ -35,9 +35,16 @@ struct module; | |||
35 | struct key_entry; | 35 | struct key_entry; |
36 | struct asus_wmi; | 36 | struct asus_wmi; |
37 | 37 | ||
38 | struct quirk_entry { | ||
39 | bool hotplug_wireless; | ||
40 | bool scalar_panel_brightness; | ||
41 | bool store_backlight_power; | ||
42 | int wapf; | ||
43 | }; | ||
44 | |||
38 | struct asus_wmi_driver { | 45 | struct asus_wmi_driver { |
39 | bool hotplug_wireless; | 46 | int brightness; |
40 | int wapf; | 47 | int panel_power; |
41 | 48 | ||
42 | const char *name; | 49 | const char *name; |
43 | struct module *owner; | 50 | struct module *owner; |
@@ -47,13 +54,14 @@ struct asus_wmi_driver { | |||
47 | const struct key_entry *keymap; | 54 | const struct key_entry *keymap; |
48 | const char *input_name; | 55 | const char *input_name; |
49 | const char *input_phys; | 56 | const char *input_phys; |
57 | struct quirk_entry *quirks; | ||
50 | /* Returns new code, value, and autorelease values in arguments. | 58 | /* Returns new code, value, and autorelease values in arguments. |
51 | * Return ASUS_WMI_KEY_IGNORE in code if event should be ignored. */ | 59 | * Return ASUS_WMI_KEY_IGNORE in code if event should be ignored. */ |
52 | void (*key_filter) (struct asus_wmi_driver *driver, int *code, | 60 | void (*key_filter) (struct asus_wmi_driver *driver, int *code, |
53 | unsigned int *value, bool *autorelease); | 61 | unsigned int *value, bool *autorelease); |
54 | 62 | ||
55 | int (*probe) (struct platform_device *device); | 63 | int (*probe) (struct platform_device *device); |
56 | void (*quirks) (struct asus_wmi_driver *driver); | 64 | void (*detect_quirks) (struct asus_wmi_driver *driver); |
57 | 65 | ||
58 | struct platform_driver platform_driver; | 66 | struct platform_driver platform_driver; |
59 | struct platform_device *platform_device; | 67 | struct platform_device *platform_device; |
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c deleted file mode 100644 index 6f966d6c062b..000000000000 --- a/drivers/platform/x86/asus_acpi.c +++ /dev/null | |||
@@ -1,1513 +0,0 @@ | |||
1 | /* | ||
2 | * asus_acpi.c - Asus Laptop ACPI Extras | ||
3 | * | ||
4 | * | ||
5 | * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * | ||
22 | * The development page for this driver is located at | ||
23 | * http://sourceforge.net/projects/acpi4asus/ | ||
24 | * | ||
25 | * Credits: | ||
26 | * Pontus Fuchs - Helper functions, cleanup | ||
27 | * Johann Wiesner - Small compile fixes | ||
28 | * John Belmonte - ACPI code for Toshiba laptop was a good starting point. | ||
29 | * �ic Burghard - LED display support for W1N | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
34 | |||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/module.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/types.h> | ||
40 | #include <linux/proc_fs.h> | ||
41 | #include <linux/seq_file.h> | ||
42 | #include <linux/backlight.h> | ||
43 | #include <acpi/acpi_drivers.h> | ||
44 | #include <acpi/acpi_bus.h> | ||
45 | #include <asm/uaccess.h> | ||
46 | |||
47 | #define ASUS_ACPI_VERSION "0.30" | ||
48 | |||
49 | #define PROC_ASUS "asus" /* The directory */ | ||
50 | #define PROC_MLED "mled" | ||
51 | #define PROC_WLED "wled" | ||
52 | #define PROC_TLED "tled" | ||
53 | #define PROC_BT "bluetooth" | ||
54 | #define PROC_LEDD "ledd" | ||
55 | #define PROC_INFO "info" | ||
56 | #define PROC_LCD "lcd" | ||
57 | #define PROC_BRN "brn" | ||
58 | #define PROC_DISP "disp" | ||
59 | |||
60 | #define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver" | ||
61 | #define ACPI_HOTK_CLASS "hotkey" | ||
62 | #define ACPI_HOTK_DEVICE_NAME "Hotkey" | ||
63 | |||
64 | /* | ||
65 | * Some events we use, same for all Asus | ||
66 | */ | ||
67 | #define BR_UP 0x10 | ||
68 | #define BR_DOWN 0x20 | ||
69 | |||
70 | /* | ||
71 | * Flags for hotk status | ||
72 | */ | ||
73 | #define MLED_ON 0x01 /* Mail LED */ | ||
74 | #define WLED_ON 0x02 /* Wireless LED */ | ||
75 | #define TLED_ON 0x04 /* Touchpad LED */ | ||
76 | #define BT_ON 0x08 /* Internal Bluetooth */ | ||
77 | |||
78 | MODULE_AUTHOR("Julien Lerouge, Karol Kozimor"); | ||
79 | MODULE_DESCRIPTION(ACPI_HOTK_NAME); | ||
80 | MODULE_LICENSE("GPL"); | ||
81 | |||
82 | static uid_t asus_uid; | ||
83 | static gid_t asus_gid; | ||
84 | module_param(asus_uid, uint, 0); | ||
85 | MODULE_PARM_DESC(asus_uid, "UID for entries in /proc/acpi/asus"); | ||
86 | module_param(asus_gid, uint, 0); | ||
87 | MODULE_PARM_DESC(asus_gid, "GID for entries in /proc/acpi/asus"); | ||
88 | |||
89 | /* For each model, all features implemented, | ||
90 | * those marked with R are relative to HOTK, A for absolute */ | ||
91 | struct model_data { | ||
92 | char *name; /* name of the laptop________________A */ | ||
93 | char *mt_mled; /* method to handle mled_____________R */ | ||
94 | char *mled_status; /* node to handle mled reading_______A */ | ||
95 | char *mt_wled; /* method to handle wled_____________R */ | ||
96 | char *wled_status; /* node to handle wled reading_______A */ | ||
97 | char *mt_tled; /* method to handle tled_____________R */ | ||
98 | char *tled_status; /* node to handle tled reading_______A */ | ||
99 | char *mt_ledd; /* method to handle LED display______R */ | ||
100 | char *mt_bt_switch; /* method to switch Bluetooth on/off_R */ | ||
101 | char *bt_status; /* no model currently supports this__? */ | ||
102 | char *mt_lcd_switch; /* method to turn LCD on/off_________A */ | ||
103 | char *lcd_status; /* node to read LCD panel state______A */ | ||
104 | char *brightness_up; /* method to set brightness up_______A */ | ||
105 | char *brightness_down; /* method to set brightness down ____A */ | ||
106 | char *brightness_set; /* method to set absolute brightness_R */ | ||
107 | char *brightness_get; /* method to get absolute brightness_R */ | ||
108 | char *brightness_status;/* node to get brightness____________A */ | ||
109 | char *display_set; /* method to set video output________R */ | ||
110 | char *display_get; /* method to get video output________R */ | ||
111 | }; | ||
112 | |||
113 | /* | ||
114 | * This is the main structure, we can use it to store anything interesting | ||
115 | * about the hotk device | ||
116 | */ | ||
117 | struct asus_hotk { | ||
118 | struct acpi_device *device; /* the device we are in */ | ||
119 | acpi_handle handle; /* the handle of the hotk device */ | ||
120 | char status; /* status of the hotk, for LEDs */ | ||
121 | u32 ledd_status; /* status of the LED display */ | ||
122 | struct model_data *methods; /* methods available on the laptop */ | ||
123 | u8 brightness; /* brightness level */ | ||
124 | enum { | ||
125 | A1x = 0, /* A1340D, A1300F */ | ||
126 | A2x, /* A2500H */ | ||
127 | A4G, /* A4700G */ | ||
128 | D1x, /* D1 */ | ||
129 | L2D, /* L2000D */ | ||
130 | L3C, /* L3800C */ | ||
131 | L3D, /* L3400D */ | ||
132 | L3H, /* L3H, L2000E, L5D */ | ||
133 | L4R, /* L4500R */ | ||
134 | L5x, /* L5800C */ | ||
135 | L8L, /* L8400L */ | ||
136 | M1A, /* M1300A */ | ||
137 | M2E, /* M2400E, L4400L */ | ||
138 | M6N, /* M6800N, W3400N */ | ||
139 | M6R, /* M6700R, A3000G */ | ||
140 | P30, /* Samsung P30 */ | ||
141 | S1x, /* S1300A, but also L1400B and M2400A (L84F) */ | ||
142 | S2x, /* S200 (J1 reported), Victor MP-XP7210 */ | ||
143 | W1N, /* W1000N */ | ||
144 | W5A, /* W5A */ | ||
145 | W3V, /* W3030V */ | ||
146 | xxN, /* M2400N, M3700N, M5200N, M6800N, | ||
147 | S1300N, S5200N*/ | ||
148 | A4S, /* Z81sp */ | ||
149 | F3Sa, /* (Centrino) */ | ||
150 | R1F, | ||
151 | END_MODEL | ||
152 | } model; /* Models currently supported */ | ||
153 | u16 event_count[128]; /* Count for each event TODO make this better */ | ||
154 | }; | ||
155 | |||
156 | /* Here we go */ | ||
157 | #define A1x_PREFIX "\\_SB.PCI0.ISA.EC0." | ||
158 | #define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0." | ||
159 | #define M1A_PREFIX "\\_SB.PCI0.PX40.EC0." | ||
160 | #define P30_PREFIX "\\_SB.PCI0.LPCB.EC0." | ||
161 | #define S1x_PREFIX "\\_SB.PCI0.PX40." | ||
162 | #define S2x_PREFIX A1x_PREFIX | ||
163 | #define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0." | ||
164 | |||
165 | static struct model_data model_conf[END_MODEL] = { | ||
166 | /* | ||
167 | * TODO I have seen a SWBX and AIBX method on some models, like L1400B, | ||
168 | * it seems to be a kind of switch, but what for ? | ||
169 | */ | ||
170 | |||
171 | { | ||
172 | .name = "A1x", | ||
173 | .mt_mled = "MLED", | ||
174 | .mled_status = "\\MAIL", | ||
175 | .mt_lcd_switch = A1x_PREFIX "_Q10", | ||
176 | .lcd_status = "\\BKLI", | ||
177 | .brightness_up = A1x_PREFIX "_Q0E", | ||
178 | .brightness_down = A1x_PREFIX "_Q0F"}, | ||
179 | |||
180 | { | ||
181 | .name = "A2x", | ||
182 | .mt_mled = "MLED", | ||
183 | .mt_wled = "WLED", | ||
184 | .wled_status = "\\SG66", | ||
185 | .mt_lcd_switch = "\\Q10", | ||
186 | .lcd_status = "\\BAOF", | ||
187 | .brightness_set = "SPLV", | ||
188 | .brightness_get = "GPLV", | ||
189 | .display_set = "SDSP", | ||
190 | .display_get = "\\INFB"}, | ||
191 | |||
192 | { | ||
193 | .name = "A4G", | ||
194 | .mt_mled = "MLED", | ||
195 | /* WLED present, but not controlled by ACPI */ | ||
196 | .mt_lcd_switch = xxN_PREFIX "_Q10", | ||
197 | .brightness_set = "SPLV", | ||
198 | .brightness_get = "GPLV", | ||
199 | .display_set = "SDSP", | ||
200 | .display_get = "\\ADVG"}, | ||
201 | |||
202 | { | ||
203 | .name = "D1x", | ||
204 | .mt_mled = "MLED", | ||
205 | .mt_lcd_switch = "\\Q0D", | ||
206 | .lcd_status = "\\GP11", | ||
207 | .brightness_up = "\\Q0C", | ||
208 | .brightness_down = "\\Q0B", | ||
209 | .brightness_status = "\\BLVL", | ||
210 | .display_set = "SDSP", | ||
211 | .display_get = "\\INFB"}, | ||
212 | |||
213 | { | ||
214 | .name = "L2D", | ||
215 | .mt_mled = "MLED", | ||
216 | .mled_status = "\\SGP6", | ||
217 | .mt_wled = "WLED", | ||
218 | .wled_status = "\\RCP3", | ||
219 | .mt_lcd_switch = "\\Q10", | ||
220 | .lcd_status = "\\SGP0", | ||
221 | .brightness_up = "\\Q0E", | ||
222 | .brightness_down = "\\Q0F", | ||
223 | .display_set = "SDSP", | ||
224 | .display_get = "\\INFB"}, | ||
225 | |||
226 | { | ||
227 | .name = "L3C", | ||
228 | .mt_mled = "MLED", | ||
229 | .mt_wled = "WLED", | ||
230 | .mt_lcd_switch = L3C_PREFIX "_Q10", | ||
231 | .lcd_status = "\\GL32", | ||
232 | .brightness_set = "SPLV", | ||
233 | .brightness_get = "GPLV", | ||
234 | .display_set = "SDSP", | ||
235 | .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"}, | ||
236 | |||
237 | { | ||
238 | .name = "L3D", | ||
239 | .mt_mled = "MLED", | ||
240 | .mled_status = "\\MALD", | ||
241 | .mt_wled = "WLED", | ||
242 | .mt_lcd_switch = "\\Q10", | ||
243 | .lcd_status = "\\BKLG", | ||
244 | .brightness_set = "SPLV", | ||
245 | .brightness_get = "GPLV", | ||
246 | .display_set = "SDSP", | ||
247 | .display_get = "\\INFB"}, | ||
248 | |||
249 | { | ||
250 | .name = "L3H", | ||
251 | .mt_mled = "MLED", | ||
252 | .mt_wled = "WLED", | ||
253 | .mt_lcd_switch = "EHK", | ||
254 | .lcd_status = "\\_SB.PCI0.PM.PBC", | ||
255 | .brightness_set = "SPLV", | ||
256 | .brightness_get = "GPLV", | ||
257 | .display_set = "SDSP", | ||
258 | .display_get = "\\INFB"}, | ||
259 | |||
260 | { | ||
261 | .name = "L4R", | ||
262 | .mt_mled = "MLED", | ||
263 | .mt_wled = "WLED", | ||
264 | .wled_status = "\\_SB.PCI0.SBRG.SG13", | ||
265 | .mt_lcd_switch = xxN_PREFIX "_Q10", | ||
266 | .lcd_status = "\\_SB.PCI0.SBSM.SEO4", | ||
267 | .brightness_set = "SPLV", | ||
268 | .brightness_get = "GPLV", | ||
269 | .display_set = "SDSP", | ||
270 | .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"}, | ||
271 | |||
272 | { | ||
273 | .name = "L5x", | ||
274 | .mt_mled = "MLED", | ||
275 | /* WLED present, but not controlled by ACPI */ | ||
276 | .mt_tled = "TLED", | ||
277 | .mt_lcd_switch = "\\Q0D", | ||
278 | .lcd_status = "\\BAOF", | ||
279 | .brightness_set = "SPLV", | ||
280 | .brightness_get = "GPLV", | ||
281 | .display_set = "SDSP", | ||
282 | .display_get = "\\INFB"}, | ||
283 | |||
284 | { | ||
285 | .name = "L8L" | ||
286 | /* No features, but at least support the hotkeys */ | ||
287 | }, | ||
288 | |||
289 | { | ||
290 | .name = "M1A", | ||
291 | .mt_mled = "MLED", | ||
292 | .mt_lcd_switch = M1A_PREFIX "Q10", | ||
293 | .lcd_status = "\\PNOF", | ||
294 | .brightness_up = M1A_PREFIX "Q0E", | ||
295 | .brightness_down = M1A_PREFIX "Q0F", | ||
296 | .brightness_status = "\\BRIT", | ||
297 | .display_set = "SDSP", | ||
298 | .display_get = "\\INFB"}, | ||
299 | |||
300 | { | ||
301 | .name = "M2E", | ||
302 | .mt_mled = "MLED", | ||
303 | .mt_wled = "WLED", | ||
304 | .mt_lcd_switch = "\\Q10", | ||
305 | .lcd_status = "\\GP06", | ||
306 | .brightness_set = "SPLV", | ||
307 | .brightness_get = "GPLV", | ||
308 | .display_set = "SDSP", | ||
309 | .display_get = "\\INFB"}, | ||
310 | |||
311 | { | ||
312 | .name = "M6N", | ||
313 | .mt_mled = "MLED", | ||
314 | .mt_wled = "WLED", | ||
315 | .wled_status = "\\_SB.PCI0.SBRG.SG13", | ||
316 | .mt_lcd_switch = xxN_PREFIX "_Q10", | ||
317 | .lcd_status = "\\_SB.BKLT", | ||
318 | .brightness_set = "SPLV", | ||
319 | .brightness_get = "GPLV", | ||
320 | .display_set = "SDSP", | ||
321 | .display_get = "\\SSTE"}, | ||
322 | |||
323 | { | ||
324 | .name = "M6R", | ||
325 | .mt_mled = "MLED", | ||
326 | .mt_wled = "WLED", | ||
327 | .mt_lcd_switch = xxN_PREFIX "_Q10", | ||
328 | .lcd_status = "\\_SB.PCI0.SBSM.SEO4", | ||
329 | .brightness_set = "SPLV", | ||
330 | .brightness_get = "GPLV", | ||
331 | .display_set = "SDSP", | ||
332 | .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"}, | ||
333 | |||
334 | { | ||
335 | .name = "P30", | ||
336 | .mt_wled = "WLED", | ||
337 | .mt_lcd_switch = P30_PREFIX "_Q0E", | ||
338 | .lcd_status = "\\BKLT", | ||
339 | .brightness_up = P30_PREFIX "_Q68", | ||
340 | .brightness_down = P30_PREFIX "_Q69", | ||
341 | .brightness_get = "GPLV", | ||
342 | .display_set = "SDSP", | ||
343 | .display_get = "\\DNXT"}, | ||
344 | |||
345 | { | ||
346 | .name = "S1x", | ||
347 | .mt_mled = "MLED", | ||
348 | .mled_status = "\\EMLE", | ||
349 | .mt_wled = "WLED", | ||
350 | .mt_lcd_switch = S1x_PREFIX "Q10", | ||
351 | .lcd_status = "\\PNOF", | ||
352 | .brightness_set = "SPLV", | ||
353 | .brightness_get = "GPLV"}, | ||
354 | |||
355 | { | ||
356 | .name = "S2x", | ||
357 | .mt_mled = "MLED", | ||
358 | .mled_status = "\\MAIL", | ||
359 | .mt_lcd_switch = S2x_PREFIX "_Q10", | ||
360 | .lcd_status = "\\BKLI", | ||
361 | .brightness_up = S2x_PREFIX "_Q0B", | ||
362 | .brightness_down = S2x_PREFIX "_Q0A"}, | ||
363 | |||
364 | { | ||
365 | .name = "W1N", | ||
366 | .mt_mled = "MLED", | ||
367 | .mt_wled = "WLED", | ||
368 | .mt_ledd = "SLCM", | ||
369 | .mt_lcd_switch = xxN_PREFIX "_Q10", | ||
370 | .lcd_status = "\\BKLT", | ||
371 | .brightness_set = "SPLV", | ||
372 | .brightness_get = "GPLV", | ||
373 | .display_set = "SDSP", | ||
374 | .display_get = "\\ADVG"}, | ||
375 | |||
376 | { | ||
377 | .name = "W5A", | ||
378 | .mt_bt_switch = "BLED", | ||
379 | .mt_wled = "WLED", | ||
380 | .mt_lcd_switch = xxN_PREFIX "_Q10", | ||
381 | .brightness_set = "SPLV", | ||
382 | .brightness_get = "GPLV", | ||
383 | .display_set = "SDSP", | ||
384 | .display_get = "\\ADVG"}, | ||
385 | |||
386 | { | ||
387 | .name = "W3V", | ||
388 | .mt_mled = "MLED", | ||
389 | .mt_wled = "WLED", | ||
390 | .mt_lcd_switch = xxN_PREFIX "_Q10", | ||
391 | .lcd_status = "\\BKLT", | ||
392 | .brightness_set = "SPLV", | ||
393 | .brightness_get = "GPLV", | ||
394 | .display_set = "SDSP", | ||
395 | .display_get = "\\INFB"}, | ||
396 | |||
397 | { | ||
398 | .name = "xxN", | ||
399 | .mt_mled = "MLED", | ||
400 | /* WLED present, but not controlled by ACPI */ | ||
401 | .mt_lcd_switch = xxN_PREFIX "_Q10", | ||
402 | .lcd_status = "\\BKLT", | ||
403 | .brightness_set = "SPLV", | ||
404 | .brightness_get = "GPLV", | ||
405 | .display_set = "SDSP", | ||
406 | .display_get = "\\ADVG"}, | ||
407 | |||
408 | { | ||
409 | .name = "A4S", | ||
410 | .brightness_set = "SPLV", | ||
411 | .brightness_get = "GPLV", | ||
412 | .mt_bt_switch = "BLED", | ||
413 | .mt_wled = "WLED" | ||
414 | }, | ||
415 | |||
416 | { | ||
417 | .name = "F3Sa", | ||
418 | .mt_bt_switch = "BLED", | ||
419 | .mt_wled = "WLED", | ||
420 | .mt_mled = "MLED", | ||
421 | .brightness_get = "GPLV", | ||
422 | .brightness_set = "SPLV", | ||
423 | .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10", | ||
424 | .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN", | ||
425 | .display_get = "\\ADVG", | ||
426 | .display_set = "SDSP", | ||
427 | }, | ||
428 | { | ||
429 | .name = "R1F", | ||
430 | .mt_bt_switch = "BLED", | ||
431 | .mt_mled = "MLED", | ||
432 | .mt_wled = "WLED", | ||
433 | .mt_lcd_switch = "\\Q10", | ||
434 | .lcd_status = "\\GP06", | ||
435 | .brightness_set = "SPLV", | ||
436 | .brightness_get = "GPLV", | ||
437 | .display_set = "SDSP", | ||
438 | .display_get = "\\INFB" | ||
439 | } | ||
440 | }; | ||
441 | |||
442 | /* procdir we use */ | ||
443 | static struct proc_dir_entry *asus_proc_dir; | ||
444 | |||
445 | static struct backlight_device *asus_backlight_device; | ||
446 | |||
447 | /* | ||
448 | * This header is made available to allow proper configuration given model, | ||
449 | * revision number , ... this info cannot go in struct asus_hotk because it is | ||
450 | * available before the hotk | ||
451 | */ | ||
452 | static struct acpi_table_header *asus_info; | ||
453 | |||
454 | /* The actual device the driver binds to */ | ||
455 | static struct asus_hotk *hotk; | ||
456 | |||
457 | /* | ||
458 | * The hotkey driver and autoloading declaration | ||
459 | */ | ||
460 | static int asus_hotk_add(struct acpi_device *device); | ||
461 | static int asus_hotk_remove(struct acpi_device *device, int type); | ||
462 | static void asus_hotk_notify(struct acpi_device *device, u32 event); | ||
463 | |||
464 | static const struct acpi_device_id asus_device_ids[] = { | ||
465 | {"ATK0100", 0}, | ||
466 | {"", 0}, | ||
467 | }; | ||
468 | MODULE_DEVICE_TABLE(acpi, asus_device_ids); | ||
469 | |||
470 | static struct acpi_driver asus_hotk_driver = { | ||
471 | .name = "asus_acpi", | ||
472 | .class = ACPI_HOTK_CLASS, | ||
473 | .owner = THIS_MODULE, | ||
474 | .ids = asus_device_ids, | ||
475 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
476 | .ops = { | ||
477 | .add = asus_hotk_add, | ||
478 | .remove = asus_hotk_remove, | ||
479 | .notify = asus_hotk_notify, | ||
480 | }, | ||
481 | }; | ||
482 | |||
483 | /* | ||
484 | * This function evaluates an ACPI method, given an int as parameter, the | ||
485 | * method is searched within the scope of the handle, can be NULL. The output | ||
486 | * of the method is written is output, which can also be NULL | ||
487 | * | ||
488 | * returns 1 if write is successful, 0 else. | ||
489 | */ | ||
490 | static int write_acpi_int(acpi_handle handle, const char *method, int val, | ||
491 | struct acpi_buffer *output) | ||
492 | { | ||
493 | struct acpi_object_list params; /* list of input parameters (int) */ | ||
494 | union acpi_object in_obj; /* the only param we use */ | ||
495 | acpi_status status; | ||
496 | |||
497 | params.count = 1; | ||
498 | params.pointer = &in_obj; | ||
499 | in_obj.type = ACPI_TYPE_INTEGER; | ||
500 | in_obj.integer.value = val; | ||
501 | |||
502 | status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); | ||
503 | return (status == AE_OK); | ||
504 | } | ||
505 | |||
506 | static int read_acpi_int(acpi_handle handle, const char *method, int *val) | ||
507 | { | ||
508 | struct acpi_buffer output; | ||
509 | union acpi_object out_obj; | ||
510 | acpi_status status; | ||
511 | |||
512 | output.length = sizeof(out_obj); | ||
513 | output.pointer = &out_obj; | ||
514 | |||
515 | status = acpi_evaluate_object(handle, (char *)method, NULL, &output); | ||
516 | *val = out_obj.integer.value; | ||
517 | return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); | ||
518 | } | ||
519 | |||
520 | static int asus_info_proc_show(struct seq_file *m, void *v) | ||
521 | { | ||
522 | int temp; | ||
523 | |||
524 | seq_printf(m, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n"); | ||
525 | seq_printf(m, "Model reference : %s\n", hotk->methods->name); | ||
526 | /* | ||
527 | * The SFUN method probably allows the original driver to get the list | ||
528 | * of features supported by a given model. For now, 0x0100 or 0x0800 | ||
529 | * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. | ||
530 | * The significance of others is yet to be found. | ||
531 | */ | ||
532 | if (read_acpi_int(hotk->handle, "SFUN", &temp)) | ||
533 | seq_printf(m, "SFUN value : 0x%04x\n", temp); | ||
534 | /* | ||
535 | * Another value for userspace: the ASYM method returns 0x02 for | ||
536 | * battery low and 0x04 for battery critical, its readings tend to be | ||
537 | * more accurate than those provided by _BST. | ||
538 | * Note: since not all the laptops provide this method, errors are | ||
539 | * silently ignored. | ||
540 | */ | ||
541 | if (read_acpi_int(hotk->handle, "ASYM", &temp)) | ||
542 | seq_printf(m, "ASYM value : 0x%04x\n", temp); | ||
543 | if (asus_info) { | ||
544 | seq_printf(m, "DSDT length : %d\n", asus_info->length); | ||
545 | seq_printf(m, "DSDT checksum : %d\n", asus_info->checksum); | ||
546 | seq_printf(m, "DSDT revision : %d\n", asus_info->revision); | ||
547 | seq_printf(m, "OEM id : %.*s\n", ACPI_OEM_ID_SIZE, asus_info->oem_id); | ||
548 | seq_printf(m, "OEM table id : %.*s\n", ACPI_OEM_TABLE_ID_SIZE, asus_info->oem_table_id); | ||
549 | seq_printf(m, "OEM revision : 0x%x\n", asus_info->oem_revision); | ||
550 | seq_printf(m, "ASL comp vendor id : %.*s\n", ACPI_NAME_SIZE, asus_info->asl_compiler_id); | ||
551 | seq_printf(m, "ASL comp revision : 0x%x\n", asus_info->asl_compiler_revision); | ||
552 | } | ||
553 | |||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static int asus_info_proc_open(struct inode *inode, struct file *file) | ||
558 | { | ||
559 | return single_open(file, asus_info_proc_show, NULL); | ||
560 | } | ||
561 | |||
562 | static const struct file_operations asus_info_proc_fops = { | ||
563 | .owner = THIS_MODULE, | ||
564 | .open = asus_info_proc_open, | ||
565 | .read = seq_read, | ||
566 | .llseek = seq_lseek, | ||
567 | .release = single_release, | ||
568 | }; | ||
569 | |||
570 | /* | ||
571 | * /proc handlers | ||
572 | * We write our info in page, we begin at offset off and cannot write more | ||
573 | * than count bytes. We set eof to 1 if we handle those 2 values. We return the | ||
574 | * number of bytes written in page | ||
575 | */ | ||
576 | |||
577 | /* Generic LED functions */ | ||
578 | static int read_led(const char *ledname, int ledmask) | ||
579 | { | ||
580 | if (ledname) { | ||
581 | int led_status; | ||
582 | |||
583 | if (read_acpi_int(NULL, ledname, &led_status)) | ||
584 | return led_status; | ||
585 | else | ||
586 | pr_warn("Error reading LED status\n"); | ||
587 | } | ||
588 | return (hotk->status & ledmask) ? 1 : 0; | ||
589 | } | ||
590 | |||
591 | static int parse_arg(const char __user *buf, unsigned long count, int *val) | ||
592 | { | ||
593 | char s[32]; | ||
594 | if (!count) | ||
595 | return 0; | ||
596 | if (count > 31) | ||
597 | return -EINVAL; | ||
598 | if (copy_from_user(s, buf, count)) | ||
599 | return -EFAULT; | ||
600 | s[count] = 0; | ||
601 | if (sscanf(s, "%i", val) != 1) | ||
602 | return -EINVAL; | ||
603 | return count; | ||
604 | } | ||
605 | |||
606 | /* FIXME: kill extraneous args so it can be called independently */ | ||
607 | static int | ||
608 | write_led(const char __user *buffer, unsigned long count, | ||
609 | char *ledname, int ledmask, int invert) | ||
610 | { | ||
611 | int rv, value; | ||
612 | int led_out = 0; | ||
613 | |||
614 | rv = parse_arg(buffer, count, &value); | ||
615 | if (rv > 0) | ||
616 | led_out = value ? 1 : 0; | ||
617 | |||
618 | hotk->status = | ||
619 | (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask); | ||
620 | |||
621 | if (invert) /* invert target value */ | ||
622 | led_out = !led_out; | ||
623 | |||
624 | if (!write_acpi_int(hotk->handle, ledname, led_out, NULL)) | ||
625 | pr_warn("LED (%s) write failed\n", ledname); | ||
626 | |||
627 | return rv; | ||
628 | } | ||
629 | |||
630 | /* | ||
631 | * Proc handlers for MLED | ||
632 | */ | ||
633 | static int mled_proc_show(struct seq_file *m, void *v) | ||
634 | { | ||
635 | seq_printf(m, "%d\n", read_led(hotk->methods->mled_status, MLED_ON)); | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int mled_proc_open(struct inode *inode, struct file *file) | ||
640 | { | ||
641 | return single_open(file, mled_proc_show, NULL); | ||
642 | } | ||
643 | |||
644 | static ssize_t mled_proc_write(struct file *file, const char __user *buffer, | ||
645 | size_t count, loff_t *pos) | ||
646 | { | ||
647 | return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1); | ||
648 | } | ||
649 | |||
650 | static const struct file_operations mled_proc_fops = { | ||
651 | .owner = THIS_MODULE, | ||
652 | .open = mled_proc_open, | ||
653 | .read = seq_read, | ||
654 | .llseek = seq_lseek, | ||
655 | .release = single_release, | ||
656 | .write = mled_proc_write, | ||
657 | }; | ||
658 | |||
659 | /* | ||
660 | * Proc handlers for LED display | ||
661 | */ | ||
662 | static int ledd_proc_show(struct seq_file *m, void *v) | ||
663 | { | ||
664 | seq_printf(m, "0x%08x\n", hotk->ledd_status); | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | static int ledd_proc_open(struct inode *inode, struct file *file) | ||
669 | { | ||
670 | return single_open(file, ledd_proc_show, NULL); | ||
671 | } | ||
672 | |||
673 | static ssize_t ledd_proc_write(struct file *file, const char __user *buffer, | ||
674 | size_t count, loff_t *pos) | ||
675 | { | ||
676 | int rv, value; | ||
677 | |||
678 | rv = parse_arg(buffer, count, &value); | ||
679 | if (rv > 0) { | ||
680 | if (!write_acpi_int | ||
681 | (hotk->handle, hotk->methods->mt_ledd, value, NULL)) | ||
682 | pr_warn("LED display write failed\n"); | ||
683 | else | ||
684 | hotk->ledd_status = (u32) value; | ||
685 | } | ||
686 | return rv; | ||
687 | } | ||
688 | |||
689 | static const struct file_operations ledd_proc_fops = { | ||
690 | .owner = THIS_MODULE, | ||
691 | .open = ledd_proc_open, | ||
692 | .read = seq_read, | ||
693 | .llseek = seq_lseek, | ||
694 | .release = single_release, | ||
695 | .write = ledd_proc_write, | ||
696 | }; | ||
697 | |||
698 | /* | ||
699 | * Proc handlers for WLED | ||
700 | */ | ||
701 | static int wled_proc_show(struct seq_file *m, void *v) | ||
702 | { | ||
703 | seq_printf(m, "%d\n", read_led(hotk->methods->wled_status, WLED_ON)); | ||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | static int wled_proc_open(struct inode *inode, struct file *file) | ||
708 | { | ||
709 | return single_open(file, wled_proc_show, NULL); | ||
710 | } | ||
711 | |||
712 | static ssize_t wled_proc_write(struct file *file, const char __user *buffer, | ||
713 | size_t count, loff_t *pos) | ||
714 | { | ||
715 | return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0); | ||
716 | } | ||
717 | |||
718 | static const struct file_operations wled_proc_fops = { | ||
719 | .owner = THIS_MODULE, | ||
720 | .open = wled_proc_open, | ||
721 | .read = seq_read, | ||
722 | .llseek = seq_lseek, | ||
723 | .release = single_release, | ||
724 | .write = wled_proc_write, | ||
725 | }; | ||
726 | |||
727 | /* | ||
728 | * Proc handlers for Bluetooth | ||
729 | */ | ||
730 | static int bluetooth_proc_show(struct seq_file *m, void *v) | ||
731 | { | ||
732 | seq_printf(m, "%d\n", read_led(hotk->methods->bt_status, BT_ON)); | ||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | static int bluetooth_proc_open(struct inode *inode, struct file *file) | ||
737 | { | ||
738 | return single_open(file, bluetooth_proc_show, NULL); | ||
739 | } | ||
740 | |||
741 | static ssize_t bluetooth_proc_write(struct file *file, | ||
742 | const char __user *buffer, size_t count, loff_t *pos) | ||
743 | { | ||
744 | /* Note: mt_bt_switch controls both internal Bluetooth adapter's | ||
745 | presence and its LED */ | ||
746 | return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0); | ||
747 | } | ||
748 | |||
749 | static const struct file_operations bluetooth_proc_fops = { | ||
750 | .owner = THIS_MODULE, | ||
751 | .open = bluetooth_proc_open, | ||
752 | .read = seq_read, | ||
753 | .llseek = seq_lseek, | ||
754 | .release = single_release, | ||
755 | .write = bluetooth_proc_write, | ||
756 | }; | ||
757 | |||
758 | /* | ||
759 | * Proc handlers for TLED | ||
760 | */ | ||
761 | static int tled_proc_show(struct seq_file *m, void *v) | ||
762 | { | ||
763 | seq_printf(m, "%d\n", read_led(hotk->methods->tled_status, TLED_ON)); | ||
764 | return 0; | ||
765 | } | ||
766 | |||
767 | static int tled_proc_open(struct inode *inode, struct file *file) | ||
768 | { | ||
769 | return single_open(file, tled_proc_show, NULL); | ||
770 | } | ||
771 | |||
772 | static ssize_t tled_proc_write(struct file *file, const char __user *buffer, | ||
773 | size_t count, loff_t *pos) | ||
774 | { | ||
775 | return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0); | ||
776 | } | ||
777 | |||
778 | static const struct file_operations tled_proc_fops = { | ||
779 | .owner = THIS_MODULE, | ||
780 | .open = tled_proc_open, | ||
781 | .read = seq_read, | ||
782 | .llseek = seq_lseek, | ||
783 | .release = single_release, | ||
784 | .write = tled_proc_write, | ||
785 | }; | ||
786 | |||
787 | static int get_lcd_state(void) | ||
788 | { | ||
789 | int lcd = 0; | ||
790 | |||
791 | if (hotk->model == L3H) { | ||
792 | /* L3H and the like have to be handled differently */ | ||
793 | acpi_status status = 0; | ||
794 | struct acpi_object_list input; | ||
795 | union acpi_object mt_params[2]; | ||
796 | struct acpi_buffer output; | ||
797 | union acpi_object out_obj; | ||
798 | |||
799 | input.count = 2; | ||
800 | input.pointer = mt_params; | ||
801 | /* Note: the following values are partly guessed up, but | ||
802 | otherwise they seem to work */ | ||
803 | mt_params[0].type = ACPI_TYPE_INTEGER; | ||
804 | mt_params[0].integer.value = 0x02; | ||
805 | mt_params[1].type = ACPI_TYPE_INTEGER; | ||
806 | mt_params[1].integer.value = 0x02; | ||
807 | |||
808 | output.length = sizeof(out_obj); | ||
809 | output.pointer = &out_obj; | ||
810 | |||
811 | status = | ||
812 | acpi_evaluate_object(NULL, hotk->methods->lcd_status, | ||
813 | &input, &output); | ||
814 | if (status != AE_OK) | ||
815 | return -1; | ||
816 | if (out_obj.type == ACPI_TYPE_INTEGER) | ||
817 | /* That's what the AML code does */ | ||
818 | lcd = out_obj.integer.value >> 8; | ||
819 | } else if (hotk->model == F3Sa) { | ||
820 | unsigned long long tmp; | ||
821 | union acpi_object param; | ||
822 | struct acpi_object_list input; | ||
823 | acpi_status status; | ||
824 | |||
825 | /* Read pin 11 */ | ||
826 | param.type = ACPI_TYPE_INTEGER; | ||
827 | param.integer.value = 0x11; | ||
828 | input.count = 1; | ||
829 | input.pointer = ¶m; | ||
830 | |||
831 | status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status, | ||
832 | &input, &tmp); | ||
833 | if (status != AE_OK) | ||
834 | return -1; | ||
835 | |||
836 | lcd = tmp; | ||
837 | } else { | ||
838 | /* We don't have to check anything if we are here */ | ||
839 | if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) | ||
840 | pr_warn("Error reading LCD status\n"); | ||
841 | |||
842 | if (hotk->model == L2D) | ||
843 | lcd = ~lcd; | ||
844 | } | ||
845 | |||
846 | return (lcd & 1); | ||
847 | } | ||
848 | |||
849 | static int set_lcd_state(int value) | ||
850 | { | ||
851 | int lcd = 0; | ||
852 | acpi_status status = 0; | ||
853 | |||
854 | lcd = value ? 1 : 0; | ||
855 | if (lcd != get_lcd_state()) { | ||
856 | /* switch */ | ||
857 | if (hotk->model != L3H) { | ||
858 | status = | ||
859 | acpi_evaluate_object(NULL, | ||
860 | hotk->methods->mt_lcd_switch, | ||
861 | NULL, NULL); | ||
862 | } else { | ||
863 | /* L3H and the like must be handled differently */ | ||
864 | if (!write_acpi_int | ||
865 | (hotk->handle, hotk->methods->mt_lcd_switch, 0x07, | ||
866 | NULL)) | ||
867 | status = AE_ERROR; | ||
868 | /* L3H's AML executes EHK (0x07) upon Fn+F7 keypress, | ||
869 | the exact behaviour is simulated here */ | ||
870 | } | ||
871 | if (ACPI_FAILURE(status)) | ||
872 | pr_warn("Error switching LCD\n"); | ||
873 | } | ||
874 | return 0; | ||
875 | |||
876 | } | ||
877 | |||
878 | static int lcd_proc_show(struct seq_file *m, void *v) | ||
879 | { | ||
880 | seq_printf(m, "%d\n", get_lcd_state()); | ||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | static int lcd_proc_open(struct inode *inode, struct file *file) | ||
885 | { | ||
886 | return single_open(file, lcd_proc_show, NULL); | ||
887 | } | ||
888 | |||
889 | static ssize_t lcd_proc_write(struct file *file, const char __user *buffer, | ||
890 | size_t count, loff_t *pos) | ||
891 | { | ||
892 | int rv, value; | ||
893 | |||
894 | rv = parse_arg(buffer, count, &value); | ||
895 | if (rv > 0) | ||
896 | set_lcd_state(value); | ||
897 | return rv; | ||
898 | } | ||
899 | |||
900 | static const struct file_operations lcd_proc_fops = { | ||
901 | .owner = THIS_MODULE, | ||
902 | .open = lcd_proc_open, | ||
903 | .read = seq_read, | ||
904 | .llseek = seq_lseek, | ||
905 | .release = single_release, | ||
906 | .write = lcd_proc_write, | ||
907 | }; | ||
908 | |||
909 | static int read_brightness(struct backlight_device *bd) | ||
910 | { | ||
911 | int value; | ||
912 | |||
913 | if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */ | ||
914 | if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get, | ||
915 | &value)) | ||
916 | pr_warn("Error reading brightness\n"); | ||
917 | } else if (hotk->methods->brightness_status) { /* For D1 for example */ | ||
918 | if (!read_acpi_int(NULL, hotk->methods->brightness_status, | ||
919 | &value)) | ||
920 | pr_warn("Error reading brightness\n"); | ||
921 | } else /* No GPLV method */ | ||
922 | value = hotk->brightness; | ||
923 | return value; | ||
924 | } | ||
925 | |||
926 | /* | ||
927 | * Change the brightness level | ||
928 | */ | ||
929 | static int set_brightness(int value) | ||
930 | { | ||
931 | acpi_status status = 0; | ||
932 | int ret = 0; | ||
933 | |||
934 | /* SPLV laptop */ | ||
935 | if (hotk->methods->brightness_set) { | ||
936 | if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set, | ||
937 | value, NULL)) { | ||
938 | pr_warn("Error changing brightness\n"); | ||
939 | ret = -EIO; | ||
940 | } | ||
941 | goto out; | ||
942 | } | ||
943 | |||
944 | /* No SPLV method if we are here, act as appropriate */ | ||
945 | value -= read_brightness(NULL); | ||
946 | while (value != 0) { | ||
947 | status = acpi_evaluate_object(NULL, (value > 0) ? | ||
948 | hotk->methods->brightness_up : | ||
949 | hotk->methods->brightness_down, | ||
950 | NULL, NULL); | ||
951 | (value > 0) ? value-- : value++; | ||
952 | if (ACPI_FAILURE(status)) { | ||
953 | pr_warn("Error changing brightness\n"); | ||
954 | ret = -EIO; | ||
955 | } | ||
956 | } | ||
957 | out: | ||
958 | return ret; | ||
959 | } | ||
960 | |||
961 | static int set_brightness_status(struct backlight_device *bd) | ||
962 | { | ||
963 | return set_brightness(bd->props.brightness); | ||
964 | } | ||
965 | |||
966 | static int brn_proc_show(struct seq_file *m, void *v) | ||
967 | { | ||
968 | seq_printf(m, "%d\n", read_brightness(NULL)); | ||
969 | return 0; | ||
970 | } | ||
971 | |||
972 | static int brn_proc_open(struct inode *inode, struct file *file) | ||
973 | { | ||
974 | return single_open(file, brn_proc_show, NULL); | ||
975 | } | ||
976 | |||
977 | static ssize_t brn_proc_write(struct file *file, const char __user *buffer, | ||
978 | size_t count, loff_t *pos) | ||
979 | { | ||
980 | int rv, value; | ||
981 | |||
982 | rv = parse_arg(buffer, count, &value); | ||
983 | if (rv > 0) { | ||
984 | value = (0 < value) ? ((15 < value) ? 15 : value) : 0; | ||
985 | /* 0 <= value <= 15 */ | ||
986 | set_brightness(value); | ||
987 | } | ||
988 | return rv; | ||
989 | } | ||
990 | |||
991 | static const struct file_operations brn_proc_fops = { | ||
992 | .owner = THIS_MODULE, | ||
993 | .open = brn_proc_open, | ||
994 | .read = seq_read, | ||
995 | .llseek = seq_lseek, | ||
996 | .release = single_release, | ||
997 | .write = brn_proc_write, | ||
998 | }; | ||
999 | |||
1000 | static void set_display(int value) | ||
1001 | { | ||
1002 | /* no sanity check needed for now */ | ||
1003 | if (!write_acpi_int(hotk->handle, hotk->methods->display_set, | ||
1004 | value, NULL)) | ||
1005 | pr_warn("Error setting display\n"); | ||
1006 | return; | ||
1007 | } | ||
1008 | |||
1009 | /* | ||
1010 | * Now, *this* one could be more user-friendly, but so far, no-one has | ||
1011 | * complained. The significance of bits is the same as in proc_write_disp() | ||
1012 | */ | ||
1013 | static int disp_proc_show(struct seq_file *m, void *v) | ||
1014 | { | ||
1015 | int value = 0; | ||
1016 | |||
1017 | if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value)) | ||
1018 | pr_warn("Error reading display status\n"); | ||
1019 | value &= 0x07; /* needed for some models, shouldn't hurt others */ | ||
1020 | seq_printf(m, "%d\n", value); | ||
1021 | return 0; | ||
1022 | } | ||
1023 | |||
1024 | static int disp_proc_open(struct inode *inode, struct file *file) | ||
1025 | { | ||
1026 | return single_open(file, disp_proc_show, NULL); | ||
1027 | } | ||
1028 | |||
1029 | /* | ||
1030 | * Experimental support for display switching. As of now: 1 should activate | ||
1031 | * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination | ||
1032 | * (bitwise) of these will suffice. I never actually tested 3 displays hooked | ||
1033 | * up simultaneously, so be warned. See the acpi4asus README for more info. | ||
1034 | */ | ||
1035 | static ssize_t disp_proc_write(struct file *file, const char __user *buffer, | ||
1036 | size_t count, loff_t *pos) | ||
1037 | { | ||
1038 | int rv, value; | ||
1039 | |||
1040 | rv = parse_arg(buffer, count, &value); | ||
1041 | if (rv > 0) | ||
1042 | set_display(value); | ||
1043 | return rv; | ||
1044 | } | ||
1045 | |||
1046 | static const struct file_operations disp_proc_fops = { | ||
1047 | .owner = THIS_MODULE, | ||
1048 | .open = disp_proc_open, | ||
1049 | .read = seq_read, | ||
1050 | .llseek = seq_lseek, | ||
1051 | .release = single_release, | ||
1052 | .write = disp_proc_write, | ||
1053 | }; | ||
1054 | |||
1055 | static int | ||
1056 | asus_proc_add(char *name, const struct file_operations *proc_fops, umode_t mode, | ||
1057 | struct acpi_device *device) | ||
1058 | { | ||
1059 | struct proc_dir_entry *proc; | ||
1060 | |||
1061 | proc = proc_create_data(name, mode, acpi_device_dir(device), | ||
1062 | proc_fops, acpi_driver_data(device)); | ||
1063 | if (!proc) { | ||
1064 | pr_warn(" Unable to create %s fs entry\n", name); | ||
1065 | return -1; | ||
1066 | } | ||
1067 | proc->uid = asus_uid; | ||
1068 | proc->gid = asus_gid; | ||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | static int asus_hotk_add_fs(struct acpi_device *device) | ||
1073 | { | ||
1074 | struct proc_dir_entry *proc; | ||
1075 | umode_t mode; | ||
1076 | |||
1077 | if ((asus_uid == 0) && (asus_gid == 0)) { | ||
1078 | mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP; | ||
1079 | } else { | ||
1080 | mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP; | ||
1081 | pr_warn(" asus_uid and asus_gid parameters are " | ||
1082 | "deprecated, use chown and chmod instead!\n"); | ||
1083 | } | ||
1084 | |||
1085 | acpi_device_dir(device) = asus_proc_dir; | ||
1086 | if (!acpi_device_dir(device)) | ||
1087 | return -ENODEV; | ||
1088 | |||
1089 | proc = proc_create(PROC_INFO, mode, acpi_device_dir(device), | ||
1090 | &asus_info_proc_fops); | ||
1091 | if (proc) { | ||
1092 | proc->uid = asus_uid; | ||
1093 | proc->gid = asus_gid; | ||
1094 | } else { | ||
1095 | pr_warn(" Unable to create " PROC_INFO " fs entry\n"); | ||
1096 | } | ||
1097 | |||
1098 | if (hotk->methods->mt_wled) { | ||
1099 | asus_proc_add(PROC_WLED, &wled_proc_fops, mode, device); | ||
1100 | } | ||
1101 | |||
1102 | if (hotk->methods->mt_ledd) { | ||
1103 | asus_proc_add(PROC_LEDD, &ledd_proc_fops, mode, device); | ||
1104 | } | ||
1105 | |||
1106 | if (hotk->methods->mt_mled) { | ||
1107 | asus_proc_add(PROC_MLED, &mled_proc_fops, mode, device); | ||
1108 | } | ||
1109 | |||
1110 | if (hotk->methods->mt_tled) { | ||
1111 | asus_proc_add(PROC_TLED, &tled_proc_fops, mode, device); | ||
1112 | } | ||
1113 | |||
1114 | if (hotk->methods->mt_bt_switch) { | ||
1115 | asus_proc_add(PROC_BT, &bluetooth_proc_fops, mode, device); | ||
1116 | } | ||
1117 | |||
1118 | /* | ||
1119 | * We need both read node and write method as LCD switch is also | ||
1120 | * accessible from the keyboard | ||
1121 | */ | ||
1122 | if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) { | ||
1123 | asus_proc_add(PROC_LCD, &lcd_proc_fops, mode, device); | ||
1124 | } | ||
1125 | |||
1126 | if ((hotk->methods->brightness_up && hotk->methods->brightness_down) || | ||
1127 | (hotk->methods->brightness_get && hotk->methods->brightness_set)) { | ||
1128 | asus_proc_add(PROC_BRN, &brn_proc_fops, mode, device); | ||
1129 | } | ||
1130 | |||
1131 | if (hotk->methods->display_set) { | ||
1132 | asus_proc_add(PROC_DISP, &disp_proc_fops, mode, device); | ||
1133 | } | ||
1134 | |||
1135 | return 0; | ||
1136 | } | ||
1137 | |||
1138 | static int asus_hotk_remove_fs(struct acpi_device *device) | ||
1139 | { | ||
1140 | if (acpi_device_dir(device)) { | ||
1141 | remove_proc_entry(PROC_INFO, acpi_device_dir(device)); | ||
1142 | if (hotk->methods->mt_wled) | ||
1143 | remove_proc_entry(PROC_WLED, acpi_device_dir(device)); | ||
1144 | if (hotk->methods->mt_mled) | ||
1145 | remove_proc_entry(PROC_MLED, acpi_device_dir(device)); | ||
1146 | if (hotk->methods->mt_tled) | ||
1147 | remove_proc_entry(PROC_TLED, acpi_device_dir(device)); | ||
1148 | if (hotk->methods->mt_ledd) | ||
1149 | remove_proc_entry(PROC_LEDD, acpi_device_dir(device)); | ||
1150 | if (hotk->methods->mt_bt_switch) | ||
1151 | remove_proc_entry(PROC_BT, acpi_device_dir(device)); | ||
1152 | if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) | ||
1153 | remove_proc_entry(PROC_LCD, acpi_device_dir(device)); | ||
1154 | if ((hotk->methods->brightness_up | ||
1155 | && hotk->methods->brightness_down) | ||
1156 | || (hotk->methods->brightness_get | ||
1157 | && hotk->methods->brightness_set)) | ||
1158 | remove_proc_entry(PROC_BRN, acpi_device_dir(device)); | ||
1159 | if (hotk->methods->display_set) | ||
1160 | remove_proc_entry(PROC_DISP, acpi_device_dir(device)); | ||
1161 | } | ||
1162 | return 0; | ||
1163 | } | ||
1164 | |||
1165 | static void asus_hotk_notify(struct acpi_device *device, u32 event) | ||
1166 | { | ||
1167 | /* TODO Find a better way to handle events count. */ | ||
1168 | if (!hotk) | ||
1169 | return; | ||
1170 | |||
1171 | /* | ||
1172 | * The BIOS *should* be sending us device events, but apparently | ||
1173 | * Asus uses system events instead, so just ignore any device | ||
1174 | * events we get. | ||
1175 | */ | ||
1176 | if (event > ACPI_MAX_SYS_NOTIFY) | ||
1177 | return; | ||
1178 | |||
1179 | if ((event & ~((u32) BR_UP)) < 16) | ||
1180 | hotk->brightness = (event & ~((u32) BR_UP)); | ||
1181 | else if ((event & ~((u32) BR_DOWN)) < 16) | ||
1182 | hotk->brightness = (event & ~((u32) BR_DOWN)); | ||
1183 | |||
1184 | acpi_bus_generate_proc_event(hotk->device, event, | ||
1185 | hotk->event_count[event % 128]++); | ||
1186 | |||
1187 | return; | ||
1188 | } | ||
1189 | |||
1190 | /* | ||
1191 | * Match the model string to the list of supported models. Return END_MODEL if | ||
1192 | * no match or model is NULL. | ||
1193 | */ | ||
1194 | static int asus_model_match(char *model) | ||
1195 | { | ||
1196 | if (model == NULL) | ||
1197 | return END_MODEL; | ||
1198 | |||
1199 | if (strncmp(model, "L3D", 3) == 0) | ||
1200 | return L3D; | ||
1201 | else if (strncmp(model, "L2E", 3) == 0 || | ||
1202 | strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0) | ||
1203 | return L3H; | ||
1204 | else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0) | ||
1205 | return L3C; | ||
1206 | else if (strncmp(model, "L8L", 3) == 0) | ||
1207 | return L8L; | ||
1208 | else if (strncmp(model, "L4R", 3) == 0) | ||
1209 | return L4R; | ||
1210 | else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0) | ||
1211 | return M6N; | ||
1212 | else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0) | ||
1213 | return M6R; | ||
1214 | else if (strncmp(model, "M2N", 3) == 0 || | ||
1215 | strncmp(model, "M3N", 3) == 0 || | ||
1216 | strncmp(model, "M5N", 3) == 0 || | ||
1217 | strncmp(model, "S1N", 3) == 0 || | ||
1218 | strncmp(model, "S5N", 3) == 0) | ||
1219 | return xxN; | ||
1220 | else if (strncmp(model, "M1", 2) == 0) | ||
1221 | return M1A; | ||
1222 | else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0) | ||
1223 | return M2E; | ||
1224 | else if (strncmp(model, "L2", 2) == 0) | ||
1225 | return L2D; | ||
1226 | else if (strncmp(model, "L8", 2) == 0) | ||
1227 | return S1x; | ||
1228 | else if (strncmp(model, "D1", 2) == 0) | ||
1229 | return D1x; | ||
1230 | else if (strncmp(model, "A1", 2) == 0) | ||
1231 | return A1x; | ||
1232 | else if (strncmp(model, "A2", 2) == 0) | ||
1233 | return A2x; | ||
1234 | else if (strncmp(model, "J1", 2) == 0) | ||
1235 | return S2x; | ||
1236 | else if (strncmp(model, "L5", 2) == 0) | ||
1237 | return L5x; | ||
1238 | else if (strncmp(model, "A4G", 3) == 0) | ||
1239 | return A4G; | ||
1240 | else if (strncmp(model, "W1N", 3) == 0) | ||
1241 | return W1N; | ||
1242 | else if (strncmp(model, "W3V", 3) == 0) | ||
1243 | return W3V; | ||
1244 | else if (strncmp(model, "W5A", 3) == 0) | ||
1245 | return W5A; | ||
1246 | else if (strncmp(model, "R1F", 3) == 0) | ||
1247 | return R1F; | ||
1248 | else if (strncmp(model, "A4S", 3) == 0) | ||
1249 | return A4S; | ||
1250 | else if (strncmp(model, "F3Sa", 4) == 0) | ||
1251 | return F3Sa; | ||
1252 | else | ||
1253 | return END_MODEL; | ||
1254 | } | ||
1255 | |||
1256 | /* | ||
1257 | * This function is used to initialize the hotk with right values. In this | ||
1258 | * method, we can make all the detection we want, and modify the hotk struct | ||
1259 | */ | ||
1260 | static int asus_hotk_get_info(void) | ||
1261 | { | ||
1262 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1263 | union acpi_object *model = NULL; | ||
1264 | int bsts_result; | ||
1265 | char *string = NULL; | ||
1266 | acpi_status status; | ||
1267 | |||
1268 | /* | ||
1269 | * Get DSDT headers early enough to allow for differentiating between | ||
1270 | * models, but late enough to allow acpi_bus_register_driver() to fail | ||
1271 | * before doing anything ACPI-specific. Should we encounter a machine, | ||
1272 | * which needs special handling (i.e. its hotkey device has a different | ||
1273 | * HID), this bit will be moved. A global variable asus_info contains | ||
1274 | * the DSDT header. | ||
1275 | */ | ||
1276 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); | ||
1277 | if (ACPI_FAILURE(status)) | ||
1278 | pr_warn(" Couldn't get the DSDT table header\n"); | ||
1279 | |||
1280 | /* We have to write 0 on init this far for all ASUS models */ | ||
1281 | if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { | ||
1282 | pr_err(" Hotkey initialization failed\n"); | ||
1283 | return -ENODEV; | ||
1284 | } | ||
1285 | |||
1286 | /* This needs to be called for some laptops to init properly */ | ||
1287 | if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result)) | ||
1288 | pr_warn(" Error calling BSTS\n"); | ||
1289 | else if (bsts_result) | ||
1290 | pr_notice(" BSTS called, 0x%02x returned\n", bsts_result); | ||
1291 | |||
1292 | /* | ||
1293 | * Try to match the object returned by INIT to the specific model. | ||
1294 | * Handle every possible object (or the lack of thereof) the DSDT | ||
1295 | * writers might throw at us. When in trouble, we pass NULL to | ||
1296 | * asus_model_match() and try something completely different. | ||
1297 | */ | ||
1298 | if (buffer.pointer) { | ||
1299 | model = buffer.pointer; | ||
1300 | switch (model->type) { | ||
1301 | case ACPI_TYPE_STRING: | ||
1302 | string = model->string.pointer; | ||
1303 | break; | ||
1304 | case ACPI_TYPE_BUFFER: | ||
1305 | string = model->buffer.pointer; | ||
1306 | break; | ||
1307 | default: | ||
1308 | kfree(model); | ||
1309 | model = NULL; | ||
1310 | break; | ||
1311 | } | ||
1312 | } | ||
1313 | hotk->model = asus_model_match(string); | ||
1314 | if (hotk->model == END_MODEL) { /* match failed */ | ||
1315 | if (asus_info && | ||
1316 | strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) { | ||
1317 | hotk->model = P30; | ||
1318 | pr_notice(" Samsung P30 detected, supported\n"); | ||
1319 | hotk->methods = &model_conf[hotk->model]; | ||
1320 | kfree(model); | ||
1321 | return 0; | ||
1322 | } else { | ||
1323 | hotk->model = M2E; | ||
1324 | pr_notice(" unsupported model %s, trying default values\n", | ||
1325 | string); | ||
1326 | pr_notice(" send /proc/acpi/dsdt to the developers\n"); | ||
1327 | kfree(model); | ||
1328 | return -ENODEV; | ||
1329 | } | ||
1330 | } | ||
1331 | hotk->methods = &model_conf[hotk->model]; | ||
1332 | pr_notice(" %s model detected, supported\n", string); | ||
1333 | |||
1334 | /* Sort of per-model blacklist */ | ||
1335 | if (strncmp(string, "L2B", 3) == 0) | ||
1336 | hotk->methods->lcd_status = NULL; | ||
1337 | /* L2B is similar enough to L3C to use its settings, with this only | ||
1338 | exception */ | ||
1339 | else if (strncmp(string, "A3G", 3) == 0) | ||
1340 | hotk->methods->lcd_status = "\\BLFG"; | ||
1341 | /* A3G is like M6R */ | ||
1342 | else if (strncmp(string, "S5N", 3) == 0 || | ||
1343 | strncmp(string, "M5N", 3) == 0 || | ||
1344 | strncmp(string, "W3N", 3) == 0) | ||
1345 | hotk->methods->mt_mled = NULL; | ||
1346 | /* S5N, M5N and W3N have no MLED */ | ||
1347 | else if (strncmp(string, "L5D", 3) == 0) | ||
1348 | hotk->methods->mt_wled = NULL; | ||
1349 | /* L5D's WLED is not controlled by ACPI */ | ||
1350 | else if (strncmp(string, "M2N", 3) == 0 || | ||
1351 | strncmp(string, "W3V", 3) == 0 || | ||
1352 | strncmp(string, "S1N", 3) == 0) | ||
1353 | hotk->methods->mt_wled = "WLED"; | ||
1354 | /* M2N, S1N and W3V have a usable WLED */ | ||
1355 | else if (asus_info) { | ||
1356 | if (strncmp(asus_info->oem_table_id, "L1", 2) == 0) | ||
1357 | hotk->methods->mled_status = NULL; | ||
1358 | /* S1300A reports L84F, but L1400B too, account for that */ | ||
1359 | } | ||
1360 | |||
1361 | kfree(model); | ||
1362 | |||
1363 | return 0; | ||
1364 | } | ||
1365 | |||
1366 | static int asus_hotk_check(void) | ||
1367 | { | ||
1368 | int result = 0; | ||
1369 | |||
1370 | result = acpi_bus_get_status(hotk->device); | ||
1371 | if (result) | ||
1372 | return result; | ||
1373 | |||
1374 | if (hotk->device->status.present) { | ||
1375 | result = asus_hotk_get_info(); | ||
1376 | } else { | ||
1377 | pr_err(" Hotkey device not present, aborting\n"); | ||
1378 | return -EINVAL; | ||
1379 | } | ||
1380 | |||
1381 | return result; | ||
1382 | } | ||
1383 | |||
1384 | static int asus_hotk_found; | ||
1385 | |||
1386 | static int asus_hotk_add(struct acpi_device *device) | ||
1387 | { | ||
1388 | acpi_status status = AE_OK; | ||
1389 | int result; | ||
1390 | |||
1391 | pr_notice("Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION); | ||
1392 | |||
1393 | hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); | ||
1394 | if (!hotk) | ||
1395 | return -ENOMEM; | ||
1396 | |||
1397 | hotk->handle = device->handle; | ||
1398 | strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME); | ||
1399 | strcpy(acpi_device_class(device), ACPI_HOTK_CLASS); | ||
1400 | device->driver_data = hotk; | ||
1401 | hotk->device = device; | ||
1402 | |||
1403 | result = asus_hotk_check(); | ||
1404 | if (result) | ||
1405 | goto end; | ||
1406 | |||
1407 | result = asus_hotk_add_fs(device); | ||
1408 | if (result) | ||
1409 | goto end; | ||
1410 | |||
1411 | /* For laptops without GPLV: init the hotk->brightness value */ | ||
1412 | if ((!hotk->methods->brightness_get) | ||
1413 | && (!hotk->methods->brightness_status) | ||
1414 | && (hotk->methods->brightness_up && hotk->methods->brightness_down)) { | ||
1415 | status = | ||
1416 | acpi_evaluate_object(NULL, hotk->methods->brightness_down, | ||
1417 | NULL, NULL); | ||
1418 | if (ACPI_FAILURE(status)) | ||
1419 | pr_warn(" Error changing brightness\n"); | ||
1420 | else { | ||
1421 | status = | ||
1422 | acpi_evaluate_object(NULL, | ||
1423 | hotk->methods->brightness_up, | ||
1424 | NULL, NULL); | ||
1425 | if (ACPI_FAILURE(status)) | ||
1426 | pr_warn(" Strange, error changing brightness\n"); | ||
1427 | } | ||
1428 | } | ||
1429 | |||
1430 | asus_hotk_found = 1; | ||
1431 | |||
1432 | /* LED display is off by default */ | ||
1433 | hotk->ledd_status = 0xFFF; | ||
1434 | |||
1435 | end: | ||
1436 | if (result) | ||
1437 | kfree(hotk); | ||
1438 | |||
1439 | return result; | ||
1440 | } | ||
1441 | |||
1442 | static int asus_hotk_remove(struct acpi_device *device, int type) | ||
1443 | { | ||
1444 | asus_hotk_remove_fs(device); | ||
1445 | |||
1446 | kfree(hotk); | ||
1447 | |||
1448 | return 0; | ||
1449 | } | ||
1450 | |||
1451 | static const struct backlight_ops asus_backlight_data = { | ||
1452 | .get_brightness = read_brightness, | ||
1453 | .update_status = set_brightness_status, | ||
1454 | }; | ||
1455 | |||
1456 | static void asus_acpi_exit(void) | ||
1457 | { | ||
1458 | if (asus_backlight_device) | ||
1459 | backlight_device_unregister(asus_backlight_device); | ||
1460 | |||
1461 | acpi_bus_unregister_driver(&asus_hotk_driver); | ||
1462 | remove_proc_entry(PROC_ASUS, acpi_root_dir); | ||
1463 | |||
1464 | return; | ||
1465 | } | ||
1466 | |||
1467 | static int __init asus_acpi_init(void) | ||
1468 | { | ||
1469 | struct backlight_properties props; | ||
1470 | int result; | ||
1471 | |||
1472 | result = acpi_bus_register_driver(&asus_hotk_driver); | ||
1473 | if (result < 0) | ||
1474 | return result; | ||
1475 | |||
1476 | asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); | ||
1477 | if (!asus_proc_dir) { | ||
1478 | pr_err("Unable to create /proc entry\n"); | ||
1479 | acpi_bus_unregister_driver(&asus_hotk_driver); | ||
1480 | return -ENODEV; | ||
1481 | } | ||
1482 | |||
1483 | /* | ||
1484 | * This is a bit of a kludge. We only want this module loaded | ||
1485 | * for ASUS systems, but there's currently no way to probe the | ||
1486 | * ACPI namespace for ASUS HIDs. So we just return failure if | ||
1487 | * we didn't find one, which will cause the module to be | ||
1488 | * unloaded. | ||
1489 | */ | ||
1490 | if (!asus_hotk_found) { | ||
1491 | acpi_bus_unregister_driver(&asus_hotk_driver); | ||
1492 | remove_proc_entry(PROC_ASUS, acpi_root_dir); | ||
1493 | return -ENODEV; | ||
1494 | } | ||
1495 | |||
1496 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1497 | props.type = BACKLIGHT_PLATFORM; | ||
1498 | props.max_brightness = 15; | ||
1499 | asus_backlight_device = backlight_device_register("asus", NULL, NULL, | ||
1500 | &asus_backlight_data, | ||
1501 | &props); | ||
1502 | if (IS_ERR(asus_backlight_device)) { | ||
1503 | pr_err("Could not register asus backlight device\n"); | ||
1504 | asus_backlight_device = NULL; | ||
1505 | asus_acpi_exit(); | ||
1506 | return -ENODEV; | ||
1507 | } | ||
1508 | |||
1509 | return 0; | ||
1510 | } | ||
1511 | |||
1512 | module_init(asus_acpi_init); | ||
1513 | module_exit(asus_acpi_exit); | ||
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index d96734478324..1887e2f166a4 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c | |||
@@ -882,6 +882,7 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { | |||
882 | }, | 882 | }, |
883 | { } | 883 | { } |
884 | }; | 884 | }; |
885 | MODULE_DEVICE_TABLE(dmi, compal_dmi_table); | ||
885 | 886 | ||
886 | static void initialize_power_supply_data(struct compal_data *data) | 887 | static void initialize_power_supply_data(struct compal_data *data) |
887 | { | 888 | { |
@@ -1097,16 +1098,3 @@ MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)"); | |||
1097 | MODULE_DESCRIPTION("Compal Laptop Support"); | 1098 | MODULE_DESCRIPTION("Compal Laptop Support"); |
1098 | MODULE_VERSION(DRIVER_VERSION); | 1099 | MODULE_VERSION(DRIVER_VERSION); |
1099 | MODULE_LICENSE("GPL"); | 1100 | MODULE_LICENSE("GPL"); |
1100 | |||
1101 | MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*"); | ||
1102 | MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); | ||
1103 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); | ||
1104 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); | ||
1105 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); | ||
1106 | MODULE_ALIAS("dmi:*:rnJHL90:rvrREFERENCE:*"); | ||
1107 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); | ||
1108 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); | ||
1109 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); | ||
1110 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1012:*"); | ||
1111 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*"); | ||
1112 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*"); | ||
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index d93e962f2610..a05fc9c955d8 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -117,6 +117,7 @@ static const struct dmi_system_id __initdata dell_device_table[] = { | |||
117 | }, | 117 | }, |
118 | { } | 118 | { } |
119 | }; | 119 | }; |
120 | MODULE_DEVICE_TABLE(dmi, dell_device_table); | ||
120 | 121 | ||
121 | static struct dmi_system_id __devinitdata dell_blacklist[] = { | 122 | static struct dmi_system_id __devinitdata dell_blacklist[] = { |
122 | /* Supported by compal-laptop */ | 123 | /* Supported by compal-laptop */ |
@@ -184,6 +185,33 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { | |||
184 | }, | 185 | }, |
185 | .driver_data = &quirk_dell_vostro_v130, | 186 | .driver_data = &quirk_dell_vostro_v130, |
186 | }, | 187 | }, |
188 | { | ||
189 | .callback = dmi_matched, | ||
190 | .ident = "Dell Vostro 3555", | ||
191 | .matches = { | ||
192 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
193 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), | ||
194 | }, | ||
195 | .driver_data = &quirk_dell_vostro_v130, | ||
196 | }, | ||
197 | { | ||
198 | .callback = dmi_matched, | ||
199 | .ident = "Dell Inspiron N311z", | ||
200 | .matches = { | ||
201 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
202 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), | ||
203 | }, | ||
204 | .driver_data = &quirk_dell_vostro_v130, | ||
205 | }, | ||
206 | { | ||
207 | .callback = dmi_matched, | ||
208 | .ident = "Dell Inspiron M5110", | ||
209 | .matches = { | ||
210 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
211 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), | ||
212 | }, | ||
213 | .driver_data = &quirk_dell_vostro_v130, | ||
214 | }, | ||
187 | }; | 215 | }; |
188 | 216 | ||
189 | static struct calling_interface_buffer *buffer; | 217 | static struct calling_interface_buffer *buffer; |
@@ -236,9 +264,7 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy) | |||
236 | { | 264 | { |
237 | switch (dm->type) { | 265 | switch (dm->type) { |
238 | case 0xd4: /* Indexed IO */ | 266 | case 0xd4: /* Indexed IO */ |
239 | break; | ||
240 | case 0xd5: /* Protected Area Type 1 */ | 267 | case 0xd5: /* Protected Area Type 1 */ |
241 | break; | ||
242 | case 0xd6: /* Protected Area Type 2 */ | 268 | case 0xd6: /* Protected Area Type 2 */ |
243 | break; | 269 | break; |
244 | case 0xda: /* Calling interface */ | 270 | case 0xda: /* Calling interface */ |
@@ -615,6 +641,7 @@ static void touchpad_led_set(struct led_classdev *led_cdev, | |||
615 | static struct led_classdev touchpad_led = { | 641 | static struct led_classdev touchpad_led = { |
616 | .name = "dell-laptop::touchpad", | 642 | .name = "dell-laptop::touchpad", |
617 | .brightness_set = touchpad_led_set, | 643 | .brightness_set = touchpad_led_set, |
644 | .flags = LED_CORE_SUSPENDRESUME, | ||
618 | }; | 645 | }; |
619 | 646 | ||
620 | static int __devinit touchpad_led_init(struct device *dev) | 647 | static int __devinit touchpad_led_init(struct device *dev) |
@@ -794,6 +821,3 @@ module_exit(dell_exit); | |||
794 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | 821 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); |
795 | MODULE_DESCRIPTION("Dell laptop driver"); | 822 | MODULE_DESCRIPTION("Dell laptop driver"); |
796 | MODULE_LICENSE("GPL"); | 823 | MODULE_LICENSE("GPL"); |
797 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); | ||
798 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*"); | ||
799 | MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*"); | ||
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ea44abd8df48..dab91b48d22c 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -646,7 +646,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) | |||
646 | } else { | 646 | } else { |
647 | dev = pci_get_slot(bus, 0); | 647 | dev = pci_get_slot(bus, 0); |
648 | if (dev) { | 648 | if (dev) { |
649 | pci_remove_bus_device(dev); | 649 | pci_stop_and_remove_bus_device(dev); |
650 | pci_dev_put(dev); | 650 | pci_dev_put(dev); |
651 | } | 651 | } |
652 | } | 652 | } |
@@ -1251,6 +1251,14 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc) | |||
1251 | /* | 1251 | /* |
1252 | * ACPI driver | 1252 | * ACPI driver |
1253 | */ | 1253 | */ |
1254 | static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event) | ||
1255 | { | ||
1256 | if (!eeepc->inputdev) | ||
1257 | return ; | ||
1258 | if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true)) | ||
1259 | pr_info("Unknown key %x pressed\n", event); | ||
1260 | } | ||
1261 | |||
1254 | static void eeepc_acpi_notify(struct acpi_device *device, u32 event) | 1262 | static void eeepc_acpi_notify(struct acpi_device *device, u32 event) |
1255 | { | 1263 | { |
1256 | struct eeepc_laptop *eeepc = acpi_driver_data(device); | 1264 | struct eeepc_laptop *eeepc = acpi_driver_data(device); |
@@ -1287,12 +1295,11 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event) | |||
1287 | * event will be desired value (or else ignored) | 1295 | * event will be desired value (or else ignored) |
1288 | */ | 1296 | */ |
1289 | } | 1297 | } |
1290 | sparse_keymap_report_event(eeepc->inputdev, event, | 1298 | eeepc_input_notify(eeepc, event); |
1291 | 1, true); | ||
1292 | } | 1299 | } |
1293 | } else { | 1300 | } else { |
1294 | /* Everything else is a bona-fide keypress event */ | 1301 | /* Everything else is a bona-fide keypress event */ |
1295 | sparse_keymap_report_event(eeepc->inputdev, event, 1, true); | 1302 | eeepc_input_notify(eeepc, event); |
1296 | } | 1303 | } |
1297 | } | 1304 | } |
1298 | 1305 | ||
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 9f6e64302b45..656761380342 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/input.h> | 32 | #include <linux/input.h> |
33 | #include <linux/input/sparse-keymap.h> | 33 | #include <linux/input/sparse-keymap.h> |
34 | #include <linux/dmi.h> | 34 | #include <linux/dmi.h> |
35 | #include <linux/fb.h> | ||
35 | #include <acpi/acpi_bus.h> | 36 | #include <acpi/acpi_bus.h> |
36 | 37 | ||
37 | #include "asus-wmi.h" | 38 | #include "asus-wmi.h" |
@@ -84,9 +85,81 @@ static const struct key_entry eeepc_wmi_keymap[] = { | |||
84 | { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, | 85 | { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, |
85 | { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, | 86 | { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, |
86 | { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, | 87 | { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, |
88 | { KE_KEY, 0xf3, { KEY_MENU } }, | ||
89 | { KE_KEY, 0xf5, { KEY_HOMEPAGE } }, | ||
90 | { KE_KEY, 0xf6, { KEY_ESC } }, | ||
87 | { KE_END, 0}, | 91 | { KE_END, 0}, |
88 | }; | 92 | }; |
89 | 93 | ||
94 | static struct quirk_entry quirk_asus_unknown = { | ||
95 | }; | ||
96 | |||
97 | static struct quirk_entry quirk_asus_1000h = { | ||
98 | .hotplug_wireless = true, | ||
99 | }; | ||
100 | |||
101 | static struct quirk_entry quirk_asus_et2012_type1 = { | ||
102 | .store_backlight_power = true, | ||
103 | }; | ||
104 | |||
105 | static struct quirk_entry quirk_asus_et2012_type3 = { | ||
106 | .scalar_panel_brightness = true, | ||
107 | .store_backlight_power = true, | ||
108 | }; | ||
109 | |||
110 | static struct quirk_entry *quirks; | ||
111 | |||
112 | static void et2012_quirks(void) | ||
113 | { | ||
114 | const struct dmi_device *dev = NULL; | ||
115 | char oemstring[30]; | ||
116 | |||
117 | while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { | ||
118 | if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) { | ||
119 | if (oemstring[18] == '1') | ||
120 | quirks = &quirk_asus_et2012_type1; | ||
121 | else if (oemstring[18] == '3') | ||
122 | quirks = &quirk_asus_et2012_type3; | ||
123 | break; | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | |||
128 | static int dmi_matched(const struct dmi_system_id *dmi) | ||
129 | { | ||
130 | char *model; | ||
131 | |||
132 | quirks = dmi->driver_data; | ||
133 | |||
134 | model = (char *)dmi->matches[1].substr; | ||
135 | if (unlikely(strncmp(model, "ET2012", 6) == 0)) | ||
136 | et2012_quirks(); | ||
137 | |||
138 | return 1; | ||
139 | } | ||
140 | |||
141 | static struct dmi_system_id asus_quirks[] = { | ||
142 | { | ||
143 | .callback = dmi_matched, | ||
144 | .ident = "ASUSTeK Computer INC. 1000H", | ||
145 | .matches = { | ||
146 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), | ||
147 | DMI_MATCH(DMI_PRODUCT_NAME, "1000H"), | ||
148 | }, | ||
149 | .driver_data = &quirk_asus_1000h, | ||
150 | }, | ||
151 | { | ||
152 | .callback = dmi_matched, | ||
153 | .ident = "ASUSTeK Computer INC. ET2012E/I", | ||
154 | .matches = { | ||
155 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), | ||
156 | DMI_MATCH(DMI_PRODUCT_NAME, "ET2012"), | ||
157 | }, | ||
158 | .driver_data = &quirk_asus_unknown, | ||
159 | }, | ||
160 | {}, | ||
161 | }; | ||
162 | |||
90 | static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code, | 163 | static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code, |
91 | unsigned int *value, bool *autorelease) | 164 | unsigned int *value, bool *autorelease) |
92 | { | 165 | { |
@@ -141,33 +214,16 @@ static int eeepc_wmi_probe(struct platform_device *pdev) | |||
141 | return 0; | 214 | return 0; |
142 | } | 215 | } |
143 | 216 | ||
144 | static void eeepc_dmi_check(struct asus_wmi_driver *driver) | ||
145 | { | ||
146 | const char *model; | ||
147 | |||
148 | model = dmi_get_system_info(DMI_PRODUCT_NAME); | ||
149 | if (!model) | ||
150 | return; | ||
151 | |||
152 | /* | ||
153 | * Whitelist for wlan hotplug | ||
154 | * | ||
155 | * Asus 1000H needs the current hotplug code to handle | ||
156 | * Fn+F2 correctly. We may add other Asus here later, but | ||
157 | * it seems that most of the laptops supported by asus-wmi | ||
158 | * don't need to be on this list | ||
159 | */ | ||
160 | if (strcmp(model, "1000H") == 0) { | ||
161 | driver->hotplug_wireless = true; | ||
162 | pr_info("wlan hotplug enabled\n"); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | static void eeepc_wmi_quirks(struct asus_wmi_driver *driver) | 217 | static void eeepc_wmi_quirks(struct asus_wmi_driver *driver) |
167 | { | 218 | { |
168 | driver->hotplug_wireless = hotplug_wireless; | 219 | quirks = &quirk_asus_unknown; |
169 | driver->wapf = -1; | 220 | quirks->hotplug_wireless = hotplug_wireless; |
170 | eeepc_dmi_check(driver); | 221 | |
222 | dmi_check_system(asus_quirks); | ||
223 | |||
224 | driver->quirks = quirks; | ||
225 | driver->quirks->wapf = -1; | ||
226 | driver->panel_power = FB_BLANK_UNBLANK; | ||
171 | } | 227 | } |
172 | 228 | ||
173 | static struct asus_wmi_driver asus_wmi_driver = { | 229 | static struct asus_wmi_driver asus_wmi_driver = { |
@@ -179,7 +235,7 @@ static struct asus_wmi_driver asus_wmi_driver = { | |||
179 | .input_phys = EEEPC_WMI_FILE "/input0", | 235 | .input_phys = EEEPC_WMI_FILE "/input0", |
180 | .key_filter = eeepc_wmi_key_filter, | 236 | .key_filter = eeepc_wmi_key_filter, |
181 | .probe = eeepc_wmi_probe, | 237 | .probe = eeepc_wmi_probe, |
182 | .quirks = eeepc_wmi_quirks, | 238 | .detect_quirks = eeepc_wmi_quirks, |
183 | }; | 239 | }; |
184 | 240 | ||
185 | 241 | ||
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 5a34973dc164..7387f97a2941 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c | |||
@@ -375,11 +375,11 @@ static ssize_t hdaps_variance_show(struct device *dev, | |||
375 | static ssize_t hdaps_temp1_show(struct device *dev, | 375 | static ssize_t hdaps_temp1_show(struct device *dev, |
376 | struct device_attribute *attr, char *buf) | 376 | struct device_attribute *attr, char *buf) |
377 | { | 377 | { |
378 | u8 temp; | 378 | u8 uninitialized_var(temp); |
379 | int ret; | 379 | int ret; |
380 | 380 | ||
381 | ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); | 381 | ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); |
382 | if (ret < 0) | 382 | if (ret) |
383 | return ret; | 383 | return ret; |
384 | 384 | ||
385 | return sprintf(buf, "%u\n", temp); | 385 | return sprintf(buf, "%u\n", temp); |
@@ -388,11 +388,11 @@ static ssize_t hdaps_temp1_show(struct device *dev, | |||
388 | static ssize_t hdaps_temp2_show(struct device *dev, | 388 | static ssize_t hdaps_temp2_show(struct device *dev, |
389 | struct device_attribute *attr, char *buf) | 389 | struct device_attribute *attr, char *buf) |
390 | { | 390 | { |
391 | u8 temp; | 391 | u8 uninitialized_var(temp); |
392 | int ret; | 392 | int ret; |
393 | 393 | ||
394 | ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); | 394 | ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); |
395 | if (ret < 0) | 395 | if (ret) |
396 | return ret; | 396 | return ret; |
397 | 397 | ||
398 | return sprintf(buf, "%u\n", temp); | 398 | return sprintf(buf, "%u\n", temp); |
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index f1ae5078b7ec..0a3594c7e912 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c | |||
@@ -23,21 +23,27 @@ | |||
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
25 | #include <linux/input.h> | 25 | #include <linux/input.h> |
26 | 26 | #include <linux/mfd/intel_msic.h> | |
27 | #include <asm/intel_scu_ipc.h> | ||
28 | 27 | ||
29 | #define DRIVER_NAME "msic_power_btn" | 28 | #define DRIVER_NAME "msic_power_btn" |
30 | 29 | ||
31 | #define MSIC_PB_STATUS 0x3f | ||
32 | #define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */ | 30 | #define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */ |
33 | 31 | ||
32 | /* | ||
33 | * MSIC document ti_datasheet defines the 1st bit reg 0x21 is used to mask | ||
34 | * power button interrupt | ||
35 | */ | ||
36 | #define MSIC_PWRBTNM (1 << 0) | ||
37 | |||
34 | static irqreturn_t mfld_pb_isr(int irq, void *dev_id) | 38 | static irqreturn_t mfld_pb_isr(int irq, void *dev_id) |
35 | { | 39 | { |
36 | struct input_dev *input = dev_id; | 40 | struct input_dev *input = dev_id; |
37 | int ret; | 41 | int ret; |
38 | u8 pbstat; | 42 | u8 pbstat; |
39 | 43 | ||
40 | ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat); | 44 | ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat); |
45 | dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat); | ||
46 | |||
41 | if (ret < 0) { | 47 | if (ret < 0) { |
42 | dev_err(input->dev.parent, "Read error %d while reading" | 48 | dev_err(input->dev.parent, "Read error %d while reading" |
43 | " MSIC_PB_STATUS\n", ret); | 49 | " MSIC_PB_STATUS\n", ret); |
@@ -88,6 +94,24 @@ static int __devinit mfld_pb_probe(struct platform_device *pdev) | |||
88 | } | 94 | } |
89 | 95 | ||
90 | platform_set_drvdata(pdev, input); | 96 | platform_set_drvdata(pdev, input); |
97 | |||
98 | /* | ||
99 | * SCU firmware might send power button interrupts to IA core before | ||
100 | * kernel boots and doesn't get EOI from IA core. The first bit of | ||
101 | * MSIC reg 0x21 is kept masked, and SCU firmware doesn't send new | ||
102 | * power interrupt to Android kernel. Unmask the bit when probing | ||
103 | * power button in kernel. | ||
104 | * There is a very narrow race between irq handler and power button | ||
105 | * initialization. The race happens rarely. So we needn't worry | ||
106 | * about it. | ||
107 | */ | ||
108 | error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM); | ||
109 | if (error) { | ||
110 | dev_err(&pdev->dev, "Unable to clear power button interrupt, " | ||
111 | "error: %d\n", error); | ||
112 | goto err_free_irq; | ||
113 | } | ||
114 | |||
91 | return 0; | 115 | return 0; |
92 | 116 | ||
93 | err_free_irq: | 117 | err_free_irq: |
@@ -118,17 +142,7 @@ static struct platform_driver mfld_pb_driver = { | |||
118 | .remove = __devexit_p(mfld_pb_remove), | 142 | .remove = __devexit_p(mfld_pb_remove), |
119 | }; | 143 | }; |
120 | 144 | ||
121 | static int __init mfld_pb_init(void) | 145 | module_platform_driver(mfld_pb_driver); |
122 | { | ||
123 | return platform_driver_register(&mfld_pb_driver); | ||
124 | } | ||
125 | module_init(mfld_pb_init); | ||
126 | |||
127 | static void __exit mfld_pb_exit(void) | ||
128 | { | ||
129 | platform_driver_unregister(&mfld_pb_driver); | ||
130 | } | ||
131 | module_exit(mfld_pb_exit); | ||
132 | 146 | ||
133 | MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>"); | 147 | MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>"); |
134 | MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); | 148 | MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); |
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index ccd7b1f83519..5ae9cd9c7e6e 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c | |||
@@ -33,18 +33,15 @@ | |||
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | #include <linux/pm.h> | 34 | #include <linux/pm.h> |
35 | #include <linux/thermal.h> | 35 | #include <linux/thermal.h> |
36 | 36 | #include <linux/mfd/intel_msic.h> | |
37 | #include <asm/intel_scu_ipc.h> | ||
38 | 37 | ||
39 | /* Number of thermal sensors */ | 38 | /* Number of thermal sensors */ |
40 | #define MSIC_THERMAL_SENSORS 4 | 39 | #define MSIC_THERMAL_SENSORS 4 |
41 | 40 | ||
42 | /* ADC1 - thermal registers */ | 41 | /* ADC1 - thermal registers */ |
43 | #define MSIC_THERM_ADC1CNTL1 0x1C0 | ||
44 | #define MSIC_ADC_ENBL 0x10 | 42 | #define MSIC_ADC_ENBL 0x10 |
45 | #define MSIC_ADC_START 0x08 | 43 | #define MSIC_ADC_START 0x08 |
46 | 44 | ||
47 | #define MSIC_THERM_ADC1CNTL3 0x1C2 | ||
48 | #define MSIC_ADCTHERM_ENBL 0x04 | 45 | #define MSIC_ADCTHERM_ENBL 0x04 |
49 | #define MSIC_ADCRRDATA_ENBL 0x05 | 46 | #define MSIC_ADCRRDATA_ENBL 0x05 |
50 | #define MSIC_CHANL_MASK_VAL 0x0F | 47 | #define MSIC_CHANL_MASK_VAL 0x0F |
@@ -75,8 +72,8 @@ | |||
75 | #define ADC_VAL60C 315 | 72 | #define ADC_VAL60C 315 |
76 | 73 | ||
77 | /* ADC base addresses */ | 74 | /* ADC base addresses */ |
78 | #define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ | 75 | #define ADC_CHNL_START_ADDR INTEL_MSIC_ADC1ADDR0 /* increments by 1 */ |
79 | #define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ | 76 | #define ADC_DATA_START_ADDR INTEL_MSIC_ADC1SNS0H /* increments by 2 */ |
80 | 77 | ||
81 | /* MSIC die attributes */ | 78 | /* MSIC die attributes */ |
82 | #define MSIC_DIE_ADC_MIN 488 | 79 | #define MSIC_DIE_ADC_MIN 488 |
@@ -189,17 +186,17 @@ static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp) | |||
189 | addr = td_info->chnl_addr; | 186 | addr = td_info->chnl_addr; |
190 | 187 | ||
191 | /* Enable the msic for conversion before reading */ | 188 | /* Enable the msic for conversion before reading */ |
192 | ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL); | 189 | ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCRRDATA_ENBL); |
193 | if (ret) | 190 | if (ret) |
194 | return ret; | 191 | return ret; |
195 | 192 | ||
196 | /* Re-toggle the RRDATARD bit (temporary workaround) */ | 193 | /* Re-toggle the RRDATARD bit (temporary workaround) */ |
197 | ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL); | 194 | ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCTHERM_ENBL); |
198 | if (ret) | 195 | if (ret) |
199 | return ret; | 196 | return ret; |
200 | 197 | ||
201 | /* Read the higher bits of data */ | 198 | /* Read the higher bits of data */ |
202 | ret = intel_scu_ipc_ioread8(addr, &data); | 199 | ret = intel_msic_reg_read(addr, &data); |
203 | if (ret) | 200 | if (ret) |
204 | return ret; | 201 | return ret; |
205 | 202 | ||
@@ -207,7 +204,7 @@ static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp) | |||
207 | adc_val = (data << 2); | 204 | adc_val = (data << 2); |
208 | addr++; | 205 | addr++; |
209 | 206 | ||
210 | ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */ | 207 | ret = intel_msic_reg_read(addr, &data);/* Read lower bits */ |
211 | if (ret) | 208 | if (ret) |
212 | return ret; | 209 | return ret; |
213 | 210 | ||
@@ -235,7 +232,7 @@ static int configure_adc(int val) | |||
235 | int ret; | 232 | int ret; |
236 | uint8_t data; | 233 | uint8_t data; |
237 | 234 | ||
238 | ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); | 235 | ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data); |
239 | if (ret) | 236 | if (ret) |
240 | return ret; | 237 | return ret; |
241 | 238 | ||
@@ -246,7 +243,7 @@ static int configure_adc(int val) | |||
246 | /* Just stop the ADC */ | 243 | /* Just stop the ADC */ |
247 | data &= (~MSIC_ADC_START); | 244 | data &= (~MSIC_ADC_START); |
248 | } | 245 | } |
249 | return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data); | 246 | return intel_msic_reg_write(INTEL_MSIC_ADC1CNTL1, data); |
250 | } | 247 | } |
251 | 248 | ||
252 | /** | 249 | /** |
@@ -262,21 +259,21 @@ static int set_up_therm_channel(u16 base_addr) | |||
262 | int ret; | 259 | int ret; |
263 | 260 | ||
264 | /* Enable all the sensor channels */ | 261 | /* Enable all the sensor channels */ |
265 | ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE); | 262 | ret = intel_msic_reg_write(base_addr, SKIN_SENSOR0_CODE); |
266 | if (ret) | 263 | if (ret) |
267 | return ret; | 264 | return ret; |
268 | 265 | ||
269 | ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE); | 266 | ret = intel_msic_reg_write(base_addr + 1, SKIN_SENSOR1_CODE); |
270 | if (ret) | 267 | if (ret) |
271 | return ret; | 268 | return ret; |
272 | 269 | ||
273 | ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE); | 270 | ret = intel_msic_reg_write(base_addr + 2, SYS_SENSOR_CODE); |
274 | if (ret) | 271 | if (ret) |
275 | return ret; | 272 | return ret; |
276 | 273 | ||
277 | /* Since this is the last channel, set the stop bit | 274 | /* Since this is the last channel, set the stop bit |
278 | * to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ | 275 | * to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ |
279 | ret = intel_scu_ipc_iowrite8(base_addr + 3, | 276 | ret = intel_msic_reg_write(base_addr + 3, |
280 | (MSIC_DIE_SENSOR_CODE | 0x10)); | 277 | (MSIC_DIE_SENSOR_CODE | 0x10)); |
281 | if (ret) | 278 | if (ret) |
282 | return ret; | 279 | return ret; |
@@ -295,11 +292,11 @@ static int reset_stopbit(uint16_t addr) | |||
295 | { | 292 | { |
296 | int ret; | 293 | int ret; |
297 | uint8_t data; | 294 | uint8_t data; |
298 | ret = intel_scu_ipc_ioread8(addr, &data); | 295 | ret = intel_msic_reg_read(addr, &data); |
299 | if (ret) | 296 | if (ret) |
300 | return ret; | 297 | return ret; |
301 | /* Set the stop bit to zero */ | 298 | /* Set the stop bit to zero */ |
302 | return intel_scu_ipc_iowrite8(addr, (data & 0xEF)); | 299 | return intel_msic_reg_write(addr, (data & 0xEF)); |
303 | } | 300 | } |
304 | 301 | ||
305 | /** | 302 | /** |
@@ -322,7 +319,7 @@ static int find_free_channel(void) | |||
322 | uint8_t data; | 319 | uint8_t data; |
323 | 320 | ||
324 | /* check whether ADC is enabled */ | 321 | /* check whether ADC is enabled */ |
325 | ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); | 322 | ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data); |
326 | if (ret) | 323 | if (ret) |
327 | return ret; | 324 | return ret; |
328 | 325 | ||
@@ -331,7 +328,7 @@ static int find_free_channel(void) | |||
331 | 328 | ||
332 | /* ADC is already enabled; Looking for an empty channel */ | 329 | /* ADC is already enabled; Looking for an empty channel */ |
333 | for (i = 0; i < ADC_CHANLS_MAX; i++) { | 330 | for (i = 0; i < ADC_CHANLS_MAX; i++) { |
334 | ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data); | 331 | ret = intel_msic_reg_read(ADC_CHNL_START_ADDR + i, &data); |
335 | if (ret) | 332 | if (ret) |
336 | return ret; | 333 | return ret; |
337 | 334 | ||
@@ -359,12 +356,14 @@ static int mid_initialize_adc(struct device *dev) | |||
359 | * Ensure that adctherm is disabled before we | 356 | * Ensure that adctherm is disabled before we |
360 | * initialize the ADC | 357 | * initialize the ADC |
361 | */ | 358 | */ |
362 | ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data); | 359 | ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL3, &data); |
363 | if (ret) | 360 | if (ret) |
364 | return ret; | 361 | return ret; |
365 | 362 | ||
366 | if (data & MSIC_ADCTHERM_MASK) | 363 | data &= ~MSIC_ADCTHERM_MASK; |
367 | dev_warn(dev, "ADCTHERM already set"); | 364 | ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, data); |
365 | if (ret) | ||
366 | return ret; | ||
368 | 367 | ||
369 | /* Index of the first channel in which the stop bit is set */ | 368 | /* Index of the first channel in which the stop bit is set */ |
370 | channel_index = find_free_channel(); | 369 | channel_index = find_free_channel(); |
@@ -546,10 +545,11 @@ static int mid_thermal_remove(struct platform_device *pdev) | |||
546 | return configure_adc(0); | 545 | return configure_adc(0); |
547 | } | 546 | } |
548 | 547 | ||
549 | #define DRIVER_NAME "msic_sensor" | 548 | #define DRIVER_NAME "msic_thermal" |
550 | 549 | ||
551 | static const struct platform_device_id therm_id_table[] = { | 550 | static const struct platform_device_id therm_id_table[] = { |
552 | { DRIVER_NAME, 1 }, | 551 | { DRIVER_NAME, 1 }, |
552 | { "msic_thermal", 1 }, | ||
553 | { } | 553 | { } |
554 | }; | 554 | }; |
555 | 555 | ||
@@ -565,18 +565,7 @@ static struct platform_driver mid_thermal_driver = { | |||
565 | .id_table = therm_id_table, | 565 | .id_table = therm_id_table, |
566 | }; | 566 | }; |
567 | 567 | ||
568 | static int __init mid_thermal_module_init(void) | 568 | module_platform_driver(mid_thermal_driver); |
569 | { | ||
570 | return platform_driver_register(&mid_thermal_driver); | ||
571 | } | ||
572 | |||
573 | static void __exit mid_thermal_module_exit(void) | ||
574 | { | ||
575 | platform_driver_unregister(&mid_thermal_driver); | ||
576 | } | ||
577 | |||
578 | module_init(mid_thermal_module_init); | ||
579 | module_exit(mid_thermal_module_exit); | ||
580 | 569 | ||
581 | MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>"); | 570 | MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>"); |
582 | MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); | 571 | MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); |
diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index 6ee0b5c90933..79a0c2f6be53 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c | |||
@@ -313,6 +313,7 @@ static struct dmi_system_id __initdata oaktrail_dmi_table[] = { | |||
313 | }, | 313 | }, |
314 | { } | 314 | { } |
315 | }; | 315 | }; |
316 | MODULE_DEVICE_TABLE(dmi, oaktrail_dmi_table); | ||
316 | 317 | ||
317 | static int __init oaktrail_init(void) | 318 | static int __init oaktrail_init(void) |
318 | { | 319 | { |
@@ -394,4 +395,3 @@ MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)"); | |||
394 | MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras"); | 395 | MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras"); |
395 | MODULE_VERSION(DRIVER_VERSION); | 396 | MODULE_VERSION(DRIVER_VERSION); |
396 | MODULE_LICENSE("GPL"); | 397 | MODULE_LICENSE("GPL"); |
397 | MODULE_ALIAS("dmi:*:svnIntelCorporation:pnOakTrailplatform:*"); | ||
diff --git a/drivers/platform/x86/intel_rar_register.c b/drivers/platform/x86/intel_rar_register.c deleted file mode 100644 index c8a6aed45277..000000000000 --- a/drivers/platform/x86/intel_rar_register.c +++ /dev/null | |||
@@ -1,669 +0,0 @@ | |||
1 | /* | ||
2 | * rar_register.c - An Intel Restricted Access Region register driver | ||
3 | * | ||
4 | * Copyright(c) 2009 Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 of the | ||
9 | * License, or (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||
19 | * 02111-1307, USA. | ||
20 | * | ||
21 | * ------------------------------------------------------------------- | ||
22 | * 20091204 Mark Allyn <mark.a.allyn@intel.com> | ||
23 | * Ossama Othman <ossama.othman@intel.com> | ||
24 | * Cleanup per feedback from Alan Cox and Arjan Van De Ven | ||
25 | * | ||
26 | * 20090806 Ossama Othman <ossama.othman@intel.com> | ||
27 | * Return zero high address if upper 22 bits is zero. | ||
28 | * Cleaned up checkpatch errors. | ||
29 | * Clarified that driver is dealing with bus addresses. | ||
30 | * | ||
31 | * 20090702 Ossama Othman <ossama.othman@intel.com> | ||
32 | * Removed unnecessary include directives | ||
33 | * Cleaned up spinlocks. | ||
34 | * Cleaned up logging. | ||
35 | * Improved invalid parameter checks. | ||
36 | * Fixed and simplified RAR address retrieval and RAR locking | ||
37 | * code. | ||
38 | * | ||
39 | * 20090626 Mark Allyn <mark.a.allyn@intel.com> | ||
40 | * Initial publish | ||
41 | */ | ||
42 | |||
43 | #include <linux/module.h> | ||
44 | #include <linux/pci.h> | ||
45 | #include <linux/spinlock.h> | ||
46 | #include <linux/device.h> | ||
47 | #include <linux/kernel.h> | ||
48 | #include <linux/rar_register.h> | ||
49 | |||
50 | /* === Lincroft Message Bus Interface === */ | ||
51 | #define LNC_MCR_OFFSET 0xD0 /* Message Control Register */ | ||
52 | #define LNC_MDR_OFFSET 0xD4 /* Message Data Register */ | ||
53 | |||
54 | /* Message Opcodes */ | ||
55 | #define LNC_MESSAGE_READ_OPCODE 0xD0 | ||
56 | #define LNC_MESSAGE_WRITE_OPCODE 0xE0 | ||
57 | |||
58 | /* Message Write Byte Enables */ | ||
59 | #define LNC_MESSAGE_BYTE_WRITE_ENABLES 0xF | ||
60 | |||
61 | /* B-unit Port */ | ||
62 | #define LNC_BUNIT_PORT 0x3 | ||
63 | |||
64 | /* === Lincroft B-Unit Registers - Programmed by IA32 firmware === */ | ||
65 | #define LNC_BRAR0L 0x10 | ||
66 | #define LNC_BRAR0H 0x11 | ||
67 | #define LNC_BRAR1L 0x12 | ||
68 | #define LNC_BRAR1H 0x13 | ||
69 | /* Reserved for SeP */ | ||
70 | #define LNC_BRAR2L 0x14 | ||
71 | #define LNC_BRAR2H 0x15 | ||
72 | |||
73 | /* Moorestown supports three restricted access regions. */ | ||
74 | #define MRST_NUM_RAR 3 | ||
75 | |||
76 | /* RAR Bus Address Range */ | ||
77 | struct rar_addr { | ||
78 | dma_addr_t low; | ||
79 | dma_addr_t high; | ||
80 | }; | ||
81 | |||
82 | /* | ||
83 | * We create one of these for each RAR | ||
84 | */ | ||
85 | struct client { | ||
86 | int (*callback)(unsigned long data); | ||
87 | unsigned long driver_priv; | ||
88 | bool busy; | ||
89 | }; | ||
90 | |||
91 | static DEFINE_MUTEX(rar_mutex); | ||
92 | static DEFINE_MUTEX(lnc_reg_mutex); | ||
93 | |||
94 | /* | ||
95 | * One per RAR device (currently only one device) | ||
96 | */ | ||
97 | struct rar_device { | ||
98 | struct rar_addr rar_addr[MRST_NUM_RAR]; | ||
99 | struct pci_dev *rar_dev; | ||
100 | bool registered; | ||
101 | bool allocated; | ||
102 | struct client client[MRST_NUM_RAR]; | ||
103 | }; | ||
104 | |||
105 | /* Current platforms have only one rar_device for 3 rar regions */ | ||
106 | static struct rar_device my_rar_device; | ||
107 | |||
108 | /* | ||
109 | * Abstract out multiple device support. Current platforms only | ||
110 | * have a single RAR device. | ||
111 | */ | ||
112 | |||
113 | /** | ||
114 | * alloc_rar_device - return a new RAR structure | ||
115 | * | ||
116 | * Return a new (but not yet ready) RAR device object | ||
117 | */ | ||
118 | static struct rar_device *alloc_rar_device(void) | ||
119 | { | ||
120 | if (my_rar_device.allocated) | ||
121 | return NULL; | ||
122 | my_rar_device.allocated = 1; | ||
123 | return &my_rar_device; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * free_rar_device - free a RAR object | ||
128 | * @rar: the RAR device being freed | ||
129 | * | ||
130 | * Release a RAR object and any attached resources | ||
131 | */ | ||
132 | static void free_rar_device(struct rar_device *rar) | ||
133 | { | ||
134 | pci_dev_put(rar->rar_dev); | ||
135 | rar->allocated = 0; | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * _rar_to_device - return the device handling this RAR | ||
140 | * @rar: RAR number | ||
141 | * @off: returned offset | ||
142 | * | ||
143 | * Internal helper for looking up RAR devices. This and alloc are the | ||
144 | * two functions that need touching to go to multiple RAR devices. | ||
145 | */ | ||
146 | static struct rar_device *_rar_to_device(int rar, int *off) | ||
147 | { | ||
148 | if (rar >= 0 && rar < MRST_NUM_RAR) { | ||
149 | *off = rar; | ||
150 | return &my_rar_device; | ||
151 | } | ||
152 | return NULL; | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * rar_to_device - return the device handling this RAR | ||
157 | * @rar: RAR number | ||
158 | * @off: returned offset | ||
159 | * | ||
160 | * Return the device this RAR maps to if one is present, otherwise | ||
161 | * returns NULL. Reports the offset relative to the base of this | ||
162 | * RAR device in off. | ||
163 | */ | ||
164 | static struct rar_device *rar_to_device(int rar, int *off) | ||
165 | { | ||
166 | struct rar_device *rar_dev = _rar_to_device(rar, off); | ||
167 | if (rar_dev == NULL || !rar_dev->registered) | ||
168 | return NULL; | ||
169 | return rar_dev; | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * rar_to_client - return the client handling this RAR | ||
174 | * @rar: RAR number | ||
175 | * | ||
176 | * Return the client this RAR maps to if a mapping is known, otherwise | ||
177 | * returns NULL. | ||
178 | */ | ||
179 | static struct client *rar_to_client(int rar) | ||
180 | { | ||
181 | int idx; | ||
182 | struct rar_device *r = _rar_to_device(rar, &idx); | ||
183 | if (r != NULL) | ||
184 | return &r->client[idx]; | ||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * rar_read_addr - retrieve a RAR mapping | ||
190 | * @pdev: PCI device for the RAR | ||
191 | * @offset: offset for message | ||
192 | * @addr: returned address | ||
193 | * | ||
194 | * Reads the address of a given RAR register. Returns 0 on success | ||
195 | * or an error code on failure. | ||
196 | */ | ||
197 | static int rar_read_addr(struct pci_dev *pdev, int offset, dma_addr_t *addr) | ||
198 | { | ||
199 | /* | ||
200 | * ======== The Lincroft Message Bus Interface ======== | ||
201 | * Lincroft registers may be obtained via PCI from | ||
202 | * the host bridge using the Lincroft Message Bus | ||
203 | * Interface. That message bus interface is generally | ||
204 | * comprised of two registers: a control register (MCR, 0xDO) | ||
205 | * and a data register (MDR, 0xD4). | ||
206 | * | ||
207 | * The MCR (message control register) format is the following: | ||
208 | * 1. [31:24]: Opcode | ||
209 | * 2. [23:16]: Port | ||
210 | * 3. [15:8]: Register Offset | ||
211 | * 4. [7:4]: Byte Enables (use 0xF to set all of these bits | ||
212 | * to 1) | ||
213 | * 5. [3:0]: reserved | ||
214 | * | ||
215 | * Read (0xD0) and write (0xE0) opcodes are written to the | ||
216 | * control register when reading and writing to Lincroft | ||
217 | * registers, respectively. | ||
218 | * | ||
219 | * We're interested in registers found in the Lincroft | ||
220 | * B-unit. The B-unit port is 0x3. | ||
221 | * | ||
222 | * The six B-unit RAR register offsets we use are listed | ||
223 | * earlier in this file. | ||
224 | * | ||
225 | * Lastly writing to the MCR register requires the "Byte | ||
226 | * enables" bits to be set to 1. This may be achieved by | ||
227 | * writing 0xF at bit 4. | ||
228 | * | ||
229 | * The MDR (message data register) format is the following: | ||
230 | * 1. [31:0]: Read/Write Data | ||
231 | * | ||
232 | * Data being read from this register is only available after | ||
233 | * writing the appropriate control message to the MCR | ||
234 | * register. | ||
235 | * | ||
236 | * Data being written to this register must be written before | ||
237 | * writing the appropriate control message to the MCR | ||
238 | * register. | ||
239 | */ | ||
240 | |||
241 | int result; | ||
242 | u32 addr32; | ||
243 | |||
244 | /* Construct control message */ | ||
245 | u32 const message = | ||
246 | (LNC_MESSAGE_READ_OPCODE << 24) | ||
247 | | (LNC_BUNIT_PORT << 16) | ||
248 | | (offset << 8) | ||
249 | | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4); | ||
250 | |||
251 | dev_dbg(&pdev->dev, "Offset for 'get' LNC MSG is %x\n", offset); | ||
252 | |||
253 | /* | ||
254 | * We synchronize access to the Lincroft MCR and MDR registers | ||
255 | * until BOTH the command is issued through the MCR register | ||
256 | * and the corresponding data is read from the MDR register. | ||
257 | * Otherwise a race condition would exist between accesses to | ||
258 | * both registers. | ||
259 | */ | ||
260 | |||
261 | mutex_lock(&lnc_reg_mutex); | ||
262 | |||
263 | /* Send the control message */ | ||
264 | result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message); | ||
265 | if (!result) { | ||
266 | /* Read back the address as a 32bit value */ | ||
267 | result = pci_read_config_dword(pdev, LNC_MDR_OFFSET, &addr32); | ||
268 | *addr = (dma_addr_t)addr32; | ||
269 | } | ||
270 | mutex_unlock(&lnc_reg_mutex); | ||
271 | return result; | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * rar_set_addr - Set a RAR mapping | ||
276 | * @pdev: PCI device for the RAR | ||
277 | * @offset: offset for message | ||
278 | * @addr: address to set | ||
279 | * | ||
280 | * Sets the address of a given RAR register. Returns 0 on success | ||
281 | * or an error code on failure. | ||
282 | */ | ||
283 | static int rar_set_addr(struct pci_dev *pdev, | ||
284 | int offset, | ||
285 | dma_addr_t addr) | ||
286 | { | ||
287 | /* | ||
288 | * Data being written to this register must be written before | ||
289 | * writing the appropriate control message to the MCR | ||
290 | * register. | ||
291 | * See rar_get_addrs() for a description of the | ||
292 | * message bus interface being used here. | ||
293 | */ | ||
294 | |||
295 | int result; | ||
296 | |||
297 | /* Construct control message */ | ||
298 | u32 const message = (LNC_MESSAGE_WRITE_OPCODE << 24) | ||
299 | | (LNC_BUNIT_PORT << 16) | ||
300 | | (offset << 8) | ||
301 | | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4); | ||
302 | |||
303 | /* | ||
304 | * We synchronize access to the Lincroft MCR and MDR registers | ||
305 | * until BOTH the command is issued through the MCR register | ||
306 | * and the corresponding data is read from the MDR register. | ||
307 | * Otherwise a race condition would exist between accesses to | ||
308 | * both registers. | ||
309 | */ | ||
310 | |||
311 | mutex_lock(&lnc_reg_mutex); | ||
312 | |||
313 | /* Send the control message */ | ||
314 | result = pci_write_config_dword(pdev, LNC_MDR_OFFSET, addr); | ||
315 | if (!result) | ||
316 | /* And address */ | ||
317 | result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message); | ||
318 | |||
319 | mutex_unlock(&lnc_reg_mutex); | ||
320 | return result; | ||
321 | } | ||
322 | |||
323 | /* | ||
324 | * rar_init_params - Initialize RAR parameters | ||
325 | * @rar: RAR device to initialise | ||
326 | * | ||
327 | * Initialize RAR parameters, such as bus addresses, etc. Returns 0 | ||
328 | * on success, or an error code on failure. | ||
329 | */ | ||
330 | static int init_rar_params(struct rar_device *rar) | ||
331 | { | ||
332 | struct pci_dev *pdev = rar->rar_dev; | ||
333 | unsigned int i; | ||
334 | int result = 0; | ||
335 | int offset = 0x10; /* RAR 0 to 2 in order low/high/low/high/... */ | ||
336 | |||
337 | /* Retrieve RAR start and end bus addresses. | ||
338 | * Access the RAR registers through the Lincroft Message Bus | ||
339 | * Interface on PCI device: 00:00.0 Host bridge. | ||
340 | */ | ||
341 | |||
342 | for (i = 0; i < MRST_NUM_RAR; ++i) { | ||
343 | struct rar_addr *addr = &rar->rar_addr[i]; | ||
344 | |||
345 | result = rar_read_addr(pdev, offset++, &addr->low); | ||
346 | if (result != 0) | ||
347 | return result; | ||
348 | |||
349 | result = rar_read_addr(pdev, offset++, &addr->high); | ||
350 | if (result != 0) | ||
351 | return result; | ||
352 | |||
353 | |||
354 | /* | ||
355 | * Only the upper 22 bits of the RAR addresses are | ||
356 | * stored in their corresponding RAR registers so we | ||
357 | * must set the lower 10 bits accordingly. | ||
358 | |||
359 | * The low address has its lower 10 bits cleared, and | ||
360 | * the high address has all its lower 10 bits set, | ||
361 | * e.g.: | ||
362 | * low = 0x2ffffc00 | ||
363 | */ | ||
364 | |||
365 | addr->low &= (dma_addr_t)0xfffffc00u; | ||
366 | |||
367 | /* | ||
368 | * Set bits 9:0 on uppser address if bits 31:10 are non | ||
369 | * zero; otherwize clear all bits | ||
370 | */ | ||
371 | |||
372 | if ((addr->high & 0xfffffc00u) == 0) | ||
373 | addr->high = 0; | ||
374 | else | ||
375 | addr->high |= 0x3ffu; | ||
376 | } | ||
377 | /* Done accessing the device. */ | ||
378 | |||
379 | if (result == 0) { | ||
380 | for (i = 0; i != MRST_NUM_RAR; ++i) { | ||
381 | /* | ||
382 | * "BRAR" refers to the RAR registers in the | ||
383 | * Lincroft B-unit. | ||
384 | */ | ||
385 | dev_info(&pdev->dev, "BRAR[%u] bus address range = " | ||
386 | "[%lx, %lx]\n", i, | ||
387 | (unsigned long)rar->rar_addr[i].low, | ||
388 | (unsigned long)rar->rar_addr[i].high); | ||
389 | } | ||
390 | } | ||
391 | return result; | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * rar_get_address - get the bus address in a RAR | ||
396 | * @start: return value of start address of block | ||
397 | * @end: return value of end address of block | ||
398 | * | ||
399 | * The rar_get_address function is used by other device drivers | ||
400 | * to obtain RAR address information on a RAR. It takes three | ||
401 | * parameters: | ||
402 | * | ||
403 | * The function returns a 0 upon success or an error if there is no RAR | ||
404 | * facility on this system. | ||
405 | */ | ||
406 | int rar_get_address(int rar_index, dma_addr_t *start, dma_addr_t *end) | ||
407 | { | ||
408 | int idx; | ||
409 | struct rar_device *rar = rar_to_device(rar_index, &idx); | ||
410 | |||
411 | if (rar == NULL) { | ||
412 | WARN_ON(1); | ||
413 | return -ENODEV; | ||
414 | } | ||
415 | |||
416 | *start = rar->rar_addr[idx].low; | ||
417 | *end = rar->rar_addr[idx].high; | ||
418 | return 0; | ||
419 | } | ||
420 | EXPORT_SYMBOL(rar_get_address); | ||
421 | |||
422 | /** | ||
423 | * rar_lock - lock a RAR register | ||
424 | * @rar_index: RAR to lock (0-2) | ||
425 | * | ||
426 | * The rar_lock function is ued by other device drivers to lock an RAR. | ||
427 | * once a RAR is locked, it stays locked until the next system reboot. | ||
428 | * | ||
429 | * The function returns a 0 upon success or an error if there is no RAR | ||
430 | * facility on this system, or the locking fails | ||
431 | */ | ||
432 | int rar_lock(int rar_index) | ||
433 | { | ||
434 | struct rar_device *rar; | ||
435 | int result; | ||
436 | int idx; | ||
437 | dma_addr_t low, high; | ||
438 | |||
439 | rar = rar_to_device(rar_index, &idx); | ||
440 | |||
441 | if (rar == NULL) { | ||
442 | WARN_ON(1); | ||
443 | return -EINVAL; | ||
444 | } | ||
445 | |||
446 | low = rar->rar_addr[idx].low & 0xfffffc00u; | ||
447 | high = rar->rar_addr[idx].high & 0xfffffc00u; | ||
448 | |||
449 | /* | ||
450 | * Only allow I/O from the graphics and Langwell; | ||
451 | * not from the x86 processor | ||
452 | */ | ||
453 | |||
454 | if (rar_index == RAR_TYPE_VIDEO) { | ||
455 | low |= 0x00000009; | ||
456 | high |= 0x00000015; | ||
457 | } else if (rar_index == RAR_TYPE_AUDIO) { | ||
458 | /* Only allow I/O from Langwell; nothing from x86 */ | ||
459 | low |= 0x00000008; | ||
460 | high |= 0x00000018; | ||
461 | } else | ||
462 | /* Read-only from all agents */ | ||
463 | high |= 0x00000018; | ||
464 | |||
465 | /* | ||
466 | * Now program the register using the Lincroft message | ||
467 | * bus interface. | ||
468 | */ | ||
469 | result = rar_set_addr(rar->rar_dev, | ||
470 | 2 * idx, low); | ||
471 | |||
472 | if (result == 0) | ||
473 | result = rar_set_addr(rar->rar_dev, | ||
474 | 2 * idx + 1, high); | ||
475 | |||
476 | return result; | ||
477 | } | ||
478 | EXPORT_SYMBOL(rar_lock); | ||
479 | |||
480 | /** | ||
481 | * register_rar - register a RAR handler | ||
482 | * @num: RAR we wish to register for | ||
483 | * @callback: function to call when RAR support is available | ||
484 | * @data: data to pass to this function | ||
485 | * | ||
486 | * The register_rar function is to used by other device drivers | ||
487 | * to ensure that this driver is ready. As we cannot be sure of | ||
488 | * the compile/execute order of drivers in the kernel, it is | ||
489 | * best to give this driver a callback function to call when | ||
490 | * it is ready to give out addresses. The callback function | ||
491 | * would have those steps that continue the initialization of | ||
492 | * a driver that do require a valid RAR address. One of those | ||
493 | * steps would be to call rar_get_address() | ||
494 | * | ||
495 | * This function return 0 on success or an error code on failure. | ||
496 | */ | ||
497 | int register_rar(int num, int (*callback)(unsigned long data), | ||
498 | unsigned long data) | ||
499 | { | ||
500 | /* For now we hardcode a single RAR device */ | ||
501 | struct rar_device *rar; | ||
502 | struct client *c; | ||
503 | int idx; | ||
504 | int retval = 0; | ||
505 | |||
506 | mutex_lock(&rar_mutex); | ||
507 | |||
508 | /* Do we have a client mapping for this RAR number ? */ | ||
509 | c = rar_to_client(num); | ||
510 | if (c == NULL) { | ||
511 | retval = -ERANGE; | ||
512 | goto done; | ||
513 | } | ||
514 | /* Is it claimed ? */ | ||
515 | if (c->busy) { | ||
516 | retval = -EBUSY; | ||
517 | goto done; | ||
518 | } | ||
519 | c->busy = 1; | ||
520 | |||
521 | /* See if we have a handler for this RAR yet, if we do then fire it */ | ||
522 | rar = rar_to_device(num, &idx); | ||
523 | |||
524 | if (rar) { | ||
525 | /* | ||
526 | * if the driver already registered, then we can simply | ||
527 | * call the callback right now | ||
528 | */ | ||
529 | (*callback)(data); | ||
530 | goto done; | ||
531 | } | ||
532 | |||
533 | /* Arrange to be called back when the hardware is found */ | ||
534 | c->callback = callback; | ||
535 | c->driver_priv = data; | ||
536 | done: | ||
537 | mutex_unlock(&rar_mutex); | ||
538 | return retval; | ||
539 | } | ||
540 | EXPORT_SYMBOL(register_rar); | ||
541 | |||
542 | /** | ||
543 | * unregister_rar - release a RAR allocation | ||
544 | * @num: RAR number | ||
545 | * | ||
546 | * Releases a RAR allocation, or pending allocation. If a callback is | ||
547 | * pending then this function will either complete before the unregister | ||
548 | * returns or not at all. | ||
549 | */ | ||
550 | |||
551 | void unregister_rar(int num) | ||
552 | { | ||
553 | struct client *c; | ||
554 | |||
555 | mutex_lock(&rar_mutex); | ||
556 | c = rar_to_client(num); | ||
557 | if (c == NULL || !c->busy) | ||
558 | WARN_ON(1); | ||
559 | else | ||
560 | c->busy = 0; | ||
561 | mutex_unlock(&rar_mutex); | ||
562 | } | ||
563 | EXPORT_SYMBOL(unregister_rar); | ||
564 | |||
565 | /** | ||
566 | * rar_callback - Process callbacks | ||
567 | * @rar: new RAR device | ||
568 | * | ||
569 | * Process the callbacks for a newly found RAR device. | ||
570 | */ | ||
571 | |||
572 | static void rar_callback(struct rar_device *rar) | ||
573 | { | ||
574 | struct client *c = &rar->client[0]; | ||
575 | int i; | ||
576 | |||
577 | mutex_lock(&rar_mutex); | ||
578 | |||
579 | rar->registered = 1; /* Ensure no more callbacks queue */ | ||
580 | |||
581 | for (i = 0; i < MRST_NUM_RAR; i++) { | ||
582 | if (c->callback && c->busy) { | ||
583 | c->callback(c->driver_priv); | ||
584 | c->callback = NULL; | ||
585 | } | ||
586 | c++; | ||
587 | } | ||
588 | mutex_unlock(&rar_mutex); | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * rar_probe - PCI probe callback | ||
593 | * @dev: PCI device | ||
594 | * @id: matching entry in the match table | ||
595 | * | ||
596 | * A RAR device has been discovered. Initialise it and if successful | ||
597 | * process any pending callbacks that can now be completed. | ||
598 | */ | ||
599 | static int rar_probe(struct pci_dev *dev, const struct pci_device_id *id) | ||
600 | { | ||
601 | int error; | ||
602 | struct rar_device *rar; | ||
603 | |||
604 | dev_dbg(&dev->dev, "PCI probe starting\n"); | ||
605 | |||
606 | rar = alloc_rar_device(); | ||
607 | if (rar == NULL) | ||
608 | return -EBUSY; | ||
609 | |||
610 | /* Enable the device */ | ||
611 | error = pci_enable_device(dev); | ||
612 | if (error) { | ||
613 | dev_err(&dev->dev, | ||
614 | "Error enabling RAR register PCI device\n"); | ||
615 | goto end_function; | ||
616 | } | ||
617 | |||
618 | /* Fill in the rar_device structure */ | ||
619 | rar->rar_dev = pci_dev_get(dev); | ||
620 | pci_set_drvdata(dev, rar); | ||
621 | |||
622 | /* | ||
623 | * Initialize the RAR parameters, which have to be retrieved | ||
624 | * via the message bus interface. | ||
625 | */ | ||
626 | error = init_rar_params(rar); | ||
627 | if (error) { | ||
628 | pci_disable_device(dev); | ||
629 | dev_err(&dev->dev, "Error retrieving RAR addresses\n"); | ||
630 | goto end_function; | ||
631 | } | ||
632 | /* now call anyone who has registered (using callbacks) */ | ||
633 | rar_callback(rar); | ||
634 | return 0; | ||
635 | end_function: | ||
636 | free_rar_device(rar); | ||
637 | return error; | ||
638 | } | ||
639 | |||
640 | static DEFINE_PCI_DEVICE_TABLE(rar_pci_id_tbl) = { | ||
641 | { PCI_VDEVICE(INTEL, 0x4110) }, | ||
642 | { 0 } | ||
643 | }; | ||
644 | |||
645 | MODULE_DEVICE_TABLE(pci, rar_pci_id_tbl); | ||
646 | |||
647 | /* field for registering driver to PCI device */ | ||
648 | static struct pci_driver rar_pci_driver = { | ||
649 | .name = "rar_register_driver", | ||
650 | .id_table = rar_pci_id_tbl, | ||
651 | .probe = rar_probe, | ||
652 | /* Cannot be unplugged - no remove */ | ||
653 | }; | ||
654 | |||
655 | static int __init rar_init_handler(void) | ||
656 | { | ||
657 | return pci_register_driver(&rar_pci_driver); | ||
658 | } | ||
659 | |||
660 | static void __exit rar_exit_handler(void) | ||
661 | { | ||
662 | pci_unregister_driver(&rar_pci_driver); | ||
663 | } | ||
664 | |||
665 | module_init(rar_init_handler); | ||
666 | module_exit(rar_exit_handler); | ||
667 | |||
668 | MODULE_LICENSE("GPL"); | ||
669 | MODULE_DESCRIPTION("Intel Restricted Access Region Register Driver"); | ||
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index f00d0d1e0653..9215ed72bece 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c | |||
@@ -159,7 +159,7 @@ static inline int busy_loop(void) /* Wait till scu status is busy */ | |||
159 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ | 159 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ |
160 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) | 160 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) |
161 | { | 161 | { |
162 | int i, nc, bytes, d; | 162 | int nc; |
163 | u32 offset = 0; | 163 | u32 offset = 0; |
164 | int err; | 164 | int err; |
165 | u8 cbuf[IPC_WWBUF_SIZE] = { }; | 165 | u8 cbuf[IPC_WWBUF_SIZE] = { }; |
@@ -174,55 +174,34 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) | |||
174 | return -ENODEV; | 174 | return -ENODEV; |
175 | } | 175 | } |
176 | 176 | ||
177 | if (platform != MRST_CPU_CHIP_PENWELL) { | 177 | for (nc = 0; nc < count; nc++, offset += 2) { |
178 | bytes = 0; | 178 | cbuf[offset] = addr[nc]; |
179 | d = 0; | 179 | cbuf[offset + 1] = addr[nc] >> 8; |
180 | for (i = 0; i < count; i++) { | 180 | } |
181 | cbuf[bytes++] = addr[i]; | ||
182 | cbuf[bytes++] = addr[i] >> 8; | ||
183 | if (id != IPC_CMD_PCNTRL_R) | ||
184 | cbuf[bytes++] = data[d++]; | ||
185 | if (id == IPC_CMD_PCNTRL_M) | ||
186 | cbuf[bytes++] = data[d++]; | ||
187 | } | ||
188 | for (i = 0; i < bytes; i += 4) | ||
189 | ipc_data_writel(wbuf[i/4], i); | ||
190 | ipc_command(bytes << 16 | id << 12 | 0 << 8 | op); | ||
191 | } else { | ||
192 | for (nc = 0; nc < count; nc++, offset += 2) { | ||
193 | cbuf[offset] = addr[nc]; | ||
194 | cbuf[offset + 1] = addr[nc] >> 8; | ||
195 | } | ||
196 | 181 | ||
197 | if (id == IPC_CMD_PCNTRL_R) { | 182 | if (id == IPC_CMD_PCNTRL_R) { |
198 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) | 183 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) |
199 | ipc_data_writel(wbuf[nc], offset); | 184 | ipc_data_writel(wbuf[nc], offset); |
200 | ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op); | 185 | ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op); |
201 | } else if (id == IPC_CMD_PCNTRL_W) { | 186 | } else if (id == IPC_CMD_PCNTRL_W) { |
202 | for (nc = 0; nc < count; nc++, offset += 1) | 187 | for (nc = 0; nc < count; nc++, offset += 1) |
203 | cbuf[offset] = data[nc]; | 188 | cbuf[offset] = data[nc]; |
204 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) | 189 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) |
205 | ipc_data_writel(wbuf[nc], offset); | 190 | ipc_data_writel(wbuf[nc], offset); |
206 | ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op); | 191 | ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op); |
207 | } else if (id == IPC_CMD_PCNTRL_M) { | 192 | } else if (id == IPC_CMD_PCNTRL_M) { |
208 | cbuf[offset] = data[0]; | 193 | cbuf[offset] = data[0]; |
209 | cbuf[offset + 1] = data[1]; | 194 | cbuf[offset + 1] = data[1]; |
210 | ipc_data_writel(wbuf[0], 0); /* Write wbuff */ | 195 | ipc_data_writel(wbuf[0], 0); /* Write wbuff */ |
211 | ipc_command(4 << 16 | id << 12 | 0 << 8 | op); | 196 | ipc_command(4 << 16 | id << 12 | 0 << 8 | op); |
212 | } | ||
213 | } | 197 | } |
214 | 198 | ||
215 | err = busy_loop(); | 199 | err = busy_loop(); |
216 | if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ | 200 | if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ |
217 | /* Workaround: values are read as 0 without memcpy_fromio */ | 201 | /* Workaround: values are read as 0 without memcpy_fromio */ |
218 | memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); | 202 | memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); |
219 | if (platform != MRST_CPU_CHIP_PENWELL) { | 203 | for (nc = 0; nc < count; nc++) |
220 | for (nc = 0, offset = 2; nc < count; nc++, offset += 3) | 204 | data[nc] = ipc_data_readb(nc); |
221 | data[nc] = ipc_data_readb(offset); | ||
222 | } else { | ||
223 | for (nc = 0; nc < count; nc++) | ||
224 | data[nc] = ipc_data_readb(nc); | ||
225 | } | ||
226 | } | 205 | } |
227 | mutex_unlock(&ipclock); | 206 | mutex_unlock(&ipclock); |
228 | return err; | 207 | return err; |
@@ -503,148 +482,6 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) | |||
503 | } | 482 | } |
504 | EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); | 483 | EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); |
505 | 484 | ||
506 | #define IPC_FW_LOAD_ADDR 0xFFFC0000 /* Storage location for FW image */ | ||
507 | #define IPC_FW_UPDATE_MBOX_ADDR 0xFFFFDFF4 /* Mailbox between ipc and scu */ | ||
508 | #define IPC_MAX_FW_SIZE 262144 /* 256K storage size for loading the FW image */ | ||
509 | #define IPC_FW_MIP_HEADER_SIZE 2048 /* Firmware MIP header size */ | ||
510 | /* IPC inform SCU to get ready for update process */ | ||
511 | #define IPC_CMD_FW_UPDATE_READY 0x10FE | ||
512 | /* IPC inform SCU to go for update process */ | ||
513 | #define IPC_CMD_FW_UPDATE_GO 0x20FE | ||
514 | /* Status code for fw update */ | ||
515 | #define IPC_FW_UPDATE_SUCCESS 0x444f4e45 /* Status code 'DONE' */ | ||
516 | #define IPC_FW_UPDATE_BADN 0x4241444E /* Status code 'BADN' */ | ||
517 | #define IPC_FW_TXHIGH 0x54784849 /* Status code 'IPC_FW_TXHIGH' */ | ||
518 | #define IPC_FW_TXLOW 0x54784c4f /* Status code 'IPC_FW_TXLOW' */ | ||
519 | |||
520 | struct fw_update_mailbox { | ||
521 | u32 status; | ||
522 | u32 scu_flag; | ||
523 | u32 driver_flag; | ||
524 | }; | ||
525 | |||
526 | |||
527 | /** | ||
528 | * intel_scu_ipc_fw_update - Firmware update utility | ||
529 | * @buffer: firmware buffer | ||
530 | * @length: size of firmware buffer | ||
531 | * | ||
532 | * This function provides an interface to load the firmware into | ||
533 | * the SCU. Returns 0 on success or -1 on failure | ||
534 | */ | ||
535 | int intel_scu_ipc_fw_update(u8 *buffer, u32 length) | ||
536 | { | ||
537 | void __iomem *fw_update_base; | ||
538 | struct fw_update_mailbox __iomem *mailbox = NULL; | ||
539 | int retry_cnt = 0; | ||
540 | u32 status; | ||
541 | |||
542 | mutex_lock(&ipclock); | ||
543 | fw_update_base = ioremap_nocache(IPC_FW_LOAD_ADDR, (128*1024)); | ||
544 | if (fw_update_base == NULL) { | ||
545 | mutex_unlock(&ipclock); | ||
546 | return -ENOMEM; | ||
547 | } | ||
548 | mailbox = ioremap_nocache(IPC_FW_UPDATE_MBOX_ADDR, | ||
549 | sizeof(struct fw_update_mailbox)); | ||
550 | if (mailbox == NULL) { | ||
551 | iounmap(fw_update_base); | ||
552 | mutex_unlock(&ipclock); | ||
553 | return -ENOMEM; | ||
554 | } | ||
555 | |||
556 | ipc_command(IPC_CMD_FW_UPDATE_READY); | ||
557 | |||
558 | /* Intitialize mailbox */ | ||
559 | writel(0, &mailbox->status); | ||
560 | writel(0, &mailbox->scu_flag); | ||
561 | writel(0, &mailbox->driver_flag); | ||
562 | |||
563 | /* Driver copies the 2KB MIP header to SRAM at 0xFFFC0000*/ | ||
564 | memcpy_toio(fw_update_base, buffer, 0x800); | ||
565 | |||
566 | /* Driver sends "FW Update" IPC command (CMD_ID 0xFE; MSG_ID 0x02). | ||
567 | * Upon receiving this command, SCU will write the 2K MIP header | ||
568 | * from 0xFFFC0000 into NAND. | ||
569 | * SCU will write a status code into the Mailbox, and then set scu_flag. | ||
570 | */ | ||
571 | |||
572 | ipc_command(IPC_CMD_FW_UPDATE_GO); | ||
573 | |||
574 | /*Driver stalls until scu_flag is set */ | ||
575 | while (readl(&mailbox->scu_flag) != 1) { | ||
576 | rmb(); | ||
577 | mdelay(1); | ||
578 | } | ||
579 | |||
580 | /* Driver checks Mailbox status. | ||
581 | * If the status is 'BADN', then abort (bad NAND). | ||
582 | * If the status is 'IPC_FW_TXLOW', then continue. | ||
583 | */ | ||
584 | while (readl(&mailbox->status) != IPC_FW_TXLOW) { | ||
585 | rmb(); | ||
586 | mdelay(10); | ||
587 | } | ||
588 | mdelay(10); | ||
589 | |||
590 | update_retry: | ||
591 | if (retry_cnt > 5) | ||
592 | goto update_end; | ||
593 | |||
594 | if (readl(&mailbox->status) != IPC_FW_TXLOW) | ||
595 | goto update_end; | ||
596 | buffer = buffer + 0x800; | ||
597 | memcpy_toio(fw_update_base, buffer, 0x20000); | ||
598 | writel(1, &mailbox->driver_flag); | ||
599 | while (readl(&mailbox->scu_flag) == 1) { | ||
600 | rmb(); | ||
601 | mdelay(1); | ||
602 | } | ||
603 | |||
604 | /* check for 'BADN' */ | ||
605 | if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) | ||
606 | goto update_end; | ||
607 | |||
608 | while (readl(&mailbox->status) != IPC_FW_TXHIGH) { | ||
609 | rmb(); | ||
610 | mdelay(10); | ||
611 | } | ||
612 | mdelay(10); | ||
613 | |||
614 | if (readl(&mailbox->status) != IPC_FW_TXHIGH) | ||
615 | goto update_end; | ||
616 | |||
617 | buffer = buffer + 0x20000; | ||
618 | memcpy_toio(fw_update_base, buffer, 0x20000); | ||
619 | writel(0, &mailbox->driver_flag); | ||
620 | |||
621 | while (mailbox->scu_flag == 0) { | ||
622 | rmb(); | ||
623 | mdelay(1); | ||
624 | } | ||
625 | |||
626 | /* check for 'BADN' */ | ||
627 | if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) | ||
628 | goto update_end; | ||
629 | |||
630 | if (readl(&mailbox->status) == IPC_FW_TXLOW) { | ||
631 | ++retry_cnt; | ||
632 | goto update_retry; | ||
633 | } | ||
634 | |||
635 | update_end: | ||
636 | status = readl(&mailbox->status); | ||
637 | |||
638 | iounmap(fw_update_base); | ||
639 | iounmap(mailbox); | ||
640 | mutex_unlock(&ipclock); | ||
641 | |||
642 | if (status == IPC_FW_UPDATE_SUCCESS) | ||
643 | return 0; | ||
644 | return -EIO; | ||
645 | } | ||
646 | EXPORT_SYMBOL(intel_scu_ipc_fw_update); | ||
647 | |||
648 | /* | 485 | /* |
649 | * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 | 486 | * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 |
650 | * When ioc bit is set to 1, caller api must wait for interrupt handler called | 487 | * When ioc bit is set to 1, caller api must wait for interrupt handler called |
@@ -727,7 +564,6 @@ static void ipc_remove(struct pci_dev *pdev) | |||
727 | } | 564 | } |
728 | 565 | ||
729 | static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { | 566 | static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { |
730 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)}, | ||
731 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, | 567 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, |
732 | { 0,} | 568 | { 0,} |
733 | }; | 569 | }; |
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index 2d0f9136ea9a..02bc5a6343c3 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c | |||
@@ -26,13 +26,10 @@ | |||
26 | 26 | ||
27 | static int major; | 27 | static int major; |
28 | 28 | ||
29 | #define MAX_FW_SIZE 264192 | ||
30 | |||
31 | /* ioctl commnds */ | 29 | /* ioctl commnds */ |
32 | #define INTE_SCU_IPC_REGISTER_READ 0 | 30 | #define INTE_SCU_IPC_REGISTER_READ 0 |
33 | #define INTE_SCU_IPC_REGISTER_WRITE 1 | 31 | #define INTE_SCU_IPC_REGISTER_WRITE 1 |
34 | #define INTE_SCU_IPC_REGISTER_UPDATE 2 | 32 | #define INTE_SCU_IPC_REGISTER_UPDATE 2 |
35 | #define INTE_SCU_IPC_FW_UPDATE 0xA2 | ||
36 | 33 | ||
37 | struct scu_ipc_data { | 34 | struct scu_ipc_data { |
38 | u32 count; /* No. of registers */ | 35 | u32 count; /* No. of registers */ |
@@ -88,27 +85,14 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd, | |||
88 | if (!capable(CAP_SYS_RAWIO)) | 85 | if (!capable(CAP_SYS_RAWIO)) |
89 | return -EPERM; | 86 | return -EPERM; |
90 | 87 | ||
91 | if (cmd == INTE_SCU_IPC_FW_UPDATE) { | 88 | if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) |
92 | u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL); | 89 | return -EFAULT; |
93 | if (fwbuf == NULL) | 90 | ret = scu_reg_access(cmd, &data); |
94 | return -ENOMEM; | 91 | if (ret < 0) |
95 | if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) { | 92 | return ret; |
96 | kfree(fwbuf); | 93 | if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) |
97 | return -EFAULT; | 94 | return -EFAULT; |
98 | } | 95 | return 0; |
99 | ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE); | ||
100 | kfree(fwbuf); | ||
101 | return ret; | ||
102 | } else { | ||
103 | if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) | ||
104 | return -EFAULT; | ||
105 | ret = scu_reg_access(cmd, &data); | ||
106 | if (ret < 0) | ||
107 | return ret; | ||
108 | if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) | ||
109 | return -EFAULT; | ||
110 | return 0; | ||
111 | } | ||
112 | } | 96 | } |
113 | 97 | ||
114 | static const struct file_operations scu_ipc_fops = { | 98 | static const struct file_operations scu_ipc_fops = { |
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index fd73ea89b857..e2a34b42ddc1 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c | |||
@@ -17,10 +17,18 @@ | |||
17 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
18 | #include <linux/pci.h> | 18 | #include <linux/pci.h> |
19 | #include <linux/backlight.h> | 19 | #include <linux/backlight.h> |
20 | #include <linux/leds.h> | ||
20 | #include <linux/fb.h> | 21 | #include <linux/fb.h> |
21 | #include <linux/dmi.h> | 22 | #include <linux/dmi.h> |
22 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
23 | #include <linux/rfkill.h> | 24 | #include <linux/rfkill.h> |
25 | #include <linux/acpi.h> | ||
26 | #include <linux/seq_file.h> | ||
27 | #include <linux/debugfs.h> | ||
28 | #include <linux/ctype.h> | ||
29 | #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) | ||
30 | #include <acpi/video.h> | ||
31 | #endif | ||
24 | 32 | ||
25 | /* | 33 | /* |
26 | * This driver is needed because a number of Samsung laptops do not hook | 34 | * This driver is needed because a number of Samsung laptops do not hook |
@@ -41,9 +49,20 @@ | |||
41 | #define SABI_IFACE_COMPLETE 0x04 | 49 | #define SABI_IFACE_COMPLETE 0x04 |
42 | #define SABI_IFACE_DATA 0x05 | 50 | #define SABI_IFACE_DATA 0x05 |
43 | 51 | ||
44 | /* Structure to get data back to the calling function */ | 52 | #define WL_STATUS_WLAN 0x0 |
45 | struct sabi_retval { | 53 | #define WL_STATUS_BT 0x2 |
46 | u8 retval[20]; | 54 | |
55 | /* Structure get/set data using sabi */ | ||
56 | struct sabi_data { | ||
57 | union { | ||
58 | struct { | ||
59 | u32 d0; | ||
60 | u32 d1; | ||
61 | u16 d2; | ||
62 | u8 d3; | ||
63 | }; | ||
64 | u8 data[11]; | ||
65 | }; | ||
47 | }; | 66 | }; |
48 | 67 | ||
49 | struct sabi_header_offsets { | 68 | struct sabi_header_offsets { |
@@ -60,8 +79,8 @@ struct sabi_commands { | |||
60 | * Brightness is 0 - 8, as described above. | 79 | * Brightness is 0 - 8, as described above. |
61 | * Value 0 is for the BIOS to use | 80 | * Value 0 is for the BIOS to use |
62 | */ | 81 | */ |
63 | u8 get_brightness; | 82 | u16 get_brightness; |
64 | u8 set_brightness; | 83 | u16 set_brightness; |
65 | 84 | ||
66 | /* | 85 | /* |
67 | * first byte: | 86 | * first byte: |
@@ -72,40 +91,56 @@ struct sabi_commands { | |||
72 | * 0x03 - 3G is on | 91 | * 0x03 - 3G is on |
73 | * TODO, verify 3G is correct, that doesn't seem right... | 92 | * TODO, verify 3G is correct, that doesn't seem right... |
74 | */ | 93 | */ |
75 | u8 get_wireless_button; | 94 | u16 get_wireless_button; |
76 | u8 set_wireless_button; | 95 | u16 set_wireless_button; |
77 | 96 | ||
78 | /* 0 is off, 1 is on */ | 97 | /* 0 is off, 1 is on */ |
79 | u8 get_backlight; | 98 | u16 get_backlight; |
80 | u8 set_backlight; | 99 | u16 set_backlight; |
81 | 100 | ||
82 | /* | 101 | /* |
83 | * 0x80 or 0x00 - no action | 102 | * 0x80 or 0x00 - no action |
84 | * 0x81 - recovery key pressed | 103 | * 0x81 - recovery key pressed |
85 | */ | 104 | */ |
86 | u8 get_recovery_mode; | 105 | u16 get_recovery_mode; |
87 | u8 set_recovery_mode; | 106 | u16 set_recovery_mode; |
88 | 107 | ||
89 | /* | 108 | /* |
90 | * on seclinux: 0 is low, 1 is high, | 109 | * on seclinux: 0 is low, 1 is high, |
91 | * on swsmi: 0 is normal, 1 is silent, 2 is turbo | 110 | * on swsmi: 0 is normal, 1 is silent, 2 is turbo |
92 | */ | 111 | */ |
93 | u8 get_performance_level; | 112 | u16 get_performance_level; |
94 | u8 set_performance_level; | 113 | u16 set_performance_level; |
114 | |||
115 | /* 0x80 is off, 0x81 is on */ | ||
116 | u16 get_battery_life_extender; | ||
117 | u16 set_battery_life_extender; | ||
118 | |||
119 | /* 0x80 is off, 0x81 is on */ | ||
120 | u16 get_usb_charge; | ||
121 | u16 set_usb_charge; | ||
122 | |||
123 | /* the first byte is for bluetooth and the third one is for wlan */ | ||
124 | u16 get_wireless_status; | ||
125 | u16 set_wireless_status; | ||
126 | |||
127 | /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ | ||
128 | u16 kbd_backlight; | ||
95 | 129 | ||
96 | /* | 130 | /* |
97 | * Tell the BIOS that Linux is running on this machine. | 131 | * Tell the BIOS that Linux is running on this machine. |
98 | * 81 is on, 80 is off | 132 | * 81 is on, 80 is off |
99 | */ | 133 | */ |
100 | u8 set_linux; | 134 | u16 set_linux; |
101 | }; | 135 | }; |
102 | 136 | ||
103 | struct sabi_performance_level { | 137 | struct sabi_performance_level { |
104 | const char *name; | 138 | const char *name; |
105 | u8 value; | 139 | u16 value; |
106 | }; | 140 | }; |
107 | 141 | ||
108 | struct sabi_config { | 142 | struct sabi_config { |
143 | int sabi_version; | ||
109 | const char *test_string; | 144 | const char *test_string; |
110 | u16 main_function; | 145 | u16 main_function; |
111 | const struct sabi_header_offsets header_offsets; | 146 | const struct sabi_header_offsets header_offsets; |
@@ -117,6 +152,10 @@ struct sabi_config { | |||
117 | 152 | ||
118 | static const struct sabi_config sabi_configs[] = { | 153 | static const struct sabi_config sabi_configs[] = { |
119 | { | 154 | { |
155 | /* I don't know if it is really 2, but it it is | ||
156 | * less than 3 anyway */ | ||
157 | .sabi_version = 2, | ||
158 | |||
120 | .test_string = "SECLINUX", | 159 | .test_string = "SECLINUX", |
121 | 160 | ||
122 | .main_function = 0x4c49, | 161 | .main_function = 0x4c49, |
@@ -146,6 +185,17 @@ static const struct sabi_config sabi_configs[] = { | |||
146 | .get_performance_level = 0x08, | 185 | .get_performance_level = 0x08, |
147 | .set_performance_level = 0x09, | 186 | .set_performance_level = 0x09, |
148 | 187 | ||
188 | .get_battery_life_extender = 0xFFFF, | ||
189 | .set_battery_life_extender = 0xFFFF, | ||
190 | |||
191 | .get_usb_charge = 0xFFFF, | ||
192 | .set_usb_charge = 0xFFFF, | ||
193 | |||
194 | .get_wireless_status = 0xFFFF, | ||
195 | .set_wireless_status = 0xFFFF, | ||
196 | |||
197 | .kbd_backlight = 0xFFFF, | ||
198 | |||
149 | .set_linux = 0x0a, | 199 | .set_linux = 0x0a, |
150 | }, | 200 | }, |
151 | 201 | ||
@@ -164,6 +214,8 @@ static const struct sabi_config sabi_configs[] = { | |||
164 | .max_brightness = 8, | 214 | .max_brightness = 8, |
165 | }, | 215 | }, |
166 | { | 216 | { |
217 | .sabi_version = 3, | ||
218 | |||
167 | .test_string = "SwSmi@", | 219 | .test_string = "SwSmi@", |
168 | 220 | ||
169 | .main_function = 0x5843, | 221 | .main_function = 0x5843, |
@@ -193,6 +245,17 @@ static const struct sabi_config sabi_configs[] = { | |||
193 | .get_performance_level = 0x31, | 245 | .get_performance_level = 0x31, |
194 | .set_performance_level = 0x32, | 246 | .set_performance_level = 0x32, |
195 | 247 | ||
248 | .get_battery_life_extender = 0x65, | ||
249 | .set_battery_life_extender = 0x66, | ||
250 | |||
251 | .get_usb_charge = 0x67, | ||
252 | .set_usb_charge = 0x68, | ||
253 | |||
254 | .get_wireless_status = 0x69, | ||
255 | .set_wireless_status = 0x6a, | ||
256 | |||
257 | .kbd_backlight = 0x78, | ||
258 | |||
196 | .set_linux = 0xff, | 259 | .set_linux = 0xff, |
197 | }, | 260 | }, |
198 | 261 | ||
@@ -217,16 +280,82 @@ static const struct sabi_config sabi_configs[] = { | |||
217 | { }, | 280 | { }, |
218 | }; | 281 | }; |
219 | 282 | ||
220 | static const struct sabi_config *sabi_config; | 283 | /* |
284 | * samsung-laptop/ - debugfs root directory | ||
285 | * f0000_segment - dump f0000 segment | ||
286 | * command - current command | ||
287 | * data - current data | ||
288 | * d0, d1, d2, d3 - data fields | ||
289 | * call - call SABI using command and data | ||
290 | * | ||
291 | * This allow to call arbitrary sabi commands wihout | ||
292 | * modifying the driver at all. | ||
293 | * For example, setting the keyboard backlight brightness to 5 | ||
294 | * | ||
295 | * echo 0x78 > command | ||
296 | * echo 0x0582 > d0 | ||
297 | * echo 0 > d1 | ||
298 | * echo 0 > d2 | ||
299 | * echo 0 > d3 | ||
300 | * cat call | ||
301 | */ | ||
302 | |||
303 | struct samsung_laptop_debug { | ||
304 | struct dentry *root; | ||
305 | struct sabi_data data; | ||
306 | u16 command; | ||
307 | |||
308 | struct debugfs_blob_wrapper f0000_wrapper; | ||
309 | struct debugfs_blob_wrapper data_wrapper; | ||
310 | struct debugfs_blob_wrapper sdiag_wrapper; | ||
311 | }; | ||
312 | |||
313 | struct samsung_laptop; | ||
314 | |||
315 | struct samsung_rfkill { | ||
316 | struct samsung_laptop *samsung; | ||
317 | struct rfkill *rfkill; | ||
318 | enum rfkill_type type; | ||
319 | }; | ||
320 | |||
321 | struct samsung_laptop { | ||
322 | const struct sabi_config *config; | ||
323 | |||
324 | void __iomem *sabi; | ||
325 | void __iomem *sabi_iface; | ||
326 | void __iomem *f0000_segment; | ||
327 | |||
328 | struct mutex sabi_mutex; | ||
329 | |||
330 | struct platform_device *platform_device; | ||
331 | struct backlight_device *backlight_device; | ||
332 | |||
333 | struct samsung_rfkill wlan; | ||
334 | struct samsung_rfkill bluetooth; | ||
335 | |||
336 | struct led_classdev kbd_led; | ||
337 | int kbd_led_wk; | ||
338 | struct workqueue_struct *led_workqueue; | ||
339 | struct work_struct kbd_led_work; | ||
340 | |||
341 | struct samsung_laptop_debug debug; | ||
342 | struct samsung_quirks *quirks; | ||
343 | |||
344 | bool handle_backlight; | ||
345 | bool has_stepping_quirk; | ||
346 | |||
347 | char sdiag[64]; | ||
348 | }; | ||
349 | |||
350 | struct samsung_quirks { | ||
351 | bool broken_acpi_video; | ||
352 | }; | ||
353 | |||
354 | static struct samsung_quirks samsung_unknown = {}; | ||
221 | 355 | ||
222 | static void __iomem *sabi; | 356 | static struct samsung_quirks samsung_broken_acpi_video = { |
223 | static void __iomem *sabi_iface; | 357 | .broken_acpi_video = true, |
224 | static void __iomem *f0000_segment; | 358 | }; |
225 | static struct backlight_device *backlight_device; | ||
226 | static struct mutex sabi_mutex; | ||
227 | static struct platform_device *sdev; | ||
228 | static struct rfkill *rfk; | ||
229 | static bool has_stepping_quirk; | ||
230 | 359 | ||
231 | static bool force; | 360 | static bool force; |
232 | module_param(force, bool, 0); | 361 | module_param(force, bool, 0); |
@@ -237,176 +366,143 @@ static bool debug; | |||
237 | module_param(debug, bool, S_IRUGO | S_IWUSR); | 366 | module_param(debug, bool, S_IRUGO | S_IWUSR); |
238 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | 367 | MODULE_PARM_DESC(debug, "Debug enabled or not"); |
239 | 368 | ||
240 | static int sabi_get_command(u8 command, struct sabi_retval *sretval) | 369 | static int sabi_command(struct samsung_laptop *samsung, u16 command, |
370 | struct sabi_data *in, | ||
371 | struct sabi_data *out) | ||
241 | { | 372 | { |
242 | int retval = 0; | 373 | const struct sabi_config *config = samsung->config; |
243 | u16 port = readw(sabi + sabi_config->header_offsets.port); | 374 | int ret = 0; |
375 | u16 port = readw(samsung->sabi + config->header_offsets.port); | ||
244 | u8 complete, iface_data; | 376 | u8 complete, iface_data; |
245 | 377 | ||
246 | mutex_lock(&sabi_mutex); | 378 | mutex_lock(&samsung->sabi_mutex); |
247 | |||
248 | /* enable memory to be able to write to it */ | ||
249 | outb(readb(sabi + sabi_config->header_offsets.en_mem), port); | ||
250 | |||
251 | /* write out the command */ | ||
252 | writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN); | ||
253 | writew(command, sabi_iface + SABI_IFACE_SUB); | ||
254 | writeb(0, sabi_iface + SABI_IFACE_COMPLETE); | ||
255 | outb(readb(sabi + sabi_config->header_offsets.iface_func), port); | ||
256 | |||
257 | /* write protect memory to make it safe */ | ||
258 | outb(readb(sabi + sabi_config->header_offsets.re_mem), port); | ||
259 | 379 | ||
260 | /* see if the command actually succeeded */ | 380 | if (debug) { |
261 | complete = readb(sabi_iface + SABI_IFACE_COMPLETE); | 381 | if (in) |
262 | iface_data = readb(sabi_iface + SABI_IFACE_DATA); | 382 | pr_info("SABI command:0x%04x " |
263 | if (complete != 0xaa || iface_data == 0xff) { | 383 | "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", |
264 | pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n", | 384 | command, in->d0, in->d1, in->d2, in->d3); |
265 | command, complete, iface_data); | 385 | else |
266 | retval = -EINVAL; | 386 | pr_info("SABI command:0x%04x", command); |
267 | goto exit; | ||
268 | } | 387 | } |
269 | /* | ||
270 | * Save off the data into a structure so the caller use it. | ||
271 | * Right now we only want the first 4 bytes, | ||
272 | * There are commands that need more, but not for the ones we | ||
273 | * currently care about. | ||
274 | */ | ||
275 | sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA); | ||
276 | sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1); | ||
277 | sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2); | ||
278 | sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3); | ||
279 | |||
280 | exit: | ||
281 | mutex_unlock(&sabi_mutex); | ||
282 | return retval; | ||
283 | |||
284 | } | ||
285 | |||
286 | static int sabi_set_command(u8 command, u8 data) | ||
287 | { | ||
288 | int retval = 0; | ||
289 | u16 port = readw(sabi + sabi_config->header_offsets.port); | ||
290 | u8 complete, iface_data; | ||
291 | |||
292 | mutex_lock(&sabi_mutex); | ||
293 | 388 | ||
294 | /* enable memory to be able to write to it */ | 389 | /* enable memory to be able to write to it */ |
295 | outb(readb(sabi + sabi_config->header_offsets.en_mem), port); | 390 | outb(readb(samsung->sabi + config->header_offsets.en_mem), port); |
296 | 391 | ||
297 | /* write out the command */ | 392 | /* write out the command */ |
298 | writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN); | 393 | writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); |
299 | writew(command, sabi_iface + SABI_IFACE_SUB); | 394 | writew(command, samsung->sabi_iface + SABI_IFACE_SUB); |
300 | writeb(0, sabi_iface + SABI_IFACE_COMPLETE); | 395 | writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); |
301 | writeb(data, sabi_iface + SABI_IFACE_DATA); | 396 | if (in) { |
302 | outb(readb(sabi + sabi_config->header_offsets.iface_func), port); | 397 | writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA); |
398 | writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4); | ||
399 | writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8); | ||
400 | writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10); | ||
401 | } | ||
402 | outb(readb(samsung->sabi + config->header_offsets.iface_func), port); | ||
303 | 403 | ||
304 | /* write protect memory to make it safe */ | 404 | /* write protect memory to make it safe */ |
305 | outb(readb(sabi + sabi_config->header_offsets.re_mem), port); | 405 | outb(readb(samsung->sabi + config->header_offsets.re_mem), port); |
306 | 406 | ||
307 | /* see if the command actually succeeded */ | 407 | /* see if the command actually succeeded */ |
308 | complete = readb(sabi_iface + SABI_IFACE_COMPLETE); | 408 | complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); |
309 | iface_data = readb(sabi_iface + SABI_IFACE_DATA); | 409 | iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA); |
410 | |||
411 | /* iface_data = 0xFF happens when a command is not known | ||
412 | * so we only add a warning in debug mode since we will | ||
413 | * probably issue some unknown command at startup to find | ||
414 | * out which features are supported */ | ||
415 | if (complete != 0xaa || (iface_data == 0xff && debug)) | ||
416 | pr_warn("SABI command 0x%04x failed with" | ||
417 | " completion flag 0x%02x and interface data 0x%02x", | ||
418 | command, complete, iface_data); | ||
419 | |||
310 | if (complete != 0xaa || iface_data == 0xff) { | 420 | if (complete != 0xaa || iface_data == 0xff) { |
311 | pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n", | 421 | ret = -EINVAL; |
312 | command, complete, iface_data); | 422 | goto exit; |
313 | retval = -EINVAL; | ||
314 | } | 423 | } |
315 | 424 | ||
316 | mutex_unlock(&sabi_mutex); | 425 | if (out) { |
317 | return retval; | 426 | out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA); |
318 | } | 427 | out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4); |
319 | 428 | out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2); | |
320 | static void test_backlight(void) | 429 | out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); |
321 | { | 430 | } |
322 | struct sabi_retval sretval; | ||
323 | |||
324 | sabi_get_command(sabi_config->commands.get_backlight, &sretval); | ||
325 | printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); | ||
326 | |||
327 | sabi_set_command(sabi_config->commands.set_backlight, 0); | ||
328 | printk(KERN_DEBUG "backlight should be off\n"); | ||
329 | |||
330 | sabi_get_command(sabi_config->commands.get_backlight, &sretval); | ||
331 | printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); | ||
332 | |||
333 | msleep(1000); | ||
334 | 431 | ||
335 | sabi_set_command(sabi_config->commands.set_backlight, 1); | 432 | if (debug && out) { |
336 | printk(KERN_DEBUG "backlight should be on\n"); | 433 | pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", |
434 | out->d0, out->d1, out->d2, out->d3); | ||
435 | } | ||
337 | 436 | ||
338 | sabi_get_command(sabi_config->commands.get_backlight, &sretval); | 437 | exit: |
339 | printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); | 438 | mutex_unlock(&samsung->sabi_mutex); |
439 | return ret; | ||
340 | } | 440 | } |
341 | 441 | ||
342 | static void test_wireless(void) | 442 | /* simple wrappers usable with most commands */ |
443 | static int sabi_set_commandb(struct samsung_laptop *samsung, | ||
444 | u16 command, u8 data) | ||
343 | { | 445 | { |
344 | struct sabi_retval sretval; | 446 | struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } }; |
345 | |||
346 | sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); | ||
347 | printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); | ||
348 | |||
349 | sabi_set_command(sabi_config->commands.set_wireless_button, 0); | ||
350 | printk(KERN_DEBUG "wireless led should be off\n"); | ||
351 | |||
352 | sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); | ||
353 | printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); | ||
354 | 447 | ||
355 | msleep(1000); | 448 | in.data[0] = data; |
356 | 449 | return sabi_command(samsung, command, &in, NULL); | |
357 | sabi_set_command(sabi_config->commands.set_wireless_button, 1); | ||
358 | printk(KERN_DEBUG "wireless led should be on\n"); | ||
359 | |||
360 | sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); | ||
361 | printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); | ||
362 | } | 450 | } |
363 | 451 | ||
364 | static u8 read_brightness(void) | 452 | static int read_brightness(struct samsung_laptop *samsung) |
365 | { | 453 | { |
366 | struct sabi_retval sretval; | 454 | const struct sabi_config *config = samsung->config; |
455 | const struct sabi_commands *commands = &samsung->config->commands; | ||
456 | struct sabi_data sretval; | ||
367 | int user_brightness = 0; | 457 | int user_brightness = 0; |
368 | int retval; | 458 | int retval; |
369 | 459 | ||
370 | retval = sabi_get_command(sabi_config->commands.get_brightness, | 460 | retval = sabi_command(samsung, commands->get_brightness, |
371 | &sretval); | 461 | NULL, &sretval); |
372 | if (!retval) { | 462 | if (retval) |
373 | user_brightness = sretval.retval[0]; | 463 | return retval; |
374 | if (user_brightness > sabi_config->min_brightness) | 464 | |
375 | user_brightness -= sabi_config->min_brightness; | 465 | user_brightness = sretval.data[0]; |
376 | else | 466 | if (user_brightness > config->min_brightness) |
377 | user_brightness = 0; | 467 | user_brightness -= config->min_brightness; |
378 | } | 468 | else |
469 | user_brightness = 0; | ||
470 | |||
379 | return user_brightness; | 471 | return user_brightness; |
380 | } | 472 | } |
381 | 473 | ||
382 | static void set_brightness(u8 user_brightness) | 474 | static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) |
383 | { | 475 | { |
384 | u8 user_level = user_brightness + sabi_config->min_brightness; | 476 | const struct sabi_config *config = samsung->config; |
477 | const struct sabi_commands *commands = &samsung->config->commands; | ||
478 | u8 user_level = user_brightness + config->min_brightness; | ||
385 | 479 | ||
386 | if (has_stepping_quirk && user_level != 0) { | 480 | if (samsung->has_stepping_quirk && user_level != 0) { |
387 | /* | 481 | /* |
388 | * short circuit if the specified level is what's already set | 482 | * short circuit if the specified level is what's already set |
389 | * to prevent the screen from flickering needlessly | 483 | * to prevent the screen from flickering needlessly |
390 | */ | 484 | */ |
391 | if (user_brightness == read_brightness()) | 485 | if (user_brightness == read_brightness(samsung)) |
392 | return; | 486 | return; |
393 | 487 | ||
394 | sabi_set_command(sabi_config->commands.set_brightness, 0); | 488 | sabi_set_commandb(samsung, commands->set_brightness, 0); |
395 | } | 489 | } |
396 | 490 | ||
397 | sabi_set_command(sabi_config->commands.set_brightness, user_level); | 491 | sabi_set_commandb(samsung, commands->set_brightness, user_level); |
398 | } | 492 | } |
399 | 493 | ||
400 | static int get_brightness(struct backlight_device *bd) | 494 | static int get_brightness(struct backlight_device *bd) |
401 | { | 495 | { |
402 | return (int)read_brightness(); | 496 | struct samsung_laptop *samsung = bl_get_data(bd); |
497 | |||
498 | return read_brightness(samsung); | ||
403 | } | 499 | } |
404 | 500 | ||
405 | static void check_for_stepping_quirk(void) | 501 | static void check_for_stepping_quirk(struct samsung_laptop *samsung) |
406 | { | 502 | { |
407 | u8 initial_level; | 503 | int initial_level; |
408 | u8 check_level; | 504 | int check_level; |
409 | u8 orig_level = read_brightness(); | 505 | int orig_level = read_brightness(samsung); |
410 | 506 | ||
411 | /* | 507 | /* |
412 | * Some laptops exhibit the strange behaviour of stepping toward | 508 | * Some laptops exhibit the strange behaviour of stepping toward |
@@ -416,34 +512,38 @@ static void check_for_stepping_quirk(void) | |||
416 | */ | 512 | */ |
417 | 513 | ||
418 | if (orig_level == 0) | 514 | if (orig_level == 0) |
419 | set_brightness(1); | 515 | set_brightness(samsung, 1); |
420 | 516 | ||
421 | initial_level = read_brightness(); | 517 | initial_level = read_brightness(samsung); |
422 | 518 | ||
423 | if (initial_level <= 2) | 519 | if (initial_level <= 2) |
424 | check_level = initial_level + 2; | 520 | check_level = initial_level + 2; |
425 | else | 521 | else |
426 | check_level = initial_level - 2; | 522 | check_level = initial_level - 2; |
427 | 523 | ||
428 | has_stepping_quirk = false; | 524 | samsung->has_stepping_quirk = false; |
429 | set_brightness(check_level); | 525 | set_brightness(samsung, check_level); |
430 | 526 | ||
431 | if (read_brightness() != check_level) { | 527 | if (read_brightness(samsung) != check_level) { |
432 | has_stepping_quirk = true; | 528 | samsung->has_stepping_quirk = true; |
433 | pr_info("enabled workaround for brightness stepping quirk\n"); | 529 | pr_info("enabled workaround for brightness stepping quirk\n"); |
434 | } | 530 | } |
435 | 531 | ||
436 | set_brightness(orig_level); | 532 | set_brightness(samsung, orig_level); |
437 | } | 533 | } |
438 | 534 | ||
439 | static int update_status(struct backlight_device *bd) | 535 | static int update_status(struct backlight_device *bd) |
440 | { | 536 | { |
441 | set_brightness(bd->props.brightness); | 537 | struct samsung_laptop *samsung = bl_get_data(bd); |
538 | const struct sabi_commands *commands = &samsung->config->commands; | ||
539 | |||
540 | set_brightness(samsung, bd->props.brightness); | ||
442 | 541 | ||
443 | if (bd->props.power == FB_BLANK_UNBLANK) | 542 | if (bd->props.power == FB_BLANK_UNBLANK) |
444 | sabi_set_command(sabi_config->commands.set_backlight, 1); | 543 | sabi_set_commandb(samsung, commands->set_backlight, 1); |
445 | else | 544 | else |
446 | sabi_set_command(sabi_config->commands.set_backlight, 0); | 545 | sabi_set_commandb(samsung, commands->set_backlight, 0); |
546 | |||
447 | return 0; | 547 | return 0; |
448 | } | 548 | } |
449 | 549 | ||
@@ -452,66 +552,101 @@ static const struct backlight_ops backlight_ops = { | |||
452 | .update_status = update_status, | 552 | .update_status = update_status, |
453 | }; | 553 | }; |
454 | 554 | ||
455 | static int rfkill_set(void *data, bool blocked) | 555 | static int seclinux_rfkill_set(void *data, bool blocked) |
456 | { | 556 | { |
457 | /* Do something with blocked...*/ | 557 | struct samsung_rfkill *srfkill = data; |
458 | /* | 558 | struct samsung_laptop *samsung = srfkill->samsung; |
459 | * blocked == false is on | 559 | const struct sabi_commands *commands = &samsung->config->commands; |
460 | * blocked == true is off | ||
461 | */ | ||
462 | if (blocked) | ||
463 | sabi_set_command(sabi_config->commands.set_wireless_button, 0); | ||
464 | else | ||
465 | sabi_set_command(sabi_config->commands.set_wireless_button, 1); | ||
466 | 560 | ||
467 | return 0; | 561 | return sabi_set_commandb(samsung, commands->set_wireless_button, |
562 | !blocked); | ||
468 | } | 563 | } |
469 | 564 | ||
470 | static struct rfkill_ops rfkill_ops = { | 565 | static struct rfkill_ops seclinux_rfkill_ops = { |
471 | .set_block = rfkill_set, | 566 | .set_block = seclinux_rfkill_set, |
472 | }; | 567 | }; |
473 | 568 | ||
474 | static int init_wireless(struct platform_device *sdev) | 569 | static int swsmi_wireless_status(struct samsung_laptop *samsung, |
570 | struct sabi_data *data) | ||
475 | { | 571 | { |
476 | int retval; | 572 | const struct sabi_commands *commands = &samsung->config->commands; |
477 | 573 | ||
478 | rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN, | 574 | return sabi_command(samsung, commands->get_wireless_status, |
479 | &rfkill_ops, NULL); | 575 | NULL, data); |
480 | if (!rfk) | 576 | } |
481 | return -ENOMEM; | ||
482 | |||
483 | retval = rfkill_register(rfk); | ||
484 | if (retval) { | ||
485 | rfkill_destroy(rfk); | ||
486 | return -ENODEV; | ||
487 | } | ||
488 | 577 | ||
489 | return 0; | 578 | static int swsmi_rfkill_set(void *priv, bool blocked) |
579 | { | ||
580 | struct samsung_rfkill *srfkill = priv; | ||
581 | struct samsung_laptop *samsung = srfkill->samsung; | ||
582 | const struct sabi_commands *commands = &samsung->config->commands; | ||
583 | struct sabi_data data; | ||
584 | int ret, i; | ||
585 | |||
586 | ret = swsmi_wireless_status(samsung, &data); | ||
587 | if (ret) | ||
588 | return ret; | ||
589 | |||
590 | /* Don't set the state for non-present devices */ | ||
591 | for (i = 0; i < 4; i++) | ||
592 | if (data.data[i] == 0x02) | ||
593 | data.data[1] = 0; | ||
594 | |||
595 | if (srfkill->type == RFKILL_TYPE_WLAN) | ||
596 | data.data[WL_STATUS_WLAN] = !blocked; | ||
597 | else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) | ||
598 | data.data[WL_STATUS_BT] = !blocked; | ||
599 | |||
600 | return sabi_command(samsung, commands->set_wireless_status, | ||
601 | &data, &data); | ||
490 | } | 602 | } |
491 | 603 | ||
492 | static void destroy_wireless(void) | 604 | static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) |
493 | { | 605 | { |
494 | rfkill_unregister(rfk); | 606 | struct samsung_rfkill *srfkill = priv; |
495 | rfkill_destroy(rfk); | 607 | struct samsung_laptop *samsung = srfkill->samsung; |
608 | struct sabi_data data; | ||
609 | int ret; | ||
610 | |||
611 | ret = swsmi_wireless_status(samsung, &data); | ||
612 | if (ret) | ||
613 | return ; | ||
614 | |||
615 | if (srfkill->type == RFKILL_TYPE_WLAN) | ||
616 | ret = data.data[WL_STATUS_WLAN]; | ||
617 | else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) | ||
618 | ret = data.data[WL_STATUS_BT]; | ||
619 | else | ||
620 | return ; | ||
621 | |||
622 | rfkill_set_sw_state(rfkill, !ret); | ||
496 | } | 623 | } |
497 | 624 | ||
625 | static struct rfkill_ops swsmi_rfkill_ops = { | ||
626 | .set_block = swsmi_rfkill_set, | ||
627 | .query = swsmi_rfkill_query, | ||
628 | }; | ||
629 | |||
498 | static ssize_t get_performance_level(struct device *dev, | 630 | static ssize_t get_performance_level(struct device *dev, |
499 | struct device_attribute *attr, char *buf) | 631 | struct device_attribute *attr, char *buf) |
500 | { | 632 | { |
501 | struct sabi_retval sretval; | 633 | struct samsung_laptop *samsung = dev_get_drvdata(dev); |
634 | const struct sabi_config *config = samsung->config; | ||
635 | const struct sabi_commands *commands = &config->commands; | ||
636 | struct sabi_data sretval; | ||
502 | int retval; | 637 | int retval; |
503 | int i; | 638 | int i; |
504 | 639 | ||
505 | /* Read the state */ | 640 | /* Read the state */ |
506 | retval = sabi_get_command(sabi_config->commands.get_performance_level, | 641 | retval = sabi_command(samsung, commands->get_performance_level, |
507 | &sretval); | 642 | NULL, &sretval); |
508 | if (retval) | 643 | if (retval) |
509 | return retval; | 644 | return retval; |
510 | 645 | ||
511 | /* The logic is backwards, yeah, lots of fun... */ | 646 | /* The logic is backwards, yeah, lots of fun... */ |
512 | for (i = 0; sabi_config->performance_levels[i].name; ++i) { | 647 | for (i = 0; config->performance_levels[i].name; ++i) { |
513 | if (sretval.retval[0] == sabi_config->performance_levels[i].value) | 648 | if (sretval.data[0] == config->performance_levels[i].value) |
514 | return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name); | 649 | return sprintf(buf, "%s\n", config->performance_levels[i].name); |
515 | } | 650 | } |
516 | return sprintf(buf, "%s\n", "unknown"); | 651 | return sprintf(buf, "%s\n", "unknown"); |
517 | } | 652 | } |
@@ -520,269 +655,178 @@ static ssize_t set_performance_level(struct device *dev, | |||
520 | struct device_attribute *attr, const char *buf, | 655 | struct device_attribute *attr, const char *buf, |
521 | size_t count) | 656 | size_t count) |
522 | { | 657 | { |
523 | if (count >= 1) { | 658 | struct samsung_laptop *samsung = dev_get_drvdata(dev); |
524 | int i; | 659 | const struct sabi_config *config = samsung->config; |
525 | for (i = 0; sabi_config->performance_levels[i].name; ++i) { | 660 | const struct sabi_commands *commands = &config->commands; |
526 | const struct sabi_performance_level *level = | 661 | int i; |
527 | &sabi_config->performance_levels[i]; | 662 | |
528 | if (!strncasecmp(level->name, buf, strlen(level->name))) { | 663 | if (count < 1) |
529 | sabi_set_command(sabi_config->commands.set_performance_level, | 664 | return count; |
530 | level->value); | 665 | |
531 | break; | 666 | for (i = 0; config->performance_levels[i].name; ++i) { |
532 | } | 667 | const struct sabi_performance_level *level = |
668 | &config->performance_levels[i]; | ||
669 | if (!strncasecmp(level->name, buf, strlen(level->name))) { | ||
670 | sabi_set_commandb(samsung, | ||
671 | commands->set_performance_level, | ||
672 | level->value); | ||
673 | break; | ||
533 | } | 674 | } |
534 | if (!sabi_config->performance_levels[i].name) | ||
535 | return -EINVAL; | ||
536 | } | 675 | } |
676 | |||
677 | if (!config->performance_levels[i].name) | ||
678 | return -EINVAL; | ||
679 | |||
537 | return count; | 680 | return count; |
538 | } | 681 | } |
682 | |||
539 | static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, | 683 | static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, |
540 | get_performance_level, set_performance_level); | 684 | get_performance_level, set_performance_level); |
541 | 685 | ||
686 | static int read_battery_life_extender(struct samsung_laptop *samsung) | ||
687 | { | ||
688 | const struct sabi_commands *commands = &samsung->config->commands; | ||
689 | struct sabi_data data; | ||
690 | int retval; | ||
691 | |||
692 | if (commands->get_battery_life_extender == 0xFFFF) | ||
693 | return -ENODEV; | ||
694 | |||
695 | memset(&data, 0, sizeof(data)); | ||
696 | data.data[0] = 0x80; | ||
697 | retval = sabi_command(samsung, commands->get_battery_life_extender, | ||
698 | &data, &data); | ||
542 | 699 | ||
543 | static int __init dmi_check_cb(const struct dmi_system_id *id) | 700 | if (retval) |
701 | return retval; | ||
702 | |||
703 | if (data.data[0] != 0 && data.data[0] != 1) | ||
704 | return -ENODEV; | ||
705 | |||
706 | return data.data[0]; | ||
707 | } | ||
708 | |||
709 | static int write_battery_life_extender(struct samsung_laptop *samsung, | ||
710 | int enabled) | ||
544 | { | 711 | { |
545 | pr_info("found laptop model '%s'\n", | 712 | const struct sabi_commands *commands = &samsung->config->commands; |
546 | id->ident); | 713 | struct sabi_data data; |
547 | return 1; | 714 | |
715 | memset(&data, 0, sizeof(data)); | ||
716 | data.data[0] = 0x80 | enabled; | ||
717 | return sabi_command(samsung, commands->set_battery_life_extender, | ||
718 | &data, NULL); | ||
548 | } | 719 | } |
549 | 720 | ||
550 | static struct dmi_system_id __initdata samsung_dmi_table[] = { | 721 | static ssize_t get_battery_life_extender(struct device *dev, |
551 | { | 722 | struct device_attribute *attr, |
552 | .ident = "N128", | 723 | char *buf) |
553 | .matches = { | 724 | { |
554 | DMI_MATCH(DMI_SYS_VENDOR, | 725 | struct samsung_laptop *samsung = dev_get_drvdata(dev); |
555 | "SAMSUNG ELECTRONICS CO., LTD."), | 726 | int ret; |
556 | DMI_MATCH(DMI_PRODUCT_NAME, "N128"), | 727 | |
557 | DMI_MATCH(DMI_BOARD_NAME, "N128"), | 728 | ret = read_battery_life_extender(samsung); |
558 | }, | 729 | if (ret < 0) |
559 | .callback = dmi_check_cb, | 730 | return ret; |
560 | }, | 731 | |
561 | { | 732 | return sprintf(buf, "%d\n", ret); |
562 | .ident = "N130", | 733 | } |
563 | .matches = { | 734 | |
564 | DMI_MATCH(DMI_SYS_VENDOR, | 735 | static ssize_t set_battery_life_extender(struct device *dev, |
565 | "SAMSUNG ELECTRONICS CO., LTD."), | 736 | struct device_attribute *attr, |
566 | DMI_MATCH(DMI_PRODUCT_NAME, "N130"), | 737 | const char *buf, size_t count) |
567 | DMI_MATCH(DMI_BOARD_NAME, "N130"), | 738 | { |
568 | }, | 739 | struct samsung_laptop *samsung = dev_get_drvdata(dev); |
569 | .callback = dmi_check_cb, | 740 | int ret, value; |
570 | }, | 741 | |
571 | { | 742 | if (!count || sscanf(buf, "%i", &value) != 1) |
572 | .ident = "N510", | 743 | return -EINVAL; |
573 | .matches = { | 744 | |
574 | DMI_MATCH(DMI_SYS_VENDOR, | 745 | ret = write_battery_life_extender(samsung, !!value); |
575 | "SAMSUNG ELECTRONICS CO., LTD."), | 746 | if (ret < 0) |
576 | DMI_MATCH(DMI_PRODUCT_NAME, "N510"), | 747 | return ret; |
577 | DMI_MATCH(DMI_BOARD_NAME, "N510"), | 748 | |
578 | }, | 749 | return count; |
579 | .callback = dmi_check_cb, | 750 | } |
580 | }, | 751 | |
581 | { | 752 | static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, |
582 | .ident = "X125", | 753 | get_battery_life_extender, set_battery_life_extender); |
583 | .matches = { | 754 | |
584 | DMI_MATCH(DMI_SYS_VENDOR, | 755 | static int read_usb_charge(struct samsung_laptop *samsung) |
585 | "SAMSUNG ELECTRONICS CO., LTD."), | 756 | { |
586 | DMI_MATCH(DMI_PRODUCT_NAME, "X125"), | 757 | const struct sabi_commands *commands = &samsung->config->commands; |
587 | DMI_MATCH(DMI_BOARD_NAME, "X125"), | 758 | struct sabi_data data; |
588 | }, | 759 | int retval; |
589 | .callback = dmi_check_cb, | 760 | |
590 | }, | 761 | if (commands->get_usb_charge == 0xFFFF) |
591 | { | 762 | return -ENODEV; |
592 | .ident = "X120/X170", | 763 | |
593 | .matches = { | 764 | memset(&data, 0, sizeof(data)); |
594 | DMI_MATCH(DMI_SYS_VENDOR, | 765 | data.data[0] = 0x80; |
595 | "SAMSUNG ELECTRONICS CO., LTD."), | 766 | retval = sabi_command(samsung, commands->get_usb_charge, |
596 | DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"), | 767 | &data, &data); |
597 | DMI_MATCH(DMI_BOARD_NAME, "X120/X170"), | 768 | |
598 | }, | 769 | if (retval) |
599 | .callback = dmi_check_cb, | 770 | return retval; |
600 | }, | 771 | |
601 | { | 772 | if (data.data[0] != 0 && data.data[0] != 1) |
602 | .ident = "NC10", | 773 | return -ENODEV; |
603 | .matches = { | 774 | |
604 | DMI_MATCH(DMI_SYS_VENDOR, | 775 | return data.data[0]; |
605 | "SAMSUNG ELECTRONICS CO., LTD."), | 776 | } |
606 | DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), | 777 | |
607 | DMI_MATCH(DMI_BOARD_NAME, "NC10"), | 778 | static int write_usb_charge(struct samsung_laptop *samsung, |
608 | }, | 779 | int enabled) |
609 | .callback = dmi_check_cb, | 780 | { |
610 | }, | 781 | const struct sabi_commands *commands = &samsung->config->commands; |
611 | { | 782 | struct sabi_data data; |
612 | .ident = "NP-Q45", | 783 | |
613 | .matches = { | 784 | memset(&data, 0, sizeof(data)); |
614 | DMI_MATCH(DMI_SYS_VENDOR, | 785 | data.data[0] = 0x80 | enabled; |
615 | "SAMSUNG ELECTRONICS CO., LTD."), | 786 | return sabi_command(samsung, commands->set_usb_charge, |
616 | DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"), | 787 | &data, NULL); |
617 | DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"), | 788 | } |
618 | }, | 789 | |
619 | .callback = dmi_check_cb, | 790 | static ssize_t get_usb_charge(struct device *dev, |
620 | }, | 791 | struct device_attribute *attr, |
621 | { | 792 | char *buf) |
622 | .ident = "X360", | 793 | { |
623 | .matches = { | 794 | struct samsung_laptop *samsung = dev_get_drvdata(dev); |
624 | DMI_MATCH(DMI_SYS_VENDOR, | 795 | int ret; |
625 | "SAMSUNG ELECTRONICS CO., LTD."), | 796 | |
626 | DMI_MATCH(DMI_PRODUCT_NAME, "X360"), | 797 | ret = read_usb_charge(samsung); |
627 | DMI_MATCH(DMI_BOARD_NAME, "X360"), | 798 | if (ret < 0) |
628 | }, | 799 | return ret; |
629 | .callback = dmi_check_cb, | 800 | |
630 | }, | 801 | return sprintf(buf, "%d\n", ret); |
631 | { | 802 | } |
632 | .ident = "R410 Plus", | 803 | |
633 | .matches = { | 804 | static ssize_t set_usb_charge(struct device *dev, |
634 | DMI_MATCH(DMI_SYS_VENDOR, | 805 | struct device_attribute *attr, |
635 | "SAMSUNG ELECTRONICS CO., LTD."), | 806 | const char *buf, size_t count) |
636 | DMI_MATCH(DMI_PRODUCT_NAME, "R410P"), | 807 | { |
637 | DMI_MATCH(DMI_BOARD_NAME, "R460"), | 808 | struct samsung_laptop *samsung = dev_get_drvdata(dev); |
638 | }, | 809 | int ret, value; |
639 | .callback = dmi_check_cb, | 810 | |
640 | }, | 811 | if (!count || sscanf(buf, "%i", &value) != 1) |
641 | { | 812 | return -EINVAL; |
642 | .ident = "R518", | 813 | |
643 | .matches = { | 814 | ret = write_usb_charge(samsung, !!value); |
644 | DMI_MATCH(DMI_SYS_VENDOR, | 815 | if (ret < 0) |
645 | "SAMSUNG ELECTRONICS CO., LTD."), | 816 | return ret; |
646 | DMI_MATCH(DMI_PRODUCT_NAME, "R518"), | 817 | |
647 | DMI_MATCH(DMI_BOARD_NAME, "R518"), | 818 | return count; |
648 | }, | 819 | } |
649 | .callback = dmi_check_cb, | 820 | |
650 | }, | 821 | static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, |
651 | { | 822 | get_usb_charge, set_usb_charge); |
652 | .ident = "R519/R719", | 823 | |
653 | .matches = { | 824 | static struct attribute *platform_attributes[] = { |
654 | DMI_MATCH(DMI_SYS_VENDOR, | 825 | &dev_attr_performance_level.attr, |
655 | "SAMSUNG ELECTRONICS CO., LTD."), | 826 | &dev_attr_battery_life_extender.attr, |
656 | DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"), | 827 | &dev_attr_usb_charge.attr, |
657 | DMI_MATCH(DMI_BOARD_NAME, "R519/R719"), | 828 | NULL |
658 | }, | ||
659 | .callback = dmi_check_cb, | ||
660 | }, | ||
661 | { | ||
662 | .ident = "N150/N210/N220", | ||
663 | .matches = { | ||
664 | DMI_MATCH(DMI_SYS_VENDOR, | ||
665 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
666 | DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), | ||
667 | DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), | ||
668 | }, | ||
669 | .callback = dmi_check_cb, | ||
670 | }, | ||
671 | { | ||
672 | .ident = "N220", | ||
673 | .matches = { | ||
674 | DMI_MATCH(DMI_SYS_VENDOR, | ||
675 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
676 | DMI_MATCH(DMI_PRODUCT_NAME, "N220"), | ||
677 | DMI_MATCH(DMI_BOARD_NAME, "N220"), | ||
678 | }, | ||
679 | .callback = dmi_check_cb, | ||
680 | }, | ||
681 | { | ||
682 | .ident = "N150/N210/N220/N230", | ||
683 | .matches = { | ||
684 | DMI_MATCH(DMI_SYS_VENDOR, | ||
685 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
686 | DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"), | ||
687 | DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"), | ||
688 | }, | ||
689 | .callback = dmi_check_cb, | ||
690 | }, | ||
691 | { | ||
692 | .ident = "N150P/N210P/N220P", | ||
693 | .matches = { | ||
694 | DMI_MATCH(DMI_SYS_VENDOR, | ||
695 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
696 | DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"), | ||
697 | DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"), | ||
698 | }, | ||
699 | .callback = dmi_check_cb, | ||
700 | }, | ||
701 | { | ||
702 | .ident = "R700", | ||
703 | .matches = { | ||
704 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
705 | DMI_MATCH(DMI_PRODUCT_NAME, "SR700"), | ||
706 | DMI_MATCH(DMI_BOARD_NAME, "SR700"), | ||
707 | }, | ||
708 | .callback = dmi_check_cb, | ||
709 | }, | ||
710 | { | ||
711 | .ident = "R530/R730", | ||
712 | .matches = { | ||
713 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
714 | DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"), | ||
715 | DMI_MATCH(DMI_BOARD_NAME, "R530/R730"), | ||
716 | }, | ||
717 | .callback = dmi_check_cb, | ||
718 | }, | ||
719 | { | ||
720 | .ident = "NF110/NF210/NF310", | ||
721 | .matches = { | ||
722 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
723 | DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), | ||
724 | DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), | ||
725 | }, | ||
726 | .callback = dmi_check_cb, | ||
727 | }, | ||
728 | { | ||
729 | .ident = "N145P/N250P/N260P", | ||
730 | .matches = { | ||
731 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
732 | DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), | ||
733 | DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), | ||
734 | }, | ||
735 | .callback = dmi_check_cb, | ||
736 | }, | ||
737 | { | ||
738 | .ident = "R70/R71", | ||
739 | .matches = { | ||
740 | DMI_MATCH(DMI_SYS_VENDOR, | ||
741 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
742 | DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"), | ||
743 | DMI_MATCH(DMI_BOARD_NAME, "R70/R71"), | ||
744 | }, | ||
745 | .callback = dmi_check_cb, | ||
746 | }, | ||
747 | { | ||
748 | .ident = "P460", | ||
749 | .matches = { | ||
750 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
751 | DMI_MATCH(DMI_PRODUCT_NAME, "P460"), | ||
752 | DMI_MATCH(DMI_BOARD_NAME, "P460"), | ||
753 | }, | ||
754 | .callback = dmi_check_cb, | ||
755 | }, | ||
756 | { | ||
757 | .ident = "R528/R728", | ||
758 | .matches = { | ||
759 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
760 | DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"), | ||
761 | DMI_MATCH(DMI_BOARD_NAME, "R528/R728"), | ||
762 | }, | ||
763 | .callback = dmi_check_cb, | ||
764 | }, | ||
765 | { | ||
766 | .ident = "NC210/NC110", | ||
767 | .matches = { | ||
768 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
769 | DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), | ||
770 | DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), | ||
771 | }, | ||
772 | .callback = dmi_check_cb, | ||
773 | }, | ||
774 | { | ||
775 | .ident = "X520", | ||
776 | .matches = { | ||
777 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
778 | DMI_MATCH(DMI_PRODUCT_NAME, "X520"), | ||
779 | DMI_MATCH(DMI_BOARD_NAME, "X520"), | ||
780 | }, | ||
781 | .callback = dmi_check_cb, | ||
782 | }, | ||
783 | { }, | ||
784 | }; | 829 | }; |
785 | MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); | ||
786 | 830 | ||
787 | static int find_signature(void __iomem *memcheck, const char *testStr) | 831 | static int find_signature(void __iomem *memcheck, const char *testStr) |
788 | { | 832 | { |
@@ -803,153 +847,772 @@ static int find_signature(void __iomem *memcheck, const char *testStr) | |||
803 | return loca; | 847 | return loca; |
804 | } | 848 | } |
805 | 849 | ||
806 | static int __init samsung_init(void) | 850 | static void samsung_rfkill_exit(struct samsung_laptop *samsung) |
807 | { | 851 | { |
808 | struct backlight_properties props; | 852 | if (samsung->wlan.rfkill) { |
809 | struct sabi_retval sretval; | 853 | rfkill_unregister(samsung->wlan.rfkill); |
810 | unsigned int ifaceP; | 854 | rfkill_destroy(samsung->wlan.rfkill); |
811 | int i; | 855 | samsung->wlan.rfkill = NULL; |
812 | int loca; | 856 | } |
857 | if (samsung->bluetooth.rfkill) { | ||
858 | rfkill_unregister(samsung->bluetooth.rfkill); | ||
859 | rfkill_destroy(samsung->bluetooth.rfkill); | ||
860 | samsung->bluetooth.rfkill = NULL; | ||
861 | } | ||
862 | } | ||
863 | |||
864 | static int samsung_new_rfkill(struct samsung_laptop *samsung, | ||
865 | struct samsung_rfkill *arfkill, | ||
866 | const char *name, enum rfkill_type type, | ||
867 | const struct rfkill_ops *ops, | ||
868 | int blocked) | ||
869 | { | ||
870 | struct rfkill **rfkill = &arfkill->rfkill; | ||
871 | int ret; | ||
872 | |||
873 | arfkill->type = type; | ||
874 | arfkill->samsung = samsung; | ||
875 | |||
876 | *rfkill = rfkill_alloc(name, &samsung->platform_device->dev, | ||
877 | type, ops, arfkill); | ||
878 | |||
879 | if (!*rfkill) | ||
880 | return -EINVAL; | ||
881 | |||
882 | if (blocked != -1) | ||
883 | rfkill_init_sw_state(*rfkill, blocked); | ||
884 | |||
885 | ret = rfkill_register(*rfkill); | ||
886 | if (ret) { | ||
887 | rfkill_destroy(*rfkill); | ||
888 | *rfkill = NULL; | ||
889 | return ret; | ||
890 | } | ||
891 | return 0; | ||
892 | } | ||
893 | |||
894 | static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung) | ||
895 | { | ||
896 | return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan", | ||
897 | RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1); | ||
898 | } | ||
899 | |||
900 | static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung) | ||
901 | { | ||
902 | struct sabi_data data; | ||
903 | int ret; | ||
904 | |||
905 | ret = swsmi_wireless_status(samsung, &data); | ||
906 | if (ret) { | ||
907 | /* Some swsmi laptops use the old seclinux way to control | ||
908 | * wireless devices */ | ||
909 | if (ret == -EINVAL) | ||
910 | ret = samsung_rfkill_init_seclinux(samsung); | ||
911 | return ret; | ||
912 | } | ||
913 | |||
914 | /* 0x02 seems to mean that the device is no present/available */ | ||
915 | |||
916 | if (data.data[WL_STATUS_WLAN] != 0x02) | ||
917 | ret = samsung_new_rfkill(samsung, &samsung->wlan, | ||
918 | "samsung-wlan", | ||
919 | RFKILL_TYPE_WLAN, | ||
920 | &swsmi_rfkill_ops, | ||
921 | !data.data[WL_STATUS_WLAN]); | ||
922 | if (ret) | ||
923 | goto exit; | ||
924 | |||
925 | if (data.data[WL_STATUS_BT] != 0x02) | ||
926 | ret = samsung_new_rfkill(samsung, &samsung->bluetooth, | ||
927 | "samsung-bluetooth", | ||
928 | RFKILL_TYPE_BLUETOOTH, | ||
929 | &swsmi_rfkill_ops, | ||
930 | !data.data[WL_STATUS_BT]); | ||
931 | if (ret) | ||
932 | goto exit; | ||
933 | |||
934 | exit: | ||
935 | if (ret) | ||
936 | samsung_rfkill_exit(samsung); | ||
937 | |||
938 | return ret; | ||
939 | } | ||
940 | |||
941 | static int __init samsung_rfkill_init(struct samsung_laptop *samsung) | ||
942 | { | ||
943 | if (samsung->config->sabi_version == 2) | ||
944 | return samsung_rfkill_init_seclinux(samsung); | ||
945 | if (samsung->config->sabi_version == 3) | ||
946 | return samsung_rfkill_init_swsmi(samsung); | ||
947 | return 0; | ||
948 | } | ||
949 | |||
950 | static int kbd_backlight_enable(struct samsung_laptop *samsung) | ||
951 | { | ||
952 | const struct sabi_commands *commands = &samsung->config->commands; | ||
953 | struct sabi_data data; | ||
813 | int retval; | 954 | int retval; |
814 | 955 | ||
815 | mutex_init(&sabi_mutex); | 956 | if (commands->kbd_backlight == 0xFFFF) |
957 | return -ENODEV; | ||
958 | |||
959 | memset(&data, 0, sizeof(data)); | ||
960 | data.d0 = 0xaabb; | ||
961 | retval = sabi_command(samsung, commands->kbd_backlight, | ||
962 | &data, &data); | ||
816 | 963 | ||
817 | if (!force && !dmi_check_system(samsung_dmi_table)) | 964 | if (retval) |
965 | return retval; | ||
966 | |||
967 | if (data.d0 != 0xccdd) | ||
818 | return -ENODEV; | 968 | return -ENODEV; |
969 | return 0; | ||
970 | } | ||
819 | 971 | ||
820 | f0000_segment = ioremap_nocache(0xf0000, 0xffff); | 972 | static int kbd_backlight_read(struct samsung_laptop *samsung) |
821 | if (!f0000_segment) { | 973 | { |
822 | pr_err("Can't map the segment at 0xf0000\n"); | 974 | const struct sabi_commands *commands = &samsung->config->commands; |
823 | return -EINVAL; | 975 | struct sabi_data data; |
976 | int retval; | ||
977 | |||
978 | memset(&data, 0, sizeof(data)); | ||
979 | data.data[0] = 0x81; | ||
980 | retval = sabi_command(samsung, commands->kbd_backlight, | ||
981 | &data, &data); | ||
982 | |||
983 | if (retval) | ||
984 | return retval; | ||
985 | |||
986 | return data.data[0]; | ||
987 | } | ||
988 | |||
989 | static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness) | ||
990 | { | ||
991 | const struct sabi_commands *commands = &samsung->config->commands; | ||
992 | struct sabi_data data; | ||
993 | |||
994 | memset(&data, 0, sizeof(data)); | ||
995 | data.d0 = 0x82 | ((brightness & 0xFF) << 8); | ||
996 | return sabi_command(samsung, commands->kbd_backlight, | ||
997 | &data, NULL); | ||
998 | } | ||
999 | |||
1000 | static void kbd_led_update(struct work_struct *work) | ||
1001 | { | ||
1002 | struct samsung_laptop *samsung; | ||
1003 | |||
1004 | samsung = container_of(work, struct samsung_laptop, kbd_led_work); | ||
1005 | kbd_backlight_write(samsung, samsung->kbd_led_wk); | ||
1006 | } | ||
1007 | |||
1008 | static void kbd_led_set(struct led_classdev *led_cdev, | ||
1009 | enum led_brightness value) | ||
1010 | { | ||
1011 | struct samsung_laptop *samsung; | ||
1012 | |||
1013 | samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); | ||
1014 | |||
1015 | if (value > samsung->kbd_led.max_brightness) | ||
1016 | value = samsung->kbd_led.max_brightness; | ||
1017 | else if (value < 0) | ||
1018 | value = 0; | ||
1019 | |||
1020 | samsung->kbd_led_wk = value; | ||
1021 | queue_work(samsung->led_workqueue, &samsung->kbd_led_work); | ||
1022 | } | ||
1023 | |||
1024 | static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) | ||
1025 | { | ||
1026 | struct samsung_laptop *samsung; | ||
1027 | |||
1028 | samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); | ||
1029 | return kbd_backlight_read(samsung); | ||
1030 | } | ||
1031 | |||
1032 | static void samsung_leds_exit(struct samsung_laptop *samsung) | ||
1033 | { | ||
1034 | if (!IS_ERR_OR_NULL(samsung->kbd_led.dev)) | ||
1035 | led_classdev_unregister(&samsung->kbd_led); | ||
1036 | if (samsung->led_workqueue) | ||
1037 | destroy_workqueue(samsung->led_workqueue); | ||
1038 | } | ||
1039 | |||
1040 | static int __init samsung_leds_init(struct samsung_laptop *samsung) | ||
1041 | { | ||
1042 | int ret = 0; | ||
1043 | |||
1044 | samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); | ||
1045 | if (!samsung->led_workqueue) | ||
1046 | return -ENOMEM; | ||
1047 | |||
1048 | if (kbd_backlight_enable(samsung) >= 0) { | ||
1049 | INIT_WORK(&samsung->kbd_led_work, kbd_led_update); | ||
1050 | |||
1051 | samsung->kbd_led.name = "samsung::kbd_backlight"; | ||
1052 | samsung->kbd_led.brightness_set = kbd_led_set; | ||
1053 | samsung->kbd_led.brightness_get = kbd_led_get; | ||
1054 | samsung->kbd_led.max_brightness = 8; | ||
1055 | |||
1056 | ret = led_classdev_register(&samsung->platform_device->dev, | ||
1057 | &samsung->kbd_led); | ||
1058 | } | ||
1059 | |||
1060 | if (ret) | ||
1061 | samsung_leds_exit(samsung); | ||
1062 | |||
1063 | return ret; | ||
1064 | } | ||
1065 | |||
1066 | static void samsung_backlight_exit(struct samsung_laptop *samsung) | ||
1067 | { | ||
1068 | if (samsung->backlight_device) { | ||
1069 | backlight_device_unregister(samsung->backlight_device); | ||
1070 | samsung->backlight_device = NULL; | ||
1071 | } | ||
1072 | } | ||
1073 | |||
1074 | static int __init samsung_backlight_init(struct samsung_laptop *samsung) | ||
1075 | { | ||
1076 | struct backlight_device *bd; | ||
1077 | struct backlight_properties props; | ||
1078 | |||
1079 | if (!samsung->handle_backlight) | ||
1080 | return 0; | ||
1081 | |||
1082 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1083 | props.type = BACKLIGHT_PLATFORM; | ||
1084 | props.max_brightness = samsung->config->max_brightness - | ||
1085 | samsung->config->min_brightness; | ||
1086 | |||
1087 | bd = backlight_device_register("samsung", | ||
1088 | &samsung->platform_device->dev, | ||
1089 | samsung, &backlight_ops, | ||
1090 | &props); | ||
1091 | if (IS_ERR(bd)) | ||
1092 | return PTR_ERR(bd); | ||
1093 | |||
1094 | samsung->backlight_device = bd; | ||
1095 | samsung->backlight_device->props.brightness = read_brightness(samsung); | ||
1096 | samsung->backlight_device->props.power = FB_BLANK_UNBLANK; | ||
1097 | backlight_update_status(samsung->backlight_device); | ||
1098 | |||
1099 | return 0; | ||
1100 | } | ||
1101 | |||
1102 | static umode_t samsung_sysfs_is_visible(struct kobject *kobj, | ||
1103 | struct attribute *attr, int idx) | ||
1104 | { | ||
1105 | struct device *dev = container_of(kobj, struct device, kobj); | ||
1106 | struct platform_device *pdev = to_platform_device(dev); | ||
1107 | struct samsung_laptop *samsung = platform_get_drvdata(pdev); | ||
1108 | bool ok = true; | ||
1109 | |||
1110 | if (attr == &dev_attr_performance_level.attr) | ||
1111 | ok = !!samsung->config->performance_levels[0].name; | ||
1112 | if (attr == &dev_attr_battery_life_extender.attr) | ||
1113 | ok = !!(read_battery_life_extender(samsung) >= 0); | ||
1114 | if (attr == &dev_attr_usb_charge.attr) | ||
1115 | ok = !!(read_usb_charge(samsung) >= 0); | ||
1116 | |||
1117 | return ok ? attr->mode : 0; | ||
1118 | } | ||
1119 | |||
1120 | static struct attribute_group platform_attribute_group = { | ||
1121 | .is_visible = samsung_sysfs_is_visible, | ||
1122 | .attrs = platform_attributes | ||
1123 | }; | ||
1124 | |||
1125 | static void samsung_sysfs_exit(struct samsung_laptop *samsung) | ||
1126 | { | ||
1127 | struct platform_device *device = samsung->platform_device; | ||
1128 | |||
1129 | sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); | ||
1130 | } | ||
1131 | |||
1132 | static int __init samsung_sysfs_init(struct samsung_laptop *samsung) | ||
1133 | { | ||
1134 | struct platform_device *device = samsung->platform_device; | ||
1135 | |||
1136 | return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); | ||
1137 | |||
1138 | } | ||
1139 | |||
1140 | static int show_call(struct seq_file *m, void *data) | ||
1141 | { | ||
1142 | struct samsung_laptop *samsung = m->private; | ||
1143 | struct sabi_data *sdata = &samsung->debug.data; | ||
1144 | int ret; | ||
1145 | |||
1146 | seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", | ||
1147 | samsung->debug.command, | ||
1148 | sdata->d0, sdata->d1, sdata->d2, sdata->d3); | ||
1149 | |||
1150 | ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); | ||
1151 | |||
1152 | if (ret) { | ||
1153 | seq_printf(m, "SABI command 0x%04x failed\n", | ||
1154 | samsung->debug.command); | ||
1155 | return ret; | ||
1156 | } | ||
1157 | |||
1158 | seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", | ||
1159 | sdata->d0, sdata->d1, sdata->d2, sdata->d3); | ||
1160 | return 0; | ||
1161 | } | ||
1162 | |||
1163 | static int samsung_debugfs_open(struct inode *inode, struct file *file) | ||
1164 | { | ||
1165 | return single_open(file, show_call, inode->i_private); | ||
1166 | } | ||
1167 | |||
1168 | static const struct file_operations samsung_laptop_call_io_ops = { | ||
1169 | .owner = THIS_MODULE, | ||
1170 | .open = samsung_debugfs_open, | ||
1171 | .read = seq_read, | ||
1172 | .llseek = seq_lseek, | ||
1173 | .release = single_release, | ||
1174 | }; | ||
1175 | |||
1176 | static void samsung_debugfs_exit(struct samsung_laptop *samsung) | ||
1177 | { | ||
1178 | debugfs_remove_recursive(samsung->debug.root); | ||
1179 | } | ||
1180 | |||
1181 | static int samsung_debugfs_init(struct samsung_laptop *samsung) | ||
1182 | { | ||
1183 | struct dentry *dent; | ||
1184 | |||
1185 | samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); | ||
1186 | if (!samsung->debug.root) { | ||
1187 | pr_err("failed to create debugfs directory"); | ||
1188 | goto error_debugfs; | ||
1189 | } | ||
1190 | |||
1191 | samsung->debug.f0000_wrapper.data = samsung->f0000_segment; | ||
1192 | samsung->debug.f0000_wrapper.size = 0xffff; | ||
1193 | |||
1194 | samsung->debug.data_wrapper.data = &samsung->debug.data; | ||
1195 | samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); | ||
1196 | |||
1197 | samsung->debug.sdiag_wrapper.data = samsung->sdiag; | ||
1198 | samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); | ||
1199 | |||
1200 | dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, | ||
1201 | samsung->debug.root, &samsung->debug.command); | ||
1202 | if (!dent) | ||
1203 | goto error_debugfs; | ||
1204 | |||
1205 | dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, | ||
1206 | &samsung->debug.data.d0); | ||
1207 | if (!dent) | ||
1208 | goto error_debugfs; | ||
1209 | |||
1210 | dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, | ||
1211 | &samsung->debug.data.d1); | ||
1212 | if (!dent) | ||
1213 | goto error_debugfs; | ||
1214 | |||
1215 | dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, | ||
1216 | &samsung->debug.data.d2); | ||
1217 | if (!dent) | ||
1218 | goto error_debugfs; | ||
1219 | |||
1220 | dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root, | ||
1221 | &samsung->debug.data.d3); | ||
1222 | if (!dent) | ||
1223 | goto error_debugfs; | ||
1224 | |||
1225 | dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR, | ||
1226 | samsung->debug.root, | ||
1227 | &samsung->debug.data_wrapper); | ||
1228 | if (!dent) | ||
1229 | goto error_debugfs; | ||
1230 | |||
1231 | dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, | ||
1232 | samsung->debug.root, | ||
1233 | &samsung->debug.f0000_wrapper); | ||
1234 | if (!dent) | ||
1235 | goto error_debugfs; | ||
1236 | |||
1237 | dent = debugfs_create_file("call", S_IFREG | S_IRUGO, | ||
1238 | samsung->debug.root, samsung, | ||
1239 | &samsung_laptop_call_io_ops); | ||
1240 | if (!dent) | ||
1241 | goto error_debugfs; | ||
1242 | |||
1243 | dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, | ||
1244 | samsung->debug.root, | ||
1245 | &samsung->debug.sdiag_wrapper); | ||
1246 | if (!dent) | ||
1247 | goto error_debugfs; | ||
1248 | |||
1249 | return 0; | ||
1250 | |||
1251 | error_debugfs: | ||
1252 | samsung_debugfs_exit(samsung); | ||
1253 | return -ENOMEM; | ||
1254 | } | ||
1255 | |||
1256 | static void samsung_sabi_exit(struct samsung_laptop *samsung) | ||
1257 | { | ||
1258 | const struct sabi_config *config = samsung->config; | ||
1259 | |||
1260 | /* Turn off "Linux" mode in the BIOS */ | ||
1261 | if (config && config->commands.set_linux != 0xff) | ||
1262 | sabi_set_commandb(samsung, config->commands.set_linux, 0x80); | ||
1263 | |||
1264 | if (samsung->sabi_iface) { | ||
1265 | iounmap(samsung->sabi_iface); | ||
1266 | samsung->sabi_iface = NULL; | ||
1267 | } | ||
1268 | if (samsung->f0000_segment) { | ||
1269 | iounmap(samsung->f0000_segment); | ||
1270 | samsung->f0000_segment = NULL; | ||
1271 | } | ||
1272 | |||
1273 | samsung->config = NULL; | ||
1274 | } | ||
1275 | |||
1276 | static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, | ||
1277 | unsigned int ifaceP) | ||
1278 | { | ||
1279 | const struct sabi_config *config = samsung->config; | ||
1280 | |||
1281 | printk(KERN_DEBUG "This computer supports SABI==%x\n", | ||
1282 | loca + 0xf0000 - 6); | ||
1283 | |||
1284 | printk(KERN_DEBUG "SABI header:\n"); | ||
1285 | printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", | ||
1286 | readw(samsung->sabi + config->header_offsets.port)); | ||
1287 | printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", | ||
1288 | readb(samsung->sabi + config->header_offsets.iface_func)); | ||
1289 | printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", | ||
1290 | readb(samsung->sabi + config->header_offsets.en_mem)); | ||
1291 | printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", | ||
1292 | readb(samsung->sabi + config->header_offsets.re_mem)); | ||
1293 | printk(KERN_DEBUG " SABI data offset = 0x%04x\n", | ||
1294 | readw(samsung->sabi + config->header_offsets.data_offset)); | ||
1295 | printk(KERN_DEBUG " SABI data segment = 0x%04x\n", | ||
1296 | readw(samsung->sabi + config->header_offsets.data_segment)); | ||
1297 | |||
1298 | printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); | ||
1299 | } | ||
1300 | |||
1301 | static void __init samsung_sabi_diag(struct samsung_laptop *samsung) | ||
1302 | { | ||
1303 | int loca = find_signature(samsung->f0000_segment, "SDiaG@"); | ||
1304 | int i; | ||
1305 | |||
1306 | if (loca == 0xffff) | ||
1307 | return ; | ||
1308 | |||
1309 | /* Example: | ||
1310 | * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A | ||
1311 | * | ||
1312 | * Product name: 90X3A | ||
1313 | * BIOS Version: 07HL | ||
1314 | */ | ||
1315 | loca += 1; | ||
1316 | for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { | ||
1317 | char temp = readb(samsung->f0000_segment + loca); | ||
1318 | |||
1319 | if (isalnum(temp) || temp == '/' || temp == '-') | ||
1320 | samsung->sdiag[i++] = temp; | ||
1321 | else | ||
1322 | break ; | ||
824 | } | 1323 | } |
825 | 1324 | ||
1325 | if (debug && samsung->sdiag[0]) | ||
1326 | pr_info("sdiag: %s", samsung->sdiag); | ||
1327 | } | ||
1328 | |||
1329 | static int __init samsung_sabi_init(struct samsung_laptop *samsung) | ||
1330 | { | ||
1331 | const struct sabi_config *config = NULL; | ||
1332 | const struct sabi_commands *commands; | ||
1333 | unsigned int ifaceP; | ||
1334 | int ret = 0; | ||
1335 | int i; | ||
1336 | int loca; | ||
1337 | |||
1338 | samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); | ||
1339 | if (!samsung->f0000_segment) { | ||
1340 | if (debug || force) | ||
1341 | pr_err("Can't map the segment at 0xf0000\n"); | ||
1342 | ret = -EINVAL; | ||
1343 | goto exit; | ||
1344 | } | ||
1345 | |||
1346 | samsung_sabi_diag(samsung); | ||
1347 | |||
826 | /* Try to find one of the signatures in memory to find the header */ | 1348 | /* Try to find one of the signatures in memory to find the header */ |
827 | for (i = 0; sabi_configs[i].test_string != 0; ++i) { | 1349 | for (i = 0; sabi_configs[i].test_string != 0; ++i) { |
828 | sabi_config = &sabi_configs[i]; | 1350 | samsung->config = &sabi_configs[i]; |
829 | loca = find_signature(f0000_segment, sabi_config->test_string); | 1351 | loca = find_signature(samsung->f0000_segment, |
1352 | samsung->config->test_string); | ||
830 | if (loca != 0xffff) | 1353 | if (loca != 0xffff) |
831 | break; | 1354 | break; |
832 | } | 1355 | } |
833 | 1356 | ||
834 | if (loca == 0xffff) { | 1357 | if (loca == 0xffff) { |
835 | pr_err("This computer does not support SABI\n"); | 1358 | if (debug || force) |
836 | goto error_no_signature; | 1359 | pr_err("This computer does not support SABI\n"); |
1360 | ret = -ENODEV; | ||
1361 | goto exit; | ||
837 | } | 1362 | } |
838 | 1363 | ||
1364 | config = samsung->config; | ||
1365 | commands = &config->commands; | ||
1366 | |||
839 | /* point to the SMI port Number */ | 1367 | /* point to the SMI port Number */ |
840 | loca += 1; | 1368 | loca += 1; |
841 | sabi = (f0000_segment + loca); | 1369 | samsung->sabi = (samsung->f0000_segment + loca); |
842 | |||
843 | if (debug) { | ||
844 | printk(KERN_DEBUG "This computer supports SABI==%x\n", | ||
845 | loca + 0xf0000 - 6); | ||
846 | printk(KERN_DEBUG "SABI header:\n"); | ||
847 | printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", | ||
848 | readw(sabi + sabi_config->header_offsets.port)); | ||
849 | printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", | ||
850 | readb(sabi + sabi_config->header_offsets.iface_func)); | ||
851 | printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", | ||
852 | readb(sabi + sabi_config->header_offsets.en_mem)); | ||
853 | printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", | ||
854 | readb(sabi + sabi_config->header_offsets.re_mem)); | ||
855 | printk(KERN_DEBUG " SABI data offset = 0x%04x\n", | ||
856 | readw(sabi + sabi_config->header_offsets.data_offset)); | ||
857 | printk(KERN_DEBUG " SABI data segment = 0x%04x\n", | ||
858 | readw(sabi + sabi_config->header_offsets.data_segment)); | ||
859 | } | ||
860 | 1370 | ||
861 | /* Get a pointer to the SABI Interface */ | 1371 | /* Get a pointer to the SABI Interface */ |
862 | ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4; | 1372 | ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; |
863 | ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff; | 1373 | ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; |
864 | sabi_iface = ioremap_nocache(ifaceP, 16); | ||
865 | if (!sabi_iface) { | ||
866 | pr_err("Can't remap %x\n", ifaceP); | ||
867 | goto error_no_signature; | ||
868 | } | ||
869 | if (debug) { | ||
870 | printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); | ||
871 | printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface); | ||
872 | 1374 | ||
873 | test_backlight(); | 1375 | if (debug) |
874 | test_wireless(); | 1376 | samsung_sabi_infos(samsung, loca, ifaceP); |
875 | 1377 | ||
876 | retval = sabi_get_command(sabi_config->commands.get_brightness, | 1378 | samsung->sabi_iface = ioremap_nocache(ifaceP, 16); |
877 | &sretval); | 1379 | if (!samsung->sabi_iface) { |
878 | printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]); | 1380 | pr_err("Can't remap %x\n", ifaceP); |
1381 | ret = -EINVAL; | ||
1382 | goto exit; | ||
879 | } | 1383 | } |
880 | 1384 | ||
881 | /* Turn on "Linux" mode in the BIOS */ | 1385 | /* Turn on "Linux" mode in the BIOS */ |
882 | if (sabi_config->commands.set_linux != 0xff) { | 1386 | if (commands->set_linux != 0xff) { |
883 | retval = sabi_set_command(sabi_config->commands.set_linux, | 1387 | int retval = sabi_set_commandb(samsung, |
884 | 0x81); | 1388 | commands->set_linux, 0x81); |
885 | if (retval) { | 1389 | if (retval) { |
886 | pr_warn("Linux mode was not set!\n"); | 1390 | pr_warn("Linux mode was not set!\n"); |
887 | goto error_no_platform; | 1391 | ret = -ENODEV; |
1392 | goto exit; | ||
888 | } | 1393 | } |
889 | } | 1394 | } |
890 | 1395 | ||
891 | /* Check for stepping quirk */ | 1396 | /* Check for stepping quirk */ |
892 | check_for_stepping_quirk(); | 1397 | if (samsung->handle_backlight) |
1398 | check_for_stepping_quirk(samsung); | ||
893 | 1399 | ||
894 | /* knock up a platform device to hang stuff off of */ | 1400 | pr_info("detected SABI interface: %s\n", |
895 | sdev = platform_device_register_simple("samsung", -1, NULL, 0); | 1401 | samsung->config->test_string); |
896 | if (IS_ERR(sdev)) | ||
897 | goto error_no_platform; | ||
898 | 1402 | ||
899 | /* create a backlight device to talk to this one */ | 1403 | exit: |
900 | memset(&props, 0, sizeof(struct backlight_properties)); | 1404 | if (ret) |
901 | props.type = BACKLIGHT_PLATFORM; | 1405 | samsung_sabi_exit(samsung); |
902 | props.max_brightness = sabi_config->max_brightness - | ||
903 | sabi_config->min_brightness; | ||
904 | backlight_device = backlight_device_register("samsung", &sdev->dev, | ||
905 | NULL, &backlight_ops, | ||
906 | &props); | ||
907 | if (IS_ERR(backlight_device)) | ||
908 | goto error_no_backlight; | ||
909 | |||
910 | backlight_device->props.brightness = read_brightness(); | ||
911 | backlight_device->props.power = FB_BLANK_UNBLANK; | ||
912 | backlight_update_status(backlight_device); | ||
913 | |||
914 | retval = init_wireless(sdev); | ||
915 | if (retval) | ||
916 | goto error_no_rfk; | ||
917 | 1406 | ||
918 | retval = device_create_file(&sdev->dev, &dev_attr_performance_level); | 1407 | return ret; |
919 | if (retval) | 1408 | } |
920 | goto error_file_create; | 1409 | |
1410 | static void samsung_platform_exit(struct samsung_laptop *samsung) | ||
1411 | { | ||
1412 | if (samsung->platform_device) { | ||
1413 | platform_device_unregister(samsung->platform_device); | ||
1414 | samsung->platform_device = NULL; | ||
1415 | } | ||
1416 | } | ||
1417 | |||
1418 | static int __init samsung_platform_init(struct samsung_laptop *samsung) | ||
1419 | { | ||
1420 | struct platform_device *pdev; | ||
1421 | |||
1422 | pdev = platform_device_register_simple("samsung", -1, NULL, 0); | ||
1423 | if (IS_ERR(pdev)) | ||
1424 | return PTR_ERR(pdev); | ||
921 | 1425 | ||
1426 | samsung->platform_device = pdev; | ||
1427 | platform_set_drvdata(samsung->platform_device, samsung); | ||
922 | return 0; | 1428 | return 0; |
1429 | } | ||
1430 | |||
1431 | static struct samsung_quirks *quirks; | ||
1432 | |||
1433 | static int __init samsung_dmi_matched(const struct dmi_system_id *d) | ||
1434 | { | ||
1435 | quirks = d->driver_data; | ||
1436 | return 0; | ||
1437 | } | ||
1438 | |||
1439 | static struct dmi_system_id __initdata samsung_dmi_table[] = { | ||
1440 | { | ||
1441 | .matches = { | ||
1442 | DMI_MATCH(DMI_SYS_VENDOR, | ||
1443 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
1444 | DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ | ||
1445 | }, | ||
1446 | }, | ||
1447 | { | ||
1448 | .matches = { | ||
1449 | DMI_MATCH(DMI_SYS_VENDOR, | ||
1450 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
1451 | DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ | ||
1452 | }, | ||
1453 | }, | ||
1454 | { | ||
1455 | .matches = { | ||
1456 | DMI_MATCH(DMI_SYS_VENDOR, | ||
1457 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
1458 | DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ | ||
1459 | }, | ||
1460 | }, | ||
1461 | { | ||
1462 | .matches = { | ||
1463 | DMI_MATCH(DMI_SYS_VENDOR, | ||
1464 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
1465 | DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ | ||
1466 | }, | ||
1467 | }, | ||
1468 | /* Specific DMI ids for laptop with quirks */ | ||
1469 | { | ||
1470 | .callback = samsung_dmi_matched, | ||
1471 | .ident = "N150P", | ||
1472 | .matches = { | ||
1473 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
1474 | DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), | ||
1475 | DMI_MATCH(DMI_BOARD_NAME, "N150P"), | ||
1476 | }, | ||
1477 | .driver_data = &samsung_broken_acpi_video, | ||
1478 | }, | ||
1479 | { | ||
1480 | .callback = samsung_dmi_matched, | ||
1481 | .ident = "N145P/N250P/N260P", | ||
1482 | .matches = { | ||
1483 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
1484 | DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), | ||
1485 | DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), | ||
1486 | }, | ||
1487 | .driver_data = &samsung_broken_acpi_video, | ||
1488 | }, | ||
1489 | { | ||
1490 | .callback = samsung_dmi_matched, | ||
1491 | .ident = "N150/N210/N220", | ||
1492 | .matches = { | ||
1493 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
1494 | DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), | ||
1495 | DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), | ||
1496 | }, | ||
1497 | .driver_data = &samsung_broken_acpi_video, | ||
1498 | }, | ||
1499 | { | ||
1500 | .callback = samsung_dmi_matched, | ||
1501 | .ident = "NF110/NF210/NF310", | ||
1502 | .matches = { | ||
1503 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
1504 | DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), | ||
1505 | DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), | ||
1506 | }, | ||
1507 | .driver_data = &samsung_broken_acpi_video, | ||
1508 | }, | ||
1509 | { }, | ||
1510 | }; | ||
1511 | MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); | ||
923 | 1512 | ||
924 | error_file_create: | 1513 | static struct platform_device *samsung_platform_device; |
925 | destroy_wireless(); | ||
926 | 1514 | ||
927 | error_no_rfk: | 1515 | static int __init samsung_init(void) |
928 | backlight_device_unregister(backlight_device); | 1516 | { |
1517 | struct samsung_laptop *samsung; | ||
1518 | int ret; | ||
929 | 1519 | ||
930 | error_no_backlight: | 1520 | quirks = &samsung_unknown; |
931 | platform_device_unregister(sdev); | 1521 | if (!force && !dmi_check_system(samsung_dmi_table)) |
1522 | return -ENODEV; | ||
1523 | |||
1524 | samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); | ||
1525 | if (!samsung) | ||
1526 | return -ENOMEM; | ||
932 | 1527 | ||
933 | error_no_platform: | 1528 | mutex_init(&samsung->sabi_mutex); |
934 | iounmap(sabi_iface); | 1529 | samsung->handle_backlight = true; |
1530 | samsung->quirks = quirks; | ||
935 | 1531 | ||
936 | error_no_signature: | 1532 | |
937 | iounmap(f0000_segment); | 1533 | #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) |
938 | return -EINVAL; | 1534 | /* Don't handle backlight here if the acpi video already handle it */ |
1535 | if (acpi_video_backlight_support()) { | ||
1536 | if (samsung->quirks->broken_acpi_video) { | ||
1537 | pr_info("Disabling ACPI video driver\n"); | ||
1538 | acpi_video_unregister(); | ||
1539 | } else { | ||
1540 | samsung->handle_backlight = false; | ||
1541 | } | ||
1542 | } | ||
1543 | #endif | ||
1544 | |||
1545 | ret = samsung_platform_init(samsung); | ||
1546 | if (ret) | ||
1547 | goto error_platform; | ||
1548 | |||
1549 | ret = samsung_sabi_init(samsung); | ||
1550 | if (ret) | ||
1551 | goto error_sabi; | ||
1552 | |||
1553 | #ifdef CONFIG_ACPI | ||
1554 | /* Only log that if we are really on a sabi platform */ | ||
1555 | if (acpi_video_backlight_support() && | ||
1556 | !samsung->quirks->broken_acpi_video) | ||
1557 | pr_info("Backlight controlled by ACPI video driver\n"); | ||
1558 | #endif | ||
1559 | |||
1560 | ret = samsung_sysfs_init(samsung); | ||
1561 | if (ret) | ||
1562 | goto error_sysfs; | ||
1563 | |||
1564 | ret = samsung_backlight_init(samsung); | ||
1565 | if (ret) | ||
1566 | goto error_backlight; | ||
1567 | |||
1568 | ret = samsung_rfkill_init(samsung); | ||
1569 | if (ret) | ||
1570 | goto error_rfkill; | ||
1571 | |||
1572 | ret = samsung_leds_init(samsung); | ||
1573 | if (ret) | ||
1574 | goto error_leds; | ||
1575 | |||
1576 | ret = samsung_debugfs_init(samsung); | ||
1577 | if (ret) | ||
1578 | goto error_debugfs; | ||
1579 | |||
1580 | samsung_platform_device = samsung->platform_device; | ||
1581 | return ret; | ||
1582 | |||
1583 | error_debugfs: | ||
1584 | samsung_leds_exit(samsung); | ||
1585 | error_leds: | ||
1586 | samsung_rfkill_exit(samsung); | ||
1587 | error_rfkill: | ||
1588 | samsung_backlight_exit(samsung); | ||
1589 | error_backlight: | ||
1590 | samsung_sysfs_exit(samsung); | ||
1591 | error_sysfs: | ||
1592 | samsung_sabi_exit(samsung); | ||
1593 | error_sabi: | ||
1594 | samsung_platform_exit(samsung); | ||
1595 | error_platform: | ||
1596 | kfree(samsung); | ||
1597 | return ret; | ||
939 | } | 1598 | } |
940 | 1599 | ||
941 | static void __exit samsung_exit(void) | 1600 | static void __exit samsung_exit(void) |
942 | { | 1601 | { |
943 | /* Turn off "Linux" mode in the BIOS */ | 1602 | struct samsung_laptop *samsung; |
944 | if (sabi_config->commands.set_linux != 0xff) | 1603 | |
945 | sabi_set_command(sabi_config->commands.set_linux, 0x80); | 1604 | samsung = platform_get_drvdata(samsung_platform_device); |
946 | 1605 | ||
947 | device_remove_file(&sdev->dev, &dev_attr_performance_level); | 1606 | samsung_debugfs_exit(samsung); |
948 | backlight_device_unregister(backlight_device); | 1607 | samsung_leds_exit(samsung); |
949 | destroy_wireless(); | 1608 | samsung_rfkill_exit(samsung); |
950 | iounmap(sabi_iface); | 1609 | samsung_backlight_exit(samsung); |
951 | iounmap(f0000_segment); | 1610 | samsung_sysfs_exit(samsung); |
952 | platform_device_unregister(sdev); | 1611 | samsung_sabi_exit(samsung); |
1612 | samsung_platform_exit(samsung); | ||
1613 | |||
1614 | kfree(samsung); | ||
1615 | samsung_platform_device = NULL; | ||
953 | } | 1616 | } |
954 | 1617 | ||
955 | module_init(samsung_init); | 1618 | module_init(samsung_init); |
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index c006dee5ebfe..8a51795aa02a 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -127,7 +127,7 @@ MODULE_PARM_DESC(minor, | |||
127 | "default is -1 (automatic)"); | 127 | "default is -1 (automatic)"); |
128 | #endif | 128 | #endif |
129 | 129 | ||
130 | static int kbd_backlight; /* = 1 */ | 130 | static int kbd_backlight = 1; |
131 | module_param(kbd_backlight, int, 0444); | 131 | module_param(kbd_backlight, int, 0444); |
132 | MODULE_PARM_DESC(kbd_backlight, | 132 | MODULE_PARM_DESC(kbd_backlight, |
133 | "set this to 0 to disable keyboard backlight, " | 133 | "set this to 0 to disable keyboard backlight, " |
@@ -347,6 +347,7 @@ static void sony_laptop_report_input_event(u8 event) | |||
347 | struct input_dev *jog_dev = sony_laptop_input.jog_dev; | 347 | struct input_dev *jog_dev = sony_laptop_input.jog_dev; |
348 | struct input_dev *key_dev = sony_laptop_input.key_dev; | 348 | struct input_dev *key_dev = sony_laptop_input.key_dev; |
349 | struct sony_laptop_keypress kp = { NULL }; | 349 | struct sony_laptop_keypress kp = { NULL }; |
350 | int scancode = -1; | ||
350 | 351 | ||
351 | if (event == SONYPI_EVENT_FNKEY_RELEASED || | 352 | if (event == SONYPI_EVENT_FNKEY_RELEASED || |
352 | event == SONYPI_EVENT_ANYBUTTON_RELEASED) { | 353 | event == SONYPI_EVENT_ANYBUTTON_RELEASED) { |
@@ -380,8 +381,8 @@ static void sony_laptop_report_input_event(u8 event) | |||
380 | dprintk("sony_laptop_report_input_event, event not known: %d\n", event); | 381 | dprintk("sony_laptop_report_input_event, event not known: %d\n", event); |
381 | break; | 382 | break; |
382 | } | 383 | } |
383 | if (sony_laptop_input_index[event] != -1) { | 384 | if ((scancode = sony_laptop_input_index[event]) != -1) { |
384 | kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]]; | 385 | kp.key = sony_laptop_input_keycode_map[scancode]; |
385 | if (kp.key != KEY_UNKNOWN) | 386 | if (kp.key != KEY_UNKNOWN) |
386 | kp.dev = key_dev; | 387 | kp.dev = key_dev; |
387 | } | 388 | } |
@@ -389,9 +390,11 @@ static void sony_laptop_report_input_event(u8 event) | |||
389 | } | 390 | } |
390 | 391 | ||
391 | if (kp.dev) { | 392 | if (kp.dev) { |
393 | /* if we have a scancode we emit it so we can always | ||
394 | remap the key */ | ||
395 | if (scancode != -1) | ||
396 | input_event(kp.dev, EV_MSC, MSC_SCAN, scancode); | ||
392 | input_report_key(kp.dev, kp.key, 1); | 397 | input_report_key(kp.dev, kp.key, 1); |
393 | /* we emit the scancode so we can always remap the key */ | ||
394 | input_event(kp.dev, EV_MSC, MSC_SCAN, event); | ||
395 | input_sync(kp.dev); | 398 | input_sync(kp.dev); |
396 | 399 | ||
397 | /* schedule key release */ | 400 | /* schedule key release */ |
@@ -466,7 +469,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) | |||
466 | jog_dev->name = "Sony Vaio Jogdial"; | 469 | jog_dev->name = "Sony Vaio Jogdial"; |
467 | jog_dev->id.bustype = BUS_ISA; | 470 | jog_dev->id.bustype = BUS_ISA; |
468 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; | 471 | jog_dev->id.vendor = PCI_VENDOR_ID_SONY; |
469 | key_dev->dev.parent = &acpi_device->dev; | 472 | jog_dev->dev.parent = &acpi_device->dev; |
470 | 473 | ||
471 | input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); | 474 | input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); |
472 | input_set_capability(jog_dev, EV_REL, REL_WHEEL); | 475 | input_set_capability(jog_dev, EV_REL, REL_WHEEL); |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index ea0c6075b720..d68c0002f4a2 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -8658,7 +8658,7 @@ static int __must_check __init get_thinkpad_model_data( | |||
8658 | } | 8658 | } |
8659 | 8659 | ||
8660 | s = dmi_get_system_info(DMI_PRODUCT_VERSION); | 8660 | s = dmi_get_system_info(DMI_PRODUCT_VERSION); |
8661 | if (s && !strnicmp(s, "ThinkPad", 8)) { | 8661 | if (s && !(strnicmp(s, "ThinkPad", 8) && strnicmp(s, "Lenovo", 6))) { |
8662 | tp->model_str = kstrdup(s, GFP_KERNEL); | 8662 | tp->model_str = kstrdup(s, GFP_KERNEL); |
8663 | if (!tp->model_str) | 8663 | if (!tp->model_str) |
8664 | return -ENOMEM; | 8664 | return -ENOMEM; |
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index dcdc1f4a4624..ee79ce64d9df 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -52,6 +52,8 @@ | |||
52 | #include <linux/input/sparse-keymap.h> | 52 | #include <linux/input/sparse-keymap.h> |
53 | #include <linux/leds.h> | 53 | #include <linux/leds.h> |
54 | #include <linux/slab.h> | 54 | #include <linux/slab.h> |
55 | #include <linux/workqueue.h> | ||
56 | #include <linux/i8042.h> | ||
55 | 57 | ||
56 | #include <asm/uaccess.h> | 58 | #include <asm/uaccess.h> |
57 | 59 | ||
@@ -61,6 +63,11 @@ MODULE_AUTHOR("John Belmonte"); | |||
61 | MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); | 63 | MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); |
62 | MODULE_LICENSE("GPL"); | 64 | MODULE_LICENSE("GPL"); |
63 | 65 | ||
66 | #define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" | ||
67 | |||
68 | /* Scan code for Fn key on TOS1900 models */ | ||
69 | #define TOS1900_FN_SCAN 0x6e | ||
70 | |||
64 | /* Toshiba ACPI method paths */ | 71 | /* Toshiba ACPI method paths */ |
65 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" | 72 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" |
66 | 73 | ||
@@ -95,6 +102,8 @@ MODULE_LICENSE("GPL"); | |||
95 | #define HCI_WIRELESS 0x0056 | 102 | #define HCI_WIRELESS 0x0056 |
96 | 103 | ||
97 | /* field definitions */ | 104 | /* field definitions */ |
105 | #define HCI_HOTKEY_DISABLE 0x0b | ||
106 | #define HCI_HOTKEY_ENABLE 0x09 | ||
98 | #define HCI_LCD_BRIGHTNESS_BITS 3 | 107 | #define HCI_LCD_BRIGHTNESS_BITS 3 |
99 | #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) | 108 | #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) |
100 | #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) | 109 | #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) |
@@ -111,6 +120,7 @@ struct toshiba_acpi_dev { | |||
111 | const char *method_hci; | 120 | const char *method_hci; |
112 | struct rfkill *bt_rfk; | 121 | struct rfkill *bt_rfk; |
113 | struct input_dev *hotkey_dev; | 122 | struct input_dev *hotkey_dev; |
123 | struct work_struct hotkey_work; | ||
114 | struct backlight_device *backlight_dev; | 124 | struct backlight_device *backlight_dev; |
115 | struct led_classdev led_dev; | 125 | struct led_classdev led_dev; |
116 | 126 | ||
@@ -118,14 +128,18 @@ struct toshiba_acpi_dev { | |||
118 | int last_key_event; | 128 | int last_key_event; |
119 | int key_event_valid; | 129 | int key_event_valid; |
120 | 130 | ||
121 | int illumination_supported:1; | 131 | unsigned int illumination_supported:1; |
122 | int video_supported:1; | 132 | unsigned int video_supported:1; |
123 | int fan_supported:1; | 133 | unsigned int fan_supported:1; |
124 | int system_event_supported:1; | 134 | unsigned int system_event_supported:1; |
135 | unsigned int ntfy_supported:1; | ||
136 | unsigned int info_supported:1; | ||
125 | 137 | ||
126 | struct mutex mutex; | 138 | struct mutex mutex; |
127 | }; | 139 | }; |
128 | 140 | ||
141 | static struct toshiba_acpi_dev *toshiba_acpi; | ||
142 | |||
129 | static const struct acpi_device_id toshiba_device_ids[] = { | 143 | static const struct acpi_device_id toshiba_device_ids[] = { |
130 | {"TOS6200", 0}, | 144 | {"TOS6200", 0}, |
131 | {"TOS6208", 0}, | 145 | {"TOS6208", 0}, |
@@ -138,6 +152,8 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { | |||
138 | { KE_KEY, 0x101, { KEY_MUTE } }, | 152 | { KE_KEY, 0x101, { KEY_MUTE } }, |
139 | { KE_KEY, 0x102, { KEY_ZOOMOUT } }, | 153 | { KE_KEY, 0x102, { KEY_ZOOMOUT } }, |
140 | { KE_KEY, 0x103, { KEY_ZOOMIN } }, | 154 | { KE_KEY, 0x103, { KEY_ZOOMIN } }, |
155 | { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, | ||
156 | { KE_KEY, 0x139, { KEY_ZOOMRESET } }, | ||
141 | { KE_KEY, 0x13b, { KEY_COFFEE } }, | 157 | { KE_KEY, 0x13b, { KEY_COFFEE } }, |
142 | { KE_KEY, 0x13c, { KEY_BATTERY } }, | 158 | { KE_KEY, 0x13c, { KEY_BATTERY } }, |
143 | { KE_KEY, 0x13d, { KEY_SLEEP } }, | 159 | { KE_KEY, 0x13d, { KEY_SLEEP } }, |
@@ -146,7 +162,7 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { | |||
146 | { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, | 162 | { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, |
147 | { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, | 163 | { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, |
148 | { KE_KEY, 0x142, { KEY_WLAN } }, | 164 | { KE_KEY, 0x142, { KEY_WLAN } }, |
149 | { KE_KEY, 0x143, { KEY_PROG1 } }, | 165 | { KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } }, |
150 | { KE_KEY, 0x17f, { KEY_FN } }, | 166 | { KE_KEY, 0x17f, { KEY_FN } }, |
151 | { KE_KEY, 0xb05, { KEY_PROG2 } }, | 167 | { KE_KEY, 0xb05, { KEY_PROG2 } }, |
152 | { KE_KEY, 0xb06, { KEY_WWW } }, | 168 | { KE_KEY, 0xb06, { KEY_WWW } }, |
@@ -156,6 +172,7 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { | |||
156 | { KE_KEY, 0xb32, { KEY_NEXTSONG } }, | 172 | { KE_KEY, 0xb32, { KEY_NEXTSONG } }, |
157 | { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, | 173 | { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, |
158 | { KE_KEY, 0xb5a, { KEY_MEDIA } }, | 174 | { KE_KEY, 0xb5a, { KEY_MEDIA } }, |
175 | { KE_IGNORE, 0x1430, { KEY_RESERVED } }, | ||
159 | { KE_END, 0 }, | 176 | { KE_END, 0 }, |
160 | }; | 177 | }; |
161 | 178 | ||
@@ -847,10 +864,78 @@ static const struct backlight_ops toshiba_backlight_data = { | |||
847 | .update_status = set_lcd_status, | 864 | .update_status = set_lcd_status, |
848 | }; | 865 | }; |
849 | 866 | ||
867 | static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, | ||
868 | struct serio *port) | ||
869 | { | ||
870 | if (str & 0x20) | ||
871 | return false; | ||
872 | |||
873 | if (unlikely(data == 0xe0)) | ||
874 | return false; | ||
875 | |||
876 | if ((data & 0x7f) == TOS1900_FN_SCAN) { | ||
877 | schedule_work(&toshiba_acpi->hotkey_work); | ||
878 | return true; | ||
879 | } | ||
880 | |||
881 | return false; | ||
882 | } | ||
883 | |||
884 | static void toshiba_acpi_hotkey_work(struct work_struct *work) | ||
885 | { | ||
886 | acpi_handle ec_handle = ec_get_handle(); | ||
887 | acpi_status status; | ||
888 | |||
889 | if (!ec_handle) | ||
890 | return; | ||
891 | |||
892 | status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL); | ||
893 | if (ACPI_FAILURE(status)) | ||
894 | pr_err("ACPI NTFY method execution failed\n"); | ||
895 | } | ||
896 | |||
897 | /* | ||
898 | * Returns hotkey scancode, or < 0 on failure. | ||
899 | */ | ||
900 | static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev) | ||
901 | { | ||
902 | struct acpi_buffer buf; | ||
903 | union acpi_object out_obj; | ||
904 | acpi_status status; | ||
905 | |||
906 | buf.pointer = &out_obj; | ||
907 | buf.length = sizeof(out_obj); | ||
908 | |||
909 | status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO", | ||
910 | NULL, &buf); | ||
911 | if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) { | ||
912 | pr_err("ACPI INFO method execution failed\n"); | ||
913 | return -EIO; | ||
914 | } | ||
915 | |||
916 | return out_obj.integer.value; | ||
917 | } | ||
918 | |||
919 | static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev, | ||
920 | int scancode) | ||
921 | { | ||
922 | if (scancode == 0x100) | ||
923 | return; | ||
924 | |||
925 | /* act on key press; ignore key release */ | ||
926 | if (scancode & 0x80) | ||
927 | return; | ||
928 | |||
929 | if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true)) | ||
930 | pr_info("Unknown key %x\n", scancode); | ||
931 | } | ||
932 | |||
850 | static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) | 933 | static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) |
851 | { | 934 | { |
852 | acpi_status status; | 935 | acpi_status status; |
936 | acpi_handle ec_handle, handle; | ||
853 | int error; | 937 | int error; |
938 | u32 hci_result; | ||
854 | 939 | ||
855 | dev->hotkey_dev = input_allocate_device(); | 940 | dev->hotkey_dev = input_allocate_device(); |
856 | if (!dev->hotkey_dev) { | 941 | if (!dev->hotkey_dev) { |
@@ -866,21 +951,67 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) | |||
866 | if (error) | 951 | if (error) |
867 | goto err_free_dev; | 952 | goto err_free_dev; |
868 | 953 | ||
954 | /* | ||
955 | * For some machines the SCI responsible for providing hotkey | ||
956 | * notification doesn't fire. We can trigger the notification | ||
957 | * whenever the Fn key is pressed using the NTFY method, if | ||
958 | * supported, so if it's present set up an i8042 key filter | ||
959 | * for this purpose. | ||
960 | */ | ||
961 | status = AE_ERROR; | ||
962 | ec_handle = ec_get_handle(); | ||
963 | if (ec_handle) | ||
964 | status = acpi_get_handle(ec_handle, "NTFY", &handle); | ||
965 | |||
966 | if (ACPI_SUCCESS(status)) { | ||
967 | INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work); | ||
968 | |||
969 | error = i8042_install_filter(toshiba_acpi_i8042_filter); | ||
970 | if (error) { | ||
971 | pr_err("Error installing key filter\n"); | ||
972 | goto err_free_keymap; | ||
973 | } | ||
974 | |||
975 | dev->ntfy_supported = 1; | ||
976 | } | ||
977 | |||
978 | /* | ||
979 | * Determine hotkey query interface. Prefer using the INFO | ||
980 | * method when it is available. | ||
981 | */ | ||
982 | status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle); | ||
983 | if (ACPI_SUCCESS(status)) { | ||
984 | dev->info_supported = 1; | ||
985 | } else { | ||
986 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); | ||
987 | if (hci_result == HCI_SUCCESS) | ||
988 | dev->system_event_supported = 1; | ||
989 | } | ||
990 | |||
991 | if (!dev->info_supported && !dev->system_event_supported) { | ||
992 | pr_warn("No hotkey query interface found\n"); | ||
993 | goto err_remove_filter; | ||
994 | } | ||
995 | |||
869 | status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); | 996 | status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); |
870 | if (ACPI_FAILURE(status)) { | 997 | if (ACPI_FAILURE(status)) { |
871 | pr_info("Unable to enable hotkeys\n"); | 998 | pr_info("Unable to enable hotkeys\n"); |
872 | error = -ENODEV; | 999 | error = -ENODEV; |
873 | goto err_free_keymap; | 1000 | goto err_remove_filter; |
874 | } | 1001 | } |
875 | 1002 | ||
876 | error = input_register_device(dev->hotkey_dev); | 1003 | error = input_register_device(dev->hotkey_dev); |
877 | if (error) { | 1004 | if (error) { |
878 | pr_info("Unable to register input device\n"); | 1005 | pr_info("Unable to register input device\n"); |
879 | goto err_free_keymap; | 1006 | goto err_remove_filter; |
880 | } | 1007 | } |
881 | 1008 | ||
1009 | hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result); | ||
882 | return 0; | 1010 | return 0; |
883 | 1011 | ||
1012 | err_remove_filter: | ||
1013 | if (dev->ntfy_supported) | ||
1014 | i8042_remove_filter(toshiba_acpi_i8042_filter); | ||
884 | err_free_keymap: | 1015 | err_free_keymap: |
885 | sparse_keymap_free(dev->hotkey_dev); | 1016 | sparse_keymap_free(dev->hotkey_dev); |
886 | err_free_dev: | 1017 | err_free_dev: |
@@ -895,6 +1026,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) | |||
895 | 1026 | ||
896 | remove_toshiba_proc_entries(dev); | 1027 | remove_toshiba_proc_entries(dev); |
897 | 1028 | ||
1029 | if (dev->ntfy_supported) { | ||
1030 | i8042_remove_filter(toshiba_acpi_i8042_filter); | ||
1031 | cancel_work_sync(&dev->hotkey_work); | ||
1032 | } | ||
1033 | |||
898 | if (dev->hotkey_dev) { | 1034 | if (dev->hotkey_dev) { |
899 | input_unregister_device(dev->hotkey_dev); | 1035 | input_unregister_device(dev->hotkey_dev); |
900 | sparse_keymap_free(dev->hotkey_dev); | 1036 | sparse_keymap_free(dev->hotkey_dev); |
@@ -911,6 +1047,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) | |||
911 | if (dev->illumination_supported) | 1047 | if (dev->illumination_supported) |
912 | led_classdev_unregister(&dev->led_dev); | 1048 | led_classdev_unregister(&dev->led_dev); |
913 | 1049 | ||
1050 | if (toshiba_acpi) | ||
1051 | toshiba_acpi = NULL; | ||
1052 | |||
914 | kfree(dev); | 1053 | kfree(dev); |
915 | 1054 | ||
916 | return 0; | 1055 | return 0; |
@@ -936,12 +1075,14 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
936 | { | 1075 | { |
937 | struct toshiba_acpi_dev *dev; | 1076 | struct toshiba_acpi_dev *dev; |
938 | const char *hci_method; | 1077 | const char *hci_method; |
939 | u32 hci_result; | ||
940 | u32 dummy; | 1078 | u32 dummy; |
941 | bool bt_present; | 1079 | bool bt_present; |
942 | int ret = 0; | 1080 | int ret = 0; |
943 | struct backlight_properties props; | 1081 | struct backlight_properties props; |
944 | 1082 | ||
1083 | if (toshiba_acpi) | ||
1084 | return -EBUSY; | ||
1085 | |||
945 | pr_info("Toshiba Laptop ACPI Extras version %s\n", | 1086 | pr_info("Toshiba Laptop ACPI Extras version %s\n", |
946 | TOSHIBA_ACPI_VERSION); | 1087 | TOSHIBA_ACPI_VERSION); |
947 | 1088 | ||
@@ -963,11 +1104,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
963 | 1104 | ||
964 | mutex_init(&dev->mutex); | 1105 | mutex_init(&dev->mutex); |
965 | 1106 | ||
966 | /* enable event fifo */ | ||
967 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); | ||
968 | if (hci_result == HCI_SUCCESS) | ||
969 | dev->system_event_supported = 1; | ||
970 | |||
971 | props.type = BACKLIGHT_PLATFORM; | 1107 | props.type = BACKLIGHT_PLATFORM; |
972 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | 1108 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; |
973 | dev->backlight_dev = backlight_device_register("toshiba", | 1109 | dev->backlight_dev = backlight_device_register("toshiba", |
@@ -1024,6 +1160,8 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
1024 | 1160 | ||
1025 | create_toshiba_proc_entries(dev); | 1161 | create_toshiba_proc_entries(dev); |
1026 | 1162 | ||
1163 | toshiba_acpi = dev; | ||
1164 | |||
1027 | return 0; | 1165 | return 0; |
1028 | 1166 | ||
1029 | error: | 1167 | error: |
@@ -1036,40 +1174,64 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) | |||
1036 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); | 1174 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); |
1037 | u32 hci_result, value; | 1175 | u32 hci_result, value; |
1038 | int retries = 3; | 1176 | int retries = 3; |
1177 | int scancode; | ||
1039 | 1178 | ||
1040 | if (!dev->system_event_supported || event != 0x80) | 1179 | if (event != 0x80) |
1041 | return; | 1180 | return; |
1042 | 1181 | ||
1043 | do { | 1182 | if (dev->info_supported) { |
1044 | hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); | 1183 | scancode = toshiba_acpi_query_hotkey(dev); |
1045 | switch (hci_result) { | 1184 | if (scancode < 0) |
1046 | case HCI_SUCCESS: | 1185 | pr_err("Failed to query hotkey event\n"); |
1047 | if (value == 0x100) | 1186 | else if (scancode != 0) |
1048 | continue; | 1187 | toshiba_acpi_report_hotkey(dev, scancode); |
1049 | /* act on key press; ignore key release */ | 1188 | } else if (dev->system_event_supported) { |
1050 | if (value & 0x80) | 1189 | do { |
1051 | continue; | 1190 | hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); |
1052 | 1191 | switch (hci_result) { | |
1053 | if (!sparse_keymap_report_event(dev->hotkey_dev, | 1192 | case HCI_SUCCESS: |
1054 | value, 1, true)) { | 1193 | toshiba_acpi_report_hotkey(dev, (int)value); |
1055 | pr_info("Unknown key %x\n", | 1194 | break; |
1056 | value); | 1195 | case HCI_NOT_SUPPORTED: |
1196 | /* | ||
1197 | * This is a workaround for an unresolved | ||
1198 | * issue on some machines where system events | ||
1199 | * sporadically become disabled. | ||
1200 | */ | ||
1201 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, | ||
1202 | &hci_result); | ||
1203 | pr_notice("Re-enabled hotkeys\n"); | ||
1204 | /* fall through */ | ||
1205 | default: | ||
1206 | retries--; | ||
1207 | break; | ||
1057 | } | 1208 | } |
1058 | break; | 1209 | } while (retries && hci_result != HCI_EMPTY); |
1059 | case HCI_NOT_SUPPORTED: | 1210 | } |
1060 | /* This is a workaround for an unresolved issue on | 1211 | } |
1061 | * some machines where system events sporadically | 1212 | |
1062 | * become disabled. */ | 1213 | static int toshiba_acpi_suspend(struct acpi_device *acpi_dev, |
1063 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); | 1214 | pm_message_t state) |
1064 | pr_notice("Re-enabled hotkeys\n"); | 1215 | { |
1065 | /* fall through */ | 1216 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); |
1066 | default: | 1217 | u32 result; |
1067 | retries--; | 1218 | |
1068 | break; | 1219 | if (dev->hotkey_dev) |
1069 | } | 1220 | hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result); |
1070 | } while (retries && hci_result != HCI_EMPTY); | 1221 | |
1222 | return 0; | ||
1071 | } | 1223 | } |
1072 | 1224 | ||
1225 | static int toshiba_acpi_resume(struct acpi_device *acpi_dev) | ||
1226 | { | ||
1227 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); | ||
1228 | u32 result; | ||
1229 | |||
1230 | if (dev->hotkey_dev) | ||
1231 | hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result); | ||
1232 | |||
1233 | return 0; | ||
1234 | } | ||
1073 | 1235 | ||
1074 | static struct acpi_driver toshiba_acpi_driver = { | 1236 | static struct acpi_driver toshiba_acpi_driver = { |
1075 | .name = "Toshiba ACPI driver", | 1237 | .name = "Toshiba ACPI driver", |
@@ -1080,6 +1242,8 @@ static struct acpi_driver toshiba_acpi_driver = { | |||
1080 | .add = toshiba_acpi_add, | 1242 | .add = toshiba_acpi_add, |
1081 | .remove = toshiba_acpi_remove, | 1243 | .remove = toshiba_acpi_remove, |
1082 | .notify = toshiba_acpi_notify, | 1244 | .notify = toshiba_acpi_notify, |
1245 | .suspend = toshiba_acpi_suspend, | ||
1246 | .resume = toshiba_acpi_resume, | ||
1083 | }, | 1247 | }, |
1084 | }; | 1248 | }; |
1085 | 1249 | ||
@@ -1087,6 +1251,14 @@ static int __init toshiba_acpi_init(void) | |||
1087 | { | 1251 | { |
1088 | int ret; | 1252 | int ret; |
1089 | 1253 | ||
1254 | /* | ||
1255 | * Machines with this WMI guid aren't supported due to bugs in | ||
1256 | * their AML. This check relies on wmi initializing before | ||
1257 | * toshiba_acpi to guarantee guids have been identified. | ||
1258 | */ | ||
1259 | if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) | ||
1260 | return -ENODEV; | ||
1261 | |||
1090 | toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); | 1262 | toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); |
1091 | if (!toshiba_proc_dir) { | 1263 | if (!toshiba_proc_dir) { |
1092 | pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); | 1264 | pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); |
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c index e549eeeda121..41781ed8301c 100644 --- a/drivers/platform/x86/xo1-rfkill.c +++ b/drivers/platform/x86/xo1-rfkill.c | |||
@@ -67,19 +67,8 @@ static struct platform_driver xo1_rfkill_driver = { | |||
67 | .remove = __devexit_p(xo1_rfkill_remove), | 67 | .remove = __devexit_p(xo1_rfkill_remove), |
68 | }; | 68 | }; |
69 | 69 | ||
70 | static int __init xo1_rfkill_init(void) | 70 | module_platform_driver(xo1_rfkill_driver); |
71 | { | ||
72 | return platform_driver_register(&xo1_rfkill_driver); | ||
73 | } | ||
74 | |||
75 | static void __exit xo1_rfkill_exit(void) | ||
76 | { | ||
77 | platform_driver_unregister(&xo1_rfkill_driver); | ||
78 | } | ||
79 | 71 | ||
80 | MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>"); | 72 | MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>"); |
81 | MODULE_LICENSE("GPL"); | 73 | MODULE_LICENSE("GPL"); |
82 | MODULE_ALIAS("platform:xo1-rfkill"); | 74 | MODULE_ALIAS("platform:xo1-rfkill"); |
83 | |||
84 | module_init(xo1_rfkill_init); | ||
85 | module_exit(xo1_rfkill_exit); | ||