Signed-off-by: Mathieu Tetreault <[email protected]>
---
 drivers/watchdog/itco.c | 122 +++++++++++++++++++++++++++++++---------
 1 file changed, 95 insertions(+), 27 deletions(-)

diff --git a/drivers/watchdog/itco.c b/drivers/watchdog/itco.c
index 5a21a06..6e5c5be 100644
--- a/drivers/watchdog/itco.c
+++ b/drivers/watchdog/itco.c
@@ -18,6 +18,7 @@
 
 #define PCI_DEVICE_ID_INTEL_BAYTRAIL   0x0f1c
 #define PCI_DEVICE_ID_INTEL_WPT_LP     0x9cc3
+#define PCI_DEVICE_ID_INTEL_LPC_LP     0x8c4e
 
 #define PMBASE_REG                     0x40
 # define PMBASE_ADDRMASK               0xff00
@@ -35,16 +36,104 @@
 #define PMC_REG                                0x08
 # define PMC_NO_REBOOT                 (1 << 4)
 
+/* iTCOv2 defines */
+#define RCBABASE_REG           0xf0
+#define RCBABASE_ADDRMASK      0xffffc000
+#define GCS_REG                        0x3410
+#define GCS_NO_REBOOT          (1 << 5)
+
+static UINT32 get_no_reboot_bit(char version) {
+       return version > 2 ? PMC_NO_REBOOT : GCS_NO_REBOOT;
+}
+
+static UINT32 get_wdog_base_mask(char version) {
+       return version > 2 ? PMCBASE_ADDRMASK : RCBABASE_ADDRMASK;
+}
+
+static UINT32 get_wdog_base_addr_cfg(char version) {
+       return version > 2 ? PMCBASE_REG : RCBABASE_REG;
+}
+
+static UINT32 get_wdog_offset(char version) {
+       return version > 2 ? PMC_REG : GCS_REG;
+}
+
+static UINTN get_wdog_timeout(char version, UINTN seconds) {
+       return version > 2 ? seconds : ((seconds * 10) / 6);
+}
+
+static EFI_STATUS get_wdog_base(EFI_PCI_IO *pci_io, char version, UINT32 
*base) {
+       return uefi_call_wrapper(pci_io->Pci.Read, 5, pci_io,
+                                       EfiPciIoWidthUint32,
+                                       get_wdog_base_addr_cfg(version), 1, 
base);
+}
+
+static EFI_STATUS read_wdog_register(EFI_PCI_IO *pci_io, UINT32 wdogbase, 
UINT32 wdogoffset, UINT32 *value) {
+       return uefi_call_wrapper(pci_io->Mem.Read, 6, pci_io,
+                                       EfiPciIoWidthUint32,
+                                       EFI_PCI_IO_PASS_THROUGH_BAR,
+                                       wdogbase + wdogoffset, 1, value);
+}
+
+static EFI_STATUS write_wdog_register(EFI_PCI_IO *pci_io, UINT32 wdogbase, 
UINT32 wdogoffset, UINT32 *value) {
+       return uefi_call_wrapper(pci_io->Mem.Write, 6, pci_io,
+                                       EfiPciIoWidthUint32,
+                                       EFI_PCI_IO_PASS_THROUGH_BAR,
+                                       wdogbase + wdogoffset, 1, value);
+}
+
+static EFI_STATUS clear_wdog_no_reboot(EFI_PCI_IO *pci_io, char version) {
+       EFI_STATUS status;
+       UINT32 value, wdogbase, wdogoffset, wdogmask, norebootbit;
+
+       wdogmask = get_wdog_base_mask(version);
+       wdogoffset = get_wdog_offset(version);
+
+       /* Get PMCBASE (iTCOv3) or GCS address (iTCOv2) */
+       status = get_wdog_base(pci_io, version, &wdogbase);
+       if (EFI_ERROR(status)) {
+               return status;
+       }
+       wdogbase &= wdogmask;
+
+       /* Reading NO_REBOOT flag */
+       status = read_wdog_register(pci_io, wdogbase, wdogoffset, &value);
+       if (EFI_ERROR(status)) {
+               return status;
+       }
+
+       value &= ~get_no_reboot_bit(version);
+
+       /* Writing NO_REBOOT flag */
+       status = write_wdog_register(pci_io, wdogbase, wdogoffset, &value);
+       if (EFI_ERROR(status)) {
+               return status;
+       }
+
+       return status;
+}
+
 static EFI_STATUS __attribute__((constructor))
 init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 pci_device_id,
      UINTN timeout)
 {
-       UINT32 pmbase, tcobase, pmcbase, value;
+       UINT32 pmbase, tcobase, value;
+       char itcoversion;
        EFI_STATUS status;
 
-       if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_INTEL ||
-           (pci_device_id != PCI_DEVICE_ID_INTEL_BAYTRAIL &&
-            pci_device_id != PCI_DEVICE_ID_INTEL_WPT_LP)) {
+       if (!pci_io || pci_vendor_id != PCI_VENDOR_ID_INTEL) {
+               return EFI_UNSUPPORTED;
+       }
+
+       /* Setting variables according to iTCO version */
+       if (pci_device_id == PCI_DEVICE_ID_INTEL_BAYTRAIL ||
+           pci_device_id == PCI_DEVICE_ID_INTEL_WPT_LP) {
+               /* Detected iTCO v3 */
+               itcoversion = 3;
+       } else if (pci_device_id == PCI_DEVICE_ID_INTEL_LPC_LP){
+               /* Detected iTCO v2*/
+               itcoversion = 2;
+       } else {
                return EFI_UNSUPPORTED;
        }
 
@@ -60,15 +149,6 @@ init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 
pci_device_id,
        pmbase &= PMBASE_ADDRMASK;
        tcobase = (pmbase & PMBASE_ADDRMASK) + 0x60;
 
-       /* Get PMCBASE address */
-       status = uefi_call_wrapper(pci_io->Pci.Read, 5, pci_io,
-                                  EfiPciIoWidthUint32, PMCBASE_REG,
-                                  1, &pmcbase);
-       if (EFI_ERROR(status)) {
-               return status;
-       }
-       pmcbase &= PMCBASE_ADDRMASK;
-
        /* Enable TCO SMIs */
        status = uefi_call_wrapper(pci_io->Io.Read, 6, pci_io,
                                   EfiPciIoWidthUint32,
@@ -95,7 +175,7 @@ init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 
pci_device_id,
                return status;
        }
        value &= 0xfc00;
-       value |= timeout & 0x3ff;
+       value |= get_wdog_timeout(itcoversion, timeout) & 0x3ff;
        status = uefi_call_wrapper(pci_io->Io.Write, 6, pci_io,
                                   EfiPciIoWidthUint16,
                                   EFI_PCI_IO_PASS_THROUGH_BAR,
@@ -114,19 +194,7 @@ init(EFI_PCI_IO *pci_io, UINT16 pci_vendor_id, UINT16 
pci_device_id,
                return status;
        }
 
-       /* Clear NO_REBOOT flag */
-       status = uefi_call_wrapper(pci_io->Mem.Read, 6, pci_io,
-                                  EfiPciIoWidthUint32,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  pmcbase + PMC_REG, 1, &value);
-       if (EFI_ERROR(status)) {
-               return status;
-       }
-       value &= ~PMC_NO_REBOOT;
-       status = uefi_call_wrapper(pci_io->Mem.Write, 6, pci_io,
-                                  EfiPciIoWidthUint32,
-                                  EFI_PCI_IO_PASS_THROUGH_BAR,
-                                  pmcbase + PMC_REG, 1, &value);
+       status = clear_wdog_no_reboot(pci_io, itcoversion);
        if (EFI_ERROR(status)) {
                return status;
        }
-- 
2.18.1

-- 
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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/efibootguard-dev/20181217160633.16253-2-alexandretm%40amotus.ca.
For more options, visit https://groups.google.com/d/optout.

Reply via email to