1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2013 Google Inc.
3// Copyright (c) 2015 Travis Geiselbrecht
4//
5// Use of this source code is governed by a MIT-style
6// license that can be found in the LICENSE file or at
7// https://opensource.org/licenses/MIT
8
9
10#include <assert.h>
11#include <err.h>
12#include <inttypes.h>
13#include <platform.h>
14#include <zircon/compiler.h>
15#include <zircon/types.h>
16
17#include <kernel/thread.h>
18#include <kernel/timer.h>
19#include <kernel/spinlock.h>
20
21#include <lib/watchdog.h>
22
23static spin_lock_t lock = SPIN_LOCK_INITIAL_VALUE;
24
25__WEAK void watchdog_handler(watchdog_t *dog)
26{
27    dprintf(INFO, "Watchdog \"%s\" (timeout %" PRIu64 " mSec) just fired!!\n",
28            dog->name, dog->timeout / (1000 * 1000));
29    platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_WATCHDOG);
30}
31
32static void watchdog_timer_callback(timer_t *timer, zx_time_t now, void *arg)
33{
34    watchdog_handler((watchdog_t *)arg);
35
36    /* We should never get here; watchdog handlers should always be fatal. */
37    DEBUG_ASSERT(false);
38}
39
40zx_status_t watchdog_init(watchdog_t *dog, zx_time_t timeout, const char *name)
41{
42    DEBUG_ASSERT(NULL != dog);
43    DEBUG_ASSERT(ZX_TIME_INFINITE != timeout);
44
45    dog->magic   = WATCHDOG_MAGIC;
46    dog->name    = name ? name : "unnamed watchdog";
47    dog->enabled = false;
48    dog->timeout = timeout;
49    timer_init(&dog->expire_timer);
50
51    return ZX_OK;
52}
53
54void watchdog_set_enabled(watchdog_t *dog, bool enabled)
55{
56    spin_lock_saved_state_t state;
57    spin_lock_irqsave(&lock, state);
58
59    DEBUG_ASSERT((NULL != dog) && (WATCHDOG_MAGIC == dog->magic));
60
61    if (dog->enabled == enabled)
62        goto done;
63
64    dog->enabled = enabled;
65    zx_time_t deadline = current_time() + dog->timeout;
66    if (enabled)
67        timer_set_oneshot(&dog->expire_timer, deadline, watchdog_timer_callback, dog);
68    else
69        timer_cancel(&dog->expire_timer);
70
71done:
72    spin_unlock_irqrestore(&lock, state);
73}
74
75void watchdog_pet(watchdog_t *dog)
76{
77    spin_lock_saved_state_t state;
78    spin_lock_irqsave(&lock, state);
79
80    DEBUG_ASSERT((NULL != dog) && (WATCHDOG_MAGIC == dog->magic));
81
82    if (!dog->enabled)
83        goto done;
84
85    timer_cancel(&dog->expire_timer);
86    zx_time_t deadline = current_time() + dog->timeout;
87    timer_set_oneshot(&dog->expire_timer, deadline, watchdog_timer_callback, dog);
88
89done:
90    spin_unlock_irqrestore(&lock, state);
91}
92
93
94static timer_t   hw_watchdog_timer;
95static bool      hw_watchdog_enabled;
96static zx_time_t hw_watchdog_pet_timeout;
97
98static void hw_watchdog_timer_callback(timer_t *timer, zx_time_t now, void *arg)
99{
100    platform_watchdog_pet();
101}
102
103zx_status_t watchdog_hw_init(zx_time_t timeout)
104{
105    DEBUG_ASSERT(ZX_TIME_INFINITE != timeout);
106    timer_init(&hw_watchdog_timer);
107    return platform_watchdog_init(timeout, &hw_watchdog_pet_timeout);
108}
109
110void watchdog_hw_set_enabled(bool enabled)
111{
112    spin_lock_saved_state_t state;
113    spin_lock_irqsave(&lock, state);
114
115    if (hw_watchdog_enabled == enabled)
116        goto done;
117
118    hw_watchdog_enabled = enabled;
119    platform_watchdog_set_enabled(enabled);
120    if (enabled)
121        timer_set_periodic(&hw_watchdog_timer,
122                           hw_watchdog_pet_timeout,
123                           hw_watchdog_timer_callback,
124                           NULL);
125    else
126        timer_cancel(&hw_watchdog_timer);
127
128done:
129    spin_unlock_irqrestore(&lock, state);
130}
131