1
2
3
4
5
6
7
8
9
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
21static 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
29static void imx2_wdt_expired(void *opaque)
30{
31 IMX2WdtState *s = IMX2_WDT(opaque);
32
33 s->wrsr = IMX2_WDT_WRSR_TOUT;
34
35
36 if (s->wcr & IMX2_WDT_WCR_WDE) {
37 s->wrsr = IMX2_WDT_WRSR_TOUT;
38 watchdog_perform_action();
39 }
40}
41
42static 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
67static 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
86static 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
101
102
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
114static 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
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
135static 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
202static const MemoryRegionOps imx2_wdt_ops = {
203 .read = imx2_wdt_read,
204 .write = imx2_wdt_write,
205 .endianness = DEVICE_NATIVE_ENDIAN,
206 .impl = {
207
208
209
210
211
212
213 .min_access_size = 2,
214 .max_access_size = 2,
215 .unaligned = false,
216 },
217};
218
219static 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
237static 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
269static Property imx2_wdt_properties[] = {
270 DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState, pretimeout_support,
271 false),
272 DEFINE_PROP_END_OF_LIST()
273};
274
275static 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.MX2 watchdog timer";
284 set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories);
285}
286
287static 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
294static void imx2_wdt_register_type(void)
295{
296 type_register_static(&imx2_wdt_info);
297}
298type_init(imx2_wdt_register_type)
299