diff options
Diffstat (limited to 'drivers/acpi/acpi_watchdog.c')
| -rw-r--r-- | drivers/acpi/acpi_watchdog.c | 72 | 
1 files changed, 45 insertions, 27 deletions
diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c index 4bde16fb97d8..95600309ce42 100644 --- a/drivers/acpi/acpi_watchdog.c +++ b/drivers/acpi/acpi_watchdog.c @@ -12,35 +12,51 @@  #define pr_fmt(fmt) "ACPI: watchdog: " fmt  #include <linux/acpi.h> -#include <linux/dmi.h>  #include <linux/ioport.h>  #include <linux/platform_device.h>  #include "internal.h" -static const struct dmi_system_id acpi_watchdog_skip[] = { -	{ -		/* -		 * On Lenovo Z50-70 there are two issues with the WDAT -		 * table. First some of the instructions use RTC SRAM -		 * to store persistent information. This does not work well -		 * with Linux RTC driver. Second, more important thing is -		 * that the instructions do not actually reset the system. -		 * -		 * On this particular system iTCO_wdt seems to work just -		 * fine so we prefer that over WDAT for now. -		 * -		 * See also https://bugzilla.kernel.org/show_bug.cgi?id=199033. -		 */ -		.ident = "Lenovo Z50-70", -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), -			DMI_MATCH(DMI_PRODUCT_NAME, "20354"), -			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Z50-70"), -		}, -	}, -	{} -}; +#ifdef CONFIG_RTC_MC146818_LIB +#include <linux/mc146818rtc.h> + +/* + * There are several systems where the WDAT table is accessing RTC SRAM to + * store persistent information. This does not work well with the Linux RTC + * driver so on those systems we skip WDAT driver and prefer iTCO_wdt + * instead. + * + * See also https://bugzilla.kernel.org/show_bug.cgi?id=199033. + */ +static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat) +{ +	const struct acpi_wdat_entry *entries; +	int i; + +	entries = (struct acpi_wdat_entry *)(wdat + 1); +	for (i = 0; i < wdat->entries; i++) { +		const struct acpi_generic_address *gas; + +		gas = &entries[i].register_region; +		if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { +			switch (gas->address) { +			case RTC_PORT(0): +			case RTC_PORT(1): +			case RTC_PORT(2): +			case RTC_PORT(3): +				return true; +			} +		} +	} + +	return false; +} +#else +static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat) +{ +	return false; +} +#endif  static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)  { @@ -50,9 +66,6 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)  	if (acpi_disabled)  		return NULL; -	if (dmi_check_system(acpi_watchdog_skip)) -		return NULL; -  	status = acpi_get_table(ACPI_SIG_WDAT, 0,  				(struct acpi_table_header **)&wdat);  	if (ACPI_FAILURE(status)) { @@ -60,6 +73,11 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)  		return NULL;  	} +	if (acpi_watchdog_uses_rtc(wdat)) { +		pr_info("Skipping WDAT on this system because it uses RTC SRAM\n"); +		return NULL; +	} +  	return wdat;  }  | 
