]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPlatformPkg/Drivers/SP805WatchdogDxe/SP805Watchdog.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / ArmPlatformPkg / Drivers / SP805WatchdogDxe / SP805Watchdog.c
index 799ead96861ac46e552f61a3d702e6abd3a94e2b..5821dc1958222637b214628d044c9bbbf14a3ca5 100644 (file)
@@ -1,17 +1,11 @@
 /** @file\r
-*\r
-*  Copyright (c) 2011-2013, 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 License\r
-*  which accompanies this distribution.  The full text of the license may be found at\r
-*  http://opensource.org/licenses/bsd-license.php\r
-*\r
-*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-*\r
-**/\r
 \r
+  Copyright (c) 2011-2013, ARM Limited. All rights reserved.\r
+  Copyright (c) 2018, Linaro Limited. All rights reserved.\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
 \r
 #include <PiDxe.h>\r
 \r
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <Library/IoLib.h>\r
-#include <Library/PcdLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
 #include <Library/UefiRuntimeServicesTableLib.h>\r
-#include <Library/UefiLib.h>\r
 \r
+#include <Protocol/HardwareInterrupt.h>\r
 #include <Protocol/WatchdogTimer.h>\r
-#include <Drivers/SP805Watchdog.h>\r
 \r
-EFI_EVENT                           EfiExitBootServicesEvent = (EFI_EVENT)NULL;\r
+#include "SP805Watchdog.h"\r
+\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
@@ -42,8 +39,8 @@ SP805Unlock (
   VOID\r
   )\r
 {\r
-  if( MmioRead32(SP805_WDOG_LOCK_REG) == SP805_WDOG_LOCK_IS_LOCKED ) {\r
-    MmioWrite32(SP805_WDOG_LOCK_REG, SP805_WDOG_SPECIAL_UNLOCK_CODE);\r
+  if (MmioRead32 (SP805_WDOG_LOCK_REG) == SP805_WDOG_LOCK_IS_LOCKED) {\r
+    MmioWrite32 (SP805_WDOG_LOCK_REG, SP805_WDOG_SPECIAL_UNLOCK_CODE);\r
   }\r
 }\r
 \r
@@ -60,10 +57,37 @@ SP805Lock (
   VOID\r
   )\r
 {\r
-  if( MmioRead32(SP805_WDOG_LOCK_REG) == SP805_WDOG_LOCK_IS_UNLOCKED ) {\r
+  if (MmioRead32 (SP805_WDOG_LOCK_REG) == SP805_WDOG_LOCK_IS_UNLOCKED) {\r
     // To lock it, just write in any number (except the special unlock code).\r
-    MmioWrite32(SP805_WDOG_LOCK_REG, SP805_WDOG_LOCK_IS_LOCKED);\r
+    MmioWrite32 (SP805_WDOG_LOCK_REG, SP805_WDOG_LOCK_IS_LOCKED);\r
+  }\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
@@ -76,8 +100,8 @@ SP805Stop (
   )\r
 {\r
   // Disable interrupts\r
-  if ( (MmioRead32(SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_INTEN) != 0 ) {\r
-    MmioAnd32(SP805_WDOG_CONTROL_REG, ~SP805_WDOG_CTRL_INTEN);\r
+  if ((MmioRead32 (SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_INTEN) != 0) {\r
+    MmioAnd32 (SP805_WDOG_CONTROL_REG, ~SP805_WDOG_CTRL_INTEN);\r
   }\r
 }\r
 \r
@@ -93,8 +117,8 @@ SP805Start (
   )\r
 {\r
   // Enable interrupts\r
-  if ( (MmioRead32(SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_INTEN) == 0 ) {\r
-    MmioOr32(SP805_WDOG_CONTROL_REG, SP805_WDOG_CTRL_INTEN);\r
+  if ((MmioRead32 (SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_INTEN) == 0) {\r
+    MmioOr32 (SP805_WDOG_CONTROL_REG, SP805_WDOG_CTRL_INTEN);\r
   }\r
 }\r
 \r
@@ -102,6 +126,7 @@ SP805Start (
     On exiting boot services we must make sure the SP805 Watchdog Timer\r
     is stopped.\r
 **/\r
+STATIC\r
 VOID\r
 EFIAPI\r
 ExitBootServicesEvent (\r
@@ -109,9 +134,9 @@ ExitBootServicesEvent (
   IN VOID       *Context\r
   )\r
 {\r
-  SP805Unlock();\r
-  SP805Stop();\r
-  SP805Lock();\r
+  SP805Unlock ();\r
+  SP805Stop ();\r
+  SP805Lock ();\r
 }\r
 \r
 /**\r
@@ -141,16 +166,24 @@ ExitBootServicesEvent (
                                 previously registered.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 SP805RegisterHandler (\r
-  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL   *This,\r
-  IN EFI_WATCHDOG_TIMER_NOTIFY                NotifyFunction\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 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
@@ -181,39 +214,39 @@ SP805RegisterHandler (
   @retval EFI_DEVICE_ERROR      The timer period could not be changed due to a device error.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 SP805SetTimerPeriod (\r
-  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL   *This,\r
-  IN UINT64                                   TimerPeriod   // In 100ns units\r
+  IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL  *This,\r
+  IN UINT64                            TimerPeriod          // In 100ns units\r
   )\r
 {\r
-  EFI_STATUS  Status = EFI_SUCCESS;\r
+  EFI_STATUS  Status;\r
   UINT64      Ticks64bit;\r
 \r
-  SP805Unlock();\r
+  SP805Unlock ();\r
+\r
+  Status = EFI_SUCCESS;\r
 \r
-  if( TimerPeriod == 0 ) {\r
+  if (TimerPeriod == 0) {\r
     // This is a watchdog stop request\r
-    SP805Stop();\r
-    goto EXIT;\r
+    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 = DivU64x32(MultU64x32(TimerPeriod, (UINTN)PcdGet32(PcdSP805WatchdogClockFrequencyInHz)), 20000000);\r
+    Ticks64bit = MultU64x32 (TimerPeriod, PcdGet32 (PcdSP805WatchdogClockFrequencyInHz));\r
+    Ticks64bit = DivU64x32 (Ticks64bit, 10 * 1000 * 1000);\r
 \r
     // The registers in the SP805 are only 32 bits\r
-    if(Ticks64bit > (UINT64)0xFFFFFFFF) {\r
+    if (Ticks64bit > MAX_UINT32) {\r
       // We could load the watchdog with the maximum supported value but\r
       // if a smaller value was requested, this could have the watchdog\r
       // triggering before it was intended.\r
@@ -223,15 +256,18 @@ SP805SetTimerPeriod (
     }\r
 \r
     // Update the watchdog with a 32-bit value.\r
-    MmioWrite32(SP805_WDOG_LOAD_REG, (UINT32)Ticks64bit);\r
+    MmioWrite32 (SP805_WDOG_LOAD_REG, (UINT32)Ticks64bit);\r
 \r
     // Start the watchdog\r
-    SP805Start();\r
+    SP805Start ();\r
   }\r
 \r
-  EXIT:\r
+  mTimerPeriod = TimerPeriod;\r
+\r
+EXIT:\r
   // Ensure the watchdog is locked before exiting.\r
-  SP805Lock();\r
+  SP805Lock ();\r
+  ASSERT_EFI_ERROR (Status);\r
   return Status;\r
 }\r
 \r
@@ -250,34 +286,20 @@ SP805SetTimerPeriod (
   @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.\r
 \r
 **/\r
+STATIC\r
 EFI_STATUS\r
 EFIAPI\r
 SP805GetTimerPeriod (\r
-  IN CONST EFI_WATCHDOG_TIMER_ARCH_PROTOCOL   *This,\r
-  OUT UINT64                                  *TimerPeriod\r
+  IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL  *This,\r
+  OUT UINT64                           *TimerPeriod\r
   )\r
 {\r
-  EFI_STATUS  Status = EFI_SUCCESS;\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
-  return Status;\r
+  *TimerPeriod = mTimerPeriod;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -312,10 +334,10 @@ SP805GetTimerPeriod (
   Retrieves the period of the timer interrupt in 100 nS units.\r
 \r
 **/\r
-EFI_WATCHDOG_TIMER_ARCH_PROTOCOL    gWatchdogTimer = {\r
-  (EFI_WATCHDOG_TIMER_REGISTER_HANDLER) SP805RegisterHandler,\r
-  (EFI_WATCHDOG_TIMER_SET_TIMER_PERIOD) SP805SetTimerPeriod,\r
-  (EFI_WATCHDOG_TIMER_GET_TIMER_PERIOD) SP805GetTimerPeriod\r
+STATIC EFI_WATCHDOG_TIMER_ARCH_PROTOCOL  mWatchdogTimer = {\r
+  SP805RegisterHandler,\r
+  SP805SetTimerPeriod,\r
+  SP805GetTimerPeriod\r
 };\r
 \r
 /**\r
@@ -332,13 +354,21 @@ EFI_WATCHDOG_TIMER_ARCH_PROTOCOL    gWatchdogTimer = {
 EFI_STATUS\r
 EFIAPI\r
 SP805Initialize (\r
-  IN EFI_HANDLE         ImageHandle,\r
-  IN EFI_SYSTEM_TABLE   *SystemTable\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
   )\r
 {\r
   EFI_STATUS  Status;\r
   EFI_HANDLE  Handle;\r
 \r
+  // Find the interrupt controller protocol.  ASSERT if not found.\r
+  Status = gBS->LocateProtocol (\r
+                  &gHardwareInterruptProtocolGuid,\r
+                  NULL,\r
+                  (VOID **)&mInterrupt\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
   // Unlock access to the SP805 registers\r
   SP805Unlock ();\r
 \r
@@ -346,13 +376,40 @@ SP805Initialize (
   SP805Stop ();\r
 \r
   // Set the watchdog to reset the board when triggered\r
-  if ((MmioRead32(SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_RESEN) == 0) {\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
+  SP805Lock ();\r
+\r
+  if (PcdGet32 (PcdSP805WatchdogInterrupt) > 0) {\r
+    Status = mInterrupt->RegisterInterruptSource (\r
+                           mInterrupt,\r
+                           PcdGet32 (PcdSP805WatchdogInterrupt),\r
+                           SP805InterruptHandler\r
+                           );\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((\r
+        DEBUG_ERROR,\r
+        "%a: failed to register watchdog interrupt - %r\n",\r
+        __FUNCTION__,\r
+        Status\r
+        ));\r
+      return Status;\r
+    }\r
+  } else {\r
+    DEBUG ((\r
+      DEBUG_WARN,\r
+      "%a: no interrupt specified, running in RESET mode only\n",\r
+      __FUNCTION__\r
+      ));\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
@@ -360,28 +417,32 @@ SP805Initialize (
   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiWatchdogTimerArchProtocolGuid);\r
 \r
   // Register for an ExitBootServicesEvent\r
-  Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);\r
-  if (EFI_ERROR(Status)) {\r
+  Status = gBS->CreateEvent (\r
+                  EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
+                  TPL_NOTIFY,\r
+                  ExitBootServicesEvent,\r
+                  NULL,\r
+                  &mEfiExitBootServicesEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
     Status = EFI_OUT_OF_RESOURCES;\r
     goto EXIT;\r
   }\r
 \r
   // Install the Timer Architectural Protocol onto a new handle\r
   Handle = NULL;\r
-  Status = gBS->InstallMultipleProtocolInterfaces(\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
                   &Handle,\r
-                  &gEfiWatchdogTimerArchProtocolGuid, &gWatchdogTimer,\r
+                  &gEfiWatchdogTimerArchProtocolGuid,\r
+                  &mWatchdogTimer,\r
                   NULL\r
                   );\r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     Status = EFI_OUT_OF_RESOURCES;\r
     goto EXIT;\r
   }\r
 \r
 EXIT:\r
-  if(EFI_ERROR(Status)) {\r
-    // The watchdog failed to initialize\r
-    ASSERT(FALSE);\r
-  }\r
+  ASSERT_EFI_ERROR (Status);\r
   return Status;\r
 }\r