diff options
Diffstat (limited to 'drivers/platform')
47 files changed, 9636 insertions, 2652 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index cff7cc2c1f02..45e0191c35dd 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, |
@@ -37,7 +39,7 @@ config ACER_WMI | |||
37 | 39 | ||
38 | config ACERHDF | 40 | config ACERHDF |
39 | tristate "Acer Aspire One temperature and fan driver" | 41 | tristate "Acer Aspire One temperature and fan driver" |
40 | depends on THERMAL && THERMAL_HWMON && ACPI | 42 | depends on THERMAL && ACPI |
41 | ---help--- | 43 | ---help--- |
42 | This is a driver for Acer Aspire One netbooks. It allows to access | 44 | This is a driver for Acer Aspire One netbooks. It allows to access |
43 | the temperature sensor and to control the fan. | 45 | the temperature sensor and to control the fan. |
@@ -92,12 +94,26 @@ config DELL_WMI | |||
92 | tristate "Dell WMI extras" | 94 | tristate "Dell WMI extras" |
93 | depends on ACPI_WMI | 95 | depends on ACPI_WMI |
94 | depends on INPUT | 96 | depends on INPUT |
97 | select INPUT_SPARSEKMAP | ||
95 | ---help--- | 98 | ---help--- |
96 | Say Y here if you want to support WMI-based hotkeys on Dell laptops. | 99 | Say Y here if you want to support WMI-based hotkeys on Dell laptops. |
97 | 100 | ||
98 | To compile this driver as a module, choose M here: the module will | 101 | To compile this driver as a module, choose M here: the module will |
99 | be called dell-wmi. | 102 | be called dell-wmi. |
100 | 103 | ||
104 | config DELL_WMI_AIO | ||
105 | tristate "WMI Hotkeys for Dell All-In-One series" | ||
106 | depends on ACPI_WMI | ||
107 | depends on INPUT | ||
108 | select INPUT_SPARSEKMAP | ||
109 | ---help--- | ||
110 | Say Y here if you want to support WMI-based hotkeys on Dell | ||
111 | All-In-One machines. | ||
112 | |||
113 | To compile this driver as a module, choose M here: the module will | ||
114 | be called dell-wmi-aio. | ||
115 | |||
116 | |||
101 | config FUJITSU_LAPTOP | 117 | config FUJITSU_LAPTOP |
102 | tristate "Fujitsu Laptop Extras" | 118 | tristate "Fujitsu Laptop Extras" |
103 | depends on ACPI | 119 | depends on ACPI |
@@ -130,16 +146,35 @@ config TC1100_WMI | |||
130 | depends on !X86_64 | 146 | depends on !X86_64 |
131 | depends on EXPERIMENTAL | 147 | depends on EXPERIMENTAL |
132 | depends on ACPI | 148 | depends on ACPI |
133 | select ACPI_WMI | 149 | depends on ACPI_WMI |
134 | ---help--- | 150 | ---help--- |
135 | This is a driver for the WMI extensions (wireless and bluetooth power | 151 | This is a driver for the WMI extensions (wireless and bluetooth power |
136 | control) of the HP Compaq TC1100 tablet. | 152 | control) of the HP Compaq TC1100 tablet. |
137 | 153 | ||
154 | config HP_ACCEL | ||
155 | tristate "HP laptop accelerometer" | ||
156 | depends on INPUT && ACPI | ||
157 | select SENSORS_LIS3LV02D | ||
158 | select NEW_LEDS | ||
159 | select LEDS_CLASS | ||
160 | help | ||
161 | This driver provides support for the "Mobile Data Protection System 3D" | ||
162 | or "3D DriveGuard" feature of HP laptops. On such systems the driver | ||
163 | should load automatically (via ACPI alias). | ||
164 | |||
165 | Support for a led indicating disk protection will be provided as | ||
166 | hp::hddprotect. For more information on the feature, refer to | ||
167 | Documentation/hwmon/lis3lv02d. | ||
168 | |||
169 | To compile this driver as a module, choose M here: the module will | ||
170 | be called hp_accel. | ||
171 | |||
138 | config HP_WMI | 172 | config HP_WMI |
139 | tristate "HP WMI extras" | 173 | tristate "HP WMI extras" |
140 | depends on ACPI_WMI | 174 | depends on ACPI_WMI |
141 | depends on INPUT | 175 | depends on INPUT |
142 | depends on RFKILL || RFKILL = n | 176 | depends on RFKILL || RFKILL = n |
177 | select INPUT_SPARSEKMAP | ||
143 | help | 178 | help |
144 | Say Y here if you want to support WMI-based hotkeys on HP laptops and | 179 | Say Y here if you want to support WMI-based hotkeys on HP laptops and |
145 | to read data from WMI such as docking or ambient light sensor state. | 180 | to read data from WMI such as docking or ambient light sensor state. |
@@ -152,7 +187,8 @@ config MSI_LAPTOP | |||
152 | depends on ACPI | 187 | depends on ACPI |
153 | depends on BACKLIGHT_CLASS_DEVICE | 188 | depends on BACKLIGHT_CLASS_DEVICE |
154 | depends on RFKILL | 189 | depends on RFKILL |
155 | depends on SERIO_I8042 | 190 | depends on INPUT && SERIO_I8042 |
191 | select INPUT_SPARSEKMAP | ||
156 | ---help--- | 192 | ---help--- |
157 | This is a driver for laptops built by MSI (MICRO-STAR | 193 | This is a driver for laptops built by MSI (MICRO-STAR |
158 | INTERNATIONAL): | 194 | INTERNATIONAL): |
@@ -171,6 +207,7 @@ config PANASONIC_LAPTOP | |||
171 | tristate "Panasonic Laptop Extras" | 207 | tristate "Panasonic Laptop Extras" |
172 | depends on INPUT && ACPI | 208 | depends on INPUT && ACPI |
173 | depends on BACKLIGHT_CLASS_DEVICE | 209 | depends on BACKLIGHT_CLASS_DEVICE |
210 | select INPUT_SPARSEKMAP | ||
174 | ---help--- | 211 | ---help--- |
175 | This driver adds support for access to backlight control and hotkeys | 212 | This driver adds support for access to backlight control and hotkeys |
176 | on Panasonic Let's Note laptops. | 213 | on Panasonic Let's Note laptops. |
@@ -219,10 +256,11 @@ config SONYPI_COMPAT | |||
219 | ---help--- | 256 | ---help--- |
220 | Build the sonypi driver compatibility code into the sony-laptop driver. | 257 | Build the sonypi driver compatibility code into the sony-laptop driver. |
221 | 258 | ||
222 | config IDEAPAD_ACPI | 259 | config IDEAPAD_LAPTOP |
223 | tristate "Lenovo IdeaPad ACPI Laptop Extras" | 260 | tristate "Lenovo IdeaPad Laptop Extras" |
224 | depends on ACPI | 261 | depends on ACPI |
225 | depends on RFKILL | 262 | depends on RFKILL && INPUT |
263 | select INPUT_SPARSEKMAP | ||
226 | help | 264 | help |
227 | This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. | 265 | This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. |
228 | 266 | ||
@@ -365,6 +403,26 @@ config THINKPAD_ACPI_HOTKEY_POLL | |||
365 | If you are not sure, say Y here. The driver enables polling only if | 403 | If you are not sure, say Y here. The driver enables polling only if |
366 | it is strictly necessary to do so. | 404 | it is strictly necessary to do so. |
367 | 405 | ||
406 | config SENSORS_HDAPS | ||
407 | tristate "Thinkpad Hard Drive Active Protection System (hdaps)" | ||
408 | depends on INPUT && X86 | ||
409 | select INPUT_POLLDEV | ||
410 | default n | ||
411 | help | ||
412 | This driver provides support for the IBM Hard Drive Active Protection | ||
413 | System (hdaps), which provides an accelerometer and other misc. data. | ||
414 | ThinkPads starting with the R50, T41, and X40 are supported. The | ||
415 | accelerometer data is readable via sysfs. | ||
416 | |||
417 | This driver also provides an absolute input class device, allowing | ||
418 | the laptop to act as a pinball machine-esque joystick. | ||
419 | |||
420 | If your ThinkPad is not recognized by the driver, please update to latest | ||
421 | BIOS. This is especially the case for some R52 ThinkPads. | ||
422 | |||
423 | Say Y here if you have an applicable laptop and want to experience | ||
424 | the awesome power of hdaps. | ||
425 | |||
368 | config INTEL_MENLOW | 426 | config INTEL_MENLOW |
369 | tristate "Thermal Management driver for Intel menlow platform" | 427 | tristate "Thermal Management driver for Intel menlow platform" |
370 | depends on ACPI_THERMAL | 428 | depends on ACPI_THERMAL |
@@ -394,20 +452,53 @@ config EEEPC_LAPTOP | |||
394 | Bluetooth, backlight and allows powering on/off some other | 452 | Bluetooth, backlight and allows powering on/off some other |
395 | devices. | 453 | devices. |
396 | 454 | ||
397 | If you have an Eee PC laptop, say Y or M here. | 455 | If you have an Eee PC laptop, say Y or M here. If this driver |
456 | doesn't work on your Eee PC, try eeepc-wmi instead. | ||
398 | 457 | ||
399 | config EEEPC_WMI | 458 | config ASUS_WMI |
400 | tristate "Eee PC WMI Hotkey Driver (EXPERIMENTAL)" | 459 | tristate "ASUS WMI Driver (EXPERIMENTAL)" |
401 | depends on ACPI_WMI | 460 | depends on ACPI_WMI |
402 | depends on INPUT | 461 | depends on INPUT |
462 | depends on HWMON | ||
403 | depends on EXPERIMENTAL | 463 | depends on EXPERIMENTAL |
404 | depends on BACKLIGHT_CLASS_DEVICE | 464 | depends on BACKLIGHT_CLASS_DEVICE |
465 | depends on RFKILL || RFKILL = n | ||
466 | depends on HOTPLUG_PCI | ||
405 | select INPUT_SPARSEKMAP | 467 | select INPUT_SPARSEKMAP |
468 | select LEDS_CLASS | ||
469 | select NEW_LEDS | ||
406 | ---help--- | 470 | ---help--- |
407 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. | 471 | Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new |
472 | Asus Notebooks). | ||
408 | 473 | ||
409 | To compile this driver as a module, choose M here: the module will | 474 | To compile this driver as a module, choose M here: the module will |
410 | be called eeepc-wmi. | 475 | be called asus-wmi. |
476 | |||
477 | config ASUS_NB_WMI | ||
478 | tristate "Asus Notebook WMI Driver (EXPERIMENTAL)" | ||
479 | depends on ASUS_WMI | ||
480 | ---help--- | ||
481 | This is a driver for newer Asus notebooks. It adds extra features | ||
482 | like wireless radio and bluetooth control, leds, hotkeys, backlight... | ||
483 | |||
484 | For more informations, see | ||
485 | <file:Documentation/ABI/testing/sysfs-platform-asus-wmi> | ||
486 | |||
487 | If you have an ACPI-WMI compatible Asus Notebook, say Y or M | ||
488 | here. | ||
489 | |||
490 | config EEEPC_WMI | ||
491 | tristate "Eee PC WMI Driver (EXPERIMENTAL)" | ||
492 | depends on ASUS_WMI | ||
493 | ---help--- | ||
494 | This is a driver for newer Eee PC laptops. It adds extra features | ||
495 | like wireless radio and bluetooth control, leds, hotkeys, backlight... | ||
496 | |||
497 | For more informations, see | ||
498 | <file:Documentation/ABI/testing/sysfs-platform-asus-wmi> | ||
499 | |||
500 | If you have an ACPI-WMI compatible Eee PC laptop (>= 1000), say Y or M | ||
501 | here. | ||
411 | 502 | ||
412 | config ACPI_WMI | 503 | config ACPI_WMI |
413 | tristate "WMI" | 504 | tristate "WMI" |
@@ -478,6 +569,7 @@ config TOPSTAR_LAPTOP | |||
478 | tristate "Topstar Laptop Extras" | 569 | tristate "Topstar Laptop Extras" |
479 | depends on ACPI | 570 | depends on ACPI |
480 | depends on INPUT | 571 | depends on INPUT |
572 | select INPUT_SPARSEKMAP | ||
481 | ---help--- | 573 | ---help--- |
482 | This driver adds support for hotkeys found on Topstar laptops. | 574 | This driver adds support for hotkeys found on Topstar laptops. |
483 | 575 | ||
@@ -486,12 +578,13 @@ config TOPSTAR_LAPTOP | |||
486 | config ACPI_TOSHIBA | 578 | config ACPI_TOSHIBA |
487 | tristate "Toshiba Laptop Extras" | 579 | tristate "Toshiba Laptop Extras" |
488 | depends on ACPI | 580 | depends on ACPI |
489 | depends on LEDS_CLASS | 581 | select LEDS_CLASS |
490 | depends on NEW_LEDS | 582 | select NEW_LEDS |
491 | depends on BACKLIGHT_CLASS_DEVICE | 583 | depends on BACKLIGHT_CLASS_DEVICE |
492 | depends on INPUT | 584 | depends on INPUT |
493 | depends on RFKILL || RFKILL = n | 585 | depends on RFKILL || RFKILL = n |
494 | select INPUT_POLLDEV | 586 | select INPUT_POLLDEV |
587 | select INPUT_SPARSEKMAP | ||
495 | ---help--- | 588 | ---help--- |
496 | This driver adds support for access to certain system settings | 589 | This driver adds support for access to certain system settings |
497 | on "legacy free" Toshiba laptops. These laptops can be recognized by | 590 | on "legacy free" Toshiba laptops. These laptops can be recognized by |
@@ -551,6 +644,15 @@ config INTEL_SCU_IPC | |||
551 | some embedded Intel x86 platforms. This is not needed for PC-type | 644 | some embedded Intel x86 platforms. This is not needed for PC-type |
552 | machines. | 645 | machines. |
553 | 646 | ||
647 | config INTEL_SCU_IPC_UTIL | ||
648 | tristate "Intel SCU IPC utility driver" | ||
649 | depends on INTEL_SCU_IPC | ||
650 | default y | ||
651 | ---help--- | ||
652 | The IPC Util driver provides an interface with the SCU enabling | ||
653 | low level access for debug work and updating the firmware. Say | ||
654 | N unless you will be doing this on an Intel MID platform. | ||
655 | |||
554 | config GPIO_INTEL_PMIC | 656 | config GPIO_INTEL_PMIC |
555 | bool "Intel PMIC GPIO support" | 657 | bool "Intel PMIC GPIO support" |
556 | depends on INTEL_SCU_IPC && GPIOLIB | 658 | depends on INTEL_SCU_IPC && GPIOLIB |
@@ -558,6 +660,21 @@ config GPIO_INTEL_PMIC | |||
558 | Say Y here to support GPIO via the SCU IPC interface | 660 | Say Y here to support GPIO via the SCU IPC interface |
559 | on Intel MID platforms. | 661 | on Intel MID platforms. |
560 | 662 | ||
663 | config INTEL_MID_POWER_BUTTON | ||
664 | tristate "power button driver for Intel MID platforms" | ||
665 | depends on INTEL_SCU_IPC && INPUT | ||
666 | help | ||
667 | This driver handles the power button on the Intel MID platforms. | ||
668 | |||
669 | If unsure, say N. | ||
670 | |||
671 | config INTEL_MFLD_THERMAL | ||
672 | tristate "Thermal driver for Intel Medfield platform" | ||
673 | depends on INTEL_SCU_IPC && THERMAL | ||
674 | help | ||
675 | Say Y here to enable thermal driver support for the Intel Medfield | ||
676 | platform. | ||
677 | |||
561 | config RAR_REGISTER | 678 | config RAR_REGISTER |
562 | bool "Restricted Access Region Register Driver" | 679 | bool "Restricted Access Region Register Driver" |
563 | depends on PCI && X86_MRST | 680 | depends on PCI && X86_MRST |
@@ -590,4 +707,66 @@ config INTEL_IPS | |||
590 | functionality. If in doubt, say Y here; it will only load on | 707 | functionality. If in doubt, say Y here; it will only load on |
591 | supported platforms. | 708 | supported platforms. |
592 | 709 | ||
710 | config IBM_RTL | ||
711 | tristate "Device driver to enable PRTL support" | ||
712 | depends on X86 && PCI | ||
713 | ---help--- | ||
714 | Enable support for IBM Premium Real Time Mode (PRTM). | ||
715 | This module will allow you the enter and exit PRTM in the BIOS via | ||
716 | sysfs on platforms that support this feature. System in PRTM will | ||
717 | not receive CPU-generated SMIs for recoverable errors. Use of this | ||
718 | feature without proper support may void your hardware warranty. | ||
719 | |||
720 | If the proper BIOS support is found the driver will load and create | ||
721 | /sys/devices/system/ibm_rtl/. The "state" variable will indicate | ||
722 | whether or not the BIOS is in PRTM. | ||
723 | state = 0 (BIOS SMIs on) | ||
724 | state = 1 (BIOS SMIs off) | ||
725 | |||
726 | config XO1_RFKILL | ||
727 | tristate "OLPC XO-1 software RF kill switch" | ||
728 | depends on OLPC | ||
729 | depends on RFKILL | ||
730 | ---help--- | ||
731 | Support for enabling/disabling the WLAN interface on the OLPC XO-1 | ||
732 | laptop. | ||
733 | |||
734 | config XO15_EBOOK | ||
735 | tristate "OLPC XO-1.5 ebook switch" | ||
736 | depends on ACPI && INPUT | ||
737 | ---help--- | ||
738 | Support for the ebook switch on the OLPC XO-1.5 laptop. | ||
739 | |||
740 | This switch is triggered as the screen is rotated and folded down to | ||
741 | convert the device into ebook form. | ||
742 | |||
743 | config SAMSUNG_LAPTOP | ||
744 | tristate "Samsung Laptop driver" | ||
745 | depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86 | ||
746 | ---help--- | ||
747 | This module implements a driver for a wide range of different | ||
748 | Samsung laptops. It offers control over the different | ||
749 | function keys, wireless LED, LCD backlight level, and | ||
750 | sometimes provides a "performance_control" sysfs file to allow | ||
751 | the performance level of the laptop to be changed. | ||
752 | |||
753 | To compile this driver as a module, choose M here: the module | ||
754 | will be called samsung-laptop. | ||
755 | |||
756 | config MXM_WMI | ||
757 | tristate "WMI support for MXM Laptop Graphics" | ||
758 | depends on ACPI_WMI | ||
759 | ---help--- | ||
760 | MXM is a standard for laptop graphics cards, the WMI interface | ||
761 | is required for switchable nvidia graphics machines | ||
762 | |||
763 | config INTEL_OAKTRAIL | ||
764 | tristate "Intel Oaktrail Platform Extras" | ||
765 | depends on ACPI | ||
766 | depends on RFKILL && BACKLIGHT_CLASS_DEVICE && ACPI | ||
767 | ---help--- | ||
768 | Intel Oaktrail platform need this driver to provide interfaces to | ||
769 | enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y | ||
770 | here; it will only load on supported platforms. | ||
771 | |||
593 | endif # X86_PLATFORM_DEVICES | 772 | endif # X86_PLATFORM_DEVICES |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 85fb2b84f57e..afc1f832aa67 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -3,6 +3,8 @@ | |||
3 | # x86 Platform-Specific Drivers | 3 | # x86 Platform-Specific Drivers |
4 | # | 4 | # |
5 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o | 5 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o |
6 | obj-$(CONFIG_ASUS_WMI) += asus-wmi.o | ||
7 | obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o | ||
6 | obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o | 8 | obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o |
7 | obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o | 9 | obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o |
8 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o | 10 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o |
@@ -10,13 +12,16 @@ obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o | |||
10 | obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o | 12 | obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o |
11 | obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o | 13 | obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o |
12 | obj-$(CONFIG_DELL_WMI) += dell-wmi.o | 14 | obj-$(CONFIG_DELL_WMI) += dell-wmi.o |
15 | obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o | ||
13 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o | 16 | obj-$(CONFIG_ACER_WMI) += acer-wmi.o |
14 | obj-$(CONFIG_ACERHDF) += acerhdf.o | 17 | obj-$(CONFIG_ACERHDF) += acerhdf.o |
18 | obj-$(CONFIG_HP_ACCEL) += hp_accel.o | ||
15 | obj-$(CONFIG_HP_WMI) += hp-wmi.o | 19 | obj-$(CONFIG_HP_WMI) += hp-wmi.o |
16 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o | 20 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o |
17 | obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o | 21 | obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o |
18 | obj-$(CONFIG_IDEAPAD_ACPI) += ideapad_acpi.o | 22 | obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o |
19 | obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o | 23 | obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o |
24 | obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o | ||
20 | obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o | 25 | obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o |
21 | obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o | 26 | obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o |
22 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o | 27 | obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o |
@@ -27,7 +32,15 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | |||
27 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 32 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
28 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o | 33 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o |
29 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o | 34 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o |
35 | obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o | ||
36 | obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o | ||
30 | obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o | 37 | obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o |
31 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o | 38 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o |
32 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o | 39 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o |
33 | 40 | obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o | |
41 | obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o | ||
42 | obj-$(CONFIG_IBM_RTL) += ibm_rtl.o | ||
43 | obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o | ||
44 | obj-$(CONFIG_MXM_WMI) += mxm-wmi.o | ||
45 | obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o | ||
46 | obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o | ||
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 2badee2fdeed..e1c4938b301b 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -22,6 +22,8 @@ | |||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
26 | |||
25 | #include <linux/kernel.h> | 27 | #include <linux/kernel.h> |
26 | #include <linux/module.h> | 28 | #include <linux/module.h> |
27 | #include <linux/init.h> | 29 | #include <linux/init.h> |
@@ -37,6 +39,8 @@ | |||
37 | #include <linux/workqueue.h> | 39 | #include <linux/workqueue.h> |
38 | #include <linux/debugfs.h> | 40 | #include <linux/debugfs.h> |
39 | #include <linux/slab.h> | 41 | #include <linux/slab.h> |
42 | #include <linux/input.h> | ||
43 | #include <linux/input/sparse-keymap.h> | ||
40 | 44 | ||
41 | #include <acpi/acpi_drivers.h> | 45 | #include <acpi/acpi_drivers.h> |
42 | 46 | ||
@@ -44,11 +48,6 @@ MODULE_AUTHOR("Carlos Corbacho"); | |||
44 | MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); | 48 | MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); |
45 | MODULE_LICENSE("GPL"); | 49 | MODULE_LICENSE("GPL"); |
46 | 50 | ||
47 | #define ACER_LOGPREFIX "acer-wmi: " | ||
48 | #define ACER_ERR KERN_ERR ACER_LOGPREFIX | ||
49 | #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX | ||
50 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX | ||
51 | |||
52 | /* | 51 | /* |
53 | * Magic Number | 52 | * Magic Number |
54 | * Meaning is unknown - this number is required for writing to ACPI for AMW0 | 53 | * Meaning is unknown - this number is required for writing to ACPI for AMW0 |
@@ -80,11 +79,98 @@ MODULE_LICENSE("GPL"); | |||
80 | */ | 79 | */ |
81 | #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB" | 80 | #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB" |
82 | #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" | 81 | #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" |
83 | #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" | 82 | #define WMID_GUID1 "6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3" |
84 | #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" | 83 | #define WMID_GUID2 "95764E09-FB56-4E83-B31A-37761F60994A" |
84 | #define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" | ||
85 | |||
86 | /* | ||
87 | * Acer ACPI event GUIDs | ||
88 | */ | ||
89 | #define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026" | ||
85 | 90 | ||
86 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); | 91 | MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); |
87 | MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); | 92 | MODULE_ALIAS("wmi:6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3"); |
93 | MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026"); | ||
94 | |||
95 | enum acer_wmi_event_ids { | ||
96 | WMID_HOTKEY_EVENT = 0x1, | ||
97 | }; | ||
98 | |||
99 | static const struct key_entry acer_wmi_keymap[] = { | ||
100 | {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */ | ||
101 | {KE_KEY, 0x03, {KEY_WLAN} }, /* WiFi */ | ||
102 | {KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */ | ||
103 | {KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */ | ||
104 | {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ | ||
105 | {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ | ||
106 | {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ | ||
107 | {KE_IGNORE, 0x41, {KEY_MUTE} }, | ||
108 | {KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} }, | ||
109 | {KE_IGNORE, 0x43, {KEY_NEXTSONG} }, | ||
110 | {KE_IGNORE, 0x44, {KEY_PLAYPAUSE} }, | ||
111 | {KE_IGNORE, 0x45, {KEY_STOP} }, | ||
112 | {KE_IGNORE, 0x48, {KEY_VOLUMEUP} }, | ||
113 | {KE_IGNORE, 0x49, {KEY_VOLUMEDOWN} }, | ||
114 | {KE_IGNORE, 0x61, {KEY_SWITCHVIDEOMODE} }, | ||
115 | {KE_IGNORE, 0x62, {KEY_BRIGHTNESSUP} }, | ||
116 | {KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} }, | ||
117 | {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */ | ||
118 | {KE_IGNORE, 0x81, {KEY_SLEEP} }, | ||
119 | {KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad On/Off */ | ||
120 | {KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} }, | ||
121 | {KE_END, 0} | ||
122 | }; | ||
123 | |||
124 | static struct input_dev *acer_wmi_input_dev; | ||
125 | |||
126 | struct event_return_value { | ||
127 | u8 function; | ||
128 | u8 key_num; | ||
129 | u16 device_state; | ||
130 | u32 reserved; | ||
131 | } __attribute__((packed)); | ||
132 | |||
133 | /* | ||
134 | * GUID3 Get Device Status device flags | ||
135 | */ | ||
136 | #define ACER_WMID3_GDS_WIRELESS (1<<0) /* WiFi */ | ||
137 | #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ | ||
138 | #define ACER_WMID3_GDS_WIMAX (1<<7) /* WiMAX */ | ||
139 | #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ | ||
140 | |||
141 | struct lm_input_params { | ||
142 | u8 function_num; /* Function Number */ | ||
143 | u16 commun_devices; /* Communication type devices default status */ | ||
144 | u16 devices; /* Other type devices default status */ | ||
145 | u8 lm_status; /* Launch Manager Status */ | ||
146 | u16 reserved; | ||
147 | } __attribute__((packed)); | ||
148 | |||
149 | struct lm_return_value { | ||
150 | u8 error_code; /* Error Code */ | ||
151 | u8 ec_return_value; /* EC Return Value */ | ||
152 | u16 reserved; | ||
153 | } __attribute__((packed)); | ||
154 | |||
155 | struct wmid3_gds_input_param { /* Get Device Status input parameter */ | ||
156 | u8 function_num; /* Function Number */ | ||
157 | u8 hotkey_number; /* Hotkey Number */ | ||
158 | u16 devices; /* Get Device */ | ||
159 | } __attribute__((packed)); | ||
160 | |||
161 | struct wmid3_gds_return_value { /* Get Device Status return value*/ | ||
162 | u8 error_code; /* Error Code */ | ||
163 | u8 ec_return_value; /* EC Return Value */ | ||
164 | u16 devices; /* Current Device Status */ | ||
165 | u32 reserved; | ||
166 | } __attribute__((packed)); | ||
167 | |||
168 | struct hotkey_function_type_aa { | ||
169 | u8 type; | ||
170 | u8 length; | ||
171 | u16 handle; | ||
172 | u16 commun_func_bitmap; | ||
173 | } __attribute__((packed)); | ||
88 | 174 | ||
89 | /* | 175 | /* |
90 | * Interface capability flags | 176 | * Interface capability flags |
@@ -116,15 +202,19 @@ static int mailled = -1; | |||
116 | static int brightness = -1; | 202 | static int brightness = -1; |
117 | static int threeg = -1; | 203 | static int threeg = -1; |
118 | static int force_series; | 204 | static int force_series; |
205 | static bool ec_raw_mode; | ||
206 | static bool has_type_aa; | ||
119 | 207 | ||
120 | module_param(mailled, int, 0444); | 208 | module_param(mailled, int, 0444); |
121 | module_param(brightness, int, 0444); | 209 | module_param(brightness, int, 0444); |
122 | module_param(threeg, int, 0444); | 210 | module_param(threeg, int, 0444); |
123 | module_param(force_series, int, 0444); | 211 | module_param(force_series, int, 0444); |
212 | module_param(ec_raw_mode, bool, 0444); | ||
124 | MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); | 213 | MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); |
125 | MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); | 214 | MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); |
126 | MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); | 215 | MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); |
127 | MODULE_PARM_DESC(force_series, "Force a different laptop series"); | 216 | MODULE_PARM_DESC(force_series, "Force a different laptop series"); |
217 | MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode"); | ||
128 | 218 | ||
129 | struct acer_data { | 219 | struct acer_data { |
130 | int mailled; | 220 | int mailled; |
@@ -140,6 +230,8 @@ struct acer_debug { | |||
140 | 230 | ||
141 | static struct rfkill *wireless_rfkill; | 231 | static struct rfkill *wireless_rfkill; |
142 | static struct rfkill *bluetooth_rfkill; | 232 | static struct rfkill *bluetooth_rfkill; |
233 | static struct rfkill *threeg_rfkill; | ||
234 | static bool rfkill_inited; | ||
143 | 235 | ||
144 | /* Each low-level interface must define at least some of the following */ | 236 | /* Each low-level interface must define at least some of the following */ |
145 | struct wmi_interface { | 237 | struct wmi_interface { |
@@ -659,8 +751,11 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out) | |||
659 | 751 | ||
660 | obj = (union acpi_object *) result.pointer; | 752 | obj = (union acpi_object *) result.pointer; |
661 | if (obj && obj->type == ACPI_TYPE_BUFFER && | 753 | if (obj && obj->type == ACPI_TYPE_BUFFER && |
662 | obj->buffer.length == sizeof(u32)) { | 754 | (obj->buffer.length == sizeof(u32) || |
755 | obj->buffer.length == sizeof(u64))) { | ||
663 | tmp = *((u32 *) obj->buffer.pointer); | 756 | tmp = *((u32 *) obj->buffer.pointer); |
757 | } else if (obj->type == ACPI_TYPE_INTEGER) { | ||
758 | tmp = (u32) obj->integer.value; | ||
664 | } else { | 759 | } else { |
665 | tmp = 0; | 760 | tmp = 0; |
666 | } | 761 | } |
@@ -753,6 +848,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); | 848 | return WMI_execute_u32(method_id, (u32)value, NULL); |
754 | } | 849 | } |
755 | 850 | ||
851 | static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) | ||
852 | { | ||
853 | struct hotkey_function_type_aa *type_aa; | ||
854 | |||
855 | /* We are looking for OEM-specific Type AAh */ | ||
856 | if (header->type != 0xAA) | ||
857 | return; | ||
858 | |||
859 | has_type_aa = true; | ||
860 | type_aa = (struct hotkey_function_type_aa *) header; | ||
861 | |||
862 | pr_info("Function bitmap for Communication Button: 0x%x\n", | ||
863 | type_aa->commun_func_bitmap); | ||
864 | |||
865 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) | ||
866 | interface->capability |= ACER_CAP_WIRELESS; | ||
867 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG) | ||
868 | interface->capability |= ACER_CAP_THREEG; | ||
869 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) | ||
870 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
871 | } | ||
872 | |||
756 | static acpi_status WMID_set_capabilities(void) | 873 | static acpi_status WMID_set_capabilities(void) |
757 | { | 874 | { |
758 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | 875 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; |
@@ -766,23 +883,28 @@ static acpi_status WMID_set_capabilities(void) | |||
766 | 883 | ||
767 | obj = (union acpi_object *) out.pointer; | 884 | obj = (union acpi_object *) out.pointer; |
768 | if (obj && obj->type == ACPI_TYPE_BUFFER && | 885 | if (obj && obj->type == ACPI_TYPE_BUFFER && |
769 | obj->buffer.length == sizeof(u32)) { | 886 | (obj->buffer.length == sizeof(u32) || |
887 | obj->buffer.length == sizeof(u64))) { | ||
770 | devices = *((u32 *) obj->buffer.pointer); | 888 | devices = *((u32 *) obj->buffer.pointer); |
889 | } else if (obj->type == ACPI_TYPE_INTEGER) { | ||
890 | devices = (u32) obj->integer.value; | ||
771 | } else { | 891 | } else { |
772 | kfree(out.pointer); | 892 | kfree(out.pointer); |
773 | return AE_ERROR; | 893 | return AE_ERROR; |
774 | } | 894 | } |
775 | 895 | ||
776 | /* Not sure on the meaning of the relevant bits yet to detect these */ | 896 | dmi_walk(type_aa_dmi_decode, NULL); |
777 | interface->capability |= ACER_CAP_WIRELESS; | 897 | if (!has_type_aa) { |
778 | interface->capability |= ACER_CAP_THREEG; | 898 | interface->capability |= ACER_CAP_WIRELESS; |
899 | if (devices & 0x40) | ||
900 | interface->capability |= ACER_CAP_THREEG; | ||
901 | if (devices & 0x10) | ||
902 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
903 | } | ||
779 | 904 | ||
780 | /* WMID always provides brightness methods */ | 905 | /* WMID always provides brightness methods */ |
781 | interface->capability |= ACER_CAP_BRIGHTNESS; | 906 | interface->capability |= ACER_CAP_BRIGHTNESS; |
782 | 907 | ||
783 | if (devices & 0x10) | ||
784 | interface->capability |= ACER_CAP_BLUETOOTH; | ||
785 | |||
786 | if (!(devices & 0x20)) | 908 | if (!(devices & 0x20)) |
787 | max_brightness = 0x9; | 909 | max_brightness = 0x9; |
788 | 910 | ||
@@ -860,9 +982,12 @@ static void __init acer_commandline_init(void) | |||
860 | * These will all fail silently if the value given is invalid, or the | 982 | * These will all fail silently if the value given is invalid, or the |
861 | * capability isn't available on the given interface | 983 | * capability isn't available on the given interface |
862 | */ | 984 | */ |
863 | set_u32(mailled, ACER_CAP_MAILLED); | 985 | if (mailled >= 0) |
864 | set_u32(threeg, ACER_CAP_THREEG); | 986 | set_u32(mailled, ACER_CAP_MAILLED); |
865 | set_u32(brightness, ACER_CAP_BRIGHTNESS); | 987 | if (!has_type_aa && threeg >= 0) |
988 | set_u32(threeg, ACER_CAP_THREEG); | ||
989 | if (brightness >= 0) | ||
990 | set_u32(brightness, ACER_CAP_BRIGHTNESS); | ||
866 | } | 991 | } |
867 | 992 | ||
868 | /* | 993 | /* |
@@ -886,6 +1011,7 @@ static int __devinit acer_led_init(struct device *dev) | |||
886 | 1011 | ||
887 | static void acer_led_exit(void) | 1012 | static void acer_led_exit(void) |
888 | { | 1013 | { |
1014 | set_u32(LED_OFF, ACER_CAP_MAILLED); | ||
889 | led_classdev_unregister(&mail_led); | 1015 | led_classdev_unregister(&mail_led); |
890 | } | 1016 | } |
891 | 1017 | ||
@@ -915,7 +1041,7 @@ static int update_bl_status(struct backlight_device *bd) | |||
915 | return 0; | 1041 | return 0; |
916 | } | 1042 | } |
917 | 1043 | ||
918 | static struct backlight_ops acer_bl_ops = { | 1044 | static const struct backlight_ops acer_bl_ops = { |
919 | .get_brightness = read_brightness, | 1045 | .get_brightness = read_brightness, |
920 | .update_status = update_bl_status, | 1046 | .update_status = update_bl_status, |
921 | }; | 1047 | }; |
@@ -926,11 +1052,12 @@ static int __devinit acer_backlight_init(struct device *dev) | |||
926 | struct backlight_device *bd; | 1052 | struct backlight_device *bd; |
927 | 1053 | ||
928 | memset(&props, 0, sizeof(struct backlight_properties)); | 1054 | memset(&props, 0, sizeof(struct backlight_properties)); |
1055 | props.type = BACKLIGHT_PLATFORM; | ||
929 | props.max_brightness = max_brightness; | 1056 | props.max_brightness = max_brightness; |
930 | bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops, | 1057 | bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops, |
931 | &props); | 1058 | &props); |
932 | if (IS_ERR(bd)) { | 1059 | if (IS_ERR(bd)) { |
933 | printk(ACER_ERR "Could not register Acer backlight device\n"); | 1060 | pr_err("Could not register Acer backlight device\n"); |
934 | acer_backlight_device = NULL; | 1061 | acer_backlight_device = NULL; |
935 | return PTR_ERR(bd); | 1062 | return PTR_ERR(bd); |
936 | } | 1063 | } |
@@ -948,6 +1075,186 @@ static void acer_backlight_exit(void) | |||
948 | backlight_device_unregister(acer_backlight_device); | 1075 | backlight_device_unregister(acer_backlight_device); |
949 | } | 1076 | } |
950 | 1077 | ||
1078 | static acpi_status wmid3_get_device_status(u32 *value, u16 device) | ||
1079 | { | ||
1080 | struct wmid3_gds_return_value return_value; | ||
1081 | acpi_status status; | ||
1082 | union acpi_object *obj; | ||
1083 | struct wmid3_gds_input_param params = { | ||
1084 | .function_num = 0x1, | ||
1085 | .hotkey_number = 0x01, | ||
1086 | .devices = device, | ||
1087 | }; | ||
1088 | struct acpi_buffer input = { | ||
1089 | sizeof(struct wmid3_gds_input_param), | ||
1090 | ¶ms | ||
1091 | }; | ||
1092 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1093 | |||
1094 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | ||
1095 | if (ACPI_FAILURE(status)) | ||
1096 | return status; | ||
1097 | |||
1098 | obj = output.pointer; | ||
1099 | |||
1100 | if (!obj) | ||
1101 | return AE_ERROR; | ||
1102 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1103 | kfree(obj); | ||
1104 | return AE_ERROR; | ||
1105 | } | ||
1106 | if (obj->buffer.length != 8) { | ||
1107 | pr_warn("Unknown buffer length %d\n", obj->buffer.length); | ||
1108 | kfree(obj); | ||
1109 | return AE_ERROR; | ||
1110 | } | ||
1111 | |||
1112 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
1113 | kfree(obj); | ||
1114 | |||
1115 | if (return_value.error_code || return_value.ec_return_value) | ||
1116 | pr_warn("Get Device Status failed: 0x%x - 0x%x\n", | ||
1117 | return_value.error_code, | ||
1118 | return_value.ec_return_value); | ||
1119 | else | ||
1120 | *value = !!(return_value.devices & device); | ||
1121 | |||
1122 | return status; | ||
1123 | } | ||
1124 | |||
1125 | static acpi_status get_device_status(u32 *value, u32 cap) | ||
1126 | { | ||
1127 | if (wmi_has_guid(WMID_GUID3)) { | ||
1128 | u16 device; | ||
1129 | |||
1130 | switch (cap) { | ||
1131 | case ACER_CAP_WIRELESS: | ||
1132 | device = ACER_WMID3_GDS_WIRELESS; | ||
1133 | break; | ||
1134 | case ACER_CAP_BLUETOOTH: | ||
1135 | device = ACER_WMID3_GDS_BLUETOOTH; | ||
1136 | break; | ||
1137 | case ACER_CAP_THREEG: | ||
1138 | device = ACER_WMID3_GDS_THREEG; | ||
1139 | break; | ||
1140 | default: | ||
1141 | return AE_ERROR; | ||
1142 | } | ||
1143 | return wmid3_get_device_status(value, device); | ||
1144 | |||
1145 | } else { | ||
1146 | return get_u32(value, cap); | ||
1147 | } | ||
1148 | } | ||
1149 | |||
1150 | static acpi_status wmid3_set_device_status(u32 value, u16 device) | ||
1151 | { | ||
1152 | struct wmid3_gds_return_value return_value; | ||
1153 | acpi_status status; | ||
1154 | union acpi_object *obj; | ||
1155 | u16 devices; | ||
1156 | struct wmid3_gds_input_param params = { | ||
1157 | .function_num = 0x1, | ||
1158 | .hotkey_number = 0x01, | ||
1159 | .devices = ACER_WMID3_GDS_WIRELESS | | ||
1160 | ACER_WMID3_GDS_THREEG | | ||
1161 | ACER_WMID3_GDS_WIMAX | | ||
1162 | ACER_WMID3_GDS_BLUETOOTH, | ||
1163 | }; | ||
1164 | struct acpi_buffer input = { | ||
1165 | sizeof(struct wmid3_gds_input_param), | ||
1166 | ¶ms | ||
1167 | }; | ||
1168 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1169 | struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1170 | |||
1171 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | ||
1172 | if (ACPI_FAILURE(status)) | ||
1173 | return status; | ||
1174 | |||
1175 | obj = output.pointer; | ||
1176 | |||
1177 | if (!obj) | ||
1178 | return AE_ERROR; | ||
1179 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1180 | kfree(obj); | ||
1181 | return AE_ERROR; | ||
1182 | } | ||
1183 | if (obj->buffer.length != 8) { | ||
1184 | pr_warning("Unknown buffer length %d\n", obj->buffer.length); | ||
1185 | kfree(obj); | ||
1186 | return AE_ERROR; | ||
1187 | } | ||
1188 | |||
1189 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
1190 | kfree(obj); | ||
1191 | |||
1192 | if (return_value.error_code || return_value.ec_return_value) { | ||
1193 | pr_warning("Get Current Device Status failed: " | ||
1194 | "0x%x - 0x%x\n", return_value.error_code, | ||
1195 | return_value.ec_return_value); | ||
1196 | return status; | ||
1197 | } | ||
1198 | |||
1199 | devices = return_value.devices; | ||
1200 | params.function_num = 0x2; | ||
1201 | params.hotkey_number = 0x01; | ||
1202 | params.devices = (value) ? (devices | device) : (devices & ~device); | ||
1203 | |||
1204 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2); | ||
1205 | if (ACPI_FAILURE(status)) | ||
1206 | return status; | ||
1207 | |||
1208 | obj = output2.pointer; | ||
1209 | |||
1210 | if (!obj) | ||
1211 | return AE_ERROR; | ||
1212 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1213 | kfree(obj); | ||
1214 | return AE_ERROR; | ||
1215 | } | ||
1216 | if (obj->buffer.length != 4) { | ||
1217 | pr_warning("Unknown buffer length %d\n", obj->buffer.length); | ||
1218 | kfree(obj); | ||
1219 | return AE_ERROR; | ||
1220 | } | ||
1221 | |||
1222 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
1223 | kfree(obj); | ||
1224 | |||
1225 | if (return_value.error_code || return_value.ec_return_value) | ||
1226 | pr_warning("Set Device Status failed: " | ||
1227 | "0x%x - 0x%x\n", return_value.error_code, | ||
1228 | return_value.ec_return_value); | ||
1229 | |||
1230 | return status; | ||
1231 | } | ||
1232 | |||
1233 | static acpi_status set_device_status(u32 value, u32 cap) | ||
1234 | { | ||
1235 | if (wmi_has_guid(WMID_GUID3)) { | ||
1236 | u16 device; | ||
1237 | |||
1238 | switch (cap) { | ||
1239 | case ACER_CAP_WIRELESS: | ||
1240 | device = ACER_WMID3_GDS_WIRELESS; | ||
1241 | break; | ||
1242 | case ACER_CAP_BLUETOOTH: | ||
1243 | device = ACER_WMID3_GDS_BLUETOOTH; | ||
1244 | break; | ||
1245 | case ACER_CAP_THREEG: | ||
1246 | device = ACER_WMID3_GDS_THREEG; | ||
1247 | break; | ||
1248 | default: | ||
1249 | return AE_ERROR; | ||
1250 | } | ||
1251 | return wmid3_set_device_status(value, device); | ||
1252 | |||
1253 | } else { | ||
1254 | return set_u32(value, cap); | ||
1255 | } | ||
1256 | } | ||
1257 | |||
951 | /* | 1258 | /* |
952 | * Rfkill devices | 1259 | * Rfkill devices |
953 | */ | 1260 | */ |
@@ -968,6 +1275,13 @@ static void acer_rfkill_update(struct work_struct *ignored) | |||
968 | rfkill_set_sw_state(bluetooth_rfkill, !state); | 1275 | rfkill_set_sw_state(bluetooth_rfkill, !state); |
969 | } | 1276 | } |
970 | 1277 | ||
1278 | if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { | ||
1279 | status = wmid3_get_device_status(&state, | ||
1280 | ACER_WMID3_GDS_THREEG); | ||
1281 | if (ACPI_SUCCESS(status)) | ||
1282 | rfkill_set_sw_state(threeg_rfkill, !state); | ||
1283 | } | ||
1284 | |||
971 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); | 1285 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); |
972 | } | 1286 | } |
973 | 1287 | ||
@@ -975,9 +1289,13 @@ static int acer_rfkill_set(void *data, bool blocked) | |||
975 | { | 1289 | { |
976 | acpi_status status; | 1290 | acpi_status status; |
977 | u32 cap = (unsigned long)data; | 1291 | u32 cap = (unsigned long)data; |
978 | status = set_u32(!blocked, cap); | 1292 | |
979 | if (ACPI_FAILURE(status)) | 1293 | if (rfkill_inited) { |
980 | return -ENODEV; | 1294 | status = set_device_status(!blocked, cap); |
1295 | if (ACPI_FAILURE(status)) | ||
1296 | return -ENODEV; | ||
1297 | } | ||
1298 | |||
981 | return 0; | 1299 | return 0; |
982 | } | 1300 | } |
983 | 1301 | ||
@@ -991,6 +1309,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev, | |||
991 | { | 1309 | { |
992 | int err; | 1310 | int err; |
993 | struct rfkill *rfkill_dev; | 1311 | struct rfkill *rfkill_dev; |
1312 | u32 state; | ||
1313 | acpi_status status; | ||
994 | 1314 | ||
995 | rfkill_dev = rfkill_alloc(name, dev, type, | 1315 | rfkill_dev = rfkill_alloc(name, dev, type, |
996 | &acer_rfkill_ops, | 1316 | &acer_rfkill_ops, |
@@ -998,11 +1318,17 @@ static struct rfkill *acer_rfkill_register(struct device *dev, | |||
998 | if (!rfkill_dev) | 1318 | if (!rfkill_dev) |
999 | return ERR_PTR(-ENOMEM); | 1319 | return ERR_PTR(-ENOMEM); |
1000 | 1320 | ||
1321 | status = get_device_status(&state, cap); | ||
1322 | |||
1001 | err = rfkill_register(rfkill_dev); | 1323 | err = rfkill_register(rfkill_dev); |
1002 | if (err) { | 1324 | if (err) { |
1003 | rfkill_destroy(rfkill_dev); | 1325 | rfkill_destroy(rfkill_dev); |
1004 | return ERR_PTR(err); | 1326 | return ERR_PTR(err); |
1005 | } | 1327 | } |
1328 | |||
1329 | if (ACPI_SUCCESS(status)) | ||
1330 | rfkill_set_sw_state(rfkill_dev, !state); | ||
1331 | |||
1006 | return rfkill_dev; | 1332 | return rfkill_dev; |
1007 | } | 1333 | } |
1008 | 1334 | ||
@@ -1024,14 +1350,32 @@ static int acer_rfkill_init(struct device *dev) | |||
1024 | } | 1350 | } |
1025 | } | 1351 | } |
1026 | 1352 | ||
1027 | schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); | 1353 | if (has_cap(ACER_CAP_THREEG)) { |
1354 | threeg_rfkill = acer_rfkill_register(dev, | ||
1355 | RFKILL_TYPE_WWAN, "acer-threeg", | ||
1356 | ACER_CAP_THREEG); | ||
1357 | if (IS_ERR(threeg_rfkill)) { | ||
1358 | rfkill_unregister(wireless_rfkill); | ||
1359 | rfkill_destroy(wireless_rfkill); | ||
1360 | rfkill_unregister(bluetooth_rfkill); | ||
1361 | rfkill_destroy(bluetooth_rfkill); | ||
1362 | return PTR_ERR(threeg_rfkill); | ||
1363 | } | ||
1364 | } | ||
1365 | |||
1366 | rfkill_inited = true; | ||
1367 | |||
1368 | if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
1369 | schedule_delayed_work(&acer_rfkill_work, | ||
1370 | round_jiffies_relative(HZ)); | ||
1028 | 1371 | ||
1029 | return 0; | 1372 | return 0; |
1030 | } | 1373 | } |
1031 | 1374 | ||
1032 | static void acer_rfkill_exit(void) | 1375 | static void acer_rfkill_exit(void) |
1033 | { | 1376 | { |
1034 | cancel_delayed_work_sync(&acer_rfkill_work); | 1377 | if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) |
1378 | cancel_delayed_work_sync(&acer_rfkill_work); | ||
1035 | 1379 | ||
1036 | rfkill_unregister(wireless_rfkill); | 1380 | rfkill_unregister(wireless_rfkill); |
1037 | rfkill_destroy(wireless_rfkill); | 1381 | rfkill_destroy(wireless_rfkill); |
@@ -1040,6 +1384,11 @@ static void acer_rfkill_exit(void) | |||
1040 | rfkill_unregister(bluetooth_rfkill); | 1384 | rfkill_unregister(bluetooth_rfkill); |
1041 | rfkill_destroy(bluetooth_rfkill); | 1385 | rfkill_destroy(bluetooth_rfkill); |
1042 | } | 1386 | } |
1387 | |||
1388 | if (has_cap(ACER_CAP_THREEG)) { | ||
1389 | rfkill_unregister(threeg_rfkill); | ||
1390 | rfkill_destroy(threeg_rfkill); | ||
1391 | } | ||
1043 | return; | 1392 | return; |
1044 | } | 1393 | } |
1045 | 1394 | ||
@@ -1050,7 +1399,12 @@ static ssize_t show_bool_threeg(struct device *dev, | |||
1050 | struct device_attribute *attr, char *buf) | 1399 | struct device_attribute *attr, char *buf) |
1051 | { | 1400 | { |
1052 | u32 result; \ | 1401 | u32 result; \ |
1053 | acpi_status status = get_u32(&result, ACER_CAP_THREEG); | 1402 | acpi_status status; |
1403 | if (wmi_has_guid(WMID_GUID3)) | ||
1404 | status = wmid3_get_device_status(&result, | ||
1405 | ACER_WMID3_GDS_THREEG); | ||
1406 | else | ||
1407 | status = get_u32(&result, ACER_CAP_THREEG); | ||
1054 | if (ACPI_SUCCESS(status)) | 1408 | if (ACPI_SUCCESS(status)) |
1055 | return sprintf(buf, "%u\n", result); | 1409 | return sprintf(buf, "%u\n", result); |
1056 | return sprintf(buf, "Read error\n"); | 1410 | return sprintf(buf, "Read error\n"); |
@@ -1065,7 +1419,7 @@ static ssize_t set_bool_threeg(struct device *dev, | |||
1065 | return -EINVAL; | 1419 | return -EINVAL; |
1066 | return count; | 1420 | return count; |
1067 | } | 1421 | } |
1068 | static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg, | 1422 | static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg, |
1069 | set_bool_threeg); | 1423 | set_bool_threeg); |
1070 | 1424 | ||
1071 | static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | 1425 | static ssize_t show_interface(struct device *dev, struct device_attribute *attr, |
@@ -1085,6 +1439,199 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | |||
1085 | 1439 | ||
1086 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); | 1440 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); |
1087 | 1441 | ||
1442 | static void acer_wmi_notify(u32 value, void *context) | ||
1443 | { | ||
1444 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1445 | union acpi_object *obj; | ||
1446 | struct event_return_value return_value; | ||
1447 | acpi_status status; | ||
1448 | u16 device_state; | ||
1449 | const struct key_entry *key; | ||
1450 | |||
1451 | status = wmi_get_event_data(value, &response); | ||
1452 | if (status != AE_OK) { | ||
1453 | pr_warn("bad event status 0x%x\n", status); | ||
1454 | return; | ||
1455 | } | ||
1456 | |||
1457 | obj = (union acpi_object *)response.pointer; | ||
1458 | |||
1459 | if (!obj) | ||
1460 | return; | ||
1461 | if (obj->type != ACPI_TYPE_BUFFER) { | ||
1462 | pr_warn("Unknown response received %d\n", obj->type); | ||
1463 | kfree(obj); | ||
1464 | return; | ||
1465 | } | ||
1466 | if (obj->buffer.length != 8) { | ||
1467 | pr_warn("Unknown buffer length %d\n", obj->buffer.length); | ||
1468 | kfree(obj); | ||
1469 | return; | ||
1470 | } | ||
1471 | |||
1472 | return_value = *((struct event_return_value *)obj->buffer.pointer); | ||
1473 | kfree(obj); | ||
1474 | |||
1475 | switch (return_value.function) { | ||
1476 | case WMID_HOTKEY_EVENT: | ||
1477 | device_state = return_value.device_state; | ||
1478 | pr_debug("device state: 0x%x\n", device_state); | ||
1479 | |||
1480 | key = sparse_keymap_entry_from_scancode(acer_wmi_input_dev, | ||
1481 | return_value.key_num); | ||
1482 | if (!key) { | ||
1483 | pr_warn("Unknown key number - 0x%x\n", | ||
1484 | return_value.key_num); | ||
1485 | } else { | ||
1486 | switch (key->keycode) { | ||
1487 | case KEY_WLAN: | ||
1488 | case KEY_BLUETOOTH: | ||
1489 | if (has_cap(ACER_CAP_WIRELESS)) | ||
1490 | rfkill_set_sw_state(wireless_rfkill, | ||
1491 | !(device_state & ACER_WMID3_GDS_WIRELESS)); | ||
1492 | if (has_cap(ACER_CAP_THREEG)) | ||
1493 | rfkill_set_sw_state(threeg_rfkill, | ||
1494 | !(device_state & ACER_WMID3_GDS_THREEG)); | ||
1495 | if (has_cap(ACER_CAP_BLUETOOTH)) | ||
1496 | rfkill_set_sw_state(bluetooth_rfkill, | ||
1497 | !(device_state & ACER_WMID3_GDS_BLUETOOTH)); | ||
1498 | break; | ||
1499 | } | ||
1500 | sparse_keymap_report_entry(acer_wmi_input_dev, key, | ||
1501 | 1, true); | ||
1502 | } | ||
1503 | break; | ||
1504 | default: | ||
1505 | pr_warn("Unknown function number - %d - %d\n", | ||
1506 | return_value.function, return_value.key_num); | ||
1507 | break; | ||
1508 | } | ||
1509 | } | ||
1510 | |||
1511 | static acpi_status | ||
1512 | wmid3_set_lm_mode(struct lm_input_params *params, | ||
1513 | struct lm_return_value *return_value) | ||
1514 | { | ||
1515 | acpi_status status; | ||
1516 | union acpi_object *obj; | ||
1517 | |||
1518 | struct acpi_buffer input = { sizeof(struct lm_input_params), params }; | ||
1519 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1520 | |||
1521 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); | ||
1522 | if (ACPI_FAILURE(status)) | ||
1523 | return status; | ||
1524 | |||
1525 | obj = output.pointer; | ||
1526 | |||
1527 | if (!obj) | ||
1528 | return AE_ERROR; | ||
1529 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1530 | kfree(obj); | ||
1531 | return AE_ERROR; | ||
1532 | } | ||
1533 | if (obj->buffer.length != 4) { | ||
1534 | pr_warn("Unknown buffer length %d\n", obj->buffer.length); | ||
1535 | kfree(obj); | ||
1536 | return AE_ERROR; | ||
1537 | } | ||
1538 | |||
1539 | *return_value = *((struct lm_return_value *)obj->buffer.pointer); | ||
1540 | kfree(obj); | ||
1541 | |||
1542 | return status; | ||
1543 | } | ||
1544 | |||
1545 | static int acer_wmi_enable_ec_raw(void) | ||
1546 | { | ||
1547 | struct lm_return_value return_value; | ||
1548 | acpi_status status; | ||
1549 | struct lm_input_params params = { | ||
1550 | .function_num = 0x1, | ||
1551 | .commun_devices = 0xFFFF, | ||
1552 | .devices = 0xFFFF, | ||
1553 | .lm_status = 0x00, /* Launch Manager Deactive */ | ||
1554 | }; | ||
1555 | |||
1556 | status = wmid3_set_lm_mode(¶ms, &return_value); | ||
1557 | |||
1558 | if (return_value.error_code || return_value.ec_return_value) | ||
1559 | pr_warn("Enabling EC raw mode failed: 0x%x - 0x%x\n", | ||
1560 | return_value.error_code, | ||
1561 | return_value.ec_return_value); | ||
1562 | else | ||
1563 | pr_info("Enabled EC raw mode\n"); | ||
1564 | |||
1565 | return status; | ||
1566 | } | ||
1567 | |||
1568 | static int acer_wmi_enable_lm(void) | ||
1569 | { | ||
1570 | struct lm_return_value return_value; | ||
1571 | acpi_status status; | ||
1572 | struct lm_input_params params = { | ||
1573 | .function_num = 0x1, | ||
1574 | .commun_devices = 0xFFFF, | ||
1575 | .devices = 0xFFFF, | ||
1576 | .lm_status = 0x01, /* Launch Manager Active */ | ||
1577 | }; | ||
1578 | |||
1579 | status = wmid3_set_lm_mode(¶ms, &return_value); | ||
1580 | |||
1581 | if (return_value.error_code || return_value.ec_return_value) | ||
1582 | pr_warn("Enabling Launch Manager failed: 0x%x - 0x%x\n", | ||
1583 | return_value.error_code, | ||
1584 | return_value.ec_return_value); | ||
1585 | |||
1586 | return status; | ||
1587 | } | ||
1588 | |||
1589 | static int __init acer_wmi_input_setup(void) | ||
1590 | { | ||
1591 | acpi_status status; | ||
1592 | int err; | ||
1593 | |||
1594 | acer_wmi_input_dev = input_allocate_device(); | ||
1595 | if (!acer_wmi_input_dev) | ||
1596 | return -ENOMEM; | ||
1597 | |||
1598 | acer_wmi_input_dev->name = "Acer WMI hotkeys"; | ||
1599 | acer_wmi_input_dev->phys = "wmi/input0"; | ||
1600 | acer_wmi_input_dev->id.bustype = BUS_HOST; | ||
1601 | |||
1602 | err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL); | ||
1603 | if (err) | ||
1604 | goto err_free_dev; | ||
1605 | |||
1606 | status = wmi_install_notify_handler(ACERWMID_EVENT_GUID, | ||
1607 | acer_wmi_notify, NULL); | ||
1608 | if (ACPI_FAILURE(status)) { | ||
1609 | err = -EIO; | ||
1610 | goto err_free_keymap; | ||
1611 | } | ||
1612 | |||
1613 | err = input_register_device(acer_wmi_input_dev); | ||
1614 | if (err) | ||
1615 | goto err_uninstall_notifier; | ||
1616 | |||
1617 | return 0; | ||
1618 | |||
1619 | err_uninstall_notifier: | ||
1620 | wmi_remove_notify_handler(ACERWMID_EVENT_GUID); | ||
1621 | err_free_keymap: | ||
1622 | sparse_keymap_free(acer_wmi_input_dev); | ||
1623 | err_free_dev: | ||
1624 | input_free_device(acer_wmi_input_dev); | ||
1625 | return err; | ||
1626 | } | ||
1627 | |||
1628 | static void acer_wmi_input_destroy(void) | ||
1629 | { | ||
1630 | wmi_remove_notify_handler(ACERWMID_EVENT_GUID); | ||
1631 | sparse_keymap_free(acer_wmi_input_dev); | ||
1632 | input_unregister_device(acer_wmi_input_dev); | ||
1633 | } | ||
1634 | |||
1088 | /* | 1635 | /* |
1089 | * debugfs functions | 1636 | * debugfs functions |
1090 | */ | 1637 | */ |
@@ -1101,8 +1648,11 @@ static u32 get_wmid_devices(void) | |||
1101 | 1648 | ||
1102 | obj = (union acpi_object *) out.pointer; | 1649 | obj = (union acpi_object *) out.pointer; |
1103 | if (obj && obj->type == ACPI_TYPE_BUFFER && | 1650 | if (obj && obj->type == ACPI_TYPE_BUFFER && |
1104 | obj->buffer.length == sizeof(u32)) { | 1651 | (obj->buffer.length == sizeof(u32) || |
1652 | obj->buffer.length == sizeof(u64))) { | ||
1105 | devices = *((u32 *) obj->buffer.pointer); | 1653 | devices = *((u32 *) obj->buffer.pointer); |
1654 | } else if (obj->type == ACPI_TYPE_INTEGER) { | ||
1655 | devices = (u32) obj->integer.value; | ||
1106 | } | 1656 | } |
1107 | 1657 | ||
1108 | kfree(out.pointer); | 1658 | kfree(out.pointer); |
@@ -1166,6 +1716,7 @@ pm_message_t state) | |||
1166 | 1716 | ||
1167 | if (has_cap(ACER_CAP_MAILLED)) { | 1717 | if (has_cap(ACER_CAP_MAILLED)) { |
1168 | get_u32(&value, ACER_CAP_MAILLED); | 1718 | get_u32(&value, ACER_CAP_MAILLED); |
1719 | set_u32(LED_OFF, ACER_CAP_MAILLED); | ||
1169 | data->mailled = value; | 1720 | data->mailled = value; |
1170 | } | 1721 | } |
1171 | 1722 | ||
@@ -1193,6 +1744,17 @@ static int acer_platform_resume(struct platform_device *device) | |||
1193 | return 0; | 1744 | return 0; |
1194 | } | 1745 | } |
1195 | 1746 | ||
1747 | static void acer_platform_shutdown(struct platform_device *device) | ||
1748 | { | ||
1749 | struct acer_data *data = &interface->data; | ||
1750 | |||
1751 | if (!data) | ||
1752 | return; | ||
1753 | |||
1754 | if (has_cap(ACER_CAP_MAILLED)) | ||
1755 | set_u32(LED_OFF, ACER_CAP_MAILLED); | ||
1756 | } | ||
1757 | |||
1196 | static struct platform_driver acer_platform_driver = { | 1758 | static struct platform_driver acer_platform_driver = { |
1197 | .driver = { | 1759 | .driver = { |
1198 | .name = "acer-wmi", | 1760 | .name = "acer-wmi", |
@@ -1202,6 +1764,7 @@ static struct platform_driver acer_platform_driver = { | |||
1202 | .remove = acer_platform_remove, | 1764 | .remove = acer_platform_remove, |
1203 | .suspend = acer_platform_suspend, | 1765 | .suspend = acer_platform_suspend, |
1204 | .resume = acer_platform_resume, | 1766 | .resume = acer_platform_resume, |
1767 | .shutdown = acer_platform_shutdown, | ||
1205 | }; | 1768 | }; |
1206 | 1769 | ||
1207 | static struct platform_device *acer_platform_device; | 1770 | static struct platform_device *acer_platform_device; |
@@ -1249,7 +1812,7 @@ static int create_debugfs(void) | |||
1249 | { | 1812 | { |
1250 | interface->debug.root = debugfs_create_dir("acer-wmi", NULL); | 1813 | interface->debug.root = debugfs_create_dir("acer-wmi", NULL); |
1251 | if (!interface->debug.root) { | 1814 | if (!interface->debug.root) { |
1252 | printk(ACER_ERR "Failed to create debugfs directory"); | 1815 | pr_err("Failed to create debugfs directory"); |
1253 | return -ENOMEM; | 1816 | return -ENOMEM; |
1254 | } | 1817 | } |
1255 | 1818 | ||
@@ -1270,11 +1833,10 @@ static int __init acer_wmi_init(void) | |||
1270 | { | 1833 | { |
1271 | int err; | 1834 | int err; |
1272 | 1835 | ||
1273 | printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n"); | 1836 | pr_info("Acer Laptop ACPI-WMI Extras\n"); |
1274 | 1837 | ||
1275 | if (dmi_check_system(acer_blacklist)) { | 1838 | if (dmi_check_system(acer_blacklist)) { |
1276 | printk(ACER_INFO "Blacklisted hardware detected - " | 1839 | pr_info("Blacklisted hardware detected - not loading\n"); |
1277 | "not loading\n"); | ||
1278 | return -ENODEV; | 1840 | return -ENODEV; |
1279 | } | 1841 | } |
1280 | 1842 | ||
@@ -1291,12 +1853,11 @@ static int __init acer_wmi_init(void) | |||
1291 | 1853 | ||
1292 | if (wmi_has_guid(WMID_GUID2) && interface) { | 1854 | if (wmi_has_guid(WMID_GUID2) && interface) { |
1293 | if (ACPI_FAILURE(WMID_set_capabilities())) { | 1855 | if (ACPI_FAILURE(WMID_set_capabilities())) { |
1294 | printk(ACER_ERR "Unable to detect available WMID " | 1856 | pr_err("Unable to detect available WMID devices\n"); |
1295 | "devices\n"); | ||
1296 | return -ENODEV; | 1857 | return -ENODEV; |
1297 | } | 1858 | } |
1298 | } else if (!wmi_has_guid(WMID_GUID2) && interface) { | 1859 | } else if (!wmi_has_guid(WMID_GUID2) && interface) { |
1299 | printk(ACER_ERR "No WMID device detection method found\n"); | 1860 | pr_err("No WMID device detection method found\n"); |
1300 | return -ENODEV; | 1861 | return -ENODEV; |
1301 | } | 1862 | } |
1302 | 1863 | ||
@@ -1304,8 +1865,7 @@ static int __init acer_wmi_init(void) | |||
1304 | interface = &AMW0_interface; | 1865 | interface = &AMW0_interface; |
1305 | 1866 | ||
1306 | if (ACPI_FAILURE(AMW0_set_capabilities())) { | 1867 | if (ACPI_FAILURE(AMW0_set_capabilities())) { |
1307 | printk(ACER_ERR "Unable to detect available AMW0 " | 1868 | pr_err("Unable to detect available AMW0 devices\n"); |
1308 | "devices\n"); | ||
1309 | return -ENODEV; | 1869 | return -ENODEV; |
1310 | } | 1870 | } |
1311 | } | 1871 | } |
@@ -1314,8 +1874,7 @@ static int __init acer_wmi_init(void) | |||
1314 | AMW0_find_mailled(); | 1874 | AMW0_find_mailled(); |
1315 | 1875 | ||
1316 | if (!interface) { | 1876 | if (!interface) { |
1317 | printk(ACER_ERR "No or unsupported WMI interface, unable to " | 1877 | pr_err("No or unsupported WMI interface, unable to load\n"); |
1318 | "load\n"); | ||
1319 | return -ENODEV; | 1878 | return -ENODEV; |
1320 | } | 1879 | } |
1321 | 1880 | ||
@@ -1323,13 +1882,33 @@ static int __init acer_wmi_init(void) | |||
1323 | 1882 | ||
1324 | if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { | 1883 | if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { |
1325 | interface->capability &= ~ACER_CAP_BRIGHTNESS; | 1884 | interface->capability &= ~ACER_CAP_BRIGHTNESS; |
1326 | printk(ACER_INFO "Brightness must be controlled by " | 1885 | pr_info("Brightness must be controlled by " |
1327 | "generic video driver\n"); | 1886 | "generic video driver\n"); |
1328 | } | 1887 | } |
1329 | 1888 | ||
1889 | if (wmi_has_guid(WMID_GUID3)) { | ||
1890 | if (ec_raw_mode) { | ||
1891 | if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { | ||
1892 | pr_err("Cannot enable EC raw mode\n"); | ||
1893 | return -ENODEV; | ||
1894 | } | ||
1895 | } else if (ACPI_FAILURE(acer_wmi_enable_lm())) { | ||
1896 | pr_err("Cannot enable Launch Manager mode\n"); | ||
1897 | return -ENODEV; | ||
1898 | } | ||
1899 | } else if (ec_raw_mode) { | ||
1900 | pr_info("No WMID EC raw mode enable method\n"); | ||
1901 | } | ||
1902 | |||
1903 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) { | ||
1904 | err = acer_wmi_input_setup(); | ||
1905 | if (err) | ||
1906 | return err; | ||
1907 | } | ||
1908 | |||
1330 | err = platform_driver_register(&acer_platform_driver); | 1909 | err = platform_driver_register(&acer_platform_driver); |
1331 | if (err) { | 1910 | if (err) { |
1332 | printk(ACER_ERR "Unable to register platform driver.\n"); | 1911 | pr_err("Unable to register platform driver.\n"); |
1333 | goto error_platform_register; | 1912 | goto error_platform_register; |
1334 | } | 1913 | } |
1335 | 1914 | ||
@@ -1368,17 +1947,23 @@ error_device_add: | |||
1368 | error_device_alloc: | 1947 | error_device_alloc: |
1369 | platform_driver_unregister(&acer_platform_driver); | 1948 | platform_driver_unregister(&acer_platform_driver); |
1370 | error_platform_register: | 1949 | error_platform_register: |
1950 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
1951 | acer_wmi_input_destroy(); | ||
1952 | |||
1371 | return err; | 1953 | return err; |
1372 | } | 1954 | } |
1373 | 1955 | ||
1374 | static void __exit acer_wmi_exit(void) | 1956 | static void __exit acer_wmi_exit(void) |
1375 | { | 1957 | { |
1958 | if (wmi_has_guid(ACERWMID_EVENT_GUID)) | ||
1959 | acer_wmi_input_destroy(); | ||
1960 | |||
1376 | remove_sysfs(acer_platform_device); | 1961 | remove_sysfs(acer_platform_device); |
1377 | remove_debugfs(); | 1962 | remove_debugfs(); |
1378 | platform_device_unregister(acer_platform_device); | 1963 | platform_device_unregister(acer_platform_device); |
1379 | platform_driver_unregister(&acer_platform_driver); | 1964 | platform_driver_unregister(&acer_platform_driver); |
1380 | 1965 | ||
1381 | printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n"); | 1966 | pr_info("Acer Laptop WMI Extras unloaded\n"); |
1382 | return; | 1967 | return; |
1383 | } | 1968 | } |
1384 | 1969 | ||
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 60f9cfcac93f..fca3489218b7 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c | |||
@@ -35,10 +35,8 @@ | |||
35 | 35 | ||
36 | #include <linux/kernel.h> | 36 | #include <linux/kernel.h> |
37 | #include <linux/module.h> | 37 | #include <linux/module.h> |
38 | #include <linux/fs.h> | ||
39 | #include <linux/dmi.h> | 38 | #include <linux/dmi.h> |
40 | #include <acpi/acpi_drivers.h> | 39 | #include <linux/acpi.h> |
41 | #include <linux/sched.h> | ||
42 | #include <linux/thermal.h> | 40 | #include <linux/thermal.h> |
43 | #include <linux/platform_device.h> | 41 | #include <linux/platform_device.h> |
44 | 42 | ||
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b756e07d41b4..d65df92e2acc 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -29,7 +29,7 @@ | |||
29 | * John Belmonte - ACPI code for Toshiba laptop was a good starting point. | 29 | * John Belmonte - ACPI code for Toshiba laptop was a good starting point. |
30 | * Eric Burghard - LED display support for W1N | 30 | * Eric Burghard - LED display support for W1N |
31 | * Josh Green - Light Sens support | 31 | * Josh Green - Light Sens support |
32 | * Thomas Tuttle - His first patch for led support was very helpfull | 32 | * Thomas Tuttle - His first patch for led support was very helpful |
33 | * Sam Lin - GPS support | 33 | * Sam Lin - GPS support |
34 | */ | 34 | */ |
35 | 35 | ||
@@ -50,6 +50,7 @@ | |||
50 | #include <linux/input/sparse-keymap.h> | 50 | #include <linux/input/sparse-keymap.h> |
51 | #include <linux/rfkill.h> | 51 | #include <linux/rfkill.h> |
52 | #include <linux/slab.h> | 52 | #include <linux/slab.h> |
53 | #include <linux/dmi.h> | ||
53 | #include <acpi/acpi_drivers.h> | 54 | #include <acpi/acpi_drivers.h> |
54 | #include <acpi/acpi_bus.h> | 55 | #include <acpi/acpi_bus.h> |
55 | 56 | ||
@@ -81,6 +82,8 @@ MODULE_PARM_DESC(wapf, "WAPF value"); | |||
81 | 82 | ||
82 | static int wlan_status = 1; | 83 | static int wlan_status = 1; |
83 | static int bluetooth_status = 1; | 84 | static int bluetooth_status = 1; |
85 | static int wimax_status = -1; | ||
86 | static int wwan_status = -1; | ||
84 | 87 | ||
85 | module_param(wlan_status, int, 0444); | 88 | module_param(wlan_status, int, 0444); |
86 | MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " | 89 | MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " |
@@ -92,6 +95,16 @@ MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " | |||
92 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | 95 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " |
93 | "default is 1"); | 96 | "default is 1"); |
94 | 97 | ||
98 | module_param(wimax_status, int, 0444); | ||
99 | MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot " | ||
100 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | ||
101 | "default is 1"); | ||
102 | |||
103 | module_param(wwan_status, int, 0444); | ||
104 | MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " | ||
105 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | ||
106 | "default is 1"); | ||
107 | |||
95 | /* | 108 | /* |
96 | * Some events we use, same for all Asus | 109 | * Some events we use, same for all Asus |
97 | */ | 110 | */ |
@@ -114,6 +127,8 @@ MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " | |||
114 | */ | 127 | */ |
115 | #define WL_RSTS 0x01 /* internal Wifi */ | 128 | #define WL_RSTS 0x01 /* internal Wifi */ |
116 | #define BT_RSTS 0x02 /* internal Bluetooth */ | 129 | #define BT_RSTS 0x02 /* internal Bluetooth */ |
130 | #define WM_RSTS 0x08 /* internal wimax */ | ||
131 | #define WW_RSTS 0x20 /* internal wwan */ | ||
117 | 132 | ||
118 | /* LED */ | 133 | /* LED */ |
119 | #define METHOD_MLED "MLED" | 134 | #define METHOD_MLED "MLED" |
@@ -132,52 +147,20 @@ MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " | |||
132 | */ | 147 | */ |
133 | #define METHOD_WLAN "WLED" | 148 | #define METHOD_WLAN "WLED" |
134 | #define METHOD_BLUETOOTH "BLED" | 149 | #define METHOD_BLUETOOTH "BLED" |
150 | |||
151 | /* WWAN and WIMAX */ | ||
152 | #define METHOD_WWAN "GSMC" | ||
153 | #define METHOD_WIMAX "WMXC" | ||
154 | |||
135 | #define METHOD_WL_STATUS "RSTS" | 155 | #define METHOD_WL_STATUS "RSTS" |
136 | 156 | ||
137 | /* Brightness */ | 157 | /* Brightness */ |
138 | #define METHOD_BRIGHTNESS_SET "SPLV" | 158 | #define METHOD_BRIGHTNESS_SET "SPLV" |
139 | #define METHOD_BRIGHTNESS_GET "GPLV" | 159 | #define METHOD_BRIGHTNESS_GET "GPLV" |
140 | 160 | ||
141 | /* Backlight */ | ||
142 | static acpi_handle lcd_switch_handle; | ||
143 | static char *lcd_switch_paths[] = { | ||
144 | "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ | ||
145 | "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */ | ||
146 | "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */ | ||
147 | "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */ | ||
148 | "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */ | ||
149 | "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */ | ||
150 | "\\_SB.PCI0.PX40.Q10", /* S1x */ | ||
151 | "\\Q10"}; /* A2x, L2D, L3D, M2E */ | ||
152 | |||
153 | /* Display */ | 161 | /* Display */ |
154 | #define METHOD_SWITCH_DISPLAY "SDSP" | 162 | #define METHOD_SWITCH_DISPLAY "SDSP" |
155 | 163 | ||
156 | static acpi_handle display_get_handle; | ||
157 | static char *display_get_paths[] = { | ||
158 | /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ | ||
159 | "\\_SB.PCI0.P0P1.VGA.GETD", | ||
160 | /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ | ||
161 | "\\_SB.PCI0.P0P2.VGA.GETD", | ||
162 | /* A6V A6Q */ | ||
163 | "\\_SB.PCI0.P0P3.VGA.GETD", | ||
164 | /* A6T, A6M */ | ||
165 | "\\_SB.PCI0.P0PA.VGA.GETD", | ||
166 | /* L3C */ | ||
167 | "\\_SB.PCI0.PCI1.VGAC.NMAP", | ||
168 | /* Z96F */ | ||
169 | "\\_SB.PCI0.VGA.GETD", | ||
170 | /* A2D */ | ||
171 | "\\ACTD", | ||
172 | /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ | ||
173 | "\\ADVG", | ||
174 | /* P30 */ | ||
175 | "\\DNXT", | ||
176 | /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ | ||
177 | "\\INFB", | ||
178 | /* A3F A6F A3N A3L M6N W3N W6A */ | ||
179 | "\\SSTE"}; | ||
180 | |||
181 | #define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */ | 164 | #define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */ |
182 | #define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */ | 165 | #define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */ |
183 | 166 | ||
@@ -227,7 +210,6 @@ struct asus_laptop { | |||
227 | 210 | ||
228 | int wireless_status; | 211 | int wireless_status; |
229 | bool have_rsts; | 212 | bool have_rsts; |
230 | int lcd_state; | ||
231 | 213 | ||
232 | struct rfkill *gps_rfkill; | 214 | struct rfkill *gps_rfkill; |
233 | 215 | ||
@@ -236,7 +218,6 @@ struct asus_laptop { | |||
236 | u8 light_level; /* light sensor level */ | 218 | u8 light_level; /* light sensor level */ |
237 | u8 light_switch; /* light sensor switch value */ | 219 | u8 light_switch; /* light sensor switch value */ |
238 | u16 event_count[128]; /* count for each event TODO make this better */ | 220 | u16 event_count[128]; /* count for each event TODO make this better */ |
239 | u16 *keycode_map; | ||
240 | }; | 221 | }; |
241 | 222 | ||
242 | static const struct key_entry asus_keymap[] = { | 223 | static const struct key_entry asus_keymap[] = { |
@@ -278,6 +259,7 @@ static const struct key_entry asus_keymap[] = { | |||
278 | {KE_KEY, 0x99, { KEY_PHONE } }, | 259 | {KE_KEY, 0x99, { KEY_PHONE } }, |
279 | {KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, | 260 | {KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, |
280 | {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, | 261 | {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, |
262 | {KE_KEY, 0xb5, { KEY_CALC } }, | ||
281 | {KE_END, 0}, | 263 | {KE_END, 0}, |
282 | }; | 264 | }; |
283 | 265 | ||
@@ -336,7 +318,7 @@ static int acpi_check_handle(acpi_handle handle, const char *method, | |||
336 | 318 | ||
337 | if (status != AE_OK) { | 319 | if (status != AE_OK) { |
338 | if (ret) | 320 | if (ret) |
339 | pr_warning("Error finding %s\n", method); | 321 | pr_warn("Error finding %s\n", method); |
340 | return -ENODEV; | 322 | return -ENODEV; |
341 | } | 323 | } |
342 | return 0; | 324 | return 0; |
@@ -401,7 +383,7 @@ static int asus_kled_lvl(struct asus_laptop *asus) | |||
401 | rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET, | 383 | rv = acpi_evaluate_integer(asus->handle, METHOD_KBD_LIGHT_GET, |
402 | ¶ms, &kblv); | 384 | ¶ms, &kblv); |
403 | if (ACPI_FAILURE(rv)) { | 385 | if (ACPI_FAILURE(rv)) { |
404 | pr_warning("Error reading kled level\n"); | 386 | pr_warn("Error reading kled level\n"); |
405 | return -ENODEV; | 387 | return -ENODEV; |
406 | } | 388 | } |
407 | return kblv; | 389 | return kblv; |
@@ -415,7 +397,7 @@ static int asus_kled_set(struct asus_laptop *asus, int kblv) | |||
415 | kblv = 0; | 397 | kblv = 0; |
416 | 398 | ||
417 | if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) { | 399 | if (write_acpi_int(asus->handle, METHOD_KBD_LIGHT_SET, kblv)) { |
418 | pr_warning("Keyboard LED display write failed\n"); | 400 | pr_warn("Keyboard LED display write failed\n"); |
419 | return -EINVAL; | 401 | return -EINVAL; |
420 | } | 402 | } |
421 | return 0; | 403 | return 0; |
@@ -540,48 +522,6 @@ error: | |||
540 | /* | 522 | /* |
541 | * Backlight device | 523 | * Backlight device |
542 | */ | 524 | */ |
543 | static int asus_lcd_status(struct asus_laptop *asus) | ||
544 | { | ||
545 | return asus->lcd_state; | ||
546 | } | ||
547 | |||
548 | static int asus_lcd_set(struct asus_laptop *asus, int value) | ||
549 | { | ||
550 | int lcd = 0; | ||
551 | acpi_status status = 0; | ||
552 | |||
553 | lcd = !!value; | ||
554 | |||
555 | if (lcd == asus_lcd_status(asus)) | ||
556 | return 0; | ||
557 | |||
558 | if (!lcd_switch_handle) | ||
559 | return -ENODEV; | ||
560 | |||
561 | status = acpi_evaluate_object(lcd_switch_handle, | ||
562 | NULL, NULL, NULL); | ||
563 | |||
564 | if (ACPI_FAILURE(status)) { | ||
565 | pr_warning("Error switching LCD\n"); | ||
566 | return -ENODEV; | ||
567 | } | ||
568 | |||
569 | asus->lcd_state = lcd; | ||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static void lcd_blank(struct asus_laptop *asus, int blank) | ||
574 | { | ||
575 | struct backlight_device *bd = asus->backlight_device; | ||
576 | |||
577 | asus->lcd_state = (blank == FB_BLANK_UNBLANK); | ||
578 | |||
579 | if (bd) { | ||
580 | bd->props.power = blank; | ||
581 | backlight_update_status(bd); | ||
582 | } | ||
583 | } | ||
584 | |||
585 | static int asus_read_brightness(struct backlight_device *bd) | 525 | static int asus_read_brightness(struct backlight_device *bd) |
586 | { | 526 | { |
587 | struct asus_laptop *asus = bl_get_data(bd); | 527 | struct asus_laptop *asus = bl_get_data(bd); |
@@ -591,7 +531,7 @@ static int asus_read_brightness(struct backlight_device *bd) | |||
591 | rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET, | 531 | rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET, |
592 | NULL, &value); | 532 | NULL, &value); |
593 | if (ACPI_FAILURE(rv)) | 533 | if (ACPI_FAILURE(rv)) |
594 | pr_warning("Error reading brightness\n"); | 534 | pr_warn("Error reading brightness\n"); |
595 | 535 | ||
596 | return value; | 536 | return value; |
597 | } | 537 | } |
@@ -601,7 +541,7 @@ static int asus_set_brightness(struct backlight_device *bd, int value) | |||
601 | struct asus_laptop *asus = bl_get_data(bd); | 541 | struct asus_laptop *asus = bl_get_data(bd); |
602 | 542 | ||
603 | if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) { | 543 | if (write_acpi_int(asus->handle, METHOD_BRIGHTNESS_SET, value)) { |
604 | pr_warning("Error changing brightness\n"); | 544 | pr_warn("Error changing brightness\n"); |
605 | return -EIO; | 545 | return -EIO; |
606 | } | 546 | } |
607 | return 0; | 547 | return 0; |
@@ -609,19 +549,12 @@ static int asus_set_brightness(struct backlight_device *bd, int value) | |||
609 | 549 | ||
610 | static int update_bl_status(struct backlight_device *bd) | 550 | static int update_bl_status(struct backlight_device *bd) |
611 | { | 551 | { |
612 | struct asus_laptop *asus = bl_get_data(bd); | ||
613 | int rv; | ||
614 | int value = bd->props.brightness; | 552 | int value = bd->props.brightness; |
615 | 553 | ||
616 | rv = asus_set_brightness(bd, value); | 554 | return asus_set_brightness(bd, value); |
617 | if (rv) | ||
618 | return rv; | ||
619 | |||
620 | value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; | ||
621 | return asus_lcd_set(asus, value); | ||
622 | } | 555 | } |
623 | 556 | ||
624 | static struct backlight_ops asusbl_ops = { | 557 | static const struct backlight_ops asusbl_ops = { |
625 | .get_brightness = asus_read_brightness, | 558 | .get_brightness = asus_read_brightness, |
626 | .update_status = update_bl_status, | 559 | .update_status = update_bl_status, |
627 | }; | 560 | }; |
@@ -639,29 +572,29 @@ static int asus_backlight_notify(struct asus_laptop *asus) | |||
639 | static int asus_backlight_init(struct asus_laptop *asus) | 572 | static int asus_backlight_init(struct asus_laptop *asus) |
640 | { | 573 | { |
641 | struct backlight_device *bd; | 574 | struct backlight_device *bd; |
642 | struct device *dev = &asus->platform_device->dev; | ||
643 | struct backlight_properties props; | 575 | struct backlight_properties props; |
644 | 576 | ||
645 | if (!acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) && | 577 | if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) || |
646 | !acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) && | 578 | acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL)) |
647 | lcd_switch_handle) { | 579 | return 0; |
648 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
649 | props.max_brightness = 15; | ||
650 | |||
651 | bd = backlight_device_register(ASUS_LAPTOP_FILE, dev, | ||
652 | asus, &asusbl_ops, &props); | ||
653 | if (IS_ERR(bd)) { | ||
654 | pr_err("Could not register asus backlight device\n"); | ||
655 | asus->backlight_device = NULL; | ||
656 | return PTR_ERR(bd); | ||
657 | } | ||
658 | |||
659 | asus->backlight_device = bd; | ||
660 | 580 | ||
661 | bd->props.power = FB_BLANK_UNBLANK; | 581 | memset(&props, 0, sizeof(struct backlight_properties)); |
662 | bd->props.brightness = asus_read_brightness(bd); | 582 | props.max_brightness = 15; |
663 | backlight_update_status(bd); | 583 | props.type = BACKLIGHT_PLATFORM; |
584 | |||
585 | bd = backlight_device_register(ASUS_LAPTOP_FILE, | ||
586 | &asus->platform_device->dev, asus, | ||
587 | &asusbl_ops, &props); | ||
588 | if (IS_ERR(bd)) { | ||
589 | pr_err("Could not register asus backlight device\n"); | ||
590 | asus->backlight_device = NULL; | ||
591 | return PTR_ERR(bd); | ||
664 | } | 592 | } |
593 | |||
594 | asus->backlight_device = bd; | ||
595 | bd->props.brightness = asus_read_brightness(bd); | ||
596 | bd->props.power = FB_BLANK_UNBLANK; | ||
597 | backlight_update_status(bd); | ||
665 | return 0; | 598 | return 0; |
666 | } | 599 | } |
667 | 600 | ||
@@ -797,7 +730,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr, | |||
797 | rv = parse_arg(buf, count, &value); | 730 | rv = parse_arg(buf, count, &value); |
798 | if (rv > 0) { | 731 | if (rv > 0) { |
799 | if (write_acpi_int(asus->handle, METHOD_LEDD, value)) { | 732 | if (write_acpi_int(asus->handle, METHOD_LEDD, value)) { |
800 | pr_warning("LED display write failed\n"); | 733 | pr_warn("LED display write failed\n"); |
801 | return -ENODEV; | 734 | return -ENODEV; |
802 | } | 735 | } |
803 | asus->ledd_status = (u32) value; | 736 | asus->ledd_status = (u32) value; |
@@ -819,7 +752,7 @@ static int asus_wireless_status(struct asus_laptop *asus, int mask) | |||
819 | rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS, | 752 | rv = acpi_evaluate_integer(asus->handle, METHOD_WL_STATUS, |
820 | NULL, &status); | 753 | NULL, &status); |
821 | if (ACPI_FAILURE(rv)) { | 754 | if (ACPI_FAILURE(rv)) { |
822 | pr_warning("Error reading Wireless status\n"); | 755 | pr_warn("Error reading Wireless status\n"); |
823 | return -EINVAL; | 756 | return -EINVAL; |
824 | } | 757 | } |
825 | return !!(status & mask); | 758 | return !!(status & mask); |
@@ -831,7 +764,7 @@ static int asus_wireless_status(struct asus_laptop *asus, int mask) | |||
831 | static int asus_wlan_set(struct asus_laptop *asus, int status) | 764 | static int asus_wlan_set(struct asus_laptop *asus, int status) |
832 | { | 765 | { |
833 | if (write_acpi_int(asus->handle, METHOD_WLAN, !!status)) { | 766 | if (write_acpi_int(asus->handle, METHOD_WLAN, !!status)) { |
834 | pr_warning("Error setting wlan status to %d", status); | 767 | pr_warn("Error setting wlan status to %d\n", status); |
835 | return -EIO; | 768 | return -EIO; |
836 | } | 769 | } |
837 | return 0; | 770 | return 0; |
@@ -859,7 +792,7 @@ static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, | |||
859 | static int asus_bluetooth_set(struct asus_laptop *asus, int status) | 792 | static int asus_bluetooth_set(struct asus_laptop *asus, int status) |
860 | { | 793 | { |
861 | if (write_acpi_int(asus->handle, METHOD_BLUETOOTH, !!status)) { | 794 | if (write_acpi_int(asus->handle, METHOD_BLUETOOTH, !!status)) { |
862 | pr_warning("Error setting bluetooth status to %d", status); | 795 | pr_warn("Error setting bluetooth status to %d\n", status); |
863 | return -EIO; | 796 | return -EIO; |
864 | } | 797 | } |
865 | return 0; | 798 | return 0; |
@@ -883,49 +816,72 @@ static ssize_t store_bluetooth(struct device *dev, | |||
883 | } | 816 | } |
884 | 817 | ||
885 | /* | 818 | /* |
886 | * Display | 819 | * Wimax |
887 | */ | 820 | */ |
888 | static void asus_set_display(struct asus_laptop *asus, int value) | 821 | static int asus_wimax_set(struct asus_laptop *asus, int status) |
889 | { | 822 | { |
890 | /* no sanity check needed for now */ | 823 | if (write_acpi_int(asus->handle, METHOD_WIMAX, !!status)) { |
891 | if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value)) | 824 | pr_warn("Error setting wimax status to %d\n", status); |
892 | pr_warning("Error setting display\n"); | 825 | return -EIO; |
893 | return; | 826 | } |
827 | return 0; | ||
894 | } | 828 | } |
895 | 829 | ||
896 | static int read_display(struct asus_laptop *asus) | 830 | static ssize_t show_wimax(struct device *dev, |
831 | struct device_attribute *attr, char *buf) | ||
897 | { | 832 | { |
898 | unsigned long long value = 0; | 833 | struct asus_laptop *asus = dev_get_drvdata(dev); |
899 | acpi_status rv = AE_OK; | ||
900 | 834 | ||
901 | /* | 835 | return sprintf(buf, "%d\n", asus_wireless_status(asus, WM_RSTS)); |
902 | * In most of the case, we know how to set the display, but sometime | 836 | } |
903 | * we can't read it | ||
904 | */ | ||
905 | if (display_get_handle) { | ||
906 | rv = acpi_evaluate_integer(display_get_handle, NULL, | ||
907 | NULL, &value); | ||
908 | if (ACPI_FAILURE(rv)) | ||
909 | pr_warning("Error reading display status\n"); | ||
910 | } | ||
911 | 837 | ||
912 | value &= 0x0F; /* needed for some models, shouldn't hurt others */ | 838 | static ssize_t store_wimax(struct device *dev, |
839 | struct device_attribute *attr, const char *buf, | ||
840 | size_t count) | ||
841 | { | ||
842 | struct asus_laptop *asus = dev_get_drvdata(dev); | ||
913 | 843 | ||
914 | return value; | 844 | return sysfs_acpi_set(asus, buf, count, METHOD_WIMAX); |
915 | } | 845 | } |
916 | 846 | ||
917 | /* | 847 | /* |
918 | * Now, *this* one could be more user-friendly, but so far, no-one has | 848 | * Wwan |
919 | * complained. The significance of bits is the same as in store_disp() | ||
920 | */ | 849 | */ |
921 | static ssize_t show_disp(struct device *dev, | 850 | static int asus_wwan_set(struct asus_laptop *asus, int status) |
922 | struct device_attribute *attr, char *buf) | 851 | { |
852 | if (write_acpi_int(asus->handle, METHOD_WWAN, !!status)) { | ||
853 | pr_warn("Error setting wwan status to %d\n", status); | ||
854 | return -EIO; | ||
855 | } | ||
856 | return 0; | ||
857 | } | ||
858 | |||
859 | static ssize_t show_wwan(struct device *dev, | ||
860 | struct device_attribute *attr, char *buf) | ||
923 | { | 861 | { |
924 | struct asus_laptop *asus = dev_get_drvdata(dev); | 862 | struct asus_laptop *asus = dev_get_drvdata(dev); |
925 | 863 | ||
926 | if (!display_get_handle) | 864 | return sprintf(buf, "%d\n", asus_wireless_status(asus, WW_RSTS)); |
927 | return -ENODEV; | 865 | } |
928 | return sprintf(buf, "%d\n", read_display(asus)); | 866 | |
867 | static ssize_t store_wwan(struct device *dev, | ||
868 | struct device_attribute *attr, const char *buf, | ||
869 | size_t count) | ||
870 | { | ||
871 | struct asus_laptop *asus = dev_get_drvdata(dev); | ||
872 | |||
873 | return sysfs_acpi_set(asus, buf, count, METHOD_WWAN); | ||
874 | } | ||
875 | |||
876 | /* | ||
877 | * Display | ||
878 | */ | ||
879 | static void asus_set_display(struct asus_laptop *asus, int value) | ||
880 | { | ||
881 | /* no sanity check needed for now */ | ||
882 | if (write_acpi_int(asus->handle, METHOD_SWITCH_DISPLAY, value)) | ||
883 | pr_warn("Error setting display\n"); | ||
884 | return; | ||
929 | } | 885 | } |
930 | 886 | ||
931 | /* | 887 | /* |
@@ -953,7 +909,7 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr, | |||
953 | static void asus_als_switch(struct asus_laptop *asus, int value) | 909 | static void asus_als_switch(struct asus_laptop *asus, int value) |
954 | { | 910 | { |
955 | if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) | 911 | if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) |
956 | pr_warning("Error setting light sensor switch\n"); | 912 | pr_warn("Error setting light sensor switch\n"); |
957 | asus->light_switch = value; | 913 | asus->light_switch = value; |
958 | } | 914 | } |
959 | 915 | ||
@@ -981,7 +937,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr, | |||
981 | static void asus_als_level(struct asus_laptop *asus, int value) | 937 | static void asus_als_level(struct asus_laptop *asus, int value) |
982 | { | 938 | { |
983 | if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value)) | 939 | if (write_acpi_int(asus->handle, METHOD_ALS_LEVEL, value)) |
984 | pr_warning("Error setting light sensor level\n"); | 940 | pr_warn("Error setting light sensor level\n"); |
985 | asus->light_level = value; | 941 | asus->light_level = value; |
986 | } | 942 | } |
987 | 943 | ||
@@ -1020,7 +976,7 @@ static int asus_gps_status(struct asus_laptop *asus) | |||
1020 | rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS, | 976 | rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS, |
1021 | NULL, &status); | 977 | NULL, &status); |
1022 | if (ACPI_FAILURE(rv)) { | 978 | if (ACPI_FAILURE(rv)) { |
1023 | pr_warning("Error reading GPS status\n"); | 979 | pr_warn("Error reading GPS status\n"); |
1024 | return -ENODEV; | 980 | return -ENODEV; |
1025 | } | 981 | } |
1026 | return !!status; | 982 | return !!status; |
@@ -1065,9 +1021,9 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr, | |||
1065 | */ | 1021 | */ |
1066 | static int asus_gps_rfkill_set(void *data, bool blocked) | 1022 | static int asus_gps_rfkill_set(void *data, bool blocked) |
1067 | { | 1023 | { |
1068 | acpi_handle handle = data; | 1024 | struct asus_laptop *asus = data; |
1069 | 1025 | ||
1070 | return asus_gps_switch(handle, !blocked); | 1026 | return asus_gps_switch(asus, !blocked); |
1071 | } | 1027 | } |
1072 | 1028 | ||
1073 | static const struct rfkill_ops asus_gps_rfkill_ops = { | 1029 | static const struct rfkill_ops asus_gps_rfkill_ops = { |
@@ -1094,7 +1050,7 @@ static int asus_rfkill_init(struct asus_laptop *asus) | |||
1094 | 1050 | ||
1095 | asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, | 1051 | asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, |
1096 | RFKILL_TYPE_GPS, | 1052 | RFKILL_TYPE_GPS, |
1097 | &asus_gps_rfkill_ops, NULL); | 1053 | &asus_gps_rfkill_ops, asus); |
1098 | if (!asus->gps_rfkill) | 1054 | if (!asus->gps_rfkill) |
1099 | return -EINVAL; | 1055 | return -EINVAL; |
1100 | 1056 | ||
@@ -1130,7 +1086,6 @@ static int asus_input_init(struct asus_laptop *asus) | |||
1130 | input->phys = ASUS_LAPTOP_FILE "/input0"; | 1086 | input->phys = ASUS_LAPTOP_FILE "/input0"; |
1131 | input->id.bustype = BUS_HOST; | 1087 | input->id.bustype = BUS_HOST; |
1132 | input->dev.parent = &asus->platform_device->dev; | 1088 | input->dev.parent = &asus->platform_device->dev; |
1133 | input_set_drvdata(input, asus); | ||
1134 | 1089 | ||
1135 | error = sparse_keymap_setup(input, asus_keymap, NULL); | 1090 | error = sparse_keymap_setup(input, asus_keymap, NULL); |
1136 | if (error) { | 1091 | if (error) { |
@@ -1159,6 +1114,7 @@ static void asus_input_exit(struct asus_laptop *asus) | |||
1159 | sparse_keymap_free(asus->inputdev); | 1114 | sparse_keymap_free(asus->inputdev); |
1160 | input_unregister_device(asus->inputdev); | 1115 | input_unregister_device(asus->inputdev); |
1161 | } | 1116 | } |
1117 | asus->inputdev = NULL; | ||
1162 | } | 1118 | } |
1163 | 1119 | ||
1164 | /* | 1120 | /* |
@@ -1169,15 +1125,6 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) | |||
1169 | struct asus_laptop *asus = acpi_driver_data(device); | 1125 | struct asus_laptop *asus = acpi_driver_data(device); |
1170 | u16 count; | 1126 | u16 count; |
1171 | 1127 | ||
1172 | /* | ||
1173 | * We need to tell the backlight device when the backlight power is | ||
1174 | * switched | ||
1175 | */ | ||
1176 | if (event == ATKD_LCD_ON) | ||
1177 | lcd_blank(asus, FB_BLANK_UNBLANK); | ||
1178 | else if (event == ATKD_LCD_OFF) | ||
1179 | lcd_blank(asus, FB_BLANK_POWERDOWN); | ||
1180 | |||
1181 | /* TODO Find a better way to handle events count. */ | 1128 | /* TODO Find a better way to handle events count. */ |
1182 | count = asus->event_count[event % 128]++; | 1129 | count = asus->event_count[event % 128]++; |
1183 | acpi_bus_generate_proc_event(asus->device, event, count); | 1130 | acpi_bus_generate_proc_event(asus->device, event, count); |
@@ -1200,111 +1147,111 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) | |||
1200 | 1147 | ||
1201 | static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL); | 1148 | static DEVICE_ATTR(infos, S_IRUGO, show_infos, NULL); |
1202 | static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan); | 1149 | static DEVICE_ATTR(wlan, S_IRUGO | S_IWUSR, show_wlan, store_wlan); |
1203 | static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth, | 1150 | static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, |
1204 | store_bluetooth); | 1151 | show_bluetooth, store_bluetooth); |
1205 | static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); | 1152 | static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax); |
1153 | static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan); | ||
1154 | static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp); | ||
1206 | static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); | 1155 | static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); |
1207 | static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); | 1156 | static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); |
1208 | static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); | 1157 | static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); |
1209 | static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); | 1158 | static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); |
1210 | 1159 | ||
1211 | static void asus_sysfs_exit(struct asus_laptop *asus) | 1160 | static struct attribute *asus_attributes[] = { |
1212 | { | 1161 | &dev_attr_infos.attr, |
1213 | struct platform_device *device = asus->platform_device; | 1162 | &dev_attr_wlan.attr, |
1214 | 1163 | &dev_attr_bluetooth.attr, | |
1215 | device_remove_file(&device->dev, &dev_attr_infos); | 1164 | &dev_attr_wimax.attr, |
1216 | device_remove_file(&device->dev, &dev_attr_wlan); | 1165 | &dev_attr_wwan.attr, |
1217 | device_remove_file(&device->dev, &dev_attr_bluetooth); | 1166 | &dev_attr_display.attr, |
1218 | device_remove_file(&device->dev, &dev_attr_display); | 1167 | &dev_attr_ledd.attr, |
1219 | device_remove_file(&device->dev, &dev_attr_ledd); | 1168 | &dev_attr_ls_level.attr, |
1220 | device_remove_file(&device->dev, &dev_attr_ls_switch); | 1169 | &dev_attr_ls_switch.attr, |
1221 | device_remove_file(&device->dev, &dev_attr_ls_level); | 1170 | &dev_attr_gps.attr, |
1222 | device_remove_file(&device->dev, &dev_attr_gps); | 1171 | NULL |
1223 | } | 1172 | }; |
1224 | 1173 | ||
1225 | static int asus_sysfs_init(struct asus_laptop *asus) | 1174 | static mode_t asus_sysfs_is_visible(struct kobject *kobj, |
1175 | struct attribute *attr, | ||
1176 | int idx) | ||
1226 | { | 1177 | { |
1227 | struct platform_device *device = asus->platform_device; | 1178 | struct device *dev = container_of(kobj, struct device, kobj); |
1228 | int err; | 1179 | struct platform_device *pdev = to_platform_device(dev); |
1180 | struct asus_laptop *asus = platform_get_drvdata(pdev); | ||
1181 | acpi_handle handle = asus->handle; | ||
1182 | bool supported; | ||
1229 | 1183 | ||
1230 | err = device_create_file(&device->dev, &dev_attr_infos); | 1184 | if (attr == &dev_attr_wlan.attr) { |
1231 | if (err) | 1185 | supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); |
1232 | return err; | ||
1233 | 1186 | ||
1234 | if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL)) { | 1187 | } else if (attr == &dev_attr_bluetooth.attr) { |
1235 | err = device_create_file(&device->dev, &dev_attr_wlan); | 1188 | supported = !acpi_check_handle(handle, METHOD_BLUETOOTH, NULL); |
1236 | if (err) | ||
1237 | return err; | ||
1238 | } | ||
1239 | 1189 | ||
1240 | if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL)) { | 1190 | } else if (attr == &dev_attr_display.attr) { |
1241 | err = device_create_file(&device->dev, &dev_attr_bluetooth); | 1191 | supported = !acpi_check_handle(handle, METHOD_SWITCH_DISPLAY, NULL); |
1242 | if (err) | ||
1243 | return err; | ||
1244 | } | ||
1245 | 1192 | ||
1246 | if (!acpi_check_handle(asus->handle, METHOD_SWITCH_DISPLAY, NULL)) { | 1193 | } else if (attr == &dev_attr_wimax.attr) { |
1247 | err = device_create_file(&device->dev, &dev_attr_display); | 1194 | supported = |
1248 | if (err) | 1195 | !acpi_check_handle(asus->handle, METHOD_WIMAX, NULL); |
1249 | return err; | ||
1250 | } | ||
1251 | 1196 | ||
1252 | if (!acpi_check_handle(asus->handle, METHOD_LEDD, NULL)) { | 1197 | } else if (attr == &dev_attr_wwan.attr) { |
1253 | err = device_create_file(&device->dev, &dev_attr_ledd); | 1198 | supported = !acpi_check_handle(asus->handle, METHOD_WWAN, NULL); |
1254 | if (err) | ||
1255 | return err; | ||
1256 | } | ||
1257 | 1199 | ||
1258 | if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && | 1200 | } else if (attr == &dev_attr_ledd.attr) { |
1259 | !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { | 1201 | supported = !acpi_check_handle(handle, METHOD_LEDD, NULL); |
1260 | err = device_create_file(&device->dev, &dev_attr_ls_switch); | 1202 | |
1261 | if (err) | 1203 | } else if (attr == &dev_attr_ls_switch.attr || |
1262 | return err; | 1204 | attr == &dev_attr_ls_level.attr) { |
1263 | err = device_create_file(&device->dev, &dev_attr_ls_level); | 1205 | supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && |
1264 | if (err) | 1206 | !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); |
1265 | return err; | ||
1266 | } | ||
1267 | 1207 | ||
1268 | if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && | 1208 | } else if (attr == &dev_attr_gps.attr) { |
1269 | !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && | 1209 | supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && |
1270 | !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) { | 1210 | !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && |
1271 | err = device_create_file(&device->dev, &dev_attr_gps); | 1211 | !acpi_check_handle(handle, METHOD_GPS_STATUS, NULL); |
1272 | if (err) | 1212 | } else { |
1273 | return err; | 1213 | supported = true; |
1274 | } | 1214 | } |
1275 | 1215 | ||
1276 | return err; | 1216 | return supported ? attr->mode : 0; |
1277 | } | 1217 | } |
1278 | 1218 | ||
1219 | |||
1220 | static const struct attribute_group asus_attr_group = { | ||
1221 | .is_visible = asus_sysfs_is_visible, | ||
1222 | .attrs = asus_attributes, | ||
1223 | }; | ||
1224 | |||
1279 | static int asus_platform_init(struct asus_laptop *asus) | 1225 | static int asus_platform_init(struct asus_laptop *asus) |
1280 | { | 1226 | { |
1281 | int err; | 1227 | int result; |
1282 | 1228 | ||
1283 | asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); | 1229 | asus->platform_device = platform_device_alloc(ASUS_LAPTOP_FILE, -1); |
1284 | if (!asus->platform_device) | 1230 | if (!asus->platform_device) |
1285 | return -ENOMEM; | 1231 | return -ENOMEM; |
1286 | platform_set_drvdata(asus->platform_device, asus); | 1232 | platform_set_drvdata(asus->platform_device, asus); |
1287 | 1233 | ||
1288 | err = platform_device_add(asus->platform_device); | 1234 | result = platform_device_add(asus->platform_device); |
1289 | if (err) | 1235 | if (result) |
1290 | goto fail_platform_device; | 1236 | goto fail_platform_device; |
1291 | 1237 | ||
1292 | err = asus_sysfs_init(asus); | 1238 | result = sysfs_create_group(&asus->platform_device->dev.kobj, |
1293 | if (err) | 1239 | &asus_attr_group); |
1240 | if (result) | ||
1294 | goto fail_sysfs; | 1241 | goto fail_sysfs; |
1242 | |||
1295 | return 0; | 1243 | return 0; |
1296 | 1244 | ||
1297 | fail_sysfs: | 1245 | fail_sysfs: |
1298 | asus_sysfs_exit(asus); | ||
1299 | platform_device_del(asus->platform_device); | 1246 | platform_device_del(asus->platform_device); |
1300 | fail_platform_device: | 1247 | fail_platform_device: |
1301 | platform_device_put(asus->platform_device); | 1248 | platform_device_put(asus->platform_device); |
1302 | return err; | 1249 | return result; |
1303 | } | 1250 | } |
1304 | 1251 | ||
1305 | static void asus_platform_exit(struct asus_laptop *asus) | 1252 | static void asus_platform_exit(struct asus_laptop *asus) |
1306 | { | 1253 | { |
1307 | asus_sysfs_exit(asus); | 1254 | sysfs_remove_group(&asus->platform_device->dev.kobj, &asus_attr_group); |
1308 | platform_device_unregister(asus->platform_device); | 1255 | platform_device_unregister(asus->platform_device); |
1309 | } | 1256 | } |
1310 | 1257 | ||
@@ -1315,26 +1262,6 @@ static struct platform_driver platform_driver = { | |||
1315 | } | 1262 | } |
1316 | }; | 1263 | }; |
1317 | 1264 | ||
1318 | static int asus_handle_init(char *name, acpi_handle * handle, | ||
1319 | char **paths, int num_paths) | ||
1320 | { | ||
1321 | int i; | ||
1322 | acpi_status status; | ||
1323 | |||
1324 | for (i = 0; i < num_paths; i++) { | ||
1325 | status = acpi_get_handle(NULL, paths[i], handle); | ||
1326 | if (ACPI_SUCCESS(status)) | ||
1327 | return 0; | ||
1328 | } | ||
1329 | |||
1330 | *handle = NULL; | ||
1331 | return -ENODEV; | ||
1332 | } | ||
1333 | |||
1334 | #define ASUS_HANDLE_INIT(object) \ | ||
1335 | asus_handle_init(#object, &object##_handle, object##_paths, \ | ||
1336 | ARRAY_SIZE(object##_paths)) | ||
1337 | |||
1338 | /* | 1265 | /* |
1339 | * This function is used to initialize the context with right values. In this | 1266 | * This function is used to initialize the context with right values. In this |
1340 | * method, we can make all the detection we want, and modify the asus_laptop | 1267 | * method, we can make all the detection we want, and modify the asus_laptop |
@@ -1357,7 +1284,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus) | |||
1357 | */ | 1284 | */ |
1358 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info); | 1285 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus->dsdt_info); |
1359 | if (ACPI_FAILURE(status)) | 1286 | if (ACPI_FAILURE(status)) |
1360 | pr_warning("Couldn't get the DSDT table header\n"); | 1287 | pr_warn("Couldn't get the DSDT table header\n"); |
1361 | 1288 | ||
1362 | /* We have to write 0 on init this far for all ASUS models */ | 1289 | /* We have to write 0 on init this far for all ASUS models */ |
1363 | if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) { | 1290 | if (write_acpi_int_ret(asus->handle, "INIT", 0, &buffer)) { |
@@ -1369,7 +1296,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus) | |||
1369 | status = | 1296 | status = |
1370 | acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result); | 1297 | acpi_evaluate_integer(asus->handle, "BSTS", NULL, &bsts_result); |
1371 | if (ACPI_FAILURE(status)) | 1298 | if (ACPI_FAILURE(status)) |
1372 | pr_warning("Error calling BSTS\n"); | 1299 | pr_warn("Error calling BSTS\n"); |
1373 | else if (bsts_result) | 1300 | else if (bsts_result) |
1374 | pr_notice("BSTS called, 0x%02x returned\n", | 1301 | pr_notice("BSTS called, 0x%02x returned\n", |
1375 | (uint) bsts_result); | 1302 | (uint) bsts_result); |
@@ -1408,7 +1335,8 @@ static int asus_laptop_get_info(struct asus_laptop *asus) | |||
1408 | 1335 | ||
1409 | /* | 1336 | /* |
1410 | * The HWRS method return informations about the hardware. | 1337 | * The HWRS method return informations about the hardware. |
1411 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. | 1338 | * 0x80 bit is for WLAN, 0x100 for Bluetooth, |
1339 | * 0x40 for WWAN, 0x10 for WIMAX. | ||
1412 | * The significance of others is yet to be found. | 1340 | * The significance of others is yet to be found. |
1413 | */ | 1341 | */ |
1414 | status = | 1342 | status = |
@@ -1419,17 +1347,11 @@ static int asus_laptop_get_info(struct asus_laptop *asus) | |||
1419 | if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL)) | 1347 | if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL)) |
1420 | asus->have_rsts = true; | 1348 | asus->have_rsts = true; |
1421 | 1349 | ||
1422 | /* Scheduled for removal */ | ||
1423 | ASUS_HANDLE_INIT(lcd_switch); | ||
1424 | ASUS_HANDLE_INIT(display_get); | ||
1425 | |||
1426 | kfree(model); | 1350 | kfree(model); |
1427 | 1351 | ||
1428 | return AE_OK; | 1352 | return AE_OK; |
1429 | } | 1353 | } |
1430 | 1354 | ||
1431 | static bool asus_device_present; | ||
1432 | |||
1433 | static int __devinit asus_acpi_init(struct asus_laptop *asus) | 1355 | static int __devinit asus_acpi_init(struct asus_laptop *asus) |
1434 | { | 1356 | { |
1435 | int result = 0; | 1357 | int result = 0; |
@@ -1453,6 +1375,12 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) | |||
1453 | if (wlan_status >= 0) | 1375 | if (wlan_status >= 0) |
1454 | asus_wlan_set(asus, !!wlan_status); | 1376 | asus_wlan_set(asus, !!wlan_status); |
1455 | 1377 | ||
1378 | if (wimax_status >= 0) | ||
1379 | asus_wimax_set(asus, !!wimax_status); | ||
1380 | |||
1381 | if (wwan_status >= 0) | ||
1382 | asus_wwan_set(asus, !!wwan_status); | ||
1383 | |||
1456 | /* Keyboard Backlight is on by default */ | 1384 | /* Keyboard Backlight is on by default */ |
1457 | if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL)) | 1385 | if (!acpi_check_handle(asus->handle, METHOD_KBD_LIGHT_SET, NULL)) |
1458 | asus_kled_set(asus, 1); | 1386 | asus_kled_set(asus, 1); |
@@ -1470,10 +1398,25 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) | |||
1470 | asus_als_level(asus, asus->light_level); | 1398 | asus_als_level(asus, asus->light_level); |
1471 | } | 1399 | } |
1472 | 1400 | ||
1473 | asus->lcd_state = 1; /* LCD should be on when the module load */ | ||
1474 | return result; | 1401 | return result; |
1475 | } | 1402 | } |
1476 | 1403 | ||
1404 | static void __devinit asus_dmi_check(void) | ||
1405 | { | ||
1406 | const char *model; | ||
1407 | |||
1408 | model = dmi_get_system_info(DMI_PRODUCT_NAME); | ||
1409 | if (!model) | ||
1410 | return; | ||
1411 | |||
1412 | /* On L1400B WLED control the sound card, don't mess with it ... */ | ||
1413 | if (strncmp(model, "L1400B", 6) == 0) { | ||
1414 | wlan_status = -1; | ||
1415 | } | ||
1416 | } | ||
1417 | |||
1418 | static bool asus_device_present; | ||
1419 | |||
1477 | static int __devinit asus_acpi_add(struct acpi_device *device) | 1420 | static int __devinit asus_acpi_add(struct acpi_device *device) |
1478 | { | 1421 | { |
1479 | struct asus_laptop *asus; | 1422 | struct asus_laptop *asus; |
@@ -1490,6 +1433,8 @@ static int __devinit asus_acpi_add(struct acpi_device *device) | |||
1490 | device->driver_data = asus; | 1433 | device->driver_data = asus; |
1491 | asus->device = device; | 1434 | asus->device = device; |
1492 | 1435 | ||
1436 | asus_dmi_check(); | ||
1437 | |||
1493 | result = asus_acpi_init(asus); | 1438 | result = asus_acpi_init(asus); |
1494 | if (result) | 1439 | if (result) |
1495 | goto fail_platform; | 1440 | goto fail_platform; |
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c new file mode 100644 index 000000000000..0580d99b0798 --- /dev/null +++ b/drivers/platform/x86/asus-nb-wmi.c | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * Asus Notebooks WMI hotkey driver | ||
3 | * | ||
4 | * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/input.h> | ||
27 | #include <linux/input/sparse-keymap.h> | ||
28 | |||
29 | #include "asus-wmi.h" | ||
30 | |||
31 | #define ASUS_NB_WMI_FILE "asus-nb-wmi" | ||
32 | |||
33 | MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>"); | ||
34 | MODULE_DESCRIPTION("Asus Notebooks WMI Hotkey Driver"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | #define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" | ||
38 | |||
39 | MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID); | ||
40 | |||
41 | static const struct key_entry asus_nb_wmi_keymap[] = { | ||
42 | { KE_KEY, 0x30, { KEY_VOLUMEUP } }, | ||
43 | { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, | ||
44 | { KE_KEY, 0x32, { KEY_MUTE } }, | ||
45 | { KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */ | ||
46 | { KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */ | ||
47 | { KE_KEY, 0x40, { KEY_PREVIOUSSONG } }, | ||
48 | { KE_KEY, 0x41, { KEY_NEXTSONG } }, | ||
49 | { KE_KEY, 0x43, { KEY_STOPCD } }, | ||
50 | { KE_KEY, 0x45, { KEY_PLAYPAUSE } }, | ||
51 | { KE_KEY, 0x4c, { KEY_MEDIA } }, | ||
52 | { KE_KEY, 0x50, { KEY_EMAIL } }, | ||
53 | { KE_KEY, 0x51, { KEY_WWW } }, | ||
54 | { KE_KEY, 0x55, { KEY_CALC } }, | ||
55 | { KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */ | ||
56 | { KE_KEY, 0x5D, { KEY_WLAN } }, | ||
57 | { KE_KEY, 0x5E, { KEY_WLAN } }, | ||
58 | { KE_KEY, 0x5F, { KEY_WLAN } }, | ||
59 | { KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, | ||
60 | { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, | ||
61 | { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, | ||
62 | { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, | ||
63 | { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, | ||
64 | { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, | ||
65 | { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, | ||
66 | { KE_KEY, 0x82, { KEY_CAMERA } }, | ||
67 | { KE_KEY, 0x88, { KEY_RFKILL } }, | ||
68 | { KE_KEY, 0x8A, { KEY_PROG1 } }, | ||
69 | { KE_KEY, 0x95, { KEY_MEDIA } }, | ||
70 | { KE_KEY, 0x99, { KEY_PHONE } }, | ||
71 | { KE_KEY, 0xb5, { KEY_CALC } }, | ||
72 | { KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, | ||
73 | { KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, | ||
74 | { KE_END, 0}, | ||
75 | }; | ||
76 | |||
77 | static struct asus_wmi_driver asus_nb_wmi_driver = { | ||
78 | .name = ASUS_NB_WMI_FILE, | ||
79 | .owner = THIS_MODULE, | ||
80 | .event_guid = ASUS_NB_WMI_EVENT_GUID, | ||
81 | .keymap = asus_nb_wmi_keymap, | ||
82 | .input_name = "Asus WMI hotkeys", | ||
83 | .input_phys = ASUS_NB_WMI_FILE "/input0", | ||
84 | }; | ||
85 | |||
86 | |||
87 | static int __init asus_nb_wmi_init(void) | ||
88 | { | ||
89 | return asus_wmi_register_driver(&asus_nb_wmi_driver); | ||
90 | } | ||
91 | |||
92 | static void __exit asus_nb_wmi_exit(void) | ||
93 | { | ||
94 | asus_wmi_unregister_driver(&asus_nb_wmi_driver); | ||
95 | } | ||
96 | |||
97 | module_init(asus_nb_wmi_init); | ||
98 | module_exit(asus_nb_wmi_exit); | ||
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c new file mode 100644 index 000000000000..3c7857c71a23 --- /dev/null +++ b/drivers/platform/x86/asus-wmi.c | |||
@@ -0,0 +1,1657 @@ | |||
1 | /* | ||
2 | * Asus PC WMI hotkey driver | ||
3 | * | ||
4 | * Copyright(C) 2010 Intel Corporation. | ||
5 | * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com> | ||
6 | * | ||
7 | * Portions based on wistron_btns.c: | ||
8 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | ||
9 | * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> | ||
10 | * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | */ | ||
26 | |||
27 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
28 | |||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/input.h> | ||
35 | #include <linux/input/sparse-keymap.h> | ||
36 | #include <linux/fb.h> | ||
37 | #include <linux/backlight.h> | ||
38 | #include <linux/leds.h> | ||
39 | #include <linux/rfkill.h> | ||
40 | #include <linux/pci.h> | ||
41 | #include <linux/pci_hotplug.h> | ||
42 | #include <linux/hwmon.h> | ||
43 | #include <linux/hwmon-sysfs.h> | ||
44 | #include <linux/debugfs.h> | ||
45 | #include <linux/seq_file.h> | ||
46 | #include <linux/platform_device.h> | ||
47 | #include <acpi/acpi_bus.h> | ||
48 | #include <acpi/acpi_drivers.h> | ||
49 | |||
50 | #include "asus-wmi.h" | ||
51 | |||
52 | MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>, " | ||
53 | "Yong Wang <yong.y.wang@intel.com>"); | ||
54 | MODULE_DESCRIPTION("Asus Generic WMI Driver"); | ||
55 | MODULE_LICENSE("GPL"); | ||
56 | |||
57 | #define to_platform_driver(drv) \ | ||
58 | (container_of((drv), struct platform_driver, driver)) | ||
59 | |||
60 | #define to_asus_wmi_driver(pdrv) \ | ||
61 | (container_of((pdrv), struct asus_wmi_driver, platform_driver)) | ||
62 | |||
63 | #define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" | ||
64 | |||
65 | #define NOTIFY_BRNUP_MIN 0x11 | ||
66 | #define NOTIFY_BRNUP_MAX 0x1f | ||
67 | #define NOTIFY_BRNDOWN_MIN 0x20 | ||
68 | #define NOTIFY_BRNDOWN_MAX 0x2e | ||
69 | |||
70 | /* WMI Methods */ | ||
71 | #define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ | ||
72 | #define ASUS_WMI_METHODID_SFBD 0x44424653 /* Set First Boot Device */ | ||
73 | #define ASUS_WMI_METHODID_GLCD 0x44434C47 /* Get LCD status */ | ||
74 | #define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */ | ||
75 | #define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */ | ||
76 | #define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */ | ||
77 | #define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */ | ||
78 | #define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */ | ||
79 | #define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ | ||
80 | #define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */ | ||
81 | #define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */ | ||
82 | #define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */ | ||
83 | #define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/ | ||
84 | #define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */ | ||
85 | #define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */ | ||
86 | #define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */ | ||
87 | #define ASUS_WMI_METHODID_KBFT 0x5446424B /* KeyBoard FilTer */ | ||
88 | #define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */ | ||
89 | #define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */ | ||
90 | |||
91 | #define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE | ||
92 | |||
93 | /* Wireless */ | ||
94 | #define ASUS_WMI_DEVID_HW_SWITCH 0x00010001 | ||
95 | #define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002 | ||
96 | #define ASUS_WMI_DEVID_WLAN 0x00010011 | ||
97 | #define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 | ||
98 | #define ASUS_WMI_DEVID_GPS 0x00010015 | ||
99 | #define ASUS_WMI_DEVID_WIMAX 0x00010017 | ||
100 | #define ASUS_WMI_DEVID_WWAN3G 0x00010019 | ||
101 | #define ASUS_WMI_DEVID_UWB 0x00010021 | ||
102 | |||
103 | /* Leds */ | ||
104 | /* 0x000200XX and 0x000400XX */ | ||
105 | |||
106 | /* Backlight and Brightness */ | ||
107 | #define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 | ||
108 | #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 | ||
109 | #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 | ||
110 | #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ | ||
111 | |||
112 | /* Misc */ | ||
113 | #define ASUS_WMI_DEVID_CAMERA 0x00060013 | ||
114 | |||
115 | /* Storage */ | ||
116 | #define ASUS_WMI_DEVID_CARDREADER 0x00080013 | ||
117 | |||
118 | /* Input */ | ||
119 | #define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 | ||
120 | #define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 | ||
121 | |||
122 | /* Fan, Thermal */ | ||
123 | #define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 | ||
124 | #define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 | ||
125 | |||
126 | /* Power */ | ||
127 | #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 | ||
128 | |||
129 | /* DSTS masks */ | ||
130 | #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 | ||
131 | #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 | ||
132 | #define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 | ||
133 | #define ASUS_WMI_DSTS_USER_BIT 0x00020000 | ||
134 | #define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 | ||
135 | #define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF | ||
136 | #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 | ||
137 | |||
138 | struct bios_args { | ||
139 | u32 arg0; | ||
140 | u32 arg1; | ||
141 | } __packed; | ||
142 | |||
143 | /* | ||
144 | * <platform>/ - debugfs root directory | ||
145 | * dev_id - current dev_id | ||
146 | * ctrl_param - current ctrl_param | ||
147 | * method_id - current method_id | ||
148 | * devs - call DEVS(dev_id, ctrl_param) and print result | ||
149 | * dsts - call DSTS(dev_id) and print result | ||
150 | * call - call method_id(dev_id, ctrl_param) and print result | ||
151 | */ | ||
152 | struct asus_wmi_debug { | ||
153 | struct dentry *root; | ||
154 | u32 method_id; | ||
155 | u32 dev_id; | ||
156 | u32 ctrl_param; | ||
157 | }; | ||
158 | |||
159 | struct asus_rfkill { | ||
160 | struct asus_wmi *asus; | ||
161 | struct rfkill *rfkill; | ||
162 | u32 dev_id; | ||
163 | }; | ||
164 | |||
165 | struct asus_wmi { | ||
166 | int dsts_id; | ||
167 | int spec; | ||
168 | int sfun; | ||
169 | |||
170 | struct input_dev *inputdev; | ||
171 | struct backlight_device *backlight_device; | ||
172 | struct device *hwmon_device; | ||
173 | struct platform_device *platform_device; | ||
174 | |||
175 | struct led_classdev tpd_led; | ||
176 | int tpd_led_wk; | ||
177 | struct workqueue_struct *led_workqueue; | ||
178 | struct work_struct tpd_led_work; | ||
179 | |||
180 | struct asus_rfkill wlan; | ||
181 | struct asus_rfkill bluetooth; | ||
182 | struct asus_rfkill wimax; | ||
183 | struct asus_rfkill wwan3g; | ||
184 | |||
185 | struct hotplug_slot *hotplug_slot; | ||
186 | struct mutex hotplug_lock; | ||
187 | struct mutex wmi_lock; | ||
188 | struct workqueue_struct *hotplug_workqueue; | ||
189 | struct work_struct hotplug_work; | ||
190 | |||
191 | struct asus_wmi_debug debug; | ||
192 | |||
193 | struct asus_wmi_driver *driver; | ||
194 | }; | ||
195 | |||
196 | static int asus_wmi_input_init(struct asus_wmi *asus) | ||
197 | { | ||
198 | int err; | ||
199 | |||
200 | asus->inputdev = input_allocate_device(); | ||
201 | if (!asus->inputdev) | ||
202 | return -ENOMEM; | ||
203 | |||
204 | asus->inputdev->name = asus->driver->input_name; | ||
205 | asus->inputdev->phys = asus->driver->input_phys; | ||
206 | asus->inputdev->id.bustype = BUS_HOST; | ||
207 | asus->inputdev->dev.parent = &asus->platform_device->dev; | ||
208 | |||
209 | err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL); | ||
210 | if (err) | ||
211 | goto err_free_dev; | ||
212 | |||
213 | err = input_register_device(asus->inputdev); | ||
214 | if (err) | ||
215 | goto err_free_keymap; | ||
216 | |||
217 | return 0; | ||
218 | |||
219 | err_free_keymap: | ||
220 | sparse_keymap_free(asus->inputdev); | ||
221 | err_free_dev: | ||
222 | input_free_device(asus->inputdev); | ||
223 | return err; | ||
224 | } | ||
225 | |||
226 | static void asus_wmi_input_exit(struct asus_wmi *asus) | ||
227 | { | ||
228 | if (asus->inputdev) { | ||
229 | sparse_keymap_free(asus->inputdev); | ||
230 | input_unregister_device(asus->inputdev); | ||
231 | } | ||
232 | |||
233 | asus->inputdev = NULL; | ||
234 | } | ||
235 | |||
236 | static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, | ||
237 | u32 *retval) | ||
238 | { | ||
239 | struct bios_args args = { | ||
240 | .arg0 = arg0, | ||
241 | .arg1 = arg1, | ||
242 | }; | ||
243 | struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; | ||
244 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
245 | acpi_status status; | ||
246 | union acpi_object *obj; | ||
247 | u32 tmp; | ||
248 | |||
249 | status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id, | ||
250 | &input, &output); | ||
251 | |||
252 | if (ACPI_FAILURE(status)) | ||
253 | goto exit; | ||
254 | |||
255 | obj = (union acpi_object *)output.pointer; | ||
256 | if (obj && obj->type == ACPI_TYPE_INTEGER) | ||
257 | tmp = (u32) obj->integer.value; | ||
258 | else | ||
259 | tmp = 0; | ||
260 | |||
261 | if (retval) | ||
262 | *retval = tmp; | ||
263 | |||
264 | kfree(obj); | ||
265 | |||
266 | exit: | ||
267 | if (ACPI_FAILURE(status)) | ||
268 | return -EIO; | ||
269 | |||
270 | if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) | ||
271 | return -ENODEV; | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval) | ||
277 | { | ||
278 | return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval); | ||
279 | } | ||
280 | |||
281 | static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, | ||
282 | u32 *retval) | ||
283 | { | ||
284 | return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id, | ||
285 | ctrl_param, retval); | ||
286 | } | ||
287 | |||
288 | /* Helper for special devices with magic return codes */ | ||
289 | static int asus_wmi_get_devstate_bits(struct asus_wmi *asus, | ||
290 | u32 dev_id, u32 mask) | ||
291 | { | ||
292 | u32 retval = 0; | ||
293 | int err; | ||
294 | |||
295 | err = asus_wmi_get_devstate(asus, dev_id, &retval); | ||
296 | |||
297 | if (err < 0) | ||
298 | return err; | ||
299 | |||
300 | if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT)) | ||
301 | return -ENODEV; | ||
302 | |||
303 | if (mask == ASUS_WMI_DSTS_STATUS_BIT) { | ||
304 | if (retval & ASUS_WMI_DSTS_UNKNOWN_BIT) | ||
305 | return -ENODEV; | ||
306 | } | ||
307 | |||
308 | return retval & mask; | ||
309 | } | ||
310 | |||
311 | static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id) | ||
312 | { | ||
313 | return asus_wmi_get_devstate_bits(asus, dev_id, | ||
314 | ASUS_WMI_DSTS_STATUS_BIT); | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * LEDs | ||
319 | */ | ||
320 | /* | ||
321 | * These functions actually update the LED's, and are called from a | ||
322 | * workqueue. By doing this as separate work rather than when the LED | ||
323 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | ||
324 | * potentially bad time, such as a timer interrupt. | ||
325 | */ | ||
326 | static void tpd_led_update(struct work_struct *work) | ||
327 | { | ||
328 | int ctrl_param; | ||
329 | struct asus_wmi *asus; | ||
330 | |||
331 | asus = container_of(work, struct asus_wmi, tpd_led_work); | ||
332 | |||
333 | ctrl_param = asus->tpd_led_wk; | ||
334 | asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL); | ||
335 | } | ||
336 | |||
337 | static void tpd_led_set(struct led_classdev *led_cdev, | ||
338 | enum led_brightness value) | ||
339 | { | ||
340 | struct asus_wmi *asus; | ||
341 | |||
342 | asus = container_of(led_cdev, struct asus_wmi, tpd_led); | ||
343 | |||
344 | asus->tpd_led_wk = !!value; | ||
345 | queue_work(asus->led_workqueue, &asus->tpd_led_work); | ||
346 | } | ||
347 | |||
348 | static int read_tpd_led_state(struct asus_wmi *asus) | ||
349 | { | ||
350 | return asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED); | ||
351 | } | ||
352 | |||
353 | static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | ||
354 | { | ||
355 | struct asus_wmi *asus; | ||
356 | |||
357 | asus = container_of(led_cdev, struct asus_wmi, tpd_led); | ||
358 | |||
359 | return read_tpd_led_state(asus); | ||
360 | } | ||
361 | |||
362 | static int asus_wmi_led_init(struct asus_wmi *asus) | ||
363 | { | ||
364 | int rv; | ||
365 | |||
366 | if (read_tpd_led_state(asus) < 0) | ||
367 | return 0; | ||
368 | |||
369 | asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); | ||
370 | if (!asus->led_workqueue) | ||
371 | return -ENOMEM; | ||
372 | INIT_WORK(&asus->tpd_led_work, tpd_led_update); | ||
373 | |||
374 | asus->tpd_led.name = "asus::touchpad"; | ||
375 | asus->tpd_led.brightness_set = tpd_led_set; | ||
376 | asus->tpd_led.brightness_get = tpd_led_get; | ||
377 | asus->tpd_led.max_brightness = 1; | ||
378 | |||
379 | rv = led_classdev_register(&asus->platform_device->dev, &asus->tpd_led); | ||
380 | if (rv) { | ||
381 | destroy_workqueue(asus->led_workqueue); | ||
382 | return rv; | ||
383 | } | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static void asus_wmi_led_exit(struct asus_wmi *asus) | ||
389 | { | ||
390 | if (asus->tpd_led.dev) | ||
391 | led_classdev_unregister(&asus->tpd_led); | ||
392 | if (asus->led_workqueue) | ||
393 | destroy_workqueue(asus->led_workqueue); | ||
394 | } | ||
395 | |||
396 | /* | ||
397 | * PCI hotplug (for wlan rfkill) | ||
398 | */ | ||
399 | static bool asus_wlan_rfkill_blocked(struct asus_wmi *asus) | ||
400 | { | ||
401 | int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); | ||
402 | |||
403 | if (result < 0) | ||
404 | return false; | ||
405 | return !result; | ||
406 | } | ||
407 | |||
408 | static void asus_rfkill_hotplug(struct asus_wmi *asus) | ||
409 | { | ||
410 | struct pci_dev *dev; | ||
411 | struct pci_bus *bus; | ||
412 | bool blocked; | ||
413 | bool absent; | ||
414 | u32 l; | ||
415 | |||
416 | mutex_lock(&asus->wmi_lock); | ||
417 | blocked = asus_wlan_rfkill_blocked(asus); | ||
418 | mutex_unlock(&asus->wmi_lock); | ||
419 | |||
420 | mutex_lock(&asus->hotplug_lock); | ||
421 | |||
422 | if (asus->wlan.rfkill) | ||
423 | rfkill_set_sw_state(asus->wlan.rfkill, blocked); | ||
424 | |||
425 | if (asus->hotplug_slot) { | ||
426 | bus = pci_find_bus(0, 1); | ||
427 | if (!bus) { | ||
428 | pr_warn("Unable to find PCI bus 1?\n"); | ||
429 | goto out_unlock; | ||
430 | } | ||
431 | |||
432 | if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { | ||
433 | pr_err("Unable to read PCI config space?\n"); | ||
434 | goto out_unlock; | ||
435 | } | ||
436 | absent = (l == 0xffffffff); | ||
437 | |||
438 | if (blocked != absent) { | ||
439 | pr_warn("BIOS says wireless lan is %s, " | ||
440 | "but the pci device is %s\n", | ||
441 | blocked ? "blocked" : "unblocked", | ||
442 | absent ? "absent" : "present"); | ||
443 | pr_warn("skipped wireless hotplug as probably " | ||
444 | "inappropriate for this model\n"); | ||
445 | goto out_unlock; | ||
446 | } | ||
447 | |||
448 | if (!blocked) { | ||
449 | dev = pci_get_slot(bus, 0); | ||
450 | if (dev) { | ||
451 | /* Device already present */ | ||
452 | pci_dev_put(dev); | ||
453 | goto out_unlock; | ||
454 | } | ||
455 | dev = pci_scan_single_device(bus, 0); | ||
456 | if (dev) { | ||
457 | pci_bus_assign_resources(bus); | ||
458 | if (pci_bus_add_device(dev)) | ||
459 | pr_err("Unable to hotplug wifi\n"); | ||
460 | } | ||
461 | } else { | ||
462 | dev = pci_get_slot(bus, 0); | ||
463 | if (dev) { | ||
464 | pci_remove_bus_device(dev); | ||
465 | pci_dev_put(dev); | ||
466 | } | ||
467 | } | ||
468 | } | ||
469 | |||
470 | out_unlock: | ||
471 | mutex_unlock(&asus->hotplug_lock); | ||
472 | } | ||
473 | |||
474 | static void asus_rfkill_notify(acpi_handle handle, u32 event, void *data) | ||
475 | { | ||
476 | struct asus_wmi *asus = data; | ||
477 | |||
478 | if (event != ACPI_NOTIFY_BUS_CHECK) | ||
479 | return; | ||
480 | |||
481 | /* | ||
482 | * We can't call directly asus_rfkill_hotplug because most | ||
483 | * of the time WMBC is still being executed and not reetrant. | ||
484 | * There is currently no way to tell ACPICA that we want this | ||
485 | * method to be serialized, we schedule a asus_rfkill_hotplug | ||
486 | * call later, in a safer context. | ||
487 | */ | ||
488 | queue_work(asus->hotplug_workqueue, &asus->hotplug_work); | ||
489 | } | ||
490 | |||
491 | static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node) | ||
492 | { | ||
493 | acpi_status status; | ||
494 | acpi_handle handle; | ||
495 | |||
496 | status = acpi_get_handle(NULL, node, &handle); | ||
497 | |||
498 | if (ACPI_SUCCESS(status)) { | ||
499 | status = acpi_install_notify_handler(handle, | ||
500 | ACPI_SYSTEM_NOTIFY, | ||
501 | asus_rfkill_notify, asus); | ||
502 | if (ACPI_FAILURE(status)) | ||
503 | pr_warn("Failed to register notify on %s\n", node); | ||
504 | } else | ||
505 | return -ENODEV; | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node) | ||
511 | { | ||
512 | acpi_status status = AE_OK; | ||
513 | acpi_handle handle; | ||
514 | |||
515 | status = acpi_get_handle(NULL, node, &handle); | ||
516 | |||
517 | if (ACPI_SUCCESS(status)) { | ||
518 | status = acpi_remove_notify_handler(handle, | ||
519 | ACPI_SYSTEM_NOTIFY, | ||
520 | asus_rfkill_notify); | ||
521 | if (ACPI_FAILURE(status)) | ||
522 | pr_err("Error removing rfkill notify handler %s\n", | ||
523 | node); | ||
524 | } | ||
525 | } | ||
526 | |||
527 | static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, | ||
528 | u8 *value) | ||
529 | { | ||
530 | struct asus_wmi *asus = hotplug_slot->private; | ||
531 | int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); | ||
532 | |||
533 | if (result < 0) | ||
534 | return result; | ||
535 | |||
536 | *value = !!result; | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static void asus_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) | ||
541 | { | ||
542 | kfree(hotplug_slot->info); | ||
543 | kfree(hotplug_slot); | ||
544 | } | ||
545 | |||
546 | static struct hotplug_slot_ops asus_hotplug_slot_ops = { | ||
547 | .owner = THIS_MODULE, | ||
548 | .get_adapter_status = asus_get_adapter_status, | ||
549 | .get_power_status = asus_get_adapter_status, | ||
550 | }; | ||
551 | |||
552 | static void asus_hotplug_work(struct work_struct *work) | ||
553 | { | ||
554 | struct asus_wmi *asus; | ||
555 | |||
556 | asus = container_of(work, struct asus_wmi, hotplug_work); | ||
557 | asus_rfkill_hotplug(asus); | ||
558 | } | ||
559 | |||
560 | static int asus_setup_pci_hotplug(struct asus_wmi *asus) | ||
561 | { | ||
562 | int ret = -ENOMEM; | ||
563 | struct pci_bus *bus = pci_find_bus(0, 1); | ||
564 | |||
565 | if (!bus) { | ||
566 | pr_err("Unable to find wifi PCI bus\n"); | ||
567 | return -ENODEV; | ||
568 | } | ||
569 | |||
570 | asus->hotplug_workqueue = | ||
571 | create_singlethread_workqueue("hotplug_workqueue"); | ||
572 | if (!asus->hotplug_workqueue) | ||
573 | goto error_workqueue; | ||
574 | |||
575 | INIT_WORK(&asus->hotplug_work, asus_hotplug_work); | ||
576 | |||
577 | asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); | ||
578 | if (!asus->hotplug_slot) | ||
579 | goto error_slot; | ||
580 | |||
581 | asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), | ||
582 | GFP_KERNEL); | ||
583 | if (!asus->hotplug_slot->info) | ||
584 | goto error_info; | ||
585 | |||
586 | asus->hotplug_slot->private = asus; | ||
587 | asus->hotplug_slot->release = &asus_cleanup_pci_hotplug; | ||
588 | asus->hotplug_slot->ops = &asus_hotplug_slot_ops; | ||
589 | asus_get_adapter_status(asus->hotplug_slot, | ||
590 | &asus->hotplug_slot->info->adapter_status); | ||
591 | |||
592 | ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi"); | ||
593 | if (ret) { | ||
594 | pr_err("Unable to register hotplug slot - %d\n", ret); | ||
595 | goto error_register; | ||
596 | } | ||
597 | |||
598 | return 0; | ||
599 | |||
600 | error_register: | ||
601 | kfree(asus->hotplug_slot->info); | ||
602 | error_info: | ||
603 | kfree(asus->hotplug_slot); | ||
604 | asus->hotplug_slot = NULL; | ||
605 | error_slot: | ||
606 | destroy_workqueue(asus->hotplug_workqueue); | ||
607 | error_workqueue: | ||
608 | return ret; | ||
609 | } | ||
610 | |||
611 | /* | ||
612 | * Rfkill devices | ||
613 | */ | ||
614 | static int asus_rfkill_set(void *data, bool blocked) | ||
615 | { | ||
616 | struct asus_rfkill *priv = data; | ||
617 | u32 ctrl_param = !blocked; | ||
618 | |||
619 | return asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL); | ||
620 | } | ||
621 | |||
622 | static void asus_rfkill_query(struct rfkill *rfkill, void *data) | ||
623 | { | ||
624 | struct asus_rfkill *priv = data; | ||
625 | int result; | ||
626 | |||
627 | result = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id); | ||
628 | |||
629 | if (result < 0) | ||
630 | return; | ||
631 | |||
632 | rfkill_set_sw_state(priv->rfkill, !result); | ||
633 | } | ||
634 | |||
635 | static int asus_rfkill_wlan_set(void *data, bool blocked) | ||
636 | { | ||
637 | struct asus_rfkill *priv = data; | ||
638 | struct asus_wmi *asus = priv->asus; | ||
639 | int ret; | ||
640 | |||
641 | /* | ||
642 | * This handler is enabled only if hotplug is enabled. | ||
643 | * In this case, the asus_wmi_set_devstate() will | ||
644 | * trigger a wmi notification and we need to wait | ||
645 | * this call to finish before being able to call | ||
646 | * any wmi method | ||
647 | */ | ||
648 | mutex_lock(&asus->wmi_lock); | ||
649 | ret = asus_rfkill_set(data, blocked); | ||
650 | mutex_unlock(&asus->wmi_lock); | ||
651 | return ret; | ||
652 | } | ||
653 | |||
654 | static const struct rfkill_ops asus_rfkill_wlan_ops = { | ||
655 | .set_block = asus_rfkill_wlan_set, | ||
656 | .query = asus_rfkill_query, | ||
657 | }; | ||
658 | |||
659 | static const struct rfkill_ops asus_rfkill_ops = { | ||
660 | .set_block = asus_rfkill_set, | ||
661 | .query = asus_rfkill_query, | ||
662 | }; | ||
663 | |||
664 | static int asus_new_rfkill(struct asus_wmi *asus, | ||
665 | struct asus_rfkill *arfkill, | ||
666 | const char *name, enum rfkill_type type, int dev_id) | ||
667 | { | ||
668 | int result = asus_wmi_get_devstate_simple(asus, dev_id); | ||
669 | struct rfkill **rfkill = &arfkill->rfkill; | ||
670 | |||
671 | if (result < 0) | ||
672 | return result; | ||
673 | |||
674 | arfkill->dev_id = dev_id; | ||
675 | arfkill->asus = asus; | ||
676 | |||
677 | if (dev_id == ASUS_WMI_DEVID_WLAN && asus->driver->hotplug_wireless) | ||
678 | *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, | ||
679 | &asus_rfkill_wlan_ops, arfkill); | ||
680 | else | ||
681 | *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, | ||
682 | &asus_rfkill_ops, arfkill); | ||
683 | |||
684 | if (!*rfkill) | ||
685 | return -EINVAL; | ||
686 | |||
687 | rfkill_init_sw_state(*rfkill, !result); | ||
688 | result = rfkill_register(*rfkill); | ||
689 | if (result) { | ||
690 | rfkill_destroy(*rfkill); | ||
691 | *rfkill = NULL; | ||
692 | return result; | ||
693 | } | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static void asus_wmi_rfkill_exit(struct asus_wmi *asus) | ||
698 | { | ||
699 | asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5"); | ||
700 | asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6"); | ||
701 | asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7"); | ||
702 | if (asus->wlan.rfkill) { | ||
703 | rfkill_unregister(asus->wlan.rfkill); | ||
704 | rfkill_destroy(asus->wlan.rfkill); | ||
705 | asus->wlan.rfkill = NULL; | ||
706 | } | ||
707 | /* | ||
708 | * Refresh pci hotplug in case the rfkill state was changed after | ||
709 | * asus_unregister_rfkill_notifier() | ||
710 | */ | ||
711 | asus_rfkill_hotplug(asus); | ||
712 | if (asus->hotplug_slot) | ||
713 | pci_hp_deregister(asus->hotplug_slot); | ||
714 | if (asus->hotplug_workqueue) | ||
715 | destroy_workqueue(asus->hotplug_workqueue); | ||
716 | |||
717 | if (asus->bluetooth.rfkill) { | ||
718 | rfkill_unregister(asus->bluetooth.rfkill); | ||
719 | rfkill_destroy(asus->bluetooth.rfkill); | ||
720 | asus->bluetooth.rfkill = NULL; | ||
721 | } | ||
722 | if (asus->wimax.rfkill) { | ||
723 | rfkill_unregister(asus->wimax.rfkill); | ||
724 | rfkill_destroy(asus->wimax.rfkill); | ||
725 | asus->wimax.rfkill = NULL; | ||
726 | } | ||
727 | if (asus->wwan3g.rfkill) { | ||
728 | rfkill_unregister(asus->wwan3g.rfkill); | ||
729 | rfkill_destroy(asus->wwan3g.rfkill); | ||
730 | asus->wwan3g.rfkill = NULL; | ||
731 | } | ||
732 | } | ||
733 | |||
734 | static int asus_wmi_rfkill_init(struct asus_wmi *asus) | ||
735 | { | ||
736 | int result = 0; | ||
737 | |||
738 | mutex_init(&asus->hotplug_lock); | ||
739 | mutex_init(&asus->wmi_lock); | ||
740 | |||
741 | result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan", | ||
742 | RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN); | ||
743 | |||
744 | if (result && result != -ENODEV) | ||
745 | goto exit; | ||
746 | |||
747 | result = asus_new_rfkill(asus, &asus->bluetooth, | ||
748 | "asus-bluetooth", RFKILL_TYPE_BLUETOOTH, | ||
749 | ASUS_WMI_DEVID_BLUETOOTH); | ||
750 | |||
751 | if (result && result != -ENODEV) | ||
752 | goto exit; | ||
753 | |||
754 | result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax", | ||
755 | RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX); | ||
756 | |||
757 | if (result && result != -ENODEV) | ||
758 | goto exit; | ||
759 | |||
760 | result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g", | ||
761 | RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G); | ||
762 | |||
763 | if (result && result != -ENODEV) | ||
764 | goto exit; | ||
765 | |||
766 | if (!asus->driver->hotplug_wireless) | ||
767 | goto exit; | ||
768 | |||
769 | result = asus_setup_pci_hotplug(asus); | ||
770 | /* | ||
771 | * If we get -EBUSY then something else is handling the PCI hotplug - | ||
772 | * don't fail in this case | ||
773 | */ | ||
774 | if (result == -EBUSY) | ||
775 | result = 0; | ||
776 | |||
777 | asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5"); | ||
778 | asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6"); | ||
779 | asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7"); | ||
780 | /* | ||
781 | * Refresh pci hotplug in case the rfkill state was changed during | ||
782 | * setup. | ||
783 | */ | ||
784 | asus_rfkill_hotplug(asus); | ||
785 | |||
786 | exit: | ||
787 | if (result && result != -ENODEV) | ||
788 | asus_wmi_rfkill_exit(asus); | ||
789 | |||
790 | if (result == -ENODEV) | ||
791 | result = 0; | ||
792 | |||
793 | return result; | ||
794 | } | ||
795 | |||
796 | /* | ||
797 | * Hwmon device | ||
798 | */ | ||
799 | static ssize_t asus_hwmon_pwm1(struct device *dev, | ||
800 | struct device_attribute *attr, | ||
801 | char *buf) | ||
802 | { | ||
803 | struct asus_wmi *asus = dev_get_drvdata(dev); | ||
804 | u32 value; | ||
805 | int err; | ||
806 | |||
807 | err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value); | ||
808 | |||
809 | if (err < 0) | ||
810 | return err; | ||
811 | |||
812 | value |= 0xFF; | ||
813 | |||
814 | if (value == 1) /* Low Speed */ | ||
815 | value = 85; | ||
816 | else if (value == 2) | ||
817 | value = 170; | ||
818 | else if (value == 3) | ||
819 | value = 255; | ||
820 | else if (value != 0) { | ||
821 | pr_err("Unknown fan speed %#x", value); | ||
822 | value = -1; | ||
823 | } | ||
824 | |||
825 | return sprintf(buf, "%d\n", value); | ||
826 | } | ||
827 | |||
828 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL, 0); | ||
829 | |||
830 | static ssize_t | ||
831 | show_name(struct device *dev, struct device_attribute *attr, char *buf) | ||
832 | { | ||
833 | return sprintf(buf, "asus\n"); | ||
834 | } | ||
835 | static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); | ||
836 | |||
837 | static struct attribute *hwmon_attributes[] = { | ||
838 | &sensor_dev_attr_pwm1.dev_attr.attr, | ||
839 | &sensor_dev_attr_name.dev_attr.attr, | ||
840 | NULL | ||
841 | }; | ||
842 | |||
843 | static mode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, | ||
844 | struct attribute *attr, int idx) | ||
845 | { | ||
846 | struct device *dev = container_of(kobj, struct device, kobj); | ||
847 | struct platform_device *pdev = to_platform_device(dev->parent); | ||
848 | struct asus_wmi *asus = platform_get_drvdata(pdev); | ||
849 | bool ok = true; | ||
850 | int dev_id = -1; | ||
851 | u32 value = ASUS_WMI_UNSUPPORTED_METHOD; | ||
852 | |||
853 | if (attr == &sensor_dev_attr_pwm1.dev_attr.attr) | ||
854 | dev_id = ASUS_WMI_DEVID_FAN_CTRL; | ||
855 | |||
856 | if (dev_id != -1) { | ||
857 | int err = asus_wmi_get_devstate(asus, dev_id, &value); | ||
858 | |||
859 | if (err < 0) | ||
860 | return err; | ||
861 | } | ||
862 | |||
863 | if (dev_id == ASUS_WMI_DEVID_FAN_CTRL) { | ||
864 | /* | ||
865 | * We need to find a better way, probably using sfun, | ||
866 | * bits or spec ... | ||
867 | * Currently we disable it if: | ||
868 | * - ASUS_WMI_UNSUPPORTED_METHOD is returned | ||
869 | * - reverved bits are non-zero | ||
870 | * - sfun and presence bit are not set | ||
871 | */ | ||
872 | if (value != ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000 | ||
873 | || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT))) | ||
874 | ok = false; | ||
875 | } | ||
876 | |||
877 | return ok ? attr->mode : 0; | ||
878 | } | ||
879 | |||
880 | static struct attribute_group hwmon_attribute_group = { | ||
881 | .is_visible = asus_hwmon_sysfs_is_visible, | ||
882 | .attrs = hwmon_attributes | ||
883 | }; | ||
884 | |||
885 | static void asus_wmi_hwmon_exit(struct asus_wmi *asus) | ||
886 | { | ||
887 | struct device *hwmon; | ||
888 | |||
889 | hwmon = asus->hwmon_device; | ||
890 | if (!hwmon) | ||
891 | return; | ||
892 | sysfs_remove_group(&hwmon->kobj, &hwmon_attribute_group); | ||
893 | hwmon_device_unregister(hwmon); | ||
894 | asus->hwmon_device = NULL; | ||
895 | } | ||
896 | |||
897 | static int asus_wmi_hwmon_init(struct asus_wmi *asus) | ||
898 | { | ||
899 | struct device *hwmon; | ||
900 | int result; | ||
901 | |||
902 | hwmon = hwmon_device_register(&asus->platform_device->dev); | ||
903 | if (IS_ERR(hwmon)) { | ||
904 | pr_err("Could not register asus hwmon device\n"); | ||
905 | return PTR_ERR(hwmon); | ||
906 | } | ||
907 | asus->hwmon_device = hwmon; | ||
908 | result = sysfs_create_group(&hwmon->kobj, &hwmon_attribute_group); | ||
909 | if (result) | ||
910 | asus_wmi_hwmon_exit(asus); | ||
911 | return result; | ||
912 | } | ||
913 | |||
914 | /* | ||
915 | * Backlight | ||
916 | */ | ||
917 | static int read_backlight_power(struct asus_wmi *asus) | ||
918 | { | ||
919 | int ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BACKLIGHT); | ||
920 | |||
921 | if (ret < 0) | ||
922 | return ret; | ||
923 | |||
924 | return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | ||
925 | } | ||
926 | |||
927 | static int read_brightness_max(struct asus_wmi *asus) | ||
928 | { | ||
929 | u32 retval; | ||
930 | int err; | ||
931 | |||
932 | err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); | ||
933 | |||
934 | if (err < 0) | ||
935 | return err; | ||
936 | |||
937 | retval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK; | ||
938 | retval >>= 8; | ||
939 | |||
940 | if (!retval) | ||
941 | return -ENODEV; | ||
942 | |||
943 | return retval; | ||
944 | } | ||
945 | |||
946 | static int read_brightness(struct backlight_device *bd) | ||
947 | { | ||
948 | struct asus_wmi *asus = bl_get_data(bd); | ||
949 | u32 retval; | ||
950 | int err; | ||
951 | |||
952 | err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); | ||
953 | |||
954 | if (err < 0) | ||
955 | return err; | ||
956 | |||
957 | return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; | ||
958 | } | ||
959 | |||
960 | static int update_bl_status(struct backlight_device *bd) | ||
961 | { | ||
962 | struct asus_wmi *asus = bl_get_data(bd); | ||
963 | u32 ctrl_param; | ||
964 | int power, err; | ||
965 | |||
966 | ctrl_param = bd->props.brightness; | ||
967 | |||
968 | err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, | ||
969 | ctrl_param, NULL); | ||
970 | |||
971 | if (err < 0) | ||
972 | return err; | ||
973 | |||
974 | power = read_backlight_power(asus); | ||
975 | if (power != -ENODEV && bd->props.power != power) { | ||
976 | ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); | ||
977 | err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, | ||
978 | ctrl_param, NULL); | ||
979 | } | ||
980 | return err; | ||
981 | } | ||
982 | |||
983 | static const struct backlight_ops asus_wmi_bl_ops = { | ||
984 | .get_brightness = read_brightness, | ||
985 | .update_status = update_bl_status, | ||
986 | }; | ||
987 | |||
988 | static int asus_wmi_backlight_notify(struct asus_wmi *asus, int code) | ||
989 | { | ||
990 | struct backlight_device *bd = asus->backlight_device; | ||
991 | int old = bd->props.brightness; | ||
992 | int new = old; | ||
993 | |||
994 | if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | ||
995 | new = code - NOTIFY_BRNUP_MIN + 1; | ||
996 | else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX) | ||
997 | new = code - NOTIFY_BRNDOWN_MIN; | ||
998 | |||
999 | bd->props.brightness = new; | ||
1000 | backlight_update_status(bd); | ||
1001 | backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); | ||
1002 | |||
1003 | return old; | ||
1004 | } | ||
1005 | |||
1006 | static int asus_wmi_backlight_init(struct asus_wmi *asus) | ||
1007 | { | ||
1008 | struct backlight_device *bd; | ||
1009 | struct backlight_properties props; | ||
1010 | int max; | ||
1011 | int power; | ||
1012 | |||
1013 | max = read_brightness_max(asus); | ||
1014 | |||
1015 | if (max == -ENODEV) | ||
1016 | max = 0; | ||
1017 | else if (max < 0) | ||
1018 | return max; | ||
1019 | |||
1020 | power = read_backlight_power(asus); | ||
1021 | |||
1022 | if (power == -ENODEV) | ||
1023 | power = FB_BLANK_UNBLANK; | ||
1024 | else if (power < 0) | ||
1025 | return power; | ||
1026 | |||
1027 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1028 | props.type = BACKLIGHT_PLATFORM; | ||
1029 | props.max_brightness = max; | ||
1030 | bd = backlight_device_register(asus->driver->name, | ||
1031 | &asus->platform_device->dev, asus, | ||
1032 | &asus_wmi_bl_ops, &props); | ||
1033 | if (IS_ERR(bd)) { | ||
1034 | pr_err("Could not register backlight device\n"); | ||
1035 | return PTR_ERR(bd); | ||
1036 | } | ||
1037 | |||
1038 | asus->backlight_device = bd; | ||
1039 | |||
1040 | bd->props.brightness = read_brightness(bd); | ||
1041 | bd->props.power = power; | ||
1042 | backlight_update_status(bd); | ||
1043 | |||
1044 | return 0; | ||
1045 | } | ||
1046 | |||
1047 | static void asus_wmi_backlight_exit(struct asus_wmi *asus) | ||
1048 | { | ||
1049 | if (asus->backlight_device) | ||
1050 | backlight_device_unregister(asus->backlight_device); | ||
1051 | |||
1052 | asus->backlight_device = NULL; | ||
1053 | } | ||
1054 | |||
1055 | static void asus_wmi_notify(u32 value, void *context) | ||
1056 | { | ||
1057 | struct asus_wmi *asus = context; | ||
1058 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1059 | union acpi_object *obj; | ||
1060 | acpi_status status; | ||
1061 | int code; | ||
1062 | int orig_code; | ||
1063 | |||
1064 | status = wmi_get_event_data(value, &response); | ||
1065 | if (status != AE_OK) { | ||
1066 | pr_err("bad event status 0x%x\n", status); | ||
1067 | return; | ||
1068 | } | ||
1069 | |||
1070 | obj = (union acpi_object *)response.pointer; | ||
1071 | |||
1072 | if (!obj || obj->type != ACPI_TYPE_INTEGER) | ||
1073 | goto exit; | ||
1074 | |||
1075 | code = obj->integer.value; | ||
1076 | orig_code = code; | ||
1077 | |||
1078 | if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | ||
1079 | code = NOTIFY_BRNUP_MIN; | ||
1080 | else if (code >= NOTIFY_BRNDOWN_MIN && | ||
1081 | code <= NOTIFY_BRNDOWN_MAX) | ||
1082 | code = NOTIFY_BRNDOWN_MIN; | ||
1083 | |||
1084 | if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { | ||
1085 | if (!acpi_video_backlight_support()) | ||
1086 | asus_wmi_backlight_notify(asus, orig_code); | ||
1087 | } else if (!sparse_keymap_report_event(asus->inputdev, code, 1, true)) | ||
1088 | pr_info("Unknown key %x pressed\n", code); | ||
1089 | |||
1090 | exit: | ||
1091 | kfree(obj); | ||
1092 | } | ||
1093 | |||
1094 | /* | ||
1095 | * Sys helpers | ||
1096 | */ | ||
1097 | static int parse_arg(const char *buf, unsigned long count, int *val) | ||
1098 | { | ||
1099 | if (!count) | ||
1100 | return 0; | ||
1101 | if (sscanf(buf, "%i", val) != 1) | ||
1102 | return -EINVAL; | ||
1103 | return count; | ||
1104 | } | ||
1105 | |||
1106 | static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid, | ||
1107 | const char *buf, size_t count) | ||
1108 | { | ||
1109 | u32 retval; | ||
1110 | int rv, err, value; | ||
1111 | |||
1112 | value = asus_wmi_get_devstate_simple(asus, devid); | ||
1113 | if (value == -ENODEV) /* Check device presence */ | ||
1114 | return value; | ||
1115 | |||
1116 | rv = parse_arg(buf, count, &value); | ||
1117 | err = asus_wmi_set_devstate(devid, value, &retval); | ||
1118 | |||
1119 | if (err < 0) | ||
1120 | return err; | ||
1121 | |||
1122 | return rv; | ||
1123 | } | ||
1124 | |||
1125 | static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf) | ||
1126 | { | ||
1127 | int value = asus_wmi_get_devstate_simple(asus, devid); | ||
1128 | |||
1129 | if (value < 0) | ||
1130 | return value; | ||
1131 | |||
1132 | return sprintf(buf, "%d\n", value); | ||
1133 | } | ||
1134 | |||
1135 | #define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ | ||
1136 | static ssize_t show_##_name(struct device *dev, \ | ||
1137 | struct device_attribute *attr, \ | ||
1138 | char *buf) \ | ||
1139 | { \ | ||
1140 | struct asus_wmi *asus = dev_get_drvdata(dev); \ | ||
1141 | \ | ||
1142 | return show_sys_wmi(asus, _cm, buf); \ | ||
1143 | } \ | ||
1144 | static ssize_t store_##_name(struct device *dev, \ | ||
1145 | struct device_attribute *attr, \ | ||
1146 | const char *buf, size_t count) \ | ||
1147 | { \ | ||
1148 | struct asus_wmi *asus = dev_get_drvdata(dev); \ | ||
1149 | \ | ||
1150 | return store_sys_wmi(asus, _cm, buf, count); \ | ||
1151 | } \ | ||
1152 | static struct device_attribute dev_attr_##_name = { \ | ||
1153 | .attr = { \ | ||
1154 | .name = __stringify(_name), \ | ||
1155 | .mode = _mode }, \ | ||
1156 | .show = show_##_name, \ | ||
1157 | .store = store_##_name, \ | ||
1158 | } | ||
1159 | |||
1160 | ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD); | ||
1161 | ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA); | ||
1162 | ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER); | ||
1163 | |||
1164 | static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, | ||
1165 | const char *buf, size_t count) | ||
1166 | { | ||
1167 | int value; | ||
1168 | |||
1169 | if (!count || sscanf(buf, "%i", &value) != 1) | ||
1170 | return -EINVAL; | ||
1171 | if (value < 0 || value > 2) | ||
1172 | return -EINVAL; | ||
1173 | |||
1174 | return asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL); | ||
1175 | } | ||
1176 | |||
1177 | static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); | ||
1178 | |||
1179 | static struct attribute *platform_attributes[] = { | ||
1180 | &dev_attr_cpufv.attr, | ||
1181 | &dev_attr_camera.attr, | ||
1182 | &dev_attr_cardr.attr, | ||
1183 | &dev_attr_touchpad.attr, | ||
1184 | NULL | ||
1185 | }; | ||
1186 | |||
1187 | static mode_t asus_sysfs_is_visible(struct kobject *kobj, | ||
1188 | struct attribute *attr, int idx) | ||
1189 | { | ||
1190 | struct device *dev = container_of(kobj, struct device, kobj); | ||
1191 | struct platform_device *pdev = to_platform_device(dev); | ||
1192 | struct asus_wmi *asus = platform_get_drvdata(pdev); | ||
1193 | bool ok = true; | ||
1194 | int devid = -1; | ||
1195 | |||
1196 | if (attr == &dev_attr_camera.attr) | ||
1197 | devid = ASUS_WMI_DEVID_CAMERA; | ||
1198 | else if (attr == &dev_attr_cardr.attr) | ||
1199 | devid = ASUS_WMI_DEVID_CARDREADER; | ||
1200 | else if (attr == &dev_attr_touchpad.attr) | ||
1201 | devid = ASUS_WMI_DEVID_TOUCHPAD; | ||
1202 | |||
1203 | if (devid != -1) | ||
1204 | ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); | ||
1205 | |||
1206 | return ok ? attr->mode : 0; | ||
1207 | } | ||
1208 | |||
1209 | static struct attribute_group platform_attribute_group = { | ||
1210 | .is_visible = asus_sysfs_is_visible, | ||
1211 | .attrs = platform_attributes | ||
1212 | }; | ||
1213 | |||
1214 | static void asus_wmi_sysfs_exit(struct platform_device *device) | ||
1215 | { | ||
1216 | sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); | ||
1217 | } | ||
1218 | |||
1219 | static int asus_wmi_sysfs_init(struct platform_device *device) | ||
1220 | { | ||
1221 | return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); | ||
1222 | } | ||
1223 | |||
1224 | /* | ||
1225 | * Platform device | ||
1226 | */ | ||
1227 | static int asus_wmi_platform_init(struct asus_wmi *asus) | ||
1228 | { | ||
1229 | int rv; | ||
1230 | |||
1231 | /* INIT enable hotkeys on some models */ | ||
1232 | if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv)) | ||
1233 | pr_info("Initialization: %#x", rv); | ||
1234 | |||
1235 | /* We don't know yet what to do with this version... */ | ||
1236 | if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) { | ||
1237 | pr_info("BIOS WMI version: %d.%d", rv >> 8, rv & 0xFF); | ||
1238 | asus->spec = rv; | ||
1239 | } | ||
1240 | |||
1241 | /* | ||
1242 | * The SFUN method probably allows the original driver to get the list | ||
1243 | * of features supported by a given model. For now, 0x0100 or 0x0800 | ||
1244 | * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. | ||
1245 | * The significance of others is yet to be found. | ||
1246 | */ | ||
1247 | if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) { | ||
1248 | pr_info("SFUN value: %#x", rv); | ||
1249 | asus->sfun = rv; | ||
1250 | } | ||
1251 | |||
1252 | /* | ||
1253 | * Eee PC and Notebooks seems to have different method_id for DSTS, | ||
1254 | * but it may also be related to the BIOS's SPEC. | ||
1255 | * Note, on most Eeepc, there is no way to check if a method exist | ||
1256 | * or note, while on notebooks, they returns 0xFFFFFFFE on failure, | ||
1257 | * but once again, SPEC may probably be used for that kind of things. | ||
1258 | */ | ||
1259 | if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) | ||
1260 | asus->dsts_id = ASUS_WMI_METHODID_DSTS; | ||
1261 | else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, 0, 0, NULL)) | ||
1262 | asus->dsts_id = ASUS_WMI_METHODID_DSTS2; | ||
1263 | |||
1264 | if (!asus->dsts_id) { | ||
1265 | pr_err("Can't find DSTS"); | ||
1266 | return -ENODEV; | ||
1267 | } | ||
1268 | |||
1269 | return asus_wmi_sysfs_init(asus->platform_device); | ||
1270 | } | ||
1271 | |||
1272 | static void asus_wmi_platform_exit(struct asus_wmi *asus) | ||
1273 | { | ||
1274 | asus_wmi_sysfs_exit(asus->platform_device); | ||
1275 | } | ||
1276 | |||
1277 | /* | ||
1278 | * debugfs | ||
1279 | */ | ||
1280 | struct asus_wmi_debugfs_node { | ||
1281 | struct asus_wmi *asus; | ||
1282 | char *name; | ||
1283 | int (*show) (struct seq_file *m, void *data); | ||
1284 | }; | ||
1285 | |||
1286 | static int show_dsts(struct seq_file *m, void *data) | ||
1287 | { | ||
1288 | struct asus_wmi *asus = m->private; | ||
1289 | int err; | ||
1290 | u32 retval = -1; | ||
1291 | |||
1292 | err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval); | ||
1293 | |||
1294 | if (err < 0) | ||
1295 | return err; | ||
1296 | |||
1297 | seq_printf(m, "DSTS(%#x) = %#x\n", asus->debug.dev_id, retval); | ||
1298 | |||
1299 | return 0; | ||
1300 | } | ||
1301 | |||
1302 | static int show_devs(struct seq_file *m, void *data) | ||
1303 | { | ||
1304 | struct asus_wmi *asus = m->private; | ||
1305 | int err; | ||
1306 | u32 retval = -1; | ||
1307 | |||
1308 | err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param, | ||
1309 | &retval); | ||
1310 | |||
1311 | if (err < 0) | ||
1312 | return err; | ||
1313 | |||
1314 | seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id, | ||
1315 | asus->debug.ctrl_param, retval); | ||
1316 | |||
1317 | return 0; | ||
1318 | } | ||
1319 | |||
1320 | static int show_call(struct seq_file *m, void *data) | ||
1321 | { | ||
1322 | struct asus_wmi *asus = m->private; | ||
1323 | struct bios_args args = { | ||
1324 | .arg0 = asus->debug.dev_id, | ||
1325 | .arg1 = asus->debug.ctrl_param, | ||
1326 | }; | ||
1327 | struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; | ||
1328 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1329 | union acpi_object *obj; | ||
1330 | acpi_status status; | ||
1331 | |||
1332 | status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, | ||
1333 | 1, asus->debug.method_id, | ||
1334 | &input, &output); | ||
1335 | |||
1336 | if (ACPI_FAILURE(status)) | ||
1337 | return -EIO; | ||
1338 | |||
1339 | obj = (union acpi_object *)output.pointer; | ||
1340 | if (obj && obj->type == ACPI_TYPE_INTEGER) | ||
1341 | seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id, | ||
1342 | asus->debug.dev_id, asus->debug.ctrl_param, | ||
1343 | (u32) obj->integer.value); | ||
1344 | else | ||
1345 | seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id, | ||
1346 | asus->debug.dev_id, asus->debug.ctrl_param, | ||
1347 | obj ? obj->type : -1); | ||
1348 | |||
1349 | kfree(obj); | ||
1350 | |||
1351 | return 0; | ||
1352 | } | ||
1353 | |||
1354 | static struct asus_wmi_debugfs_node asus_wmi_debug_files[] = { | ||
1355 | {NULL, "devs", show_devs}, | ||
1356 | {NULL, "dsts", show_dsts}, | ||
1357 | {NULL, "call", show_call}, | ||
1358 | }; | ||
1359 | |||
1360 | static int asus_wmi_debugfs_open(struct inode *inode, struct file *file) | ||
1361 | { | ||
1362 | struct asus_wmi_debugfs_node *node = inode->i_private; | ||
1363 | |||
1364 | return single_open(file, node->show, node->asus); | ||
1365 | } | ||
1366 | |||
1367 | static const struct file_operations asus_wmi_debugfs_io_ops = { | ||
1368 | .owner = THIS_MODULE, | ||
1369 | .open = asus_wmi_debugfs_open, | ||
1370 | .read = seq_read, | ||
1371 | .llseek = seq_lseek, | ||
1372 | .release = single_release, | ||
1373 | }; | ||
1374 | |||
1375 | static void asus_wmi_debugfs_exit(struct asus_wmi *asus) | ||
1376 | { | ||
1377 | debugfs_remove_recursive(asus->debug.root); | ||
1378 | } | ||
1379 | |||
1380 | static int asus_wmi_debugfs_init(struct asus_wmi *asus) | ||
1381 | { | ||
1382 | struct dentry *dent; | ||
1383 | int i; | ||
1384 | |||
1385 | asus->debug.root = debugfs_create_dir(asus->driver->name, NULL); | ||
1386 | if (!asus->debug.root) { | ||
1387 | pr_err("failed to create debugfs directory"); | ||
1388 | goto error_debugfs; | ||
1389 | } | ||
1390 | |||
1391 | dent = debugfs_create_x32("method_id", S_IRUGO | S_IWUSR, | ||
1392 | asus->debug.root, &asus->debug.method_id); | ||
1393 | if (!dent) | ||
1394 | goto error_debugfs; | ||
1395 | |||
1396 | dent = debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, | ||
1397 | asus->debug.root, &asus->debug.dev_id); | ||
1398 | if (!dent) | ||
1399 | goto error_debugfs; | ||
1400 | |||
1401 | dent = debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR, | ||
1402 | asus->debug.root, &asus->debug.ctrl_param); | ||
1403 | if (!dent) | ||
1404 | goto error_debugfs; | ||
1405 | |||
1406 | for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) { | ||
1407 | struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i]; | ||
1408 | |||
1409 | node->asus = asus; | ||
1410 | dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, | ||
1411 | asus->debug.root, node, | ||
1412 | &asus_wmi_debugfs_io_ops); | ||
1413 | if (!dent) { | ||
1414 | pr_err("failed to create debug file: %s\n", node->name); | ||
1415 | goto error_debugfs; | ||
1416 | } | ||
1417 | } | ||
1418 | |||
1419 | return 0; | ||
1420 | |||
1421 | error_debugfs: | ||
1422 | asus_wmi_debugfs_exit(asus); | ||
1423 | return -ENOMEM; | ||
1424 | } | ||
1425 | |||
1426 | /* | ||
1427 | * WMI Driver | ||
1428 | */ | ||
1429 | static int asus_wmi_add(struct platform_device *pdev) | ||
1430 | { | ||
1431 | struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver); | ||
1432 | struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv); | ||
1433 | struct asus_wmi *asus; | ||
1434 | acpi_status status; | ||
1435 | int err; | ||
1436 | |||
1437 | asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL); | ||
1438 | if (!asus) | ||
1439 | return -ENOMEM; | ||
1440 | |||
1441 | asus->driver = wdrv; | ||
1442 | asus->platform_device = pdev; | ||
1443 | wdrv->platform_device = pdev; | ||
1444 | platform_set_drvdata(asus->platform_device, asus); | ||
1445 | |||
1446 | if (wdrv->quirks) | ||
1447 | wdrv->quirks(asus->driver); | ||
1448 | |||
1449 | err = asus_wmi_platform_init(asus); | ||
1450 | if (err) | ||
1451 | goto fail_platform; | ||
1452 | |||
1453 | err = asus_wmi_input_init(asus); | ||
1454 | if (err) | ||
1455 | goto fail_input; | ||
1456 | |||
1457 | err = asus_wmi_hwmon_init(asus); | ||
1458 | if (err) | ||
1459 | goto fail_hwmon; | ||
1460 | |||
1461 | err = asus_wmi_led_init(asus); | ||
1462 | if (err) | ||
1463 | goto fail_leds; | ||
1464 | |||
1465 | err = asus_wmi_rfkill_init(asus); | ||
1466 | if (err) | ||
1467 | goto fail_rfkill; | ||
1468 | |||
1469 | if (!acpi_video_backlight_support()) { | ||
1470 | err = asus_wmi_backlight_init(asus); | ||
1471 | if (err && err != -ENODEV) | ||
1472 | goto fail_backlight; | ||
1473 | } else | ||
1474 | pr_info("Backlight controlled by ACPI video driver\n"); | ||
1475 | |||
1476 | status = wmi_install_notify_handler(asus->driver->event_guid, | ||
1477 | asus_wmi_notify, asus); | ||
1478 | if (ACPI_FAILURE(status)) { | ||
1479 | pr_err("Unable to register notify handler - %d\n", status); | ||
1480 | err = -ENODEV; | ||
1481 | goto fail_wmi_handler; | ||
1482 | } | ||
1483 | |||
1484 | err = asus_wmi_debugfs_init(asus); | ||
1485 | if (err) | ||
1486 | goto fail_debugfs; | ||
1487 | |||
1488 | return 0; | ||
1489 | |||
1490 | fail_debugfs: | ||
1491 | wmi_remove_notify_handler(asus->driver->event_guid); | ||
1492 | fail_wmi_handler: | ||
1493 | asus_wmi_backlight_exit(asus); | ||
1494 | fail_backlight: | ||
1495 | asus_wmi_rfkill_exit(asus); | ||
1496 | fail_rfkill: | ||
1497 | asus_wmi_led_exit(asus); | ||
1498 | fail_leds: | ||
1499 | asus_wmi_hwmon_exit(asus); | ||
1500 | fail_hwmon: | ||
1501 | asus_wmi_input_exit(asus); | ||
1502 | fail_input: | ||
1503 | asus_wmi_platform_exit(asus); | ||
1504 | fail_platform: | ||
1505 | kfree(asus); | ||
1506 | return err; | ||
1507 | } | ||
1508 | |||
1509 | static int asus_wmi_remove(struct platform_device *device) | ||
1510 | { | ||
1511 | struct asus_wmi *asus; | ||
1512 | |||
1513 | asus = platform_get_drvdata(device); | ||
1514 | wmi_remove_notify_handler(asus->driver->event_guid); | ||
1515 | asus_wmi_backlight_exit(asus); | ||
1516 | asus_wmi_input_exit(asus); | ||
1517 | asus_wmi_hwmon_exit(asus); | ||
1518 | asus_wmi_led_exit(asus); | ||
1519 | asus_wmi_rfkill_exit(asus); | ||
1520 | asus_wmi_debugfs_exit(asus); | ||
1521 | asus_wmi_platform_exit(asus); | ||
1522 | |||
1523 | kfree(asus); | ||
1524 | return 0; | ||
1525 | } | ||
1526 | |||
1527 | /* | ||
1528 | * Platform driver - hibernate/resume callbacks | ||
1529 | */ | ||
1530 | static int asus_hotk_thaw(struct device *device) | ||
1531 | { | ||
1532 | struct asus_wmi *asus = dev_get_drvdata(device); | ||
1533 | |||
1534 | if (asus->wlan.rfkill) { | ||
1535 | bool wlan; | ||
1536 | |||
1537 | /* | ||
1538 | * Work around bios bug - acpi _PTS turns off the wireless led | ||
1539 | * during suspend. Normally it restores it on resume, but | ||
1540 | * we should kick it ourselves in case hibernation is aborted. | ||
1541 | */ | ||
1542 | wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); | ||
1543 | asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL); | ||
1544 | } | ||
1545 | |||
1546 | return 0; | ||
1547 | } | ||
1548 | |||
1549 | static int asus_hotk_restore(struct device *device) | ||
1550 | { | ||
1551 | struct asus_wmi *asus = dev_get_drvdata(device); | ||
1552 | int bl; | ||
1553 | |||
1554 | /* Refresh both wlan rfkill state and pci hotplug */ | ||
1555 | if (asus->wlan.rfkill) | ||
1556 | asus_rfkill_hotplug(asus); | ||
1557 | |||
1558 | if (asus->bluetooth.rfkill) { | ||
1559 | bl = !asus_wmi_get_devstate_simple(asus, | ||
1560 | ASUS_WMI_DEVID_BLUETOOTH); | ||
1561 | rfkill_set_sw_state(asus->bluetooth.rfkill, bl); | ||
1562 | } | ||
1563 | if (asus->wimax.rfkill) { | ||
1564 | bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX); | ||
1565 | rfkill_set_sw_state(asus->wimax.rfkill, bl); | ||
1566 | } | ||
1567 | if (asus->wwan3g.rfkill) { | ||
1568 | bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G); | ||
1569 | rfkill_set_sw_state(asus->wwan3g.rfkill, bl); | ||
1570 | } | ||
1571 | |||
1572 | return 0; | ||
1573 | } | ||
1574 | |||
1575 | static const struct dev_pm_ops asus_pm_ops = { | ||
1576 | .thaw = asus_hotk_thaw, | ||
1577 | .restore = asus_hotk_restore, | ||
1578 | }; | ||
1579 | |||
1580 | static int asus_wmi_probe(struct platform_device *pdev) | ||
1581 | { | ||
1582 | struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver); | ||
1583 | struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv); | ||
1584 | int ret; | ||
1585 | |||
1586 | if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) { | ||
1587 | pr_warn("Management GUID not found\n"); | ||
1588 | return -ENODEV; | ||
1589 | } | ||
1590 | |||
1591 | if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) { | ||
1592 | pr_warn("Event GUID not found\n"); | ||
1593 | return -ENODEV; | ||
1594 | } | ||
1595 | |||
1596 | if (wdrv->probe) { | ||
1597 | ret = wdrv->probe(pdev); | ||
1598 | if (ret) | ||
1599 | return ret; | ||
1600 | } | ||
1601 | |||
1602 | return asus_wmi_add(pdev); | ||
1603 | } | ||
1604 | |||
1605 | static bool used; | ||
1606 | |||
1607 | int asus_wmi_register_driver(struct asus_wmi_driver *driver) | ||
1608 | { | ||
1609 | struct platform_driver *platform_driver; | ||
1610 | struct platform_device *platform_device; | ||
1611 | |||
1612 | if (used) | ||
1613 | return -EBUSY; | ||
1614 | |||
1615 | platform_driver = &driver->platform_driver; | ||
1616 | platform_driver->remove = asus_wmi_remove; | ||
1617 | platform_driver->driver.owner = driver->owner; | ||
1618 | platform_driver->driver.name = driver->name; | ||
1619 | platform_driver->driver.pm = &asus_pm_ops; | ||
1620 | |||
1621 | platform_device = platform_create_bundle(platform_driver, | ||
1622 | asus_wmi_probe, | ||
1623 | NULL, 0, NULL, 0); | ||
1624 | if (IS_ERR(platform_device)) | ||
1625 | return PTR_ERR(platform_device); | ||
1626 | |||
1627 | used = true; | ||
1628 | return 0; | ||
1629 | } | ||
1630 | EXPORT_SYMBOL_GPL(asus_wmi_register_driver); | ||
1631 | |||
1632 | void asus_wmi_unregister_driver(struct asus_wmi_driver *driver) | ||
1633 | { | ||
1634 | platform_device_unregister(driver->platform_device); | ||
1635 | platform_driver_unregister(&driver->platform_driver); | ||
1636 | used = false; | ||
1637 | } | ||
1638 | EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver); | ||
1639 | |||
1640 | static int __init asus_wmi_init(void) | ||
1641 | { | ||
1642 | if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) { | ||
1643 | pr_info("Asus Management GUID not found"); | ||
1644 | return -ENODEV; | ||
1645 | } | ||
1646 | |||
1647 | pr_info("ASUS WMI generic driver loaded"); | ||
1648 | return 0; | ||
1649 | } | ||
1650 | |||
1651 | static void __exit asus_wmi_exit(void) | ||
1652 | { | ||
1653 | pr_info("ASUS WMI generic driver unloaded"); | ||
1654 | } | ||
1655 | |||
1656 | module_init(asus_wmi_init); | ||
1657 | module_exit(asus_wmi_exit); | ||
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h new file mode 100644 index 000000000000..c044522c8766 --- /dev/null +++ b/drivers/platform/x86/asus-wmi.h | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * Asus PC WMI hotkey driver | ||
3 | * | ||
4 | * Copyright(C) 2010 Intel Corporation. | ||
5 | * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com> | ||
6 | * | ||
7 | * Portions based on wistron_btns.c: | ||
8 | * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | ||
9 | * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> | ||
10 | * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | */ | ||
26 | |||
27 | #ifndef _ASUS_WMI_H_ | ||
28 | #define _ASUS_WMI_H_ | ||
29 | |||
30 | #include <linux/platform_device.h> | ||
31 | |||
32 | struct module; | ||
33 | struct key_entry; | ||
34 | struct asus_wmi; | ||
35 | |||
36 | struct asus_wmi_driver { | ||
37 | bool hotplug_wireless; | ||
38 | |||
39 | const char *name; | ||
40 | struct module *owner; | ||
41 | |||
42 | const char *event_guid; | ||
43 | |||
44 | const struct key_entry *keymap; | ||
45 | const char *input_name; | ||
46 | const char *input_phys; | ||
47 | |||
48 | int (*probe) (struct platform_device *device); | ||
49 | void (*quirks) (struct asus_wmi_driver *driver); | ||
50 | |||
51 | struct platform_driver platform_driver; | ||
52 | struct platform_device *platform_device; | ||
53 | }; | ||
54 | |||
55 | int asus_wmi_register_driver(struct asus_wmi_driver *driver); | ||
56 | void asus_wmi_unregister_driver(struct asus_wmi_driver *driver); | ||
57 | |||
58 | #endif /* !_ASUS_WMI_H_ */ | ||
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index ca05aefd03bf..d9312b3073e5 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c | |||
@@ -30,6 +30,8 @@ | |||
30 | * | 30 | * |
31 | */ | 31 | */ |
32 | 32 | ||
33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
34 | |||
33 | #include <linux/kernel.h> | 35 | #include <linux/kernel.h> |
34 | #include <linux/module.h> | 36 | #include <linux/module.h> |
35 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
@@ -581,8 +583,7 @@ static int read_led(const char *ledname, int ledmask) | |||
581 | if (read_acpi_int(NULL, ledname, &led_status)) | 583 | if (read_acpi_int(NULL, ledname, &led_status)) |
582 | return led_status; | 584 | return led_status; |
583 | else | 585 | else |
584 | printk(KERN_WARNING "Asus ACPI: Error reading LED " | 586 | pr_warn("Error reading LED status\n"); |
585 | "status\n"); | ||
586 | } | 587 | } |
587 | return (hotk->status & ledmask) ? 1 : 0; | 588 | return (hotk->status & ledmask) ? 1 : 0; |
588 | } | 589 | } |
@@ -621,8 +622,7 @@ write_led(const char __user *buffer, unsigned long count, | |||
621 | led_out = !led_out; | 622 | led_out = !led_out; |
622 | 623 | ||
623 | if (!write_acpi_int(hotk->handle, ledname, led_out, NULL)) | 624 | if (!write_acpi_int(hotk->handle, ledname, led_out, NULL)) |
624 | printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n", | 625 | pr_warn("LED (%s) write failed\n", ledname); |
625 | ledname); | ||
626 | 626 | ||
627 | return rv; | 627 | return rv; |
628 | } | 628 | } |
@@ -679,8 +679,7 @@ static ssize_t ledd_proc_write(struct file *file, const char __user *buffer, | |||
679 | if (rv > 0) { | 679 | if (rv > 0) { |
680 | if (!write_acpi_int | 680 | if (!write_acpi_int |
681 | (hotk->handle, hotk->methods->mt_ledd, value, NULL)) | 681 | (hotk->handle, hotk->methods->mt_ledd, value, NULL)) |
682 | printk(KERN_WARNING | 682 | pr_warn("LED display write failed\n"); |
683 | "Asus ACPI: LED display write failed\n"); | ||
684 | else | 683 | else |
685 | hotk->ledd_status = (u32) value; | 684 | hotk->ledd_status = (u32) value; |
686 | } | 685 | } |
@@ -838,8 +837,7 @@ static int get_lcd_state(void) | |||
838 | } else { | 837 | } else { |
839 | /* We don't have to check anything if we are here */ | 838 | /* We don't have to check anything if we are here */ |
840 | if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) | 839 | if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) |
841 | printk(KERN_WARNING | 840 | pr_warn("Error reading LCD status\n"); |
842 | "Asus ACPI: Error reading LCD status\n"); | ||
843 | 841 | ||
844 | if (hotk->model == L2D) | 842 | if (hotk->model == L2D) |
845 | lcd = ~lcd; | 843 | lcd = ~lcd; |
@@ -871,7 +869,7 @@ static int set_lcd_state(int value) | |||
871 | the exact behaviour is simulated here */ | 869 | the exact behaviour is simulated here */ |
872 | } | 870 | } |
873 | if (ACPI_FAILURE(status)) | 871 | if (ACPI_FAILURE(status)) |
874 | printk(KERN_WARNING "Asus ACPI: Error switching LCD\n"); | 872 | pr_warn("Error switching LCD\n"); |
875 | } | 873 | } |
876 | return 0; | 874 | return 0; |
877 | 875 | ||
@@ -915,13 +913,11 @@ static int read_brightness(struct backlight_device *bd) | |||
915 | if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */ | 913 | if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */ |
916 | if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get, | 914 | if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get, |
917 | &value)) | 915 | &value)) |
918 | printk(KERN_WARNING | 916 | pr_warn("Error reading brightness\n"); |
919 | "Asus ACPI: Error reading brightness\n"); | ||
920 | } else if (hotk->methods->brightness_status) { /* For D1 for example */ | 917 | } else if (hotk->methods->brightness_status) { /* For D1 for example */ |
921 | if (!read_acpi_int(NULL, hotk->methods->brightness_status, | 918 | if (!read_acpi_int(NULL, hotk->methods->brightness_status, |
922 | &value)) | 919 | &value)) |
923 | printk(KERN_WARNING | 920 | pr_warn("Error reading brightness\n"); |
924 | "Asus ACPI: Error reading brightness\n"); | ||
925 | } else /* No GPLV method */ | 921 | } else /* No GPLV method */ |
926 | value = hotk->brightness; | 922 | value = hotk->brightness; |
927 | return value; | 923 | return value; |
@@ -939,8 +935,7 @@ static int set_brightness(int value) | |||
939 | if (hotk->methods->brightness_set) { | 935 | if (hotk->methods->brightness_set) { |
940 | if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set, | 936 | if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set, |
941 | value, NULL)) { | 937 | value, NULL)) { |
942 | printk(KERN_WARNING | 938 | pr_warn("Error changing brightness\n"); |
943 | "Asus ACPI: Error changing brightness\n"); | ||
944 | ret = -EIO; | 939 | ret = -EIO; |
945 | } | 940 | } |
946 | goto out; | 941 | goto out; |
@@ -955,8 +950,7 @@ static int set_brightness(int value) | |||
955 | NULL, NULL); | 950 | NULL, NULL); |
956 | (value > 0) ? value-- : value++; | 951 | (value > 0) ? value-- : value++; |
957 | if (ACPI_FAILURE(status)) { | 952 | if (ACPI_FAILURE(status)) { |
958 | printk(KERN_WARNING | 953 | pr_warn("Error changing brightness\n"); |
959 | "Asus ACPI: Error changing brightness\n"); | ||
960 | ret = -EIO; | 954 | ret = -EIO; |
961 | } | 955 | } |
962 | } | 956 | } |
@@ -1008,7 +1002,7 @@ static void set_display(int value) | |||
1008 | /* no sanity check needed for now */ | 1002 | /* no sanity check needed for now */ |
1009 | if (!write_acpi_int(hotk->handle, hotk->methods->display_set, | 1003 | if (!write_acpi_int(hotk->handle, hotk->methods->display_set, |
1010 | value, NULL)) | 1004 | value, NULL)) |
1011 | printk(KERN_WARNING "Asus ACPI: Error setting display\n"); | 1005 | pr_warn("Error setting display\n"); |
1012 | return; | 1006 | return; |
1013 | } | 1007 | } |
1014 | 1008 | ||
@@ -1021,8 +1015,7 @@ static int disp_proc_show(struct seq_file *m, void *v) | |||
1021 | int value = 0; | 1015 | int value = 0; |
1022 | 1016 | ||
1023 | if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value)) | 1017 | if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value)) |
1024 | printk(KERN_WARNING | 1018 | pr_warn("Error reading display status\n"); |
1025 | "Asus ACPI: Error reading display status\n"); | ||
1026 | value &= 0x07; /* needed for some models, shouldn't hurt others */ | 1019 | value &= 0x07; /* needed for some models, shouldn't hurt others */ |
1027 | seq_printf(m, "%d\n", value); | 1020 | seq_printf(m, "%d\n", value); |
1028 | return 0; | 1021 | return 0; |
@@ -1068,7 +1061,7 @@ asus_proc_add(char *name, const struct file_operations *proc_fops, mode_t mode, | |||
1068 | proc = proc_create_data(name, mode, acpi_device_dir(device), | 1061 | proc = proc_create_data(name, mode, acpi_device_dir(device), |
1069 | proc_fops, acpi_driver_data(device)); | 1062 | proc_fops, acpi_driver_data(device)); |
1070 | if (!proc) { | 1063 | if (!proc) { |
1071 | printk(KERN_WARNING " Unable to create %s fs entry\n", name); | 1064 | pr_warn(" Unable to create %s fs entry\n", name); |
1072 | return -1; | 1065 | return -1; |
1073 | } | 1066 | } |
1074 | proc->uid = asus_uid; | 1067 | proc->uid = asus_uid; |
@@ -1081,18 +1074,12 @@ static int asus_hotk_add_fs(struct acpi_device *device) | |||
1081 | struct proc_dir_entry *proc; | 1074 | struct proc_dir_entry *proc; |
1082 | mode_t mode; | 1075 | mode_t mode; |
1083 | 1076 | ||
1084 | /* | ||
1085 | * If parameter uid or gid is not changed, keep the default setting for | ||
1086 | * our proc entries (-rw-rw-rw-) else, it means we care about security, | ||
1087 | * and then set to -rw-rw---- | ||
1088 | */ | ||
1089 | |||
1090 | if ((asus_uid == 0) && (asus_gid == 0)) { | 1077 | if ((asus_uid == 0) && (asus_gid == 0)) { |
1091 | mode = S_IFREG | S_IRUGO | S_IWUGO; | 1078 | mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP; |
1092 | } else { | 1079 | } else { |
1093 | mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP; | 1080 | mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP; |
1094 | printk(KERN_WARNING " asus_uid and asus_gid parameters are " | 1081 | pr_warn(" asus_uid and asus_gid parameters are " |
1095 | "deprecated, use chown and chmod instead!\n"); | 1082 | "deprecated, use chown and chmod instead!\n"); |
1096 | } | 1083 | } |
1097 | 1084 | ||
1098 | acpi_device_dir(device) = asus_proc_dir; | 1085 | acpi_device_dir(device) = asus_proc_dir; |
@@ -1105,8 +1092,7 @@ static int asus_hotk_add_fs(struct acpi_device *device) | |||
1105 | proc->uid = asus_uid; | 1092 | proc->uid = asus_uid; |
1106 | proc->gid = asus_gid; | 1093 | proc->gid = asus_gid; |
1107 | } else { | 1094 | } else { |
1108 | printk(KERN_WARNING " Unable to create " PROC_INFO | 1095 | pr_warn(" Unable to create " PROC_INFO " fs entry\n"); |
1109 | " fs entry\n"); | ||
1110 | } | 1096 | } |
1111 | 1097 | ||
1112 | if (hotk->methods->mt_wled) { | 1098 | if (hotk->methods->mt_wled) { |
@@ -1289,20 +1275,19 @@ static int asus_hotk_get_info(void) | |||
1289 | */ | 1275 | */ |
1290 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); | 1276 | status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); |
1291 | if (ACPI_FAILURE(status)) | 1277 | if (ACPI_FAILURE(status)) |
1292 | printk(KERN_WARNING " Couldn't get the DSDT table header\n"); | 1278 | pr_warn(" Couldn't get the DSDT table header\n"); |
1293 | 1279 | ||
1294 | /* We have to write 0 on init this far for all ASUS models */ | 1280 | /* We have to write 0 on init this far for all ASUS models */ |
1295 | if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { | 1281 | if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { |
1296 | printk(KERN_ERR " Hotkey initialization failed\n"); | 1282 | pr_err(" Hotkey initialization failed\n"); |
1297 | return -ENODEV; | 1283 | return -ENODEV; |
1298 | } | 1284 | } |
1299 | 1285 | ||
1300 | /* This needs to be called for some laptops to init properly */ | 1286 | /* This needs to be called for some laptops to init properly */ |
1301 | if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result)) | 1287 | if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result)) |
1302 | printk(KERN_WARNING " Error calling BSTS\n"); | 1288 | pr_warn(" Error calling BSTS\n"); |
1303 | else if (bsts_result) | 1289 | else if (bsts_result) |
1304 | printk(KERN_NOTICE " BSTS called, 0x%02x returned\n", | 1290 | pr_notice(" BSTS called, 0x%02x returned\n", bsts_result); |
1305 | bsts_result); | ||
1306 | 1291 | ||
1307 | /* | 1292 | /* |
1308 | * Try to match the object returned by INIT to the specific model. | 1293 | * Try to match the object returned by INIT to the specific model. |
@@ -1330,23 +1315,21 @@ static int asus_hotk_get_info(void) | |||
1330 | if (asus_info && | 1315 | if (asus_info && |
1331 | strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) { | 1316 | strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) { |
1332 | hotk->model = P30; | 1317 | hotk->model = P30; |
1333 | printk(KERN_NOTICE | 1318 | pr_notice(" Samsung P30 detected, supported\n"); |
1334 | " Samsung P30 detected, supported\n"); | ||
1335 | hotk->methods = &model_conf[hotk->model]; | 1319 | hotk->methods = &model_conf[hotk->model]; |
1336 | kfree(model); | 1320 | kfree(model); |
1337 | return 0; | 1321 | return 0; |
1338 | } else { | 1322 | } else { |
1339 | hotk->model = M2E; | 1323 | hotk->model = M2E; |
1340 | printk(KERN_NOTICE " unsupported model %s, trying " | 1324 | pr_notice(" unsupported model %s, trying default values\n", |
1341 | "default values\n", string); | 1325 | string); |
1342 | printk(KERN_NOTICE | 1326 | pr_notice(" send /proc/acpi/dsdt to the developers\n"); |
1343 | " send /proc/acpi/dsdt to the developers\n"); | ||
1344 | kfree(model); | 1327 | kfree(model); |
1345 | return -ENODEV; | 1328 | return -ENODEV; |
1346 | } | 1329 | } |
1347 | } | 1330 | } |
1348 | hotk->methods = &model_conf[hotk->model]; | 1331 | hotk->methods = &model_conf[hotk->model]; |
1349 | printk(KERN_NOTICE " %s model detected, supported\n", string); | 1332 | pr_notice(" %s model detected, supported\n", string); |
1350 | 1333 | ||
1351 | /* Sort of per-model blacklist */ | 1334 | /* Sort of per-model blacklist */ |
1352 | if (strncmp(string, "L2B", 3) == 0) | 1335 | if (strncmp(string, "L2B", 3) == 0) |
@@ -1391,7 +1374,7 @@ static int asus_hotk_check(void) | |||
1391 | if (hotk->device->status.present) { | 1374 | if (hotk->device->status.present) { |
1392 | result = asus_hotk_get_info(); | 1375 | result = asus_hotk_get_info(); |
1393 | } else { | 1376 | } else { |
1394 | printk(KERN_ERR " Hotkey device not present, aborting\n"); | 1377 | pr_err(" Hotkey device not present, aborting\n"); |
1395 | return -EINVAL; | 1378 | return -EINVAL; |
1396 | } | 1379 | } |
1397 | 1380 | ||
@@ -1405,8 +1388,7 @@ static int asus_hotk_add(struct acpi_device *device) | |||
1405 | acpi_status status = AE_OK; | 1388 | acpi_status status = AE_OK; |
1406 | int result; | 1389 | int result; |
1407 | 1390 | ||
1408 | printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n", | 1391 | pr_notice("Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION); |
1409 | ASUS_ACPI_VERSION); | ||
1410 | 1392 | ||
1411 | hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); | 1393 | hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); |
1412 | if (!hotk) | 1394 | if (!hotk) |
@@ -1434,15 +1416,14 @@ static int asus_hotk_add(struct acpi_device *device) | |||
1434 | acpi_evaluate_object(NULL, hotk->methods->brightness_down, | 1416 | acpi_evaluate_object(NULL, hotk->methods->brightness_down, |
1435 | NULL, NULL); | 1417 | NULL, NULL); |
1436 | if (ACPI_FAILURE(status)) | 1418 | if (ACPI_FAILURE(status)) |
1437 | printk(KERN_WARNING " Error changing brightness\n"); | 1419 | pr_warn(" Error changing brightness\n"); |
1438 | else { | 1420 | else { |
1439 | status = | 1421 | status = |
1440 | acpi_evaluate_object(NULL, | 1422 | acpi_evaluate_object(NULL, |
1441 | hotk->methods->brightness_up, | 1423 | hotk->methods->brightness_up, |
1442 | NULL, NULL); | 1424 | NULL, NULL); |
1443 | if (ACPI_FAILURE(status)) | 1425 | if (ACPI_FAILURE(status)) |
1444 | printk(KERN_WARNING " Strange, error changing" | 1426 | pr_warn(" Strange, error changing brightness\n"); |
1445 | " brightness\n"); | ||
1446 | } | 1427 | } |
1447 | } | 1428 | } |
1448 | 1429 | ||
@@ -1467,7 +1448,7 @@ static int asus_hotk_remove(struct acpi_device *device, int type) | |||
1467 | return 0; | 1448 | return 0; |
1468 | } | 1449 | } |
1469 | 1450 | ||
1470 | static struct backlight_ops asus_backlight_data = { | 1451 | static const struct backlight_ops asus_backlight_data = { |
1471 | .get_brightness = read_brightness, | 1452 | .get_brightness = read_brightness, |
1472 | .update_status = set_brightness_status, | 1453 | .update_status = set_brightness_status, |
1473 | }; | 1454 | }; |
@@ -1494,7 +1475,7 @@ static int __init asus_acpi_init(void) | |||
1494 | 1475 | ||
1495 | asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); | 1476 | asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); |
1496 | if (!asus_proc_dir) { | 1477 | if (!asus_proc_dir) { |
1497 | printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); | 1478 | pr_err("Unable to create /proc entry\n"); |
1498 | acpi_bus_unregister_driver(&asus_hotk_driver); | 1479 | acpi_bus_unregister_driver(&asus_hotk_driver); |
1499 | return -ENODEV; | 1480 | return -ENODEV; |
1500 | } | 1481 | } |
@@ -1513,12 +1494,13 @@ static int __init asus_acpi_init(void) | |||
1513 | } | 1494 | } |
1514 | 1495 | ||
1515 | memset(&props, 0, sizeof(struct backlight_properties)); | 1496 | memset(&props, 0, sizeof(struct backlight_properties)); |
1497 | props.type = BACKLIGHT_PLATFORM; | ||
1516 | props.max_brightness = 15; | 1498 | props.max_brightness = 15; |
1517 | asus_backlight_device = backlight_device_register("asus", NULL, NULL, | 1499 | asus_backlight_device = backlight_device_register("asus", NULL, NULL, |
1518 | &asus_backlight_data, | 1500 | &asus_backlight_data, |
1519 | &props); | 1501 | &props); |
1520 | if (IS_ERR(asus_backlight_device)) { | 1502 | if (IS_ERR(asus_backlight_device)) { |
1521 | printk(KERN_ERR "Could not register asus backlight device\n"); | 1503 | pr_err("Could not register asus backlight device\n"); |
1522 | asus_backlight_device = NULL; | 1504 | asus_backlight_device = NULL; |
1523 | asus_acpi_exit(); | 1505 | asus_acpi_exit(); |
1524 | return -ENODEV; | 1506 | return -ENODEV; |
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 341cbfef93ee..94f93b621d7b 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 | ||
@@ -562,6 +564,7 @@ static int cmpc_ipml_add(struct acpi_device *acpi) | |||
562 | return -ENOMEM; | 564 | return -ENOMEM; |
563 | 565 | ||
564 | memset(&props, 0, sizeof(struct backlight_properties)); | 566 | memset(&props, 0, sizeof(struct backlight_properties)); |
567 | props.type = BACKLIGHT_PLATFORM; | ||
565 | props.max_brightness = 7; | 568 | props.max_brightness = 7; |
566 | ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, | 569 | ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, |
567 | acpi->handle, &cmpc_bl_ops, | 570 | acpi->handle, &cmpc_bl_ops, |
@@ -653,8 +656,9 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event) | |||
653 | 656 | ||
654 | if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) | 657 | if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) |
655 | code = cmpc_keys_codes[event & 0x0F]; | 658 | code = cmpc_keys_codes[event & 0x0F]; |
656 | inputdev = dev_get_drvdata(&dev->dev);; | 659 | inputdev = dev_get_drvdata(&dev->dev); |
657 | input_report_key(inputdev, code, !(event & 0x10)); | 660 | input_report_key(inputdev, code, !(event & 0x10)); |
661 | input_sync(inputdev); | ||
658 | } | 662 | } |
659 | 663 | ||
660 | static void cmpc_keys_idev_init(struct input_dev *inputdev) | 664 | 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..8877b836d27c 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c | |||
@@ -68,6 +68,8 @@ | |||
68 | * only enabled on a JHL90 board until it is verified that they work on the | 68 | * only enabled on a JHL90 board until it is verified that they work on the |
69 | * other boards too. See the extra_features variable. */ | 69 | * other boards too. See the extra_features variable. */ |
70 | 70 | ||
71 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
72 | |||
71 | #include <linux/module.h> | 73 | #include <linux/module.h> |
72 | #include <linux/kernel.h> | 74 | #include <linux/kernel.h> |
73 | #include <linux/init.h> | 75 | #include <linux/init.h> |
@@ -200,8 +202,8 @@ static bool extra_features; | |||
200 | * watching the output of address 0x4F (do an ec_transaction writing 0x33 | 202 | * watching the output of address 0x4F (do an ec_transaction writing 0x33 |
201 | * into 0x4F and read a few bytes from the output, like so: | 203 | * into 0x4F and read a few bytes from the output, like so: |
202 | * u8 writeData = 0x33; | 204 | * u8 writeData = 0x33; |
203 | * ec_transaction(0x4F, &writeData, 1, buffer, 32, 0); | 205 | * ec_transaction(0x4F, &writeData, 1, buffer, 32); |
204 | * That address is labled "fan1 table information" in the service manual. | 206 | * That address is labeled "fan1 table information" in the service manual. |
205 | * It should be clear which value in 'buffer' changes). This seems to be | 207 | * It should be clear which value in 'buffer' changes). This seems to be |
206 | * related to fan speed. It isn't a proper 'realtime' fan speed value | 208 | * related to fan speed. It isn't a proper 'realtime' fan speed value |
207 | * though, because physically stopping or speeding up the fan doesn't | 209 | * though, because physically stopping or speeding up the fan doesn't |
@@ -275,7 +277,7 @@ static int set_backlight_level(int level) | |||
275 | 277 | ||
276 | ec_write(BACKLIGHT_LEVEL_ADDR, level); | 278 | ec_write(BACKLIGHT_LEVEL_ADDR, level); |
277 | 279 | ||
278 | return 1; | 280 | return 0; |
279 | } | 281 | } |
280 | 282 | ||
281 | static int get_backlight_level(void) | 283 | static int get_backlight_level(void) |
@@ -286,7 +288,7 @@ static int get_backlight_level(void) | |||
286 | static void set_backlight_state(bool on) | 288 | static void set_backlight_state(bool on) |
287 | { | 289 | { |
288 | u8 data = on ? BACKLIGHT_STATE_ON_DATA : BACKLIGHT_STATE_OFF_DATA; | 290 | u8 data = on ? BACKLIGHT_STATE_ON_DATA : BACKLIGHT_STATE_OFF_DATA; |
289 | ec_transaction(BACKLIGHT_STATE_ADDR, &data, 1, NULL, 0, 0); | 291 | ec_transaction(BACKLIGHT_STATE_ADDR, &data, 1, NULL, 0); |
290 | } | 292 | } |
291 | 293 | ||
292 | 294 | ||
@@ -294,24 +296,24 @@ static void set_backlight_state(bool on) | |||
294 | static void pwm_enable_control(void) | 296 | static void pwm_enable_control(void) |
295 | { | 297 | { |
296 | unsigned char writeData = PWM_ENABLE_DATA; | 298 | unsigned char writeData = PWM_ENABLE_DATA; |
297 | ec_transaction(PWM_ENABLE_ADDR, &writeData, 1, NULL, 0, 0); | 299 | ec_transaction(PWM_ENABLE_ADDR, &writeData, 1, NULL, 0); |
298 | } | 300 | } |
299 | 301 | ||
300 | static void pwm_disable_control(void) | 302 | static void pwm_disable_control(void) |
301 | { | 303 | { |
302 | unsigned char writeData = PWM_DISABLE_DATA; | 304 | unsigned char writeData = PWM_DISABLE_DATA; |
303 | ec_transaction(PWM_DISABLE_ADDR, &writeData, 1, NULL, 0, 0); | 305 | ec_transaction(PWM_DISABLE_ADDR, &writeData, 1, NULL, 0); |
304 | } | 306 | } |
305 | 307 | ||
306 | static void set_pwm(int pwm) | 308 | static void set_pwm(int pwm) |
307 | { | 309 | { |
308 | ec_transaction(PWM_ADDRESS, &pwm_lookup_table[pwm], 1, NULL, 0, 0); | 310 | ec_transaction(PWM_ADDRESS, &pwm_lookup_table[pwm], 1, NULL, 0); |
309 | } | 311 | } |
310 | 312 | ||
311 | static int get_fan_rpm(void) | 313 | static int get_fan_rpm(void) |
312 | { | 314 | { |
313 | u8 value, data = FAN_DATA; | 315 | u8 value, data = FAN_DATA; |
314 | ec_transaction(FAN_ADDRESS, &data, 1, &value, 1, 0); | 316 | ec_transaction(FAN_ADDRESS, &data, 1, &value, 1); |
315 | return 100 * (int)value; | 317 | return 100 * (int)value; |
316 | } | 318 | } |
317 | 319 | ||
@@ -760,19 +762,17 @@ static struct rfkill *bt_rfkill; | |||
760 | 762 | ||
761 | static int dmi_check_cb(const struct dmi_system_id *id) | 763 | static int dmi_check_cb(const struct dmi_system_id *id) |
762 | { | 764 | { |
763 | printk(KERN_INFO DRIVER_NAME": Identified laptop model '%s'\n", | 765 | pr_info("Identified laptop model '%s'\n", id->ident); |
764 | id->ident); | ||
765 | extra_features = false; | 766 | extra_features = false; |
766 | return 0; | 767 | return 1; |
767 | } | 768 | } |
768 | 769 | ||
769 | static int dmi_check_cb_extra(const struct dmi_system_id *id) | 770 | static int dmi_check_cb_extra(const struct dmi_system_id *id) |
770 | { | 771 | { |
771 | printk(KERN_INFO DRIVER_NAME": Identified laptop model '%s', " | 772 | pr_info("Identified laptop model '%s', enabling extra features\n", |
772 | "enabling extra features\n", | ||
773 | id->ident); | 773 | id->ident); |
774 | extra_features = true; | 774 | extra_features = true; |
775 | return 0; | 775 | return 1; |
776 | } | 776 | } |
777 | 777 | ||
778 | static struct dmi_system_id __initdata compal_dmi_table[] = { | 778 | static struct dmi_system_id __initdata compal_dmi_table[] = { |
@@ -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 | ||
@@ -948,20 +956,19 @@ static int __init compal_init(void) | |||
948 | int ret; | 956 | int ret; |
949 | 957 | ||
950 | if (acpi_disabled) { | 958 | if (acpi_disabled) { |
951 | printk(KERN_ERR DRIVER_NAME": ACPI needs to be enabled for " | 959 | pr_err("ACPI needs to be enabled for this driver to work!\n"); |
952 | "this driver to work!\n"); | ||
953 | return -ENODEV; | 960 | return -ENODEV; |
954 | } | 961 | } |
955 | 962 | ||
956 | if (!force && !dmi_check_system(compal_dmi_table)) { | 963 | if (!force && !dmi_check_system(compal_dmi_table)) { |
957 | printk(KERN_ERR DRIVER_NAME": Motherboard not recognized (You " | 964 | pr_err("Motherboard not recognized (You could try the module's force-parameter)\n"); |
958 | "could try the module's force-parameter)"); | ||
959 | return -ENODEV; | 965 | return -ENODEV; |
960 | } | 966 | } |
961 | 967 | ||
962 | if (!acpi_video_backlight_support()) { | 968 | if (!acpi_video_backlight_support()) { |
963 | struct backlight_properties props; | 969 | struct backlight_properties props; |
964 | memset(&props, 0, sizeof(struct backlight_properties)); | 970 | memset(&props, 0, sizeof(struct backlight_properties)); |
971 | props.type = BACKLIGHT_PLATFORM; | ||
965 | props.max_brightness = BACKLIGHT_LEVEL_MAX; | 972 | props.max_brightness = BACKLIGHT_LEVEL_MAX; |
966 | compalbl_device = backlight_device_register(DRIVER_NAME, | 973 | compalbl_device = backlight_device_register(DRIVER_NAME, |
967 | NULL, NULL, | 974 | NULL, NULL, |
@@ -989,8 +996,7 @@ static int __init compal_init(void) | |||
989 | if (ret) | 996 | if (ret) |
990 | goto err_rfkill; | 997 | goto err_rfkill; |
991 | 998 | ||
992 | printk(KERN_INFO DRIVER_NAME": Driver "DRIVER_VERSION | 999 | pr_info("Driver " DRIVER_VERSION " successfully loaded\n"); |
993 | " successfully loaded\n"); | ||
994 | return 0; | 1000 | return 0; |
995 | 1001 | ||
996 | err_rfkill: | 1002 | err_rfkill: |
@@ -1024,8 +1030,10 @@ static int __devinit compal_probe(struct platform_device *pdev) | |||
1024 | initialize_fan_control_data(data); | 1030 | initialize_fan_control_data(data); |
1025 | 1031 | ||
1026 | err = sysfs_create_group(&pdev->dev.kobj, &compal_attribute_group); | 1032 | err = sysfs_create_group(&pdev->dev.kobj, &compal_attribute_group); |
1027 | if (err) | 1033 | if (err) { |
1034 | kfree(data); | ||
1028 | return err; | 1035 | return err; |
1036 | } | ||
1029 | 1037 | ||
1030 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | 1038 | data->hwmon_dev = hwmon_device_register(&pdev->dev); |
1031 | if (IS_ERR(data->hwmon_dev)) { | 1039 | if (IS_ERR(data->hwmon_dev)) { |
@@ -1055,7 +1063,7 @@ static void __exit compal_cleanup(void) | |||
1055 | rfkill_destroy(wifi_rfkill); | 1063 | rfkill_destroy(wifi_rfkill); |
1056 | rfkill_destroy(bt_rfkill); | 1064 | rfkill_destroy(bt_rfkill); |
1057 | 1065 | ||
1058 | printk(KERN_INFO DRIVER_NAME": Driver unloaded\n"); | 1066 | pr_info("Driver unloaded\n"); |
1059 | } | 1067 | } |
1060 | 1068 | ||
1061 | static int __devexit compal_remove(struct platform_device *pdev) | 1069 | static int __devexit compal_remove(struct platform_device *pdev) |
@@ -1065,8 +1073,7 @@ static int __devexit compal_remove(struct platform_device *pdev) | |||
1065 | if (!extra_features) | 1073 | if (!extra_features) |
1066 | return 0; | 1074 | return 0; |
1067 | 1075 | ||
1068 | printk(KERN_INFO DRIVER_NAME": Unloading: resetting fan control " | 1076 | pr_info("Unloading: resetting fan control to motherboard\n"); |
1069 | "to motherboard\n"); | ||
1070 | pwm_disable_control(); | 1077 | pwm_disable_control(); |
1071 | 1078 | ||
1072 | data = platform_get_drvdata(pdev); | 1079 | data = platform_get_drvdata(pdev); |
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 4413975912e0..e39ab1d3ed87 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -11,6 +11,8 @@ | |||
11 | * published by the Free Software Foundation. | 11 | * published by the Free Software Foundation. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
15 | |||
14 | #include <linux/module.h> | 16 | #include <linux/module.h> |
15 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
16 | #include <linux/init.h> | 18 | #include <linux/init.h> |
@@ -25,6 +27,8 @@ | |||
25 | #include <linux/mm.h> | 27 | #include <linux/mm.h> |
26 | #include <linux/i8042.h> | 28 | #include <linux/i8042.h> |
27 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/debugfs.h> | ||
31 | #include <linux/seq_file.h> | ||
28 | #include "../../firmware/dcdbas.h" | 32 | #include "../../firmware/dcdbas.h" |
29 | 33 | ||
30 | #define BRIGHTNESS_TOKEN 0x7d | 34 | #define BRIGHTNESS_TOKEN 0x7d |
@@ -325,6 +329,75 @@ static const struct rfkill_ops dell_rfkill_ops = { | |||
325 | .query = dell_rfkill_query, | 329 | .query = dell_rfkill_query, |
326 | }; | 330 | }; |
327 | 331 | ||
332 | static struct dentry *dell_laptop_dir; | ||
333 | |||
334 | static int dell_debugfs_show(struct seq_file *s, void *data) | ||
335 | { | ||
336 | int status; | ||
337 | |||
338 | get_buffer(); | ||
339 | dell_send_request(buffer, 17, 11); | ||
340 | status = buffer->output[1]; | ||
341 | release_buffer(); | ||
342 | |||
343 | seq_printf(s, "status:\t0x%X\n", status); | ||
344 | seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n", | ||
345 | status & BIT(0)); | ||
346 | seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n", | ||
347 | (status & BIT(1)) >> 1); | ||
348 | seq_printf(s, "Bit 2 : Wifi is supported: %lu\n", | ||
349 | (status & BIT(2)) >> 2); | ||
350 | seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n", | ||
351 | (status & BIT(3)) >> 3); | ||
352 | seq_printf(s, "Bit 4 : WWAN is supported: %lu\n", | ||
353 | (status & BIT(4)) >> 4); | ||
354 | seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n", | ||
355 | (status & BIT(5)) >> 5); | ||
356 | seq_printf(s, "Bit 8 : Wifi is installed: %lu\n", | ||
357 | (status & BIT(8)) >> 8); | ||
358 | seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n", | ||
359 | (status & BIT(9)) >> 9); | ||
360 | seq_printf(s, "Bit 10: WWAN is installed: %lu\n", | ||
361 | (status & BIT(10)) >> 10); | ||
362 | seq_printf(s, "Bit 16: Hardware switch is on: %lu\n", | ||
363 | (status & BIT(16)) >> 16); | ||
364 | seq_printf(s, "Bit 17: Wifi is blocked: %lu\n", | ||
365 | (status & BIT(17)) >> 17); | ||
366 | seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n", | ||
367 | (status & BIT(18)) >> 18); | ||
368 | seq_printf(s, "Bit 19: WWAN is blocked: %lu\n", | ||
369 | (status & BIT(19)) >> 19); | ||
370 | |||
371 | seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state); | ||
372 | seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n", | ||
373 | hwswitch_state & BIT(0)); | ||
374 | seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n", | ||
375 | (hwswitch_state & BIT(1)) >> 1); | ||
376 | seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n", | ||
377 | (hwswitch_state & BIT(2)) >> 2); | ||
378 | seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n", | ||
379 | (hwswitch_state & BIT(7)) >> 7); | ||
380 | seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n", | ||
381 | (hwswitch_state & BIT(8)) >> 8); | ||
382 | seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n", | ||
383 | (hwswitch_state & BIT(15)) >> 15); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int dell_debugfs_open(struct inode *inode, struct file *file) | ||
389 | { | ||
390 | return single_open(file, dell_debugfs_show, inode->i_private); | ||
391 | } | ||
392 | |||
393 | static const struct file_operations dell_debugfs_fops = { | ||
394 | .owner = THIS_MODULE, | ||
395 | .open = dell_debugfs_open, | ||
396 | .read = seq_read, | ||
397 | .llseek = seq_lseek, | ||
398 | .release = single_release, | ||
399 | }; | ||
400 | |||
328 | static void dell_update_rfkill(struct work_struct *ignored) | 401 | static void dell_update_rfkill(struct work_struct *ignored) |
329 | { | 402 | { |
330 | if (wifi_rfkill) | 403 | if (wifi_rfkill) |
@@ -343,8 +416,7 @@ static int __init dell_setup_rfkill(void) | |||
343 | int ret; | 416 | int ret; |
344 | 417 | ||
345 | if (dmi_check_system(dell_blacklist)) { | 418 | if (dmi_check_system(dell_blacklist)) { |
346 | printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - " | 419 | pr_info("Blacklisted hardware detected - not enabling rfkill\n"); |
347 | "not enabling rfkill\n"); | ||
348 | return 0; | 420 | return 0; |
349 | } | 421 | } |
350 | 422 | ||
@@ -468,14 +540,14 @@ static int dell_get_intensity(struct backlight_device *bd) | |||
468 | else | 540 | else |
469 | dell_send_request(buffer, 0, 1); | 541 | dell_send_request(buffer, 0, 1); |
470 | 542 | ||
543 | ret = buffer->output[1]; | ||
544 | |||
471 | out: | 545 | out: |
472 | release_buffer(); | 546 | release_buffer(); |
473 | if (ret) | 547 | return ret; |
474 | return ret; | ||
475 | return buffer->output[1]; | ||
476 | } | 548 | } |
477 | 549 | ||
478 | static struct backlight_ops dell_ops = { | 550 | static const struct backlight_ops dell_ops = { |
479 | .get_brightness = dell_get_intensity, | 551 | .get_brightness = dell_get_intensity, |
480 | .update_status = dell_send_intensity, | 552 | .update_status = dell_send_intensity, |
481 | }; | 553 | }; |
@@ -515,7 +587,7 @@ static int __init dell_init(void) | |||
515 | dmi_walk(find_tokens, NULL); | 587 | dmi_walk(find_tokens, NULL); |
516 | 588 | ||
517 | if (!da_tokens) { | 589 | if (!da_tokens) { |
518 | printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n"); | 590 | pr_info("Unable to find dmi tokens\n"); |
519 | return -ENODEV; | 591 | return -ENODEV; |
520 | } | 592 | } |
521 | 593 | ||
@@ -545,17 +617,21 @@ static int __init dell_init(void) | |||
545 | ret = dell_setup_rfkill(); | 617 | ret = dell_setup_rfkill(); |
546 | 618 | ||
547 | if (ret) { | 619 | if (ret) { |
548 | printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n"); | 620 | pr_warn("Unable to setup rfkill\n"); |
549 | goto fail_rfkill; | 621 | goto fail_rfkill; |
550 | } | 622 | } |
551 | 623 | ||
552 | ret = i8042_install_filter(dell_laptop_i8042_filter); | 624 | ret = i8042_install_filter(dell_laptop_i8042_filter); |
553 | if (ret) { | 625 | if (ret) { |
554 | printk(KERN_WARNING | 626 | pr_warn("Unable to install key filter\n"); |
555 | "dell-laptop: Unable to install key filter\n"); | ||
556 | goto fail_filter; | 627 | goto fail_filter; |
557 | } | 628 | } |
558 | 629 | ||
630 | dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); | ||
631 | if (dell_laptop_dir != NULL) | ||
632 | debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, | ||
633 | &dell_debugfs_fops); | ||
634 | |||
559 | #ifdef CONFIG_ACPI | 635 | #ifdef CONFIG_ACPI |
560 | /* In the event of an ACPI backlight being available, don't | 636 | /* In the event of an ACPI backlight being available, don't |
561 | * register the platform controller. | 637 | * register the platform controller. |
@@ -575,6 +651,7 @@ static int __init dell_init(void) | |||
575 | if (max_intensity) { | 651 | if (max_intensity) { |
576 | struct backlight_properties props; | 652 | struct backlight_properties props; |
577 | memset(&props, 0, sizeof(struct backlight_properties)); | 653 | memset(&props, 0, sizeof(struct backlight_properties)); |
654 | props.type = BACKLIGHT_PLATFORM; | ||
578 | props.max_brightness = max_intensity; | 655 | props.max_brightness = max_intensity; |
579 | dell_backlight_device = backlight_device_register("dell_backlight", | 656 | dell_backlight_device = backlight_device_register("dell_backlight", |
580 | &platform_device->dev, | 657 | &platform_device->dev, |
@@ -615,6 +692,7 @@ fail_platform_driver: | |||
615 | 692 | ||
616 | static void __exit dell_exit(void) | 693 | static void __exit dell_exit(void) |
617 | { | 694 | { |
695 | debugfs_remove_recursive(dell_laptop_dir); | ||
618 | i8042_remove_filter(dell_laptop_i8042_filter); | 696 | i8042_remove_filter(dell_laptop_i8042_filter); |
619 | cancel_delayed_work_sync(&dell_rfkill_work); | 697 | cancel_delayed_work_sync(&dell_rfkill_work); |
620 | backlight_device_unregister(dell_backlight_device); | 698 | backlight_device_unregister(dell_backlight_device); |
diff --git a/drivers/platform/x86/dell-wmi-aio.c b/drivers/platform/x86/dell-wmi-aio.c new file mode 100644 index 000000000000..3f945457f71c --- /dev/null +++ b/drivers/platform/x86/dell-wmi-aio.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * WMI hotkeys support for Dell All-In-One series | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | */ | ||
18 | |||
19 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/input.h> | ||
26 | #include <linux/input/sparse-keymap.h> | ||
27 | #include <acpi/acpi_drivers.h> | ||
28 | #include <linux/acpi.h> | ||
29 | #include <linux/string.h> | ||
30 | |||
31 | MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series"); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | #define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4" | ||
35 | #define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8" | ||
36 | |||
37 | static const char *dell_wmi_aio_guids[] = { | ||
38 | EVENT_GUID1, | ||
39 | EVENT_GUID2, | ||
40 | NULL | ||
41 | }; | ||
42 | |||
43 | MODULE_ALIAS("wmi:"EVENT_GUID1); | ||
44 | MODULE_ALIAS("wmi:"EVENT_GUID2); | ||
45 | |||
46 | static const struct key_entry dell_wmi_aio_keymap[] = { | ||
47 | { KE_KEY, 0xc0, { KEY_VOLUMEUP } }, | ||
48 | { KE_KEY, 0xc1, { KEY_VOLUMEDOWN } }, | ||
49 | { KE_END, 0 } | ||
50 | }; | ||
51 | |||
52 | static struct input_dev *dell_wmi_aio_input_dev; | ||
53 | |||
54 | static void dell_wmi_aio_notify(u32 value, void *context) | ||
55 | { | ||
56 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
57 | union acpi_object *obj; | ||
58 | acpi_status status; | ||
59 | |||
60 | status = wmi_get_event_data(value, &response); | ||
61 | if (status != AE_OK) { | ||
62 | pr_info("bad event status 0x%x\n", status); | ||
63 | return; | ||
64 | } | ||
65 | |||
66 | obj = (union acpi_object *)response.pointer; | ||
67 | if (obj) { | ||
68 | unsigned int scancode; | ||
69 | |||
70 | switch (obj->type) { | ||
71 | case ACPI_TYPE_INTEGER: | ||
72 | /* Most All-In-One correctly return integer scancode */ | ||
73 | scancode = obj->integer.value; | ||
74 | sparse_keymap_report_event(dell_wmi_aio_input_dev, | ||
75 | scancode, 1, true); | ||
76 | break; | ||
77 | case ACPI_TYPE_BUFFER: | ||
78 | /* Broken machines return the scancode in a buffer */ | ||
79 | if (obj->buffer.pointer && obj->buffer.length > 0) { | ||
80 | scancode = obj->buffer.pointer[0]; | ||
81 | sparse_keymap_report_event( | ||
82 | dell_wmi_aio_input_dev, | ||
83 | scancode, 1, true); | ||
84 | } | ||
85 | break; | ||
86 | } | ||
87 | } | ||
88 | kfree(obj); | ||
89 | } | ||
90 | |||
91 | static int __init dell_wmi_aio_input_setup(void) | ||
92 | { | ||
93 | int err; | ||
94 | |||
95 | dell_wmi_aio_input_dev = input_allocate_device(); | ||
96 | |||
97 | if (!dell_wmi_aio_input_dev) | ||
98 | return -ENOMEM; | ||
99 | |||
100 | dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys"; | ||
101 | dell_wmi_aio_input_dev->phys = "wmi/input0"; | ||
102 | dell_wmi_aio_input_dev->id.bustype = BUS_HOST; | ||
103 | |||
104 | err = sparse_keymap_setup(dell_wmi_aio_input_dev, | ||
105 | dell_wmi_aio_keymap, NULL); | ||
106 | if (err) { | ||
107 | pr_err("Unable to setup input device keymap\n"); | ||
108 | goto err_free_dev; | ||
109 | } | ||
110 | err = input_register_device(dell_wmi_aio_input_dev); | ||
111 | if (err) { | ||
112 | pr_info("Unable to register input device\n"); | ||
113 | goto err_free_keymap; | ||
114 | } | ||
115 | return 0; | ||
116 | |||
117 | err_free_keymap: | ||
118 | sparse_keymap_free(dell_wmi_aio_input_dev); | ||
119 | err_free_dev: | ||
120 | input_free_device(dell_wmi_aio_input_dev); | ||
121 | return err; | ||
122 | } | ||
123 | |||
124 | static const char *dell_wmi_aio_find(void) | ||
125 | { | ||
126 | int i; | ||
127 | |||
128 | for (i = 0; dell_wmi_aio_guids[i] != NULL; i++) | ||
129 | if (wmi_has_guid(dell_wmi_aio_guids[i])) | ||
130 | return dell_wmi_aio_guids[i]; | ||
131 | |||
132 | return NULL; | ||
133 | } | ||
134 | |||
135 | static int __init dell_wmi_aio_init(void) | ||
136 | { | ||
137 | int err; | ||
138 | const char *guid; | ||
139 | |||
140 | guid = dell_wmi_aio_find(); | ||
141 | if (!guid) { | ||
142 | pr_warn("No known WMI GUID found\n"); | ||
143 | return -ENXIO; | ||
144 | } | ||
145 | |||
146 | err = dell_wmi_aio_input_setup(); | ||
147 | if (err) | ||
148 | return err; | ||
149 | |||
150 | err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL); | ||
151 | if (err) { | ||
152 | pr_err("Unable to register notify handler - %d\n", err); | ||
153 | sparse_keymap_free(dell_wmi_aio_input_dev); | ||
154 | input_unregister_device(dell_wmi_aio_input_dev); | ||
155 | return err; | ||
156 | } | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static void __exit dell_wmi_aio_exit(void) | ||
162 | { | ||
163 | const char *guid; | ||
164 | |||
165 | guid = dell_wmi_aio_find(); | ||
166 | wmi_remove_notify_handler(guid); | ||
167 | sparse_keymap_free(dell_wmi_aio_input_dev); | ||
168 | input_unregister_device(dell_wmi_aio_input_dev); | ||
169 | } | ||
170 | |||
171 | module_init(dell_wmi_aio_init); | ||
172 | module_exit(dell_wmi_aio_exit); | ||
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 08fb70f6d9bf..ce790827e199 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -23,12 +23,15 @@ | |||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 | */ | 24 | */ |
25 | 25 | ||
26 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
27 | |||
26 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
27 | #include <linux/module.h> | 29 | #include <linux/module.h> |
28 | #include <linux/init.h> | 30 | #include <linux/init.h> |
29 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
30 | #include <linux/types.h> | 32 | #include <linux/types.h> |
31 | #include <linux/input.h> | 33 | #include <linux/input.h> |
34 | #include <linux/input/sparse-keymap.h> | ||
32 | #include <acpi/acpi_drivers.h> | 35 | #include <acpi/acpi_drivers.h> |
33 | #include <linux/acpi.h> | 36 | #include <linux/acpi.h> |
34 | #include <linux/string.h> | 37 | #include <linux/string.h> |
@@ -44,78 +47,70 @@ static int acpi_video; | |||
44 | 47 | ||
45 | MODULE_ALIAS("wmi:"DELL_EVENT_GUID); | 48 | MODULE_ALIAS("wmi:"DELL_EVENT_GUID); |
46 | 49 | ||
47 | struct key_entry { | ||
48 | char type; /* See KE_* below */ | ||
49 | u16 code; | ||
50 | u16 keycode; | ||
51 | }; | ||
52 | |||
53 | enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; | ||
54 | |||
55 | /* | 50 | /* |
56 | * Certain keys are flagged as KE_IGNORE. All of these are either | 51 | * Certain keys are flagged as KE_IGNORE. All of these are either |
57 | * notifications (rather than requests for change) or are also sent | 52 | * notifications (rather than requests for change) or are also sent |
58 | * via the keyboard controller so should not be sent again. | 53 | * via the keyboard controller so should not be sent again. |
59 | */ | 54 | */ |
60 | 55 | ||
61 | static struct key_entry dell_legacy_wmi_keymap[] = { | 56 | static const struct key_entry dell_wmi_legacy_keymap[] __initconst = { |
62 | {KE_KEY, 0xe045, KEY_PROG1}, | 57 | { KE_KEY, 0xe045, { KEY_PROG1 } }, |
63 | {KE_KEY, 0xe009, KEY_EJECTCD}, | 58 | { KE_KEY, 0xe009, { KEY_EJECTCD } }, |
64 | 59 | ||
65 | /* These also contain the brightness level at offset 6 */ | 60 | /* These also contain the brightness level at offset 6 */ |
66 | {KE_KEY, 0xe006, KEY_BRIGHTNESSUP}, | 61 | { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } }, |
67 | {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN}, | 62 | { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } }, |
68 | 63 | ||
69 | /* Battery health status button */ | 64 | /* Battery health status button */ |
70 | {KE_KEY, 0xe007, KEY_BATTERY}, | 65 | { KE_KEY, 0xe007, { KEY_BATTERY } }, |
71 | 66 | ||
72 | /* This is actually for all radios. Although physically a | 67 | /* This is actually for all radios. Although physically a |
73 | * switch, the notification does not provide an indication of | 68 | * switch, the notification does not provide an indication of |
74 | * state and so it should be reported as a key */ | 69 | * state and so it should be reported as a key */ |
75 | {KE_KEY, 0xe008, KEY_WLAN}, | 70 | { KE_KEY, 0xe008, { KEY_WLAN } }, |
76 | 71 | ||
77 | /* The next device is at offset 6, the active devices are at | 72 | /* The next device is at offset 6, the active devices are at |
78 | offset 8 and the attached devices at offset 10 */ | 73 | offset 8 and the attached devices at offset 10 */ |
79 | {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE}, | 74 | { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } }, |
80 | 75 | ||
81 | {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, | 76 | { KE_IGNORE, 0xe00c, { KEY_KBDILLUMTOGGLE } }, |
82 | 77 | ||
83 | /* BIOS error detected */ | 78 | /* BIOS error detected */ |
84 | {KE_IGNORE, 0xe00d, KEY_RESERVED}, | 79 | { KE_IGNORE, 0xe00d, { KEY_RESERVED } }, |
85 | 80 | ||
86 | /* Wifi Catcher */ | 81 | /* Wifi Catcher */ |
87 | {KE_KEY, 0xe011, KEY_PROG2}, | 82 | { KE_KEY, 0xe011, {KEY_PROG2 } }, |
88 | 83 | ||
89 | /* Ambient light sensor toggle */ | 84 | /* Ambient light sensor toggle */ |
90 | {KE_IGNORE, 0xe013, KEY_RESERVED}, | 85 | { KE_IGNORE, 0xe013, { KEY_RESERVED } }, |
91 | 86 | ||
92 | {KE_IGNORE, 0xe020, KEY_MUTE}, | 87 | { KE_IGNORE, 0xe020, { KEY_MUTE } }, |
93 | {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN}, | 88 | { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } }, |
94 | {KE_IGNORE, 0xe030, KEY_VOLUMEUP}, | 89 | { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } }, |
95 | {KE_IGNORE, 0xe033, KEY_KBDILLUMUP}, | 90 | { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } }, |
96 | {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN}, | 91 | { KE_IGNORE, 0xe034, { KEY_KBDILLUMDOWN } }, |
97 | {KE_IGNORE, 0xe03a, KEY_CAPSLOCK}, | 92 | { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } }, |
98 | {KE_IGNORE, 0xe045, KEY_NUMLOCK}, | 93 | { KE_IGNORE, 0xe045, { KEY_NUMLOCK } }, |
99 | {KE_IGNORE, 0xe046, KEY_SCROLLLOCK}, | 94 | { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } }, |
100 | {KE_END, 0} | 95 | { KE_END, 0 } |
101 | }; | 96 | }; |
102 | 97 | ||
103 | static bool dell_new_hk_type; | 98 | static bool dell_new_hk_type; |
104 | 99 | ||
105 | struct dell_new_keymap_entry { | 100 | struct dell_bios_keymap_entry { |
106 | u16 scancode; | 101 | u16 scancode; |
107 | u16 keycode; | 102 | u16 keycode; |
108 | }; | 103 | }; |
109 | 104 | ||
110 | struct dell_hotkey_table { | 105 | struct dell_bios_hotkey_table { |
111 | struct dmi_header header; | 106 | struct dmi_header header; |
112 | struct dell_new_keymap_entry keymap[]; | 107 | struct dell_bios_keymap_entry keymap[]; |
113 | 108 | ||
114 | }; | 109 | }; |
115 | 110 | ||
116 | static struct key_entry *dell_new_wmi_keymap; | 111 | static const struct dell_bios_hotkey_table *dell_bios_hotkey_table; |
117 | 112 | ||
118 | static u16 bios_to_linux_keycode[256] = { | 113 | static const u16 bios_to_linux_keycode[256] __initconst = { |
119 | 114 | ||
120 | KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, | 115 | KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, |
121 | KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, | 116 | KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, |
@@ -138,85 +133,30 @@ static u16 bios_to_linux_keycode[256] = { | |||
138 | KEY_PROG3 | 133 | KEY_PROG3 |
139 | }; | 134 | }; |
140 | 135 | ||
141 | |||
142 | static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap; | ||
143 | |||
144 | static struct input_dev *dell_wmi_input_dev; | 136 | static struct input_dev *dell_wmi_input_dev; |
145 | 137 | ||
146 | static struct key_entry *dell_wmi_get_entry_by_scancode(unsigned int code) | ||
147 | { | ||
148 | struct key_entry *key; | ||
149 | |||
150 | for (key = dell_wmi_keymap; key->type != KE_END; key++) | ||
151 | if (code == key->code) | ||
152 | return key; | ||
153 | |||
154 | return NULL; | ||
155 | } | ||
156 | |||
157 | static struct key_entry *dell_wmi_get_entry_by_keycode(unsigned int keycode) | ||
158 | { | ||
159 | struct key_entry *key; | ||
160 | |||
161 | for (key = dell_wmi_keymap; key->type != KE_END; key++) | ||
162 | if (key->type == KE_KEY && keycode == key->keycode) | ||
163 | return key; | ||
164 | |||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | static int dell_wmi_getkeycode(struct input_dev *dev, | ||
169 | unsigned int scancode, unsigned int *keycode) | ||
170 | { | ||
171 | struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); | ||
172 | |||
173 | if (key && key->type == KE_KEY) { | ||
174 | *keycode = key->keycode; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | return -EINVAL; | ||
179 | } | ||
180 | |||
181 | static int dell_wmi_setkeycode(struct input_dev *dev, | ||
182 | unsigned int scancode, unsigned int keycode) | ||
183 | { | ||
184 | struct key_entry *key; | ||
185 | unsigned int old_keycode; | ||
186 | |||
187 | key = dell_wmi_get_entry_by_scancode(scancode); | ||
188 | if (key && key->type == KE_KEY) { | ||
189 | old_keycode = key->keycode; | ||
190 | key->keycode = keycode; | ||
191 | set_bit(keycode, dev->keybit); | ||
192 | if (!dell_wmi_get_entry_by_keycode(old_keycode)) | ||
193 | clear_bit(old_keycode, dev->keybit); | ||
194 | return 0; | ||
195 | } | ||
196 | return -EINVAL; | ||
197 | } | ||
198 | |||
199 | static void dell_wmi_notify(u32 value, void *context) | 138 | static void dell_wmi_notify(u32 value, void *context) |
200 | { | 139 | { |
201 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 140 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
202 | static struct key_entry *key; | ||
203 | union acpi_object *obj; | 141 | union acpi_object *obj; |
204 | acpi_status status; | 142 | acpi_status status; |
205 | 143 | ||
206 | status = wmi_get_event_data(value, &response); | 144 | status = wmi_get_event_data(value, &response); |
207 | if (status != AE_OK) { | 145 | if (status != AE_OK) { |
208 | printk(KERN_INFO "dell-wmi: bad event status 0x%x\n", status); | 146 | pr_info("bad event status 0x%x\n", status); |
209 | return; | 147 | return; |
210 | } | 148 | } |
211 | 149 | ||
212 | obj = (union acpi_object *)response.pointer; | 150 | obj = (union acpi_object *)response.pointer; |
213 | 151 | ||
214 | if (obj && obj->type == ACPI_TYPE_BUFFER) { | 152 | if (obj && obj->type == ACPI_TYPE_BUFFER) { |
153 | const struct key_entry *key; | ||
215 | int reported_key; | 154 | int reported_key; |
216 | u16 *buffer_entry = (u16 *)obj->buffer.pointer; | 155 | u16 *buffer_entry = (u16 *)obj->buffer.pointer; |
156 | |||
217 | if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { | 157 | if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { |
218 | printk(KERN_INFO "dell-wmi: Received unknown WMI event" | 158 | pr_info("Received unknown WMI event (0x%x)\n", |
219 | " (0x%x)\n", buffer_entry[1]); | 159 | buffer_entry[1]); |
220 | kfree(obj); | 160 | kfree(obj); |
221 | return; | 161 | return; |
222 | } | 162 | } |
@@ -226,103 +166,108 @@ static void dell_wmi_notify(u32 value, void *context) | |||
226 | else | 166 | else |
227 | reported_key = (int)buffer_entry[1] & 0xffff; | 167 | reported_key = (int)buffer_entry[1] & 0xffff; |
228 | 168 | ||
229 | key = dell_wmi_get_entry_by_scancode(reported_key); | 169 | key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, |
230 | 170 | reported_key); | |
231 | if (!key) { | 171 | if (!key) { |
232 | printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", | 172 | pr_info("Unknown key %x pressed\n", reported_key); |
233 | reported_key); | ||
234 | } else if ((key->keycode == KEY_BRIGHTNESSUP || | 173 | } else if ((key->keycode == KEY_BRIGHTNESSUP || |
235 | key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) { | 174 | key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) { |
236 | /* Don't report brightness notifications that will also | 175 | /* Don't report brightness notifications that will also |
237 | * come via ACPI */ | 176 | * come via ACPI */ |
238 | ; | 177 | ; |
239 | } else { | 178 | } else { |
240 | input_report_key(dell_wmi_input_dev, key->keycode, 1); | 179 | sparse_keymap_report_entry(dell_wmi_input_dev, key, |
241 | input_sync(dell_wmi_input_dev); | 180 | 1, true); |
242 | input_report_key(dell_wmi_input_dev, key->keycode, 0); | ||
243 | input_sync(dell_wmi_input_dev); | ||
244 | } | 181 | } |
245 | } | 182 | } |
246 | kfree(obj); | 183 | kfree(obj); |
247 | } | 184 | } |
248 | 185 | ||
249 | 186 | static const struct key_entry * __init dell_wmi_prepare_new_keymap(void) | |
250 | static void setup_new_hk_map(const struct dmi_header *dm) | ||
251 | { | 187 | { |
252 | 188 | int hotkey_num = (dell_bios_hotkey_table->header.length - 4) / | |
189 | sizeof(struct dell_bios_keymap_entry); | ||
190 | struct key_entry *keymap; | ||
253 | int i; | 191 | int i; |
254 | int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry); | ||
255 | struct dell_hotkey_table *table = | ||
256 | container_of(dm, struct dell_hotkey_table, header); | ||
257 | 192 | ||
258 | dell_new_wmi_keymap = kzalloc((hotkey_num+1) * | 193 | keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL); |
259 | sizeof(struct key_entry), GFP_KERNEL); | 194 | if (!keymap) |
195 | return NULL; | ||
260 | 196 | ||
261 | for (i = 0; i < hotkey_num; i++) { | 197 | for (i = 0; i < hotkey_num; i++) { |
262 | dell_new_wmi_keymap[i].type = KE_KEY; | 198 | const struct dell_bios_keymap_entry *bios_entry = |
263 | dell_new_wmi_keymap[i].code = table->keymap[i].scancode; | 199 | &dell_bios_hotkey_table->keymap[i]; |
264 | dell_new_wmi_keymap[i].keycode = | 200 | keymap[i].type = KE_KEY; |
265 | (table->keymap[i].keycode > 255) ? 0 : | 201 | keymap[i].code = bios_entry->scancode; |
266 | bios_to_linux_keycode[table->keymap[i].keycode]; | 202 | keymap[i].keycode = bios_entry->keycode < 256 ? |
203 | bios_to_linux_keycode[bios_entry->keycode] : | ||
204 | KEY_RESERVED; | ||
267 | } | 205 | } |
268 | 206 | ||
269 | dell_new_wmi_keymap[i].type = KE_END; | 207 | keymap[hotkey_num].type = KE_END; |
270 | dell_new_wmi_keymap[i].code = 0; | ||
271 | dell_new_wmi_keymap[i].keycode = 0; | ||
272 | |||
273 | dell_wmi_keymap = dell_new_wmi_keymap; | ||
274 | 208 | ||
209 | return keymap; | ||
275 | } | 210 | } |
276 | 211 | ||
277 | |||
278 | static void find_hk_type(const struct dmi_header *dm, void *dummy) | ||
279 | { | ||
280 | |||
281 | if ((dm->type == 0xb2) && (dm->length > 6)) { | ||
282 | dell_new_hk_type = true; | ||
283 | setup_new_hk_map(dm); | ||
284 | } | ||
285 | |||
286 | } | ||
287 | |||
288 | |||
289 | static int __init dell_wmi_input_setup(void) | 212 | static int __init dell_wmi_input_setup(void) |
290 | { | 213 | { |
291 | struct key_entry *key; | ||
292 | int err; | 214 | int err; |
293 | 215 | ||
294 | dell_wmi_input_dev = input_allocate_device(); | 216 | dell_wmi_input_dev = input_allocate_device(); |
295 | |||
296 | if (!dell_wmi_input_dev) | 217 | if (!dell_wmi_input_dev) |
297 | return -ENOMEM; | 218 | return -ENOMEM; |
298 | 219 | ||
299 | dell_wmi_input_dev->name = "Dell WMI hotkeys"; | 220 | dell_wmi_input_dev->name = "Dell WMI hotkeys"; |
300 | dell_wmi_input_dev->phys = "wmi/input0"; | 221 | dell_wmi_input_dev->phys = "wmi/input0"; |
301 | dell_wmi_input_dev->id.bustype = BUS_HOST; | 222 | dell_wmi_input_dev->id.bustype = BUS_HOST; |
302 | dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode; | 223 | |
303 | dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode; | 224 | if (dell_new_hk_type) { |
304 | 225 | const struct key_entry *keymap = dell_wmi_prepare_new_keymap(); | |
305 | for (key = dell_wmi_keymap; key->type != KE_END; key++) { | 226 | if (!keymap) { |
306 | switch (key->type) { | 227 | err = -ENOMEM; |
307 | case KE_KEY: | 228 | goto err_free_dev; |
308 | set_bit(EV_KEY, dell_wmi_input_dev->evbit); | ||
309 | set_bit(key->keycode, dell_wmi_input_dev->keybit); | ||
310 | break; | ||
311 | case KE_SW: | ||
312 | set_bit(EV_SW, dell_wmi_input_dev->evbit); | ||
313 | set_bit(key->keycode, dell_wmi_input_dev->swbit); | ||
314 | break; | ||
315 | } | 229 | } |
316 | } | ||
317 | 230 | ||
318 | err = input_register_device(dell_wmi_input_dev); | 231 | err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); |
319 | 232 | ||
320 | if (err) { | 233 | /* |
321 | input_free_device(dell_wmi_input_dev); | 234 | * Sparse keymap library makes a copy of keymap so we |
322 | return err; | 235 | * don't need the original one that was allocated. |
236 | */ | ||
237 | kfree(keymap); | ||
238 | } else { | ||
239 | err = sparse_keymap_setup(dell_wmi_input_dev, | ||
240 | dell_wmi_legacy_keymap, NULL); | ||
323 | } | 241 | } |
242 | if (err) | ||
243 | goto err_free_dev; | ||
244 | |||
245 | err = input_register_device(dell_wmi_input_dev); | ||
246 | if (err) | ||
247 | goto err_free_keymap; | ||
324 | 248 | ||
325 | return 0; | 249 | return 0; |
250 | |||
251 | err_free_keymap: | ||
252 | sparse_keymap_free(dell_wmi_input_dev); | ||
253 | err_free_dev: | ||
254 | input_free_device(dell_wmi_input_dev); | ||
255 | return err; | ||
256 | } | ||
257 | |||
258 | static void dell_wmi_input_destroy(void) | ||
259 | { | ||
260 | sparse_keymap_free(dell_wmi_input_dev); | ||
261 | input_unregister_device(dell_wmi_input_dev); | ||
262 | } | ||
263 | |||
264 | static void __init find_hk_type(const struct dmi_header *dm, void *dummy) | ||
265 | { | ||
266 | if (dm->type == 0xb2 && dm->length > 6) { | ||
267 | dell_new_hk_type = true; | ||
268 | dell_bios_hotkey_table = | ||
269 | container_of(dm, struct dell_bios_hotkey_table, header); | ||
270 | } | ||
326 | } | 271 | } |
327 | 272 | ||
328 | static int __init dell_wmi_init(void) | 273 | static int __init dell_wmi_init(void) |
@@ -331,7 +276,7 @@ static int __init dell_wmi_init(void) | |||
331 | acpi_status status; | 276 | acpi_status status; |
332 | 277 | ||
333 | if (!wmi_has_guid(DELL_EVENT_GUID)) { | 278 | if (!wmi_has_guid(DELL_EVENT_GUID)) { |
334 | printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); | 279 | pr_warn("No known WMI GUID found\n"); |
335 | return -ENODEV; | 280 | return -ENODEV; |
336 | } | 281 | } |
337 | 282 | ||
@@ -339,34 +284,24 @@ static int __init dell_wmi_init(void) | |||
339 | acpi_video = acpi_video_backlight_support(); | 284 | acpi_video = acpi_video_backlight_support(); |
340 | 285 | ||
341 | err = dell_wmi_input_setup(); | 286 | err = dell_wmi_input_setup(); |
342 | if (err) { | 287 | if (err) |
343 | if (dell_new_hk_type) | ||
344 | kfree(dell_wmi_keymap); | ||
345 | return err; | 288 | return err; |
346 | } | ||
347 | 289 | ||
348 | status = wmi_install_notify_handler(DELL_EVENT_GUID, | 290 | status = wmi_install_notify_handler(DELL_EVENT_GUID, |
349 | dell_wmi_notify, NULL); | 291 | dell_wmi_notify, NULL); |
350 | if (ACPI_FAILURE(status)) { | 292 | if (ACPI_FAILURE(status)) { |
351 | input_unregister_device(dell_wmi_input_dev); | 293 | dell_wmi_input_destroy(); |
352 | if (dell_new_hk_type) | 294 | pr_err("Unable to register notify handler - %d\n", status); |
353 | kfree(dell_wmi_keymap); | ||
354 | printk(KERN_ERR | ||
355 | "dell-wmi: Unable to register notify handler - %d\n", | ||
356 | status); | ||
357 | return -ENODEV; | 295 | return -ENODEV; |
358 | } | 296 | } |
359 | 297 | ||
360 | return 0; | 298 | return 0; |
361 | } | 299 | } |
300 | module_init(dell_wmi_init); | ||
362 | 301 | ||
363 | static void __exit dell_wmi_exit(void) | 302 | static void __exit dell_wmi_exit(void) |
364 | { | 303 | { |
365 | wmi_remove_notify_handler(DELL_EVENT_GUID); | 304 | wmi_remove_notify_handler(DELL_EVENT_GUID); |
366 | input_unregister_device(dell_wmi_input_dev); | 305 | dell_wmi_input_destroy(); |
367 | if (dell_new_hk_type) | ||
368 | kfree(dell_wmi_keymap); | ||
369 | } | 306 | } |
370 | |||
371 | module_init(dell_wmi_init); | ||
372 | module_exit(dell_wmi_exit); | 307 | module_exit(dell_wmi_exit); |
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 6b8e06206c46..1c45d92e2163 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -165,6 +165,7 @@ struct eeepc_laptop { | |||
165 | u16 event_count[128]; /* count for each event */ | 165 | u16 event_count[128]; /* count for each event */ |
166 | 166 | ||
167 | struct platform_device *platform_device; | 167 | struct platform_device *platform_device; |
168 | struct acpi_device *device; /* the device we are in */ | ||
168 | struct device *hwmon_device; | 169 | struct device *hwmon_device; |
169 | struct backlight_device *backlight_device; | 170 | struct backlight_device *backlight_device; |
170 | 171 | ||
@@ -227,7 +228,7 @@ static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value) | |||
227 | return -ENODEV; | 228 | return -ENODEV; |
228 | 229 | ||
229 | if (write_acpi_int(eeepc->handle, method, value)) | 230 | if (write_acpi_int(eeepc->handle, method, value)) |
230 | pr_warning("Error writing %s\n", method); | 231 | pr_warn("Error writing %s\n", method); |
231 | return 0; | 232 | return 0; |
232 | } | 233 | } |
233 | 234 | ||
@@ -242,7 +243,7 @@ static int get_acpi(struct eeepc_laptop *eeepc, int cm) | |||
242 | return -ENODEV; | 243 | return -ENODEV; |
243 | 244 | ||
244 | if (read_acpi_int(eeepc->handle, method, &value)) | 245 | if (read_acpi_int(eeepc->handle, method, &value)) |
245 | pr_warning("Error reading %s\n", method); | 246 | pr_warn("Error reading %s\n", method); |
246 | return value; | 247 | return value; |
247 | } | 248 | } |
248 | 249 | ||
@@ -260,7 +261,7 @@ static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, | |||
260 | status = acpi_get_handle(eeepc->handle, (char *)method, | 261 | status = acpi_get_handle(eeepc->handle, (char *)method, |
261 | handle); | 262 | handle); |
262 | if (status != AE_OK) { | 263 | if (status != AE_OK) { |
263 | pr_warning("Error finding %s\n", method); | 264 | pr_warn("Error finding %s\n", method); |
264 | return -ENODEV; | 265 | return -ENODEV; |
265 | } | 266 | } |
266 | return 0; | 267 | return 0; |
@@ -416,7 +417,7 @@ static ssize_t store_cpufv_disabled(struct device *dev, | |||
416 | switch (value) { | 417 | switch (value) { |
417 | case 0: | 418 | case 0: |
418 | if (eeepc->cpufv_disabled) | 419 | if (eeepc->cpufv_disabled) |
419 | pr_warning("cpufv enabled (not officially supported " | 420 | pr_warn("cpufv enabled (not officially supported " |
420 | "on this model)\n"); | 421 | "on this model)\n"); |
421 | eeepc->cpufv_disabled = false; | 422 | eeepc->cpufv_disabled = false; |
422 | return rv; | 423 | return rv; |
@@ -528,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev, | |||
528 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); | 529 | queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); |
529 | } | 530 | } |
530 | 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 | |||
531 | static int eeepc_led_init(struct eeepc_laptop *eeepc) | 541 | static int eeepc_led_init(struct eeepc_laptop *eeepc) |
532 | { | 542 | { |
533 | int rv; | 543 | int rv; |
@@ -542,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) | |||
542 | 552 | ||
543 | eeepc->tpd_led.name = "eeepc::touchpad"; | 553 | eeepc->tpd_led.name = "eeepc::touchpad"; |
544 | 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; | ||
545 | eeepc->tpd_led.max_brightness = 1; | 557 | eeepc->tpd_led.max_brightness = 1; |
546 | 558 | ||
547 | rv = led_classdev_register(&eeepc->platform_device->dev, | 559 | rv = led_classdev_register(&eeepc->platform_device->dev, |
@@ -573,8 +585,9 @@ static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc) | |||
573 | return true; | 585 | return true; |
574 | } | 586 | } |
575 | 587 | ||
576 | static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc) | 588 | static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) |
577 | { | 589 | { |
590 | struct pci_dev *port; | ||
578 | struct pci_dev *dev; | 591 | struct pci_dev *dev; |
579 | struct pci_bus *bus; | 592 | struct pci_bus *bus; |
580 | bool blocked = eeepc_wlan_rfkill_blocked(eeepc); | 593 | bool blocked = eeepc_wlan_rfkill_blocked(eeepc); |
@@ -587,9 +600,16 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc) | |||
587 | mutex_lock(&eeepc->hotplug_lock); | 600 | mutex_lock(&eeepc->hotplug_lock); |
588 | 601 | ||
589 | if (eeepc->hotplug_slot) { | 602 | if (eeepc->hotplug_slot) { |
590 | bus = pci_find_bus(0, 1); | 603 | port = acpi_get_pci_dev(handle); |
604 | if (!port) { | ||
605 | pr_warning("Unable to find port\n"); | ||
606 | goto out_unlock; | ||
607 | } | ||
608 | |||
609 | bus = port->subordinate; | ||
610 | |||
591 | if (!bus) { | 611 | if (!bus) { |
592 | pr_warning("Unable to find PCI bus 1?\n"); | 612 | pr_warn("Unable to find PCI bus 1?\n"); |
593 | goto out_unlock; | 613 | goto out_unlock; |
594 | } | 614 | } |
595 | 615 | ||
@@ -597,15 +617,16 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc) | |||
597 | pr_err("Unable to read PCI config space?\n"); | 617 | pr_err("Unable to read PCI config space?\n"); |
598 | goto out_unlock; | 618 | goto out_unlock; |
599 | } | 619 | } |
620 | |||
600 | absent = (l == 0xffffffff); | 621 | absent = (l == 0xffffffff); |
601 | 622 | ||
602 | if (blocked != absent) { | 623 | if (blocked != absent) { |
603 | pr_warning("BIOS says wireless lan is %s, " | 624 | pr_warn("BIOS says wireless lan is %s, " |
604 | "but the pci device is %s\n", | 625 | "but the pci device is %s\n", |
605 | blocked ? "blocked" : "unblocked", | 626 | blocked ? "blocked" : "unblocked", |
606 | absent ? "absent" : "present"); | 627 | absent ? "absent" : "present"); |
607 | pr_warning("skipped wireless hotplug as probably " | 628 | pr_warn("skipped wireless hotplug as probably " |
608 | "inappropriate for this model\n"); | 629 | "inappropriate for this model\n"); |
609 | goto out_unlock; | 630 | goto out_unlock; |
610 | } | 631 | } |
611 | 632 | ||
@@ -635,6 +656,17 @@ out_unlock: | |||
635 | mutex_unlock(&eeepc->hotplug_lock); | 656 | mutex_unlock(&eeepc->hotplug_lock); |
636 | } | 657 | } |
637 | 658 | ||
659 | static void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node) | ||
660 | { | ||
661 | acpi_status status = AE_OK; | ||
662 | acpi_handle handle; | ||
663 | |||
664 | status = acpi_get_handle(NULL, node, &handle); | ||
665 | |||
666 | if (ACPI_SUCCESS(status)) | ||
667 | eeepc_rfkill_hotplug(eeepc, handle); | ||
668 | } | ||
669 | |||
638 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | 670 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) |
639 | { | 671 | { |
640 | struct eeepc_laptop *eeepc = data; | 672 | struct eeepc_laptop *eeepc = data; |
@@ -642,7 +674,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | |||
642 | if (event != ACPI_NOTIFY_BUS_CHECK) | 674 | if (event != ACPI_NOTIFY_BUS_CHECK) |
643 | return; | 675 | return; |
644 | 676 | ||
645 | eeepc_rfkill_hotplug(eeepc); | 677 | eeepc_rfkill_hotplug(eeepc, handle); |
646 | } | 678 | } |
647 | 679 | ||
648 | static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, | 680 | static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, |
@@ -659,7 +691,13 @@ static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, | |||
659 | eeepc_rfkill_notify, | 691 | eeepc_rfkill_notify, |
660 | eeepc); | 692 | eeepc); |
661 | if (ACPI_FAILURE(status)) | 693 | if (ACPI_FAILURE(status)) |
662 | pr_warning("Failed to register notify on %s\n", node); | 694 | pr_warn("Failed to register notify on %s\n", node); |
695 | |||
696 | /* | ||
697 | * Refresh pci hotplug in case the rfkill state was | ||
698 | * changed during setup. | ||
699 | */ | ||
700 | eeepc_rfkill_hotplug(eeepc, handle); | ||
663 | } else | 701 | } else |
664 | return -ENODEV; | 702 | return -ENODEV; |
665 | 703 | ||
@@ -681,6 +719,12 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc, | |||
681 | if (ACPI_FAILURE(status)) | 719 | if (ACPI_FAILURE(status)) |
682 | pr_err("Error removing rfkill notify handler %s\n", | 720 | pr_err("Error removing rfkill notify handler %s\n", |
683 | node); | 721 | node); |
722 | /* | ||
723 | * Refresh pci hotplug in case the rfkill | ||
724 | * state was changed after | ||
725 | * eeepc_unregister_rfkill_notifier() | ||
726 | */ | ||
727 | eeepc_rfkill_hotplug(eeepc, handle); | ||
684 | } | 728 | } |
685 | } | 729 | } |
686 | 730 | ||
@@ -804,11 +848,7 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) | |||
804 | rfkill_destroy(eeepc->wlan_rfkill); | 848 | rfkill_destroy(eeepc->wlan_rfkill); |
805 | eeepc->wlan_rfkill = NULL; | 849 | eeepc->wlan_rfkill = NULL; |
806 | } | 850 | } |
807 | /* | 851 | |
808 | * Refresh pci hotplug in case the rfkill state was changed after | ||
809 | * eeepc_unregister_rfkill_notifier() | ||
810 | */ | ||
811 | eeepc_rfkill_hotplug(eeepc); | ||
812 | if (eeepc->hotplug_slot) | 852 | if (eeepc->hotplug_slot) |
813 | pci_hp_deregister(eeepc->hotplug_slot); | 853 | pci_hp_deregister(eeepc->hotplug_slot); |
814 | 854 | ||
@@ -877,11 +917,6 @@ static int eeepc_rfkill_init(struct eeepc_laptop *eeepc) | |||
877 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); | 917 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); |
878 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); | 918 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); |
879 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); | 919 | eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); |
880 | /* | ||
881 | * Refresh pci hotplug in case the rfkill state was changed during | ||
882 | * setup. | ||
883 | */ | ||
884 | eeepc_rfkill_hotplug(eeepc); | ||
885 | 920 | ||
886 | exit: | 921 | exit: |
887 | if (result && result != -ENODEV) | 922 | if (result && result != -ENODEV) |
@@ -916,8 +951,11 @@ static int eeepc_hotk_restore(struct device *device) | |||
916 | struct eeepc_laptop *eeepc = dev_get_drvdata(device); | 951 | struct eeepc_laptop *eeepc = dev_get_drvdata(device); |
917 | 952 | ||
918 | /* Refresh both wlan rfkill state and pci hotplug */ | 953 | /* Refresh both wlan rfkill state and pci hotplug */ |
919 | if (eeepc->wlan_rfkill) | 954 | if (eeepc->wlan_rfkill) { |
920 | eeepc_rfkill_hotplug(eeepc); | 955 | eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P5"); |
956 | eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P6"); | ||
957 | eeepc_rfkill_hotplug_update(eeepc, "\\_SB.PCI0.P0P7"); | ||
958 | } | ||
921 | 959 | ||
922 | if (eeepc->bluetooth_rfkill) | 960 | if (eeepc->bluetooth_rfkill) |
923 | rfkill_set_sw_state(eeepc->bluetooth_rfkill, | 961 | rfkill_set_sw_state(eeepc->bluetooth_rfkill, |
@@ -1114,7 +1152,7 @@ static int update_bl_status(struct backlight_device *bd) | |||
1114 | return set_brightness(bd, bd->props.brightness); | 1152 | return set_brightness(bd, bd->props.brightness); |
1115 | } | 1153 | } |
1116 | 1154 | ||
1117 | static struct backlight_ops eeepcbl_ops = { | 1155 | static const struct backlight_ops eeepcbl_ops = { |
1118 | .get_brightness = read_brightness, | 1156 | .get_brightness = read_brightness, |
1119 | .update_status = update_bl_status, | 1157 | .update_status = update_bl_status, |
1120 | }; | 1158 | }; |
@@ -1135,6 +1173,7 @@ static int eeepc_backlight_init(struct eeepc_laptop *eeepc) | |||
1135 | struct backlight_device *bd; | 1173 | struct backlight_device *bd; |
1136 | 1174 | ||
1137 | memset(&props, 0, sizeof(struct backlight_properties)); | 1175 | memset(&props, 0, sizeof(struct backlight_properties)); |
1176 | props.type = BACKLIGHT_PLATFORM; | ||
1138 | props.max_brightness = 15; | 1177 | props.max_brightness = 15; |
1139 | bd = backlight_device_register(EEEPC_LAPTOP_FILE, | 1178 | bd = backlight_device_register(EEEPC_LAPTOP_FILE, |
1140 | &eeepc->platform_device->dev, eeepc, | 1179 | &eeepc->platform_device->dev, eeepc, |
@@ -1193,9 +1232,9 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) | |||
1193 | eeepc->inputdev = input; | 1232 | eeepc->inputdev = input; |
1194 | return 0; | 1233 | return 0; |
1195 | 1234 | ||
1196 | err_free_keymap: | 1235 | err_free_keymap: |
1197 | sparse_keymap_free(input); | 1236 | sparse_keymap_free(input); |
1198 | err_free_dev: | 1237 | err_free_dev: |
1199 | input_free_device(input); | 1238 | input_free_device(input); |
1200 | return error; | 1239 | return error; |
1201 | } | 1240 | } |
@@ -1206,6 +1245,7 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc) | |||
1206 | sparse_keymap_free(eeepc->inputdev); | 1245 | sparse_keymap_free(eeepc->inputdev); |
1207 | input_unregister_device(eeepc->inputdev); | 1246 | input_unregister_device(eeepc->inputdev); |
1208 | } | 1247 | } |
1248 | eeepc->inputdev = NULL; | ||
1209 | } | 1249 | } |
1210 | 1250 | ||
1211 | /* | 1251 | /* |
@@ -1308,7 +1348,7 @@ static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) | |||
1308 | { | 1348 | { |
1309 | int dummy; | 1349 | int dummy; |
1310 | 1350 | ||
1311 | /* Some BIOSes do not report cm although it is avaliable. | 1351 | /* Some BIOSes do not report cm although it is available. |
1312 | Check if cm_getv[cm] works and, if yes, assume cm should be set. */ | 1352 | Check if cm_getv[cm] works and, if yes, assume cm should be set. */ |
1313 | if (!(eeepc->cm_supported & (1 << cm)) | 1353 | if (!(eeepc->cm_supported & (1 << cm)) |
1314 | && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) { | 1354 | && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) { |
@@ -1326,16 +1366,15 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc) | |||
1326 | cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); | 1366 | cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); |
1327 | } | 1367 | } |
1328 | 1368 | ||
1329 | static int eeepc_acpi_init(struct eeepc_laptop *eeepc, | 1369 | static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc) |
1330 | struct acpi_device *device) | ||
1331 | { | 1370 | { |
1332 | unsigned int init_flags; | 1371 | unsigned int init_flags; |
1333 | int result; | 1372 | int result; |
1334 | 1373 | ||
1335 | result = acpi_bus_get_status(device); | 1374 | result = acpi_bus_get_status(eeepc->device); |
1336 | if (result) | 1375 | if (result) |
1337 | return result; | 1376 | return result; |
1338 | if (!device->status.present) { | 1377 | if (!eeepc->device->status.present) { |
1339 | pr_err("Hotkey device not present, aborting\n"); | 1378 | pr_err("Hotkey device not present, aborting\n"); |
1340 | return -ENODEV; | 1379 | return -ENODEV; |
1341 | } | 1380 | } |
@@ -1384,12 +1423,13 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device) | |||
1384 | strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); | 1423 | strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); |
1385 | strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); | 1424 | strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); |
1386 | device->driver_data = eeepc; | 1425 | device->driver_data = eeepc; |
1426 | eeepc->device = device; | ||
1387 | 1427 | ||
1388 | eeepc->hotplug_disabled = hotplug_disabled; | 1428 | eeepc->hotplug_disabled = hotplug_disabled; |
1389 | 1429 | ||
1390 | eeepc_dmi_check(eeepc); | 1430 | eeepc_dmi_check(eeepc); |
1391 | 1431 | ||
1392 | result = eeepc_acpi_init(eeepc, device); | 1432 | result = eeepc_acpi_init(eeepc); |
1393 | if (result) | 1433 | if (result) |
1394 | goto fail_platform; | 1434 | goto fail_platform; |
1395 | eeepc_enable_camera(eeepc); | 1435 | eeepc_enable_camera(eeepc); |
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 9dc50fbf3d0b..4aa867a9b88b 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-2011 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> |
@@ -28,385 +29,141 @@ | |||
28 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
29 | #include <linux/module.h> | 30 | #include <linux/module.h> |
30 | #include <linux/init.h> | 31 | #include <linux/init.h> |
31 | #include <linux/types.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/input.h> | 32 | #include <linux/input.h> |
34 | #include <linux/input/sparse-keymap.h> | 33 | #include <linux/input/sparse-keymap.h> |
35 | #include <linux/fb.h> | 34 | #include <linux/dmi.h> |
36 | #include <linux/backlight.h> | ||
37 | #include <linux/platform_device.h> | ||
38 | #include <acpi/acpi_bus.h> | 35 | #include <acpi/acpi_bus.h> |
39 | #include <acpi/acpi_drivers.h> | 36 | |
37 | #include "asus-wmi.h" | ||
40 | 38 | ||
41 | #define EEEPC_WMI_FILE "eeepc-wmi" | 39 | #define EEEPC_WMI_FILE "eeepc-wmi" |
42 | 40 | ||
43 | MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); | 41 | MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>"); |
44 | MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); | 42 | MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); |
45 | MODULE_LICENSE("GPL"); | 43 | MODULE_LICENSE("GPL"); |
46 | 44 | ||
45 | #define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ | ||
46 | |||
47 | #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" | 47 | #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" |
48 | #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" | ||
49 | 48 | ||
50 | MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); | 49 | MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); |
51 | MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); | ||
52 | 50 | ||
53 | #define NOTIFY_BRNUP_MIN 0x11 | 51 | static bool hotplug_wireless; |
54 | #define NOTIFY_BRNUP_MAX 0x1f | ||
55 | #define NOTIFY_BRNDOWN_MIN 0x20 | ||
56 | #define NOTIFY_BRNDOWN_MAX 0x2e | ||
57 | 52 | ||
58 | #define EEEPC_WMI_METHODID_DEVS 0x53564544 | 53 | module_param(hotplug_wireless, bool, 0444); |
59 | #define EEEPC_WMI_METHODID_DSTS 0x53544344 | 54 | MODULE_PARM_DESC(hotplug_wireless, |
60 | 55 | "Enable hotplug for wireless device. " | |
61 | #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 | 56 | "If your laptop needs that, please report to " |
57 | "acpi4asus-user@lists.sourceforge.net."); | ||
62 | 58 | ||
63 | static const struct key_entry eeepc_wmi_keymap[] = { | 59 | static const struct key_entry eeepc_wmi_keymap[] = { |
64 | /* Sleep already handled via generic ACPI code */ | 60 | /* Sleep already handled via generic ACPI code */ |
65 | { KE_KEY, 0x5d, { KEY_WLAN } }, | ||
66 | { KE_KEY, 0x32, { KEY_MUTE } }, | ||
67 | { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, | ||
68 | { KE_KEY, 0x30, { KEY_VOLUMEUP } }, | 61 | { KE_KEY, 0x30, { KEY_VOLUMEUP } }, |
69 | { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, | 62 | { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, |
70 | { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, | 63 | { KE_KEY, 0x32, { KEY_MUTE } }, |
64 | { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ | ||
65 | { KE_KEY, 0x5d, { KEY_WLAN } }, | ||
66 | { KE_KEY, 0x6b, { KEY_TOUCHPAD_TOGGLE } }, /* Toggle Touchpad */ | ||
67 | { KE_KEY, 0x82, { KEY_CAMERA } }, | ||
68 | { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, | ||
69 | { KE_KEY, 0x88, { KEY_WLAN } }, | ||
70 | { KE_KEY, 0xbd, { KEY_CAMERA } }, | ||
71 | { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, | 71 | { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, |
72 | { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ | ||
73 | { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ | ||
74 | { KE_KEY, 0xe8, { KEY_SCREENLOCK } }, | ||
75 | { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, | ||
76 | { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, | ||
77 | { KE_KEY, 0xec, { KEY_CAMERA_UP } }, | ||
78 | { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, | ||
79 | { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, | ||
80 | { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, | ||
72 | { KE_END, 0}, | 81 | { KE_END, 0}, |
73 | }; | 82 | }; |
74 | 83 | ||
75 | struct bios_args { | 84 | static acpi_status eeepc_wmi_parse_device(acpi_handle handle, u32 level, |
76 | u32 dev_id; | 85 | void *context, void **retval) |
77 | u32 ctrl_param; | ||
78 | }; | ||
79 | |||
80 | struct eeepc_wmi { | ||
81 | struct input_dev *inputdev; | ||
82 | struct backlight_device *backlight_device; | ||
83 | }; | ||
84 | |||
85 | static struct platform_device *platform_device; | ||
86 | |||
87 | static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) | ||
88 | { | 86 | { |
89 | int err; | 87 | pr_warn("Found legacy ATKD device (%s)\n", EEEPC_ACPI_HID); |
90 | 88 | *(bool *)context = true; | |
91 | eeepc->inputdev = input_allocate_device(); | 89 | return AE_CTRL_TERMINATE; |
92 | if (!eeepc->inputdev) | ||
93 | return -ENOMEM; | ||
94 | |||
95 | eeepc->inputdev->name = "Eee PC WMI hotkeys"; | ||
96 | eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; | ||
97 | eeepc->inputdev->id.bustype = BUS_HOST; | ||
98 | eeepc->inputdev->dev.parent = &platform_device->dev; | ||
99 | |||
100 | err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); | ||
101 | if (err) | ||
102 | goto err_free_dev; | ||
103 | |||
104 | err = input_register_device(eeepc->inputdev); | ||
105 | if (err) | ||
106 | goto err_free_keymap; | ||
107 | |||
108 | return 0; | ||
109 | |||
110 | err_free_keymap: | ||
111 | sparse_keymap_free(eeepc->inputdev); | ||
112 | err_free_dev: | ||
113 | input_free_device(eeepc->inputdev); | ||
114 | return err; | ||
115 | } | 90 | } |
116 | 91 | ||
117 | static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) | 92 | static int eeepc_wmi_check_atkd(void) |
118 | { | 93 | { |
119 | if (eeepc->inputdev) { | ||
120 | sparse_keymap_free(eeepc->inputdev); | ||
121 | input_unregister_device(eeepc->inputdev); | ||
122 | } | ||
123 | |||
124 | eeepc->inputdev = NULL; | ||
125 | } | ||
126 | |||
127 | static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) | ||
128 | { | ||
129 | struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; | ||
130 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
131 | union acpi_object *obj; | ||
132 | acpi_status status; | 94 | acpi_status status; |
133 | u32 tmp; | 95 | bool found = false; |
134 | |||
135 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | ||
136 | 1, EEEPC_WMI_METHODID_DSTS, &input, &output); | ||
137 | 96 | ||
138 | if (ACPI_FAILURE(status)) | 97 | status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device, |
139 | return status; | 98 | &found, NULL); |
140 | 99 | ||
141 | obj = (union acpi_object *)output.pointer; | 100 | if (ACPI_FAILURE(status) || !found) |
142 | if (obj && obj->type == ACPI_TYPE_INTEGER) | ||
143 | tmp = (u32)obj->integer.value; | ||
144 | else | ||
145 | tmp = 0; | ||
146 | |||
147 | if (ctrl_param) | ||
148 | *ctrl_param = tmp; | ||
149 | |||
150 | kfree(obj); | ||
151 | |||
152 | return status; | ||
153 | |||
154 | } | ||
155 | |||
156 | static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) | ||
157 | { | ||
158 | struct bios_args args = { | ||
159 | .dev_id = dev_id, | ||
160 | .ctrl_param = ctrl_param, | ||
161 | }; | ||
162 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | ||
163 | acpi_status status; | ||
164 | |||
165 | status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | ||
166 | 1, EEEPC_WMI_METHODID_DEVS, &input, NULL); | ||
167 | |||
168 | return status; | ||
169 | } | ||
170 | |||
171 | static int read_brightness(struct backlight_device *bd) | ||
172 | { | ||
173 | static u32 ctrl_param; | ||
174 | acpi_status status; | ||
175 | |||
176 | status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); | ||
177 | |||
178 | if (ACPI_FAILURE(status)) | ||
179 | return -1; | ||
180 | else | ||
181 | return ctrl_param & 0xFF; | ||
182 | } | ||
183 | |||
184 | static int update_bl_status(struct backlight_device *bd) | ||
185 | { | ||
186 | |||
187 | static u32 ctrl_param; | ||
188 | acpi_status status; | ||
189 | |||
190 | ctrl_param = bd->props.brightness; | ||
191 | |||
192 | status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param); | ||
193 | |||
194 | if (ACPI_FAILURE(status)) | ||
195 | return -1; | ||
196 | else | ||
197 | return 0; | 101 | return 0; |
102 | return -1; | ||
198 | } | 103 | } |
199 | 104 | ||
200 | static const struct backlight_ops eeepc_wmi_bl_ops = { | 105 | static int eeepc_wmi_probe(struct platform_device *pdev) |
201 | .get_brightness = read_brightness, | ||
202 | .update_status = update_bl_status, | ||
203 | }; | ||
204 | |||
205 | static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code) | ||
206 | { | 106 | { |
207 | struct backlight_device *bd = eeepc->backlight_device; | 107 | if (eeepc_wmi_check_atkd()) { |
208 | int old = bd->props.brightness; | 108 | pr_warn("WMI device present, but legacy ATKD device is also " |
209 | int new = old; | 109 | "present and enabled\n"); |
210 | 110 | pr_warn("You probably booted with acpi_osi=\"Linux\" or " | |
211 | if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | 111 | "acpi_osi=\"!Windows 2009\"\n"); |
212 | new = code - NOTIFY_BRNUP_MIN + 1; | 112 | pr_warn("Can't load eeepc-wmi, use default acpi_osi " |
213 | else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX) | 113 | "(preferred) or eeepc-laptop\n"); |
214 | new = code - NOTIFY_BRNDOWN_MIN; | 114 | return -EBUSY; |
215 | |||
216 | bd->props.brightness = new; | ||
217 | backlight_update_status(bd); | ||
218 | backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); | ||
219 | |||
220 | return old; | ||
221 | } | ||
222 | |||
223 | static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) | ||
224 | { | ||
225 | struct backlight_device *bd; | ||
226 | struct backlight_properties props; | ||
227 | |||
228 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
229 | props.max_brightness = 15; | ||
230 | bd = backlight_device_register(EEEPC_WMI_FILE, | ||
231 | &platform_device->dev, eeepc, | ||
232 | &eeepc_wmi_bl_ops, &props); | ||
233 | if (IS_ERR(bd)) { | ||
234 | pr_err("Could not register backlight device\n"); | ||
235 | return PTR_ERR(bd); | ||
236 | } | 115 | } |
237 | |||
238 | eeepc->backlight_device = bd; | ||
239 | |||
240 | bd->props.brightness = read_brightness(bd); | ||
241 | bd->props.power = FB_BLANK_UNBLANK; | ||
242 | backlight_update_status(bd); | ||
243 | |||
244 | return 0; | 116 | return 0; |
245 | } | 117 | } |
246 | 118 | ||
247 | static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc) | 119 | static void eeepc_dmi_check(struct asus_wmi_driver *driver) |
248 | { | ||
249 | if (eeepc->backlight_device) | ||
250 | backlight_device_unregister(eeepc->backlight_device); | ||
251 | |||
252 | eeepc->backlight_device = NULL; | ||
253 | } | ||
254 | |||
255 | static void eeepc_wmi_notify(u32 value, void *context) | ||
256 | { | 120 | { |
257 | struct eeepc_wmi *eeepc = context; | 121 | const char *model; |
258 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
259 | union acpi_object *obj; | ||
260 | acpi_status status; | ||
261 | int code; | ||
262 | int orig_code; | ||
263 | 122 | ||
264 | status = wmi_get_event_data(value, &response); | 123 | model = dmi_get_system_info(DMI_PRODUCT_NAME); |
265 | if (status != AE_OK) { | 124 | if (!model) |
266 | pr_err("bad event status 0x%x\n", status); | ||
267 | return; | 125 | return; |
268 | } | ||
269 | |||
270 | obj = (union acpi_object *)response.pointer; | ||
271 | |||
272 | if (obj && obj->type == ACPI_TYPE_INTEGER) { | ||
273 | code = obj->integer.value; | ||
274 | orig_code = code; | ||
275 | |||
276 | if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | ||
277 | code = NOTIFY_BRNUP_MIN; | ||
278 | else if (code >= NOTIFY_BRNDOWN_MIN && | ||
279 | code <= NOTIFY_BRNDOWN_MAX) | ||
280 | code = NOTIFY_BRNDOWN_MIN; | ||
281 | |||
282 | if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { | ||
283 | if (!acpi_video_backlight_support()) | ||
284 | eeepc_wmi_backlight_notify(eeepc, orig_code); | ||
285 | } | ||
286 | |||
287 | if (!sparse_keymap_report_event(eeepc->inputdev, | ||
288 | code, 1, true)) | ||
289 | pr_info("Unknown key %x pressed\n", code); | ||
290 | } | ||
291 | |||
292 | kfree(obj); | ||
293 | } | ||
294 | |||
295 | static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) | ||
296 | { | ||
297 | struct eeepc_wmi *eeepc; | ||
298 | int err; | ||
299 | acpi_status status; | ||
300 | |||
301 | eeepc = platform_get_drvdata(device); | ||
302 | |||
303 | err = eeepc_wmi_input_init(eeepc); | ||
304 | if (err) | ||
305 | goto error_input; | ||
306 | |||
307 | if (!acpi_video_backlight_support()) { | ||
308 | err = eeepc_wmi_backlight_init(eeepc); | ||
309 | if (err) | ||
310 | goto error_backlight; | ||
311 | } else | ||
312 | pr_info("Backlight controlled by ACPI video driver\n"); | ||
313 | 126 | ||
314 | status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, | 127 | /* |
315 | eeepc_wmi_notify, eeepc); | 128 | * Whitelist for wlan hotplug |
316 | if (ACPI_FAILURE(status)) { | 129 | * |
317 | pr_err("Unable to register notify handler - %d\n", | 130 | * Asus 1000H needs the current hotplug code to handle |
318 | status); | 131 | * Fn+F2 correctly. We may add other Asus here later, but |
319 | err = -ENODEV; | 132 | * it seems that most of the laptops supported by asus-wmi |
320 | goto error_wmi; | 133 | * don't need to be on this list |
134 | */ | ||
135 | if (strcmp(model, "1000H") == 0) { | ||
136 | driver->hotplug_wireless = true; | ||
137 | pr_info("wlan hotplug enabled\n"); | ||
321 | } | 138 | } |
322 | |||
323 | return 0; | ||
324 | |||
325 | error_wmi: | ||
326 | eeepc_wmi_backlight_exit(eeepc); | ||
327 | error_backlight: | ||
328 | eeepc_wmi_input_exit(eeepc); | ||
329 | error_input: | ||
330 | return err; | ||
331 | } | 139 | } |
332 | 140 | ||
333 | static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) | 141 | static void eeepc_wmi_quirks(struct asus_wmi_driver *driver) |
334 | { | 142 | { |
335 | struct eeepc_wmi *eeepc; | 143 | driver->hotplug_wireless = hotplug_wireless; |
336 | 144 | eeepc_dmi_check(driver); | |
337 | eeepc = platform_get_drvdata(device); | ||
338 | wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | ||
339 | eeepc_wmi_backlight_exit(eeepc); | ||
340 | eeepc_wmi_input_exit(eeepc); | ||
341 | |||
342 | return 0; | ||
343 | } | 145 | } |
344 | 146 | ||
345 | static struct platform_driver platform_driver = { | 147 | static struct asus_wmi_driver asus_wmi_driver = { |
346 | .driver = { | 148 | .name = EEEPC_WMI_FILE, |
347 | .name = EEEPC_WMI_FILE, | 149 | .owner = THIS_MODULE, |
348 | .owner = THIS_MODULE, | 150 | .event_guid = EEEPC_WMI_EVENT_GUID, |
349 | }, | 151 | .keymap = eeepc_wmi_keymap, |
350 | .probe = eeepc_wmi_platform_probe, | 152 | .input_name = "Eee PC WMI hotkeys", |
351 | .remove = __devexit_p(eeepc_wmi_platform_remove), | 153 | .input_phys = EEEPC_WMI_FILE "/input0", |
154 | .probe = eeepc_wmi_probe, | ||
155 | .quirks = eeepc_wmi_quirks, | ||
352 | }; | 156 | }; |
353 | 157 | ||
158 | |||
354 | static int __init eeepc_wmi_init(void) | 159 | static int __init eeepc_wmi_init(void) |
355 | { | 160 | { |
356 | struct eeepc_wmi *eeepc; | 161 | return asus_wmi_register_driver(&asus_wmi_driver); |
357 | int err; | ||
358 | |||
359 | if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || | ||
360 | !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { | ||
361 | pr_warning("No known WMI GUID found\n"); | ||
362 | return -ENODEV; | ||
363 | } | ||
364 | |||
365 | eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); | ||
366 | if (!eeepc) | ||
367 | return -ENOMEM; | ||
368 | |||
369 | platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); | ||
370 | if (!platform_device) { | ||
371 | pr_warning("Unable to allocate platform device\n"); | ||
372 | err = -ENOMEM; | ||
373 | goto fail_platform; | ||
374 | } | ||
375 | |||
376 | err = platform_device_add(platform_device); | ||
377 | if (err) { | ||
378 | pr_warning("Unable to add platform device\n"); | ||
379 | goto put_dev; | ||
380 | } | ||
381 | |||
382 | platform_set_drvdata(platform_device, eeepc); | ||
383 | |||
384 | err = platform_driver_register(&platform_driver); | ||
385 | if (err) { | ||
386 | pr_warning("Unable to register platform driver\n"); | ||
387 | goto del_dev; | ||
388 | } | ||
389 | |||
390 | return 0; | ||
391 | |||
392 | del_dev: | ||
393 | platform_device_del(platform_device); | ||
394 | put_dev: | ||
395 | platform_device_put(platform_device); | ||
396 | fail_platform: | ||
397 | kfree(eeepc); | ||
398 | |||
399 | return err; | ||
400 | } | 162 | } |
401 | 163 | ||
402 | static void __exit eeepc_wmi_exit(void) | 164 | static void __exit eeepc_wmi_exit(void) |
403 | { | 165 | { |
404 | struct eeepc_wmi *eeepc; | 166 | asus_wmi_unregister_driver(&asus_wmi_driver); |
405 | |||
406 | eeepc = platform_get_drvdata(platform_device); | ||
407 | platform_driver_unregister(&platform_driver); | ||
408 | platform_device_unregister(platform_device); | ||
409 | kfree(eeepc); | ||
410 | } | 167 | } |
411 | 168 | ||
412 | module_init(eeepc_wmi_init); | 169 | module_init(eeepc_wmi_init); |
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index f44cd2620ff9..6b26666b37f2 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c | |||
@@ -56,6 +56,8 @@ | |||
56 | * | 56 | * |
57 | */ | 57 | */ |
58 | 58 | ||
59 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
60 | |||
59 | #include <linux/module.h> | 61 | #include <linux/module.h> |
60 | #include <linux/kernel.h> | 62 | #include <linux/kernel.h> |
61 | #include <linux/init.h> | 63 | #include <linux/init.h> |
@@ -437,7 +439,7 @@ static int bl_update_status(struct backlight_device *b) | |||
437 | return ret; | 439 | return ret; |
438 | } | 440 | } |
439 | 441 | ||
440 | static struct backlight_ops fujitsubl_ops = { | 442 | static const struct backlight_ops fujitsubl_ops = { |
441 | .get_brightness = bl_get_brightness, | 443 | .get_brightness = bl_get_brightness, |
442 | .update_status = bl_update_status, | 444 | .update_status = bl_update_status, |
443 | }; | 445 | }; |
@@ -585,8 +587,7 @@ static struct platform_driver fujitsupf_driver = { | |||
585 | static void dmi_check_cb_common(const struct dmi_system_id *id) | 587 | static void dmi_check_cb_common(const struct dmi_system_id *id) |
586 | { | 588 | { |
587 | acpi_handle handle; | 589 | acpi_handle handle; |
588 | printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n", | 590 | pr_info("Identified laptop model '%s'\n", id->ident); |
589 | id->ident); | ||
590 | if (use_alt_lcd_levels == -1) { | 591 | if (use_alt_lcd_levels == -1) { |
591 | if (ACPI_SUCCESS(acpi_get_handle(NULL, | 592 | if (ACPI_SUCCESS(acpi_get_handle(NULL, |
592 | "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) | 593 | "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) |
@@ -689,13 +690,13 @@ static int acpi_fujitsu_add(struct acpi_device *device) | |||
689 | if (error) | 690 | if (error) |
690 | goto err_free_input_dev; | 691 | goto err_free_input_dev; |
691 | 692 | ||
692 | result = acpi_bus_get_power(fujitsu->acpi_handle, &state); | 693 | result = acpi_bus_update_power(fujitsu->acpi_handle, &state); |
693 | if (result) { | 694 | if (result) { |
694 | printk(KERN_ERR "Error reading power state\n"); | 695 | pr_err("Error reading power state\n"); |
695 | goto err_unregister_input_dev; | 696 | goto err_unregister_input_dev; |
696 | } | 697 | } |
697 | 698 | ||
698 | printk(KERN_INFO "ACPI: %s [%s] (%s)\n", | 699 | pr_info("ACPI: %s [%s] (%s)\n", |
699 | acpi_device_name(device), acpi_device_bid(device), | 700 | acpi_device_name(device), acpi_device_bid(device), |
700 | !device->power.state ? "on" : "off"); | 701 | !device->power.state ? "on" : "off"); |
701 | 702 | ||
@@ -707,7 +708,7 @@ static int acpi_fujitsu_add(struct acpi_device *device) | |||
707 | if (ACPI_FAILURE | 708 | if (ACPI_FAILURE |
708 | (acpi_evaluate_object | 709 | (acpi_evaluate_object |
709 | (device->handle, METHOD_NAME__INI, NULL, NULL))) | 710 | (device->handle, METHOD_NAME__INI, NULL, NULL))) |
710 | printk(KERN_ERR "_INI Method failed\n"); | 711 | pr_err("_INI Method failed\n"); |
711 | } | 712 | } |
712 | 713 | ||
713 | /* do config (detect defaults) */ | 714 | /* do config (detect defaults) */ |
@@ -827,7 +828,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
827 | error = kfifo_alloc(&fujitsu_hotkey->fifo, RINGBUFFERSIZE * sizeof(int), | 828 | error = kfifo_alloc(&fujitsu_hotkey->fifo, RINGBUFFERSIZE * sizeof(int), |
828 | GFP_KERNEL); | 829 | GFP_KERNEL); |
829 | if (error) { | 830 | if (error) { |
830 | printk(KERN_ERR "kfifo_alloc failed\n"); | 831 | pr_err("kfifo_alloc failed\n"); |
831 | goto err_stop; | 832 | goto err_stop; |
832 | } | 833 | } |
833 | 834 | ||
@@ -857,15 +858,15 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
857 | if (error) | 858 | if (error) |
858 | goto err_free_input_dev; | 859 | goto err_free_input_dev; |
859 | 860 | ||
860 | result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state); | 861 | result = acpi_bus_update_power(fujitsu_hotkey->acpi_handle, &state); |
861 | if (result) { | 862 | if (result) { |
862 | printk(KERN_ERR "Error reading power state\n"); | 863 | pr_err("Error reading power state\n"); |
863 | goto err_unregister_input_dev; | 864 | goto err_unregister_input_dev; |
864 | } | 865 | } |
865 | 866 | ||
866 | printk(KERN_INFO "ACPI: %s [%s] (%s)\n", | 867 | pr_info("ACPI: %s [%s] (%s)\n", |
867 | acpi_device_name(device), acpi_device_bid(device), | 868 | acpi_device_name(device), acpi_device_bid(device), |
868 | !device->power.state ? "on" : "off"); | 869 | !device->power.state ? "on" : "off"); |
869 | 870 | ||
870 | fujitsu_hotkey->dev = device; | 871 | fujitsu_hotkey->dev = device; |
871 | 872 | ||
@@ -875,7 +876,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
875 | if (ACPI_FAILURE | 876 | if (ACPI_FAILURE |
876 | (acpi_evaluate_object | 877 | (acpi_evaluate_object |
877 | (device->handle, METHOD_NAME__INI, NULL, NULL))) | 878 | (device->handle, METHOD_NAME__INI, NULL, NULL))) |
878 | printk(KERN_ERR "_INI Method failed\n"); | 879 | pr_err("_INI Method failed\n"); |
879 | } | 880 | } |
880 | 881 | ||
881 | i = 0; | 882 | i = 0; |
@@ -897,8 +898,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
897 | call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); | 898 | call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); |
898 | 899 | ||
899 | /* Suspect this is a keymap of the application panel, print it */ | 900 | /* Suspect this is a keymap of the application panel, print it */ |
900 | printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", | 901 | pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); |
901 | call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); | ||
902 | 902 | ||
903 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) | 903 | #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) |
904 | if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { | 904 | if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { |
@@ -907,8 +907,8 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
907 | if (result == 0) { | 907 | if (result == 0) { |
908 | fujitsu_hotkey->logolamp_registered = 1; | 908 | fujitsu_hotkey->logolamp_registered = 1; |
909 | } else { | 909 | } else { |
910 | printk(KERN_ERR "fujitsu-laptop: Could not register " | 910 | pr_err("Could not register LED handler for logo lamp, error %i\n", |
911 | "LED handler for logo lamp, error %i\n", result); | 911 | result); |
912 | } | 912 | } |
913 | } | 913 | } |
914 | 914 | ||
@@ -919,8 +919,8 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
919 | if (result == 0) { | 919 | if (result == 0) { |
920 | fujitsu_hotkey->kblamps_registered = 1; | 920 | fujitsu_hotkey->kblamps_registered = 1; |
921 | } else { | 921 | } else { |
922 | printk(KERN_ERR "fujitsu-laptop: Could not register " | 922 | pr_err("Could not register LED handler for keyboard lamps, error %i\n", |
923 | "LED handler for keyboard lamps, error %i\n", result); | 923 | result); |
924 | } | 924 | } |
925 | } | 925 | } |
926 | #endif | 926 | #endif |
@@ -1128,6 +1128,7 @@ static int __init fujitsu_init(void) | |||
1128 | 1128 | ||
1129 | memset(&props, 0, sizeof(struct backlight_properties)); | 1129 | memset(&props, 0, sizeof(struct backlight_properties)); |
1130 | max_brightness = fujitsu->max_brightness; | 1130 | max_brightness = fujitsu->max_brightness; |
1131 | props.type = BACKLIGHT_PLATFORM; | ||
1131 | props.max_brightness = max_brightness - 1; | 1132 | props.max_brightness = max_brightness - 1; |
1132 | fujitsu->bl_device = backlight_device_register("fujitsu-laptop", | 1133 | fujitsu->bl_device = backlight_device_register("fujitsu-laptop", |
1133 | NULL, NULL, | 1134 | NULL, NULL, |
@@ -1168,8 +1169,7 @@ static int __init fujitsu_init(void) | |||
1168 | fujitsu->bl_device->props.power = 0; | 1169 | fujitsu->bl_device->props.power = 0; |
1169 | } | 1170 | } |
1170 | 1171 | ||
1171 | printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION | 1172 | pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n"); |
1172 | " successfully loaded.\n"); | ||
1173 | 1173 | ||
1174 | return 0; | 1174 | return 0; |
1175 | 1175 | ||
@@ -1215,7 +1215,7 @@ static void __exit fujitsu_cleanup(void) | |||
1215 | 1215 | ||
1216 | kfree(fujitsu); | 1216 | kfree(fujitsu); |
1217 | 1217 | ||
1218 | printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); | 1218 | pr_info("driver unloaded\n"); |
1219 | } | 1219 | } |
1220 | 1220 | ||
1221 | module_init(fujitsu_init); | 1221 | module_init(fujitsu_init); |
@@ -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/hdaps.c b/drivers/platform/x86/hdaps.c new file mode 100644 index 000000000000..5a34973dc164 --- /dev/null +++ b/drivers/platform/x86/hdaps.c | |||
@@ -0,0 +1,638 @@ | |||
1 | /* | ||
2 | * hdaps.c - driver for IBM's Hard Drive Active Protection System | ||
3 | * | ||
4 | * Copyright (C) 2005 Robert Love <rml@novell.com> | ||
5 | * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> | ||
6 | * | ||
7 | * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads | ||
8 | * starting with the R40, T41, and X40. It provides a basic two-axis | ||
9 | * accelerometer and other data, such as the device's temperature. | ||
10 | * | ||
11 | * This driver is based on the document by Mark A. Smith available at | ||
12 | * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial | ||
13 | * and error. | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify it | ||
16 | * under the terms of the GNU General Public License v2 as published by the | ||
17 | * Free Software Foundation. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
20 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
21 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
22 | * more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License along with | ||
25 | * this program; if not, write to the Free Software Foundation, Inc., | ||
26 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | ||
27 | */ | ||
28 | |||
29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
30 | |||
31 | #include <linux/delay.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/input-polldev.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/mutex.h> | ||
36 | #include <linux/module.h> | ||
37 | #include <linux/timer.h> | ||
38 | #include <linux/dmi.h> | ||
39 | #include <linux/jiffies.h> | ||
40 | #include <linux/io.h> | ||
41 | |||
42 | #define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ | ||
43 | #define HDAPS_NR_PORTS 0x30 /* number of ports: 0x1600 - 0x162f */ | ||
44 | |||
45 | #define HDAPS_PORT_STATE 0x1611 /* device state */ | ||
46 | #define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ | ||
47 | #define HDAPS_PORT_XPOS 0x1614 /* x-axis position */ | ||
48 | #define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */ | ||
49 | #define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */ | ||
50 | #define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ | ||
51 | #define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ | ||
52 | #define HDAPS_PORT_UNKNOWN 0x161c /* what is this? */ | ||
53 | #define HDAPS_PORT_KMACT 0x161d /* keyboard or mouse activity */ | ||
54 | |||
55 | #define STATE_FRESH 0x50 /* accelerometer data is fresh */ | ||
56 | |||
57 | #define KEYBD_MASK 0x20 /* set if keyboard activity */ | ||
58 | #define MOUSE_MASK 0x40 /* set if mouse activity */ | ||
59 | #define KEYBD_ISSET(n) (!! (n & KEYBD_MASK)) /* keyboard used? */ | ||
60 | #define MOUSE_ISSET(n) (!! (n & MOUSE_MASK)) /* mouse used? */ | ||
61 | |||
62 | #define INIT_TIMEOUT_MSECS 4000 /* wait up to 4s for device init ... */ | ||
63 | #define INIT_WAIT_MSECS 200 /* ... in 200ms increments */ | ||
64 | |||
65 | #define HDAPS_POLL_INTERVAL 50 /* poll for input every 1/20s (50 ms)*/ | ||
66 | #define HDAPS_INPUT_FUZZ 4 /* input event threshold */ | ||
67 | #define HDAPS_INPUT_FLAT 4 | ||
68 | |||
69 | #define HDAPS_X_AXIS (1 << 0) | ||
70 | #define HDAPS_Y_AXIS (1 << 1) | ||
71 | #define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS) | ||
72 | |||
73 | static struct platform_device *pdev; | ||
74 | static struct input_polled_dev *hdaps_idev; | ||
75 | static unsigned int hdaps_invert; | ||
76 | static u8 km_activity; | ||
77 | static int rest_x; | ||
78 | static int rest_y; | ||
79 | |||
80 | static DEFINE_MUTEX(hdaps_mtx); | ||
81 | |||
82 | /* | ||
83 | * __get_latch - Get the value from a given port. Callers must hold hdaps_mtx. | ||
84 | */ | ||
85 | static inline u8 __get_latch(u16 port) | ||
86 | { | ||
87 | return inb(port) & 0xff; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * __check_latch - Check a port latch for a given value. Returns zero if the | ||
92 | * port contains the given value. Callers must hold hdaps_mtx. | ||
93 | */ | ||
94 | static inline int __check_latch(u16 port, u8 val) | ||
95 | { | ||
96 | if (__get_latch(port) == val) | ||
97 | return 0; | ||
98 | return -EINVAL; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * __wait_latch - Wait up to 100us for a port latch to get a certain value, | ||
103 | * returning zero if the value is obtained. Callers must hold hdaps_mtx. | ||
104 | */ | ||
105 | static int __wait_latch(u16 port, u8 val) | ||
106 | { | ||
107 | unsigned int i; | ||
108 | |||
109 | for (i = 0; i < 20; i++) { | ||
110 | if (!__check_latch(port, val)) | ||
111 | return 0; | ||
112 | udelay(5); | ||
113 | } | ||
114 | |||
115 | return -EIO; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * __device_refresh - request a refresh from the accelerometer. Does not wait | ||
120 | * for refresh to complete. Callers must hold hdaps_mtx. | ||
121 | */ | ||
122 | static void __device_refresh(void) | ||
123 | { | ||
124 | udelay(200); | ||
125 | if (inb(0x1604) != STATE_FRESH) { | ||
126 | outb(0x11, 0x1610); | ||
127 | outb(0x01, 0x161f); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * __device_refresh_sync - request a synchronous refresh from the | ||
133 | * accelerometer. We wait for the refresh to complete. Returns zero if | ||
134 | * successful and nonzero on error. Callers must hold hdaps_mtx. | ||
135 | */ | ||
136 | static int __device_refresh_sync(void) | ||
137 | { | ||
138 | __device_refresh(); | ||
139 | return __wait_latch(0x1604, STATE_FRESH); | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * __device_complete - indicate to the accelerometer that we are done reading | ||
144 | * data, and then initiate an async refresh. Callers must hold hdaps_mtx. | ||
145 | */ | ||
146 | static inline void __device_complete(void) | ||
147 | { | ||
148 | inb(0x161f); | ||
149 | inb(0x1604); | ||
150 | __device_refresh(); | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * hdaps_readb_one - reads a byte from a single I/O port, placing the value in | ||
155 | * the given pointer. Returns zero on success or a negative error on failure. | ||
156 | * Can sleep. | ||
157 | */ | ||
158 | static int hdaps_readb_one(unsigned int port, u8 *val) | ||
159 | { | ||
160 | int ret; | ||
161 | |||
162 | mutex_lock(&hdaps_mtx); | ||
163 | |||
164 | /* do a sync refresh -- we need to be sure that we read fresh data */ | ||
165 | ret = __device_refresh_sync(); | ||
166 | if (ret) | ||
167 | goto out; | ||
168 | |||
169 | *val = inb(port); | ||
170 | __device_complete(); | ||
171 | |||
172 | out: | ||
173 | mutex_unlock(&hdaps_mtx); | ||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | /* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */ | ||
178 | static int __hdaps_read_pair(unsigned int port1, unsigned int port2, | ||
179 | int *x, int *y) | ||
180 | { | ||
181 | /* do a sync refresh -- we need to be sure that we read fresh data */ | ||
182 | if (__device_refresh_sync()) | ||
183 | return -EIO; | ||
184 | |||
185 | *y = inw(port2); | ||
186 | *x = inw(port1); | ||
187 | km_activity = inb(HDAPS_PORT_KMACT); | ||
188 | __device_complete(); | ||
189 | |||
190 | /* hdaps_invert is a bitvector to negate the axes */ | ||
191 | if (hdaps_invert & HDAPS_X_AXIS) | ||
192 | *x = -*x; | ||
193 | if (hdaps_invert & HDAPS_Y_AXIS) | ||
194 | *y = -*y; | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * hdaps_read_pair - reads the values from a pair of ports, placing the values | ||
201 | * in the given pointers. Returns zero on success. Can sleep. | ||
202 | */ | ||
203 | static int hdaps_read_pair(unsigned int port1, unsigned int port2, | ||
204 | int *val1, int *val2) | ||
205 | { | ||
206 | int ret; | ||
207 | |||
208 | mutex_lock(&hdaps_mtx); | ||
209 | ret = __hdaps_read_pair(port1, port2, val1, val2); | ||
210 | mutex_unlock(&hdaps_mtx); | ||
211 | |||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * hdaps_device_init - initialize the accelerometer. Returns zero on success | ||
217 | * and negative error code on failure. Can sleep. | ||
218 | */ | ||
219 | static int hdaps_device_init(void) | ||
220 | { | ||
221 | int total, ret = -ENXIO; | ||
222 | |||
223 | mutex_lock(&hdaps_mtx); | ||
224 | |||
225 | outb(0x13, 0x1610); | ||
226 | outb(0x01, 0x161f); | ||
227 | if (__wait_latch(0x161f, 0x00)) | ||
228 | goto out; | ||
229 | |||
230 | /* | ||
231 | * Most ThinkPads return 0x01. | ||
232 | * | ||
233 | * Others--namely the R50p, T41p, and T42p--return 0x03. These laptops | ||
234 | * have "inverted" axises. | ||
235 | * | ||
236 | * The 0x02 value occurs when the chip has been previously initialized. | ||
237 | */ | ||
238 | if (__check_latch(0x1611, 0x03) && | ||
239 | __check_latch(0x1611, 0x02) && | ||
240 | __check_latch(0x1611, 0x01)) | ||
241 | goto out; | ||
242 | |||
243 | printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x)\n", | ||
244 | __get_latch(0x1611)); | ||
245 | |||
246 | outb(0x17, 0x1610); | ||
247 | outb(0x81, 0x1611); | ||
248 | outb(0x01, 0x161f); | ||
249 | if (__wait_latch(0x161f, 0x00)) | ||
250 | goto out; | ||
251 | if (__wait_latch(0x1611, 0x00)) | ||
252 | goto out; | ||
253 | if (__wait_latch(0x1612, 0x60)) | ||
254 | goto out; | ||
255 | if (__wait_latch(0x1613, 0x00)) | ||
256 | goto out; | ||
257 | outb(0x14, 0x1610); | ||
258 | outb(0x01, 0x1611); | ||
259 | outb(0x01, 0x161f); | ||
260 | if (__wait_latch(0x161f, 0x00)) | ||
261 | goto out; | ||
262 | outb(0x10, 0x1610); | ||
263 | outb(0xc8, 0x1611); | ||
264 | outb(0x00, 0x1612); | ||
265 | outb(0x02, 0x1613); | ||
266 | outb(0x01, 0x161f); | ||
267 | if (__wait_latch(0x161f, 0x00)) | ||
268 | goto out; | ||
269 | if (__device_refresh_sync()) | ||
270 | goto out; | ||
271 | if (__wait_latch(0x1611, 0x00)) | ||
272 | goto out; | ||
273 | |||
274 | /* we have done our dance, now let's wait for the applause */ | ||
275 | for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { | ||
276 | int x, y; | ||
277 | |||
278 | /* a read of the device helps push it into action */ | ||
279 | __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); | ||
280 | if (!__wait_latch(0x1611, 0x02)) { | ||
281 | ret = 0; | ||
282 | break; | ||
283 | } | ||
284 | |||
285 | msleep(INIT_WAIT_MSECS); | ||
286 | } | ||
287 | |||
288 | out: | ||
289 | mutex_unlock(&hdaps_mtx); | ||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | |||
294 | /* Device model stuff */ | ||
295 | |||
296 | static int hdaps_probe(struct platform_device *dev) | ||
297 | { | ||
298 | int ret; | ||
299 | |||
300 | ret = hdaps_device_init(); | ||
301 | if (ret) | ||
302 | return ret; | ||
303 | |||
304 | pr_info("device successfully initialized\n"); | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static int hdaps_resume(struct platform_device *dev) | ||
309 | { | ||
310 | return hdaps_device_init(); | ||
311 | } | ||
312 | |||
313 | static struct platform_driver hdaps_driver = { | ||
314 | .probe = hdaps_probe, | ||
315 | .resume = hdaps_resume, | ||
316 | .driver = { | ||
317 | .name = "hdaps", | ||
318 | .owner = THIS_MODULE, | ||
319 | }, | ||
320 | }; | ||
321 | |||
322 | /* | ||
323 | * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_mtx. | ||
324 | */ | ||
325 | static void hdaps_calibrate(void) | ||
326 | { | ||
327 | __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y); | ||
328 | } | ||
329 | |||
330 | static void hdaps_mousedev_poll(struct input_polled_dev *dev) | ||
331 | { | ||
332 | struct input_dev *input_dev = dev->input; | ||
333 | int x, y; | ||
334 | |||
335 | mutex_lock(&hdaps_mtx); | ||
336 | |||
337 | if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y)) | ||
338 | goto out; | ||
339 | |||
340 | input_report_abs(input_dev, ABS_X, x - rest_x); | ||
341 | input_report_abs(input_dev, ABS_Y, y - rest_y); | ||
342 | input_sync(input_dev); | ||
343 | |||
344 | out: | ||
345 | mutex_unlock(&hdaps_mtx); | ||
346 | } | ||
347 | |||
348 | |||
349 | /* Sysfs Files */ | ||
350 | |||
351 | static ssize_t hdaps_position_show(struct device *dev, | ||
352 | struct device_attribute *attr, char *buf) | ||
353 | { | ||
354 | int ret, x, y; | ||
355 | |||
356 | ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y); | ||
357 | if (ret) | ||
358 | return ret; | ||
359 | |||
360 | return sprintf(buf, "(%d,%d)\n", x, y); | ||
361 | } | ||
362 | |||
363 | static ssize_t hdaps_variance_show(struct device *dev, | ||
364 | struct device_attribute *attr, char *buf) | ||
365 | { | ||
366 | int ret, x, y; | ||
367 | |||
368 | ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y); | ||
369 | if (ret) | ||
370 | return ret; | ||
371 | |||
372 | return sprintf(buf, "(%d,%d)\n", x, y); | ||
373 | } | ||
374 | |||
375 | static ssize_t hdaps_temp1_show(struct device *dev, | ||
376 | struct device_attribute *attr, char *buf) | ||
377 | { | ||
378 | u8 temp; | ||
379 | int ret; | ||
380 | |||
381 | ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); | ||
382 | if (ret < 0) | ||
383 | return ret; | ||
384 | |||
385 | return sprintf(buf, "%u\n", temp); | ||
386 | } | ||
387 | |||
388 | static ssize_t hdaps_temp2_show(struct device *dev, | ||
389 | struct device_attribute *attr, char *buf) | ||
390 | { | ||
391 | u8 temp; | ||
392 | int ret; | ||
393 | |||
394 | ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); | ||
395 | if (ret < 0) | ||
396 | return ret; | ||
397 | |||
398 | return sprintf(buf, "%u\n", temp); | ||
399 | } | ||
400 | |||
401 | static ssize_t hdaps_keyboard_activity_show(struct device *dev, | ||
402 | struct device_attribute *attr, | ||
403 | char *buf) | ||
404 | { | ||
405 | return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity)); | ||
406 | } | ||
407 | |||
408 | static ssize_t hdaps_mouse_activity_show(struct device *dev, | ||
409 | struct device_attribute *attr, | ||
410 | char *buf) | ||
411 | { | ||
412 | return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity)); | ||
413 | } | ||
414 | |||
415 | static ssize_t hdaps_calibrate_show(struct device *dev, | ||
416 | struct device_attribute *attr, char *buf) | ||
417 | { | ||
418 | return sprintf(buf, "(%d,%d)\n", rest_x, rest_y); | ||
419 | } | ||
420 | |||
421 | static ssize_t hdaps_calibrate_store(struct device *dev, | ||
422 | struct device_attribute *attr, | ||
423 | const char *buf, size_t count) | ||
424 | { | ||
425 | mutex_lock(&hdaps_mtx); | ||
426 | hdaps_calibrate(); | ||
427 | mutex_unlock(&hdaps_mtx); | ||
428 | |||
429 | return count; | ||
430 | } | ||
431 | |||
432 | static ssize_t hdaps_invert_show(struct device *dev, | ||
433 | struct device_attribute *attr, char *buf) | ||
434 | { | ||
435 | return sprintf(buf, "%u\n", hdaps_invert); | ||
436 | } | ||
437 | |||
438 | static ssize_t hdaps_invert_store(struct device *dev, | ||
439 | struct device_attribute *attr, | ||
440 | const char *buf, size_t count) | ||
441 | { | ||
442 | int invert; | ||
443 | |||
444 | if (sscanf(buf, "%d", &invert) != 1 || | ||
445 | invert < 0 || invert > HDAPS_BOTH_AXES) | ||
446 | return -EINVAL; | ||
447 | |||
448 | hdaps_invert = invert; | ||
449 | hdaps_calibrate(); | ||
450 | |||
451 | return count; | ||
452 | } | ||
453 | |||
454 | static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL); | ||
455 | static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL); | ||
456 | static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL); | ||
457 | static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL); | ||
458 | static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL); | ||
459 | static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL); | ||
460 | static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store); | ||
461 | static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store); | ||
462 | |||
463 | static struct attribute *hdaps_attributes[] = { | ||
464 | &dev_attr_position.attr, | ||
465 | &dev_attr_variance.attr, | ||
466 | &dev_attr_temp1.attr, | ||
467 | &dev_attr_temp2.attr, | ||
468 | &dev_attr_keyboard_activity.attr, | ||
469 | &dev_attr_mouse_activity.attr, | ||
470 | &dev_attr_calibrate.attr, | ||
471 | &dev_attr_invert.attr, | ||
472 | NULL, | ||
473 | }; | ||
474 | |||
475 | static struct attribute_group hdaps_attribute_group = { | ||
476 | .attrs = hdaps_attributes, | ||
477 | }; | ||
478 | |||
479 | |||
480 | /* Module stuff */ | ||
481 | |||
482 | /* hdaps_dmi_match - found a match. return one, short-circuiting the hunt. */ | ||
483 | static int __init hdaps_dmi_match(const struct dmi_system_id *id) | ||
484 | { | ||
485 | pr_info("%s detected\n", id->ident); | ||
486 | return 1; | ||
487 | } | ||
488 | |||
489 | /* hdaps_dmi_match_invert - found an inverted match. */ | ||
490 | static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id) | ||
491 | { | ||
492 | hdaps_invert = (unsigned long)id->driver_data; | ||
493 | pr_info("inverting axis (%u) readings\n", hdaps_invert); | ||
494 | return hdaps_dmi_match(id); | ||
495 | } | ||
496 | |||
497 | #define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \ | ||
498 | .ident = vendor " " model, \ | ||
499 | .callback = hdaps_dmi_match_invert, \ | ||
500 | .driver_data = (void *)axes, \ | ||
501 | .matches = { \ | ||
502 | DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ | ||
503 | DMI_MATCH(DMI_PRODUCT_VERSION, model) \ | ||
504 | } \ | ||
505 | } | ||
506 | |||
507 | #define HDAPS_DMI_MATCH_NORMAL(vendor, model) \ | ||
508 | HDAPS_DMI_MATCH_INVERT(vendor, model, 0) | ||
509 | |||
510 | /* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match | ||
511 | "ThinkPad T42p", so the order of the entries matters. | ||
512 | If your ThinkPad is not recognized, please update to latest | ||
513 | BIOS. This is especially the case for some R52 ThinkPads. */ | ||
514 | static struct dmi_system_id __initdata hdaps_whitelist[] = { | ||
515 | HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES), | ||
516 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), | ||
517 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), | ||
518 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), | ||
519 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES), | ||
520 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES), | ||
521 | HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES), | ||
522 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), | ||
523 | HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES), | ||
524 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), | ||
525 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), | ||
526 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T400", HDAPS_BOTH_AXES), | ||
527 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES), | ||
528 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES), | ||
529 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES), | ||
530 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), | ||
531 | HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS), | ||
532 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES), | ||
533 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES), | ||
534 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES), | ||
535 | HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), | ||
536 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES), | ||
537 | HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES), | ||
538 | { .ident = NULL } | ||
539 | }; | ||
540 | |||
541 | static int __init hdaps_init(void) | ||
542 | { | ||
543 | struct input_dev *idev; | ||
544 | int ret; | ||
545 | |||
546 | if (!dmi_check_system(hdaps_whitelist)) { | ||
547 | pr_warn("supported laptop not found!\n"); | ||
548 | ret = -ENODEV; | ||
549 | goto out; | ||
550 | } | ||
551 | |||
552 | if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) { | ||
553 | ret = -ENXIO; | ||
554 | goto out; | ||
555 | } | ||
556 | |||
557 | ret = platform_driver_register(&hdaps_driver); | ||
558 | if (ret) | ||
559 | goto out_region; | ||
560 | |||
561 | pdev = platform_device_register_simple("hdaps", -1, NULL, 0); | ||
562 | if (IS_ERR(pdev)) { | ||
563 | ret = PTR_ERR(pdev); | ||
564 | goto out_driver; | ||
565 | } | ||
566 | |||
567 | ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group); | ||
568 | if (ret) | ||
569 | goto out_device; | ||
570 | |||
571 | hdaps_idev = input_allocate_polled_device(); | ||
572 | if (!hdaps_idev) { | ||
573 | ret = -ENOMEM; | ||
574 | goto out_group; | ||
575 | } | ||
576 | |||
577 | hdaps_idev->poll = hdaps_mousedev_poll; | ||
578 | hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL; | ||
579 | |||
580 | /* initial calibrate for the input device */ | ||
581 | hdaps_calibrate(); | ||
582 | |||
583 | /* initialize the input class */ | ||
584 | idev = hdaps_idev->input; | ||
585 | idev->name = "hdaps"; | ||
586 | idev->phys = "isa1600/input0"; | ||
587 | idev->id.bustype = BUS_ISA; | ||
588 | idev->dev.parent = &pdev->dev; | ||
589 | idev->evbit[0] = BIT_MASK(EV_ABS); | ||
590 | input_set_abs_params(idev, ABS_X, | ||
591 | -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); | ||
592 | input_set_abs_params(idev, ABS_Y, | ||
593 | -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); | ||
594 | |||
595 | ret = input_register_polled_device(hdaps_idev); | ||
596 | if (ret) | ||
597 | goto out_idev; | ||
598 | |||
599 | pr_info("driver successfully loaded\n"); | ||
600 | return 0; | ||
601 | |||
602 | out_idev: | ||
603 | input_free_polled_device(hdaps_idev); | ||
604 | out_group: | ||
605 | sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); | ||
606 | out_device: | ||
607 | platform_device_unregister(pdev); | ||
608 | out_driver: | ||
609 | platform_driver_unregister(&hdaps_driver); | ||
610 | out_region: | ||
611 | release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); | ||
612 | out: | ||
613 | pr_warn("driver init failed (ret=%d)!\n", ret); | ||
614 | return ret; | ||
615 | } | ||
616 | |||
617 | static void __exit hdaps_exit(void) | ||
618 | { | ||
619 | input_unregister_polled_device(hdaps_idev); | ||
620 | input_free_polled_device(hdaps_idev); | ||
621 | sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); | ||
622 | platform_device_unregister(pdev); | ||
623 | platform_driver_unregister(&hdaps_driver); | ||
624 | release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS); | ||
625 | |||
626 | pr_info("driver unloaded\n"); | ||
627 | } | ||
628 | |||
629 | module_init(hdaps_init); | ||
630 | module_exit(hdaps_exit); | ||
631 | |||
632 | module_param_named(invert, hdaps_invert, int, 0); | ||
633 | MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, " | ||
634 | "2 invert y-axis, 3 invert both axes."); | ||
635 | |||
636 | MODULE_AUTHOR("Robert Love"); | ||
637 | MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); | ||
638 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index c1741142a4cb..e2faa3cbb792 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * HP WMI hotkeys | 2 | * HP WMI hotkeys |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Red Hat <mjg@redhat.com> | 4 | * Copyright (C) 2008 Red Hat <mjg@redhat.com> |
5 | * Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi> | ||
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> |
@@ -23,12 +24,15 @@ | |||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 | */ | 25 | */ |
25 | 26 | ||
27 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
28 | |||
26 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
27 | #include <linux/module.h> | 30 | #include <linux/module.h> |
28 | #include <linux/init.h> | 31 | #include <linux/init.h> |
29 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
30 | #include <linux/types.h> | 33 | #include <linux/types.h> |
31 | #include <linux/input.h> | 34 | #include <linux/input.h> |
35 | #include <linux/input/sparse-keymap.h> | ||
32 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
33 | #include <linux/acpi.h> | 37 | #include <linux/acpi.h> |
34 | #include <linux/rfkill.h> | 38 | #include <linux/rfkill.h> |
@@ -50,9 +54,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); | |||
50 | #define HPWMI_HARDWARE_QUERY 0x4 | 54 | #define HPWMI_HARDWARE_QUERY 0x4 |
51 | #define HPWMI_WIRELESS_QUERY 0x5 | 55 | #define HPWMI_WIRELESS_QUERY 0x5 |
52 | #define HPWMI_HOTKEY_QUERY 0xc | 56 | #define HPWMI_HOTKEY_QUERY 0xc |
53 | 57 | #define HPWMI_WIRELESS2_QUERY 0x1b | |
54 | #define PREFIX "HP WMI: " | ||
55 | #define UNIMP "Unimplemented " | ||
56 | 58 | ||
57 | enum hp_wmi_radio { | 59 | enum hp_wmi_radio { |
58 | HPWMI_WIFI = 0, | 60 | HPWMI_WIFI = 0, |
@@ -85,27 +87,58 @@ struct bios_args { | |||
85 | struct bios_return { | 87 | struct bios_return { |
86 | u32 sigpass; | 88 | u32 sigpass; |
87 | u32 return_code; | 89 | u32 return_code; |
88 | u32 value; | ||
89 | }; | 90 | }; |
90 | 91 | ||
91 | struct key_entry { | 92 | enum hp_return_value { |
92 | char type; /* See KE_* below */ | 93 | HPWMI_RET_WRONG_SIGNATURE = 0x02, |
93 | u16 code; | 94 | HPWMI_RET_UNKNOWN_COMMAND = 0x03, |
94 | u16 keycode; | 95 | HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, |
96 | HPWMI_RET_INVALID_PARAMETERS = 0x05, | ||
97 | }; | ||
98 | |||
99 | enum hp_wireless2_bits { | ||
100 | HPWMI_POWER_STATE = 0x01, | ||
101 | HPWMI_POWER_SOFT = 0x02, | ||
102 | HPWMI_POWER_BIOS = 0x04, | ||
103 | HPWMI_POWER_HARD = 0x08, | ||
95 | }; | 104 | }; |
96 | 105 | ||
97 | enum { KE_KEY, KE_END }; | 106 | #define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \ |
98 | 107 | != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) | |
99 | static struct key_entry hp_wmi_keymap[] = { | 108 | #define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) |
100 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, | 109 | |
101 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, | 110 | struct bios_rfkill2_device_state { |
102 | {KE_KEY, 0x20e6, KEY_PROG1}, | 111 | u8 radio_type; |
103 | {KE_KEY, 0x20e8, KEY_MEDIA}, | 112 | u8 bus_type; |
104 | {KE_KEY, 0x2142, KEY_MEDIA}, | 113 | u16 vendor_id; |
105 | {KE_KEY, 0x213b, KEY_INFO}, | 114 | u16 product_id; |
106 | {KE_KEY, 0x2169, KEY_DIRECTION}, | 115 | u16 subsys_vendor_id; |
107 | {KE_KEY, 0x231b, KEY_HELP}, | 116 | u16 subsys_product_id; |
108 | {KE_END, 0} | 117 | u8 rfkill_id; |
118 | u8 power; | ||
119 | u8 unknown[4]; | ||
120 | }; | ||
121 | |||
122 | /* 7 devices fit into the 128 byte buffer */ | ||
123 | #define HPWMI_MAX_RFKILL2_DEVICES 7 | ||
124 | |||
125 | struct bios_rfkill2_state { | ||
126 | u8 unknown[7]; | ||
127 | u8 count; | ||
128 | u8 pad[8]; | ||
129 | struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; | ||
130 | }; | ||
131 | |||
132 | static const struct key_entry hp_wmi_keymap[] = { | ||
133 | { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, | ||
134 | { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, | ||
135 | { KE_KEY, 0x20e6, { KEY_PROG1 } }, | ||
136 | { KE_KEY, 0x20e8, { KEY_MEDIA } }, | ||
137 | { KE_KEY, 0x2142, { KEY_MEDIA } }, | ||
138 | { KE_KEY, 0x213b, { KEY_INFO } }, | ||
139 | { KE_KEY, 0x2169, { KEY_DIRECTION } }, | ||
140 | { KE_KEY, 0x231b, { KEY_HELP } }, | ||
141 | { KE_END, 0 } | ||
109 | }; | 142 | }; |
110 | 143 | ||
111 | static struct input_dev *hp_wmi_input_dev; | 144 | static struct input_dev *hp_wmi_input_dev; |
@@ -115,6 +148,15 @@ static struct rfkill *wifi_rfkill; | |||
115 | static struct rfkill *bluetooth_rfkill; | 148 | static struct rfkill *bluetooth_rfkill; |
116 | static struct rfkill *wwan_rfkill; | 149 | static struct rfkill *wwan_rfkill; |
117 | 150 | ||
151 | struct rfkill2_device { | ||
152 | u8 id; | ||
153 | int num; | ||
154 | struct rfkill *rfkill; | ||
155 | }; | ||
156 | |||
157 | static int rfkill2_count; | ||
158 | static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; | ||
159 | |||
118 | static const struct dev_pm_ops hp_wmi_pm_ops = { | 160 | static const struct dev_pm_ops hp_wmi_pm_ops = { |
119 | .resume = hp_wmi_resume_handler, | 161 | .resume = hp_wmi_resume_handler, |
120 | .restore = hp_wmi_resume_handler, | 162 | .restore = hp_wmi_resume_handler, |
@@ -136,7 +178,8 @@ static struct platform_driver hp_wmi_driver = { | |||
136 | * query: The commandtype -> What should be queried | 178 | * query: The commandtype -> What should be queried |
137 | * write: The command -> 0 read, 1 write, 3 ODM specific | 179 | * write: The command -> 0 read, 1 write, 3 ODM specific |
138 | * buffer: Buffer used as input and/or output | 180 | * buffer: Buffer used as input and/or output |
139 | * buffersize: Size of buffer | 181 | * insize: Size of input buffer |
182 | * outsize: Size of output buffer | ||
140 | * | 183 | * |
141 | * returns zero on success | 184 | * returns zero on success |
142 | * an HP WMI query specific error code (which is positive) | 185 | * an HP WMI query specific error code (which is positive) |
@@ -147,25 +190,30 @@ static struct platform_driver hp_wmi_driver = { | |||
147 | * size. E.g. Battery info query (0x7) is defined to have 1 byte input | 190 | * size. E.g. Battery info query (0x7) is defined to have 1 byte input |
148 | * and 128 byte output. The caller would do: | 191 | * and 128 byte output. The caller would do: |
149 | * buffer = kzalloc(128, GFP_KERNEL); | 192 | * buffer = kzalloc(128, GFP_KERNEL); |
150 | * ret = hp_wmi_perform_query(0x7, 0, buffer, 128) | 193 | * ret = hp_wmi_perform_query(0x7, 0, buffer, 1, 128) |
151 | */ | 194 | */ |
152 | static int hp_wmi_perform_query(int query, int write, u32 *buffer, | 195 | static int hp_wmi_perform_query(int query, int write, void *buffer, |
153 | int buffersize) | 196 | int insize, int outsize) |
154 | { | 197 | { |
155 | struct bios_return bios_return; | 198 | struct bios_return *bios_return; |
156 | acpi_status status; | 199 | int actual_outsize; |
157 | union acpi_object *obj; | 200 | union acpi_object *obj; |
158 | struct bios_args args = { | 201 | struct bios_args args = { |
159 | .signature = 0x55434553, | 202 | .signature = 0x55434553, |
160 | .command = write ? 0x2 : 0x1, | 203 | .command = write ? 0x2 : 0x1, |
161 | .commandtype = query, | 204 | .commandtype = query, |
162 | .datasize = buffersize, | 205 | .datasize = insize, |
163 | .data = *buffer, | 206 | .data = 0, |
164 | }; | 207 | }; |
165 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; | 208 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; |
166 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 209 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
210 | u32 rc; | ||
211 | |||
212 | if (WARN_ON(insize > sizeof(args.data))) | ||
213 | return -EINVAL; | ||
214 | memcpy(&args.data, buffer, insize); | ||
167 | 215 | ||
168 | status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); | 216 | wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); |
169 | 217 | ||
170 | obj = output.pointer; | 218 | obj = output.pointer; |
171 | 219 | ||
@@ -176,9 +224,26 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, | |||
176 | return -EINVAL; | 224 | return -EINVAL; |
177 | } | 225 | } |
178 | 226 | ||
179 | bios_return = *((struct bios_return *)obj->buffer.pointer); | 227 | bios_return = (struct bios_return *)obj->buffer.pointer; |
228 | rc = bios_return->return_code; | ||
229 | |||
230 | if (rc) { | ||
231 | if (rc != HPWMI_RET_UNKNOWN_CMDTYPE) | ||
232 | pr_warn("query 0x%x returned error 0x%x\n", query, rc); | ||
233 | kfree(obj); | ||
234 | return rc; | ||
235 | } | ||
236 | |||
237 | if (!outsize) { | ||
238 | /* ignore output data */ | ||
239 | kfree(obj); | ||
240 | return 0; | ||
241 | } | ||
180 | 242 | ||
181 | memcpy(buffer, &bios_return.value, sizeof(bios_return.value)); | 243 | actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); |
244 | memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); | ||
245 | memset(buffer + actual_outsize, 0, outsize - actual_outsize); | ||
246 | kfree(obj); | ||
182 | return 0; | 247 | return 0; |
183 | } | 248 | } |
184 | 249 | ||
@@ -186,7 +251,7 @@ static int hp_wmi_display_state(void) | |||
186 | { | 251 | { |
187 | int state = 0; | 252 | int state = 0; |
188 | int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state, | 253 | int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state, |
189 | sizeof(state)); | 254 | sizeof(state), sizeof(state)); |
190 | if (ret) | 255 | if (ret) |
191 | return -EINVAL; | 256 | return -EINVAL; |
192 | return state; | 257 | return state; |
@@ -196,7 +261,7 @@ static int hp_wmi_hddtemp_state(void) | |||
196 | { | 261 | { |
197 | int state = 0; | 262 | int state = 0; |
198 | int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state, | 263 | int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state, |
199 | sizeof(state)); | 264 | sizeof(state), sizeof(state)); |
200 | if (ret) | 265 | if (ret) |
201 | return -EINVAL; | 266 | return -EINVAL; |
202 | return state; | 267 | return state; |
@@ -206,7 +271,7 @@ static int hp_wmi_als_state(void) | |||
206 | { | 271 | { |
207 | int state = 0; | 272 | int state = 0; |
208 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state, | 273 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state, |
209 | sizeof(state)); | 274 | sizeof(state), sizeof(state)); |
210 | if (ret) | 275 | if (ret) |
211 | return -EINVAL; | 276 | return -EINVAL; |
212 | return state; | 277 | return state; |
@@ -216,7 +281,7 @@ static int hp_wmi_dock_state(void) | |||
216 | { | 281 | { |
217 | int state = 0; | 282 | int state = 0; |
218 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, | 283 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, |
219 | sizeof(state)); | 284 | sizeof(state), sizeof(state)); |
220 | 285 | ||
221 | if (ret) | 286 | if (ret) |
222 | return -EINVAL; | 287 | return -EINVAL; |
@@ -228,7 +293,7 @@ static int hp_wmi_tablet_state(void) | |||
228 | { | 293 | { |
229 | int state = 0; | 294 | int state = 0; |
230 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, | 295 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, |
231 | sizeof(state)); | 296 | sizeof(state), sizeof(state)); |
232 | if (ret) | 297 | if (ret) |
233 | return ret; | 298 | return ret; |
234 | 299 | ||
@@ -242,7 +307,7 @@ static int hp_wmi_set_block(void *data, bool blocked) | |||
242 | int ret; | 307 | int ret; |
243 | 308 | ||
244 | ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, | 309 | ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, |
245 | &query, sizeof(query)); | 310 | &query, sizeof(query), 0); |
246 | if (ret) | 311 | if (ret) |
247 | return -EINVAL; | 312 | return -EINVAL; |
248 | return 0; | 313 | return 0; |
@@ -257,7 +322,8 @@ static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) | |||
257 | int wireless = 0; | 322 | int wireless = 0; |
258 | int mask; | 323 | int mask; |
259 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, | 324 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, |
260 | &wireless, sizeof(wireless)); | 325 | &wireless, sizeof(wireless), |
326 | sizeof(wireless)); | ||
261 | /* TBD: Pass error */ | 327 | /* TBD: Pass error */ |
262 | 328 | ||
263 | mask = 0x200 << (r * 8); | 329 | mask = 0x200 << (r * 8); |
@@ -273,7 +339,8 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) | |||
273 | int wireless = 0; | 339 | int wireless = 0; |
274 | int mask; | 340 | int mask; |
275 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, | 341 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, |
276 | &wireless, sizeof(wireless)); | 342 | &wireless, sizeof(wireless), |
343 | sizeof(wireless)); | ||
277 | /* TBD: Pass error */ | 344 | /* TBD: Pass error */ |
278 | 345 | ||
279 | mask = 0x800 << (r * 8); | 346 | mask = 0x800 << (r * 8); |
@@ -284,6 +351,50 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) | |||
284 | return true; | 351 | return true; |
285 | } | 352 | } |
286 | 353 | ||
354 | static int hp_wmi_rfkill2_set_block(void *data, bool blocked) | ||
355 | { | ||
356 | int rfkill_id = (int)(long)data; | ||
357 | char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; | ||
358 | |||
359 | if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1, | ||
360 | buffer, sizeof(buffer), 0)) | ||
361 | return -EINVAL; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static const struct rfkill_ops hp_wmi_rfkill2_ops = { | ||
366 | .set_block = hp_wmi_rfkill2_set_block, | ||
367 | }; | ||
368 | |||
369 | static int hp_wmi_rfkill2_refresh(void) | ||
370 | { | ||
371 | int err, i; | ||
372 | struct bios_rfkill2_state state; | ||
373 | |||
374 | err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state, | ||
375 | 0, sizeof(state)); | ||
376 | if (err) | ||
377 | return err; | ||
378 | |||
379 | for (i = 0; i < rfkill2_count; i++) { | ||
380 | int num = rfkill2[i].num; | ||
381 | struct bios_rfkill2_device_state *devstate; | ||
382 | devstate = &state.device[num]; | ||
383 | |||
384 | if (num >= state.count || | ||
385 | devstate->rfkill_id != rfkill2[i].id) { | ||
386 | pr_warn("power configuration of the wireless devices unexpectedly changed\n"); | ||
387 | continue; | ||
388 | } | ||
389 | |||
390 | rfkill_set_states(rfkill2[i].rfkill, | ||
391 | IS_SWBLOCKED(devstate->power), | ||
392 | IS_HWBLOCKED(devstate->power)); | ||
393 | } | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
287 | static ssize_t show_display(struct device *dev, struct device_attribute *attr, | 398 | static ssize_t show_display(struct device *dev, struct device_attribute *attr, |
288 | char *buf) | 399 | char *buf) |
289 | { | 400 | { |
@@ -334,7 +445,7 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr, | |||
334 | { | 445 | { |
335 | u32 tmp = simple_strtoul(buf, NULL, 10); | 446 | u32 tmp = simple_strtoul(buf, NULL, 10); |
336 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp, | 447 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp, |
337 | sizeof(tmp)); | 448 | sizeof(tmp), sizeof(tmp)); |
338 | if (ret) | 449 | if (ret) |
339 | return -EINVAL; | 450 | return -EINVAL; |
340 | 451 | ||
@@ -347,64 +458,9 @@ static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); | |||
347 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); | 458 | static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); |
348 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); | 459 | static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); |
349 | 460 | ||
350 | static struct key_entry *hp_wmi_get_entry_by_scancode(unsigned int code) | ||
351 | { | ||
352 | struct key_entry *key; | ||
353 | |||
354 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
355 | if (code == key->code) | ||
356 | return key; | ||
357 | |||
358 | return NULL; | ||
359 | } | ||
360 | |||
361 | static struct key_entry *hp_wmi_get_entry_by_keycode(unsigned int keycode) | ||
362 | { | ||
363 | struct key_entry *key; | ||
364 | |||
365 | for (key = hp_wmi_keymap; key->type != KE_END; key++) | ||
366 | if (key->type == KE_KEY && keycode == key->keycode) | ||
367 | return key; | ||
368 | |||
369 | return NULL; | ||
370 | } | ||
371 | |||
372 | static int hp_wmi_getkeycode(struct input_dev *dev, | ||
373 | unsigned int scancode, unsigned int *keycode) | ||
374 | { | ||
375 | struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode); | ||
376 | |||
377 | if (key && key->type == KE_KEY) { | ||
378 | *keycode = key->keycode; | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | return -EINVAL; | ||
383 | } | ||
384 | |||
385 | static int hp_wmi_setkeycode(struct input_dev *dev, | ||
386 | unsigned int scancode, unsigned int keycode) | ||
387 | { | ||
388 | struct key_entry *key; | ||
389 | unsigned int old_keycode; | ||
390 | |||
391 | key = hp_wmi_get_entry_by_scancode(scancode); | ||
392 | if (key && key->type == KE_KEY) { | ||
393 | old_keycode = key->keycode; | ||
394 | key->keycode = keycode; | ||
395 | set_bit(keycode, dev->keybit); | ||
396 | if (!hp_wmi_get_entry_by_keycode(old_keycode)) | ||
397 | clear_bit(old_keycode, dev->keybit); | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | return -EINVAL; | ||
402 | } | ||
403 | |||
404 | static void hp_wmi_notify(u32 value, void *context) | 461 | static void hp_wmi_notify(u32 value, void *context) |
405 | { | 462 | { |
406 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 463 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
407 | static struct key_entry *key; | ||
408 | union acpi_object *obj; | 464 | union acpi_object *obj; |
409 | u32 event_id, event_data; | 465 | u32 event_id, event_data; |
410 | int key_code = 0, ret; | 466 | int key_code = 0, ret; |
@@ -413,7 +469,7 @@ static void hp_wmi_notify(u32 value, void *context) | |||
413 | 469 | ||
414 | status = wmi_get_event_data(value, &response); | 470 | status = wmi_get_event_data(value, &response); |
415 | if (status != AE_OK) { | 471 | if (status != AE_OK) { |
416 | printk(KERN_INFO PREFIX "bad event status 0x%x\n", status); | 472 | pr_info("bad event status 0x%x\n", status); |
417 | return; | 473 | return; |
418 | } | 474 | } |
419 | 475 | ||
@@ -422,8 +478,7 @@ static void hp_wmi_notify(u32 value, void *context) | |||
422 | if (!obj) | 478 | if (!obj) |
423 | return; | 479 | return; |
424 | if (obj->type != ACPI_TYPE_BUFFER) { | 480 | if (obj->type != ACPI_TYPE_BUFFER) { |
425 | printk(KERN_INFO "hp-wmi: Unknown response received %d\n", | 481 | pr_info("Unknown response received %d\n", obj->type); |
426 | obj->type); | ||
427 | kfree(obj); | 482 | kfree(obj); |
428 | return; | 483 | return; |
429 | } | 484 | } |
@@ -440,8 +495,7 @@ static void hp_wmi_notify(u32 value, void *context) | |||
440 | event_id = *location; | 495 | event_id = *location; |
441 | event_data = *(location + 2); | 496 | event_data = *(location + 2); |
442 | } else { | 497 | } else { |
443 | printk(KERN_INFO "hp-wmi: Unknown buffer length %d\n", | 498 | pr_info("Unknown buffer length %d\n", obj->buffer.length); |
444 | obj->buffer.length); | ||
445 | kfree(obj); | 499 | kfree(obj); |
446 | return; | 500 | return; |
447 | } | 501 | } |
@@ -462,26 +516,21 @@ static void hp_wmi_notify(u32 value, void *context) | |||
462 | case HPWMI_BEZEL_BUTTON: | 516 | case HPWMI_BEZEL_BUTTON: |
463 | ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, | 517 | ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, |
464 | &key_code, | 518 | &key_code, |
519 | sizeof(key_code), | ||
465 | sizeof(key_code)); | 520 | sizeof(key_code)); |
466 | if (ret) | 521 | if (ret) |
467 | break; | 522 | break; |
468 | key = hp_wmi_get_entry_by_scancode(key_code); | 523 | |
469 | if (key) { | 524 | if (!sparse_keymap_report_event(hp_wmi_input_dev, |
470 | switch (key->type) { | 525 | key_code, 1, true)) |
471 | case KE_KEY: | 526 | pr_info("Unknown key code - 0x%x\n", key_code); |
472 | input_report_key(hp_wmi_input_dev, | ||
473 | key->keycode, 1); | ||
474 | input_sync(hp_wmi_input_dev); | ||
475 | input_report_key(hp_wmi_input_dev, | ||
476 | key->keycode, 0); | ||
477 | input_sync(hp_wmi_input_dev); | ||
478 | break; | ||
479 | } | ||
480 | } else | ||
481 | printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", | ||
482 | key_code); | ||
483 | break; | 527 | break; |
484 | case HPWMI_WIRELESS: | 528 | case HPWMI_WIRELESS: |
529 | if (rfkill2_count) { | ||
530 | hp_wmi_rfkill2_refresh(); | ||
531 | break; | ||
532 | } | ||
533 | |||
485 | if (wifi_rfkill) | 534 | if (wifi_rfkill) |
486 | rfkill_set_states(wifi_rfkill, | 535 | rfkill_set_states(wifi_rfkill, |
487 | hp_wmi_get_sw_state(HPWMI_WIFI), | 536 | hp_wmi_get_sw_state(HPWMI_WIFI), |
@@ -496,21 +545,19 @@ static void hp_wmi_notify(u32 value, void *context) | |||
496 | hp_wmi_get_hw_state(HPWMI_WWAN)); | 545 | hp_wmi_get_hw_state(HPWMI_WWAN)); |
497 | break; | 546 | break; |
498 | case HPWMI_CPU_BATTERY_THROTTLE: | 547 | case HPWMI_CPU_BATTERY_THROTTLE: |
499 | printk(KERN_INFO PREFIX UNIMP "CPU throttle because of 3 Cell" | 548 | pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); |
500 | " battery event detected\n"); | ||
501 | break; | 549 | break; |
502 | case HPWMI_LOCK_SWITCH: | 550 | case HPWMI_LOCK_SWITCH: |
503 | break; | 551 | break; |
504 | default: | 552 | default: |
505 | printk(KERN_INFO PREFIX "Unknown event_id - %d - 0x%x\n", | 553 | pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data); |
506 | event_id, event_data); | ||
507 | break; | 554 | break; |
508 | } | 555 | } |
509 | } | 556 | } |
510 | 557 | ||
511 | static int __init hp_wmi_input_setup(void) | 558 | static int __init hp_wmi_input_setup(void) |
512 | { | 559 | { |
513 | struct key_entry *key; | 560 | acpi_status status; |
514 | int err; | 561 | int err; |
515 | 562 | ||
516 | hp_wmi_input_dev = input_allocate_device(); | 563 | hp_wmi_input_dev = input_allocate_device(); |
@@ -520,21 +567,14 @@ static int __init hp_wmi_input_setup(void) | |||
520 | hp_wmi_input_dev->name = "HP WMI hotkeys"; | 567 | hp_wmi_input_dev->name = "HP WMI hotkeys"; |
521 | hp_wmi_input_dev->phys = "wmi/input0"; | 568 | hp_wmi_input_dev->phys = "wmi/input0"; |
522 | hp_wmi_input_dev->id.bustype = BUS_HOST; | 569 | hp_wmi_input_dev->id.bustype = BUS_HOST; |
523 | hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode; | ||
524 | hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode; | ||
525 | |||
526 | for (key = hp_wmi_keymap; key->type != KE_END; key++) { | ||
527 | switch (key->type) { | ||
528 | case KE_KEY: | ||
529 | set_bit(EV_KEY, hp_wmi_input_dev->evbit); | ||
530 | set_bit(key->keycode, hp_wmi_input_dev->keybit); | ||
531 | break; | ||
532 | } | ||
533 | } | ||
534 | 570 | ||
535 | set_bit(EV_SW, hp_wmi_input_dev->evbit); | 571 | __set_bit(EV_SW, hp_wmi_input_dev->evbit); |
536 | set_bit(SW_DOCK, hp_wmi_input_dev->swbit); | 572 | __set_bit(SW_DOCK, hp_wmi_input_dev->swbit); |
537 | set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); | 573 | __set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); |
574 | |||
575 | err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL); | ||
576 | if (err) | ||
577 | goto err_free_dev; | ||
538 | 578 | ||
539 | /* Set initial hardware state */ | 579 | /* Set initial hardware state */ |
540 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); | 580 | input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); |
@@ -542,14 +582,32 @@ static int __init hp_wmi_input_setup(void) | |||
542 | hp_wmi_tablet_state()); | 582 | hp_wmi_tablet_state()); |
543 | input_sync(hp_wmi_input_dev); | 583 | input_sync(hp_wmi_input_dev); |
544 | 584 | ||
545 | err = input_register_device(hp_wmi_input_dev); | 585 | status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); |
546 | 586 | if (ACPI_FAILURE(status)) { | |
547 | if (err) { | 587 | err = -EIO; |
548 | input_free_device(hp_wmi_input_dev); | 588 | goto err_free_keymap; |
549 | return err; | ||
550 | } | 589 | } |
551 | 590 | ||
591 | err = input_register_device(hp_wmi_input_dev); | ||
592 | if (err) | ||
593 | goto err_uninstall_notifier; | ||
594 | |||
552 | return 0; | 595 | return 0; |
596 | |||
597 | err_uninstall_notifier: | ||
598 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
599 | err_free_keymap: | ||
600 | sparse_keymap_free(hp_wmi_input_dev); | ||
601 | err_free_dev: | ||
602 | input_free_device(hp_wmi_input_dev); | ||
603 | return err; | ||
604 | } | ||
605 | |||
606 | static void hp_wmi_input_destroy(void) | ||
607 | { | ||
608 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
609 | sparse_keymap_free(hp_wmi_input_dev); | ||
610 | input_unregister_device(hp_wmi_input_dev); | ||
553 | } | 611 | } |
554 | 612 | ||
555 | static void cleanup_sysfs(struct platform_device *device) | 613 | static void cleanup_sysfs(struct platform_device *device) |
@@ -561,32 +619,16 @@ static void cleanup_sysfs(struct platform_device *device) | |||
561 | device_remove_file(&device->dev, &dev_attr_tablet); | 619 | device_remove_file(&device->dev, &dev_attr_tablet); |
562 | } | 620 | } |
563 | 621 | ||
564 | static int __devinit hp_wmi_bios_setup(struct platform_device *device) | 622 | static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) |
565 | { | 623 | { |
566 | int err; | 624 | int err; |
567 | int wireless = 0; | 625 | int wireless = 0; |
568 | 626 | ||
569 | err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless, | 627 | err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless, |
570 | sizeof(wireless)); | 628 | sizeof(wireless), sizeof(wireless)); |
571 | if (err) | 629 | if (err) |
572 | return err; | 630 | return err; |
573 | 631 | ||
574 | err = device_create_file(&device->dev, &dev_attr_display); | ||
575 | if (err) | ||
576 | goto add_sysfs_error; | ||
577 | err = device_create_file(&device->dev, &dev_attr_hddtemp); | ||
578 | if (err) | ||
579 | goto add_sysfs_error; | ||
580 | err = device_create_file(&device->dev, &dev_attr_als); | ||
581 | if (err) | ||
582 | goto add_sysfs_error; | ||
583 | err = device_create_file(&device->dev, &dev_attr_dock); | ||
584 | if (err) | ||
585 | goto add_sysfs_error; | ||
586 | err = device_create_file(&device->dev, &dev_attr_tablet); | ||
587 | if (err) | ||
588 | goto add_sysfs_error; | ||
589 | |||
590 | if (wireless & 0x1) { | 632 | if (wireless & 0x1) { |
591 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, | 633 | wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, |
592 | RFKILL_TYPE_WLAN, | 634 | RFKILL_TYPE_WLAN, |
@@ -632,14 +674,130 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) | |||
632 | return 0; | 674 | return 0; |
633 | register_wwan_err: | 675 | register_wwan_err: |
634 | rfkill_destroy(wwan_rfkill); | 676 | rfkill_destroy(wwan_rfkill); |
677 | wwan_rfkill = NULL; | ||
635 | if (bluetooth_rfkill) | 678 | if (bluetooth_rfkill) |
636 | rfkill_unregister(bluetooth_rfkill); | 679 | rfkill_unregister(bluetooth_rfkill); |
637 | register_bluetooth_error: | 680 | register_bluetooth_error: |
638 | rfkill_destroy(bluetooth_rfkill); | 681 | rfkill_destroy(bluetooth_rfkill); |
682 | bluetooth_rfkill = NULL; | ||
639 | if (wifi_rfkill) | 683 | if (wifi_rfkill) |
640 | rfkill_unregister(wifi_rfkill); | 684 | rfkill_unregister(wifi_rfkill); |
641 | register_wifi_error: | 685 | register_wifi_error: |
642 | rfkill_destroy(wifi_rfkill); | 686 | rfkill_destroy(wifi_rfkill); |
687 | wifi_rfkill = NULL; | ||
688 | return err; | ||
689 | } | ||
690 | |||
691 | static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device) | ||
692 | { | ||
693 | int err, i; | ||
694 | struct bios_rfkill2_state state; | ||
695 | err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state, | ||
696 | 0, sizeof(state)); | ||
697 | if (err) | ||
698 | return err; | ||
699 | |||
700 | if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { | ||
701 | pr_warn("unable to parse 0x1b query output\n"); | ||
702 | return -EINVAL; | ||
703 | } | ||
704 | |||
705 | for (i = 0; i < state.count; i++) { | ||
706 | struct rfkill *rfkill; | ||
707 | enum rfkill_type type; | ||
708 | char *name; | ||
709 | switch (state.device[i].radio_type) { | ||
710 | case HPWMI_WIFI: | ||
711 | type = RFKILL_TYPE_WLAN; | ||
712 | name = "hp-wifi"; | ||
713 | break; | ||
714 | case HPWMI_BLUETOOTH: | ||
715 | type = RFKILL_TYPE_BLUETOOTH; | ||
716 | name = "hp-bluetooth"; | ||
717 | break; | ||
718 | case HPWMI_WWAN: | ||
719 | type = RFKILL_TYPE_WWAN; | ||
720 | name = "hp-wwan"; | ||
721 | break; | ||
722 | default: | ||
723 | pr_warn("unknown device type 0x%x\n", | ||
724 | state.device[i].radio_type); | ||
725 | continue; | ||
726 | } | ||
727 | |||
728 | if (!state.device[i].vendor_id) { | ||
729 | pr_warn("zero device %d while %d reported\n", | ||
730 | i, state.count); | ||
731 | continue; | ||
732 | } | ||
733 | |||
734 | rfkill = rfkill_alloc(name, &device->dev, type, | ||
735 | &hp_wmi_rfkill2_ops, (void *)(long)i); | ||
736 | if (!rfkill) { | ||
737 | err = -ENOMEM; | ||
738 | goto fail; | ||
739 | } | ||
740 | |||
741 | rfkill2[rfkill2_count].id = state.device[i].rfkill_id; | ||
742 | rfkill2[rfkill2_count].num = i; | ||
743 | rfkill2[rfkill2_count].rfkill = rfkill; | ||
744 | |||
745 | rfkill_init_sw_state(rfkill, | ||
746 | IS_SWBLOCKED(state.device[i].power)); | ||
747 | rfkill_set_hw_state(rfkill, | ||
748 | IS_HWBLOCKED(state.device[i].power)); | ||
749 | |||
750 | if (!(state.device[i].power & HPWMI_POWER_BIOS)) | ||
751 | pr_info("device %s blocked by BIOS\n", name); | ||
752 | |||
753 | err = rfkill_register(rfkill); | ||
754 | if (err) { | ||
755 | rfkill_destroy(rfkill); | ||
756 | goto fail; | ||
757 | } | ||
758 | |||
759 | rfkill2_count++; | ||
760 | } | ||
761 | |||
762 | return 0; | ||
763 | fail: | ||
764 | for (; rfkill2_count > 0; rfkill2_count--) { | ||
765 | rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); | ||
766 | rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); | ||
767 | } | ||
768 | return err; | ||
769 | } | ||
770 | |||
771 | static int __devinit hp_wmi_bios_setup(struct platform_device *device) | ||
772 | { | ||
773 | int err; | ||
774 | |||
775 | /* clear detected rfkill devices */ | ||
776 | wifi_rfkill = NULL; | ||
777 | bluetooth_rfkill = NULL; | ||
778 | wwan_rfkill = NULL; | ||
779 | rfkill2_count = 0; | ||
780 | |||
781 | if (hp_wmi_rfkill_setup(device)) | ||
782 | hp_wmi_rfkill2_setup(device); | ||
783 | |||
784 | err = device_create_file(&device->dev, &dev_attr_display); | ||
785 | if (err) | ||
786 | goto add_sysfs_error; | ||
787 | err = device_create_file(&device->dev, &dev_attr_hddtemp); | ||
788 | if (err) | ||
789 | goto add_sysfs_error; | ||
790 | err = device_create_file(&device->dev, &dev_attr_als); | ||
791 | if (err) | ||
792 | goto add_sysfs_error; | ||
793 | err = device_create_file(&device->dev, &dev_attr_dock); | ||
794 | if (err) | ||
795 | goto add_sysfs_error; | ||
796 | err = device_create_file(&device->dev, &dev_attr_tablet); | ||
797 | if (err) | ||
798 | goto add_sysfs_error; | ||
799 | return 0; | ||
800 | |||
643 | add_sysfs_error: | 801 | add_sysfs_error: |
644 | cleanup_sysfs(device); | 802 | cleanup_sysfs(device); |
645 | return err; | 803 | return err; |
@@ -647,8 +805,14 @@ add_sysfs_error: | |||
647 | 805 | ||
648 | static int __exit hp_wmi_bios_remove(struct platform_device *device) | 806 | static int __exit hp_wmi_bios_remove(struct platform_device *device) |
649 | { | 807 | { |
808 | int i; | ||
650 | cleanup_sysfs(device); | 809 | cleanup_sysfs(device); |
651 | 810 | ||
811 | for (i = 0; i < rfkill2_count; i++) { | ||
812 | rfkill_unregister(rfkill2[i].rfkill); | ||
813 | rfkill_destroy(rfkill2[i].rfkill); | ||
814 | } | ||
815 | |||
652 | if (wifi_rfkill) { | 816 | if (wifi_rfkill) { |
653 | rfkill_unregister(wifi_rfkill); | 817 | rfkill_unregister(wifi_rfkill); |
654 | rfkill_destroy(wifi_rfkill); | 818 | rfkill_destroy(wifi_rfkill); |
@@ -681,6 +845,9 @@ static int hp_wmi_resume_handler(struct device *device) | |||
681 | input_sync(hp_wmi_input_dev); | 845 | input_sync(hp_wmi_input_dev); |
682 | } | 846 | } |
683 | 847 | ||
848 | if (rfkill2_count) | ||
849 | hp_wmi_rfkill2_refresh(); | ||
850 | |||
684 | if (wifi_rfkill) | 851 | if (wifi_rfkill) |
685 | rfkill_set_states(wifi_rfkill, | 852 | rfkill_set_states(wifi_rfkill, |
686 | hp_wmi_get_sw_state(HPWMI_WIFI), | 853 | hp_wmi_get_sw_state(HPWMI_WIFI), |
@@ -704,15 +871,9 @@ static int __init hp_wmi_init(void) | |||
704 | int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); | 871 | int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); |
705 | 872 | ||
706 | if (event_capable) { | 873 | if (event_capable) { |
707 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, | ||
708 | hp_wmi_notify, NULL); | ||
709 | if (ACPI_FAILURE(err)) | ||
710 | return -EINVAL; | ||
711 | err = hp_wmi_input_setup(); | 874 | err = hp_wmi_input_setup(); |
712 | if (err) { | 875 | if (err) |
713 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
714 | return err; | 876 | return err; |
715 | } | ||
716 | } | 877 | } |
717 | 878 | ||
718 | if (bios_capable) { | 879 | if (bios_capable) { |
@@ -739,20 +900,17 @@ err_device_add: | |||
739 | err_device_alloc: | 900 | err_device_alloc: |
740 | platform_driver_unregister(&hp_wmi_driver); | 901 | platform_driver_unregister(&hp_wmi_driver); |
741 | err_driver_reg: | 902 | err_driver_reg: |
742 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 903 | if (event_capable) |
743 | input_unregister_device(hp_wmi_input_dev); | 904 | hp_wmi_input_destroy(); |
744 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
745 | } | ||
746 | 905 | ||
747 | return err; | 906 | return err; |
748 | } | 907 | } |
749 | 908 | ||
750 | static void __exit hp_wmi_exit(void) | 909 | static void __exit hp_wmi_exit(void) |
751 | { | 910 | { |
752 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 911 | if (wmi_has_guid(HPWMI_EVENT_GUID)) |
753 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | 912 | hp_wmi_input_destroy(); |
754 | input_unregister_device(hp_wmi_input_dev); | 913 | |
755 | } | ||
756 | if (hp_wmi_platform_dev) { | 914 | if (hp_wmi_platform_dev) { |
757 | platform_device_unregister(hp_wmi_platform_dev); | 915 | platform_device_unregister(hp_wmi_platform_dev); |
758 | platform_driver_unregister(&hp_wmi_driver); | 916 | platform_driver_unregister(&hp_wmi_driver); |
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c new file mode 100644 index 000000000000..1b52d00e2f90 --- /dev/null +++ b/drivers/platform/x86/hp_accel.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS | ||
3 | * | ||
4 | * Copyright (C) 2007-2008 Yan Burman | ||
5 | * Copyright (C) 2008 Eric Piel | ||
6 | * Copyright (C) 2008-2009 Pavel Machek | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
24 | |||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/dmi.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/wait.h> | ||
34 | #include <linux/poll.h> | ||
35 | #include <linux/freezer.h> | ||
36 | #include <linux/uaccess.h> | ||
37 | #include <linux/leds.h> | ||
38 | #include <linux/atomic.h> | ||
39 | #include <acpi/acpi_drivers.h> | ||
40 | #include "../../misc/lis3lv02d/lis3lv02d.h" | ||
41 | |||
42 | #define DRIVER_NAME "hp_accel" | ||
43 | #define ACPI_MDPS_CLASS "accelerometer" | ||
44 | |||
45 | /* Delayed LEDs infrastructure ------------------------------------ */ | ||
46 | |||
47 | /* Special LED class that can defer work */ | ||
48 | struct delayed_led_classdev { | ||
49 | struct led_classdev led_classdev; | ||
50 | struct work_struct work; | ||
51 | enum led_brightness new_brightness; | ||
52 | |||
53 | unsigned int led; /* For driver */ | ||
54 | void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); | ||
55 | }; | ||
56 | |||
57 | static inline void delayed_set_status_worker(struct work_struct *work) | ||
58 | { | ||
59 | struct delayed_led_classdev *data = | ||
60 | container_of(work, struct delayed_led_classdev, work); | ||
61 | |||
62 | data->set_brightness(data, data->new_brightness); | ||
63 | } | ||
64 | |||
65 | static inline void delayed_sysfs_set(struct led_classdev *led_cdev, | ||
66 | enum led_brightness brightness) | ||
67 | { | ||
68 | struct delayed_led_classdev *data = container_of(led_cdev, | ||
69 | struct delayed_led_classdev, led_classdev); | ||
70 | data->new_brightness = brightness; | ||
71 | schedule_work(&data->work); | ||
72 | } | ||
73 | |||
74 | /* HP-specific accelerometer driver ------------------------------------ */ | ||
75 | |||
76 | /* For automatic insertion of the module */ | ||
77 | static struct acpi_device_id lis3lv02d_device_ids[] = { | ||
78 | {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ | ||
79 | {"", 0}, | ||
80 | }; | ||
81 | MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); | ||
82 | |||
83 | |||
84 | /** | ||
85 | * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. | ||
86 | * @lis3: pointer to the device struct | ||
87 | * | ||
88 | * Returns 0 on success. | ||
89 | */ | ||
90 | int lis3lv02d_acpi_init(struct lis3lv02d *lis3) | ||
91 | { | ||
92 | struct acpi_device *dev = lis3->bus_priv; | ||
93 | if (acpi_evaluate_object(dev->handle, METHOD_NAME__INI, | ||
94 | NULL, NULL) != AE_OK) | ||
95 | return -EINVAL; | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * lis3lv02d_acpi_read - ACPI ALRD method: read a register | ||
102 | * @lis3: pointer to the device struct | ||
103 | * @reg: the register to read | ||
104 | * @ret: result of the operation | ||
105 | * | ||
106 | * Returns 0 on success. | ||
107 | */ | ||
108 | int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) | ||
109 | { | ||
110 | struct acpi_device *dev = lis3->bus_priv; | ||
111 | union acpi_object arg0 = { ACPI_TYPE_INTEGER }; | ||
112 | struct acpi_object_list args = { 1, &arg0 }; | ||
113 | unsigned long long lret; | ||
114 | acpi_status status; | ||
115 | |||
116 | arg0.integer.value = reg; | ||
117 | |||
118 | status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); | ||
119 | *ret = lret; | ||
120 | return (status != AE_OK) ? -EINVAL : 0; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * lis3lv02d_acpi_write - ACPI ALWR method: write to a register | ||
125 | * @lis3: pointer to the device struct | ||
126 | * @reg: the register to write to | ||
127 | * @val: the value to write | ||
128 | * | ||
129 | * Returns 0 on success. | ||
130 | */ | ||
131 | int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) | ||
132 | { | ||
133 | struct acpi_device *dev = lis3->bus_priv; | ||
134 | unsigned long long ret; /* Not used when writting */ | ||
135 | union acpi_object in_obj[2]; | ||
136 | struct acpi_object_list args = { 2, in_obj }; | ||
137 | |||
138 | in_obj[0].type = ACPI_TYPE_INTEGER; | ||
139 | in_obj[0].integer.value = reg; | ||
140 | in_obj[1].type = ACPI_TYPE_INTEGER; | ||
141 | in_obj[1].integer.value = val; | ||
142 | |||
143 | if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) | ||
144 | return -EINVAL; | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) | ||
150 | { | ||
151 | lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); | ||
152 | pr_info("hardware type %s found\n", dmi->ident); | ||
153 | |||
154 | return 1; | ||
155 | } | ||
156 | |||
157 | /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). | ||
158 | * If the value is negative, the opposite of the hw value is used. */ | ||
159 | #define DEFINE_CONV(name, x, y, z) \ | ||
160 | static union axis_conversion lis3lv02d_axis_##name = \ | ||
161 | { .as_array = { x, y, z } } | ||
162 | DEFINE_CONV(normal, 1, 2, 3); | ||
163 | DEFINE_CONV(y_inverted, 1, -2, 3); | ||
164 | DEFINE_CONV(x_inverted, -1, 2, 3); | ||
165 | DEFINE_CONV(z_inverted, 1, 2, -3); | ||
166 | DEFINE_CONV(xy_swap, 2, 1, 3); | ||
167 | DEFINE_CONV(xy_rotated_left, -2, 1, 3); | ||
168 | DEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); | ||
169 | DEFINE_CONV(xy_swap_inverted, -2, -1, 3); | ||
170 | DEFINE_CONV(xy_rotated_right, 2, -1, 3); | ||
171 | DEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); | ||
172 | |||
173 | #define AXIS_DMI_MATCH(_ident, _name, _axis) { \ | ||
174 | .ident = _ident, \ | ||
175 | .callback = lis3lv02d_dmi_matched, \ | ||
176 | .matches = { \ | ||
177 | DMI_MATCH(DMI_PRODUCT_NAME, _name) \ | ||
178 | }, \ | ||
179 | .driver_data = &lis3lv02d_axis_##_axis \ | ||
180 | } | ||
181 | |||
182 | #define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ | ||
183 | _class2, _name2, \ | ||
184 | _axis) { \ | ||
185 | .ident = _ident, \ | ||
186 | .callback = lis3lv02d_dmi_matched, \ | ||
187 | .matches = { \ | ||
188 | DMI_MATCH(DMI_##_class1, _name1), \ | ||
189 | DMI_MATCH(DMI_##_class2, _name2), \ | ||
190 | }, \ | ||
191 | .driver_data = &lis3lv02d_axis_##_axis \ | ||
192 | } | ||
193 | static struct dmi_system_id lis3lv02d_dmi_ids[] = { | ||
194 | /* product names are truncated to match all kinds of a same model */ | ||
195 | AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), | ||
196 | AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), | ||
197 | AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), | ||
198 | AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), | ||
199 | AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), | ||
200 | AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), | ||
201 | AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), | ||
202 | AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), | ||
203 | AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), | ||
204 | AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), | ||
205 | AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), | ||
206 | AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), | ||
207 | AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), | ||
208 | AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), | ||
209 | AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), | ||
210 | AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), | ||
211 | AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), | ||
212 | /* Intel-based HP Pavilion dv5 */ | ||
213 | AXIS_DMI_MATCH2("HPDV5_I", | ||
214 | PRODUCT_NAME, "HP Pavilion dv5", | ||
215 | BOARD_NAME, "3603", | ||
216 | x_inverted), | ||
217 | /* AMD-based HP Pavilion dv5 */ | ||
218 | AXIS_DMI_MATCH2("HPDV5_A", | ||
219 | PRODUCT_NAME, "HP Pavilion dv5", | ||
220 | BOARD_NAME, "3600", | ||
221 | y_inverted), | ||
222 | AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), | ||
223 | AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), | ||
224 | AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), | ||
225 | AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), | ||
226 | AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), | ||
227 | AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), | ||
228 | AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), | ||
229 | AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), | ||
230 | AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), | ||
231 | { NULL, } | ||
232 | /* Laptop models without axis info (yet): | ||
233 | * "NC6910" "HP Compaq 6910" | ||
234 | * "NC2400" "HP Compaq nc2400" | ||
235 | * "NX74x0" "HP Compaq nx74" | ||
236 | * "NX6325" "HP Compaq nx6325" | ||
237 | * "NC4400" "HP Compaq nc4400" | ||
238 | */ | ||
239 | }; | ||
240 | |||
241 | static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) | ||
242 | { | ||
243 | struct acpi_device *dev = lis3_dev.bus_priv; | ||
244 | unsigned long long ret; /* Not used when writing */ | ||
245 | union acpi_object in_obj[1]; | ||
246 | struct acpi_object_list args = { 1, in_obj }; | ||
247 | |||
248 | in_obj[0].type = ACPI_TYPE_INTEGER; | ||
249 | in_obj[0].integer.value = !!value; | ||
250 | |||
251 | acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); | ||
252 | } | ||
253 | |||
254 | static struct delayed_led_classdev hpled_led = { | ||
255 | .led_classdev = { | ||
256 | .name = "hp::hddprotect", | ||
257 | .default_trigger = "none", | ||
258 | .brightness_set = delayed_sysfs_set, | ||
259 | .flags = LED_CORE_SUSPENDRESUME, | ||
260 | }, | ||
261 | .set_brightness = hpled_set, | ||
262 | }; | ||
263 | |||
264 | static acpi_status | ||
265 | lis3lv02d_get_resource(struct acpi_resource *resource, void *context) | ||
266 | { | ||
267 | if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { | ||
268 | struct acpi_resource_extended_irq *irq; | ||
269 | u32 *device_irq = context; | ||
270 | |||
271 | irq = &resource->data.extended_irq; | ||
272 | *device_irq = irq->interrupts[0]; | ||
273 | } | ||
274 | |||
275 | return AE_OK; | ||
276 | } | ||
277 | |||
278 | static void lis3lv02d_enum_resources(struct acpi_device *device) | ||
279 | { | ||
280 | acpi_status status; | ||
281 | |||
282 | status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, | ||
283 | lis3lv02d_get_resource, &lis3_dev.irq); | ||
284 | if (ACPI_FAILURE(status)) | ||
285 | printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n"); | ||
286 | } | ||
287 | |||
288 | static int lis3lv02d_add(struct acpi_device *device) | ||
289 | { | ||
290 | int ret; | ||
291 | |||
292 | if (!device) | ||
293 | return -EINVAL; | ||
294 | |||
295 | lis3_dev.bus_priv = device; | ||
296 | lis3_dev.init = lis3lv02d_acpi_init; | ||
297 | lis3_dev.read = lis3lv02d_acpi_read; | ||
298 | lis3_dev.write = lis3lv02d_acpi_write; | ||
299 | strcpy(acpi_device_name(device), DRIVER_NAME); | ||
300 | strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); | ||
301 | device->driver_data = &lis3_dev; | ||
302 | |||
303 | /* obtain IRQ number of our device from ACPI */ | ||
304 | lis3lv02d_enum_resources(device); | ||
305 | |||
306 | /* If possible use a "standard" axes order */ | ||
307 | if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { | ||
308 | pr_info("Using custom axes %d,%d,%d\n", | ||
309 | lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); | ||
310 | } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { | ||
311 | pr_info("laptop model unknown, using default axes configuration\n"); | ||
312 | lis3_dev.ac = lis3lv02d_axis_normal; | ||
313 | } | ||
314 | |||
315 | /* call the core layer do its init */ | ||
316 | ret = lis3lv02d_init_device(&lis3_dev); | ||
317 | if (ret) | ||
318 | return ret; | ||
319 | |||
320 | INIT_WORK(&hpled_led.work, delayed_set_status_worker); | ||
321 | ret = led_classdev_register(NULL, &hpled_led.led_classdev); | ||
322 | if (ret) { | ||
323 | lis3lv02d_joystick_disable(); | ||
324 | lis3lv02d_poweroff(&lis3_dev); | ||
325 | flush_work(&hpled_led.work); | ||
326 | return ret; | ||
327 | } | ||
328 | |||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | static int lis3lv02d_remove(struct acpi_device *device, int type) | ||
333 | { | ||
334 | if (!device) | ||
335 | return -EINVAL; | ||
336 | |||
337 | lis3lv02d_joystick_disable(); | ||
338 | lis3lv02d_poweroff(&lis3_dev); | ||
339 | |||
340 | led_classdev_unregister(&hpled_led.led_classdev); | ||
341 | flush_work(&hpled_led.work); | ||
342 | |||
343 | return lis3lv02d_remove_fs(&lis3_dev); | ||
344 | } | ||
345 | |||
346 | |||
347 | #ifdef CONFIG_PM | ||
348 | static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) | ||
349 | { | ||
350 | /* make sure the device is off when we suspend */ | ||
351 | lis3lv02d_poweroff(&lis3_dev); | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int lis3lv02d_resume(struct acpi_device *device) | ||
356 | { | ||
357 | lis3lv02d_poweron(&lis3_dev); | ||
358 | return 0; | ||
359 | } | ||
360 | #else | ||
361 | #define lis3lv02d_suspend NULL | ||
362 | #define lis3lv02d_resume NULL | ||
363 | #endif | ||
364 | |||
365 | /* For the HP MDPS aka 3D Driveguard */ | ||
366 | static struct acpi_driver lis3lv02d_driver = { | ||
367 | .name = DRIVER_NAME, | ||
368 | .class = ACPI_MDPS_CLASS, | ||
369 | .ids = lis3lv02d_device_ids, | ||
370 | .ops = { | ||
371 | .add = lis3lv02d_add, | ||
372 | .remove = lis3lv02d_remove, | ||
373 | .suspend = lis3lv02d_suspend, | ||
374 | .resume = lis3lv02d_resume, | ||
375 | } | ||
376 | }; | ||
377 | |||
378 | static int __init lis3lv02d_init_module(void) | ||
379 | { | ||
380 | int ret; | ||
381 | |||
382 | if (acpi_disabled) | ||
383 | return -ENODEV; | ||
384 | |||
385 | ret = acpi_bus_register_driver(&lis3lv02d_driver); | ||
386 | if (ret < 0) | ||
387 | return ret; | ||
388 | |||
389 | pr_info("driver loaded\n"); | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static void __exit lis3lv02d_exit_module(void) | ||
395 | { | ||
396 | acpi_bus_unregister_driver(&lis3lv02d_driver); | ||
397 | } | ||
398 | |||
399 | MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); | ||
400 | MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); | ||
401 | MODULE_LICENSE("GPL"); | ||
402 | |||
403 | module_init(lis3lv02d_init_module); | ||
404 | module_exit(lis3lv02d_exit_module); | ||
diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c new file mode 100644 index 000000000000..811d436cd677 --- /dev/null +++ b/drivers/platform/x86/ibm_rtl.c | |||
@@ -0,0 +1,339 @@ | |||
1 | /* | ||
2 | * IBM Real-Time Linux driver | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * Copyright (C) IBM Corporation, 2010 | ||
19 | * | ||
20 | * Author: Keith Mannthey <kmannth@us.ibm.com> | ||
21 | * Vernon Mauery <vernux@us.ibm.com> | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
26 | |||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <linux/sysdev.h> | ||
32 | #include <linux/dmi.h> | ||
33 | #include <linux/efi.h> | ||
34 | #include <linux/mutex.h> | ||
35 | #include <asm/bios_ebda.h> | ||
36 | |||
37 | static bool force; | ||
38 | module_param(force, bool, 0); | ||
39 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | ||
40 | |||
41 | static bool debug; | ||
42 | module_param(debug, bool, 0644); | ||
43 | MODULE_PARM_DESC(debug, "Show debug output"); | ||
44 | |||
45 | MODULE_LICENSE("GPL"); | ||
46 | MODULE_AUTHOR("Keith Mannthey <kmmanth@us.ibm.com>"); | ||
47 | MODULE_AUTHOR("Vernon Mauery <vernux@us.ibm.com>"); | ||
48 | |||
49 | #define RTL_ADDR_TYPE_IO 1 | ||
50 | #define RTL_ADDR_TYPE_MMIO 2 | ||
51 | |||
52 | #define RTL_CMD_ENTER_PRTM 1 | ||
53 | #define RTL_CMD_EXIT_PRTM 2 | ||
54 | |||
55 | /* The RTL table as presented by the EBDA: */ | ||
56 | struct ibm_rtl_table { | ||
57 | char signature[5]; /* signature should be "_RTL_" */ | ||
58 | u8 version; | ||
59 | u8 rt_status; | ||
60 | u8 command; | ||
61 | u8 command_status; | ||
62 | u8 cmd_address_type; | ||
63 | u8 cmd_granularity; | ||
64 | u8 cmd_offset; | ||
65 | u16 reserve1; | ||
66 | u32 cmd_port_address; /* platform dependent address */ | ||
67 | u32 cmd_port_value; /* platform dependent value */ | ||
68 | } __attribute__((packed)); | ||
69 | |||
70 | /* to locate "_RTL_" signature do a masked 5-byte integer compare */ | ||
71 | #define RTL_SIGNATURE 0x0000005f4c54525fULL | ||
72 | #define RTL_MASK 0x000000ffffffffffULL | ||
73 | |||
74 | #define RTL_DEBUG(fmt, ...) \ | ||
75 | do { \ | ||
76 | if (debug) \ | ||
77 | pr_info(fmt, ##__VA_ARGS__); \ | ||
78 | } while (0) | ||
79 | |||
80 | static DEFINE_MUTEX(rtl_lock); | ||
81 | static struct ibm_rtl_table __iomem *rtl_table; | ||
82 | static void __iomem *ebda_map; | ||
83 | static void __iomem *rtl_cmd_addr; | ||
84 | static u8 rtl_cmd_type; | ||
85 | static u8 rtl_cmd_width; | ||
86 | |||
87 | #ifndef readq | ||
88 | static inline __u64 readq(const volatile void __iomem *addr) | ||
89 | { | ||
90 | const volatile u32 __iomem *p = addr; | ||
91 | u32 low, high; | ||
92 | |||
93 | low = readl(p); | ||
94 | high = readl(p + 1); | ||
95 | |||
96 | return low + ((u64)high << 32); | ||
97 | } | ||
98 | #endif | ||
99 | |||
100 | static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len) | ||
101 | { | ||
102 | if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO) | ||
103 | return ioremap(addr, len); | ||
104 | return ioport_map(addr, len); | ||
105 | } | ||
106 | |||
107 | static void rtl_port_unmap(void __iomem *addr) | ||
108 | { | ||
109 | if (addr && rtl_cmd_type == RTL_ADDR_TYPE_MMIO) | ||
110 | iounmap(addr); | ||
111 | else | ||
112 | ioport_unmap(addr); | ||
113 | } | ||
114 | |||
115 | static int ibm_rtl_write(u8 value) | ||
116 | { | ||
117 | int ret = 0, count = 0; | ||
118 | static u32 cmd_port_val; | ||
119 | |||
120 | RTL_DEBUG("%s(%d)\n", __func__, value); | ||
121 | |||
122 | value = value == 1 ? RTL_CMD_ENTER_PRTM : RTL_CMD_EXIT_PRTM; | ||
123 | |||
124 | mutex_lock(&rtl_lock); | ||
125 | |||
126 | if (ioread8(&rtl_table->rt_status) != value) { | ||
127 | iowrite8(value, &rtl_table->command); | ||
128 | |||
129 | switch (rtl_cmd_width) { | ||
130 | case 8: | ||
131 | cmd_port_val = ioread8(&rtl_table->cmd_port_value); | ||
132 | RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); | ||
133 | iowrite8((u8)cmd_port_val, rtl_cmd_addr); | ||
134 | break; | ||
135 | case 16: | ||
136 | cmd_port_val = ioread16(&rtl_table->cmd_port_value); | ||
137 | RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); | ||
138 | iowrite16((u16)cmd_port_val, rtl_cmd_addr); | ||
139 | break; | ||
140 | case 32: | ||
141 | cmd_port_val = ioread32(&rtl_table->cmd_port_value); | ||
142 | RTL_DEBUG("cmd_port_val = %u\n", cmd_port_val); | ||
143 | iowrite32(cmd_port_val, rtl_cmd_addr); | ||
144 | break; | ||
145 | } | ||
146 | |||
147 | while (ioread8(&rtl_table->command)) { | ||
148 | msleep(10); | ||
149 | if (count++ > 500) { | ||
150 | pr_err("Hardware not responding to " | ||
151 | "mode switch request\n"); | ||
152 | ret = -EIO; | ||
153 | break; | ||
154 | } | ||
155 | |||
156 | } | ||
157 | |||
158 | if (ioread8(&rtl_table->command_status)) { | ||
159 | RTL_DEBUG("command_status reports failed command\n"); | ||
160 | ret = -EIO; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | mutex_unlock(&rtl_lock); | ||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | static ssize_t rtl_show_version(struct sysdev_class * dev, | ||
169 | struct sysdev_class_attribute *attr, | ||
170 | char *buf) | ||
171 | { | ||
172 | return sprintf(buf, "%d\n", (int)ioread8(&rtl_table->version)); | ||
173 | } | ||
174 | |||
175 | static ssize_t rtl_show_state(struct sysdev_class *dev, | ||
176 | struct sysdev_class_attribute *attr, | ||
177 | char *buf) | ||
178 | { | ||
179 | return sprintf(buf, "%d\n", ioread8(&rtl_table->rt_status)); | ||
180 | } | ||
181 | |||
182 | static ssize_t rtl_set_state(struct sysdev_class *dev, | ||
183 | struct sysdev_class_attribute *attr, | ||
184 | const char *buf, | ||
185 | size_t count) | ||
186 | { | ||
187 | ssize_t ret; | ||
188 | |||
189 | if (count < 1 || count > 2) | ||
190 | return -EINVAL; | ||
191 | |||
192 | switch (buf[0]) { | ||
193 | case '0': | ||
194 | ret = ibm_rtl_write(0); | ||
195 | break; | ||
196 | case '1': | ||
197 | ret = ibm_rtl_write(1); | ||
198 | break; | ||
199 | default: | ||
200 | ret = -EINVAL; | ||
201 | } | ||
202 | if (ret >= 0) | ||
203 | ret = count; | ||
204 | |||
205 | return ret; | ||
206 | } | ||
207 | |||
208 | static struct sysdev_class class_rtl = { | ||
209 | .name = "ibm_rtl", | ||
210 | }; | ||
211 | |||
212 | static SYSDEV_CLASS_ATTR(version, S_IRUGO, rtl_show_version, NULL); | ||
213 | static SYSDEV_CLASS_ATTR(state, 0600, rtl_show_state, rtl_set_state); | ||
214 | |||
215 | static struct sysdev_class_attribute *rtl_attributes[] = { | ||
216 | &attr_version, | ||
217 | &attr_state, | ||
218 | NULL | ||
219 | }; | ||
220 | |||
221 | |||
222 | static int rtl_setup_sysfs(void) { | ||
223 | int ret, i; | ||
224 | ret = sysdev_class_register(&class_rtl); | ||
225 | |||
226 | if (!ret) { | ||
227 | for (i = 0; rtl_attributes[i]; i ++) | ||
228 | sysdev_class_create_file(&class_rtl, rtl_attributes[i]); | ||
229 | } | ||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | static void rtl_teardown_sysfs(void) { | ||
234 | int i; | ||
235 | for (i = 0; rtl_attributes[i]; i ++) | ||
236 | sysdev_class_remove_file(&class_rtl, rtl_attributes[i]); | ||
237 | sysdev_class_unregister(&class_rtl); | ||
238 | } | ||
239 | |||
240 | |||
241 | static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = { | ||
242 | { \ | ||
243 | .matches = { \ | ||
244 | DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \ | ||
245 | }, \ | ||
246 | }, | ||
247 | { } | ||
248 | }; | ||
249 | |||
250 | static int __init ibm_rtl_init(void) { | ||
251 | unsigned long ebda_addr, ebda_size; | ||
252 | unsigned int ebda_kb; | ||
253 | int ret = -ENODEV, i; | ||
254 | |||
255 | if (force) | ||
256 | pr_warn("module loaded by force\n"); | ||
257 | /* first ensure that we are running on IBM HW */ | ||
258 | else if (efi_enabled || !dmi_check_system(ibm_rtl_dmi_table)) | ||
259 | return -ENODEV; | ||
260 | |||
261 | /* Get the address for the Extended BIOS Data Area */ | ||
262 | ebda_addr = get_bios_ebda(); | ||
263 | if (!ebda_addr) { | ||
264 | RTL_DEBUG("no BIOS EBDA found\n"); | ||
265 | return -ENODEV; | ||
266 | } | ||
267 | |||
268 | ebda_map = ioremap(ebda_addr, 4); | ||
269 | if (!ebda_map) | ||
270 | return -ENOMEM; | ||
271 | |||
272 | /* First word in the EDBA is the Size in KB */ | ||
273 | ebda_kb = ioread16(ebda_map); | ||
274 | RTL_DEBUG("EBDA is %d kB\n", ebda_kb); | ||
275 | |||
276 | if (ebda_kb == 0) | ||
277 | goto out; | ||
278 | |||
279 | iounmap(ebda_map); | ||
280 | ebda_size = ebda_kb*1024; | ||
281 | |||
282 | /* Remap the whole table */ | ||
283 | ebda_map = ioremap(ebda_addr, ebda_size); | ||
284 | if (!ebda_map) | ||
285 | return -ENOMEM; | ||
286 | |||
287 | /* search for the _RTL_ signature at the start of the table */ | ||
288 | for (i = 0 ; i < ebda_size/sizeof(unsigned int); i++) { | ||
289 | struct ibm_rtl_table __iomem * tmp; | ||
290 | tmp = (struct ibm_rtl_table __iomem *) (ebda_map+i); | ||
291 | if ((readq(&tmp->signature) & RTL_MASK) == RTL_SIGNATURE) { | ||
292 | phys_addr_t addr; | ||
293 | unsigned int plen; | ||
294 | RTL_DEBUG("found RTL_SIGNATURE at %p\n", tmp); | ||
295 | rtl_table = tmp; | ||
296 | /* The address, value, width and offset are platform | ||
297 | * dependent and found in the ibm_rtl_table */ | ||
298 | rtl_cmd_width = ioread8(&rtl_table->cmd_granularity); | ||
299 | rtl_cmd_type = ioread8(&rtl_table->cmd_address_type); | ||
300 | RTL_DEBUG("rtl_cmd_width = %u, rtl_cmd_type = %u\n", | ||
301 | rtl_cmd_width, rtl_cmd_type); | ||
302 | addr = ioread32(&rtl_table->cmd_port_address); | ||
303 | RTL_DEBUG("addr = %#llx\n", (unsigned long long)addr); | ||
304 | plen = rtl_cmd_width/sizeof(char); | ||
305 | rtl_cmd_addr = rtl_port_map(addr, plen); | ||
306 | RTL_DEBUG("rtl_cmd_addr = %p\n", rtl_cmd_addr); | ||
307 | if (!rtl_cmd_addr) { | ||
308 | ret = -ENOMEM; | ||
309 | break; | ||
310 | } | ||
311 | ret = rtl_setup_sysfs(); | ||
312 | break; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | out: | ||
317 | if (ret) { | ||
318 | iounmap(ebda_map); | ||
319 | rtl_port_unmap(rtl_cmd_addr); | ||
320 | } | ||
321 | |||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | static void __exit ibm_rtl_exit(void) | ||
326 | { | ||
327 | if (rtl_table) { | ||
328 | RTL_DEBUG("cleaning up"); | ||
329 | /* do not leave the machine in SMI-free mode */ | ||
330 | ibm_rtl_write(0); | ||
331 | /* unmap, unlink and remove all traces */ | ||
332 | rtl_teardown_sysfs(); | ||
333 | iounmap(ebda_map); | ||
334 | rtl_port_unmap(rtl_cmd_addr); | ||
335 | } | ||
336 | } | ||
337 | |||
338 | module_init(ibm_rtl_init); | ||
339 | module_exit(ibm_rtl_exit); | ||
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c new file mode 100644 index 000000000000..bfdda33feb26 --- /dev/null +++ b/drivers/platform/x86/ideapad-laptop.c | |||
@@ -0,0 +1,497 @@ | |||
1 | /* | ||
2 | * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras | ||
3 | * | ||
4 | * Copyright © 2010 Intel Corporation | ||
5 | * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
20 | * 02110-1301, USA. | ||
21 | */ | ||
22 | |||
23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
24 | |||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/types.h> | ||
29 | #include <acpi/acpi_bus.h> | ||
30 | #include <acpi/acpi_drivers.h> | ||
31 | #include <linux/rfkill.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/input.h> | ||
34 | #include <linux/input/sparse-keymap.h> | ||
35 | |||
36 | #define IDEAPAD_RFKILL_DEV_NUM (3) | ||
37 | |||
38 | struct ideapad_private { | ||
39 | struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; | ||
40 | struct platform_device *platform_device; | ||
41 | struct input_dev *inputdev; | ||
42 | }; | ||
43 | |||
44 | static acpi_handle ideapad_handle; | ||
45 | static bool no_bt_rfkill; | ||
46 | module_param(no_bt_rfkill, bool, 0444); | ||
47 | MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); | ||
48 | |||
49 | /* | ||
50 | * ACPI Helpers | ||
51 | */ | ||
52 | #define IDEAPAD_EC_TIMEOUT (100) /* in ms */ | ||
53 | |||
54 | static int read_method_int(acpi_handle handle, const char *method, int *val) | ||
55 | { | ||
56 | acpi_status status; | ||
57 | unsigned long long result; | ||
58 | |||
59 | status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); | ||
60 | if (ACPI_FAILURE(status)) { | ||
61 | *val = -1; | ||
62 | return -1; | ||
63 | } else { | ||
64 | *val = result; | ||
65 | return 0; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | static int method_vpcr(acpi_handle handle, int cmd, int *ret) | ||
70 | { | ||
71 | acpi_status status; | ||
72 | unsigned long long result; | ||
73 | struct acpi_object_list params; | ||
74 | union acpi_object in_obj; | ||
75 | |||
76 | params.count = 1; | ||
77 | params.pointer = &in_obj; | ||
78 | in_obj.type = ACPI_TYPE_INTEGER; | ||
79 | in_obj.integer.value = cmd; | ||
80 | |||
81 | status = acpi_evaluate_integer(handle, "VPCR", ¶ms, &result); | ||
82 | |||
83 | if (ACPI_FAILURE(status)) { | ||
84 | *ret = -1; | ||
85 | return -1; | ||
86 | } else { | ||
87 | *ret = result; | ||
88 | return 0; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | static int method_vpcw(acpi_handle handle, int cmd, int data) | ||
93 | { | ||
94 | struct acpi_object_list params; | ||
95 | union acpi_object in_obj[2]; | ||
96 | acpi_status status; | ||
97 | |||
98 | params.count = 2; | ||
99 | params.pointer = in_obj; | ||
100 | in_obj[0].type = ACPI_TYPE_INTEGER; | ||
101 | in_obj[0].integer.value = cmd; | ||
102 | in_obj[1].type = ACPI_TYPE_INTEGER; | ||
103 | in_obj[1].integer.value = data; | ||
104 | |||
105 | status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); | ||
106 | if (status != AE_OK) | ||
107 | return -1; | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) | ||
112 | { | ||
113 | int val; | ||
114 | unsigned long int end_jiffies; | ||
115 | |||
116 | if (method_vpcw(handle, 1, cmd)) | ||
117 | return -1; | ||
118 | |||
119 | for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; | ||
120 | time_before(jiffies, end_jiffies);) { | ||
121 | schedule(); | ||
122 | if (method_vpcr(handle, 1, &val)) | ||
123 | return -1; | ||
124 | if (val == 0) { | ||
125 | if (method_vpcr(handle, 0, &val)) | ||
126 | return -1; | ||
127 | *data = val; | ||
128 | return 0; | ||
129 | } | ||
130 | } | ||
131 | pr_err("timeout in read_ec_cmd\n"); | ||
132 | return -1; | ||
133 | } | ||
134 | |||
135 | static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) | ||
136 | { | ||
137 | int val; | ||
138 | unsigned long int end_jiffies; | ||
139 | |||
140 | if (method_vpcw(handle, 0, data)) | ||
141 | return -1; | ||
142 | if (method_vpcw(handle, 1, cmd)) | ||
143 | return -1; | ||
144 | |||
145 | for (end_jiffies = jiffies+(HZ)*IDEAPAD_EC_TIMEOUT/1000+1; | ||
146 | time_before(jiffies, end_jiffies);) { | ||
147 | schedule(); | ||
148 | if (method_vpcr(handle, 1, &val)) | ||
149 | return -1; | ||
150 | if (val == 0) | ||
151 | return 0; | ||
152 | } | ||
153 | pr_err("timeout in write_ec_cmd\n"); | ||
154 | return -1; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * camera power | ||
159 | */ | ||
160 | static ssize_t show_ideapad_cam(struct device *dev, | ||
161 | struct device_attribute *attr, | ||
162 | char *buf) | ||
163 | { | ||
164 | unsigned long result; | ||
165 | |||
166 | if (read_ec_data(ideapad_handle, 0x1D, &result)) | ||
167 | return sprintf(buf, "-1\n"); | ||
168 | return sprintf(buf, "%lu\n", result); | ||
169 | } | ||
170 | |||
171 | static ssize_t store_ideapad_cam(struct device *dev, | ||
172 | struct device_attribute *attr, | ||
173 | const char *buf, size_t count) | ||
174 | { | ||
175 | int ret, state; | ||
176 | |||
177 | if (!count) | ||
178 | return 0; | ||
179 | if (sscanf(buf, "%i", &state) != 1) | ||
180 | return -EINVAL; | ||
181 | ret = write_ec_cmd(ideapad_handle, 0x1E, state); | ||
182 | if (ret < 0) | ||
183 | return ret; | ||
184 | return count; | ||
185 | } | ||
186 | |||
187 | static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); | ||
188 | |||
189 | /* | ||
190 | * Rfkill | ||
191 | */ | ||
192 | struct ideapad_rfk_data { | ||
193 | char *name; | ||
194 | int cfgbit; | ||
195 | int opcode; | ||
196 | int type; | ||
197 | }; | ||
198 | |||
199 | const struct ideapad_rfk_data ideapad_rfk_data[] = { | ||
200 | { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, | ||
201 | { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, | ||
202 | { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, | ||
203 | }; | ||
204 | |||
205 | static int ideapad_rfk_set(void *data, bool blocked) | ||
206 | { | ||
207 | unsigned long opcode = (unsigned long)data; | ||
208 | |||
209 | return write_ec_cmd(ideapad_handle, opcode, !blocked); | ||
210 | } | ||
211 | |||
212 | static struct rfkill_ops ideapad_rfk_ops = { | ||
213 | .set_block = ideapad_rfk_set, | ||
214 | }; | ||
215 | |||
216 | static void ideapad_sync_rfk_state(struct acpi_device *adevice) | ||
217 | { | ||
218 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
219 | unsigned long hw_blocked; | ||
220 | int i; | ||
221 | |||
222 | if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) | ||
223 | return; | ||
224 | hw_blocked = !hw_blocked; | ||
225 | |||
226 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) | ||
227 | if (priv->rfk[i]) | ||
228 | rfkill_set_hw_state(priv->rfk[i], hw_blocked); | ||
229 | } | ||
230 | |||
231 | static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, | ||
232 | int dev) | ||
233 | { | ||
234 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
235 | int ret; | ||
236 | unsigned long sw_blocked; | ||
237 | |||
238 | if (no_bt_rfkill && | ||
239 | (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { | ||
240 | /* Force to enable bluetooth when no_bt_rfkill=1 */ | ||
241 | write_ec_cmd(ideapad_handle, | ||
242 | ideapad_rfk_data[dev].opcode, 1); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, | ||
247 | ideapad_rfk_data[dev].type, &ideapad_rfk_ops, | ||
248 | (void *)(long)dev); | ||
249 | if (!priv->rfk[dev]) | ||
250 | return -ENOMEM; | ||
251 | |||
252 | if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1, | ||
253 | &sw_blocked)) { | ||
254 | rfkill_init_sw_state(priv->rfk[dev], 0); | ||
255 | } else { | ||
256 | sw_blocked = !sw_blocked; | ||
257 | rfkill_init_sw_state(priv->rfk[dev], sw_blocked); | ||
258 | } | ||
259 | |||
260 | ret = rfkill_register(priv->rfk[dev]); | ||
261 | if (ret) { | ||
262 | rfkill_destroy(priv->rfk[dev]); | ||
263 | return ret; | ||
264 | } | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice, | ||
269 | int dev) | ||
270 | { | ||
271 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
272 | |||
273 | if (!priv->rfk[dev]) | ||
274 | return; | ||
275 | |||
276 | rfkill_unregister(priv->rfk[dev]); | ||
277 | rfkill_destroy(priv->rfk[dev]); | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Platform device | ||
282 | */ | ||
283 | static struct attribute *ideapad_attributes[] = { | ||
284 | &dev_attr_camera_power.attr, | ||
285 | NULL | ||
286 | }; | ||
287 | |||
288 | static struct attribute_group ideapad_attribute_group = { | ||
289 | .attrs = ideapad_attributes | ||
290 | }; | ||
291 | |||
292 | static int __devinit ideapad_platform_init(struct ideapad_private *priv) | ||
293 | { | ||
294 | int result; | ||
295 | |||
296 | priv->platform_device = platform_device_alloc("ideapad", -1); | ||
297 | if (!priv->platform_device) | ||
298 | return -ENOMEM; | ||
299 | platform_set_drvdata(priv->platform_device, priv); | ||
300 | |||
301 | result = platform_device_add(priv->platform_device); | ||
302 | if (result) | ||
303 | goto fail_platform_device; | ||
304 | |||
305 | result = sysfs_create_group(&priv->platform_device->dev.kobj, | ||
306 | &ideapad_attribute_group); | ||
307 | if (result) | ||
308 | goto fail_sysfs; | ||
309 | return 0; | ||
310 | |||
311 | fail_sysfs: | ||
312 | platform_device_del(priv->platform_device); | ||
313 | fail_platform_device: | ||
314 | platform_device_put(priv->platform_device); | ||
315 | return result; | ||
316 | } | ||
317 | |||
318 | static void ideapad_platform_exit(struct ideapad_private *priv) | ||
319 | { | ||
320 | sysfs_remove_group(&priv->platform_device->dev.kobj, | ||
321 | &ideapad_attribute_group); | ||
322 | platform_device_unregister(priv->platform_device); | ||
323 | } | ||
324 | |||
325 | /* | ||
326 | * input device | ||
327 | */ | ||
328 | static const struct key_entry ideapad_keymap[] = { | ||
329 | { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, | ||
330 | { KE_KEY, 0x0D, { KEY_WLAN } }, | ||
331 | { KE_END, 0 }, | ||
332 | }; | ||
333 | |||
334 | static int __devinit ideapad_input_init(struct ideapad_private *priv) | ||
335 | { | ||
336 | struct input_dev *inputdev; | ||
337 | int error; | ||
338 | |||
339 | inputdev = input_allocate_device(); | ||
340 | if (!inputdev) { | ||
341 | pr_info("Unable to allocate input device\n"); | ||
342 | return -ENOMEM; | ||
343 | } | ||
344 | |||
345 | inputdev->name = "Ideapad extra buttons"; | ||
346 | inputdev->phys = "ideapad/input0"; | ||
347 | inputdev->id.bustype = BUS_HOST; | ||
348 | inputdev->dev.parent = &priv->platform_device->dev; | ||
349 | |||
350 | error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); | ||
351 | if (error) { | ||
352 | pr_err("Unable to setup input device keymap\n"); | ||
353 | goto err_free_dev; | ||
354 | } | ||
355 | |||
356 | error = input_register_device(inputdev); | ||
357 | if (error) { | ||
358 | pr_err("Unable to register input device\n"); | ||
359 | goto err_free_keymap; | ||
360 | } | ||
361 | |||
362 | priv->inputdev = inputdev; | ||
363 | return 0; | ||
364 | |||
365 | err_free_keymap: | ||
366 | sparse_keymap_free(inputdev); | ||
367 | err_free_dev: | ||
368 | input_free_device(inputdev); | ||
369 | return error; | ||
370 | } | ||
371 | |||
372 | static void __devexit ideapad_input_exit(struct ideapad_private *priv) | ||
373 | { | ||
374 | sparse_keymap_free(priv->inputdev); | ||
375 | input_unregister_device(priv->inputdev); | ||
376 | priv->inputdev = NULL; | ||
377 | } | ||
378 | |||
379 | static void ideapad_input_report(struct ideapad_private *priv, | ||
380 | unsigned long scancode) | ||
381 | { | ||
382 | sparse_keymap_report_event(priv->inputdev, scancode, 1, true); | ||
383 | } | ||
384 | |||
385 | /* | ||
386 | * module init/exit | ||
387 | */ | ||
388 | static const struct acpi_device_id ideapad_device_ids[] = { | ||
389 | { "VPC2004", 0}, | ||
390 | { "", 0}, | ||
391 | }; | ||
392 | MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); | ||
393 | |||
394 | static int __devinit ideapad_acpi_add(struct acpi_device *adevice) | ||
395 | { | ||
396 | int ret, i, cfg; | ||
397 | struct ideapad_private *priv; | ||
398 | |||
399 | if (read_method_int(adevice->handle, "_CFG", &cfg)) | ||
400 | return -ENODEV; | ||
401 | |||
402 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
403 | if (!priv) | ||
404 | return -ENOMEM; | ||
405 | dev_set_drvdata(&adevice->dev, priv); | ||
406 | ideapad_handle = adevice->handle; | ||
407 | |||
408 | ret = ideapad_platform_init(priv); | ||
409 | if (ret) | ||
410 | goto platform_failed; | ||
411 | |||
412 | ret = ideapad_input_init(priv); | ||
413 | if (ret) | ||
414 | goto input_failed; | ||
415 | |||
416 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { | ||
417 | if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) | ||
418 | ideapad_register_rfkill(adevice, i); | ||
419 | else | ||
420 | priv->rfk[i] = NULL; | ||
421 | } | ||
422 | ideapad_sync_rfk_state(adevice); | ||
423 | |||
424 | return 0; | ||
425 | |||
426 | input_failed: | ||
427 | ideapad_platform_exit(priv); | ||
428 | platform_failed: | ||
429 | kfree(priv); | ||
430 | return ret; | ||
431 | } | ||
432 | |||
433 | static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) | ||
434 | { | ||
435 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
436 | int i; | ||
437 | |||
438 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) | ||
439 | ideapad_unregister_rfkill(adevice, i); | ||
440 | ideapad_input_exit(priv); | ||
441 | ideapad_platform_exit(priv); | ||
442 | dev_set_drvdata(&adevice->dev, NULL); | ||
443 | kfree(priv); | ||
444 | |||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | ||
449 | { | ||
450 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
451 | acpi_handle handle = adevice->handle; | ||
452 | unsigned long vpc1, vpc2, vpc_bit; | ||
453 | |||
454 | if (read_ec_data(handle, 0x10, &vpc1)) | ||
455 | return; | ||
456 | if (read_ec_data(handle, 0x1A, &vpc2)) | ||
457 | return; | ||
458 | |||
459 | vpc1 = (vpc2 << 8) | vpc1; | ||
460 | for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { | ||
461 | if (test_bit(vpc_bit, &vpc1)) { | ||
462 | if (vpc_bit == 9) | ||
463 | ideapad_sync_rfk_state(adevice); | ||
464 | else if (vpc_bit == 4) | ||
465 | read_ec_data(handle, 0x12, &vpc2); | ||
466 | else | ||
467 | ideapad_input_report(priv, vpc_bit); | ||
468 | } | ||
469 | } | ||
470 | } | ||
471 | |||
472 | static struct acpi_driver ideapad_acpi_driver = { | ||
473 | .name = "ideapad_acpi", | ||
474 | .class = "IdeaPad", | ||
475 | .ids = ideapad_device_ids, | ||
476 | .ops.add = ideapad_acpi_add, | ||
477 | .ops.remove = ideapad_acpi_remove, | ||
478 | .ops.notify = ideapad_acpi_notify, | ||
479 | .owner = THIS_MODULE, | ||
480 | }; | ||
481 | |||
482 | static int __init ideapad_acpi_module_init(void) | ||
483 | { | ||
484 | return acpi_bus_register_driver(&ideapad_acpi_driver); | ||
485 | } | ||
486 | |||
487 | static void __exit ideapad_acpi_module_exit(void) | ||
488 | { | ||
489 | acpi_bus_unregister_driver(&ideapad_acpi_driver); | ||
490 | } | ||
491 | |||
492 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | ||
493 | MODULE_DESCRIPTION("IdeaPad ACPI Extras"); | ||
494 | MODULE_LICENSE("GPL"); | ||
495 | |||
496 | module_init(ideapad_acpi_module_init); | ||
497 | module_exit(ideapad_acpi_module_exit); | ||
diff --git a/drivers/platform/x86/ideapad_acpi.c b/drivers/platform/x86/ideapad_acpi.c deleted file mode 100644 index 798496353e8c..000000000000 --- a/drivers/platform/x86/ideapad_acpi.c +++ /dev/null | |||
@@ -1,306 +0,0 @@ | |||
1 | /* | ||
2 | * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras | ||
3 | * | ||
4 | * Copyright © 2010 Intel Corporation | ||
5 | * Copyright © 2010 David Woodhouse <dwmw2@infradead.org> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
20 | * 02110-1301, USA. | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <acpi/acpi_bus.h> | ||
28 | #include <acpi/acpi_drivers.h> | ||
29 | #include <linux/rfkill.h> | ||
30 | |||
31 | #define IDEAPAD_DEV_CAMERA 0 | ||
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 | |||
37 | struct ideapad_private { | ||
38 | struct rfkill *rfk[5]; | ||
39 | }; | ||
40 | |||
41 | static struct { | ||
42 | char *name; | ||
43 | int type; | ||
44 | } ideapad_rfk_data[] = { | ||
45 | /* camera has no rfkill */ | ||
46 | { "ideapad_wlan", RFKILL_TYPE_WLAN }, | ||
47 | { "ideapad_bluetooth", RFKILL_TYPE_BLUETOOTH }, | ||
48 | { "ideapad_3g", RFKILL_TYPE_WWAN }, | ||
49 | { "ideapad_killsw", RFKILL_TYPE_WLAN } | ||
50 | }; | ||
51 | |||
52 | static int ideapad_dev_exists(int device) | ||
53 | { | ||
54 | acpi_status status; | ||
55 | union acpi_object in_param; | ||
56 | struct acpi_object_list input = { 1, &in_param }; | ||
57 | struct acpi_buffer output; | ||
58 | union acpi_object out_obj; | ||
59 | |||
60 | output.length = sizeof(out_obj); | ||
61 | output.pointer = &out_obj; | ||
62 | |||
63 | in_param.type = ACPI_TYPE_INTEGER; | ||
64 | in_param.integer.value = device + 1; | ||
65 | |||
66 | status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output); | ||
67 | if (ACPI_FAILURE(status)) { | ||
68 | printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status); | ||
69 | return -ENODEV; | ||
70 | } | ||
71 | if (out_obj.type != ACPI_TYPE_INTEGER) { | ||
72 | printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n"); | ||
73 | return -ENODEV; | ||
74 | } | ||
75 | return out_obj.integer.value; | ||
76 | } | ||
77 | |||
78 | static int ideapad_dev_get_state(int device) | ||
79 | { | ||
80 | acpi_status status; | ||
81 | union acpi_object in_param; | ||
82 | struct acpi_object_list input = { 1, &in_param }; | ||
83 | struct acpi_buffer output; | ||
84 | union acpi_object out_obj; | ||
85 | |||
86 | output.length = sizeof(out_obj); | ||
87 | output.pointer = &out_obj; | ||
88 | |||
89 | in_param.type = ACPI_TYPE_INTEGER; | ||
90 | in_param.integer.value = device + 1; | ||
91 | |||
92 | status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output); | ||
93 | if (ACPI_FAILURE(status)) { | ||
94 | printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status); | ||
95 | return -ENODEV; | ||
96 | } | ||
97 | if (out_obj.type != ACPI_TYPE_INTEGER) { | ||
98 | printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n"); | ||
99 | return -ENODEV; | ||
100 | } | ||
101 | return out_obj.integer.value; | ||
102 | } | ||
103 | |||
104 | static int ideapad_dev_set_state(int device, int state) | ||
105 | { | ||
106 | acpi_status status; | ||
107 | union acpi_object in_params[2]; | ||
108 | struct acpi_object_list input = { 2, in_params }; | ||
109 | |||
110 | in_params[0].type = ACPI_TYPE_INTEGER; | ||
111 | in_params[0].integer.value = device + 1; | ||
112 | in_params[1].type = ACPI_TYPE_INTEGER; | ||
113 | in_params[1].integer.value = state; | ||
114 | |||
115 | status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL); | ||
116 | if (ACPI_FAILURE(status)) { | ||
117 | printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status); | ||
118 | return -ENODEV; | ||
119 | } | ||
120 | return 0; | ||
121 | } | ||
122 | static ssize_t show_ideapad_cam(struct device *dev, | ||
123 | struct device_attribute *attr, | ||
124 | char *buf) | ||
125 | { | ||
126 | int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA); | ||
127 | if (state < 0) | ||
128 | return state; | ||
129 | |||
130 | return sprintf(buf, "%d\n", state); | ||
131 | } | ||
132 | |||
133 | static ssize_t store_ideapad_cam(struct device *dev, | ||
134 | struct device_attribute *attr, | ||
135 | const char *buf, size_t count) | ||
136 | { | ||
137 | int ret, state; | ||
138 | |||
139 | if (!count) | ||
140 | return 0; | ||
141 | if (sscanf(buf, "%i", &state) != 1) | ||
142 | return -EINVAL; | ||
143 | ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, !!state); | ||
144 | if (ret < 0) | ||
145 | return ret; | ||
146 | return count; | ||
147 | } | ||
148 | |||
149 | static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); | ||
150 | |||
151 | static int ideapad_rfk_set(void *data, bool blocked) | ||
152 | { | ||
153 | int device = (unsigned long)data; | ||
154 | |||
155 | if (device == IDEAPAD_DEV_KILLSW) | ||
156 | return -EINVAL; | ||
157 | return ideapad_dev_set_state(device, !blocked); | ||
158 | } | ||
159 | |||
160 | static struct rfkill_ops ideapad_rfk_ops = { | ||
161 | .set_block = ideapad_rfk_set, | ||
162 | }; | ||
163 | |||
164 | static void ideapad_sync_rfk_state(struct acpi_device *adevice) | ||
165 | { | ||
166 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
167 | int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW); | ||
168 | int i; | ||
169 | |||
170 | rfkill_set_hw_state(priv->rfk[IDEAPAD_DEV_KILLSW], hw_blocked); | ||
171 | for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) | ||
172 | if (priv->rfk[i]) | ||
173 | rfkill_set_hw_state(priv->rfk[i], hw_blocked); | ||
174 | if (hw_blocked) | ||
175 | return; | ||
176 | |||
177 | for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) | ||
178 | if (priv->rfk[i]) | ||
179 | rfkill_set_sw_state(priv->rfk[i], !ideapad_dev_get_state(i)); | ||
180 | } | ||
181 | |||
182 | static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) | ||
183 | { | ||
184 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
185 | int ret; | ||
186 | |||
187 | priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev-1].name, &adevice->dev, | ||
188 | ideapad_rfk_data[dev-1].type, &ideapad_rfk_ops, | ||
189 | (void *)(long)dev); | ||
190 | if (!priv->rfk[dev]) | ||
191 | return -ENOMEM; | ||
192 | |||
193 | ret = rfkill_register(priv->rfk[dev]); | ||
194 | if (ret) { | ||
195 | rfkill_destroy(priv->rfk[dev]); | ||
196 | return ret; | ||
197 | } | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) | ||
202 | { | ||
203 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
204 | |||
205 | if (!priv->rfk[dev]) | ||
206 | return; | ||
207 | |||
208 | rfkill_unregister(priv->rfk[dev]); | ||
209 | rfkill_destroy(priv->rfk[dev]); | ||
210 | } | ||
211 | |||
212 | static const struct acpi_device_id ideapad_device_ids[] = { | ||
213 | { "VPC2004", 0}, | ||
214 | { "", 0}, | ||
215 | }; | ||
216 | MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); | ||
217 | |||
218 | static int ideapad_acpi_add(struct acpi_device *adevice) | ||
219 | { | ||
220 | int i; | ||
221 | int devs_present[5]; | ||
222 | struct ideapad_private *priv; | ||
223 | |||
224 | for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { | ||
225 | devs_present[i] = ideapad_dev_exists(i); | ||
226 | if (devs_present[i] < 0) | ||
227 | return devs_present[i]; | ||
228 | } | ||
229 | |||
230 | /* The hardware switch is always present */ | ||
231 | devs_present[IDEAPAD_DEV_KILLSW] = 1; | ||
232 | |||
233 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
234 | if (!priv) | ||
235 | return -ENOMEM; | ||
236 | |||
237 | if (devs_present[IDEAPAD_DEV_CAMERA]) { | ||
238 | int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); | ||
239 | if (ret) { | ||
240 | kfree(priv); | ||
241 | return ret; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | dev_set_drvdata(&adevice->dev, priv); | ||
246 | for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { | ||
247 | if (!devs_present[i]) | ||
248 | continue; | ||
249 | |||
250 | ideapad_register_rfkill(adevice, i); | ||
251 | } | ||
252 | ideapad_sync_rfk_state(adevice); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static int ideapad_acpi_remove(struct acpi_device *adevice, int type) | ||
257 | { | ||
258 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
259 | int i; | ||
260 | |||
261 | device_remove_file(&adevice->dev, &dev_attr_camera_power); | ||
262 | |||
263 | for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) | ||
264 | ideapad_unregister_rfkill(adevice, i); | ||
265 | |||
266 | dev_set_drvdata(&adevice->dev, NULL); | ||
267 | kfree(priv); | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | ||
272 | { | ||
273 | ideapad_sync_rfk_state(adevice); | ||
274 | } | ||
275 | |||
276 | static struct acpi_driver ideapad_acpi_driver = { | ||
277 | .name = "ideapad_acpi", | ||
278 | .class = "IdeaPad", | ||
279 | .ids = ideapad_device_ids, | ||
280 | .ops.add = ideapad_acpi_add, | ||
281 | .ops.remove = ideapad_acpi_remove, | ||
282 | .ops.notify = ideapad_acpi_notify, | ||
283 | .owner = THIS_MODULE, | ||
284 | }; | ||
285 | |||
286 | |||
287 | static int __init ideapad_acpi_module_init(void) | ||
288 | { | ||
289 | acpi_bus_register_driver(&ideapad_acpi_driver); | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | |||
295 | static void __exit ideapad_acpi_module_exit(void) | ||
296 | { | ||
297 | acpi_bus_unregister_driver(&ideapad_acpi_driver); | ||
298 | |||
299 | } | ||
300 | |||
301 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | ||
302 | MODULE_DESCRIPTION("IdeaPad ACPI Extras"); | ||
303 | MODULE_LICENSE("GPL"); | ||
304 | |||
305 | module_init(ideapad_acpi_module_init); | ||
306 | module_exit(ideapad_acpi_module_exit); | ||
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index c44a5e8b8b82..5ffe7c398148 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c | |||
@@ -75,6 +75,7 @@ | |||
75 | #include <drm/i915_drm.h> | 75 | #include <drm/i915_drm.h> |
76 | #include <asm/msr.h> | 76 | #include <asm/msr.h> |
77 | #include <asm/processor.h> | 77 | #include <asm/processor.h> |
78 | #include "intel_ips.h" | ||
78 | 79 | ||
79 | #define PCI_DEVICE_ID_INTEL_THERMAL_SENSOR 0x3b32 | 80 | #define PCI_DEVICE_ID_INTEL_THERMAL_SENSOR 0x3b32 |
80 | 81 | ||
@@ -245,6 +246,7 @@ | |||
245 | #define thm_writel(off, val) writel((val), ips->regmap + (off)) | 246 | #define thm_writel(off, val) writel((val), ips->regmap + (off)) |
246 | 247 | ||
247 | static const int IPS_ADJUST_PERIOD = 5000; /* ms */ | 248 | static const int IPS_ADJUST_PERIOD = 5000; /* ms */ |
249 | static bool late_i915_load = false; | ||
248 | 250 | ||
249 | /* For initial average collection */ | 251 | /* For initial average collection */ |
250 | static const int IPS_SAMPLE_PERIOD = 200; /* ms */ | 252 | static const int IPS_SAMPLE_PERIOD = 200; /* ms */ |
@@ -339,6 +341,22 @@ struct ips_driver { | |||
339 | u64 orig_turbo_ratios; | 341 | u64 orig_turbo_ratios; |
340 | }; | 342 | }; |
341 | 343 | ||
344 | static bool | ||
345 | ips_gpu_turbo_enabled(struct ips_driver *ips); | ||
346 | |||
347 | #ifndef readq | ||
348 | static inline __u64 readq(const volatile void __iomem *addr) | ||
349 | { | ||
350 | const volatile u32 __iomem *p = addr; | ||
351 | u32 low, high; | ||
352 | |||
353 | low = readl(p); | ||
354 | high = readl(p + 1); | ||
355 | |||
356 | return low + ((u64)high << 32); | ||
357 | } | ||
358 | #endif | ||
359 | |||
342 | /** | 360 | /** |
343 | * ips_cpu_busy - is CPU busy? | 361 | * ips_cpu_busy - is CPU busy? |
344 | * @ips: IPS driver struct | 362 | * @ips: IPS driver struct |
@@ -517,7 +535,7 @@ static void ips_disable_cpu_turbo(struct ips_driver *ips) | |||
517 | */ | 535 | */ |
518 | static bool ips_gpu_busy(struct ips_driver *ips) | 536 | static bool ips_gpu_busy(struct ips_driver *ips) |
519 | { | 537 | { |
520 | if (!ips->gpu_turbo_enabled) | 538 | if (!ips_gpu_turbo_enabled(ips)) |
521 | return false; | 539 | return false; |
522 | 540 | ||
523 | return ips->gpu_busy(); | 541 | return ips->gpu_busy(); |
@@ -532,7 +550,7 @@ static bool ips_gpu_busy(struct ips_driver *ips) | |||
532 | */ | 550 | */ |
533 | static void ips_gpu_raise(struct ips_driver *ips) | 551 | static void ips_gpu_raise(struct ips_driver *ips) |
534 | { | 552 | { |
535 | if (!ips->gpu_turbo_enabled) | 553 | if (!ips_gpu_turbo_enabled(ips)) |
536 | return; | 554 | return; |
537 | 555 | ||
538 | if (!ips->gpu_raise()) | 556 | if (!ips->gpu_raise()) |
@@ -549,7 +567,7 @@ static void ips_gpu_raise(struct ips_driver *ips) | |||
549 | */ | 567 | */ |
550 | static void ips_gpu_lower(struct ips_driver *ips) | 568 | static void ips_gpu_lower(struct ips_driver *ips) |
551 | { | 569 | { |
552 | if (!ips->gpu_turbo_enabled) | 570 | if (!ips_gpu_turbo_enabled(ips)) |
553 | return; | 571 | return; |
554 | 572 | ||
555 | if (!ips->gpu_lower()) | 573 | if (!ips->gpu_lower()) |
@@ -1106,7 +1124,7 @@ static int ips_monitor(void *data) | |||
1106 | last_msecs = jiffies_to_msecs(jiffies); | 1124 | last_msecs = jiffies_to_msecs(jiffies); |
1107 | expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); | 1125 | expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); |
1108 | 1126 | ||
1109 | __set_current_state(TASK_UNINTERRUPTIBLE); | 1127 | __set_current_state(TASK_INTERRUPTIBLE); |
1110 | mod_timer(&timer, expire); | 1128 | mod_timer(&timer, expire); |
1111 | schedule(); | 1129 | schedule(); |
1112 | 1130 | ||
@@ -1454,6 +1472,31 @@ out_err: | |||
1454 | return false; | 1472 | return false; |
1455 | } | 1473 | } |
1456 | 1474 | ||
1475 | static bool | ||
1476 | ips_gpu_turbo_enabled(struct ips_driver *ips) | ||
1477 | { | ||
1478 | if (!ips->gpu_busy && late_i915_load) { | ||
1479 | if (ips_get_i915_syms(ips)) { | ||
1480 | dev_info(&ips->dev->dev, | ||
1481 | "i915 driver attached, reenabling gpu turbo\n"); | ||
1482 | ips->gpu_turbo_enabled = !(thm_readl(THM_HTS) & HTS_GTD_DIS); | ||
1483 | } | ||
1484 | } | ||
1485 | |||
1486 | return ips->gpu_turbo_enabled; | ||
1487 | } | ||
1488 | |||
1489 | void | ||
1490 | ips_link_to_i915_driver(void) | ||
1491 | { | ||
1492 | /* We can't cleanly get at the various ips_driver structs from | ||
1493 | * this caller (the i915 driver), so just set a flag saying | ||
1494 | * that it's time to try getting the symbols again. | ||
1495 | */ | ||
1496 | late_i915_load = true; | ||
1497 | } | ||
1498 | EXPORT_SYMBOL_GPL(ips_link_to_i915_driver); | ||
1499 | |||
1457 | static DEFINE_PCI_DEVICE_TABLE(ips_id_table) = { | 1500 | static DEFINE_PCI_DEVICE_TABLE(ips_id_table) = { |
1458 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, | 1501 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, |
1459 | PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, | 1502 | PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, |
diff --git a/drivers/platform/x86/intel_ips.h b/drivers/platform/x86/intel_ips.h new file mode 100644 index 000000000000..73299beff5b3 --- /dev/null +++ b/drivers/platform/x86/intel_ips.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Intel Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
16 | * | ||
17 | * The full GNU General Public License is included in this distribution in | ||
18 | * the file called "COPYING". | ||
19 | */ | ||
20 | |||
21 | void ips_link_to_i915_driver(void); | ||
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index eacd5da7dd24..809adea4965f 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c | |||
@@ -27,6 +27,8 @@ | |||
27 | * to get/set bandwidth. | 27 | * to get/set bandwidth. |
28 | */ | 28 | */ |
29 | 29 | ||
30 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
31 | |||
30 | #include <linux/kernel.h> | 32 | #include <linux/kernel.h> |
31 | #include <linux/module.h> | 33 | #include <linux/module.h> |
32 | #include <linux/init.h> | 34 | #include <linux/init.h> |
@@ -135,8 +137,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, | |||
135 | acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list, | 137 | acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list, |
136 | &temp); | 138 | &temp); |
137 | 139 | ||
138 | printk(KERN_INFO | 140 | pr_info("Bandwidth value was %ld: status is %d\n", state, status); |
139 | "Bandwidth value was %ld: status is %d\n", state, status); | ||
140 | if (ACPI_FAILURE(status)) | 141 | if (ACPI_FAILURE(status)) |
141 | return -EFAULT; | 142 | return -EFAULT; |
142 | 143 | ||
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c new file mode 100644 index 000000000000..f1ae5078b7ec --- /dev/null +++ b/drivers/platform/x86/intel_mid_powerbtn.c | |||
@@ -0,0 +1,136 @@ | |||
1 | /* | ||
2 | * Power button driver for Medfield. | ||
3 | * | ||
4 | * Copyright (C) 2010 Intel Corp | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 of the License. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/input.h> | ||
26 | |||
27 | #include <asm/intel_scu_ipc.h> | ||
28 | |||
29 | #define DRIVER_NAME "msic_power_btn" | ||
30 | |||
31 | #define MSIC_PB_STATUS 0x3f | ||
32 | #define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */ | ||
33 | |||
34 | static irqreturn_t mfld_pb_isr(int irq, void *dev_id) | ||
35 | { | ||
36 | struct input_dev *input = dev_id; | ||
37 | int ret; | ||
38 | u8 pbstat; | ||
39 | |||
40 | ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat); | ||
41 | if (ret < 0) { | ||
42 | dev_err(input->dev.parent, "Read error %d while reading" | ||
43 | " MSIC_PB_STATUS\n", ret); | ||
44 | } else { | ||
45 | input_event(input, EV_KEY, KEY_POWER, | ||
46 | !(pbstat & MSIC_PB_LEVEL)); | ||
47 | input_sync(input); | ||
48 | } | ||
49 | |||
50 | return IRQ_HANDLED; | ||
51 | } | ||
52 | |||
53 | static int __devinit mfld_pb_probe(struct platform_device *pdev) | ||
54 | { | ||
55 | struct input_dev *input; | ||
56 | int irq = platform_get_irq(pdev, 0); | ||
57 | int error; | ||
58 | |||
59 | if (irq < 0) | ||
60 | return -EINVAL; | ||
61 | |||
62 | input = input_allocate_device(); | ||
63 | if (!input) { | ||
64 | dev_err(&pdev->dev, "Input device allocation error\n"); | ||
65 | return -ENOMEM; | ||
66 | } | ||
67 | |||
68 | input->name = pdev->name; | ||
69 | input->phys = "power-button/input0"; | ||
70 | input->id.bustype = BUS_HOST; | ||
71 | input->dev.parent = &pdev->dev; | ||
72 | |||
73 | input_set_capability(input, EV_KEY, KEY_POWER); | ||
74 | |||
75 | error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0, | ||
76 | DRIVER_NAME, input); | ||
77 | if (error) { | ||
78 | dev_err(&pdev->dev, "Unable to request irq %d for mfld power" | ||
79 | "button\n", irq); | ||
80 | goto err_free_input; | ||
81 | } | ||
82 | |||
83 | error = input_register_device(input); | ||
84 | if (error) { | ||
85 | dev_err(&pdev->dev, "Unable to register input dev, error " | ||
86 | "%d\n", error); | ||
87 | goto err_free_irq; | ||
88 | } | ||
89 | |||
90 | platform_set_drvdata(pdev, input); | ||
91 | return 0; | ||
92 | |||
93 | err_free_irq: | ||
94 | free_irq(irq, input); | ||
95 | err_free_input: | ||
96 | input_free_device(input); | ||
97 | return error; | ||
98 | } | ||
99 | |||
100 | static int __devexit mfld_pb_remove(struct platform_device *pdev) | ||
101 | { | ||
102 | struct input_dev *input = platform_get_drvdata(pdev); | ||
103 | int irq = platform_get_irq(pdev, 0); | ||
104 | |||
105 | free_irq(irq, input); | ||
106 | input_unregister_device(input); | ||
107 | platform_set_drvdata(pdev, NULL); | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static struct platform_driver mfld_pb_driver = { | ||
113 | .driver = { | ||
114 | .name = DRIVER_NAME, | ||
115 | .owner = THIS_MODULE, | ||
116 | }, | ||
117 | .probe = mfld_pb_probe, | ||
118 | .remove = __devexit_p(mfld_pb_remove), | ||
119 | }; | ||
120 | |||
121 | static int __init mfld_pb_init(void) | ||
122 | { | ||
123 | return platform_driver_register(&mfld_pb_driver); | ||
124 | } | ||
125 | module_init(mfld_pb_init); | ||
126 | |||
127 | static void __exit mfld_pb_exit(void) | ||
128 | { | ||
129 | platform_driver_unregister(&mfld_pb_driver); | ||
130 | } | ||
131 | module_exit(mfld_pb_exit); | ||
132 | |||
133 | MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>"); | ||
134 | MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); | ||
135 | MODULE_LICENSE("GPL v2"); | ||
136 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c new file mode 100644 index 000000000000..3a578323122b --- /dev/null +++ b/drivers/platform/x86/intel_mid_thermal.c | |||
@@ -0,0 +1,571 @@ | |||
1 | /* | ||
2 | * intel_mid_thermal.c - Intel MID platform thermal driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Intel Corporation | ||
5 | * | ||
6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; version 2 of the License. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along | ||
18 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
20 | * | ||
21 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
22 | * Author: Durgadoss R <durgadoss.r@intel.com> | ||
23 | */ | ||
24 | |||
25 | #define pr_fmt(fmt) "intel_mid_thermal: " fmt | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/err.h> | ||
30 | #include <linux/param.h> | ||
31 | #include <linux/device.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/pm.h> | ||
35 | #include <linux/thermal.h> | ||
36 | |||
37 | #include <asm/intel_scu_ipc.h> | ||
38 | |||
39 | /* Number of thermal sensors */ | ||
40 | #define MSIC_THERMAL_SENSORS 4 | ||
41 | |||
42 | /* ADC1 - thermal registers */ | ||
43 | #define MSIC_THERM_ADC1CNTL1 0x1C0 | ||
44 | #define MSIC_ADC_ENBL 0x10 | ||
45 | #define MSIC_ADC_START 0x08 | ||
46 | |||
47 | #define MSIC_THERM_ADC1CNTL3 0x1C2 | ||
48 | #define MSIC_ADCTHERM_ENBL 0x04 | ||
49 | #define MSIC_ADCRRDATA_ENBL 0x05 | ||
50 | #define MSIC_CHANL_MASK_VAL 0x0F | ||
51 | |||
52 | #define MSIC_STOPBIT_MASK 16 | ||
53 | #define MSIC_ADCTHERM_MASK 4 | ||
54 | /* Number of ADC channels */ | ||
55 | #define ADC_CHANLS_MAX 15 | ||
56 | #define ADC_LOOP_MAX (ADC_CHANLS_MAX - MSIC_THERMAL_SENSORS) | ||
57 | |||
58 | /* ADC channel code values */ | ||
59 | #define SKIN_SENSOR0_CODE 0x08 | ||
60 | #define SKIN_SENSOR1_CODE 0x09 | ||
61 | #define SYS_SENSOR_CODE 0x0A | ||
62 | #define MSIC_DIE_SENSOR_CODE 0x03 | ||
63 | |||
64 | #define SKIN_THERM_SENSOR0 0 | ||
65 | #define SKIN_THERM_SENSOR1 1 | ||
66 | #define SYS_THERM_SENSOR2 2 | ||
67 | #define MSIC_DIE_THERM_SENSOR3 3 | ||
68 | |||
69 | /* ADC code range */ | ||
70 | #define ADC_MAX 977 | ||
71 | #define ADC_MIN 162 | ||
72 | #define ADC_VAL0C 887 | ||
73 | #define ADC_VAL20C 720 | ||
74 | #define ADC_VAL40C 508 | ||
75 | #define ADC_VAL60C 315 | ||
76 | |||
77 | /* ADC base addresses */ | ||
78 | #define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ | ||
79 | #define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ | ||
80 | |||
81 | /* MSIC die attributes */ | ||
82 | #define MSIC_DIE_ADC_MIN 488 | ||
83 | #define MSIC_DIE_ADC_MAX 1004 | ||
84 | |||
85 | /* This holds the address of the first free ADC channel, | ||
86 | * among the 15 channels | ||
87 | */ | ||
88 | static int channel_index; | ||
89 | |||
90 | struct platform_info { | ||
91 | struct platform_device *pdev; | ||
92 | struct thermal_zone_device *tzd[MSIC_THERMAL_SENSORS]; | ||
93 | }; | ||
94 | |||
95 | struct thermal_device_info { | ||
96 | unsigned int chnl_addr; | ||
97 | int direct; | ||
98 | /* This holds the current temperature in millidegree celsius */ | ||
99 | long curr_temp; | ||
100 | }; | ||
101 | |||
102 | /** | ||
103 | * to_msic_die_temp - converts adc_val to msic_die temperature | ||
104 | * @adc_val: ADC value to be converted | ||
105 | * | ||
106 | * Can sleep | ||
107 | */ | ||
108 | static int to_msic_die_temp(uint16_t adc_val) | ||
109 | { | ||
110 | return (368 * (adc_val) / 1000) - 220; | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * is_valid_adc - checks whether the adc code is within the defined range | ||
115 | * @min: minimum value for the sensor | ||
116 | * @max: maximum value for the sensor | ||
117 | * | ||
118 | * Can sleep | ||
119 | */ | ||
120 | static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max) | ||
121 | { | ||
122 | return (adc_val >= min) && (adc_val <= max); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * adc_to_temp - converts the ADC code to temperature in C | ||
127 | * @direct: true if ths channel is direct index | ||
128 | * @adc_val: the adc_val that needs to be converted | ||
129 | * @tp: temperature return value | ||
130 | * | ||
131 | * Linear approximation is used to covert the skin adc value into temperature. | ||
132 | * This technique is used to avoid very long look-up table to get | ||
133 | * the appropriate temp value from ADC value. | ||
134 | * The adc code vs sensor temp curve is split into five parts | ||
135 | * to achieve very close approximate temp value with less than | ||
136 | * 0.5C error | ||
137 | */ | ||
138 | static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp) | ||
139 | { | ||
140 | int temp; | ||
141 | |||
142 | /* Direct conversion for die temperature */ | ||
143 | if (direct) { | ||
144 | if (is_valid_adc(adc_val, MSIC_DIE_ADC_MIN, MSIC_DIE_ADC_MAX)) { | ||
145 | *tp = to_msic_die_temp(adc_val) * 1000; | ||
146 | return 0; | ||
147 | } | ||
148 | return -ERANGE; | ||
149 | } | ||
150 | |||
151 | if (!is_valid_adc(adc_val, ADC_MIN, ADC_MAX)) | ||
152 | return -ERANGE; | ||
153 | |||
154 | /* Linear approximation for skin temperature */ | ||
155 | if (adc_val > ADC_VAL0C) | ||
156 | temp = 177 - (adc_val/5); | ||
157 | else if ((adc_val <= ADC_VAL0C) && (adc_val > ADC_VAL20C)) | ||
158 | temp = 111 - (adc_val/8); | ||
159 | else if ((adc_val <= ADC_VAL20C) && (adc_val > ADC_VAL40C)) | ||
160 | temp = 92 - (adc_val/10); | ||
161 | else if ((adc_val <= ADC_VAL40C) && (adc_val > ADC_VAL60C)) | ||
162 | temp = 91 - (adc_val/10); | ||
163 | else | ||
164 | temp = 112 - (adc_val/6); | ||
165 | |||
166 | /* Convert temperature in celsius to milli degree celsius */ | ||
167 | *tp = temp * 1000; | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * mid_read_temp - read sensors for temperature | ||
173 | * @temp: holds the current temperature for the sensor after reading | ||
174 | * | ||
175 | * reads the adc_code from the channel and converts it to real | ||
176 | * temperature. The converted value is stored in temp. | ||
177 | * | ||
178 | * Can sleep | ||
179 | */ | ||
180 | static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp) | ||
181 | { | ||
182 | struct thermal_device_info *td_info = tzd->devdata; | ||
183 | uint16_t adc_val, addr; | ||
184 | uint8_t data = 0; | ||
185 | int ret; | ||
186 | unsigned long curr_temp; | ||
187 | |||
188 | |||
189 | addr = td_info->chnl_addr; | ||
190 | |||
191 | /* Enable the msic for conversion before reading */ | ||
192 | ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL); | ||
193 | if (ret) | ||
194 | return ret; | ||
195 | |||
196 | /* Re-toggle the RRDATARD bit (temporary workaround) */ | ||
197 | ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL); | ||
198 | if (ret) | ||
199 | return ret; | ||
200 | |||
201 | /* Read the higher bits of data */ | ||
202 | ret = intel_scu_ipc_ioread8(addr, &data); | ||
203 | if (ret) | ||
204 | return ret; | ||
205 | |||
206 | /* Shift bits to accommodate the lower two data bits */ | ||
207 | adc_val = (data << 2); | ||
208 | addr++; | ||
209 | |||
210 | ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */ | ||
211 | if (ret) | ||
212 | return ret; | ||
213 | |||
214 | /* Adding lower two bits to the higher bits */ | ||
215 | data &= 03; | ||
216 | adc_val += data; | ||
217 | |||
218 | /* Convert ADC value to temperature */ | ||
219 | ret = adc_to_temp(td_info->direct, adc_val, &curr_temp); | ||
220 | if (ret == 0) | ||
221 | *temp = td_info->curr_temp = curr_temp; | ||
222 | return ret; | ||
223 | } | ||
224 | |||
225 | /** | ||
226 | * configure_adc - enables/disables the ADC for conversion | ||
227 | * @val: zero: disables the ADC non-zero:enables the ADC | ||
228 | * | ||
229 | * Enable/Disable the ADC depending on the argument | ||
230 | * | ||
231 | * Can sleep | ||
232 | */ | ||
233 | static int configure_adc(int val) | ||
234 | { | ||
235 | int ret; | ||
236 | uint8_t data; | ||
237 | |||
238 | ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); | ||
239 | if (ret) | ||
240 | return ret; | ||
241 | |||
242 | if (val) { | ||
243 | /* Enable and start the ADC */ | ||
244 | data |= (MSIC_ADC_ENBL | MSIC_ADC_START); | ||
245 | } else { | ||
246 | /* Just stop the ADC */ | ||
247 | data &= (~MSIC_ADC_START); | ||
248 | } | ||
249 | return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data); | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * set_up_therm_channel - enable thermal channel for conversion | ||
254 | * @base_addr: index of free msic ADC channel | ||
255 | * | ||
256 | * Enable all the three channels for conversion | ||
257 | * | ||
258 | * Can sleep | ||
259 | */ | ||
260 | static int set_up_therm_channel(u16 base_addr) | ||
261 | { | ||
262 | int ret; | ||
263 | |||
264 | /* Enable all the sensor channels */ | ||
265 | ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE); | ||
266 | if (ret) | ||
267 | return ret; | ||
268 | |||
269 | ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE); | ||
270 | if (ret) | ||
271 | return ret; | ||
272 | |||
273 | ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE); | ||
274 | if (ret) | ||
275 | return ret; | ||
276 | |||
277 | /* Since this is the last channel, set the stop bit | ||
278 | * to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ | ||
279 | ret = intel_scu_ipc_iowrite8(base_addr + 3, | ||
280 | (MSIC_DIE_SENSOR_CODE | 0x10)); | ||
281 | if (ret) | ||
282 | return ret; | ||
283 | |||
284 | /* Enable ADC and start it */ | ||
285 | return configure_adc(1); | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * reset_stopbit - sets the stop bit to 0 on the given channel | ||
290 | * @addr: address of the channel | ||
291 | * | ||
292 | * Can sleep | ||
293 | */ | ||
294 | static int reset_stopbit(uint16_t addr) | ||
295 | { | ||
296 | int ret; | ||
297 | uint8_t data; | ||
298 | ret = intel_scu_ipc_ioread8(addr, &data); | ||
299 | if (ret) | ||
300 | return ret; | ||
301 | /* Set the stop bit to zero */ | ||
302 | return intel_scu_ipc_iowrite8(addr, (data & 0xEF)); | ||
303 | } | ||
304 | |||
305 | /** | ||
306 | * find_free_channel - finds an empty channel for conversion | ||
307 | * | ||
308 | * If the ADC is not enabled then start using 0th channel | ||
309 | * itself. Otherwise find an empty channel by looking for a | ||
310 | * channel in which the stopbit is set to 1. returns the index | ||
311 | * of the first free channel if succeeds or an error code. | ||
312 | * | ||
313 | * Context: can sleep | ||
314 | * | ||
315 | * FIXME: Ultimately the channel allocator will move into the intel_scu_ipc | ||
316 | * code. | ||
317 | */ | ||
318 | static int find_free_channel(void) | ||
319 | { | ||
320 | int ret; | ||
321 | int i; | ||
322 | uint8_t data; | ||
323 | |||
324 | /* check whether ADC is enabled */ | ||
325 | ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); | ||
326 | if (ret) | ||
327 | return ret; | ||
328 | |||
329 | if ((data & MSIC_ADC_ENBL) == 0) | ||
330 | return 0; | ||
331 | |||
332 | /* ADC is already enabled; Looking for an empty channel */ | ||
333 | for (i = 0; i < ADC_CHANLS_MAX; i++) { | ||
334 | ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data); | ||
335 | if (ret) | ||
336 | return ret; | ||
337 | |||
338 | if (data & MSIC_STOPBIT_MASK) { | ||
339 | ret = i; | ||
340 | break; | ||
341 | } | ||
342 | } | ||
343 | return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret; | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * mid_initialize_adc - initializing the ADC | ||
348 | * @dev: our device structure | ||
349 | * | ||
350 | * Initialize the ADC for reading thermistor values. Can sleep. | ||
351 | */ | ||
352 | static int mid_initialize_adc(struct device *dev) | ||
353 | { | ||
354 | u8 data; | ||
355 | u16 base_addr; | ||
356 | int ret; | ||
357 | |||
358 | /* | ||
359 | * Ensure that adctherm is disabled before we | ||
360 | * initialize the ADC | ||
361 | */ | ||
362 | ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data); | ||
363 | if (ret) | ||
364 | return ret; | ||
365 | |||
366 | if (data & MSIC_ADCTHERM_MASK) | ||
367 | dev_warn(dev, "ADCTHERM already set"); | ||
368 | |||
369 | /* Index of the first channel in which the stop bit is set */ | ||
370 | channel_index = find_free_channel(); | ||
371 | if (channel_index < 0) { | ||
372 | dev_err(dev, "No free ADC channels"); | ||
373 | return channel_index; | ||
374 | } | ||
375 | |||
376 | base_addr = ADC_CHNL_START_ADDR + channel_index; | ||
377 | |||
378 | if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) { | ||
379 | /* Reset stop bit for channels other than 0 and 12 */ | ||
380 | ret = reset_stopbit(base_addr); | ||
381 | if (ret) | ||
382 | return ret; | ||
383 | |||
384 | /* Index of the first free channel */ | ||
385 | base_addr++; | ||
386 | channel_index++; | ||
387 | } | ||
388 | |||
389 | ret = set_up_therm_channel(base_addr); | ||
390 | if (ret) { | ||
391 | dev_err(dev, "unable to enable ADC"); | ||
392 | return ret; | ||
393 | } | ||
394 | dev_dbg(dev, "ADC initialization successful"); | ||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | /** | ||
399 | * initialize_sensor - sets default temp and timer ranges | ||
400 | * @index: index of the sensor | ||
401 | * | ||
402 | * Context: can sleep | ||
403 | */ | ||
404 | static struct thermal_device_info *initialize_sensor(int index) | ||
405 | { | ||
406 | struct thermal_device_info *td_info = | ||
407 | kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL); | ||
408 | |||
409 | if (!td_info) | ||
410 | return NULL; | ||
411 | |||
412 | /* Set the base addr of the channel for this sensor */ | ||
413 | td_info->chnl_addr = ADC_DATA_START_ADDR + 2 * (channel_index + index); | ||
414 | /* Sensor 3 is direct conversion */ | ||
415 | if (index == 3) | ||
416 | td_info->direct = 1; | ||
417 | return td_info; | ||
418 | } | ||
419 | |||
420 | /** | ||
421 | * mid_thermal_resume - resume routine | ||
422 | * @pdev: platform device structure | ||
423 | * | ||
424 | * mid thermal resume: re-initializes the adc. Can sleep. | ||
425 | */ | ||
426 | static int mid_thermal_resume(struct platform_device *pdev) | ||
427 | { | ||
428 | return mid_initialize_adc(&pdev->dev); | ||
429 | } | ||
430 | |||
431 | /** | ||
432 | * mid_thermal_suspend - suspend routine | ||
433 | * @pdev: platform device structure | ||
434 | * | ||
435 | * mid thermal suspend implements the suspend functionality | ||
436 | * by stopping the ADC. Can sleep. | ||
437 | */ | ||
438 | static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg) | ||
439 | { | ||
440 | /* | ||
441 | * This just stops the ADC and does not disable it. | ||
442 | * temporary workaround until we have a generic ADC driver. | ||
443 | * If 0 is passed, it disables the ADC. | ||
444 | */ | ||
445 | return configure_adc(0); | ||
446 | } | ||
447 | |||
448 | /** | ||
449 | * read_curr_temp - reads the current temperature and stores in temp | ||
450 | * @temp: holds the current temperature value after reading | ||
451 | * | ||
452 | * Can sleep | ||
453 | */ | ||
454 | static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp) | ||
455 | { | ||
456 | WARN_ON(tzd == NULL); | ||
457 | return mid_read_temp(tzd, temp); | ||
458 | } | ||
459 | |||
460 | /* Can't be const */ | ||
461 | static struct thermal_zone_device_ops tzd_ops = { | ||
462 | .get_temp = read_curr_temp, | ||
463 | }; | ||
464 | |||
465 | /** | ||
466 | * mid_thermal_probe - mfld thermal initialize | ||
467 | * @pdev: platform device structure | ||
468 | * | ||
469 | * mid thermal probe initializes the hardware and registers | ||
470 | * all the sensors with the generic thermal framework. Can sleep. | ||
471 | */ | ||
472 | static int mid_thermal_probe(struct platform_device *pdev) | ||
473 | { | ||
474 | static char *name[MSIC_THERMAL_SENSORS] = { | ||
475 | "skin0", "skin1", "sys", "msicdie" | ||
476 | }; | ||
477 | |||
478 | int ret; | ||
479 | int i; | ||
480 | struct platform_info *pinfo; | ||
481 | |||
482 | pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL); | ||
483 | if (!pinfo) | ||
484 | return -ENOMEM; | ||
485 | |||
486 | /* Initializing the hardware */ | ||
487 | ret = mid_initialize_adc(&pdev->dev); | ||
488 | if (ret) { | ||
489 | dev_err(&pdev->dev, "ADC init failed"); | ||
490 | kfree(pinfo); | ||
491 | return ret; | ||
492 | } | ||
493 | |||
494 | /* Register each sensor with the generic thermal framework*/ | ||
495 | for (i = 0; i < MSIC_THERMAL_SENSORS; i++) { | ||
496 | pinfo->tzd[i] = thermal_zone_device_register(name[i], | ||
497 | 0, initialize_sensor(i), &tzd_ops, 0, 0, 0, 0); | ||
498 | if (IS_ERR(pinfo->tzd[i])) | ||
499 | goto reg_fail; | ||
500 | } | ||
501 | |||
502 | pinfo->pdev = pdev; | ||
503 | platform_set_drvdata(pdev, pinfo); | ||
504 | return 0; | ||
505 | |||
506 | reg_fail: | ||
507 | ret = PTR_ERR(pinfo->tzd[i]); | ||
508 | while (--i >= 0) | ||
509 | thermal_zone_device_unregister(pinfo->tzd[i]); | ||
510 | configure_adc(0); | ||
511 | kfree(pinfo); | ||
512 | return ret; | ||
513 | } | ||
514 | |||
515 | /** | ||
516 | * mid_thermal_remove - mfld thermal finalize | ||
517 | * @dev: platform device structure | ||
518 | * | ||
519 | * MLFD thermal remove unregisters all the sensors from the generic | ||
520 | * thermal framework. Can sleep. | ||
521 | */ | ||
522 | static int mid_thermal_remove(struct platform_device *pdev) | ||
523 | { | ||
524 | int i; | ||
525 | struct platform_info *pinfo = platform_get_drvdata(pdev); | ||
526 | |||
527 | for (i = 0; i < MSIC_THERMAL_SENSORS; i++) | ||
528 | thermal_zone_device_unregister(pinfo->tzd[i]); | ||
529 | |||
530 | kfree(pinfo); | ||
531 | platform_set_drvdata(pdev, NULL); | ||
532 | |||
533 | /* Stop the ADC */ | ||
534 | return configure_adc(0); | ||
535 | } | ||
536 | |||
537 | #define DRIVER_NAME "msic_sensor" | ||
538 | |||
539 | static const struct platform_device_id therm_id_table[] = { | ||
540 | { DRIVER_NAME, 1 }, | ||
541 | { } | ||
542 | }; | ||
543 | |||
544 | static struct platform_driver mid_thermal_driver = { | ||
545 | .driver = { | ||
546 | .name = DRIVER_NAME, | ||
547 | .owner = THIS_MODULE, | ||
548 | }, | ||
549 | .probe = mid_thermal_probe, | ||
550 | .suspend = mid_thermal_suspend, | ||
551 | .resume = mid_thermal_resume, | ||
552 | .remove = __devexit_p(mid_thermal_remove), | ||
553 | .id_table = therm_id_table, | ||
554 | }; | ||
555 | |||
556 | static int __init mid_thermal_module_init(void) | ||
557 | { | ||
558 | return platform_driver_register(&mid_thermal_driver); | ||
559 | } | ||
560 | |||
561 | static void __exit mid_thermal_module_exit(void) | ||
562 | { | ||
563 | platform_driver_unregister(&mid_thermal_driver); | ||
564 | } | ||
565 | |||
566 | module_init(mid_thermal_module_init); | ||
567 | module_exit(mid_thermal_module_exit); | ||
568 | |||
569 | MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>"); | ||
570 | MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); | ||
571 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c new file mode 100644 index 000000000000..7f88c7923fc6 --- /dev/null +++ b/drivers/platform/x86/intel_oaktrail.c | |||
@@ -0,0 +1,397 @@ | |||
1 | /* | ||
2 | * intel_oaktrail.c - Intel OakTrail Platform support. | ||
3 | * | ||
4 | * Copyright (C) 2010-2011 Intel Corporation | ||
5 | * Author: Yin Kangkai (kangkai.yin@intel.com) | ||
6 | * | ||
7 | * based on Compal driver, Copyright (C) 2008 Cezary Jackiewicz | ||
8 | * <cezary.jackiewicz (at) gmail.com>, based on MSI driver | ||
9 | * Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
24 | * 02110-1301, USA. | ||
25 | * | ||
26 | * This driver does below things: | ||
27 | * 1. registers itself in the Linux backlight control in | ||
28 | * /sys/class/backlight/intel_oaktrail/ | ||
29 | * | ||
30 | * 2. registers in the rfkill subsystem here: /sys/class/rfkill/rfkillX/ | ||
31 | * for these components: wifi, bluetooth, wwan (3g), gps | ||
32 | * | ||
33 | * This driver might work on other products based on Oaktrail. If you | ||
34 | * want to try it you can pass force=1 as argument to the module which | ||
35 | * will force it to load even when the DMI data doesn't identify the | ||
36 | * product as compatible. | ||
37 | */ | ||
38 | |||
39 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
40 | |||
41 | #include <linux/module.h> | ||
42 | #include <linux/kernel.h> | ||
43 | #include <linux/init.h> | ||
44 | #include <linux/acpi.h> | ||
45 | #include <linux/fb.h> | ||
46 | #include <linux/mutex.h> | ||
47 | #include <linux/err.h> | ||
48 | #include <linux/i2c.h> | ||
49 | #include <linux/backlight.h> | ||
50 | #include <linux/platform_device.h> | ||
51 | #include <linux/dmi.h> | ||
52 | #include <linux/rfkill.h> | ||
53 | #include <acpi/acpi_bus.h> | ||
54 | #include <acpi/acpi_drivers.h> | ||
55 | |||
56 | |||
57 | #define DRIVER_NAME "intel_oaktrail" | ||
58 | #define DRIVER_VERSION "0.4ac1" | ||
59 | |||
60 | /* | ||
61 | * This is the devices status address in EC space, and the control bits | ||
62 | * definition: | ||
63 | * | ||
64 | * (1 << 0): Camera enable/disable, RW. | ||
65 | * (1 << 1): Bluetooth enable/disable, RW. | ||
66 | * (1 << 2): GPS enable/disable, RW. | ||
67 | * (1 << 3): WiFi enable/disable, RW. | ||
68 | * (1 << 4): WWAN (3G) enable/disalbe, RW. | ||
69 | * (1 << 5): Touchscreen enable/disable, Read Only. | ||
70 | */ | ||
71 | #define OT_EC_DEVICE_STATE_ADDRESS 0xD6 | ||
72 | |||
73 | #define OT_EC_CAMERA_MASK (1 << 0) | ||
74 | #define OT_EC_BT_MASK (1 << 1) | ||
75 | #define OT_EC_GPS_MASK (1 << 2) | ||
76 | #define OT_EC_WIFI_MASK (1 << 3) | ||
77 | #define OT_EC_WWAN_MASK (1 << 4) | ||
78 | #define OT_EC_TS_MASK (1 << 5) | ||
79 | |||
80 | /* | ||
81 | * This is the address in EC space and commands used to control LCD backlight: | ||
82 | * | ||
83 | * Two steps needed to change the LCD backlight: | ||
84 | * 1. write the backlight percentage into OT_EC_BL_BRIGHTNESS_ADDRESS; | ||
85 | * 2. write OT_EC_BL_CONTROL_ON_DATA into OT_EC_BL_CONTROL_ADDRESS. | ||
86 | * | ||
87 | * To read the LCD back light, just read out the value from | ||
88 | * OT_EC_BL_BRIGHTNESS_ADDRESS. | ||
89 | * | ||
90 | * LCD backlight brightness range: 0 - 100 (OT_EC_BL_BRIGHTNESS_MAX) | ||
91 | */ | ||
92 | #define OT_EC_BL_BRIGHTNESS_ADDRESS 0x44 | ||
93 | #define OT_EC_BL_BRIGHTNESS_MAX 100 | ||
94 | #define OT_EC_BL_CONTROL_ADDRESS 0x3A | ||
95 | #define OT_EC_BL_CONTROL_ON_DATA 0x1A | ||
96 | |||
97 | |||
98 | static int force; | ||
99 | module_param(force, bool, 0); | ||
100 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | ||
101 | |||
102 | static struct platform_device *oaktrail_device; | ||
103 | static struct backlight_device *oaktrail_bl_device; | ||
104 | static struct rfkill *bt_rfkill; | ||
105 | static struct rfkill *gps_rfkill; | ||
106 | static struct rfkill *wifi_rfkill; | ||
107 | static struct rfkill *wwan_rfkill; | ||
108 | |||
109 | |||
110 | /* rfkill */ | ||
111 | static int oaktrail_rfkill_set(void *data, bool blocked) | ||
112 | { | ||
113 | u8 value; | ||
114 | u8 result; | ||
115 | unsigned long radio = (unsigned long) data; | ||
116 | |||
117 | ec_read(OT_EC_DEVICE_STATE_ADDRESS, &result); | ||
118 | |||
119 | if (!blocked) | ||
120 | value = (u8) (result | radio); | ||
121 | else | ||
122 | value = (u8) (result & ~radio); | ||
123 | |||
124 | ec_write(OT_EC_DEVICE_STATE_ADDRESS, value); | ||
125 | |||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static const struct rfkill_ops oaktrail_rfkill_ops = { | ||
130 | .set_block = oaktrail_rfkill_set, | ||
131 | }; | ||
132 | |||
133 | static struct rfkill *oaktrail_rfkill_new(char *name, enum rfkill_type type, | ||
134 | unsigned long mask) | ||
135 | { | ||
136 | struct rfkill *rfkill_dev; | ||
137 | u8 value; | ||
138 | int err; | ||
139 | |||
140 | rfkill_dev = rfkill_alloc(name, &oaktrail_device->dev, type, | ||
141 | &oaktrail_rfkill_ops, (void *)mask); | ||
142 | if (!rfkill_dev) | ||
143 | return ERR_PTR(-ENOMEM); | ||
144 | |||
145 | ec_read(OT_EC_DEVICE_STATE_ADDRESS, &value); | ||
146 | rfkill_init_sw_state(rfkill_dev, (value & mask) != 1); | ||
147 | |||
148 | err = rfkill_register(rfkill_dev); | ||
149 | if (err) { | ||
150 | rfkill_destroy(rfkill_dev); | ||
151 | return ERR_PTR(err); | ||
152 | } | ||
153 | |||
154 | return rfkill_dev; | ||
155 | } | ||
156 | |||
157 | static inline void __oaktrail_rfkill_cleanup(struct rfkill *rf) | ||
158 | { | ||
159 | if (rf) { | ||
160 | rfkill_unregister(rf); | ||
161 | rfkill_destroy(rf); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | static void oaktrail_rfkill_cleanup(void) | ||
166 | { | ||
167 | __oaktrail_rfkill_cleanup(wifi_rfkill); | ||
168 | __oaktrail_rfkill_cleanup(bt_rfkill); | ||
169 | __oaktrail_rfkill_cleanup(gps_rfkill); | ||
170 | __oaktrail_rfkill_cleanup(wwan_rfkill); | ||
171 | } | ||
172 | |||
173 | static int oaktrail_rfkill_init(void) | ||
174 | { | ||
175 | int ret; | ||
176 | |||
177 | wifi_rfkill = oaktrail_rfkill_new("oaktrail-wifi", | ||
178 | RFKILL_TYPE_WLAN, | ||
179 | OT_EC_WIFI_MASK); | ||
180 | if (IS_ERR(wifi_rfkill)) { | ||
181 | ret = PTR_ERR(wifi_rfkill); | ||
182 | wifi_rfkill = NULL; | ||
183 | goto cleanup; | ||
184 | } | ||
185 | |||
186 | bt_rfkill = oaktrail_rfkill_new("oaktrail-bluetooth", | ||
187 | RFKILL_TYPE_BLUETOOTH, | ||
188 | OT_EC_BT_MASK); | ||
189 | if (IS_ERR(bt_rfkill)) { | ||
190 | ret = PTR_ERR(bt_rfkill); | ||
191 | bt_rfkill = NULL; | ||
192 | goto cleanup; | ||
193 | } | ||
194 | |||
195 | gps_rfkill = oaktrail_rfkill_new("oaktrail-gps", | ||
196 | RFKILL_TYPE_GPS, | ||
197 | OT_EC_GPS_MASK); | ||
198 | if (IS_ERR(gps_rfkill)) { | ||
199 | ret = PTR_ERR(gps_rfkill); | ||
200 | gps_rfkill = NULL; | ||
201 | goto cleanup; | ||
202 | } | ||
203 | |||
204 | wwan_rfkill = oaktrail_rfkill_new("oaktrail-wwan", | ||
205 | RFKILL_TYPE_WWAN, | ||
206 | OT_EC_WWAN_MASK); | ||
207 | if (IS_ERR(wwan_rfkill)) { | ||
208 | ret = PTR_ERR(wwan_rfkill); | ||
209 | wwan_rfkill = NULL; | ||
210 | goto cleanup; | ||
211 | } | ||
212 | |||
213 | return 0; | ||
214 | |||
215 | cleanup: | ||
216 | oaktrail_rfkill_cleanup(); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | |||
221 | /* backlight */ | ||
222 | static int get_backlight_brightness(struct backlight_device *b) | ||
223 | { | ||
224 | u8 value; | ||
225 | ec_read(OT_EC_BL_BRIGHTNESS_ADDRESS, &value); | ||
226 | |||
227 | return value; | ||
228 | } | ||
229 | |||
230 | static int set_backlight_brightness(struct backlight_device *b) | ||
231 | { | ||
232 | u8 percent = (u8) b->props.brightness; | ||
233 | if (percent < 0 || percent > OT_EC_BL_BRIGHTNESS_MAX) | ||
234 | return -EINVAL; | ||
235 | |||
236 | ec_write(OT_EC_BL_BRIGHTNESS_ADDRESS, percent); | ||
237 | ec_write(OT_EC_BL_CONTROL_ADDRESS, OT_EC_BL_CONTROL_ON_DATA); | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static const struct backlight_ops oaktrail_bl_ops = { | ||
243 | .get_brightness = get_backlight_brightness, | ||
244 | .update_status = set_backlight_brightness, | ||
245 | }; | ||
246 | |||
247 | static int oaktrail_backlight_init(void) | ||
248 | { | ||
249 | struct backlight_device *bd; | ||
250 | struct backlight_properties props; | ||
251 | |||
252 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
253 | props.type = BACKLIGHT_PLATFORM; | ||
254 | props.max_brightness = OT_EC_BL_BRIGHTNESS_MAX; | ||
255 | bd = backlight_device_register(DRIVER_NAME, | ||
256 | &oaktrail_device->dev, NULL, | ||
257 | &oaktrail_bl_ops, | ||
258 | &props); | ||
259 | |||
260 | if (IS_ERR(bd)) { | ||
261 | oaktrail_bl_device = NULL; | ||
262 | pr_warning("Unable to register backlight device\n"); | ||
263 | return PTR_ERR(bd); | ||
264 | } | ||
265 | |||
266 | oaktrail_bl_device = bd; | ||
267 | |||
268 | bd->props.brightness = get_backlight_brightness(bd); | ||
269 | bd->props.power = FB_BLANK_UNBLANK; | ||
270 | backlight_update_status(bd); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static void oaktrail_backlight_exit(void) | ||
276 | { | ||
277 | if (oaktrail_bl_device) | ||
278 | backlight_device_unregister(oaktrail_bl_device); | ||
279 | } | ||
280 | |||
281 | static int __devinit oaktrail_probe(struct platform_device *pdev) | ||
282 | { | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static int __devexit oaktrail_remove(struct platform_device *pdev) | ||
287 | { | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static struct platform_driver oaktrail_driver = { | ||
292 | .driver = { | ||
293 | .name = DRIVER_NAME, | ||
294 | .owner = THIS_MODULE, | ||
295 | }, | ||
296 | .probe = oaktrail_probe, | ||
297 | .remove = __devexit_p(oaktrail_remove) | ||
298 | }; | ||
299 | |||
300 | static int dmi_check_cb(const struct dmi_system_id *id) | ||
301 | { | ||
302 | pr_info("Identified model '%s'\n", id->ident); | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static struct dmi_system_id __initdata oaktrail_dmi_table[] = { | ||
307 | { | ||
308 | .ident = "OakTrail platform", | ||
309 | .matches = { | ||
310 | DMI_MATCH(DMI_PRODUCT_NAME, "OakTrail platform"), | ||
311 | }, | ||
312 | .callback = dmi_check_cb | ||
313 | }, | ||
314 | { } | ||
315 | }; | ||
316 | |||
317 | static int __init oaktrail_init(void) | ||
318 | { | ||
319 | int ret; | ||
320 | |||
321 | if (acpi_disabled) { | ||
322 | pr_err("ACPI needs to be enabled for this driver to work!\n"); | ||
323 | return -ENODEV; | ||
324 | } | ||
325 | |||
326 | if (!force && !dmi_check_system(oaktrail_dmi_table)) { | ||
327 | pr_err("Platform not recognized (You could try the module's force-parameter)"); | ||
328 | return -ENODEV; | ||
329 | } | ||
330 | |||
331 | ret = platform_driver_register(&oaktrail_driver); | ||
332 | if (ret) { | ||
333 | pr_warning("Unable to register platform driver\n"); | ||
334 | goto err_driver_reg; | ||
335 | } | ||
336 | |||
337 | oaktrail_device = platform_device_alloc(DRIVER_NAME, -1); | ||
338 | if (!oaktrail_device) { | ||
339 | pr_warning("Unable to allocate platform device\n"); | ||
340 | ret = -ENOMEM; | ||
341 | goto err_device_alloc; | ||
342 | } | ||
343 | |||
344 | ret = platform_device_add(oaktrail_device); | ||
345 | if (ret) { | ||
346 | pr_warning("Unable to add platform device\n"); | ||
347 | goto err_device_add; | ||
348 | } | ||
349 | |||
350 | if (!acpi_video_backlight_support()) { | ||
351 | ret = oaktrail_backlight_init(); | ||
352 | if (ret) | ||
353 | goto err_backlight; | ||
354 | |||
355 | } else | ||
356 | pr_info("Backlight controlled by ACPI video driver\n"); | ||
357 | |||
358 | ret = oaktrail_rfkill_init(); | ||
359 | if (ret) { | ||
360 | pr_warning("Setup rfkill failed\n"); | ||
361 | goto err_rfkill; | ||
362 | } | ||
363 | |||
364 | pr_info("Driver "DRIVER_VERSION" successfully loaded\n"); | ||
365 | return 0; | ||
366 | |||
367 | err_rfkill: | ||
368 | oaktrail_backlight_exit(); | ||
369 | err_backlight: | ||
370 | platform_device_del(oaktrail_device); | ||
371 | err_device_add: | ||
372 | platform_device_put(oaktrail_device); | ||
373 | err_device_alloc: | ||
374 | platform_driver_unregister(&oaktrail_driver); | ||
375 | err_driver_reg: | ||
376 | |||
377 | return ret; | ||
378 | } | ||
379 | |||
380 | static void __exit oaktrail_cleanup(void) | ||
381 | { | ||
382 | oaktrail_backlight_exit(); | ||
383 | oaktrail_rfkill_cleanup(); | ||
384 | platform_device_unregister(oaktrail_device); | ||
385 | platform_driver_unregister(&oaktrail_driver); | ||
386 | |||
387 | pr_info("Driver unloaded\n"); | ||
388 | } | ||
389 | |||
390 | module_init(oaktrail_init); | ||
391 | module_exit(oaktrail_cleanup); | ||
392 | |||
393 | MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)"); | ||
394 | MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras"); | ||
395 | MODULE_VERSION(DRIVER_VERSION); | ||
396 | MODULE_LICENSE("GPL"); | ||
397 | MODULE_ALIAS("dmi:*:svnIntelCorporation:pnOakTrailplatform:*"); | ||
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 5cdcff653918..1686c1e07d5d 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c | |||
@@ -19,6 +19,8 @@ | |||
19 | * Moorestown platform PMIC chip | 19 | * Moorestown platform PMIC chip |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #define pr_fmt(fmt) "%s: " fmt, __func__ | ||
23 | |||
22 | #include <linux/module.h> | 24 | #include <linux/module.h> |
23 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
24 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
@@ -29,7 +31,6 @@ | |||
29 | #include <linux/init.h> | 31 | #include <linux/init.h> |
30 | #include <linux/io.h> | 32 | #include <linux/io.h> |
31 | #include <linux/gpio.h> | 33 | #include <linux/gpio.h> |
32 | #include <linux/interrupt.h> | ||
33 | #include <asm/intel_scu_ipc.h> | 34 | #include <asm/intel_scu_ipc.h> |
34 | #include <linux/device.h> | 35 | #include <linux/device.h> |
35 | #include <linux/intel_pmic_gpio.h> | 36 | #include <linux/intel_pmic_gpio.h> |
@@ -61,23 +62,18 @@ enum pmic_gpio_register { | |||
61 | #define GPOSW_DOU 0x08 | 62 | #define GPOSW_DOU 0x08 |
62 | #define GPOSW_RDRV 0x30 | 63 | #define GPOSW_RDRV 0x30 |
63 | 64 | ||
65 | #define GPIO_UPDATE_TYPE 0x80000000 | ||
64 | 66 | ||
65 | #define NUM_GPIO 24 | 67 | #define NUM_GPIO 24 |
66 | 68 | ||
67 | struct pmic_gpio_irq { | ||
68 | spinlock_t lock; | ||
69 | u32 trigger[NUM_GPIO]; | ||
70 | u32 dirty; | ||
71 | struct work_struct work; | ||
72 | }; | ||
73 | |||
74 | |||
75 | struct pmic_gpio { | 69 | struct pmic_gpio { |
70 | struct mutex buslock; | ||
76 | struct gpio_chip chip; | 71 | struct gpio_chip chip; |
77 | struct pmic_gpio_irq irqtypes; | ||
78 | void *gpiointr; | 72 | void *gpiointr; |
79 | int irq; | 73 | int irq; |
80 | unsigned irq_base; | 74 | unsigned irq_base; |
75 | unsigned int update_type; | ||
76 | u32 trigger_type; | ||
81 | }; | 77 | }; |
82 | 78 | ||
83 | static void pmic_program_irqtype(int gpio, int type) | 79 | static void pmic_program_irqtype(int gpio, int type) |
@@ -93,42 +89,10 @@ static void pmic_program_irqtype(int gpio, int type) | |||
93 | intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10); | 89 | intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10); |
94 | }; | 90 | }; |
95 | 91 | ||
96 | static void pmic_irqtype_work(struct work_struct *work) | ||
97 | { | ||
98 | struct pmic_gpio_irq *t = | ||
99 | container_of(work, struct pmic_gpio_irq, work); | ||
100 | unsigned long flags; | ||
101 | int i; | ||
102 | u16 type; | ||
103 | |||
104 | spin_lock_irqsave(&t->lock, flags); | ||
105 | /* As we drop the lock, we may need multiple scans if we race the | ||
106 | pmic_irq_type function */ | ||
107 | while (t->dirty) { | ||
108 | /* | ||
109 | * For each pin that has the dirty bit set send an IPC | ||
110 | * message to configure the hardware via the PMIC | ||
111 | */ | ||
112 | for (i = 0; i < NUM_GPIO; i++) { | ||
113 | if (!(t->dirty & (1 << i))) | ||
114 | continue; | ||
115 | t->dirty &= ~(1 << i); | ||
116 | /* We can't trust the array entry or dirty | ||
117 | once the lock is dropped */ | ||
118 | type = t->trigger[i]; | ||
119 | spin_unlock_irqrestore(&t->lock, flags); | ||
120 | pmic_program_irqtype(i, type); | ||
121 | spin_lock_irqsave(&t->lock, flags); | ||
122 | } | ||
123 | } | ||
124 | spin_unlock_irqrestore(&t->lock, flags); | ||
125 | } | ||
126 | |||
127 | static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | 92 | static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) |
128 | { | 93 | { |
129 | if (offset > 8) { | 94 | if (offset > 8) { |
130 | printk(KERN_ERR | 95 | pr_err("only pin 0-7 support input\n"); |
131 | "%s: only pin 0-7 support input\n", __func__); | ||
132 | return -1;/* we only have 8 GPIO can use as input */ | 96 | return -1;/* we only have 8 GPIO can use as input */ |
133 | } | 97 | } |
134 | return intel_scu_ipc_update_register(GPIO0 + offset, | 98 | return intel_scu_ipc_update_register(GPIO0 + offset, |
@@ -142,19 +106,18 @@ static int pmic_gpio_direction_output(struct gpio_chip *chip, | |||
142 | 106 | ||
143 | if (offset < 8)/* it is GPIO */ | 107 | if (offset < 8)/* it is GPIO */ |
144 | rc = intel_scu_ipc_update_register(GPIO0 + offset, | 108 | rc = intel_scu_ipc_update_register(GPIO0 + offset, |
145 | GPIO_DRV | GPIO_DOU | GPIO_DIR, | 109 | GPIO_DRV | (value ? GPIO_DOU : 0), |
146 | GPIO_DRV | (value ? GPIO_DOU : 0)); | 110 | GPIO_DRV | GPIO_DOU | GPIO_DIR); |
147 | else if (offset < 16)/* it is GPOSW */ | 111 | else if (offset < 16)/* it is GPOSW */ |
148 | rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, | 112 | rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, |
149 | GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, | 113 | GPOSW_DRV | (value ? GPOSW_DOU : 0), |
150 | GPOSW_DRV | (value ? GPOSW_DOU : 0)); | 114 | GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); |
151 | else if (offset > 15 && offset < 24)/* it is GPO */ | 115 | else if (offset > 15 && offset < 24)/* it is GPO */ |
152 | rc = intel_scu_ipc_update_register(GPO, | 116 | rc = intel_scu_ipc_update_register(GPO, |
153 | 1 << (offset - 16), | 117 | value ? 1 << (offset - 16) : 0, |
154 | value ? 1 << (offset - 16) : 0); | 118 | 1 << (offset - 16)); |
155 | else { | 119 | else { |
156 | printk(KERN_ERR | 120 | pr_err("invalid PMIC GPIO pin %d!\n", offset); |
157 | "%s: invalid PMIC GPIO pin %d!\n", __func__, offset); | ||
158 | WARN_ON(1); | 121 | WARN_ON(1); |
159 | } | 122 | } |
160 | 123 | ||
@@ -179,37 +142,36 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | |||
179 | { | 142 | { |
180 | if (offset < 8)/* it is GPIO */ | 143 | if (offset < 8)/* it is GPIO */ |
181 | intel_scu_ipc_update_register(GPIO0 + offset, | 144 | intel_scu_ipc_update_register(GPIO0 + offset, |
182 | GPIO_DRV | GPIO_DOU, | 145 | GPIO_DRV | (value ? GPIO_DOU : 0), |
183 | GPIO_DRV | (value ? GPIO_DOU : 0)); | 146 | GPIO_DRV | GPIO_DOU); |
184 | else if (offset < 16)/* it is GPOSW */ | 147 | else if (offset < 16)/* it is GPOSW */ |
185 | intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, | 148 | intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, |
186 | GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, | 149 | GPOSW_DRV | (value ? GPOSW_DOU : 0), |
187 | GPOSW_DRV | (value ? GPOSW_DOU : 0)); | 150 | GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV); |
188 | else if (offset > 15 && offset < 24) /* it is GPO */ | 151 | else if (offset > 15 && offset < 24) /* it is GPO */ |
189 | intel_scu_ipc_update_register(GPO, | 152 | intel_scu_ipc_update_register(GPO, |
190 | 1 << (offset - 16), | 153 | value ? 1 << (offset - 16) : 0, |
191 | value ? 1 << (offset - 16) : 0); | 154 | 1 << (offset - 16)); |
192 | } | 155 | } |
193 | 156 | ||
194 | static int pmic_irq_type(unsigned irq, unsigned type) | 157 | /* |
158 | * This is called from genirq with pg->buslock locked and | ||
159 | * irq_desc->lock held. We can not access the scu bus here, so we | ||
160 | * store the change and update in the bus_sync_unlock() function below | ||
161 | */ | ||
162 | static int pmic_irq_type(struct irq_data *data, unsigned type) | ||
195 | { | 163 | { |
196 | struct pmic_gpio *pg = get_irq_chip_data(irq); | 164 | struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); |
197 | u32 gpio = irq - pg->irq_base; | 165 | u32 gpio = data->irq - pg->irq_base; |
198 | unsigned long flags; | ||
199 | 166 | ||
200 | if (gpio > pg->chip.ngpio) | 167 | if (gpio >= pg->chip.ngpio) |
201 | return -EINVAL; | 168 | return -EINVAL; |
202 | 169 | ||
203 | spin_lock_irqsave(&pg->irqtypes.lock, flags); | 170 | pg->trigger_type = type; |
204 | pg->irqtypes.trigger[gpio] = type; | 171 | pg->update_type = gpio | GPIO_UPDATE_TYPE; |
205 | pg->irqtypes.dirty |= (1 << gpio); | ||
206 | spin_unlock_irqrestore(&pg->irqtypes.lock, flags); | ||
207 | schedule_work(&pg->irqtypes.work); | ||
208 | return 0; | 172 | return 0; |
209 | } | 173 | } |
210 | 174 | ||
211 | |||
212 | |||
213 | static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | 175 | static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) |
214 | { | 176 | { |
215 | struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip); | 177 | struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip); |
@@ -217,35 +179,55 @@ static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | |||
217 | return pg->irq_base + offset; | 179 | return pg->irq_base + offset; |
218 | } | 180 | } |
219 | 181 | ||
220 | /* the gpiointr register is read-clear, so just do nothing. */ | 182 | static void pmic_bus_lock(struct irq_data *data) |
221 | static void pmic_irq_unmask(unsigned irq) | ||
222 | { | 183 | { |
223 | }; | 184 | struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); |
224 | 185 | ||
225 | static void pmic_irq_mask(unsigned irq) | 186 | mutex_lock(&pg->buslock); |
187 | } | ||
188 | |||
189 | static void pmic_bus_sync_unlock(struct irq_data *data) | ||
226 | { | 190 | { |
227 | }; | 191 | struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); |
192 | |||
193 | if (pg->update_type) { | ||
194 | unsigned int gpio = pg->update_type & ~GPIO_UPDATE_TYPE; | ||
195 | |||
196 | pmic_program_irqtype(gpio, pg->trigger_type); | ||
197 | pg->update_type = 0; | ||
198 | } | ||
199 | mutex_unlock(&pg->buslock); | ||
200 | } | ||
201 | |||
202 | /* the gpiointr register is read-clear, so just do nothing. */ | ||
203 | static void pmic_irq_unmask(struct irq_data *data) { } | ||
204 | |||
205 | static void pmic_irq_mask(struct irq_data *data) { } | ||
228 | 206 | ||
229 | static struct irq_chip pmic_irqchip = { | 207 | static struct irq_chip pmic_irqchip = { |
230 | .name = "PMIC-GPIO", | 208 | .name = "PMIC-GPIO", |
231 | .mask = pmic_irq_mask, | 209 | .irq_mask = pmic_irq_mask, |
232 | .unmask = pmic_irq_unmask, | 210 | .irq_unmask = pmic_irq_unmask, |
233 | .set_type = pmic_irq_type, | 211 | .irq_set_type = pmic_irq_type, |
212 | .irq_bus_lock = pmic_bus_lock, | ||
213 | .irq_bus_sync_unlock = pmic_bus_sync_unlock, | ||
234 | }; | 214 | }; |
235 | 215 | ||
236 | static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) | 216 | static irqreturn_t pmic_irq_handler(int irq, void *data) |
237 | { | 217 | { |
238 | struct pmic_gpio *pg = (struct pmic_gpio *)get_irq_data(irq); | 218 | struct pmic_gpio *pg = data; |
239 | u8 intsts = *((u8 *)pg->gpiointr + 4); | 219 | u8 intsts = *((u8 *)pg->gpiointr + 4); |
240 | int gpio; | 220 | int gpio; |
221 | irqreturn_t ret = IRQ_NONE; | ||
241 | 222 | ||
242 | for (gpio = 0; gpio < 8; gpio++) { | 223 | for (gpio = 0; gpio < 8; gpio++) { |
243 | if (intsts & (1 << gpio)) { | 224 | if (intsts & (1 << gpio)) { |
244 | pr_debug("pmic pin %d triggered\n", gpio); | 225 | pr_debug("pmic pin %d triggered\n", gpio); |
245 | generic_handle_irq(pg->irq_base + gpio); | 226 | generic_handle_irq(pg->irq_base + gpio); |
227 | ret = IRQ_HANDLED; | ||
246 | } | 228 | } |
247 | } | 229 | } |
248 | desc->chip->eoi(irq); | 230 | return ret; |
249 | } | 231 | } |
250 | 232 | ||
251 | static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) | 233 | static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) |
@@ -278,7 +260,7 @@ static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) | |||
278 | /* setting up SRAM mapping for GPIOINT register */ | 260 | /* setting up SRAM mapping for GPIOINT register */ |
279 | pg->gpiointr = ioremap_nocache(pdata->gpiointr, 8); | 261 | pg->gpiointr = ioremap_nocache(pdata->gpiointr, 8); |
280 | if (!pg->gpiointr) { | 262 | if (!pg->gpiointr) { |
281 | printk(KERN_ERR "%s: Can not map GPIOINT.\n", __func__); | 263 | pr_err("Can not map GPIOINT\n"); |
282 | retval = -EINVAL; | 264 | retval = -EINVAL; |
283 | goto err2; | 265 | goto err2; |
284 | } | 266 | } |
@@ -294,21 +276,27 @@ static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) | |||
294 | pg->chip.can_sleep = 1; | 276 | pg->chip.can_sleep = 1; |
295 | pg->chip.dev = dev; | 277 | pg->chip.dev = dev; |
296 | 278 | ||
297 | INIT_WORK(&pg->irqtypes.work, pmic_irqtype_work); | 279 | mutex_init(&pg->buslock); |
298 | spin_lock_init(&pg->irqtypes.lock); | ||
299 | 280 | ||
300 | pg->chip.dev = dev; | 281 | pg->chip.dev = dev; |
301 | retval = gpiochip_add(&pg->chip); | 282 | retval = gpiochip_add(&pg->chip); |
302 | if (retval) { | 283 | if (retval) { |
303 | printk(KERN_ERR "%s: Can not add pmic gpio chip.\n", __func__); | 284 | pr_err("Can not add pmic gpio chip\n"); |
304 | goto err; | 285 | goto err; |
305 | } | 286 | } |
306 | set_irq_data(pg->irq, pg); | 287 | |
307 | set_irq_chained_handler(pg->irq, pmic_irq_handler); | 288 | retval = request_irq(pg->irq, pmic_irq_handler, 0, "pmic", pg); |
289 | if (retval) { | ||
290 | pr_warn("Interrupt request failed\n"); | ||
291 | goto err; | ||
292 | } | ||
293 | |||
308 | for (i = 0; i < 8; i++) { | 294 | for (i = 0; i < 8; i++) { |
309 | set_irq_chip_and_handler_name(i + pg->irq_base, &pmic_irqchip, | 295 | irq_set_chip_and_handler_name(i + pg->irq_base, |
310 | handle_simple_irq, "demux"); | 296 | &pmic_irqchip, |
311 | set_irq_chip_data(i + pg->irq_base, pg); | 297 | handle_simple_irq, |
298 | "demux"); | ||
299 | irq_set_chip_data(i + pg->irq_base, pg); | ||
312 | } | 300 | } |
313 | return 0; | 301 | return 0; |
314 | err: | 302 | err: |
diff --git a/drivers/platform/x86/intel_rar_register.c b/drivers/platform/x86/intel_rar_register.c index 2b11a33325e6..bde47e9080cd 100644 --- a/drivers/platform/x86/intel_rar_register.c +++ b/drivers/platform/x86/intel_rar_register.c | |||
@@ -485,7 +485,7 @@ EXPORT_SYMBOL(rar_lock); | |||
485 | * | 485 | * |
486 | * The register_rar function is to used by other device drivers | 486 | * The register_rar function is to used by other device drivers |
487 | * to ensure that this driver is ready. As we cannot be sure of | 487 | * to ensure that this driver is ready. As we cannot be sure of |
488 | * the compile/execute order of drivers in ther kernel, it is | 488 | * the compile/execute order of drivers in the kernel, it is |
489 | * best to give this driver a callback function to call when | 489 | * best to give this driver a callback function to call when |
490 | * it is ready to give out addresses. The callback function | 490 | * it is ready to give out addresses. The callback function |
491 | * would have those steps that continue the initialization of | 491 | * would have those steps that continue the initialization of |
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 6abe18e638e9..940accbe28d3 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * as published by the Free Software Foundation; version 2 | 9 | * as published by the Free Software Foundation; version 2 |
10 | * of the License. | 10 | * of the License. |
11 | * | 11 | * |
12 | * SCU runing in ARC processor communicates with other entity running in IA | 12 | * SCU running in ARC processor communicates with other entity running in IA |
13 | * core through IPC mechanism which in turn messaging between IA core ad SCU. | 13 | * core through IPC mechanism which in turn messaging between IA core ad SCU. |
14 | * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and | 14 | * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and |
15 | * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with | 15 | * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with |
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/pm.h> | 23 | #include <linux/pm.h> |
24 | #include <linux/pci.h> | 24 | #include <linux/pci.h> |
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <linux/sfi.h> | ||
26 | #include <asm/mrst.h> | 27 | #include <asm/mrst.h> |
27 | #include <asm/intel_scu_ipc.h> | 28 | #include <asm/intel_scu_ipc.h> |
28 | 29 | ||
@@ -159,7 +160,7 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) | |||
159 | { | 160 | { |
160 | int i, nc, bytes, d; | 161 | int i, nc, bytes, d; |
161 | u32 offset = 0; | 162 | u32 offset = 0; |
162 | u32 err = 0; | 163 | int err; |
163 | u8 cbuf[IPC_WWBUF_SIZE] = { }; | 164 | u8 cbuf[IPC_WWBUF_SIZE] = { }; |
164 | u32 *wbuf = (u32 *)&cbuf; | 165 | u32 *wbuf = (u32 *)&cbuf; |
165 | 166 | ||
@@ -402,7 +403,7 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register); | |||
402 | */ | 403 | */ |
403 | int intel_scu_ipc_simple_command(int cmd, int sub) | 404 | int intel_scu_ipc_simple_command(int cmd, int sub) |
404 | { | 405 | { |
405 | u32 err = 0; | 406 | int err; |
406 | 407 | ||
407 | mutex_lock(&ipclock); | 408 | mutex_lock(&ipclock); |
408 | if (ipcdev.pdev == NULL) { | 409 | if (ipcdev.pdev == NULL) { |
@@ -432,8 +433,7 @@ EXPORT_SYMBOL(intel_scu_ipc_simple_command); | |||
432 | int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, | 433 | int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, |
433 | u32 *out, int outlen) | 434 | u32 *out, int outlen) |
434 | { | 435 | { |
435 | u32 err = 0; | 436 | int i, err; |
436 | int i = 0; | ||
437 | 437 | ||
438 | mutex_lock(&ipclock); | 438 | mutex_lock(&ipclock); |
439 | if (ipcdev.pdev == NULL) { | 439 | if (ipcdev.pdev == NULL) { |
@@ -495,7 +495,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) | |||
495 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); | 495 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); |
496 | 496 | ||
497 | mutex_unlock(&ipclock); | 497 | mutex_unlock(&ipclock); |
498 | return -1; | 498 | return -EIO; |
499 | } | 499 | } |
500 | mutex_unlock(&ipclock); | 500 | mutex_unlock(&ipclock); |
501 | return 0; | 501 | return 0; |
@@ -640,7 +640,7 @@ update_end: | |||
640 | 640 | ||
641 | if (status == IPC_FW_UPDATE_SUCCESS) | 641 | if (status == IPC_FW_UPDATE_SUCCESS) |
642 | return 0; | 642 | return 0; |
643 | return -1; | 643 | return -EIO; |
644 | } | 644 | } |
645 | EXPORT_SYMBOL(intel_scu_ipc_fw_update); | 645 | EXPORT_SYMBOL(intel_scu_ipc_fw_update); |
646 | 646 | ||
@@ -698,6 +698,9 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
698 | iounmap(ipcdev.ipc_base); | 698 | iounmap(ipcdev.ipc_base); |
699 | return -ENOMEM; | 699 | return -ENOMEM; |
700 | } | 700 | } |
701 | |||
702 | intel_scu_devices_create(); | ||
703 | |||
701 | return 0; | 704 | return 0; |
702 | } | 705 | } |
703 | 706 | ||
@@ -719,6 +722,7 @@ static void ipc_remove(struct pci_dev *pdev) | |||
719 | iounmap(ipcdev.ipc_base); | 722 | iounmap(ipcdev.ipc_base); |
720 | iounmap(ipcdev.i2c_base); | 723 | iounmap(ipcdev.i2c_base); |
721 | ipcdev.pdev = NULL; | 724 | ipcdev.pdev = NULL; |
725 | intel_scu_devices_destroy(); | ||
722 | } | 726 | } |
723 | 727 | ||
724 | static const struct pci_device_id pci_ids[] = { | 728 | static const struct pci_device_id pci_ids[] = { |
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c new file mode 100644 index 000000000000..b93a03259c16 --- /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/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 7e9bb6df9d39..3ff629df9f01 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c | |||
@@ -51,6 +51,8 @@ | |||
51 | * laptop as MSI S270. YMMV. | 51 | * laptop as MSI S270. YMMV. |
52 | */ | 52 | */ |
53 | 53 | ||
54 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
55 | |||
54 | #include <linux/module.h> | 56 | #include <linux/module.h> |
55 | #include <linux/kernel.h> | 57 | #include <linux/kernel.h> |
56 | #include <linux/init.h> | 58 | #include <linux/init.h> |
@@ -60,6 +62,8 @@ | |||
60 | #include <linux/platform_device.h> | 62 | #include <linux/platform_device.h> |
61 | #include <linux/rfkill.h> | 63 | #include <linux/rfkill.h> |
62 | #include <linux/i8042.h> | 64 | #include <linux/i8042.h> |
65 | #include <linux/input.h> | ||
66 | #include <linux/input/sparse-keymap.h> | ||
63 | 67 | ||
64 | #define MSI_DRIVER_VERSION "0.5" | 68 | #define MSI_DRIVER_VERSION "0.5" |
65 | 69 | ||
@@ -78,6 +82,9 @@ | |||
78 | #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d | 82 | #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d |
79 | #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) | 83 | #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) |
80 | 84 | ||
85 | #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 | ||
86 | #define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) | ||
87 | |||
81 | static int msi_laptop_resume(struct platform_device *device); | 88 | static int msi_laptop_resume(struct platform_device *device); |
82 | 89 | ||
83 | #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f | 90 | #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f |
@@ -90,6 +97,14 @@ static int auto_brightness; | |||
90 | module_param(auto_brightness, int, 0); | 97 | module_param(auto_brightness, int, 0); |
91 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); | 98 | MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); |
92 | 99 | ||
100 | static const struct key_entry msi_laptop_keymap[] = { | ||
101 | {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */ | ||
102 | {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */ | ||
103 | {KE_END, 0} | ||
104 | }; | ||
105 | |||
106 | static struct input_dev *msi_laptop_input_dev; | ||
107 | |||
93 | static bool old_ec_model; | 108 | static bool old_ec_model; |
94 | static int wlan_s, bluetooth_s, threeg_s; | 109 | static int wlan_s, bluetooth_s, threeg_s; |
95 | static int threeg_exists; | 110 | static int threeg_exists; |
@@ -120,7 +135,7 @@ static int set_lcd_level(int level) | |||
120 | buf[1] = (u8) (level*31); | 135 | buf[1] = (u8) (level*31); |
121 | 136 | ||
122 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), | 137 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), |
123 | NULL, 0, 1); | 138 | NULL, 0); |
124 | } | 139 | } |
125 | 140 | ||
126 | static int get_lcd_level(void) | 141 | static int get_lcd_level(void) |
@@ -129,7 +144,7 @@ static int get_lcd_level(void) | |||
129 | int result; | 144 | int result; |
130 | 145 | ||
131 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, | 146 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, |
132 | &rdata, 1, 1); | 147 | &rdata, 1); |
133 | if (result < 0) | 148 | if (result < 0) |
134 | return result; | 149 | return result; |
135 | 150 | ||
@@ -142,7 +157,7 @@ static int get_auto_brightness(void) | |||
142 | int result; | 157 | int result; |
143 | 158 | ||
144 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, | 159 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, |
145 | &rdata, 1, 1); | 160 | &rdata, 1); |
146 | if (result < 0) | 161 | if (result < 0) |
147 | return result; | 162 | return result; |
148 | 163 | ||
@@ -157,7 +172,7 @@ static int set_auto_brightness(int enable) | |||
157 | wdata[0] = 4; | 172 | wdata[0] = 4; |
158 | 173 | ||
159 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, | 174 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, |
160 | &rdata, 1, 1); | 175 | &rdata, 1); |
161 | if (result < 0) | 176 | if (result < 0) |
162 | return result; | 177 | return result; |
163 | 178 | ||
@@ -165,7 +180,7 @@ static int set_auto_brightness(int enable) | |||
165 | wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); | 180 | wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); |
166 | 181 | ||
167 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, | 182 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, |
168 | NULL, 0, 1); | 183 | NULL, 0); |
169 | } | 184 | } |
170 | 185 | ||
171 | static ssize_t set_device_state(const char *buf, size_t count, u8 mask) | 186 | static ssize_t set_device_state(const char *buf, size_t count, u8 mask) |
@@ -202,7 +217,7 @@ static int get_wireless_state(int *wlan, int *bluetooth) | |||
202 | u8 wdata = 0, rdata; | 217 | u8 wdata = 0, rdata; |
203 | int result; | 218 | int result; |
204 | 219 | ||
205 | result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1); | 220 | result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1); |
206 | if (result < 0) | 221 | if (result < 0) |
207 | return -1; | 222 | return -1; |
208 | 223 | ||
@@ -432,8 +447,7 @@ static struct platform_device *msipf_device; | |||
432 | 447 | ||
433 | static int dmi_check_cb(const struct dmi_system_id *id) | 448 | static int dmi_check_cb(const struct dmi_system_id *id) |
434 | { | 449 | { |
435 | printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n", | 450 | pr_info("Identified laptop model '%s'\n", id->ident); |
436 | id->ident); | ||
437 | return 1; | 451 | return 1; |
438 | } | 452 | } |
439 | 453 | ||
@@ -605,6 +619,21 @@ static void msi_update_rfkill(struct work_struct *ignored) | |||
605 | } | 619 | } |
606 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); | 620 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); |
607 | 621 | ||
622 | static void msi_send_touchpad_key(struct work_struct *ignored) | ||
623 | { | ||
624 | u8 rdata; | ||
625 | int result; | ||
626 | |||
627 | result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata); | ||
628 | if (result < 0) | ||
629 | return; | ||
630 | |||
631 | sparse_keymap_report_event(msi_laptop_input_dev, | ||
632 | (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ? | ||
633 | KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true); | ||
634 | } | ||
635 | static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); | ||
636 | |||
608 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | 637 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, |
609 | struct serio *port) | 638 | struct serio *port) |
610 | { | 639 | { |
@@ -613,12 +642,17 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | |||
613 | if (str & 0x20) | 642 | if (str & 0x20) |
614 | return false; | 643 | return false; |
615 | 644 | ||
616 | /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/ | 645 | /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/ |
617 | if (unlikely(data == 0xe0)) { | 646 | if (unlikely(data == 0xe0)) { |
618 | extended = true; | 647 | extended = true; |
619 | return false; | 648 | return false; |
620 | } else if (unlikely(extended)) { | 649 | } else if (unlikely(extended)) { |
650 | extended = false; | ||
621 | switch (data) { | 651 | switch (data) { |
652 | case 0xE4: | ||
653 | schedule_delayed_work(&msi_touchpad_work, | ||
654 | round_jiffies_relative(0.5 * HZ)); | ||
655 | break; | ||
622 | case 0x54: | 656 | case 0x54: |
623 | case 0x62: | 657 | case 0x62: |
624 | case 0x76: | 658 | case 0x76: |
@@ -626,7 +660,6 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | |||
626 | round_jiffies_relative(0.5 * HZ)); | 660 | round_jiffies_relative(0.5 * HZ)); |
627 | break; | 661 | break; |
628 | } | 662 | } |
629 | extended = false; | ||
630 | } | 663 | } |
631 | 664 | ||
632 | return false; | 665 | return false; |
@@ -731,7 +764,43 @@ static int msi_laptop_resume(struct platform_device *device) | |||
731 | return 0; | 764 | return 0; |
732 | } | 765 | } |
733 | 766 | ||
734 | static int load_scm_model_init(struct platform_device *sdev) | 767 | static int __init msi_laptop_input_setup(void) |
768 | { | ||
769 | int err; | ||
770 | |||
771 | msi_laptop_input_dev = input_allocate_device(); | ||
772 | if (!msi_laptop_input_dev) | ||
773 | return -ENOMEM; | ||
774 | |||
775 | msi_laptop_input_dev->name = "MSI Laptop hotkeys"; | ||
776 | msi_laptop_input_dev->phys = "msi-laptop/input0"; | ||
777 | msi_laptop_input_dev->id.bustype = BUS_HOST; | ||
778 | |||
779 | err = sparse_keymap_setup(msi_laptop_input_dev, | ||
780 | msi_laptop_keymap, NULL); | ||
781 | if (err) | ||
782 | goto err_free_dev; | ||
783 | |||
784 | err = input_register_device(msi_laptop_input_dev); | ||
785 | if (err) | ||
786 | goto err_free_keymap; | ||
787 | |||
788 | return 0; | ||
789 | |||
790 | err_free_keymap: | ||
791 | sparse_keymap_free(msi_laptop_input_dev); | ||
792 | err_free_dev: | ||
793 | input_free_device(msi_laptop_input_dev); | ||
794 | return err; | ||
795 | } | ||
796 | |||
797 | static void msi_laptop_input_destroy(void) | ||
798 | { | ||
799 | sparse_keymap_free(msi_laptop_input_dev); | ||
800 | input_unregister_device(msi_laptop_input_dev); | ||
801 | } | ||
802 | |||
803 | static int __init load_scm_model_init(struct platform_device *sdev) | ||
735 | { | 804 | { |
736 | u8 data; | 805 | u8 data; |
737 | int result; | 806 | int result; |
@@ -759,16 +828,23 @@ static int load_scm_model_init(struct platform_device *sdev) | |||
759 | if (result < 0) | 828 | if (result < 0) |
760 | goto fail_rfkill; | 829 | goto fail_rfkill; |
761 | 830 | ||
831 | /* setup input device */ | ||
832 | result = msi_laptop_input_setup(); | ||
833 | if (result) | ||
834 | goto fail_input; | ||
835 | |||
762 | result = i8042_install_filter(msi_laptop_i8042_filter); | 836 | result = i8042_install_filter(msi_laptop_i8042_filter); |
763 | if (result) { | 837 | if (result) { |
764 | printk(KERN_ERR | 838 | pr_err("Unable to install key filter\n"); |
765 | "msi-laptop: Unable to install key filter\n"); | ||
766 | goto fail_filter; | 839 | goto fail_filter; |
767 | } | 840 | } |
768 | 841 | ||
769 | return 0; | 842 | return 0; |
770 | 843 | ||
771 | fail_filter: | 844 | fail_filter: |
845 | msi_laptop_input_destroy(); | ||
846 | |||
847 | fail_input: | ||
772 | rfkill_cleanup(); | 848 | rfkill_cleanup(); |
773 | 849 | ||
774 | fail_rfkill: | 850 | fail_rfkill: |
@@ -799,11 +875,11 @@ static int __init msi_init(void) | |||
799 | /* Register backlight stuff */ | 875 | /* Register backlight stuff */ |
800 | 876 | ||
801 | if (acpi_video_backlight_support()) { | 877 | if (acpi_video_backlight_support()) { |
802 | printk(KERN_INFO "MSI: Brightness ignored, must be controlled " | 878 | pr_info("Brightness ignored, must be controlled by ACPI video driver\n"); |
803 | "by ACPI video driver\n"); | ||
804 | } else { | 879 | } else { |
805 | struct backlight_properties props; | 880 | struct backlight_properties props; |
806 | memset(&props, 0, sizeof(struct backlight_properties)); | 881 | memset(&props, 0, sizeof(struct backlight_properties)); |
882 | props.type = BACKLIGHT_PLATFORM; | ||
807 | props.max_brightness = MSI_LCD_LEVEL_MAX - 1; | 883 | props.max_brightness = MSI_LCD_LEVEL_MAX - 1; |
808 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, | 884 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, |
809 | NULL, &msibl_ops, | 885 | NULL, &msibl_ops, |
@@ -853,7 +929,7 @@ static int __init msi_init(void) | |||
853 | if (auto_brightness != 2) | 929 | if (auto_brightness != 2) |
854 | set_auto_brightness(auto_brightness); | 930 | set_auto_brightness(auto_brightness); |
855 | 931 | ||
856 | printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n"); | 932 | pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n"); |
857 | 933 | ||
858 | return 0; | 934 | return 0; |
859 | 935 | ||
@@ -885,6 +961,7 @@ static void __exit msi_cleanup(void) | |||
885 | { | 961 | { |
886 | if (load_scm_model) { | 962 | if (load_scm_model) { |
887 | i8042_remove_filter(msi_laptop_i8042_filter); | 963 | i8042_remove_filter(msi_laptop_i8042_filter); |
964 | msi_laptop_input_destroy(); | ||
888 | cancel_delayed_work_sync(&msi_rfkill_work); | 965 | cancel_delayed_work_sync(&msi_rfkill_work); |
889 | rfkill_cleanup(); | 966 | rfkill_cleanup(); |
890 | } | 967 | } |
@@ -900,7 +977,7 @@ static void __exit msi_cleanup(void) | |||
900 | if (auto_brightness != 2) | 977 | if (auto_brightness != 2) |
901 | set_auto_brightness(1); | 978 | set_auto_brightness(1); |
902 | 979 | ||
903 | printk(KERN_INFO "msi-laptop: driver unloaded.\n"); | 980 | pr_info("driver unloaded\n"); |
904 | } | 981 | } |
905 | 982 | ||
906 | module_init(msi_init); | 983 | module_init(msi_init); |
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index 42a5469a2459..c832e3356cd6 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c | |||
@@ -20,6 +20,7 @@ | |||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
23 | 24 | ||
24 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
25 | #include <linux/input.h> | 26 | #include <linux/input.h> |
@@ -36,23 +37,22 @@ MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45"); | |||
36 | MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"); | 37 | MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"); |
37 | 38 | ||
38 | #define DRV_NAME "msi-wmi" | 39 | #define DRV_NAME "msi-wmi" |
39 | #define DRV_PFX DRV_NAME ": " | ||
40 | 40 | ||
41 | #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" | 41 | #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" |
42 | #define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" | 42 | #define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" |
43 | 43 | ||
44 | #define dprintk(msg...) pr_debug(DRV_PFX msg) | 44 | #define SCANCODE_BASE 0xD0 |
45 | 45 | #define MSI_WMI_BRIGHTNESSUP SCANCODE_BASE | |
46 | #define KEYCODE_BASE 0xD0 | 46 | #define MSI_WMI_BRIGHTNESSDOWN (SCANCODE_BASE + 1) |
47 | #define MSI_WMI_BRIGHTNESSUP KEYCODE_BASE | 47 | #define MSI_WMI_VOLUMEUP (SCANCODE_BASE + 2) |
48 | #define MSI_WMI_BRIGHTNESSDOWN (KEYCODE_BASE + 1) | 48 | #define MSI_WMI_VOLUMEDOWN (SCANCODE_BASE + 3) |
49 | #define MSI_WMI_VOLUMEUP (KEYCODE_BASE + 2) | 49 | #define MSI_WMI_MUTE (SCANCODE_BASE + 4) |
50 | #define MSI_WMI_VOLUMEDOWN (KEYCODE_BASE + 3) | ||
51 | static struct key_entry msi_wmi_keymap[] = { | 50 | static struct key_entry msi_wmi_keymap[] = { |
52 | { KE_KEY, MSI_WMI_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} }, | 51 | { KE_KEY, MSI_WMI_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} }, |
53 | { KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} }, | 52 | { KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} }, |
54 | { KE_KEY, MSI_WMI_VOLUMEUP, {KEY_VOLUMEUP} }, | 53 | { KE_KEY, MSI_WMI_VOLUMEUP, {KEY_VOLUMEUP} }, |
55 | { KE_KEY, MSI_WMI_VOLUMEDOWN, {KEY_VOLUMEDOWN} }, | 54 | { KE_KEY, MSI_WMI_VOLUMEDOWN, {KEY_VOLUMEDOWN} }, |
55 | { KE_KEY, MSI_WMI_MUTE, {KEY_MUTE} }, | ||
56 | { KE_END, 0} | 56 | { KE_END, 0} |
57 | }; | 57 | }; |
58 | static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; | 58 | static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; |
@@ -76,7 +76,7 @@ static int msi_wmi_query_block(int instance, int *ret) | |||
76 | 76 | ||
77 | if (!obj || obj->type != ACPI_TYPE_INTEGER) { | 77 | if (!obj || obj->type != ACPI_TYPE_INTEGER) { |
78 | if (obj) { | 78 | if (obj) { |
79 | printk(KERN_ERR DRV_PFX "query block returned object " | 79 | pr_err("query block returned object " |
80 | "type: %d - buffer length:%d\n", obj->type, | 80 | "type: %d - buffer length:%d\n", obj->type, |
81 | obj->type == ACPI_TYPE_BUFFER ? | 81 | obj->type == ACPI_TYPE_BUFFER ? |
82 | obj->buffer.length : 0); | 82 | obj->buffer.length : 0); |
@@ -95,8 +95,8 @@ static int msi_wmi_set_block(int instance, int value) | |||
95 | 95 | ||
96 | struct acpi_buffer input = { sizeof(int), &value }; | 96 | struct acpi_buffer input = { sizeof(int), &value }; |
97 | 97 | ||
98 | dprintk("Going to set block of instance: %d - value: %d\n", | 98 | pr_debug("Going to set block of instance: %d - value: %d\n", |
99 | instance, value); | 99 | instance, value); |
100 | 100 | ||
101 | status = wmi_set_block(MSIWMI_BIOS_GUID, instance, &input); | 101 | status = wmi_set_block(MSIWMI_BIOS_GUID, instance, &input); |
102 | 102 | ||
@@ -110,20 +110,19 @@ static int bl_get(struct backlight_device *bd) | |||
110 | /* Instance 1 is "get backlight", cmp with DSDT */ | 110 | /* Instance 1 is "get backlight", cmp with DSDT */ |
111 | err = msi_wmi_query_block(1, &ret); | 111 | err = msi_wmi_query_block(1, &ret); |
112 | if (err) { | 112 | if (err) { |
113 | printk(KERN_ERR DRV_PFX "Could not query backlight: %d\n", err); | 113 | pr_err("Could not query backlight: %d\n", err); |
114 | return -EINVAL; | 114 | return -EINVAL; |
115 | } | 115 | } |
116 | dprintk("Get: Query block returned: %d\n", ret); | 116 | pr_debug("Get: Query block returned: %d\n", ret); |
117 | for (level = 0; level < ARRAY_SIZE(backlight_map); level++) { | 117 | for (level = 0; level < ARRAY_SIZE(backlight_map); level++) { |
118 | if (backlight_map[level] == ret) { | 118 | if (backlight_map[level] == ret) { |
119 | dprintk("Current backlight level: 0x%X - index: %d\n", | 119 | pr_debug("Current backlight level: 0x%X - index: %d\n", |
120 | backlight_map[level], level); | 120 | backlight_map[level], level); |
121 | break; | 121 | break; |
122 | } | 122 | } |
123 | } | 123 | } |
124 | if (level == ARRAY_SIZE(backlight_map)) { | 124 | if (level == ARRAY_SIZE(backlight_map)) { |
125 | printk(KERN_ERR DRV_PFX "get: Invalid brightness value: 0x%X\n", | 125 | pr_err("get: Invalid brightness value: 0x%X\n", ret); |
126 | ret); | ||
127 | return -EINVAL; | 126 | return -EINVAL; |
128 | } | 127 | } |
129 | return level; | 128 | return level; |
@@ -154,7 +153,7 @@ static void msi_wmi_notify(u32 value, void *context) | |||
154 | 153 | ||
155 | status = wmi_get_event_data(value, &response); | 154 | status = wmi_get_event_data(value, &response); |
156 | if (status != AE_OK) { | 155 | if (status != AE_OK) { |
157 | printk(KERN_INFO DRV_PFX "bad event status 0x%x\n", status); | 156 | pr_info("bad event status 0x%x\n", status); |
158 | return; | 157 | return; |
159 | } | 158 | } |
160 | 159 | ||
@@ -162,40 +161,39 @@ static void msi_wmi_notify(u32 value, void *context) | |||
162 | 161 | ||
163 | if (obj && obj->type == ACPI_TYPE_INTEGER) { | 162 | if (obj && obj->type == ACPI_TYPE_INTEGER) { |
164 | int eventcode = obj->integer.value; | 163 | int eventcode = obj->integer.value; |
165 | dprintk("Eventcode: 0x%x\n", eventcode); | 164 | pr_debug("Eventcode: 0x%x\n", eventcode); |
166 | key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev, | 165 | key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev, |
167 | eventcode); | 166 | eventcode); |
168 | if (key) { | 167 | if (key) { |
169 | ktime_t diff; | 168 | ktime_t diff; |
170 | cur = ktime_get_real(); | 169 | cur = ktime_get_real(); |
171 | diff = ktime_sub(cur, last_pressed[key->code - | 170 | diff = ktime_sub(cur, last_pressed[key->code - |
172 | KEYCODE_BASE]); | 171 | SCANCODE_BASE]); |
173 | /* Ignore event if the same event happened in a 50 ms | 172 | /* Ignore event if the same event happened in a 50 ms |
174 | timeframe -> Key press may result in 10-20 GPEs */ | 173 | timeframe -> Key press may result in 10-20 GPEs */ |
175 | if (ktime_to_us(diff) < 1000 * 50) { | 174 | if (ktime_to_us(diff) < 1000 * 50) { |
176 | dprintk("Suppressed key event 0x%X - " | 175 | pr_debug("Suppressed key event 0x%X - " |
177 | "Last press was %lld us ago\n", | 176 | "Last press was %lld us ago\n", |
178 | key->code, ktime_to_us(diff)); | 177 | key->code, ktime_to_us(diff)); |
179 | return; | 178 | return; |
180 | } | 179 | } |
181 | last_pressed[key->code - KEYCODE_BASE] = cur; | 180 | last_pressed[key->code - SCANCODE_BASE] = cur; |
182 | 181 | ||
183 | if (key->type == KE_KEY && | 182 | if (key->type == KE_KEY && |
184 | /* Brightness is served via acpi video driver */ | 183 | /* Brightness is served via acpi video driver */ |
185 | (!acpi_video_backlight_support() || | 184 | (!acpi_video_backlight_support() || |
186 | (key->code != MSI_WMI_BRIGHTNESSUP && | 185 | (key->code != MSI_WMI_BRIGHTNESSUP && |
187 | key->code != MSI_WMI_BRIGHTNESSDOWN))) { | 186 | key->code != MSI_WMI_BRIGHTNESSDOWN))) { |
188 | dprintk("Send key: 0x%X - " | 187 | pr_debug("Send key: 0x%X - " |
189 | "Input layer keycode: %d\n", key->code, | 188 | "Input layer keycode: %d\n", |
190 | key->keycode); | 189 | key->code, key->keycode); |
191 | sparse_keymap_report_entry(msi_wmi_input_dev, | 190 | sparse_keymap_report_entry(msi_wmi_input_dev, |
192 | key, 1, true); | 191 | key, 1, true); |
193 | } | 192 | } |
194 | } else | 193 | } else |
195 | printk(KERN_INFO "Unknown key pressed - %x\n", | 194 | pr_info("Unknown key pressed - %x\n", eventcode); |
196 | eventcode); | ||
197 | } else | 195 | } else |
198 | printk(KERN_INFO DRV_PFX "Unknown event received\n"); | 196 | pr_info("Unknown event received\n"); |
199 | kfree(response.pointer); | 197 | kfree(response.pointer); |
200 | } | 198 | } |
201 | 199 | ||
@@ -236,8 +234,7 @@ static int __init msi_wmi_init(void) | |||
236 | int err; | 234 | int err; |
237 | 235 | ||
238 | if (!wmi_has_guid(MSIWMI_EVENT_GUID)) { | 236 | if (!wmi_has_guid(MSIWMI_EVENT_GUID)) { |
239 | printk(KERN_ERR | 237 | pr_err("This machine doesn't have MSI-hotkeys through WMI\n"); |
240 | "This machine doesn't have MSI-hotkeys through WMI\n"); | ||
241 | return -ENODEV; | 238 | return -ENODEV; |
242 | } | 239 | } |
243 | err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, | 240 | err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, |
@@ -252,6 +249,7 @@ static int __init msi_wmi_init(void) | |||
252 | if (!acpi_video_backlight_support()) { | 249 | if (!acpi_video_backlight_support()) { |
253 | struct backlight_properties props; | 250 | struct backlight_properties props; |
254 | memset(&props, 0, sizeof(struct backlight_properties)); | 251 | memset(&props, 0, sizeof(struct backlight_properties)); |
252 | props.type = BACKLIGHT_PLATFORM; | ||
255 | props.max_brightness = ARRAY_SIZE(backlight_map) - 1; | 253 | props.max_brightness = ARRAY_SIZE(backlight_map) - 1; |
256 | backlight = backlight_device_register(DRV_NAME, NULL, NULL, | 254 | backlight = backlight_device_register(DRV_NAME, NULL, NULL, |
257 | &msi_backlight_ops, | 255 | &msi_backlight_ops, |
@@ -267,7 +265,7 @@ static int __init msi_wmi_init(void) | |||
267 | 265 | ||
268 | backlight->props.brightness = err; | 266 | backlight->props.brightness = err; |
269 | } | 267 | } |
270 | dprintk("Event handler installed\n"); | 268 | pr_debug("Event handler installed\n"); |
271 | 269 | ||
272 | return 0; | 270 | return 0; |
273 | 271 | ||
diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c new file mode 100644 index 000000000000..0aea63b3729a --- /dev/null +++ b/drivers/platform/x86/mxm-wmi.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * MXM WMI driver | ||
3 | * | ||
4 | * Copyright(C) 2010 Red Hat. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <acpi/acpi_bus.h> | ||
24 | #include <acpi/acpi_drivers.h> | ||
25 | |||
26 | MODULE_AUTHOR("Dave Airlie"); | ||
27 | MODULE_DESCRIPTION("MXM WMI Driver"); | ||
28 | MODULE_LICENSE("GPL"); | ||
29 | |||
30 | #define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" | ||
31 | |||
32 | MODULE_ALIAS("wmi:"MXM_WMMX_GUID); | ||
33 | |||
34 | #define MXM_WMMX_FUNC_MXDS 0x5344584D /* "MXDS" */ | ||
35 | #define MXM_WMMX_FUNC_MXMX 0x53445344 /* "MXMX" */ | ||
36 | |||
37 | struct mxds_args { | ||
38 | u32 func; | ||
39 | u32 args; | ||
40 | u32 xarg; | ||
41 | }; | ||
42 | |||
43 | int mxm_wmi_call_mxds(int adapter) | ||
44 | { | ||
45 | struct mxds_args args = { | ||
46 | .func = MXM_WMMX_FUNC_MXDS, | ||
47 | .args = 0, | ||
48 | .xarg = 1, | ||
49 | }; | ||
50 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | ||
51 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
52 | acpi_status status; | ||
53 | |||
54 | printk("calling mux switch %d\n", adapter); | ||
55 | |||
56 | status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input, | ||
57 | &output); | ||
58 | |||
59 | if (ACPI_FAILURE(status)) | ||
60 | return status; | ||
61 | |||
62 | printk("mux switched %d\n", status); | ||
63 | return 0; | ||
64 | |||
65 | } | ||
66 | EXPORT_SYMBOL_GPL(mxm_wmi_call_mxds); | ||
67 | |||
68 | int mxm_wmi_call_mxmx(int adapter) | ||
69 | { | ||
70 | struct mxds_args args = { | ||
71 | .func = MXM_WMMX_FUNC_MXMX, | ||
72 | .args = 0, | ||
73 | .xarg = 1, | ||
74 | }; | ||
75 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | ||
76 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
77 | acpi_status status; | ||
78 | |||
79 | printk("calling mux switch %d\n", adapter); | ||
80 | |||
81 | status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input, | ||
82 | &output); | ||
83 | |||
84 | if (ACPI_FAILURE(status)) | ||
85 | return status; | ||
86 | |||
87 | printk("mux mutex set switched %d\n", status); | ||
88 | return 0; | ||
89 | |||
90 | } | ||
91 | EXPORT_SYMBOL_GPL(mxm_wmi_call_mxmx); | ||
92 | |||
93 | bool mxm_wmi_supported(void) | ||
94 | { | ||
95 | bool guid_valid; | ||
96 | guid_valid = wmi_has_guid(MXM_WMMX_GUID); | ||
97 | return guid_valid; | ||
98 | } | ||
99 | EXPORT_SYMBOL_GPL(mxm_wmi_supported); | ||
100 | |||
101 | static int __init mxm_wmi_init(void) | ||
102 | { | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static void __exit mxm_wmi_exit(void) | ||
107 | { | ||
108 | } | ||
109 | |||
110 | module_init(mxm_wmi_init); | ||
111 | module_exit(mxm_wmi_exit); | ||
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index ec01c3d8fc5a..05be30ee158b 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c | |||
@@ -128,6 +128,7 @@ | |||
128 | #include <acpi/acpi_bus.h> | 128 | #include <acpi/acpi_bus.h> |
129 | #include <acpi/acpi_drivers.h> | 129 | #include <acpi/acpi_drivers.h> |
130 | #include <linux/input.h> | 130 | #include <linux/input.h> |
131 | #include <linux/input/sparse-keymap.h> | ||
131 | 132 | ||
132 | 133 | ||
133 | #ifndef ACPI_HOTKEY_COMPONENT | 134 | #ifndef ACPI_HOTKEY_COMPONENT |
@@ -200,30 +201,29 @@ static struct acpi_driver acpi_pcc_driver = { | |||
200 | }, | 201 | }, |
201 | }; | 202 | }; |
202 | 203 | ||
203 | #define KEYMAP_SIZE 11 | 204 | static const struct key_entry panasonic_keymap[] = { |
204 | static const unsigned int initial_keymap[KEYMAP_SIZE] = { | 205 | { KE_KEY, 0, { KEY_RESERVED } }, |
205 | /* 0 */ KEY_RESERVED, | 206 | { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } }, |
206 | /* 1 */ KEY_BRIGHTNESSDOWN, | 207 | { KE_KEY, 2, { KEY_BRIGHTNESSUP } }, |
207 | /* 2 */ KEY_BRIGHTNESSUP, | 208 | { KE_KEY, 3, { KEY_DISPLAYTOGGLE } }, |
208 | /* 3 */ KEY_DISPLAYTOGGLE, | 209 | { KE_KEY, 4, { KEY_MUTE } }, |
209 | /* 4 */ KEY_MUTE, | 210 | { KE_KEY, 5, { KEY_VOLUMEDOWN } }, |
210 | /* 5 */ KEY_VOLUMEDOWN, | 211 | { KE_KEY, 6, { KEY_VOLUMEUP } }, |
211 | /* 6 */ KEY_VOLUMEUP, | 212 | { KE_KEY, 7, { KEY_SLEEP } }, |
212 | /* 7 */ KEY_SLEEP, | 213 | { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */ |
213 | /* 8 */ KEY_PROG1, /* Change CPU boost */ | 214 | { KE_KEY, 9, { KEY_BATTERY } }, |
214 | /* 9 */ KEY_BATTERY, | 215 | { KE_KEY, 10, { KEY_SUSPEND } }, |
215 | /* 10 */ KEY_SUSPEND, | 216 | { KE_END, 0 } |
216 | }; | 217 | }; |
217 | 218 | ||
218 | struct pcc_acpi { | 219 | struct pcc_acpi { |
219 | acpi_handle handle; | 220 | acpi_handle handle; |
220 | unsigned long num_sifr; | 221 | unsigned long num_sifr; |
221 | int sticky_mode; | 222 | int sticky_mode; |
222 | u32 *sinf; | 223 | u32 *sinf; |
223 | struct acpi_device *device; | 224 | struct acpi_device *device; |
224 | struct input_dev *input_dev; | 225 | struct input_dev *input_dev; |
225 | struct backlight_device *backlight; | 226 | struct backlight_device *backlight; |
226 | unsigned int keymap[KEYMAP_SIZE]; | ||
227 | }; | 227 | }; |
228 | 228 | ||
229 | struct pcc_keyinput { | 229 | struct pcc_keyinput { |
@@ -267,7 +267,7 @@ static inline int acpi_pcc_get_sqty(struct acpi_device *device) | |||
267 | } | 267 | } |
268 | } | 268 | } |
269 | 269 | ||
270 | static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) | 270 | static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc) |
271 | { | 271 | { |
272 | acpi_status status; | 272 | acpi_status status; |
273 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | 273 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; |
@@ -285,6 +285,7 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) | |||
285 | hkey = buffer.pointer; | 285 | hkey = buffer.pointer; |
286 | if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { | 286 | if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { |
287 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); | 287 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); |
288 | status = AE_ERROR; | ||
288 | goto end; | 289 | goto end; |
289 | } | 290 | } |
290 | 291 | ||
@@ -298,12 +299,12 @@ static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf) | |||
298 | for (i = 0; i < hkey->package.count; i++) { | 299 | for (i = 0; i < hkey->package.count; i++) { |
299 | union acpi_object *element = &(hkey->package.elements[i]); | 300 | union acpi_object *element = &(hkey->package.elements[i]); |
300 | if (likely(element->type == ACPI_TYPE_INTEGER)) { | 301 | if (likely(element->type == ACPI_TYPE_INTEGER)) { |
301 | sinf[i] = element->integer.value; | 302 | pcc->sinf[i] = element->integer.value; |
302 | } else | 303 | } else |
303 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 304 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
304 | "Invalid HKEY.SINF data\n")); | 305 | "Invalid HKEY.SINF data\n")); |
305 | } | 306 | } |
306 | sinf[hkey->package.count] = -1; | 307 | pcc->sinf[hkey->package.count] = -1; |
307 | 308 | ||
308 | end: | 309 | end: |
309 | kfree(buffer.pointer); | 310 | kfree(buffer.pointer); |
@@ -321,7 +322,7 @@ static int bl_get(struct backlight_device *bd) | |||
321 | { | 322 | { |
322 | struct pcc_acpi *pcc = bl_get_data(bd); | 323 | struct pcc_acpi *pcc = bl_get_data(bd); |
323 | 324 | ||
324 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) | 325 | if (!acpi_pcc_retrieve_biosdata(pcc)) |
325 | return -EIO; | 326 | return -EIO; |
326 | 327 | ||
327 | return pcc->sinf[SINF_AC_CUR_BRIGHT]; | 328 | return pcc->sinf[SINF_AC_CUR_BRIGHT]; |
@@ -333,7 +334,7 @@ static int bl_set_status(struct backlight_device *bd) | |||
333 | int bright = bd->props.brightness; | 334 | int bright = bd->props.brightness; |
334 | int rc; | 335 | int rc; |
335 | 336 | ||
336 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) | 337 | if (!acpi_pcc_retrieve_biosdata(pcc)) |
337 | return -EIO; | 338 | return -EIO; |
338 | 339 | ||
339 | if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT]) | 340 | if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT]) |
@@ -367,7 +368,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr, | |||
367 | struct acpi_device *acpi = to_acpi_device(dev); | 368 | struct acpi_device *acpi = to_acpi_device(dev); |
368 | struct pcc_acpi *pcc = acpi_driver_data(acpi); | 369 | struct pcc_acpi *pcc = acpi_driver_data(acpi); |
369 | 370 | ||
370 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) | 371 | if (!acpi_pcc_retrieve_biosdata(pcc)) |
371 | return -EIO; | 372 | return -EIO; |
372 | 373 | ||
373 | return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); | 374 | return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); |
@@ -379,7 +380,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, | |||
379 | struct acpi_device *acpi = to_acpi_device(dev); | 380 | struct acpi_device *acpi = to_acpi_device(dev); |
380 | struct pcc_acpi *pcc = acpi_driver_data(acpi); | 381 | struct pcc_acpi *pcc = acpi_driver_data(acpi); |
381 | 382 | ||
382 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) | 383 | if (!acpi_pcc_retrieve_biosdata(pcc)) |
383 | return -EIO; | 384 | return -EIO; |
384 | 385 | ||
385 | return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); | 386 | return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); |
@@ -391,7 +392,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr, | |||
391 | struct acpi_device *acpi = to_acpi_device(dev); | 392 | struct acpi_device *acpi = to_acpi_device(dev); |
392 | struct pcc_acpi *pcc = acpi_driver_data(acpi); | 393 | struct pcc_acpi *pcc = acpi_driver_data(acpi); |
393 | 394 | ||
394 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) | 395 | if (!acpi_pcc_retrieve_biosdata(pcc)) |
395 | return -EIO; | 396 | return -EIO; |
396 | 397 | ||
397 | return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); | 398 | return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); |
@@ -403,7 +404,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr, | |||
403 | struct acpi_device *acpi = to_acpi_device(dev); | 404 | struct acpi_device *acpi = to_acpi_device(dev); |
404 | struct pcc_acpi *pcc = acpi_driver_data(acpi); | 405 | struct pcc_acpi *pcc = acpi_driver_data(acpi); |
405 | 406 | ||
406 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) | 407 | if (!acpi_pcc_retrieve_biosdata(pcc)) |
407 | return -EIO; | 408 | return -EIO; |
408 | 409 | ||
409 | return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); | 410 | return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); |
@@ -446,56 +447,10 @@ static struct attribute_group pcc_attr_group = { | |||
446 | 447 | ||
447 | /* hotkey input device driver */ | 448 | /* hotkey input device driver */ |
448 | 449 | ||
449 | static int pcc_getkeycode(struct input_dev *dev, | ||
450 | unsigned int scancode, unsigned int *keycode) | ||
451 | { | ||
452 | struct pcc_acpi *pcc = input_get_drvdata(dev); | ||
453 | |||
454 | if (scancode >= ARRAY_SIZE(pcc->keymap)) | ||
455 | return -EINVAL; | ||
456 | |||
457 | *keycode = pcc->keymap[scancode]; | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int keymap_get_by_keycode(struct pcc_acpi *pcc, unsigned int keycode) | ||
463 | { | ||
464 | int i; | ||
465 | |||
466 | for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) { | ||
467 | if (pcc->keymap[i] == keycode) | ||
468 | return i+1; | ||
469 | } | ||
470 | |||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int pcc_setkeycode(struct input_dev *dev, | ||
475 | unsigned int scancode, unsigned int keycode) | ||
476 | { | ||
477 | struct pcc_acpi *pcc = input_get_drvdata(dev); | ||
478 | int oldkeycode; | ||
479 | |||
480 | if (scancode >= ARRAY_SIZE(pcc->keymap)) | ||
481 | return -EINVAL; | ||
482 | |||
483 | oldkeycode = pcc->keymap[scancode]; | ||
484 | pcc->keymap[scancode] = keycode; | ||
485 | |||
486 | set_bit(keycode, dev->keybit); | ||
487 | |||
488 | if (!keymap_get_by_keycode(pcc, oldkeycode)) | ||
489 | clear_bit(oldkeycode, dev->keybit); | ||
490 | |||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) | 450 | static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) |
495 | { | 451 | { |
496 | struct input_dev *hotk_input_dev = pcc->input_dev; | 452 | struct input_dev *hotk_input_dev = pcc->input_dev; |
497 | int rc; | 453 | int rc; |
498 | int key_code, hkey_num; | ||
499 | unsigned long long result; | 454 | unsigned long long result; |
500 | 455 | ||
501 | rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, | 456 | rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY, |
@@ -508,25 +463,10 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) | |||
508 | 463 | ||
509 | acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result); | 464 | acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result); |
510 | 465 | ||
511 | hkey_num = result & 0xf; | 466 | if (!sparse_keymap_report_event(hotk_input_dev, |
512 | 467 | result & 0xf, result & 0x80, false)) | |
513 | if (hkey_num < 0 || hkey_num >= ARRAY_SIZE(pcc->keymap)) { | ||
514 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 468 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
515 | "hotkey number out of range: %d\n", | 469 | "Unknown hotkey event: %d\n", result)); |
516 | hkey_num)); | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | key_code = pcc->keymap[hkey_num]; | ||
521 | |||
522 | if (key_code != KEY_RESERVED) { | ||
523 | int pushed = (result & 0x80) ? TRUE : FALSE; | ||
524 | |||
525 | input_report_key(hotk_input_dev, key_code, pushed); | ||
526 | input_sync(hotk_input_dev); | ||
527 | } | ||
528 | |||
529 | return; | ||
530 | } | 470 | } |
531 | 471 | ||
532 | static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) | 472 | static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) |
@@ -545,40 +485,55 @@ static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) | |||
545 | 485 | ||
546 | static int acpi_pcc_init_input(struct pcc_acpi *pcc) | 486 | static int acpi_pcc_init_input(struct pcc_acpi *pcc) |
547 | { | 487 | { |
548 | int i, rc; | 488 | struct input_dev *input_dev; |
489 | int error; | ||
549 | 490 | ||
550 | pcc->input_dev = input_allocate_device(); | 491 | input_dev = input_allocate_device(); |
551 | if (!pcc->input_dev) { | 492 | if (!input_dev) { |
552 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 493 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
553 | "Couldn't allocate input device for hotkey")); | 494 | "Couldn't allocate input device for hotkey")); |
554 | return -ENOMEM; | 495 | return -ENOMEM; |
555 | } | 496 | } |
556 | 497 | ||
557 | pcc->input_dev->evbit[0] = BIT(EV_KEY); | 498 | input_dev->name = ACPI_PCC_DRIVER_NAME; |
558 | 499 | input_dev->phys = ACPI_PCC_INPUT_PHYS; | |
559 | pcc->input_dev->name = ACPI_PCC_DRIVER_NAME; | 500 | input_dev->id.bustype = BUS_HOST; |
560 | pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS; | 501 | input_dev->id.vendor = 0x0001; |
561 | pcc->input_dev->id.bustype = BUS_HOST; | 502 | input_dev->id.product = 0x0001; |
562 | pcc->input_dev->id.vendor = 0x0001; | 503 | input_dev->id.version = 0x0100; |
563 | pcc->input_dev->id.product = 0x0001; | ||
564 | pcc->input_dev->id.version = 0x0100; | ||
565 | pcc->input_dev->getkeycode = pcc_getkeycode; | ||
566 | pcc->input_dev->setkeycode = pcc_setkeycode; | ||
567 | 504 | ||
568 | /* load initial keymap */ | 505 | error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL); |
569 | memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap)); | 506 | if (error) { |
507 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
508 | "Unable to setup input device keymap\n")); | ||
509 | goto err_free_dev; | ||
510 | } | ||
570 | 511 | ||
571 | for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) | 512 | error = input_register_device(input_dev); |
572 | __set_bit(pcc->keymap[i], pcc->input_dev->keybit); | 513 | if (error) { |
573 | __clear_bit(KEY_RESERVED, pcc->input_dev->keybit); | 514 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
515 | "Unable to register input device\n")); | ||
516 | goto err_free_keymap; | ||
517 | } | ||
574 | 518 | ||
575 | input_set_drvdata(pcc->input_dev, pcc); | 519 | pcc->input_dev = input_dev; |
520 | return 0; | ||
576 | 521 | ||
577 | rc = input_register_device(pcc->input_dev); | 522 | err_free_keymap: |
578 | if (rc < 0) | 523 | sparse_keymap_free(input_dev); |
579 | input_free_device(pcc->input_dev); | 524 | err_free_dev: |
525 | input_free_device(input_dev); | ||
526 | return error; | ||
527 | } | ||
580 | 528 | ||
581 | return rc; | 529 | static void acpi_pcc_destroy_input(struct pcc_acpi *pcc) |
530 | { | ||
531 | sparse_keymap_free(pcc->input_dev); | ||
532 | input_unregister_device(pcc->input_dev); | ||
533 | /* | ||
534 | * No need to input_free_device() since core input API refcounts | ||
535 | * and free()s the device. | ||
536 | */ | ||
582 | } | 537 | } |
583 | 538 | ||
584 | /* kernel module interface */ | 539 | /* kernel module interface */ |
@@ -636,22 +591,24 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) | |||
636 | if (result) { | 591 | if (result) { |
637 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 592 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
638 | "Error installing keyinput handler\n")); | 593 | "Error installing keyinput handler\n")); |
639 | goto out_hotkey; | 594 | goto out_sinf; |
640 | } | 595 | } |
641 | 596 | ||
642 | if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) { | 597 | if (!acpi_pcc_retrieve_biosdata(pcc)) { |
643 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 598 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
644 | "Couldn't retrieve BIOS data\n")); | 599 | "Couldn't retrieve BIOS data\n")); |
600 | result = -EIO; | ||
645 | goto out_input; | 601 | goto out_input; |
646 | } | 602 | } |
647 | /* initialize backlight */ | 603 | /* initialize backlight */ |
648 | memset(&props, 0, sizeof(struct backlight_properties)); | 604 | memset(&props, 0, sizeof(struct backlight_properties)); |
605 | props.type = BACKLIGHT_PLATFORM; | ||
649 | props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT]; | 606 | props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT]; |
650 | pcc->backlight = backlight_device_register("panasonic", NULL, pcc, | 607 | pcc->backlight = backlight_device_register("panasonic", NULL, pcc, |
651 | &pcc_backlight_ops, &props); | 608 | &pcc_backlight_ops, &props); |
652 | if (IS_ERR(pcc->backlight)) { | 609 | if (IS_ERR(pcc->backlight)) { |
653 | result = PTR_ERR(pcc->backlight); | 610 | result = PTR_ERR(pcc->backlight); |
654 | goto out_sinf; | 611 | goto out_input; |
655 | } | 612 | } |
656 | 613 | ||
657 | /* read the initial brightness setting from the hardware */ | 614 | /* read the initial brightness setting from the hardware */ |
@@ -669,12 +626,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) | |||
669 | 626 | ||
670 | out_backlight: | 627 | out_backlight: |
671 | backlight_device_unregister(pcc->backlight); | 628 | backlight_device_unregister(pcc->backlight); |
629 | out_input: | ||
630 | acpi_pcc_destroy_input(pcc); | ||
672 | out_sinf: | 631 | out_sinf: |
673 | kfree(pcc->sinf); | 632 | kfree(pcc->sinf); |
674 | out_input: | ||
675 | input_unregister_device(pcc->input_dev); | ||
676 | /* no need to input_free_device() since core input API refcount and | ||
677 | * free()s the device */ | ||
678 | out_hotkey: | 633 | out_hotkey: |
679 | kfree(pcc); | 634 | kfree(pcc); |
680 | 635 | ||
@@ -709,9 +664,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) | |||
709 | 664 | ||
710 | backlight_device_unregister(pcc->backlight); | 665 | backlight_device_unregister(pcc->backlight); |
711 | 666 | ||
712 | input_unregister_device(pcc->input_dev); | 667 | acpi_pcc_destroy_input(pcc); |
713 | /* no need to input_free_device() since core input API refcount and | ||
714 | * free()s the device */ | ||
715 | 668 | ||
716 | kfree(pcc->sinf); | 669 | kfree(pcc->sinf); |
717 | kfree(pcc); | 670 | kfree(pcc); |
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c new file mode 100644 index 000000000000..d347116d150e --- /dev/null +++ b/drivers/platform/x86/samsung-laptop.c | |||
@@ -0,0 +1,843 @@ | |||
1 | /* | ||
2 | * Samsung Laptop driver | ||
3 | * | ||
4 | * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de) | ||
5 | * Copyright (C) 2009,2011 Novell Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/backlight.h> | ||
20 | #include <linux/fb.h> | ||
21 | #include <linux/dmi.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/rfkill.h> | ||
24 | |||
25 | /* | ||
26 | * This driver is needed because a number of Samsung laptops do not hook | ||
27 | * their control settings through ACPI. So we have to poke around in the | ||
28 | * BIOS to do things like brightness values, and "special" key controls. | ||
29 | */ | ||
30 | |||
31 | /* | ||
32 | * We have 0 - 8 as valid brightness levels. The specs say that level 0 should | ||
33 | * be reserved by the BIOS (which really doesn't make much sense), we tell | ||
34 | * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8 | ||
35 | */ | ||
36 | #define MAX_BRIGHT 0x07 | ||
37 | |||
38 | |||
39 | #define SABI_IFACE_MAIN 0x00 | ||
40 | #define SABI_IFACE_SUB 0x02 | ||
41 | #define SABI_IFACE_COMPLETE 0x04 | ||
42 | #define SABI_IFACE_DATA 0x05 | ||
43 | |||
44 | /* Structure to get data back to the calling function */ | ||
45 | struct sabi_retval { | ||
46 | u8 retval[20]; | ||
47 | }; | ||
48 | |||
49 | struct sabi_header_offsets { | ||
50 | u8 port; | ||
51 | u8 re_mem; | ||
52 | u8 iface_func; | ||
53 | u8 en_mem; | ||
54 | u8 data_offset; | ||
55 | u8 data_segment; | ||
56 | }; | ||
57 | |||
58 | struct sabi_commands { | ||
59 | /* | ||
60 | * Brightness is 0 - 8, as described above. | ||
61 | * Value 0 is for the BIOS to use | ||
62 | */ | ||
63 | u8 get_brightness; | ||
64 | u8 set_brightness; | ||
65 | |||
66 | /* | ||
67 | * first byte: | ||
68 | * 0x00 - wireless is off | ||
69 | * 0x01 - wireless is on | ||
70 | * second byte: | ||
71 | * 0x02 - 3G is off | ||
72 | * 0x03 - 3G is on | ||
73 | * TODO, verify 3G is correct, that doesn't seem right... | ||
74 | */ | ||
75 | u8 get_wireless_button; | ||
76 | u8 set_wireless_button; | ||
77 | |||
78 | /* 0 is off, 1 is on */ | ||
79 | u8 get_backlight; | ||
80 | u8 set_backlight; | ||
81 | |||
82 | /* | ||
83 | * 0x80 or 0x00 - no action | ||
84 | * 0x81 - recovery key pressed | ||
85 | */ | ||
86 | u8 get_recovery_mode; | ||
87 | u8 set_recovery_mode; | ||
88 | |||
89 | /* | ||
90 | * on seclinux: 0 is low, 1 is high, | ||
91 | * on swsmi: 0 is normal, 1 is silent, 2 is turbo | ||
92 | */ | ||
93 | u8 get_performance_level; | ||
94 | u8 set_performance_level; | ||
95 | |||
96 | /* | ||
97 | * Tell the BIOS that Linux is running on this machine. | ||
98 | * 81 is on, 80 is off | ||
99 | */ | ||
100 | u8 set_linux; | ||
101 | }; | ||
102 | |||
103 | struct sabi_performance_level { | ||
104 | const char *name; | ||
105 | u8 value; | ||
106 | }; | ||
107 | |||
108 | struct sabi_config { | ||
109 | const char *test_string; | ||
110 | u16 main_function; | ||
111 | const struct sabi_header_offsets header_offsets; | ||
112 | const struct sabi_commands commands; | ||
113 | const struct sabi_performance_level performance_levels[4]; | ||
114 | u8 min_brightness; | ||
115 | u8 max_brightness; | ||
116 | }; | ||
117 | |||
118 | static const struct sabi_config sabi_configs[] = { | ||
119 | { | ||
120 | .test_string = "SECLINUX", | ||
121 | |||
122 | .main_function = 0x4c49, | ||
123 | |||
124 | .header_offsets = { | ||
125 | .port = 0x00, | ||
126 | .re_mem = 0x02, | ||
127 | .iface_func = 0x03, | ||
128 | .en_mem = 0x04, | ||
129 | .data_offset = 0x05, | ||
130 | .data_segment = 0x07, | ||
131 | }, | ||
132 | |||
133 | .commands = { | ||
134 | .get_brightness = 0x00, | ||
135 | .set_brightness = 0x01, | ||
136 | |||
137 | .get_wireless_button = 0x02, | ||
138 | .set_wireless_button = 0x03, | ||
139 | |||
140 | .get_backlight = 0x04, | ||
141 | .set_backlight = 0x05, | ||
142 | |||
143 | .get_recovery_mode = 0x06, | ||
144 | .set_recovery_mode = 0x07, | ||
145 | |||
146 | .get_performance_level = 0x08, | ||
147 | .set_performance_level = 0x09, | ||
148 | |||
149 | .set_linux = 0x0a, | ||
150 | }, | ||
151 | |||
152 | .performance_levels = { | ||
153 | { | ||
154 | .name = "silent", | ||
155 | .value = 0, | ||
156 | }, | ||
157 | { | ||
158 | .name = "normal", | ||
159 | .value = 1, | ||
160 | }, | ||
161 | { }, | ||
162 | }, | ||
163 | .min_brightness = 1, | ||
164 | .max_brightness = 8, | ||
165 | }, | ||
166 | { | ||
167 | .test_string = "SwSmi@", | ||
168 | |||
169 | .main_function = 0x5843, | ||
170 | |||
171 | .header_offsets = { | ||
172 | .port = 0x00, | ||
173 | .re_mem = 0x04, | ||
174 | .iface_func = 0x02, | ||
175 | .en_mem = 0x03, | ||
176 | .data_offset = 0x05, | ||
177 | .data_segment = 0x07, | ||
178 | }, | ||
179 | |||
180 | .commands = { | ||
181 | .get_brightness = 0x10, | ||
182 | .set_brightness = 0x11, | ||
183 | |||
184 | .get_wireless_button = 0x12, | ||
185 | .set_wireless_button = 0x13, | ||
186 | |||
187 | .get_backlight = 0x2d, | ||
188 | .set_backlight = 0x2e, | ||
189 | |||
190 | .get_recovery_mode = 0xff, | ||
191 | .set_recovery_mode = 0xff, | ||
192 | |||
193 | .get_performance_level = 0x31, | ||
194 | .set_performance_level = 0x32, | ||
195 | |||
196 | .set_linux = 0xff, | ||
197 | }, | ||
198 | |||
199 | .performance_levels = { | ||
200 | { | ||
201 | .name = "normal", | ||
202 | .value = 0, | ||
203 | }, | ||
204 | { | ||
205 | .name = "silent", | ||
206 | .value = 1, | ||
207 | }, | ||
208 | { | ||
209 | .name = "overclock", | ||
210 | .value = 2, | ||
211 | }, | ||
212 | { }, | ||
213 | }, | ||
214 | .min_brightness = 0, | ||
215 | .max_brightness = 8, | ||
216 | }, | ||
217 | { }, | ||
218 | }; | ||
219 | |||
220 | static const struct sabi_config *sabi_config; | ||
221 | |||
222 | static void __iomem *sabi; | ||
223 | static void __iomem *sabi_iface; | ||
224 | static void __iomem *f0000_segment; | ||
225 | static struct backlight_device *backlight_device; | ||
226 | static struct mutex sabi_mutex; | ||
227 | static struct platform_device *sdev; | ||
228 | static struct rfkill *rfk; | ||
229 | |||
230 | static int force; | ||
231 | module_param(force, bool, 0); | ||
232 | MODULE_PARM_DESC(force, | ||
233 | "Disable the DMI check and forces the driver to be loaded"); | ||
234 | |||
235 | static int debug; | ||
236 | module_param(debug, bool, S_IRUGO | S_IWUSR); | ||
237 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||
238 | |||
239 | static int sabi_get_command(u8 command, struct sabi_retval *sretval) | ||
240 | { | ||
241 | int retval = 0; | ||
242 | u16 port = readw(sabi + sabi_config->header_offsets.port); | ||
243 | u8 complete, iface_data; | ||
244 | |||
245 | mutex_lock(&sabi_mutex); | ||
246 | |||
247 | /* enable memory to be able to write to it */ | ||
248 | outb(readb(sabi + sabi_config->header_offsets.en_mem), port); | ||
249 | |||
250 | /* write out the command */ | ||
251 | writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN); | ||
252 | writew(command, sabi_iface + SABI_IFACE_SUB); | ||
253 | writeb(0, sabi_iface + SABI_IFACE_COMPLETE); | ||
254 | outb(readb(sabi + sabi_config->header_offsets.iface_func), port); | ||
255 | |||
256 | /* write protect memory to make it safe */ | ||
257 | outb(readb(sabi + sabi_config->header_offsets.re_mem), port); | ||
258 | |||
259 | /* see if the command actually succeeded */ | ||
260 | complete = readb(sabi_iface + SABI_IFACE_COMPLETE); | ||
261 | iface_data = readb(sabi_iface + SABI_IFACE_DATA); | ||
262 | if (complete != 0xaa || iface_data == 0xff) { | ||
263 | pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n", | ||
264 | command, complete, iface_data); | ||
265 | retval = -EINVAL; | ||
266 | goto exit; | ||
267 | } | ||
268 | /* | ||
269 | * Save off the data into a structure so the caller use it. | ||
270 | * Right now we only want the first 4 bytes, | ||
271 | * There are commands that need more, but not for the ones we | ||
272 | * currently care about. | ||
273 | */ | ||
274 | sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA); | ||
275 | sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1); | ||
276 | sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2); | ||
277 | sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3); | ||
278 | |||
279 | exit: | ||
280 | mutex_unlock(&sabi_mutex); | ||
281 | return retval; | ||
282 | |||
283 | } | ||
284 | |||
285 | static int sabi_set_command(u8 command, u8 data) | ||
286 | { | ||
287 | int retval = 0; | ||
288 | u16 port = readw(sabi + sabi_config->header_offsets.port); | ||
289 | u8 complete, iface_data; | ||
290 | |||
291 | mutex_lock(&sabi_mutex); | ||
292 | |||
293 | /* enable memory to be able to write to it */ | ||
294 | outb(readb(sabi + sabi_config->header_offsets.en_mem), port); | ||
295 | |||
296 | /* write out the command */ | ||
297 | writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN); | ||
298 | writew(command, sabi_iface + SABI_IFACE_SUB); | ||
299 | writeb(0, sabi_iface + SABI_IFACE_COMPLETE); | ||
300 | writeb(data, sabi_iface + SABI_IFACE_DATA); | ||
301 | outb(readb(sabi + sabi_config->header_offsets.iface_func), port); | ||
302 | |||
303 | /* write protect memory to make it safe */ | ||
304 | outb(readb(sabi + sabi_config->header_offsets.re_mem), port); | ||
305 | |||
306 | /* see if the command actually succeeded */ | ||
307 | complete = readb(sabi_iface + SABI_IFACE_COMPLETE); | ||
308 | iface_data = readb(sabi_iface + SABI_IFACE_DATA); | ||
309 | if (complete != 0xaa || iface_data == 0xff) { | ||
310 | pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n", | ||
311 | command, complete, iface_data); | ||
312 | retval = -EINVAL; | ||
313 | } | ||
314 | |||
315 | mutex_unlock(&sabi_mutex); | ||
316 | return retval; | ||
317 | } | ||
318 | |||
319 | static void test_backlight(void) | ||
320 | { | ||
321 | struct sabi_retval sretval; | ||
322 | |||
323 | sabi_get_command(sabi_config->commands.get_backlight, &sretval); | ||
324 | printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); | ||
325 | |||
326 | sabi_set_command(sabi_config->commands.set_backlight, 0); | ||
327 | printk(KERN_DEBUG "backlight should be off\n"); | ||
328 | |||
329 | sabi_get_command(sabi_config->commands.get_backlight, &sretval); | ||
330 | printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); | ||
331 | |||
332 | msleep(1000); | ||
333 | |||
334 | sabi_set_command(sabi_config->commands.set_backlight, 1); | ||
335 | printk(KERN_DEBUG "backlight should be on\n"); | ||
336 | |||
337 | sabi_get_command(sabi_config->commands.get_backlight, &sretval); | ||
338 | printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); | ||
339 | } | ||
340 | |||
341 | static void test_wireless(void) | ||
342 | { | ||
343 | struct sabi_retval sretval; | ||
344 | |||
345 | sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); | ||
346 | printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); | ||
347 | |||
348 | sabi_set_command(sabi_config->commands.set_wireless_button, 0); | ||
349 | printk(KERN_DEBUG "wireless led should be off\n"); | ||
350 | |||
351 | sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); | ||
352 | printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); | ||
353 | |||
354 | msleep(1000); | ||
355 | |||
356 | sabi_set_command(sabi_config->commands.set_wireless_button, 1); | ||
357 | printk(KERN_DEBUG "wireless led should be on\n"); | ||
358 | |||
359 | sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); | ||
360 | printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); | ||
361 | } | ||
362 | |||
363 | static u8 read_brightness(void) | ||
364 | { | ||
365 | struct sabi_retval sretval; | ||
366 | int user_brightness = 0; | ||
367 | int retval; | ||
368 | |||
369 | retval = sabi_get_command(sabi_config->commands.get_brightness, | ||
370 | &sretval); | ||
371 | if (!retval) { | ||
372 | user_brightness = sretval.retval[0]; | ||
373 | if (user_brightness != 0) | ||
374 | user_brightness -= sabi_config->min_brightness; | ||
375 | } | ||
376 | return user_brightness; | ||
377 | } | ||
378 | |||
379 | static void set_brightness(u8 user_brightness) | ||
380 | { | ||
381 | u8 user_level = user_brightness - sabi_config->min_brightness; | ||
382 | |||
383 | sabi_set_command(sabi_config->commands.set_brightness, user_level); | ||
384 | } | ||
385 | |||
386 | static int get_brightness(struct backlight_device *bd) | ||
387 | { | ||
388 | return (int)read_brightness(); | ||
389 | } | ||
390 | |||
391 | static int update_status(struct backlight_device *bd) | ||
392 | { | ||
393 | set_brightness(bd->props.brightness); | ||
394 | |||
395 | if (bd->props.power == FB_BLANK_UNBLANK) | ||
396 | sabi_set_command(sabi_config->commands.set_backlight, 1); | ||
397 | else | ||
398 | sabi_set_command(sabi_config->commands.set_backlight, 0); | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static const struct backlight_ops backlight_ops = { | ||
403 | .get_brightness = get_brightness, | ||
404 | .update_status = update_status, | ||
405 | }; | ||
406 | |||
407 | static int rfkill_set(void *data, bool blocked) | ||
408 | { | ||
409 | /* Do something with blocked...*/ | ||
410 | /* | ||
411 | * blocked == false is on | ||
412 | * blocked == true is off | ||
413 | */ | ||
414 | if (blocked) | ||
415 | sabi_set_command(sabi_config->commands.set_wireless_button, 0); | ||
416 | else | ||
417 | sabi_set_command(sabi_config->commands.set_wireless_button, 1); | ||
418 | |||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static struct rfkill_ops rfkill_ops = { | ||
423 | .set_block = rfkill_set, | ||
424 | }; | ||
425 | |||
426 | static int init_wireless(struct platform_device *sdev) | ||
427 | { | ||
428 | int retval; | ||
429 | |||
430 | rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN, | ||
431 | &rfkill_ops, NULL); | ||
432 | if (!rfk) | ||
433 | return -ENOMEM; | ||
434 | |||
435 | retval = rfkill_register(rfk); | ||
436 | if (retval) { | ||
437 | rfkill_destroy(rfk); | ||
438 | return -ENODEV; | ||
439 | } | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | static void destroy_wireless(void) | ||
445 | { | ||
446 | rfkill_unregister(rfk); | ||
447 | rfkill_destroy(rfk); | ||
448 | } | ||
449 | |||
450 | static ssize_t get_performance_level(struct device *dev, | ||
451 | struct device_attribute *attr, char *buf) | ||
452 | { | ||
453 | struct sabi_retval sretval; | ||
454 | int retval; | ||
455 | int i; | ||
456 | |||
457 | /* Read the state */ | ||
458 | retval = sabi_get_command(sabi_config->commands.get_performance_level, | ||
459 | &sretval); | ||
460 | if (retval) | ||
461 | return retval; | ||
462 | |||
463 | /* The logic is backwards, yeah, lots of fun... */ | ||
464 | for (i = 0; sabi_config->performance_levels[i].name; ++i) { | ||
465 | if (sretval.retval[0] == sabi_config->performance_levels[i].value) | ||
466 | return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name); | ||
467 | } | ||
468 | return sprintf(buf, "%s\n", "unknown"); | ||
469 | } | ||
470 | |||
471 | static ssize_t set_performance_level(struct device *dev, | ||
472 | struct device_attribute *attr, const char *buf, | ||
473 | size_t count) | ||
474 | { | ||
475 | if (count >= 1) { | ||
476 | int i; | ||
477 | for (i = 0; sabi_config->performance_levels[i].name; ++i) { | ||
478 | const struct sabi_performance_level *level = | ||
479 | &sabi_config->performance_levels[i]; | ||
480 | if (!strncasecmp(level->name, buf, strlen(level->name))) { | ||
481 | sabi_set_command(sabi_config->commands.set_performance_level, | ||
482 | level->value); | ||
483 | break; | ||
484 | } | ||
485 | } | ||
486 | if (!sabi_config->performance_levels[i].name) | ||
487 | return -EINVAL; | ||
488 | } | ||
489 | return count; | ||
490 | } | ||
491 | static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, | ||
492 | get_performance_level, set_performance_level); | ||
493 | |||
494 | |||
495 | static int __init dmi_check_cb(const struct dmi_system_id *id) | ||
496 | { | ||
497 | pr_info("found laptop model '%s'\n", | ||
498 | id->ident); | ||
499 | return 1; | ||
500 | } | ||
501 | |||
502 | static struct dmi_system_id __initdata samsung_dmi_table[] = { | ||
503 | { | ||
504 | .ident = "N128", | ||
505 | .matches = { | ||
506 | DMI_MATCH(DMI_SYS_VENDOR, | ||
507 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
508 | DMI_MATCH(DMI_PRODUCT_NAME, "N128"), | ||
509 | DMI_MATCH(DMI_BOARD_NAME, "N128"), | ||
510 | }, | ||
511 | .callback = dmi_check_cb, | ||
512 | }, | ||
513 | { | ||
514 | .ident = "N130", | ||
515 | .matches = { | ||
516 | DMI_MATCH(DMI_SYS_VENDOR, | ||
517 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
518 | DMI_MATCH(DMI_PRODUCT_NAME, "N130"), | ||
519 | DMI_MATCH(DMI_BOARD_NAME, "N130"), | ||
520 | }, | ||
521 | .callback = dmi_check_cb, | ||
522 | }, | ||
523 | { | ||
524 | .ident = "X125", | ||
525 | .matches = { | ||
526 | DMI_MATCH(DMI_SYS_VENDOR, | ||
527 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
528 | DMI_MATCH(DMI_PRODUCT_NAME, "X125"), | ||
529 | DMI_MATCH(DMI_BOARD_NAME, "X125"), | ||
530 | }, | ||
531 | .callback = dmi_check_cb, | ||
532 | }, | ||
533 | { | ||
534 | .ident = "X120/X170", | ||
535 | .matches = { | ||
536 | DMI_MATCH(DMI_SYS_VENDOR, | ||
537 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
538 | DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"), | ||
539 | DMI_MATCH(DMI_BOARD_NAME, "X120/X170"), | ||
540 | }, | ||
541 | .callback = dmi_check_cb, | ||
542 | }, | ||
543 | { | ||
544 | .ident = "NC10", | ||
545 | .matches = { | ||
546 | DMI_MATCH(DMI_SYS_VENDOR, | ||
547 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
548 | DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), | ||
549 | DMI_MATCH(DMI_BOARD_NAME, "NC10"), | ||
550 | }, | ||
551 | .callback = dmi_check_cb, | ||
552 | }, | ||
553 | { | ||
554 | .ident = "NP-Q45", | ||
555 | .matches = { | ||
556 | DMI_MATCH(DMI_SYS_VENDOR, | ||
557 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
558 | DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"), | ||
559 | DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"), | ||
560 | }, | ||
561 | .callback = dmi_check_cb, | ||
562 | }, | ||
563 | { | ||
564 | .ident = "X360", | ||
565 | .matches = { | ||
566 | DMI_MATCH(DMI_SYS_VENDOR, | ||
567 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
568 | DMI_MATCH(DMI_PRODUCT_NAME, "X360"), | ||
569 | DMI_MATCH(DMI_BOARD_NAME, "X360"), | ||
570 | }, | ||
571 | .callback = dmi_check_cb, | ||
572 | }, | ||
573 | { | ||
574 | .ident = "R410 Plus", | ||
575 | .matches = { | ||
576 | DMI_MATCH(DMI_SYS_VENDOR, | ||
577 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
578 | DMI_MATCH(DMI_PRODUCT_NAME, "R410P"), | ||
579 | DMI_MATCH(DMI_BOARD_NAME, "R460"), | ||
580 | }, | ||
581 | .callback = dmi_check_cb, | ||
582 | }, | ||
583 | { | ||
584 | .ident = "R518", | ||
585 | .matches = { | ||
586 | DMI_MATCH(DMI_SYS_VENDOR, | ||
587 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
588 | DMI_MATCH(DMI_PRODUCT_NAME, "R518"), | ||
589 | DMI_MATCH(DMI_BOARD_NAME, "R518"), | ||
590 | }, | ||
591 | .callback = dmi_check_cb, | ||
592 | }, | ||
593 | { | ||
594 | .ident = "R519/R719", | ||
595 | .matches = { | ||
596 | DMI_MATCH(DMI_SYS_VENDOR, | ||
597 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
598 | DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"), | ||
599 | DMI_MATCH(DMI_BOARD_NAME, "R519/R719"), | ||
600 | }, | ||
601 | .callback = dmi_check_cb, | ||
602 | }, | ||
603 | { | ||
604 | .ident = "N150/N210/N220/N230", | ||
605 | .matches = { | ||
606 | DMI_MATCH(DMI_SYS_VENDOR, | ||
607 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
608 | DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"), | ||
609 | DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"), | ||
610 | }, | ||
611 | .callback = dmi_check_cb, | ||
612 | }, | ||
613 | { | ||
614 | .ident = "N150P/N210P/N220P", | ||
615 | .matches = { | ||
616 | DMI_MATCH(DMI_SYS_VENDOR, | ||
617 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
618 | DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"), | ||
619 | DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"), | ||
620 | }, | ||
621 | .callback = dmi_check_cb, | ||
622 | }, | ||
623 | { | ||
624 | .ident = "R530/R730", | ||
625 | .matches = { | ||
626 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
627 | DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"), | ||
628 | DMI_MATCH(DMI_BOARD_NAME, "R530/R730"), | ||
629 | }, | ||
630 | .callback = dmi_check_cb, | ||
631 | }, | ||
632 | { | ||
633 | .ident = "NF110/NF210/NF310", | ||
634 | .matches = { | ||
635 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
636 | DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), | ||
637 | DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), | ||
638 | }, | ||
639 | .callback = dmi_check_cb, | ||
640 | }, | ||
641 | { | ||
642 | .ident = "N145P/N250P/N260P", | ||
643 | .matches = { | ||
644 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
645 | DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), | ||
646 | DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), | ||
647 | }, | ||
648 | .callback = dmi_check_cb, | ||
649 | }, | ||
650 | { | ||
651 | .ident = "R70/R71", | ||
652 | .matches = { | ||
653 | DMI_MATCH(DMI_SYS_VENDOR, | ||
654 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
655 | DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"), | ||
656 | DMI_MATCH(DMI_BOARD_NAME, "R70/R71"), | ||
657 | }, | ||
658 | .callback = dmi_check_cb, | ||
659 | }, | ||
660 | { | ||
661 | .ident = "P460", | ||
662 | .matches = { | ||
663 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
664 | DMI_MATCH(DMI_PRODUCT_NAME, "P460"), | ||
665 | DMI_MATCH(DMI_BOARD_NAME, "P460"), | ||
666 | }, | ||
667 | .callback = dmi_check_cb, | ||
668 | }, | ||
669 | { }, | ||
670 | }; | ||
671 | MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); | ||
672 | |||
673 | static int find_signature(void __iomem *memcheck, const char *testStr) | ||
674 | { | ||
675 | int i = 0; | ||
676 | int loca; | ||
677 | |||
678 | for (loca = 0; loca < 0xffff; loca++) { | ||
679 | char temp = readb(memcheck + loca); | ||
680 | |||
681 | if (temp == testStr[i]) { | ||
682 | if (i == strlen(testStr)-1) | ||
683 | break; | ||
684 | ++i; | ||
685 | } else { | ||
686 | i = 0; | ||
687 | } | ||
688 | } | ||
689 | return loca; | ||
690 | } | ||
691 | |||
692 | static int __init samsung_init(void) | ||
693 | { | ||
694 | struct backlight_properties props; | ||
695 | struct sabi_retval sretval; | ||
696 | unsigned int ifaceP; | ||
697 | int i; | ||
698 | int loca; | ||
699 | int retval; | ||
700 | |||
701 | mutex_init(&sabi_mutex); | ||
702 | |||
703 | if (!force && !dmi_check_system(samsung_dmi_table)) | ||
704 | return -ENODEV; | ||
705 | |||
706 | f0000_segment = ioremap_nocache(0xf0000, 0xffff); | ||
707 | if (!f0000_segment) { | ||
708 | pr_err("Can't map the segment at 0xf0000\n"); | ||
709 | return -EINVAL; | ||
710 | } | ||
711 | |||
712 | /* Try to find one of the signatures in memory to find the header */ | ||
713 | for (i = 0; sabi_configs[i].test_string != 0; ++i) { | ||
714 | sabi_config = &sabi_configs[i]; | ||
715 | loca = find_signature(f0000_segment, sabi_config->test_string); | ||
716 | if (loca != 0xffff) | ||
717 | break; | ||
718 | } | ||
719 | |||
720 | if (loca == 0xffff) { | ||
721 | pr_err("This computer does not support SABI\n"); | ||
722 | goto error_no_signature; | ||
723 | } | ||
724 | |||
725 | /* point to the SMI port Number */ | ||
726 | loca += 1; | ||
727 | sabi = (f0000_segment + loca); | ||
728 | |||
729 | if (debug) { | ||
730 | printk(KERN_DEBUG "This computer supports SABI==%x\n", | ||
731 | loca + 0xf0000 - 6); | ||
732 | printk(KERN_DEBUG "SABI header:\n"); | ||
733 | printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", | ||
734 | readw(sabi + sabi_config->header_offsets.port)); | ||
735 | printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", | ||
736 | readb(sabi + sabi_config->header_offsets.iface_func)); | ||
737 | printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", | ||
738 | readb(sabi + sabi_config->header_offsets.en_mem)); | ||
739 | printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", | ||
740 | readb(sabi + sabi_config->header_offsets.re_mem)); | ||
741 | printk(KERN_DEBUG " SABI data offset = 0x%04x\n", | ||
742 | readw(sabi + sabi_config->header_offsets.data_offset)); | ||
743 | printk(KERN_DEBUG " SABI data segment = 0x%04x\n", | ||
744 | readw(sabi + sabi_config->header_offsets.data_segment)); | ||
745 | } | ||
746 | |||
747 | /* Get a pointer to the SABI Interface */ | ||
748 | ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4; | ||
749 | ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff; | ||
750 | sabi_iface = ioremap_nocache(ifaceP, 16); | ||
751 | if (!sabi_iface) { | ||
752 | pr_err("Can't remap %x\n", ifaceP); | ||
753 | goto exit; | ||
754 | } | ||
755 | if (debug) { | ||
756 | printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); | ||
757 | printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface); | ||
758 | |||
759 | test_backlight(); | ||
760 | test_wireless(); | ||
761 | |||
762 | retval = sabi_get_command(sabi_config->commands.get_brightness, | ||
763 | &sretval); | ||
764 | printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]); | ||
765 | } | ||
766 | |||
767 | /* Turn on "Linux" mode in the BIOS */ | ||
768 | if (sabi_config->commands.set_linux != 0xff) { | ||
769 | retval = sabi_set_command(sabi_config->commands.set_linux, | ||
770 | 0x81); | ||
771 | if (retval) { | ||
772 | pr_warn("Linux mode was not set!\n"); | ||
773 | goto error_no_platform; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | /* knock up a platform device to hang stuff off of */ | ||
778 | sdev = platform_device_register_simple("samsung", -1, NULL, 0); | ||
779 | if (IS_ERR(sdev)) | ||
780 | goto error_no_platform; | ||
781 | |||
782 | /* create a backlight device to talk to this one */ | ||
783 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
784 | props.type = BACKLIGHT_PLATFORM; | ||
785 | props.max_brightness = sabi_config->max_brightness; | ||
786 | backlight_device = backlight_device_register("samsung", &sdev->dev, | ||
787 | NULL, &backlight_ops, | ||
788 | &props); | ||
789 | if (IS_ERR(backlight_device)) | ||
790 | goto error_no_backlight; | ||
791 | |||
792 | backlight_device->props.brightness = read_brightness(); | ||
793 | backlight_device->props.power = FB_BLANK_UNBLANK; | ||
794 | backlight_update_status(backlight_device); | ||
795 | |||
796 | retval = init_wireless(sdev); | ||
797 | if (retval) | ||
798 | goto error_no_rfk; | ||
799 | |||
800 | retval = device_create_file(&sdev->dev, &dev_attr_performance_level); | ||
801 | if (retval) | ||
802 | goto error_file_create; | ||
803 | |||
804 | exit: | ||
805 | return 0; | ||
806 | |||
807 | error_file_create: | ||
808 | destroy_wireless(); | ||
809 | |||
810 | error_no_rfk: | ||
811 | backlight_device_unregister(backlight_device); | ||
812 | |||
813 | error_no_backlight: | ||
814 | platform_device_unregister(sdev); | ||
815 | |||
816 | error_no_platform: | ||
817 | iounmap(sabi_iface); | ||
818 | |||
819 | error_no_signature: | ||
820 | iounmap(f0000_segment); | ||
821 | return -EINVAL; | ||
822 | } | ||
823 | |||
824 | static void __exit samsung_exit(void) | ||
825 | { | ||
826 | /* Turn off "Linux" mode in the BIOS */ | ||
827 | if (sabi_config->commands.set_linux != 0xff) | ||
828 | sabi_set_command(sabi_config->commands.set_linux, 0x80); | ||
829 | |||
830 | device_remove_file(&sdev->dev, &dev_attr_performance_level); | ||
831 | backlight_device_unregister(backlight_device); | ||
832 | destroy_wireless(); | ||
833 | iounmap(sabi_iface); | ||
834 | iounmap(f0000_segment); | ||
835 | platform_device_unregister(sdev); | ||
836 | } | ||
837 | |||
838 | module_init(samsung_init); | ||
839 | module_exit(samsung_exit); | ||
840 | |||
841 | MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); | ||
842 | MODULE_DESCRIPTION("Samsung Backlight driver"); | ||
843 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index e3154ff7a39f..bbd182e178cb 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -42,6 +42,8 @@ | |||
42 | * | 42 | * |
43 | */ | 43 | */ |
44 | 44 | ||
45 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
46 | |||
45 | #include <linux/kernel.h> | 47 | #include <linux/kernel.h> |
46 | #include <linux/module.h> | 48 | #include <linux/module.h> |
47 | #include <linux/moduleparam.h> | 49 | #include <linux/moduleparam.h> |
@@ -70,9 +72,10 @@ | |||
70 | #include <linux/miscdevice.h> | 72 | #include <linux/miscdevice.h> |
71 | #endif | 73 | #endif |
72 | 74 | ||
73 | #define DRV_PFX "sony-laptop: " | 75 | #define dprintk(fmt, ...) \ |
74 | #define dprintk(msg...) do { \ | 76 | do { \ |
75 | if (debug) printk(KERN_WARNING DRV_PFX msg); \ | 77 | if (debug) \ |
78 | pr_warn(fmt, ##__VA_ARGS__); \ | ||
76 | } while (0) | 79 | } while (0) |
77 | 80 | ||
78 | #define SONY_LAPTOP_DRIVER_VERSION "0.6" | 81 | #define SONY_LAPTOP_DRIVER_VERSION "0.6" |
@@ -124,6 +127,21 @@ MODULE_PARM_DESC(minor, | |||
124 | "default is -1 (automatic)"); | 127 | "default is -1 (automatic)"); |
125 | #endif | 128 | #endif |
126 | 129 | ||
130 | static int kbd_backlight; /* = 1 */ | ||
131 | module_param(kbd_backlight, int, 0444); | ||
132 | MODULE_PARM_DESC(kbd_backlight, | ||
133 | "set this to 0 to disable keyboard backlight, " | ||
134 | "1 to enable it (default: 0)"); | ||
135 | |||
136 | static int kbd_backlight_timeout; /* = 0 */ | ||
137 | module_param(kbd_backlight_timeout, int, 0444); | ||
138 | MODULE_PARM_DESC(kbd_backlight_timeout, | ||
139 | "set this to 0 to set the default 10 seconds timeout, " | ||
140 | "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout " | ||
141 | "(default: 0)"); | ||
142 | |||
143 | static void sony_nc_kbd_backlight_resume(void); | ||
144 | |||
127 | enum sony_nc_rfkill { | 145 | enum sony_nc_rfkill { |
128 | SONY_WIFI, | 146 | SONY_WIFI, |
129 | SONY_BLUETOOTH, | 147 | SONY_BLUETOOTH, |
@@ -235,6 +253,7 @@ static int sony_laptop_input_index[] = { | |||
235 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ | 253 | 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ |
236 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ | 254 | -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ |
237 | 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ | 255 | 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ |
256 | 59, /* 72 SONYPI_EVENT_VENDOR_PRESSED */ | ||
238 | }; | 257 | }; |
239 | 258 | ||
240 | static int sony_laptop_input_keycode_map[] = { | 259 | static int sony_laptop_input_keycode_map[] = { |
@@ -297,6 +316,7 @@ static int sony_laptop_input_keycode_map[] = { | |||
297 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ | 316 | KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ |
298 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ | 317 | KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ |
299 | KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ | 318 | KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ |
319 | KEY_VENDOR, /* 59 SONYPI_EVENT_VENDOR_PRESSED */ | ||
300 | }; | 320 | }; |
301 | 321 | ||
302 | /* release buttons after a short delay if pressed */ | 322 | /* release buttons after a short delay if pressed */ |
@@ -400,7 +420,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) | |||
400 | error = kfifo_alloc(&sony_laptop_input.fifo, | 420 | error = kfifo_alloc(&sony_laptop_input.fifo, |
401 | SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); | 421 | SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); |
402 | if (error) { | 422 | if (error) { |
403 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); | 423 | pr_err("kfifo_alloc failed\n"); |
404 | goto err_dec_users; | 424 | goto err_dec_users; |
405 | } | 425 | } |
406 | 426 | ||
@@ -589,7 +609,7 @@ struct sony_nc_value { | |||
589 | int value; /* current setting */ | 609 | int value; /* current setting */ |
590 | int valid; /* Has ever been set */ | 610 | int valid; /* Has ever been set */ |
591 | int debug; /* active only in debug mode ? */ | 611 | int debug; /* active only in debug mode ? */ |
592 | struct device_attribute devattr; /* sysfs atribute */ | 612 | struct device_attribute devattr; /* sysfs attribute */ |
593 | }; | 613 | }; |
594 | 614 | ||
595 | #define SNC_HANDLE_NAMES(_name, _values...) \ | 615 | #define SNC_HANDLE_NAMES(_name, _values...) \ |
@@ -684,7 +704,7 @@ static int acpi_callgetfunc(acpi_handle handle, char *name, int *result) | |||
684 | return 0; | 704 | return 0; |
685 | } | 705 | } |
686 | 706 | ||
687 | printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n"); | 707 | pr_warn("acpi_callreadfunc failed\n"); |
688 | 708 | ||
689 | return -1; | 709 | return -1; |
690 | } | 710 | } |
@@ -710,8 +730,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, | |||
710 | if (status == AE_OK) { | 730 | if (status == AE_OK) { |
711 | if (result != NULL) { | 731 | if (result != NULL) { |
712 | if (out_obj.type != ACPI_TYPE_INTEGER) { | 732 | if (out_obj.type != ACPI_TYPE_INTEGER) { |
713 | printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad " | 733 | pr_warn("acpi_evaluate_object bad return type\n"); |
714 | "return type\n"); | ||
715 | return -1; | 734 | return -1; |
716 | } | 735 | } |
717 | *result = out_obj.integer.value; | 736 | *result = out_obj.integer.value; |
@@ -719,34 +738,111 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, | |||
719 | return 0; | 738 | return 0; |
720 | } | 739 | } |
721 | 740 | ||
722 | printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n"); | 741 | pr_warn("acpi_evaluate_object failed\n"); |
723 | 742 | ||
724 | return -1; | 743 | return -1; |
725 | } | 744 | } |
726 | 745 | ||
727 | static int sony_find_snc_handle(int handle) | 746 | struct sony_nc_handles { |
747 | u16 cap[0x10]; | ||
748 | struct device_attribute devattr; | ||
749 | }; | ||
750 | |||
751 | static struct sony_nc_handles *handles; | ||
752 | |||
753 | static ssize_t sony_nc_handles_show(struct device *dev, | ||
754 | struct device_attribute *attr, char *buffer) | ||
755 | { | ||
756 | ssize_t len = 0; | ||
757 | int i; | ||
758 | |||
759 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { | ||
760 | len += snprintf(buffer + len, PAGE_SIZE - len, "0x%.4x ", | ||
761 | handles->cap[i]); | ||
762 | } | ||
763 | len += snprintf(buffer + len, PAGE_SIZE - len, "\n"); | ||
764 | |||
765 | return len; | ||
766 | } | ||
767 | |||
768 | static int sony_nc_handles_setup(struct platform_device *pd) | ||
728 | { | 769 | { |
729 | int i; | 770 | int i; |
730 | int result; | 771 | int result; |
731 | 772 | ||
732 | for (i = 0x20; i < 0x30; i++) { | 773 | handles = kzalloc(sizeof(*handles), GFP_KERNEL); |
733 | acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result); | 774 | if (!handles) |
734 | if (result == handle) | 775 | return -ENOMEM; |
735 | return i-0x20; | 776 | |
777 | for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { | ||
778 | if (!acpi_callsetfunc(sony_nc_acpi_handle, | ||
779 | "SN00", i + 0x20, &result)) { | ||
780 | dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", | ||
781 | result, i); | ||
782 | handles->cap[i] = result; | ||
783 | } | ||
784 | } | ||
785 | |||
786 | if (debug) { | ||
787 | sysfs_attr_init(&handles->devattr.attr); | ||
788 | handles->devattr.attr.name = "handles"; | ||
789 | handles->devattr.attr.mode = S_IRUGO; | ||
790 | handles->devattr.show = sony_nc_handles_show; | ||
791 | |||
792 | /* allow reading capabilities via sysfs */ | ||
793 | if (device_create_file(&pd->dev, &handles->devattr)) { | ||
794 | kfree(handles); | ||
795 | handles = NULL; | ||
796 | return -1; | ||
797 | } | ||
798 | } | ||
799 | |||
800 | return 0; | ||
801 | } | ||
802 | |||
803 | static int sony_nc_handles_cleanup(struct platform_device *pd) | ||
804 | { | ||
805 | if (handles) { | ||
806 | if (debug) | ||
807 | device_remove_file(&pd->dev, &handles->devattr); | ||
808 | kfree(handles); | ||
809 | handles = NULL; | ||
736 | } | 810 | } |
811 | return 0; | ||
812 | } | ||
813 | |||
814 | static int sony_find_snc_handle(int handle) | ||
815 | { | ||
816 | int i; | ||
817 | |||
818 | /* not initialized yet, return early */ | ||
819 | if (!handles) | ||
820 | return -1; | ||
737 | 821 | ||
822 | for (i = 0; i < 0x10; i++) { | ||
823 | if (handles->cap[i] == handle) { | ||
824 | dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", | ||
825 | handle, i); | ||
826 | return i; | ||
827 | } | ||
828 | } | ||
829 | dprintk("handle 0x%.4x not found\n", handle); | ||
738 | return -1; | 830 | return -1; |
739 | } | 831 | } |
740 | 832 | ||
741 | static int sony_call_snc_handle(int handle, int argument, int *result) | 833 | static int sony_call_snc_handle(int handle, int argument, int *result) |
742 | { | 834 | { |
835 | int ret = 0; | ||
743 | int offset = sony_find_snc_handle(handle); | 836 | int offset = sony_find_snc_handle(handle); |
744 | 837 | ||
745 | if (offset < 0) | 838 | if (offset < 0) |
746 | return -1; | 839 | return -1; |
747 | 840 | ||
748 | return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, | 841 | ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, |
749 | result); | 842 | result); |
843 | dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument, | ||
844 | *result); | ||
845 | return ret; | ||
750 | } | 846 | } |
751 | 847 | ||
752 | /* | 848 | /* |
@@ -839,6 +935,14 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, | |||
839 | /* | 935 | /* |
840 | * Backlight device | 936 | * Backlight device |
841 | */ | 937 | */ |
938 | struct sony_backlight_props { | ||
939 | struct backlight_device *dev; | ||
940 | int handle; | ||
941 | u8 offset; | ||
942 | u8 maxlvl; | ||
943 | }; | ||
944 | struct sony_backlight_props sony_bl_props; | ||
945 | |||
842 | static int sony_backlight_update_status(struct backlight_device *bd) | 946 | static int sony_backlight_update_status(struct backlight_device *bd) |
843 | { | 947 | { |
844 | return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", | 948 | return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", |
@@ -855,11 +959,40 @@ static int sony_backlight_get_brightness(struct backlight_device *bd) | |||
855 | return value - 1; | 959 | return value - 1; |
856 | } | 960 | } |
857 | 961 | ||
858 | static struct backlight_device *sony_backlight_device; | 962 | static int sony_nc_get_brightness_ng(struct backlight_device *bd) |
859 | static struct backlight_ops sony_backlight_ops = { | 963 | { |
964 | int result; | ||
965 | struct sony_backlight_props *sdev = | ||
966 | (struct sony_backlight_props *)bl_get_data(bd); | ||
967 | |||
968 | sony_call_snc_handle(sdev->handle, 0x0200, &result); | ||
969 | |||
970 | return (result & 0xff) - sdev->offset; | ||
971 | } | ||
972 | |||
973 | static int sony_nc_update_status_ng(struct backlight_device *bd) | ||
974 | { | ||
975 | int value, result; | ||
976 | struct sony_backlight_props *sdev = | ||
977 | (struct sony_backlight_props *)bl_get_data(bd); | ||
978 | |||
979 | value = bd->props.brightness + sdev->offset; | ||
980 | if (sony_call_snc_handle(sdev->handle, 0x0100 | (value << 16), &result)) | ||
981 | return -EIO; | ||
982 | |||
983 | return value; | ||
984 | } | ||
985 | |||
986 | static const struct backlight_ops sony_backlight_ops = { | ||
987 | .options = BL_CORE_SUSPENDRESUME, | ||
860 | .update_status = sony_backlight_update_status, | 988 | .update_status = sony_backlight_update_status, |
861 | .get_brightness = sony_backlight_get_brightness, | 989 | .get_brightness = sony_backlight_get_brightness, |
862 | }; | 990 | }; |
991 | static const struct backlight_ops sony_backlight_ng_ops = { | ||
992 | .options = BL_CORE_SUSPENDRESUME, | ||
993 | .update_status = sony_nc_update_status_ng, | ||
994 | .get_brightness = sony_nc_get_brightness_ng, | ||
995 | }; | ||
863 | 996 | ||
864 | /* | 997 | /* |
865 | * New SNC-only Vaios event mapping to driver known keys | 998 | * New SNC-only Vaios event mapping to driver known keys |
@@ -894,10 +1027,18 @@ static struct sony_nc_event sony_100_events[] = { | |||
894 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, | 1027 | { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, |
895 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, | 1028 | { 0x8C, SONYPI_EVENT_FNKEY_F12 }, |
896 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, | 1029 | { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, |
1030 | { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, | ||
1031 | { 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
897 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, | 1032 | { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, |
898 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 1033 | { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
899 | { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, | 1034 | { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, |
900 | { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 1035 | { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
1036 | { 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED }, | ||
1037 | { 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
1038 | { 0xa5, SONYPI_EVENT_VENDOR_PRESSED }, | ||
1039 | { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
1040 | { 0xa6, SONYPI_EVENT_HELP_PRESSED }, | ||
1041 | { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
901 | { 0, 0 }, | 1042 | { 0, 0 }, |
902 | }; | 1043 | }; |
903 | 1044 | ||
@@ -962,10 +1103,8 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) | |||
962 | } | 1103 | } |
963 | 1104 | ||
964 | if (!key_event->data) | 1105 | if (!key_event->data) |
965 | printk(KERN_INFO DRV_PFX | 1106 | pr_info("Unknown event: 0x%x 0x%x\n", |
966 | "Unknown event: 0x%x 0x%x\n", | 1107 | key_handle, ev); |
967 | key_handle, | ||
968 | ev); | ||
969 | else | 1108 | else |
970 | sony_laptop_report_input_event(ev); | 1109 | sony_laptop_report_input_event(ev); |
971 | } | 1110 | } |
@@ -986,7 +1125,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, | |||
986 | struct acpi_device_info *info; | 1125 | struct acpi_device_info *info; |
987 | 1126 | ||
988 | if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { | 1127 | if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { |
989 | printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", | 1128 | pr_warn("method: name: %4.4s, args %X\n", |
990 | (char *)&info->name, info->param_count); | 1129 | (char *)&info->name, info->param_count); |
991 | 1130 | ||
992 | kfree(info); | 1131 | kfree(info); |
@@ -1027,7 +1166,7 @@ static int sony_nc_resume(struct acpi_device *device) | |||
1027 | ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, | 1166 | ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, |
1028 | item->value, NULL); | 1167 | item->value, NULL); |
1029 | if (ret < 0) { | 1168 | if (ret < 0) { |
1030 | printk("%s: %d\n", __func__, ret); | 1169 | pr_err("%s: %d\n", __func__, ret); |
1031 | break; | 1170 | break; |
1032 | } | 1171 | } |
1033 | } | 1172 | } |
@@ -1044,14 +1183,12 @@ static int sony_nc_resume(struct acpi_device *device) | |||
1044 | sony_nc_function_setup(device); | 1183 | sony_nc_function_setup(device); |
1045 | } | 1184 | } |
1046 | 1185 | ||
1047 | /* set the last requested brightness level */ | ||
1048 | if (sony_backlight_device && | ||
1049 | sony_backlight_update_status(sony_backlight_device) < 0) | ||
1050 | printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n"); | ||
1051 | |||
1052 | /* re-read rfkill state */ | 1186 | /* re-read rfkill state */ |
1053 | sony_nc_rfkill_update(); | 1187 | sony_nc_rfkill_update(); |
1054 | 1188 | ||
1189 | /* restore kbd backlight states */ | ||
1190 | sony_nc_kbd_backlight_resume(); | ||
1191 | |||
1055 | return 0; | 1192 | return 0; |
1056 | } | 1193 | } |
1057 | 1194 | ||
@@ -1131,7 +1268,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, | |||
1131 | return err; | 1268 | return err; |
1132 | } | 1269 | } |
1133 | 1270 | ||
1134 | static void sony_nc_rfkill_update() | 1271 | static void sony_nc_rfkill_update(void) |
1135 | { | 1272 | { |
1136 | enum sony_nc_rfkill i; | 1273 | enum sony_nc_rfkill i; |
1137 | int result; | 1274 | int result; |
@@ -1196,11 +1333,11 @@ static void sony_nc_rfkill_setup(struct acpi_device *device) | |||
1196 | 1333 | ||
1197 | device_enum = (union acpi_object *) buffer.pointer; | 1334 | device_enum = (union acpi_object *) buffer.pointer; |
1198 | if (!device_enum) { | 1335 | if (!device_enum) { |
1199 | pr_err("Invalid SN06 return object\n"); | 1336 | pr_err("No SN06 return object\n"); |
1200 | goto out_no_enum; | 1337 | goto out_no_enum; |
1201 | } | 1338 | } |
1202 | if (device_enum->type != ACPI_TYPE_BUFFER) { | 1339 | if (device_enum->type != ACPI_TYPE_BUFFER) { |
1203 | pr_err("Invalid SN06 return object type 0x%.2x\n", | 1340 | pr_err("Invalid SN06 return object 0x%.2x\n", |
1204 | device_enum->type); | 1341 | device_enum->type); |
1205 | goto out_no_enum; | 1342 | goto out_no_enum; |
1206 | } | 1343 | } |
@@ -1235,6 +1372,306 @@ out_no_enum: | |||
1235 | return; | 1372 | return; |
1236 | } | 1373 | } |
1237 | 1374 | ||
1375 | /* Keyboard backlight feature */ | ||
1376 | #define KBDBL_HANDLER 0x137 | ||
1377 | #define KBDBL_PRESENT 0xB00 | ||
1378 | #define SET_MODE 0xC00 | ||
1379 | #define SET_STATE 0xD00 | ||
1380 | #define SET_TIMEOUT 0xE00 | ||
1381 | |||
1382 | struct kbd_backlight { | ||
1383 | int mode; | ||
1384 | int timeout; | ||
1385 | struct device_attribute mode_attr; | ||
1386 | struct device_attribute timeout_attr; | ||
1387 | }; | ||
1388 | |||
1389 | static struct kbd_backlight *kbdbl_handle; | ||
1390 | |||
1391 | static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) | ||
1392 | { | ||
1393 | int result; | ||
1394 | |||
1395 | if (value > 1) | ||
1396 | return -EINVAL; | ||
1397 | |||
1398 | if (sony_call_snc_handle(KBDBL_HANDLER, | ||
1399 | (value << 0x10) | SET_MODE, &result)) | ||
1400 | return -EIO; | ||
1401 | |||
1402 | /* Try to turn the light on/off immediately */ | ||
1403 | sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE, | ||
1404 | &result); | ||
1405 | |||
1406 | kbdbl_handle->mode = value; | ||
1407 | |||
1408 | return 0; | ||
1409 | } | ||
1410 | |||
1411 | static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, | ||
1412 | struct device_attribute *attr, | ||
1413 | const char *buffer, size_t count) | ||
1414 | { | ||
1415 | int ret = 0; | ||
1416 | unsigned long value; | ||
1417 | |||
1418 | if (count > 31) | ||
1419 | return -EINVAL; | ||
1420 | |||
1421 | if (strict_strtoul(buffer, 10, &value)) | ||
1422 | return -EINVAL; | ||
1423 | |||
1424 | ret = __sony_nc_kbd_backlight_mode_set(value); | ||
1425 | if (ret < 0) | ||
1426 | return ret; | ||
1427 | |||
1428 | return count; | ||
1429 | } | ||
1430 | |||
1431 | static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, | ||
1432 | struct device_attribute *attr, char *buffer) | ||
1433 | { | ||
1434 | ssize_t count = 0; | ||
1435 | count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode); | ||
1436 | return count; | ||
1437 | } | ||
1438 | |||
1439 | static int __sony_nc_kbd_backlight_timeout_set(u8 value) | ||
1440 | { | ||
1441 | int result; | ||
1442 | |||
1443 | if (value > 3) | ||
1444 | return -EINVAL; | ||
1445 | |||
1446 | if (sony_call_snc_handle(KBDBL_HANDLER, | ||
1447 | (value << 0x10) | SET_TIMEOUT, &result)) | ||
1448 | return -EIO; | ||
1449 | |||
1450 | kbdbl_handle->timeout = value; | ||
1451 | |||
1452 | return 0; | ||
1453 | } | ||
1454 | |||
1455 | static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, | ||
1456 | struct device_attribute *attr, | ||
1457 | const char *buffer, size_t count) | ||
1458 | { | ||
1459 | int ret = 0; | ||
1460 | unsigned long value; | ||
1461 | |||
1462 | if (count > 31) | ||
1463 | return -EINVAL; | ||
1464 | |||
1465 | if (strict_strtoul(buffer, 10, &value)) | ||
1466 | return -EINVAL; | ||
1467 | |||
1468 | ret = __sony_nc_kbd_backlight_timeout_set(value); | ||
1469 | if (ret < 0) | ||
1470 | return ret; | ||
1471 | |||
1472 | return count; | ||
1473 | } | ||
1474 | |||
1475 | static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, | ||
1476 | struct device_attribute *attr, char *buffer) | ||
1477 | { | ||
1478 | ssize_t count = 0; | ||
1479 | count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout); | ||
1480 | return count; | ||
1481 | } | ||
1482 | |||
1483 | static int sony_nc_kbd_backlight_setup(struct platform_device *pd) | ||
1484 | { | ||
1485 | int result; | ||
1486 | |||
1487 | if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result)) | ||
1488 | return 0; | ||
1489 | if (!(result & 0x02)) | ||
1490 | return 0; | ||
1491 | |||
1492 | kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL); | ||
1493 | if (!kbdbl_handle) | ||
1494 | return -ENOMEM; | ||
1495 | |||
1496 | sysfs_attr_init(&kbdbl_handle->mode_attr.attr); | ||
1497 | kbdbl_handle->mode_attr.attr.name = "kbd_backlight"; | ||
1498 | kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; | ||
1499 | kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show; | ||
1500 | kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store; | ||
1501 | |||
1502 | sysfs_attr_init(&kbdbl_handle->timeout_attr.attr); | ||
1503 | kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout"; | ||
1504 | kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; | ||
1505 | kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; | ||
1506 | kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; | ||
1507 | |||
1508 | if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr)) | ||
1509 | goto outkzalloc; | ||
1510 | |||
1511 | if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr)) | ||
1512 | goto outmode; | ||
1513 | |||
1514 | __sony_nc_kbd_backlight_mode_set(kbd_backlight); | ||
1515 | __sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout); | ||
1516 | |||
1517 | return 0; | ||
1518 | |||
1519 | outmode: | ||
1520 | device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); | ||
1521 | outkzalloc: | ||
1522 | kfree(kbdbl_handle); | ||
1523 | kbdbl_handle = NULL; | ||
1524 | return -1; | ||
1525 | } | ||
1526 | |||
1527 | static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) | ||
1528 | { | ||
1529 | if (kbdbl_handle) { | ||
1530 | int result; | ||
1531 | |||
1532 | device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); | ||
1533 | device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); | ||
1534 | |||
1535 | /* restore the default hw behaviour */ | ||
1536 | sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result); | ||
1537 | sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result); | ||
1538 | |||
1539 | kfree(kbdbl_handle); | ||
1540 | } | ||
1541 | return 0; | ||
1542 | } | ||
1543 | |||
1544 | static void sony_nc_kbd_backlight_resume(void) | ||
1545 | { | ||
1546 | int ignore = 0; | ||
1547 | |||
1548 | if (!kbdbl_handle) | ||
1549 | return; | ||
1550 | |||
1551 | if (kbdbl_handle->mode == 0) | ||
1552 | sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore); | ||
1553 | |||
1554 | if (kbdbl_handle->timeout != 0) | ||
1555 | sony_call_snc_handle(KBDBL_HANDLER, | ||
1556 | (kbdbl_handle->timeout << 0x10) | SET_TIMEOUT, | ||
1557 | &ignore); | ||
1558 | } | ||
1559 | |||
1560 | static void sony_nc_backlight_ng_read_limits(int handle, | ||
1561 | struct sony_backlight_props *props) | ||
1562 | { | ||
1563 | int offset; | ||
1564 | acpi_status status; | ||
1565 | u8 brlvl, i; | ||
1566 | u8 min = 0xff, max = 0x00; | ||
1567 | struct acpi_object_list params; | ||
1568 | union acpi_object in_obj; | ||
1569 | union acpi_object *lvl_enum; | ||
1570 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1571 | |||
1572 | props->handle = handle; | ||
1573 | props->offset = 0; | ||
1574 | props->maxlvl = 0xff; | ||
1575 | |||
1576 | offset = sony_find_snc_handle(handle); | ||
1577 | if (offset < 0) | ||
1578 | return; | ||
1579 | |||
1580 | /* try to read the boundaries from ACPI tables, if we fail the above | ||
1581 | * defaults should be reasonable | ||
1582 | */ | ||
1583 | params.count = 1; | ||
1584 | params.pointer = &in_obj; | ||
1585 | in_obj.type = ACPI_TYPE_INTEGER; | ||
1586 | in_obj.integer.value = offset; | ||
1587 | status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, | ||
1588 | &buffer); | ||
1589 | if (ACPI_FAILURE(status)) | ||
1590 | return; | ||
1591 | |||
1592 | lvl_enum = (union acpi_object *) buffer.pointer; | ||
1593 | if (!lvl_enum) { | ||
1594 | pr_err("No SN06 return object."); | ||
1595 | return; | ||
1596 | } | ||
1597 | if (lvl_enum->type != ACPI_TYPE_BUFFER) { | ||
1598 | pr_err("Invalid SN06 return object 0x%.2x\n", | ||
1599 | lvl_enum->type); | ||
1600 | goto out_invalid; | ||
1601 | } | ||
1602 | |||
1603 | /* the buffer lists brightness levels available, brightness levels are | ||
1604 | * from 0 to 8 in the array, other values are used by ALS control. | ||
1605 | */ | ||
1606 | for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) { | ||
1607 | |||
1608 | brlvl = *(lvl_enum->buffer.pointer + i); | ||
1609 | dprintk("Brightness level: %d\n", brlvl); | ||
1610 | |||
1611 | if (!brlvl) | ||
1612 | break; | ||
1613 | |||
1614 | if (brlvl > max) | ||
1615 | max = brlvl; | ||
1616 | if (brlvl < min) | ||
1617 | min = brlvl; | ||
1618 | } | ||
1619 | props->offset = min; | ||
1620 | props->maxlvl = max; | ||
1621 | dprintk("Brightness levels: min=%d max=%d\n", props->offset, | ||
1622 | props->maxlvl); | ||
1623 | |||
1624 | out_invalid: | ||
1625 | kfree(buffer.pointer); | ||
1626 | return; | ||
1627 | } | ||
1628 | |||
1629 | static void sony_nc_backlight_setup(void) | ||
1630 | { | ||
1631 | acpi_handle unused; | ||
1632 | int max_brightness = 0; | ||
1633 | const struct backlight_ops *ops = NULL; | ||
1634 | struct backlight_properties props; | ||
1635 | |||
1636 | if (sony_find_snc_handle(0x12f) != -1) { | ||
1637 | ops = &sony_backlight_ng_ops; | ||
1638 | sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props); | ||
1639 | max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; | ||
1640 | |||
1641 | } else if (sony_find_snc_handle(0x137) != -1) { | ||
1642 | ops = &sony_backlight_ng_ops; | ||
1643 | sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props); | ||
1644 | max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; | ||
1645 | |||
1646 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", | ||
1647 | &unused))) { | ||
1648 | ops = &sony_backlight_ops; | ||
1649 | max_brightness = SONY_MAX_BRIGHTNESS - 1; | ||
1650 | |||
1651 | } else | ||
1652 | return; | ||
1653 | |||
1654 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1655 | props.type = BACKLIGHT_PLATFORM; | ||
1656 | props.max_brightness = max_brightness; | ||
1657 | sony_bl_props.dev = backlight_device_register("sony", NULL, | ||
1658 | &sony_bl_props, | ||
1659 | ops, &props); | ||
1660 | |||
1661 | if (IS_ERR(sony_bl_props.dev)) { | ||
1662 | pr_warn("unable to register backlight device\n"); | ||
1663 | sony_bl_props.dev = NULL; | ||
1664 | } else | ||
1665 | sony_bl_props.dev->props.brightness = | ||
1666 | ops->get_brightness(sony_bl_props.dev); | ||
1667 | } | ||
1668 | |||
1669 | static void sony_nc_backlight_cleanup(void) | ||
1670 | { | ||
1671 | if (sony_bl_props.dev) | ||
1672 | backlight_device_unregister(sony_bl_props.dev); | ||
1673 | } | ||
1674 | |||
1238 | static int sony_nc_add(struct acpi_device *device) | 1675 | static int sony_nc_add(struct acpi_device *device) |
1239 | { | 1676 | { |
1240 | acpi_status status; | 1677 | acpi_status status; |
@@ -1242,8 +1679,7 @@ static int sony_nc_add(struct acpi_device *device) | |||
1242 | acpi_handle handle; | 1679 | acpi_handle handle; |
1243 | struct sony_nc_value *item; | 1680 | struct sony_nc_value *item; |
1244 | 1681 | ||
1245 | printk(KERN_INFO DRV_PFX "%s v%s.\n", | 1682 | pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); |
1246 | SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); | ||
1247 | 1683 | ||
1248 | sony_nc_acpi_device = device; | 1684 | sony_nc_acpi_device = device; |
1249 | strcpy(acpi_device_class(device), "sony/hotkey"); | 1685 | strcpy(acpi_device_class(device), "sony/hotkey"); |
@@ -1259,13 +1695,18 @@ static int sony_nc_add(struct acpi_device *device) | |||
1259 | goto outwalk; | 1695 | goto outwalk; |
1260 | } | 1696 | } |
1261 | 1697 | ||
1698 | result = sony_pf_add(); | ||
1699 | if (result) | ||
1700 | goto outpresent; | ||
1701 | |||
1262 | if (debug) { | 1702 | if (debug) { |
1263 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, | 1703 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, |
1264 | 1, sony_walk_callback, NULL, NULL, NULL); | 1704 | sony_nc_acpi_handle, 1, sony_walk_callback, |
1705 | NULL, NULL, NULL); | ||
1265 | if (ACPI_FAILURE(status)) { | 1706 | if (ACPI_FAILURE(status)) { |
1266 | printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n"); | 1707 | pr_warn("unable to walk acpi resources\n"); |
1267 | result = -ENODEV; | 1708 | result = -ENODEV; |
1268 | goto outwalk; | 1709 | goto outpresent; |
1269 | } | 1710 | } |
1270 | } | 1711 | } |
1271 | 1712 | ||
@@ -1278,6 +1719,12 @@ static int sony_nc_add(struct acpi_device *device) | |||
1278 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", | 1719 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", |
1279 | &handle))) { | 1720 | &handle))) { |
1280 | dprintk("Doing SNC setup\n"); | 1721 | dprintk("Doing SNC setup\n"); |
1722 | result = sony_nc_handles_setup(sony_pf_device); | ||
1723 | if (result) | ||
1724 | goto outpresent; | ||
1725 | result = sony_nc_kbd_backlight_setup(sony_pf_device); | ||
1726 | if (result) | ||
1727 | goto outsnc; | ||
1281 | sony_nc_function_setup(device); | 1728 | sony_nc_function_setup(device); |
1282 | sony_nc_rfkill_setup(device); | 1729 | sony_nc_rfkill_setup(device); |
1283 | } | 1730 | } |
@@ -1285,39 +1732,16 @@ static int sony_nc_add(struct acpi_device *device) | |||
1285 | /* setup input devices and helper fifo */ | 1732 | /* setup input devices and helper fifo */ |
1286 | result = sony_laptop_setup_input(device); | 1733 | result = sony_laptop_setup_input(device); |
1287 | if (result) { | 1734 | if (result) { |
1288 | printk(KERN_ERR DRV_PFX | 1735 | pr_err("Unable to create input devices\n"); |
1289 | "Unable to create input devices.\n"); | 1736 | goto outkbdbacklight; |
1290 | goto outwalk; | ||
1291 | } | 1737 | } |
1292 | 1738 | ||
1293 | if (acpi_video_backlight_support()) { | 1739 | if (acpi_video_backlight_support()) { |
1294 | printk(KERN_INFO DRV_PFX "brightness ignored, must be " | 1740 | pr_info("brightness ignored, must be controlled by ACPI video driver\n"); |
1295 | "controlled by ACPI video driver\n"); | 1741 | } else { |
1296 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", | 1742 | sony_nc_backlight_setup(); |
1297 | &handle))) { | ||
1298 | struct backlight_properties props; | ||
1299 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1300 | props.max_brightness = SONY_MAX_BRIGHTNESS - 1; | ||
1301 | sony_backlight_device = backlight_device_register("sony", NULL, | ||
1302 | NULL, | ||
1303 | &sony_backlight_ops, | ||
1304 | &props); | ||
1305 | |||
1306 | if (IS_ERR(sony_backlight_device)) { | ||
1307 | printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); | ||
1308 | sony_backlight_device = NULL; | ||
1309 | } else { | ||
1310 | sony_backlight_device->props.brightness = | ||
1311 | sony_backlight_get_brightness | ||
1312 | (sony_backlight_device); | ||
1313 | } | ||
1314 | |||
1315 | } | 1743 | } |
1316 | 1744 | ||
1317 | result = sony_pf_add(); | ||
1318 | if (result) | ||
1319 | goto outbacklight; | ||
1320 | |||
1321 | /* create sony_pf sysfs attributes related to the SNC device */ | 1745 | /* create sony_pf sysfs attributes related to the SNC device */ |
1322 | for (item = sony_nc_values; item->name; ++item) { | 1746 | for (item = sony_nc_values; item->name; ++item) { |
1323 | 1747 | ||
@@ -1363,14 +1787,19 @@ static int sony_nc_add(struct acpi_device *device) | |||
1363 | for (item = sony_nc_values; item->name; ++item) { | 1787 | for (item = sony_nc_values; item->name; ++item) { |
1364 | device_remove_file(&sony_pf_device->dev, &item->devattr); | 1788 | device_remove_file(&sony_pf_device->dev, &item->devattr); |
1365 | } | 1789 | } |
1366 | sony_pf_remove(); | 1790 | sony_nc_backlight_cleanup(); |
1367 | |||
1368 | outbacklight: | ||
1369 | if (sony_backlight_device) | ||
1370 | backlight_device_unregister(sony_backlight_device); | ||
1371 | 1791 | ||
1372 | sony_laptop_remove_input(); | 1792 | sony_laptop_remove_input(); |
1373 | 1793 | ||
1794 | outkbdbacklight: | ||
1795 | sony_nc_kbd_backlight_cleanup(sony_pf_device); | ||
1796 | |||
1797 | outsnc: | ||
1798 | sony_nc_handles_cleanup(sony_pf_device); | ||
1799 | |||
1800 | outpresent: | ||
1801 | sony_pf_remove(); | ||
1802 | |||
1374 | outwalk: | 1803 | outwalk: |
1375 | sony_nc_rfkill_cleanup(); | 1804 | sony_nc_rfkill_cleanup(); |
1376 | return result; | 1805 | return result; |
@@ -1380,8 +1809,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) | |||
1380 | { | 1809 | { |
1381 | struct sony_nc_value *item; | 1810 | struct sony_nc_value *item; |
1382 | 1811 | ||
1383 | if (sony_backlight_device) | 1812 | sony_nc_backlight_cleanup(); |
1384 | backlight_device_unregister(sony_backlight_device); | ||
1385 | 1813 | ||
1386 | sony_nc_acpi_device = NULL; | 1814 | sony_nc_acpi_device = NULL; |
1387 | 1815 | ||
@@ -1389,6 +1817,8 @@ static int sony_nc_remove(struct acpi_device *device, int type) | |||
1389 | device_remove_file(&sony_pf_device->dev, &item->devattr); | 1817 | device_remove_file(&sony_pf_device->dev, &item->devattr); |
1390 | } | 1818 | } |
1391 | 1819 | ||
1820 | sony_nc_kbd_backlight_cleanup(sony_pf_device); | ||
1821 | sony_nc_handles_cleanup(sony_pf_device); | ||
1392 | sony_pf_remove(); | 1822 | sony_pf_remove(); |
1393 | sony_laptop_remove_input(); | 1823 | sony_laptop_remove_input(); |
1394 | sony_nc_rfkill_cleanup(); | 1824 | sony_nc_rfkill_cleanup(); |
@@ -1427,7 +1857,6 @@ static struct acpi_driver sony_nc_driver = { | |||
1427 | #define SONYPI_DEVICE_TYPE1 0x00000001 | 1857 | #define SONYPI_DEVICE_TYPE1 0x00000001 |
1428 | #define SONYPI_DEVICE_TYPE2 0x00000002 | 1858 | #define SONYPI_DEVICE_TYPE2 0x00000002 |
1429 | #define SONYPI_DEVICE_TYPE3 0x00000004 | 1859 | #define SONYPI_DEVICE_TYPE3 0x00000004 |
1430 | #define SONYPI_DEVICE_TYPE4 0x00000008 | ||
1431 | 1860 | ||
1432 | #define SONYPI_TYPE1_OFFSET 0x04 | 1861 | #define SONYPI_TYPE1_OFFSET 0x04 |
1433 | #define SONYPI_TYPE2_OFFSET 0x12 | 1862 | #define SONYPI_TYPE2_OFFSET 0x12 |
@@ -1573,8 +2002,8 @@ static struct sonypi_event sonypi_blueev[] = { | |||
1573 | 2002 | ||
1574 | /* The set of possible wireless events */ | 2003 | /* The set of possible wireless events */ |
1575 | static struct sonypi_event sonypi_wlessev[] = { | 2004 | static struct sonypi_event sonypi_wlessev[] = { |
1576 | { 0x59, SONYPI_EVENT_WIRELESS_ON }, | 2005 | { 0x59, SONYPI_EVENT_IGNORE }, |
1577 | { 0x5a, SONYPI_EVENT_WIRELESS_OFF }, | 2006 | { 0x5a, SONYPI_EVENT_IGNORE }, |
1578 | { 0, 0 } | 2007 | { 0, 0 } |
1579 | }; | 2008 | }; |
1580 | 2009 | ||
@@ -1831,9 +2260,9 @@ out: | |||
1831 | if (pcidev) | 2260 | if (pcidev) |
1832 | pci_dev_put(pcidev); | 2261 | pci_dev_put(pcidev); |
1833 | 2262 | ||
1834 | printk(KERN_INFO DRV_PFX "detected Type%d model\n", | 2263 | pr_info("detected Type%d model\n", |
1835 | dev->model == SONYPI_DEVICE_TYPE1 ? 1 : | 2264 | dev->model == SONYPI_DEVICE_TYPE1 ? 1 : |
1836 | dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); | 2265 | dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); |
1837 | } | 2266 | } |
1838 | 2267 | ||
1839 | /* camera tests and poweron/poweroff */ | 2268 | /* camera tests and poweron/poweroff */ |
@@ -1879,7 +2308,7 @@ static int __sony_pic_camera_ready(void) | |||
1879 | static int __sony_pic_camera_off(void) | 2308 | static int __sony_pic_camera_off(void) |
1880 | { | 2309 | { |
1881 | if (!camera) { | 2310 | if (!camera) { |
1882 | printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); | 2311 | pr_warn("camera control not enabled\n"); |
1883 | return -ENODEV; | 2312 | return -ENODEV; |
1884 | } | 2313 | } |
1885 | 2314 | ||
@@ -1899,7 +2328,7 @@ static int __sony_pic_camera_on(void) | |||
1899 | int i, j, x; | 2328 | int i, j, x; |
1900 | 2329 | ||
1901 | if (!camera) { | 2330 | if (!camera) { |
1902 | printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); | 2331 | pr_warn("camera control not enabled\n"); |
1903 | return -ENODEV; | 2332 | return -ENODEV; |
1904 | } | 2333 | } |
1905 | 2334 | ||
@@ -1922,7 +2351,7 @@ static int __sony_pic_camera_on(void) | |||
1922 | } | 2351 | } |
1923 | 2352 | ||
1924 | if (j == 0) { | 2353 | if (j == 0) { |
1925 | printk(KERN_WARNING DRV_PFX "failed to power on camera\n"); | 2354 | pr_warn("failed to power on camera\n"); |
1926 | return -ENODEV; | 2355 | return -ENODEV; |
1927 | } | 2356 | } |
1928 | 2357 | ||
@@ -1978,8 +2407,7 @@ int sony_pic_camera_command(int command, u8 value) | |||
1978 | ITERATIONS_SHORT); | 2407 | ITERATIONS_SHORT); |
1979 | break; | 2408 | break; |
1980 | default: | 2409 | default: |
1981 | printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n", | 2410 | pr_err("sony_pic_camera_command invalid: %d\n", command); |
1982 | command); | ||
1983 | break; | 2411 | break; |
1984 | } | 2412 | } |
1985 | mutex_unlock(&spic_dev.lock); | 2413 | mutex_unlock(&spic_dev.lock); |
@@ -2236,7 +2664,7 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, | |||
2236 | mutex_lock(&spic_dev.lock); | 2664 | mutex_lock(&spic_dev.lock); |
2237 | switch (cmd) { | 2665 | switch (cmd) { |
2238 | case SONYPI_IOCGBRT: | 2666 | case SONYPI_IOCGBRT: |
2239 | if (sony_backlight_device == NULL) { | 2667 | if (sony_bl_props.dev == NULL) { |
2240 | ret = -EIO; | 2668 | ret = -EIO; |
2241 | break; | 2669 | break; |
2242 | } | 2670 | } |
@@ -2249,7 +2677,7 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, | |||
2249 | ret = -EFAULT; | 2677 | ret = -EFAULT; |
2250 | break; | 2678 | break; |
2251 | case SONYPI_IOCSBRT: | 2679 | case SONYPI_IOCSBRT: |
2252 | if (sony_backlight_device == NULL) { | 2680 | if (sony_bl_props.dev == NULL) { |
2253 | ret = -EIO; | 2681 | ret = -EIO; |
2254 | break; | 2682 | break; |
2255 | } | 2683 | } |
@@ -2263,8 +2691,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, | |||
2263 | break; | 2691 | break; |
2264 | } | 2692 | } |
2265 | /* sync the backlight device status */ | 2693 | /* sync the backlight device status */ |
2266 | sony_backlight_device->props.brightness = | 2694 | sony_bl_props.dev->props.brightness = |
2267 | sony_backlight_get_brightness(sony_backlight_device); | 2695 | sony_backlight_get_brightness(sony_bl_props.dev); |
2268 | break; | 2696 | break; |
2269 | case SONYPI_IOCGBAT1CAP: | 2697 | case SONYPI_IOCGBAT1CAP: |
2270 | if (ec_read16(SONYPI_BAT1_FULL, &val16)) { | 2698 | if (ec_read16(SONYPI_BAT1_FULL, &val16)) { |
@@ -2360,6 +2788,7 @@ static const struct file_operations sonypi_misc_fops = { | |||
2360 | .release = sonypi_misc_release, | 2788 | .release = sonypi_misc_release, |
2361 | .fasync = sonypi_misc_fasync, | 2789 | .fasync = sonypi_misc_fasync, |
2362 | .unlocked_ioctl = sonypi_misc_ioctl, | 2790 | .unlocked_ioctl = sonypi_misc_ioctl, |
2791 | .llseek = noop_llseek, | ||
2363 | }; | 2792 | }; |
2364 | 2793 | ||
2365 | static struct miscdevice sonypi_misc_device = { | 2794 | static struct miscdevice sonypi_misc_device = { |
@@ -2384,7 +2813,7 @@ static int sonypi_compat_init(void) | |||
2384 | error = | 2813 | error = |
2385 | kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); | 2814 | kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); |
2386 | if (error) { | 2815 | if (error) { |
2387 | printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); | 2816 | pr_err("kfifo_alloc failed\n"); |
2388 | return error; | 2817 | return error; |
2389 | } | 2818 | } |
2390 | 2819 | ||
@@ -2394,12 +2823,12 @@ static int sonypi_compat_init(void) | |||
2394 | sonypi_misc_device.minor = minor; | 2823 | sonypi_misc_device.minor = minor; |
2395 | error = misc_register(&sonypi_misc_device); | 2824 | error = misc_register(&sonypi_misc_device); |
2396 | if (error) { | 2825 | if (error) { |
2397 | printk(KERN_ERR DRV_PFX "misc_register failed\n"); | 2826 | pr_err("misc_register failed\n"); |
2398 | goto err_free_kfifo; | 2827 | goto err_free_kfifo; |
2399 | } | 2828 | } |
2400 | if (minor == -1) | 2829 | if (minor == -1) |
2401 | printk(KERN_INFO DRV_PFX "device allocated minor is %d\n", | 2830 | pr_info("device allocated minor is %d\n", |
2402 | sonypi_misc_device.minor); | 2831 | sonypi_misc_device.minor); |
2403 | 2832 | ||
2404 | return 0; | 2833 | return 0; |
2405 | 2834 | ||
@@ -2458,9 +2887,8 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) | |||
2458 | } | 2887 | } |
2459 | for (i = 0; i < p->interrupt_count; i++) { | 2888 | for (i = 0; i < p->interrupt_count; i++) { |
2460 | if (!p->interrupts[i]) { | 2889 | if (!p->interrupts[i]) { |
2461 | printk(KERN_WARNING DRV_PFX | 2890 | pr_warn("Invalid IRQ %d\n", |
2462 | "Invalid IRQ %d\n", | 2891 | p->interrupts[i]); |
2463 | p->interrupts[i]); | ||
2464 | continue; | 2892 | continue; |
2465 | } | 2893 | } |
2466 | interrupt = kzalloc(sizeof(*interrupt), | 2894 | interrupt = kzalloc(sizeof(*interrupt), |
@@ -2498,14 +2926,14 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) | |||
2498 | ioport->io2.address_length); | 2926 | ioport->io2.address_length); |
2499 | } | 2927 | } |
2500 | else { | 2928 | else { |
2501 | printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n"); | 2929 | pr_err("Unknown SPIC Type, more than 2 IO Ports\n"); |
2502 | return AE_ERROR; | 2930 | return AE_ERROR; |
2503 | } | 2931 | } |
2504 | return AE_OK; | 2932 | return AE_OK; |
2505 | } | 2933 | } |
2506 | default: | 2934 | default: |
2507 | dprintk("Resource %d isn't an IRQ nor an IO port\n", | 2935 | dprintk("Resource %d isn't an IRQ nor an IO port\n", |
2508 | resource->type); | 2936 | resource->type); |
2509 | 2937 | ||
2510 | case ACPI_RESOURCE_TYPE_END_TAG: | 2938 | case ACPI_RESOURCE_TYPE_END_TAG: |
2511 | return AE_OK; | 2939 | return AE_OK; |
@@ -2526,7 +2954,7 @@ static int sony_pic_possible_resources(struct acpi_device *device) | |||
2526 | dprintk("Evaluating _STA\n"); | 2954 | dprintk("Evaluating _STA\n"); |
2527 | result = acpi_bus_get_status(device); | 2955 | result = acpi_bus_get_status(device); |
2528 | if (result) { | 2956 | if (result) { |
2529 | printk(KERN_WARNING DRV_PFX "Unable to read status\n"); | 2957 | pr_warn("Unable to read status\n"); |
2530 | goto end; | 2958 | goto end; |
2531 | } | 2959 | } |
2532 | 2960 | ||
@@ -2542,9 +2970,7 @@ static int sony_pic_possible_resources(struct acpi_device *device) | |||
2542 | status = acpi_walk_resources(device->handle, METHOD_NAME__PRS, | 2970 | status = acpi_walk_resources(device->handle, METHOD_NAME__PRS, |
2543 | sony_pic_read_possible_resource, &spic_dev); | 2971 | sony_pic_read_possible_resource, &spic_dev); |
2544 | if (ACPI_FAILURE(status)) { | 2972 | if (ACPI_FAILURE(status)) { |
2545 | printk(KERN_WARNING DRV_PFX | 2973 | pr_warn("Failure evaluating %s\n", METHOD_NAME__PRS); |
2546 | "Failure evaluating %s\n", | ||
2547 | METHOD_NAME__PRS); | ||
2548 | result = -ENODEV; | 2974 | result = -ENODEV; |
2549 | } | 2975 | } |
2550 | end: | 2976 | end: |
@@ -2657,7 +3083,7 @@ static int sony_pic_enable(struct acpi_device *device, | |||
2657 | 3083 | ||
2658 | /* check for total failure */ | 3084 | /* check for total failure */ |
2659 | if (ACPI_FAILURE(status)) { | 3085 | if (ACPI_FAILURE(status)) { |
2660 | printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n"); | 3086 | pr_err("Error evaluating _SRS\n"); |
2661 | result = -ENODEV; | 3087 | result = -ENODEV; |
2662 | goto end; | 3088 | goto end; |
2663 | } | 3089 | } |
@@ -2713,6 +3139,9 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) | |||
2713 | if (ev == dev->event_types[i].events[j].data) { | 3139 | if (ev == dev->event_types[i].events[j].data) { |
2714 | device_event = | 3140 | device_event = |
2715 | dev->event_types[i].events[j].event; | 3141 | dev->event_types[i].events[j].event; |
3142 | /* some events may require ignoring */ | ||
3143 | if (!device_event) | ||
3144 | return IRQ_HANDLED; | ||
2716 | goto found; | 3145 | goto found; |
2717 | } | 3146 | } |
2718 | } | 3147 | } |
@@ -2732,7 +3161,6 @@ found: | |||
2732 | sony_laptop_report_input_event(device_event); | 3161 | sony_laptop_report_input_event(device_event); |
2733 | acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event); | 3162 | acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event); |
2734 | sonypi_compat_report_event(device_event); | 3163 | sonypi_compat_report_event(device_event); |
2735 | |||
2736 | return IRQ_HANDLED; | 3164 | return IRQ_HANDLED; |
2737 | } | 3165 | } |
2738 | 3166 | ||
@@ -2747,7 +3175,7 @@ static int sony_pic_remove(struct acpi_device *device, int type) | |||
2747 | struct sony_pic_irq *irq, *tmp_irq; | 3175 | struct sony_pic_irq *irq, *tmp_irq; |
2748 | 3176 | ||
2749 | if (sony_pic_disable(device)) { | 3177 | if (sony_pic_disable(device)) { |
2750 | printk(KERN_ERR DRV_PFX "Couldn't disable device.\n"); | 3178 | pr_err("Couldn't disable device\n"); |
2751 | return -ENXIO; | 3179 | return -ENXIO; |
2752 | } | 3180 | } |
2753 | 3181 | ||
@@ -2787,8 +3215,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
2787 | struct sony_pic_ioport *io, *tmp_io; | 3215 | struct sony_pic_ioport *io, *tmp_io; |
2788 | struct sony_pic_irq *irq, *tmp_irq; | 3216 | struct sony_pic_irq *irq, *tmp_irq; |
2789 | 3217 | ||
2790 | printk(KERN_INFO DRV_PFX "%s v%s.\n", | 3218 | pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); |
2791 | SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); | ||
2792 | 3219 | ||
2793 | spic_dev.acpi_dev = device; | 3220 | spic_dev.acpi_dev = device; |
2794 | strcpy(acpi_device_class(device), "sony/hotkey"); | 3221 | strcpy(acpi_device_class(device), "sony/hotkey"); |
@@ -2798,16 +3225,14 @@ static int sony_pic_add(struct acpi_device *device) | |||
2798 | /* read _PRS resources */ | 3225 | /* read _PRS resources */ |
2799 | result = sony_pic_possible_resources(device); | 3226 | result = sony_pic_possible_resources(device); |
2800 | if (result) { | 3227 | if (result) { |
2801 | printk(KERN_ERR DRV_PFX | 3228 | pr_err("Unable to read possible resources\n"); |
2802 | "Unable to read possible resources.\n"); | ||
2803 | goto err_free_resources; | 3229 | goto err_free_resources; |
2804 | } | 3230 | } |
2805 | 3231 | ||
2806 | /* setup input devices and helper fifo */ | 3232 | /* setup input devices and helper fifo */ |
2807 | result = sony_laptop_setup_input(device); | 3233 | result = sony_laptop_setup_input(device); |
2808 | if (result) { | 3234 | if (result) { |
2809 | printk(KERN_ERR DRV_PFX | 3235 | pr_err("Unable to create input devices\n"); |
2810 | "Unable to create input devices.\n"); | ||
2811 | goto err_free_resources; | 3236 | goto err_free_resources; |
2812 | } | 3237 | } |
2813 | 3238 | ||
@@ -2817,7 +3242,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
2817 | /* request io port */ | 3242 | /* request io port */ |
2818 | list_for_each_entry_reverse(io, &spic_dev.ioports, list) { | 3243 | list_for_each_entry_reverse(io, &spic_dev.ioports, list) { |
2819 | if (request_region(io->io1.minimum, io->io1.address_length, | 3244 | if (request_region(io->io1.minimum, io->io1.address_length, |
2820 | "Sony Programable I/O Device")) { | 3245 | "Sony Programmable I/O Device")) { |
2821 | dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n", | 3246 | dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n", |
2822 | io->io1.minimum, io->io1.maximum, | 3247 | io->io1.minimum, io->io1.maximum, |
2823 | io->io1.address_length); | 3248 | io->io1.address_length); |
@@ -2825,7 +3250,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
2825 | if (io->io2.minimum) { | 3250 | if (io->io2.minimum) { |
2826 | if (request_region(io->io2.minimum, | 3251 | if (request_region(io->io2.minimum, |
2827 | io->io2.address_length, | 3252 | io->io2.address_length, |
2828 | "Sony Programable I/O Device")) { | 3253 | "Sony Programmable I/O Device")) { |
2829 | dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n", | 3254 | dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n", |
2830 | io->io2.minimum, io->io2.maximum, | 3255 | io->io2.minimum, io->io2.maximum, |
2831 | io->io2.address_length); | 3256 | io->io2.address_length); |
@@ -2848,7 +3273,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
2848 | } | 3273 | } |
2849 | } | 3274 | } |
2850 | if (!spic_dev.cur_ioport) { | 3275 | if (!spic_dev.cur_ioport) { |
2851 | printk(KERN_ERR DRV_PFX "Failed to request_region.\n"); | 3276 | pr_err("Failed to request_region\n"); |
2852 | result = -ENODEV; | 3277 | result = -ENODEV; |
2853 | goto err_remove_compat; | 3278 | goto err_remove_compat; |
2854 | } | 3279 | } |
@@ -2868,7 +3293,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
2868 | } | 3293 | } |
2869 | } | 3294 | } |
2870 | if (!spic_dev.cur_irq) { | 3295 | if (!spic_dev.cur_irq) { |
2871 | printk(KERN_ERR DRV_PFX "Failed to request_irq.\n"); | 3296 | pr_err("Failed to request_irq\n"); |
2872 | result = -ENODEV; | 3297 | result = -ENODEV; |
2873 | goto err_release_region; | 3298 | goto err_release_region; |
2874 | } | 3299 | } |
@@ -2876,7 +3301,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
2876 | /* set resource status _SRS */ | 3301 | /* set resource status _SRS */ |
2877 | result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); | 3302 | result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); |
2878 | if (result) { | 3303 | if (result) { |
2879 | printk(KERN_ERR DRV_PFX "Couldn't enable device.\n"); | 3304 | pr_err("Couldn't enable device\n"); |
2880 | goto err_free_irq; | 3305 | goto err_free_irq; |
2881 | } | 3306 | } |
2882 | 3307 | ||
@@ -2985,8 +3410,7 @@ static int __init sony_laptop_init(void) | |||
2985 | if (!no_spic && dmi_check_system(sonypi_dmi_table)) { | 3410 | if (!no_spic && dmi_check_system(sonypi_dmi_table)) { |
2986 | result = acpi_bus_register_driver(&sony_pic_driver); | 3411 | result = acpi_bus_register_driver(&sony_pic_driver); |
2987 | if (result) { | 3412 | if (result) { |
2988 | printk(KERN_ERR DRV_PFX | 3413 | pr_err("Unable to register SPIC driver\n"); |
2989 | "Unable to register SPIC driver."); | ||
2990 | goto out; | 3414 | goto out; |
2991 | } | 3415 | } |
2992 | spic_drv_registered = 1; | 3416 | spic_drv_registered = 1; |
@@ -2994,7 +3418,7 @@ static int __init sony_laptop_init(void) | |||
2994 | 3418 | ||
2995 | result = acpi_bus_register_driver(&sony_nc_driver); | 3419 | result = acpi_bus_register_driver(&sony_nc_driver); |
2996 | if (result) { | 3420 | if (result) { |
2997 | printk(KERN_ERR DRV_PFX "Unable to register SNC driver."); | 3421 | pr_err("Unable to register SNC driver\n"); |
2998 | goto out_unregister_pic; | 3422 | goto out_unregister_pic; |
2999 | } | 3423 | } |
3000 | 3424 | ||
diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index 1fe0f1feff71..e24f5ae475af 100644 --- a/drivers/platform/x86/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c | |||
@@ -25,6 +25,8 @@ | |||
25 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 25 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
26 | */ | 26 | */ |
27 | 27 | ||
28 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
29 | |||
28 | #include <linux/kernel.h> | 30 | #include <linux/kernel.h> |
29 | #include <linux/module.h> | 31 | #include <linux/module.h> |
30 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
@@ -40,9 +42,6 @@ | |||
40 | #define TC1100_INSTANCE_WIRELESS 1 | 42 | #define TC1100_INSTANCE_WIRELESS 1 |
41 | #define TC1100_INSTANCE_JOGDIAL 2 | 43 | #define TC1100_INSTANCE_JOGDIAL 2 |
42 | 44 | ||
43 | #define TC1100_LOGPREFIX "tc1100-wmi: " | ||
44 | #define TC1100_INFO KERN_INFO TC1100_LOGPREFIX | ||
45 | |||
46 | MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); | 45 | MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); |
47 | MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); | 46 | MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); |
48 | MODULE_LICENSE("GPL"); | 47 | MODULE_LICENSE("GPL"); |
@@ -162,7 +161,7 @@ set_bool_##value(struct device *dev, struct device_attribute *attr, \ | |||
162 | return -EINVAL; \ | 161 | return -EINVAL; \ |
163 | return count; \ | 162 | return count; \ |
164 | } \ | 163 | } \ |
165 | static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \ | 164 | static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ |
166 | show_bool_##value, set_bool_##value); | 165 | show_bool_##value, set_bool_##value); |
167 | 166 | ||
168 | show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); | 167 | show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); |
@@ -264,7 +263,7 @@ static int __init tc1100_init(void) | |||
264 | if (error) | 263 | if (error) |
265 | goto err_device_del; | 264 | goto err_device_del; |
266 | 265 | ||
267 | printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n"); | 266 | pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n"); |
268 | return 0; | 267 | return 0; |
269 | 268 | ||
270 | err_device_del: | 269 | err_device_del: |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 2d61186ad5a2..26c5b117df22 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -21,6 +21,8 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
25 | |||
24 | #define TPACPI_VERSION "0.24" | 26 | #define TPACPI_VERSION "0.24" |
25 | #define TPACPI_SYSFS_VERSION 0x020700 | 27 | #define TPACPI_SYSFS_VERSION 0x020700 |
26 | 28 | ||
@@ -128,7 +130,8 @@ enum { | |||
128 | }; | 130 | }; |
129 | 131 | ||
130 | /* ACPI HIDs */ | 132 | /* ACPI HIDs */ |
131 | #define TPACPI_ACPI_HKEY_HID "IBM0068" | 133 | #define TPACPI_ACPI_IBM_HKEY_HID "IBM0068" |
134 | #define TPACPI_ACPI_LENOVO_HKEY_HID "LEN0068" | ||
132 | #define TPACPI_ACPI_EC_HID "PNP0C09" | 135 | #define TPACPI_ACPI_EC_HID "PNP0C09" |
133 | 136 | ||
134 | /* Input IDs */ | 137 | /* Input IDs */ |
@@ -181,6 +184,10 @@ enum tpacpi_hkey_event_t { | |||
181 | 184 | ||
182 | /* Misc bay events */ | 185 | /* Misc bay events */ |
183 | TP_HKEY_EV_OPTDRV_EJ = 0x3006, /* opt. drive tray ejected */ | 186 | TP_HKEY_EV_OPTDRV_EJ = 0x3006, /* opt. drive tray ejected */ |
187 | TP_HKEY_EV_HOTPLUG_DOCK = 0x4010, /* docked into hotplug dock | ||
188 | or port replicator */ | ||
189 | TP_HKEY_EV_HOTPLUG_UNDOCK = 0x4011, /* undocked from hotplug | ||
190 | dock or port replicator */ | ||
184 | 191 | ||
185 | /* User-interface events */ | 192 | /* User-interface events */ |
186 | TP_HKEY_EV_LID_CLOSE = 0x5001, /* laptop lid closed */ | 193 | TP_HKEY_EV_LID_CLOSE = 0x5001, /* laptop lid closed */ |
@@ -191,6 +198,10 @@ enum tpacpi_hkey_event_t { | |||
191 | TP_HKEY_EV_PEN_REMOVED = 0x500c, /* tablet pen removed */ | 198 | TP_HKEY_EV_PEN_REMOVED = 0x500c, /* tablet pen removed */ |
192 | TP_HKEY_EV_BRGHT_CHANGED = 0x5010, /* backlight control event */ | 199 | TP_HKEY_EV_BRGHT_CHANGED = 0x5010, /* backlight control event */ |
193 | 200 | ||
201 | /* Key-related user-interface events */ | ||
202 | TP_HKEY_EV_KEY_NUMLOCK = 0x6000, /* NumLock key pressed */ | ||
203 | TP_HKEY_EV_KEY_FN = 0x6005, /* Fn key pressed? E420 */ | ||
204 | |||
194 | /* Thermal events */ | 205 | /* Thermal events */ |
195 | TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */ | 206 | TP_HKEY_EV_ALARM_BAT_HOT = 0x6011, /* battery too hot */ |
196 | TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */ | 207 | TP_HKEY_EV_ALARM_BAT_XHOT = 0x6012, /* battery critically hot */ |
@@ -198,6 +209,10 @@ enum tpacpi_hkey_event_t { | |||
198 | TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */ | 209 | TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */ |
199 | TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */ | 210 | TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */ |
200 | 211 | ||
212 | TP_HKEY_EV_UNK_6040 = 0x6040, /* Related to AC change? | ||
213 | some sort of APM hint, | ||
214 | W520 */ | ||
215 | |||
201 | /* Misc */ | 216 | /* Misc */ |
202 | TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ | 217 | TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */ |
203 | }; | 218 | }; |
@@ -223,17 +238,6 @@ enum tpacpi_hkey_event_t { | |||
223 | 238 | ||
224 | #define TPACPI_MAX_ACPI_ARGS 3 | 239 | #define TPACPI_MAX_ACPI_ARGS 3 |
225 | 240 | ||
226 | /* printk headers */ | ||
227 | #define TPACPI_LOG TPACPI_FILE ": " | ||
228 | #define TPACPI_EMERG KERN_EMERG TPACPI_LOG | ||
229 | #define TPACPI_ALERT KERN_ALERT TPACPI_LOG | ||
230 | #define TPACPI_CRIT KERN_CRIT TPACPI_LOG | ||
231 | #define TPACPI_ERR KERN_ERR TPACPI_LOG | ||
232 | #define TPACPI_WARN KERN_WARNING TPACPI_LOG | ||
233 | #define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG | ||
234 | #define TPACPI_INFO KERN_INFO TPACPI_LOG | ||
235 | #define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG | ||
236 | |||
237 | /* Debugging printk groups */ | 241 | /* Debugging printk groups */ |
238 | #define TPACPI_DBG_ALL 0xffff | 242 | #define TPACPI_DBG_ALL 0xffff |
239 | #define TPACPI_DBG_DISCLOSETASK 0x8000 | 243 | #define TPACPI_DBG_DISCLOSETASK 0x8000 |
@@ -388,34 +392,36 @@ static int tpacpi_uwb_emulstate; | |||
388 | * Debugging helpers | 392 | * Debugging helpers |
389 | */ | 393 | */ |
390 | 394 | ||
391 | #define dbg_printk(a_dbg_level, format, arg...) \ | 395 | #define dbg_printk(a_dbg_level, format, arg...) \ |
392 | do { if (dbg_level & (a_dbg_level)) \ | 396 | do { \ |
393 | printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \ | 397 | if (dbg_level & (a_dbg_level)) \ |
394 | } while (0) | 398 | printk(KERN_DEBUG pr_fmt("%s: " format), \ |
399 | __func__, ##arg); \ | ||
400 | } while (0) | ||
395 | 401 | ||
396 | #ifdef CONFIG_THINKPAD_ACPI_DEBUG | 402 | #ifdef CONFIG_THINKPAD_ACPI_DEBUG |
397 | #define vdbg_printk dbg_printk | 403 | #define vdbg_printk dbg_printk |
398 | static const char *str_supported(int is_supported); | 404 | static const char *str_supported(int is_supported); |
399 | #else | 405 | #else |
400 | #define vdbg_printk(a_dbg_level, format, arg...) \ | 406 | static inline const char *str_supported(int is_supported) { return ""; } |
401 | do { } while (0) | 407 | #define vdbg_printk(a_dbg_level, format, arg...) \ |
408 | no_printk(format, ##arg) | ||
402 | #endif | 409 | #endif |
403 | 410 | ||
404 | static void tpacpi_log_usertask(const char * const what) | 411 | static void tpacpi_log_usertask(const char * const what) |
405 | { | 412 | { |
406 | printk(TPACPI_DEBUG "%s: access by process with PID %d\n", | 413 | printk(KERN_DEBUG pr_fmt("%s: access by process with PID %d\n"), |
407 | what, task_tgid_vnr(current)); | 414 | what, task_tgid_vnr(current)); |
408 | } | 415 | } |
409 | 416 | ||
410 | #define tpacpi_disclose_usertask(what, format, arg...) \ | 417 | #define tpacpi_disclose_usertask(what, format, arg...) \ |
411 | do { \ | 418 | do { \ |
412 | if (unlikely( \ | 419 | if (unlikely((dbg_level & TPACPI_DBG_DISCLOSETASK) && \ |
413 | (dbg_level & TPACPI_DBG_DISCLOSETASK) && \ | 420 | (tpacpi_lifecycle == TPACPI_LIFE_RUNNING))) { \ |
414 | (tpacpi_lifecycle == TPACPI_LIFE_RUNNING))) { \ | 421 | printk(KERN_DEBUG pr_fmt("%s: PID %d: " format), \ |
415 | printk(TPACPI_DEBUG "%s: PID %d: " format, \ | 422 | what, task_tgid_vnr(current), ## arg); \ |
416 | what, task_tgid_vnr(current), ## arg); \ | 423 | } \ |
417 | } \ | 424 | } while (0) |
418 | } while (0) | ||
419 | 425 | ||
420 | /* | 426 | /* |
421 | * Quirk handling helpers | 427 | * Quirk handling helpers |
@@ -534,15 +540,6 @@ TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ | |||
534 | "HKEY", /* all others */ | 540 | "HKEY", /* all others */ |
535 | ); /* 570 */ | 541 | ); /* 570 */ |
536 | 542 | ||
537 | TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ | ||
538 | "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ | ||
539 | "\\_SB.PCI0.VID0", /* 770e */ | ||
540 | "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ | ||
541 | "\\_SB.PCI0.AGP.VGA", /* X100e and a few others */ | ||
542 | "\\_SB.PCI0.AGP.VID", /* all others */ | ||
543 | ); /* R30, R31 */ | ||
544 | |||
545 | |||
546 | /************************************************************************* | 543 | /************************************************************************* |
547 | * ACPI helpers | 544 | * ACPI helpers |
548 | */ | 545 | */ |
@@ -562,7 +559,7 @@ static int acpi_evalf(acpi_handle handle, | |||
562 | int quiet; | 559 | int quiet; |
563 | 560 | ||
564 | if (!*fmt) { | 561 | if (!*fmt) { |
565 | printk(TPACPI_ERR "acpi_evalf() called with empty format\n"); | 562 | pr_err("acpi_evalf() called with empty format\n"); |
566 | return 0; | 563 | return 0; |
567 | } | 564 | } |
568 | 565 | ||
@@ -587,8 +584,9 @@ static int acpi_evalf(acpi_handle handle, | |||
587 | break; | 584 | break; |
588 | /* add more types as needed */ | 585 | /* add more types as needed */ |
589 | default: | 586 | default: |
590 | printk(TPACPI_ERR "acpi_evalf() called " | 587 | pr_err("acpi_evalf() called " |
591 | "with invalid format character '%c'\n", c); | 588 | "with invalid format character '%c'\n", c); |
589 | va_end(ap); | ||
592 | return 0; | 590 | return 0; |
593 | } | 591 | } |
594 | } | 592 | } |
@@ -615,13 +613,13 @@ static int acpi_evalf(acpi_handle handle, | |||
615 | break; | 613 | break; |
616 | /* add more types as needed */ | 614 | /* add more types as needed */ |
617 | default: | 615 | default: |
618 | printk(TPACPI_ERR "acpi_evalf() called " | 616 | pr_err("acpi_evalf() called " |
619 | "with invalid format character '%c'\n", res_type); | 617 | "with invalid format character '%c'\n", res_type); |
620 | return 0; | 618 | return 0; |
621 | } | 619 | } |
622 | 620 | ||
623 | if (!success && !quiet) | 621 | if (!success && !quiet) |
624 | printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %s\n", | 622 | pr_err("acpi_evalf(%s, %s, ...) failed: %s\n", |
625 | method, fmt0, acpi_format_exception(status)); | 623 | method, fmt0, acpi_format_exception(status)); |
626 | 624 | ||
627 | return success; | 625 | return success; |
@@ -765,8 +763,7 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) | |||
765 | 763 | ||
766 | rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device); | 764 | rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device); |
767 | if (rc < 0) { | 765 | if (rc < 0) { |
768 | printk(TPACPI_ERR "acpi_bus_get_device(%s) failed: %d\n", | 766 | pr_err("acpi_bus_get_device(%s) failed: %d\n", ibm->name, rc); |
769 | ibm->name, rc); | ||
770 | return -ENODEV; | 767 | return -ENODEV; |
771 | } | 768 | } |
772 | 769 | ||
@@ -779,12 +776,10 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) | |||
779 | ibm->acpi->type, dispatch_acpi_notify, ibm); | 776 | ibm->acpi->type, dispatch_acpi_notify, ibm); |
780 | if (ACPI_FAILURE(status)) { | 777 | if (ACPI_FAILURE(status)) { |
781 | if (status == AE_ALREADY_EXISTS) { | 778 | if (status == AE_ALREADY_EXISTS) { |
782 | printk(TPACPI_NOTICE | 779 | pr_notice("another device driver is already " |
783 | "another device driver is already " | 780 | "handling %s events\n", ibm->name); |
784 | "handling %s events\n", ibm->name); | ||
785 | } else { | 781 | } else { |
786 | printk(TPACPI_ERR | 782 | pr_err("acpi_install_notify_handler(%s) failed: %s\n", |
787 | "acpi_install_notify_handler(%s) failed: %s\n", | ||
788 | ibm->name, acpi_format_exception(status)); | 783 | ibm->name, acpi_format_exception(status)); |
789 | } | 784 | } |
790 | return -ENODEV; | 785 | return -ENODEV; |
@@ -809,8 +804,7 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) | |||
809 | 804 | ||
810 | ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); | 805 | ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); |
811 | if (!ibm->acpi->driver) { | 806 | if (!ibm->acpi->driver) { |
812 | printk(TPACPI_ERR | 807 | pr_err("failed to allocate memory for ibm->acpi->driver\n"); |
813 | "failed to allocate memory for ibm->acpi->driver\n"); | ||
814 | return -ENOMEM; | 808 | return -ENOMEM; |
815 | } | 809 | } |
816 | 810 | ||
@@ -821,7 +815,7 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) | |||
821 | 815 | ||
822 | rc = acpi_bus_register_driver(ibm->acpi->driver); | 816 | rc = acpi_bus_register_driver(ibm->acpi->driver); |
823 | if (rc < 0) { | 817 | if (rc < 0) { |
824 | printk(TPACPI_ERR "acpi_bus_register_driver(%s) failed: %d\n", | 818 | pr_err("acpi_bus_register_driver(%s) failed: %d\n", |
825 | ibm->name, rc); | 819 | ibm->name, rc); |
826 | kfree(ibm->acpi->driver); | 820 | kfree(ibm->acpi->driver); |
827 | ibm->acpi->driver = NULL; | 821 | ibm->acpi->driver = NULL; |
@@ -1079,15 +1073,14 @@ static int parse_strtoul(const char *buf, | |||
1079 | static void tpacpi_disable_brightness_delay(void) | 1073 | static void tpacpi_disable_brightness_delay(void) |
1080 | { | 1074 | { |
1081 | if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0)) | 1075 | if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0)) |
1082 | printk(TPACPI_NOTICE | 1076 | pr_notice("ACPI backlight control delay disabled\n"); |
1083 | "ACPI backlight control delay disabled\n"); | ||
1084 | } | 1077 | } |
1085 | 1078 | ||
1086 | static void printk_deprecated_attribute(const char * const what, | 1079 | static void printk_deprecated_attribute(const char * const what, |
1087 | const char * const details) | 1080 | const char * const details) |
1088 | { | 1081 | { |
1089 | tpacpi_log_usertask("deprecated sysfs attribute"); | 1082 | tpacpi_log_usertask("deprecated sysfs attribute"); |
1090 | printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " | 1083 | pr_warn("WARNING: sysfs attribute %s is deprecated and " |
1091 | "will be removed. %s\n", | 1084 | "will be removed. %s\n", |
1092 | what, details); | 1085 | what, details); |
1093 | } | 1086 | } |
@@ -1262,8 +1255,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1262 | &tpacpi_rfk_rfkill_ops, | 1255 | &tpacpi_rfk_rfkill_ops, |
1263 | atp_rfk); | 1256 | atp_rfk); |
1264 | if (!atp_rfk || !atp_rfk->rfkill) { | 1257 | if (!atp_rfk || !atp_rfk->rfkill) { |
1265 | printk(TPACPI_ERR | 1258 | pr_err("failed to allocate memory for rfkill class\n"); |
1266 | "failed to allocate memory for rfkill class\n"); | ||
1267 | kfree(atp_rfk); | 1259 | kfree(atp_rfk); |
1268 | return -ENOMEM; | 1260 | return -ENOMEM; |
1269 | } | 1261 | } |
@@ -1273,9 +1265,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1273 | 1265 | ||
1274 | sw_status = (tp_rfkops->get_status)(); | 1266 | sw_status = (tp_rfkops->get_status)(); |
1275 | if (sw_status < 0) { | 1267 | if (sw_status < 0) { |
1276 | printk(TPACPI_ERR | 1268 | pr_err("failed to read initial state for %s, error %d\n", |
1277 | "failed to read initial state for %s, error %d\n", | 1269 | name, sw_status); |
1278 | name, sw_status); | ||
1279 | } else { | 1270 | } else { |
1280 | sw_state = (sw_status == TPACPI_RFK_RADIO_OFF); | 1271 | sw_state = (sw_status == TPACPI_RFK_RADIO_OFF); |
1281 | if (set_default) { | 1272 | if (set_default) { |
@@ -1289,9 +1280,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1289 | 1280 | ||
1290 | res = rfkill_register(atp_rfk->rfkill); | 1281 | res = rfkill_register(atp_rfk->rfkill); |
1291 | if (res < 0) { | 1282 | if (res < 0) { |
1292 | printk(TPACPI_ERR | 1283 | pr_err("failed to register %s rfkill switch: %d\n", name, res); |
1293 | "failed to register %s rfkill switch: %d\n", | ||
1294 | name, res); | ||
1295 | rfkill_destroy(atp_rfk->rfkill); | 1284 | rfkill_destroy(atp_rfk->rfkill); |
1296 | kfree(atp_rfk); | 1285 | kfree(atp_rfk); |
1297 | return res; | 1286 | return res; |
@@ -1299,7 +1288,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, | |||
1299 | 1288 | ||
1300 | tpacpi_rfkill_switches[id] = atp_rfk; | 1289 | tpacpi_rfkill_switches[id] = atp_rfk; |
1301 | 1290 | ||
1302 | printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n", | 1291 | pr_info("rfkill switch %s: radio is %sblocked\n", |
1303 | name, (sw_state || hw_state) ? "" : "un"); | 1292 | name, (sw_state || hw_state) ? "" : "un"); |
1304 | return 0; | 1293 | return 0; |
1305 | } | 1294 | } |
@@ -1823,10 +1812,8 @@ static void __init tpacpi_check_outdated_fw(void) | |||
1823 | * broken, or really stable to begin with, so it is | 1812 | * broken, or really stable to begin with, so it is |
1824 | * best if the user upgrades the firmware anyway. | 1813 | * best if the user upgrades the firmware anyway. |
1825 | */ | 1814 | */ |
1826 | printk(TPACPI_WARN | 1815 | pr_warn("WARNING: Outdated ThinkPad BIOS/EC firmware\n"); |
1827 | "WARNING: Outdated ThinkPad BIOS/EC firmware\n"); | 1816 | pr_warn("WARNING: This firmware may be missing critical bug " |
1828 | printk(TPACPI_WARN | ||
1829 | "WARNING: This firmware may be missing critical bug " | ||
1830 | "fixes and/or important features\n"); | 1817 | "fixes and/or important features\n"); |
1831 | } | 1818 | } |
1832 | } | 1819 | } |
@@ -2115,9 +2102,7 @@ void static hotkey_mask_warn_incomplete_mask(void) | |||
2115 | (hotkey_all_mask | TPACPI_HKEY_NVRAM_KNOWN_MASK); | 2102 | (hotkey_all_mask | TPACPI_HKEY_NVRAM_KNOWN_MASK); |
2116 | 2103 | ||
2117 | if (wantedmask) | 2104 | if (wantedmask) |
2118 | printk(TPACPI_NOTICE | 2105 | pr_notice("required events 0x%08x not enabled!\n", wantedmask); |
2119 | "required events 0x%08x not enabled!\n", | ||
2120 | wantedmask); | ||
2121 | } | 2106 | } |
2122 | 2107 | ||
2123 | /* | 2108 | /* |
@@ -2155,10 +2140,9 @@ static int hotkey_mask_set(u32 mask) | |||
2155 | * a given event. | 2140 | * a given event. |
2156 | */ | 2141 | */ |
2157 | if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) { | 2142 | if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) { |
2158 | printk(TPACPI_NOTICE | 2143 | pr_notice("asked for hotkey mask 0x%08x, but " |
2159 | "asked for hotkey mask 0x%08x, but " | 2144 | "firmware forced it to 0x%08x\n", |
2160 | "firmware forced it to 0x%08x\n", | 2145 | fwmask, hotkey_acpi_mask); |
2161 | fwmask, hotkey_acpi_mask); | ||
2162 | } | 2146 | } |
2163 | 2147 | ||
2164 | if (tpacpi_lifecycle != TPACPI_LIFE_EXITING) | 2148 | if (tpacpi_lifecycle != TPACPI_LIFE_EXITING) |
@@ -2182,13 +2166,11 @@ static int hotkey_user_mask_set(const u32 mask) | |||
2182 | (mask == 0xffff || mask == 0xffffff || | 2166 | (mask == 0xffff || mask == 0xffffff || |
2183 | mask == 0xffffffff)) { | 2167 | mask == 0xffffffff)) { |
2184 | tp_warned.hotkey_mask_ff = 1; | 2168 | tp_warned.hotkey_mask_ff = 1; |
2185 | printk(TPACPI_NOTICE | 2169 | pr_notice("setting the hotkey mask to 0x%08x is likely " |
2186 | "setting the hotkey mask to 0x%08x is likely " | 2170 | "not the best way to go about it\n", mask); |
2187 | "not the best way to go about it\n", mask); | 2171 | pr_notice("please consider using the driver defaults, " |
2188 | printk(TPACPI_NOTICE | 2172 | "and refer to up-to-date thinkpad-acpi " |
2189 | "please consider using the driver defaults, " | 2173 | "documentation\n"); |
2190 | "and refer to up-to-date thinkpad-acpi " | ||
2191 | "documentation\n"); | ||
2192 | } | 2174 | } |
2193 | 2175 | ||
2194 | /* Try to enable what the user asked for, plus whatever we need. | 2176 | /* Try to enable what the user asked for, plus whatever we need. |
@@ -2274,16 +2256,12 @@ static void tpacpi_input_send_key(const unsigned int scancode) | |||
2274 | if (keycode != KEY_RESERVED) { | 2256 | if (keycode != KEY_RESERVED) { |
2275 | mutex_lock(&tpacpi_inputdev_send_mutex); | 2257 | mutex_lock(&tpacpi_inputdev_send_mutex); |
2276 | 2258 | ||
2259 | input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); | ||
2277 | input_report_key(tpacpi_inputdev, keycode, 1); | 2260 | input_report_key(tpacpi_inputdev, keycode, 1); |
2278 | if (keycode == KEY_UNKNOWN) | ||
2279 | input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, | ||
2280 | scancode); | ||
2281 | input_sync(tpacpi_inputdev); | 2261 | input_sync(tpacpi_inputdev); |
2282 | 2262 | ||
2263 | input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); | ||
2283 | input_report_key(tpacpi_inputdev, keycode, 0); | 2264 | input_report_key(tpacpi_inputdev, keycode, 0); |
2284 | if (keycode == KEY_UNKNOWN) | ||
2285 | input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, | ||
2286 | scancode); | ||
2287 | input_sync(tpacpi_inputdev); | 2265 | input_sync(tpacpi_inputdev); |
2288 | 2266 | ||
2289 | mutex_unlock(&tpacpi_inputdev_send_mutex); | 2267 | mutex_unlock(&tpacpi_inputdev_send_mutex); |
@@ -2410,7 +2388,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | |||
2410 | * This code is supposed to duplicate the IBM firmware behaviour: | 2388 | * This code is supposed to duplicate the IBM firmware behaviour: |
2411 | * - Pressing MUTE issues mute hotkey message, even when already mute | 2389 | * - Pressing MUTE issues mute hotkey message, even when already mute |
2412 | * - Pressing Volume up/down issues volume up/down hotkey messages, | 2390 | * - Pressing Volume up/down issues volume up/down hotkey messages, |
2413 | * even when already at maximum or minumum volume | 2391 | * even when already at maximum or minimum volume |
2414 | * - The act of unmuting issues volume up/down notification, | 2392 | * - The act of unmuting issues volume up/down notification, |
2415 | * depending which key was used to unmute | 2393 | * depending which key was used to unmute |
2416 | * | 2394 | * |
@@ -2576,8 +2554,7 @@ static void hotkey_poll_setup(const bool may_warn) | |||
2576 | NULL, TPACPI_NVRAM_KTHREAD_NAME); | 2554 | NULL, TPACPI_NVRAM_KTHREAD_NAME); |
2577 | if (IS_ERR(tpacpi_hotkey_task)) { | 2555 | if (IS_ERR(tpacpi_hotkey_task)) { |
2578 | tpacpi_hotkey_task = NULL; | 2556 | tpacpi_hotkey_task = NULL; |
2579 | printk(TPACPI_ERR | 2557 | pr_err("could not create kernel thread " |
2580 | "could not create kernel thread " | ||
2581 | "for hotkey polling\n"); | 2558 | "for hotkey polling\n"); |
2582 | } | 2559 | } |
2583 | } | 2560 | } |
@@ -2585,11 +2562,10 @@ static void hotkey_poll_setup(const bool may_warn) | |||
2585 | hotkey_poll_stop_sync(); | 2562 | hotkey_poll_stop_sync(); |
2586 | if (may_warn && (poll_driver_mask || poll_user_mask) && | 2563 | if (may_warn && (poll_driver_mask || poll_user_mask) && |
2587 | hotkey_poll_freq == 0) { | 2564 | hotkey_poll_freq == 0) { |
2588 | printk(TPACPI_NOTICE | 2565 | pr_notice("hot keys 0x%08x and/or events 0x%08x " |
2589 | "hot keys 0x%08x and/or events 0x%08x " | 2566 | "require polling, which is currently " |
2590 | "require polling, which is currently " | 2567 | "disabled\n", |
2591 | "disabled\n", | 2568 | poll_user_mask, poll_driver_mask); |
2592 | poll_user_mask, poll_driver_mask); | ||
2593 | } | 2569 | } |
2594 | } | 2570 | } |
2595 | } | 2571 | } |
@@ -2813,13 +2789,13 @@ static ssize_t hotkey_source_mask_store(struct device *dev, | |||
2813 | mutex_unlock(&hotkey_mutex); | 2789 | mutex_unlock(&hotkey_mutex); |
2814 | 2790 | ||
2815 | if (rc < 0) | 2791 | if (rc < 0) |
2816 | printk(TPACPI_ERR "hotkey_source_mask: failed to update the" | 2792 | pr_err("hotkey_source_mask: " |
2817 | "firmware event mask!\n"); | 2793 | "failed to update the firmware event mask!\n"); |
2818 | 2794 | ||
2819 | if (r_ev) | 2795 | if (r_ev) |
2820 | printk(TPACPI_NOTICE "hotkey_source_mask: " | 2796 | pr_notice("hotkey_source_mask: " |
2821 | "some important events were disabled: " | 2797 | "some important events were disabled: 0x%04x\n", |
2822 | "0x%04x\n", r_ev); | 2798 | r_ev); |
2823 | 2799 | ||
2824 | tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t); | 2800 | tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t); |
2825 | 2801 | ||
@@ -2993,7 +2969,7 @@ static void tpacpi_send_radiosw_update(void) | |||
2993 | * rfkill input events, or we will race the rfkill core input | 2969 | * rfkill input events, or we will race the rfkill core input |
2994 | * handler. | 2970 | * handler. |
2995 | * | 2971 | * |
2996 | * tpacpi_inputdev_send_mutex works as a syncronization point | 2972 | * tpacpi_inputdev_send_mutex works as a synchronization point |
2997 | * for the above. | 2973 | * for the above. |
2998 | * | 2974 | * |
2999 | * We optimize to avoid numerous calls to hotkey_get_wlsw. | 2975 | * We optimize to avoid numerous calls to hotkey_get_wlsw. |
@@ -3050,8 +3026,7 @@ static void hotkey_exit(void) | |||
3050 | if (((tp_features.hotkey_mask && | 3026 | if (((tp_features.hotkey_mask && |
3051 | hotkey_mask_set(hotkey_orig_mask)) | | 3027 | hotkey_mask_set(hotkey_orig_mask)) | |
3052 | hotkey_status_set(false)) != 0) | 3028 | hotkey_status_set(false)) != 0) |
3053 | printk(TPACPI_ERR | 3029 | pr_err("failed to restore hot key mask " |
3054 | "failed to restore hot key mask " | ||
3055 | "to BIOS defaults\n"); | 3030 | "to BIOS defaults\n"); |
3056 | } | 3031 | } |
3057 | 3032 | ||
@@ -3290,10 +3265,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3290 | for HKEY interface version 0x100 */ | 3265 | for HKEY interface version 0x100 */ |
3291 | if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { | 3266 | if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { |
3292 | if ((hkeyv >> 8) != 1) { | 3267 | if ((hkeyv >> 8) != 1) { |
3293 | printk(TPACPI_ERR "unknown version of the " | 3268 | pr_err("unknown version of the HKEY interface: 0x%x\n", |
3294 | "HKEY interface: 0x%x\n", hkeyv); | 3269 | hkeyv); |
3295 | printk(TPACPI_ERR "please report this to %s\n", | 3270 | pr_err("please report this to %s\n", TPACPI_MAIL); |
3296 | TPACPI_MAIL); | ||
3297 | } else { | 3271 | } else { |
3298 | /* | 3272 | /* |
3299 | * MHKV 0x100 in A31, R40, R40e, | 3273 | * MHKV 0x100 in A31, R40, R40e, |
@@ -3306,8 +3280,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3306 | /* Paranoia check AND init hotkey_all_mask */ | 3280 | /* Paranoia check AND init hotkey_all_mask */ |
3307 | if (!acpi_evalf(hkey_handle, &hotkey_all_mask, | 3281 | if (!acpi_evalf(hkey_handle, &hotkey_all_mask, |
3308 | "MHKA", "qd")) { | 3282 | "MHKA", "qd")) { |
3309 | printk(TPACPI_ERR | 3283 | pr_err("missing MHKA handler, " |
3310 | "missing MHKA handler, " | ||
3311 | "please report this to %s\n", | 3284 | "please report this to %s\n", |
3312 | TPACPI_MAIL); | 3285 | TPACPI_MAIL); |
3313 | /* Fallback: pre-init for FN+F3,F4,F12 */ | 3286 | /* Fallback: pre-init for FN+F3,F4,F12 */ |
@@ -3345,16 +3318,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3345 | if (dbg_wlswemul) { | 3318 | if (dbg_wlswemul) { |
3346 | tp_features.hotkey_wlsw = 1; | 3319 | tp_features.hotkey_wlsw = 1; |
3347 | radiosw_state = !!tpacpi_wlsw_emulstate; | 3320 | radiosw_state = !!tpacpi_wlsw_emulstate; |
3348 | printk(TPACPI_INFO | 3321 | pr_info("radio switch emulation enabled\n"); |
3349 | "radio switch emulation enabled\n"); | ||
3350 | } else | 3322 | } else |
3351 | #endif | 3323 | #endif |
3352 | /* Not all thinkpads have a hardware radio switch */ | 3324 | /* Not all thinkpads have a hardware radio switch */ |
3353 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | 3325 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { |
3354 | tp_features.hotkey_wlsw = 1; | 3326 | tp_features.hotkey_wlsw = 1; |
3355 | radiosw_state = !!status; | 3327 | radiosw_state = !!status; |
3356 | printk(TPACPI_INFO | 3328 | pr_info("radio switch found; radios are %s\n", |
3357 | "radio switch found; radios are %s\n", | ||
3358 | enabled(status, 0)); | 3329 | enabled(status, 0)); |
3359 | } | 3330 | } |
3360 | if (tp_features.hotkey_wlsw) | 3331 | if (tp_features.hotkey_wlsw) |
@@ -3365,8 +3336,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3365 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { | 3336 | if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { |
3366 | tp_features.hotkey_tablet = 1; | 3337 | tp_features.hotkey_tablet = 1; |
3367 | tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK); | 3338 | tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK); |
3368 | printk(TPACPI_INFO | 3339 | pr_info("possible tablet mode switch found; " |
3369 | "possible tablet mode switch found; " | ||
3370 | "ThinkPad in %s mode\n", | 3340 | "ThinkPad in %s mode\n", |
3371 | (tabletsw_state) ? "tablet" : "laptop"); | 3341 | (tabletsw_state) ? "tablet" : "laptop"); |
3372 | res = add_to_attr_set(hotkey_dev_attributes, | 3342 | res = add_to_attr_set(hotkey_dev_attributes, |
@@ -3384,8 +3354,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3384 | hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, | 3354 | hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, |
3385 | GFP_KERNEL); | 3355 | GFP_KERNEL); |
3386 | if (!hotkey_keycode_map) { | 3356 | if (!hotkey_keycode_map) { |
3387 | printk(TPACPI_ERR | 3357 | pr_err("failed to allocate memory for key map\n"); |
3388 | "failed to allocate memory for key map\n"); | ||
3389 | res = -ENOMEM; | 3358 | res = -ENOMEM; |
3390 | goto err_exit; | 3359 | goto err_exit; |
3391 | } | 3360 | } |
@@ -3428,13 +3397,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3428 | * userspace. tpacpi_detect_brightness_capabilities() must have | 3397 | * userspace. tpacpi_detect_brightness_capabilities() must have |
3429 | * been called before this point */ | 3398 | * been called before this point */ |
3430 | if (tp_features.bright_acpimode && acpi_video_backlight_support()) { | 3399 | if (tp_features.bright_acpimode && acpi_video_backlight_support()) { |
3431 | printk(TPACPI_INFO | 3400 | pr_info("This ThinkPad has standard ACPI backlight " |
3432 | "This ThinkPad has standard ACPI backlight " | 3401 | "brightness control, supported by the ACPI " |
3433 | "brightness control, supported by the ACPI " | 3402 | "video driver\n"); |
3434 | "video driver\n"); | 3403 | pr_notice("Disabling thinkpad-acpi brightness events " |
3435 | printk(TPACPI_NOTICE | 3404 | "by default...\n"); |
3436 | "Disabling thinkpad-acpi brightness events " | ||
3437 | "by default...\n"); | ||
3438 | 3405 | ||
3439 | /* Disable brightness up/down on Lenovo thinkpads when | 3406 | /* Disable brightness up/down on Lenovo thinkpads when |
3440 | * ACPI is handling them, otherwise it is plain impossible | 3407 | * ACPI is handling them, otherwise it is plain impossible |
@@ -3541,8 +3508,7 @@ static bool hotkey_notify_wakeup(const u32 hkey, | |||
3541 | 3508 | ||
3542 | case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */ | 3509 | case TP_HKEY_EV_WKUP_S3_BATLOW: /* Battery on critical low level/S3 */ |
3543 | case TP_HKEY_EV_WKUP_S4_BATLOW: /* Battery on critical low level/S4 */ | 3510 | case TP_HKEY_EV_WKUP_S4_BATLOW: /* Battery on critical low level/S4 */ |
3544 | printk(TPACPI_ALERT | 3511 | pr_alert("EMERGENCY WAKEUP: battery almost empty\n"); |
3545 | "EMERGENCY WAKEUP: battery almost empty\n"); | ||
3546 | /* how to auto-heal: */ | 3512 | /* how to auto-heal: */ |
3547 | /* 2313: woke up from S3, go to S4/S5 */ | 3513 | /* 2313: woke up from S3, go to S4/S5 */ |
3548 | /* 2413: woke up from S4, go to S5 */ | 3514 | /* 2413: woke up from S4, go to S5 */ |
@@ -3553,14 +3519,40 @@ static bool hotkey_notify_wakeup(const u32 hkey, | |||
3553 | } | 3519 | } |
3554 | 3520 | ||
3555 | if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) { | 3521 | if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) { |
3556 | printk(TPACPI_INFO | 3522 | pr_info("woke up due to a hot-unplug request...\n"); |
3557 | "woke up due to a hot-unplug " | ||
3558 | "request...\n"); | ||
3559 | hotkey_wakeup_reason_notify_change(); | 3523 | hotkey_wakeup_reason_notify_change(); |
3560 | } | 3524 | } |
3561 | return true; | 3525 | return true; |
3562 | } | 3526 | } |
3563 | 3527 | ||
3528 | static bool hotkey_notify_dockevent(const u32 hkey, | ||
3529 | bool *send_acpi_ev, | ||
3530 | bool *ignore_acpi_ev) | ||
3531 | { | ||
3532 | /* 0x4000-0x4FFF: dock-related events */ | ||
3533 | *send_acpi_ev = true; | ||
3534 | *ignore_acpi_ev = false; | ||
3535 | |||
3536 | switch (hkey) { | ||
3537 | case TP_HKEY_EV_UNDOCK_ACK: | ||
3538 | /* ACPI undock operation completed after wakeup */ | ||
3539 | hotkey_autosleep_ack = 1; | ||
3540 | pr_info("undocked\n"); | ||
3541 | hotkey_wakeup_hotunplug_complete_notify_change(); | ||
3542 | return true; | ||
3543 | |||
3544 | case TP_HKEY_EV_HOTPLUG_DOCK: /* docked to port replicator */ | ||
3545 | pr_info("docked into hotplug port replicator\n"); | ||
3546 | return true; | ||
3547 | case TP_HKEY_EV_HOTPLUG_UNDOCK: /* undocked from port replicator */ | ||
3548 | pr_info("undocked from hotplug port replicator\n"); | ||
3549 | return true; | ||
3550 | |||
3551 | default: | ||
3552 | return false; | ||
3553 | } | ||
3554 | } | ||
3555 | |||
3564 | static bool hotkey_notify_usrevent(const u32 hkey, | 3556 | static bool hotkey_notify_usrevent(const u32 hkey, |
3565 | bool *send_acpi_ev, | 3557 | bool *send_acpi_ev, |
3566 | bool *ignore_acpi_ev) | 3558 | bool *ignore_acpi_ev) |
@@ -3595,49 +3587,52 @@ static bool hotkey_notify_usrevent(const u32 hkey, | |||
3595 | 3587 | ||
3596 | static void thermal_dump_all_sensors(void); | 3588 | static void thermal_dump_all_sensors(void); |
3597 | 3589 | ||
3598 | static bool hotkey_notify_thermal(const u32 hkey, | 3590 | static bool hotkey_notify_6xxx(const u32 hkey, |
3599 | bool *send_acpi_ev, | 3591 | bool *send_acpi_ev, |
3600 | bool *ignore_acpi_ev) | 3592 | bool *ignore_acpi_ev) |
3601 | { | 3593 | { |
3602 | bool known = true; | 3594 | bool known = true; |
3603 | 3595 | ||
3604 | /* 0x6000-0x6FFF: thermal alarms */ | 3596 | /* 0x6000-0x6FFF: thermal alarms/notices and keyboard events */ |
3605 | *send_acpi_ev = true; | 3597 | *send_acpi_ev = true; |
3606 | *ignore_acpi_ev = false; | 3598 | *ignore_acpi_ev = false; |
3607 | 3599 | ||
3608 | switch (hkey) { | 3600 | switch (hkey) { |
3609 | case TP_HKEY_EV_THM_TABLE_CHANGED: | 3601 | case TP_HKEY_EV_THM_TABLE_CHANGED: |
3610 | printk(TPACPI_INFO | 3602 | pr_info("EC reports that Thermal Table has changed\n"); |
3611 | "EC reports that Thermal Table has changed\n"); | ||
3612 | /* recommended action: do nothing, we don't have | 3603 | /* recommended action: do nothing, we don't have |
3613 | * Lenovo ATM information */ | 3604 | * Lenovo ATM information */ |
3614 | return true; | 3605 | return true; |
3615 | case TP_HKEY_EV_ALARM_BAT_HOT: | 3606 | case TP_HKEY_EV_ALARM_BAT_HOT: |
3616 | printk(TPACPI_CRIT | 3607 | pr_crit("THERMAL ALARM: battery is too hot!\n"); |
3617 | "THERMAL ALARM: battery is too hot!\n"); | ||
3618 | /* recommended action: warn user through gui */ | 3608 | /* recommended action: warn user through gui */ |
3619 | break; | 3609 | break; |
3620 | case TP_HKEY_EV_ALARM_BAT_XHOT: | 3610 | case TP_HKEY_EV_ALARM_BAT_XHOT: |
3621 | printk(TPACPI_ALERT | 3611 | pr_alert("THERMAL EMERGENCY: battery is extremely hot!\n"); |
3622 | "THERMAL EMERGENCY: battery is extremely hot!\n"); | ||
3623 | /* recommended action: immediate sleep/hibernate */ | 3612 | /* recommended action: immediate sleep/hibernate */ |
3624 | break; | 3613 | break; |
3625 | case TP_HKEY_EV_ALARM_SENSOR_HOT: | 3614 | case TP_HKEY_EV_ALARM_SENSOR_HOT: |
3626 | printk(TPACPI_CRIT | 3615 | pr_crit("THERMAL ALARM: " |
3627 | "THERMAL ALARM: " | ||
3628 | "a sensor reports something is too hot!\n"); | 3616 | "a sensor reports something is too hot!\n"); |
3629 | /* recommended action: warn user through gui, that */ | 3617 | /* recommended action: warn user through gui, that */ |
3630 | /* some internal component is too hot */ | 3618 | /* some internal component is too hot */ |
3631 | break; | 3619 | break; |
3632 | case TP_HKEY_EV_ALARM_SENSOR_XHOT: | 3620 | case TP_HKEY_EV_ALARM_SENSOR_XHOT: |
3633 | printk(TPACPI_ALERT | 3621 | pr_alert("THERMAL EMERGENCY: " |
3634 | "THERMAL EMERGENCY: " | 3622 | "a sensor reports something is extremely hot!\n"); |
3635 | "a sensor reports something is extremely hot!\n"); | ||
3636 | /* recommended action: immediate sleep/hibernate */ | 3623 | /* recommended action: immediate sleep/hibernate */ |
3637 | break; | 3624 | break; |
3625 | |||
3626 | case TP_HKEY_EV_KEY_NUMLOCK: | ||
3627 | case TP_HKEY_EV_KEY_FN: | ||
3628 | /* key press events, we just ignore them as long as the EC | ||
3629 | * is still reporting them in the normal keyboard stream */ | ||
3630 | *send_acpi_ev = false; | ||
3631 | *ignore_acpi_ev = true; | ||
3632 | return true; | ||
3633 | |||
3638 | default: | 3634 | default: |
3639 | printk(TPACPI_ALERT | 3635 | pr_warn("unknown possible thermal alarm or keyboard event received\n"); |
3640 | "THERMAL ALERT: unknown thermal alarm received\n"); | ||
3641 | known = false; | 3636 | known = false; |
3642 | } | 3637 | } |
3643 | 3638 | ||
@@ -3654,8 +3649,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
3654 | bool known_ev; | 3649 | bool known_ev; |
3655 | 3650 | ||
3656 | if (event != 0x80) { | 3651 | if (event != 0x80) { |
3657 | printk(TPACPI_ERR | 3652 | pr_err("unknown HKEY notification event %d\n", event); |
3658 | "unknown HKEY notification event %d\n", event); | ||
3659 | /* forward it to userspace, maybe it knows how to handle it */ | 3653 | /* forward it to userspace, maybe it knows how to handle it */ |
3660 | acpi_bus_generate_netlink_event( | 3654 | acpi_bus_generate_netlink_event( |
3661 | ibm->acpi->device->pnp.device_class, | 3655 | ibm->acpi->device->pnp.device_class, |
@@ -3666,7 +3660,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
3666 | 3660 | ||
3667 | while (1) { | 3661 | while (1) { |
3668 | if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { | 3662 | if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) { |
3669 | printk(TPACPI_ERR "failed to retrieve HKEY event\n"); | 3663 | pr_err("failed to retrieve HKEY event\n"); |
3670 | return; | 3664 | return; |
3671 | } | 3665 | } |
3672 | 3666 | ||
@@ -3694,8 +3688,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
3694 | switch (hkey) { | 3688 | switch (hkey) { |
3695 | case TP_HKEY_EV_BAYEJ_ACK: | 3689 | case TP_HKEY_EV_BAYEJ_ACK: |
3696 | hotkey_autosleep_ack = 1; | 3690 | hotkey_autosleep_ack = 1; |
3697 | printk(TPACPI_INFO | 3691 | pr_info("bay ejected\n"); |
3698 | "bay ejected\n"); | ||
3699 | hotkey_wakeup_hotunplug_complete_notify_change(); | 3692 | hotkey_wakeup_hotunplug_complete_notify_change(); |
3700 | known_ev = true; | 3693 | known_ev = true; |
3701 | break; | 3694 | break; |
@@ -3708,16 +3701,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
3708 | } | 3701 | } |
3709 | break; | 3702 | break; |
3710 | case 4: | 3703 | case 4: |
3711 | /* 0x4000-0x4FFF: dock-related wakeups */ | 3704 | /* 0x4000-0x4FFF: dock-related events */ |
3712 | if (hkey == TP_HKEY_EV_UNDOCK_ACK) { | 3705 | known_ev = hotkey_notify_dockevent(hkey, &send_acpi_ev, |
3713 | hotkey_autosleep_ack = 1; | 3706 | &ignore_acpi_ev); |
3714 | printk(TPACPI_INFO | ||
3715 | "undocked\n"); | ||
3716 | hotkey_wakeup_hotunplug_complete_notify_change(); | ||
3717 | known_ev = true; | ||
3718 | } else { | ||
3719 | known_ev = false; | ||
3720 | } | ||
3721 | break; | 3707 | break; |
3722 | case 5: | 3708 | case 5: |
3723 | /* 0x5000-0x5FFF: human interface helpers */ | 3709 | /* 0x5000-0x5FFF: human interface helpers */ |
@@ -3725,8 +3711,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
3725 | &ignore_acpi_ev); | 3711 | &ignore_acpi_ev); |
3726 | break; | 3712 | break; |
3727 | case 6: | 3713 | case 6: |
3728 | /* 0x6000-0x6FFF: thermal alarms */ | 3714 | /* 0x6000-0x6FFF: thermal alarms/notices and |
3729 | known_ev = hotkey_notify_thermal(hkey, &send_acpi_ev, | 3715 | * keyboard events */ |
3716 | known_ev = hotkey_notify_6xxx(hkey, &send_acpi_ev, | ||
3730 | &ignore_acpi_ev); | 3717 | &ignore_acpi_ev); |
3731 | break; | 3718 | break; |
3732 | case 7: | 3719 | case 7: |
@@ -3743,11 +3730,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
3743 | known_ev = false; | 3730 | known_ev = false; |
3744 | } | 3731 | } |
3745 | if (!known_ev) { | 3732 | if (!known_ev) { |
3746 | printk(TPACPI_NOTICE | 3733 | pr_notice("unhandled HKEY event 0x%04x\n", hkey); |
3747 | "unhandled HKEY event 0x%04x\n", hkey); | 3734 | pr_notice("please report the conditions when this " |
3748 | printk(TPACPI_NOTICE | 3735 | "event happened to %s\n", TPACPI_MAIL); |
3749 | "please report the conditions when this " | ||
3750 | "event happened to %s\n", TPACPI_MAIL); | ||
3751 | } | 3736 | } |
3752 | 3737 | ||
3753 | /* Legacy events */ | 3738 | /* Legacy events */ |
@@ -3780,8 +3765,7 @@ static void hotkey_resume(void) | |||
3780 | 3765 | ||
3781 | if (hotkey_status_set(true) < 0 || | 3766 | if (hotkey_status_set(true) < 0 || |
3782 | hotkey_mask_set(hotkey_acpi_mask) < 0) | 3767 | hotkey_mask_set(hotkey_acpi_mask) < 0) |
3783 | printk(TPACPI_ERR | 3768 | pr_err("error while attempting to reset the event " |
3784 | "error while attempting to reset the event " | ||
3785 | "firmware interface\n"); | 3769 | "firmware interface\n"); |
3786 | 3770 | ||
3787 | tpacpi_send_radiosw_update(); | 3771 | tpacpi_send_radiosw_update(); |
@@ -3826,14 +3810,12 @@ static void hotkey_enabledisable_warn(bool enable) | |||
3826 | { | 3810 | { |
3827 | tpacpi_log_usertask("procfs hotkey enable/disable"); | 3811 | tpacpi_log_usertask("procfs hotkey enable/disable"); |
3828 | if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable), | 3812 | if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable), |
3829 | TPACPI_WARN | 3813 | pr_fmt("hotkey enable/disable functionality has been " |
3830 | "hotkey enable/disable functionality has been " | 3814 | "removed from the driver. " |
3831 | "removed from the driver. Hotkeys are always " | 3815 | "Hotkeys are always enabled.\n"))) |
3832 | "enabled\n")) | 3816 | pr_err("Please remove the hotkey=enable module " |
3833 | printk(TPACPI_ERR | 3817 | "parameter, it is deprecated. " |
3834 | "Please remove the hotkey=enable module " | 3818 | "Hotkeys are always enabled.\n"); |
3835 | "parameter, it is deprecated. Hotkeys are always " | ||
3836 | "enabled\n"); | ||
3837 | } | 3819 | } |
3838 | 3820 | ||
3839 | static int hotkey_write(char *buf) | 3821 | static int hotkey_write(char *buf) |
@@ -3882,7 +3864,8 @@ errexit: | |||
3882 | } | 3864 | } |
3883 | 3865 | ||
3884 | static const struct acpi_device_id ibm_htk_device_ids[] = { | 3866 | static const struct acpi_device_id ibm_htk_device_ids[] = { |
3885 | {TPACPI_ACPI_HKEY_HID, 0}, | 3867 | {TPACPI_ACPI_IBM_HKEY_HID, 0}, |
3868 | {TPACPI_ACPI_LENOVO_HKEY_HID, 0}, | ||
3886 | {"", 0}, | 3869 | {"", 0}, |
3887 | }; | 3870 | }; |
3888 | 3871 | ||
@@ -4012,8 +3995,7 @@ static void bluetooth_shutdown(void) | |||
4012 | /* Order firmware to save current state to NVRAM */ | 3995 | /* Order firmware to save current state to NVRAM */ |
4013 | if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", | 3996 | if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", |
4014 | TP_ACPI_BLTH_SAVE_STATE)) | 3997 | TP_ACPI_BLTH_SAVE_STATE)) |
4015 | printk(TPACPI_NOTICE | 3998 | pr_notice("failed to save bluetooth state to NVRAM\n"); |
4016 | "failed to save bluetooth state to NVRAM\n"); | ||
4017 | else | 3999 | else |
4018 | vdbg_printk(TPACPI_DBG_RFKILL, | 4000 | vdbg_printk(TPACPI_DBG_RFKILL, |
4019 | "bluestooth state saved to NVRAM\n"); | 4001 | "bluestooth state saved to NVRAM\n"); |
@@ -4052,8 +4034,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
4052 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 4034 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
4053 | if (dbg_bluetoothemul) { | 4035 | if (dbg_bluetoothemul) { |
4054 | tp_features.bluetooth = 1; | 4036 | tp_features.bluetooth = 1; |
4055 | printk(TPACPI_INFO | 4037 | pr_info("bluetooth switch emulation enabled\n"); |
4056 | "bluetooth switch emulation enabled\n"); | ||
4057 | } else | 4038 | } else |
4058 | #endif | 4039 | #endif |
4059 | if (tp_features.bluetooth && | 4040 | if (tp_features.bluetooth && |
@@ -4204,8 +4185,7 @@ static void wan_shutdown(void) | |||
4204 | /* Order firmware to save current state to NVRAM */ | 4185 | /* Order firmware to save current state to NVRAM */ |
4205 | if (!acpi_evalf(NULL, NULL, "\\WGSV", "vd", | 4186 | if (!acpi_evalf(NULL, NULL, "\\WGSV", "vd", |
4206 | TP_ACPI_WGSV_SAVE_STATE)) | 4187 | TP_ACPI_WGSV_SAVE_STATE)) |
4207 | printk(TPACPI_NOTICE | 4188 | pr_notice("failed to save WWAN state to NVRAM\n"); |
4208 | "failed to save WWAN state to NVRAM\n"); | ||
4209 | else | 4189 | else |
4210 | vdbg_printk(TPACPI_DBG_RFKILL, | 4190 | vdbg_printk(TPACPI_DBG_RFKILL, |
4211 | "WWAN state saved to NVRAM\n"); | 4191 | "WWAN state saved to NVRAM\n"); |
@@ -4242,8 +4222,7 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
4242 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 4222 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
4243 | if (dbg_wwanemul) { | 4223 | if (dbg_wwanemul) { |
4244 | tp_features.wan = 1; | 4224 | tp_features.wan = 1; |
4245 | printk(TPACPI_INFO | 4225 | pr_info("wwan switch emulation enabled\n"); |
4246 | "wwan switch emulation enabled\n"); | ||
4247 | } else | 4226 | } else |
4248 | #endif | 4227 | #endif |
4249 | if (tp_features.wan && | 4228 | if (tp_features.wan && |
@@ -4383,8 +4362,7 @@ static int __init uwb_init(struct ibm_init_struct *iibm) | |||
4383 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 4362 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
4384 | if (dbg_uwbemul) { | 4363 | if (dbg_uwbemul) { |
4385 | tp_features.uwb = 1; | 4364 | tp_features.uwb = 1; |
4386 | printk(TPACPI_INFO | 4365 | pr_info("uwb switch emulation enabled\n"); |
4387 | "uwb switch emulation enabled\n"); | ||
4388 | } else | 4366 | } else |
4389 | #endif | 4367 | #endif |
4390 | if (tp_features.uwb && | 4368 | if (tp_features.uwb && |
@@ -4445,6 +4423,15 @@ static int video_orig_autosw; | |||
4445 | static int video_autosw_get(void); | 4423 | static int video_autosw_get(void); |
4446 | static int video_autosw_set(int enable); | 4424 | static int video_autosw_set(int enable); |
4447 | 4425 | ||
4426 | TPACPI_HANDLE(vid, root, | ||
4427 | "\\_SB.PCI.AGP.VGA", /* 570 */ | ||
4428 | "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ | ||
4429 | "\\_SB.PCI0.VID0", /* 770e */ | ||
4430 | "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ | ||
4431 | "\\_SB.PCI0.AGP.VGA", /* X100e and a few others */ | ||
4432 | "\\_SB.PCI0.AGP.VID", /* all others */ | ||
4433 | ); /* R30, R31 */ | ||
4434 | |||
4448 | TPACPI_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ | 4435 | TPACPI_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ |
4449 | 4436 | ||
4450 | static int __init video_init(struct ibm_init_struct *iibm) | 4437 | static int __init video_init(struct ibm_init_struct *iibm) |
@@ -4488,7 +4475,7 @@ static void video_exit(void) | |||
4488 | dbg_printk(TPACPI_DBG_EXIT, | 4475 | dbg_printk(TPACPI_DBG_EXIT, |
4489 | "restoring original video autoswitch mode\n"); | 4476 | "restoring original video autoswitch mode\n"); |
4490 | if (video_autosw_set(video_orig_autosw)) | 4477 | if (video_autosw_set(video_orig_autosw)) |
4491 | printk(TPACPI_ERR "error while trying to restore original " | 4478 | pr_err("error while trying to restore original " |
4492 | "video autoswitch mode\n"); | 4479 | "video autoswitch mode\n"); |
4493 | } | 4480 | } |
4494 | 4481 | ||
@@ -4561,8 +4548,7 @@ static int video_outputsw_set(int status) | |||
4561 | res = acpi_evalf(vid_handle, NULL, | 4548 | res = acpi_evalf(vid_handle, NULL, |
4562 | "ASWT", "vdd", status * 0x100, 0); | 4549 | "ASWT", "vdd", status * 0x100, 0); |
4563 | if (!autosw && video_autosw_set(autosw)) { | 4550 | if (!autosw && video_autosw_set(autosw)) { |
4564 | printk(TPACPI_ERR | 4551 | pr_err("video auto-switch left enabled due to error\n"); |
4565 | "video auto-switch left enabled due to error\n"); | ||
4566 | return -EIO; | 4552 | return -EIO; |
4567 | } | 4553 | } |
4568 | break; | 4554 | break; |
@@ -4631,8 +4617,7 @@ static int video_outputsw_cycle(void) | |||
4631 | return -ENOSYS; | 4617 | return -ENOSYS; |
4632 | } | 4618 | } |
4633 | if (!autosw && video_autosw_set(autosw)) { | 4619 | if (!autosw && video_autosw_set(autosw)) { |
4634 | printk(TPACPI_ERR | 4620 | pr_err("video auto-switch left enabled due to error\n"); |
4635 | "video auto-switch left enabled due to error\n"); | ||
4636 | return -EIO; | 4621 | return -EIO; |
4637 | } | 4622 | } |
4638 | 4623 | ||
@@ -5349,7 +5334,7 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5349 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, | 5334 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, |
5350 | GFP_KERNEL); | 5335 | GFP_KERNEL); |
5351 | if (!tpacpi_leds) { | 5336 | if (!tpacpi_leds) { |
5352 | printk(TPACPI_ERR "Out of memory for LED data\n"); | 5337 | pr_err("Out of memory for LED data\n"); |
5353 | return -ENOMEM; | 5338 | return -ENOMEM; |
5354 | } | 5339 | } |
5355 | 5340 | ||
@@ -5368,9 +5353,8 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5368 | } | 5353 | } |
5369 | 5354 | ||
5370 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS | 5355 | #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS |
5371 | printk(TPACPI_NOTICE | 5356 | pr_notice("warning: userspace override of important " |
5372 | "warning: userspace override of important " | 5357 | "firmware LEDs is enabled\n"); |
5373 | "firmware LEDs is enabled\n"); | ||
5374 | #endif | 5358 | #endif |
5375 | return 0; | 5359 | return 0; |
5376 | } | 5360 | } |
@@ -5640,17 +5624,16 @@ static void thermal_dump_all_sensors(void) | |||
5640 | if (n <= 0) | 5624 | if (n <= 0) |
5641 | return; | 5625 | return; |
5642 | 5626 | ||
5643 | printk(TPACPI_NOTICE | 5627 | pr_notice("temperatures (Celsius):"); |
5644 | "temperatures (Celsius):"); | ||
5645 | 5628 | ||
5646 | for (i = 0; i < n; i++) { | 5629 | for (i = 0; i < n; i++) { |
5647 | if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA) | 5630 | if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA) |
5648 | printk(KERN_CONT " %d", (int)(t.temp[i] / 1000)); | 5631 | pr_cont(" %d", (int)(t.temp[i] / 1000)); |
5649 | else | 5632 | else |
5650 | printk(KERN_CONT " N/A"); | 5633 | pr_cont(" N/A"); |
5651 | } | 5634 | } |
5652 | 5635 | ||
5653 | printk(KERN_CONT "\n"); | 5636 | pr_cont("\n"); |
5654 | } | 5637 | } |
5655 | 5638 | ||
5656 | /* sysfs temp##_input -------------------------------------------------- */ | 5639 | /* sysfs temp##_input -------------------------------------------------- */ |
@@ -5770,14 +5753,12 @@ static int __init thermal_init(struct ibm_init_struct *iibm) | |||
5770 | if (ta1 == 0) { | 5753 | if (ta1 == 0) { |
5771 | /* This is sheer paranoia, but we handle it anyway */ | 5754 | /* This is sheer paranoia, but we handle it anyway */ |
5772 | if (acpi_tmp7) { | 5755 | if (acpi_tmp7) { |
5773 | printk(TPACPI_ERR | 5756 | pr_err("ThinkPad ACPI EC access misbehaving, " |
5774 | "ThinkPad ACPI EC access misbehaving, " | ||
5775 | "falling back to ACPI TMPx access " | 5757 | "falling back to ACPI TMPx access " |
5776 | "mode\n"); | 5758 | "mode\n"); |
5777 | thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; | 5759 | thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; |
5778 | } else { | 5760 | } else { |
5779 | printk(TPACPI_ERR | 5761 | pr_err("ThinkPad ACPI EC access misbehaving, " |
5780 | "ThinkPad ACPI EC access misbehaving, " | ||
5781 | "disabling thermal sensors access\n"); | 5762 | "disabling thermal sensors access\n"); |
5782 | thermal_read_mode = TPACPI_THERMAL_NONE; | 5763 | thermal_read_mode = TPACPI_THERMAL_NONE; |
5783 | } | 5764 | } |
@@ -6109,7 +6090,7 @@ static void tpacpi_brightness_notify_change(void) | |||
6109 | BACKLIGHT_UPDATE_HOTKEY); | 6090 | BACKLIGHT_UPDATE_HOTKEY); |
6110 | } | 6091 | } |
6111 | 6092 | ||
6112 | static struct backlight_ops ibm_backlight_data = { | 6093 | static const struct backlight_ops ibm_backlight_data = { |
6113 | .get_brightness = brightness_get, | 6094 | .get_brightness = brightness_get, |
6114 | .update_status = brightness_update_status, | 6095 | .update_status = brightness_update_status, |
6115 | }; | 6096 | }; |
@@ -6130,8 +6111,8 @@ static int __init tpacpi_query_bcl_levels(acpi_handle handle) | |||
6130 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, "_BCL", NULL, &buffer))) { | 6111 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, "_BCL", NULL, &buffer))) { |
6131 | obj = (union acpi_object *)buffer.pointer; | 6112 | obj = (union acpi_object *)buffer.pointer; |
6132 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | 6113 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { |
6133 | printk(TPACPI_ERR "Unknown _BCL data, " | 6114 | pr_err("Unknown _BCL data, please report this to %s\n", |
6134 | "please report this to %s\n", TPACPI_MAIL); | 6115 | TPACPI_MAIL); |
6135 | rc = 0; | 6116 | rc = 0; |
6136 | } else { | 6117 | } else { |
6137 | rc = obj->package.count; | 6118 | rc = obj->package.count; |
@@ -6215,18 +6196,15 @@ static void __init tpacpi_detect_brightness_capabilities(void) | |||
6215 | switch (b) { | 6196 | switch (b) { |
6216 | case 16: | 6197 | case 16: |
6217 | bright_maxlvl = 15; | 6198 | bright_maxlvl = 15; |
6218 | printk(TPACPI_INFO | 6199 | pr_info("detected a 16-level brightness capable ThinkPad\n"); |
6219 | "detected a 16-level brightness capable ThinkPad\n"); | ||
6220 | break; | 6200 | break; |
6221 | case 8: | 6201 | case 8: |
6222 | case 0: | 6202 | case 0: |
6223 | bright_maxlvl = 7; | 6203 | bright_maxlvl = 7; |
6224 | printk(TPACPI_INFO | 6204 | pr_info("detected a 8-level brightness capable ThinkPad\n"); |
6225 | "detected a 8-level brightness capable ThinkPad\n"); | ||
6226 | break; | 6205 | break; |
6227 | default: | 6206 | default: |
6228 | printk(TPACPI_ERR | 6207 | pr_err("Unsupported brightness interface, " |
6229 | "Unsupported brightness interface, " | ||
6230 | "please contact %s\n", TPACPI_MAIL); | 6208 | "please contact %s\n", TPACPI_MAIL); |
6231 | tp_features.bright_unkfw = 1; | 6209 | tp_features.bright_unkfw = 1; |
6232 | bright_maxlvl = b - 1; | 6210 | bright_maxlvl = b - 1; |
@@ -6261,22 +6239,19 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6261 | 6239 | ||
6262 | if (acpi_video_backlight_support()) { | 6240 | if (acpi_video_backlight_support()) { |
6263 | if (brightness_enable > 1) { | 6241 | if (brightness_enable > 1) { |
6264 | printk(TPACPI_INFO | 6242 | pr_info("Standard ACPI backlight interface " |
6265 | "Standard ACPI backlight interface " | 6243 | "available, not loading native one\n"); |
6266 | "available, not loading native one.\n"); | ||
6267 | return 1; | 6244 | return 1; |
6268 | } else if (brightness_enable == 1) { | 6245 | } else if (brightness_enable == 1) { |
6269 | printk(TPACPI_WARN | 6246 | pr_warn("Cannot enable backlight brightness support, " |
6270 | "Cannot enable backlight brightness support, " | ||
6271 | "ACPI is already handling it. Refer to the " | 6247 | "ACPI is already handling it. Refer to the " |
6272 | "acpi_backlight kernel parameter\n"); | 6248 | "acpi_backlight kernel parameter.\n"); |
6273 | return 1; | 6249 | return 1; |
6274 | } | 6250 | } |
6275 | } else if (tp_features.bright_acpimode && brightness_enable > 1) { | 6251 | } else if (tp_features.bright_acpimode && brightness_enable > 1) { |
6276 | printk(TPACPI_NOTICE | 6252 | pr_notice("Standard ACPI backlight interface not " |
6277 | "Standard ACPI backlight interface not " | 6253 | "available, thinkpad_acpi native " |
6278 | "available, thinkpad_acpi native " | 6254 | "brightness control enabled\n"); |
6279 | "brightness control enabled\n"); | ||
6280 | } | 6255 | } |
6281 | 6256 | ||
6282 | /* | 6257 | /* |
@@ -6310,6 +6285,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6310 | return 1; | 6285 | return 1; |
6311 | 6286 | ||
6312 | memset(&props, 0, sizeof(struct backlight_properties)); | 6287 | memset(&props, 0, sizeof(struct backlight_properties)); |
6288 | props.type = BACKLIGHT_PLATFORM; | ||
6313 | props.max_brightness = bright_maxlvl; | 6289 | props.max_brightness = bright_maxlvl; |
6314 | props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; | 6290 | props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; |
6315 | ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, | 6291 | ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, |
@@ -6319,19 +6295,17 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6319 | if (IS_ERR(ibm_backlight_device)) { | 6295 | if (IS_ERR(ibm_backlight_device)) { |
6320 | int rc = PTR_ERR(ibm_backlight_device); | 6296 | int rc = PTR_ERR(ibm_backlight_device); |
6321 | ibm_backlight_device = NULL; | 6297 | ibm_backlight_device = NULL; |
6322 | printk(TPACPI_ERR "Could not register backlight device\n"); | 6298 | pr_err("Could not register backlight device\n"); |
6323 | return rc; | 6299 | return rc; |
6324 | } | 6300 | } |
6325 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, | 6301 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, |
6326 | "brightness is supported\n"); | 6302 | "brightness is supported\n"); |
6327 | 6303 | ||
6328 | if (quirks & TPACPI_BRGHT_Q_ASK) { | 6304 | if (quirks & TPACPI_BRGHT_Q_ASK) { |
6329 | printk(TPACPI_NOTICE | 6305 | pr_notice("brightness: will use unverified default: " |
6330 | "brightness: will use unverified default: " | 6306 | "brightness_mode=%d\n", brightness_mode); |
6331 | "brightness_mode=%d\n", brightness_mode); | 6307 | pr_notice("brightness: please report to %s whether it works well " |
6332 | printk(TPACPI_NOTICE | 6308 | "or not on your ThinkPad\n", TPACPI_MAIL); |
6333 | "brightness: please report to %s whether it works well " | ||
6334 | "or not on your ThinkPad\n", TPACPI_MAIL); | ||
6335 | } | 6309 | } |
6336 | 6310 | ||
6337 | /* Added by mistake in early 2007. Probably useless, but it could | 6311 | /* Added by mistake in early 2007. Probably useless, but it could |
@@ -6345,7 +6319,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6345 | "as change notification\n"); | 6319 | "as change notification\n"); |
6346 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | 6320 | tpacpi_hotkey_driver_mask_set(hotkey_driver_mask |
6347 | | TP_ACPI_HKEY_BRGHTUP_MASK | 6321 | | TP_ACPI_HKEY_BRGHTUP_MASK |
6348 | | TP_ACPI_HKEY_BRGHTDWN_MASK);; | 6322 | | TP_ACPI_HKEY_BRGHTDWN_MASK); |
6349 | return 0; | 6323 | return 0; |
6350 | } | 6324 | } |
6351 | 6325 | ||
@@ -6804,8 +6778,7 @@ static int __init volume_create_alsa_mixer(void) | |||
6804 | rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, | 6778 | rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, |
6805 | sizeof(struct tpacpi_alsa_data), &card); | 6779 | sizeof(struct tpacpi_alsa_data), &card); |
6806 | if (rc < 0 || !card) { | 6780 | if (rc < 0 || !card) { |
6807 | printk(TPACPI_ERR | 6781 | pr_err("Failed to create ALSA card structures: %d\n", rc); |
6808 | "Failed to create ALSA card structures: %d\n", rc); | ||
6809 | return 1; | 6782 | return 1; |
6810 | } | 6783 | } |
6811 | 6784 | ||
@@ -6839,9 +6812,8 @@ static int __init volume_create_alsa_mixer(void) | |||
6839 | ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL); | 6812 | ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL); |
6840 | rc = snd_ctl_add(card, ctl_vol); | 6813 | rc = snd_ctl_add(card, ctl_vol); |
6841 | if (rc < 0) { | 6814 | if (rc < 0) { |
6842 | printk(TPACPI_ERR | 6815 | pr_err("Failed to create ALSA volume control: %d\n", |
6843 | "Failed to create ALSA volume control: %d\n", | 6816 | rc); |
6844 | rc); | ||
6845 | goto err_exit; | 6817 | goto err_exit; |
6846 | } | 6818 | } |
6847 | data->ctl_vol_id = &ctl_vol->id; | 6819 | data->ctl_vol_id = &ctl_vol->id; |
@@ -6850,8 +6822,7 @@ static int __init volume_create_alsa_mixer(void) | |||
6850 | ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); | 6822 | ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); |
6851 | rc = snd_ctl_add(card, ctl_mute); | 6823 | rc = snd_ctl_add(card, ctl_mute); |
6852 | if (rc < 0) { | 6824 | if (rc < 0) { |
6853 | printk(TPACPI_ERR "Failed to create ALSA mute control: %d\n", | 6825 | pr_err("Failed to create ALSA mute control: %d\n", rc); |
6854 | rc); | ||
6855 | goto err_exit; | 6826 | goto err_exit; |
6856 | } | 6827 | } |
6857 | data->ctl_mute_id = &ctl_mute->id; | 6828 | data->ctl_mute_id = &ctl_mute->id; |
@@ -6859,7 +6830,7 @@ static int __init volume_create_alsa_mixer(void) | |||
6859 | snd_card_set_dev(card, &tpacpi_pdev->dev); | 6830 | snd_card_set_dev(card, &tpacpi_pdev->dev); |
6860 | rc = snd_card_register(card); | 6831 | rc = snd_card_register(card); |
6861 | if (rc < 0) { | 6832 | if (rc < 0) { |
6862 | printk(TPACPI_ERR "Failed to register ALSA card: %d\n", rc); | 6833 | pr_err("Failed to register ALSA card: %d\n", rc); |
6863 | goto err_exit; | 6834 | goto err_exit; |
6864 | } | 6835 | } |
6865 | 6836 | ||
@@ -6915,9 +6886,8 @@ static int __init volume_init(struct ibm_init_struct *iibm) | |||
6915 | return -EINVAL; | 6886 | return -EINVAL; |
6916 | 6887 | ||
6917 | if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { | 6888 | if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { |
6918 | printk(TPACPI_ERR | 6889 | pr_err("UCMS step volume mode not implemented, " |
6919 | "UCMS step volume mode not implemented, " | 6890 | "please contact %s\n", TPACPI_MAIL); |
6920 | "please contact %s\n", TPACPI_MAIL); | ||
6921 | return 1; | 6891 | return 1; |
6922 | } | 6892 | } |
6923 | 6893 | ||
@@ -6981,13 +6951,11 @@ static int __init volume_init(struct ibm_init_struct *iibm) | |||
6981 | 6951 | ||
6982 | rc = volume_create_alsa_mixer(); | 6952 | rc = volume_create_alsa_mixer(); |
6983 | if (rc) { | 6953 | if (rc) { |
6984 | printk(TPACPI_ERR | 6954 | pr_err("Could not create the ALSA mixer interface\n"); |
6985 | "Could not create the ALSA mixer interface\n"); | ||
6986 | return rc; | 6955 | return rc; |
6987 | } | 6956 | } |
6988 | 6957 | ||
6989 | printk(TPACPI_INFO | 6958 | pr_info("Console audio control enabled, mode: %s\n", |
6990 | "Console audio control enabled, mode: %s\n", | ||
6991 | (volume_control_allowed) ? | 6959 | (volume_control_allowed) ? |
6992 | "override (read/write)" : | 6960 | "override (read/write)" : |
6993 | "monitor (read only)"); | 6961 | "monitor (read only)"); |
@@ -7049,12 +7017,10 @@ static int volume_write(char *buf) | |||
7049 | if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) { | 7017 | if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) { |
7050 | if (unlikely(!tp_warned.volume_ctrl_forbidden)) { | 7018 | if (unlikely(!tp_warned.volume_ctrl_forbidden)) { |
7051 | tp_warned.volume_ctrl_forbidden = 1; | 7019 | tp_warned.volume_ctrl_forbidden = 1; |
7052 | printk(TPACPI_NOTICE | 7020 | pr_notice("Console audio control in monitor mode, " |
7053 | "Console audio control in monitor mode, " | 7021 | "changes are not allowed\n"); |
7054 | "changes are not allowed.\n"); | 7022 | pr_notice("Use the volume_control=1 module parameter " |
7055 | printk(TPACPI_NOTICE | 7023 | "to enable volume control\n"); |
7056 | "Use the volume_control=1 module parameter " | ||
7057 | "to enable volume control\n"); | ||
7058 | } | 7024 | } |
7059 | return -EPERM; | 7025 | return -EPERM; |
7060 | } | 7026 | } |
@@ -7129,8 +7095,7 @@ static void inline volume_alsa_notify_change(void) | |||
7129 | 7095 | ||
7130 | static int __init volume_init(struct ibm_init_struct *iibm) | 7096 | static int __init volume_init(struct ibm_init_struct *iibm) |
7131 | { | 7097 | { |
7132 | printk(TPACPI_INFO | 7098 | pr_info("volume: disabled as there is no ALSA support in this kernel\n"); |
7133 | "volume: disabled as there is no ALSA support in this kernel\n"); | ||
7134 | 7099 | ||
7135 | return 1; | 7100 | return 1; |
7136 | } | 7101 | } |
@@ -7193,7 +7158,7 @@ static struct ibm_struct volume_driver_data = { | |||
7193 | * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41) | 7158 | * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41) |
7194 | * | 7159 | * |
7195 | * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at | 7160 | * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at |
7196 | * boot. Apparently the EC does not intialize it, so unless ACPI DSDT | 7161 | * boot. Apparently the EC does not initialize it, so unless ACPI DSDT |
7197 | * does so, its initial value is meaningless (0x07). | 7162 | * does so, its initial value is meaningless (0x07). |
7198 | * | 7163 | * |
7199 | * For firmware bugs, refer to: | 7164 | * For firmware bugs, refer to: |
@@ -7337,9 +7302,8 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ | |||
7337 | static void fan_quirk1_setup(void) | 7302 | static void fan_quirk1_setup(void) |
7338 | { | 7303 | { |
7339 | if (fan_control_initial_status == 0x07) { | 7304 | if (fan_control_initial_status == 0x07) { |
7340 | printk(TPACPI_NOTICE | 7305 | pr_notice("fan_init: initial fan status is unknown, " |
7341 | "fan_init: initial fan status is unknown, " | 7306 | "assuming it is in auto mode\n"); |
7342 | "assuming it is in auto mode\n"); | ||
7343 | tp_features.fan_ctrl_status_undef = 1; | 7307 | tp_features.fan_ctrl_status_undef = 1; |
7344 | } | 7308 | } |
7345 | } | 7309 | } |
@@ -7726,8 +7690,7 @@ static void fan_watchdog_reset(void) | |||
7726 | if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task, | 7690 | if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task, |
7727 | msecs_to_jiffies(fan_watchdog_maxinterval | 7691 | msecs_to_jiffies(fan_watchdog_maxinterval |
7728 | * 1000))) { | 7692 | * 1000))) { |
7729 | printk(TPACPI_ERR | 7693 | pr_err("failed to queue the fan watchdog, " |
7730 | "failed to queue the fan watchdog, " | ||
7731 | "watchdog will not trigger\n"); | 7694 | "watchdog will not trigger\n"); |
7732 | } | 7695 | } |
7733 | } else | 7696 | } else |
@@ -7741,11 +7704,11 @@ static void fan_watchdog_fire(struct work_struct *ignored) | |||
7741 | if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) | 7704 | if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING) |
7742 | return; | 7705 | return; |
7743 | 7706 | ||
7744 | printk(TPACPI_NOTICE "fan watchdog: enabling fan\n"); | 7707 | pr_notice("fan watchdog: enabling fan\n"); |
7745 | rc = fan_set_enable(); | 7708 | rc = fan_set_enable(); |
7746 | if (rc < 0) { | 7709 | if (rc < 0) { |
7747 | printk(TPACPI_ERR "fan watchdog: error %d while enabling fan, " | 7710 | pr_err("fan watchdog: error %d while enabling fan, " |
7748 | "will try again later...\n", -rc); | 7711 | "will try again later...\n", -rc); |
7749 | /* reschedule for later */ | 7712 | /* reschedule for later */ |
7750 | fan_watchdog_reset(); | 7713 | fan_watchdog_reset(); |
7751 | } | 7714 | } |
@@ -8049,8 +8012,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
8049 | "secondary fan support enabled\n"); | 8012 | "secondary fan support enabled\n"); |
8050 | } | 8013 | } |
8051 | } else { | 8014 | } else { |
8052 | printk(TPACPI_ERR | 8015 | pr_err("ThinkPad ACPI EC access misbehaving, " |
8053 | "ThinkPad ACPI EC access misbehaving, " | ||
8054 | "fan status and control unavailable\n"); | 8016 | "fan status and control unavailable\n"); |
8055 | return 1; | 8017 | return 1; |
8056 | } | 8018 | } |
@@ -8150,9 +8112,8 @@ static void fan_suspend(pm_message_t state) | |||
8150 | fan_control_resume_level = 0; | 8112 | fan_control_resume_level = 0; |
8151 | rc = fan_get_status_safe(&fan_control_resume_level); | 8113 | rc = fan_get_status_safe(&fan_control_resume_level); |
8152 | if (rc < 0) | 8114 | if (rc < 0) |
8153 | printk(TPACPI_NOTICE | 8115 | pr_notice("failed to read fan level for later " |
8154 | "failed to read fan level for later " | 8116 | "restore during resume: %d\n", rc); |
8155 | "restore during resume: %d\n", rc); | ||
8156 | 8117 | ||
8157 | /* if it is undefined, don't attempt to restore it. | 8118 | /* if it is undefined, don't attempt to restore it. |
8158 | * KEEP THIS LAST */ | 8119 | * KEEP THIS LAST */ |
@@ -8207,13 +8168,11 @@ static void fan_resume(void) | |||
8207 | return; | 8168 | return; |
8208 | } | 8169 | } |
8209 | if (do_set) { | 8170 | if (do_set) { |
8210 | printk(TPACPI_NOTICE | 8171 | pr_notice("restoring fan level to 0x%02x\n", |
8211 | "restoring fan level to 0x%02x\n", | 8172 | fan_control_resume_level); |
8212 | fan_control_resume_level); | ||
8213 | rc = fan_set_level_safe(fan_control_resume_level); | 8173 | rc = fan_set_level_safe(fan_control_resume_level); |
8214 | if (rc < 0) | 8174 | if (rc < 0) |
8215 | printk(TPACPI_NOTICE | 8175 | pr_notice("failed to restore fan level: %d\n", rc); |
8216 | "failed to restore fan level: %d\n", rc); | ||
8217 | } | 8176 | } |
8218 | } | 8177 | } |
8219 | 8178 | ||
@@ -8305,8 +8264,8 @@ static int fan_write_cmd_level(const char *cmd, int *rc) | |||
8305 | 8264 | ||
8306 | *rc = fan_set_level_safe(level); | 8265 | *rc = fan_set_level_safe(level); |
8307 | if (*rc == -ENXIO) | 8266 | if (*rc == -ENXIO) |
8308 | printk(TPACPI_ERR "level command accepted for unsupported " | 8267 | pr_err("level command accepted for unsupported access mode %d\n", |
8309 | "access mode %d", fan_control_access_mode); | 8268 | fan_control_access_mode); |
8310 | else if (!*rc) | 8269 | else if (!*rc) |
8311 | tpacpi_disclose_usertask("procfs fan", | 8270 | tpacpi_disclose_usertask("procfs fan", |
8312 | "set level to %d\n", level); | 8271 | "set level to %d\n", level); |
@@ -8321,8 +8280,8 @@ static int fan_write_cmd_enable(const char *cmd, int *rc) | |||
8321 | 8280 | ||
8322 | *rc = fan_set_enable(); | 8281 | *rc = fan_set_enable(); |
8323 | if (*rc == -ENXIO) | 8282 | if (*rc == -ENXIO) |
8324 | printk(TPACPI_ERR "enable command accepted for unsupported " | 8283 | pr_err("enable command accepted for unsupported access mode %d\n", |
8325 | "access mode %d", fan_control_access_mode); | 8284 | fan_control_access_mode); |
8326 | else if (!*rc) | 8285 | else if (!*rc) |
8327 | tpacpi_disclose_usertask("procfs fan", "enable\n"); | 8286 | tpacpi_disclose_usertask("procfs fan", "enable\n"); |
8328 | 8287 | ||
@@ -8336,8 +8295,8 @@ static int fan_write_cmd_disable(const char *cmd, int *rc) | |||
8336 | 8295 | ||
8337 | *rc = fan_set_disable(); | 8296 | *rc = fan_set_disable(); |
8338 | if (*rc == -ENXIO) | 8297 | if (*rc == -ENXIO) |
8339 | printk(TPACPI_ERR "disable command accepted for unsupported " | 8298 | pr_err("disable command accepted for unsupported access mode %d\n", |
8340 | "access mode %d", fan_control_access_mode); | 8299 | fan_control_access_mode); |
8341 | else if (!*rc) | 8300 | else if (!*rc) |
8342 | tpacpi_disclose_usertask("procfs fan", "disable\n"); | 8301 | tpacpi_disclose_usertask("procfs fan", "disable\n"); |
8343 | 8302 | ||
@@ -8356,8 +8315,8 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) | |||
8356 | 8315 | ||
8357 | *rc = fan_set_speed(speed); | 8316 | *rc = fan_set_speed(speed); |
8358 | if (*rc == -ENXIO) | 8317 | if (*rc == -ENXIO) |
8359 | printk(TPACPI_ERR "speed command accepted for unsupported " | 8318 | pr_err("speed command accepted for unsupported access mode %d\n", |
8360 | "access mode %d", fan_control_access_mode); | 8319 | fan_control_access_mode); |
8361 | else if (!*rc) | 8320 | else if (!*rc) |
8362 | tpacpi_disclose_usertask("procfs fan", | 8321 | tpacpi_disclose_usertask("procfs fan", |
8363 | "set speed to %d\n", speed); | 8322 | "set speed to %d\n", speed); |
@@ -8497,7 +8456,6 @@ static void ibm_exit(struct ibm_struct *ibm) | |||
8497 | ibm->acpi->type, | 8456 | ibm->acpi->type, |
8498 | dispatch_acpi_notify); | 8457 | dispatch_acpi_notify); |
8499 | ibm->flags.acpi_notify_installed = 0; | 8458 | ibm->flags.acpi_notify_installed = 0; |
8500 | ibm->flags.acpi_notify_installed = 0; | ||
8501 | } | 8459 | } |
8502 | 8460 | ||
8503 | if (ibm->flags.proc_created) { | 8461 | if (ibm->flags.proc_created) { |
@@ -8561,8 +8519,8 @@ static int __init ibm_init(struct ibm_init_struct *iibm) | |||
8561 | if (ibm->acpi->notify) { | 8519 | if (ibm->acpi->notify) { |
8562 | ret = setup_acpi_notify(ibm); | 8520 | ret = setup_acpi_notify(ibm); |
8563 | if (ret == -ENODEV) { | 8521 | if (ret == -ENODEV) { |
8564 | printk(TPACPI_NOTICE "disabling subdriver %s\n", | 8522 | pr_notice("disabling subdriver %s\n", |
8565 | ibm->name); | 8523 | ibm->name); |
8566 | ret = 0; | 8524 | ret = 0; |
8567 | goto err_out; | 8525 | goto err_out; |
8568 | } | 8526 | } |
@@ -8584,8 +8542,7 @@ static int __init ibm_init(struct ibm_init_struct *iibm) | |||
8584 | entry = proc_create_data(ibm->name, mode, proc_dir, | 8542 | entry = proc_create_data(ibm->name, mode, proc_dir, |
8585 | &dispatch_proc_fops, ibm); | 8543 | &dispatch_proc_fops, ibm); |
8586 | if (!entry) { | 8544 | if (!entry) { |
8587 | printk(TPACPI_ERR "unable to create proc entry %s\n", | 8545 | pr_err("unable to create proc entry %s\n", ibm->name); |
8588 | ibm->name); | ||
8589 | ret = -ENODEV; | 8546 | ret = -ENODEV; |
8590 | goto err_out; | 8547 | goto err_out; |
8591 | } | 8548 | } |
@@ -8621,8 +8578,7 @@ static bool __pure __init tpacpi_is_valid_fw_id(const char* const s, | |||
8621 | tpacpi_is_fw_digit(s[1]) && | 8578 | tpacpi_is_fw_digit(s[1]) && |
8622 | s[2] == t && s[3] == 'T' && | 8579 | s[2] == t && s[3] == 'T' && |
8623 | tpacpi_is_fw_digit(s[4]) && | 8580 | tpacpi_is_fw_digit(s[4]) && |
8624 | tpacpi_is_fw_digit(s[5]) && | 8581 | tpacpi_is_fw_digit(s[5]); |
8625 | s[6] == 'W' && s[7] == 'W'; | ||
8626 | } | 8582 | } |
8627 | 8583 | ||
8628 | /* returns 0 - probe ok, or < 0 - probe error. | 8584 | /* returns 0 - probe ok, or < 0 - probe error. |
@@ -8685,13 +8641,11 @@ static int __must_check __init get_thinkpad_model_data( | |||
8685 | tp->ec_release = (ec_fw_string[4] << 8) | 8641 | tp->ec_release = (ec_fw_string[4] << 8) |
8686 | | ec_fw_string[5]; | 8642 | | ec_fw_string[5]; |
8687 | } else { | 8643 | } else { |
8688 | printk(TPACPI_NOTICE | 8644 | pr_notice("ThinkPad firmware release %s " |
8689 | "ThinkPad firmware release %s " | 8645 | "doesn't match the known patterns\n", |
8690 | "doesn't match the known patterns\n", | 8646 | ec_fw_string); |
8691 | ec_fw_string); | 8647 | pr_notice("please report this to %s\n", |
8692 | printk(TPACPI_NOTICE | 8648 | TPACPI_MAIL); |
8693 | "please report this to %s\n", | ||
8694 | TPACPI_MAIL); | ||
8695 | } | 8649 | } |
8696 | break; | 8650 | break; |
8697 | } | 8651 | } |
@@ -8735,8 +8689,7 @@ static int __init probe_for_thinkpad(void) | |||
8735 | tpacpi_acpi_handle_locate("ec", TPACPI_ACPI_EC_HID, &ec_handle); | 8689 | tpacpi_acpi_handle_locate("ec", TPACPI_ACPI_EC_HID, &ec_handle); |
8736 | if (!ec_handle) { | 8690 | if (!ec_handle) { |
8737 | if (is_thinkpad) | 8691 | if (is_thinkpad) |
8738 | printk(TPACPI_ERR | 8692 | pr_err("Not yet supported ThinkPad detected!\n"); |
8739 | "Not yet supported ThinkPad detected!\n"); | ||
8740 | return -ENODEV; | 8693 | return -ENODEV; |
8741 | } | 8694 | } |
8742 | 8695 | ||
@@ -8748,10 +8701,10 @@ static int __init probe_for_thinkpad(void) | |||
8748 | 8701 | ||
8749 | static void __init thinkpad_acpi_init_banner(void) | 8702 | static void __init thinkpad_acpi_init_banner(void) |
8750 | { | 8703 | { |
8751 | printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION); | 8704 | pr_info("%s v%s\n", TPACPI_DESC, TPACPI_VERSION); |
8752 | printk(TPACPI_INFO "%s\n", TPACPI_URL); | 8705 | pr_info("%s\n", TPACPI_URL); |
8753 | 8706 | ||
8754 | printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n", | 8707 | pr_info("ThinkPad BIOS %s, EC %s\n", |
8755 | (thinkpad_id.bios_version_str) ? | 8708 | (thinkpad_id.bios_version_str) ? |
8756 | thinkpad_id.bios_version_str : "unknown", | 8709 | thinkpad_id.bios_version_str : "unknown", |
8757 | (thinkpad_id.ec_version_str) ? | 8710 | (thinkpad_id.ec_version_str) ? |
@@ -8760,7 +8713,7 @@ static void __init thinkpad_acpi_init_banner(void) | |||
8760 | BUG_ON(!thinkpad_id.vendor); | 8713 | BUG_ON(!thinkpad_id.vendor); |
8761 | 8714 | ||
8762 | if (thinkpad_id.model_str) | 8715 | if (thinkpad_id.model_str) |
8763 | printk(TPACPI_INFO "%s %s, model %s\n", | 8716 | pr_info("%s %s, model %s\n", |
8764 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? | 8717 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? |
8765 | "IBM" : ((thinkpad_id.vendor == | 8718 | "IBM" : ((thinkpad_id.vendor == |
8766 | PCI_VENDOR_ID_LENOVO) ? | 8719 | PCI_VENDOR_ID_LENOVO) ? |
@@ -9026,8 +8979,7 @@ static int __init thinkpad_acpi_module_init(void) | |||
9026 | 8979 | ||
9027 | ret = get_thinkpad_model_data(&thinkpad_id); | 8980 | ret = get_thinkpad_model_data(&thinkpad_id); |
9028 | if (ret) { | 8981 | if (ret) { |
9029 | printk(TPACPI_ERR | 8982 | pr_err("unable to get DMI data: %d\n", ret); |
9030 | "unable to get DMI data: %d\n", ret); | ||
9031 | thinkpad_acpi_module_exit(); | 8983 | thinkpad_acpi_module_exit(); |
9032 | return ret; | 8984 | return ret; |
9033 | } | 8985 | } |
@@ -9053,16 +9005,14 @@ static int __init thinkpad_acpi_module_init(void) | |||
9053 | 9005 | ||
9054 | proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir); | 9006 | proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir); |
9055 | if (!proc_dir) { | 9007 | if (!proc_dir) { |
9056 | printk(TPACPI_ERR | 9008 | pr_err("unable to create proc dir " TPACPI_PROC_DIR "\n"); |
9057 | "unable to create proc dir " TPACPI_PROC_DIR); | ||
9058 | thinkpad_acpi_module_exit(); | 9009 | thinkpad_acpi_module_exit(); |
9059 | return -ENODEV; | 9010 | return -ENODEV; |
9060 | } | 9011 | } |
9061 | 9012 | ||
9062 | ret = platform_driver_register(&tpacpi_pdriver); | 9013 | ret = platform_driver_register(&tpacpi_pdriver); |
9063 | if (ret) { | 9014 | if (ret) { |
9064 | printk(TPACPI_ERR | 9015 | pr_err("unable to register main platform driver\n"); |
9065 | "unable to register main platform driver\n"); | ||
9066 | thinkpad_acpi_module_exit(); | 9016 | thinkpad_acpi_module_exit(); |
9067 | return ret; | 9017 | return ret; |
9068 | } | 9018 | } |
@@ -9070,8 +9020,7 @@ static int __init thinkpad_acpi_module_init(void) | |||
9070 | 9020 | ||
9071 | ret = platform_driver_register(&tpacpi_hwmon_pdriver); | 9021 | ret = platform_driver_register(&tpacpi_hwmon_pdriver); |
9072 | if (ret) { | 9022 | if (ret) { |
9073 | printk(TPACPI_ERR | 9023 | pr_err("unable to register hwmon platform driver\n"); |
9074 | "unable to register hwmon platform driver\n"); | ||
9075 | thinkpad_acpi_module_exit(); | 9024 | thinkpad_acpi_module_exit(); |
9076 | return ret; | 9025 | return ret; |
9077 | } | 9026 | } |
@@ -9084,8 +9033,7 @@ static int __init thinkpad_acpi_module_init(void) | |||
9084 | &tpacpi_hwmon_pdriver.driver); | 9033 | &tpacpi_hwmon_pdriver.driver); |
9085 | } | 9034 | } |
9086 | if (ret) { | 9035 | if (ret) { |
9087 | printk(TPACPI_ERR | 9036 | pr_err("unable to create sysfs driver attributes\n"); |
9088 | "unable to create sysfs driver attributes\n"); | ||
9089 | thinkpad_acpi_module_exit(); | 9037 | thinkpad_acpi_module_exit(); |
9090 | return ret; | 9038 | return ret; |
9091 | } | 9039 | } |
@@ -9098,7 +9046,7 @@ static int __init thinkpad_acpi_module_init(void) | |||
9098 | if (IS_ERR(tpacpi_pdev)) { | 9046 | if (IS_ERR(tpacpi_pdev)) { |
9099 | ret = PTR_ERR(tpacpi_pdev); | 9047 | ret = PTR_ERR(tpacpi_pdev); |
9100 | tpacpi_pdev = NULL; | 9048 | tpacpi_pdev = NULL; |
9101 | printk(TPACPI_ERR "unable to register platform device\n"); | 9049 | pr_err("unable to register platform device\n"); |
9102 | thinkpad_acpi_module_exit(); | 9050 | thinkpad_acpi_module_exit(); |
9103 | return ret; | 9051 | return ret; |
9104 | } | 9052 | } |
@@ -9108,16 +9056,14 @@ static int __init thinkpad_acpi_module_init(void) | |||
9108 | if (IS_ERR(tpacpi_sensors_pdev)) { | 9056 | if (IS_ERR(tpacpi_sensors_pdev)) { |
9109 | ret = PTR_ERR(tpacpi_sensors_pdev); | 9057 | ret = PTR_ERR(tpacpi_sensors_pdev); |
9110 | tpacpi_sensors_pdev = NULL; | 9058 | tpacpi_sensors_pdev = NULL; |
9111 | printk(TPACPI_ERR | 9059 | pr_err("unable to register hwmon platform device\n"); |
9112 | "unable to register hwmon platform device\n"); | ||
9113 | thinkpad_acpi_module_exit(); | 9060 | thinkpad_acpi_module_exit(); |
9114 | return ret; | 9061 | return ret; |
9115 | } | 9062 | } |
9116 | ret = device_create_file(&tpacpi_sensors_pdev->dev, | 9063 | ret = device_create_file(&tpacpi_sensors_pdev->dev, |
9117 | &dev_attr_thinkpad_acpi_pdev_name); | 9064 | &dev_attr_thinkpad_acpi_pdev_name); |
9118 | if (ret) { | 9065 | if (ret) { |
9119 | printk(TPACPI_ERR | 9066 | pr_err("unable to create sysfs hwmon device attributes\n"); |
9120 | "unable to create sysfs hwmon device attributes\n"); | ||
9121 | thinkpad_acpi_module_exit(); | 9067 | thinkpad_acpi_module_exit(); |
9122 | return ret; | 9068 | return ret; |
9123 | } | 9069 | } |
@@ -9126,14 +9072,14 @@ static int __init thinkpad_acpi_module_init(void) | |||
9126 | if (IS_ERR(tpacpi_hwmon)) { | 9072 | if (IS_ERR(tpacpi_hwmon)) { |
9127 | ret = PTR_ERR(tpacpi_hwmon); | 9073 | ret = PTR_ERR(tpacpi_hwmon); |
9128 | tpacpi_hwmon = NULL; | 9074 | tpacpi_hwmon = NULL; |
9129 | printk(TPACPI_ERR "unable to register hwmon device\n"); | 9075 | pr_err("unable to register hwmon device\n"); |
9130 | thinkpad_acpi_module_exit(); | 9076 | thinkpad_acpi_module_exit(); |
9131 | return ret; | 9077 | return ret; |
9132 | } | 9078 | } |
9133 | mutex_init(&tpacpi_inputdev_send_mutex); | 9079 | mutex_init(&tpacpi_inputdev_send_mutex); |
9134 | tpacpi_inputdev = input_allocate_device(); | 9080 | tpacpi_inputdev = input_allocate_device(); |
9135 | if (!tpacpi_inputdev) { | 9081 | if (!tpacpi_inputdev) { |
9136 | printk(TPACPI_ERR "unable to allocate input device\n"); | 9082 | pr_err("unable to allocate input device\n"); |
9137 | thinkpad_acpi_module_exit(); | 9083 | thinkpad_acpi_module_exit(); |
9138 | return -ENOMEM; | 9084 | return -ENOMEM; |
9139 | } else { | 9085 | } else { |
@@ -9165,7 +9111,7 @@ static int __init thinkpad_acpi_module_init(void) | |||
9165 | 9111 | ||
9166 | ret = input_register_device(tpacpi_inputdev); | 9112 | ret = input_register_device(tpacpi_inputdev); |
9167 | if (ret < 0) { | 9113 | if (ret < 0) { |
9168 | printk(TPACPI_ERR "unable to register input device\n"); | 9114 | pr_err("unable to register input device\n"); |
9169 | thinkpad_acpi_module_exit(); | 9115 | thinkpad_acpi_module_exit(); |
9170 | return ret; | 9116 | return ret; |
9171 | } else { | 9117 | } else { |
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index ff4b476f1950..4c20447ddbb7 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/acpi.h> | 20 | #include <linux/acpi.h> |
21 | #include <linux/input.h> | 21 | #include <linux/input.h> |
22 | #include <linux/input/sparse-keymap.h> | ||
22 | 23 | ||
23 | #define ACPI_TOPSTAR_CLASS "topstar" | 24 | #define ACPI_TOPSTAR_CLASS "topstar" |
24 | 25 | ||
@@ -26,52 +27,37 @@ struct topstar_hkey { | |||
26 | struct input_dev *inputdev; | 27 | struct input_dev *inputdev; |
27 | }; | 28 | }; |
28 | 29 | ||
29 | struct tps_key_entry { | 30 | static const struct key_entry topstar_keymap[] = { |
30 | u8 code; | 31 | { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } }, |
31 | u16 keycode; | 32 | { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } }, |
32 | }; | 33 | { KE_KEY, 0x83, { KEY_VOLUMEUP } }, |
33 | 34 | { KE_KEY, 0x84, { KEY_VOLUMEDOWN } }, | |
34 | static struct tps_key_entry topstar_keymap[] = { | 35 | { KE_KEY, 0x85, { KEY_MUTE } }, |
35 | { 0x80, KEY_BRIGHTNESSUP }, | 36 | { KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } }, |
36 | { 0x81, KEY_BRIGHTNESSDOWN }, | 37 | { KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */ |
37 | { 0x83, KEY_VOLUMEUP }, | 38 | { KE_KEY, 0x88, { KEY_WLAN } }, |
38 | { 0x84, KEY_VOLUMEDOWN }, | 39 | { KE_KEY, 0x8a, { KEY_WWW } }, |
39 | { 0x85, KEY_MUTE }, | 40 | { KE_KEY, 0x8b, { KEY_MAIL } }, |
40 | { 0x86, KEY_SWITCHVIDEOMODE }, | 41 | { KE_KEY, 0x8c, { KEY_MEDIA } }, |
41 | { 0x87, KEY_F13 }, /* touchpad enable/disable key */ | ||
42 | { 0x88, KEY_WLAN }, | ||
43 | { 0x8a, KEY_WWW }, | ||
44 | { 0x8b, KEY_MAIL }, | ||
45 | { 0x8c, KEY_MEDIA }, | ||
46 | { 0x96, KEY_F14 }, /* G key? */ | ||
47 | { } | ||
48 | }; | ||
49 | |||
50 | static struct tps_key_entry *tps_get_key_by_scancode(unsigned int code) | ||
51 | { | ||
52 | struct tps_key_entry *key; | ||
53 | |||
54 | for (key = topstar_keymap; key->code; key++) | ||
55 | if (code == key->code) | ||
56 | return key; | ||
57 | 42 | ||
58 | return NULL; | 43 | /* Known non hotkey events don't handled or that we don't care yet */ |
59 | } | 44 | { KE_IGNORE, 0x8e, }, |
60 | 45 | { KE_IGNORE, 0x8f, }, | |
61 | static struct tps_key_entry *tps_get_key_by_keycode(unsigned int code) | 46 | { KE_IGNORE, 0x90, }, |
62 | { | ||
63 | struct tps_key_entry *key; | ||
64 | 47 | ||
65 | for (key = topstar_keymap; key->code; key++) | 48 | /* |
66 | if (code == key->keycode) | 49 | * 'G key' generate two event codes, convert to only |
67 | return key; | 50 | * one event/key code for now, consider replacing by |
51 | * a switch (3G switch - SW_3G?) | ||
52 | */ | ||
53 | { KE_KEY, 0x96, { KEY_F14 } }, | ||
54 | { KE_KEY, 0x97, { KEY_F14 } }, | ||
68 | 55 | ||
69 | return NULL; | 56 | { KE_END, 0 } |
70 | } | 57 | }; |
71 | 58 | ||
72 | static void acpi_topstar_notify(struct acpi_device *device, u32 event) | 59 | static void acpi_topstar_notify(struct acpi_device *device, u32 event) |
73 | { | 60 | { |
74 | struct tps_key_entry *key; | ||
75 | static bool dup_evnt[2]; | 61 | static bool dup_evnt[2]; |
76 | bool *dup; | 62 | bool *dup; |
77 | struct topstar_hkey *hkey = acpi_driver_data(device); | 63 | struct topstar_hkey *hkey = acpi_driver_data(device); |
@@ -86,27 +72,8 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event) | |||
86 | *dup = true; | 72 | *dup = true; |
87 | } | 73 | } |
88 | 74 | ||
89 | /* | 75 | if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true)) |
90 | * 'G key' generate two event codes, convert to only | 76 | pr_info("unknown event = 0x%02x\n", event); |
91 | * one event/key code for now (3G switch?) | ||
92 | */ | ||
93 | if (event == 0x97) | ||
94 | event = 0x96; | ||
95 | |||
96 | key = tps_get_key_by_scancode(event); | ||
97 | if (key) { | ||
98 | input_report_key(hkey->inputdev, key->keycode, 1); | ||
99 | input_sync(hkey->inputdev); | ||
100 | input_report_key(hkey->inputdev, key->keycode, 0); | ||
101 | input_sync(hkey->inputdev); | ||
102 | return; | ||
103 | } | ||
104 | |||
105 | /* Known non hotkey events don't handled or that we don't care yet */ | ||
106 | if (event == 0x8e || event == 0x8f || event == 0x90) | ||
107 | return; | ||
108 | |||
109 | pr_info("unknown event = 0x%02x\n", event); | ||
110 | } | 77 | } |
111 | 78 | ||
112 | static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) | 79 | static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) |
@@ -127,62 +94,41 @@ static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) | |||
127 | return 0; | 94 | return 0; |
128 | } | 95 | } |
129 | 96 | ||
130 | static int topstar_getkeycode(struct input_dev *dev, | ||
131 | unsigned int scancode, unsigned int *keycode) | ||
132 | { | ||
133 | struct tps_key_entry *key = tps_get_key_by_scancode(scancode); | ||
134 | |||
135 | if (!key) | ||
136 | return -EINVAL; | ||
137 | |||
138 | *keycode = key->keycode; | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int topstar_setkeycode(struct input_dev *dev, | ||
143 | unsigned int scancode, unsigned int keycode) | ||
144 | { | ||
145 | struct tps_key_entry *key; | ||
146 | int old_keycode; | ||
147 | |||
148 | key = tps_get_key_by_scancode(scancode); | ||
149 | |||
150 | if (!key) | ||
151 | return -EINVAL; | ||
152 | |||
153 | old_keycode = key->keycode; | ||
154 | key->keycode = keycode; | ||
155 | set_bit(keycode, dev->keybit); | ||
156 | if (!tps_get_key_by_keycode(old_keycode)) | ||
157 | clear_bit(old_keycode, dev->keybit); | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) | 97 | static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) |
162 | { | 98 | { |
163 | struct tps_key_entry *key; | 99 | struct input_dev *input; |
100 | int error; | ||
164 | 101 | ||
165 | hkey->inputdev = input_allocate_device(); | 102 | input = input_allocate_device(); |
166 | if (!hkey->inputdev) { | 103 | if (!input) { |
167 | pr_err("Unable to allocate input device\n"); | 104 | pr_err("Unable to allocate input device\n"); |
168 | return -ENODEV; | 105 | return -ENOMEM; |
169 | } | 106 | } |
170 | hkey->inputdev->name = "Topstar Laptop extra buttons"; | 107 | |
171 | hkey->inputdev->phys = "topstar/input0"; | 108 | input->name = "Topstar Laptop extra buttons"; |
172 | hkey->inputdev->id.bustype = BUS_HOST; | 109 | input->phys = "topstar/input0"; |
173 | hkey->inputdev->getkeycode = topstar_getkeycode; | 110 | input->id.bustype = BUS_HOST; |
174 | hkey->inputdev->setkeycode = topstar_setkeycode; | 111 | |
175 | for (key = topstar_keymap; key->code; key++) { | 112 | error = sparse_keymap_setup(input, topstar_keymap, NULL); |
176 | set_bit(EV_KEY, hkey->inputdev->evbit); | 113 | if (error) { |
177 | set_bit(key->keycode, hkey->inputdev->keybit); | 114 | pr_err("Unable to setup input device keymap\n"); |
115 | goto err_free_dev; | ||
178 | } | 116 | } |
179 | if (input_register_device(hkey->inputdev)) { | 117 | |
118 | error = input_register_device(input); | ||
119 | if (error) { | ||
180 | pr_err("Unable to register input device\n"); | 120 | pr_err("Unable to register input device\n"); |
181 | input_free_device(hkey->inputdev); | 121 | goto err_free_keymap; |
182 | return -ENODEV; | ||
183 | } | 122 | } |
184 | 123 | ||
124 | hkey->inputdev = input; | ||
185 | return 0; | 125 | return 0; |
126 | |||
127 | err_free_keymap: | ||
128 | sparse_keymap_free(input); | ||
129 | err_free_dev: | ||
130 | input_free_device(input); | ||
131 | return error; | ||
186 | } | 132 | } |
187 | 133 | ||
188 | static int acpi_topstar_add(struct acpi_device *device) | 134 | static int acpi_topstar_add(struct acpi_device *device) |
@@ -216,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device, int type) | |||
216 | 162 | ||
217 | acpi_topstar_fncx_switch(device, false); | 163 | acpi_topstar_fncx_switch(device, false); |
218 | 164 | ||
165 | sparse_keymap_free(tps_hkey->inputdev); | ||
219 | input_unregister_device(tps_hkey->inputdev); | 166 | input_unregister_device(tps_hkey->inputdev); |
220 | kfree(tps_hkey); | 167 | kfree(tps_hkey); |
221 | 168 | ||
@@ -247,7 +194,7 @@ static int __init topstar_laptop_init(void) | |||
247 | if (ret < 0) | 194 | if (ret < 0) |
248 | return ret; | 195 | return ret; |
249 | 196 | ||
250 | printk(KERN_INFO "Topstar Laptop ACPI extras driver loaded\n"); | 197 | pr_info("ACPI extras driver loaded\n"); |
251 | 198 | ||
252 | return 0; | 199 | return 0; |
253 | } | 200 | } |
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 7d67a45bb2b0..cb009b2629ee 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -35,6 +35,8 @@ | |||
35 | * | 35 | * |
36 | */ | 36 | */ |
37 | 37 | ||
38 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
39 | |||
38 | #define TOSHIBA_ACPI_VERSION "0.19" | 40 | #define TOSHIBA_ACPI_VERSION "0.19" |
39 | #define PROC_INTERFACE_VERSION 1 | 41 | #define PROC_INTERFACE_VERSION 1 |
40 | 42 | ||
@@ -48,6 +50,7 @@ | |||
48 | #include <linux/platform_device.h> | 50 | #include <linux/platform_device.h> |
49 | #include <linux/rfkill.h> | 51 | #include <linux/rfkill.h> |
50 | #include <linux/input.h> | 52 | #include <linux/input.h> |
53 | #include <linux/input/sparse-keymap.h> | ||
51 | #include <linux/leds.h> | 54 | #include <linux/leds.h> |
52 | #include <linux/slab.h> | 55 | #include <linux/slab.h> |
53 | 56 | ||
@@ -59,11 +62,6 @@ MODULE_AUTHOR("John Belmonte"); | |||
59 | MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); | 62 | MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); |
60 | MODULE_LICENSE("GPL"); | 63 | MODULE_LICENSE("GPL"); |
61 | 64 | ||
62 | #define MY_LOGPREFIX "toshiba_acpi: " | ||
63 | #define MY_ERR KERN_ERR MY_LOGPREFIX | ||
64 | #define MY_NOTICE KERN_NOTICE MY_LOGPREFIX | ||
65 | #define MY_INFO KERN_INFO MY_LOGPREFIX | ||
66 | |||
67 | /* Toshiba ACPI method paths */ | 65 | /* Toshiba ACPI method paths */ |
68 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" | 66 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" |
69 | #define TOSH_INTERFACE_1 "\\_SB_.VALD" | 67 | #define TOSH_INTERFACE_1 "\\_SB_.VALD" |
@@ -121,36 +119,29 @@ static const struct acpi_device_id toshiba_device_ids[] = { | |||
121 | }; | 119 | }; |
122 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); | 120 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); |
123 | 121 | ||
124 | struct key_entry { | 122 | static const struct key_entry toshiba_acpi_keymap[] __initconst = { |
125 | char type; | 123 | { KE_KEY, 0x101, { KEY_MUTE } }, |
126 | u16 code; | 124 | { KE_KEY, 0x102, { KEY_ZOOMOUT } }, |
127 | u16 keycode; | 125 | { KE_KEY, 0x103, { KEY_ZOOMIN } }, |
128 | }; | 126 | { KE_KEY, 0x13b, { KEY_COFFEE } }, |
129 | 127 | { KE_KEY, 0x13c, { KEY_BATTERY } }, | |
130 | enum {KE_KEY, KE_END}; | 128 | { KE_KEY, 0x13d, { KEY_SLEEP } }, |
131 | 129 | { KE_KEY, 0x13e, { KEY_SUSPEND } }, | |
132 | static struct key_entry toshiba_acpi_keymap[] = { | 130 | { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } }, |
133 | {KE_KEY, 0x101, KEY_MUTE}, | 131 | { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, |
134 | {KE_KEY, 0x102, KEY_ZOOMOUT}, | 132 | { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, |
135 | {KE_KEY, 0x103, KEY_ZOOMIN}, | 133 | { KE_KEY, 0x142, { KEY_WLAN } }, |
136 | {KE_KEY, 0x13b, KEY_COFFEE}, | 134 | { KE_KEY, 0x143, { KEY_PROG1 } }, |
137 | {KE_KEY, 0x13c, KEY_BATTERY}, | 135 | { KE_KEY, 0x17f, { KEY_FN } }, |
138 | {KE_KEY, 0x13d, KEY_SLEEP}, | 136 | { KE_KEY, 0xb05, { KEY_PROG2 } }, |
139 | {KE_KEY, 0x13e, KEY_SUSPEND}, | 137 | { KE_KEY, 0xb06, { KEY_WWW } }, |
140 | {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, | 138 | { KE_KEY, 0xb07, { KEY_MAIL } }, |
141 | {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, | 139 | { KE_KEY, 0xb30, { KEY_STOP } }, |
142 | {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, | 140 | { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } }, |
143 | {KE_KEY, 0x142, KEY_WLAN}, | 141 | { KE_KEY, 0xb32, { KEY_NEXTSONG } }, |
144 | {KE_KEY, 0x143, KEY_PROG1}, | 142 | { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, |
145 | {KE_KEY, 0xb05, KEY_PROG2}, | 143 | { KE_KEY, 0xb5a, { KEY_MEDIA } }, |
146 | {KE_KEY, 0xb06, KEY_WWW}, | 144 | { KE_END, 0 }, |
147 | {KE_KEY, 0xb07, KEY_MAIL}, | ||
148 | {KE_KEY, 0xb30, KEY_STOP}, | ||
149 | {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, | ||
150 | {KE_KEY, 0xb32, KEY_NEXTSONG}, | ||
151 | {KE_KEY, 0xb33, KEY_PLAYPAUSE}, | ||
152 | {KE_KEY, 0xb5a, KEY_MEDIA}, | ||
153 | {KE_END, 0, 0}, | ||
154 | }; | 145 | }; |
155 | 146 | ||
156 | /* utility | 147 | /* utility |
@@ -307,7 +298,7 @@ static int toshiba_illumination_available(void) | |||
307 | in[0] = 0xf100; | 298 | in[0] = 0xf100; |
308 | status = hci_raw(in, out); | 299 | status = hci_raw(in, out); |
309 | if (ACPI_FAILURE(status)) { | 300 | if (ACPI_FAILURE(status)) { |
310 | printk(MY_INFO "Illumination device not available\n"); | 301 | pr_info("Illumination device not available\n"); |
311 | return 0; | 302 | return 0; |
312 | } | 303 | } |
313 | in[0] = 0xf400; | 304 | in[0] = 0xf400; |
@@ -326,7 +317,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, | |||
326 | in[0] = 0xf100; | 317 | in[0] = 0xf100; |
327 | status = hci_raw(in, out); | 318 | status = hci_raw(in, out); |
328 | if (ACPI_FAILURE(status)) { | 319 | if (ACPI_FAILURE(status)) { |
329 | printk(MY_INFO "Illumination device not available\n"); | 320 | pr_info("Illumination device not available\n"); |
330 | return; | 321 | return; |
331 | } | 322 | } |
332 | 323 | ||
@@ -337,7 +328,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, | |||
337 | in[2] = 1; | 328 | in[2] = 1; |
338 | status = hci_raw(in, out); | 329 | status = hci_raw(in, out); |
339 | if (ACPI_FAILURE(status)) { | 330 | if (ACPI_FAILURE(status)) { |
340 | printk(MY_INFO "ACPI call for illumination failed.\n"); | 331 | pr_info("ACPI call for illumination failed\n"); |
341 | return; | 332 | return; |
342 | } | 333 | } |
343 | } else { | 334 | } else { |
@@ -347,7 +338,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, | |||
347 | in[2] = 0; | 338 | in[2] = 0; |
348 | status = hci_raw(in, out); | 339 | status = hci_raw(in, out); |
349 | if (ACPI_FAILURE(status)) { | 340 | if (ACPI_FAILURE(status)) { |
350 | printk(MY_INFO "ACPI call for illumination failed.\n"); | 341 | pr_info("ACPI call for illumination failed.\n"); |
351 | return; | 342 | return; |
352 | } | 343 | } |
353 | } | 344 | } |
@@ -370,7 +361,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | |||
370 | in[0] = 0xf100; | 361 | in[0] = 0xf100; |
371 | status = hci_raw(in, out); | 362 | status = hci_raw(in, out); |
372 | if (ACPI_FAILURE(status)) { | 363 | if (ACPI_FAILURE(status)) { |
373 | printk(MY_INFO "Illumination device not available\n"); | 364 | pr_info("Illumination device not available\n"); |
374 | return LED_OFF; | 365 | return LED_OFF; |
375 | } | 366 | } |
376 | 367 | ||
@@ -379,7 +370,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | |||
379 | in[1] = 0x14e; | 370 | in[1] = 0x14e; |
380 | status = hci_raw(in, out); | 371 | status = hci_raw(in, out); |
381 | if (ACPI_FAILURE(status)) { | 372 | if (ACPI_FAILURE(status)) { |
382 | printk(MY_INFO "ACPI call for illumination failed.\n"); | 373 | pr_info("ACPI call for illumination failed.\n"); |
383 | return LED_OFF; | 374 | return LED_OFF; |
384 | } | 375 | } |
385 | 376 | ||
@@ -523,7 +514,7 @@ static int lcd_proc_show(struct seq_file *m, void *v) | |||
523 | seq_printf(m, "brightness_levels: %d\n", | 514 | seq_printf(m, "brightness_levels: %d\n", |
524 | HCI_LCD_BRIGHTNESS_LEVELS); | 515 | HCI_LCD_BRIGHTNESS_LEVELS); |
525 | } else { | 516 | } else { |
526 | printk(MY_ERR "Error reading LCD brightness\n"); | 517 | pr_err("Error reading LCD brightness\n"); |
527 | } | 518 | } |
528 | 519 | ||
529 | return 0; | 520 | return 0; |
@@ -598,7 +589,7 @@ static int video_proc_show(struct seq_file *m, void *v) | |||
598 | seq_printf(m, "crt_out: %d\n", is_crt); | 589 | seq_printf(m, "crt_out: %d\n", is_crt); |
599 | seq_printf(m, "tv_out: %d\n", is_tv); | 590 | seq_printf(m, "tv_out: %d\n", is_tv); |
600 | } else { | 591 | } else { |
601 | printk(MY_ERR "Error reading video out status\n"); | 592 | pr_err("Error reading video out status\n"); |
602 | } | 593 | } |
603 | 594 | ||
604 | return 0; | 595 | return 0; |
@@ -692,7 +683,7 @@ static int fan_proc_show(struct seq_file *m, void *v) | |||
692 | seq_printf(m, "running: %d\n", (value > 0)); | 683 | seq_printf(m, "running: %d\n", (value > 0)); |
693 | seq_printf(m, "force_on: %d\n", force_fan); | 684 | seq_printf(m, "force_on: %d\n", force_fan); |
694 | } else { | 685 | } else { |
695 | printk(MY_ERR "Error reading fan status\n"); | 686 | pr_err("Error reading fan status\n"); |
696 | } | 687 | } |
697 | 688 | ||
698 | return 0; | 689 | return 0; |
@@ -756,9 +747,9 @@ static int keys_proc_show(struct seq_file *m, void *v) | |||
756 | * some machines where system events sporadically | 747 | * some machines where system events sporadically |
757 | * become disabled. */ | 748 | * become disabled. */ |
758 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); | 749 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); |
759 | printk(MY_NOTICE "Re-enabled hotkeys\n"); | 750 | pr_notice("Re-enabled hotkeys\n"); |
760 | } else { | 751 | } else { |
761 | printk(MY_ERR "Error reading hotkey status\n"); | 752 | pr_err("Error reading hotkey status\n"); |
762 | goto end; | 753 | goto end; |
763 | } | 754 | } |
764 | } | 755 | } |
@@ -847,69 +838,14 @@ static void remove_toshiba_proc_entries(void) | |||
847 | remove_proc_entry("version", toshiba_proc_dir); | 838 | remove_proc_entry("version", toshiba_proc_dir); |
848 | } | 839 | } |
849 | 840 | ||
850 | static struct backlight_ops toshiba_backlight_data = { | 841 | static const struct backlight_ops toshiba_backlight_data = { |
851 | .get_brightness = get_lcd, | 842 | .get_brightness = get_lcd, |
852 | .update_status = set_lcd_status, | 843 | .update_status = set_lcd_status, |
853 | }; | 844 | }; |
854 | 845 | ||
855 | static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code) | ||
856 | { | ||
857 | struct key_entry *key; | ||
858 | |||
859 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) | ||
860 | if (code == key->code) | ||
861 | return key; | ||
862 | |||
863 | return NULL; | ||
864 | } | ||
865 | |||
866 | static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code) | ||
867 | { | ||
868 | struct key_entry *key; | ||
869 | |||
870 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) | ||
871 | if (code == key->keycode && key->type == KE_KEY) | ||
872 | return key; | ||
873 | |||
874 | return NULL; | ||
875 | } | ||
876 | |||
877 | static int toshiba_acpi_getkeycode(struct input_dev *dev, | ||
878 | unsigned int scancode, unsigned int *keycode) | ||
879 | { | ||
880 | struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); | ||
881 | |||
882 | if (key && key->type == KE_KEY) { | ||
883 | *keycode = key->keycode; | ||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | return -EINVAL; | ||
888 | } | ||
889 | |||
890 | static int toshiba_acpi_setkeycode(struct input_dev *dev, | ||
891 | unsigned int scancode, unsigned int keycode) | ||
892 | { | ||
893 | struct key_entry *key; | ||
894 | unsigned int old_keycode; | ||
895 | |||
896 | key = toshiba_acpi_get_entry_by_scancode(scancode); | ||
897 | if (key && key->type == KE_KEY) { | ||
898 | old_keycode = key->keycode; | ||
899 | key->keycode = keycode; | ||
900 | set_bit(keycode, dev->keybit); | ||
901 | if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) | ||
902 | clear_bit(old_keycode, dev->keybit); | ||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | return -EINVAL; | ||
907 | } | ||
908 | |||
909 | static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) | 846 | static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) |
910 | { | 847 | { |
911 | u32 hci_result, value; | 848 | u32 hci_result, value; |
912 | struct key_entry *key; | ||
913 | 849 | ||
914 | if (event != 0x80) | 850 | if (event != 0x80) |
915 | return; | 851 | return; |
@@ -922,87 +858,89 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) | |||
922 | if (value & 0x80) | 858 | if (value & 0x80) |
923 | continue; | 859 | continue; |
924 | 860 | ||
925 | key = toshiba_acpi_get_entry_by_scancode | 861 | if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, |
926 | (value); | 862 | value, 1, true)) { |
927 | if (!key) { | 863 | pr_info("Unknown key %x\n", |
928 | printk(MY_INFO "Unknown key %x\n", | ||
929 | value); | 864 | value); |
930 | continue; | ||
931 | } | 865 | } |
932 | input_report_key(toshiba_acpi.hotkey_dev, | ||
933 | key->keycode, 1); | ||
934 | input_sync(toshiba_acpi.hotkey_dev); | ||
935 | input_report_key(toshiba_acpi.hotkey_dev, | ||
936 | key->keycode, 0); | ||
937 | input_sync(toshiba_acpi.hotkey_dev); | ||
938 | } else if (hci_result == HCI_NOT_SUPPORTED) { | 866 | } else if (hci_result == HCI_NOT_SUPPORTED) { |
939 | /* This is a workaround for an unresolved issue on | 867 | /* This is a workaround for an unresolved issue on |
940 | * some machines where system events sporadically | 868 | * some machines where system events sporadically |
941 | * become disabled. */ | 869 | * become disabled. */ |
942 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); | 870 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); |
943 | printk(MY_NOTICE "Re-enabled hotkeys\n"); | 871 | pr_notice("Re-enabled hotkeys\n"); |
944 | } | 872 | } |
945 | } while (hci_result != HCI_EMPTY); | 873 | } while (hci_result != HCI_EMPTY); |
946 | } | 874 | } |
947 | 875 | ||
948 | static int toshiba_acpi_setup_keyboard(char *device) | 876 | static int __init toshiba_acpi_setup_keyboard(char *device) |
949 | { | 877 | { |
950 | acpi_status status; | 878 | acpi_status status; |
951 | acpi_handle handle; | 879 | int error; |
952 | int result; | ||
953 | const struct key_entry *key; | ||
954 | 880 | ||
955 | status = acpi_get_handle(NULL, device, &handle); | 881 | status = acpi_get_handle(NULL, device, &toshiba_acpi.handle); |
956 | if (ACPI_FAILURE(status)) { | 882 | if (ACPI_FAILURE(status)) { |
957 | printk(MY_INFO "Unable to get notification device\n"); | 883 | pr_info("Unable to get notification device\n"); |
958 | return -ENODEV; | ||
959 | } | ||
960 | |||
961 | toshiba_acpi.handle = handle; | ||
962 | |||
963 | status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); | ||
964 | if (ACPI_FAILURE(status)) { | ||
965 | printk(MY_INFO "Unable to enable hotkeys\n"); | ||
966 | return -ENODEV; | ||
967 | } | ||
968 | |||
969 | status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, | ||
970 | toshiba_acpi_notify, NULL); | ||
971 | if (ACPI_FAILURE(status)) { | ||
972 | printk(MY_INFO "Unable to install hotkey notification\n"); | ||
973 | return -ENODEV; | 884 | return -ENODEV; |
974 | } | 885 | } |
975 | 886 | ||
976 | toshiba_acpi.hotkey_dev = input_allocate_device(); | 887 | toshiba_acpi.hotkey_dev = input_allocate_device(); |
977 | if (!toshiba_acpi.hotkey_dev) { | 888 | if (!toshiba_acpi.hotkey_dev) { |
978 | printk(MY_INFO "Unable to register input device\n"); | 889 | pr_info("Unable to register input device\n"); |
979 | return -ENOMEM; | 890 | return -ENOMEM; |
980 | } | 891 | } |
981 | 892 | ||
982 | toshiba_acpi.hotkey_dev->name = "Toshiba input device"; | 893 | toshiba_acpi.hotkey_dev->name = "Toshiba input device"; |
983 | toshiba_acpi.hotkey_dev->phys = device; | 894 | toshiba_acpi.hotkey_dev->phys = device; |
984 | toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; | 895 | toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; |
985 | toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; | ||
986 | toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; | ||
987 | 896 | ||
988 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { | 897 | error = sparse_keymap_setup(toshiba_acpi.hotkey_dev, |
989 | set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); | 898 | toshiba_acpi_keymap, NULL); |
990 | set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); | 899 | if (error) |
900 | goto err_free_dev; | ||
901 | |||
902 | status = acpi_install_notify_handler(toshiba_acpi.handle, | ||
903 | ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL); | ||
904 | if (ACPI_FAILURE(status)) { | ||
905 | pr_info("Unable to install hotkey notification\n"); | ||
906 | error = -ENODEV; | ||
907 | goto err_free_keymap; | ||
991 | } | 908 | } |
992 | 909 | ||
993 | result = input_register_device(toshiba_acpi.hotkey_dev); | 910 | status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL); |
994 | if (result) { | 911 | if (ACPI_FAILURE(status)) { |
995 | printk(MY_INFO "Unable to register input device\n"); | 912 | pr_info("Unable to enable hotkeys\n"); |
996 | return result; | 913 | error = -ENODEV; |
914 | goto err_remove_notify; | ||
915 | } | ||
916 | |||
917 | error = input_register_device(toshiba_acpi.hotkey_dev); | ||
918 | if (error) { | ||
919 | pr_info("Unable to register input device\n"); | ||
920 | goto err_remove_notify; | ||
997 | } | 921 | } |
998 | 922 | ||
999 | return 0; | 923 | return 0; |
924 | |||
925 | err_remove_notify: | ||
926 | acpi_remove_notify_handler(toshiba_acpi.handle, | ||
927 | ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); | ||
928 | err_free_keymap: | ||
929 | sparse_keymap_free(toshiba_acpi.hotkey_dev); | ||
930 | err_free_dev: | ||
931 | input_free_device(toshiba_acpi.hotkey_dev); | ||
932 | toshiba_acpi.hotkey_dev = NULL; | ||
933 | return error; | ||
1000 | } | 934 | } |
1001 | 935 | ||
1002 | static void toshiba_acpi_exit(void) | 936 | static void toshiba_acpi_exit(void) |
1003 | { | 937 | { |
1004 | if (toshiba_acpi.hotkey_dev) | 938 | if (toshiba_acpi.hotkey_dev) { |
939 | acpi_remove_notify_handler(toshiba_acpi.handle, | ||
940 | ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); | ||
941 | sparse_keymap_free(toshiba_acpi.hotkey_dev); | ||
1005 | input_unregister_device(toshiba_acpi.hotkey_dev); | 942 | input_unregister_device(toshiba_acpi.hotkey_dev); |
943 | } | ||
1006 | 944 | ||
1007 | if (toshiba_acpi.bt_rfk) { | 945 | if (toshiba_acpi.bt_rfk) { |
1008 | rfkill_unregister(toshiba_acpi.bt_rfk); | 946 | rfkill_unregister(toshiba_acpi.bt_rfk); |
@@ -1017,9 +955,6 @@ static void toshiba_acpi_exit(void) | |||
1017 | if (toshiba_proc_dir) | 955 | if (toshiba_proc_dir) |
1018 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | 956 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); |
1019 | 957 | ||
1020 | acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, | ||
1021 | toshiba_acpi_notify); | ||
1022 | |||
1023 | if (toshiba_acpi.illumination_installed) | 958 | if (toshiba_acpi.illumination_installed) |
1024 | led_classdev_unregister(&toshiba_led); | 959 | led_classdev_unregister(&toshiba_led); |
1025 | 960 | ||
@@ -1042,17 +977,17 @@ static int __init toshiba_acpi_init(void) | |||
1042 | if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { | 977 | if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { |
1043 | method_hci = TOSH_INTERFACE_1 GHCI_METHOD; | 978 | method_hci = TOSH_INTERFACE_1 GHCI_METHOD; |
1044 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) | 979 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) |
1045 | printk(MY_INFO "Unable to activate hotkeys\n"); | 980 | pr_info("Unable to activate hotkeys\n"); |
1046 | } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { | 981 | } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { |
1047 | method_hci = TOSH_INTERFACE_2 GHCI_METHOD; | 982 | method_hci = TOSH_INTERFACE_2 GHCI_METHOD; |
1048 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) | 983 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) |
1049 | printk(MY_INFO "Unable to activate hotkeys\n"); | 984 | pr_info("Unable to activate hotkeys\n"); |
1050 | } else | 985 | } else |
1051 | return -ENODEV; | 986 | return -ENODEV; |
1052 | 987 | ||
1053 | printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", | 988 | pr_info("Toshiba Laptop ACPI Extras version %s\n", |
1054 | TOSHIBA_ACPI_VERSION); | 989 | TOSHIBA_ACPI_VERSION); |
1055 | printk(MY_INFO " HCI method: %s\n", method_hci); | 990 | pr_info(" HCI method: %s\n", method_hci); |
1056 | 991 | ||
1057 | mutex_init(&toshiba_acpi.mutex); | 992 | mutex_init(&toshiba_acpi.mutex); |
1058 | 993 | ||
@@ -1060,7 +995,7 @@ static int __init toshiba_acpi_init(void) | |||
1060 | -1, NULL, 0); | 995 | -1, NULL, 0); |
1061 | if (IS_ERR(toshiba_acpi.p_dev)) { | 996 | if (IS_ERR(toshiba_acpi.p_dev)) { |
1062 | ret = PTR_ERR(toshiba_acpi.p_dev); | 997 | ret = PTR_ERR(toshiba_acpi.p_dev); |
1063 | printk(MY_ERR "unable to register platform device\n"); | 998 | pr_err("unable to register platform device\n"); |
1064 | toshiba_acpi.p_dev = NULL; | 999 | toshiba_acpi.p_dev = NULL; |
1065 | toshiba_acpi_exit(); | 1000 | toshiba_acpi_exit(); |
1066 | return ret; | 1001 | return ret; |
@@ -1080,6 +1015,7 @@ static int __init toshiba_acpi_init(void) | |||
1080 | create_toshiba_proc_entries(); | 1015 | create_toshiba_proc_entries(); |
1081 | } | 1016 | } |
1082 | 1017 | ||
1018 | props.type = BACKLIGHT_PLATFORM; | ||
1083 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | 1019 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; |
1084 | toshiba_backlight_device = backlight_device_register("toshiba", | 1020 | toshiba_backlight_device = backlight_device_register("toshiba", |
1085 | &toshiba_acpi.p_dev->dev, | 1021 | &toshiba_acpi.p_dev->dev, |
@@ -1089,7 +1025,7 @@ static int __init toshiba_acpi_init(void) | |||
1089 | if (IS_ERR(toshiba_backlight_device)) { | 1025 | if (IS_ERR(toshiba_backlight_device)) { |
1090 | ret = PTR_ERR(toshiba_backlight_device); | 1026 | ret = PTR_ERR(toshiba_backlight_device); |
1091 | 1027 | ||
1092 | printk(KERN_ERR "Could not register toshiba backlight device\n"); | 1028 | pr_err("Could not register toshiba backlight device\n"); |
1093 | toshiba_backlight_device = NULL; | 1029 | toshiba_backlight_device = NULL; |
1094 | toshiba_acpi_exit(); | 1030 | toshiba_acpi_exit(); |
1095 | return ret; | 1031 | return ret; |
@@ -1103,14 +1039,14 @@ static int __init toshiba_acpi_init(void) | |||
1103 | &toshiba_rfk_ops, | 1039 | &toshiba_rfk_ops, |
1104 | &toshiba_acpi); | 1040 | &toshiba_acpi); |
1105 | if (!toshiba_acpi.bt_rfk) { | 1041 | if (!toshiba_acpi.bt_rfk) { |
1106 | printk(MY_ERR "unable to allocate rfkill device\n"); | 1042 | pr_err("unable to allocate rfkill device\n"); |
1107 | toshiba_acpi_exit(); | 1043 | toshiba_acpi_exit(); |
1108 | return -ENOMEM; | 1044 | return -ENOMEM; |
1109 | } | 1045 | } |
1110 | 1046 | ||
1111 | ret = rfkill_register(toshiba_acpi.bt_rfk); | 1047 | ret = rfkill_register(toshiba_acpi.bt_rfk); |
1112 | if (ret) { | 1048 | if (ret) { |
1113 | printk(MY_ERR "unable to register rfkill device\n"); | 1049 | pr_err("unable to register rfkill device\n"); |
1114 | rfkill_destroy(toshiba_acpi.bt_rfk); | 1050 | rfkill_destroy(toshiba_acpi.bt_rfk); |
1115 | toshiba_acpi_exit(); | 1051 | toshiba_acpi_exit(); |
1116 | return ret; | 1052 | return ret; |
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 944068611919..5fb7186694df 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c | |||
@@ -17,6 +17,8 @@ | |||
17 | * delivered. | 17 | * delivered. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
21 | |||
20 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
21 | #include <linux/module.h> | 23 | #include <linux/module.h> |
22 | #include <linux/init.h> | 24 | #include <linux/init.h> |
@@ -70,14 +72,13 @@ static int toshiba_bluetooth_enable(acpi_handle handle) | |||
70 | if (!(result & 0x01)) | 72 | if (!(result & 0x01)) |
71 | return 0; | 73 | return 0; |
72 | 74 | ||
73 | printk(KERN_INFO "toshiba_bluetooth: Re-enabling Toshiba Bluetooth\n"); | 75 | pr_info("Re-enabling Toshiba Bluetooth\n"); |
74 | res1 = acpi_evaluate_object(handle, "AUSB", NULL, NULL); | 76 | res1 = acpi_evaluate_object(handle, "AUSB", NULL, NULL); |
75 | res2 = acpi_evaluate_object(handle, "BTPO", NULL, NULL); | 77 | res2 = acpi_evaluate_object(handle, "BTPO", NULL, NULL); |
76 | if (!ACPI_FAILURE(res1) || !ACPI_FAILURE(res2)) | 78 | if (!ACPI_FAILURE(res1) || !ACPI_FAILURE(res2)) |
77 | return 0; | 79 | return 0; |
78 | 80 | ||
79 | printk(KERN_WARNING "toshiba_bluetooth: Failed to re-enable " | 81 | pr_warn("Failed to re-enable Toshiba Bluetooth\n"); |
80 | "Toshiba Bluetooth\n"); | ||
81 | 82 | ||
82 | return -ENODEV; | 83 | return -ENODEV; |
83 | } | 84 | } |
@@ -107,8 +108,8 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) | |||
107 | &bt_present); | 108 | &bt_present); |
108 | 109 | ||
109 | if (!ACPI_FAILURE(status) && bt_present) { | 110 | if (!ACPI_FAILURE(status) && bt_present) { |
110 | printk(KERN_INFO "Detected Toshiba ACPI Bluetooth device - " | 111 | pr_info("Detected Toshiba ACPI Bluetooth device - " |
111 | "installing RFKill handler\n"); | 112 | "installing RFKill handler\n"); |
112 | result = toshiba_bluetooth_enable(device->handle); | 113 | result = toshiba_bluetooth_enable(device->handle); |
113 | } | 114 | } |
114 | 115 | ||
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b2978a04317f..f23d5a84e7b1 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -27,6 +27,8 @@ | |||
27 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 27 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
28 | */ | 28 | */ |
29 | 29 | ||
30 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
31 | |||
30 | #include <linux/kernel.h> | 32 | #include <linux/kernel.h> |
31 | #include <linux/init.h> | 33 | #include <linux/init.h> |
32 | #include <linux/types.h> | 34 | #include <linux/types.h> |
@@ -44,9 +46,8 @@ MODULE_LICENSE("GPL"); | |||
44 | 46 | ||
45 | #define ACPI_WMI_CLASS "wmi" | 47 | #define ACPI_WMI_CLASS "wmi" |
46 | 48 | ||
47 | #define PREFIX "ACPI: WMI: " | ||
48 | |||
49 | static DEFINE_MUTEX(wmi_data_lock); | 49 | static DEFINE_MUTEX(wmi_data_lock); |
50 | static LIST_HEAD(wmi_block_list); | ||
50 | 51 | ||
51 | struct guid_block { | 52 | struct guid_block { |
52 | char guid[16]; | 53 | char guid[16]; |
@@ -67,10 +68,9 @@ struct wmi_block { | |||
67 | acpi_handle handle; | 68 | acpi_handle handle; |
68 | wmi_notify_handler handler; | 69 | wmi_notify_handler handler; |
69 | void *handler_data; | 70 | void *handler_data; |
70 | struct device *dev; | 71 | struct device dev; |
71 | }; | 72 | }; |
72 | 73 | ||
73 | static struct wmi_block wmi_blocks; | ||
74 | 74 | ||
75 | /* | 75 | /* |
76 | * If the GUID data block is marked as expensive, we must enable and | 76 | * If the GUID data block is marked as expensive, we must enable and |
@@ -110,7 +110,7 @@ static struct acpi_driver acpi_wmi_driver = { | |||
110 | .add = acpi_wmi_add, | 110 | .add = acpi_wmi_add, |
111 | .remove = acpi_wmi_remove, | 111 | .remove = acpi_wmi_remove, |
112 | .notify = acpi_wmi_notify, | 112 | .notify = acpi_wmi_notify, |
113 | }, | 113 | }, |
114 | }; | 114 | }; |
115 | 115 | ||
116 | /* | 116 | /* |
@@ -128,30 +128,18 @@ static struct acpi_driver acpi_wmi_driver = { | |||
128 | */ | 128 | */ |
129 | static int wmi_parse_hexbyte(const u8 *src) | 129 | static int wmi_parse_hexbyte(const u8 *src) |
130 | { | 130 | { |
131 | unsigned int x; /* For correct wrapping */ | ||
132 | int h; | 131 | int h; |
132 | int value; | ||
133 | 133 | ||
134 | /* high part */ | 134 | /* high part */ |
135 | x = src[0]; | 135 | h = value = hex_to_bin(src[0]); |
136 | if (x - '0' <= '9' - '0') { | 136 | if (value < 0) |
137 | h = x - '0'; | ||
138 | } else if (x - 'a' <= 'f' - 'a') { | ||
139 | h = x - 'a' + 10; | ||
140 | } else if (x - 'A' <= 'F' - 'A') { | ||
141 | h = x - 'A' + 10; | ||
142 | } else { | ||
143 | return -1; | 137 | return -1; |
144 | } | ||
145 | h <<= 4; | ||
146 | 138 | ||
147 | /* low part */ | 139 | /* low part */ |
148 | x = src[1]; | 140 | value = hex_to_bin(src[1]); |
149 | if (x - '0' <= '9' - '0') | 141 | if (value >= 0) |
150 | return h | (x - '0'); | 142 | return (h << 4) | value; |
151 | if (x - 'a' <= 'f' - 'a') | ||
152 | return h | (x - 'a' + 10); | ||
153 | if (x - 'A' <= 'F' - 'A') | ||
154 | return h | (x - 'A' + 10); | ||
155 | return -1; | 143 | return -1; |
156 | } | 144 | } |
157 | 145 | ||
@@ -232,7 +220,7 @@ static int wmi_gtoa(const char *in, char *out) | |||
232 | for (i = 10; i <= 15; i++) | 220 | for (i = 10; i <= 15; i++) |
233 | out += sprintf(out, "%02X", in[i] & 0xFF); | 221 | out += sprintf(out, "%02X", in[i] & 0xFF); |
234 | 222 | ||
235 | out = '\0'; | 223 | *out = '\0'; |
236 | return 0; | 224 | return 0; |
237 | } | 225 | } |
238 | 226 | ||
@@ -246,7 +234,7 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) | |||
246 | wmi_parse_guid(guid_string, tmp); | 234 | wmi_parse_guid(guid_string, tmp); |
247 | wmi_swap_bytes(tmp, guid_input); | 235 | wmi_swap_bytes(tmp, guid_input); |
248 | 236 | ||
249 | list_for_each(p, &wmi_blocks.list) { | 237 | list_for_each(p, &wmi_block_list) { |
250 | wblock = list_entry(p, struct wmi_block, list); | 238 | wblock = list_entry(p, struct wmi_block, list); |
251 | block = &wblock->gblock; | 239 | block = &wblock->gblock; |
252 | 240 | ||
@@ -487,30 +475,29 @@ const struct acpi_buffer *in) | |||
487 | } | 475 | } |
488 | EXPORT_SYMBOL_GPL(wmi_set_block); | 476 | EXPORT_SYMBOL_GPL(wmi_set_block); |
489 | 477 | ||
490 | static void wmi_dump_wdg(struct guid_block *g) | 478 | static void wmi_dump_wdg(const struct guid_block *g) |
491 | { | 479 | { |
492 | char guid_string[37]; | 480 | char guid_string[37]; |
493 | 481 | ||
494 | wmi_gtoa(g->guid, guid_string); | 482 | wmi_gtoa(g->guid, guid_string); |
495 | printk(KERN_INFO PREFIX "%s:\n", guid_string); | 483 | |
496 | printk(KERN_INFO PREFIX "\tobject_id: %c%c\n", | 484 | pr_info("%s:\n", guid_string); |
497 | g->object_id[0], g->object_id[1]); | 485 | pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); |
498 | printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id); | 486 | pr_info("\tnotify_id: %02X\n", g->notify_id); |
499 | printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved); | 487 | pr_info("\treserved: %02X\n", g->reserved); |
500 | printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count); | 488 | pr_info("\tinstance_count: %d\n", g->instance_count); |
501 | printk(KERN_INFO PREFIX "\tflags: %#x", g->flags); | 489 | pr_info("\tflags: %#x", g->flags); |
502 | if (g->flags) { | 490 | if (g->flags) { |
503 | printk(" "); | ||
504 | if (g->flags & ACPI_WMI_EXPENSIVE) | 491 | if (g->flags & ACPI_WMI_EXPENSIVE) |
505 | printk("ACPI_WMI_EXPENSIVE "); | 492 | pr_cont(" ACPI_WMI_EXPENSIVE"); |
506 | if (g->flags & ACPI_WMI_METHOD) | 493 | if (g->flags & ACPI_WMI_METHOD) |
507 | printk("ACPI_WMI_METHOD "); | 494 | pr_cont(" ACPI_WMI_METHOD"); |
508 | if (g->flags & ACPI_WMI_STRING) | 495 | if (g->flags & ACPI_WMI_STRING) |
509 | printk("ACPI_WMI_STRING "); | 496 | pr_cont(" ACPI_WMI_STRING"); |
510 | if (g->flags & ACPI_WMI_EVENT) | 497 | if (g->flags & ACPI_WMI_EVENT) |
511 | printk("ACPI_WMI_EVENT "); | 498 | pr_cont(" ACPI_WMI_EVENT"); |
512 | } | 499 | } |
513 | printk("\n"); | 500 | pr_cont("\n"); |
514 | 501 | ||
515 | } | 502 | } |
516 | 503 | ||
@@ -522,7 +509,7 @@ static void wmi_notify_debug(u32 value, void *context) | |||
522 | 509 | ||
523 | status = wmi_get_event_data(value, &response); | 510 | status = wmi_get_event_data(value, &response); |
524 | if (status != AE_OK) { | 511 | if (status != AE_OK) { |
525 | printk(KERN_INFO "wmi: bad event status 0x%x\n", status); | 512 | pr_info("bad event status 0x%x\n", status); |
526 | return; | 513 | return; |
527 | } | 514 | } |
528 | 515 | ||
@@ -531,22 +518,22 @@ static void wmi_notify_debug(u32 value, void *context) | |||
531 | if (!obj) | 518 | if (!obj) |
532 | return; | 519 | return; |
533 | 520 | ||
534 | printk(KERN_INFO PREFIX "DEBUG Event "); | 521 | pr_info("DEBUG Event "); |
535 | switch(obj->type) { | 522 | switch(obj->type) { |
536 | case ACPI_TYPE_BUFFER: | 523 | case ACPI_TYPE_BUFFER: |
537 | printk("BUFFER_TYPE - length %d\n", obj->buffer.length); | 524 | pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); |
538 | break; | 525 | break; |
539 | case ACPI_TYPE_STRING: | 526 | case ACPI_TYPE_STRING: |
540 | printk("STRING_TYPE - %s\n", obj->string.pointer); | 527 | pr_cont("STRING_TYPE - %s\n", obj->string.pointer); |
541 | break; | 528 | break; |
542 | case ACPI_TYPE_INTEGER: | 529 | case ACPI_TYPE_INTEGER: |
543 | printk("INTEGER_TYPE - %llu\n", obj->integer.value); | 530 | pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); |
544 | break; | 531 | break; |
545 | case ACPI_TYPE_PACKAGE: | 532 | case ACPI_TYPE_PACKAGE: |
546 | printk("PACKAGE_TYPE - %d elements\n", obj->package.count); | 533 | pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); |
547 | break; | 534 | break; |
548 | default: | 535 | default: |
549 | printk("object type 0x%X\n", obj->type); | 536 | pr_cont("object type 0x%X\n", obj->type); |
550 | } | 537 | } |
551 | kfree(obj); | 538 | kfree(obj); |
552 | } | 539 | } |
@@ -562,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid, | |||
562 | wmi_notify_handler handler, void *data) | 549 | wmi_notify_handler handler, void *data) |
563 | { | 550 | { |
564 | struct wmi_block *block; | 551 | struct wmi_block *block; |
565 | acpi_status status; | 552 | acpi_status status = AE_NOT_EXIST; |
553 | char tmp[16], guid_input[16]; | ||
554 | struct list_head *p; | ||
566 | 555 | ||
567 | if (!guid || !handler) | 556 | if (!guid || !handler) |
568 | return AE_BAD_PARAMETER; | 557 | return AE_BAD_PARAMETER; |
569 | 558 | ||
570 | if (!find_guid(guid, &block)) | 559 | wmi_parse_guid(guid, tmp); |
571 | return AE_NOT_EXIST; | 560 | wmi_swap_bytes(tmp, guid_input); |
572 | 561 | ||
573 | if (block->handler && block->handler != wmi_notify_debug) | 562 | list_for_each(p, &wmi_block_list) { |
574 | return AE_ALREADY_ACQUIRED; | 563 | acpi_status wmi_status; |
564 | block = list_entry(p, struct wmi_block, list); | ||
575 | 565 | ||
576 | block->handler = handler; | 566 | if (memcmp(block->gblock.guid, guid_input, 16) == 0) { |
577 | block->handler_data = data; | 567 | if (block->handler && |
568 | block->handler != wmi_notify_debug) | ||
569 | return AE_ALREADY_ACQUIRED; | ||
578 | 570 | ||
579 | 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 | } | ||
580 | 580 | ||
581 | return status; | 581 | return status; |
582 | } | 582 | } |
@@ -590,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); | |||
590 | acpi_status wmi_remove_notify_handler(const char *guid) | 590 | acpi_status wmi_remove_notify_handler(const char *guid) |
591 | { | 591 | { |
592 | struct wmi_block *block; | 592 | struct wmi_block *block; |
593 | acpi_status status = AE_OK; | 593 | acpi_status status = AE_NOT_EXIST; |
594 | char tmp[16], guid_input[16]; | ||
595 | struct list_head *p; | ||
594 | 596 | ||
595 | if (!guid) | 597 | if (!guid) |
596 | return AE_BAD_PARAMETER; | 598 | return AE_BAD_PARAMETER; |
597 | 599 | ||
598 | if (!find_guid(guid, &block)) | 600 | wmi_parse_guid(guid, tmp); |
599 | return AE_NOT_EXIST; | 601 | wmi_swap_bytes(tmp, guid_input); |
600 | 602 | ||
601 | if (!block->handler || block->handler == wmi_notify_debug) | 603 | list_for_each(p, &wmi_block_list) { |
602 | return AE_NULL_ENTRY; | 604 | acpi_status wmi_status; |
605 | block = list_entry(p, struct wmi_block, list); | ||
603 | 606 | ||
604 | if (debug_event) { | 607 | if (memcmp(block->gblock.guid, guid_input, 16) == 0) { |
605 | block->handler = wmi_notify_debug; | 608 | if (!block->handler || |
606 | } else { | 609 | block->handler == wmi_notify_debug) |
607 | status = wmi_method_enable(block, 0); | 610 | return AE_NULL_ENTRY; |
608 | block->handler = NULL; | 611 | |
609 | 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 | } | ||
610 | } | 625 | } |
626 | |||
611 | return status; | 627 | return status; |
612 | } | 628 | } |
613 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); | 629 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); |
@@ -633,7 +649,7 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) | |||
633 | params[0].type = ACPI_TYPE_INTEGER; | 649 | params[0].type = ACPI_TYPE_INTEGER; |
634 | params[0].integer.value = event; | 650 | params[0].integer.value = event; |
635 | 651 | ||
636 | list_for_each(p, &wmi_blocks.list) { | 652 | list_for_each(p, &wmi_block_list) { |
637 | wblock = list_entry(p, struct wmi_block, list); | 653 | wblock = list_entry(p, struct wmi_block, list); |
638 | gblock = &wblock->gblock; | 654 | gblock = &wblock->gblock; |
639 | 655 | ||
@@ -662,7 +678,7 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); | |||
662 | /* | 678 | /* |
663 | * sysfs interface | 679 | * sysfs interface |
664 | */ | 680 | */ |
665 | static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, | 681 | static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, |
666 | char *buf) | 682 | char *buf) |
667 | { | 683 | { |
668 | char guid_string[37]; | 684 | char guid_string[37]; |
@@ -676,7 +692,11 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, | |||
676 | 692 | ||
677 | return sprintf(buf, "wmi:%s\n", guid_string); | 693 | return sprintf(buf, "wmi:%s\n", guid_string); |
678 | } | 694 | } |
679 | static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); | 695 | |
696 | static struct device_attribute wmi_dev_attrs[] = { | ||
697 | __ATTR_RO(modalias), | ||
698 | __ATTR_NULL | ||
699 | }; | ||
680 | 700 | ||
681 | static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) | 701 | static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) |
682 | { | 702 | { |
@@ -702,108 +722,51 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
702 | 722 | ||
703 | static void wmi_dev_free(struct device *dev) | 723 | static void wmi_dev_free(struct device *dev) |
704 | { | 724 | { |
705 | kfree(dev); | 725 | struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); |
726 | |||
727 | kfree(wmi_block); | ||
706 | } | 728 | } |
707 | 729 | ||
708 | static struct class wmi_class = { | 730 | static struct class wmi_class = { |
709 | .name = "wmi", | 731 | .name = "wmi", |
710 | .dev_release = wmi_dev_free, | 732 | .dev_release = wmi_dev_free, |
711 | .dev_uevent = wmi_dev_uevent, | 733 | .dev_uevent = wmi_dev_uevent, |
734 | .dev_attrs = wmi_dev_attrs, | ||
712 | }; | 735 | }; |
713 | 736 | ||
714 | static int wmi_create_devs(void) | 737 | static int wmi_create_device(const struct guid_block *gblock, |
738 | struct wmi_block *wblock, acpi_handle handle) | ||
715 | { | 739 | { |
716 | int result; | ||
717 | char guid_string[37]; | 740 | char guid_string[37]; |
718 | struct guid_block *gblock; | ||
719 | struct wmi_block *wblock; | ||
720 | struct list_head *p; | ||
721 | struct device *guid_dev; | ||
722 | |||
723 | /* Create devices for all the GUIDs */ | ||
724 | list_for_each(p, &wmi_blocks.list) { | ||
725 | wblock = list_entry(p, struct wmi_block, list); | ||
726 | |||
727 | guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); | ||
728 | if (!guid_dev) | ||
729 | return -ENOMEM; | ||
730 | |||
731 | wblock->dev = guid_dev; | ||
732 | |||
733 | guid_dev->class = &wmi_class; | ||
734 | dev_set_drvdata(guid_dev, wblock); | ||
735 | |||
736 | gblock = &wblock->gblock; | ||
737 | 741 | ||
738 | wmi_gtoa(gblock->guid, guid_string); | 742 | wblock->dev.class = &wmi_class; |
739 | dev_set_name(guid_dev, guid_string); | ||
740 | 743 | ||
741 | result = device_register(guid_dev); | 744 | wmi_gtoa(gblock->guid, guid_string); |
742 | if (result) | 745 | dev_set_name(&wblock->dev, guid_string); |
743 | return result; | ||
744 | 746 | ||
745 | result = device_create_file(guid_dev, &dev_attr_modalias); | 747 | dev_set_drvdata(&wblock->dev, wblock); |
746 | if (result) | ||
747 | return result; | ||
748 | } | ||
749 | 748 | ||
750 | return 0; | 749 | return device_register(&wblock->dev); |
751 | } | 750 | } |
752 | 751 | ||
753 | static void wmi_remove_devs(void) | 752 | static void wmi_free_devices(void) |
754 | { | 753 | { |
755 | struct guid_block *gblock; | 754 | struct wmi_block *wblock, *next; |
756 | struct wmi_block *wblock; | ||
757 | struct list_head *p; | ||
758 | struct device *guid_dev; | ||
759 | 755 | ||
760 | /* Delete devices for all the GUIDs */ | 756 | /* Delete devices for all the GUIDs */ |
761 | list_for_each(p, &wmi_blocks.list) { | 757 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) |
762 | wblock = list_entry(p, struct wmi_block, list); | 758 | if (wblock->dev.class) |
763 | 759 | device_unregister(&wblock->dev); | |
764 | guid_dev = wblock->dev; | ||
765 | gblock = &wblock->gblock; | ||
766 | |||
767 | device_remove_file(guid_dev, &dev_attr_modalias); | ||
768 | |||
769 | device_unregister(guid_dev); | ||
770 | } | ||
771 | } | ||
772 | |||
773 | static void wmi_class_exit(void) | ||
774 | { | ||
775 | wmi_remove_devs(); | ||
776 | class_unregister(&wmi_class); | ||
777 | } | ||
778 | |||
779 | static int wmi_class_init(void) | ||
780 | { | ||
781 | int ret; | ||
782 | |||
783 | ret = class_register(&wmi_class); | ||
784 | if (ret) | ||
785 | return ret; | ||
786 | |||
787 | ret = wmi_create_devs(); | ||
788 | if (ret) | ||
789 | wmi_class_exit(); | ||
790 | |||
791 | return ret; | ||
792 | } | 760 | } |
793 | 761 | ||
794 | static bool guid_already_parsed(const char *guid_string) | 762 | static bool guid_already_parsed(const char *guid_string) |
795 | { | 763 | { |
796 | struct guid_block *gblock; | ||
797 | struct wmi_block *wblock; | 764 | struct wmi_block *wblock; |
798 | struct list_head *p; | ||
799 | 765 | ||
800 | list_for_each(p, &wmi_blocks.list) { | 766 | list_for_each_entry(wblock, &wmi_block_list, list) |
801 | wblock = list_entry(p, struct wmi_block, list); | 767 | if (memcmp(wblock->gblock.guid, guid_string, 16) == 0) |
802 | gblock = &wblock->gblock; | ||
803 | |||
804 | if (strncmp(gblock->guid, guid_string, 16) == 0) | ||
805 | return true; | 768 | return true; |
806 | } | 769 | |
807 | return false; | 770 | return false; |
808 | } | 771 | } |
809 | 772 | ||
@@ -814,68 +777,67 @@ static acpi_status parse_wdg(acpi_handle handle) | |||
814 | { | 777 | { |
815 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | 778 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; |
816 | union acpi_object *obj; | 779 | union acpi_object *obj; |
817 | struct guid_block *gblock; | 780 | const struct guid_block *gblock; |
818 | struct wmi_block *wblock; | 781 | struct wmi_block *wblock; |
819 | char guid_string[37]; | ||
820 | acpi_status status; | 782 | acpi_status status; |
783 | int retval; | ||
821 | u32 i, total; | 784 | u32 i, total; |
822 | 785 | ||
823 | status = acpi_evaluate_object(handle, "_WDG", NULL, &out); | 786 | status = acpi_evaluate_object(handle, "_WDG", NULL, &out); |
824 | |||
825 | if (ACPI_FAILURE(status)) | 787 | if (ACPI_FAILURE(status)) |
826 | return status; | 788 | return -ENXIO; |
827 | 789 | ||
828 | obj = (union acpi_object *) out.pointer; | 790 | obj = (union acpi_object *) out.pointer; |
791 | if (!obj) | ||
792 | return -ENXIO; | ||
829 | 793 | ||
830 | if (obj->type != ACPI_TYPE_BUFFER) | 794 | if (obj->type != ACPI_TYPE_BUFFER) { |
831 | return AE_ERROR; | 795 | retval = -ENXIO; |
832 | |||
833 | total = obj->buffer.length / sizeof(struct guid_block); | ||
834 | |||
835 | gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); | ||
836 | if (!gblock) { | ||
837 | status = AE_NO_MEMORY; | ||
838 | goto out_free_pointer; | 796 | goto out_free_pointer; |
839 | } | 797 | } |
840 | 798 | ||
799 | gblock = (const struct guid_block *)obj->buffer.pointer; | ||
800 | total = obj->buffer.length / sizeof(struct guid_block); | ||
801 | |||
841 | 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 | |||
842 | /* | 813 | /* |
843 | Some WMI devices, like those for nVidia hooks, have a | 814 | Some WMI devices, like those for nVidia hooks, have a |
844 | 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 |
845 | case yet, so for now, we'll just ignore the duplicate. | 816 | case yet, so for now, we'll just ignore the duplicate |
846 | Anyone who wants to add support for that device can come | 817 | for device creation. |
847 | up with a better workaround for the mess then. | ||
848 | */ | 818 | */ |
849 | if (guid_already_parsed(gblock[i].guid) == true) { | 819 | if (!guid_already_parsed(gblock[i].guid)) { |
850 | wmi_gtoa(gblock[i].guid, guid_string); | 820 | retval = wmi_create_device(&gblock[i], wblock, handle); |
851 | printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n", | 821 | if (retval) { |
852 | guid_string); | 822 | wmi_free_devices(); |
853 | continue; | 823 | goto out_free_pointer; |
824 | } | ||
854 | } | 825 | } |
855 | if (debug_dump_wdg) | ||
856 | wmi_dump_wdg(&gblock[i]); | ||
857 | 826 | ||
858 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | 827 | list_add_tail(&wblock->list, &wmi_block_list); |
859 | if (!wblock) { | ||
860 | status = AE_NO_MEMORY; | ||
861 | goto out_free_gblock; | ||
862 | } | ||
863 | 828 | ||
864 | wblock->gblock = gblock[i]; | ||
865 | wblock->handle = handle; | ||
866 | if (debug_event) { | 829 | if (debug_event) { |
867 | wblock->handler = wmi_notify_debug; | 830 | wblock->handler = wmi_notify_debug; |
868 | status = wmi_method_enable(wblock, 1); | 831 | wmi_method_enable(wblock, 1); |
869 | } | 832 | } |
870 | list_add_tail(&wblock->list, &wmi_blocks.list); | ||
871 | } | 833 | } |
872 | 834 | ||
873 | out_free_gblock: | 835 | retval = 0; |
874 | kfree(gblock); | 836 | |
875 | out_free_pointer: | 837 | out_free_pointer: |
876 | kfree(out.pointer); | 838 | kfree(out.pointer); |
877 | 839 | ||
878 | return status; | 840 | return retval; |
879 | } | 841 | } |
880 | 842 | ||
881 | /* | 843 | /* |
@@ -929,7 +891,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) | |||
929 | struct list_head *p; | 891 | struct list_head *p; |
930 | char guid_string[37]; | 892 | char guid_string[37]; |
931 | 893 | ||
932 | list_for_each(p, &wmi_blocks.list) { | 894 | list_for_each(p, &wmi_block_list) { |
933 | wblock = list_entry(p, struct wmi_block, list); | 895 | wblock = list_entry(p, struct wmi_block, list); |
934 | block = &wblock->gblock; | 896 | block = &wblock->gblock; |
935 | 897 | ||
@@ -939,8 +901,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) | |||
939 | wblock->handler(event, wblock->handler_data); | 901 | wblock->handler(event, wblock->handler_data); |
940 | if (debug_event) { | 902 | if (debug_event) { |
941 | wmi_gtoa(wblock->gblock.guid, guid_string); | 903 | wmi_gtoa(wblock->gblock.guid, guid_string); |
942 | printk(KERN_INFO PREFIX "DEBUG Event GUID:" | 904 | pr_info("DEBUG Event GUID: %s\n", guid_string); |
943 | " %s\n", guid_string); | ||
944 | } | 905 | } |
945 | 906 | ||
946 | acpi_bus_generate_netlink_event( | 907 | acpi_bus_generate_netlink_event( |
@@ -955,6 +916,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) | |||
955 | { | 916 | { |
956 | acpi_remove_address_space_handler(device->handle, | 917 | acpi_remove_address_space_handler(device->handle, |
957 | ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); | 918 | ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); |
919 | wmi_free_devices(); | ||
958 | 920 | ||
959 | return 0; | 921 | return 0; |
960 | } | 922 | } |
@@ -962,68 +924,57 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) | |||
962 | static int acpi_wmi_add(struct acpi_device *device) | 924 | static int acpi_wmi_add(struct acpi_device *device) |
963 | { | 925 | { |
964 | acpi_status status; | 926 | acpi_status status; |
965 | int result = 0; | 927 | int error; |
966 | 928 | ||
967 | status = acpi_install_address_space_handler(device->handle, | 929 | status = acpi_install_address_space_handler(device->handle, |
968 | ACPI_ADR_SPACE_EC, | 930 | ACPI_ADR_SPACE_EC, |
969 | &acpi_wmi_ec_space_handler, | 931 | &acpi_wmi_ec_space_handler, |
970 | NULL, NULL); | 932 | NULL, NULL); |
971 | if (ACPI_FAILURE(status)) | ||
972 | return -ENODEV; | ||
973 | |||
974 | status = parse_wdg(device->handle); | ||
975 | if (ACPI_FAILURE(status)) { | 933 | if (ACPI_FAILURE(status)) { |
976 | printk(KERN_ERR PREFIX "Error installing EC region handler\n"); | 934 | pr_err("Error installing EC region handler\n"); |
977 | return -ENODEV; | 935 | return -ENODEV; |
978 | } | 936 | } |
979 | 937 | ||
980 | return result; | 938 | error = parse_wdg(device->handle); |
939 | if (error) { | ||
940 | acpi_remove_address_space_handler(device->handle, | ||
941 | ACPI_ADR_SPACE_EC, | ||
942 | &acpi_wmi_ec_space_handler); | ||
943 | pr_err("Failed to parse WDG method\n"); | ||
944 | return error; | ||
945 | } | ||
946 | |||
947 | return 0; | ||
981 | } | 948 | } |
982 | 949 | ||
983 | static int __init acpi_wmi_init(void) | 950 | static int __init acpi_wmi_init(void) |
984 | { | 951 | { |
985 | int result; | 952 | int error; |
986 | |||
987 | INIT_LIST_HEAD(&wmi_blocks.list); | ||
988 | 953 | ||
989 | if (acpi_disabled) | 954 | if (acpi_disabled) |
990 | return -ENODEV; | 955 | return -ENODEV; |
991 | 956 | ||
992 | result = acpi_bus_register_driver(&acpi_wmi_driver); | 957 | error = class_register(&wmi_class); |
993 | 958 | if (error) | |
994 | if (result < 0) { | 959 | return error; |
995 | printk(KERN_INFO PREFIX "Error loading mapper\n"); | ||
996 | return -ENODEV; | ||
997 | } | ||
998 | 960 | ||
999 | result = wmi_class_init(); | 961 | error = acpi_bus_register_driver(&acpi_wmi_driver); |
1000 | if (result) { | 962 | if (error) { |
1001 | acpi_bus_unregister_driver(&acpi_wmi_driver); | 963 | pr_err("Error loading mapper\n"); |
1002 | return result; | 964 | class_unregister(&wmi_class); |
965 | return error; | ||
1003 | } | 966 | } |
1004 | 967 | ||
1005 | printk(KERN_INFO PREFIX "Mapper loaded\n"); | 968 | pr_info("Mapper loaded\n"); |
1006 | 969 | return 0; | |
1007 | return result; | ||
1008 | } | 970 | } |
1009 | 971 | ||
1010 | static void __exit acpi_wmi_exit(void) | 972 | static void __exit acpi_wmi_exit(void) |
1011 | { | 973 | { |
1012 | struct list_head *p, *tmp; | ||
1013 | struct wmi_block *wblock; | ||
1014 | |||
1015 | wmi_class_exit(); | ||
1016 | |||
1017 | acpi_bus_unregister_driver(&acpi_wmi_driver); | 974 | acpi_bus_unregister_driver(&acpi_wmi_driver); |
975 | class_unregister(&wmi_class); | ||
1018 | 976 | ||
1019 | list_for_each_safe(p, tmp, &wmi_blocks.list) { | 977 | pr_info("Mapper unloaded\n"); |
1020 | wblock = list_entry(p, struct wmi_block, list); | ||
1021 | |||
1022 | list_del(p); | ||
1023 | kfree(wblock); | ||
1024 | } | ||
1025 | |||
1026 | printk(KERN_INFO PREFIX "Mapper unloaded\n"); | ||
1027 | } | 978 | } |
1028 | 979 | ||
1029 | subsys_initcall(acpi_wmi_init); | 980 | subsys_initcall(acpi_wmi_init); |
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c new file mode 100644 index 000000000000..e549eeeda121 --- /dev/null +++ b/drivers/platform/x86/xo1-rfkill.c | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * Support for rfkill through the OLPC XO-1 laptop embedded controller | ||
3 | * | ||
4 | * Copyright (C) 2010 One Laptop per Child | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/rfkill.h> | ||
15 | |||
16 | #include <asm/olpc.h> | ||
17 | |||
18 | static int rfkill_set_block(void *data, bool blocked) | ||
19 | { | ||
20 | unsigned char cmd; | ||
21 | if (blocked) | ||
22 | cmd = EC_WLAN_ENTER_RESET; | ||
23 | else | ||
24 | cmd = EC_WLAN_LEAVE_RESET; | ||
25 | |||
26 | return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); | ||
27 | } | ||
28 | |||
29 | static const struct rfkill_ops rfkill_ops = { | ||
30 | .set_block = rfkill_set_block, | ||
31 | }; | ||
32 | |||
33 | static int __devinit xo1_rfkill_probe(struct platform_device *pdev) | ||
34 | { | ||
35 | struct rfkill *rfk; | ||
36 | int r; | ||
37 | |||
38 | rfk = rfkill_alloc(pdev->name, &pdev->dev, RFKILL_TYPE_WLAN, | ||
39 | &rfkill_ops, NULL); | ||
40 | if (!rfk) | ||
41 | return -ENOMEM; | ||
42 | |||
43 | r = rfkill_register(rfk); | ||
44 | if (r) { | ||
45 | rfkill_destroy(rfk); | ||
46 | return r; | ||
47 | } | ||
48 | |||
49 | platform_set_drvdata(pdev, rfk); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static int __devexit xo1_rfkill_remove(struct platform_device *pdev) | ||
54 | { | ||
55 | struct rfkill *rfk = platform_get_drvdata(pdev); | ||
56 | rfkill_unregister(rfk); | ||
57 | rfkill_destroy(rfk); | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static struct platform_driver xo1_rfkill_driver = { | ||
62 | .driver = { | ||
63 | .name = "xo1-rfkill", | ||
64 | .owner = THIS_MODULE, | ||
65 | }, | ||
66 | .probe = xo1_rfkill_probe, | ||
67 | .remove = __devexit_p(xo1_rfkill_remove), | ||
68 | }; | ||
69 | |||
70 | static int __init xo1_rfkill_init(void) | ||
71 | { | ||
72 | return platform_driver_register(&xo1_rfkill_driver); | ||
73 | } | ||
74 | |||
75 | static void __exit xo1_rfkill_exit(void) | ||
76 | { | ||
77 | platform_driver_unregister(&xo1_rfkill_driver); | ||
78 | } | ||
79 | |||
80 | MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>"); | ||
81 | MODULE_LICENSE("GPL"); | ||
82 | MODULE_ALIAS("platform:xo1-rfkill"); | ||
83 | |||
84 | module_init(xo1_rfkill_init); | ||
85 | module_exit(xo1_rfkill_exit); | ||
diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c new file mode 100644 index 000000000000..fad153dc0355 --- /dev/null +++ b/drivers/platform/x86/xo15-ebook.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * OLPC XO-1.5 ebook switch driver | ||
3 | * (based on generic ACPI button driver) | ||
4 | * | ||
5 | * Copyright (C) 2009 Paul Fox <pgf@laptop.org> | ||
6 | * Copyright (C) 2010 One Laptop per Child | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/input.h> | ||
21 | #include <acpi/acpi_bus.h> | ||
22 | #include <acpi/acpi_drivers.h> | ||
23 | |||
24 | #define MODULE_NAME "xo15-ebook" | ||
25 | |||
26 | #define XO15_EBOOK_CLASS MODULE_NAME | ||
27 | #define XO15_EBOOK_TYPE_UNKNOWN 0x00 | ||
28 | #define XO15_EBOOK_NOTIFY_STATUS 0x80 | ||
29 | |||
30 | #define XO15_EBOOK_SUBCLASS "ebook" | ||
31 | #define XO15_EBOOK_HID "XO15EBK" | ||
32 | #define XO15_EBOOK_DEVICE_NAME "EBook Switch" | ||
33 | |||
34 | ACPI_MODULE_NAME(MODULE_NAME); | ||
35 | |||
36 | MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | |||
39 | static const struct acpi_device_id ebook_device_ids[] = { | ||
40 | { XO15_EBOOK_HID, 0 }, | ||
41 | { "", 0 }, | ||
42 | }; | ||
43 | MODULE_DEVICE_TABLE(acpi, ebook_device_ids); | ||
44 | |||
45 | struct ebook_switch { | ||
46 | struct input_dev *input; | ||
47 | char phys[32]; /* for input device */ | ||
48 | }; | ||
49 | |||
50 | static int ebook_send_state(struct acpi_device *device) | ||
51 | { | ||
52 | struct ebook_switch *button = acpi_driver_data(device); | ||
53 | unsigned long long state; | ||
54 | acpi_status status; | ||
55 | |||
56 | status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state); | ||
57 | if (ACPI_FAILURE(status)) | ||
58 | return -EIO; | ||
59 | |||
60 | /* input layer checks if event is redundant */ | ||
61 | input_report_switch(button->input, SW_TABLET_MODE, !state); | ||
62 | input_sync(button->input); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static void ebook_switch_notify(struct acpi_device *device, u32 event) | ||
67 | { | ||
68 | switch (event) { | ||
69 | case ACPI_FIXED_HARDWARE_EVENT: | ||
70 | case XO15_EBOOK_NOTIFY_STATUS: | ||
71 | ebook_send_state(device); | ||
72 | break; | ||
73 | default: | ||
74 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
75 | "Unsupported event [0x%x]\n", event)); | ||
76 | break; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | static int ebook_switch_resume(struct acpi_device *device) | ||
81 | { | ||
82 | return ebook_send_state(device); | ||
83 | } | ||
84 | |||
85 | static int ebook_switch_add(struct acpi_device *device) | ||
86 | { | ||
87 | struct ebook_switch *button; | ||
88 | struct input_dev *input; | ||
89 | const char *hid = acpi_device_hid(device); | ||
90 | char *name, *class; | ||
91 | int error; | ||
92 | |||
93 | button = kzalloc(sizeof(struct ebook_switch), GFP_KERNEL); | ||
94 | if (!button) | ||
95 | return -ENOMEM; | ||
96 | |||
97 | device->driver_data = button; | ||
98 | |||
99 | button->input = input = input_allocate_device(); | ||
100 | if (!input) { | ||
101 | error = -ENOMEM; | ||
102 | goto err_free_button; | ||
103 | } | ||
104 | |||
105 | name = acpi_device_name(device); | ||
106 | class = acpi_device_class(device); | ||
107 | |||
108 | if (strcmp(hid, XO15_EBOOK_HID)) { | ||
109 | pr_err("Unsupported hid [%s]\n", hid); | ||
110 | error = -ENODEV; | ||
111 | goto err_free_input; | ||
112 | } | ||
113 | |||
114 | strcpy(name, XO15_EBOOK_DEVICE_NAME); | ||
115 | sprintf(class, "%s/%s", XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS); | ||
116 | |||
117 | snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); | ||
118 | |||
119 | input->name = name; | ||
120 | input->phys = button->phys; | ||
121 | input->id.bustype = BUS_HOST; | ||
122 | input->dev.parent = &device->dev; | ||
123 | |||
124 | input->evbit[0] = BIT_MASK(EV_SW); | ||
125 | set_bit(SW_TABLET_MODE, input->swbit); | ||
126 | |||
127 | error = input_register_device(input); | ||
128 | if (error) | ||
129 | goto err_free_input; | ||
130 | |||
131 | ebook_send_state(device); | ||
132 | |||
133 | if (device->wakeup.flags.valid) { | ||
134 | /* Button's GPE is run-wake GPE */ | ||
135 | acpi_enable_gpe(device->wakeup.gpe_device, | ||
136 | device->wakeup.gpe_number); | ||
137 | device_set_wakeup_enable(&device->dev, true); | ||
138 | } | ||
139 | |||
140 | return 0; | ||
141 | |||
142 | err_free_input: | ||
143 | input_free_device(input); | ||
144 | err_free_button: | ||
145 | kfree(button); | ||
146 | return error; | ||
147 | } | ||
148 | |||
149 | static int ebook_switch_remove(struct acpi_device *device, int type) | ||
150 | { | ||
151 | struct ebook_switch *button = acpi_driver_data(device); | ||
152 | |||
153 | input_unregister_device(button->input); | ||
154 | kfree(button); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static struct acpi_driver xo15_ebook_driver = { | ||
159 | .name = MODULE_NAME, | ||
160 | .class = XO15_EBOOK_CLASS, | ||
161 | .ids = ebook_device_ids, | ||
162 | .ops = { | ||
163 | .add = ebook_switch_add, | ||
164 | .resume = ebook_switch_resume, | ||
165 | .remove = ebook_switch_remove, | ||
166 | .notify = ebook_switch_notify, | ||
167 | }, | ||
168 | }; | ||
169 | |||
170 | static int __init xo15_ebook_init(void) | ||
171 | { | ||
172 | return acpi_bus_register_driver(&xo15_ebook_driver); | ||
173 | } | ||
174 | |||
175 | static void __exit xo15_ebook_exit(void) | ||
176 | { | ||
177 | acpi_bus_unregister_driver(&xo15_ebook_driver); | ||
178 | } | ||
179 | |||
180 | module_init(xo15_ebook_init); | ||
181 | module_exit(xo15_ebook_exit); | ||