0001
0002
0003
0004
0005
0006
0007
0008
0009 #define pr_fmt(fmt) "ACPI: watchdog: " fmt
0010
0011 #include <linux/acpi.h>
0012 #include <linux/ioport.h>
0013 #include <linux/platform_device.h>
0014
0015 #include "internal.h"
0016
0017 #ifdef CONFIG_RTC_MC146818_LIB
0018 #include <linux/mc146818rtc.h>
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028 static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat)
0029 {
0030 const struct acpi_wdat_entry *entries;
0031 int i;
0032
0033 entries = (struct acpi_wdat_entry *)(wdat + 1);
0034 for (i = 0; i < wdat->entries; i++) {
0035 const struct acpi_generic_address *gas;
0036
0037 gas = &entries[i].register_region;
0038 if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
0039 switch (gas->address) {
0040 case RTC_PORT(0):
0041 case RTC_PORT(1):
0042 case RTC_PORT(2):
0043 case RTC_PORT(3):
0044 return true;
0045 }
0046 }
0047 }
0048
0049 return false;
0050 }
0051 #else
0052 static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat)
0053 {
0054 return false;
0055 }
0056 #endif
0057
0058 static bool acpi_no_watchdog;
0059
0060 static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
0061 {
0062 const struct acpi_table_wdat *wdat = NULL;
0063 acpi_status status;
0064
0065 if (acpi_disabled || acpi_no_watchdog)
0066 return NULL;
0067
0068 status = acpi_get_table(ACPI_SIG_WDAT, 0,
0069 (struct acpi_table_header **)&wdat);
0070 if (ACPI_FAILURE(status)) {
0071
0072 return NULL;
0073 }
0074
0075 if (acpi_watchdog_uses_rtc(wdat)) {
0076 acpi_put_table((struct acpi_table_header *)wdat);
0077 pr_info("Skipping WDAT on this system because it uses RTC SRAM\n");
0078 return NULL;
0079 }
0080
0081 return wdat;
0082 }
0083
0084
0085
0086
0087
0088 bool acpi_has_watchdog(void)
0089 {
0090 return !!acpi_watchdog_get_wdat();
0091 }
0092 EXPORT_SYMBOL_GPL(acpi_has_watchdog);
0093
0094
0095 static int __init disable_acpi_watchdog(char *str)
0096 {
0097 acpi_no_watchdog = true;
0098 return 1;
0099 }
0100 __setup("acpi_no_watchdog", disable_acpi_watchdog);
0101
0102 void __init acpi_watchdog_init(void)
0103 {
0104 const struct acpi_wdat_entry *entries;
0105 const struct acpi_table_wdat *wdat;
0106 struct list_head resource_list;
0107 struct resource_entry *rentry;
0108 struct platform_device *pdev;
0109 struct resource *resources;
0110 size_t nresources = 0;
0111 int i;
0112
0113 wdat = acpi_watchdog_get_wdat();
0114 if (!wdat) {
0115
0116 return;
0117 }
0118
0119
0120 if (!(wdat->flags & ACPI_WDAT_ENABLED))
0121 goto fail_put_wdat;
0122
0123
0124 if (wdat->pci_segment != 0xff || wdat->pci_bus != 0xff ||
0125 wdat->pci_device != 0xff || wdat->pci_function != 0xff)
0126 goto fail_put_wdat;
0127
0128 INIT_LIST_HEAD(&resource_list);
0129
0130 entries = (struct acpi_wdat_entry *)(wdat + 1);
0131 for (i = 0; i < wdat->entries; i++) {
0132 const struct acpi_generic_address *gas;
0133 struct resource_entry *rentry;
0134 struct resource res = {};
0135 bool found;
0136
0137 gas = &entries[i].register_region;
0138
0139 res.start = gas->address;
0140 res.end = res.start + ACPI_ACCESS_BYTE_WIDTH(gas->access_width) - 1;
0141 if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
0142 res.flags = IORESOURCE_MEM;
0143 } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
0144 res.flags = IORESOURCE_IO;
0145 } else {
0146 pr_warn("Unsupported address space: %u\n",
0147 gas->space_id);
0148 goto fail_free_resource_list;
0149 }
0150
0151 found = false;
0152 resource_list_for_each_entry(rentry, &resource_list) {
0153 if (rentry->res->flags == res.flags &&
0154 resource_union(rentry->res, &res, rentry->res)) {
0155 found = true;
0156 break;
0157 }
0158 }
0159
0160 if (!found) {
0161 rentry = resource_list_create_entry(NULL, 0);
0162 if (!rentry)
0163 goto fail_free_resource_list;
0164
0165 *rentry->res = res;
0166 resource_list_add_tail(rentry, &resource_list);
0167 nresources++;
0168 }
0169 }
0170
0171 resources = kcalloc(nresources, sizeof(*resources), GFP_KERNEL);
0172 if (!resources)
0173 goto fail_free_resource_list;
0174
0175 i = 0;
0176 resource_list_for_each_entry(rentry, &resource_list)
0177 resources[i++] = *rentry->res;
0178
0179 pdev = platform_device_register_simple("wdat_wdt", PLATFORM_DEVID_NONE,
0180 resources, nresources);
0181 if (IS_ERR(pdev))
0182 pr_err("Device creation failed: %ld\n", PTR_ERR(pdev));
0183
0184 kfree(resources);
0185
0186 fail_free_resource_list:
0187 resource_list_free(&resource_list);
0188 fail_put_wdat:
0189 acpi_put_table((struct acpi_table_header *)wdat);
0190 }