@@ -202,10 +202,10 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset,
*/
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 +215,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 +322,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);
}