2 * Copyright (c) 2018, Impinj, Inc.
4 * i.MX2 Watchdog IP block
6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
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.
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"
19 #include "hw/watchdog/wdt_imx2.h"
22 static void imx2_wdt_interrupt(void *opaque
)
24 IMX2WdtState
*s
= IMX2_WDT(opaque
);
26 trace_imx2_wdt_interrupt();
28 s
->wicr
|= IMX2_WDT_WICR_WTIS
;
29 qemu_set_irq(s
->irq
, 1);
32 static void imx2_wdt_expired(void *opaque
)
34 IMX2WdtState
*s
= IMX2_WDT(opaque
);
36 trace_imx2_wdt_expired();
38 s
->wrsr
= IMX2_WDT_WRSR_TOUT
;
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();
47 static void imx2_wdt_reset(DeviceState
*dev
)
49 IMX2WdtState
*s
= IMX2_WDT(dev
);
51 ptimer_transaction_begin(s
->timer
);
52 ptimer_stop(s
->timer
);
53 ptimer_transaction_commit(s
->timer
);
55 if (s
->pretimeout_support
) {
56 ptimer_transaction_begin(s
->itimer
);
57 ptimer_stop(s
->itimer
);
58 ptimer_transaction_commit(s
->itimer
);
61 s
->wicr_locked
= false;
62 s
->wcr_locked
= false;
63 s
->wcr_wde_locked
= false;
65 s
->wcr
= IMX2_WDT_WCR_WDA
| IMX2_WDT_WCR_SRS
;
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
;
72 static uint64_t imx2_wdt_read(void *opaque
, hwaddr addr
, unsigned int size
)
74 IMX2WdtState
*s
= IMX2_WDT(opaque
);
95 trace_imx2_wdt_read(addr
, value
);
100 static void imx_wdt2_update_itimer(IMX2WdtState
*s
, bool start
)
102 bool running
= (s
->wcr
& IMX2_WDT_WCR_WDE
) && (s
->wcr
& IMX2_WDT_WCR_WT
);
103 bool enabled
= s
->wicr
& IMX2_WDT_WICR_WIE
;
105 ptimer_transaction_begin(s
->itimer
);
106 if (start
|| !enabled
) {
107 ptimer_stop(s
->itimer
);
109 if (running
&& enabled
) {
110 int count
= ptimer_get_count(s
->timer
);
111 int pretimeout
= s
->wicr
& IMX2_WDT_WICR_WICT
;
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
118 if (count
> pretimeout
) {
119 ptimer_set_count(s
->itimer
, count
- pretimeout
);
121 ptimer_run(s
->itimer
, 1);
125 ptimer_transaction_commit(s
->itimer
);
128 static void imx_wdt2_update_timer(IMX2WdtState
*s
, bool start
)
130 ptimer_transaction_begin(s
->timer
);
132 ptimer_stop(s
->timer
);
134 if ((s
->wcr
& IMX2_WDT_WCR_WDE
) && (s
->wcr
& IMX2_WDT_WCR_WT
)) {
135 int count
= (s
->wcr
& IMX2_WDT_WCR_WT
) >> 8;
137 /* A value of 0 reflects one period (0.5s). */
138 ptimer_set_count(s
->timer
, count
+ 1);
140 ptimer_run(s
->timer
, 1);
143 ptimer_transaction_commit(s
->timer
);
144 if (s
->pretimeout_support
) {
145 imx_wdt2_update_itimer(s
, start
);
149 static void imx2_wdt_write(void *opaque
, hwaddr addr
,
150 uint64_t value
, unsigned int size
)
152 IMX2WdtState
*s
= IMX2_WDT(opaque
);
154 trace_imx2_wdt_write(addr
, value
);
159 value
&= ~IMX2_WDT_WCR_LOCK_MASK
;
160 value
|= (s
->wicr
& IMX2_WDT_WCR_LOCK_MASK
);
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;
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;
177 if (!(value
& IMX2_WDT_WCR_SRS
)) {
178 s
->wrsr
= IMX2_WDT_WRSR_SFTW
;
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();
184 s
->wcr
|= IMX2_WDT_WCR_SRS
;
185 imx_wdt2_update_timer(s
, true);
188 if (s
->wsr
== IMX2_WDT_SEQ1
&& value
== IMX2_WDT_SEQ2
) {
189 imx_wdt2_update_timer(s
, false);
196 if (!s
->pretimeout_support
) {
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
);
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);
209 imx_wdt2_update_itimer(s
, true);
210 s
->wicr_locked
= true;
213 s
->wmcr
= value
& IMX2_WDT_WMCR_PDE
;
218 static const MemoryRegionOps imx2_wdt_ops
= {
219 .read
= imx2_wdt_read
,
220 .write
= imx2_wdt_write
,
221 .endianness
= DEVICE_NATIVE_ENDIAN
,
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.
229 .min_access_size
= 2,
230 .max_access_size
= 2,
235 static const VMStateDescription vmstate_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()
253 static void imx2_wdt_realize(DeviceState
*dev
, Error
**errp
)
255 IMX2WdtState
*s
= IMX2_WDT(dev
);
256 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
258 memory_region_init_io(&s
->mmio
, OBJECT(dev
),
262 sysbus_init_mmio(sbd
, &s
->mmio
);
263 sysbus_init_irq(sbd
, &s
->irq
);
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
);
285 static Property imx2_wdt_properties
[] = {
286 DEFINE_PROP_BOOL("pretimeout-support", IMX2WdtState
, pretimeout_support
,
288 DEFINE_PROP_END_OF_LIST()
291 static void imx2_wdt_class_init(ObjectClass
*klass
, void *data
)
293 DeviceClass
*dc
= DEVICE_CLASS(klass
);
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
);
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
,
310 static void imx2_wdt_register_type(void)
312 type_register_static(&imx2_wdt_info
);
314 type_init(imx2_wdt_register_type
)