diff options
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 135 |
1 files changed, 80 insertions, 55 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d556e95c532c..fde08a997557 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -336,6 +336,7 @@ static struct { u32 second_fan:1; u32 beep_needs_two_args:1; u32 mixer_no_level_control:1; + u32 battery_force_primary:1; u32 input_device_registered:1; u32 platform_drv_registered:1; u32 platform_drv_attrs_registered:1; @@ -344,7 +345,6 @@ static struct { u32 sensors_pdev_attrs_registered:1; u32 hotkey_poll_active:1; u32 has_adaptive_kbd:1; - u32 battery:1; } tp_features; static struct { @@ -359,9 +359,9 @@ struct thinkpad_id_data { char *bios_version_str; /* Something like 1ZET51WW (1.03z) */ char *ec_version_str; /* Something like 1ZHT51WW-1.04a */ - u16 bios_model; /* 1Y = 0x5931, 0 = unknown */ - u16 ec_model; - u16 bios_release; /* 1ZETK1WW = 0x314b, 0 = unknown */ + u32 bios_model; /* 1Y = 0x3159, 0 = unknown */ + u32 ec_model; + u16 bios_release; /* 1ZETK1WW = 0x4b31, 0 = unknown */ u16 ec_release; char *model_str; /* ThinkPad T43 */ @@ -445,17 +445,20 @@ do { \ /* * Quirk handling helpers * - * ThinkPad IDs and versions seen in the field so far - * are two-characters from the set [0-9A-Z], i.e. base 36. + * ThinkPad IDs and versions seen in the field so far are + * two or three characters from the set [0-9A-Z], i.e. base 36. * * We use values well outside that range as specials. */ -#define TPACPI_MATCH_ANY 0xffffU +#define TPACPI_MATCH_ANY 0xffffffffU +#define TPACPI_MATCH_ANY_VERSION 0xffffU #define TPACPI_MATCH_UNKNOWN 0U -/* TPID('1', 'Y') == 0x5931 */ -#define TPID(__c1, __c2) (((__c2) << 8) | (__c1)) +/* TPID('1', 'Y') == 0x3159 */ +#define TPID(__c1, __c2) (((__c1) << 8) | (__c2)) +#define TPID3(__c1, __c2, __c3) (((__c1) << 16) | ((__c2) << 8) | (__c3)) +#define TPVER TPID #define TPACPI_Q_IBM(__id1, __id2, __quirk) \ { .vendor = PCI_VENDOR_ID_IBM, \ @@ -469,6 +472,12 @@ do { \ .ec = TPACPI_MATCH_ANY, \ .quirks = (__quirk) } +#define TPACPI_Q_LNV3(__id1, __id2, __id3, __quirk) \ + { .vendor = PCI_VENDOR_ID_LENOVO, \ + .bios = TPID3(__id1, __id2, __id3), \ + .ec = TPACPI_MATCH_ANY, \ + .quirks = (__quirk) } + #define TPACPI_QEC_LNV(__id1, __id2, __quirk) \ { .vendor = PCI_VENDOR_ID_LENOVO, \ .bios = TPACPI_MATCH_ANY, \ @@ -477,8 +486,8 @@ do { \ struct tpacpi_quirk { unsigned int vendor; - u16 bios; - u16 ec; + u32 bios; + u32 ec; unsigned long quirks; }; @@ -1648,16 +1657,16 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) { .vendor = (__v), \ .bios = TPID(__id1, __id2), \ .ec = TPACPI_MATCH_ANY, \ - .quirks = TPACPI_MATCH_ANY << 16 \ - | (__bv1) << 8 | (__bv2) } + .quirks = TPACPI_MATCH_ANY_VERSION << 16 \ + | TPVER(__bv1, __bv2) } #define TPV_Q_X(__v, __bid1, __bid2, __bv1, __bv2, \ __eid, __ev1, __ev2) \ { .vendor = (__v), \ .bios = TPID(__bid1, __bid2), \ .ec = __eid, \ - .quirks = (__ev1) << 24 | (__ev2) << 16 \ - | (__bv1) << 8 | (__bv2) } + .quirks = TPVER(__ev1, __ev2) << 16 \ + | TPVER(__bv1, __bv2) } #define TPV_QI0(__id1, __id2, __bv1, __bv2) \ TPV_Q(PCI_VENDOR_ID_IBM, __id1, __id2, __bv1, __bv2) @@ -1799,7 +1808,7 @@ static void __init tpacpi_check_outdated_fw(void) /* note that unknown versions are set to 0x0000 and we use that */ if ((bios_version > thinkpad_id.bios_release) || (ec_version > thinkpad_id.ec_release && - ec_version != TPACPI_MATCH_ANY)) { + ec_version != TPACPI_MATCH_ANY_VERSION)) { /* * The changelogs would let us track down the exact * reason, but it is just too much of a pain to track @@ -1929,7 +1938,7 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ /* first new observed key (star, favorites) is 0x1311 */ TP_ACPI_HOTKEYSCAN_STAR = 69, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL2, - TP_ACPI_HOTKEYSCAN_UNK25, + TP_ACPI_HOTKEYSCAN_CALCULATOR, TP_ACPI_HOTKEYSCAN_BLUETOOTH, TP_ACPI_HOTKEYSCAN_KEYBOARD, @@ -3450,7 +3459,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) KEY_FAVORITES, /* Favorite app, 0x311 */ KEY_RESERVED, /* Clipping tool */ - KEY_RESERVED, + KEY_CALC, /* Calculator (above numpad, P52) */ KEY_BLUETOOTH, /* Bluetooth */ KEY_KEYBOARD /* Keyboard, 0x315 */ }, @@ -9366,7 +9375,9 @@ static int tpacpi_battery_probe(int battery) { int ret = 0; - memset(&battery_info, 0, sizeof(struct tpacpi_battery_driver_data)); + memset(&battery_info.batteries[battery], 0, + sizeof(battery_info.batteries[battery])); + /* * 1) Get the current start threshold * 2) Check for support @@ -9421,7 +9432,8 @@ static int tpacpi_battery_probe(int battery) static int tpacpi_battery_get_id(const char *battery_name) { - if (strcmp(battery_name, "BAT0") == 0) + if (strcmp(battery_name, "BAT0") == 0 || + tp_features.battery_force_primary) return BAT_PRIMARY; if (strcmp(battery_name, "BAT1") == 0) return BAT_SECONDARY; @@ -9597,8 +9609,26 @@ static struct acpi_battery_hook battery_hook = { /* Subdriver init/exit */ +static const struct tpacpi_quirk battery_quirk_table[] __initconst = { + /* + * Individual addressing is broken on models that expose the + * primary battery as BAT1. + */ + TPACPI_Q_LNV('J', '7', true), /* B5400 */ + TPACPI_Q_LNV('J', 'I', true), /* Thinkpad 11e */ + TPACPI_Q_LNV3('R', '0', 'B', true), /* Thinkpad 11e gen 3 */ + TPACPI_Q_LNV3('R', '0', 'C', true), /* Thinkpad 13 */ + TPACPI_Q_LNV3('R', '0', 'J', true), /* Thinkpad 13 gen 2 */ +}; + static int __init tpacpi_battery_init(struct ibm_init_struct *ibm) { + memset(&battery_info, 0, sizeof(battery_info)); + + tp_features.battery_force_primary = tpacpi_check_quirks( + battery_quirk_table, + ARRAY_SIZE(battery_quirk_table)); + battery_hook_register(&battery_hook); return 0; } @@ -9809,36 +9839,37 @@ err_out: /* Probing */ -static bool __pure __init tpacpi_is_fw_digit(const char c) +static char __init tpacpi_parse_fw_id(const char * const s, + u32 *model, u16 *release) { - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'); -} + int i; + + if (!s || strlen(s) < 8) + goto invalid; + + for (i = 0; i < 8; i++) + if (!((s[i] >= '0' && s[i] <= '9') || + (s[i] >= 'A' && s[i] <= 'Z'))) + goto invalid; -static bool __pure __init tpacpi_is_valid_fw_id(const char * const s, - const char t) -{ /* * Most models: xxyTkkWW (#.##c) * Ancient 570/600 and -SL lacks (#.##c) */ - if (s && strlen(s) >= 8 && - tpacpi_is_fw_digit(s[0]) && - tpacpi_is_fw_digit(s[1]) && - s[2] == t && - (s[3] == 'T' || s[3] == 'N') && - tpacpi_is_fw_digit(s[4]) && - tpacpi_is_fw_digit(s[5])) - return true; + if (s[3] == 'T' || s[3] == 'N') { + *model = TPID(s[0], s[1]); + *release = TPVER(s[4], s[5]); + return s[2]; /* New models: xxxyTkkW (#.##c); T550 and some others */ - return s && strlen(s) >= 8 && - tpacpi_is_fw_digit(s[0]) && - tpacpi_is_fw_digit(s[1]) && - tpacpi_is_fw_digit(s[2]) && - s[3] == t && - (s[4] == 'T' || s[4] == 'N') && - tpacpi_is_fw_digit(s[5]) && - tpacpi_is_fw_digit(s[6]); + } else if (s[4] == 'T' || s[4] == 'N') { + *model = TPID3(s[0], s[1], s[2]); + *release = TPVER(s[5], s[6]); + return s[3]; + } + +invalid: + return '\0'; } /* returns 0 - probe ok, or < 0 - probe error. @@ -9850,6 +9881,7 @@ static int __must_check __init get_thinkpad_model_data( const struct dmi_device *dev = NULL; char ec_fw_string[18]; char const *s; + char t; if (!tp) return -EINVAL; @@ -9869,15 +9901,11 @@ static int __must_check __init get_thinkpad_model_data( return -ENOMEM; /* Really ancient ThinkPad 240X will fail this, which is fine */ - if (!(tpacpi_is_valid_fw_id(tp->bios_version_str, 'E') || - tpacpi_is_valid_fw_id(tp->bios_version_str, 'C'))) + t = tpacpi_parse_fw_id(tp->bios_version_str, + &tp->bios_model, &tp->bios_release); + if (t != 'E' && t != 'C') return 0; - tp->bios_model = tp->bios_version_str[0] - | (tp->bios_version_str[1] << 8); - tp->bios_release = (tp->bios_version_str[4] << 8) - | tp->bios_version_str[5]; - /* * ThinkPad T23 or newer, A31 or newer, R50e or newer, * X32 or newer, all Z series; Some models must have an @@ -9896,12 +9924,9 @@ static int __must_check __init get_thinkpad_model_data( if (!tp->ec_version_str) return -ENOMEM; - if (tpacpi_is_valid_fw_id(ec_fw_string, 'H')) { - tp->ec_model = ec_fw_string[0] - | (ec_fw_string[1] << 8); - tp->ec_release = (ec_fw_string[4] << 8) - | ec_fw_string[5]; - } else { + t = tpacpi_parse_fw_id(ec_fw_string, + &tp->ec_model, &tp->ec_release); + if (t != 'H') { pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n", ec_fw_string); pr_notice("please report this to %s\n", |