aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/timer/imx_epit.c207
1 files changed, 113 insertions, 94 deletions
diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c
index 39f47222d0..e04427542f 100644
--- a/hw/timer/imx_epit.c
+++ b/hw/timer/imx_epit.c
@@ -191,129 +191,148 @@ static void imx_epit_reload_compare_timer(IMXEPITState *s)
}
}
-static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
- unsigned size)
+static void imx_epit_write_cr(IMXEPITState *s, uint32_t value)
{
- IMXEPITState *s = IMX_EPIT(opaque);
- uint64_t oldcr;
-
- DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
- (uint32_t)value);
+ uint32_t oldcr = s->cr;
- switch (offset >> 2) {
- case 0: /* CR */
-
- oldcr = s->cr;
- s->cr = value & 0x03ffffff;
- if (s->cr & CR_SWR) {
- /* handle the reset */
- imx_epit_reset(s, false);
- }
+ s->cr = value & 0x03ffffff;
- /*
- * The interrupt state can change due to:
- * - reset clears both SR.OCIF and CR.OCIE
- * - write to CR.EN or CR.OCIE
- */
- imx_epit_update_int(s);
+ if (s->cr & CR_SWR) {
+ /* handle the reset */
+ imx_epit_reset(s, false);
+ }
- /*
- * TODO: could we 'break' here for reset? following operations appear
- * to duplicate the work imx_epit_reset() already did.
- */
+ /*
+ * The interrupt state can change due to:
+ * - reset clears both SR.OCIF and CR.OCIE
+ * - write to CR.EN or CR.OCIE
+ */
+ imx_epit_update_int(s);
- ptimer_transaction_begin(s->timer_cmp);
- ptimer_transaction_begin(s->timer_reload);
+ /*
+ * TODO: could we 'break' here for reset? following operations appear
+ * to duplicate the work imx_epit_reset() already did.
+ */
- /* Update the frequency. Has been done already in case of a reset. */
- if (!(s->cr & CR_SWR)) {
- imx_epit_set_freq(s);
- }
+ ptimer_transaction_begin(s->timer_cmp);
+ ptimer_transaction_begin(s->timer_reload);
- if (s->freq && (s->cr & CR_EN) && !(oldcr & CR_EN)) {
- if (s->cr & CR_ENMOD) {
- if (s->cr & CR_RLD) {
- ptimer_set_limit(s->timer_reload, s->lr, 1);
- ptimer_set_limit(s->timer_cmp, s->lr, 1);
- } else {
- ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1);
- ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1);
- }
- }
+ /* Update the frequency. Has been done already in case of a reset. */
+ if (!(s->cr & CR_SWR)) {
+ imx_epit_set_freq(s);
+ }
- imx_epit_reload_compare_timer(s);
- ptimer_run(s->timer_reload, 0);
- if (s->cr & CR_OCIEN) {
- ptimer_run(s->timer_cmp, 0);
+ if (s->freq && (s->cr & CR_EN) && !(oldcr & CR_EN)) {
+ if (s->cr & CR_ENMOD) {
+ if (s->cr & CR_RLD) {
+ ptimer_set_limit(s->timer_reload, s->lr, 1);
+ ptimer_set_limit(s->timer_cmp, s->lr, 1);
} else {
- ptimer_stop(s->timer_cmp);
- }
- } else if (!(s->cr & CR_EN)) {
- /* stop both timers */
- ptimer_stop(s->timer_reload);
- ptimer_stop(s->timer_cmp);
- } else if (s->cr & CR_OCIEN) {
- if (!(oldcr & CR_OCIEN)) {
- imx_epit_reload_compare_timer(s);
- ptimer_run(s->timer_cmp, 0);
+ ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1);
+ ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1);
}
+ }
+
+ imx_epit_reload_compare_timer(s);
+ ptimer_run(s->timer_reload, 0);
+ if (s->cr & CR_OCIEN) {
+ ptimer_run(s->timer_cmp, 0);
} else {
ptimer_stop(s->timer_cmp);
}
+ } else if (!(s->cr & CR_EN)) {
+ /* stop both timers */
+ ptimer_stop(s->timer_reload);
+ ptimer_stop(s->timer_cmp);
+ } else if (s->cr & CR_OCIEN) {
+ if (!(oldcr & CR_OCIEN)) {
+ imx_epit_reload_compare_timer(s);
+ ptimer_run(s->timer_cmp, 0);
+ }
+ } else {
+ ptimer_stop(s->timer_cmp);
+ }
- ptimer_transaction_commit(s->timer_cmp);
- ptimer_transaction_commit(s->timer_reload);
+ ptimer_transaction_commit(s->timer_cmp);
+ ptimer_transaction_commit(s->timer_reload);
+}
+
+static void imx_epit_write_sr(IMXEPITState *s, uint32_t value)
+{
+ /* writing 1 to SR.OCIF clears this bit and turns the interrupt off */
+ if (value & SR_OCIF) {
+ s->sr = 0; /* SR.OCIF is the only bit in this register anyway */
+ imx_epit_update_int(s);
+ }
+}
+
+static void imx_epit_write_lr(IMXEPITState *s, uint32_t value)
+{
+ s->lr = value;
+
+ ptimer_transaction_begin(s->timer_cmp);
+ ptimer_transaction_begin(s->timer_reload);
+ if (s->cr & CR_RLD) {
+ /* Also set the limit if the LRD bit is set */
+ /* If IOVW bit is set then set the timer value */
+ ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW);
+ ptimer_set_limit(s->timer_cmp, s->lr, 0);
+ } else if (s->cr & CR_IOVW) {
+ /* If IOVW bit is set then set the timer value */
+ ptimer_set_count(s->timer_reload, s->lr);
+ }
+ /*
+ * Commit the change to s->timer_reload, so it can propagate. Otherwise
+ * the timer interrupt may not fire properly. The commit must happen
+ * before calling imx_epit_reload_compare_timer(), which reads
+ * s->timer_reload internally again.
+ */
+ ptimer_transaction_commit(s->timer_reload);
+ imx_epit_reload_compare_timer(s);
+ ptimer_transaction_commit(s->timer_cmp);
+}
+
+static void imx_epit_write_cmp(IMXEPITState *s, uint32_t value)
+{
+ s->cmp = value;
+
+ ptimer_transaction_begin(s->timer_cmp);
+ imx_epit_reload_compare_timer(s);
+ ptimer_transaction_commit(s->timer_cmp);
+}
+
+static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ IMXEPITState *s = IMX_EPIT(opaque);
+
+ DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
+ (uint32_t)value);
+
+ switch (offset >> 2) {
+ case 0: /* CR */
+ imx_epit_write_cr(s, (uint32_t)value);
break;
- case 1: /* SR - ACK*/
- /* writing 1 to SR.OCIF clears this bit and turns the interrupt off */
- if (value & SR_OCIF) {
- s->sr = 0; /* SR.OCIF is the only bit in this register anyway */
- imx_epit_update_int(s);
- }
+ case 1: /* SR */
+ imx_epit_write_sr(s, (uint32_t)value);
break;
- case 2: /* LR - set ticks */
- s->lr = value;
-
- ptimer_transaction_begin(s->timer_cmp);
- ptimer_transaction_begin(s->timer_reload);
- if (s->cr & CR_RLD) {
- /* Also set the limit if the LRD bit is set */
- /* If IOVW bit is set then set the timer value */
- ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW);
- ptimer_set_limit(s->timer_cmp, s->lr, 0);
- } else if (s->cr & CR_IOVW) {
- /* If IOVW bit is set then set the timer value */
- ptimer_set_count(s->timer_reload, s->lr);
- }
- /*
- * Commit the change to s->timer_reload, so it can propagate. Otherwise
- * the timer interrupt may not fire properly. The commit must happen
- * before calling imx_epit_reload_compare_timer(), which reads
- * s->timer_reload internally again.
- */
- ptimer_transaction_commit(s->timer_reload);
- imx_epit_reload_compare_timer(s);
- ptimer_transaction_commit(s->timer_cmp);
+ case 2: /* LR */
+ imx_epit_write_lr(s, (uint32_t)value);
break;
case 3: /* CMP */
- s->cmp = value;
-
- ptimer_transaction_begin(s->timer_cmp);
- imx_epit_reload_compare_timer(s);
- ptimer_transaction_commit(s->timer_cmp);
-
+ imx_epit_write_cmp(s, (uint32_t)value);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
-
break;
}
}
+
static void imx_epit_cmp(void *opaque)
{
IMXEPITState *s = IMX_EPIT(opaque);