#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
}\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
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
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
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
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
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
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