master
c 296 lines 8.27 KB
Raw
1 /*
2 * K230 Watchdog Compatible with kendryte K230 SDK
3 *
4 * Copyright (c) 2025 Mig Yang <temashking@foxmail.com>
5 * Copyright (c) 2025 Chao Liu <chao.liu.zevorn@gmail.com>
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 *
9 * Provides a board compatible with the kendryte K230 SDK
10 *
11 * K230 Technical Reference Manual V0.3.1 (2024-11-18):
12 * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
13 *
14 * For more information, see <https://www.kendryte.com/en/proDetail/230>
15 */
16 #include "qemu/osdep.h"
17 #include "qemu/bitops.h"
18 #include "qemu/module.h"
19 #include "system/watchdog.h"
20 #include "migration/vmstate.h"
21 #include "hw/core/qdev-properties.h"
22 #include "hw/watchdog/k230_wdt.h"
23 #include "trace.h"
24
25 static void k230_wdt_timeout(void *opaque)
26 {
27 K230WdtState *s = K230_WDT(opaque);
28
29 trace_k230_wdt_timeout();
30
31 /* Set interrupt status if in interrupt mode */
32 if (s->cr & K230_WDT_CR_RMOD) {
33 s->stat |= K230_WDT_STAT_INT;
34 s->interrupt_pending = true;
35 qemu_set_irq(s->irq, 1);
36 trace_k230_wdt_interrupt();
37 } else {
38 /* Direct reset mode */
39 trace_k230_wdt_reset();
40 watchdog_perform_action();
41 }
42
43 /* Restart counter */
44 s->current_count = s->timeout_value;
45 ptimer_set_count(s->timer, s->current_count);
46 ptimer_run(s->timer, 1);
47 }
48
49 static void k230_wdt_reset(DeviceState *dev)
50 {
51 K230WdtState *s = K230_WDT(dev);
52
53 trace_k230_wdt_reset_device();
54
55 ptimer_transaction_begin(s->timer);
56 ptimer_stop(s->timer);
57 ptimer_transaction_commit(s->timer);
58
59 /* Reset registers to default values */
60 s->cr = 0;
61 s->torr = 0;
62 s->ccvr = 0xFFFFFFFF;
63 s->stat = 0;
64 s->prot_level = 0x2;
65
66 s->interrupt_pending = false;
67 s->enabled = false;
68 s->timeout_value = 0;
69 s->current_count = 0xFFFFFFFF;
70 }
71
72 static uint64_t k230_wdt_read(void *opaque, hwaddr addr, unsigned int size)
73 {
74 K230WdtState *s = K230_WDT(opaque);
75 uint32_t value = 0;
76
77 switch (addr) {
78 case K230_WDT_CR:
79 value = s->cr;
80 break;
81 case K230_WDT_TORR:
82 value = s->torr;
83 break;
84 case K230_WDT_CCVR:
85 if (s->enabled) {
86 value = ptimer_get_count(s->timer);
87 } else {
88 value = s->current_count;
89 }
90 break;
91 case K230_WDT_STAT:
92 value = s->stat;
93 break;
94 case K230_WDT_PROT_LEVEL:
95 value = s->prot_level;
96 break;
97 case K230_WDT_COMP_PARAM_5:
98 value = 0; /* Upper limit of Timeout Period parameters */
99 break;
100 case K230_WDT_COMP_PARAM_4:
101 value = 0; /* Upper limit of Initial Timeout Period parameters */
102 break;
103 case K230_WDT_COMP_PARAM_3:
104 value = 0; /* Derived from WDT_TOP_RST parameter */
105 break;
106 case K230_WDT_COMP_PARAM_2:
107 value = 0xFFFFFFFF; /* Derived from WDT_RST_CNT parameter */
108 break;
109 case K230_WDT_COMP_PARAM_1:
110 /* Component parameters */
111 value = (32 << K230_WDT_CNT_WIDTH_SHIFT) | /* 32-bit counter */
112 (0 << K230_WDT_DFLT_TOP_INIT_SHIFT) |
113 (0 << K230_WDT_DFLT_TOP_SHIFT) |
114 (K230_WDT_RPL_16_CYCLES << K230_WDT_DFLT_RPL_SHIFT) |
115 (2 << K230_WDT_APB_DATA_WIDTH_SHIFT) | /* 32-bit APB */
116 K230_WDT_USE_FIX_TOP; /* Use fixed timeout values */
117 break;
118 case K230_WDT_COMP_VERSION:
119 value = K230_WDT_COMP_VERSION_VAL;
120 break;
121 case K230_WDT_COMP_TYPE:
122 value = K230_WDT_COMP_TYPE_VAL;
123 break;
124 default:
125 /* Other registers return 0 */
126 break;
127 }
128
129 trace_k230_wdt_read(addr, value);
130 return value;
131 }
132
133 static void k230_wdt_update_timer(K230WdtState *s)
134 {
135 ptimer_transaction_begin(s->timer);
136
137 if (s->enabled && s->timeout_value > 0) {
138 ptimer_set_count(s->timer, s->current_count);
139 ptimer_run(s->timer, 1);
140 } else {
141 ptimer_stop(s->timer);
142 }
143
144 ptimer_transaction_commit(s->timer);
145 }
146
147 static uint32_t k230_wdt_calculate_timeout(uint32_t top_value)
148 {
149 /* Calculate timeout based on TOP value */
150 /* For fixed timeout mode: 2^(16 + top_value) */
151 if (top_value <= 15) {
152 return 1 << (16 + top_value);
153 }
154 return 1 << 31; /* Maximum value for 32-bit counter */
155 }
156
157 static void k230_wdt_write(void *opaque, hwaddr addr,
158 uint64_t value, unsigned int size)
159 {
160 K230WdtState *s = K230_WDT(opaque);
161
162 trace_k230_wdt_write(addr, value);
163
164 switch (addr) {
165 case K230_WDT_CR:
166 s->cr = value & (K230_WDT_CR_RPL_MASK << K230_WDT_CR_RPL_SHIFT |
167 K230_WDT_CR_RMOD | K230_WDT_CR_WDT_EN);
168
169 /* Update enabled state */
170 s->enabled = (s->cr & K230_WDT_CR_WDT_EN) != 0;
171
172 /* Update timer */
173 k230_wdt_update_timer(s);
174 break;
175
176 case K230_WDT_TORR:
177 s->torr = value & K230_WDT_TORR_TOP_MASK;
178
179 /* Calculate new timeout value */
180 s->timeout_value = k230_wdt_calculate_timeout(s->torr);
181 s->current_count = s->timeout_value;
182
183 /* Update timer if enabled */
184 if (s->enabled) {
185 k230_wdt_update_timer(s);
186 }
187 break;
188
189 case K230_WDT_CRR:
190 /* Restart counter with magic value 0x76 */
191 if ((value & 0xFF) == K230_WDT_CRR_RESTART) {
192 trace_k230_wdt_restart();
193 s->current_count = s->timeout_value;
194
195 /* Clear interrupt if pending */
196 if (s->interrupt_pending) {
197 s->stat &= ~K230_WDT_STAT_INT;
198 s->interrupt_pending = false;
199 qemu_set_irq(s->irq, 0);
200 }
201
202 /* Update timer */
203 k230_wdt_update_timer(s);
204 }
205 break;
206
207 case K230_WDT_EOI:
208 /* Clear interrupt */
209 s->stat &= ~K230_WDT_STAT_INT;
210 s->interrupt_pending = false;
211 qemu_set_irq(s->irq, 0);
212 break;
213
214 case K230_WDT_PROT_LEVEL:
215 s->prot_level = value & 0x7;
216 break;
217
218 default:
219 /* Read-only registers, ignore writes */
220 break;
221 }
222 }
223
224 static const MemoryRegionOps k230_wdt_ops = {
225 .read = k230_wdt_read,
226 .write = k230_wdt_write,
227 .endianness = DEVICE_LITTLE_ENDIAN,
228 .impl = {
229 .min_access_size = 4,
230 .max_access_size = 4,
231 .unaligned = false,
232 },
233 };
234
235 static const VMStateDescription vmstate_k230_wdt = {
236 .name = "k230.wdt",
237 .fields = (const VMStateField[]) {
238 VMSTATE_PTIMER(timer, K230WdtState),
239 VMSTATE_UINT32(cr, K230WdtState),
240 VMSTATE_UINT32(torr, K230WdtState),
241 VMSTATE_UINT32(ccvr, K230WdtState),
242 VMSTATE_UINT32(stat, K230WdtState),
243 VMSTATE_UINT32(prot_level, K230WdtState),
244 VMSTATE_BOOL(interrupt_pending, K230WdtState),
245 VMSTATE_BOOL(enabled, K230WdtState),
246 VMSTATE_UINT32(timeout_value, K230WdtState),
247 VMSTATE_UINT32(current_count, K230WdtState),
248 VMSTATE_END_OF_LIST()
249 }
250 };
251
252 static void k230_wdt_realize(DeviceState *dev, Error **errp)
253 {
254 K230WdtState *s = K230_WDT(dev);
255 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
256
257 memory_region_init_io(&s->mmio, OBJECT(dev),
258 &k230_wdt_ops, s,
259 TYPE_K230_WDT,
260 K230_WDT_MMIO_SIZE);
261 sysbus_init_mmio(sbd, &s->mmio);
262 sysbus_init_irq(sbd, &s->irq);
263
264 s->timer = ptimer_init(k230_wdt_timeout, s,
265 PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
266 PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
267 PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
268
269 ptimer_transaction_begin(s->timer);
270 ptimer_set_freq(s->timer, K230_WDT_DEFAULT_FREQ);
271 ptimer_transaction_commit(s->timer);
272 }
273
274 static void k230_wdt_class_init(ObjectClass *klass, const void *data)
275 {
276 DeviceClass *dc = DEVICE_CLASS(klass);
277
278 dc->realize = k230_wdt_realize;
279 device_class_set_legacy_reset(dc, k230_wdt_reset);
280 dc->vmsd = &vmstate_k230_wdt;
281 dc->desc = "K230 watchdog timer";
282 set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
283 }
284
285 static const TypeInfo k230_wdt_info = {
286 .name = TYPE_K230_WDT,
287 .parent = TYPE_SYS_BUS_DEVICE,
288 .instance_size = sizeof(K230WdtState),
289 .class_init = k230_wdt_class_init,
290 };
291
292 static void k230_wdt_register_type(void)
293 {
294 type_register_static(&k230_wdt_info);
295 }
296 type_init(k230_wdt_register_type)