aboutsummaryrefslogtreecommitdiff
path: root/hw/watchdog/cmsdk-apb-watchdog.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/watchdog/cmsdk-apb-watchdog.c')
-rw-r--r--hw/watchdog/cmsdk-apb-watchdog.c34
1 files changed, 27 insertions, 7 deletions
diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c
index e4d25a25f7..ed5ff4257c 100644
--- a/hw/watchdog/cmsdk-apb-watchdog.c
+++ b/hw/watchdog/cmsdk-apb-watchdog.c
@@ -196,16 +196,13 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset,
switch (offset) {
case A_WDOGLOAD:
- /*
- * Reset the load value and the current count, and make sure
- * we're counting.
- */
+ /* Reset the load value and the current count. */
ptimer_transaction_begin(s->timer);
ptimer_set_limit(s->timer, value, 1);
- ptimer_run(s->timer, 0);
ptimer_transaction_commit(s->timer);
break;
- case A_WDOGCONTROL:
+ case A_WDOGCONTROL: {
+ uint32_t prev_control = s->control;
if (s->is_luminary && 0 != (R_WDOGCONTROL_INTEN_MASK & s->control)) {
/*
* The Luminary version of this device ignores writes to
@@ -215,8 +212,25 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset,
break;
}
s->control = value & R_WDOGCONTROL_VALID_MASK;
+ if (R_WDOGCONTROL_INTEN_MASK & (s->control ^ prev_control)) {
+ ptimer_transaction_begin(s->timer);
+ if (R_WDOGCONTROL_INTEN_MASK & s->control) {
+ /*
+ * Set HIGH to enable the counter and the interrupt. Reloads
+ * the counter from the value in WDOGLOAD when the interrupt
+ * is enabled, after previously being disabled.
+ */
+ ptimer_set_count(s->timer, ptimer_get_limit(s->timer));
+ ptimer_run(s->timer, 0);
+ } else {
+ /* Or LOW to disable the counter and interrupt. */
+ ptimer_stop(s->timer);
+ }
+ ptimer_transaction_commit(s->timer);
+ }
cmsdk_apb_watchdog_update(s);
break;
+ }
case A_WDOGINTCLR:
s->intstatus = 0;
ptimer_transaction_begin(s->timer);
@@ -305,8 +319,14 @@ static void cmsdk_apb_watchdog_reset(DeviceState *dev)
s->resetstatus = 0;
/* Set the limit and the count */
ptimer_transaction_begin(s->timer);
+ /*
+ * We need to stop the ptimer before setting its limit reset value. If the
+ * order is the opposite when the code executes the stop after setting a new
+ * limit it may want to recalculate the count based on the current time (if
+ * the timer was currently running) and it won't get the proper reset value.
+ */
+ ptimer_stop(s->timer);
ptimer_set_limit(s->timer, 0xffffffff, 1);
- ptimer_run(s->timer, 0);
ptimer_transaction_commit(s->timer);
}