From e28393c0c4416dffb46ca481e670f10c6a35baca Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:23 -0300 Subject: thinkpad-acpi: constrain IBM-era support to IBM boxes Lenovo is playing around with its ACPI BIOS, and will end up reusing method names. Their memory is not nearly as long as thinkpad-acpi's... Secure most of the old IBM codepaths against running in a non-IBM box. This would happen on the Lenovo X100e in video_init(), for example. We would misdetect it as an ancient model 570 firmware. Also, refuse to load the driver if we cannot identify the vendor. No ACPI ThinkPad in existence lacks this information, AFAIK. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 57 ++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 63290b33c879..21759dacff6c 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -480,6 +480,15 @@ static unsigned long __init tpacpi_check_quirks( return 0; } +static inline bool __pure __init tpacpi_is_lenovo(void) +{ + return thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO; +} + +static inline bool __pure __init tpacpi_is_ibm(void) +{ + return thinkpad_id.vendor == PCI_VENDOR_ID_IBM; +} /**************************************************************************** **************************************************************************** @@ -1886,7 +1895,9 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) (thinkpad_id.ec_version_str) ? thinkpad_id.ec_version_str : "unknown"); - if (thinkpad_id.vendor && thinkpad_id.model_str) + BUG_ON(!thinkpad_id.vendor); + + if (thinkpad_id.model_str) printk(TPACPI_INFO "%s %s, model %s\n", (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? "IBM" : ((thinkpad_id.vendor == @@ -3353,7 +3364,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) goto err_exit; } - if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { + if (tpacpi_is_lenovo()) { dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "using Lenovo default hot key map\n"); memcpy(hotkey_keycode_map, &lenovo_keycode_map, @@ -4422,7 +4433,8 @@ static int __init video_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n"); TPACPI_ACPIHANDLE_INIT(vid); - TPACPI_ACPIHANDLE_INIT(vid2); + if (tpacpi_is_ibm()) + TPACPI_ACPIHANDLE_INIT(vid2); if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) /* G41, assume IVGA doesn't change */ @@ -4431,10 +4443,12 @@ static int __init video_init(struct ibm_init_struct *iibm) if (!vid_handle) /* video switching not supported on R30, R31 */ video_supported = TPACPI_VIDEO_NONE; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) + else if (tpacpi_is_ibm() && + acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) /* 570 */ video_supported = TPACPI_VIDEO_570; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) + else if (tpacpi_is_ibm() && + acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) /* 600e/x, 770e, 770x */ video_supported = TPACPI_VIDEO_770; else @@ -4811,8 +4825,10 @@ static int __init light_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); - TPACPI_ACPIHANDLE_INIT(ledb); - TPACPI_ACPIHANDLE_INIT(lght); + if (tpacpi_is_ibm()) { + TPACPI_ACPIHANDLE_INIT(ledb); + TPACPI_ACPIHANDLE_INIT(lght); + } TPACPI_ACPIHANDLE_INIT(cmos); INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); @@ -5284,10 +5300,10 @@ static int __init led_init(struct ibm_init_struct *iibm) if (!led_handle) /* led not supported on R30, R31 */ led_supported = TPACPI_LED_NONE; - else if (strlencmp(led_path, "SLED") == 0) + else if (tpacpi_is_ibm() && strlencmp(led_path, "SLED") == 0) /* 570 */ led_supported = TPACPI_LED_570; - else if (strlencmp(led_path, "SYSL") == 0) + else if (tpacpi_is_ibm() && strlencmp(led_path, "SYSL") == 0) /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ led_supported = TPACPI_LED_OLD; else @@ -5741,11 +5757,12 @@ static int __init thermal_init(struct ibm_init_struct *iibm) TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; } } else if (acpi_tmp7) { - if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + if (tpacpi_is_ibm() && + acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { /* 600e/x, 770e, 770x */ thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT; } else { - /* Standard ACPI TMPx access, max 8 sensors */ + /* IBM/LENOVO DSDT EC.TMPx access, max 8 sensors */ thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; } } else { @@ -6249,7 +6266,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) } /* Safety */ - if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM && + if (!tpacpi_is_ibm() && (brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM || brightness_mode == TPACPI_BRGHT_MODE_EC)) return -EINVAL; @@ -7968,9 +7985,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) tp_features.second_fan = 0; fan_control_desired_level = 7; - TPACPI_ACPIHANDLE_INIT(fans); - TPACPI_ACPIHANDLE_INIT(gfan); - TPACPI_ACPIHANDLE_INIT(sfan); + if (tpacpi_is_ibm()) { + TPACPI_ACPIHANDLE_INIT(fans); + TPACPI_ACPIHANDLE_INIT(gfan); + TPACPI_ACPIHANDLE_INIT(sfan); + } quirks = tpacpi_check_quirks(fan_quirk_table, ARRAY_SIZE(fan_quirk_table)); @@ -8662,6 +8681,10 @@ static int __init probe_for_thinkpad(void) if (acpi_disabled) return -ENODEV; + /* It would be dangerous to run the driver in this case */ + if (!tpacpi_is_ibm() && !tpacpi_is_lenovo()) + return -ENODEV; + /* * Non-ancient models have better DMI tagging, but very old models * don't. tpacpi_is_fw_known() is a cheat to help in that case. @@ -9059,9 +9082,7 @@ static int __init thinkpad_acpi_module_init(void) tpacpi_inputdev->name = "ThinkPad Extra Buttons"; tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0"; tpacpi_inputdev->id.bustype = BUS_HOST; - tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ? - thinkpad_id.vendor : - PCI_VENDOR_ID_IBM; + tpacpi_inputdev->id.vendor = thinkpad_id.vendor; tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; -- cgit v1.2.3 From a318930d06a7a93bd50000c7112f995b459adda7 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:26 -0300 Subject: thinkpad-acpi: X100e quick fixes The X100e needs some quick fixes to work semi-right with this driver. There are much better ways to do this, but we can start with a quick update and do it properly later. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 21759dacff6c..7817cef6aae1 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -516,6 +516,7 @@ TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ "\\_SB.PCI0.ICH3.EC0", /* R31 */ + "\\_SB.PCI0.LPC0.EC", /* X100e and a few others */ "\\_SB.PCI0.LPC.EC", /* all others */ ); @@ -537,6 +538,7 @@ TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ "\\_SB.PCI0.VID0", /* 770e */ "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ + "\\_SB.PCI0.AGP.VGA", /* X100e and a few others */ "\\_SB.PCI0.AGP.VID", /* all others */ ); /* R30, R31 */ -- cgit v1.2.3 From 5d756db99a7382d5cd173e912d527e9ee73d0596 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:28 -0300 Subject: thinkpad-acpi: fix volume/mute hotkey poll handling The hotkey polling code is supposed to generate hotkey messages as close to the way the IBM event-based volume hotkey firmware does as possible, i.e: * Pressing MUTE issues a mute hotkey event, even if already mute; * Pressing Volume up/down issues a volume up/down hotkey event, even if already at maximum or minumum volume; * The act of unmuting issues a volume up/down event, depending on which hotkey was used to unmute. Fix the code to do just that (mute handling was incorrect), and handle multiple hotkey presses between two polling cycles. The new code uses the volume_toggle bit in NVRAM only to detect repeated presses of the mute key and multiple presses of the volume keys trying to go past the end of the volume scale. This will work around a bug in recent Lenovo firmware (e.g. T400), which causes the firmware to not update the volume_toggle bit in certain situations. Reported-by: Yang Zhe Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 71 +++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 7817cef6aae1..1f27b350ae01 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -122,6 +122,11 @@ enum { TP_NVRAM_POS_LEVEL_VOLUME = 0, }; +/* Misc NVRAM-related */ +enum { + TP_NVRAM_LEVEL_VOLUME_MAX = 14, +}; + /* ACPI HIDs */ #define TPACPI_ACPI_HKEY_HID "IBM0068" @@ -2418,6 +2423,21 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, tpacpi_hotkey_send_key(__scancode); \ } while (0) + void issue_volchange(const unsigned int oldvol, + const unsigned int newvol) + { + unsigned int i = oldvol; + + while (i > newvol) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); + i--; + } + while (i < newvol) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); + i++; + } + } + TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle); @@ -2427,24 +2447,47 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle); - /* handle volume */ - if (oldn->volume_toggle != newn->volume_toggle) { - if (oldn->mute != newn->mute) { + /* + * Handle volume + * + * This code is supposed to duplicate the IBM firmware behaviour: + * - Pressing MUTE issues mute hotkey message, even when already mute + * - Pressing Volume up/down issues volume up/down hotkey messages, + * even when already at maximum or minumum volume + * - The act of unmuting issues volume up/down notification, + * depending which key was used to unmute + * + * We are constrained to what the NVRAM can tell us, which is not much + * and certainly not enough if more than one volume hotkey was pressed + * since the last poll cycle. + * + * Just to make our life interesting, some newer Lenovo ThinkPads have + * bugs in the BIOS and may fail to update volume_toggle properly. + */ + if (newn->mute) { + /* muted */ + if (!oldn->mute || + oldn->volume_toggle != newn->volume_toggle || + oldn->volume_level != newn->volume_level) { + /* recently muted, or repeated mute keypress, or + * multiple presses ending in mute */ + issue_volchange(oldn->volume_level, newn->volume_level); TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); } - if (oldn->volume_level > newn->volume_level) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); - } else if (oldn->volume_level < newn->volume_level) { + } else { + /* unmute */ + if (oldn->mute) { + /* recently unmuted, issue 'unmute' keypress */ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); - } else if (oldn->mute == newn->mute) { - /* repeated key presses that didn't change state */ - if (newn->mute) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); - } else if (newn->volume_level != 0) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); - } else { + } + if (oldn->volume_level != newn->volume_level) { + issue_volchange(oldn->volume_level, newn->volume_level); + } else if (oldn->volume_toggle != newn->volume_toggle) { + /* repeated vol up/down keypress at end of scale ? */ + if (newn->volume_level == 0) TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); - } + else if (newn->volume_level >= TP_NVRAM_LEVEL_VOLUME_MAX) + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); } } -- cgit v1.2.3 From 7a43f788988ac47b21ce258197c5014b5249c9f5 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:31 -0300 Subject: thinkpad-acpi: move greeting messages out of the first subdriver (v2) Move the driver initial greetings out of the first subdriver, as we do a lot of other initialization before that point, and the initial greetings should go as soon as the driver decides that it should load. These greetings are not cosmetic, they make my life easier when users report bugs. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 56 +++++++++++++++++------------------- 1 file changed, 27 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 1f27b350ae01..a768a69d58d2 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -1888,36 +1888,9 @@ static bool __init tpacpi_is_fw_known(void) ****************************************************************************/ /************************************************************************* - * thinkpad-acpi init subdriver + * thinkpad-acpi metadata subdriver */ -static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) -{ - printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION); - printk(TPACPI_INFO "%s\n", TPACPI_URL); - - printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n", - (thinkpad_id.bios_version_str) ? - thinkpad_id.bios_version_str : "unknown", - (thinkpad_id.ec_version_str) ? - thinkpad_id.ec_version_str : "unknown"); - - BUG_ON(!thinkpad_id.vendor); - - if (thinkpad_id.model_str) - printk(TPACPI_INFO "%s %s, model %s\n", - (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? - "IBM" : ((thinkpad_id.vendor == - PCI_VENDOR_ID_LENOVO) ? - "Lenovo" : "Unknown vendor"), - thinkpad_id.model_str, - (thinkpad_id.nummodel_str) ? - thinkpad_id.nummodel_str : "unknown"); - - tpacpi_check_outdated_fw(); - return 0; -} - static int thinkpad_acpi_driver_read(struct seq_file *m) { seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC); @@ -8753,12 +8726,34 @@ static int __init probe_for_thinkpad(void) return 0; } +static void __init thinkpad_acpi_init_banner(void) +{ + printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION); + printk(TPACPI_INFO "%s\n", TPACPI_URL); + + printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n", + (thinkpad_id.bios_version_str) ? + thinkpad_id.bios_version_str : "unknown", + (thinkpad_id.ec_version_str) ? + thinkpad_id.ec_version_str : "unknown"); + + BUG_ON(!thinkpad_id.vendor); + + if (thinkpad_id.model_str) + printk(TPACPI_INFO "%s %s, model %s\n", + (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? + "IBM" : ((thinkpad_id.vendor == + PCI_VENDOR_ID_LENOVO) ? + "Lenovo" : "Unknown vendor"), + thinkpad_id.model_str, + (thinkpad_id.nummodel_str) ? + thinkpad_id.nummodel_str : "unknown"); +} /* Module init, exit, parameters */ static struct ibm_init_struct ibms_init[] __initdata = { { - .init = thinkpad_acpi_driver_init, .data = &thinkpad_acpi_driver_data, }, { @@ -9028,6 +9023,9 @@ static int __init thinkpad_acpi_module_init(void) /* Driver initialization */ + thinkpad_acpi_init_banner(); + tpacpi_check_outdated_fw(); + TPACPI_ACPIHANDLE_INIT(ecrd); TPACPI_ACPIHANDLE_INIT(ecwr); -- cgit v1.2.3 From 77775838bb76173d7a1ed28f75dfe388962aceca Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:33 -0300 Subject: thinkpad-acpi: let other subdrivers know backlight level range Extract the backlight level range size detection from the brightness subdriver, and allow the other subdrivers access to that information. This also allows us to relocate some code to a more convenient place. The moved code was largerly unmodified, except for the return type of tpacpi_check_std_acpi_brightness_support(), which now is correctly marked as returning "unsigned int", and and two cosmetic fixes to make checkpatch.pl happy. Fixes for the NVRAM polling mode for the brightness hotkeys will need this. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 251 +++++++++++++++++++---------------- 1 file changed, 140 insertions(+), 111 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a768a69d58d2..b03bf5153b0f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -304,8 +304,8 @@ static struct { u32 hotkey_tablet:1; u32 light:1; u32 light_status:1; - u32 bright_16levels:1; u32 bright_acpimode:1; + u32 bright_unkfw:1; u32 wan:1; u32 uwb:1; u32 fan_ctrl_status_undef:1; @@ -368,6 +368,9 @@ struct tpacpi_led_classdev { unsigned int led; }; +/* brightness level capabilities */ +static unsigned int bright_maxlvl; /* 0 = unknown */ + #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES static int dbg_wlswemul; static int tpacpi_wlsw_emulstate; @@ -1051,80 +1054,6 @@ static void tpacpi_disable_brightness_delay(void) "ACPI backlight control delay disabled\n"); } -static int __init tpacpi_query_bcl_levels(acpi_handle handle) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - int rc; - - if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { - obj = (union acpi_object *)buffer.pointer; - if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { - printk(TPACPI_ERR "Unknown _BCL data, " - "please report this to %s\n", TPACPI_MAIL); - rc = 0; - } else { - rc = obj->package.count; - } - } else { - return 0; - } - - kfree(buffer.pointer); - return rc; -} - -static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, - u32 lvl, void *context, void **rv) -{ - char name[ACPI_PATH_SEGMENT_LENGTH]; - struct acpi_buffer buffer = { sizeof(name), &name }; - - if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && - !strncmp("_BCL", name, sizeof(name) - 1)) { - BUG_ON(!rv || !*rv); - **(int **)rv = tpacpi_query_bcl_levels(handle); - return AE_CTRL_TERMINATE; - } else { - return AE_OK; - } -} - -/* - * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map - */ -static int __init tpacpi_check_std_acpi_brightness_support(void) -{ - int status; - int bcl_levels = 0; - void *bcl_ptr = &bcl_levels; - - if (!vid_handle) { - TPACPI_ACPIHANDLE_INIT(vid); - } - if (!vid_handle) - return 0; - - /* - * Search for a _BCL method, and execute it. This is safe on all - * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista - * BIOS in ACPI backlight control mode. We do NOT have to care - * about calling the _BCL method in an enabled video device, any - * will do for our purposes. - */ - - status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, - tpacpi_acpi_walk_find_bcl, NULL, NULL, - &bcl_ptr); - - if (ACPI_SUCCESS(status) && bcl_levels > 2) { - tp_features.bright_acpimode = 1; - return (bcl_levels - 2); - } - - return 0; -} - static void printk_deprecated_attribute(const char * const what, const char * const details) { @@ -3420,11 +3349,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) } /* Do not issue duplicate brightness change events to - * userspace */ - if (!tp_features.bright_acpimode) - /* update bright_acpimode... */ - tpacpi_check_std_acpi_brightness_support(); - + * userspace. tpacpi_detect_brightness_capabilities() must have + * been called before this point */ if (tp_features.bright_acpimode && acpi_video_backlight_support()) { printk(TPACPI_INFO "This ThinkPad has standard ACPI backlight " @@ -5989,7 +5915,7 @@ static unsigned int tpacpi_brightness_nvram_get(void) lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; - lnvram &= (tp_features.bright_16levels) ? 0x0f : 0x07; + lnvram &= bright_maxlvl; return lnvram; } @@ -6098,8 +6024,7 @@ static int brightness_set(unsigned int value) { int res; - if (value > ((tp_features.bright_16levels)? 15 : 7) || - value < 0) + if (value > bright_maxlvl || value < 0) return -EINVAL; vdbg_printk(TPACPI_DBG_BRGHT, @@ -6174,6 +6099,80 @@ static struct backlight_ops ibm_backlight_data = { /* --------------------------------------------------------------------- */ +static int __init tpacpi_query_bcl_levels(acpi_handle handle) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + int rc; + + if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { + obj = (union acpi_object *)buffer.pointer; + if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { + printk(TPACPI_ERR "Unknown _BCL data, " + "please report this to %s\n", TPACPI_MAIL); + rc = 0; + } else { + rc = obj->package.count; + } + } else { + return 0; + } + + kfree(buffer.pointer); + return rc; +} + +static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, + u32 lvl, void *context, void **rv) +{ + char name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer buffer = { sizeof(name), &name }; + + if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && + !strncmp("_BCL", name, sizeof(name) - 1)) { + BUG_ON(!rv || !*rv); + **(int **)rv = tpacpi_query_bcl_levels(handle); + return AE_CTRL_TERMINATE; + } else { + return AE_OK; + } +} + +/* + * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map + */ +static unsigned int __init tpacpi_check_std_acpi_brightness_support(void) +{ + int status; + int bcl_levels = 0; + void *bcl_ptr = &bcl_levels; + + if (!vid_handle) + TPACPI_ACPIHANDLE_INIT(vid); + + if (!vid_handle) + return 0; + + /* + * Search for a _BCL method, and execute it. This is safe on all + * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista + * BIOS in ACPI backlight control mode. We do NOT have to care + * about calling the _BCL method in an enabled video device, any + * will do for our purposes. + */ + + status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, + tpacpi_acpi_walk_find_bcl, NULL, NULL, + &bcl_ptr); + + if (ACPI_SUCCESS(status) && bcl_levels > 2) { + tp_features.bright_acpimode = 1; + return bcl_levels - 2; + } + + return 0; +} + /* * These are only useful for models that have only one possibility * of GPU. If the BIOS model handles both ATI and Intel, don't use @@ -6204,6 +6203,47 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { TPACPI_Q_IBM('7', '5', TPACPI_BRGHT_Q_NOEC), /* X41 Tablet */ }; +/* + * Returns < 0 for error, otherwise sets tp_features.bright_* + * and bright_maxlvl. + */ +static void __init tpacpi_detect_brightness_capabilities(void) +{ + unsigned int b; + + vdbg_printk(TPACPI_DBG_INIT, + "detecting firmware brightness interface capabilities\n"); + + /* we could run a quirks check here (same table used by + * brightness_init) if needed */ + + /* + * We always attempt to detect acpi support, so as to switch + * Lenovo Vista BIOS to ACPI brightness mode even if we are not + * going to publish a backlight interface + */ + b = tpacpi_check_std_acpi_brightness_support(); + switch (b) { + case 16: + bright_maxlvl = 15; + printk(TPACPI_INFO + "detected a 16-level brightness capable ThinkPad\n"); + break; + case 8: + case 0: + bright_maxlvl = 7; + printk(TPACPI_INFO + "detected a 8-level brightness capable ThinkPad\n"); + break; + default: + printk(TPACPI_ERR + "Unsupported brightness interface, " + "please contact %s\n", TPACPI_MAIL); + tp_features.bright_unkfw = 1; + bright_maxlvl = b - 1; + } +} + static int __init brightness_init(struct ibm_init_struct *iibm) { struct backlight_properties props; @@ -6217,14 +6257,13 @@ static int __init brightness_init(struct ibm_init_struct *iibm) quirks = tpacpi_check_quirks(brightness_quirk_table, ARRAY_SIZE(brightness_quirk_table)); - /* - * We always attempt to detect acpi support, so as to switch - * Lenovo Vista BIOS to ACPI brightness mode even if we are not - * going to publish a backlight interface - */ - b = tpacpi_check_std_acpi_brightness_support(); - if (b > 0) { + /* tpacpi_detect_brightness_capabilities() must have run already */ + + /* if it is unknown, we don't handle it: it wouldn't be safe */ + if (tp_features.bright_unkfw) + return 1; + if (tp_features.bright_acpimode) { if (acpi_video_backlight_support()) { if (brightness_enable > 1) { printk(TPACPI_NOTICE @@ -6253,15 +6292,6 @@ static int __init brightness_init(struct ibm_init_struct *iibm) return 1; } - if (b > 16) { - printk(TPACPI_ERR - "Unsupported brightness interface, " - "please contact %s\n", TPACPI_MAIL); - return 1; - } - if (b == 16) - tp_features.bright_16levels = 1; - /* * Check for module parameter bogosity, note that we * init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be @@ -6292,12 +6322,9 @@ static int __init brightness_init(struct ibm_init_struct *iibm) if (tpacpi_brightness_get_raw(&b) < 0) return 1; - if (tp_features.bright_16levels) - printk(TPACPI_INFO - "detected a 16-level brightness capable ThinkPad\n"); - memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = (tp_features.bright_16levels) ? 15 : 7; + props.max_brightness = bright_maxlvl; + props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; ibm_backlight_device = backlight_device_register(TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, &ibm_backlight_data, @@ -6320,7 +6347,6 @@ static int __init brightness_init(struct ibm_init_struct *iibm) "or not on your ThinkPad\n", TPACPI_MAIL); } - ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; backlight_update_status(ibm_backlight_device); vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, @@ -6363,9 +6389,8 @@ static int brightness_read(struct seq_file *m) } else { seq_printf(m, "level:\t\t%d\n", level); seq_printf(m, "commands:\tup, down\n"); - seq_printf(m, "commands:\tlevel " - " ( is 0-%d)\n", - (tp_features.bright_16levels) ? 15 : 7); + seq_printf(m, "commands:\tlevel ( is 0-%d)\n", + bright_maxlvl); } return 0; @@ -6376,7 +6401,6 @@ static int brightness_write(char *buf) int level; int rc; char *cmd; - int max_level = (tp_features.bright_16levels) ? 15 : 7; level = brightness_get(NULL); if (level < 0) @@ -6384,13 +6408,13 @@ static int brightness_write(char *buf) while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "up") == 0) { - if (level < max_level) + if (level < bright_maxlvl) level++; } else if (strlencmp(cmd, "down") == 0) { if (level > 0) level--; } else if (sscanf(cmd, "level %d", &level) == 1 && - level >= 0 && level <= max_level) { + level >= 0 && level <= bright_maxlvl) { /* new level set */ } else return -EINVAL; @@ -9130,6 +9154,11 @@ static int __init thinkpad_acpi_module_init(void) tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; } + + /* Init subdriver dependencies */ + tpacpi_detect_brightness_capabilities(); + + /* Init subdrivers */ for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { ret = ibm_init(&ibms_init[i]); if (ret >= 0 && *ibms_init[i].param) -- cgit v1.2.3 From 289990228155cbc58a35c1b266af00f387caa595 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:36 -0300 Subject: thinkpad-acpi: fix brightness hotkey poll handling Handle multiple brightness hotkey presses between two polling cycles. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index b03bf5153b0f..fa412a43f5e0 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -2340,6 +2340,21 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, } } + void issue_brightnesschange(const unsigned int oldbrt, + const unsigned int newbrt) + { + unsigned int i = oldbrt; + + while (i > newbrt) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); + i--; + } + while (i < newbrt) { + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); + i++; + } + } + TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle); @@ -2394,19 +2409,16 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, } /* handle brightness */ - if (oldn->brightness_toggle != newn->brightness_toggle) { - if (oldn->brightness_level < newn->brightness_level) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); - } else if (oldn->brightness_level > newn->brightness_level) { + if (oldn->brightness_level != newn->brightness_level) { + issue_brightnesschange(oldn->brightness_level, + newn->brightness_level); + } else if (oldn->brightness_toggle != newn->brightness_toggle) { + /* repeated key presses that didn't change state */ + if (newn->brightness_level == 0) TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); - } else { - /* repeated key presses that didn't change state */ - if (newn->brightness_level != 0) { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); - } else { - TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); - } - } + else if (newn->brightness_level >= bright_maxlvl + && !tp_features.bright_unkfw) + TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); } #undef TPACPI_COMPARE_KEY -- cgit v1.2.3 From 38e11cdec90f1dd7355db4aed8a1857258e99485 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:40 -0300 Subject: thinkpad-acpi: disclose usertask for ALSA callbacks Disclose the user task doing ALSA access when requested by the debug bitmask. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index fa412a43f5e0..b03cb3e04582 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6740,6 +6740,8 @@ static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol, static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + tpacpi_disclose_usertask("ALSA", "set volume to %ld\n", + ucontrol->value.integer.value[0]); return volume_alsa_set_volume(ucontrol->value.integer.value[0]); } @@ -6763,6 +6765,9 @@ static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol, static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + tpacpi_disclose_usertask("ALSA", "%smute\n", + ucontrol->value.integer.value[0] ? + "un" : ""); return volume_alsa_set_mute(!ucontrol->value.integer.value[0]); } -- cgit v1.2.3 From 437e470c4ca818c97426afa3a67fbd7e34cffe00 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:43 -0300 Subject: thinkpad-acpi: detect EC node using its HID (v2) Use the EC HID (PNP0C09) to locate its main node, instead of a static list. Suggested-by: Matthew Garrett Signed-off-by: Henrique de Moraes Holschuh Cc: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 53 ++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index b03cb3e04582..877a7802e830 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -129,6 +129,7 @@ enum { /* ACPI HIDs */ #define TPACPI_ACPI_HKEY_HID "IBM0068" +#define TPACPI_ACPI_EC_HID "PNP0C09" /* Input IDs */ #define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ @@ -511,6 +512,7 @@ static inline bool __pure __init tpacpi_is_ibm(void) */ static acpi_handle root_handle; +static acpi_handle ec_handle; #define TPACPI_HANDLE(object, parent, paths...) \ static acpi_handle object##_handle; \ @@ -518,16 +520,6 @@ static acpi_handle root_handle; static char *object##_path; \ static char *object##_paths[] = { paths } -TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ - "\\_SB.PCI.ISA.EC", /* 570 */ - "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ - "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ - "\\_SB.PCI0.ICH3.EC0", /* R31 */ - "\\_SB.PCI0.LPC0.EC", /* X100e and a few others */ - "\\_SB.PCI0.LPC.EC", /* all others */ - ); - TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */ TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */ @@ -708,6 +700,43 @@ static void drv_acpi_handle_init(char *name, *handle = NULL; } +static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle, + u32 level, void *context, void **return_value) +{ + *(acpi_handle *)return_value = handle; + + return AE_CTRL_TERMINATE; +} + +static void __init tpacpi_acpi_handle_locate(const char *name, + const char *hid, + acpi_handle *handle) +{ + acpi_status status; + acpi_handle device_found; + + BUG_ON(!name || !hid || !handle); + vdbg_printk(TPACPI_DBG_INIT, + "trying to locate ACPI handle for %s, using HID %s\n", + name, hid); + + memset(&device_found, 0, sizeof(device_found)); + status = acpi_get_devices(hid, tpacpi_acpi_handle_locate_callback, + (void *)name, &device_found); + + *handle = NULL; + + if (ACPI_SUCCESS(status)) { + *handle = device_found; + dbg_printk(TPACPI_DBG_INIT, + "Found ACPI handle for %s\n", name); + } else { + vdbg_printk(TPACPI_DBG_INIT, + "Could not locate an ACPI handle for %s: %s\n", + name, acpi_format_exception(status)); + } +} + static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) { struct ibm_struct *ibm = data; @@ -8752,8 +8781,8 @@ static int __init probe_for_thinkpad(void) (thinkpad_id.ec_model != 0) || tpacpi_is_fw_known(); - /* ec is required because many other handles are relative to it */ - TPACPI_ACPIHANDLE_INIT(ec); + /* The EC handler is required */ + tpacpi_acpi_handle_locate("ec", TPACPI_ACPI_EC_HID, &ec_handle); if (!ec_handle) { if (is_thinkpad) printk(TPACPI_ERR -- cgit v1.2.3 From 263f4a30e4f1dc5385650738c1dcf3728036ecc4 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:45 -0300 Subject: thinkpad-acpi: acpi_evalf fixes Use acpi_format_exception() in acpi_evalf() instead of logging numeric errors. Also, when ACPICA returns an error, we should not be touching the return object, as it is invalid. In debug mode, acpi_evalf() callers would printk the returned crap (but fortunately, not use it). Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 877a7802e830..da3a70b280f2 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -605,9 +605,10 @@ static int acpi_evalf(acpi_handle handle, switch (res_type) { case 'd': /* int */ - if (res) + success = (status == AE_OK && + out_obj.type == ACPI_TYPE_INTEGER); + if (success && res) *(int *)res = out_obj.integer.value; - success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; break; case 'v': /* void */ success = status == AE_OK; @@ -620,8 +621,8 @@ static int acpi_evalf(acpi_handle handle, } if (!success && !quiet) - printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", - method, fmt0, status); + printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %s\n", + method, fmt0, acpi_format_exception(status)); return success; } -- cgit v1.2.3 From 72f19921217c2267adc65cbe69c63da970578a14 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:48 -0300 Subject: thinkpad-acpi: explain errors from acpi_install_notify_handler Log more human-friendly errors instead of numeric values when setup_acpi_notify() fails to install a notification handler. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index da3a70b280f2..a39f159d4f2f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -785,8 +785,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) "handling %s events\n", ibm->name); } else { printk(TPACPI_ERR - "acpi_install_notify_handler(%s) failed: %d\n", - ibm->name, status); + "acpi_install_notify_handler(%s) failed: %s\n", + ibm->name, acpi_format_exception(status)); } return -ENODEV; } -- cgit v1.2.3 From 2cbb5c8f5533facb606adc5986ce40da2e987d6d Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:50 -0300 Subject: thinkpad-acpi: don't depend on led_path for led firmware type (v2) Don't depend on the contents of led_path to know which LED interface the firmware wants. This removes the only user of *_path for the thinkpad-acpi ACPI handlers, which will simplify future code. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 47 +++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a39f159d4f2f..29ad27346443 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -5009,11 +5009,7 @@ enum { /* For TPACPI_LED_OLD */ static enum led_access_mode led_supported; -TPACPI_HANDLE(led, ec, "SLED", /* 570 */ - "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, */ - /* T20-22, X20-21 */ - "LED", /* all others */ - ); /* R30, R31 */ +static acpi_handle led_handle; #define TPACPI_LED_NUMLEDS 16 static struct tpacpi_led_classdev *tpacpi_leds; @@ -5273,6 +5269,32 @@ static const struct tpacpi_quirk led_useful_qtable[] __initconst = { #undef TPACPI_LEDQ_IBM #undef TPACPI_LEDQ_LNV +static enum led_access_mode __init led_init_detect_mode(void) +{ + acpi_status status; + + if (tpacpi_is_ibm()) { + /* 570 */ + status = acpi_get_handle(ec_handle, "SLED", &led_handle); + if (ACPI_SUCCESS(status)) + return TPACPI_LED_570; + + /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + status = acpi_get_handle(ec_handle, "SYSL", &led_handle); + if (ACPI_SUCCESS(status)) + return TPACPI_LED_OLD; + } + + /* most others */ + status = acpi_get_handle(ec_handle, "LED", &led_handle); + if (ACPI_SUCCESS(status)) + return TPACPI_LED_NEW; + + /* R30, R31, and unknown firmwares */ + led_handle = NULL; + return TPACPI_LED_NONE; +} + static int __init led_init(struct ibm_init_struct *iibm) { unsigned int i; @@ -5281,20 +5303,7 @@ static int __init led_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); - TPACPI_ACPIHANDLE_INIT(led); - - if (!led_handle) - /* led not supported on R30, R31 */ - led_supported = TPACPI_LED_NONE; - else if (tpacpi_is_ibm() && strlencmp(led_path, "SLED") == 0) - /* 570 */ - led_supported = TPACPI_LED_570; - else if (tpacpi_is_ibm() && strlencmp(led_path, "SYSL") == 0) - /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - led_supported = TPACPI_LED_OLD; - else - /* all others */ - led_supported = TPACPI_LED_NEW; + led_supported = led_init_detect_mode(); vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", str_supported(led_supported), led_supported); -- cgit v1.2.3 From ef07a5abadfcb2470fc9cbfbee0cb41076b4ba9b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:54 -0300 Subject: thinkpad-acpi: clean up ACPI handles handling 1. Remove _path, as its only user was already removed in a previous commit 2. Move all handle initialization, as well as _parent and _paths to __init.* sections. This reduces the driver's runtime footprint nicely. Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 29ad27346443..b3bfce953838 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -516,9 +516,9 @@ static acpi_handle ec_handle; #define TPACPI_HANDLE(object, parent, paths...) \ static acpi_handle object##_handle; \ - static acpi_handle *object##_parent = &parent##_handle; \ - static char *object##_path; \ - static char *object##_paths[] = { paths } + static const acpi_handle *object##_parent __initdata = \ + &parent##_handle; \ + static char *object##_paths[] __initdata = { paths } TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */ TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */ @@ -673,11 +673,11 @@ static int issue_thinkpad_cmos_command(int cmos_cmd) #define TPACPI_ACPIHANDLE_INIT(object) \ drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \ - object##_paths, ARRAY_SIZE(object##_paths), &object##_path) + object##_paths, ARRAY_SIZE(object##_paths)) -static void drv_acpi_handle_init(char *name, - acpi_handle *handle, acpi_handle parent, - char **paths, int num_paths, char **path) +static void __init drv_acpi_handle_init(const char *name, + acpi_handle *handle, const acpi_handle parent, + char **paths, const int num_paths) { int i; acpi_status status; @@ -688,10 +688,9 @@ static void drv_acpi_handle_init(char *name, for (i = 0; i < num_paths; i++) { status = acpi_get_handle(parent, paths[i], handle); if (ACPI_SUCCESS(status)) { - *path = paths[i]; dbg_printk(TPACPI_DBG_INIT, "Found ACPI handle %s for %s\n", - *path, name); + paths[i], name); return; } } -- cgit v1.2.3 From 7d9745cf239ca98cf1f694bff4765a276b05ee68 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sun, 16 May 2010 19:45:57 -0300 Subject: thinkpad-acpi: document backlight level writeback at driver init Document this, it is no fun to try to second guess why this sort of stuff is in place years after it was added... Signed-off-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index b3bfce953838..4bdb13796e24 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6397,6 +6397,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm) "or not on your ThinkPad\n", TPACPI_MAIL); } + /* Added by mistake in early 2007. Probably useless, but it could + * be working around some unknown firmware problem where the value + * read at startup doesn't match the real hardware state... so leave + * it in place just in case */ backlight_update_status(ibm_backlight_device); vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, -- cgit v1.2.3 From d5c051f1080e0eec55f3fc42c37d941681941628 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 27 Apr 2010 13:15:00 -0300 Subject: classmate-laptop: Add RFKILL support. The RFKILL device shares the same ACPI device used for backlight. So, it required a new struct sharing both a backlight_device and a rfkill device. Signed-off-by: Thadeu Lima de Souza Cascardo --- drivers/platform/x86/classmate-laptop.c | 170 +++++++++++++++++++++++++++----- 1 file changed, 148 insertions(+), 22 deletions(-) (limited to 'drivers') 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 @@ #include #include #include +#include MODULE_LICENSE("GPL"); @@ -37,7 +38,7 @@ struct cmpc_accel { #define CMPC_ACCEL_HID "ACCE0000" #define CMPC_TABLET_HID "TBLT0000" -#define CMPC_BL_HID "IPML200" +#define CMPC_IPML_HID "IPML200" #define CMPC_KEYS_HID "FnBT0000" /* @@ -461,43 +462,168 @@ static const struct backlight_ops cmpc_bl_ops = { .update_status = cmpc_bl_update_status }; -static int cmpc_bl_add(struct acpi_device *acpi) +/* + * RFKILL code. + */ + +static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle, + unsigned long long *value) { - struct backlight_properties props; + union acpi_object param; + struct acpi_object_list input; + unsigned long long output; + acpi_status status; + + param.type = ACPI_TYPE_INTEGER; + param.integer.value = 0xC1; + input.count = 1; + input.pointer = ¶m; + status = acpi_evaluate_integer(handle, "GRDI", &input, &output); + if (ACPI_SUCCESS(status)) + *value = output; + return status; +} + +static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle, + unsigned long long value) +{ + union acpi_object param[2]; + struct acpi_object_list input; + acpi_status status; + unsigned long long output; + + param[0].type = ACPI_TYPE_INTEGER; + param[0].integer.value = 0xC1; + param[1].type = ACPI_TYPE_INTEGER; + param[1].integer.value = value; + input.count = 2; + input.pointer = param; + status = acpi_evaluate_integer(handle, "GWRI", &input, &output); + return status; +} + +static void cmpc_rfkill_query(struct rfkill *rfkill, void *data) +{ + acpi_status status; + acpi_handle handle; + unsigned long long state; + bool blocked; + + handle = data; + status = cmpc_get_rfkill_wlan(handle, &state); + if (ACPI_SUCCESS(status)) { + blocked = state & 1 ? false : true; + rfkill_set_sw_state(rfkill, blocked); + } +} + +static int cmpc_rfkill_block(void *data, bool blocked) +{ + acpi_status status; + acpi_handle handle; + unsigned long long state; + + handle = data; + status = cmpc_get_rfkill_wlan(handle, &state); + if (ACPI_FAILURE(status)) + return -ENODEV; + if (blocked) + state &= ~1; + else + state |= 1; + status = cmpc_set_rfkill_wlan(handle, state); + if (ACPI_FAILURE(status)) + return -ENODEV; + return 0; +} + +static const struct rfkill_ops cmpc_rfkill_ops = { + .query = cmpc_rfkill_query, + .set_block = cmpc_rfkill_block, +}; + +/* + * Common backlight and rfkill code. + */ + +struct ipml200_dev { struct backlight_device *bd; + struct rfkill *rf; +}; + +static int cmpc_ipml_add(struct acpi_device *acpi) +{ + int retval; + struct ipml200_dev *ipml; + struct backlight_properties props; + + ipml = kmalloc(sizeof(*ipml), GFP_KERNEL); + if (ipml == NULL) + return -ENOMEM; memset(&props, 0, sizeof(struct backlight_properties)); props.max_brightness = 7; - bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle, - &cmpc_bl_ops, &props); - if (IS_ERR(bd)) - return PTR_ERR(bd); - dev_set_drvdata(&acpi->dev, bd); + ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, + acpi->handle, &cmpc_bl_ops, + &props); + if (IS_ERR(ipml->bd)) { + retval = PTR_ERR(ipml->bd); + goto out_bd; + } + + ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, + &cmpc_rfkill_ops, acpi->handle); + /* rfkill_alloc may fail if RFKILL is disabled. We should still work + * anyway. */ + if (!IS_ERR(ipml->rf)) { + retval = rfkill_register(ipml->rf); + if (retval) { + rfkill_destroy(ipml->rf); + ipml->rf = NULL; + } + } else { + ipml->rf = NULL; + } + + dev_set_drvdata(&acpi->dev, ipml); return 0; + +out_bd: + kfree(ipml); + return retval; } -static int cmpc_bl_remove(struct acpi_device *acpi, int type) +static int cmpc_ipml_remove(struct acpi_device *acpi, int type) { - struct backlight_device *bd; + struct ipml200_dev *ipml; + + ipml = dev_get_drvdata(&acpi->dev); + + backlight_device_unregister(ipml->bd); + + if (ipml->rf) { + rfkill_unregister(ipml->rf); + rfkill_destroy(ipml->rf); + } + + kfree(ipml); - bd = dev_get_drvdata(&acpi->dev); - backlight_device_unregister(bd); return 0; } -static const struct acpi_device_id cmpc_bl_device_ids[] = { - {CMPC_BL_HID, 0}, +static const struct acpi_device_id cmpc_ipml_device_ids[] = { + {CMPC_IPML_HID, 0}, {"", 0} }; -static struct acpi_driver cmpc_bl_acpi_driver = { +static struct acpi_driver cmpc_ipml_acpi_driver = { .owner = THIS_MODULE, .name = "cmpc", .class = "cmpc", - .ids = cmpc_bl_device_ids, + .ids = cmpc_ipml_device_ids, .ops = { - .add = cmpc_bl_add, - .remove = cmpc_bl_remove + .add = cmpc_ipml_add, + .remove = cmpc_ipml_remove } }; @@ -580,7 +706,7 @@ static int cmpc_init(void) if (r) goto failed_keys; - r = acpi_bus_register_driver(&cmpc_bl_acpi_driver); + r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); if (r) goto failed_bl; @@ -598,7 +724,7 @@ failed_accel: acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); failed_tablet: - acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); + acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); failed_bl: acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); @@ -611,7 +737,7 @@ static void cmpc_exit(void) { acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); - acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); + acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); } @@ -621,7 +747,7 @@ module_exit(cmpc_exit); static const struct acpi_device_id cmpc_device_ids[] = { {CMPC_ACCEL_HID, 0}, {CMPC_TABLET_HID, 0}, - {CMPC_BL_HID, 0}, + {CMPC_IPML_HID, 0}, {CMPC_KEYS_HID, 0}, {"", 0} }; -- cgit v1.2.3 From 9a58a3333923c7fef4ba6ac9afd817429e63a1fe Mon Sep 17 00:00:00 2001 From: Sreedhara DS Date: Mon, 26 Apr 2010 18:13:05 +0100 Subject: IPC driver for Intel Mobile Internet Device (MID) platforms The IPC (inter processor communications) is used to provide the communications between kernel and system control units on some embedded Intel x86 platforms. (Various bits of clean up and restructuring by Alan Cox) Signed-off-by: Sreedhara DS Signed-off-by: Alan Cox --- drivers/platform/x86/Kconfig | 8 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_scu_ipc.c | 829 +++++++++++++++++++++++++++++++++++ 3 files changed, 838 insertions(+) create mode 100644 drivers/platform/x86/intel_scu_ipc.c (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 6c3320d75055..619134151ca6 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -527,4 +527,12 @@ config ACPI_CMPC keys as input device, backlight device, tablet and accelerometer devices. +config INTEL_SCU_IPC + bool "Intel SCU IPC Support" + depends on X86_MRST + default y + ---help--- + IPC is used to bridge the communications between kernel and SCU on + some embedded Intel x86 platforms. + 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 obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o +obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o 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 @@ +/* + * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism + * + * (C) Copyright 2008-2010 Intel Corporation + * Author: Sreedhara DS (sreedhara.ds@intel.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + * + * SCU runing in ARC processor communicates with other entity running in IA + * core through IPC mechanism which in turn messaging between IA core ad SCU. + * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and + * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with + * IPC-1 Driver provides an API for power control unit registers (e.g. MSIC) + * along with other APIs. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* IPC defines the following message types */ +#define IPCMSG_WATCHDOG_TIMER 0xF8 /* Set Kernel Watchdog Threshold */ +#define IPCMSG_BATTERY 0xEF /* Coulomb Counter Accumulator */ +#define IPCMSG_FW_UPDATE 0xFE /* Firmware update */ +#define IPCMSG_PCNTRL 0xFF /* Power controller unit read/write */ +#define IPCMSG_FW_REVISION 0xF4 /* Get firmware revision */ + +/* Command id associated with message IPCMSG_PCNTRL */ +#define IPC_CMD_PCNTRL_W 0 /* Register write */ +#define IPC_CMD_PCNTRL_R 1 /* Register read */ +#define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */ + +/* Miscelaneous Command ids */ +#define IPC_CMD_INDIRECT_RD 2 /* 32bit indirect read */ +#define IPC_CMD_INDIRECT_WR 5 /* 32bit indirect write */ + +/* + * IPC register summary + * + * IPC register blocks are memory mapped at fixed address of 0xFF11C000 + * To read or write information to the SCU, driver writes to IPC-1 memory + * mapped registers (base address 0xFF11C000). The following is the IPC + * mechanism + * + * 1. IA core cDMI interface claims this transaction and converts it to a + * Transaction Layer Packet (TLP) message which is sent across the cDMI. + * + * 2. South Complex cDMI block receives this message and writes it to + * the IPC-1 register block, causing an interrupt to the SCU + * + * 3. SCU firmware decodes this interrupt and IPC message and the appropriate + * message handler is called within firmware. + */ + +#define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */ +#define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */ +#define IPC_WWBUF_SIZE 16 /* IPC Write buffer Size */ +#define IPC_RWBUF_SIZE 16 /* IPC Read buffer Size */ +#define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ +#define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */ + +static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id); +static void ipc_remove(struct pci_dev *pdev); + +struct intel_scu_ipc_dev { + struct pci_dev *pdev; + void __iomem *ipc_base; + void __iomem *i2c_base; +}; + +static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ + +static int platform = 1; +module_param(platform, int, 0); +MODULE_PARM_DESC(platform, "1 for moorestown platform"); + + + + +/* + * IPC Read Buffer (Read Only): + * 16 byte buffer for receiving data from SCU, if IPC command + * processing results in response data + */ +#define IPC_READ_BUFFER 0x90 + +#define IPC_I2C_CNTRL_ADDR 0 +#define I2C_DATA_ADDR 0x04 + +static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ + +/* + * Command Register (Write Only): + * A write to this register results in an interrupt to the SCU core processor + * Format: + * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)| + */ +static inline void ipc_command(u32 cmd) /* Send ipc command */ +{ + writel(cmd, ipcdev.ipc_base); +} + +/* + * IPC Write Buffer (Write Only): + * 16-byte buffer for sending data associated with IPC command to + * SCU. Size of the data is specified in the IPC_COMMAND_REG register + */ +static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */ +{ + writel(data, ipcdev.ipc_base + 0x80 + offset); +} + +/* + * IPC destination Pointer (Write Only): + * Use content as pointer for destination write + */ +static inline void ipc_write_dptr(u32 data) /* Write dptr data */ +{ + writel(data, ipcdev.ipc_base + 0x0C); +} + +/* + * IPC Source Pointer (Write Only): + * Use content as pointer for read location +*/ +static inline void ipc_write_sptr(u32 data) /* Write dptr data */ +{ + writel(data, ipcdev.ipc_base + 0x08); +} + +/* + * Status Register (Read Only): + * Driver will read this register to get the ready/busy status of the IPC + * block and error status of the IPC command that was just processed by SCU + * Format: + * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)| + */ + +static inline u8 ipc_read_status(void) +{ + return __raw_readl(ipcdev.ipc_base + 0x04); +} + +static inline u8 ipc_data_readb(u32 offset) /* Read ipc byte data */ +{ + return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset); +} + +static inline u8 ipc_data_readl(u32 offset) /* Read ipc u32 data */ +{ + return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); +} + +static inline int busy_loop(void) /* Wait till scu status is busy */ +{ + u32 status = 0; + u32 loop_count = 0; + + status = ipc_read_status(); + while (status & 1) { + udelay(1); /* scu processing time is in few u secods */ + status = ipc_read_status(); + loop_count++; + /* break if scu doesn't reset busy bit after huge retry */ + if (loop_count > 100000) { + dev_err(&ipcdev.pdev->dev, "IPC timed out"); + return -ETIMEDOUT; + } + } + return (status >> 1) & 1; +} + +/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ +static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) +{ + int nc; + u32 offset = 0; + u32 err = 0; + u8 cbuf[IPC_WWBUF_SIZE] = { '\0' }; + u32 *wbuf = (u32 *)&cbuf; + + mutex_lock(&ipclock); + if (ipcdev.pdev == NULL) { + mutex_unlock(&ipclock); + return -ENODEV; + } + + if (platform == 1) { + /* Entry is 4 bytes for read/write, 5 bytes for read modify */ + for (nc = 0; nc < count; nc++) { + cbuf[offset] = addr[nc]; + cbuf[offset + 1] = addr[nc] >> 8; + if (id != IPC_CMD_PCNTRL_R) + cbuf[offset + 2] = data[nc]; + if (id == IPC_CMD_PCNTRL_M) { + cbuf[offset + 3] = data[nc + 1]; + offset += 1; + } + offset += 3; + } + for (nc = 0, offset = 0; nc < count; nc++, offset += 4) + ipc_data_writel(wbuf[nc], offset); /* Write wbuff */ + + } else { + for (nc = 0, offset = 0; nc < count; nc++, offset += 2) + ipc_data_writel(addr[nc], offset); /* Write addresses */ + if (id != IPC_CMD_PCNTRL_R) { + for (nc = 0; nc < count; nc++, offset++) + ipc_data_writel(data[nc], offset); /* Write data */ + if (id == IPC_CMD_PCNTRL_M) + ipc_data_writel(data[nc + 1], offset); /* Mask value*/ + } + } + + if (id != IPC_CMD_PCNTRL_M) + ipc_command((count * 3) << 16 | id << 12 | 0 << 8 | op); + else + ipc_command((count * 4) << 16 | id << 12 | 0 << 8 | op); + + err = busy_loop(); + + if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ + /* Workaround: values are read as 0 without memcpy_fromio */ + memcpy_fromio(cbuf, ipcdev.ipc_base + IPC_READ_BUFFER, 16); + if (platform == 1) { + for (nc = 0, offset = 2; nc < count; nc++, offset += 3) + data[nc] = ipc_data_readb(offset); + } else { + for (nc = 0; nc < count; nc++) + data[nc] = ipc_data_readb(nc); + } + } + mutex_unlock(&ipclock); + return err; +} + +/** + * intel_scu_ipc_ioread8 - read a word via the SCU + * @addr: register on SCU + * @data: return pointer for read byte + * + * Read a single register. Returns 0 on success or an error code. All + * locking between SCU accesses is handled for the caller. + * + * This function may sleep. + */ +int intel_scu_ipc_ioread8(u16 addr, u8 *data) +{ + return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); +} +EXPORT_SYMBOL(intel_scu_ipc_ioread8); + +/** + * intel_scu_ipc_ioread16 - read a word via the SCU + * @addr: register on SCU + * @data: return pointer for read word + * + * Read a register pair. Returns 0 on success or an error code. All + * locking between SCU accesses is handled for the caller. + * + * This function may sleep. + */ +int intel_scu_ipc_ioread16(u16 addr, u16 *data) +{ + u16 x[2] = {addr, addr + 1 }; + return pwr_reg_rdwr(x, (u8 *)data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); +} +EXPORT_SYMBOL(intel_scu_ipc_ioread16); + +/** + * intel_scu_ipc_ioread32 - read a dword via the SCU + * @addr: register on SCU + * @data: return pointer for read dword + * + * Read four registers. Returns 0 on success or an error code. All + * locking between SCU accesses is handled for the caller. + * + * This function may sleep. + */ +int intel_scu_ipc_ioread32(u16 addr, u32 *data) +{ + u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; + return pwr_reg_rdwr(x, (u8 *)data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); +} +EXPORT_SYMBOL(intel_scu_ipc_ioread32); + +/** + * intel_scu_ipc_iowrite8 - write a byte via the SCU + * @addr: register on SCU + * @data: byte to write + * + * Write a single register. Returns 0 on success or an error code. All + * locking between SCU accesses is handled for the caller. + * + * This function may sleep. + */ +int intel_scu_ipc_iowrite8(u16 addr, u8 data) +{ + return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); +} +EXPORT_SYMBOL(intel_scu_ipc_iowrite8); + +/** + * intel_scu_ipc_iowrite16 - write a word via the SCU + * @addr: register on SCU + * @data: word to write + * + * Write two registers. Returns 0 on success or an error code. All + * locking between SCU accesses is handled for the caller. + * + * This function may sleep. + */ +int intel_scu_ipc_iowrite16(u16 addr, u16 data) +{ + u16 x[2] = {addr, addr + 1 }; + return pwr_reg_rdwr(x, (u8 *)&data, 2, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); +} +EXPORT_SYMBOL(intel_scu_ipc_iowrite16); + +/** + * intel_scu_ipc_iowrite32 - write a dword via the SCU + * @addr: register on SCU + * @data: dword to write + * + * Write four registers. Returns 0 on success or an error code. All + * locking between SCU accesses is handled for the caller. + * + * This function may sleep. + */ +int intel_scu_ipc_iowrite32(u16 addr, u32 data) +{ + u16 x[4] = {addr, addr + 1, addr + 2, addr + 3}; + return pwr_reg_rdwr(x, (u8 *)&data, 4, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); +} +EXPORT_SYMBOL(intel_scu_ipc_iowrite32); + +/** + * intel_scu_ipc_readvv - read a set of registers + * @addr: register list + * @data: bytes to return + * @len: length of array + * + * Read registers. Returns 0 on success or an error code. All + * locking between SCU accesses is handled for the caller. + * + * The largest array length permitted by the hardware is 5 items. + * + * This function may sleep. + */ +int intel_scu_ipc_readv(u16 *addr, u8 *data, int len) +{ + return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R); +} +EXPORT_SYMBOL(intel_scu_ipc_readv); + +/** + * intel_scu_ipc_writev - write a set of registers + * @addr: register list + * @data: bytes to write + * @len: length of array + * + * Write registers. Returns 0 on success or an error code. All + * locking between SCU accesses is handled for the caller. + * + * The largest array length permitted by the hardware is 5 items. + * + * This function may sleep. + * + */ +int intel_scu_ipc_writev(u16 *addr, u8 *data, int len) +{ + return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W); +} +EXPORT_SYMBOL(intel_scu_ipc_writev); + + +/** + * intel_scu_ipc_update_register - r/m/w a register + * @addr: register address + * @bits: bits to update + * @mask: mask of bits to update + * + * Read-modify-write power control unit register. The first data argument + * must be register value and second is mask value + * mask is a bitmap that indicates which bits to update. + * 0 = masked. Don't modify this bit, 1 = modify this bit. + * returns 0 on success or an error code. + * + * This function may sleep. Locking between SCU accesses is handled + * for the caller. + */ +int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask) +{ + u8 data[2] = { bits, mask }; + return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M); +} +EXPORT_SYMBOL(intel_scu_ipc_update_register); + +/** + * intel_scu_ipc_register_read - 32bit indirect read + * @addr: register address + * @value: 32bit value return + * + * Performs IA 32 bit indirect read, returns 0 on success, or an + * error code. + * + * Can be used when SCCB(System Controller Configuration Block) register + * HRIM(Honor Restricted IPC Messages) is set (bit 23) + * + * This function may sleep. Locking for SCU accesses is handled for + * the caller. + */ +int intel_scu_ipc_register_read(u32 addr, u32 *value) +{ + u32 err = 0; + + mutex_lock(&ipclock); + if (ipcdev.pdev == NULL) { + mutex_unlock(&ipclock); + return -ENODEV; + } + ipc_write_sptr(addr); + ipc_command(4 << 16 | IPC_CMD_INDIRECT_RD); + err = busy_loop(); + *value = ipc_data_readl(0); + mutex_unlock(&ipclock); + return err; +} +EXPORT_SYMBOL(intel_scu_ipc_register_read); + +/** + * intel_scu_ipc_register_write - 32bit indirect write + * @addr: register address + * @value: 32bit value to write + * + * Performs IA 32 bit indirect write, returns 0 on success, or an + * error code. + * + * Can be used when SCCB(System Controller Configuration Block) register + * HRIM(Honor Restricted IPC Messages) is set (bit 23) + * + * This function may sleep. Locking for SCU accesses is handled for + * the caller. + */ +int intel_scu_ipc_register_write(u32 addr, u32 value) +{ + u32 err = 0; + + mutex_lock(&ipclock); + if (ipcdev.pdev == NULL) { + mutex_unlock(&ipclock); + return -ENODEV; + } + ipc_write_dptr(addr); + ipc_data_writel(value, 0); + ipc_command(4 << 16 | IPC_CMD_INDIRECT_WR); + err = busy_loop(); + mutex_unlock(&ipclock); + return err; +} +EXPORT_SYMBOL(intel_scu_ipc_register_write); + +/** + * intel_scu_ipc_simple_command - send a simple command + * @cmd: command + * @sub: sub type + * + * Issue a simple command to the SCU. Do not use this interface if + * you must then access data as any data values may be overwritten + * by another SCU access by the time this function returns. + * + * This function may sleep. Locking for SCU accesses is handled for + * the caller. + */ +int intel_scu_ipc_simple_command(int cmd, int sub) +{ + u32 err = 0; + + mutex_lock(&ipclock); + if (ipcdev.pdev == NULL) { + mutex_unlock(&ipclock); + return -ENODEV; + } + ipc_command(cmd << 12 | sub); + err = busy_loop(); + mutex_unlock(&ipclock); + return err; +} +EXPORT_SYMBOL(intel_scu_ipc_simple_command); + +/** + * intel_scu_ipc_command - command with data + * @cmd: command + * @sub: sub type + * @in: input data + * @inlen: input length + * @out: output data + * @outlein: output length + * + * Issue a command to the SCU which involves data transfers. Do the + * data copies under the lock but leave it for the caller to interpret + */ + +int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, + u32 *out, int outlen) +{ + u32 err = 0; + int i = 0; + + mutex_lock(&ipclock); + if (ipcdev.pdev == NULL) { + mutex_unlock(&ipclock); + return -ENODEV; + } + + for (i = 0; i < inlen; i++) + ipc_data_writel(*in++, 4 * i); + + ipc_command(cmd << 12 | sub); + err = busy_loop(); + + for (i = 0; i < outlen; i++) + *out++ = ipc_data_readl(4 * i); + + mutex_unlock(&ipclock); + return err; +} +EXPORT_SYMBOL(intel_scu_ipc_command); + +/*I2C commands */ +#define IPC_I2C_WRITE 1 /* I2C Write command */ +#define IPC_I2C_READ 2 /* I2C Read command */ + +/** + * intel_scu_ipc_i2c_cntrl - I2C read/write operations + * @addr: I2C address + command bits + * @data: data to read/write + * + * Perform an an I2C read/write operation via the SCU. All locking is + * handled for the caller. This function may sleep. + * + * Returns an error code or 0 on success. + * + * This has to be in the IPC driver for the locking. + */ +int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) +{ + u32 cmd = 0; + + mutex_lock(&ipclock); + cmd = (addr >> 24) & 0xFF; + if (cmd == IPC_I2C_READ) { + writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); + /* Write not getting updated without delay */ + mdelay(1); + *data = readl(ipcdev.i2c_base + I2C_DATA_ADDR); + } else if (cmd == IPC_I2C_WRITE) { + writel(addr, ipcdev.i2c_base + I2C_DATA_ADDR); + mdelay(1); + writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR); + } else { + dev_err(&ipcdev.pdev->dev, + "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); + + mutex_unlock(&ipclock); + return -1; + } + mutex_unlock(&ipclock); + return 0; +} +EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); + +#define IPC_FW_LOAD_ADDR 0xFFFC0000 /* Storage location for FW image */ +#define IPC_FW_UPDATE_MBOX_ADDR 0xFFFFDFF4 /* Mailbox between ipc and scu */ +#define IPC_MAX_FW_SIZE 262144 /* 256K storage size for loading the FW image */ +#define IPC_FW_MIP_HEADER_SIZE 2048 /* Firmware MIP header size */ +/* IPC inform SCU to get ready for update process */ +#define IPC_CMD_FW_UPDATE_READY 0x10FE +/* IPC inform SCU to go for update process */ +#define IPC_CMD_FW_UPDATE_GO 0x20FE +/* Status code for fw update */ +#define IPC_FW_UPDATE_SUCCESS 0x444f4e45 /* Status code 'DONE' */ +#define IPC_FW_UPDATE_BADN 0x4241444E /* Status code 'BADN' */ +#define IPC_FW_TXHIGH 0x54784849 /* Status code 'IPC_FW_TXHIGH' */ +#define IPC_FW_TXLOW 0x54784c4f /* Status code 'IPC_FW_TXLOW' */ + +struct fw_update_mailbox { + u32 status; + u32 scu_flag; + u32 driver_flag; +}; + + +/** + * intel_scu_ipc_fw_update - Firmware update utility + * @buffer: firmware buffer + * @length: size of firmware buffer + * + * This function provides an interface to load the firmware into + * the SCU. Returns 0 on success or -1 on failure + */ +int intel_scu_ipc_fw_update(u8 *buffer, u32 length) +{ + void __iomem *fw_update_base; + struct fw_update_mailbox __iomem *mailbox = NULL; + int retry_cnt = 0; + u32 status; + + mutex_lock(&ipclock); + fw_update_base = ioremap_nocache(IPC_FW_LOAD_ADDR, (128*1024)); + if (fw_update_base == NULL) { + mutex_unlock(&ipclock); + return -ENOMEM; + } + mailbox = ioremap_nocache(IPC_FW_UPDATE_MBOX_ADDR, + sizeof(struct fw_update_mailbox)); + if (mailbox == NULL) { + iounmap(fw_update_base); + mutex_unlock(&ipclock); + return -ENOMEM; + } + + ipc_command(IPC_CMD_FW_UPDATE_READY); + + /* Intitialize mailbox */ + writel(0, &mailbox->status); + writel(0, &mailbox->scu_flag); + writel(0, &mailbox->driver_flag); + + /* Driver copies the 2KB MIP header to SRAM at 0xFFFC0000*/ + memcpy_toio(fw_update_base, buffer, 0x800); + + /* Driver sends "FW Update" IPC command (CMD_ID 0xFE; MSG_ID 0x02). + * Upon receiving this command, SCU will write the 2K MIP header + * from 0xFFFC0000 into NAND. + * SCU will write a status code into the Mailbox, and then set scu_flag. + */ + + ipc_command(IPC_CMD_FW_UPDATE_GO); + + /*Driver stalls until scu_flag is set */ + while (readl(&mailbox->scu_flag) != 1) { + rmb(); + mdelay(1); + } + + /* Driver checks Mailbox status. + * If the status is 'BADN', then abort (bad NAND). + * If the status is 'IPC_FW_TXLOW', then continue. + */ + while (readl(&mailbox->status) != IPC_FW_TXLOW) { + rmb(); + mdelay(10); + } + mdelay(10); + +update_retry: + if (retry_cnt > 5) + goto update_end; + + if (readl(&mailbox->status) != IPC_FW_TXLOW) + goto update_end; + buffer = buffer + 0x800; + memcpy_toio(fw_update_base, buffer, 0x20000); + writel(1, &mailbox->driver_flag); + while (readl(&mailbox->scu_flag) == 1) { + rmb(); + mdelay(1); + } + + /* check for 'BADN' */ + if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) + goto update_end; + + while (readl(&mailbox->status) != IPC_FW_TXHIGH) { + rmb(); + mdelay(10); + } + mdelay(10); + + if (readl(&mailbox->status) != IPC_FW_TXHIGH) + goto update_end; + + buffer = buffer + 0x20000; + memcpy_toio(fw_update_base, buffer, 0x20000); + writel(0, &mailbox->driver_flag); + + while (mailbox->scu_flag == 0) { + rmb(); + mdelay(1); + } + + /* check for 'BADN' */ + if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) + goto update_end; + + if (readl(&mailbox->status) == IPC_FW_TXLOW) { + ++retry_cnt; + goto update_retry; + } + +update_end: + status = readl(&mailbox->status); + + iounmap(fw_update_base); + iounmap(mailbox); + mutex_unlock(&ipclock); + + if (status == IPC_FW_UPDATE_SUCCESS) + return 0; + return -1; +} +EXPORT_SYMBOL(intel_scu_ipc_fw_update); + +/* + * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 + * When ioc bit is set to 1, caller api must wait for interrupt handler called + * which in turn unlocks the caller api. Currently this is not used + * + * This is edge triggered so we need take no action to clear anything + */ +static irqreturn_t ioc(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +/** + * ipc_probe - probe an Intel SCU IPC + * @dev: the PCI device matching + * @id: entry in the match table + * + * Enable and install an intel SCU IPC. This appears in the PCI space + * but uses some hard coded addresses as well. + */ +static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int err; + resource_size_t pci_resource; + + if (ipcdev.pdev) /* We support only one SCU */ + return -EBUSY; + + ipcdev.pdev = pci_dev_get(dev); + + err = pci_enable_device(dev); + if (err) + return err; + + err = pci_request_regions(dev, "intel_scu_ipc"); + if (err) + return err; + + pci_resource = pci_resource_start(dev, 0); + if (!pci_resource) + return -ENOMEM; + + if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) + return -EBUSY; + + ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR); + if (!ipcdev.ipc_base) + return -ENOMEM; + + ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR); + if (!ipcdev.i2c_base) { + iounmap(ipcdev.ipc_base); + return -ENOMEM; + } + return 0; +} + +/** + * ipc_remove - remove a bound IPC device + * @pdev: PCI device + * + * In practice the SCU is not removable but this function is also + * called for each device on a module unload or cleanup which is the + * path that will get used. + * + * Free up the mappings and release the PCI resources + */ +static void ipc_remove(struct pci_dev *pdev) +{ + free_irq(pdev->irq, &ipcdev); + pci_release_regions(pdev); + pci_dev_put(ipcdev.pdev); + iounmap(ipcdev.ipc_base); + iounmap(ipcdev.i2c_base); + ipcdev.pdev = NULL; +} + +static const struct pci_device_id pci_ids[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)}, + { 0,} +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver ipc_driver = { + .name = "intel_scu_ipc", + .id_table = pci_ids, + .probe = ipc_probe, + .remove = ipc_remove, +}; + + +static int __init intel_scu_ipc_init(void) +{ + return pci_register_driver(&ipc_driver); +} + +static void __exit intel_scu_ipc_exit(void) +{ + pci_unregister_driver(&ipc_driver); +} + +MODULE_AUTHOR("Sreedhara DS "); +MODULE_DESCRIPTION("Intel SCU IPC driver"); +MODULE_LICENSE("GPL"); + +module_init(intel_scu_ipc_init); +module_exit(intel_scu_ipc_exit); -- cgit v1.2.3 From 89a7644be2c59eea443b0db2514fd42d5de909f8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 7 May 2010 11:24:11 -0700 Subject: eeepc-wmi: depends on BACKLIGHT_CLASS_DEVICE eeepc-wmi uses backlight*() interfaces so it should depend on BACKLIGHT_CLASS_DEVICE. eeepc-wmi.c:(.text+0x2d7f54): undefined reference to `backlight_force_update' eeepc-wmi.c:(.text+0x2d8012): undefined reference to `backlight_device_register' eeepc-wmi.c:(.devinit.text+0x1c31c): undefined reference to `backlight_device_unregister' eeepc-wmi.c:(.devexit.text+0x2f8b): undefined reference to `backlight_device_unregister' Signed-off-by: Randy Dunlap Cc: stable@kernel.org --- drivers/platform/x86/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 619134151ca6..0ec796d9da1a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -390,6 +390,7 @@ config EEEPC_WMI depends on ACPI_WMI depends on INPUT depends on EXPERIMENTAL + depends on BACKLIGHT_CLASS_DEVICE select INPUT_SPARSEKMAP ---help--- Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. -- cgit v1.2.3 From 6c62673cf58516e1b82329ac90ebf3ff3f485672 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 7 May 2010 14:30:00 -0400 Subject: drivers/platform/x86: Clarify the MRST IPC driver description slightly Make it clear that this driver is only needed for embedded hardware, not PCs. Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0ec796d9da1a..3e1b8a288719 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -534,6 +534,7 @@ config INTEL_SCU_IPC default y ---help--- IPC is used to bridge the communications between kernel and SCU on - some embedded Intel x86 platforms. + some embedded Intel x86 platforms. This is not needed for PC-type + machines. endif # X86_PLATFORM_DEVICES -- cgit v1.2.3 From 6c75dd0f965b7b3480d0e0e9b8d9747988dfe815 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 13 May 2010 22:06:20 +0200 Subject: drivers/platform/x86: Use kzalloc Use kzalloc rather than the combination of kmalloc and memset. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,size,flags; statement S; @@ -x = kmalloc(size,flags); +x = kzalloc(size,flags); if (x == NULL) S -memset(x, 0, size); // Signed-off-by: Julia Lawall --- drivers/platform/x86/fujitsu-laptop.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') 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) if (acpi_disabled) return -ENODEV; - fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL); + fujitsu = kzalloc(sizeof(struct fujitsu_t), GFP_KERNEL); if (!fujitsu) return -ENOMEM; - memset(fujitsu, 0, sizeof(struct fujitsu_t)); fujitsu->keycode1 = KEY_PROG1; fujitsu->keycode2 = KEY_PROG2; fujitsu->keycode3 = KEY_PROG3; @@ -1150,12 +1149,11 @@ static int __init fujitsu_init(void) /* Register hotkey driver */ - fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL); + fujitsu_hotkey = kzalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL); if (!fujitsu_hotkey) { ret = -ENOMEM; goto fail_hotkey; } - memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t)); result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver); if (result < 0) { -- cgit v1.2.3 From 2c6719a3ef2f7da59a622ba1176ad41d553f8f43 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 15 May 2010 23:22:18 +0200 Subject: drivers/platform/x86: Use kmemdup Use kmemdup when some other buffer is immediately copied into the allocated region. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression from,to,size,flag; statement S; @@ - to = \(kmalloc\|kzalloc\)(size,flag); + to = kmemdup(from,size,flag); if (to==NULL || ...) S - memcpy(to, from, size); // Signed-off-by: Julia Lawall --- drivers/platform/x86/wmi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 39ec5b6c2e3a..17df134a6f03 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -756,12 +756,10 @@ static __init acpi_status parse_wdg(acpi_handle handle) total = obj->buffer.length / sizeof(struct guid_block); - gblock = kzalloc(obj->buffer.length, GFP_KERNEL); + gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); if (!gblock) return AE_NO_MEMORY; - memcpy(gblock, obj->buffer.pointer, obj->buffer.length); - for (i = 0; i < total; i++) { /* Some WMI devices, like those for nVidia hooks, have a -- cgit v1.2.3 From d0a4aa2b2603d85353450a30a6a3ea2010b041da Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Wed, 12 May 2010 09:58:07 -0700 Subject: msi-laptop: Add N014 N051 dmi information to scm models table Add new MSI netbook N014 and N051 dmi information to scm models table. Signed-off-by: Lee, Chun-Yi Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/msi-laptop.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 996223a7c009..bcd9c0d23b39 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -469,6 +469,26 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { }, .callback = dmi_check_cb }, + { + .ident = "MSI N051", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-N051"), + DMI_MATCH(DMI_CHASSIS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD") + }, + .callback = dmi_check_cb + }, + { + .ident = "MSI N014", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"), + }, + .callback = dmi_check_cb + }, { } }; @@ -788,3 +808,5 @@ MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-105 MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); +MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*"); +MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); -- cgit v1.2.3 From 1f27e17bfa2ad34a2dbb61cd097d9ecf506639df Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Wed, 12 May 2010 09:58:08 -0700 Subject: msi-laptop: Add MSI CR620 notebook dmi information to scm models table Add new MSI notebook CR620 dmi information to scm models table. Signed-off-by: Lee, Chun-Yi Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/msi-laptop.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index bcd9c0d23b39..45197d75db91 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -489,6 +489,15 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { }, .callback = dmi_check_cb }, + { + .ident = "MSI CR620", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "CR620"), + }, + .callback = dmi_check_cb + }, { } }; @@ -810,3 +819,4 @@ MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct1 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*"); MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*"); MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*"); +MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*"); -- cgit v1.2.3 From 3bb970214fce6495573843e4b7b786f8ea94cd70 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Wed, 12 May 2010 09:58:09 -0700 Subject: msi-laptop: Set rfkill init state when msi-laptop intiial Setup Wlan/Bluetooth/3G rfkill initial state to sync with the hardware state from EC 0x2e address. Signed-off-by: Lee, Chun-Yi Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/msi-laptop.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 45197d75db91..34bec2e26843 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -581,11 +581,31 @@ static void rfkill_cleanup(void) } } +static void msi_init_rfkill(struct work_struct *ignored) +{ + if (rfk_wlan) { + rfkill_set_sw_state(rfk_wlan, !wlan_s); + rfkill_wlan_set(NULL, !wlan_s); + } + if (rfk_bluetooth) { + rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); + rfkill_bluetooth_set(NULL, !bluetooth_s); + } + if (rfk_threeg) { + rfkill_set_sw_state(rfk_threeg, !threeg_s); + rfkill_threeg_set(NULL, !threeg_s); + } +} +static DECLARE_DELAYED_WORK(msi_rfkill_init, msi_init_rfkill); + static int rfkill_init(struct platform_device *sdev) { /* add rfkill */ int retval; + /* keep the hardware wireless state */ + get_wireless_state_ec_standard(); + rfk_bluetooth = rfkill_alloc("msi-bluetooth", &sdev->dev, RFKILL_TYPE_BLUETOOTH, &rfkill_bluetooth_ops, NULL); @@ -619,6 +639,10 @@ static int rfkill_init(struct platform_device *sdev) goto err_threeg; } + /* schedule to run rfkill state initial */ + schedule_delayed_work(&msi_rfkill_init, + round_jiffies_relative(1 * HZ)); + return 0; err_threeg: -- cgit v1.2.3 From 339e75329a447363658d08833bf9f98909f419cd Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Wed, 12 May 2010 09:58:10 -0700 Subject: msi-laptop: Add i8042 filter to sync sw state with BIOS when function key pressed There have some MSI netbook change devices state by EC when user press wlan/bluetooth/wwan function keys. So, add a i8042 filter to sync sw state with BIOS when function keys pressed. Signed-off-by: Lee, Chun-Yi Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/msi-laptop.c | 59 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 34bec2e26843..b08a108c9776 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -59,6 +59,7 @@ #include #include #include +#include #define MSI_DRIVER_VERSION "0.5" @@ -581,6 +582,46 @@ static void rfkill_cleanup(void) } } +static void msi_update_rfkill(struct work_struct *ignored) +{ + get_wireless_state_ec_standard(); + + if (rfk_wlan) + rfkill_set_sw_state(rfk_wlan, !wlan_s); + if (rfk_bluetooth) + rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); + if (rfk_threeg) + rfkill_set_sw_state(rfk_threeg, !threeg_s); +} +static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); + +static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended; + + if (str & 0x20) + return false; + + /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/ + if (unlikely(data == 0xe0)) { + extended = true; + return false; + } else if (unlikely(extended)) { + switch (data) { + case 0x54: + case 0x62: + case 0x76: + schedule_delayed_work(&msi_rfkill_work, + round_jiffies_relative(0.5 * HZ)); + break; + } + extended = false; + } + + return false; +} + static void msi_init_rfkill(struct work_struct *ignored) { if (rfk_wlan) { @@ -706,9 +747,24 @@ static int load_scm_model_init(struct platform_device *sdev) /* initial rfkill */ result = rfkill_init(sdev); if (result < 0) - return result; + goto fail_rfkill; + + result = i8042_install_filter(msi_laptop_i8042_filter); + if (result) { + printk(KERN_ERR + "msi-laptop: Unable to install key filter\n"); + goto fail_filter; + } return 0; + +fail_filter: + rfkill_cleanup(); + +fail_rfkill: + + return result; + } static int __init msi_init(void) @@ -819,6 +875,7 @@ static void __exit msi_cleanup(void) platform_driver_unregister(&msipf_driver); backlight_device_unregister(msibl_device); + i8042_remove_filter(msi_laptop_i8042_filter); rfkill_cleanup(); /* Enable automatic brightness control again */ -- cgit v1.2.3 From 1ac34072bda53e889ac1c5a73ea59f42a769485a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 May 2010 12:03:00 -0700 Subject: msi-laptop: fix up some coding style issues found by checkpatch Cc: Lee, Chun-Yi Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/msi-laptop.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index b08a108c9776..5822d0ac68d2 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -119,7 +119,8 @@ static int set_lcd_level(int level) buf[0] = 0x80; buf[1] = (u8) (level*31); - return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1); + return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), + NULL, 0, 1); } static int get_lcd_level(void) @@ -127,7 +128,8 @@ static int get_lcd_level(void) u8 wdata = 0, rdata; int result; - result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, + &rdata, 1, 1); if (result < 0) return result; @@ -139,7 +141,8 @@ static int get_auto_brightness(void) u8 wdata = 4, rdata; int result; - result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, + &rdata, 1, 1); if (result < 0) return result; @@ -153,14 +156,16 @@ static int set_auto_brightness(int enable) wdata[0] = 4; - result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1); + result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, + &rdata, 1, 1); if (result < 0) return result; wdata[0] = 0x84; wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); - return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); + return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, + NULL, 0, 1); } static ssize_t set_device_state(const char *buf, size_t count, u8 mask) @@ -255,7 +260,7 @@ static int bl_update_status(struct backlight_device *b) return set_lcd_level(b->props.brightness); } -static struct backlight_ops msibl_ops = { +static const struct backlight_ops msibl_ops = { .get_brightness = bl_get_brightness, .update_status = bl_update_status, }; @@ -354,7 +359,8 @@ static ssize_t store_lcd_level(struct device *dev, int level, ret; - if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX)) + if (sscanf(buf, "%i", &level) != 1 || + (level < 0 || level >= MSI_LCD_LEVEL_MAX)) return -EINVAL; ret = set_lcd_level(level); @@ -394,7 +400,8 @@ static ssize_t store_auto_brightness(struct device *dev, } static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); -static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness); +static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, + store_auto_brightness); static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL); static DEVICE_ATTR(wlan, 0444, show_wlan, NULL); static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); @@ -425,8 +432,9 @@ static struct platform_device *msipf_device; static int dmi_check_cb(const struct dmi_system_id *id) { - printk("msi-laptop: Identified laptop model '%s'.\n", id->ident); - return 0; + printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n", + id->ident); + return 0; } static struct dmi_system_id __initdata msi_dmi_table[] = { @@ -436,7 +444,8 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"), DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), - DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD") + DMI_MATCH(DMI_CHASSIS_VENDOR, + "MICRO-STAR INT'L CO.,LTD") }, .callback = dmi_check_cb }, @@ -466,7 +475,8 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), - DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD") + DMI_MATCH(DMI_CHASSIS_VENDOR, + "MICRO-STAR INT'L CO.,LTD") }, .callback = dmi_check_cb }, @@ -823,7 +833,8 @@ static int __init msi_init(void) goto fail_platform_device1; } - ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group); + ret = sysfs_create_group(&msipf_device->dev.kobj, + &msipf_attribute_group); if (ret) goto fail_platform_device2; -- cgit v1.2.3 From 7ab52521f2d850e922f33e5586a47e6c83ec6f11 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Sat, 15 May 2010 06:18:54 +0800 Subject: Clean up all objects used by scm model when driver initial fail or exit Clean up i8042 filter, rfkill and cancel delayed work when msi-laptop driver initial fail or exit on MSI scm model. Signed-off-by: Lee, Chun-Yi --- drivers/platform/x86/msi-laptop.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 5822d0ac68d2..704b5faac599 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -859,6 +859,11 @@ static int __init msi_init(void) fail_platform_device2: + if (load_scm_model) { + i8042_remove_filter(msi_laptop_i8042_filter); + cancel_delayed_work_sync(&msi_rfkill_work); + rfkill_cleanup(); + } platform_device_del(msipf_device); fail_platform_device1: @@ -878,6 +883,11 @@ fail_backlight: static void __exit msi_cleanup(void) { + if (load_scm_model) { + i8042_remove_filter(msi_laptop_i8042_filter); + cancel_delayed_work_sync(&msi_rfkill_work); + rfkill_cleanup(); + } sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); if (!old_ec_model && threeg_exists) @@ -886,9 +896,6 @@ static void __exit msi_cleanup(void) platform_driver_unregister(&msipf_driver); backlight_device_unregister(msibl_device); - i8042_remove_filter(msi_laptop_i8042_filter); - rfkill_cleanup(); - /* Enable automatic brightness control again */ if (auto_brightness != 2) set_auto_brightness(1); -- cgit v1.2.3 From fc3155b2c6419a442c6f8b34a3bf31f8efe0fe33 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Mon, 3 May 2010 15:30:15 +0200 Subject: X86 platform wmi: Introduce debug param to log all WMI events To give people easily an idea what could be WMI driven on their system. Introduces: wmi.debug=[01] Tested on an acer: ACPI: WMI: DEBUG Event INTEGER_TYPE - 65535 Situation where a driver registers for specific event and debug handler gets overridden and set again if the registering driver gets unloaded again is untested, but should work. Signed-off-by: Thomas Renninger CC: platform-driver-x86@vger.kernel.org CC: mjg59@srcf.ucam.org CC: corentin.chary@gmail.com Signed-off-by: Carlos Corbacho Signed-off-by: Matthew Garrett --- drivers/platform/x86/wmi.c | 58 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 17df134a6f03..e820f4cddb61 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -81,6 +81,11 @@ static struct wmi_block wmi_blocks; #define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ #define ACPI_WMI_EVENT 0x8 /* GUID is an event */ +static int debug_event; +module_param(debug_event, bool, 0444); +MODULE_PARM_DESC(debug_event, + "Log WMI Events [0/1]"); + static int acpi_wmi_remove(struct acpi_device *device, int type); static int acpi_wmi_add(struct acpi_device *device); static void acpi_wmi_notify(struct acpi_device *device, u32 event); @@ -477,6 +482,37 @@ const struct acpi_buffer *in) } EXPORT_SYMBOL_GPL(wmi_set_block); +static void wmi_notify_debug(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + wmi_get_event_data(value, &response); + + obj = (union acpi_object *)response.pointer; + + if (!obj) + return; + + printk(KERN_INFO PREFIX "DEBUG Event "); + switch(obj->type) { + case ACPI_TYPE_BUFFER: + printk("BUFFER_TYPE - length %d\n", obj->buffer.length); + break; + case ACPI_TYPE_STRING: + printk("STRING_TYPE - %s\n", obj->string.pointer); + break; + case ACPI_TYPE_INTEGER: + printk("INTEGER_TYPE - %llu\n", obj->integer.value); + break; + case ACPI_TYPE_PACKAGE: + printk("PACKAGE_TYPE - %d elements\n", obj->package.count); + break; + default: + printk("object type 0x%X\n", obj->type); + } +} + /** * wmi_install_notify_handler - Register handler for WMI events * @handler: Function to handle notifications @@ -496,7 +532,7 @@ wmi_notify_handler handler, void *data) if (!find_guid(guid, &block)) return AE_NOT_EXIST; - if (block->handler) + if (block->handler && block->handler != wmi_notify_debug) return AE_ALREADY_ACQUIRED; block->handler = handler; @@ -516,7 +552,7 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); acpi_status wmi_remove_notify_handler(const char *guid) { struct wmi_block *block; - acpi_status status; + acpi_status status = AE_OK; if (!guid) return AE_BAD_PARAMETER; @@ -524,14 +560,16 @@ acpi_status wmi_remove_notify_handler(const char *guid) if (!find_guid(guid, &block)) return AE_NOT_EXIST; - if (!block->handler) + if (!block->handler || block->handler == wmi_notify_debug) return AE_NULL_ENTRY; - status = wmi_method_enable(block, 0); - - block->handler = NULL; - block->handler_data = NULL; - + if (debug_event) { + block->handler = wmi_notify_debug; + } else { + status = wmi_method_enable(block, 0); + block->handler = NULL; + block->handler_data = NULL; + } return status; } EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); @@ -780,6 +818,10 @@ static __init acpi_status parse_wdg(acpi_handle handle) wblock->gblock = gblock[i]; wblock->handle = handle; + if (debug_event) { + wblock->handler = wmi_notify_debug; + status = wmi_method_enable(wblock, 1); + } list_add_tail(&wblock->list, &wmi_blocks.list); } -- cgit v1.2.3 From 7715348cbe28da80fd5372fd68748e852a9d8468 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Mon, 3 May 2010 15:30:16 +0200 Subject: X86 platform wmi: Also log GUID string when an event happens and debug is set Output in log with debug=1: ACPI: WMI: DEBUG Event INTEGER_TYPE - 65535 ACPI: WMI: DEBUG Event GUID: CC1A61AC-4256-41A3-B9E0-05A445ADE2F5 Signed-off-by: Thomas Renninger CC: platform-driver-x86@vger.kernel.org CC: mjg59@srcf.ucam.org CC: corentin.chary@gmail.com Signed-off-by: Carlos Corbacho Signed-off-by: Matthew Garrett --- drivers/platform/x86/wmi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index e820f4cddb61..f16768db4468 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -880,6 +880,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) struct guid_block *block; struct wmi_block *wblock; struct list_head *p; + char guid_string[37]; list_for_each(p, &wmi_blocks.list) { wblock = list_entry(p, struct wmi_block, list); @@ -889,6 +890,11 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event) (block->notify_id == event)) { if (wblock->handler) wblock->handler(event, wblock->handler_data); + if (debug_event) { + wmi_gtoa(wblock->gblock.guid, guid_string); + printk(KERN_INFO PREFIX "DEBUG Event GUID:" + " %s\n", guid_string); + } acpi_bus_generate_netlink_event( device->pnp.device_class, dev_name(&device->dev), -- cgit v1.2.3 From a929aae0e0a91d89b60774316ede6c1e2e10dc4e Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Mon, 3 May 2010 15:30:17 +0200 Subject: X86 platfrom wmi: Add debug facility to dump WMI data in a readable way Signed-off-by: Thomas Renninger CC: platform-driver-x86@vger.kernel.org CC: mjg59@srcf.ucam.org CC: corentin.chary@gmail.com Signed-off-by: Carlos Corbacho Signed-off-by: Matthew Garrett --- drivers/platform/x86/wmi.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index f16768db4468..e4eaa14ed987 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -86,6 +86,11 @@ module_param(debug_event, bool, 0444); MODULE_PARM_DESC(debug_event, "Log WMI Events [0/1]"); +static int debug_dump_wdg; +module_param(debug_dump_wdg, bool, 0444); +MODULE_PARM_DESC(debug_dump_wdg, + "Dump available WMI interfaces [0/1]"); + static int acpi_wmi_remove(struct acpi_device *device, int type); static int acpi_wmi_add(struct acpi_device *device); static void acpi_wmi_notify(struct acpi_device *device, u32 event); @@ -482,6 +487,33 @@ const struct acpi_buffer *in) } EXPORT_SYMBOL_GPL(wmi_set_block); +static void wmi_dump_wdg(struct guid_block *g) +{ + char guid_string[37]; + + wmi_gtoa(g->guid, guid_string); + printk(KERN_INFO PREFIX "%s:\n", guid_string); + printk(KERN_INFO PREFIX "\tobject_id: %c%c\n", + g->object_id[0], g->object_id[1]); + printk(KERN_INFO PREFIX "\tnotify_id: %02X\n", g->notify_id); + printk(KERN_INFO PREFIX "\treserved: %02X\n", g->reserved); + printk(KERN_INFO PREFIX "\tinstance_count: %d\n", g->instance_count); + printk(KERN_INFO PREFIX "\tflags: %#x", g->flags); + if (g->flags) { + printk(" "); + if (g->flags & ACPI_WMI_EXPENSIVE) + printk("ACPI_WMI_EXPENSIVE "); + if (g->flags & ACPI_WMI_METHOD) + printk("ACPI_WMI_METHOD "); + if (g->flags & ACPI_WMI_STRING) + printk("ACPI_WMI_STRING "); + if (g->flags & ACPI_WMI_EVENT) + printk("ACPI_WMI_EVENT "); + } + printk("\n"); + +} + static void wmi_notify_debug(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -812,6 +844,9 @@ static __init acpi_status parse_wdg(acpi_handle handle) guid_string); continue; } + if (debug_dump_wdg) + wmi_dump_wdg(&gblock[i]); + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); if (!wblock) return AE_NO_MEMORY; -- cgit v1.2.3 From b7670ed6509f322713bbd96eed0301c322f2ae02 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 19 May 2010 12:37:01 +0200 Subject: drivers/platform/x86/eeepc-wmi.c: fix build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/platform/x86/eeepc-wmi.c: In function ‘eeepc_wmi_notify’: drivers/platform/x86/eeepc-wmi.c:209: warning: ‘new’ may be used uninitialized in this function drivers/platform/x86/eeepc-wmi.c:209: note: ‘new’ was declared here Signed-off-by: Daniel Mack Cc: Matthew Garrett Cc: Yong Wang Cc: Corentin Chary Cc: Dmitry Torokhov Cc: Tejun Heo Cc: platform-driver-x86@vger.kernel.org Acked-By: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') 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) { struct backlight_device *bd = eeepc->backlight_device; int old = bd->props.brightness; - int new; + int new = old; if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) new = code - NOTIFY_BRNUP_MIN + 1; -- cgit v1.2.3 From 785cfc0324b9321efb85b0935af2b474d615daa1 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Wed, 19 May 2010 20:03:05 +0800 Subject: Move N014, N051 and CR620 dmi information to load scm dmi table Found the N014, N051 and CR620 are must the same with N034 there are load scm serial model. So, this patch move N014, N051 and CR620 dmi information to right dmi table: msi_load_scm_models_dmi_table[] Signed-off-by: Lee, Chun-Yi --- drivers/platform/x86/msi-laptop.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 704b5faac599..afd762b58ad9 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -480,6 +480,21 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { }, .callback = dmi_check_cb }, + { } +}; + +static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = { + { + .ident = "MSI N034", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"), + DMI_MATCH(DMI_CHASSIS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD") + }, + .callback = dmi_check_cb + }, { .ident = "MSI N051", .matches = { @@ -512,21 +527,6 @@ static struct dmi_system_id __initdata msi_dmi_table[] = { { } }; -static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = { - { - .ident = "MSI N034", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, - "MICRO-STAR INTERNATIONAL CO., LTD"), - DMI_MATCH(DMI_PRODUCT_NAME, "MS-N034"), - DMI_MATCH(DMI_CHASSIS_VENDOR, - "MICRO-STAR INTERNATIONAL CO., LTD") - }, - .callback = dmi_check_cb - }, - { } -}; - static int rfkill_bluetooth_set(void *data, bool blocked) { /* Do something with blocked...*/ -- cgit v1.2.3