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