root/drivers/acpi/acpi_watchdog.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. acpi_watchdog_uses_rtc
  2. acpi_watchdog_uses_rtc
  3. acpi_watchdog_get_wdat
  4. acpi_has_watchdog
  5. disable_acpi_watchdog
  6. acpi_watchdog_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * ACPI watchdog table parsing support.
   4  *
   5  * Copyright (C) 2016, Intel Corporation
   6  * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
   7  */
   8 
   9 #define pr_fmt(fmt) "ACPI: watchdog: " fmt
  10 
  11 #include <linux/acpi.h>
  12 #include <linux/ioport.h>
  13 #include <linux/platform_device.h>
  14 
  15 #include "internal.h"
  16 
  17 #ifdef CONFIG_RTC_MC146818_LIB
  18 #include <linux/mc146818rtc.h>
  19 
  20 /*
  21  * There are several systems where the WDAT table is accessing RTC SRAM to
  22  * store persistent information. This does not work well with the Linux RTC
  23  * driver so on those systems we skip WDAT driver and prefer iTCO_wdt
  24  * instead.
  25  *
  26  * See also https://bugzilla.kernel.org/show_bug.cgi?id=199033.
  27  */
  28 static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat)
  29 {
  30         const struct acpi_wdat_entry *entries;
  31         int i;
  32 
  33         entries = (struct acpi_wdat_entry *)(wdat + 1);
  34         for (i = 0; i < wdat->entries; i++) {
  35                 const struct acpi_generic_address *gas;
  36 
  37                 gas = &entries[i].register_region;
  38                 if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
  39                         switch (gas->address) {
  40                         case RTC_PORT(0):
  41                         case RTC_PORT(1):
  42                         case RTC_PORT(2):
  43                         case RTC_PORT(3):
  44                                 return true;
  45                         }
  46                 }
  47         }
  48 
  49         return false;
  50 }
  51 #else
  52 static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat)
  53 {
  54         return false;
  55 }
  56 #endif
  57 
  58 static bool acpi_no_watchdog;
  59 
  60 static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
  61 {
  62         const struct acpi_table_wdat *wdat = NULL;
  63         acpi_status status;
  64 
  65         if (acpi_disabled || acpi_no_watchdog)
  66                 return NULL;
  67 
  68         status = acpi_get_table(ACPI_SIG_WDAT, 0,
  69                                 (struct acpi_table_header **)&wdat);
  70         if (ACPI_FAILURE(status)) {
  71                 /* It is fine if there is no WDAT */
  72                 return NULL;
  73         }
  74 
  75         if (acpi_watchdog_uses_rtc(wdat)) {
  76                 pr_info("Skipping WDAT on this system because it uses RTC SRAM\n");
  77                 return NULL;
  78         }
  79 
  80         return wdat;
  81 }
  82 
  83 /**
  84  * Returns true if this system should prefer ACPI based watchdog instead of
  85  * the native one (which are typically the same hardware).
  86  */
  87 bool acpi_has_watchdog(void)
  88 {
  89         return !!acpi_watchdog_get_wdat();
  90 }
  91 EXPORT_SYMBOL_GPL(acpi_has_watchdog);
  92 
  93 /* ACPI watchdog can be disabled on boot command line */
  94 static int __init disable_acpi_watchdog(char *str)
  95 {
  96         acpi_no_watchdog = true;
  97         return 1;
  98 }
  99 __setup("acpi_no_watchdog", disable_acpi_watchdog);
 100 
 101 void __init acpi_watchdog_init(void)
 102 {
 103         const struct acpi_wdat_entry *entries;
 104         const struct acpi_table_wdat *wdat;
 105         struct list_head resource_list;
 106         struct resource_entry *rentry;
 107         struct platform_device *pdev;
 108         struct resource *resources;
 109         size_t nresources = 0;
 110         int i;
 111 
 112         wdat = acpi_watchdog_get_wdat();
 113         if (!wdat) {
 114                 /* It is fine if there is no WDAT */
 115                 return;
 116         }
 117 
 118         /* Watchdog disabled by BIOS */
 119         if (!(wdat->flags & ACPI_WDAT_ENABLED))
 120                 return;
 121 
 122         /* Skip legacy PCI WDT devices */
 123         if (wdat->pci_segment != 0xff || wdat->pci_bus != 0xff ||
 124             wdat->pci_device != 0xff || wdat->pci_function != 0xff)
 125                 return;
 126 
 127         INIT_LIST_HEAD(&resource_list);
 128 
 129         entries = (struct acpi_wdat_entry *)(wdat + 1);
 130         for (i = 0; i < wdat->entries; i++) {
 131                 const struct acpi_generic_address *gas;
 132                 struct resource_entry *rentry;
 133                 struct resource res = {};
 134                 bool found;
 135 
 136                 gas = &entries[i].register_region;
 137 
 138                 res.start = gas->address;
 139                 res.end = res.start + ACPI_ACCESS_BYTE_WIDTH(gas->access_width) - 1;
 140                 if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
 141                         res.flags = IORESOURCE_MEM;
 142                 } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
 143                         res.flags = IORESOURCE_IO;
 144                 } else {
 145                         pr_warn("Unsupported address space: %u\n",
 146                                 gas->space_id);
 147                         goto fail_free_resource_list;
 148                 }
 149 
 150                 found = false;
 151                 resource_list_for_each_entry(rentry, &resource_list) {
 152                         if (rentry->res->flags == res.flags &&
 153                             resource_overlaps(rentry->res, &res)) {
 154                                 if (res.start < rentry->res->start)
 155                                         rentry->res->start = res.start;
 156                                 if (res.end > rentry->res->end)
 157                                         rentry->res->end = res.end;
 158                                 found = true;
 159                                 break;
 160                         }
 161                 }
 162 
 163                 if (!found) {
 164                         rentry = resource_list_create_entry(NULL, 0);
 165                         if (!rentry)
 166                                 goto fail_free_resource_list;
 167 
 168                         *rentry->res = res;
 169                         resource_list_add_tail(rentry, &resource_list);
 170                         nresources++;
 171                 }
 172         }
 173 
 174         resources = kcalloc(nresources, sizeof(*resources), GFP_KERNEL);
 175         if (!resources)
 176                 goto fail_free_resource_list;
 177 
 178         i = 0;
 179         resource_list_for_each_entry(rentry, &resource_list)
 180                 resources[i++] = *rentry->res;
 181 
 182         pdev = platform_device_register_simple("wdat_wdt", PLATFORM_DEVID_NONE,
 183                                                resources, nresources);
 184         if (IS_ERR(pdev))
 185                 pr_err("Device creation failed: %ld\n", PTR_ERR(pdev));
 186 
 187         kfree(resources);
 188 
 189 fail_free_resource_list:
 190         resource_list_free(&resource_list);
 191 }

/* [<][>][^][v][top][bottom][index][help] */