This adds support for AMD FCH Watchdog in efibootguard.
This watchdog is known to exist on the following AMD platforms:
* AMD Ryzen Embedded V1000 (V-Series APU)
* AMD Ryzen Embedded R1000 (R-Series APU)
* AMD EPYC Embedded E3000 (E-Series CPU)
Signed-off-by: Arsalan H. Awan <
Arsala...@mentor.com>
---
Makefile.am | 1 +
drivers/watchdog/amdfch_wdt.c | 290 ++++++++++++++++++++++++++++++++++
2 files changed, 291 insertions(+)
create mode 100644 drivers/watchdog/amdfch_wdt.c
diff --git a/Makefile.am b/Makefile.am
index 74fa7f0..0496384 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -123,6 +123,7 @@ efi_loadername = efibootguard$(MACHINE_TYPE_NAME).efi
efi_sources = \
drivers/watchdog/init_array_start.S \
+ drivers/watchdog/amdfch_wdt.c \
drivers/watchdog/i6300esb.c \
drivers/watchdog/atom-quark.c \
drivers/watchdog/itco.c \
diff --git a/drivers/watchdog/amdfch_wdt.c b/drivers/watchdog/amdfch_wdt.c
new file mode 100644
index 0000000..e9b7451
--- /dev/null
+++ b/drivers/watchdog/amdfch_wdt.c
@@ -0,0 +1,290 @@
+/*
+ * EFI Boot Guard
+ *
+ * Copyright (C) 2020 Mentor Graphics, A Siemens business
+ *
+ * Authors:
+ * Arsalan H. Awan <
Arsala...@mentor.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <efi.h>
+#include <efilib.h>
+#include <pci/header.h>
+
+/* #define AMDFCH_WDT_DEBUG */
+
+#define AMDFCH_WDT_MIN_TIMEOUT 0x0001 /* minimum timeout value */
+#define AMDFCH_WDT_MAX_TIMEOUT 0xFFFF /* maximum timeout value */
+
+/* Watchdog register definitions */
+#define AMD_ACPI_MMIO_BASE 0xFED80000
+#define AMDFCH_WDT_MEM_MAP_OFFSET 0xB00
+#define AMDFCH_WDT_MEM_MAP_SIZE 0x100
+
+#define AMDFCH_WDT_CONTROL(base) ((base) + 0x00) /* Watchdog Control */
+ #define AMDFCH_WDT_START_STOP_BIT (1 << 0)
+ #define AMDFCH_WDT_FIRED_BIT (1 << 1)
+ #define AMDFCH_WDT_ACTION_RESET_BIT (1 << 2)
+ #define AMDFCH_WDT_DISABLE_BIT (1 << 3)
+ /* 6:4 bits Reserved */
+ #define AMDFCH_WDT_TRIGGER_BIT (1 << 7)
+#define AMDFCH_WDT_COUNT(base) ((base) + 0x04) /* Watchdog Count */
+ #define AMDFCH_WDT_COUNT_MASK 0xFFFF
+
+#define AMD_PM_WATCHDOG_EN_REG 0x00
+ #define AMD_PM_WATCHDOG_TIMER_EN (0x01 << 7)
+
+#define AMD_PM_WATCHDOG_CONFIG_REG 0x03
+ #define AMD_PM_WATCHDOG_32USEC_RES 0x0
+ #define AMD_PM_WATCHDOG_10MSEC_RES 0x1
+ #define AMD_PM_WATCHDOG_100MSEC_RES 0x2
+ #define AMD_PM_WATCHDOG_1SEC_RES 0x3
+#define AMD_PM_WATCHDOG_CONFIG_MASK 0x3
+
+/* IO port address for indirect access using ACPI PM registers */
+#define AMD_IO_PM_INDEX_REG 0xCD6
+#define AMD_IO_PM_DATA_REG 0xCD7
+
+#define PCI_DEVICE_ID_AMD_CARRIZO_SMBUS 0x790B
+#define PCI_VENDOR_ID_AMD 0x1022
+
+static struct
+{
+ UINTN base;
+ EFI_PCI_IO *pci_io;
+} watchdog;
+
+static EFI_STATUS writel (UINT32 val, UINT32 addr)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n- writel (%X, %X) ", val, addr);
+#endif
+ EFI_STATUS status;
+
+ status = uefi_call_wrapper(watchdog.pci_io->Mem.Write, 6, watchdog.pci_io,
+ EfiPciIoWidthUint32,
+ EFI_PCI_IO_PASS_THROUGH_BAR,
+ addr, 1, &val);
+
+ if (EFI_ERROR(status)) {
+ Print(L"Error while writel (%X, %X): %r", val, addr, status);
+ }
+
+ return status;
+}
+
+static UINT32 readl (UINT32 addr)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n- readl (%X) ", addr);
+#endif
+ UINT32 val = 0;
+ EFI_STATUS status;
+
+ status = uefi_call_wrapper(watchdog.pci_io->Mem.Read, 6, watchdog.pci_io,
+ EfiPciIoWidthUint32,
+ EFI_PCI_IO_PASS_THROUGH_BAR,
+ addr, 1, &val);
+
+ if (EFI_ERROR(status)) {
+ Print(L"Error while readl (%X): %r", addr, status);
+ }
+
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"= %X ", val);
+#endif
+ return val;
+}
+
+static EFI_STATUS outb (UINT8 val, UINT16 reg)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n- outb (%X, %X) ", val, reg);
+#endif
+ EFI_STATUS status;
+
+ status = uefi_call_wrapper(watchdog.pci_io->Io.Write, 6, watchdog.pci_io,
+ EfiPciIoWidthUint8,
+ EFI_PCI_IO_PASS_THROUGH_BAR,
+ reg, 1, &val);
+ if (EFI_ERROR(status)) {
+ Print(L"Error while outb (%X, %X): %r", val, reg, status);
+ }
+
+ return status;
+}
+
+static UINT8 inb (UINT16 reg)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n- inb (%X) ", reg);
+#endif
+ EFI_STATUS status;
+ UINT8 val = 0;
+
+ status = uefi_call_wrapper(watchdog.pci_io->Io.Read, 6, watchdog.pci_io,
+ EfiPciIoWidthUint8,
+ EFI_PCI_IO_PASS_THROUGH_BAR,
+ reg, 1, &val);
+ if (EFI_ERROR(status)) {
+ Print(L"Error while inb (%X): %r", reg, status);
+ }
+
+#ifdef AMDFCH_WDT_DEBUG
+ Print(L"= %X ", val);
+#endif
+ return val;
+}
+
+static EFI_STATUS amdfch_wdt_enable (VOID)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n-- amdfch_wdt_enable() ");
+#endif
+ UINT8 val;
+ /* Enable watchdog timer */
+ outb(AMD_PM_WATCHDOG_EN_REG, AMD_IO_PM_INDEX_REG);
+ val = inb(AMD_IO_PM_DATA_REG);
+ val |= AMD_PM_WATCHDOG_TIMER_EN;
+ return outb(val, AMD_IO_PM_DATA_REG);
+}
+
+static EFI_STATUS amdfch_wdt_set_resolution (UINT8 freq)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n-- amdfch_wdt_set_resolution(%d) ", freq);
+#endif
+ UINT8 val;
+ /* Set the watchdog timer resolution */
+ outb(AMD_PM_WATCHDOG_CONFIG_REG, AMD_IO_PM_INDEX_REG);
+ val = inb(AMD_IO_PM_DATA_REG);
+ /* Clear the previous frequency setting, if any */
+ val &= ~AMD_PM_WATCHDOG_CONFIG_MASK;
+ /* Set the new frequency value */
+ val |= freq;
+ return outb(val, AMD_IO_PM_DATA_REG);
+}
+
+static EFI_STATUS amdfch_wdt_set_timeout_action (CONST CHAR16 * action)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n-- amdfch_wdt_set_timeout_action(%s) ", action);
+#endif
+ UINT32 val;
+ val = readl (AMDFCH_WDT_CONTROL(watchdog.base));
+
+ /*
+ * Set the watchdog timeout action.
+ *
+ * If action is specified anything other than reboot or shutdown,
+ * we default it to reboot.
+ */
+ if (StrnCmp(action, L"shutdown", 8) == 0)
+ val |= AMDFCH_WDT_ACTION_RESET_BIT;
+ else
+ val &= ~AMDFCH_WDT_ACTION_RESET_BIT;
+
+ return writel (val, AMDFCH_WDT_CONTROL(watchdog.base));
+}
+
+static EFI_STATUS amdfch_wdt_set_time (UINT32 t)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n-- amdfch_wdt_set_time(%d) ", t);
+#endif
+ if (t < AMDFCH_WDT_MIN_TIMEOUT)
+ t = AMDFCH_WDT_MIN_TIMEOUT;
+ else if (t > AMDFCH_WDT_MAX_TIMEOUT)
+ t = AMDFCH_WDT_MAX_TIMEOUT;
+
+ /* Write new timeout value to watchdog COUNT register */
+ return writel (t, AMDFCH_WDT_COUNT(watchdog.base));
+}
+
+static EFI_STATUS amdfch_wdt_start (VOID)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n-- amdfch_wdt_start() ");
+#endif
+ UINT32 val;
+ /* Start the watchdog timer */
+ val = readl (AMDFCH_WDT_CONTROL(watchdog.base));
+ val |= AMDFCH_WDT_START_STOP_BIT;
+ return writel (val, AMDFCH_WDT_CONTROL(watchdog.base));
+}
+
+static EFI_STATUS amdfch_wdt_ping (VOID)
+{
+#ifdef AMDFCH_WDT_DEBUG
+ Print (L"\n-- amdfch_wdt_ping() ");
+#endif
+ UINT32 val;
+ /* Trigger/Ping watchdog timer */
+ val = readl (AMDFCH_WDT_CONTROL(watchdog.base));
+ val |= AMDFCH_WDT_TRIGGER_BIT;
+ return writel (val, AMDFCH_WDT_CONTROL(watchdog.base));
+}
+
+static EFI_STATUS __attribute__((constructor))
+init (EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 pci_device_id,
+ UINTN timeout)
+{
+ EFI_STATUS status;
+
+ if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_AMD ||
+ pci_device_id != PCI_DEVICE_ID_AMD_CARRIZO_SMBUS) {
+ return EFI_UNSUPPORTED;
+ }
+
+ watchdog.base = AMD_ACPI_MMIO_BASE + AMDFCH_WDT_MEM_MAP_OFFSET;
+ watchdog.pci_io = pci_io;
+
+ Print(L"Detected AMD FCH Watchdog Timer\n");
+
+#ifdef AMDFCH_WDT_DEBUG
+ Print(L"\nwatchdog.base = %X\n", watchdog.base);
+#endif
+
+ status = amdfch_wdt_enable ();
+ if (EFI_ERROR(status)) {
+ Print(L"Error: amdfch_wdt_enable () failed.");
+ return status;
+ }
+
+ status = amdfch_wdt_set_resolution (AMD_PM_WATCHDOG_1SEC_RES);
+ if (EFI_ERROR(status)) {
+ Print(L"Error: amdfch_wdt_set_resolution (%d) failed.", AMD_PM_WATCHDOG_1SEC_RES);
+ return status;
+ }
+
+ status = amdfch_wdt_set_timeout_action (L"reboot");
+ if (EFI_ERROR(status)) {
+ Print(L"Error: amdfch_wdt_set_timeout_action (\"reboot\") failed.");
+ return status;
+ }
+
+ status = amdfch_wdt_set_time (timeout);
+ if (EFI_ERROR(status)) {
+ Print(L"Error: amdfch_wdt_set_time (%d) failed.", timeout);
+ return status;
+ }
+
+ status = amdfch_wdt_start ();
+ if (EFI_ERROR(status)) {
+ Print(L"Error: amdfch_wdt_start () failed.");
+ return status;
+ }
+
+ status = amdfch_wdt_ping ();
+ if (EFI_ERROR(status)) {
+ Print(L"Error: amdfch_wdt_ping () failed.");
+ return status;
+ }
+
+ return status;
+}
--
2.17.1