+++ /dev/null
-/* Copyright 2019 SiFive, Inc */
-/* SPDX-License-Identifier: Apache-2.0 */
-
-#include <metal/machine/platform.h>
-
-#ifdef METAL_SIFIVE_WDOG0
-
-#include <metal/drivers/sifive_uart0.h>
-#include <metal/machine.h>
-
-#include <limits.h>
-
-/* WDOGCFG */
-#define METAL_WDOGCFG_SCALE_MASK 7
-#define METAL_WDOGCFG_RSTEN (1 << 8)
-#define METAL_WDOGCFG_ZEROCMP (1 << 9)
-#define METAL_WDOGCFG_ENALWAYS (1 << 12)
-#define METAL_WDOGCFG_COREAWAKE (1 << 13)
-#define METAL_WDOGCFG_IP (1 << 28)
-
-/* WDOGCMP */
-#define METAL_WDOGCMP_MASK 0xFFFF
-
-#define WDOG_REG(base, offset) (((unsigned long)base + offset))
-#define WDOG_REGB(base, offset) (__METAL_ACCESS_ONCE((__metal_io_u8 *)WDOG_REG(base, offset)))
-#define WDOG_REGW(base, offset) (__METAL_ACCESS_ONCE((__metal_io_u32 *)WDOG_REG(base, offset)))
-
-/* All writes to watchdog registers must be precedded by a write of
- * a magic number to WDOGKEY */
-#define WDOG_UNLOCK(base) (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGKEY) = METAL_SIFIVE_WDOG0_MAGIC_KEY)
-
-/* Unlock the watchdog and then perform a register access */
-#define WDOG_UNLOCK_REGW(base, offset) \
- WDOG_UNLOCK(base);\
- WDOG_REGW(base, offset)
-
-int __metal_driver_sifive_wdog0_feed(const struct metal_watchdog *const wdog)
-{
- const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
-
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGFEED) = METAL_SIFIVE_WDOG0_MAGIC_FOOD;
-
- return 0;
-}
-
-long int __metal_driver_sifive_wdog0_get_rate(const struct metal_watchdog *const wdog)
-{
- const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
- const struct metal_clock *const clock = __metal_driver_sifive_wdog0_clock(wdog);
-
- const long int clock_rate = metal_clock_get_rate_hz(clock);
-
- if (clock_rate == 0)
- return -1;
-
- const unsigned int scale = (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) & METAL_WDOGCFG_SCALE_MASK);
-
- return clock_rate / (1 << scale);
-}
-
-long int __metal_driver_sifive_wdog0_set_rate(const struct metal_watchdog *const wdog, const long int rate)
-{
- const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
- const struct metal_clock *const clock = __metal_driver_sifive_wdog0_clock(wdog);
-
- const long int clock_rate = metal_clock_get_rate_hz(clock);
-
- if (rate >= clock_rate) {
- /* We can't scale the rate above the driving clock. Clear the scale
- * field and return the driving clock rate */
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_SCALE_MASK);
- return clock_rate;
- }
-
- /* Look for the closest scale value */
- long min_diff = LONG_MAX;
- unsigned int min_scale = 0;
- for (int i = 0; i < METAL_WDOGCFG_SCALE_MASK; i++) {
- const long int new_rate = clock_rate / (1 << i);
-
- long int diff = rate - new_rate;
- if (diff < 0)
- diff *= -1;
-
- if (diff < min_diff) {
- min_diff = diff;
- min_scale = i;
- }
- }
-
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_SCALE_MASK);
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= (METAL_WDOGCFG_SCALE_MASK & min_scale);
-
- return clock_rate / (1 << min_scale);
-}
-
-long int __metal_driver_sifive_wdog0_get_timeout(const struct metal_watchdog *const wdog)
-{
- const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
-
- return (WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP) & METAL_WDOGCMP_MASK);
-}
-
-long int __metal_driver_sifive_wdog0_set_timeout(const struct metal_watchdog *const wdog, const long int timeout)
-{
- const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
-
- /* Cap the timeout at the max value */
- const long int set_timeout = timeout > METAL_WDOGCMP_MASK ? METAL_WDOGCMP_MASK : timeout;
-
- /* If we edit the timeout value in-place by masking the compare value to 0 and
- * then writing it, we cause a spurious interrupt because the compare value
- * is temporarily 0. Instead, read the value into a local variable, modify it
- * there, and then write the whole register back */
- uint32_t wdogcmp = WDOG_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP);
-
- wdogcmp &= ~(METAL_WDOGCMP_MASK);
- wdogcmp |= set_timeout;
-
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCMP) = wdogcmp;
-
- return set_timeout;
-}
-
-int __metal_driver_sifive_wdog0_set_result(const struct metal_watchdog *const wdog,
- const enum metal_watchdog_result result)
-{
- const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
-
- /* Turn off reset enable and counter reset */
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_RSTEN | METAL_WDOGCFG_ZEROCMP);
-
- switch (result) {
- default:
- case METAL_WATCHDOG_NO_RESULT:
- break;
- case METAL_WATCHDOG_INTERRUPT:
- /* Reset counter to zero after match */
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_ZEROCMP;
- break;
- case METAL_WATCHDOG_FULL_RESET:
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_RSTEN;
- break;
- }
-
- return 0;
-}
-
-int __metal_driver_sifive_wdog0_run(const struct metal_watchdog *const wdog,
- const enum metal_watchdog_run_option option)
-{
- const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
-
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_ENALWAYS | METAL_WDOGCFG_COREAWAKE);
-
- switch (option) {
- default:
- case METAL_WATCHDOG_STOP:
- break;
- case METAL_WATCHDOG_RUN_ALWAYS:
- /* Feed the watchdog before starting to reset counter */
- __metal_driver_sifive_wdog0_feed(wdog);
-
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_ENALWAYS;
- break;
- case METAL_WATCHDOG_RUN_AWAKE:
- /* Feed the watchdog before starting to reset counter */
- __metal_driver_sifive_wdog0_feed(wdog);
-
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) |= METAL_WDOGCFG_COREAWAKE;
- break;
- }
-
- return 0;
-}
-
-struct metal_interrupt *__metal_driver_sifive_wdog0_get_interrupt(const struct metal_watchdog *const wdog)
-{
- return __metal_driver_sifive_wdog0_interrupt_parent(wdog);
-}
-
-int __metal_driver_sifive_wdog0_get_interrupt_id(const struct metal_watchdog *const wdog)
-{
- return __metal_driver_sifive_wdog0_interrupt_line(wdog);
-}
-
-int __metal_driver_sifive_wdog0_clear_interrupt(const struct metal_watchdog *const wdog)
-{
- const uintptr_t base = (uintptr_t)__metal_driver_sifive_wdog0_control_base(wdog);
-
- /* Clear the interrupt pending bit */
- WDOG_UNLOCK_REGW(base, METAL_SIFIVE_WDOG0_WDOGCFG) &= ~(METAL_WDOGCFG_IP);
-
- return 0;
-}
-
-__METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_wdog0) = {
- .watchdog.feed = __metal_driver_sifive_wdog0_feed,
- .watchdog.get_rate = __metal_driver_sifive_wdog0_get_rate,
- .watchdog.set_rate = __metal_driver_sifive_wdog0_set_rate,
- .watchdog.get_timeout = __metal_driver_sifive_wdog0_get_timeout,
- .watchdog.set_timeout = __metal_driver_sifive_wdog0_set_timeout,
- .watchdog.set_result = __metal_driver_sifive_wdog0_set_result,
- .watchdog.run = __metal_driver_sifive_wdog0_run,
- .watchdog.get_interrupt = __metal_driver_sifive_wdog0_get_interrupt,
- .watchdog.get_interrupt_id = __metal_driver_sifive_wdog0_get_interrupt_id,
- .watchdog.clear_interrupt = __metal_driver_sifive_wdog0_clear_interrupt,
-};
-
-#endif /* METAL_SIFIVE_WDOG0 */
-
-typedef int no_empty_translation_units;
-