capstone: Update to upstream "next" branch
[qemu.git] / hw / watchdog / wdt_imx2.c
1 /*
2 * Copyright (c) 2018, Impinj, Inc.
3 *
4 * i.MX2 Watchdog IP block
5 *
6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 * See the COPYING file in the top-level directory.
10 */
11
12 #include "qemu/osdep.h"
13 #include "qemu/bitops.h"
14 #include "qemu/module.h"
15 #include "sysemu/watchdog.h"
16 #include "migration/vmstate.h"
17 #include "hw/qdev-properties.h"
18
19 #include "hw/watchdog/wdt_imx2.h"
20
21 static void imx2_wdt_interrupt(void *opaque)
22 {
23 IMX2WdtState *s = IMX2_WDT(opaque);
24
25 s->wicr |= IMX2_WDT_WICR_WTIS;
26 qemu_set_irq(s->irq, 1);
27 }
28
29 static void imx2_wdt_expired(void *opaque)
30 {
31 IMX2WdtState *s = IMX2_WDT(opaque);
32
33 s->wrsr = IMX2_WDT_WRSR_TOUT;
34
35 /* Perform watchdog action if watchdog is enabled */
36 if (s->wcr & IMX2_WDT_WCR_WDE) {
37 s->wrsr = IMX2_WDT_WRSR_TOUT;
38 watchdog_perform_action();
39 }
40 }
41
42 static void imx2_wdt_reset(DeviceState *dev)
43 {
44 IMX2WdtState *s = IMX2_WDT(dev);
45
46 ptimer_transaction_begin(s->timer);
47 ptimer_stop(s->timer);
48 ptimer_transaction_commit(s->timer);
49
50 if (s->pretimeout_support) {
51 ptimer_transaction_begin(s->itimer);
52 ptimer_stop(s->itimer);
53 ptimer_transaction_commit(s->itimer);
54 }
55
56 s->wicr_locked = false;
57 s->wcr_locked = false;
58 s->wcr_wde_locked = false;
59
60 s->wcr = IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS;
61 s->wsr = 0;
62 s->wrsr &= ~(IMX2_WDT_WRSR_TOUT | IMX2_WDT_WRSR_SFTW);
63 s->wicr = IMX2_WDT_WICR_WICT_DEF;
64 s->wmcr = IMX2_WDT_WMCR_PDE;
65 }
66
67 static uint64_t imx2_wdt_read(void *opaque, hwaddr addr, unsigned int size)
68 {
69 IMX2WdtState *s = IMX2_WDT(opaque);
70
71 switch (addr) {
72 case IMX2_WDT_WCR:
73 return s->wcr;
74 case IMX2_WDT_WSR:
75 return s->wsr;
76 case IMX2_WDT_WRSR:
77 return s->wrsr;
78 case IMX2_WDT_WICR:
79 return s->wicr;
80 case IMX2_WDT_WMCR:
81 return s->wmcr;
82 }
83 return 0;
84 }
85
86 static void imx_wdt2_update_itimer(IMX2WdtState *s, bool start)
87 {
88 bool running = (s->wcr & IMX2_WDT_WCR_WDE) && (s->wcr & IMX2_WDT_WCR_WT);
89 bool enabled = s->wicr & IMX2_WDT_WICR_WIE;
90
91 ptimer_transaction_begin(s->itimer);
92 if (start || !enabled) {
93 ptimer_stop(s->itimer);
94 }
95 if (running && enabled) {
96 int count = ptimer_get_count(s->timer);
97 int pretimeout = s->wicr & IMX2_WDT_WICR_WICT;
98
99 /*
100 * Only (re-)start pretimeout timer if its counter value is larger
101 * than 0. Otherwise it will fire right away and we'll get an
102 * interrupt loop.
103 */
104 if (count > pretimeout) {
105 ptimer_set_count(s->itimer, count - pretimeout);
106 if (start) {
107 ptimer_run(s->itimer, 1);
108 }
109 }
110 }
111 ptimer_transaction_commit(s->itimer);
112 }
113
114 static void imx_wdt2_update_timer(IMX2WdtState *s, bool start)
115 {
116 ptimer_transaction_begin(s->timer);
117 if (start) {
118 ptimer_stop(s->timer);
119 }
120 if ((s->wcr & IMX2_WDT_WCR_WDE) && (s->wcr & IMX2_WDT_WCR_WT)) {
121 int count = (s->wcr & IMX2_WDT_WCR_WT) >> 8;
122
123 /* A value of 0 reflects one period (0.5s). */
124 ptimer_set_count(s->timer, count + 1);
125 if (start) {
126 ptimer_run(s->timer, 1);
127 }
128 }
129 ptimer_transaction_commit(s->timer);
130 if (s->pretimeout_support) {
131 imx_wdt2_update_itimer(s, start);
132 }
133 }
134
135 static void imx2_wdt_write(void *opaque, hwaddr addr,
136 uint64_t value, unsigned int size)
137 {
138 IMX2WdtState *s = IMX2_WDT(opaque);
139
140 switch (addr) {
141 case IMX2_WDT_WCR:
142 if (s->wcr_locked) {
143 value &= ~IMX2_WDT_WCR_LOCK_MASK;
144 value |= (s->wicr & IMX2_WDT_WCR_LOCK_MASK);
145 }
146 s->wcr_locked = true;
147 if (s->wcr_wde_locked) {
148 value &= ~IMX2_WDT_WCR_WDE;
149 value |= (s->wicr & ~IMX2_WDT_WCR_WDE);
150 } else if (value & IMX2_WDT_WCR_WDE) {
151 s->wcr_wde_locked = true;
152 }
153 if (s->wcr_wdt_locked) {
154 value &= ~IMX2_WDT_WCR_WDT;
155 value |= (s->wicr & ~IMX2_WDT_WCR_WDT);
156 } else if (value & IMX2_WDT_WCR_WDT) {
157 s->wcr_wdt_locked = true;
158 }
159
160 s->wcr = value;
161 if (!(value & IMX2_WDT_WCR_SRS)) {
162 s->wrsr = IMX2_WDT_WRSR_SFTW;
163 }
164 if (!(value & (IMX2_WDT_WCR_WDA | IMX2_WDT_WCR_SRS)) ||
165 (!(value & IMX2_WDT_WCR_WT) && (value & IMX2_WDT_WCR_WDE))) {
166 watchdog_perform_action();
167 }
168 s->wcr |= IMX2_WDT_WCR_SRS;
169 imx_wdt2_update_timer(s, true);
170 break;
171 case IMX2_WDT_WSR:
172 if (s->wsr == IMX2_WDT_SEQ1 && value == IMX2_WDT_SEQ2) {
173 imx_wdt2_update_timer(s, false);
174 }
175 s->wsr = value;
176 break;
177 case IMX2_WDT_WRSR:
178 break;
179 case IMX2_WDT_WICR:
180 if (!s->pretimeout_support) {
181 return;
182 }
183 value &= IMX2_WDT_WICR_LOCK_MASK | IMX2_WDT_WICR_WTIS;
184 if (s->wicr_locked) {
185 value &= IMX2_WDT_WICR_WTIS;
186 value |= (s->wicr & IMX2_WDT_WICR_LOCK_MASK);
187 }
188 s->wicr = value | (s->wicr & IMX2_WDT_WICR_WTIS);
189 if (value & IMX2_WDT_WICR_WTIS) {
190 s->wicr &= ~IMX2_WDT_WICR_WTIS;
191 qemu_set_irq(s->irq, 0);
192 }
193 imx_wdt2_update_itimer(s, true);
194 s->wicr_locked = true;
195 break;
196 case IMX2_WDT_WMCR:
197 s->wmcr = value & IMX2_WDT_WMCR_PDE;
198 break;
199 }
200 }
201
202 static const MemoryRegionOps imx2_wdt_ops = {
203 .read = imx2_wdt_read,
204 .write = imx2_wdt_write,
205 .endianness = DEVICE_NATIVE_ENDIAN,
206 .impl = {
207 /*
208 * Our device would not work correctly if the guest was doing
209 * unaligned access. This might not be a limitation on the
210 * real device but in practice there is no reason for a guest
211 * to access this device unaligned.
212 */
213 .min_access_size = 2,
214 .max_access_size = 2,
215 .unaligned = false,
216 },
217 };
218
219 static const VMStateDescription vmstate_imx2_wdt = {
220 .name = "imx2.wdt",
221 .fields = (VMStateField[]) {
222 VMSTATE_PTIMER(timer, IMX2WdtState),
223 VMSTATE_PTIMER(itimer, IMX2WdtState),
224 VMSTATE_BOOL(wicr_locked, IMX2WdtState),
225 VMSTATE_BOOL(wcr_locked, IMX2WdtState),
226 VMSTATE_BOOL(wcr_wde_locked, IMX2WdtState),
227 VMSTATE_BOOL(wcr_wdt_locked, IMX2WdtState),
228 VMSTATE_UINT16(wcr, IMX2WdtState),
229 VMSTATE_UINT16(wsr, IMX2WdtState),
230 VMSTATE_UINT16(wrsr, IMX2WdtState),
231 VMSTATE_UINT16(wmcr, IMX2WdtState),
232 VMSTATE_UINT16(wicr, IMX2WdtState),
233 VMSTATE_END_OF_LIST()
234 }
235 };
236
237 static void imx2_wdt_realize(DeviceState *dev, Error **errp)
238 {
239 IMX2WdtState *s = IMX2_WDT(dev);
240 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
241
242 memory_region_init_io(&s->mmio, OBJECT(dev),
243 &imx2_wdt_ops, s,
244 TYPE_IMX2_WDT,
245 IMX2_WDT_MMIO_SIZE);
246 sysbus_init_mmio(sbd, &s->mmio);
247 sysbus_init_irq(sbd, &s->irq);
248
249 s->timer = ptimer_init(imx2_wdt_expired, s,
250 PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
251 PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
252 PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
253 ptimer_transaction_begin(s->timer);
254 ptimer_set_freq(s->timer, 2);
255 ptimer_set_limit(s->timer, 0xff, 1);
256 ptimer_transaction_commit(s->timer);
257 if (s->pretimeout_support) {
258 s->itimer = ptimer_init(imx2_wdt_interrupt, s,
259 PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
260 PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
261 PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
262 ptimer_transaction_begin(s->itimer);
263 ptimer_set_freq(s->itimer, 2);
264 ptimer_set_limit(s->itimer, 0xff, 1);
265 ptimer_transaction_commit(s->itimer);
266 }
267 }
268
269 static Property imx2_wdt_properties[] = {
270 DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState, pretimeout_support,
271 false),
272 DEFINE_PROP_END_OF_LIST()
273 };
274
275 static void imx2_wdt_class_init(ObjectClass *klass, void *data)
276 {
277 DeviceClass *dc = DEVICE_CLASS(klass);
278
279 device_class_set_props(dc, imx2_wdt_properties);
280 dc->realize = imx2_wdt_realize;
281 dc->reset = imx2_wdt_reset;
282 dc->vmsd = &vmstate_imx2_wdt;
283 dc->desc = "i.MX watchdog timer";
284 set_bit(DEVICE_CATEGORY_MISC, dc->categories);
285 }
286
287 static const TypeInfo imx2_wdt_info = {
288 .name = TYPE_IMX2_WDT,
289 .parent = TYPE_SYS_BUS_DEVICE,
290 .instance_size = sizeof(IMX2WdtState),
291 .class_init = imx2_wdt_class_init,
292 };
293
294 static WatchdogTimerModel model = {
295 .wdt_name = "imx2-watchdog",
296 .wdt_description = "i.MX2 Watchdog",
297 };
298
299 static void imx2_wdt_register_type(void)
300 {
301 watchdog_add_model(&model);
302 type_register_static(&imx2_wdt_info);
303 }
304 type_init(imx2_wdt_register_type)