]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/sifive_wdog0.c
Update RISCC-V-RV32-SiFive_HiFive1_FreedomStudio project to latest tools and metal...
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_FreedomStudio / freedom-metal / src / drivers / sifive_wdog0.c
1 /* Copyright 2019 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
3
4 #include <metal/machine/platform.h>
5
6 #ifdef METAL_SIFIVE_WDOG0
7
8 #include <metal/drivers/sifive_uart0.h>
9 #include <metal/machine.h>
10
11 #include <limits.h>
12
13 /* WDOGCFG */
14 #define METAL_WDOGCFG_SCALE_MASK 7
15 #define METAL_WDOGCFG_RSTEN (1 << 8)
16 #define METAL_WDOGCFG_ZEROCMP (1 << 9)
17 #define METAL_WDOGCFG_ENALWAYS (1 << 12)
18 #define METAL_WDOGCFG_COREAWAKE (1 << 13)
19 #define METAL_WDOGCFG_IP (1 << 28)
20
21 /* WDOGCMP */
22 #define METAL_WDOGCMP_MASK 0xFFFF
23
24 #define WDOG_REG(base, offset)   (((unsigned long)base + offset))
25 #define WDOG_REGB(base, offset)  (__METAL_ACCESS_ONCE((__metal_io_u8  *)WDOG_REG(base, offset)))
26 #define WDOG_REGW(base, offset)  (__METAL_ACCESS_ONCE((__metal_io_u32 *)WDOG_REG(base, offset)))
27
28 /* All writes to watchdog registers must be precedded by a write of
29  * a magic number to WDOGKEY */
30 #define WDOG_UNLOCK(base) (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGKEY) = METAL_SIFIVE_WDOG0_MAGIC_KEY)
31
32 /* Unlock the watchdog and then perform a register access */
33 #define WDOG_UNLOCK_REGW(base, offset) \
34     WDOG_UNLOCK(base);\
35     WDOG_REGW(base, offset)
36
37 int __metal_driver_sifive_wdog0_feed(const struct metal_watchdog *const wdog)
38 {
39     const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
40
41     WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGFEED) = METAL_SIFIVE_WDOG0_MAGIC_FOOD;
42
43     return 0;
44 }
45
46 long int __metal_driver_sifive_wdog0_get_rate(const struct metal_watchdog *const wdog)
47 {
48     const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
49     const struct metal_clock *const clock = __metal_driver_sifive_wdog0_clock(wdog);
50     
51     const long int clock_rate = metal_clock_get_rate_hz(clock);
52
53     if (clock_rate == 0)
54         return -1;
55
56     const unsigned int scale = (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) & METAL_WDOGCFG_SCALE_MASK);
57
58     return clock_rate / (1 << scale);
59 }
60
61 long int __metal_driver_sifive_wdog0_set_rate(const struct metal_watchdog *const wdog, const long int rate)
62 {
63     const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
64     const struct metal_clock *const clock = __metal_driver_sifive_wdog0_clock(wdog);
65     
66     const long int clock_rate = metal_clock_get_rate_hz(clock);
67
68     if (rate >= clock_rate) {
69         /* We can't scale the rate above the driving clock. Clear the scale
70          * field and return the driving clock rate */
71         WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_SCALE_MASK);
72         return clock_rate;
73     }
74
75     /* Look for the closest scale value */
76     long min_diff = LONG_MAX;
77     unsigned int min_scale = 0;
78     for (int i = 0; i < METAL_WDOGCFG_SCALE_MASK; i++) {
79         const long int new_rate = clock_rate / (1 << i);
80
81         long int diff = rate - new_rate;
82         if (diff < 0)
83             diff *= -1;
84
85         if (diff < min_diff) {
86             min_diff = diff;
87             min_scale = i;
88         }
89     }
90
91     WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_SCALE_MASK);
92     WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= (METAL_WDOGCFG_SCALE_MASK & min_scale);
93
94     return clock_rate / (1 << min_scale);
95 }
96
97 long int __metal_driver_sifive_wdog0_get_timeout(const struct metal_watchdog *const wdog)
98 {
99     const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
100
101     return (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP) & METAL_WDOGCMP_MASK);
102 }
103
104 long int __metal_driver_sifive_wdog0_set_timeout(const struct metal_watchdog *const wdog, const long int timeout)
105 {
106     const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
107
108     /* Cap the timeout at the max value */
109     const long int set_timeout = timeout > METAL_WDOGCMP_MASK ? METAL_WDOGCMP_MASK : timeout;
110
111     /* If we edit the timeout value in-place by masking the compare value to 0 and
112      * then writing it, we cause a spurious interrupt because the compare value
113      * is temporarily 0. Instead, read the value into a local variable, modify it
114      * there, and then write the whole register back */
115     uint32_t wdogcmp = WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP);
116
117     wdogcmp &= ~(METAL_WDOGCMP_MASK);
118     wdogcmp |= set_timeout;
119
120     WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP) = wdogcmp;
121
122     return set_timeout;
123 }
124
125 int __metal_driver_sifive_wdog0_set_result(const struct metal_watchdog *const wdog,
126                                            const enum metal_watchdog_result result)
127 {
128     const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
129
130     /* Turn off reset enable and counter reset */
131     WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_RSTEN | METAL_WDOGCFG_ZEROCMP);
132
133     switch (result) {
134     default:
135     case METAL_WATCHDOG_NO_RESULT:
136         break;
137     case METAL_WATCHDOG_INTERRUPT:
138         /* Reset counter to zero after match */
139         WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_ZEROCMP;
140         break;
141     case METAL_WATCHDOG_FULL_RESET:
142         WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_RSTEN;
143         break;
144     }
145
146     return 0;
147 }
148
149 int __metal_driver_sifive_wdog0_run(const struct metal_watchdog *const wdog,
150                                     const enum metal_watchdog_run_option option)
151 {
152     const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
153
154     WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_ENALWAYS | METAL_WDOGCFG_COREAWAKE);
155
156     switch (option) {
157     default:
158     case METAL_WATCHDOG_STOP:
159         break;
160     case METAL_WATCHDOG_RUN_ALWAYS:
161         /* Feed the watchdog before starting to reset counter */
162         __metal_driver_sifive_wdog0_feed(wdog);
163
164         WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_ENALWAYS;
165         break;
166     case METAL_WATCHDOG_RUN_AWAKE:
167         /* Feed the watchdog before starting to reset counter */
168         __metal_driver_sifive_wdog0_feed(wdog);
169
170         WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_COREAWAKE;
171         break;
172     }
173
174     return 0;
175 }
176
177 struct metal_interrupt *__metal_driver_sifive_wdog0_get_interrupt(const struct metal_watchdog *const wdog)
178 {
179     return __metal_driver_sifive_wdog0_interrupt_parent(wdog);
180 }
181
182 int __metal_driver_sifive_wdog0_get_interrupt_id(const struct metal_watchdog *const wdog)
183 {
184     return __metal_driver_sifive_wdog0_interrupt_line(wdog);
185 }
186
187 int __metal_driver_sifive_wdog0_clear_interrupt(const struct metal_watchdog *const wdog)
188 {
189     const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
190
191     /* Clear the interrupt pending bit */
192     WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_IP);
193
194     return 0;
195 }
196
197 __METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_wdog0) = {
198     .watchdog.feed = __metal_driver_sifive_wdog0_feed,
199     .watchdog.get_rate = __metal_driver_sifive_wdog0_get_rate,
200     .watchdog.set_rate = __metal_driver_sifive_wdog0_set_rate,
201     .watchdog.get_timeout = __metal_driver_sifive_wdog0_get_timeout,
202     .watchdog.set_timeout = __metal_driver_sifive_wdog0_set_timeout,
203     .watchdog.set_result = __metal_driver_sifive_wdog0_set_result,
204     .watchdog.run = __metal_driver_sifive_wdog0_run,
205     .watchdog.get_interrupt = __metal_driver_sifive_wdog0_get_interrupt,
206     .watchdog.get_interrupt_id = __metal_driver_sifive_wdog0_get_interrupt_id,
207     .watchdog.clear_interrupt = __metal_driver_sifive_wdog0_clear_interrupt,
208 };
209
210 #endif /* METAL_SIFIVE_WDOG0 */
211
212 typedef int no_empty_translation_units;
213