Re-use the w83627hf code from the Linux kernel to support the
NCT6116 chip found on the SIMATIC IPC BX-59A. The code was
greatly simplified to only support that particular chip.

The original intent was to use WDAT for this platform but it
turned out to be impossible because it would require holding
precious shared I/O registers.

See 
https://lore.kernel.org/all/df8d53db-0056-434d-953b-991025e6c...@roeck-us.net/

Signed-off-by: Cedric Hombourger <cedric.hombour...@siemens.com>
---
 Makefile.am                     |   1 +
 README.md                       |   1 +
 drivers/watchdog/w83627hf_wdt.c | 226 ++++++++++++++++++++++++++++++++
 include/simatic.h               |   5 +-
 4 files changed, 231 insertions(+), 2 deletions(-)
 create mode 100644 drivers/watchdog/w83627hf_wdt.c

diff --git a/Makefile.am b/Makefile.am
index 6d4c160..b7b3936 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -171,6 +171,7 @@ efi_sources_watchdogs = \
        drivers/watchdog/i6300esb.c \
        drivers/watchdog/atom-quark.c \
        drivers/watchdog/ipc4x7e_wdt.c \
+       drivers/watchdog/w83627hf_wdt.c \
        drivers/watchdog/ipmi_wdt.c \
        drivers/watchdog/itco.c \
        drivers/watchdog/hpwdt.c
diff --git a/README.md b/README.md
index c6d13fd..ef8a2fe 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@ The following watchdog drivers are implemented (and are 
probed in this order):
 * Intel i6300esb
 * Intel Quark
 * Siemens SIMATIC IPC4x7E
+* Siemens SIMATIC BX-59A
 * Intel TCO
 * HPE ProLiant
 
diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c
new file mode 100644
index 0000000..547fd29
--- /dev/null
+++ b/drivers/watchdog/w83627hf_wdt.c
@@ -0,0 +1,226 @@
+/*
+ * EFI Boot Guard
+ *
+ * Modified version of the w83627hf_wdt.c driver found in the Linux kernel
+ *
+ *     (c) Copyright 2013 Guenter Roeck
+ *             converted to watchdog infrastructure
+ *
+ *     (c) Copyright 2007 Vlad Drukker <v...@storewiz.com>
+ *             added support for W83627THF.
+ *
+ *     (c) Copyright 2003,2007 Pádraig Brady <p...@draigbrady.com>
+ *
+ *     Based on advantechwdt.c which is based on wdt.c.
+ *     Original copyright messages:
+ *
+ *     (c) Copyright 2000-2001 Marek Michalkiewicz <mar...@linux.org.pl>
+ *
+ *     (c) Copyright 1996 Alan Cox <a...@lxorguk.ukuu.org.uk>,
+ *                                             All Rights Reserved.
+ *
+ *     Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ *     warranty for any of this software. This material is provided
+ *     "AS-IS" and at no charge.
+ *
+ *     (c) Copyright 1995    Alan Cox <a...@lxorguk.ukuu.org.uk>
+ *
+ * Changes and EFI Boot Guard specific code:
+ *
+ *     by Cedric Hombourger <cedric.hombour...@siemens.com>
+ *     (c) Copyright (c) Siemens AG, 2023
+ *
+ * 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>
+#include <sys/io.h>
+#include <mmio.h>
+#include "simatic.h"
+#include "utils.h"
+
+/* Use the host bridge device found on the BX-59A to limit probing to Intel
+ * based machines that may be a BX59A; technically we do not use/need PCI
+ * for this driver but only port I/Os */
+#define PCI_DEVICE_ID_INTEL_HOST_BRIDGE        0xa700
+
+#define WDT_EFER (wdt_io+0)   /* Extended Function Enable Registers */
+#define WDT_EFIR (wdt_io+0)   /* Extended Function Index Register
+                                                       (same as EFER) */
+#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
+
+#define W83627HF_LD_WDT                0x08
+
+#define NCT6116_ID             0xd2
+
+#define NCT6102D_WDT_TIMEOUT   0xf1
+#define NCT6102D_WDT_CONTROL   0xf0
+#define NCT6102D_WDT_CSR       0xf2
+
+#define WDT_CSR_STATUS         0x10
+#define WDT_CSR_KBD            0x40
+#define WDT_CSR_MOUSE          0x80
+
+enum chips { nct6116 };
+
+static int wdt_io;
+static int cr_wdt_timeout;     /* WDT timeout register */
+static int cr_wdt_control;     /* WDT control register */
+static int cr_wdt_csr;         /* WDT control & status register */
+static int wdt_cfg_enter = 0x87;/* key to unlock configuration space */
+static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */
+
+static void superio_outb(int reg, int val)
+{
+       outb(reg, WDT_EFER);
+       outb(val, WDT_EFDR);
+}
+
+static inline int superio_inb(int reg)
+{
+       outb(reg, WDT_EFER);
+       return inb(WDT_EFDR);
+}
+
+static int superio_enter(void)
+{
+       outb_p(wdt_cfg_enter, WDT_EFER); /* Enter extended function mode */
+       outb_p(wdt_cfg_enter, WDT_EFER); /* Again according to manual */
+       return 0;
+}
+
+static void superio_select(int ld)
+{
+       superio_outb(0x07, ld);
+}
+
+static void superio_exit(void)
+{
+       outb_p(wdt_cfg_leave, WDT_EFER); /* Leave extended function mode */
+}
+
+static int wdt_find(int addr)
+{
+       UINT8 val;
+       int ret;
+
+       wdt_io = addr;
+       ret = superio_enter();
+       if (ret)
+               return ret;
+       superio_select(W83627HF_LD_WDT);
+       val = superio_inb(0x20);
+       switch(val) {
+       case NCT6116_ID:
+               ret = nct6116;
+               cr_wdt_timeout = NCT6102D_WDT_TIMEOUT;
+               cr_wdt_control = NCT6102D_WDT_CONTROL;
+               cr_wdt_csr = NCT6102D_WDT_CSR;
+               break;
+       default:
+               ret = -1;
+               break;
+       }
+       superio_exit();
+       return ret;
+}
+
+static int w83627hf_init(enum chips chip)
+{
+       int ret;
+       unsigned char t;
+
+       ret = superio_enter();
+       if (ret)
+               return ret;
+
+       superio_select(W83627HF_LD_WDT);
+
+       /* set CR30 bit 0 to activate GPIO2 */
+       t = superio_inb(0x30);
+       if (!(t & 0x01))
+               superio_outb(0x30, t | 0x01);
+
+       switch (chip) {
+       case nct6116:
+               /*
+                * These chips have a fixed WDTO# output pin (W83627UHG),
+                * or support more than one WDTO# output pin.
+                * Don't touch its configuration, and hope the BIOS
+                * does the right thing.
+                */
+               t = superio_inb(cr_wdt_control);
+               t |= 0x02;      /* enable the WDTO# output low pulse
+                                * to the KBRST# pin */
+               superio_outb(cr_wdt_control, t);
+               break;
+       default:
+               break;
+       }
+
+       t = superio_inb(cr_wdt_timeout);
+       if (t != 0) {
+               WARNING(L"Watchdog already running.\n");
+       }
+
+       /* set second mode & disable keyboard turning off watchdog */
+       t = superio_inb(cr_wdt_control) & ~0x0C;
+       superio_outb(cr_wdt_control, t);
+
+       /* reset status, disable keyboard & mouse turning off watchdog */
+       t = superio_inb(cr_wdt_csr);
+       t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD | WDT_CSR_MOUSE);
+       superio_outb(cr_wdt_csr, t);
+
+       superio_exit();
+
+       return 0;
+}
+
+static int wdt_set_time(unsigned int timeout)
+{
+       int ret;
+
+       ret = superio_enter();
+       if (ret)
+               return ret;
+
+       superio_select(W83627HF_LD_WDT);
+       superio_outb(cr_wdt_timeout, timeout);
+       superio_exit();
+
+       return 0;
+}
+
+static EFI_STATUS __attribute__((constructor))
+init(const EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 pci_device_id,
+     UINTN timeout)
+{
+       int chip, ret;
+
+       if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_INTEL ||
+           pci_device_id != PCI_DEVICE_ID_INTEL_HOST_BRIDGE) {
+               return EFI_UNSUPPORTED;
+       }
+
+       switch (simatic_station_id()) {
+       case SIMATIC_IPCBX_59A:
+               chip = wdt_find(0x2e);
+               if (chip < 0)
+                       return EFI_UNSUPPORTED;
+               INFO(L"Detected SIMATIC BX59A watchdog\n");
+               ret = w83627hf_init(chip);
+               if (ret < 0)
+                       return EFI_UNSUPPORTED;
+               ret = wdt_set_time(timeout);
+               if (ret < 0)
+                       return EFI_UNSUPPORTED;
+               return EFI_SUCCESS;
+       }
+       return EFI_UNSUPPORTED;
+}
diff --git a/include/simatic.h b/include/simatic.h
index 33429c5..724e79d 100644
--- a/include/simatic.h
+++ b/include/simatic.h
@@ -23,8 +23,9 @@
 
 #define SIMATIC_OEM_ENTRY_TYPE_BINARY          0xff
 
-#define SIMATIC_IPC427E                                0xa01
-#define SIMATIC_IPC477E                                0xa02
+#define SIMATIC_IPC427E                                0x0a01
+#define SIMATIC_IPC477E                                0x0a02
+#define SIMATIC_IPCBX_59A                      0x1202
 
 typedef struct {
        UINT8   type;
-- 
2.39.2

-- 
You received this message because you are subscribed to the Google Groups "EFI 
Boot Guard" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to efibootguard-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/efibootguard-dev/20231019153004.209654-3-cedric.hombourger%40siemens.com.

Reply via email to