2 * ACPI watchdog table parsing support.
4 * Copyright (C) 2016, Intel Corporation
5 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #define pr_fmt(fmt) "ACPI: watchdog: " fmt
14 #include <linux/acpi.h>
15 #include <linux/dmi.h>
16 #include <linux/ioport.h>
17 #include <linux/platform_device.h>
21 static const struct dmi_system_id acpi_watchdog_skip
[] = {
24 * On Lenovo Z50-70 there are two issues with the WDAT
25 * table. First some of the instructions use RTC SRAM
26 * to store persistent information. This does not work well
27 * with Linux RTC driver. Second, more important thing is
28 * that the instructions do not actually reset the system.
30 * On this particular system iTCO_wdt seems to work just
31 * fine so we prefer that over WDAT for now.
33 * See also https://bugzilla.kernel.org/show_bug.cgi?id=199033.
35 .ident
= "Lenovo Z50-70",
37 DMI_MATCH(DMI_SYS_VENDOR
, "LENOVO"),
38 DMI_MATCH(DMI_PRODUCT_NAME
, "20354"),
39 DMI_MATCH(DMI_PRODUCT_VERSION
, "Lenovo Z50-70"),
45 static const struct acpi_table_wdat
*acpi_watchdog_get_wdat(void)
47 const struct acpi_table_wdat
*wdat
= NULL
;
53 if (dmi_check_system(acpi_watchdog_skip
))
56 status
= acpi_get_table(ACPI_SIG_WDAT
, 0,
57 (struct acpi_table_header
**)&wdat
);
58 if (ACPI_FAILURE(status
)) {
59 /* It is fine if there is no WDAT */
67 * Returns true if this system should prefer ACPI based watchdog instead of
68 * the native one (which are typically the same hardware).
70 bool acpi_has_watchdog(void)
72 return !!acpi_watchdog_get_wdat();
74 EXPORT_SYMBOL_GPL(acpi_has_watchdog
);
76 void __init
acpi_watchdog_init(void)
78 const struct acpi_wdat_entry
*entries
;
79 const struct acpi_table_wdat
*wdat
;
80 struct list_head resource_list
;
81 struct resource_entry
*rentry
;
82 struct platform_device
*pdev
;
83 struct resource
*resources
;
84 size_t nresources
= 0;
87 wdat
= acpi_watchdog_get_wdat();
89 /* It is fine if there is no WDAT */
93 /* Watchdog disabled by BIOS */
94 if (!(wdat
->flags
& ACPI_WDAT_ENABLED
))
97 /* Skip legacy PCI WDT devices */
98 if (wdat
->pci_segment
!= 0xff || wdat
->pci_bus
!= 0xff ||
99 wdat
->pci_device
!= 0xff || wdat
->pci_function
!= 0xff)
102 INIT_LIST_HEAD(&resource_list
);
104 entries
= (struct acpi_wdat_entry
*)(wdat
+ 1);
105 for (i
= 0; i
< wdat
->entries
; i
++) {
106 const struct acpi_generic_address
*gas
;
107 struct resource_entry
*rentry
;
108 struct resource res
= {};
111 gas
= &entries
[i
].register_region
;
113 res
.start
= gas
->address
;
114 if (gas
->space_id
== ACPI_ADR_SPACE_SYSTEM_MEMORY
) {
115 res
.flags
= IORESOURCE_MEM
;
116 res
.end
= res
.start
+ ALIGN(gas
->access_width
, 4) - 1;
117 } else if (gas
->space_id
== ACPI_ADR_SPACE_SYSTEM_IO
) {
118 res
.flags
= IORESOURCE_IO
;
119 res
.end
= res
.start
+ gas
->access_width
- 1;
121 pr_warn("Unsupported address space: %u\n",
123 goto fail_free_resource_list
;
127 resource_list_for_each_entry(rentry
, &resource_list
) {
128 if (rentry
->res
->flags
== res
.flags
&&
129 resource_overlaps(rentry
->res
, &res
)) {
130 if (res
.start
< rentry
->res
->start
)
131 rentry
->res
->start
= res
.start
;
132 if (res
.end
> rentry
->res
->end
)
133 rentry
->res
->end
= res
.end
;
140 rentry
= resource_list_create_entry(NULL
, 0);
142 goto fail_free_resource_list
;
145 resource_list_add_tail(rentry
, &resource_list
);
150 resources
= kcalloc(nresources
, sizeof(*resources
), GFP_KERNEL
);
152 goto fail_free_resource_list
;
155 resource_list_for_each_entry(rentry
, &resource_list
)
156 resources
[i
++] = *rentry
->res
;
158 pdev
= platform_device_register_simple("wdat_wdt", PLATFORM_DEVID_NONE
,
159 resources
, nresources
);
161 pr_err("Device creation failed: %ld\n", PTR_ERR(pdev
));
165 fail_free_resource_list
:
166 resource_list_free(&resource_list
);