Merge tag 'pwm/for-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
[sfrench/cifs-2.6.git] / drivers / acpi / acpi_watchdog.c
index 4bde16fb97d8818f59e893adf9bb642fed5f9d5c..95600309ce420a21d32f50f6b8095a6fe335fb78 100644 (file)
 #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;
 }