TianoCore EDK2 master
Loading...
Searching...
No Matches
SP805Watchdog.c
Go to the documentation of this file.
1
10#include <PiDxe.h>
11
12#include <Library/BaseLib.h>
14#include <Library/DebugLib.h>
15#include <Library/IoLib.h>
18
21
22#include "SP805Watchdog.h"
23
24STATIC EFI_EVENT mEfiExitBootServicesEvent;
26STATIC EFI_WATCHDOG_TIMER_NOTIFY mWatchdogNotify;
27STATIC UINT64 mTimerPeriod;
28
37VOID
39 VOID
40 )
41{
42 if (MmioRead32 (SP805_WDOG_LOCK_REG) == SP805_WDOG_LOCK_IS_LOCKED) {
43 MmioWrite32 (SP805_WDOG_LOCK_REG, SP805_WDOG_SPECIAL_UNLOCK_CODE);
44 }
45}
46
55VOID
57 VOID
58 )
59{
60 if (MmioRead32 (SP805_WDOG_LOCK_REG) == SP805_WDOG_LOCK_IS_UNLOCKED) {
61 // To lock it, just write in any number (except the special unlock code).
62 MmioWrite32 (SP805_WDOG_LOCK_REG, SP805_WDOG_LOCK_IS_LOCKED);
63 }
64}
65
67VOID
68EFIAPI
69SP805InterruptHandler (
70 IN HARDWARE_INTERRUPT_SOURCE Source,
71 IN EFI_SYSTEM_CONTEXT SystemContext
72 )
73{
74 SP805Unlock ();
75 MmioWrite32 (SP805_WDOG_INT_CLR_REG, 0); // write of any value clears the irq
76 SP805Lock ();
77
78 mInterrupt->EndOfInterrupt (mInterrupt, Source);
79
80 //
81 // The notify function should be called with the elapsed number of ticks
82 // since the watchdog was armed, which should exceed the timer period.
83 // We don't actually know the elapsed number of ticks, so let's return
84 // the timer period plus 1.
85 //
86 if (mWatchdogNotify != NULL) {
87 mWatchdogNotify (mTimerPeriod + 1);
88 }
89
90 gRT->ResetSystem (EfiResetCold, EFI_TIMEOUT, 0, NULL);
91}
92
97VOID
99 VOID
100 )
101{
102 // Disable interrupts
103 if ((MmioRead32 (SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_INTEN) != 0) {
104 MmioAnd32 (SP805_WDOG_CONTROL_REG, (UINT32) ~SP805_WDOG_CTRL_INTEN);
105 }
106}
107
113STATIC
114VOID
116 VOID
117 )
118{
119 // Enable interrupts
120 if ((MmioRead32 (SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_INTEN) == 0) {
121 MmioOr32 (SP805_WDOG_CONTROL_REG, SP805_WDOG_CTRL_INTEN);
122 }
123}
124
129STATIC
130VOID
131EFIAPI
133 IN EFI_EVENT Event,
134 IN VOID *Context
135 )
136{
137 SP805Unlock ();
138 SP805Stop ();
139 SP805Lock ();
140}
141
169STATIC
171EFIAPI
175 )
176{
177 if ((mWatchdogNotify == NULL) && (NotifyFunction == NULL)) {
178 return EFI_INVALID_PARAMETER;
179 }
180
181 if ((mWatchdogNotify != NULL) && (NotifyFunction != NULL)) {
182 return EFI_ALREADY_STARTED;
183 }
184
185 mWatchdogNotify = NotifyFunction;
186 return EFI_SUCCESS;
187}
188
217STATIC
219EFIAPI
222 IN UINT64 TimerPeriod // In 100ns units
223 )
224{
225 EFI_STATUS Status;
226 UINT64 Ticks64bit;
227
228 SP805Unlock ();
229
230 Status = EFI_SUCCESS;
231
232 if (TimerPeriod == 0) {
233 // This is a watchdog stop request
234 SP805Stop ();
235 } else {
236 // Calculate the Watchdog ticks required for a delay of (TimerTicks * 100) nanoseconds
237 // The SP805 will count down to zero and generate an interrupt.
238 //
239 // WatchdogTicks = ((TimerPeriod * 100 * SP805_CLOCK_FREQUENCY) / 1GHz);
240 //
241 // i.e.:
242 //
243 // WatchdogTicks = (TimerPeriod * SP805_CLOCK_FREQUENCY) / 10 MHz ;
244
245 Ticks64bit = MultU64x32 (TimerPeriod, PcdGet32 (PcdSP805WatchdogClockFrequencyInHz));
246 Ticks64bit = DivU64x32 (Ticks64bit, 10 * 1000 * 1000);
247
248 // The registers in the SP805 are only 32 bits
249 if (Ticks64bit > MAX_UINT32) {
250 // We could load the watchdog with the maximum supported value but
251 // if a smaller value was requested, this could have the watchdog
252 // triggering before it was intended.
253 // Better generate an error to let the caller know.
254 Status = EFI_DEVICE_ERROR;
255 goto EXIT;
256 }
257
258 // Update the watchdog with a 32-bit value.
259 MmioWrite32 (SP805_WDOG_LOAD_REG, (UINT32)Ticks64bit);
260
261 // Start the watchdog
262 SP805Start ();
263 }
264
265 mTimerPeriod = TimerPeriod;
266
267EXIT:
268 // Ensure the watchdog is locked before exiting.
269 SP805Lock ();
270 ASSERT_EFI_ERROR (Status);
271 return Status;
272}
273
289STATIC
291EFIAPI
294 OUT UINT64 *TimerPeriod
295 )
296{
297 if (TimerPeriod == NULL) {
298 return EFI_INVALID_PARAMETER;
299 }
300
301 *TimerPeriod = mTimerPeriod;
302 return EFI_SUCCESS;
303}
304
341};
342
355EFIAPI
357 IN EFI_HANDLE ImageHandle,
358 IN EFI_SYSTEM_TABLE *SystemTable
359 )
360{
361 EFI_STATUS Status;
362 EFI_HANDLE Handle;
363
364 // Find the interrupt controller protocol. ASSERT if not found.
365 Status = gBS->LocateProtocol (
366 &gHardwareInterruptProtocolGuid,
367 NULL,
368 (VOID **)&mInterrupt
369 );
370 ASSERT_EFI_ERROR (Status);
371
372 // Unlock access to the SP805 registers
373 SP805Unlock ();
374
375 // Stop the watchdog from triggering unexpectedly
376 SP805Stop ();
377
378 // Set the watchdog to reset the board when triggered
379 // This is a last resort in case the interrupt handler fails
380 if ((MmioRead32 (SP805_WDOG_CONTROL_REG) & SP805_WDOG_CTRL_RESEN) == 0) {
381 MmioOr32 (SP805_WDOG_CONTROL_REG, SP805_WDOG_CTRL_RESEN);
382 }
383
384 // Clear any pending interrupts
385 MmioWrite32 (SP805_WDOG_INT_CLR_REG, 0); // write of any value clears the irq
386
387 // Prohibit any rogue access to SP805 registers
388 SP805Lock ();
389
390 if (PcdGet32 (PcdSP805WatchdogInterrupt) > 0) {
391 Status = mInterrupt->RegisterInterruptSource (
392 mInterrupt,
393 PcdGet32 (PcdSP805WatchdogInterrupt),
394 SP805InterruptHandler
395 );
396 if (EFI_ERROR (Status)) {
397 DEBUG ((
398 DEBUG_ERROR,
399 "%a: failed to register watchdog interrupt - %r\n",
400 __func__,
401 Status
402 ));
403 return Status;
404 }
405 } else {
406 DEBUG ((
407 DEBUG_WARN,
408 "%a: no interrupt specified, running in RESET mode only\n",
409 __func__
410 ));
411 }
412
413 //
414 // Make sure the Watchdog Timer Architectural Protocol has not been installed in the system yet.
415 // This will avoid conflicts with the universal watchdog
416 //
417 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiWatchdogTimerArchProtocolGuid);
418
419 // Register for an ExitBootServicesEvent
420 Status = gBS->CreateEvent (
421 EVT_SIGNAL_EXIT_BOOT_SERVICES,
422 TPL_NOTIFY,
424 NULL,
425 &mEfiExitBootServicesEvent
426 );
427 if (EFI_ERROR (Status)) {
428 Status = EFI_OUT_OF_RESOURCES;
429 goto EXIT;
430 }
431
432 // Install the Timer Architectural Protocol onto a new handle
433 Handle = NULL;
434 Status = gBS->InstallMultipleProtocolInterfaces (
435 &Handle,
436 &gEfiWatchdogTimerArchProtocolGuid,
438 NULL
439 );
440 if (EFI_ERROR (Status)) {
441 Status = EFI_OUT_OF_RESOURCES;
442 goto EXIT;
443 }
444
445EXIT:
446 ASSERT_EFI_ERROR (Status);
447 return Status;
448}
UINT64 EFIAPI DivU64x32(IN UINT64 Dividend, IN UINT32 Divisor)
Definition: DivU64x32.c:29
UINT64 EFIAPI MultU64x32(IN UINT64 Multiplicand, IN UINT32 Multiplier)
Definition: MultU64x32.c:27
UINT32 EFIAPI MmioAnd32(IN UINTN Address, IN UINT32 AndData)
Definition: IoHighLevel.c:1814
UINT32 EFIAPI MmioOr32(IN UINTN Address, IN UINT32 OrData)
Definition: IoHighLevel.c:1785
UINT32 EFIAPI MmioRead32(IN UINTN Address)
Definition: IoLib.c:262
UINT32 EFIAPI MmioWrite32(IN UINTN Address, IN UINT32 Value)
Definition: IoLib.c:309
EFI_RUNTIME_SERVICES * gRT
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG(Expression)
Definition: DebugLib.h:434
#define ASSERT_PROTOCOL_ALREADY_INSTALLED(Handle, Guid)
Definition: DebugLib.h:535
VOID(EFIAPI * EFI_WATCHDOG_TIMER_NOTIFY)(IN UINT64 Time)
Definition: WatchdogTimer.h:37
#define PcdGet32(TokenName)
Definition: PcdLib.h:362
STATIC VOID SP805Start(VOID)
STATIC EFI_STATUS EFIAPI SP805SetTimerPeriod(IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, IN UINT64 TimerPeriod)
STATIC VOID SP805Lock(VOID)
Definition: SP805Watchdog.c:56
STATIC EFI_STATUS EFIAPI SP805GetTimerPeriod(IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, OUT UINT64 *TimerPeriod)
STATIC VOID SP805Unlock(VOID)
Definition: SP805Watchdog.c:38
EFI_STATUS EFIAPI SP805Initialize(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
STATIC VOID SP805Stop(VOID)
Definition: SP805Watchdog.c:98
STATIC EFI_STATUS EFIAPI SP805RegisterHandler(IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, IN EFI_WATCHDOG_TIMER_NOTIFY NotifyFunction)
STATIC VOID EFIAPI ExitBootServicesEvent(IN EFI_EVENT Event, IN VOID *Context)
STATIC EFI_WATCHDOG_TIMER_ARCH_PROTOCOL mWatchdogTimer
VOID EFIAPI NotifyFunction(IN EFI_EVENT Event, IN VOID *Context)
Definition: ScsiBus.c:1492
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_EVENT
Definition: UefiBaseType.h:37
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
@ EfiResetCold