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