diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-10 18:39:48 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-10 18:39:48 -0500 |
commit | 443e6221e465efa8efb752a8405a759ef1161af9 (patch) | |
tree | 8732213fc309a14f8613849b1ff2a2951757aff1 | |
parent | 0caca697a2d173c6beff3c24e7d9481b732bd131 (diff) | |
parent | 1a7d946993aaf2a79e9c65abbe169a108e351bcb (diff) |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (36 commits)
sony-laptop: support new hotkeys on the P, Z and EC series
platform/x86: Consistently select LEDS Kconfig options
sony-laptop: fix sparse non-ANSI function warning
intel_ips: fix sparse non-ANSI function warning
Support KHLB2 in the compal laptop driver
acer-wmi: Enabled Acer Launch Manager mode
[PATCH] intel_pmic_gpio: modify EOI handling following change of kernel irq subsystem
ACPI Thinkpad: We must always call va_end() after va_start() but do not do so in thinkpad_acpi.c::acpi_evalf()
acer-wmi: Initialize wlan/bluetooth/wwan rfkill software block state
acer-wmi: Detect the WiFi/Bluetooth/3G devices available
acer-wmi: Add 3G rfkill sysfs file
acer-wmi: Add acer wmi hotkey events support
platform/x86: Kconfig: Replace select by depends on ACPI_WMI
ideapad: pass ideapad_priv as argument (part 2)
ideapad: pass ideapad_priv as argument (part 1)
ideapad: add markups, unify comments and return result when init
ideapad: add hotkey support
ideapad: let camera power control entry under platform driver
ideapad: add platform driver for ideapad
fujitsu-laptop: fix compiler warning on pnp_ids
...
-rw-r--r-- | Documentation/ABI/testing/sysfs-platform-ideapad-laptop | 6 | ||||
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 27 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 429 | ||||
-rw-r--r-- | drivers/platform/x86/classmate-laptop.c | 19 | ||||
-rw-r--r-- | drivers/platform/x86/compal-laptop.c | 8 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 11 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-wmi.c | 609 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 259 | ||||
-rw-r--r-- | drivers/platform/x86/intel_ips.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmic_gpio.c | 6 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipcutil.c | 133 | ||||
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 12 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 3 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 133 | ||||
-rw-r--r-- | include/linux/sonypi.h | 1 |
19 files changed, 1431 insertions, 242 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop new file mode 100644 index 000000000000..807fca2ae2a4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop | |||
@@ -0,0 +1,6 @@ | |||
1 | What: /sys/devices/platform/ideapad/camera_power | ||
2 | Date: Dec 2010 | ||
3 | KernelVersion: 2.6.37 | ||
4 | Contact: "Ike Panhc <ike.pan@canonical.com>" | ||
5 | Description: | ||
6 | Control the power of camera module. 1 means on, 0 means off. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 9e4d4ca690e2..5181828012ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -2271,6 +2271,14 @@ W: http://acpi4asus.sf.net | |||
2271 | S: Maintained | 2271 | S: Maintained |
2272 | F: drivers/platform/x86/eeepc-laptop.c | 2272 | F: drivers/platform/x86/eeepc-laptop.c |
2273 | 2273 | ||
2274 | EEEPC WMI EXTRAS DRIVER | ||
2275 | M: Corentin Chary <corentincj@iksaif.net> | ||
2276 | L: acpi4asus-user@lists.sourceforge.net | ||
2277 | L: platform-driver-x86@vger.kernel.org | ||
2278 | W: http://acpi4asus.sf.net | ||
2279 | S: Maintained | ||
2280 | F: drivers/platform/x86/eeepc-wmi.c | ||
2281 | |||
2274 | EFIFB FRAMEBUFFER DRIVER | 2282 | EFIFB FRAMEBUFFER DRIVER |
2275 | L: linux-fbdev@vger.kernel.org | 2283 | L: linux-fbdev@vger.kernel.org |
2276 | M: Peter Jones <pjones@redhat.com> | 2284 | M: Peter Jones <pjones@redhat.com> |
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index faec777b1ed4..d163bc2e2b9e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -18,12 +18,14 @@ if X86_PLATFORM_DEVICES | |||
18 | config ACER_WMI | 18 | config ACER_WMI |
19 | tristate "Acer WMI Laptop Extras" | 19 | tristate "Acer WMI Laptop Extras" |
20 | depends on ACPI | 20 | depends on ACPI |
21 | depends on LEDS_CLASS | 21 | select LEDS_CLASS |
22 | depends on NEW_LEDS | 22 | select NEW_LEDS |
23 | depends on BACKLIGHT_CLASS_DEVICE | 23 | depends on BACKLIGHT_CLASS_DEVICE |
24 | depends on SERIO_I8042 | 24 | depends on SERIO_I8042 |
25 | depends on INPUT | ||
25 | depends on RFKILL || RFKILL = n | 26 | depends on RFKILL || RFKILL = n |
26 | select ACPI_WMI | 27 | depends on ACPI_WMI |
28 | select INPUT_SPARSEKMAP | ||
27 | ---help--- | 29 | ---help--- |
28 | This is a driver for newer Acer (and Wistron) laptops. It adds | 30 | This is a driver for newer Acer (and Wistron) laptops. It adds |
29 | wireless radio and bluetooth control, and on some laptops, | 31 | wireless radio and bluetooth control, and on some laptops, |
@@ -131,7 +133,7 @@ config TC1100_WMI | |||
131 | depends on !X86_64 | 133 | depends on !X86_64 |
132 | depends on EXPERIMENTAL | 134 | depends on EXPERIMENTAL |
133 | depends on ACPI | 135 | depends on ACPI |
134 | select ACPI_WMI | 136 | depends on ACPI_WMI |
135 | ---help--- | 137 | ---help--- |
136 | This is a driver for the WMI extensions (wireless and bluetooth power | 138 | This is a driver for the WMI extensions (wireless and bluetooth power |
137 | control) of the HP Compaq TC1100 tablet. | 139 | control) of the HP Compaq TC1100 tablet. |
@@ -226,6 +228,7 @@ config IDEAPAD_LAPTOP | |||
226 | tristate "Lenovo IdeaPad Laptop Extras" | 228 | tristate "Lenovo IdeaPad Laptop Extras" |
227 | depends on ACPI | 229 | depends on ACPI |
228 | depends on RFKILL | 230 | depends on RFKILL |
231 | select INPUT_SPARSEKMAP | ||
229 | help | 232 | help |
230 | This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. | 233 | This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. |
231 | 234 | ||
@@ -425,7 +428,10 @@ config EEEPC_WMI | |||
425 | depends on INPUT | 428 | depends on INPUT |
426 | depends on EXPERIMENTAL | 429 | depends on EXPERIMENTAL |
427 | depends on BACKLIGHT_CLASS_DEVICE | 430 | depends on BACKLIGHT_CLASS_DEVICE |
431 | depends on RFKILL || RFKILL = n | ||
428 | select INPUT_SPARSEKMAP | 432 | select INPUT_SPARSEKMAP |
433 | select LEDS_CLASS | ||
434 | select NEW_LEDS | ||
429 | ---help--- | 435 | ---help--- |
430 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. | 436 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. |
431 | 437 | ||
@@ -510,8 +516,8 @@ config TOPSTAR_LAPTOP | |||
510 | config ACPI_TOSHIBA | 516 | config ACPI_TOSHIBA |
511 | tristate "Toshiba Laptop Extras" | 517 | tristate "Toshiba Laptop Extras" |
512 | depends on ACPI | 518 | depends on ACPI |
513 | depends on LEDS_CLASS | 519 | select LEDS_CLASS |
514 | depends on NEW_LEDS | 520 | select NEW_LEDS |
515 | depends on BACKLIGHT_CLASS_DEVICE | 521 | depends on BACKLIGHT_CLASS_DEVICE |
516 | depends on INPUT | 522 | depends on INPUT |
517 | depends on RFKILL || RFKILL = n | 523 | depends on RFKILL || RFKILL = n |
@@ -576,6 +582,15 @@ config INTEL_SCU_IPC | |||
576 | some embedded Intel x86 platforms. This is not needed for PC-type | 582 | some embedded Intel x86 platforms. This is not needed for PC-type |
577 | machines. | 583 | machines. |
578 | 584 | ||
585 | config INTEL_SCU_IPC_UTIL | ||
586 | tristate "Intel SCU IPC utility driver" | ||
587 | depends on INTEL_SCU_IPC | ||
588 | default y | ||
589 | ---help--- | ||
590 | The IPC Util driver provides an interface with the SCU enabling | ||
591 | low level access for debug work and updating the firmware. Say | ||
592 | N unless you will be doing this on an Intel MID platform. | ||
593 | |||
579 | config GPIO_INTEL_PMIC | 594 | config GPIO_INTEL_PMIC |
580 | bool "Intel PMIC GPIO support" | 595 | bool "Intel PMIC GPIO support" |
581 | depends on INTEL_SCU_IPC && GPIOLIB | 596 | depends on INTEL_SCU_IPC && GPIOLIB |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9950ccc940b5..4ec4ff8f9182 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -28,6 +28,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | |||
28 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 28 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
29 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o | 29 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o |
30 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o | 30 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o |
31 | obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o | ||
31 | obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o | 32 | obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o |
32 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o | 33 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o |
33 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o | 34 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o |
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c8c65375bfe2..ee40d681edd0 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -37,6 +37,9 @@ | |||
37 | #include <linux/workqueue.h> | 37 | #include <linux/workqueue.h> |
38 | #include <linux/debugfs.h> | 38 | #include <linux/debugfs.h> |
39 | #include <linux/slab.h> | 39 | #include <linux/slab.h> |
40 | #include <linux/input.h> | ||
41 | #include <linux/input/sparse-keymap.h> | ||
42 | #include <linux/dmi.h> | ||
40 | 43 | ||
41 | #include <acpi/acpi_drivers.h> | 44 | #include <acpi/acpi_drivers.h> |
42 | 45 | ||
@@ -48,6 +51,7 @@ MODULE_LICENSE("GPL"); | |||
48 | #define ACER_ERR KERN_ERR ACER_LOGPREFIX | 51 | #define ACER_ERR KERN_ERR ACER_LOGPREFIX |
49 | #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX | 52 | #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX |
50 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX | 53 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX |
54 | #define ACER_WARNING KERN_WARNING ACER_LOGPREFIX | ||
51 | 55 | ||
52 | /* | 56 | /* |
53 | * Magic Number | 57 | * Magic Number |
@@ -82,9 +86,82 @@ MODULE_LICENSE("GPL"); | |||
82 | #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" | 86 | #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" |
83 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" | 87 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" |
84 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" | 88 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" |
89 | #define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" | ||
90 | |||
91 | /* | ||
92 | * Acer ACPI event GUIDs | ||
93 | */ | ||
94 | #define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026" | ||
85 | 95 | ||
86 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); | 96 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); |
87 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); | 97 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); |
98 | MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026"); | ||
99 | |||
100 | enum acer_wmi_event_ids { | ||
101 | WMID_HOTKEY_EVENT = 0x1, | ||
102 | }; | ||
103 | |||
104 | static const struct key_entry acer_wmi_keymap[] = { | ||
105 | {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */ | ||
106 | {KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */ | ||
107 | {KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */ | ||
108 | {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ | ||
109 | {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ | ||
110 | {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ | ||
111 | {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */ | ||
112 | {KE_KEY, 0x82, {KEY_F22} }, /* Touch Pad On/Off */ | ||
113 | {KE_END, 0} | ||
114 | }; | ||
115 | |||
116 | static struct input_dev *acer_wmi_input_dev; | ||
117 | |||
118 | struct event_return_value { | ||
119 | u8 function; | ||
120 | u8 key_num; | ||
121 | u16 device_state; | ||
122 | u32 reserved; | ||
123 | } __attribute__((packed)); | ||
124 | |||
125 | /* | ||
126 | * GUID3 Get Device Status device flags | ||
127 | */ | ||
128 | #define ACER_WMID3_GDS_WIRELESS (1<<0) /* WiFi */ | ||
129 | #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ | ||
130 | #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ | ||
131 | |||
132 | struct lm_input_params { | ||
133 | u8 function_num; /* Function Number */ | ||
134 | u16 commun_devices; /* Communication type devices default status */ | ||
135 | u16 devices; /* Other type devices default status */ | ||
136 | u8 lm_status; /* Launch Manager Status */ | ||
137 | u16 reserved; | ||
138 | } __attribute__((packed)); | ||
139 | |||
140 | struct lm_return_value { | ||
141 | u8 error_code; /* Error Code */ | ||
142 | u8 ec_return_value; /* EC Return Value */ | ||
143 | u16 reserved; | ||
144 | } __attribute__((packed)); | ||
145 | |||
146 | struct wmid3_gds_input_param { /* Get Device Status input parameter */ | ||
147 | u8 function_num; /* Function Number */ | ||
148 | u8 hotkey_number; /* Hotkey Number */ | ||
149 | u16 devices; /* Get Device */ | ||
150 | } __attribute__((packed)); | ||
151 | |||
152 | struct wmid3_gds_return_value { /* Get Device Status return value*/ | ||
153 | u8 error_code; /* Error Code */ | ||
154 | u8 ec_return_value; /* EC Return Value */ | ||
155 | u16 devices; /* Current Device Status */ | ||
156 | u32 reserved; | ||
157 | } __attribute__((packed)); | ||
158 | |||
159 | struct hotkey_function_type_aa { | ||
160 | u8 type; | ||
161 | u8 length; | ||
162 | u16 handle; | ||
163 | u16 commun_func_bitmap; | ||
164 | } __attribute__((packed)); | ||
88 | 165 | ||
89 | /* | 166 | /* |
90 | * Interface capability flags | 167 | * Interface capability flags |
@@ -116,15 +193,19 @@ static int mailled = -1; | |||
116 | static int brightness = -1; | 193 | static int brightness = -1; |
117 | static int threeg = -1; | 194 | static int threeg = -1; |
118 | static int force_series; | 195 | static int force_series; |
196 | static bool ec_raw_mode; | ||
197 | static bool has_type_aa; | ||
119 | 198 | ||
120 | module_param(mailled, int, 0444); | 199 | module_param(mailled, int, 0444); |
121 | module_param(brightness, int, 0444); | 200 | module_param(brightness, int, 0444); |
122 | module_param(threeg, int, 0444); | 201 | module_param(threeg, int, 0444); |
123 | module_param(force_series, int, 0444); | 202 | module_param(force_series, int, 0444); |
203 | module_param(ec_raw_mode, bool, 0444); | ||
124 | MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); | 204 | MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); |
125 | MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); | 205 | MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); |
126 | MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); | 206 | MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); |
127 | MODULE_PARM_DESC(force_series, "Force a different laptop series"); | 207 | MODULE_PARM_DESC(force_series, "Force a different laptop series"); |
208 | MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode"); | ||
128 | 209 | ||
129 | struct acer_data { | 210 | struct acer_data { |
130 | int mailled; | 211 | int mailled; |
@@ -140,6 +221,7 @@ struct acer_debug { | |||
140 | 221 | ||
141 | static struct rfkill *wireless_rfkill; | 222 | static struct rfkill *wireless_rfkill; |
142 | static struct rfkill *bluetooth_rfkill; | 223 | static struct rfkill *bluetooth_rfkill; |
224 | static struct rfkill *threeg_rfkill; | ||
143 | 225 | ||
144 | /* Each low-level interface must define at least some of the following */ | 226 | /* Each low-level interface must define at least some of the following */ |
145 | struct wmi_interface { | 227 | struct wmi_interface { |
@@ -753,6 +835,28 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) | |||
753 | return WMI_execute_u32(method_id, (u32)value, NULL); | 835 | return WMI_execute_u32(method_id, (u32)value, NULL); |
754 | } | 836 | } |
755 | 837 | ||
838 | static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) | ||
839 | { | ||
840 | struct hotkey_function_type_aa *type_aa; | ||
841 | |||
842 | /* We are looking for OEM-specific Type AAh */ | ||
843 | if (header->type != 0xAA) | ||
844 | return; | ||
845 | |||
846 | has_type_aa = true; | ||
847 | type_aa = (struct hotkey_function_type_aa *) header; | ||
848 | |||
849 | printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n", | ||
850 | type_aa->commun_func_bitmap); | ||
851 | |||
852 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) | ||
853 | interface->capability |= ACER_CAP_WIRELESS; | ||
854 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG) | ||
855 | interface->capability |= ACER_CAP_THREEG; | ||
856 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) | ||
857 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
858 | } | ||
859 | |||
756 | static acpi_status WMID_set_capabilities(void) | 860 | static acpi_status WMID_set_capabilities(void) |
757 | { | 861 | { |
758 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | 862 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; |
@@ -773,16 +877,17 @@ static acpi_status WMID_set_capabilities(void) | |||
773 | return AE_ERROR; | 877 | return AE_ERROR; |
774 | } | 878 | } |
775 | 879 | ||
776 | /* Not sure on the meaning of the relevant bits yet to detect these */ | 880 | dmi_walk(type_aa_dmi_decode, NULL); |
777 | interface->capability |= ACER_CAP_WIRELESS; | 881 | if (!has_type_aa) { |
778 | interface->capability |= ACER_CAP_THREEG; | 882 | interface->capability |= ACER_CAP_WIRELESS; |
883 | interface->capability |= ACER_CAP_THREEG; | ||
884 | if (devices & 0x10) | ||
885 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
886 | } | ||
779 | 887 | ||
780 | /* WMID always provides brightness methods */ | 888 | /* WMID always provides brightness methods */ |
781 | interface->capability |= ACER_CAP_BRIGHTNESS; | 889 | interface->capability |= ACER_CAP_BRIGHTNESS; |
782 | 890 | ||
783 | if (devices & 0x10) | ||
784 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
785 | |||
786 | if (!(devices & 0x20)) | 891 | if (!(devices & 0x20)) |
787 | max_brightness = 0x9; | 892 | max_brightness = 0x9; |
788 | 893 | ||
@@ -861,7 +966,8 @@ static void __init acer_commandline_init(void) | |||
861 | * capability isn't available on the given interface | 966 | * capability isn't available on the given interface |
862 | */ | 967 | */ |
863 | set_u32(mailled, ACER_CAP_MAILLED); | 968 | set_u32(mailled, ACER_CAP_MAILLED); |
864 | set_u32(threeg, ACER_CAP_THREEG); | 969 | if (!has_type_aa) |
970 | set_u32(threeg, ACER_CAP_THREEG); | ||
865 | set_u32(brightness, ACER_CAP_BRIGHTNESS); | 971 | set_u32(brightness, ACER_CAP_BRIGHTNESS); |
866 | } | 972 | } |
867 | 973 | ||
@@ -948,6 +1054,79 @@ static void acer_backlight_exit(void) | |||
948 | backlight_device_unregister(acer_backlight_device); | 1054 | backlight_device_unregister(acer_backlight_device); |
949 | } | 1055 | } |
950 | 1056 | ||
1057 | static acpi_status wmid3_get_device_status(u32 *value, u16 device) | ||
1058 | { | ||
1059 | struct wmid3_gds_return_value return_value; | ||
1060 | acpi_status status; | ||
1061 | union acpi_object *obj; | ||
1062 | struct wmid3_gds_input_param params = { | ||
1063 | .function_num = 0x1, | ||
1064 | .hotkey_number = 0x01, | ||
1065 | .devices = device, | ||
1066 | }; | ||
1067 | struct acpi_buffer input = { | ||
1068 | sizeof(struct wmid3_gds_input_param), | ||
1069 | ¶ms | ||
1070 | }; | ||
1071 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1072 | |||
1073 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | ||
1074 | if (ACPI_FAILURE(status)) | ||
1075 | return status; | ||
1076 | |||
1077 | obj = output.pointer; | ||
1078 | |||
1079 | if (!obj) | ||
1080 | return AE_ERROR; | ||
1081 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1082 | kfree(obj); | ||
1083 | return AE_ERROR; | ||
1084 | } | ||
1085 | if (obj->buffer.length != 8) { | ||
1086 | printk(ACER_WARNING "Unknown buffer length %d\n", | ||
1087 | obj->buffer.length); | ||
1088 | kfree(obj); | ||
1089 | return AE_ERROR; | ||
1090 | } | ||
1091 | |||
1092 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
1093 | kfree(obj); | ||
1094 | |||
1095 | if (return_value.error_code || return_value.ec_return_value) | ||
1096 | printk(ACER_WARNING "Get Device Status failed: " | ||
1097 | "0x%x - 0x%x\n", return_value.error_code, | ||
1098 | return_value.ec_return_value); | ||
1099 | else | ||
1100 | *value = !!(return_value.devices & device); | ||
1101 | |||
1102 | return status; | ||
1103 | } | ||
1104 | |||
1105 | static acpi_status get_device_status(u32 *value, u32 cap) | ||
1106 | { | ||
1107 | if (wmi_has_guid(WMID_GUID3)) { | ||
1108 | u16 device; | ||
1109 | |||
1110 | switch (cap) { | ||
1111 | case ACER_CAP_WIRELESS: | ||
1112 | device = ACER_WMID3_GDS_WIRELESS; | ||
1113 | break; | ||
1114 | case ACER_CAP_BLUETOOTH: | ||
1115 | device = ACER_WMID3_GDS_BLUETOOTH; | ||
1116 | break; | ||
1117 | case ACER_CAP_THREEG: | ||
1118 | device = ACER_WMID3_GDS_THREEG; | ||
1119 | break; | ||
1120 | default: | ||
1121 | return AE_ERROR; | ||
1122 | } | ||
1123 | return wmid3_get_device_status(value, device); | ||
1124 | |||
1125 | } else { | ||
1126 | return get_u32(value, cap); | ||
1127 | } | ||
1128 | } | ||
1129 | |||
951 | /* | 1130 | /* |
952 | * Rfkill devices | 1131 | * Rfkill devices |
953 | */ | 1132 | */ |
@@ -968,6 +1147,13 @@ static void acer_rfkill_update(struct work_struct *ignored) | |||
968 | rfkill_set_sw_state(bluetooth_rfkill, !state); | 1147 | rfkill_set_sw_state(bluetooth_rfkill, !state); |
969 | } | 1148 | } |
970 | 1149 | ||
1150 | if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { | ||
1151 | status = wmid3_get_device_status(&state, | ||
1152 | ACER_WMID3_GDS_THREEG); | ||
1153 | if (ACPI_SUCCESS(status)) | ||
1154 | rfkill_set_sw_state(threeg_rfkill, !state); | ||
1155 | } | ||
1156 | |||
971 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); | 1157 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); |
972 | } | 1158 | } |
973 | 1159 | ||
@@ -991,6 +1177,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev, | |||
991 | { | 1177 | { |
992 | int err; | 1178 | int err; |
993 | struct rfkill *rfkill_dev; | 1179 | struct rfkill *rfkill_dev; |
1180 | u32 state; | ||
1181 | acpi_status status; | ||
994 | 1182 | ||
995 | rfkill_dev = rfkill_alloc(name, dev, type, | 1183 | rfkill_dev = rfkill_alloc(name, dev, type, |
996 | &acer_rfkill_ops, | 1184 | &acer_rfkill_ops, |
@@ -998,6 +1186,10 @@ static struct rfkill *acer_rfkill_register(struct device *dev, | |||
998 | if (!rfkill_dev) | 1186 | if (!rfkill_dev) |
999 | return ERR_PTR(-ENOMEM); | 1187 | return ERR_PTR(-ENOMEM); |
1000 | 1188 | ||
1189 | status = get_device_status(&state, cap); | ||
1190 | if (ACPI_SUCCESS(status)) | ||
1191 | rfkill_init_sw_state(rfkill_dev, !state); | ||
1192 | |||
1001 | err = rfkill_register(rfkill_dev); | 1193 | err = rfkill_register(rfkill_dev); |
1002 | if (err) { | 1194 | if (err) { |
1003 | rfkill_destroy(rfkill_dev); | 1195 | rfkill_destroy(rfkill_dev); |
@@ -1024,6 +1216,19 @@ static int acer_rfkill_init(struct device *dev) | |||
1024 | } | 1216 | } |
1025 | } | 1217 | } |
1026 | 1218 | ||
1219 | if (has_cap(ACER_CAP_THREEG)) { | ||
1220 | threeg_rfkill = acer_rfkill_register(dev, | ||
1221 | RFKILL_TYPE_WWAN, "acer-threeg", | ||
1222 | ACER_CAP_THREEG); | ||
1223 | if (IS_ERR(threeg_rfkill)) { | ||
1224 | rfkill_unregister(wireless_rfkill); | ||
1225 | rfkill_destroy(wireless_rfkill); | ||
1226 | rfkill_unregister(bluetooth_rfkill); | ||
1227 | rfkill_destroy(bluetooth_rfkill); | ||
1228 | return PTR_ERR(threeg_rfkill); | ||
1229 | } | ||
1230 | } | ||
1231 | |||
1027 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); | 1232 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); |
1028 | 1233 | ||
1029 | return 0; | 1234 | return 0; |
@@ -1040,6 +1245,11 @@ static void acer_rfkill_exit(void) | |||
1040 | rfkill_unregister(bluetooth_rfkill); | 1245 | rfkill_unregister(bluetooth_rfkill); |
1041 | rfkill_destroy(bluetooth_rfkill); | 1246 | rfkill_destroy(bluetooth_rfkill); |
1042 | } | 1247 | } |
1248 | |||
1249 | if (has_cap(ACER_CAP_THREEG)) { | ||
1250 | rfkill_unregister(threeg_rfkill); | ||
1251 | rfkill_destroy(threeg_rfkill); | ||
1252 | } | ||
1043 | return; | 1253 | return; |
1044 | } | 1254 | } |
1045 | 1255 | ||
@@ -1050,7 +1260,12 @@ static ssize_t show_bool_threeg(struct device *dev, | |||
1050 | struct device_attribute *attr, char *buf) | 1260 | struct device_attribute *attr, char *buf) |
1051 | { | 1261 | { |
1052 | u32 result; \ | 1262 | u32 result; \ |
1053 | acpi_status status = get_u32(&result, ACER_CAP_THREEG); | 1263 | acpi_status status; |
1264 | if (wmi_has_guid(WMID_GUID3)) | ||
1265 | status = wmid3_get_device_status(&result, | ||
1266 | ACER_WMID3_GDS_THREEG); | ||
1267 | else | ||
1268 | status = get_u32(&result, ACER_CAP_THREEG); | ||
1054 | if (ACPI_SUCCESS(status)) | 1269 | if (ACPI_SUCCESS(status)) |
1055 | return sprintf(buf, "%u\n", result); | 1270 | return sprintf(buf, "%u\n", result); |
1056 | return sprintf(buf, "Read error\n"); | 1271 | return sprintf(buf, "Read error\n"); |
@@ -1085,6 +1300,178 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | |||
1085 | 1300 | ||
1086 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); | 1301 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); |
1087 | 1302 | ||
1303 | static void acer_wmi_notify(u32 value, void *context) | ||
1304 | { | ||
1305 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1306 | union acpi_object *obj; | ||
1307 | struct event_return_value return_value; | ||
1308 | acpi_status status; | ||
1309 | |||
1310 | status = wmi_get_event_data(value, &response); | ||
1311 | if (status != AE_OK) { | ||
1312 | printk(ACER_WARNING "bad event status 0x%x\n", status); | ||
1313 | return; | ||
1314 | } | ||
1315 | |||
1316 | obj = (union acpi_object *)response.pointer; | ||
1317 | |||
1318 | if (!obj) | ||
1319 | return; | ||
1320 | if (obj->type != ACPI_TYPE_BUFFER) { | ||
1321 | printk(ACER_WARNING "Unknown response received %d\n", | ||
1322 | obj->type); | ||
1323 | kfree(obj); | ||
1324 | return; | ||
1325 | } | ||
1326 | if (obj->buffer.length != 8) { | ||
1327 | printk(ACER_WARNING "Unknown buffer length %d\n", | ||
1328 | obj->buffer.length); | ||
1329 | kfree(obj); | ||
1330 | return; | ||
1331 | } | ||
1332 | |||
1333 | return_value = *((struct event_return_value *)obj->buffer.pointer); | ||
1334 | kfree(obj); | ||
1335 | |||
1336 | switch (return_value.function) { | ||
1337 | case WMID_HOTKEY_EVENT: | ||
1338 | if (!sparse_keymap_report_event(acer_wmi_input_dev, | ||
1339 | return_value.key_num, 1, true)) | ||
1340 | printk(ACER_WARNING "Unknown key number - 0x%x\n", | ||
1341 | return_value.key_num); | ||
1342 | break; | ||
1343 | default: | ||
1344 | printk(ACER_WARNING "Unknown function number - %d - %d\n", | ||
1345 | return_value.function, return_value.key_num); | ||
1346 | break; | ||
1347 | } | ||
1348 | } | ||
1349 | |||
1350 | static acpi_status | ||
1351 | wmid3_set_lm_mode(struct lm_input_params *params, | ||
1352 | struct lm_return_value *return_value) | ||
1353 | { | ||
1354 | acpi_status status; | ||
1355 | union acpi_object *obj; | ||
1356 | |||
1357 | struct acpi_buffer input = { sizeof(struct lm_input_params), params }; | ||
1358 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1359 | |||
1360 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); | ||
1361 | if (ACPI_FAILURE(status)) | ||
1362 | return status; | ||
1363 | |||
1364 | obj = output.pointer; | ||
1365 | |||
1366 | if (!obj) | ||
1367 | return AE_ERROR; | ||
1368 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1369 | kfree(obj); | ||
1370 | return AE_ERROR; | ||
1371 | } | ||
1372 | if (obj->buffer.length != 4) { | ||
1373 | printk(ACER_WARNING "Unknown buffer length %d\n", | ||
1374 | obj->buffer.length); | ||
1375 | kfree(obj); | ||
1376 | return AE_ERROR; | ||
1377 | } | ||
1378 | |||
1379 | *return_value = *((struct lm_return_value *)obj->buffer.pointer); | ||
1380 | kfree(obj); | ||
1381 | |||
1382 | return status; | ||
1383 | } | ||
1384 | |||
1385 | static int acer_wmi_enable_ec_raw(void) | ||
1386 | { | ||
1387 | struct lm_return_value return_value; | ||
1388 | acpi_status status; | ||
1389 | struct lm_input_params params = { | ||
1390 | .function_num = 0x1, | ||
1391 | .commun_devices = 0xFFFF, | ||
1392 | .devices = 0xFFFF, | ||
1393 | .lm_status = 0x00, /* Launch Manager Deactive */ | ||
1394 | }; | ||
1395 | |||
1396 | status = wmid3_set_lm_mode(¶ms, &return_value); | ||
1397 | |||
1398 | if (return_value.error_code || return_value.ec_return_value) | ||
1399 | printk(ACER_WARNING "Enabling EC raw mode failed: " | ||
1400 | "0x%x - 0x%x\n", return_value.error_code, | ||
1401 | return_value.ec_return_value); | ||
1402 | else | ||
1403 | printk(ACER_INFO "Enabled EC raw mode"); | ||
1404 | |||
1405 | return status; | ||
1406 | } | ||
1407 | |||
1408 | static int acer_wmi_enable_lm(void) | ||
1409 | { | ||
1410 | struct lm_return_value return_value; | ||
1411 | acpi_status status; | ||
1412 | struct lm_input_params params = { | ||
1413 | .function_num = 0x1, | ||
1414 | .commun_devices = 0xFFFF, | ||
1415 | .devices = 0xFFFF, | ||
1416 | .lm_status = 0x01, /* Launch Manager Active */ | ||
1417 | }; | ||
1418 | |||
1419 | status = wmid3_set_lm_mode(¶ms, &return_value); | ||
1420 | |||
1421 | if (return_value.error_code || return_value.ec_return_value) | ||
1422 | printk(ACER_WARNING "Enabling Launch Manager failed: " | ||
1423 | "0x%x - 0x%x\n", return_value.error_code, | ||
1424 | return_value.ec_return_value); | ||
1425 | |||
1426 | return status; | ||
1427 | } | ||
1428 | |||
1429 | static int __init acer_wmi_input_setup(void) | ||
1430 | { | ||
1431 | acpi_status status; | ||
1432 | int err; | ||
1433 | |||
1434 | acer_wmi_input_dev = input_allocate_device(); | ||
1435 | if (!acer_wmi_input_dev) | ||
1436 | return -ENOMEM; | ||
1437 | |||
1438 | acer_wmi_input_dev->name = "Acer WMI hotkeys"; | ||
1439 | acer_wmi_input_dev->phys = "wmi/input0"; | ||
1440 | acer_wmi_input_dev->id.bustype = BUS_HOST; | ||
1441 | |||
1442 | err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL); | ||
1443 | if (err) | ||
1444 | goto err_free_dev; | ||
1445 | |||
1446 | status = wmi_install_notify_handler(ACERWMID_EVENT_GUID, | ||
1447 | acer_wmi_notify, NULL); | ||
1448 | if (ACPI_FAILURE(status)) { | ||
1449 | err = -EIO; | ||
1450 | goto err_free_keymap; | ||
1451 | } | ||
1452 | |||
1453 | err = input_register_device(acer_wmi_input_dev); | ||
1454 | if (err) | ||
1455 | goto err_uninstall_notifier; | ||
1456 | |||
1457 | return 0; | ||
1458 | |||
1459 | err_uninstall_notifier: | ||
1460 | wmi_remove_notify_handler(ACERWMID_EVENT_GUID); | ||
1461 | err_free_keymap: | ||
1462 | sparse_keymap_free(acer_wmi_input_dev); | ||
1463 | err_free_dev: | ||
1464 | input_free_device(acer_wmi_input_dev); | ||
1465 | return err; | ||
1466 | } | ||
1467 | |||
1468 | static void acer_wmi_input_destroy(void) | ||
1469 | { | ||
1470 | wmi_remove_notify_handler(ACERWMID_EVENT_GUID); | ||
1471 | sparse_keymap_free(acer_wmi_input_dev); | ||
1472 | input_unregister_device(acer_wmi_input_dev); | ||
1473 | } | ||
1474 | |||
1088 | /* | 1475 | /* |
1089 | * debugfs functions | 1476 | * debugfs functions |
1090 | */ | 1477 | */ |
@@ -1327,6 +1714,26 @@ static int __init acer_wmi_init(void) | |||
1327 | "generic video driver\n"); | 1714 | "generic video driver\n"); |
1328 | } | 1715 | } |
1329 | 1716 | ||
1717 | if (wmi_has_guid(WMID_GUID3)) { | ||
1718 | if (ec_raw_mode) { | ||
1719 | if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { | ||
1720 | printk(ACER_ERR "Cannot enable EC raw mode\n"); | ||
1721 | return -ENODEV; | ||
1722 | } | ||
1723 | } else if (ACPI_FAILURE(acer_wmi_enable_lm())) { | ||
1724 | printk(ACER_ERR "Cannot enable Launch Manager mode\n"); | ||
1725 | return -ENODEV; | ||
1726 | } | ||
1727 | } else if (ec_raw_mode) { | ||
1728 | printk(ACER_INFO "No WMID EC raw mode enable method\n"); | ||
1729 | } | ||
1730 | |||
1731 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) { | ||
1732 | err = acer_wmi_input_setup(); | ||
1733 | if (err) | ||
1734 | return err; | ||
1735 | } | ||
1736 | |||
1330 | err = platform_driver_register(&acer_platform_driver); | 1737 | err = platform_driver_register(&acer_platform_driver); |
1331 | if (err) { | 1738 | if (err) { |
1332 | printk(ACER_ERR "Unable to register platform driver.\n"); | 1739 | printk(ACER_ERR "Unable to register platform driver.\n"); |
@@ -1368,11 +1775,17 @@ error_device_add: | |||
1368 | error_device_alloc: | 1775 | error_device_alloc: |
1369 | platform_driver_unregister(&acer_platform_driver); | 1776 | platform_driver_unregister(&acer_platform_driver); |
1370 | error_platform_register: | 1777 | error_platform_register: |
1778 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
1779 | acer_wmi_input_destroy(); | ||
1780 | |||
1371 | return err; | 1781 | return err; |
1372 | } | 1782 | } |
1373 | 1783 | ||
1374 | static void __exit acer_wmi_exit(void) | 1784 | static void __exit acer_wmi_exit(void) |
1375 | { | 1785 | { |
1786 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
1787 | acer_wmi_input_destroy(); | ||
1788 | |||
1376 | remove_sysfs(acer_platform_device); | 1789 | remove_sysfs(acer_platform_device); |
1377 | remove_debugfs(); | 1790 | remove_debugfs(); |
1378 | platform_device_unregister(acer_platform_device); | 1791 | platform_device_unregister(acer_platform_device); |
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 341cbfef93ee..911135425224 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c | |||
@@ -522,18 +522,20 @@ static int cmpc_rfkill_block(void *data, bool blocked) | |||
522 | acpi_status status; | 522 | acpi_status status; |
523 | acpi_handle handle; | 523 | acpi_handle handle; |
524 | unsigned long long state; | 524 | unsigned long long state; |
525 | bool is_blocked; | ||
525 | 526 | ||
526 | handle = data; | 527 | handle = data; |
527 | status = cmpc_get_rfkill_wlan(handle, &state); | 528 | status = cmpc_get_rfkill_wlan(handle, &state); |
528 | if (ACPI_FAILURE(status)) | 529 | if (ACPI_FAILURE(status)) |
529 | return -ENODEV; | 530 | return -ENODEV; |
530 | if (blocked) | 531 | /* Check if we really need to call cmpc_set_rfkill_wlan */ |
531 | state &= ~1; | 532 | is_blocked = state & 1 ? false : true; |
532 | else | 533 | if (is_blocked != blocked) { |
533 | state |= 1; | 534 | state = blocked ? 0 : 1; |
534 | status = cmpc_set_rfkill_wlan(handle, state); | 535 | status = cmpc_set_rfkill_wlan(handle, state); |
535 | if (ACPI_FAILURE(status)) | 536 | if (ACPI_FAILURE(status)) |
536 | return -ENODEV; | 537 | return -ENODEV; |
538 | } | ||
537 | return 0; | 539 | return 0; |
538 | } | 540 | } |
539 | 541 | ||
@@ -653,8 +655,9 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event) | |||
653 | 655 | ||
654 | if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) | 656 | if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) |
655 | code = cmpc_keys_codes[event & 0x0F]; | 657 | code = cmpc_keys_codes[event & 0x0F]; |
656 | inputdev = dev_get_drvdata(&dev->dev);; | 658 | inputdev = dev_get_drvdata(&dev->dev); |
657 | input_report_key(inputdev, code, !(event & 0x10)); | 659 | input_report_key(inputdev, code, !(event & 0x10)); |
660 | input_sync(inputdev); | ||
658 | } | 661 | } |
659 | 662 | ||
660 | static void cmpc_keys_idev_init(struct input_dev *inputdev) | 663 | static void cmpc_keys_idev_init(struct input_dev *inputdev) |
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 097083cac413..034572b980c9 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c | |||
@@ -872,6 +872,14 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { | |||
872 | }, | 872 | }, |
873 | .callback = dmi_check_cb_extra | 873 | .callback = dmi_check_cb_extra |
874 | }, | 874 | }, |
875 | { | ||
876 | .ident = "KHLB2", | ||
877 | .matches = { | ||
878 | DMI_MATCH(DMI_BOARD_NAME, "KHLB2"), | ||
879 | DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"), | ||
880 | }, | ||
881 | .callback = dmi_check_cb_extra | ||
882 | }, | ||
875 | { } | 883 | { } |
876 | }; | 884 | }; |
877 | 885 | ||
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index b2edfdcdcb84..e9fc530e7dc2 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -529,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev, | |||
529 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); | 529 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); |
530 | } | 530 | } |
531 | 531 | ||
532 | static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | ||
533 | { | ||
534 | struct eeepc_laptop *eeepc; | ||
535 | |||
536 | eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); | ||
537 | |||
538 | return get_acpi(eeepc, CM_ASL_TPD); | ||
539 | } | ||
540 | |||
532 | static int eeepc_led_init(struct eeepc_laptop *eeepc) | 541 | static int eeepc_led_init(struct eeepc_laptop *eeepc) |
533 | { | 542 | { |
534 | int rv; | 543 | int rv; |
@@ -543,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) | |||
543 | 552 | ||
544 | eeepc->tpd_led.name = "eeepc::touchpad"; | 553 | eeepc->tpd_led.name = "eeepc::touchpad"; |
545 | eeepc->tpd_led.brightness_set = tpd_led_set; | 554 | eeepc->tpd_led.brightness_set = tpd_led_set; |
555 | if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */ | ||
556 | eeepc->tpd_led.brightness_get = tpd_led_get; | ||
546 | eeepc->tpd_led.max_brightness = 1; | 557 | eeepc->tpd_led.max_brightness = 1; |
547 | 558 | ||
548 | rv = led_classdev_register(&eeepc->platform_device->dev, | 559 | rv = led_classdev_register(&eeepc->platform_device->dev, |
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0d50fbbe2478..4d38f98aa976 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Eee PC WMI hotkey driver | 2 | * Eee PC WMI hotkey driver |
3 | * | 3 | * |
4 | * Copyright(C) 2010 Intel Corporation. | 4 | * Copyright(C) 2010 Intel Corporation. |
5 | * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com> | ||
5 | * | 6 | * |
6 | * Portions based on wistron_btns.c: | 7 | * Portions based on wistron_btns.c: |
7 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | 8 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> |
@@ -34,6 +35,10 @@ | |||
34 | #include <linux/input/sparse-keymap.h> | 35 | #include <linux/input/sparse-keymap.h> |
35 | #include <linux/fb.h> | 36 | #include <linux/fb.h> |
36 | #include <linux/backlight.h> | 37 | #include <linux/backlight.h> |
38 | #include <linux/leds.h> | ||
39 | #include <linux/rfkill.h> | ||
40 | #include <linux/debugfs.h> | ||
41 | #include <linux/seq_file.h> | ||
37 | #include <linux/platform_device.h> | 42 | #include <linux/platform_device.h> |
38 | #include <acpi/acpi_bus.h> | 43 | #include <acpi/acpi_bus.h> |
39 | #include <acpi/acpi_drivers.h> | 44 | #include <acpi/acpi_drivers.h> |
@@ -44,6 +49,8 @@ MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); | |||
44 | MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); | 49 | MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); |
45 | MODULE_LICENSE("GPL"); | 50 | MODULE_LICENSE("GPL"); |
46 | 51 | ||
52 | #define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ | ||
53 | |||
47 | #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" | 54 | #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" |
48 | #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" | 55 | #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" |
49 | 56 | ||
@@ -60,6 +67,10 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); | |||
60 | #define EEEPC_WMI_METHODID_CFVS 0x53564643 | 67 | #define EEEPC_WMI_METHODID_CFVS 0x53564643 |
61 | 68 | ||
62 | #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 | 69 | #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 |
70 | #define EEEPC_WMI_DEVID_TPDLED 0x00100011 | ||
71 | #define EEEPC_WMI_DEVID_WLAN 0x00010011 | ||
72 | #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 | ||
73 | #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 | ||
63 | 74 | ||
64 | static const struct key_entry eeepc_wmi_keymap[] = { | 75 | static const struct key_entry eeepc_wmi_keymap[] = { |
65 | /* Sleep already handled via generic ACPI code */ | 76 | /* Sleep already handled via generic ACPI code */ |
@@ -83,11 +94,37 @@ struct bios_args { | |||
83 | u32 ctrl_param; | 94 | u32 ctrl_param; |
84 | }; | 95 | }; |
85 | 96 | ||
97 | /* | ||
98 | * eeepc-wmi/ - debugfs root directory | ||
99 | * dev_id - current dev_id | ||
100 | * ctrl_param - current ctrl_param | ||
101 | * devs - call DEVS(dev_id, ctrl_param) and print result | ||
102 | * dsts - call DSTS(dev_id) and print result | ||
103 | */ | ||
104 | struct eeepc_wmi_debug { | ||
105 | struct dentry *root; | ||
106 | u32 dev_id; | ||
107 | u32 ctrl_param; | ||
108 | }; | ||
109 | |||
86 | struct eeepc_wmi { | 110 | struct eeepc_wmi { |
87 | struct input_dev *inputdev; | 111 | struct input_dev *inputdev; |
88 | struct backlight_device *backlight_device; | 112 | struct backlight_device *backlight_device; |
113 | struct platform_device *platform_device; | ||
114 | |||
115 | struct led_classdev tpd_led; | ||
116 | int tpd_led_wk; | ||
117 | struct workqueue_struct *led_workqueue; | ||
118 | struct work_struct tpd_led_work; | ||
119 | |||
120 | struct rfkill *wlan_rfkill; | ||
121 | struct rfkill *bluetooth_rfkill; | ||
122 | struct rfkill *wwan3g_rfkill; | ||
123 | |||
124 | struct eeepc_wmi_debug debug; | ||
89 | }; | 125 | }; |
90 | 126 | ||
127 | /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ | ||
91 | static struct platform_device *platform_device; | 128 | static struct platform_device *platform_device; |
92 | 129 | ||
93 | static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) | 130 | static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) |
@@ -101,7 +138,7 @@ static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) | |||
101 | eeepc->inputdev->name = "Eee PC WMI hotkeys"; | 138 | eeepc->inputdev->name = "Eee PC WMI hotkeys"; |
102 | eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; | 139 | eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; |
103 | eeepc->inputdev->id.bustype = BUS_HOST; | 140 | eeepc->inputdev->id.bustype = BUS_HOST; |
104 | eeepc->inputdev->dev.parent = &platform_device->dev; | 141 | eeepc->inputdev->dev.parent = &eeepc->platform_device->dev; |
105 | 142 | ||
106 | err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); | 143 | err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); |
107 | if (err) | 144 | if (err) |
@@ -130,7 +167,7 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) | |||
130 | eeepc->inputdev = NULL; | 167 | eeepc->inputdev = NULL; |
131 | } | 168 | } |
132 | 169 | ||
133 | static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) | 170 | static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) |
134 | { | 171 | { |
135 | struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; | 172 | struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; |
136 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 173 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
@@ -150,8 +187,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) | |||
150 | else | 187 | else |
151 | tmp = 0; | 188 | tmp = 0; |
152 | 189 | ||
153 | if (ctrl_param) | 190 | if (retval) |
154 | *ctrl_param = tmp; | 191 | *retval = tmp; |
155 | 192 | ||
156 | kfree(obj); | 193 | kfree(obj); |
157 | 194 | ||
@@ -159,7 +196,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) | |||
159 | 196 | ||
160 | } | 197 | } |
161 | 198 | ||
162 | static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) | 199 | static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, |
200 | u32 *retval) | ||
163 | { | 201 | { |
164 | struct bios_args args = { | 202 | struct bios_args args = { |
165 | .dev_id = dev_id, | 203 | .dev_id = dev_id, |
@@ -168,34 +206,281 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) | |||
168 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | 206 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; |
169 | acpi_status status; | 207 | acpi_status status; |
170 | 208 | ||
171 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | 209 | if (!retval) { |
172 | 1, EEEPC_WMI_METHODID_DEVS, &input, NULL); | 210 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, |
211 | EEEPC_WMI_METHODID_DEVS, | ||
212 | &input, NULL); | ||
213 | } else { | ||
214 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
215 | union acpi_object *obj; | ||
216 | u32 tmp; | ||
217 | |||
218 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, | ||
219 | EEEPC_WMI_METHODID_DEVS, | ||
220 | &input, &output); | ||
221 | |||
222 | if (ACPI_FAILURE(status)) | ||
223 | return status; | ||
224 | |||
225 | obj = (union acpi_object *)output.pointer; | ||
226 | if (obj && obj->type == ACPI_TYPE_INTEGER) | ||
227 | tmp = (u32)obj->integer.value; | ||
228 | else | ||
229 | tmp = 0; | ||
230 | |||
231 | *retval = tmp; | ||
232 | |||
233 | kfree(obj); | ||
234 | } | ||
173 | 235 | ||
174 | return status; | 236 | return status; |
175 | } | 237 | } |
176 | 238 | ||
239 | /* | ||
240 | * LEDs | ||
241 | */ | ||
242 | /* | ||
243 | * These functions actually update the LED's, and are called from a | ||
244 | * workqueue. By doing this as separate work rather than when the LED | ||
245 | * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a | ||
246 | * potentially bad time, such as a timer interrupt. | ||
247 | */ | ||
248 | static void tpd_led_update(struct work_struct *work) | ||
249 | { | ||
250 | int ctrl_param; | ||
251 | struct eeepc_wmi *eeepc; | ||
252 | |||
253 | eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); | ||
254 | |||
255 | ctrl_param = eeepc->tpd_led_wk; | ||
256 | eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL); | ||
257 | } | ||
258 | |||
259 | static void tpd_led_set(struct led_classdev *led_cdev, | ||
260 | enum led_brightness value) | ||
261 | { | ||
262 | struct eeepc_wmi *eeepc; | ||
263 | |||
264 | eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | ||
265 | |||
266 | eeepc->tpd_led_wk = !!value; | ||
267 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); | ||
268 | } | ||
269 | |||
270 | static int read_tpd_state(struct eeepc_wmi *eeepc) | ||
271 | { | ||
272 | u32 retval; | ||
273 | acpi_status status; | ||
274 | |||
275 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval); | ||
276 | |||
277 | if (ACPI_FAILURE(status)) | ||
278 | return -1; | ||
279 | else if (!retval || retval == 0x00060000) | ||
280 | /* | ||
281 | * if touchpad led is present, DSTS will set some bits, | ||
282 | * usually 0x00020000. | ||
283 | * 0x00060000 means that the device is not supported | ||
284 | */ | ||
285 | return -ENODEV; | ||
286 | else | ||
287 | /* Status is stored in the first bit */ | ||
288 | return retval & 0x1; | ||
289 | } | ||
290 | |||
291 | static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | ||
292 | { | ||
293 | struct eeepc_wmi *eeepc; | ||
294 | |||
295 | eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | ||
296 | |||
297 | return read_tpd_state(eeepc); | ||
298 | } | ||
299 | |||
300 | static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) | ||
301 | { | ||
302 | int rv; | ||
303 | |||
304 | if (read_tpd_state(eeepc) < 0) | ||
305 | return 0; | ||
306 | |||
307 | eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); | ||
308 | if (!eeepc->led_workqueue) | ||
309 | return -ENOMEM; | ||
310 | INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); | ||
311 | |||
312 | eeepc->tpd_led.name = "eeepc::touchpad"; | ||
313 | eeepc->tpd_led.brightness_set = tpd_led_set; | ||
314 | eeepc->tpd_led.brightness_get = tpd_led_get; | ||
315 | eeepc->tpd_led.max_brightness = 1; | ||
316 | |||
317 | rv = led_classdev_register(&eeepc->platform_device->dev, | ||
318 | &eeepc->tpd_led); | ||
319 | if (rv) { | ||
320 | destroy_workqueue(eeepc->led_workqueue); | ||
321 | return rv; | ||
322 | } | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) | ||
328 | { | ||
329 | if (eeepc->tpd_led.dev) | ||
330 | led_classdev_unregister(&eeepc->tpd_led); | ||
331 | if (eeepc->led_workqueue) | ||
332 | destroy_workqueue(eeepc->led_workqueue); | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Rfkill devices | ||
337 | */ | ||
338 | static int eeepc_rfkill_set(void *data, bool blocked) | ||
339 | { | ||
340 | int dev_id = (unsigned long)data; | ||
341 | u32 ctrl_param = !blocked; | ||
342 | |||
343 | return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); | ||
344 | } | ||
345 | |||
346 | static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) | ||
347 | { | ||
348 | int dev_id = (unsigned long)data; | ||
349 | u32 retval; | ||
350 | acpi_status status; | ||
351 | |||
352 | status = eeepc_wmi_get_devstate(dev_id, &retval); | ||
353 | |||
354 | if (ACPI_FAILURE(status)) | ||
355 | return ; | ||
356 | |||
357 | rfkill_set_sw_state(rfkill, !(retval & 0x1)); | ||
358 | } | ||
359 | |||
360 | static const struct rfkill_ops eeepc_rfkill_ops = { | ||
361 | .set_block = eeepc_rfkill_set, | ||
362 | .query = eeepc_rfkill_query, | ||
363 | }; | ||
364 | |||
365 | static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, | ||
366 | struct rfkill **rfkill, | ||
367 | const char *name, | ||
368 | enum rfkill_type type, int dev_id) | ||
369 | { | ||
370 | int result; | ||
371 | u32 retval; | ||
372 | acpi_status status; | ||
373 | |||
374 | status = eeepc_wmi_get_devstate(dev_id, &retval); | ||
375 | |||
376 | if (ACPI_FAILURE(status)) | ||
377 | return -1; | ||
378 | |||
379 | /* If the device is present, DSTS will always set some bits | ||
380 | * 0x00070000 - 1110000000000000000 - device supported | ||
381 | * 0x00060000 - 1100000000000000000 - not supported | ||
382 | * 0x00020000 - 0100000000000000000 - device supported | ||
383 | * 0x00010000 - 0010000000000000000 - not supported / special mode ? | ||
384 | */ | ||
385 | if (!retval || retval == 0x00060000) | ||
386 | return -ENODEV; | ||
387 | |||
388 | *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, | ||
389 | &eeepc_rfkill_ops, (void *)(long)dev_id); | ||
390 | |||
391 | if (!*rfkill) | ||
392 | return -EINVAL; | ||
393 | |||
394 | rfkill_init_sw_state(*rfkill, !(retval & 0x1)); | ||
395 | result = rfkill_register(*rfkill); | ||
396 | if (result) { | ||
397 | rfkill_destroy(*rfkill); | ||
398 | *rfkill = NULL; | ||
399 | return result; | ||
400 | } | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) | ||
405 | { | ||
406 | if (eeepc->wlan_rfkill) { | ||
407 | rfkill_unregister(eeepc->wlan_rfkill); | ||
408 | rfkill_destroy(eeepc->wlan_rfkill); | ||
409 | eeepc->wlan_rfkill = NULL; | ||
410 | } | ||
411 | if (eeepc->bluetooth_rfkill) { | ||
412 | rfkill_unregister(eeepc->bluetooth_rfkill); | ||
413 | rfkill_destroy(eeepc->bluetooth_rfkill); | ||
414 | eeepc->bluetooth_rfkill = NULL; | ||
415 | } | ||
416 | if (eeepc->wwan3g_rfkill) { | ||
417 | rfkill_unregister(eeepc->wwan3g_rfkill); | ||
418 | rfkill_destroy(eeepc->wwan3g_rfkill); | ||
419 | eeepc->wwan3g_rfkill = NULL; | ||
420 | } | ||
421 | } | ||
422 | |||
423 | static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) | ||
424 | { | ||
425 | int result = 0; | ||
426 | |||
427 | result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, | ||
428 | "eeepc-wlan", RFKILL_TYPE_WLAN, | ||
429 | EEEPC_WMI_DEVID_WLAN); | ||
430 | |||
431 | if (result && result != -ENODEV) | ||
432 | goto exit; | ||
433 | |||
434 | result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, | ||
435 | "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, | ||
436 | EEEPC_WMI_DEVID_BLUETOOTH); | ||
437 | |||
438 | if (result && result != -ENODEV) | ||
439 | goto exit; | ||
440 | |||
441 | result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, | ||
442 | "eeepc-wwan3g", RFKILL_TYPE_WWAN, | ||
443 | EEEPC_WMI_DEVID_WWAN3G); | ||
444 | |||
445 | if (result && result != -ENODEV) | ||
446 | goto exit; | ||
447 | |||
448 | exit: | ||
449 | if (result && result != -ENODEV) | ||
450 | eeepc_wmi_rfkill_exit(eeepc); | ||
451 | |||
452 | if (result == -ENODEV) | ||
453 | result = 0; | ||
454 | |||
455 | return result; | ||
456 | } | ||
457 | |||
458 | /* | ||
459 | * Backlight | ||
460 | */ | ||
177 | static int read_brightness(struct backlight_device *bd) | 461 | static int read_brightness(struct backlight_device *bd) |
178 | { | 462 | { |
179 | static u32 ctrl_param; | 463 | u32 retval; |
180 | acpi_status status; | 464 | acpi_status status; |
181 | 465 | ||
182 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); | 466 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval); |
183 | 467 | ||
184 | if (ACPI_FAILURE(status)) | 468 | if (ACPI_FAILURE(status)) |
185 | return -1; | 469 | return -1; |
186 | else | 470 | else |
187 | return ctrl_param & 0xFF; | 471 | return retval & 0xFF; |
188 | } | 472 | } |
189 | 473 | ||
190 | static int update_bl_status(struct backlight_device *bd) | 474 | static int update_bl_status(struct backlight_device *bd) |
191 | { | 475 | { |
192 | 476 | ||
193 | static u32 ctrl_param; | 477 | u32 ctrl_param; |
194 | acpi_status status; | 478 | acpi_status status; |
195 | 479 | ||
196 | ctrl_param = bd->props.brightness; | 480 | ctrl_param = bd->props.brightness; |
197 | 481 | ||
198 | status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param); | 482 | status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, |
483 | ctrl_param, NULL); | ||
199 | 484 | ||
200 | if (ACPI_FAILURE(status)) | 485 | if (ACPI_FAILURE(status)) |
201 | return -1; | 486 | return -1; |
@@ -234,7 +519,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) | |||
234 | memset(&props, 0, sizeof(struct backlight_properties)); | 519 | memset(&props, 0, sizeof(struct backlight_properties)); |
235 | props.max_brightness = 15; | 520 | props.max_brightness = 15; |
236 | bd = backlight_device_register(EEEPC_WMI_FILE, | 521 | bd = backlight_device_register(EEEPC_WMI_FILE, |
237 | &platform_device->dev, eeepc, | 522 | &eeepc->platform_device->dev, eeepc, |
238 | &eeepc_wmi_bl_ops, &props); | 523 | &eeepc_wmi_bl_ops, &props); |
239 | if (IS_ERR(bd)) { | 524 | if (IS_ERR(bd)) { |
240 | pr_err("Could not register backlight device\n"); | 525 | pr_err("Could not register backlight device\n"); |
@@ -321,65 +606,240 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, | |||
321 | 606 | ||
322 | static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); | 607 | static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); |
323 | 608 | ||
609 | static struct attribute *platform_attributes[] = { | ||
610 | &dev_attr_cpufv.attr, | ||
611 | NULL | ||
612 | }; | ||
613 | |||
614 | static struct attribute_group platform_attribute_group = { | ||
615 | .attrs = platform_attributes | ||
616 | }; | ||
617 | |||
324 | static void eeepc_wmi_sysfs_exit(struct platform_device *device) | 618 | static void eeepc_wmi_sysfs_exit(struct platform_device *device) |
325 | { | 619 | { |
326 | device_remove_file(&device->dev, &dev_attr_cpufv); | 620 | sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); |
327 | } | 621 | } |
328 | 622 | ||
329 | static int eeepc_wmi_sysfs_init(struct platform_device *device) | 623 | static int eeepc_wmi_sysfs_init(struct platform_device *device) |
330 | { | 624 | { |
331 | int retval = -ENOMEM; | 625 | return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); |
626 | } | ||
332 | 627 | ||
333 | retval = device_create_file(&device->dev, &dev_attr_cpufv); | 628 | /* |
334 | if (retval) | 629 | * Platform device |
335 | goto error_sysfs; | 630 | */ |
631 | static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc) | ||
632 | { | ||
633 | int err; | ||
336 | 634 | ||
635 | eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); | ||
636 | if (!eeepc->platform_device) | ||
637 | return -ENOMEM; | ||
638 | platform_set_drvdata(eeepc->platform_device, eeepc); | ||
639 | |||
640 | err = platform_device_add(eeepc->platform_device); | ||
641 | if (err) | ||
642 | goto fail_platform_device; | ||
643 | |||
644 | err = eeepc_wmi_sysfs_init(eeepc->platform_device); | ||
645 | if (err) | ||
646 | goto fail_sysfs; | ||
337 | return 0; | 647 | return 0; |
338 | 648 | ||
339 | error_sysfs: | 649 | fail_sysfs: |
340 | eeepc_wmi_sysfs_exit(platform_device); | 650 | platform_device_del(eeepc->platform_device); |
341 | return retval; | 651 | fail_platform_device: |
652 | platform_device_put(eeepc->platform_device); | ||
653 | return err; | ||
342 | } | 654 | } |
343 | 655 | ||
344 | static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) | 656 | static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) |
345 | { | 657 | { |
658 | eeepc_wmi_sysfs_exit(eeepc->platform_device); | ||
659 | platform_device_unregister(eeepc->platform_device); | ||
660 | } | ||
661 | |||
662 | /* | ||
663 | * debugfs | ||
664 | */ | ||
665 | struct eeepc_wmi_debugfs_node { | ||
346 | struct eeepc_wmi *eeepc; | 666 | struct eeepc_wmi *eeepc; |
347 | int err; | 667 | char *name; |
668 | int (*show)(struct seq_file *m, void *data); | ||
669 | }; | ||
670 | |||
671 | static int show_dsts(struct seq_file *m, void *data) | ||
672 | { | ||
673 | struct eeepc_wmi *eeepc = m->private; | ||
348 | acpi_status status; | 674 | acpi_status status; |
675 | u32 retval = -1; | ||
349 | 676 | ||
350 | eeepc = platform_get_drvdata(device); | 677 | status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval); |
678 | |||
679 | if (ACPI_FAILURE(status)) | ||
680 | return -EIO; | ||
681 | |||
682 | seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval); | ||
683 | |||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | static int show_devs(struct seq_file *m, void *data) | ||
688 | { | ||
689 | struct eeepc_wmi *eeepc = m->private; | ||
690 | acpi_status status; | ||
691 | u32 retval = -1; | ||
692 | |||
693 | status = eeepc_wmi_set_devstate(eeepc->debug.dev_id, | ||
694 | eeepc->debug.ctrl_param, &retval); | ||
695 | if (ACPI_FAILURE(status)) | ||
696 | return -EIO; | ||
697 | |||
698 | seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id, | ||
699 | eeepc->debug.ctrl_param, retval); | ||
700 | |||
701 | return 0; | ||
702 | } | ||
703 | |||
704 | static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = { | ||
705 | { NULL, "devs", show_devs }, | ||
706 | { NULL, "dsts", show_dsts }, | ||
707 | }; | ||
708 | |||
709 | static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file) | ||
710 | { | ||
711 | struct eeepc_wmi_debugfs_node *node = inode->i_private; | ||
712 | |||
713 | return single_open(file, node->show, node->eeepc); | ||
714 | } | ||
715 | |||
716 | static const struct file_operations eeepc_wmi_debugfs_io_ops = { | ||
717 | .owner = THIS_MODULE, | ||
718 | .open = eeepc_wmi_debugfs_open, | ||
719 | .read = seq_read, | ||
720 | .llseek = seq_lseek, | ||
721 | .release = single_release, | ||
722 | }; | ||
723 | |||
724 | static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc) | ||
725 | { | ||
726 | debugfs_remove_recursive(eeepc->debug.root); | ||
727 | } | ||
728 | |||
729 | static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc) | ||
730 | { | ||
731 | struct dentry *dent; | ||
732 | int i; | ||
733 | |||
734 | eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL); | ||
735 | if (!eeepc->debug.root) { | ||
736 | pr_err("failed to create debugfs directory"); | ||
737 | goto error_debugfs; | ||
738 | } | ||
739 | |||
740 | dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR, | ||
741 | eeepc->debug.root, &eeepc->debug.dev_id); | ||
742 | if (!dent) | ||
743 | goto error_debugfs; | ||
744 | |||
745 | dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR, | ||
746 | eeepc->debug.root, &eeepc->debug.ctrl_param); | ||
747 | if (!dent) | ||
748 | goto error_debugfs; | ||
749 | |||
750 | for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) { | ||
751 | struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i]; | ||
752 | |||
753 | node->eeepc = eeepc; | ||
754 | dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, | ||
755 | eeepc->debug.root, node, | ||
756 | &eeepc_wmi_debugfs_io_ops); | ||
757 | if (!dent) { | ||
758 | pr_err("failed to create debug file: %s\n", node->name); | ||
759 | goto error_debugfs; | ||
760 | } | ||
761 | } | ||
762 | |||
763 | return 0; | ||
764 | |||
765 | error_debugfs: | ||
766 | eeepc_wmi_debugfs_exit(eeepc); | ||
767 | return -ENOMEM; | ||
768 | } | ||
769 | |||
770 | /* | ||
771 | * WMI Driver | ||
772 | */ | ||
773 | static struct platform_device * __init eeepc_wmi_add(void) | ||
774 | { | ||
775 | struct eeepc_wmi *eeepc; | ||
776 | acpi_status status; | ||
777 | int err; | ||
778 | |||
779 | eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); | ||
780 | if (!eeepc) | ||
781 | return ERR_PTR(-ENOMEM); | ||
782 | |||
783 | /* | ||
784 | * Register the platform device first. It is used as a parent for the | ||
785 | * sub-devices below. | ||
786 | */ | ||
787 | err = eeepc_wmi_platform_init(eeepc); | ||
788 | if (err) | ||
789 | goto fail_platform; | ||
351 | 790 | ||
352 | err = eeepc_wmi_input_init(eeepc); | 791 | err = eeepc_wmi_input_init(eeepc); |
353 | if (err) | 792 | if (err) |
354 | goto error_input; | 793 | goto fail_input; |
794 | |||
795 | err = eeepc_wmi_led_init(eeepc); | ||
796 | if (err) | ||
797 | goto fail_leds; | ||
798 | |||
799 | err = eeepc_wmi_rfkill_init(eeepc); | ||
800 | if (err) | ||
801 | goto fail_rfkill; | ||
355 | 802 | ||
356 | if (!acpi_video_backlight_support()) { | 803 | if (!acpi_video_backlight_support()) { |
357 | err = eeepc_wmi_backlight_init(eeepc); | 804 | err = eeepc_wmi_backlight_init(eeepc); |
358 | if (err) | 805 | if (err) |
359 | goto error_backlight; | 806 | goto fail_backlight; |
360 | } else | 807 | } else |
361 | pr_info("Backlight controlled by ACPI video driver\n"); | 808 | pr_info("Backlight controlled by ACPI video driver\n"); |
362 | 809 | ||
363 | status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, | 810 | status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, |
364 | eeepc_wmi_notify, eeepc); | 811 | eeepc_wmi_notify, eeepc); |
365 | if (ACPI_FAILURE(status)) { | 812 | if (ACPI_FAILURE(status)) { |
366 | pr_err("Unable to register notify handler - %d\n", | 813 | pr_err("Unable to register notify handler - %d\n", |
367 | status); | 814 | status); |
368 | err = -ENODEV; | 815 | err = -ENODEV; |
369 | goto error_wmi; | 816 | goto fail_wmi_handler; |
370 | } | 817 | } |
371 | 818 | ||
372 | return 0; | 819 | err = eeepc_wmi_debugfs_init(eeepc); |
820 | if (err) | ||
821 | goto fail_debugfs; | ||
373 | 822 | ||
374 | error_wmi: | 823 | return eeepc->platform_device; |
824 | |||
825 | fail_debugfs: | ||
826 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | ||
827 | fail_wmi_handler: | ||
375 | eeepc_wmi_backlight_exit(eeepc); | 828 | eeepc_wmi_backlight_exit(eeepc); |
376 | error_backlight: | 829 | fail_backlight: |
830 | eeepc_wmi_rfkill_exit(eeepc); | ||
831 | fail_rfkill: | ||
832 | eeepc_wmi_led_exit(eeepc); | ||
833 | fail_leds: | ||
377 | eeepc_wmi_input_exit(eeepc); | 834 | eeepc_wmi_input_exit(eeepc); |
378 | error_input: | 835 | fail_input: |
379 | return err; | 836 | eeepc_wmi_platform_exit(eeepc); |
837 | fail_platform: | ||
838 | kfree(eeepc); | ||
839 | return ERR_PTR(err); | ||
380 | } | 840 | } |
381 | 841 | ||
382 | static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) | 842 | static int eeepc_wmi_remove(struct platform_device *device) |
383 | { | 843 | { |
384 | struct eeepc_wmi *eeepc; | 844 | struct eeepc_wmi *eeepc; |
385 | 845 | ||
@@ -387,7 +847,12 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) | |||
387 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | 847 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); |
388 | eeepc_wmi_backlight_exit(eeepc); | 848 | eeepc_wmi_backlight_exit(eeepc); |
389 | eeepc_wmi_input_exit(eeepc); | 849 | eeepc_wmi_input_exit(eeepc); |
850 | eeepc_wmi_led_exit(eeepc); | ||
851 | eeepc_wmi_rfkill_exit(eeepc); | ||
852 | eeepc_wmi_debugfs_exit(eeepc); | ||
853 | eeepc_wmi_platform_exit(eeepc); | ||
390 | 854 | ||
855 | kfree(eeepc); | ||
391 | return 0; | 856 | return 0; |
392 | } | 857 | } |
393 | 858 | ||
@@ -396,13 +861,31 @@ static struct platform_driver platform_driver = { | |||
396 | .name = EEEPC_WMI_FILE, | 861 | .name = EEEPC_WMI_FILE, |
397 | .owner = THIS_MODULE, | 862 | .owner = THIS_MODULE, |
398 | }, | 863 | }, |
399 | .probe = eeepc_wmi_platform_probe, | ||
400 | .remove = __devexit_p(eeepc_wmi_platform_remove), | ||
401 | }; | 864 | }; |
402 | 865 | ||
866 | static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level, | ||
867 | void *context, void **retval) | ||
868 | { | ||
869 | pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID); | ||
870 | *(bool *)context = true; | ||
871 | return AE_CTRL_TERMINATE; | ||
872 | } | ||
873 | |||
874 | static int __init eeepc_wmi_check_atkd(void) | ||
875 | { | ||
876 | acpi_status status; | ||
877 | bool found = false; | ||
878 | |||
879 | status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device, | ||
880 | &found, NULL); | ||
881 | |||
882 | if (ACPI_FAILURE(status) || !found) | ||
883 | return 0; | ||
884 | return -1; | ||
885 | } | ||
886 | |||
403 | static int __init eeepc_wmi_init(void) | 887 | static int __init eeepc_wmi_init(void) |
404 | { | 888 | { |
405 | struct eeepc_wmi *eeepc; | ||
406 | int err; | 889 | int err; |
407 | 890 | ||
408 | if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || | 891 | if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || |
@@ -411,58 +894,40 @@ static int __init eeepc_wmi_init(void) | |||
411 | return -ENODEV; | 894 | return -ENODEV; |
412 | } | 895 | } |
413 | 896 | ||
414 | eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); | 897 | if (eeepc_wmi_check_atkd()) { |
415 | if (!eeepc) | 898 | pr_warning("WMI device present, but legacy ATKD device is also " |
416 | return -ENOMEM; | 899 | "present and enabled."); |
417 | 900 | pr_warning("You probably booted with acpi_osi=\"Linux\" or " | |
418 | platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); | 901 | "acpi_osi=\"!Windows 2009\""); |
419 | if (!platform_device) { | 902 | pr_warning("Can't load eeepc-wmi, use default acpi_osi " |
420 | pr_warning("Unable to allocate platform device\n"); | 903 | "(preferred) or eeepc-laptop"); |
421 | err = -ENOMEM; | 904 | return -ENODEV; |
422 | goto fail_platform; | ||
423 | } | 905 | } |
424 | 906 | ||
425 | err = platform_device_add(platform_device); | 907 | platform_device = eeepc_wmi_add(); |
426 | if (err) { | 908 | if (IS_ERR(platform_device)) { |
427 | pr_warning("Unable to add platform device\n"); | 909 | err = PTR_ERR(platform_device); |
428 | goto put_dev; | 910 | goto fail_eeepc_wmi; |
429 | } | 911 | } |
430 | 912 | ||
431 | platform_set_drvdata(platform_device, eeepc); | ||
432 | |||
433 | err = platform_driver_register(&platform_driver); | 913 | err = platform_driver_register(&platform_driver); |
434 | if (err) { | 914 | if (err) { |
435 | pr_warning("Unable to register platform driver\n"); | 915 | pr_warning("Unable to register platform driver\n"); |
436 | goto del_dev; | 916 | goto fail_platform_driver; |
437 | } | 917 | } |
438 | 918 | ||
439 | err = eeepc_wmi_sysfs_init(platform_device); | ||
440 | if (err) | ||
441 | goto del_sysfs; | ||
442 | |||
443 | return 0; | 919 | return 0; |
444 | 920 | ||
445 | del_sysfs: | 921 | fail_platform_driver: |
446 | eeepc_wmi_sysfs_exit(platform_device); | 922 | eeepc_wmi_remove(platform_device); |
447 | del_dev: | 923 | fail_eeepc_wmi: |
448 | platform_device_del(platform_device); | ||
449 | put_dev: | ||
450 | platform_device_put(platform_device); | ||
451 | fail_platform: | ||
452 | kfree(eeepc); | ||
453 | |||
454 | return err; | 924 | return err; |
455 | } | 925 | } |
456 | 926 | ||
457 | static void __exit eeepc_wmi_exit(void) | 927 | static void __exit eeepc_wmi_exit(void) |
458 | { | 928 | { |
459 | struct eeepc_wmi *eeepc; | 929 | eeepc_wmi_remove(platform_device); |
460 | |||
461 | eeepc_wmi_sysfs_exit(platform_device); | ||
462 | eeepc = platform_get_drvdata(platform_device); | ||
463 | platform_driver_unregister(&platform_driver); | 930 | platform_driver_unregister(&platform_driver); |
464 | platform_device_unregister(platform_device); | ||
465 | kfree(eeepc); | ||
466 | } | 931 | } |
467 | 932 | ||
468 | module_init(eeepc_wmi_init); | 933 | module_init(eeepc_wmi_init); |
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index f44cd2620ff9..ad88b2ec34a1 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c | |||
@@ -1240,7 +1240,7 @@ MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); | |||
1240 | MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); | 1240 | MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); |
1241 | MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); | 1241 | MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); |
1242 | 1242 | ||
1243 | static struct pnp_device_id pnp_ids[] = { | 1243 | static struct pnp_device_id pnp_ids[] __used = { |
1244 | {.id = "FUJ02bf"}, | 1244 | {.id = "FUJ02bf"}, |
1245 | {.id = "FUJ02B1"}, | 1245 | {.id = "FUJ02B1"}, |
1246 | {.id = "FUJ02E3"}, | 1246 | {.id = "FUJ02E3"}, |
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 5ff12205aa6b..114d95247cdf 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras | 2 | * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras |
3 | * | 3 | * |
4 | * Copyright © 2010 Intel Corporation | 4 | * Copyright © 2010 Intel Corporation |
5 | * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> | 5 | * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> |
@@ -27,31 +27,19 @@ | |||
27 | #include <acpi/acpi_bus.h> | 27 | #include <acpi/acpi_bus.h> |
28 | #include <acpi/acpi_drivers.h> | 28 | #include <acpi/acpi_drivers.h> |
29 | #include <linux/rfkill.h> | 29 | #include <linux/rfkill.h> |
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/input.h> | ||
32 | #include <linux/input/sparse-keymap.h> | ||
30 | 33 | ||
31 | #define IDEAPAD_DEV_CAMERA 0 | 34 | #define IDEAPAD_RFKILL_DEV_NUM (3) |
32 | #define IDEAPAD_DEV_WLAN 1 | ||
33 | #define IDEAPAD_DEV_BLUETOOTH 2 | ||
34 | #define IDEAPAD_DEV_3G 3 | ||
35 | #define IDEAPAD_DEV_KILLSW 4 | ||
36 | 35 | ||
37 | struct ideapad_private { | 36 | struct ideapad_private { |
38 | acpi_handle handle; | 37 | struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; |
39 | struct rfkill *rfk[5]; | 38 | struct platform_device *platform_device; |
40 | } *ideapad_priv; | 39 | struct input_dev *inputdev; |
41 | |||
42 | static struct { | ||
43 | char *name; | ||
44 | int cfgbit; | ||
45 | int opcode; | ||
46 | int type; | ||
47 | } ideapad_rfk_data[] = { | ||
48 | { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, | ||
49 | { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, | ||
50 | { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, | ||
51 | { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, | ||
52 | { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } | ||
53 | }; | 40 | }; |
54 | 41 | ||
42 | static acpi_handle ideapad_handle; | ||
55 | static bool no_bt_rfkill; | 43 | static bool no_bt_rfkill; |
56 | module_param(no_bt_rfkill, bool, 0444); | 44 | module_param(no_bt_rfkill, bool, 0444); |
57 | MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); | 45 | MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); |
@@ -163,17 +151,17 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) | |||
163 | pr_err("timeout in write_ec_cmd\n"); | 151 | pr_err("timeout in write_ec_cmd\n"); |
164 | return -1; | 152 | return -1; |
165 | } | 153 | } |
166 | /* the above is ACPI helpers */ | ||
167 | 154 | ||
155 | /* | ||
156 | * camera power | ||
157 | */ | ||
168 | static ssize_t show_ideapad_cam(struct device *dev, | 158 | static ssize_t show_ideapad_cam(struct device *dev, |
169 | struct device_attribute *attr, | 159 | struct device_attribute *attr, |
170 | char *buf) | 160 | char *buf) |
171 | { | 161 | { |
172 | struct ideapad_private *priv = dev_get_drvdata(dev); | ||
173 | acpi_handle handle = priv->handle; | ||
174 | unsigned long result; | 162 | unsigned long result; |
175 | 163 | ||
176 | if (read_ec_data(handle, 0x1D, &result)) | 164 | if (read_ec_data(ideapad_handle, 0x1D, &result)) |
177 | return sprintf(buf, "-1\n"); | 165 | return sprintf(buf, "-1\n"); |
178 | return sprintf(buf, "%lu\n", result); | 166 | return sprintf(buf, "%lu\n", result); |
179 | } | 167 | } |
@@ -182,15 +170,13 @@ static ssize_t store_ideapad_cam(struct device *dev, | |||
182 | struct device_attribute *attr, | 170 | struct device_attribute *attr, |
183 | const char *buf, size_t count) | 171 | const char *buf, size_t count) |
184 | { | 172 | { |
185 | struct ideapad_private *priv = dev_get_drvdata(dev); | ||
186 | acpi_handle handle = priv->handle; | ||
187 | int ret, state; | 173 | int ret, state; |
188 | 174 | ||
189 | if (!count) | 175 | if (!count) |
190 | return 0; | 176 | return 0; |
191 | if (sscanf(buf, "%i", &state) != 1) | 177 | if (sscanf(buf, "%i", &state) != 1) |
192 | return -EINVAL; | 178 | return -EINVAL; |
193 | ret = write_ec_cmd(handle, 0x1E, state); | 179 | ret = write_ec_cmd(ideapad_handle, 0x1E, state); |
194 | if (ret < 0) | 180 | if (ret < 0) |
195 | return ret; | 181 | return ret; |
196 | return count; | 182 | return count; |
@@ -198,16 +184,27 @@ static ssize_t store_ideapad_cam(struct device *dev, | |||
198 | 184 | ||
199 | static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); | 185 | static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); |
200 | 186 | ||
187 | /* | ||
188 | * Rfkill | ||
189 | */ | ||
190 | struct ideapad_rfk_data { | ||
191 | char *name; | ||
192 | int cfgbit; | ||
193 | int opcode; | ||
194 | int type; | ||
195 | }; | ||
196 | |||
197 | const struct ideapad_rfk_data ideapad_rfk_data[] = { | ||
198 | { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, | ||
199 | { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, | ||
200 | { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, | ||
201 | }; | ||
202 | |||
201 | static int ideapad_rfk_set(void *data, bool blocked) | 203 | static int ideapad_rfk_set(void *data, bool blocked) |
202 | { | 204 | { |
203 | int device = (unsigned long)data; | 205 | unsigned long opcode = (unsigned long)data; |
204 | 206 | ||
205 | if (device == IDEAPAD_DEV_KILLSW) | 207 | return write_ec_cmd(ideapad_handle, opcode, !blocked); |
206 | return -EINVAL; | ||
207 | |||
208 | return write_ec_cmd(ideapad_priv->handle, | ||
209 | ideapad_rfk_data[device].opcode, | ||
210 | !blocked); | ||
211 | } | 208 | } |
212 | 209 | ||
213 | static struct rfkill_ops ideapad_rfk_ops = { | 210 | static struct rfkill_ops ideapad_rfk_ops = { |
@@ -217,20 +214,20 @@ static struct rfkill_ops ideapad_rfk_ops = { | |||
217 | static void ideapad_sync_rfk_state(struct acpi_device *adevice) | 214 | static void ideapad_sync_rfk_state(struct acpi_device *adevice) |
218 | { | 215 | { |
219 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | 216 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); |
220 | acpi_handle handle = priv->handle; | ||
221 | unsigned long hw_blocked; | 217 | unsigned long hw_blocked; |
222 | int i; | 218 | int i; |
223 | 219 | ||
224 | if (read_ec_data(handle, 0x23, &hw_blocked)) | 220 | if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) |
225 | return; | 221 | return; |
226 | hw_blocked = !hw_blocked; | 222 | hw_blocked = !hw_blocked; |
227 | 223 | ||
228 | for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) | 224 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) |
229 | if (priv->rfk[i]) | 225 | if (priv->rfk[i]) |
230 | rfkill_set_hw_state(priv->rfk[i], hw_blocked); | 226 | rfkill_set_hw_state(priv->rfk[i], hw_blocked); |
231 | } | 227 | } |
232 | 228 | ||
233 | static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | 229 | static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, |
230 | int dev) | ||
234 | { | 231 | { |
235 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | 232 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); |
236 | int ret; | 233 | int ret; |
@@ -239,7 +236,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | |||
239 | if (no_bt_rfkill && | 236 | if (no_bt_rfkill && |
240 | (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { | 237 | (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { |
241 | /* Force to enable bluetooth when no_bt_rfkill=1 */ | 238 | /* Force to enable bluetooth when no_bt_rfkill=1 */ |
242 | write_ec_cmd(ideapad_priv->handle, | 239 | write_ec_cmd(ideapad_handle, |
243 | ideapad_rfk_data[dev].opcode, 1); | 240 | ideapad_rfk_data[dev].opcode, 1); |
244 | return 0; | 241 | return 0; |
245 | } | 242 | } |
@@ -250,7 +247,7 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | |||
250 | if (!priv->rfk[dev]) | 247 | if (!priv->rfk[dev]) |
251 | return -ENOMEM; | 248 | return -ENOMEM; |
252 | 249 | ||
253 | if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, | 250 | if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1, |
254 | &sw_blocked)) { | 251 | &sw_blocked)) { |
255 | rfkill_init_sw_state(priv->rfk[dev], 0); | 252 | rfkill_init_sw_state(priv->rfk[dev], 0); |
256 | } else { | 253 | } else { |
@@ -266,7 +263,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | |||
266 | return 0; | 263 | return 0; |
267 | } | 264 | } |
268 | 265 | ||
269 | static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) | 266 | static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice, |
267 | int dev) | ||
270 | { | 268 | { |
271 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | 269 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); |
272 | 270 | ||
@@ -277,73 +275,177 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) | |||
277 | rfkill_destroy(priv->rfk[dev]); | 275 | rfkill_destroy(priv->rfk[dev]); |
278 | } | 276 | } |
279 | 277 | ||
278 | /* | ||
279 | * Platform device | ||
280 | */ | ||
281 | static struct attribute *ideapad_attributes[] = { | ||
282 | &dev_attr_camera_power.attr, | ||
283 | NULL | ||
284 | }; | ||
285 | |||
286 | static struct attribute_group ideapad_attribute_group = { | ||
287 | .attrs = ideapad_attributes | ||
288 | }; | ||
289 | |||
290 | static int __devinit ideapad_platform_init(struct ideapad_private *priv) | ||
291 | { | ||
292 | int result; | ||
293 | |||
294 | priv->platform_device = platform_device_alloc("ideapad", -1); | ||
295 | if (!priv->platform_device) | ||
296 | return -ENOMEM; | ||
297 | platform_set_drvdata(priv->platform_device, priv); | ||
298 | |||
299 | result = platform_device_add(priv->platform_device); | ||
300 | if (result) | ||
301 | goto fail_platform_device; | ||
302 | |||
303 | result = sysfs_create_group(&priv->platform_device->dev.kobj, | ||
304 | &ideapad_attribute_group); | ||
305 | if (result) | ||
306 | goto fail_sysfs; | ||
307 | return 0; | ||
308 | |||
309 | fail_sysfs: | ||
310 | platform_device_del(priv->platform_device); | ||
311 | fail_platform_device: | ||
312 | platform_device_put(priv->platform_device); | ||
313 | return result; | ||
314 | } | ||
315 | |||
316 | static void ideapad_platform_exit(struct ideapad_private *priv) | ||
317 | { | ||
318 | sysfs_remove_group(&priv->platform_device->dev.kobj, | ||
319 | &ideapad_attribute_group); | ||
320 | platform_device_unregister(priv->platform_device); | ||
321 | } | ||
322 | |||
323 | /* | ||
324 | * input device | ||
325 | */ | ||
326 | static const struct key_entry ideapad_keymap[] = { | ||
327 | { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, | ||
328 | { KE_KEY, 0x0D, { KEY_WLAN } }, | ||
329 | { KE_END, 0 }, | ||
330 | }; | ||
331 | |||
332 | static int __devinit ideapad_input_init(struct ideapad_private *priv) | ||
333 | { | ||
334 | struct input_dev *inputdev; | ||
335 | int error; | ||
336 | |||
337 | inputdev = input_allocate_device(); | ||
338 | if (!inputdev) { | ||
339 | pr_info("Unable to allocate input device\n"); | ||
340 | return -ENOMEM; | ||
341 | } | ||
342 | |||
343 | inputdev->name = "Ideapad extra buttons"; | ||
344 | inputdev->phys = "ideapad/input0"; | ||
345 | inputdev->id.bustype = BUS_HOST; | ||
346 | inputdev->dev.parent = &priv->platform_device->dev; | ||
347 | |||
348 | error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); | ||
349 | if (error) { | ||
350 | pr_err("Unable to setup input device keymap\n"); | ||
351 | goto err_free_dev; | ||
352 | } | ||
353 | |||
354 | error = input_register_device(inputdev); | ||
355 | if (error) { | ||
356 | pr_err("Unable to register input device\n"); | ||
357 | goto err_free_keymap; | ||
358 | } | ||
359 | |||
360 | priv->inputdev = inputdev; | ||
361 | return 0; | ||
362 | |||
363 | err_free_keymap: | ||
364 | sparse_keymap_free(inputdev); | ||
365 | err_free_dev: | ||
366 | input_free_device(inputdev); | ||
367 | return error; | ||
368 | } | ||
369 | |||
370 | static void __devexit ideapad_input_exit(struct ideapad_private *priv) | ||
371 | { | ||
372 | sparse_keymap_free(priv->inputdev); | ||
373 | input_unregister_device(priv->inputdev); | ||
374 | priv->inputdev = NULL; | ||
375 | } | ||
376 | |||
377 | static void ideapad_input_report(struct ideapad_private *priv, | ||
378 | unsigned long scancode) | ||
379 | { | ||
380 | sparse_keymap_report_event(priv->inputdev, scancode, 1, true); | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * module init/exit | ||
385 | */ | ||
280 | static const struct acpi_device_id ideapad_device_ids[] = { | 386 | static const struct acpi_device_id ideapad_device_ids[] = { |
281 | { "VPC2004", 0}, | 387 | { "VPC2004", 0}, |
282 | { "", 0}, | 388 | { "", 0}, |
283 | }; | 389 | }; |
284 | MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); | 390 | MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); |
285 | 391 | ||
286 | static int ideapad_acpi_add(struct acpi_device *adevice) | 392 | static int __devinit ideapad_acpi_add(struct acpi_device *adevice) |
287 | { | 393 | { |
288 | int i, cfg; | 394 | int ret, i, cfg; |
289 | int devs_present[5]; | ||
290 | struct ideapad_private *priv; | 395 | struct ideapad_private *priv; |
291 | 396 | ||
292 | if (read_method_int(adevice->handle, "_CFG", &cfg)) | 397 | if (read_method_int(adevice->handle, "_CFG", &cfg)) |
293 | return -ENODEV; | 398 | return -ENODEV; |
294 | 399 | ||
295 | for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { | ||
296 | if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) | ||
297 | devs_present[i] = 1; | ||
298 | else | ||
299 | devs_present[i] = 0; | ||
300 | } | ||
301 | |||
302 | /* The hardware switch is always present */ | ||
303 | devs_present[IDEAPAD_DEV_KILLSW] = 1; | ||
304 | |||
305 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | 400 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
306 | if (!priv) | 401 | if (!priv) |
307 | return -ENOMEM; | 402 | return -ENOMEM; |
403 | dev_set_drvdata(&adevice->dev, priv); | ||
404 | ideapad_handle = adevice->handle; | ||
308 | 405 | ||
309 | if (devs_present[IDEAPAD_DEV_CAMERA]) { | 406 | ret = ideapad_platform_init(priv); |
310 | int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); | 407 | if (ret) |
311 | if (ret) { | 408 | goto platform_failed; |
312 | kfree(priv); | ||
313 | return ret; | ||
314 | } | ||
315 | } | ||
316 | 409 | ||
317 | priv->handle = adevice->handle; | 410 | ret = ideapad_input_init(priv); |
318 | dev_set_drvdata(&adevice->dev, priv); | 411 | if (ret) |
319 | ideapad_priv = priv; | 412 | goto input_failed; |
320 | for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { | ||
321 | if (!devs_present[i]) | ||
322 | continue; | ||
323 | 413 | ||
324 | ideapad_register_rfkill(adevice, i); | 414 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { |
415 | if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) | ||
416 | ideapad_register_rfkill(adevice, i); | ||
417 | else | ||
418 | priv->rfk[i] = NULL; | ||
325 | } | 419 | } |
326 | ideapad_sync_rfk_state(adevice); | 420 | ideapad_sync_rfk_state(adevice); |
421 | |||
327 | return 0; | 422 | return 0; |
423 | |||
424 | input_failed: | ||
425 | ideapad_platform_exit(priv); | ||
426 | platform_failed: | ||
427 | kfree(priv); | ||
428 | return ret; | ||
328 | } | 429 | } |
329 | 430 | ||
330 | static int ideapad_acpi_remove(struct acpi_device *adevice, int type) | 431 | static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) |
331 | { | 432 | { |
332 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | 433 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); |
333 | int i; | 434 | int i; |
334 | 435 | ||
335 | device_remove_file(&adevice->dev, &dev_attr_camera_power); | 436 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) |
336 | |||
337 | for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) | ||
338 | ideapad_unregister_rfkill(adevice, i); | 437 | ideapad_unregister_rfkill(adevice, i); |
339 | 438 | ideapad_input_exit(priv); | |
439 | ideapad_platform_exit(priv); | ||
340 | dev_set_drvdata(&adevice->dev, NULL); | 440 | dev_set_drvdata(&adevice->dev, NULL); |
341 | kfree(priv); | 441 | kfree(priv); |
442 | |||
342 | return 0; | 443 | return 0; |
343 | } | 444 | } |
344 | 445 | ||
345 | static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | 446 | static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) |
346 | { | 447 | { |
448 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
347 | acpi_handle handle = adevice->handle; | 449 | acpi_handle handle = adevice->handle; |
348 | unsigned long vpc1, vpc2, vpc_bit; | 450 | unsigned long vpc1, vpc2, vpc_bit; |
349 | 451 | ||
@@ -357,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | |||
357 | if (test_bit(vpc_bit, &vpc1)) { | 459 | if (test_bit(vpc_bit, &vpc1)) { |
358 | if (vpc_bit == 9) | 460 | if (vpc_bit == 9) |
359 | ideapad_sync_rfk_state(adevice); | 461 | ideapad_sync_rfk_state(adevice); |
462 | else | ||
463 | ideapad_input_report(priv, vpc_bit); | ||
360 | } | 464 | } |
361 | } | 465 | } |
362 | } | 466 | } |
@@ -371,19 +475,14 @@ static struct acpi_driver ideapad_acpi_driver = { | |||
371 | .owner = THIS_MODULE, | 475 | .owner = THIS_MODULE, |
372 | }; | 476 | }; |
373 | 477 | ||
374 | |||
375 | static int __init ideapad_acpi_module_init(void) | 478 | static int __init ideapad_acpi_module_init(void) |
376 | { | 479 | { |
377 | acpi_bus_register_driver(&ideapad_acpi_driver); | 480 | return acpi_bus_register_driver(&ideapad_acpi_driver); |
378 | |||
379 | return 0; | ||
380 | } | 481 | } |
381 | 482 | ||
382 | |||
383 | static void __exit ideapad_acpi_module_exit(void) | 483 | static void __exit ideapad_acpi_module_exit(void) |
384 | { | 484 | { |
385 | acpi_bus_unregister_driver(&ideapad_acpi_driver); | 485 | acpi_bus_unregister_driver(&ideapad_acpi_driver); |
386 | |||
387 | } | 486 | } |
388 | 487 | ||
389 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 488 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index f0b3ad13c273..1294a39373ba 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c | |||
@@ -1474,7 +1474,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips) | |||
1474 | } | 1474 | } |
1475 | 1475 | ||
1476 | void | 1476 | void |
1477 | ips_link_to_i915_driver() | 1477 | ips_link_to_i915_driver(void) |
1478 | { | 1478 | { |
1479 | /* We can't cleanly get at the various ips_driver structs from | 1479 | /* We can't cleanly get at the various ips_driver structs from |
1480 | * this caller (the i915 driver), so just set a flag saying | 1480 | * this caller (the i915 driver), so just set a flag saying |
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index e61db9dfebef..930e62762365 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c | |||
@@ -244,7 +244,11 @@ static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) | |||
244 | generic_handle_irq(pg->irq_base + gpio); | 244 | generic_handle_irq(pg->irq_base + gpio); |
245 | } | 245 | } |
246 | } | 246 | } |
247 | desc->chip->eoi(irq); | 247 | |
248 | if (desc->chip->irq_eoi) | ||
249 | desc->chip->irq_eoi(irq_get_irq_data(irq)); | ||
250 | else | ||
251 | dev_warn(pg->chip.dev, "missing EOI handler for irq %d\n", irq); | ||
248 | } | 252 | } |
249 | 253 | ||
250 | static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) | 254 | static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) |
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index ca35b0ce944a..1752ef006d26 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c | |||
@@ -497,7 +497,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) | |||
497 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); | 497 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); |
498 | 498 | ||
499 | mutex_unlock(&ipclock); | 499 | mutex_unlock(&ipclock); |
500 | return -1; | 500 | return -EIO; |
501 | } | 501 | } |
502 | mutex_unlock(&ipclock); | 502 | mutex_unlock(&ipclock); |
503 | return 0; | 503 | return 0; |
@@ -642,7 +642,7 @@ update_end: | |||
642 | 642 | ||
643 | if (status == IPC_FW_UPDATE_SUCCESS) | 643 | if (status == IPC_FW_UPDATE_SUCCESS) |
644 | return 0; | 644 | return 0; |
645 | return -1; | 645 | return -EIO; |
646 | } | 646 | } |
647 | EXPORT_SYMBOL(intel_scu_ipc_fw_update); | 647 | EXPORT_SYMBOL(intel_scu_ipc_fw_update); |
648 | 648 | ||
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c new file mode 100644 index 000000000000..ba3231d0819e --- /dev/null +++ b/drivers/platform/x86/intel_scu_ipcutil.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism | ||
3 | * | ||
4 | * (C) Copyright 2008-2010 Intel Corporation | ||
5 | * Author: Sreedhara DS (sreedhara.ds@intel.com) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; version 2 | ||
10 | * of the License. | ||
11 | * | ||
12 | * This driver provides ioctl interfaces to call intel scu ipc driver api | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/fcntl.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/uaccess.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <asm/intel_scu_ipc.h> | ||
26 | |||
27 | static u32 major; | ||
28 | |||
29 | #define MAX_FW_SIZE 264192 | ||
30 | |||
31 | /* ioctl commnds */ | ||
32 | #define INTE_SCU_IPC_REGISTER_READ 0 | ||
33 | #define INTE_SCU_IPC_REGISTER_WRITE 1 | ||
34 | #define INTE_SCU_IPC_REGISTER_UPDATE 2 | ||
35 | #define INTE_SCU_IPC_FW_UPDATE 0xA2 | ||
36 | |||
37 | struct scu_ipc_data { | ||
38 | u32 count; /* No. of registers */ | ||
39 | u16 addr[5]; /* Register addresses */ | ||
40 | u8 data[5]; /* Register data */ | ||
41 | u8 mask; /* Valid for read-modify-write */ | ||
42 | }; | ||
43 | |||
44 | /** | ||
45 | * scu_reg_access - implement register access ioctls | ||
46 | * @cmd: command we are doing (read/write/update) | ||
47 | * @data: kernel copy of ioctl data | ||
48 | * | ||
49 | * Allow the user to perform register accesses on the SCU via the | ||
50 | * kernel interface | ||
51 | */ | ||
52 | |||
53 | static int scu_reg_access(u32 cmd, struct scu_ipc_data *data) | ||
54 | { | ||
55 | int count = data->count; | ||
56 | |||
57 | if (count == 0 || count == 3 || count > 4) | ||
58 | return -EINVAL; | ||
59 | |||
60 | switch (cmd) { | ||
61 | case INTE_SCU_IPC_REGISTER_READ: | ||
62 | return intel_scu_ipc_readv(data->addr, data->data, count); | ||
63 | case INTE_SCU_IPC_REGISTER_WRITE: | ||
64 | return intel_scu_ipc_writev(data->addr, data->data, count); | ||
65 | case INTE_SCU_IPC_REGISTER_UPDATE: | ||
66 | return intel_scu_ipc_update_register(data->addr[0], | ||
67 | data->data[0], data->mask); | ||
68 | default: | ||
69 | return -ENOTTY; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * scu_ipc_ioctl - control ioctls for the SCU | ||
75 | * @fp: file handle of the SCU device | ||
76 | * @cmd: ioctl coce | ||
77 | * @arg: pointer to user passed structure | ||
78 | * | ||
79 | * Support the I/O and firmware flashing interfaces of the SCU | ||
80 | */ | ||
81 | static long scu_ipc_ioctl(struct file *fp, unsigned int cmd, | ||
82 | unsigned long arg) | ||
83 | { | ||
84 | int ret; | ||
85 | struct scu_ipc_data data; | ||
86 | void __user *argp = (void __user *)arg; | ||
87 | |||
88 | if (!capable(CAP_SYS_RAWIO)) | ||
89 | return -EPERM; | ||
90 | |||
91 | if (cmd == INTE_SCU_IPC_FW_UPDATE) { | ||
92 | u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL); | ||
93 | if (fwbuf == NULL) | ||
94 | return -ENOMEM; | ||
95 | if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) { | ||
96 | kfree(fwbuf); | ||
97 | return -EFAULT; | ||
98 | } | ||
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 | } | ||
113 | |||
114 | static const struct file_operations scu_ipc_fops = { | ||
115 | .unlocked_ioctl = scu_ipc_ioctl, | ||
116 | }; | ||
117 | |||
118 | static int __init ipc_module_init(void) | ||
119 | { | ||
120 | return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); | ||
121 | } | ||
122 | |||
123 | static void __exit ipc_module_exit(void) | ||
124 | { | ||
125 | unregister_chrdev(major, "intel_mid_scu"); | ||
126 | } | ||
127 | |||
128 | module_init(ipc_module_init); | ||
129 | module_exit(ipc_module_exit); | ||
130 | |||
131 | MODULE_LICENSE("GPL V2"); | ||
132 | MODULE_DESCRIPTION("Utility driver for intel scu ipc"); | ||
133 | MODULE_AUTHOR("Sreedhara <sreedhara.ds@intel.com>"); | ||
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f200677851b8..b4a95bb2f232 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -235,6 +235,7 @@ static int sony_laptop_input_index[] = { | |||
235 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ | 235 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ |
236 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ | 236 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ |
237 | 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ | 237 | 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ |
238 | 59, /* 72 SONYPI_EVENT_VENDOR_PRESSED */ | ||
238 | }; | 239 | }; |
239 | 240 | ||
240 | static int sony_laptop_input_keycode_map[] = { | 241 | static int sony_laptop_input_keycode_map[] = { |
@@ -297,6 +298,7 @@ static int sony_laptop_input_keycode_map[] = { | |||
297 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ | 298 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ |
298 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ | 299 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ |
299 | KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ | 300 | KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ |
301 | KEY_VENDOR, /* 59 SONYPI_EVENT_VENDOR_PRESSED */ | ||
300 | }; | 302 | }; |
301 | 303 | ||
302 | /* release buttons after a short delay if pressed */ | 304 | /* release buttons after a short delay if pressed */ |
@@ -894,10 +896,18 @@ static struct sony_nc_event sony_100_events[] = { | |||
894 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, | 896 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, |
895 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, | 897 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, |
896 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, | 898 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, |
899 | { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, | ||
900 | { 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
897 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, | 901 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, |
898 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 902 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
899 | { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, | 903 | { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, |
900 | { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 904 | { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
905 | { 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED }, | ||
906 | { 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
907 | { 0xa5, SONYPI_EVENT_VENDOR_PRESSED }, | ||
908 | { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
909 | { 0xa6, SONYPI_EVENT_HELP_PRESSED }, | ||
910 | { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
901 | { 0, 0 }, | 911 | { 0, 0 }, |
902 | }; | 912 | }; |
903 | 913 | ||
@@ -1131,7 +1141,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, | |||
1131 | return err; | 1141 | return err; |
1132 | } | 1142 | } |
1133 | 1143 | ||
1134 | static void sony_nc_rfkill_update() | 1144 | static void sony_nc_rfkill_update(void) |
1135 | { | 1145 | { |
1136 | enum sony_nc_rfkill i; | 1146 | enum sony_nc_rfkill i; |
1137 | int result; | 1147 | int result; |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index e8c21994b36d..a974ca383cb9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -589,6 +589,7 @@ static int acpi_evalf(acpi_handle handle, | |||
589 | default: | 589 | default: |
590 | printk(TPACPI_ERR "acpi_evalf() called " | 590 | printk(TPACPI_ERR "acpi_evalf() called " |
591 | "with invalid format character '%c'\n", c); | 591 | "with invalid format character '%c'\n", c); |
592 | va_end(ap); | ||
592 | return 0; | 593 | return 0; |
593 | } | 594 | } |
594 | } | 595 | } |
@@ -6345,7 +6346,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6345 | "as change notification\n"); | 6346 | "as change notification\n"); |
6346 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | 6347 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask |
6347 | | TP_ACPI_HKEY_BRGHTUP_MASK | 6348 | | TP_ACPI_HKEY_BRGHTUP_MASK |
6348 | | TP_ACPI_HKEY_BRGHTDWN_MASK);; | 6349 | | TP_ACPI_HKEY_BRGHTDWN_MASK); |
6349 | return 0; | 6350 | return 0; |
6350 | } | 6351 | } |
6351 | 6352 | ||
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index aecd9a9b549f..05cc79672a8b 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid, | |||
549 | wmi_notify_handler handler, void *data) | 549 | wmi_notify_handler handler, void *data) |
550 | { | 550 | { |
551 | struct wmi_block *block; | 551 | struct wmi_block *block; |
552 | acpi_status status; | 552 | acpi_status status = AE_NOT_EXIST; |
553 | char tmp[16], guid_input[16]; | ||
554 | struct list_head *p; | ||
553 | 555 | ||
554 | if (!guid || !handler) | 556 | if (!guid || !handler) |
555 | return AE_BAD_PARAMETER; | 557 | return AE_BAD_PARAMETER; |
556 | 558 | ||
557 | if (!find_guid(guid, &block)) | 559 | wmi_parse_guid(guid, tmp); |
558 | return AE_NOT_EXIST; | 560 | wmi_swap_bytes(tmp, guid_input); |
559 | 561 | ||
560 | if (block->handler && block->handler != wmi_notify_debug) | 562 | list_for_each(p, &wmi_block_list) { |
561 | return AE_ALREADY_ACQUIRED; | 563 | acpi_status wmi_status; |
564 | block = list_entry(p, struct wmi_block, list); | ||
562 | 565 | ||
563 | block->handler = handler; | 566 | if (memcmp(block->gblock.guid, guid_input, 16) == 0) { |
564 | block->handler_data = data; | 567 | if (block->handler && |
568 | block->handler != wmi_notify_debug) | ||
569 | return AE_ALREADY_ACQUIRED; | ||
565 | 570 | ||
566 | status = wmi_method_enable(block, 1); | 571 | block->handler = handler; |
572 | block->handler_data = data; | ||
573 | |||
574 | wmi_status = wmi_method_enable(block, 1); | ||
575 | if ((wmi_status != AE_OK) || | ||
576 | ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) | ||
577 | status = wmi_status; | ||
578 | } | ||
579 | } | ||
567 | 580 | ||
568 | return status; | 581 | return status; |
569 | } | 582 | } |
@@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); | |||
577 | acpi_status wmi_remove_notify_handler(const char *guid) | 590 | acpi_status wmi_remove_notify_handler(const char *guid) |
578 | { | 591 | { |
579 | struct wmi_block *block; | 592 | struct wmi_block *block; |
580 | acpi_status status = AE_OK; | 593 | acpi_status status = AE_NOT_EXIST; |
594 | char tmp[16], guid_input[16]; | ||
595 | struct list_head *p; | ||
581 | 596 | ||
582 | if (!guid) | 597 | if (!guid) |
583 | return AE_BAD_PARAMETER; | 598 | return AE_BAD_PARAMETER; |
584 | 599 | ||
585 | if (!find_guid(guid, &block)) | 600 | wmi_parse_guid(guid, tmp); |
586 | return AE_NOT_EXIST; | 601 | wmi_swap_bytes(tmp, guid_input); |
587 | 602 | ||
588 | if (!block->handler || block->handler == wmi_notify_debug) | 603 | list_for_each(p, &wmi_block_list) { |
589 | return AE_NULL_ENTRY; | 604 | acpi_status wmi_status; |
605 | block = list_entry(p, struct wmi_block, list); | ||
590 | 606 | ||
591 | if (debug_event) { | 607 | if (memcmp(block->gblock.guid, guid_input, 16) == 0) { |
592 | block->handler = wmi_notify_debug; | 608 | if (!block->handler || |
593 | } else { | 609 | block->handler == wmi_notify_debug) |
594 | status = wmi_method_enable(block, 0); | 610 | return AE_NULL_ENTRY; |
595 | block->handler = NULL; | 611 | |
596 | block->handler_data = NULL; | 612 | if (debug_event) { |
613 | block->handler = wmi_notify_debug; | ||
614 | status = AE_OK; | ||
615 | } else { | ||
616 | wmi_status = wmi_method_enable(block, 0); | ||
617 | block->handler = NULL; | ||
618 | block->handler_data = NULL; | ||
619 | if ((wmi_status != AE_OK) || | ||
620 | ((wmi_status == AE_OK) && | ||
621 | (status == AE_NOT_EXIST))) | ||
622 | status = wmi_status; | ||
623 | } | ||
624 | } | ||
597 | } | 625 | } |
626 | |||
598 | return status; | 627 | return status; |
599 | } | 628 | } |
600 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); | 629 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); |
@@ -705,22 +734,11 @@ static struct class wmi_class = { | |||
705 | .dev_attrs = wmi_dev_attrs, | 734 | .dev_attrs = wmi_dev_attrs, |
706 | }; | 735 | }; |
707 | 736 | ||
708 | static struct wmi_block *wmi_create_device(const struct guid_block *gblock, | 737 | static int wmi_create_device(const struct guid_block *gblock, |
709 | acpi_handle handle) | 738 | struct wmi_block *wblock, acpi_handle handle) |
710 | { | 739 | { |
711 | struct wmi_block *wblock; | ||
712 | int error; | ||
713 | char guid_string[37]; | 740 | char guid_string[37]; |
714 | 741 | ||
715 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | ||
716 | if (!wblock) { | ||
717 | error = -ENOMEM; | ||
718 | goto err_out; | ||
719 | } | ||
720 | |||
721 | wblock->handle = handle; | ||
722 | wblock->gblock = *gblock; | ||
723 | |||
724 | wblock->dev.class = &wmi_class; | 742 | wblock->dev.class = &wmi_class; |
725 | 743 | ||
726 | wmi_gtoa(gblock->guid, guid_string); | 744 | wmi_gtoa(gblock->guid, guid_string); |
@@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock, | |||
728 | 746 | ||
729 | dev_set_drvdata(&wblock->dev, wblock); | 747 | dev_set_drvdata(&wblock->dev, wblock); |
730 | 748 | ||
731 | error = device_register(&wblock->dev); | 749 | return device_register(&wblock->dev); |
732 | if (error) | ||
733 | goto err_free; | ||
734 | |||
735 | list_add_tail(&wblock->list, &wmi_block_list); | ||
736 | return wblock; | ||
737 | |||
738 | err_free: | ||
739 | kfree(wblock); | ||
740 | err_out: | ||
741 | return ERR_PTR(error); | ||
742 | } | 750 | } |
743 | 751 | ||
744 | static void wmi_free_devices(void) | 752 | static void wmi_free_devices(void) |
@@ -747,7 +755,8 @@ static void wmi_free_devices(void) | |||
747 | 755 | ||
748 | /* Delete devices for all the GUIDs */ | 756 | /* Delete devices for all the GUIDs */ |
749 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) | 757 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) |
750 | device_unregister(&wblock->dev); | 758 | if (wblock->dev.class) |
759 | device_unregister(&wblock->dev); | ||
751 | } | 760 | } |
752 | 761 | ||
753 | static bool guid_already_parsed(const char *guid_string) | 762 | static bool guid_already_parsed(const char *guid_string) |
@@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
770 | union acpi_object *obj; | 779 | union acpi_object *obj; |
771 | const struct guid_block *gblock; | 780 | const struct guid_block *gblock; |
772 | struct wmi_block *wblock; | 781 | struct wmi_block *wblock; |
773 | char guid_string[37]; | ||
774 | acpi_status status; | 782 | acpi_status status; |
775 | int retval; | 783 | int retval; |
776 | u32 i, total; | 784 | u32 i, total; |
@@ -792,28 +800,31 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
792 | total = obj->buffer.length / sizeof(struct guid_block); | 800 | total = obj->buffer.length / sizeof(struct guid_block); |
793 | 801 | ||
794 | for (i = 0; i < total; i++) { | 802 | for (i = 0; i < total; i++) { |
803 | if (debug_dump_wdg) | ||
804 | wmi_dump_wdg(&gblock[i]); | ||
805 | |||
806 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | ||
807 | if (!wblock) | ||
808 | return AE_NO_MEMORY; | ||
809 | |||
810 | wblock->handle = handle; | ||
811 | wblock->gblock = gblock[i]; | ||
812 | |||
795 | /* | 813 | /* |
796 | Some WMI devices, like those for nVidia hooks, have a | 814 | Some WMI devices, like those for nVidia hooks, have a |
797 | duplicate GUID. It's not clear what we should do in this | 815 | duplicate GUID. It's not clear what we should do in this |
798 | case yet, so for now, we'll just ignore the duplicate. | 816 | case yet, so for now, we'll just ignore the duplicate |
799 | Anyone who wants to add support for that device can come | 817 | for device creation. |
800 | up with a better workaround for the mess then. | ||
801 | */ | 818 | */ |
802 | if (guid_already_parsed(gblock[i].guid) == true) { | 819 | if (!guid_already_parsed(gblock[i].guid)) { |
803 | wmi_gtoa(gblock[i].guid, guid_string); | 820 | retval = wmi_create_device(&gblock[i], wblock, handle); |
804 | pr_info("Skipping duplicate GUID %s\n", guid_string); | 821 | if (retval) { |
805 | continue; | 822 | wmi_free_devices(); |
823 | goto out_free_pointer; | ||
824 | } | ||
806 | } | 825 | } |
807 | 826 | ||
808 | if (debug_dump_wdg) | 827 | list_add_tail(&wblock->list, &wmi_block_list); |
809 | wmi_dump_wdg(&gblock[i]); | ||
810 | |||
811 | wblock = wmi_create_device(&gblock[i], handle); | ||
812 | if (IS_ERR(wblock)) { | ||
813 | retval = PTR_ERR(wblock); | ||
814 | wmi_free_devices(); | ||
815 | break; | ||
816 | } | ||
817 | 828 | ||
818 | if (debug_event) { | 829 | if (debug_event) { |
819 | wblock->handler = wmi_notify_debug; | 830 | wblock->handler = wmi_notify_debug; |
diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index 4f95c1aac2fd..0e6dc3891942 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h | |||
@@ -112,6 +112,7 @@ | |||
112 | #define SONYPI_EVENT_VOLUME_DEC_PRESSED 70 | 112 | #define SONYPI_EVENT_VOLUME_DEC_PRESSED 70 |
113 | #define SONYPI_EVENT_BRIGHTNESS_PRESSED 71 | 113 | #define SONYPI_EVENT_BRIGHTNESS_PRESSED 71 |
114 | #define SONYPI_EVENT_MEDIA_PRESSED 72 | 114 | #define SONYPI_EVENT_MEDIA_PRESSED 72 |
115 | #define SONYPI_EVENT_VENDOR_PRESSED 73 | ||
115 | 116 | ||
116 | /* get/set brightness */ | 117 | /* get/set brightness */ |
117 | #define SONYPI_IOCGBRT _IOR('v', 0, __u8) | 118 | #define SONYPI_IOCGBRT _IOR('v', 0, __u8) |