SIMATIC IPCs 227E/277E share the same external watchdog (SCH5347)
than the IPC 4x7Es we already support. Initialization of the
SAFE_EN_N line differs a bit and was therefore moved to separate
setup functions. Programming and enabling of the timeout is the
same and is now handled in set_timeout(). With this change, both
Linux and efibootguard would use the same chip and no longer
depend on the built-in iTCO watchdog.

Signed-off-by: Cedric Hombourger <[email protected]>
---
 drivers/watchdog/ipc4x7e_wdt.c | 126 +++++++++++++++++++++------------
 1 file changed, 81 insertions(+), 45 deletions(-)

diff --git a/drivers/watchdog/ipc4x7e_wdt.c b/drivers/watchdog/ipc4x7e_wdt.c
index 24faebe..12a9a87 100644
--- a/drivers/watchdog/ipc4x7e_wdt.c
+++ b/drivers/watchdog/ipc4x7e_wdt.c
@@ -25,6 +25,8 @@
 
 #define SIMATIC_OEM_ENTRY_TYPE_BINARY          0xff
 
+#define SIMATIC_IPC227E                                0x901
+#define SIMATIC_IPC277E                                0x902
 #define SIMATIC_IPC427E                                0xa01
 #define SIMATIC_IPC477E                                0xa02
 
@@ -55,6 +57,8 @@ typedef struct {
 /* drives SAFE_EN_N */
 #define PAD_CFG_DW0_GPP_A_23                   0x4b8
 #define  PAD_CFG_GPIOTXSTATE                   (1 << 0)
+#define GP_STATUS_REG_227E                     0x404D
+#define SAFE_EN_N_227E                         0x04
 
 static UINT32 get_station_id(SMBIOS_STRUCTURE_POINTER oem_strct)
 {
@@ -134,6 +138,70 @@ static UINT64 get_sbreg_rba(VOID)
        return sbreg;
 }
 
+/*
+ * created from linux/drivers/watchdog/simatic-ipc-wdt.c
+ * (submitted upstream but hasn't reached mainline)
+ */
+static VOID setup_2x7E(VOID)
+{
+       UINT16 resetbit;
+
+       /* enable SAFE_EN_N on GP_STATUS_REG_227E */
+       resetbit = inw(GP_STATUS_REG_227E);
+       resetbit &= ~SAFE_EN_N_227E;
+       outw(resetbit, GP_STATUS_REG_227E);
+}
+
+static VOID setup_4x7E(VOID)
+{
+       UINTN pad_cfg;
+
+       /*
+        * Drive SAFE_EN_N low to allow that watchdog can trigger a
+        * hard reset.
+        */
+       pad_cfg = get_sbreg_rba() + (GPIO_COMMUNITY0_PORT_ID << 16) +
+               PAD_CFG_DW0_GPP_A_23;
+       writel(readl(pad_cfg) & ~PAD_CFG_GPIOTXSTATE, pad_cfg);
+}
+
+static void set_timeout(UINTN timeout)
+{
+       UINT8 val;
+
+       if (timeout <= 2) {
+               val = 0 << SIMATIC_WD_SCALER_SHIFT;
+       } else if (timeout <= 4) {
+               val = 1 << SIMATIC_WD_SCALER_SHIFT;
+       } else if (timeout <= 6) {
+               val = 2 << SIMATIC_WD_SCALER_SHIFT;
+       } else if (timeout <= 8) {
+               val = 3 << SIMATIC_WD_SCALER_SHIFT;
+       } else if (timeout <= 16) {
+               val = 4 << SIMATIC_WD_SCALER_SHIFT;
+       } else if (timeout <= 32) {
+               val = 5 << SIMATIC_WD_SCALER_SHIFT;
+       } else if (timeout <= 48) {
+               val = 6 << SIMATIC_WD_SCALER_SHIFT;
+       } else {
+               val = 7 << SIMATIC_WD_SCALER_SHIFT;
+       }
+       val |= SIMATIC_WD_MACRO_MOD;
+       if (inb(SIMATIC_WD_ENABLE_REG) & SIMATIC_WD_TRIGGERED) {
+               Print(L"NOTE: Detected watchdog triggered reboot\n");
+               /* acknowledge, turning off the LED */
+               val |= SIMATIC_WD_TRIGGERED;
+       }
+       outb(val, SIMATIC_WD_ENABLE_REG);
+
+       /* Enable the watchdog after programming it, just to be safe. */
+       val |= SIMATIC_WD_ENABLE;
+       outb(val, SIMATIC_WD_ENABLE_REG);
+
+       /* Trigger the watchdog once. Now the clock ticks. */
+       inb(SIMATIC_WD_TRIGGER_REG);
+}
+
 static EFI_STATUS __attribute__((constructor))
 init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 pci_device_id,
      UINTN timeout)
@@ -141,13 +209,6 @@ init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 
pci_device_id,
        SMBIOS_STRUCTURE_TABLE *smbios_table;
        SMBIOS_STRUCTURE_POINTER smbios_struct;
        EFI_STATUS status;
-       UINTN pad_cfg;
-       UINT8 val;
-
-       if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_INTEL ||
-           pci_device_id != PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_LPC) {
-               return EFI_UNSUPPORTED;
-       }
 
        status = LibGetSystemConfigurationTable(&SMBIOSTableGuid,
                                                (VOID **)&smbios_table);
@@ -161,50 +222,25 @@ init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 
pci_device_id,
        }
 
        switch (get_station_id(smbios_struct)) {
+       case SIMATIC_IPC227E:
+       case SIMATIC_IPC277E:
+               Print(L"Detected SIMATIC IPC2x7E watchdog\n");
+
+               setup_2x7E();
+               set_timeout(timeout);
+               return EFI_SUCCESS;
+
        case SIMATIC_IPC427E:
        case SIMATIC_IPC477E:
                Print(L"Detected SIMATIC IPC4x7E watchdog\n");
 
-               /*
-                * Drive SAFE_EN_N low to allow that watchdog can trigger a
-                * hard reset.
-                */
-               pad_cfg = get_sbreg_rba() + (GPIO_COMMUNITY0_PORT_ID << 16) +
-                       PAD_CFG_DW0_GPP_A_23;
-               writel(readl(pad_cfg) & ~PAD_CFG_GPIOTXSTATE, pad_cfg);
-
-               if (timeout <= 2) {
-                       val = 0 << SIMATIC_WD_SCALER_SHIFT;
-               } else if (timeout <= 4) {
-                       val = 1 << SIMATIC_WD_SCALER_SHIFT;
-               } else if (timeout <= 6) {
-                       val = 2 << SIMATIC_WD_SCALER_SHIFT;
-               } else if (timeout <= 8) {
-                       val = 3 << SIMATIC_WD_SCALER_SHIFT;
-               } else if (timeout <= 16) {
-                       val = 4 << SIMATIC_WD_SCALER_SHIFT;
-               } else if (timeout <= 32) {
-                       val = 5 << SIMATIC_WD_SCALER_SHIFT;
-               } else if (timeout <= 48) {
-                       val = 6 << SIMATIC_WD_SCALER_SHIFT;
-               } else {
-                       val = 7 << SIMATIC_WD_SCALER_SHIFT;
+               if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_INTEL ||
+                   pci_device_id != PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_LPC) {
+                       return EFI_UNSUPPORTED;
                }
-               val |= SIMATIC_WD_MACRO_MOD;
-               if (inb(SIMATIC_WD_ENABLE_REG) & SIMATIC_WD_TRIGGERED) {
-                       Print(L"NOTE: Detected watchdog triggered reboot\n");
-                       /* acknowledge, turning off the LED */
-                       val |= SIMATIC_WD_TRIGGERED;
-               }
-               outb(val, SIMATIC_WD_ENABLE_REG);
-
-               /* Enable the watchdog after programming it, just to be safe. */
-               val |= SIMATIC_WD_ENABLE;
-               outb(val, SIMATIC_WD_ENABLE_REG);
-
-               /* Trigger the watchdog once. Now the clock ticks. */
-               inb(SIMATIC_WD_TRIGGER_REG);
 
+               setup_4x7E();
+               set_timeout(timeout);
                return EFI_SUCCESS;
 
        default:
-- 
2.30.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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/efibootguard-dev/20210712133719.305-1-Cedric_Hombourger%40mentor.com.

Reply via email to