1 /*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <lk/err.h>
9 #include <lk/debug.h>
10 #include <lk/trace.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <lib/watchdog.h>
14 #include <platform.h>
15 #include <platform/zynq.h>
16
17 /* Driver for the system wide watchdog timer for Zynq */
18
19 #define LOCAL_TRACE 0
20
21 /* three magic values for the MODE, CONTROL, and RESTART registers */
22 #define SWDT_MODE_ZKEY (0xabc << 12)
23 #define SWDT_CONTROL_CKEY (0x248 << 14)
24 #define SWDT_RESTART_RSTKEY (0x1999)
25
26 /* reserved field in the MODE register */
27 #define SWDT_MODE_RESERVED (0x4 << 4)
28
29 /* routines called from lib/watchdog */
platform_watchdog_init(lk_time_t target_timeout,lk_time_t * recommended_pet_period)30 status_t platform_watchdog_init(lk_time_t target_timeout,
31 lk_time_t *recommended_pet_period) {
32 LTRACEF("target_timeout %u\n", (uint32_t)target_timeout);
33
34 /* make sure the swdt is stopped */
35 SWDT->MODE = SWDT_MODE_ZKEY | SWDT_MODE_RESERVED;
36
37 /* make sure swdt has the proper clock */
38 SLCR->WDT_CLK_SEL = 0; // cpu 1x
39
40 uint32_t swdt_clock = zynq_get_swdt_freq();
41
42 /* assuming a prescalar of / 4096, figure out the restart value */
43 uint32_t restart = ((swdt_clock / 4096) * target_timeout) / 1000;
44
45 /* make sure the restart value is <= 24 bits */
46 if (restart > 0x00ffffff)
47 restart = 0x00ffffff;
48
49 LTRACEF("restart value %u\n", restart);
50
51 /* the bottom 12 bits of restart are set to 0xfff by hardware */
52 restart |= 0xfff;
53
54 /* pet period is / 2 the computed restart value */
55 if (recommended_pet_period)
56 *recommended_pet_period = ((restart * 1000) / (swdt_clock / 4096)) / 2;
57
58 LTRACEF("recommended pet period %u\n", (uint32_t)*recommended_pet_period);
59
60 /* set up the swdt */
61
62 /* load counter restart (top 12 bits of restart count), pclk / 4096 */
63 SWDT->CONTROL = SWDT_CONTROL_CKEY | ((restart >> 12) << 2) | 3;
64
65 /* zero it out */
66 SWDT->RESTART = SWDT_RESTART_RSTKEY;
67
68 DMB;
69
70 return NO_ERROR;
71 }
72
platform_watchdog_set_enabled(bool enabled)73 void platform_watchdog_set_enabled(bool enabled) {
74 LTRACEF("enabled %u\n", enabled);
75
76 if (enabled) {
77 /* wide irq length, reset enable on counter zero, enable */
78 SWDT->MODE = SWDT_MODE_ZKEY | SWDT_MODE_RESERVED | (3 << 7) | (1<<1) | 1;
79
80 /* start it by petting it once, in case it already latched */
81 SWDT->RESTART = SWDT_RESTART_RSTKEY;
82 } else {
83 /* disable everything */
84 SWDT->MODE = SWDT_MODE_ZKEY | SWDT_MODE_RESERVED;
85 }
86 DMB;
87 }
88
platform_watchdog_pet(void)89 void platform_watchdog_pet(void) {
90 //LTRACEF("pet\n");
91 SWDT->RESTART = SWDT_RESTART_RSTKEY;
92 DMB;
93 }
94
95