hw/timer/imx_epit: factor out register write handlers
Signed-off-by: Axel Heider <axel.heider@hensoldt.net> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
3d46158125
commit
793a6ea075
@ -191,129 +191,148 @@ static void imx_epit_reload_compare_timer(IMXEPITState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_epit_write_cr(IMXEPITState *s, uint32_t value)
|
||||
{
|
||||
uint32_t oldcr = s->cr;
|
||||
|
||||
s->cr = value & 0x03ffffff;
|
||||
|
||||
if (s->cr & CR_SWR) {
|
||||
/* handle the reset */
|
||||
imx_epit_reset(s, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* TODO: could we 'break' here for reset? following operations appear
|
||||
* to duplicate the work imx_epit_reset() already did.
|
||||
*/
|
||||
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
ptimer_transaction_begin(s->timer_reload);
|
||||
|
||||
/* Update the frequency. Has been done already in case of a reset. */
|
||||
if (!(s->cr & CR_SWR)) {
|
||||
imx_epit_set_freq(s);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
uint64_t oldcr;
|
||||
|
||||
DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
|
||||
(uint32_t)value);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* TODO: could we 'break' here for reset? following operations appear
|
||||
* to duplicate the work imx_epit_reset() already did.
|
||||
*/
|
||||
|
||||
ptimer_transaction_begin(s->timer_cmp);
|
||||
ptimer_transaction_begin(s->timer_reload);
|
||||
|
||||
/* Update the frequency. Has been done already in case of a reset. */
|
||||
if (!(s->cr & CR_SWR)) {
|
||||
imx_epit_set_freq(s);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user