|
|
|
|
@@ -309,6 +309,20 @@ struct ibm_init_struct {
|
|
|
|
|
struct ibm_struct *data;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* DMI Quirks */
|
|
|
|
|
struct quirk_entry {
|
|
|
|
|
bool btusb_bug;
|
|
|
|
|
u32 s2idle_bug_mmio;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct quirk_entry quirk_btusb_bug = {
|
|
|
|
|
.btusb_bug = true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct quirk_entry quirk_s2idle_bug = {
|
|
|
|
|
.s2idle_bug_mmio = 0xfed80380,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
|
u32 bluetooth:1;
|
|
|
|
|
u32 hotkey:1;
|
|
|
|
|
@@ -338,6 +352,7 @@ static struct {
|
|
|
|
|
u32 hotkey_poll_active:1;
|
|
|
|
|
u32 has_adaptive_kbd:1;
|
|
|
|
|
u32 kbd_lang:1;
|
|
|
|
|
struct quirk_entry *quirks;
|
|
|
|
|
} tp_features;
|
|
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
|
@@ -4359,9 +4374,10 @@ static void bluetooth_exit(void)
|
|
|
|
|
bluetooth_shutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const struct dmi_system_id bt_fwbug_list[] __initconst = {
|
|
|
|
|
static const struct dmi_system_id fwbug_list[] __initconst = {
|
|
|
|
|
{
|
|
|
|
|
.ident = "ThinkPad E485",
|
|
|
|
|
.driver_data = &quirk_btusb_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_BOARD_NAME, "20KU"),
|
|
|
|
|
@@ -4369,6 +4385,7 @@ static const struct dmi_system_id bt_fwbug_list[] __initconst = {
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "ThinkPad E585",
|
|
|
|
|
.driver_data = &quirk_btusb_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_BOARD_NAME, "20KV"),
|
|
|
|
|
@@ -4376,6 +4393,7 @@ static const struct dmi_system_id bt_fwbug_list[] __initconst = {
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "ThinkPad A285 - 20MW",
|
|
|
|
|
.driver_data = &quirk_btusb_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_BOARD_NAME, "20MW"),
|
|
|
|
|
@@ -4383,6 +4401,7 @@ static const struct dmi_system_id bt_fwbug_list[] __initconst = {
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "ThinkPad A285 - 20MX",
|
|
|
|
|
.driver_data = &quirk_btusb_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_BOARD_NAME, "20MX"),
|
|
|
|
|
@@ -4390,6 +4409,7 @@ static const struct dmi_system_id bt_fwbug_list[] __initconst = {
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "ThinkPad A485 - 20MU",
|
|
|
|
|
.driver_data = &quirk_btusb_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_BOARD_NAME, "20MU"),
|
|
|
|
|
@@ -4397,14 +4417,125 @@ static const struct dmi_system_id bt_fwbug_list[] __initconst = {
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "ThinkPad A485 - 20MV",
|
|
|
|
|
.driver_data = &quirk_btusb_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_BOARD_NAME, "20MV"),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "L14 Gen2 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "20X5"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "T14s Gen2 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "20XF"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "X13 Gen2 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "20XH"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "T14 Gen2 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "20XK"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "T14 Gen1 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "20UD"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "T14 Gen1 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "20UE"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "T14s Gen1 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "20UH"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "P14s Gen1 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "20Y1"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.ident = "P14s Gen2 AMD",
|
|
|
|
|
.driver_data = &quirk_s2idle_bug,
|
|
|
|
|
.matches = {
|
|
|
|
|
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
|
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "21A0"),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_SUSPEND
|
|
|
|
|
/*
|
|
|
|
|
* Lenovo laptops from a variety of generations run a SMI handler during the D3->D0
|
|
|
|
|
* transition that occurs specifically when exiting suspend to idle which can cause
|
|
|
|
|
* large delays during resume when the IOMMU translation layer is enabled (the default
|
|
|
|
|
* behavior) for NVME devices:
|
|
|
|
|
*
|
|
|
|
|
* To avoid this firmware problem, skip the SMI handler on these machines before the
|
|
|
|
|
* D0 transition occurs.
|
|
|
|
|
*/
|
|
|
|
|
static void thinkpad_acpi_amd_s2idle_restore(void)
|
|
|
|
|
{
|
|
|
|
|
struct resource *res;
|
|
|
|
|
void __iomem *addr;
|
|
|
|
|
u8 val;
|
|
|
|
|
|
|
|
|
|
res = request_mem_region_muxed(tp_features.quirks->s2idle_bug_mmio, 1,
|
|
|
|
|
"thinkpad_acpi_pm80");
|
|
|
|
|
if (!res)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
addr = ioremap(tp_features.quirks->s2idle_bug_mmio, 1);
|
|
|
|
|
if (!addr)
|
|
|
|
|
goto cleanup_resource;
|
|
|
|
|
|
|
|
|
|
val = ioread8(addr);
|
|
|
|
|
iowrite8(val & ~BIT(0), addr);
|
|
|
|
|
|
|
|
|
|
iounmap(addr);
|
|
|
|
|
cleanup_resource:
|
|
|
|
|
release_resource(res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct acpi_s2idle_dev_ops thinkpad_acpi_s2idle_dev_ops = {
|
|
|
|
|
.restore = thinkpad_acpi_amd_s2idle_restore,
|
|
|
|
|
};
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static const struct pci_device_id fwbug_cards_ids[] __initconst = {
|
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x24F3) },
|
|
|
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x24FD) },
|
|
|
|
|
@@ -4419,7 +4550,8 @@ static int __init have_bt_fwbug(void)
|
|
|
|
|
* Some AMD based ThinkPads have a firmware bug that calling
|
|
|
|
|
* "GBDC" will cause bluetooth on Intel wireless cards blocked
|
|
|
|
|
*/
|
|
|
|
|
if (dmi_check_system(bt_fwbug_list) && pci_dev_present(fwbug_cards_ids)) {
|
|
|
|
|
if (tp_features.quirks && tp_features.quirks->btusb_bug &&
|
|
|
|
|
pci_dev_present(fwbug_cards_ids)) {
|
|
|
|
|
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL,
|
|
|
|
|
FW_BUG "disable bluetooth subdriver for Intel cards\n");
|
|
|
|
|
return 1;
|
|
|
|
|
@@ -8748,24 +8880,27 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
|
|
|
|
fan_status_access_mode = TPACPI_FAN_RD_TPEC;
|
|
|
|
|
if (quirks & TPACPI_FAN_Q1)
|
|
|
|
|
fan_quirk1_setup();
|
|
|
|
|
if (quirks & TPACPI_FAN_2FAN) {
|
|
|
|
|
tp_features.second_fan = 1;
|
|
|
|
|
pr_info("secondary fan support enabled\n");
|
|
|
|
|
}
|
|
|
|
|
if (quirks & TPACPI_FAN_2CTL) {
|
|
|
|
|
tp_features.second_fan = 1;
|
|
|
|
|
tp_features.second_fan_ctl = 1;
|
|
|
|
|
pr_info("secondary fan control enabled\n");
|
|
|
|
|
}
|
|
|
|
|
/* Try and probe the 2nd fan */
|
|
|
|
|
tp_features.second_fan = 1; /* needed for get_speed to work */
|
|
|
|
|
res = fan2_get_speed(&speed);
|
|
|
|
|
if (res >= 0) {
|
|
|
|
|
/* It responded - so let's assume it's there */
|
|
|
|
|
tp_features.second_fan = 1;
|
|
|
|
|
tp_features.second_fan_ctl = 1;
|
|
|
|
|
pr_info("secondary fan control detected & enabled\n");
|
|
|
|
|
} else {
|
|
|
|
|
/* Fan not auto-detected */
|
|
|
|
|
tp_features.second_fan = 0;
|
|
|
|
|
if (quirks & TPACPI_FAN_2FAN) {
|
|
|
|
|
tp_features.second_fan = 1;
|
|
|
|
|
pr_info("secondary fan support enabled\n");
|
|
|
|
|
}
|
|
|
|
|
if (quirks & TPACPI_FAN_2CTL) {
|
|
|
|
|
tp_features.second_fan = 1;
|
|
|
|
|
tp_features.second_fan_ctl = 1;
|
|
|
|
|
pr_info("secondary fan control enabled\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
@@ -11455,6 +11590,10 @@ static void thinkpad_acpi_module_exit(void)
|
|
|
|
|
|
|
|
|
|
tpacpi_lifecycle = TPACPI_LIFE_EXITING;
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_SUSPEND
|
|
|
|
|
if (tp_features.quirks && tp_features.quirks->s2idle_bug_mmio)
|
|
|
|
|
acpi_unregister_lps0_dev(&thinkpad_acpi_s2idle_dev_ops);
|
|
|
|
|
#endif
|
|
|
|
|
if (tpacpi_hwmon)
|
|
|
|
|
hwmon_device_unregister(tpacpi_hwmon);
|
|
|
|
|
if (tp_features.sensors_pdrv_registered)
|
|
|
|
|
@@ -11496,6 +11635,7 @@ static void thinkpad_acpi_module_exit(void)
|
|
|
|
|
|
|
|
|
|
static int __init thinkpad_acpi_module_init(void)
|
|
|
|
|
{
|
|
|
|
|
const struct dmi_system_id *dmi_id;
|
|
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
|
|
tpacpi_lifecycle = TPACPI_LIFE_INIT;
|
|
|
|
|
@@ -11535,6 +11675,10 @@ static int __init thinkpad_acpi_module_init(void)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dmi_id = dmi_first_match(fwbug_list);
|
|
|
|
|
if (dmi_id)
|
|
|
|
|
tp_features.quirks = dmi_id->driver_data;
|
|
|
|
|
|
|
|
|
|
/* Device initialization */
|
|
|
|
|
tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, -1,
|
|
|
|
|
NULL, 0);
|
|
|
|
|
@@ -11623,6 +11767,13 @@ static int __init thinkpad_acpi_module_init(void)
|
|
|
|
|
tp_features.input_device_registered = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_SUSPEND
|
|
|
|
|
if (tp_features.quirks && tp_features.quirks->s2idle_bug_mmio) {
|
|
|
|
|
if (!acpi_register_lps0_dev(&thinkpad_acpi_s2idle_dev_ops))
|
|
|
|
|
pr_info("Using s2idle quirk to avoid %s platform firmware bug\n",
|
|
|
|
|
(dmi_id && dmi_id->ident) ? dmi_id->ident : "");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|