1 /* Copyright 2019 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
4 #include <metal/machine/platform.h>
6 #ifdef METAL_SIFIVE_WDOG0
8 #include <metal/drivers/sifive_uart0.h>
9 #include <metal/machine.h>
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)
22 #define METAL_WDOGCMP_MASK 0xFFFF
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)))
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)
32 /* Unlock the watchdog and then perform a register access */
33 #define WDOG_UNLOCK_REGW(base, offset) \
35 WDOG_REGW(base, offset)
37 int __metal_driver_sifive_wdog0_feed(const struct metal_watchdog *const wdog)
39 const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
41 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGFEED) = METAL_SIFIVE_WDOG0_MAGIC_FOOD;
46 long int __metal_driver_sifive_wdog0_get_rate(const struct metal_watchdog *const wdog)
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);
51 const long int clock_rate = metal_clock_get_rate_hz(clock);
56 const unsigned int scale = (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) & METAL_WDOGCFG_SCALE_MASK);
58 return clock_rate / (1 << scale);
61 long int __metal_driver_sifive_wdog0_set_rate(const struct metal_watchdog *const wdog, const long int rate)
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);
66 const long int clock_rate = metal_clock_get_rate_hz(clock);
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);
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);
81 long int diff = rate - new_rate;
85 if (diff < min_diff) {
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);
94 return clock_rate / (1 << min_scale);
97 long int __metal_driver_sifive_wdog0_get_timeout(const struct metal_watchdog *const wdog)
99 const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
101 return (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP) & METAL_WDOGCMP_MASK);
104 long int __metal_driver_sifive_wdog0_set_timeout(const struct metal_watchdog *const wdog, const long int timeout)
106 const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
108 /* Cap the timeout at the max value */
109 const long int set_timeout = timeout > METAL_WDOGCMP_MASK ? METAL_WDOGCMP_MASK : timeout;
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);
117 wdogcmp &= ~(METAL_WDOGCMP_MASK);
118 wdogcmp |= set_timeout;
120 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP) = wdogcmp;
125 int __metal_driver_sifive_wdog0_set_result(const struct metal_watchdog *const wdog,
126 const enum metal_watchdog_result result)
128 const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
130 /* Turn off reset enable and counter reset */
131 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_RSTEN | METAL_WDOGCFG_ZEROCMP);
135 case METAL_WATCHDOG_NO_RESULT:
137 case METAL_WATCHDOG_INTERRUPT:
138 /* Reset counter to zero after match */
139 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_ZEROCMP;
141 case METAL_WATCHDOG_FULL_RESET:
142 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_RSTEN;
149 int __metal_driver_sifive_wdog0_run(const struct metal_watchdog *const wdog,
150 const enum metal_watchdog_run_option option)
152 const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
154 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_ENALWAYS | METAL_WDOGCFG_COREAWAKE);
158 case METAL_WATCHDOG_STOP:
160 case METAL_WATCHDOG_RUN_ALWAYS:
161 /* Feed the watchdog before starting to reset counter */
162 __metal_driver_sifive_wdog0_feed(wdog);
164 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_ENALWAYS;
166 case METAL_WATCHDOG_RUN_AWAKE:
167 /* Feed the watchdog before starting to reset counter */
168 __metal_driver_sifive_wdog0_feed(wdog);
170 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_COREAWAKE;
177 struct metal_interrupt *__metal_driver_sifive_wdog0_get_interrupt(const struct metal_watchdog *const wdog)
179 return __metal_driver_sifive_wdog0_interrupt_parent(wdog);
182 int __metal_driver_sifive_wdog0_get_interrupt_id(const struct metal_watchdog *const wdog)
184 return __metal_driver_sifive_wdog0_interrupt_line(wdog);
187 int __metal_driver_sifive_wdog0_clear_interrupt(const struct metal_watchdog *const wdog)
189 const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
191 /* Clear the interrupt pending bit */
192 WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_IP);
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,
210 #endif /* METAL_SIFIVE_WDOG0 */
212 typedef int no_empty_translation_units;