]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPlatformPkg/Drivers/SP805WatchdogDxe/SP805Watchdog.c
ArmPlatformPkg/SP805WatchdogDxe: switch to interrupt mode
[mirror_edk2.git] / ArmPlatformPkg / Drivers / SP805WatchdogDxe / SP805Watchdog.c
index 12c2f0a1fe499688fc318e7b64c27a8dcf985ec6..5bbb12af6019990b89dde2af4f632c30c70653fe 100644 (file)
 #include <Library/DebugLib.h>\r
 #include <Library/IoLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
 \r
+#include <Protocol/HardwareInterrupt.h>\r
 #include <Protocol/WatchdogTimer.h>\r
 \r
 #include "SP805Watchdog.h"\r
 \r
-STATIC EFI_EVENT          mEfiExitBootServicesEvent;\r
+STATIC EFI_EVENT                        mEfiExitBootServicesEvent;\r
+STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL  *mInterrupt;\r
+STATIC EFI_WATCHDOG_TIMER_NOTIFY        mWatchdogNotify;\r
+STATIC UINT32                           mTimerPeriod;\r
 \r
 /**\r
   Make sure the SP805 registers are unlocked for writing.\r
@@ -65,6 +70,33 @@ SP805Lock (
   }\r
 }\r
 \r
+STATIC\r
+VOID\r
+EFIAPI\r
+SP805InterruptHandler (\r
+  IN  HARDWARE_INTERRUPT_SOURCE   Source,\r
+  IN  EFI_SYSTEM_CONTEXT          SystemContext\r
+  )\r
+{\r
+  SP805Unlock ();\r
+  MmioWrite32 (SP805_WDOG_INT_CLR_REG, 0); // write of any value clears the irq\r
+  SP805Lock ();\r
+\r
+  mInterrupt->EndOfInterrupt (mInterrupt, Source);\r
+\r
+  //\r
+  // The notify function should be called with the elapsed number of ticks\r
+  // since the watchdog was armed, which should exceed the timer period.\r
+  // We don't actually know the elapsed number of ticks, so let's return\r
+  // the timer period plus 1.\r
+  //\r
+  if (mWatchdogNotify != NULL) {\r
+    mWatchdogNotify (mTimerPeriod + 1);\r
+  }\r
+\r
+  gRT->ResetSystem (EfiResetCold, EFI_TIMEOUT, 0, NULL);\r
+}\r
+\r
 /**\r
   Stop the SP805 watchdog timer from counting down by disabling interrupts.\r
 **/\r
@@ -149,9 +181,16 @@ SP805RegisterHandler (
   IN EFI_WATCHDOG_TIMER_NOTIFY                NotifyFunction\r
   )\r
 {\r
-  // ERROR: This function is not supported.\r
-  // The hardware watchdog will reset the board\r
-  return EFI_INVALID_PARAMETER;\r
+  if (mWatchdogNotify == NULL && NotifyFunction == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (mWatchdogNotify != NULL && NotifyFunction != NULL) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  mWatchdogNotify = NotifyFunction;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -202,19 +241,16 @@ SP805SetTimerPeriod (
     SP805Stop ();\r
   } else {\r
     // Calculate the Watchdog ticks required for a delay of (TimerTicks * 100) nanoseconds\r
-    // The SP805 will count down to ZERO once, generate an interrupt and\r
-    // then it will again reload the initial value and start again.\r
-    // On the second time when it reaches ZERO, it will actually reset the board.\r
-    // Therefore, we need to load half the required delay.\r
+    // The SP805 will count down to zero and generate an interrupt.\r
     //\r
-    // WatchdogTicks = ((TimerPeriod * 100 * SP805_CLOCK_FREQUENCY) / 1GHz) / 2 ;\r
+    // WatchdogTicks = ((TimerPeriod * 100 * SP805_CLOCK_FREQUENCY) / 1GHz);\r
     //\r
     // i.e.:\r
     //\r
-    // WatchdogTicks = (TimerPeriod * SP805_CLOCK_FREQUENCY) / 20 MHz ;\r
+    // WatchdogTicks = (TimerPeriod * SP805_CLOCK_FREQUENCY) / 10 MHz ;\r
 \r
     Ticks64bit = MultU64x32 (TimerPeriod, PcdGet32 (PcdSP805WatchdogClockFrequencyInHz));\r
-    Ticks64bit = DivU64x32 (Ticks64bit, 20000000);\r
+    Ticks64bit = DivU64x32 (Ticks64bit, 10 * 1000 * 1000);\r
 \r
     // The registers in the SP805 are only 32 bits\r
     if (Ticks64bit > MAX_UINT32) {\r
@@ -233,9 +269,12 @@ SP805SetTimerPeriod (
     SP805Start ();\r
   }\r
 \r
+  mTimerPeriod = TimerPeriod;\r
+\r
 EXIT:\r
   // Ensure the watchdog is locked before exiting.\r
   SP805Lock ();\r
+  ASSERT_EFI_ERROR (Status);\r
   return Status;\r
 }\r
 \r
@@ -262,25 +301,11 @@ SP805GetTimerPeriod (
   OUT UINT64                                  *TimerPeriod\r
   )\r
 {\r
-  UINT64      ReturnValue;\r
-\r
   if (TimerPeriod == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  // Check if the watchdog is stopped\r
-  if ((MmioRead32 (SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_INTEN) == 0) {\r
-    // It is stopped, so return zero.\r
-    ReturnValue = 0;\r
-  } else {\r
-    // Convert the Watchdog ticks into TimerPeriod\r
-    // Ensure 64bit arithmetic throughout because the Watchdog ticks may already\r
-    // be at the maximum 32 bit value and we still need to multiply that by 600.\r
-    ReturnValue = MultU64x32 (MmioRead32 (SP805_WDOG_LOAD_REG), 600);\r
-  }\r
-\r
-  *TimerPeriod = ReturnValue;\r
-\r
+  *TimerPeriod = mTimerPeriod;\r
   return EFI_SUCCESS;\r
 }\r
 \r
@@ -343,6 +368,11 @@ SP805Initialize (
   EFI_STATUS  Status;\r
   EFI_HANDLE  Handle;\r
 \r
+  // Find the interrupt controller protocol.  ASSERT if not found.\r
+  Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL,\r
+                  (VOID **)&mInterrupt);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
   // Unlock access to the SP805 registers\r
   SP805Unlock ();\r
 \r
@@ -350,13 +380,31 @@ SP805Initialize (
   SP805Stop ();\r
 \r
   // Set the watchdog to reset the board when triggered\r
+  // This is a last resort in case the interrupt handler fails\r
   if ((MmioRead32 (SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_RESEN) == 0) {\r
     MmioOr32 (SP805_WDOG_CONTROL_REG, SP805_WDOG_CTRL_RESEN);\r
   }\r
 \r
+  // Clear any pending interrupts\r
+  MmioWrite32 (SP805_WDOG_INT_CLR_REG, 0); // write of any value clears the irq\r
+\r
   // Prohibit any rogue access to SP805 registers\r
   SP805Lock ();\r
 \r
+  if (PcdGet32 (PcdSP805WatchdogInterrupt) > 0) {\r
+    Status = mInterrupt->RegisterInterruptSource (mInterrupt,\r
+                           PcdGet32 (PcdSP805WatchdogInterrupt),\r
+                           SP805InterruptHandler);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "%a: failed to register watchdog interrupt - %r\n",\r
+        __FUNCTION__, Status));\r
+      return Status;\r
+    }\r
+  } else {\r
+    DEBUG ((DEBUG_WARN, "%a: no interrupt specified, running in RESET mode only\n",\r
+      __FUNCTION__));\r
+  }\r
+\r
   //\r
   // Make sure the Watchdog Timer Architectural Protocol has not been installed in the system yet.\r
   // This will avoid conflicts with the universal watchdog\r