summaryrefslogtreecommitdiff
path: root/chip/lm4/watchdog.c
blob: ca76da2c2450f2b1f724d815b7c346dd54247920 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Watchdog driver */

#include <stdint.h>

#include "board.h"
#include "common.h"
#include "config.h"
#include "registers.h"
#include "gpio.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
#include "util.h"

/*
 * We use watchdog 0 which is clocked on the system clock
 * to avoid the penalty cycles on each write access
 */

/* magic value to unlock the watchdog registers */
#define LM4_WATCHDOG_MAGIC_WORD  0x1ACCE551

/* watchdog counter initial value */
static uint32_t watchdog_period;

/* console debug command prototypes */
int command_task_info(int argc, char **argv);
int command_timer_info(int argc, char **argv);

/**
 * watchdog debug trace.
 *
 * It is triggered if the watchdog has not been reloaded after 1x the timeout
 * period, after 2x the period an hardware reset is triggering.
 */
void watchdog_trace(uint32_t excep_lr, uint32_t excep_sp)
{
	uint32_t psp;
	uint32_t *stack;

	/* we do NOT reset the watchdog interrupt here, it will be done in
	 * watchdog_reload() or fire the reset
	 * instead de-activate the interrupt in the NVIC :
	 * so, we will get the trace only once
	 */
	task_disable_irq(LM4_IRQ_WATCHDOG);

	asm("mrs %0, psp":"=r"(psp));
	if ((excep_lr & 0xf) == 1) {
		/* we were already in exception context */
		stack = (uint32_t *)excep_sp;
	} else {
		/* we were in task context */
		stack = (uint32_t *)psp;
	}

	uart_printf("### WATCHDOG PC=%08x / LR=%08x / pSP=%08x ###\n",
	            stack[6], stack[5], psp);
	/* ensure this debug message is always flushed to the UART */
	uart_emergency_flush();
	/* if we are blocked in a high priority IT handler, the following
	 * debug messages might not appear but they are useless in that
	 * situation.
	 */
	command_task_info(0, NULL);
	uart_emergency_flush();
	command_timer_info(0, NULL);
	uart_emergency_flush();
}

void IRQ_HANDLER(LM4_IRQ_WATCHDOG)(void) __attribute__((naked));
void IRQ_HANDLER(LM4_IRQ_WATCHDOG)(void)
{
	asm volatile("mov r0, lr\n"
	             "mov r1, sp\n"
	             "push {lr}\n"
	             "bl watchdog_trace\n"
	             "pop {lr}\n"
	             "mov r0, lr\n"
		     "b task_resched_if_needed\n");
}
const struct irq_priority IRQ_BUILD_NAME(prio_, LM4_IRQ_WATCHDOG, )
	__attribute__((section(".rodata.irqprio")))
		= {LM4_IRQ_WATCHDOG, 0}; /* put the watchdog at the highest
					    priority */

void watchdog_reload(void)
{
	uint32_t status = LM4_WATCHDOG_RIS(0);

	/* unlock watchdog registers */
	LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD;

	/* As we reboot only on the second time-out,
	 * if we have already reached 1 time-out
	 * we need to reset the interrupt bit.
	 */
	if (status)
		LM4_WATCHDOG_ICR(0) = status;

	/* reload the watchdog counter */
	LM4_WATCHDOG_LOAD(0) = watchdog_period;

	/* re-lock watchdog registers */
	LM4_WATCHDOG_LOCK(0) = 0xdeaddead;
}

int watchdog_init(int period_ms)
{
	volatile uint32_t scratch  __attribute__((unused));

	/* Enable watchdog 0 clock */
	LM4_SYSTEM_RCGCWD |= 0x1;
	/* Wait 3 clock cycles before using the module */
	scratch = LM4_SYSTEM_RCGCWD;

	/* Unlock watchdog registers */
	LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD;

	/* Set the time-out period */
	watchdog_period = period_ms * (CPU_CLOCK / 1000);
	LM4_WATCHDOG_LOAD(0) = watchdog_period;

	/* de-activate the watchdog when the JTAG stops the CPU */
	LM4_WATCHDOG_TEST(0) |= 1 << 8;

	/* reset after 2 time-out,
	 * activate the watchdog and lock the control register
	 */
	LM4_WATCHDOG_CTL(0) = 0x3;

	/* Reset watchdog interrupt bits */
	LM4_WATCHDOG_ICR(0) = LM4_WATCHDOG_RIS(0);

	/* Lock watchdog registers against unintended accesses */
	LM4_WATCHDOG_LOCK(0) = 0xdeaddead;

	/* Enable watchdog interrupt */
	task_enable_irq(LM4_IRQ_WATCHDOG);

	return EC_SUCCESS;
}

/* Low priority task to reload the watchdog */
void watchdog_task(void)
{
	/* Print when the watchdog task starts.  This is the lowest priority
	 * task, so this only starts once all other tasks have gotten a chance
	 * to do their task inits and have gone to sleep. */
	uart_printf("[watchdog task started at %d us]\n", get_time().le.lo);

	while (1) {
#ifdef BOARD_bds
		gpio_set_level(GPIO_DEBUG_LED, 1);
#endif
		usleep(500000);
		watchdog_reload();
#ifdef BOARD_bds
		gpio_set_level(GPIO_DEBUG_LED, 0);
#endif
		usleep(500000);
		watchdog_reload();
	}
}