1 /*
2 * Copyright (c) 2022 TOKITA Hiroshi
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT gd_gd32_fwdgt
8
9 #include <zephyr/drivers/watchdog.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/sys_clock.h>
12
13 #include <gd32_fwdgt.h>
14
15 LOG_MODULE_REGISTER(wdt_fwdgt_gd32, CONFIG_WDT_LOG_LEVEL);
16
17 #define FWDGT_RELOAD_MAX (0xFFFU)
18 #define FWDGT_PRESCALER_MAX (256U)
19
20 #if defined(CONFIG_GD32_HAS_IRC_32K)
21 #define RCU_IRC_LOW_SPEED RCU_IRC32K
22 #elif defined(CONFIG_GD32_HAS_IRC_40K)
23 #define RCU_IRC_LOW_SPEED RCU_IRC40K
24 #else
25 #error IRC frequency was not configured
26 #endif
27
28 #define IS_VALID_FWDGT_PRESCALER(psc) \
29 (((psc) == FWDGT_PSC_DIV4) || ((psc) == FWDGT_PSC_DIV8) || \
30 ((psc) == FWDGT_PSC_DIV16) || ((psc) == FWDGT_PSC_DIV32) || \
31 ((psc) == FWDGT_PSC_DIV64) || ((psc) == FWDGT_PSC_DIV128) || \
32 ((psc) == FWDGT_PSC_DIV256))
33
34 #define FWDGT_INITIAL_TIMEOUT DT_INST_PROP(0, initial_timeout_ms)
35
36 #if (FWDGT_INITIAL_TIMEOUT <= 0)
37 #error Must be initial-timeout > 0
38 #elif (FWDGT_INITIAL_TIMEOUT > \
39 (FWDGT_PRESCALER_MAX * FWDGT_RELOAD_MAX * MSEC_PER_SEC / \
40 CONFIG_GD32_LOW_SPEED_IRC_FREQUENCY))
41 #error Must be initial-timeout <= (256 * 4095 * 1000 / GD32_LOW_SPEED_IRC_FREQUENCY)
42 #endif
43
44 /**
45 * @brief Calculates FWDGT config value from timeout.
46 *
47 * @param timeout Timeout value in milliseconds.
48 * @param prescaler Pointer to the storage of prescaler value.
49 * @param reload Pointer to the storage of reload value.
50 *
51 * @return 0 on success, -EINVAL if the timeout is out of range
52 */
gd32_fwdgt_calc_timeout(uint32_t timeout,uint32_t * prescaler,uint32_t * reload)53 static int gd32_fwdgt_calc_timeout(uint32_t timeout, uint32_t *prescaler,
54 uint32_t *reload)
55 {
56 uint16_t divider = 4U;
57 uint8_t shift = 0U;
58 uint32_t ticks = (uint64_t)CONFIG_GD32_LOW_SPEED_IRC_FREQUENCY *
59 timeout / MSEC_PER_SEC;
60
61 while ((ticks / divider) > FWDGT_RELOAD_MAX) {
62 shift++;
63 divider = 4U << shift;
64 }
65
66 if (!IS_VALID_FWDGT_PRESCALER(PSC_PSC(shift)) || timeout == 0U) {
67 return -EINVAL;
68 }
69
70 /* convert the 'shift' to prescaler value */
71 *prescaler = PSC_PSC(shift);
72 *reload = (ticks / divider) - 1U;
73
74 return 0;
75 }
76
gd32_fwdgt_setup(const struct device * dev,uint8_t options)77 static int gd32_fwdgt_setup(const struct device *dev, uint8_t options)
78 {
79 ARG_UNUSED(dev);
80
81 if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) != 0U) {
82 #if CONFIG_GD32_DBG_SUPPORT
83 dbg_periph_enable(DBG_FWDGT_HOLD);
84 #else
85 LOG_ERR("Debug support not enabled");
86 return -ENOTSUP;
87 #endif
88 }
89
90 if ((options & WDT_OPT_PAUSE_IN_SLEEP) != 0U) {
91 LOG_ERR("WDT_OPT_PAUSE_IN_SLEEP not supported");
92 return -ENOTSUP;
93 }
94
95 fwdgt_enable();
96
97 return 0;
98 }
99
gd32_fwdgt_disable(const struct device * dev)100 static int gd32_fwdgt_disable(const struct device *dev)
101 {
102 /* watchdog cannot be stopped once started */
103 ARG_UNUSED(dev);
104
105 return -EPERM;
106 }
107
gd32_fwdgt_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * config)108 static int gd32_fwdgt_install_timeout(const struct device *dev,
109 const struct wdt_timeout_cfg *config)
110 {
111 uint32_t prescaler = 0U;
112 uint32_t reload = 0U;
113 ErrStatus errstat = ERROR;
114
115 /* Callback is not supported by FWDGT */
116 if (config->callback != NULL) {
117 LOG_ERR("callback not supported by FWDGT");
118 return -ENOTSUP;
119 }
120
121 /* Calculate prescaler and reload value from timeout value */
122 if (gd32_fwdgt_calc_timeout(config->window.max, &prescaler,
123 &reload) != 0) {
124 LOG_ERR("window max is out of range");
125 return -EINVAL;
126 }
127
128 /* Configure and run FWDGT */
129 fwdgt_write_enable();
130 errstat = fwdgt_config(reload, prescaler);
131 if (errstat != SUCCESS) {
132 LOG_ERR("fwdgt_config() failed: %d", errstat);
133 return -EINVAL;
134 }
135 fwdgt_write_disable();
136
137 return 0;
138 }
139
gd32_fwdgt_feed(const struct device * dev,int channel_id)140 static int gd32_fwdgt_feed(const struct device *dev, int channel_id)
141 {
142 ARG_UNUSED(channel_id);
143
144 fwdgt_counter_reload();
145
146 return 0;
147 }
148
149 static const struct wdt_driver_api fwdgt_gd32_api = {
150 .setup = gd32_fwdgt_setup,
151 .disable = gd32_fwdgt_disable,
152 .install_timeout = gd32_fwdgt_install_timeout,
153 .feed = gd32_fwdgt_feed,
154 };
155
gd32_fwdgt_init(const struct device * dev)156 static int gd32_fwdgt_init(const struct device *dev)
157 {
158 int ret = 0;
159
160 /* Turn on and wait stabilize system clock oscillator. */
161 rcu_osci_on(RCU_IRC_LOW_SPEED);
162 while (!rcu_osci_stab_wait(RCU_IRC_LOW_SPEED)) {
163 }
164
165 #if !defined(CONFIG_WDT_DISABLE_AT_BOOT)
166 const struct wdt_timeout_cfg config = {
167 .window.max = FWDGT_INITIAL_TIMEOUT
168 };
169
170 ret = gd32_fwdgt_install_timeout(dev, &config);
171 #endif
172
173 return ret;
174 }
175
176 DEVICE_DT_INST_DEFINE(0, gd32_fwdgt_init, NULL, NULL, NULL, POST_KERNEL,
177 CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &fwdgt_gd32_api);
178