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