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,
|
static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
IMXEPITState *s = IMX_EPIT(opaque);
|
IMXEPITState *s = IMX_EPIT(opaque);
|
||||||
uint64_t oldcr;
|
|
||||||
|
|
||||||
DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
|
DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(offset >> 2),
|
||||||
(uint32_t)value);
|
(uint32_t)value);
|
||||||
|
|
||||||
switch (offset >> 2) {
|
switch (offset >> 2) {
|
||||||
case 0: /* CR */
|
case 0: /* CR */
|
||||||
|
imx_epit_write_cr(s, (uint32_t)value);
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: /* SR - ACK*/
|
case 1: /* SR */
|
||||||
/* writing 1 to SR.OCIF clears this bit and turns the interrupt off */
|
imx_epit_write_sr(s, (uint32_t)value);
|
||||||
if (value & SR_OCIF) {
|
|
||||||
s->sr = 0; /* SR.OCIF is the only bit in this register anyway */
|
|
||||||
imx_epit_update_int(s);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: /* LR - set ticks */
|
case 2: /* LR */
|
||||||
s->lr = value;
|
imx_epit_write_lr(s, (uint32_t)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);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: /* CMP */
|
case 3: /* CMP */
|
||||||
s->cmp = value;
|
imx_epit_write_cmp(s, (uint32_t)value);
|
||||||
|
|
||||||
ptimer_transaction_begin(s->timer_cmp);
|
|
||||||
imx_epit_reload_compare_timer(s);
|
|
||||||
ptimer_transaction_commit(s->timer_cmp);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
||||||
HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
|
HWADDR_PRIx "\n", TYPE_IMX_EPIT, __func__, offset);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void imx_epit_cmp(void *opaque)
|
static void imx_epit_cmp(void *opaque)
|
||||||
{
|
{
|
||||||
IMXEPITState *s = IMX_EPIT(opaque);
|
IMXEPITState *s = IMX_EPIT(opaque);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user