|
@@ -364,6 +364,73 @@ static void tpacpi_log_usertask(const char * const what)
|
|
|
} \
|
|
|
} while (0)
|
|
|
|
|
|
+/*
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ * We use values well outside that range as specials.
|
|
|
+ */
|
|
|
+
|
|
|
+#define TPACPI_MATCH_ANY 0xffffU
|
|
|
+#define TPACPI_MATCH_UNKNOWN 0U
|
|
|
+
|
|
|
+/* TPID('1', 'Y') == 0x5931 */
|
|
|
+#define TPID(__c1, __c2) (((__c2) << 8) | (__c1))
|
|
|
+
|
|
|
+#define TPACPI_Q_IBM(__id1, __id2, __quirk) \
|
|
|
+ { .vendor = PCI_VENDOR_ID_IBM, \
|
|
|
+ .bios = TPID(__id1, __id2), \
|
|
|
+ .ec = TPACPI_MATCH_ANY, \
|
|
|
+ .quirks = (__quirk) }
|
|
|
+
|
|
|
+#define TPACPI_Q_LNV(__id1, __id2, __quirk) \
|
|
|
+ { .vendor = PCI_VENDOR_ID_LENOVO, \
|
|
|
+ .bios = TPID(__id1, __id2), \
|
|
|
+ .ec = TPACPI_MATCH_ANY, \
|
|
|
+ .quirks = (__quirk) }
|
|
|
+
|
|
|
+struct tpacpi_quirk {
|
|
|
+ unsigned int vendor;
|
|
|
+ u16 bios;
|
|
|
+ u16 ec;
|
|
|
+ unsigned long quirks;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * tpacpi_check_quirks() - search BIOS/EC version on a list
|
|
|
+ * @qlist: array of &struct tpacpi_quirk
|
|
|
+ * @qlist_size: number of elements in @qlist
|
|
|
+ *
|
|
|
+ * Iterates over a quirks list until one is found that matches the
|
|
|
+ * ThinkPad's vendor, BIOS and EC model.
|
|
|
+ *
|
|
|
+ * Returns 0 if nothing matches, otherwise returns the quirks field of
|
|
|
+ * the matching &struct tpacpi_quirk entry.
|
|
|
+ *
|
|
|
+ * The match criteria is: vendor, ec and bios much match.
|
|
|
+ */
|
|
|
+static unsigned long __init tpacpi_check_quirks(
|
|
|
+ const struct tpacpi_quirk *qlist,
|
|
|
+ unsigned int qlist_size)
|
|
|
+{
|
|
|
+ while (qlist_size) {
|
|
|
+ if ((qlist->vendor == thinkpad_id.vendor ||
|
|
|
+ qlist->vendor == TPACPI_MATCH_ANY) &&
|
|
|
+ (qlist->bios == thinkpad_id.bios_model ||
|
|
|
+ qlist->bios == TPACPI_MATCH_ANY) &&
|
|
|
+ (qlist->ec == thinkpad_id.ec_model ||
|
|
|
+ qlist->ec == TPACPI_MATCH_ANY))
|
|
|
+ return qlist->quirks;
|
|
|
+
|
|
|
+ qlist_size--;
|
|
|
+ qlist++;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/****************************************************************************
|
|
|
****************************************************************************
|
|
|
*
|
|
@@ -6223,30 +6290,18 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
|
|
|
* We assume 0x07 really means auto mode while this quirk is active,
|
|
|
* as this is far more likely than the ThinkPad being in level 7,
|
|
|
* which is only used by the firmware during thermal emergencies.
|
|
|
+ *
|
|
|
+ * Enable for TP-1Y (T43), TP-78 (R51e), TP-76 (R52),
|
|
|
+ * TP-70 (T43, R52), which are known to be buggy.
|
|
|
*/
|
|
|
|
|
|
-static void fan_quirk1_detect(void)
|
|
|
+static void fan_quirk1_setup(void)
|
|
|
{
|
|
|
- /* In some ThinkPads, neither the EC nor the ACPI
|
|
|
- * DSDT initialize the HFSP register, and it ends up
|
|
|
- * being initially set to 0x07 when it *could* be
|
|
|
- * either 0x07 or 0x80.
|
|
|
- *
|
|
|
- * Enable for TP-1Y (T43), TP-78 (R51e),
|
|
|
- * TP-76 (R52), TP-70 (T43, R52), which are known
|
|
|
- * to be buggy. */
|
|
|
if (fan_control_initial_status == 0x07) {
|
|
|
- switch (thinkpad_id.ec_model) {
|
|
|
- case 0x5931: /* TP-1Y */
|
|
|
- case 0x3837: /* TP-78 */
|
|
|
- case 0x3637: /* TP-76 */
|
|
|
- case 0x3037: /* TP-70 */
|
|
|
- printk(TPACPI_NOTICE
|
|
|
- "fan_init: initial fan status is unknown, "
|
|
|
- "assuming it is in auto mode\n");
|
|
|
- tp_features.fan_ctrl_status_undef = 1;
|
|
|
- ;;
|
|
|
- }
|
|
|
+ printk(TPACPI_NOTICE
|
|
|
+ "fan_init: initial fan status is unknown, "
|
|
|
+ "assuming it is in auto mode\n");
|
|
|
+ tp_features.fan_ctrl_status_undef = 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -6804,9 +6859,27 @@ static const struct attribute_group fan_attr_group = {
|
|
|
.attrs = fan_attributes,
|
|
|
};
|
|
|
|
|
|
+#define TPACPI_FAN_Q1 0x0001
|
|
|
+
|
|
|
+#define TPACPI_FAN_QI(__id1, __id2, __quirks) \
|
|
|
+ { .vendor = PCI_VENDOR_ID_IBM, \
|
|
|
+ .bios = TPACPI_MATCH_ANY, \
|
|
|
+ .ec = TPID(__id1, __id2), \
|
|
|
+ .quirks = __quirks }
|
|
|
+
|
|
|
+static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
|
|
|
+ TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1),
|
|
|
+ TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1),
|
|
|
+ TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1),
|
|
|
+ TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1),
|
|
|
+};
|
|
|
+
|
|
|
+#undef TPACPI_FAN_QI
|
|
|
+
|
|
|
static int __init fan_init(struct ibm_init_struct *iibm)
|
|
|
{
|
|
|
int rc;
|
|
|
+ unsigned long quirks;
|
|
|
|
|
|
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
|
|
|
"initializing fan subdriver\n");
|
|
@@ -6823,6 +6896,9 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
|
|
TPACPI_ACPIHANDLE_INIT(gfan);
|
|
|
TPACPI_ACPIHANDLE_INIT(sfan);
|
|
|
|
|
|
+ quirks = tpacpi_check_quirks(fan_quirk_table,
|
|
|
+ ARRAY_SIZE(fan_quirk_table));
|
|
|
+
|
|
|
if (gfan_handle) {
|
|
|
/* 570, 600e/x, 770e, 770x */
|
|
|
fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
|
|
@@ -6832,7 +6908,8 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
|
|
if (likely(acpi_ec_read(fan_status_offset,
|
|
|
&fan_control_initial_status))) {
|
|
|
fan_status_access_mode = TPACPI_FAN_RD_TPEC;
|
|
|
- fan_quirk1_detect();
|
|
|
+ if (quirks & TPACPI_FAN_Q1)
|
|
|
+ fan_quirk1_setup();
|
|
|
} else {
|
|
|
printk(TPACPI_ERR
|
|
|
"ThinkPad ACPI EC access misbehaving, "
|