diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 20:13:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 20:13:24 -0400 |
commit | 27a3353a4525afe984f3b793681869d636136b69 (patch) | |
tree | 6c89654b6203b5b3196c128ced8a6b6c8b60b58a | |
parent | 6f68fbaafbaa033205cd131d3e1f3c4b914e9b78 (diff) | |
parent | 785cfc0324b9321efb85b0935af2b474d615daa1 (diff) |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (32 commits)
Move N014, N051 and CR620 dmi information to load scm dmi table
drivers/platform/x86/eeepc-wmi.c: fix build warning
X86 platfrom wmi: Add debug facility to dump WMI data in a readable way
X86 platform wmi: Also log GUID string when an event happens and debug is set
X86 platform wmi: Introduce debug param to log all WMI events
Clean up all objects used by scm model when driver initial fail or exit
msi-laptop: fix up some coding style issues found by checkpatch
msi-laptop: Add i8042 filter to sync sw state with BIOS when function key pressed
msi-laptop: Set rfkill init state when msi-laptop intiial
msi-laptop: Add MSI CR620 notebook dmi information to scm models table
msi-laptop: Add N014 N051 dmi information to scm models table
drivers/platform/x86: Use kmemdup
drivers/platform/x86: Use kzalloc
drivers/platform/x86: Clarify the MRST IPC driver description slightly
eeepc-wmi: depends on BACKLIGHT_CLASS_DEVICE
IPC driver for Intel Mobile Internet Device (MID) platforms
classmate-laptop: Add RFKILL support.
thinkpad-acpi: document backlight level writeback at driver init
thinkpad-acpi: clean up ACPI handles handling
thinkpad-acpi: don't depend on led_path for led firmware type (v2)
...
-rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 66 | ||||
-rw-r--r-- | arch/x86/include/asm/intel_scu_ipc.h | 55 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 10 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/classmate-laptop.c | 170 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-wmi.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 6 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 829 | ||||
-rw-r--r-- | drivers/platform/x86/msi-laptop.c | 163 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 600 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 103 |
11 files changed, 1710 insertions, 295 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 39c0a09d0105..fc15538d8b46 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
@@ -292,13 +292,13 @@ sysfs notes: | |||
292 | 292 | ||
293 | Warning: when in NVRAM mode, the volume up/down/mute | 293 | Warning: when in NVRAM mode, the volume up/down/mute |
294 | keys are synthesized according to changes in the mixer, | 294 | keys are synthesized according to changes in the mixer, |
295 | so you have to use volume up or volume down to unmute, | 295 | which uses a single volume up or volume down hotkey |
296 | as per the ThinkPad volume mixer user interface. When | 296 | press to unmute, as per the ThinkPad volume mixer user |
297 | in ACPI event mode, volume up/down/mute are reported as | 297 | interface. When in ACPI event mode, volume up/down/mute |
298 | separate events, but this behaviour may be corrected in | 298 | events are reported by the firmware and can behave |
299 | future releases of this driver, in which case the | 299 | differently (and that behaviour changes with firmware |
300 | ThinkPad volume mixer user interface semantics will be | 300 | version -- not just with firmware models -- as well as |
301 | enforced. | 301 | OSI(Linux) state). |
302 | 302 | ||
303 | hotkey_poll_freq: | 303 | hotkey_poll_freq: |
304 | frequency in Hz for hot key polling. It must be between | 304 | frequency in Hz for hot key polling. It must be between |
@@ -309,7 +309,7 @@ sysfs notes: | |||
309 | will cause hot key presses that require NVRAM polling | 309 | will cause hot key presses that require NVRAM polling |
310 | to never be reported. | 310 | to never be reported. |
311 | 311 | ||
312 | Setting hotkey_poll_freq too low will cause repeated | 312 | Setting hotkey_poll_freq too low may cause repeated |
313 | pressings of the same hot key to be misreported as a | 313 | pressings of the same hot key to be misreported as a |
314 | single key press, or to not even be detected at all. | 314 | single key press, or to not even be detected at all. |
315 | The recommended polling frequency is 10Hz. | 315 | The recommended polling frequency is 10Hz. |
@@ -397,6 +397,7 @@ ACPI Scan | |||
397 | event code Key Notes | 397 | event code Key Notes |
398 | 398 | ||
399 | 0x1001 0x00 FN+F1 - | 399 | 0x1001 0x00 FN+F1 - |
400 | |||
400 | 0x1002 0x01 FN+F2 IBM: battery (rare) | 401 | 0x1002 0x01 FN+F2 IBM: battery (rare) |
401 | Lenovo: Screen lock | 402 | Lenovo: Screen lock |
402 | 403 | ||
@@ -404,7 +405,8 @@ event code Key Notes | |||
404 | this hot key, even with hot keys | 405 | this hot key, even with hot keys |
405 | disabled or with Fn+F3 masked | 406 | disabled or with Fn+F3 masked |
406 | off | 407 | off |
407 | IBM: screen lock | 408 | IBM: screen lock, often turns |
409 | off the ThinkLight as side-effect | ||
408 | Lenovo: battery | 410 | Lenovo: battery |
409 | 411 | ||
410 | 0x1004 0x03 FN+F4 Sleep button (ACPI sleep button | 412 | 0x1004 0x03 FN+F4 Sleep button (ACPI sleep button |
@@ -433,7 +435,8 @@ event code Key Notes | |||
433 | Do you feel lucky today? | 435 | Do you feel lucky today? |
434 | 436 | ||
435 | 0x1008 0x07 FN+F8 IBM: toggle screen expand | 437 | 0x1008 0x07 FN+F8 IBM: toggle screen expand |
436 | Lenovo: configure UltraNav | 438 | Lenovo: configure UltraNav, |
439 | or toggle screen expand | ||
437 | 440 | ||
438 | 0x1009 0x08 FN+F9 - | 441 | 0x1009 0x08 FN+F9 - |
439 | .. .. .. | 442 | .. .. .. |
@@ -444,7 +447,7 @@ event code Key Notes | |||
444 | either through the ACPI event, | 447 | either through the ACPI event, |
445 | or through a hotkey event. | 448 | or through a hotkey event. |
446 | The firmware may refuse to | 449 | The firmware may refuse to |
447 | generate further FN+F4 key | 450 | generate further FN+F12 key |
448 | press events until a S3 or S4 | 451 | press events until a S3 or S4 |
449 | ACPI sleep cycle is performed, | 452 | ACPI sleep cycle is performed, |
450 | or some time passes. | 453 | or some time passes. |
@@ -512,15 +515,19 @@ events for switches: | |||
512 | SW_RFKILL_ALL T60 and later hardware rfkill rocker switch | 515 | SW_RFKILL_ALL T60 and later hardware rfkill rocker switch |
513 | SW_TABLET_MODE Tablet ThinkPads HKEY events 0x5009 and 0x500A | 516 | SW_TABLET_MODE Tablet ThinkPads HKEY events 0x5009 and 0x500A |
514 | 517 | ||
515 | Non hot-key ACPI HKEY event map: | 518 | Non hotkey ACPI HKEY event map: |
519 | ------------------------------- | ||
520 | |||
521 | Events that are not propagated by the driver, except for legacy | ||
522 | compatibility purposes when hotkey_report_mode is set to 1: | ||
523 | |||
516 | 0x5001 Lid closed | 524 | 0x5001 Lid closed |
517 | 0x5002 Lid opened | 525 | 0x5002 Lid opened |
518 | 0x5009 Tablet swivel: switched to tablet mode | 526 | 0x5009 Tablet swivel: switched to tablet mode |
519 | 0x500A Tablet swivel: switched to normal mode | 527 | 0x500A Tablet swivel: switched to normal mode |
520 | 0x7000 Radio Switch may have changed state | 528 | 0x7000 Radio Switch may have changed state |
521 | 529 | ||
522 | The above events are not propagated by the driver, except for legacy | 530 | Events that are never propagated by the driver: |
523 | compatibility purposes when hotkey_report_mode is set to 1. | ||
524 | 531 | ||
525 | 0x2304 System is waking up from suspend to undock | 532 | 0x2304 System is waking up from suspend to undock |
526 | 0x2305 System is waking up from suspend to eject bay | 533 | 0x2305 System is waking up from suspend to eject bay |
@@ -528,14 +535,39 @@ compatibility purposes when hotkey_report_mode is set to 1. | |||
528 | 0x2405 System is waking up from hibernation to eject bay | 535 | 0x2405 System is waking up from hibernation to eject bay |
529 | 0x5010 Brightness level changed/control event | 536 | 0x5010 Brightness level changed/control event |
530 | 537 | ||
531 | The above events are never propagated by the driver. | 538 | Events that are propagated by the driver to userspace: |
532 | 539 | ||
540 | 0x2313 ALARM: System is waking up from suspend because | ||
541 | the battery is nearly empty | ||
542 | 0x2413 ALARM: System is waking up from hibernation because | ||
543 | the battery is nearly empty | ||
533 | 0x3003 Bay ejection (see 0x2x05) complete, can sleep again | 544 | 0x3003 Bay ejection (see 0x2x05) complete, can sleep again |
545 | 0x3006 Bay hotplug request (hint to power up SATA link when | ||
546 | the optical drive tray is ejected) | ||
534 | 0x4003 Undocked (see 0x2x04), can sleep again | 547 | 0x4003 Undocked (see 0x2x04), can sleep again |
535 | 0x500B Tablet pen inserted into its storage bay | 548 | 0x500B Tablet pen inserted into its storage bay |
536 | 0x500C Tablet pen removed from its storage bay | 549 | 0x500C Tablet pen removed from its storage bay |
537 | 550 | 0x6011 ALARM: battery is too hot | |
538 | The above events are propagated by the driver. | 551 | 0x6012 ALARM: battery is extremely hot |
552 | 0x6021 ALARM: a sensor is too hot | ||
553 | 0x6022 ALARM: a sensor is extremely hot | ||
554 | 0x6030 System thermal table changed | ||
555 | |||
556 | Battery nearly empty alarms are a last resort attempt to get the | ||
557 | operating system to hibernate or shutdown cleanly (0x2313), or shutdown | ||
558 | cleanly (0x2413) before power is lost. They must be acted upon, as the | ||
559 | wake up caused by the firmware will have negated most safety nets... | ||
560 | |||
561 | When any of the "too hot" alarms happen, according to Lenovo the user | ||
562 | should suspend or hibernate the laptop (and in the case of battery | ||
563 | alarms, unplug the AC adapter) to let it cool down. These alarms do | ||
564 | signal that something is wrong, they should never happen on normal | ||
565 | operating conditions. | ||
566 | |||
567 | The "extremely hot" alarms are emergencies. According to Lenovo, the | ||
568 | operating system is to force either an immediate suspend or hibernate | ||
569 | cycle, or a system shutdown. Obviously, something is very wrong if this | ||
570 | happens. | ||
539 | 571 | ||
540 | Compatibility notes: | 572 | Compatibility notes: |
541 | 573 | ||
diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h new file mode 100644 index 000000000000..4470c9ad4a3e --- /dev/null +++ b/arch/x86/include/asm/intel_scu_ipc.h | |||
@@ -0,0 +1,55 @@ | |||
1 | #ifndef _ASM_X86_INTEL_SCU_IPC_H_ | ||
2 | #define _ASM_X86_INTEL_SCU_IPC_H_ | ||
3 | |||
4 | /* Read single register */ | ||
5 | int intel_scu_ipc_ioread8(u16 addr, u8 *data); | ||
6 | |||
7 | /* Read two sequential registers */ | ||
8 | int intel_scu_ipc_ioread16(u16 addr, u16 *data); | ||
9 | |||
10 | /* Read four sequential registers */ | ||
11 | int intel_scu_ipc_ioread32(u16 addr, u32 *data); | ||
12 | |||
13 | /* Read a vector */ | ||
14 | int intel_scu_ipc_readv(u16 *addr, u8 *data, int len); | ||
15 | |||
16 | /* Write single register */ | ||
17 | int intel_scu_ipc_iowrite8(u16 addr, u8 data); | ||
18 | |||
19 | /* Write two sequential registers */ | ||
20 | int intel_scu_ipc_iowrite16(u16 addr, u16 data); | ||
21 | |||
22 | /* Write four sequential registers */ | ||
23 | int intel_scu_ipc_iowrite32(u16 addr, u32 data); | ||
24 | |||
25 | /* Write a vector */ | ||
26 | int intel_scu_ipc_writev(u16 *addr, u8 *data, int len); | ||
27 | |||
28 | /* Update single register based on the mask */ | ||
29 | int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask); | ||
30 | |||
31 | /* | ||
32 | * Indirect register read | ||
33 | * Can be used when SCCB(System Controller Configuration Block) register | ||
34 | * HRIM(Honor Restricted IPC Messages) is set (bit 23) | ||
35 | */ | ||
36 | int intel_scu_ipc_register_read(u32 addr, u32 *data); | ||
37 | |||
38 | /* | ||
39 | * Indirect register write | ||
40 | * Can be used when SCCB(System Controller Configuration Block) register | ||
41 | * HRIM(Honor Restricted IPC Messages) is set (bit 23) | ||
42 | */ | ||
43 | int intel_scu_ipc_register_write(u32 addr, u32 data); | ||
44 | |||
45 | /* Issue commands to the SCU with or without data */ | ||
46 | int intel_scu_ipc_simple_command(int cmd, int sub); | ||
47 | int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, | ||
48 | u32 *out, int outlen); | ||
49 | /* I2C control api */ | ||
50 | int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data); | ||
51 | |||
52 | /* Update FW version */ | ||
53 | int intel_scu_ipc_fw_update(u8 *buffer, u32 length); | ||
54 | |||
55 | #endif | ||
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 6c3320d75055..3e1b8a288719 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -390,6 +390,7 @@ config EEEPC_WMI | |||
390 | depends on ACPI_WMI | 390 | depends on ACPI_WMI |
391 | depends on INPUT | 391 | depends on INPUT |
392 | depends on EXPERIMENTAL | 392 | depends on EXPERIMENTAL |
393 | depends on BACKLIGHT_CLASS_DEVICE | ||
393 | select INPUT_SPARSEKMAP | 394 | select INPUT_SPARSEKMAP |
394 | ---help--- | 395 | ---help--- |
395 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. | 396 | Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. |
@@ -527,4 +528,13 @@ config ACPI_CMPC | |||
527 | keys as input device, backlight device, tablet and accelerometer | 528 | keys as input device, backlight device, tablet and accelerometer |
528 | devices. | 529 | devices. |
529 | 530 | ||
531 | config INTEL_SCU_IPC | ||
532 | bool "Intel SCU IPC Support" | ||
533 | depends on X86_MRST | ||
534 | default y | ||
535 | ---help--- | ||
536 | IPC is used to bridge the communications between kernel and SCU on | ||
537 | some embedded Intel x86 platforms. This is not needed for PC-type | ||
538 | machines. | ||
539 | |||
530 | endif # X86_PLATFORM_DEVICES | 540 | endif # X86_PLATFORM_DEVICES |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index a906490e3530..8770bfe71431 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -25,3 +25,4 @@ obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o | |||
25 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | 25 | obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o |
26 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 26 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
27 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o | 27 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o |
28 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o | ||
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 7f9e5ddc9498..3bf399fe2bbc 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <acpi/acpi_drivers.h> | 24 | #include <acpi/acpi_drivers.h> |
25 | #include <linux/backlight.h> | 25 | #include <linux/backlight.h> |
26 | #include <linux/input.h> | 26 | #include <linux/input.h> |
27 | #include <linux/rfkill.h> | ||
27 | 28 | ||
28 | MODULE_LICENSE("GPL"); | 29 | MODULE_LICENSE("GPL"); |
29 | 30 | ||
@@ -37,7 +38,7 @@ struct cmpc_accel { | |||
37 | 38 | ||
38 | #define CMPC_ACCEL_HID "ACCE0000" | 39 | #define CMPC_ACCEL_HID "ACCE0000" |
39 | #define CMPC_TABLET_HID "TBLT0000" | 40 | #define CMPC_TABLET_HID "TBLT0000" |
40 | #define CMPC_BL_HID "IPML200" | 41 | #define CMPC_IPML_HID "IPML200" |
41 | #define CMPC_KEYS_HID "FnBT0000" | 42 | #define CMPC_KEYS_HID "FnBT0000" |
42 | 43 | ||
43 | /* | 44 | /* |
@@ -461,43 +462,168 @@ static const struct backlight_ops cmpc_bl_ops = { | |||
461 | .update_status = cmpc_bl_update_status | 462 | .update_status = cmpc_bl_update_status |
462 | }; | 463 | }; |
463 | 464 | ||
464 | static int cmpc_bl_add(struct acpi_device *acpi) | 465 | /* |
466 | * RFKILL code. | ||
467 | */ | ||
468 | |||
469 | static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle, | ||
470 | unsigned long long *value) | ||
465 | { | 471 | { |
466 | struct backlight_properties props; | 472 | union acpi_object param; |
473 | struct acpi_object_list input; | ||
474 | unsigned long long output; | ||
475 | acpi_status status; | ||
476 | |||
477 | param.type = ACPI_TYPE_INTEGER; | ||
478 | param.integer.value = 0xC1; | ||
479 | input.count = 1; | ||
480 | input.pointer = ¶m; | ||
481 | status = acpi_evaluate_integer(handle, "GRDI", &input, &output); | ||
482 | if (ACPI_SUCCESS(status)) | ||
483 | *value = output; | ||
484 | return status; | ||
485 | } | ||
486 | |||
487 | static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle, | ||
488 | unsigned long long value) | ||
489 | { | ||
490 | union acpi_object param[2]; | ||
491 | struct acpi_object_list input; | ||
492 | acpi_status status; | ||
493 | unsigned long long output; | ||
494 | |||
495 | param[0].type = ACPI_TYPE_INTEGER; | ||
496 | param[0].integer.value = 0xC1; | ||
497 | param[1].type = ACPI_TYPE_INTEGER; | ||
498 | param[1].integer.value = value; | ||
499 | input.count = 2; | ||
500 | input.pointer = param; | ||
501 | status = acpi_evaluate_integer(handle, "GWRI", &input, &output); | ||
502 | return status; | ||
503 | } | ||
504 | |||
505 | static void cmpc_rfkill_query(struct rfkill *rfkill, void *data) | ||
506 | { | ||
507 | acpi_status status; | ||
508 | acpi_handle handle; | ||
509 | unsigned long long state; | ||
510 | bool blocked; | ||
511 | |||
512 | handle = data; | ||
513 | status = cmpc_get_rfkill_wlan(handle, &state); | ||
514 | if (ACPI_SUCCESS(status)) { | ||
515 | blocked = state & 1 ? false : true; | ||
516 | rfkill_set_sw_state(rfkill, blocked); | ||
517 | } | ||
518 | } | ||
519 | |||
520 | static int cmpc_rfkill_block(void *data, bool blocked) | ||
521 | { | ||
522 | acpi_status status; | ||
523 | acpi_handle handle; | ||
524 | unsigned long long state; | ||
525 | |||
526 | handle = data; | ||
527 | status = cmpc_get_rfkill_wlan(handle, &state); | ||
528 | if (ACPI_FAILURE(status)) | ||
529 | return -ENODEV; | ||
530 | if (blocked) | ||
531 | state &= ~1; | ||
532 | else | ||
533 | state |= 1; | ||
534 | status = cmpc_set_rfkill_wlan(handle, state); | ||
535 | if (ACPI_FAILURE(status)) | ||
536 | return -ENODEV; | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static const struct rfkill_ops cmpc_rfkill_ops = { | ||
541 | .query = cmpc_rfkill_query, | ||
542 | .set_block = cmpc_rfkill_block, | ||
543 | }; | ||
544 | |||
545 | /* | ||
546 | * Common backlight and rfkill code. | ||
547 | */ | ||
548 | |||
549 | struct ipml200_dev { | ||
467 | struct backlight_device *bd; | 550 | struct backlight_device *bd; |
551 | struct rfkill *rf; | ||
552 | }; | ||
553 | |||
554 | static int cmpc_ipml_add(struct acpi_device *acpi) | ||
555 | { | ||
556 | int retval; | ||
557 | struct ipml200_dev *ipml; | ||
558 | struct backlight_properties props; | ||
559 | |||
560 | ipml = kmalloc(sizeof(*ipml), GFP_KERNEL); | ||
561 | if (ipml == NULL) | ||
562 | return -ENOMEM; | ||
468 | 563 | ||
469 | memset(&props, 0, sizeof(struct backlight_properties)); | 564 | memset(&props, 0, sizeof(struct backlight_properties)); |
470 | props.max_brightness = 7; | 565 | props.max_brightness = 7; |
471 | bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle, | 566 | ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, |
472 | &cmpc_bl_ops, &props); | 567 | acpi->handle, &cmpc_bl_ops, |
473 | if (IS_ERR(bd)) | 568 | &props); |
474 | return PTR_ERR(bd); | 569 | if (IS_ERR(ipml->bd)) { |
475 | dev_set_drvdata(&acpi->dev, bd); | 570 | retval = PTR_ERR(ipml->bd); |
571 | goto out_bd; | ||
572 | } | ||
573 | |||
574 | ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, | ||
575 | &cmpc_rfkill_ops, acpi->handle); | ||
576 | /* rfkill_alloc may fail if RFKILL is disabled. We should still work | ||
577 | * anyway. */ | ||
578 | if (!IS_ERR(ipml->rf)) { | ||
579 | retval = rfkill_register(ipml->rf); | ||
580 | if (retval) { | ||
581 | rfkill_destroy(ipml->rf); | ||
582 | ipml->rf = NULL; | ||
583 | } | ||
584 | } else { | ||
585 | ipml->rf = NULL; | ||
586 | } | ||
587 | |||
588 | dev_set_drvdata(&acpi->dev, ipml); | ||
476 | return 0; | 589 | return 0; |
590 | |||
591 | out_bd: | ||
592 | kfree(ipml); | ||
593 | return retval; | ||
477 | } | 594 | } |
478 | 595 | ||
479 | static int cmpc_bl_remove(struct acpi_device *acpi, int type) | 596 | static int cmpc_ipml_remove(struct acpi_device *acpi, int type) |
480 | { | 597 | { |
481 | struct backlight_device *bd; | 598 | struct ipml200_dev *ipml; |
599 | |||
600 | ipml = dev_get_drvdata(&acpi->dev); | ||
601 | |||
602 | backlight_device_unregister(ipml->bd); | ||
603 | |||
604 | if (ipml->rf) { | ||
605 | rfkill_unregister(ipml->rf); | ||
606 | rfkill_destroy(ipml->rf); | ||
607 | } | ||
608 | |||
609 | kfree(ipml); | ||
482 | 610 | ||
483 | bd = dev_get_drvdata(&acpi->dev); | ||
484 | backlight_device_unregister(bd); | ||
485 | return 0; | 611 | return 0; |
486 | } | 612 | } |
487 | 613 | ||
488 | static const struct acpi_device_id cmpc_bl_device_ids[] = { | 614 | static const struct acpi_device_id cmpc_ipml_device_ids[] = { |
489 | {CMPC_BL_HID, 0}, | 615 | {CMPC_IPML_HID, 0}, |
490 | {"", 0} | 616 | {"", 0} |
491 | }; | 617 | }; |
492 | 618 | ||
493 | static struct acpi_driver cmpc_bl_acpi_driver = { | 619 | static struct acpi_driver cmpc_ipml_acpi_driver = { |
494 | .owner = THIS_MODULE, | 620 | .owner = THIS_MODULE, |
495 | .name = "cmpc", | 621 | .name = "cmpc", |
496 | .class = "cmpc", | 622 | .class = "cmpc", |
497 | .ids = cmpc_bl_device_ids, | 623 | .ids = cmpc_ipml_device_ids, |
498 | .ops = { | 624 | .ops = { |
499 | .add = cmpc_bl_add, | 625 | .add = cmpc_ipml_add, |
500 | .remove = cmpc_bl_remove | 626 | .remove = cmpc_ipml_remove |
501 | } | 627 | } |
502 | }; | 628 | }; |
503 | 629 | ||
@@ -580,7 +706,7 @@ static int cmpc_init(void) | |||
580 | if (r) | 706 | if (r) |
581 | goto failed_keys; | 707 | goto failed_keys; |
582 | 708 | ||
583 | r = acpi_bus_register_driver(&cmpc_bl_acpi_driver); | 709 | r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); |
584 | if (r) | 710 | if (r) |
585 | goto failed_bl; | 711 | goto failed_bl; |
586 | 712 | ||
@@ -598,7 +724,7 @@ failed_accel: | |||
598 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); | 724 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); |
599 | 725 | ||
600 | failed_tablet: | 726 | failed_tablet: |
601 | acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); | 727 | acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); |
602 | 728 | ||
603 | failed_bl: | 729 | failed_bl: |
604 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); | 730 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); |
@@ -611,7 +737,7 @@ static void cmpc_exit(void) | |||
611 | { | 737 | { |
612 | acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); | 738 | acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); |
613 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); | 739 | acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); |
614 | acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); | 740 | acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); |
615 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); | 741 | acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); |
616 | } | 742 | } |
617 | 743 | ||
@@ -621,7 +747,7 @@ module_exit(cmpc_exit); | |||
621 | static const struct acpi_device_id cmpc_device_ids[] = { | 747 | static const struct acpi_device_id cmpc_device_ids[] = { |
622 | {CMPC_ACCEL_HID, 0}, | 748 | {CMPC_ACCEL_HID, 0}, |
623 | {CMPC_TABLET_HID, 0}, | 749 | {CMPC_TABLET_HID, 0}, |
624 | {CMPC_BL_HID, 0}, | 750 | {CMPC_IPML_HID, 0}, |
625 | {CMPC_KEYS_HID, 0}, | 751 | {CMPC_KEYS_HID, 0}, |
626 | {"", 0} | 752 | {"", 0} |
627 | }; | 753 | }; |
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index b227eb469f49..9dc50fbf3d0b 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c | |||
@@ -206,7 +206,7 @@ static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code) | |||
206 | { | 206 | { |
207 | struct backlight_device *bd = eeepc->backlight_device; | 207 | struct backlight_device *bd = eeepc->backlight_device; |
208 | int old = bd->props.brightness; | 208 | int old = bd->props.brightness; |
209 | int new; | 209 | int new = old; |
210 | 210 | ||
211 | if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | 211 | if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) |
212 | new = code - NOTIFY_BRNUP_MIN + 1; | 212 | new = code - NOTIFY_BRNUP_MIN + 1; |
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 47b4fd75aa34..e325aeb37d2e 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c | |||
@@ -1090,10 +1090,9 @@ static int __init fujitsu_init(void) | |||
1090 | if (acpi_disabled) | 1090 | if (acpi_disabled) |
1091 | return -ENODEV; | 1091 | return -ENODEV; |
1092 | 1092 | ||
1093 | fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL); | 1093 | fujitsu = kzalloc(sizeof(struct fujitsu_t), GFP_KERNEL); |
1094 | if (!fujitsu) | 1094 | if (!fujitsu) |
1095 | return -ENOMEM; | 1095 | return -ENOMEM; |
1096 | memset(fujitsu, 0, sizeof(struct fujitsu_t)); | ||
1097 | fujitsu->keycode1 = KEY_PROG1; | 1096 | fujitsu->keycode1 = KEY_PROG1; |
1098 | fujitsu->keycode2 = KEY_PROG2; | 1097 | fujitsu->keycode2 = KEY_PROG2; |
1099 | fujitsu->keycode3 = KEY_PROG3; | 1098 | fujitsu->keycode3 = KEY_PROG3; |
@@ -1150,12 +1149,11 @@ static int __init fujitsu_init(void) | |||
1150 | 1149 | ||
1151 | /* Register hotkey driver */ | 1150 | /* Register hotkey driver */ |
1152 | 1151 | ||
1153 | fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL); | 1152 | fujitsu_hotkey = kzalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL); |
1154 | if (!fujitsu_hotkey) { | 1153 | if (!fujitsu_hotkey) { |
1155 | ret = -ENOMEM; | 1154 | ret = -ENOMEM; |
1156 | goto fail_hotkey; | 1155 | goto fail_hotkey; |
1157 | } | 1156 | } |
1158 | memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t)); | ||
1159 | 1157 | ||
1160 | result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver); | 1158 | result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver); |
1161 | if (result < 0) { | 1159 | if (result < 0) { |
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c new file mode 100644 index 000000000000..576c3ed92435 --- /dev/null +++ b/drivers/platform/x86/intel_scu_ipc.c | |||
@@ -0,0 +1,829 @@ | |||
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 | * SCU runing in ARC processor communicates with other entity running in IA | ||
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 | ||
15 | * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with | ||
16 | * IPC-1 Driver provides an API for power control unit registers (e.g. MSIC) | ||
17 | * along with other APIs. | ||
18 | */ | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/sysdev.h> | ||
23 | #include <linux/pm.h> | ||
24 | #include <linux/pci.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <asm/setup.h> | ||
27 | #include <asm/intel_scu_ipc.h> | ||
28 | |||
29 | /* IPC defines the following message types */ | ||
30 | #define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ | ||
31 | #define IPCMSG_BATTERY 0xEF /* Coulomb Counter Accumulator */ | ||
32 | #define IPCMSG_FW_UPDATE 0xFE /* Firmware update */ | ||
33 | #define IPCMSG_PCNTRL 0xFF /* Power controller unit read/write */ | ||
34 | #define IPCMSG_FW_REVISION 0xF4 /* Get firmware revision */ | ||
35 | |||
36 | /* Command id associated with message IPCMSG_PCNTRL */ | ||
37 | #define IPC_CMD_PCNTRL_W 0 /* Register write */ | ||
38 | #define IPC_CMD_PCNTRL_R 1 /* Register read */ | ||
39 | #define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */ | ||
40 | |||
41 | /* Miscelaneous Command ids */ | ||
42 | #define IPC_CMD_INDIRECT_RD 2 /* 32bit indirect read */ | ||
43 | #define IPC_CMD_INDIRECT_WR 5 /* 32bit indirect write */ | ||
44 | |||
45 | /* | ||
46 | * IPC register summary | ||
47 | * | ||
48 | * IPC register blocks are memory mapped at fixed address of 0xFF11C000 | ||
49 | * To read or write information to the SCU, driver writes to IPC-1 memory | ||
50 | * mapped registers (base address 0xFF11C000). The following is the IPC | ||
51 | * mechanism | ||
52 | * | ||
53 | * 1. IA core cDMI interface claims this transaction and converts it to a | ||
54 | * Transaction Layer Packet (TLP) message which is sent across the cDMI. | ||
55 | * | ||
56 | * 2. South Complex cDMI block receives this message and writes it to | ||
57 | * the IPC-1 register block, causing an interrupt to the SCU | ||
58 | * | ||
59 | * 3. SCU firmware decodes this interrupt and IPC message and the appropriate | ||
60 | * message handler is called within firmware. | ||
61 | */ | ||
62 | |||
63 | #define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */ | ||
64 | #define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */ | ||
65 | #define IPC_WWBUF_SIZE 16 /* IPC Write buffer Size */ | ||
66 | #define IPC_RWBUF_SIZE 16 /* IPC Read buffer Size */ | ||
67 | #define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ | ||
68 | #define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */ | ||
69 | |||
70 | static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id); | ||
71 | static void ipc_remove(struct pci_dev *pdev); | ||
72 | |||
73 | struct intel_scu_ipc_dev { | ||
74 | struct pci_dev *pdev; | ||
75 | void __iomem *ipc_base; | ||
76 | void __iomem *i2c_base; | ||
77 | }; | ||
78 | |||
79 | static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ | ||
80 | |||
81 | static int platform = 1; | ||
82 | module_param(platform, int, 0); | ||
83 | MODULE_PARM_DESC(platform, "1 for moorestown platform"); | ||
84 | |||
85 | |||
86 | |||
87 | |||
88 | /* | ||
89 | * IPC Read Buffer (Read Only): | ||
90 | * 16 byte buffer for receiving data from SCU, if IPC command | ||
91 | * processing results in response data | ||
92 | */ | ||
93 | #define IPC_READ_BUFFER 0x90 | ||
94 | |||
95 | #define IPC_I2C_CNTRL_ADDR 0 | ||
96 | #define I2C_DATA_ADDR 0x04 | ||
97 | |||
98 | static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ | ||
99 | |||
100 | /* | ||
101 | * Command Register (Write Only): | ||
102 | * A write to this register results in an interrupt to the SCU core processor | ||
103 | * Format: | ||
104 | * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)| | ||
105 | */ | ||
106 | static inline void ipc_command(u32 cmd) /* Send ipc command */ | ||
107 | { | ||
108 | writel(cmd, ipcdev.ipc_base); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * IPC Write Buffer (Write Only): | ||
113 | * 16-byte buffer for sending data associated with IPC command to | ||
114 | * SCU. Size of the data is specified in the IPC_COMMAND_REG register | ||
115 | */ | ||
116 | static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */ | ||
117 | { | ||
118 | writel(data, ipcdev.ipc_base + 0x80 + offset); | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * IPC destination Pointer (Write Only): | ||
123 | * Use content as pointer for destination write | ||
124 | */ | ||
125 | static inline void ipc_write_dptr(u32 data) /* Write dptr data */ | ||
126 | { | ||
127 | writel(data, ipcdev.ipc_base + 0x0C); | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * IPC Source Pointer (Write Only): | ||
132 | * Use content as pointer for read location | ||
133 | */ | ||
134 | static inline void ipc_write_sptr(u32 data) /* Write dptr data */ | ||
135 | { | ||
136 | writel(data, ipcdev.ipc_base + 0x08); | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Status Register (Read Only): | ||
141 | * Driver will read this register to get the ready/busy status of the IPC | ||
142 | * block and error status of the IPC command that was just processed by SCU | ||
143 | * Format: | ||
144 | * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)| | ||
145 | */ | ||
146 | |||
147 | static inline u8 ipc_read_status(void) | ||
148 | { | ||
149 | return __raw_readl(ipcdev.ipc_base + 0x04); | ||
150 | } | ||
151 | |||
152 | static inline u8 ipc_data_readb(u32 offset) /* Read ipc byte data */ | ||
153 | { | ||
154 | return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset); | ||
155 | } | ||
156 | |||
157 | static inline u8 ipc_data_readl(u32 offset) /* Read ipc u32 data */ | ||
158 | { | ||
159 | return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); | ||
160 | } | ||
161 | |||
162 | static inline int busy_loop(void) /* Wait till scu status is busy */ | ||
163 | { | ||
164 | u32 status = 0; | ||
165 | u32 loop_count = 0; | ||
166 | |||
167 | status = ipc_read_status(); | ||
168 | while (status & 1) { | ||
169 | udelay(1); /* scu processing time is in few u secods */ | ||
170 | status = ipc_read_status(); | ||
171 | loop_count++; | ||
172 | /* break if scu doesn't reset busy bit after huge retry */ | ||
173 | if (loop_count > 100000) { | ||
174 | dev_err(&ipcdev.pdev->dev, "IPC timed out"); | ||
175 | return -ETIMEDOUT; | ||
176 | } | ||
177 | } | ||
178 | return (status >> 1) & 1; | ||
179 | } | ||
180 | |||
181 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ | ||
182 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) | ||
183 | { | ||
184 | int nc; | ||
185 | u32 offset = 0; | ||
186 | u32 err = 0; | ||
187 | u8 cbuf[IPC_WWBUF_SIZE] = { '\0' }; | ||
188 | u32 *wbuf = (u32 *)&cbuf; | ||
189 | |||
190 | mutex_lock(&ipclock); | ||
191 | if (ipcdev.pdev == NULL) { | ||
192 | mutex_unlock(&ipclock); | ||
193 | return -ENODEV; | ||
194 | } | ||
195 | |||
196 | if (platform == 1) { | ||
197 | /* Entry is 4 bytes for read/write, 5 bytes for read modify */ | ||
198 | for (nc = 0; nc < count; nc++) { | ||
199 | cbuf[offset] = addr[nc]; | ||
200 | cbuf[offset + 1] = addr[nc] >> 8; | ||
201 | if (id != IPC_CMD_PCNTRL_R) | ||
202 | cbuf[offset + 2] = data[nc]; | ||
203 | if (id == IPC_CMD_PCNTRL_M) { | ||
204 | cbuf[offset + 3] = data[nc + 1]; | ||
205 | offset += 1; | ||
206 | } | ||
207 | offset += 3; | ||
208 | } | ||
209 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) | ||
210 | ipc_data_writel(wbuf[nc], offset); /* Write wbuff */ | ||
211 | |||
212 | } else { | ||
213 | for (nc = 0, offset = 0; nc < count; nc++, offset += 2) | ||
214 | ipc_data_writel(addr[nc], offset); /* Write addresses */ | ||
215 | if (id != IPC_CMD_PCNTRL_R) { | ||
216 | for (nc = 0; nc < count; nc++, offset++) | ||
217 | ipc_data_writel(data[nc], offset); /* Write data */ | ||
218 | if (id == IPC_CMD_PCNTRL_M) | ||
219 | ipc_data_writel(data[nc + 1], offset); /* Mask value*/ | ||
220 | } | ||
221 | } | ||
222 | |||
223 | if (id != IPC_CMD_PCNTRL_M) | ||
224 | ipc_command((count * 3) << 16 | id << 12 | 0 << 8 | op); | ||
225 | else | ||
226 | ipc_command((count * 4) << 16 | id << 12 | 0 << 8 | op); | ||
227 | |||
228 | err = busy_loop(); | ||
229 | |||
230 | if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ | ||
231 | /* Workaround: values are read as 0 without memcpy_fromio */ | ||
232 | memcpy_fromio(cbuf, ipcdev.ipc_base + IPC_READ_BUFFER, 16); | ||
233 | if (platform == 1) { | ||
234 | for (nc = 0, offset = 2; nc < count; nc++, offset += 3) | ||
235 | data[nc] = ipc_data_readb(offset); | ||
236 | } else { | ||
237 | for (nc = 0; nc < count; nc++) | ||
238 | data[nc] = ipc_data_readb(nc); | ||
239 | } | ||
240 | } | ||
241 | mutex_unlock(&ipclock); | ||
242 | return err; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * intel_scu_ipc_ioread8 - read a word via the SCU | ||
247 | * @addr: register on SCU | ||
248 | * @data: return pointer for read byte | ||
249 | * | ||
250 | * Read a single register. Returns 0 on success or an error code. All | ||
251 | * locking between SCU accesses is handled for the caller. | ||
252 | * | ||
253 | * This function may sleep. | ||
254 | */ | ||
255 | int intel_scu_ipc_ioread8(u16 addr, u8 *data) | ||
256 | { | ||
257 | return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); | ||
258 | } | ||
259 | EXPORT_SYMBOL(intel_scu_ipc_ioread8); | ||
260 | |||
261 | /** | ||
262 | * intel_scu_ipc_ioread16 - read a word via the SCU | ||
263 | * @addr: register on SCU | ||
264 | * @data: return pointer for read word | ||
265 | * | ||
266 | * Read a register pair. Returns 0 on success or an error code. All | ||
267 | * locking between SCU accesses is handled for the caller. | ||
268 | * | ||
269 | * This function may sleep. | ||
270 | */ | ||
271 | int intel_scu_ipc_ioread16(u16 addr, u16 *data) | ||
272 | { | ||
273 | u16 x[2] = {addr, addr + 1 }; | ||
274 | return pwr_reg_rdwr(x, (u8 *)data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); | ||
275 | } | ||
276 | EXPORT_SYMBOL(intel_scu_ipc_ioread16); | ||
277 | |||
278 | /** | ||
279 | * intel_scu_ipc_ioread32 - read a dword via the SCU | ||
280 | * @addr: register on SCU | ||
281 | * @data: return pointer for read dword | ||
282 | * | ||
283 | * Read four registers. Returns 0 on success or an error code. All | ||
284 | * locking between SCU accesses is handled for the caller. | ||
285 | * | ||
286 | * This function may sleep. | ||
287 | */ | ||
288 | int intel_scu_ipc_ioread32(u16 addr, u32 *data) | ||
289 | { | ||
290 | u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; | ||
291 | return pwr_reg_rdwr(x, (u8 *)data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); | ||
292 | } | ||
293 | EXPORT_SYMBOL(intel_scu_ipc_ioread32); | ||
294 | |||
295 | /** | ||
296 | * intel_scu_ipc_iowrite8 - write a byte via the SCU | ||
297 | * @addr: register on SCU | ||
298 | * @data: byte to write | ||
299 | * | ||
300 | * Write a single register. Returns 0 on success or an error code. All | ||
301 | * locking between SCU accesses is handled for the caller. | ||
302 | * | ||
303 | * This function may sleep. | ||
304 | */ | ||
305 | int intel_scu_ipc_iowrite8(u16 addr, u8 data) | ||
306 | { | ||
307 | return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); | ||
308 | } | ||
309 | EXPORT_SYMBOL(intel_scu_ipc_iowrite8); | ||
310 | |||
311 | /** | ||
312 | * intel_scu_ipc_iowrite16 - write a word via the SCU | ||
313 | * @addr: register on SCU | ||
314 | * @data: word to write | ||
315 | * | ||
316 | * Write two registers. Returns 0 on success or an error code. All | ||
317 | * locking between SCU accesses is handled for the caller. | ||
318 | * | ||
319 | * This function may sleep. | ||
320 | */ | ||
321 | int intel_scu_ipc_iowrite16(u16 addr, u16 data) | ||
322 | { | ||
323 | u16 x[2] = {addr, addr + 1 }; | ||
324 | return pwr_reg_rdwr(x, (u8 *)&data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); | ||
325 | } | ||
326 | EXPORT_SYMBOL(intel_scu_ipc_iowrite16); | ||
327 | |||
328 | /** | ||
329 | * intel_scu_ipc_iowrite32 - write a dword via the SCU | ||
330 | * @addr: register on SCU | ||
331 | * @data: dword to write | ||
332 | * | ||
333 | * Write four registers. Returns 0 on success or an error code. All | ||
334 | * locking between SCU accesses is handled for the caller. | ||
335 | * | ||
336 | * This function may sleep. | ||
337 | */ | ||
338 | int intel_scu_ipc_iowrite32(u16 addr, u32 data) | ||
339 | { | ||
340 | u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; | ||
341 | return pwr_reg_rdwr(x, (u8 *)&data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); | ||
342 | } | ||
343 | EXPORT_SYMBOL(intel_scu_ipc_iowrite32); | ||
344 | |||
345 | /** | ||
346 | * intel_scu_ipc_readvv - read a set of registers | ||
347 | * @addr: register list | ||
348 | * @data: bytes to return | ||
349 | * @len: length of array | ||
350 | * | ||
351 | * Read registers. Returns 0 on success or an error code. All | ||
352 | * locking between SCU accesses is handled for the caller. | ||
353 | * | ||
354 | * The largest array length permitted by the hardware is 5 items. | ||
355 | * | ||
356 | * This function may sleep. | ||
357 | */ | ||
358 | int intel_scu_ipc_readv(u16 *addr, u8 *data, int len) | ||
359 | { | ||
360 | return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); | ||
361 | } | ||
362 | EXPORT_SYMBOL(intel_scu_ipc_readv); | ||
363 | |||
364 | /** | ||
365 | * intel_scu_ipc_writev - write a set of registers | ||
366 | * @addr: register list | ||
367 | * @data: bytes to write | ||
368 | * @len: length of array | ||
369 | * | ||
370 | * Write registers. Returns 0 on success or an error code. All | ||
371 | * locking between SCU accesses is handled for the caller. | ||
372 | * | ||
373 | * The largest array length permitted by the hardware is 5 items. | ||
374 | * | ||
375 | * This function may sleep. | ||
376 | * | ||
377 | */ | ||
378 | int intel_scu_ipc_writev(u16 *addr, u8 *data, int len) | ||
379 | { | ||
380 | return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); | ||
381 | } | ||
382 | EXPORT_SYMBOL(intel_scu_ipc_writev); | ||
383 | |||
384 | |||
385 | /** | ||
386 | * intel_scu_ipc_update_register - r/m/w a register | ||
387 | * @addr: register address | ||
388 | * @bits: bits to update | ||
389 | * @mask: mask of bits to update | ||
390 | * | ||
391 | * Read-modify-write power control unit register. The first data argument | ||
392 | * must be register value and second is mask value | ||
393 | * mask is a bitmap that indicates which bits to update. | ||
394 | * 0 = masked. Don't modify this bit, 1 = modify this bit. | ||
395 | * returns 0 on success or an error code. | ||
396 | * | ||
397 | * This function may sleep. Locking between SCU accesses is handled | ||
398 | * for the caller. | ||
399 | */ | ||
400 | int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask) | ||
401 | { | ||
402 | u8 data[2] = { bits, mask }; | ||
403 | return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); | ||
404 | } | ||
405 | EXPORT_SYMBOL(intel_scu_ipc_update_register); | ||
406 | |||
407 | /** | ||
408 | * intel_scu_ipc_register_read - 32bit indirect read | ||
409 | * @addr: register address | ||
410 | * @value: 32bit value return | ||
411 | * | ||
412 | * Performs IA 32 bit indirect read, returns 0 on success, or an | ||
413 | * error code. | ||
414 | * | ||
415 | * Can be used when SCCB(System Controller Configuration Block) register | ||
416 | * HRIM(Honor Restricted IPC Messages) is set (bit 23) | ||
417 | * | ||
418 | * This function may sleep. Locking for SCU accesses is handled for | ||
419 | * the caller. | ||
420 | */ | ||
421 | int intel_scu_ipc_register_read(u32 addr, u32 *value) | ||
422 | { | ||
423 | u32 err = 0; | ||
424 | |||
425 | mutex_lock(&ipclock); | ||
426 | if (ipcdev.pdev == NULL) { | ||
427 | mutex_unlock(&ipclock); | ||
428 | return -ENODEV; | ||
429 | } | ||
430 | ipc_write_sptr(addr); | ||
431 | ipc_command(4 << 16 | IPC_CMD_INDIRECT_RD); | ||
432 | err = busy_loop(); | ||
433 | *value = ipc_data_readl(0); | ||
434 | mutex_unlock(&ipclock); | ||
435 | return err; | ||
436 | } | ||
437 | EXPORT_SYMBOL(intel_scu_ipc_register_read); | ||
438 | |||
439 | /** | ||
440 | * intel_scu_ipc_register_write - 32bit indirect write | ||
441 | * @addr: register address | ||
442 | * @value: 32bit value to write | ||
443 | * | ||
444 | * Performs IA 32 bit indirect write, returns 0 on success, or an | ||
445 | * error code. | ||
446 | * | ||
447 | * Can be used when SCCB(System Controller Configuration Block) register | ||
448 | * HRIM(Honor Restricted IPC Messages) is set (bit 23) | ||
449 | * | ||
450 | * This function may sleep. Locking for SCU accesses is handled for | ||
451 | * the caller. | ||
452 | */ | ||
453 | int intel_scu_ipc_register_write(u32 addr, u32 value) | ||
454 | { | ||
455 | u32 err = 0; | ||
456 | |||
457 | mutex_lock(&ipclock); | ||
458 | if (ipcdev.pdev == NULL) { | ||
459 | mutex_unlock(&ipclock); | ||
460 | return -ENODEV; | ||
461 | } | ||
462 | ipc_write_dptr(addr); | ||
463 | ipc_data_writel(value, 0); | ||
464 | ipc_command(4 << 16 | IPC_CMD_INDIRECT_WR); | ||
465 | err = busy_loop(); | ||
466 | mutex_unlock(&ipclock); | ||
467 | return err; | ||
468 | } | ||
469 | EXPORT_SYMBOL(intel_scu_ipc_register_write); | ||
470 | |||
471 | /** | ||
472 | * intel_scu_ipc_simple_command - send a simple command | ||
473 | * @cmd: command | ||
474 | * @sub: sub type | ||
475 | * | ||
476 | * Issue a simple command to the SCU. Do not use this interface if | ||
477 | * you must then access data as any data values may be overwritten | ||
478 | * by another SCU access by the time this function returns. | ||
479 | * | ||
480 | * This function may sleep. Locking for SCU accesses is handled for | ||
481 | * the caller. | ||
482 | */ | ||
483 | int intel_scu_ipc_simple_command(int cmd, int sub) | ||
484 | { | ||
485 | u32 err = 0; | ||
486 | |||
487 | mutex_lock(&ipclock); | ||
488 | if (ipcdev.pdev == NULL) { | ||
489 | mutex_unlock(&ipclock); | ||
490 | return -ENODEV; | ||
491 | } | ||
492 | ipc_command(cmd << 12 | sub); | ||
493 | err = busy_loop(); | ||
494 | mutex_unlock(&ipclock); | ||
495 | return err; | ||
496 | } | ||
497 | EXPORT_SYMBOL(intel_scu_ipc_simple_command); | ||
498 | |||
499 | /** | ||
500 | * intel_scu_ipc_command - command with data | ||
501 | * @cmd: command | ||
502 | * @sub: sub type | ||
503 | * @in: input data | ||
504 | * @inlen: input length | ||
505 | * @out: output data | ||
506 | * @outlein: output length | ||
507 | * | ||
508 | * Issue a command to the SCU which involves data transfers. Do the | ||
509 | * data copies under the lock but leave it for the caller to interpret | ||
510 | */ | ||
511 | |||
512 | int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, | ||
513 | u32 *out, int outlen) | ||
514 | { | ||
515 | u32 err = 0; | ||
516 | int i = 0; | ||
517 | |||
518 | mutex_lock(&ipclock); | ||
519 | if (ipcdev.pdev == NULL) { | ||
520 | mutex_unlock(&ipclock); | ||
521 | return -ENODEV; | ||
522 | } | ||
523 | |||
524 | for (i = 0; i < inlen; i++) | ||
525 | ipc_data_writel(*in++, 4 * i); | ||
526 | |||
527 | ipc_command(cmd << 12 | sub); | ||
528 | err = busy_loop(); | ||
529 | |||
530 | for (i = 0; i < outlen; i++) | ||
531 | *out++ = ipc_data_readl(4 * i); | ||
532 | |||
533 | mutex_unlock(&ipclock); | ||
534 | return err; | ||
535 | } | ||
536 | EXPORT_SYMBOL(intel_scu_ipc_command); | ||
537 | |||
538 | /*I2C commands */ | ||
539 | #define IPC_I2C_WRITE 1 /* I2C Write command */ | ||
540 | #define IPC_I2C_READ 2 /* I2C Read command */ | ||
541 | |||
542 | /** | ||
543 | * intel_scu_ipc_i2c_cntrl - I2C read/write operations | ||
544 | * @addr: I2C address + command bits | ||
545 | * @data: data to read/write | ||
546 | * | ||
547 | * Perform an an I2C read/write operation via the SCU. All locking is | ||
548 | * handled for the caller. This function may sleep. | ||
549 | * | ||
550 | * Returns an error code or 0 on success. | ||
551 | * | ||
552 | * This has to be in the IPC driver for the locking. | ||
553 | */ | ||
554 | int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) | ||
555 | { | ||
556 | u32 cmd = 0; | ||
557 | |||
558 | mutex_lock(&ipclock); | ||
559 | cmd = (addr >> 24) & 0xFF; | ||
560 | if (cmd == IPC_I2C_READ) { | ||
561 | writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); | ||
562 | /* Write not getting updated without delay */ | ||
563 | mdelay(1); | ||
564 | *data = readl(ipcdev.i2c_base + I2C_DATA_ADDR); | ||
565 | } else if (cmd == IPC_I2C_WRITE) { | ||
566 | writel(addr, ipcdev.i2c_base + I2C_DATA_ADDR); | ||
567 | mdelay(1); | ||
568 | writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); | ||
569 | } else { | ||
570 | dev_err(&ipcdev.pdev->dev, | ||
571 | "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); | ||
572 | |||
573 | mutex_unlock(&ipclock); | ||
574 | return -1; | ||
575 | } | ||
576 | mutex_unlock(&ipclock); | ||
577 | return 0; | ||
578 | } | ||
579 | EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); | ||
580 | |||
581 | #define IPC_FW_LOAD_ADDR 0xFFFC0000 /* Storage location for FW image */ | ||
582 | #define IPC_FW_UPDATE_MBOX_ADDR 0xFFFFDFF4 /* Mailbox between ipc and scu */ | ||
583 | #define IPC_MAX_FW_SIZE 262144 /* 256K storage size for loading the FW image */ | ||
584 | #define IPC_FW_MIP_HEADER_SIZE 2048 /* Firmware MIP header size */ | ||
585 | /* IPC inform SCU to get ready for update process */ | ||
586 | #define IPC_CMD_FW_UPDATE_READY 0x10FE | ||
587 | /* IPC inform SCU to go for update process */ | ||
588 | #define IPC_CMD_FW_UPDATE_GO 0x20FE | ||
589 | /* Status code for fw update */ | ||
590 | #define IPC_FW_UPDATE_SUCCESS 0x444f4e45 /* Status code 'DONE' */ | ||
591 | #define IPC_FW_UPDATE_BADN 0x4241444E /* Status code 'BADN' */ | ||
592 | #define IPC_FW_TXHIGH 0x54784849 /* Status code 'IPC_FW_TXHIGH' */ | ||
593 | #define IPC_FW_TXLOW 0x54784c4f /* Status code 'IPC_FW_TXLOW' */ | ||
594 | |||
595 | struct fw_update_mailbox { | ||
596 | u32 status; | ||
597 | u32 scu_flag; | ||
598 | u32 driver_flag; | ||
599 | }; | ||
600 | |||
601 | |||
602 | /** | ||
603 | * intel_scu_ipc_fw_update - Firmware update utility | ||
604 | * @buffer: firmware buffer | ||
605 | * @length: size of firmware buffer | ||
606 | * | ||
607 | * This function provides an interface to load the firmware into | ||
608 | * the SCU. Returns 0 on success or -1 on failure | ||
609 | */ | ||
610 | int intel_scu_ipc_fw_update(u8 *buffer, u32 length) | ||
611 | { | ||
612 | void __iomem *fw_update_base; | ||
613 | struct fw_update_mailbox __iomem *mailbox = NULL; | ||
614 | int retry_cnt = 0; | ||
615 | u32 status; | ||
616 | |||
617 | mutex_lock(&ipclock); | ||
618 | fw_update_base = ioremap_nocache(IPC_FW_LOAD_ADDR, (128*1024)); | ||
619 | if (fw_update_base == NULL) { | ||
620 | mutex_unlock(&ipclock); | ||
621 | return -ENOMEM; | ||
622 | } | ||
623 | mailbox = ioremap_nocache(IPC_FW_UPDATE_MBOX_ADDR, | ||
624 | sizeof(struct fw_update_mailbox)); | ||
625 | if (mailbox == NULL) { | ||
626 | iounmap(fw_update_base); | ||
627 | mutex_unlock(&ipclock); | ||
628 | return -ENOMEM; | ||
629 | } | ||
630 | |||
631 | ipc_command(IPC_CMD_FW_UPDATE_READY); | ||
632 | |||
633 | /* Intitialize mailbox */ | ||
634 | writel(0, &mailbox->status); | ||
635 | writel(0, &mailbox->scu_flag); | ||
636 | writel(0, &mailbox->driver_flag); | ||
637 | |||
638 | /* Driver copies the 2KB MIP header to SRAM at 0xFFFC0000*/ | ||
639 | memcpy_toio(fw_update_base, buffer, 0x800); | ||
640 | |||
641 | /* Driver sends "FW Update" IPC command (CMD_ID 0xFE; MSG_ID 0x02). | ||
642 | * Upon receiving this command, SCU will write the 2K MIP header | ||
643 | * from 0xFFFC0000 into NAND. | ||
644 | * SCU will write a status code into the Mailbox, and then set scu_flag. | ||
645 | */ | ||
646 | |||
647 | ipc_command(IPC_CMD_FW_UPDATE_GO); | ||
648 | |||
649 | /*Driver stalls until scu_flag is set */ | ||
650 | while (readl(&mailbox->scu_flag) != 1) { | ||
651 | rmb(); | ||
652 | mdelay(1); | ||
653 | } | ||
654 | |||
655 | /* Driver checks Mailbox status. | ||
656 | * If the status is 'BADN', then abort (bad NAND). | ||
657 | * If the status is 'IPC_FW_TXLOW', then continue. | ||
658 | */ | ||
659 | while (readl(&mailbox->status) != IPC_FW_TXLOW) { | ||
660 | rmb(); | ||
661 | mdelay(10); | ||
662 | } | ||
663 | mdelay(10); | ||
664 | |||
665 | update_retry: | ||
666 | if (retry_cnt > 5) | ||
667 | goto update_end; | ||
668 | |||
669 | if (readl(&mailbox->status) != IPC_FW_TXLOW) | ||
670 | goto update_end; | ||
671 | buffer = buffer + 0x800; | ||
672 | memcpy_toio(fw_update_base, buffer, 0x20000); | ||
673 | writel(1, &mailbox->driver_flag); | ||
674 | while (readl(&mailbox->scu_flag) == 1) { | ||
675 | rmb(); | ||
676 | mdelay(1); | ||
677 | } | ||
678 | |||
679 | /* check for 'BADN' */ | ||
680 | if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) | ||
681 | goto update_end; | ||
682 | |||
683 | while (readl(&mailbox->status) != IPC_FW_TXHIGH) { | ||
684 | rmb(); | ||
685 | mdelay(10); | ||
686 | } | ||
687 | mdelay(10); | ||
688 | |||
689 | if (readl(&mailbox->status) != IPC_FW_TXHIGH) | ||
690 | goto update_end; | ||
691 | |||
692 | buffer = buffer + 0x20000; | ||
693 | memcpy_toio(fw_update_base, buffer, 0x20000); | ||
694 | writel(0, &mailbox->driver_flag); | ||
695 | |||
696 | while (mailbox->scu_flag == 0) { | ||
697 | rmb(); | ||
698 | mdelay(1); | ||
699 | } | ||
700 | |||
701 | /* check for 'BADN' */ | ||
702 | if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) | ||
703 | goto update_end; | ||
704 | |||
705 | if (readl(&mailbox->status) == IPC_FW_TXLOW) { | ||
706 | ++retry_cnt; | ||
707 | goto update_retry; | ||
708 | } | ||
709 | |||
710 | update_end: | ||
711 | status = readl(&mailbox->status); | ||
712 | |||
713 | iounmap(fw_update_base); | ||
714 | iounmap(mailbox); | ||
715 | mutex_unlock(&ipclock); | ||
716 | |||
717 | if (status == IPC_FW_UPDATE_SUCCESS) | ||
718 | return 0; | ||
719 | return -1; | ||
720 | } | ||
721 | EXPORT_SYMBOL(intel_scu_ipc_fw_update); | ||
722 | |||
723 | /* | ||
724 | * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 | ||
725 | * When ioc bit is set to 1, caller api must wait for interrupt handler called | ||
726 | * which in turn unlocks the caller api. Currently this is not used | ||
727 | * | ||
728 | * This is edge triggered so we need take no action to clear anything | ||
729 | */ | ||
730 | static irqreturn_t ioc(int irq, void *dev_id) | ||
731 | { | ||
732 | return IRQ_HANDLED; | ||
733 | } | ||
734 | |||
735 | /** | ||
736 | * ipc_probe - probe an Intel SCU IPC | ||
737 | * @dev: the PCI device matching | ||
738 | * @id: entry in the match table | ||
739 | * | ||
740 | * Enable and install an intel SCU IPC. This appears in the PCI space | ||
741 | * but uses some hard coded addresses as well. | ||
742 | */ | ||
743 | static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) | ||
744 | { | ||
745 | int err; | ||
746 | resource_size_t pci_resource; | ||
747 | |||
748 | if (ipcdev.pdev) /* We support only one SCU */ | ||
749 | return -EBUSY; | ||
750 | |||
751 | ipcdev.pdev = pci_dev_get(dev); | ||
752 | |||
753 | err = pci_enable_device(dev); | ||
754 | if (err) | ||
755 | return err; | ||
756 | |||
757 | err = pci_request_regions(dev, "intel_scu_ipc"); | ||
758 | if (err) | ||
759 | return err; | ||
760 | |||
761 | pci_resource = pci_resource_start(dev, 0); | ||
762 | if (!pci_resource) | ||
763 | return -ENOMEM; | ||
764 | |||
765 | if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) | ||
766 | return -EBUSY; | ||
767 | |||
768 | ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR); | ||
769 | if (!ipcdev.ipc_base) | ||
770 | return -ENOMEM; | ||
771 | |||
772 | ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR); | ||
773 | if (!ipcdev.i2c_base) { | ||
774 | iounmap(ipcdev.ipc_base); | ||
775 | return -ENOMEM; | ||
776 | } | ||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | /** | ||
781 | * ipc_remove - remove a bound IPC device | ||
782 | * @pdev: PCI device | ||
783 | * | ||
784 | * In practice the SCU is not removable but this function is also | ||
785 | * called for each device on a module unload or cleanup which is the | ||
786 | * path that will get used. | ||
787 | * | ||
788 | * Free up the mappings and release the PCI resources | ||
789 | */ | ||
790 | static void ipc_remove(struct pci_dev *pdev) | ||
791 | { | ||
792 | free_irq(pdev->irq, &ipcdev); | ||
793 | pci_release_regions(pdev); | ||
794 | pci_dev_put(ipcdev.pdev); | ||
795 | iounmap(ipcdev.ipc_base); | ||
796 | iounmap(ipcdev.i2c_base); | ||
797 | ipcdev.pdev = NULL; | ||
798 | } | ||
799 | |||
800 | static const struct pci_device_id pci_ids[] = { | ||
801 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)}, | ||
802 | { 0,} | ||
803 | }; | ||
804 | MODULE_DEVICE_TABLE(pci, pci_ids); | ||
805 | |||
806 | static struct pci_driver ipc_driver = { | ||
807 | .name = "intel_scu_ipc", | ||
808 | .id_table = pci_ids, | ||
809 | .probe = ipc_probe, | ||
810 | .remove = ipc_remove, | ||
811 | }; | ||
812 | |||
813 | |||
814 | static int __init intel_scu_ipc_init(void) | ||
815 | { | ||
816 | return pci_register_driver(&ipc_driver); | ||
817 | } | ||
818 | |||
819 | static void __exit intel_scu_ipc_exit(void) | ||
820 | { | ||
821 | pci_unregister_driver(&ipc_driver); | ||
822 | } | ||
823 | |||
824 | MODULE_AUTHOR("Sreedhara DS <sreedhara.ds@intel.com>"); | ||
825 | MODULE_DESCRIPTION("Intel SCU IPC driver"); | ||
826 | MODULE_LICENSE("GPL"); | ||
827 | |||
828 | module_init(intel_scu_ipc_init); | ||
829 | module_exit(intel_scu_ipc_exit); | ||
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 996223a7c009..afd762b58ad9 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c | |||
@@ -59,6 +59,7 @@ | |||
59 | #include <linux/backlight.h> | 59 | #include <linux/backlight.h> |
60 | #include <linux/platform_device.h> | 60 | #include <linux/platform_device.h> |
61 | #include <linux/rfkill.h> | 61 | #include <linux/rfkill.h> |
62 | #include <linux/i8042.h> | ||
62 | 63 | ||
63 | #define MSI_DRIVER_VERSION "0.5" | 64 | #define MSI_DRIVER_VERSION "0.5" |
64 | 65 | ||
@@ -118,7 +119,8 @@ static int set_lcd_level(int level) | |||
118 | buf[0] = 0x80; | 119 | buf[0] = 0x80; |
119 | buf[1] = (u8) (level*31); | 120 | buf[1] = (u8) (level*31); |
120 | 121 | ||
121 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1); | 122 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), |
123 | NULL, 0, 1); | ||
122 | } | 124 | } |
123 | 125 | ||
124 | static int get_lcd_level(void) | 126 | static int get_lcd_level(void) |
@@ -126,7 +128,8 @@ static int get_lcd_level(void) | |||
126 | u8 wdata = 0, rdata; | 128 | u8 wdata = 0, rdata; |
127 | int result; | 129 | int result; |
128 | 130 | ||
129 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); | 131 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, |
132 | &rdata, 1, 1); | ||
130 | if (result < 0) | 133 | if (result < 0) |
131 | return result; | 134 | return result; |
132 | 135 | ||
@@ -138,7 +141,8 @@ static int get_auto_brightness(void) | |||
138 | u8 wdata = 4, rdata; | 141 | u8 wdata = 4, rdata; |
139 | int result; | 142 | int result; |
140 | 143 | ||
141 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); | 144 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, |
145 | &rdata, 1, 1); | ||
142 | if (result < 0) | 146 | if (result < 0) |
143 | return result; | 147 | return result; |
144 | 148 | ||
@@ -152,14 +156,16 @@ static int set_auto_brightness(int enable) | |||
152 | 156 | ||
153 | wdata[0] = 4; | 157 | wdata[0] = 4; |
154 | 158 | ||
155 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1); | 159 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, |
160 | &rdata, 1, 1); | ||
156 | if (result < 0) | 161 | if (result < 0) |
157 | return result; | 162 | return result; |
158 | 163 | ||
159 | wdata[0] = 0x84; | 164 | wdata[0] = 0x84; |
160 | wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); | 165 | wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); |
161 | 166 | ||
162 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); | 167 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, |
168 | NULL, 0, 1); | ||
163 | } | 169 | } |
164 | 170 | ||
165 | static ssize_t set_device_state(const char *buf, size_t count, u8 mask) | 171 | static ssize_t set_device_state(const char *buf, size_t count, u8 mask) |
@@ -254,7 +260,7 @@ static int bl_update_status(struct backlight_device *b) | |||
254 | return set_lcd_level(b->props.brightness); | 260 | return set_lcd_level(b->props.brightness); |
255 | } | 261 | } |
256 | 262 | ||
257 | static struct backlight_ops msibl_ops = { | 263 | static const struct backlight_ops msibl_ops = { |
258 | .get_brightness = bl_get_brightness, | 264 | .get_brightness = bl_get_brightness, |
259 | .update_status = bl_update_status, | 265 | .update_status = bl_update_status, |
260 | }; | 266 | }; |
@@ -353,7 +359,8 @@ static ssize_t store_lcd_level(struct device *dev, | |||
353 | 359 | ||
354 | int level, ret; | 360 | int level, ret; |
355 | 361 | ||
356 | if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX)) | 362 | if (sscanf(buf, "%i", &level) != 1 || |
363 | (level < 0 || level >= MSI_LCD_LEVEL_MAX)) | ||
357 | return -EINVAL; | 364 | return -EINVAL; |
358 | 365 | ||
359 | ret = set_lcd_level(level); | 366 | ret = set_lcd_level(level); |
@@ -393,7 +400,8 @@ static ssize_t store_auto_brightness(struct device *dev, | |||
393 | } | 400 | } |
394 | 401 | ||
395 | static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); | 402 | static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); |
396 | static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness); | 403 | static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, |
404 | store_auto_brightness); | ||
397 | static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); | 405 | static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); |
398 | static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); | 406 | static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); |
399 | static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); | 407 | static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); |
@@ -424,8 +432,9 @@ static struct platform_device *msipf_device; | |||
424 | 432 | ||
425 | static int dmi_check_cb(const struct dmi_system_id *id) | 433 | static int dmi_check_cb(const struct dmi_system_id *id) |
426 | { | 434 | { |
427 | printk("msi-laptop: Identified laptop model '%s'.\n", id->ident); | 435 | printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n", |
428 | return 0; | 436 | id->ident); |
437 | return 0; | ||
429 | } | 438 | } |
430 | 439 | ||
431 | static struct dmi_system_id __initdata msi_dmi_table[] = { | 440 | static struct dmi_system_id __initdata msi_dmi_table[] = { |
@@ -435,7 +444,8 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { | |||
435 | DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"), | 444 | DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"), |
436 | DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), | 445 | DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), |
437 | DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), | 446 | DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), |
438 | DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD") | 447 | DMI_MATCH(DMI_CHASSIS_VENDOR, |
448 | "MICRO-STAR INT'L CO.,LTD") | ||
439 | }, | 449 | }, |
440 | .callback = dmi_check_cb | 450 | .callback = dmi_check_cb |
441 | }, | 451 | }, |
@@ -465,7 +475,8 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { | |||
465 | DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), | 475 | DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), |
466 | DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), | 476 | DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), |
467 | DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), | 477 | DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), |
468 | DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD") | 478 | DMI_MATCH(DMI_CHASSIS_VENDOR, |
479 | "MICRO-STAR INT'L CO.,LTD") | ||
469 | }, | 480 | }, |
470 | .callback = dmi_check_cb | 481 | .callback = dmi_check_cb |
471 | }, | 482 | }, |
@@ -484,6 +495,35 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = { | |||
484 | }, | 495 | }, |
485 | .callback = dmi_check_cb | 496 | .callback = dmi_check_cb |
486 | }, | 497 | }, |
498 | { | ||
499 | .ident = "MSI N051", | ||
500 | .matches = { | ||
501 | DMI_MATCH(DMI_SYS_VENDOR, | ||
502 | "MICRO-STAR INTERNATIONAL CO., LTD"), | ||
503 | DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"), | ||
504 | DMI_MATCH(DMI_CHASSIS_VENDOR, | ||
505 | "MICRO-STAR INTERNATIONAL CO., LTD") | ||
506 | }, | ||
507 | .callback = dmi_check_cb | ||
508 | }, | ||
509 | { | ||
510 | .ident = "MSI N014", | ||
511 | .matches = { | ||
512 | DMI_MATCH(DMI_SYS_VENDOR, | ||
513 | "MICRO-STAR INTERNATIONAL CO., LTD"), | ||
514 | DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"), | ||
515 | }, | ||
516 | .callback = dmi_check_cb | ||
517 | }, | ||
518 | { | ||
519 | .ident = "MSI CR620", | ||
520 | .matches = { | ||
521 | DMI_MATCH(DMI_SYS_VENDOR, | ||
522 | "Micro-Star International"), | ||
523 | DMI_MATCH(DMI_PRODUCT_NAME, "CR620"), | ||
524 | }, | ||
525 | .callback = dmi_check_cb | ||
526 | }, | ||
487 | { } | 527 | { } |
488 | }; | 528 | }; |
489 | 529 | ||
@@ -552,11 +592,71 @@ static void rfkill_cleanup(void) | |||
552 | } | 592 | } |
553 | } | 593 | } |
554 | 594 | ||
595 | static void msi_update_rfkill(struct work_struct *ignored) | ||
596 | { | ||
597 | get_wireless_state_ec_standard(); | ||
598 | |||
599 | if (rfk_wlan) | ||
600 | rfkill_set_sw_state(rfk_wlan, !wlan_s); | ||
601 | if (rfk_bluetooth) | ||
602 | rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); | ||
603 | if (rfk_threeg) | ||
604 | rfkill_set_sw_state(rfk_threeg, !threeg_s); | ||
605 | } | ||
606 | static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); | ||
607 | |||
608 | static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, | ||
609 | struct serio *port) | ||
610 | { | ||
611 | static bool extended; | ||
612 | |||
613 | if (str & 0x20) | ||
614 | return false; | ||
615 | |||
616 | /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/ | ||
617 | if (unlikely(data == 0xe0)) { | ||
618 | extended = true; | ||
619 | return false; | ||
620 | } else if (unlikely(extended)) { | ||
621 | switch (data) { | ||
622 | case 0x54: | ||
623 | case 0x62: | ||
624 | case 0x76: | ||
625 | schedule_delayed_work(&msi_rfkill_work, | ||
626 | round_jiffies_relative(0.5 * HZ)); | ||
627 | break; | ||
628 | } | ||
629 | extended = false; | ||
630 | } | ||
631 | |||
632 | return false; | ||
633 | } | ||
634 | |||
635 | static void msi_init_rfkill(struct work_struct *ignored) | ||
636 | { | ||
637 | if (rfk_wlan) { | ||
638 | rfkill_set_sw_state(rfk_wlan, !wlan_s); | ||
639 | rfkill_wlan_set(NULL, !wlan_s); | ||
640 | } | ||
641 | if (rfk_bluetooth) { | ||
642 | rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); | ||
643 | rfkill_bluetooth_set(NULL, !bluetooth_s); | ||
644 | } | ||
645 | if (rfk_threeg) { | ||
646 | rfkill_set_sw_state(rfk_threeg, !threeg_s); | ||
647 | rfkill_threeg_set(NULL, !threeg_s); | ||
648 | } | ||
649 | } | ||
650 | static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill); | ||
651 | |||
555 | static int rfkill_init(struct platform_device *sdev) | 652 | static int rfkill_init(struct platform_device *sdev) |
556 | { | 653 | { |
557 | /* add rfkill */ | 654 | /* add rfkill */ |
558 | int retval; | 655 | int retval; |
559 | 656 | ||
657 | /* keep the hardware wireless state */ | ||
658 | get_wireless_state_ec_standard(); | ||
659 | |||
560 | rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, | 660 | rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, |
561 | RFKILL_TYPE_BLUETOOTH, | 661 | RFKILL_TYPE_BLUETOOTH, |
562 | &rfkill_bluetooth_ops, NULL); | 662 | &rfkill_bluetooth_ops, NULL); |
@@ -590,6 +690,10 @@ static int rfkill_init(struct platform_device *sdev) | |||
590 | goto err_threeg; | 690 | goto err_threeg; |
591 | } | 691 | } |
592 | 692 | ||
693 | /* schedule to run rfkill state initial */ | ||
694 | schedule_delayed_work(&msi_rfkill_init, | ||
695 | round_jiffies_relative(1 * HZ)); | ||
696 | |||
593 | return 0; | 697 | return 0; |
594 | 698 | ||
595 | err_threeg: | 699 | err_threeg: |
@@ -653,9 +757,24 @@ static int load_scm_model_init(struct platform_device *sdev) | |||
653 | /* initial rfkill */ | 757 | /* initial rfkill */ |
654 | result = rfkill_init(sdev); | 758 | result = rfkill_init(sdev); |
655 | if (result < 0) | 759 | if (result < 0) |
656 | return result; | 760 | goto fail_rfkill; |
761 | |||
762 | result = i8042_install_filter(msi_laptop_i8042_filter); | ||
763 | if (result) { | ||
764 | printk(KERN_ERR | ||
765 | "msi-laptop: Unable to install key filter\n"); | ||
766 | goto fail_filter; | ||
767 | } | ||
657 | 768 | ||
658 | return 0; | 769 | return 0; |
770 | |||
771 | fail_filter: | ||
772 | rfkill_cleanup(); | ||
773 | |||
774 | fail_rfkill: | ||
775 | |||
776 | return result; | ||
777 | |||
659 | } | 778 | } |
660 | 779 | ||
661 | static int __init msi_init(void) | 780 | static int __init msi_init(void) |
@@ -714,7 +833,8 @@ static int __init msi_init(void) | |||
714 | goto fail_platform_device1; | 833 | goto fail_platform_device1; |
715 | } | 834 | } |
716 | 835 | ||
717 | ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); | 836 | ret = sysfs_create_group(&msipf_device->dev.kobj, |
837 | &msipf_attribute_group); | ||
718 | if (ret) | 838 | if (ret) |
719 | goto fail_platform_device2; | 839 | goto fail_platform_device2; |
720 | 840 | ||
@@ -739,6 +859,11 @@ static int __init msi_init(void) | |||
739 | 859 | ||
740 | fail_platform_device2: | 860 | fail_platform_device2: |
741 | 861 | ||
862 | if (load_scm_model) { | ||
863 | i8042_remove_filter(msi_laptop_i8042_filter); | ||
864 | cancel_delayed_work_sync(&msi_rfkill_work); | ||
865 | rfkill_cleanup(); | ||
866 | } | ||
742 | platform_device_del(msipf_device); | 867 | platform_device_del(msipf_device); |
743 | 868 | ||
744 | fail_platform_device1: | 869 | fail_platform_device1: |
@@ -758,6 +883,11 @@ fail_backlight: | |||
758 | 883 | ||
759 | static void __exit msi_cleanup(void) | 884 | static void __exit msi_cleanup(void) |
760 | { | 885 | { |
886 | if (load_scm_model) { | ||
887 | i8042_remove_filter(msi_laptop_i8042_filter); | ||
888 | cancel_delayed_work_sync(&msi_rfkill_work); | ||
889 | rfkill_cleanup(); | ||
890 | } | ||
761 | 891 | ||
762 | sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); | 892 | sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); |
763 | if (!old_ec_model && threeg_exists) | 893 | if (!old_ec_model && threeg_exists) |
@@ -766,8 +896,6 @@ static void __exit msi_cleanup(void) | |||
766 | platform_driver_unregister(&msipf_driver); | 896 | platform_driver_unregister(&msipf_driver); |
767 | backlight_device_unregister(msibl_device); | 897 | backlight_device_unregister(msibl_device); |
768 | 898 | ||
769 | rfkill_cleanup(); | ||
770 | |||
771 | /* Enable automatic brightness control again */ | 899 | /* Enable automatic brightness control again */ |
772 | if (auto_brightness != 2) | 900 | if (auto_brightness != 2) |
773 | set_auto_brightness(1); | 901 | set_auto_brightness(1); |
@@ -788,3 +916,6 @@ MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-105 | |||
788 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); | 916 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); |
789 | MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); | 917 | MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); |
790 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); | 918 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); |
919 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*"); | ||
920 | MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); | ||
921 | MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*"); | ||
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 63290b33c879..4bdb13796e24 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -122,8 +122,14 @@ enum { | |||
122 | TP_NVRAM_POS_LEVEL_VOLUME = 0, | 122 | TP_NVRAM_POS_LEVEL_VOLUME = 0, |
123 | }; | 123 | }; |
124 | 124 | ||
125 | /* Misc NVRAM-related */ | ||
126 | enum { | ||
127 | TP_NVRAM_LEVEL_VOLUME_MAX = 14, | ||
128 | }; | ||
129 | |||
125 | /* ACPI HIDs */ | 130 | /* ACPI HIDs */ |
126 | #define TPACPI_ACPI_HKEY_HID "IBM0068" | 131 | #define TPACPI_ACPI_HKEY_HID "IBM0068" |
132 | #define TPACPI_ACPI_EC_HID "PNP0C09" | ||
127 | 133 | ||
128 | /* Input IDs */ | 134 | /* Input IDs */ |
129 | #define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ | 135 | #define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ |
@@ -299,8 +305,8 @@ static struct { | |||
299 | u32 hotkey_tablet:1; | 305 | u32 hotkey_tablet:1; |
300 | u32 light:1; | 306 | u32 light:1; |
301 | u32 light_status:1; | 307 | u32 light_status:1; |
302 | u32 bright_16levels:1; | ||
303 | u32 bright_acpimode:1; | 308 | u32 bright_acpimode:1; |
309 | u32 bright_unkfw:1; | ||
304 | u32 wan:1; | 310 | u32 wan:1; |
305 | u32 uwb:1; | 311 | u32 uwb:1; |
306 | u32 fan_ctrl_status_undef:1; | 312 | u32 fan_ctrl_status_undef:1; |
@@ -363,6 +369,9 @@ struct tpacpi_led_classdev { | |||
363 | unsigned int led; | 369 | unsigned int led; |
364 | }; | 370 | }; |
365 | 371 | ||
372 | /* brightness level capabilities */ | ||
373 | static unsigned int bright_maxlvl; /* 0 = unknown */ | ||
374 | |||
366 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | 375 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
367 | static int dbg_wlswemul; | 376 | static int dbg_wlswemul; |
368 | static int tpacpi_wlsw_emulstate; | 377 | static int tpacpi_wlsw_emulstate; |
@@ -480,6 +489,15 @@ static unsigned long __init tpacpi_check_quirks( | |||
480 | return 0; | 489 | return 0; |
481 | } | 490 | } |
482 | 491 | ||
492 | static inline bool __pure __init tpacpi_is_lenovo(void) | ||
493 | { | ||
494 | return thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO; | ||
495 | } | ||
496 | |||
497 | static inline bool __pure __init tpacpi_is_ibm(void) | ||
498 | { | ||
499 | return thinkpad_id.vendor == PCI_VENDOR_ID_IBM; | ||
500 | } | ||
483 | 501 | ||
484 | /**************************************************************************** | 502 | /**************************************************************************** |
485 | **************************************************************************** | 503 | **************************************************************************** |
@@ -494,21 +512,13 @@ static unsigned long __init tpacpi_check_quirks( | |||
494 | */ | 512 | */ |
495 | 513 | ||
496 | static acpi_handle root_handle; | 514 | static acpi_handle root_handle; |
515 | static acpi_handle ec_handle; | ||
497 | 516 | ||
498 | #define TPACPI_HANDLE(object, parent, paths...) \ | 517 | #define TPACPI_HANDLE(object, parent, paths...) \ |
499 | static acpi_handle object##_handle; \ | 518 | static acpi_handle object##_handle; \ |
500 | static acpi_handle *object##_parent = &parent##_handle; \ | 519 | static const acpi_handle *object##_parent __initdata = \ |
501 | static char *object##_path; \ | 520 | &parent##_handle; \ |
502 | static char *object##_paths[] = { paths } | 521 | static char *object##_paths[] __initdata = { paths } |
503 | |||
504 | TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ | ||
505 | "\\_SB.PCI.ISA.EC", /* 570 */ | ||
506 | "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ | ||
507 | "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ | ||
508 | "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ | ||
509 | "\\_SB.PCI0.ICH3.EC0", /* R31 */ | ||
510 | "\\_SB.PCI0.LPC.EC", /* all others */ | ||
511 | ); | ||
512 | 522 | ||
513 | TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */ | 523 | TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */ |
514 | TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */ | 524 | TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */ |
@@ -528,6 +538,7 @@ TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ | |||
528 | "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ | 538 | "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ |
529 | "\\_SB.PCI0.VID0", /* 770e */ | 539 | "\\_SB.PCI0.VID0", /* 770e */ |
530 | "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ | 540 | "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ |
541 | "\\_SB.PCI0.AGP.VGA", /* X100e and a few others */ | ||
531 | "\\_SB.PCI0.AGP.VID", /* all others */ | 542 | "\\_SB.PCI0.AGP.VID", /* all others */ |
532 | ); /* R30, R31 */ | 543 | ); /* R30, R31 */ |
533 | 544 | ||
@@ -594,9 +605,10 @@ static int acpi_evalf(acpi_handle handle, | |||
594 | 605 | ||
595 | switch (res_type) { | 606 | switch (res_type) { |
596 | case 'd': /* int */ | 607 | case 'd': /* int */ |
597 | if (res) | 608 | success = (status == AE_OK && |
609 | out_obj.type == ACPI_TYPE_INTEGER); | ||
610 | if (success && res) | ||
598 | *(int *)res = out_obj.integer.value; | 611 | *(int *)res = out_obj.integer.value; |
599 | success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; | ||
600 | break; | 612 | break; |
601 | case 'v': /* void */ | 613 | case 'v': /* void */ |
602 | success = status == AE_OK; | 614 | success = status == AE_OK; |
@@ -609,8 +621,8 @@ static int acpi_evalf(acpi_handle handle, | |||
609 | } | 621 | } |
610 | 622 | ||
611 | if (!success && !quiet) | 623 | if (!success && !quiet) |
612 | printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", | 624 | printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %s\n", |
613 | method, fmt0, status); | 625 | method, fmt0, acpi_format_exception(status)); |
614 | 626 | ||
615 | return success; | 627 | return success; |
616 | } | 628 | } |
@@ -661,11 +673,11 @@ static int issue_thinkpad_cmos_command(int cmos_cmd) | |||
661 | 673 | ||
662 | #define TPACPI_ACPIHANDLE_INIT(object) \ | 674 | #define TPACPI_ACPIHANDLE_INIT(object) \ |
663 | drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \ | 675 | drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \ |
664 | object##_paths, ARRAY_SIZE(object##_paths), &object##_path) | 676 | object##_paths, ARRAY_SIZE(object##_paths)) |
665 | 677 | ||
666 | static void drv_acpi_handle_init(char *name, | 678 | static void __init drv_acpi_handle_init(const char *name, |
667 | acpi_handle *handle, acpi_handle parent, | 679 | acpi_handle *handle, const acpi_handle parent, |
668 | char **paths, int num_paths, char **path) | 680 | char **paths, const int num_paths) |
669 | { | 681 | { |
670 | int i; | 682 | int i; |
671 | acpi_status status; | 683 | acpi_status status; |
@@ -676,10 +688,9 @@ static void drv_acpi_handle_init(char *name, | |||
676 | for (i = 0; i < num_paths; i++) { | 688 | for (i = 0; i < num_paths; i++) { |
677 | status = acpi_get_handle(parent, paths[i], handle); | 689 | status = acpi_get_handle(parent, paths[i], handle); |
678 | if (ACPI_SUCCESS(status)) { | 690 | if (ACPI_SUCCESS(status)) { |
679 | *path = paths[i]; | ||
680 | dbg_printk(TPACPI_DBG_INIT, | 691 | dbg_printk(TPACPI_DBG_INIT, |
681 | "Found ACPI handle %s for %s\n", | 692 | "Found ACPI handle %s for %s\n", |
682 | *path, name); | 693 | paths[i], name); |
683 | return; | 694 | return; |
684 | } | 695 | } |
685 | } | 696 | } |
@@ -689,6 +700,43 @@ static void drv_acpi_handle_init(char *name, | |||
689 | *handle = NULL; | 700 | *handle = NULL; |
690 | } | 701 | } |
691 | 702 | ||
703 | static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle, | ||
704 | u32 level, void *context, void **return_value) | ||
705 | { | ||
706 | *(acpi_handle *)return_value = handle; | ||
707 | |||
708 | return AE_CTRL_TERMINATE; | ||
709 | } | ||
710 | |||
711 | static void __init tpacpi_acpi_handle_locate(const char *name, | ||
712 | const char *hid, | ||
713 | acpi_handle *handle) | ||
714 | { | ||
715 | acpi_status status; | ||
716 | acpi_handle device_found; | ||
717 | |||
718 | BUG_ON(!name || !hid || !handle); | ||
719 | vdbg_printk(TPACPI_DBG_INIT, | ||
720 | "trying to locate ACPI handle for %s, using HID %s\n", | ||
721 | name, hid); | ||
722 | |||
723 | memset(&device_found, 0, sizeof(device_found)); | ||
724 | status = acpi_get_devices(hid, tpacpi_acpi_handle_locate_callback, | ||
725 | (void *)name, &device_found); | ||
726 | |||
727 | *handle = NULL; | ||
728 | |||
729 | if (ACPI_SUCCESS(status)) { | ||
730 | *handle = device_found; | ||
731 | dbg_printk(TPACPI_DBG_INIT, | ||
732 | "Found ACPI handle for %s\n", name); | ||
733 | } else { | ||
734 | vdbg_printk(TPACPI_DBG_INIT, | ||
735 | "Could not locate an ACPI handle for %s: %s\n", | ||
736 | name, acpi_format_exception(status)); | ||
737 | } | ||
738 | } | ||
739 | |||
692 | static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) | 740 | static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) |
693 | { | 741 | { |
694 | struct ibm_struct *ibm = data; | 742 | struct ibm_struct *ibm = data; |
@@ -736,8 +784,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) | |||
736 | "handling %s events\n", ibm->name); | 784 | "handling %s events\n", ibm->name); |
737 | } else { | 785 | } else { |
738 | printk(TPACPI_ERR | 786 | printk(TPACPI_ERR |
739 | "acpi_install_notify_handler(%s) failed: %d\n", | 787 | "acpi_install_notify_handler(%s) failed: %s\n", |
740 | ibm->name, status); | 788 | ibm->name, acpi_format_exception(status)); |
741 | } | 789 | } |
742 | return -ENODEV; | 790 | return -ENODEV; |
743 | } | 791 | } |
@@ -1035,80 +1083,6 @@ static void tpacpi_disable_brightness_delay(void) | |||
1035 | "ACPI backlight control delay disabled\n"); | 1083 | "ACPI backlight control delay disabled\n"); |
1036 | } | 1084 | } |
1037 | 1085 | ||
1038 | static int __init tpacpi_query_bcl_levels(acpi_handle handle) | ||
1039 | { | ||
1040 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1041 | union acpi_object *obj; | ||
1042 | int rc; | ||
1043 | |||
1044 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { | ||
1045 | obj = (union acpi_object *)buffer.pointer; | ||
1046 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | ||
1047 | printk(TPACPI_ERR "Unknown _BCL data, " | ||
1048 | "please report this to %s\n", TPACPI_MAIL); | ||
1049 | rc = 0; | ||
1050 | } else { | ||
1051 | rc = obj->package.count; | ||
1052 | } | ||
1053 | } else { | ||
1054 | return 0; | ||
1055 | } | ||
1056 | |||
1057 | kfree(buffer.pointer); | ||
1058 | return rc; | ||
1059 | } | ||
1060 | |||
1061 | static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, | ||
1062 | u32 lvl, void *context, void **rv) | ||
1063 | { | ||
1064 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
1065 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
1066 | |||
1067 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
1068 | !strncmp("_BCL", name, sizeof(name) - 1)) { | ||
1069 | BUG_ON(!rv || !*rv); | ||
1070 | **(int **)rv = tpacpi_query_bcl_levels(handle); | ||
1071 | return AE_CTRL_TERMINATE; | ||
1072 | } else { | ||
1073 | return AE_OK; | ||
1074 | } | ||
1075 | } | ||
1076 | |||
1077 | /* | ||
1078 | * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map | ||
1079 | */ | ||
1080 | static int __init tpacpi_check_std_acpi_brightness_support(void) | ||
1081 | { | ||
1082 | int status; | ||
1083 | int bcl_levels = 0; | ||
1084 | void *bcl_ptr = &bcl_levels; | ||
1085 | |||
1086 | if (!vid_handle) { | ||
1087 | TPACPI_ACPIHANDLE_INIT(vid); | ||
1088 | } | ||
1089 | if (!vid_handle) | ||
1090 | return 0; | ||
1091 | |||
1092 | /* | ||
1093 | * Search for a _BCL method, and execute it. This is safe on all | ||
1094 | * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista | ||
1095 | * BIOS in ACPI backlight control mode. We do NOT have to care | ||
1096 | * about calling the _BCL method in an enabled video device, any | ||
1097 | * will do for our purposes. | ||
1098 | */ | ||
1099 | |||
1100 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | ||
1101 | tpacpi_acpi_walk_find_bcl, NULL, NULL, | ||
1102 | &bcl_ptr); | ||
1103 | |||
1104 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { | ||
1105 | tp_features.bright_acpimode = 1; | ||
1106 | return (bcl_levels - 2); | ||
1107 | } | ||
1108 | |||
1109 | return 0; | ||
1110 | } | ||
1111 | |||
1112 | static void printk_deprecated_attribute(const char * const what, | 1086 | static void printk_deprecated_attribute(const char * const what, |
1113 | const char * const details) | 1087 | const char * const details) |
1114 | { | 1088 | { |
@@ -1872,34 +1846,9 @@ static bool __init tpacpi_is_fw_known(void) | |||
1872 | ****************************************************************************/ | 1846 | ****************************************************************************/ |
1873 | 1847 | ||
1874 | /************************************************************************* | 1848 | /************************************************************************* |
1875 | * thinkpad-acpi init subdriver | 1849 | * thinkpad-acpi metadata subdriver |
1876 | */ | 1850 | */ |
1877 | 1851 | ||
1878 | static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) | ||
1879 | { | ||
1880 | printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION); | ||
1881 | printk(TPACPI_INFO "%s\n", TPACPI_URL); | ||
1882 | |||
1883 | printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n", | ||
1884 | (thinkpad_id.bios_version_str) ? | ||
1885 | thinkpad_id.bios_version_str : "unknown", | ||
1886 | (thinkpad_id.ec_version_str) ? | ||
1887 | thinkpad_id.ec_version_str : "unknown"); | ||
1888 | |||
1889 | if (thinkpad_id.vendor && thinkpad_id.model_str) | ||
1890 | printk(TPACPI_INFO "%s %s, model %s\n", | ||
1891 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? | ||
1892 | "IBM" : ((thinkpad_id.vendor == | ||
1893 | PCI_VENDOR_ID_LENOVO) ? | ||
1894 | "Lenovo" : "Unknown vendor"), | ||
1895 | thinkpad_id.model_str, | ||
1896 | (thinkpad_id.nummodel_str) ? | ||
1897 | thinkpad_id.nummodel_str : "unknown"); | ||
1898 | |||
1899 | tpacpi_check_outdated_fw(); | ||
1900 | return 0; | ||
1901 | } | ||
1902 | |||
1903 | static int thinkpad_acpi_driver_read(struct seq_file *m) | 1852 | static int thinkpad_acpi_driver_read(struct seq_file *m) |
1904 | { | 1853 | { |
1905 | seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC); | 1854 | seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC); |
@@ -2405,6 +2354,36 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | |||
2405 | tpacpi_hotkey_send_key(__scancode); \ | 2354 | tpacpi_hotkey_send_key(__scancode); \ |
2406 | } while (0) | 2355 | } while (0) |
2407 | 2356 | ||
2357 | void issue_volchange(const unsigned int oldvol, | ||
2358 | const unsigned int newvol) | ||
2359 | { | ||
2360 | unsigned int i = oldvol; | ||
2361 | |||
2362 | while (i > newvol) { | ||
2363 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); | ||
2364 | i--; | ||
2365 | } | ||
2366 | while (i < newvol) { | ||
2367 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); | ||
2368 | i++; | ||
2369 | } | ||
2370 | } | ||
2371 | |||
2372 | void issue_brightnesschange(const unsigned int oldbrt, | ||
2373 | const unsigned int newbrt) | ||
2374 | { | ||
2375 | unsigned int i = oldbrt; | ||
2376 | |||
2377 | while (i > newbrt) { | ||
2378 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); | ||
2379 | i--; | ||
2380 | } | ||
2381 | while (i < newbrt) { | ||
2382 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); | ||
2383 | i++; | ||
2384 | } | ||
2385 | } | ||
2386 | |||
2408 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); | 2387 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); |
2409 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); | 2388 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); |
2410 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle); | 2389 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle); |
@@ -2414,41 +2393,61 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | |||
2414 | 2393 | ||
2415 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle); | 2394 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle); |
2416 | 2395 | ||
2417 | /* handle volume */ | 2396 | /* |
2418 | if (oldn->volume_toggle != newn->volume_toggle) { | 2397 | * Handle volume |
2419 | if (oldn->mute != newn->mute) { | 2398 | * |
2399 | * This code is supposed to duplicate the IBM firmware behaviour: | ||
2400 | * - Pressing MUTE issues mute hotkey message, even when already mute | ||
2401 | * - Pressing Volume up/down issues volume up/down hotkey messages, | ||
2402 | * even when already at maximum or minumum volume | ||
2403 | * - The act of unmuting issues volume up/down notification, | ||
2404 | * depending which key was used to unmute | ||
2405 | * | ||
2406 | * We are constrained to what the NVRAM can tell us, which is not much | ||
2407 | * and certainly not enough if more than one volume hotkey was pressed | ||
2408 | * since the last poll cycle. | ||
2409 | * | ||
2410 | * Just to make our life interesting, some newer Lenovo ThinkPads have | ||
2411 | * bugs in the BIOS and may fail to update volume_toggle properly. | ||
2412 | */ | ||
2413 | if (newn->mute) { | ||
2414 | /* muted */ | ||
2415 | if (!oldn->mute || | ||
2416 | oldn->volume_toggle != newn->volume_toggle || | ||
2417 | oldn->volume_level != newn->volume_level) { | ||
2418 | /* recently muted, or repeated mute keypress, or | ||
2419 | * multiple presses ending in mute */ | ||
2420 | issue_volchange(oldn->volume_level, newn->volume_level); | ||
2420 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); | 2421 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); |
2421 | } | 2422 | } |
2422 | if (oldn->volume_level > newn->volume_level) { | 2423 | } else { |
2423 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); | 2424 | /* unmute */ |
2424 | } else if (oldn->volume_level < newn->volume_level) { | 2425 | if (oldn->mute) { |
2426 | /* recently unmuted, issue 'unmute' keypress */ | ||
2425 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); | 2427 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); |
2426 | } else if (oldn->mute == newn->mute) { | 2428 | } |
2427 | /* repeated key presses that didn't change state */ | 2429 | if (oldn->volume_level != newn->volume_level) { |
2428 | if (newn->mute) { | 2430 | issue_volchange(oldn->volume_level, newn->volume_level); |
2429 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); | 2431 | } else if (oldn->volume_toggle != newn->volume_toggle) { |
2430 | } else if (newn->volume_level != 0) { | 2432 | /* repeated vol up/down keypress at end of scale ? */ |
2431 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); | 2433 | if (newn->volume_level == 0) |
2432 | } else { | ||
2433 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); | 2434 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); |
2434 | } | 2435 | else if (newn->volume_level >= TP_NVRAM_LEVEL_VOLUME_MAX) |
2436 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); | ||
2435 | } | 2437 | } |
2436 | } | 2438 | } |
2437 | 2439 | ||
2438 | /* handle brightness */ | 2440 | /* handle brightness */ |
2439 | if (oldn->brightness_toggle != newn->brightness_toggle) { | 2441 | if (oldn->brightness_level != newn->brightness_level) { |
2440 | if (oldn->brightness_level < newn->brightness_level) { | 2442 | issue_brightnesschange(oldn->brightness_level, |
2441 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); | 2443 | newn->brightness_level); |
2442 | } else if (oldn->brightness_level > newn->brightness_level) { | 2444 | } else if (oldn->brightness_toggle != newn->brightness_toggle) { |
2445 | /* repeated key presses that didn't change state */ | ||
2446 | if (newn->brightness_level == 0) | ||
2443 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); | 2447 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); |
2444 | } else { | 2448 | else if (newn->brightness_level >= bright_maxlvl |
2445 | /* repeated key presses that didn't change state */ | 2449 | && !tp_features.bright_unkfw) |
2446 | if (newn->brightness_level != 0) { | 2450 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); |
2447 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); | ||
2448 | } else { | ||
2449 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); | ||
2450 | } | ||
2451 | } | ||
2452 | } | 2451 | } |
2453 | 2452 | ||
2454 | #undef TPACPI_COMPARE_KEY | 2453 | #undef TPACPI_COMPARE_KEY |
@@ -3353,7 +3352,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3353 | goto err_exit; | 3352 | goto err_exit; |
3354 | } | 3353 | } |
3355 | 3354 | ||
3356 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | 3355 | if (tpacpi_is_lenovo()) { |
3357 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, | 3356 | dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, |
3358 | "using Lenovo default hot key map\n"); | 3357 | "using Lenovo default hot key map\n"); |
3359 | memcpy(hotkey_keycode_map, &lenovo_keycode_map, | 3358 | memcpy(hotkey_keycode_map, &lenovo_keycode_map, |
@@ -3391,11 +3390,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
3391 | } | 3390 | } |
3392 | 3391 | ||
3393 | /* Do not issue duplicate brightness change events to | 3392 | /* Do not issue duplicate brightness change events to |
3394 | * userspace */ | 3393 | * userspace. tpacpi_detect_brightness_capabilities() must have |
3395 | if (!tp_features.bright_acpimode) | 3394 | * been called before this point */ |
3396 | /* update bright_acpimode... */ | ||
3397 | tpacpi_check_std_acpi_brightness_support(); | ||
3398 | |||
3399 | if (tp_features.bright_acpimode && acpi_video_backlight_support()) { | 3395 | if (tp_features.bright_acpimode && acpi_video_backlight_support()) { |
3400 | printk(TPACPI_INFO | 3396 | printk(TPACPI_INFO |
3401 | "This ThinkPad has standard ACPI backlight " | 3397 | "This ThinkPad has standard ACPI backlight " |
@@ -4422,7 +4418,8 @@ static int __init video_init(struct ibm_init_struct *iibm) | |||
4422 | vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n"); | 4418 | vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n"); |
4423 | 4419 | ||
4424 | TPACPI_ACPIHANDLE_INIT(vid); | 4420 | TPACPI_ACPIHANDLE_INIT(vid); |
4425 | TPACPI_ACPIHANDLE_INIT(vid2); | 4421 | if (tpacpi_is_ibm()) |
4422 | TPACPI_ACPIHANDLE_INIT(vid2); | ||
4426 | 4423 | ||
4427 | if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) | 4424 | if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) |
4428 | /* G41, assume IVGA doesn't change */ | 4425 | /* G41, assume IVGA doesn't change */ |
@@ -4431,10 +4428,12 @@ static int __init video_init(struct ibm_init_struct *iibm) | |||
4431 | if (!vid_handle) | 4428 | if (!vid_handle) |
4432 | /* video switching not supported on R30, R31 */ | 4429 | /* video switching not supported on R30, R31 */ |
4433 | video_supported = TPACPI_VIDEO_NONE; | 4430 | video_supported = TPACPI_VIDEO_NONE; |
4434 | else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) | 4431 | else if (tpacpi_is_ibm() && |
4432 | acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) | ||
4435 | /* 570 */ | 4433 | /* 570 */ |
4436 | video_supported = TPACPI_VIDEO_570; | 4434 | video_supported = TPACPI_VIDEO_570; |
4437 | else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) | 4435 | else if (tpacpi_is_ibm() && |
4436 | acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) | ||
4438 | /* 600e/x, 770e, 770x */ | 4437 | /* 600e/x, 770e, 770x */ |
4439 | video_supported = TPACPI_VIDEO_770; | 4438 | video_supported = TPACPI_VIDEO_770; |
4440 | else | 4439 | else |
@@ -4811,8 +4810,10 @@ static int __init light_init(struct ibm_init_struct *iibm) | |||
4811 | 4810 | ||
4812 | vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); | 4811 | vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); |
4813 | 4812 | ||
4814 | TPACPI_ACPIHANDLE_INIT(ledb); | 4813 | if (tpacpi_is_ibm()) { |
4815 | TPACPI_ACPIHANDLE_INIT(lght); | 4814 | TPACPI_ACPIHANDLE_INIT(ledb); |
4815 | TPACPI_ACPIHANDLE_INIT(lght); | ||
4816 | } | ||
4816 | TPACPI_ACPIHANDLE_INIT(cmos); | 4817 | TPACPI_ACPIHANDLE_INIT(cmos); |
4817 | INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); | 4818 | INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); |
4818 | 4819 | ||
@@ -5007,11 +5008,7 @@ enum { /* For TPACPI_LED_OLD */ | |||
5007 | 5008 | ||
5008 | static enum led_access_mode led_supported; | 5009 | static enum led_access_mode led_supported; |
5009 | 5010 | ||
5010 | TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | 5011 | static acpi_handle led_handle; |
5011 | "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, */ | ||
5012 | /* T20-22, X20-21 */ | ||
5013 | "LED", /* all others */ | ||
5014 | ); /* R30, R31 */ | ||
5015 | 5012 | ||
5016 | #define TPACPI_LED_NUMLEDS 16 | 5013 | #define TPACPI_LED_NUMLEDS 16 |
5017 | static struct tpacpi_led_classdev *tpacpi_leds; | 5014 | static struct tpacpi_led_classdev *tpacpi_leds; |
@@ -5271,6 +5268,32 @@ static const struct tpacpi_quirk led_useful_qtable[] __initconst = { | |||
5271 | #undef TPACPI_LEDQ_IBM | 5268 | #undef TPACPI_LEDQ_IBM |
5272 | #undef TPACPI_LEDQ_LNV | 5269 | #undef TPACPI_LEDQ_LNV |
5273 | 5270 | ||
5271 | static enum led_access_mode __init led_init_detect_mode(void) | ||
5272 | { | ||
5273 | acpi_status status; | ||
5274 | |||
5275 | if (tpacpi_is_ibm()) { | ||
5276 | /* 570 */ | ||
5277 | status = acpi_get_handle(ec_handle, "SLED", &led_handle); | ||
5278 | if (ACPI_SUCCESS(status)) | ||
5279 | return TPACPI_LED_570; | ||
5280 | |||
5281 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ | ||
5282 | status = acpi_get_handle(ec_handle, "SYSL", &led_handle); | ||
5283 | if (ACPI_SUCCESS(status)) | ||
5284 | return TPACPI_LED_OLD; | ||
5285 | } | ||
5286 | |||
5287 | /* most others */ | ||
5288 | status = acpi_get_handle(ec_handle, "LED", &led_handle); | ||
5289 | if (ACPI_SUCCESS(status)) | ||
5290 | return TPACPI_LED_NEW; | ||
5291 | |||
5292 | /* R30, R31, and unknown firmwares */ | ||
5293 | led_handle = NULL; | ||
5294 | return TPACPI_LED_NONE; | ||
5295 | } | ||
5296 | |||
5274 | static int __init led_init(struct ibm_init_struct *iibm) | 5297 | static int __init led_init(struct ibm_init_struct *iibm) |
5275 | { | 5298 | { |
5276 | unsigned int i; | 5299 | unsigned int i; |
@@ -5279,20 +5302,7 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
5279 | 5302 | ||
5280 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); | 5303 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); |
5281 | 5304 | ||
5282 | TPACPI_ACPIHANDLE_INIT(led); | 5305 | led_supported = led_init_detect_mode(); |
5283 | |||
5284 | if (!led_handle) | ||
5285 | /* led not supported on R30, R31 */ | ||
5286 | led_supported = TPACPI_LED_NONE; | ||
5287 | else if (strlencmp(led_path, "SLED") == 0) | ||
5288 | /* 570 */ | ||
5289 | led_supported = TPACPI_LED_570; | ||
5290 | else if (strlencmp(led_path, "SYSL") == 0) | ||
5291 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ | ||
5292 | led_supported = TPACPI_LED_OLD; | ||
5293 | else | ||
5294 | /* all others */ | ||
5295 | led_supported = TPACPI_LED_NEW; | ||
5296 | 5306 | ||
5297 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", | 5307 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", |
5298 | str_supported(led_supported), led_supported); | 5308 | str_supported(led_supported), led_supported); |
@@ -5741,11 +5751,12 @@ static int __init thermal_init(struct ibm_init_struct *iibm) | |||
5741 | TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; | 5751 | TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; |
5742 | } | 5752 | } |
5743 | } else if (acpi_tmp7) { | 5753 | } else if (acpi_tmp7) { |
5744 | if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { | 5754 | if (tpacpi_is_ibm() && |
5755 | acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { | ||
5745 | /* 600e/x, 770e, 770x */ | 5756 | /* 600e/x, 770e, 770x */ |
5746 | thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT; | 5757 | thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT; |
5747 | } else { | 5758 | } else { |
5748 | /* Standard ACPI TMPx access, max 8 sensors */ | 5759 | /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */ |
5749 | thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; | 5760 | thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; |
5750 | } | 5761 | } |
5751 | } else { | 5762 | } else { |
@@ -5954,7 +5965,7 @@ static unsigned int tpacpi_brightness_nvram_get(void) | |||
5954 | lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) | 5965 | lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) |
5955 | & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) | 5966 | & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) |
5956 | >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; | 5967 | >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; |
5957 | lnvram &= (tp_features.bright_16levels) ? 0x0f : 0x07; | 5968 | lnvram &= bright_maxlvl; |
5958 | 5969 | ||
5959 | return lnvram; | 5970 | return lnvram; |
5960 | } | 5971 | } |
@@ -6063,8 +6074,7 @@ static int brightness_set(unsigned int value) | |||
6063 | { | 6074 | { |
6064 | int res; | 6075 | int res; |
6065 | 6076 | ||
6066 | if (value > ((tp_features.bright_16levels)? 15 : 7) || | 6077 | if (value > bright_maxlvl || value < 0) |
6067 | value < 0) | ||
6068 | return -EINVAL; | 6078 | return -EINVAL; |
6069 | 6079 | ||
6070 | vdbg_printk(TPACPI_DBG_BRGHT, | 6080 | vdbg_printk(TPACPI_DBG_BRGHT, |
@@ -6139,6 +6149,80 @@ static struct backlight_ops ibm_backlight_data = { | |||
6139 | 6149 | ||
6140 | /* --------------------------------------------------------------------- */ | 6150 | /* --------------------------------------------------------------------- */ |
6141 | 6151 | ||
6152 | static int __init tpacpi_query_bcl_levels(acpi_handle handle) | ||
6153 | { | ||
6154 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
6155 | union acpi_object *obj; | ||
6156 | int rc; | ||
6157 | |||
6158 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { | ||
6159 | obj = (union acpi_object *)buffer.pointer; | ||
6160 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | ||
6161 | printk(TPACPI_ERR "Unknown _BCL data, " | ||
6162 | "please report this to %s\n", TPACPI_MAIL); | ||
6163 | rc = 0; | ||
6164 | } else { | ||
6165 | rc = obj->package.count; | ||
6166 | } | ||
6167 | } else { | ||
6168 | return 0; | ||
6169 | } | ||
6170 | |||
6171 | kfree(buffer.pointer); | ||
6172 | return rc; | ||
6173 | } | ||
6174 | |||
6175 | static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, | ||
6176 | u32 lvl, void *context, void **rv) | ||
6177 | { | ||
6178 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
6179 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
6180 | |||
6181 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
6182 | !strncmp("_BCL", name, sizeof(name) - 1)) { | ||
6183 | BUG_ON(!rv || !*rv); | ||
6184 | **(int **)rv = tpacpi_query_bcl_levels(handle); | ||
6185 | return AE_CTRL_TERMINATE; | ||
6186 | } else { | ||
6187 | return AE_OK; | ||
6188 | } | ||
6189 | } | ||
6190 | |||
6191 | /* | ||
6192 | * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map | ||
6193 | */ | ||
6194 | static unsigned int __init tpacpi_check_std_acpi_brightness_support(void) | ||
6195 | { | ||
6196 | int status; | ||
6197 | int bcl_levels = 0; | ||
6198 | void *bcl_ptr = &bcl_levels; | ||
6199 | |||
6200 | if (!vid_handle) | ||
6201 | TPACPI_ACPIHANDLE_INIT(vid); | ||
6202 | |||
6203 | if (!vid_handle) | ||
6204 | return 0; | ||
6205 | |||
6206 | /* | ||
6207 | * Search for a _BCL method, and execute it. This is safe on all | ||
6208 | * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista | ||
6209 | * BIOS in ACPI backlight control mode. We do NOT have to care | ||
6210 | * about calling the _BCL method in an enabled video device, any | ||
6211 | * will do for our purposes. | ||
6212 | */ | ||
6213 | |||
6214 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | ||
6215 | tpacpi_acpi_walk_find_bcl, NULL, NULL, | ||
6216 | &bcl_ptr); | ||
6217 | |||
6218 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { | ||
6219 | tp_features.bright_acpimode = 1; | ||
6220 | return bcl_levels - 2; | ||
6221 | } | ||
6222 | |||
6223 | return 0; | ||
6224 | } | ||
6225 | |||
6142 | /* | 6226 | /* |
6143 | * These are only useful for models that have only one possibility | 6227 | * These are only useful for models that have only one possibility |
6144 | * of GPU. If the BIOS model handles both ATI and Intel, don't use | 6228 | * of GPU. If the BIOS model handles both ATI and Intel, don't use |
@@ -6169,6 +6253,47 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { | |||
6169 | TPACPI_Q_IBM('7', '5', TPACPI_BRGHT_Q_NOEC), /* X41 Tablet */ | 6253 | TPACPI_Q_IBM('7', '5', TPACPI_BRGHT_Q_NOEC), /* X41 Tablet */ |
6170 | }; | 6254 | }; |
6171 | 6255 | ||
6256 | /* | ||
6257 | * Returns < 0 for error, otherwise sets tp_features.bright_* | ||
6258 | * and bright_maxlvl. | ||
6259 | */ | ||
6260 | static void __init tpacpi_detect_brightness_capabilities(void) | ||
6261 | { | ||
6262 | unsigned int b; | ||
6263 | |||
6264 | vdbg_printk(TPACPI_DBG_INIT, | ||
6265 | "detecting firmware brightness interface capabilities\n"); | ||
6266 | |||
6267 | /* we could run a quirks check here (same table used by | ||
6268 | * brightness_init) if needed */ | ||
6269 | |||
6270 | /* | ||
6271 | * We always attempt to detect acpi support, so as to switch | ||
6272 | * Lenovo Vista BIOS to ACPI brightness mode even if we are not | ||
6273 | * going to publish a backlight interface | ||
6274 | */ | ||
6275 | b = tpacpi_check_std_acpi_brightness_support(); | ||
6276 | switch (b) { | ||
6277 | case 16: | ||
6278 | bright_maxlvl = 15; | ||
6279 | printk(TPACPI_INFO | ||
6280 | "detected a 16-level brightness capable ThinkPad\n"); | ||
6281 | break; | ||
6282 | case 8: | ||
6283 | case 0: | ||
6284 | bright_maxlvl = 7; | ||
6285 | printk(TPACPI_INFO | ||
6286 | "detected a 8-level brightness capable ThinkPad\n"); | ||
6287 | break; | ||
6288 | default: | ||
6289 | printk(TPACPI_ERR | ||
6290 | "Unsupported brightness interface, " | ||
6291 | "please contact %s\n", TPACPI_MAIL); | ||
6292 | tp_features.bright_unkfw = 1; | ||
6293 | bright_maxlvl = b - 1; | ||
6294 | } | ||
6295 | } | ||
6296 | |||
6172 | static int __init brightness_init(struct ibm_init_struct *iibm) | 6297 | static int __init brightness_init(struct ibm_init_struct *iibm) |
6173 | { | 6298 | { |
6174 | struct backlight_properties props; | 6299 | struct backlight_properties props; |
@@ -6182,14 +6307,13 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6182 | quirks = tpacpi_check_quirks(brightness_quirk_table, | 6307 | quirks = tpacpi_check_quirks(brightness_quirk_table, |
6183 | ARRAY_SIZE(brightness_quirk_table)); | 6308 | ARRAY_SIZE(brightness_quirk_table)); |
6184 | 6309 | ||
6185 | /* | 6310 | /* tpacpi_detect_brightness_capabilities() must have run already */ |
6186 | * We always attempt to detect acpi support, so as to switch | 6311 | |
6187 | * Lenovo Vista BIOS to ACPI brightness mode even if we are not | 6312 | /* if it is unknown, we don't handle it: it wouldn't be safe */ |
6188 | * going to publish a backlight interface | 6313 | if (tp_features.bright_unkfw) |
6189 | */ | 6314 | return 1; |
6190 | b = tpacpi_check_std_acpi_brightness_support(); | ||
6191 | if (b > 0) { | ||
6192 | 6315 | ||
6316 | if (tp_features.bright_acpimode) { | ||
6193 | if (acpi_video_backlight_support()) { | 6317 | if (acpi_video_backlight_support()) { |
6194 | if (brightness_enable > 1) { | 6318 | if (brightness_enable > 1) { |
6195 | printk(TPACPI_NOTICE | 6319 | printk(TPACPI_NOTICE |
@@ -6218,15 +6342,6 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6218 | return 1; | 6342 | return 1; |
6219 | } | 6343 | } |
6220 | 6344 | ||
6221 | if (b > 16) { | ||
6222 | printk(TPACPI_ERR | ||
6223 | "Unsupported brightness interface, " | ||
6224 | "please contact %s\n", TPACPI_MAIL); | ||
6225 | return 1; | ||
6226 | } | ||
6227 | if (b == 16) | ||
6228 | tp_features.bright_16levels = 1; | ||
6229 | |||
6230 | /* | 6345 | /* |
6231 | * Check for module parameter bogosity, note that we | 6346 | * Check for module parameter bogosity, note that we |
6232 | * init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be | 6347 | * init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be |
@@ -6249,7 +6364,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6249 | } | 6364 | } |
6250 | 6365 | ||
6251 | /* Safety */ | 6366 | /* Safety */ |
6252 | if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM && | 6367 | if (!tpacpi_is_ibm() && |
6253 | (brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM || | 6368 | (brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM || |
6254 | brightness_mode == TPACPI_BRGHT_MODE_EC)) | 6369 | brightness_mode == TPACPI_BRGHT_MODE_EC)) |
6255 | return -EINVAL; | 6370 | return -EINVAL; |
@@ -6257,12 +6372,9 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6257 | if (tpacpi_brightness_get_raw(&b) < 0) | 6372 | if (tpacpi_brightness_get_raw(&b) < 0) |
6258 | return 1; | 6373 | return 1; |
6259 | 6374 | ||
6260 | if (tp_features.bright_16levels) | ||
6261 | printk(TPACPI_INFO | ||
6262 | "detected a 16-level brightness capable ThinkPad\n"); | ||
6263 | |||
6264 | memset(&props, 0, sizeof(struct backlight_properties)); | 6375 | memset(&props, 0, sizeof(struct backlight_properties)); |
6265 | props.max_brightness = (tp_features.bright_16levels) ? 15 : 7; | 6376 | props.max_brightness = bright_maxlvl; |
6377 | props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; | ||
6266 | ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, | 6378 | ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, |
6267 | NULL, NULL, | 6379 | NULL, NULL, |
6268 | &ibm_backlight_data, | 6380 | &ibm_backlight_data, |
@@ -6285,7 +6397,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
6285 | "or not on your ThinkPad\n", TPACPI_MAIL); | 6397 | "or not on your ThinkPad\n", TPACPI_MAIL); |
6286 | } | 6398 | } |
6287 | 6399 | ||
6288 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; | 6400 | /* Added by mistake in early 2007. Probably useless, but it could |
6401 | * be working around some unknown firmware problem where the value | ||
6402 | * read at startup doesn't match the real hardware state... so leave | ||
6403 | * it in place just in case */ | ||
6289 | backlight_update_status(ibm_backlight_device); | 6404 | backlight_update_status(ibm_backlight_device); |
6290 | 6405 | ||
6291 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, | 6406 | vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, |
@@ -6328,9 +6443,8 @@ static int brightness_read(struct seq_file *m) | |||
6328 | } else { | 6443 | } else { |
6329 | seq_printf(m, "level:\t\t%d\n", level); | 6444 | seq_printf(m, "level:\t\t%d\n", level); |
6330 | seq_printf(m, "commands:\tup, down\n"); | 6445 | seq_printf(m, "commands:\tup, down\n"); |
6331 | seq_printf(m, "commands:\tlevel <level>" | 6446 | seq_printf(m, "commands:\tlevel <level> (<level> is 0-%d)\n", |
6332 | " (<level> is 0-%d)\n", | 6447 | bright_maxlvl); |
6333 | (tp_features.bright_16levels) ? 15 : 7); | ||
6334 | } | 6448 | } |
6335 | 6449 | ||
6336 | return 0; | 6450 | return 0; |
@@ -6341,7 +6455,6 @@ static int brightness_write(char *buf) | |||
6341 | int level; | 6455 | int level; |
6342 | int rc; | 6456 | int rc; |
6343 | char *cmd; | 6457 | char *cmd; |
6344 | int max_level = (tp_features.bright_16levels) ? 15 : 7; | ||
6345 | 6458 | ||
6346 | level = brightness_get(NULL); | 6459 | level = brightness_get(NULL); |
6347 | if (level < 0) | 6460 | if (level < 0) |
@@ -6349,13 +6462,13 @@ static int brightness_write(char *buf) | |||
6349 | 6462 | ||
6350 | while ((cmd = next_cmd(&buf))) { | 6463 | while ((cmd = next_cmd(&buf))) { |
6351 | if (strlencmp(cmd, "up") == 0) { | 6464 | if (strlencmp(cmd, "up") == 0) { |
6352 | if (level < max_level) | 6465 | if (level < bright_maxlvl) |
6353 | level++; | 6466 | level++; |
6354 | } else if (strlencmp(cmd, "down") == 0) { | 6467 | } else if (strlencmp(cmd, "down") == 0) { |
6355 | if (level > 0) | 6468 | if (level > 0) |
6356 | level--; | 6469 | level--; |
6357 | } else if (sscanf(cmd, "level %d", &level) == 1 && | 6470 | } else if (sscanf(cmd, "level %d", &level) == 1 && |
6358 | level >= 0 && level <= max_level) { | 6471 | level >= 0 && level <= bright_maxlvl) { |
6359 | /* new level set */ | 6472 | /* new level set */ |
6360 | } else | 6473 | } else |
6361 | return -EINVAL; | 6474 | return -EINVAL; |
@@ -6669,6 +6782,8 @@ static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol, | |||
6669 | static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol, | 6782 | static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol, |
6670 | struct snd_ctl_elem_value *ucontrol) | 6783 | struct snd_ctl_elem_value *ucontrol) |
6671 | { | 6784 | { |
6785 | tpacpi_disclose_usertask("ALSA", "set volume to %ld\n", | ||
6786 | ucontrol->value.integer.value[0]); | ||
6672 | return volume_alsa_set_volume(ucontrol->value.integer.value[0]); | 6787 | return volume_alsa_set_volume(ucontrol->value.integer.value[0]); |
6673 | } | 6788 | } |
6674 | 6789 | ||
@@ -6692,6 +6807,9 @@ static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol, | |||
6692 | static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, | 6807 | static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, |
6693 | struct snd_ctl_elem_value *ucontrol) | 6808 | struct snd_ctl_elem_value *ucontrol) |
6694 | { | 6809 | { |
6810 | tpacpi_disclose_usertask("ALSA", "%smute\n", | ||
6811 | ucontrol->value.integer.value[0] ? | ||
6812 | "un" : ""); | ||
6695 | return volume_alsa_set_mute(!ucontrol->value.integer.value[0]); | 6813 | return volume_alsa_set_mute(!ucontrol->value.integer.value[0]); |
6696 | } | 6814 | } |
6697 | 6815 | ||
@@ -7968,9 +8086,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
7968 | tp_features.second_fan = 0; | 8086 | tp_features.second_fan = 0; |
7969 | fan_control_desired_level = 7; | 8087 | fan_control_desired_level = 7; |
7970 | 8088 | ||
7971 | TPACPI_ACPIHANDLE_INIT(fans); | 8089 | if (tpacpi_is_ibm()) { |
7972 | TPACPI_ACPIHANDLE_INIT(gfan); | 8090 | TPACPI_ACPIHANDLE_INIT(fans); |
7973 | TPACPI_ACPIHANDLE_INIT(sfan); | 8091 | TPACPI_ACPIHANDLE_INIT(gfan); |
8092 | TPACPI_ACPIHANDLE_INIT(sfan); | ||
8093 | } | ||
7974 | 8094 | ||
7975 | quirks = tpacpi_check_quirks(fan_quirk_table, | 8095 | quirks = tpacpi_check_quirks(fan_quirk_table, |
7976 | ARRAY_SIZE(fan_quirk_table)); | 8096 | ARRAY_SIZE(fan_quirk_table)); |
@@ -8662,6 +8782,10 @@ static int __init probe_for_thinkpad(void) | |||
8662 | if (acpi_disabled) | 8782 | if (acpi_disabled) |
8663 | return -ENODEV; | 8783 | return -ENODEV; |
8664 | 8784 | ||
8785 | /* It would be dangerous to run the driver in this case */ | ||
8786 | if (!tpacpi_is_ibm() && !tpacpi_is_lenovo()) | ||
8787 | return -ENODEV; | ||
8788 | |||
8665 | /* | 8789 | /* |
8666 | * Non-ancient models have better DMI tagging, but very old models | 8790 | * Non-ancient models have better DMI tagging, but very old models |
8667 | * don't. tpacpi_is_fw_known() is a cheat to help in that case. | 8791 | * don't. tpacpi_is_fw_known() is a cheat to help in that case. |
@@ -8670,8 +8794,8 @@ static int __init probe_for_thinkpad(void) | |||
8670 | (thinkpad_id.ec_model != 0) || | 8794 | (thinkpad_id.ec_model != 0) || |
8671 | tpacpi_is_fw_known(); | 8795 | tpacpi_is_fw_known(); |
8672 | 8796 | ||
8673 | /* ec is required because many other handles are relative to it */ | 8797 | /* The EC handler is required */ |
8674 | TPACPI_ACPIHANDLE_INIT(ec); | 8798 | tpacpi_acpi_handle_locate("ec", TPACPI_ACPI_EC_HID, &ec_handle); |
8675 | if (!ec_handle) { | 8799 | if (!ec_handle) { |
8676 | if (is_thinkpad) | 8800 | if (is_thinkpad) |
8677 | printk(TPACPI_ERR | 8801 | printk(TPACPI_ERR |
@@ -8685,12 +8809,34 @@ static int __init probe_for_thinkpad(void) | |||
8685 | return 0; | 8809 | return 0; |
8686 | } | 8810 | } |
8687 | 8811 | ||
8812 | static void __init thinkpad_acpi_init_banner(void) | ||
8813 | { | ||
8814 | printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION); | ||
8815 | printk(TPACPI_INFO "%s\n", TPACPI_URL); | ||
8816 | |||
8817 | printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n", | ||
8818 | (thinkpad_id.bios_version_str) ? | ||
8819 | thinkpad_id.bios_version_str : "unknown", | ||
8820 | (thinkpad_id.ec_version_str) ? | ||
8821 | thinkpad_id.ec_version_str : "unknown"); | ||
8822 | |||
8823 | BUG_ON(!thinkpad_id.vendor); | ||
8824 | |||
8825 | if (thinkpad_id.model_str) | ||
8826 | printk(TPACPI_INFO "%s %s, model %s\n", | ||
8827 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? | ||
8828 | "IBM" : ((thinkpad_id.vendor == | ||
8829 | PCI_VENDOR_ID_LENOVO) ? | ||
8830 | "Lenovo" : "Unknown vendor"), | ||
8831 | thinkpad_id.model_str, | ||
8832 | (thinkpad_id.nummodel_str) ? | ||
8833 | thinkpad_id.nummodel_str : "unknown"); | ||
8834 | } | ||
8688 | 8835 | ||
8689 | /* Module init, exit, parameters */ | 8836 | /* Module init, exit, parameters */ |
8690 | 8837 | ||
8691 | static struct ibm_init_struct ibms_init[] __initdata = { | 8838 | static struct ibm_init_struct ibms_init[] __initdata = { |
8692 | { | 8839 | { |
8693 | .init = thinkpad_acpi_driver_init, | ||
8694 | .data = &thinkpad_acpi_driver_data, | 8840 | .data = &thinkpad_acpi_driver_data, |
8695 | }, | 8841 | }, |
8696 | { | 8842 | { |
@@ -8960,6 +9106,9 @@ static int __init thinkpad_acpi_module_init(void) | |||
8960 | 9106 | ||
8961 | /* Driver initialization */ | 9107 | /* Driver initialization */ |
8962 | 9108 | ||
9109 | thinkpad_acpi_init_banner(); | ||
9110 | tpacpi_check_outdated_fw(); | ||
9111 | |||
8963 | TPACPI_ACPIHANDLE_INIT(ecrd); | 9112 | TPACPI_ACPIHANDLE_INIT(ecrd); |
8964 | TPACPI_ACPIHANDLE_INIT(ecwr); | 9113 | TPACPI_ACPIHANDLE_INIT(ecwr); |
8965 | 9114 | ||
@@ -9059,13 +9208,16 @@ static int __init thinkpad_acpi_module_init(void) | |||
9059 | tpacpi_inputdev->name = "ThinkPad Extra Buttons"; | 9208 | tpacpi_inputdev->name = "ThinkPad Extra Buttons"; |
9060 | tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0"; | 9209 | tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0"; |
9061 | tpacpi_inputdev->id.bustype = BUS_HOST; | 9210 | tpacpi_inputdev->id.bustype = BUS_HOST; |
9062 | tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ? | 9211 | tpacpi_inputdev->id.vendor = thinkpad_id.vendor; |
9063 | thinkpad_id.vendor : | ||
9064 | PCI_VENDOR_ID_IBM; | ||
9065 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; | 9212 | tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; |
9066 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; | 9213 | tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; |
9067 | tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; | 9214 | tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; |
9068 | } | 9215 | } |
9216 | |||
9217 | /* Init subdriver dependencies */ | ||
9218 | tpacpi_detect_brightness_capabilities(); | ||
9219 | |||
9220 | /* Init subdrivers */ | ||
9069 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { | 9221 | for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { |
9070 | ret = ibm_init(&ibms_init[i]); | 9222 | ret = ibm_init(&ibms_init[i]); |
9071 | if (ret >= 0 && *ibms_init[i].param) | 9223 | if (ret >= 0 && *ibms_init[i].param) |
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 39ec5b6c2e3a..e4eaa14ed987 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -81,6 +81,16 @@ static struct wmi_block wmi_blocks; | |||
81 | #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ | 81 | #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ |
82 | #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ | 82 | #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ |
83 | 83 | ||
84 | static int debug_event; | ||
85 | module_param(debug_event, bool, 0444); | ||
86 | MODULE_PARM_DESC(debug_event, | ||
87 | "Log WMI Events [0/1]"); | ||
88 | |||
89 | static int debug_dump_wdg; | ||
90 | module_param(debug_dump_wdg, bool, 0444); | ||
91 | MODULE_PARM_DESC(debug_dump_wdg, | ||
92 | "Dump available WMI interfaces [0/1]"); | ||
93 | |||
84 | static int acpi_wmi_remove(struct acpi_device *device, int type); | 94 | static int acpi_wmi_remove(struct acpi_device *device, int type); |
85 | static int acpi_wmi_add(struct acpi_device *device); | 95 | static int acpi_wmi_add(struct acpi_device *device); |
86 | static void acpi_wmi_notify(struct acpi_device *device, u32 event); | 96 | static void acpi_wmi_notify(struct acpi_device *device, u32 event); |
@@ -477,6 +487,64 @@ const struct acpi_buffer *in) | |||
477 | } | 487 | } |
478 | EXPORT_SYMBOL_GPL(wmi_set_block); | 488 | EXPORT_SYMBOL_GPL(wmi_set_block); |
479 | 489 | ||
490 | static void wmi_dump_wdg(struct guid_block *g) | ||
491 | { | ||
492 | char guid_string[37]; | ||
493 | |||
494 | wmi_gtoa(g->guid, guid_string); | ||
495 | printk(KERN_INFO PREFIX "%s:\n", guid_string); | ||
496 | printk(KERN_INFO PREFIX "\tobject_id: %c%c\n", | ||
497 | g->object_id[0], g->object_id[1]); | ||
498 | printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id); | ||
499 | printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved); | ||
500 | printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count); | ||
501 | printk(KERN_INFO PREFIX "\tflags: %#x", g->flags); | ||
502 | if (g->flags) { | ||
503 | printk(" "); | ||
504 | if (g->flags & ACPI_WMI_EXPENSIVE) | ||
505 | printk("ACPI_WMI_EXPENSIVE "); | ||
506 | if (g->flags & ACPI_WMI_METHOD) | ||
507 | printk("ACPI_WMI_METHOD "); | ||
508 | if (g->flags & ACPI_WMI_STRING) | ||
509 | printk("ACPI_WMI_STRING "); | ||
510 | if (g->flags & ACPI_WMI_EVENT) | ||
511 | printk("ACPI_WMI_EVENT "); | ||
512 | } | ||
513 | printk("\n"); | ||
514 | |||
515 | } | ||
516 | |||
517 | static void wmi_notify_debug(u32 value, void *context) | ||
518 | { | ||
519 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
520 | union acpi_object *obj; | ||
521 | |||
522 | wmi_get_event_data(value, &response); | ||
523 | |||
524 | obj = (union acpi_object *)response.pointer; | ||
525 | |||
526 | if (!obj) | ||
527 | return; | ||
528 | |||
529 | printk(KERN_INFO PREFIX "DEBUG Event "); | ||
530 | switch(obj->type) { | ||
531 | case ACPI_TYPE_BUFFER: | ||
532 | printk("BUFFER_TYPE - length %d\n", obj->buffer.length); | ||
533 | break; | ||
534 | case ACPI_TYPE_STRING: | ||
535 | printk("STRING_TYPE - %s\n", obj->string.pointer); | ||
536 | break; | ||
537 | case ACPI_TYPE_INTEGER: | ||
538 | printk("INTEGER_TYPE - %llu\n", obj->integer.value); | ||
539 | break; | ||
540 | case ACPI_TYPE_PACKAGE: | ||
541 | printk("PACKAGE_TYPE - %d elements\n", obj->package.count); | ||
542 | break; | ||
543 | default: | ||
544 | printk("object type 0x%X\n", obj->type); | ||
545 | } | ||
546 | } | ||
547 | |||
480 | /** | 548 | /** |
481 | * wmi_install_notify_handler - Register handler for WMI events | 549 | * wmi_install_notify_handler - Register handler for WMI events |
482 | * @handler: Function to handle notifications | 550 | * @handler: Function to handle notifications |
@@ -496,7 +564,7 @@ wmi_notify_handler handler, void *data) | |||
496 | if (!find_guid(guid, &block)) | 564 | if (!find_guid(guid, &block)) |
497 | return AE_NOT_EXIST; | 565 | return AE_NOT_EXIST; |
498 | 566 | ||
499 | if (block->handler) | 567 | if (block->handler && block->handler != wmi_notify_debug) |
500 | return AE_ALREADY_ACQUIRED; | 568 | return AE_ALREADY_ACQUIRED; |
501 | 569 | ||
502 | block->handler = handler; | 570 | block->handler = handler; |
@@ -516,7 +584,7 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); | |||
516 | acpi_status wmi_remove_notify_handler(const char *guid) | 584 | acpi_status wmi_remove_notify_handler(const char *guid) |
517 | { | 585 | { |
518 | struct wmi_block *block; | 586 | struct wmi_block *block; |
519 | acpi_status status; | 587 | acpi_status status = AE_OK; |
520 | 588 | ||
521 | if (!guid) | 589 | if (!guid) |
522 | return AE_BAD_PARAMETER; | 590 | return AE_BAD_PARAMETER; |
@@ -524,14 +592,16 @@ acpi_status wmi_remove_notify_handler(const char *guid) | |||
524 | if (!find_guid(guid, &block)) | 592 | if (!find_guid(guid, &block)) |
525 | return AE_NOT_EXIST; | 593 | return AE_NOT_EXIST; |
526 | 594 | ||
527 | if (!block->handler) | 595 | if (!block->handler || block->handler == wmi_notify_debug) |
528 | return AE_NULL_ENTRY; | 596 | return AE_NULL_ENTRY; |
529 | 597 | ||
530 | status = wmi_method_enable(block, 0); | 598 | if (debug_event) { |
531 | 599 | block->handler = wmi_notify_debug; | |
532 | block->handler = NULL; | 600 | } else { |
533 | block->handler_data = NULL; | 601 | status = wmi_method_enable(block, 0); |
534 | 602 | block->handler = NULL; | |
603 | block->handler_data = NULL; | ||
604 | } | ||
535 | return status; | 605 | return status; |
536 | } | 606 | } |
537 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); | 607 | EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); |
@@ -756,12 +826,10 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
756 | 826 | ||
757 | total = obj->buffer.length / sizeof(struct guid_block); | 827 | total = obj->buffer.length / sizeof(struct guid_block); |
758 | 828 | ||
759 | gblock = kzalloc(obj->buffer.length, GFP_KERNEL); | 829 | gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); |
760 | if (!gblock) | 830 | if (!gblock) |
761 | return AE_NO_MEMORY; | 831 | return AE_NO_MEMORY; |
762 | 832 | ||
763 | memcpy(gblock, obj->buffer.pointer, obj->buffer.length); | ||
764 | |||
765 | for (i = 0; i < total; i++) { | 833 | for (i = 0; i < total; i++) { |
766 | /* | 834 | /* |
767 | Some WMI devices, like those for nVidia hooks, have a | 835 | Some WMI devices, like those for nVidia hooks, have a |
@@ -776,12 +844,19 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
776 | guid_string); | 844 | guid_string); |
777 | continue; | 845 | continue; |
778 | } | 846 | } |
847 | if (debug_dump_wdg) | ||
848 | wmi_dump_wdg(&gblock[i]); | ||
849 | |||
779 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | 850 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); |
780 | if (!wblock) | 851 | if (!wblock) |
781 | return AE_NO_MEMORY; | 852 | return AE_NO_MEMORY; |
782 | 853 | ||
783 | wblock->gblock = gblock[i]; | 854 | wblock->gblock = gblock[i]; |
784 | wblock->handle = handle; | 855 | wblock->handle = handle; |
856 | if (debug_event) { | ||
857 | wblock->handler = wmi_notify_debug; | ||
858 | status = wmi_method_enable(wblock, 1); | ||
859 | } | ||
785 | list_add_tail(&wblock->list, &wmi_blocks.list); | 860 | list_add_tail(&wblock->list, &wmi_blocks.list); |
786 | } | 861 | } |
787 | 862 | ||
@@ -840,6 +915,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) | |||
840 | struct guid_block *block; | 915 | struct guid_block *block; |
841 | struct wmi_block *wblock; | 916 | struct wmi_block *wblock; |
842 | struct list_head *p; | 917 | struct list_head *p; |
918 | char guid_string[37]; | ||
843 | 919 | ||
844 | list_for_each(p, &wmi_blocks.list) { | 920 | list_for_each(p, &wmi_blocks.list) { |
845 | wblock = list_entry(p, struct wmi_block, list); | 921 | wblock = list_entry(p, struct wmi_block, list); |
@@ -849,6 +925,11 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) | |||
849 | (block->notify_id == event)) { | 925 | (block->notify_id == event)) { |
850 | if (wblock->handler) | 926 | if (wblock->handler) |
851 | wblock->handler(event, wblock->handler_data); | 927 | wblock->handler(event, wblock->handler_data); |
928 | if (debug_event) { | ||
929 | wmi_gtoa(wblock->gblock.guid, guid_string); | ||
930 | printk(KERN_INFO PREFIX "DEBUG Event GUID:" | ||
931 | " %s\n", guid_string); | ||
932 | } | ||
852 | 933 | ||
853 | acpi_bus_generate_netlink_event( | 934 | acpi_bus_generate_netlink_event( |
854 | device->pnp.device_class, dev_name(&device->dev), | 935 | device->pnp.device_class, dev_name(&device->dev), |