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