diff options
| -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), |
