]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.c
ArmPkg/GenericWatchdogDxe: implement RegisterHandler() method
[mirror_edk2.git] / ArmPkg / Drivers / GenericWatchdogDxe / GenericWatchdogDxe.c
index c5372056f3ea080c350ff67ce8f78f0a77663343..a1ef0363eb399cbfd2221e5c8025c009c630b973 100644 (file)
@@ -1,6 +1,6 @@
 /** @file\r
 *\r
-*  Copyright (c) 2013-2017, ARM Limited. All rights reserved.\r
+*  Copyright (c) 2013-2018, ARM Limited. All rights reserved.\r
 *\r
 *  This program and the accompanying materials\r
 *  are licensed and made available under the terms and conditions of the BSD\r
@@ -24,8 +24,8 @@
 #include <Library/UefiLib.h>\r
 #include <Library/ArmGenericTimerCounterLib.h>\r
 \r
+#include <Protocol/HardwareInterrupt2.h>\r
 #include <Protocol/WatchdogTimer.h>\r
-#include <Protocol/HardwareInterrupt.h>\r
 \r
 #include "GenericWatchdog.h"\r
 \r
 #define TIME_UNITS_PER_SECOND 10000000\r
 \r
 // Tick frequency of the generic timer basis of the generic watchdog.\r
-UINTN mTimerFrequencyHz = 0;\r
+STATIC UINTN mTimerFrequencyHz = 0;\r
 \r
 /* In cases where the compare register was set manually, information about\r
    how long the watchdog was asked to wait cannot be retrieved from hardware.\r
    It is therefore stored here. 0 means the timer is not running. */\r
-UINT64 mNumTimerTicks = 0;\r
+STATIC UINT64 mNumTimerTicks = 0;\r
 \r
-EFI_HARDWARE_INTERRUPT_PROTOCOL *mInterruptProtocol;\r
+STATIC EFI_HARDWARE_INTERRUPT2_PROTOCOL *mInterruptProtocol;\r
+STATIC EFI_WATCHDOG_TIMER_NOTIFY        mWatchdogNotify;\r
 \r
-EFI_STATUS\r
+STATIC\r
+VOID\r
 WatchdogWriteOffsetRegister (\r
   UINT32  Value\r
   )\r
 {\r
-  return MmioWrite32 (GENERIC_WDOG_OFFSET_REG, Value);\r
+  MmioWrite32 (GENERIC_WDOG_OFFSET_REG, Value);\r
 }\r
 \r
-EFI_STATUS\r
+STATIC\r
+VOID\r
 WatchdogWriteCompareRegister (\r
   UINT64  Value\r
   )\r
 {\r
-  return MmioWrite64 (GENERIC_WDOG_COMPARE_VALUE_REG, Value);\r
+  MmioWrite32 (GENERIC_WDOG_COMPARE_VALUE_REG_LOW, Value & MAX_UINT32);\r
+  MmioWrite32 (GENERIC_WDOG_COMPARE_VALUE_REG_HIGH, (Value >> 32) & MAX_UINT32);\r
 }\r
 \r
-EFI_STATUS\r
+STATIC\r
+VOID\r
 WatchdogEnable (\r
   VOID\r
   )\r
 {\r
-  return MmioWrite32 (GENERIC_WDOG_CONTROL_STATUS_REG, GENERIC_WDOG_ENABLED);\r
+  MmioWrite32 (GENERIC_WDOG_CONTROL_STATUS_REG, GENERIC_WDOG_ENABLED);\r
 }\r
 \r
-EFI_STATUS\r
+STATIC\r
+VOID\r
 WatchdogDisable (\r
   VOID\r
   )\r
 {\r
-  return MmioWrite32 (GENERIC_WDOG_CONTROL_STATUS_REG, GENERIC_WDOG_DISABLED);\r
+  MmioWrite32 (GENERIC_WDOG_CONTROL_STATUS_REG, GENERIC_WDOG_DISABLED);\r
 }\r
 \r
 /** On exiting boot services we must make sure the Watchdog Timer\r
     is stopped.\r
 **/\r
+STATIC\r
 VOID\r
 EFIAPI\r
 WatchdogExitBootServicesEvent (\r
@@ -92,6 +99,7 @@ WatchdogExitBootServicesEvent (
 /* This function is called when the watchdog's first signal (WS0) goes high.\r
    It uses the ResetSystem Runtime Service to reset the board.\r
 */\r
+STATIC\r
 VOID\r
 EFIAPI\r
 WatchdogInterruptHandler (\r
@@ -100,17 +108,25 @@ WatchdogInterruptHandler (
   )\r
 {\r
   STATIC CONST CHAR16 ResetString[]= L"The generic watchdog timer ran out.";\r
+  UINT64              TimerPeriod;\r
 \r
   WatchdogDisable ();\r
 \r
   mInterruptProtocol->EndOfInterrupt (mInterruptProtocol, Source);\r
 \r
-  gRT->ResetSystem (\r
-         EfiResetCold,\r
-         EFI_TIMEOUT,\r
-         StrSize (ResetString),\r
-         (VOID *) &ResetString\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
+    TimerPeriod = ((TIME_UNITS_PER_SECOND / mTimerFrequencyHz) * mNumTimerTicks);\r
+    mWatchdogNotify (TimerPeriod + 1);\r
+  }\r
+\r
+  gRT->ResetSystem (EfiResetCold, EFI_TIMEOUT, StrSize (ResetString),\r
+         (CHAR16 *)ResetString);\r
 \r
   // If we got here then the reset didn't work\r
   ASSERT (FALSE);\r
@@ -140,16 +156,24 @@ WatchdogInterruptHandler (
   @retval EFI_UNSUPPORTED       The code does not support NotifyFunction.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 WatchdogRegisterHandler (\r
-  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL   *This,\r
+  IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL         *This,\r
   IN EFI_WATCHDOG_TIMER_NOTIFY                NotifyFunction\r
   )\r
 {\r
-  // ERROR: This function is not supported.\r
-  // The watchdog will reset the board\r
-  return EFI_UNSUPPORTED;\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
@@ -163,25 +187,24 @@ WatchdogRegisterHandler (
                            then the watchdog timer is disabled.\r
 \r
   @retval EFI_SUCCESS           The watchdog timer has been programmed to fire\r
-                                in Time  100ns units.\r
-  @retval EFI_DEVICE_ERROR      A watchdog timer could not be programmed due\r
-                                to a device error.\r
+                                in TimerPeriod 100ns units.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 WatchdogSetTimerPeriod (\r
-  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL   *This,\r
+  IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL         *This,\r
   IN UINT64                                   TimerPeriod   // In 100ns units\r
   )\r
 {\r
   UINTN       SystemCount;\r
-  EFI_STATUS  Status;\r
 \r
   // if TimerPeriod is 0, this is a request to stop the watchdog.\r
   if (TimerPeriod == 0) {\r
     mNumTimerTicks = 0;\r
-    return WatchdogDisable ();\r
+    WatchdogDisable ();\r
+    return EFI_SUCCESS;\r
   }\r
 \r
   // Work out how many timer ticks will equate to TimerPeriod\r
@@ -195,19 +218,16 @@ WatchdogSetTimerPeriod (
        because enabling the watchdog causes an "explicit refresh", which\r
        clobbers the compare register (WCV). In order to make sure this doesn't\r
        trigger an interrupt, set the offset to max. */\r
-    Status = WatchdogWriteOffsetRegister (MAX_UINT32);\r
-    if (EFI_ERROR (Status)) {\r
-      return Status;\r
-    }\r
+    WatchdogWriteOffsetRegister (MAX_UINT32);\r
     WatchdogEnable ();\r
     SystemCount = ArmGenericTimerGetSystemCount ();\r
-    Status      = WatchdogWriteCompareRegister (SystemCount + mNumTimerTicks);\r
+    WatchdogWriteCompareRegister (SystemCount + mNumTimerTicks);\r
   } else {\r
-    Status = WatchdogWriteOffsetRegister ((UINT32)mNumTimerTicks);\r
+    WatchdogWriteOffsetRegister ((UINT32)mNumTimerTicks);\r
     WatchdogEnable ();\r
   }\r
 \r
-  return Status;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -226,10 +246,11 @@ WatchdogSetTimerPeriod (
   @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 WatchdogGetTimerPeriod (\r
-  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL   *This,\r
+  IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL         *This,\r
   OUT UINT64                                  *TimerPeriod\r
   )\r
 {\r
@@ -274,13 +295,13 @@ WatchdogGetTimerPeriod (
   Retrieves the period of the timer interrupt in 100ns units.\r
 \r
 **/\r
-EFI_WATCHDOG_TIMER_ARCH_PROTOCOL    gWatchdogTimer = {\r
-  (EFI_WATCHDOG_TIMER_REGISTER_HANDLER)WatchdogRegisterHandler,\r
-  (EFI_WATCHDOG_TIMER_SET_TIMER_PERIOD)WatchdogSetTimerPeriod,\r
-  (EFI_WATCHDOG_TIMER_GET_TIMER_PERIOD)WatchdogGetTimerPeriod\r
+STATIC EFI_WATCHDOG_TIMER_ARCH_PROTOCOL mWatchdogTimer = {\r
+  WatchdogRegisterHandler,\r
+  WatchdogSetTimerPeriod,\r
+  WatchdogGetTimerPeriod\r
 };\r
 \r
-EFI_EVENT                           EfiExitBootServicesEvent = (EFI_EVENT)NULL;\r
+STATIC EFI_EVENT mEfiExitBootServicesEvent;\r
 \r
 EFI_STATUS\r
 EFIAPI\r
@@ -292,6 +313,10 @@ GenericWatchdogEntry (
   EFI_STATUS                      Status;\r
   EFI_HANDLE                      Handle;\r
 \r
+  Status = gBS->LocateProtocol (&gHardwareInterrupt2ProtocolGuid, NULL,\r
+                  (VOID **)&mInterruptProtocol);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
   /* Make sure the Watchdog Timer Architectural Protocol has not been installed\r
      in the system yet.\r
      This will avoid conflicts with the universal watchdog */\r
@@ -300,44 +325,45 @@ GenericWatchdogEntry (
   mTimerFrequencyHz = ArmGenericTimerGetTimerFreq ();\r
   ASSERT (mTimerFrequencyHz != 0);\r
 \r
-  // Register for an ExitBootServicesEvent\r
-  Status = gBS->CreateEvent (\r
-                  EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
-                  TPL_NOTIFY,\r
-                  WatchdogExitBootServicesEvent,\r
-                  NULL,\r
-                  &EfiExitBootServicesEvent\r
-                  );\r
-  if (!EFI_ERROR (Status)) {\r
-    // Install interrupt handler\r
-    Status = gBS->LocateProtocol (\r
-                    &gHardwareInterruptProtocolGuid,\r
-                    NULL,\r
-                    (VOID **)&mInterruptProtocol\r
-                    );\r
-    if (!EFI_ERROR (Status)) {\r
-      Status = mInterruptProtocol->RegisterInterruptSource (\r
-                 mInterruptProtocol,\r
-                 FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum),\r
-                 WatchdogInterruptHandler\r
-                 );\r
-      if (!EFI_ERROR (Status)) {\r
-        // Install the Timer Architectural Protocol onto a new handle\r
-        Handle = NULL;\r
-        Status = gBS->InstallMultipleProtocolInterfaces (\r
-                        &Handle,\r
-                        &gEfiWatchdogTimerArchProtocolGuid,\r
-                        &gWatchdogTimer,\r
-                        NULL\r
-                        );\r
-      }\r
-    }\r
+  // Install interrupt handler\r
+  Status = mInterruptProtocol->RegisterInterruptSource (mInterruptProtocol,\r
+                                 FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum),\r
+                                 WatchdogInterruptHandler);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
   }\r
 \r
+  Status = mInterruptProtocol->SetTriggerType (mInterruptProtocol,\r
+                                 FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum),\r
+                                 EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING);\r
+  if (EFI_ERROR (Status)) {\r
+    goto UnregisterHandler;\r
+  }\r
+\r
+  // Install the Timer Architectural Protocol onto a new handle\r
+  Handle = NULL;\r
+  Status = gBS->InstallMultipleProtocolInterfaces (&Handle,\r
+                  &gEfiWatchdogTimerArchProtocolGuid, &mWatchdogTimer,\r
+                  NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    goto UnregisterHandler;\r
+  }\r
+\r
+  // Register for an ExitBootServicesEvent\r
+  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY,\r
+                  WatchdogExitBootServicesEvent, NULL,\r
+                  &mEfiExitBootServicesEvent);\r
   ASSERT_EFI_ERROR (Status);\r
 \r
   mNumTimerTicks = 0;\r
   WatchdogDisable ();\r
 \r
+  return EFI_SUCCESS;\r
+\r
+UnregisterHandler:\r
+  // Unregister the handler\r
+  mInterruptProtocol->RegisterInterruptSource (mInterruptProtocol,\r
+                        FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum),\r
+                        NULL);\r
   return Status;\r
 }\r