hw/ptimer: Introduce timer policy feature
Some of the timer devices may behave differently from what ptimer provides. Introduce ptimer policy feature that allows ptimer users to change default and wrong timer behaviour, for example to continuously trigger periodic timer when load value is equal to "0". Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Message-id: 994cd608ec392da6e58f0643800dda595edb9d97.1473252818.git.digetx@gmail.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
780d23e54e
commit
e7ea81c37d
@ -837,7 +837,7 @@ static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s,
|
||||
s->freq = freq;
|
||||
|
||||
bh = qemu_bh_new(mv88w8618_timer_tick, s);
|
||||
s->ptimer = ptimer_init(bh);
|
||||
s->ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
}
|
||||
|
||||
static uint64_t mv88w8618_pit_read(void *opaque, hwaddr offset,
|
||||
|
@ -21,6 +21,7 @@ struct ptimer_state
|
||||
int64_t period;
|
||||
int64_t last_event;
|
||||
int64_t next_event;
|
||||
uint8_t policy_mask;
|
||||
QEMUBH *bh;
|
||||
QEMUTimer *timer;
|
||||
};
|
||||
@ -243,12 +244,13 @@ const VMStateDescription vmstate_ptimer = {
|
||||
}
|
||||
};
|
||||
|
||||
ptimer_state *ptimer_init(QEMUBH *bh)
|
||||
ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask)
|
||||
{
|
||||
ptimer_state *s;
|
||||
|
||||
s = (ptimer_state *)g_malloc0(sizeof(ptimer_state));
|
||||
s->bh = bh;
|
||||
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s);
|
||||
s->policy_mask = policy_mask;
|
||||
return s;
|
||||
}
|
||||
|
@ -548,7 +548,7 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
st->nr = i;
|
||||
st->bh = qemu_bh_new(timer_hit, st);
|
||||
st->ptimer = ptimer_init(st->bh);
|
||||
st->ptimer = ptimer_init(st->bh, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_set_freq(st->ptimer, s->freqhz);
|
||||
}
|
||||
return;
|
||||
|
@ -139,7 +139,7 @@ static m5206_timer_state *m5206_timer_init(qemu_irq irq)
|
||||
|
||||
s = (m5206_timer_state *)g_malloc0(sizeof(m5206_timer_state));
|
||||
bh = qemu_bh_new(m5206_timer_trigger, s);
|
||||
s->timer = ptimer_init(bh);
|
||||
s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
s->irq = irq;
|
||||
m5206_timer_reset(s);
|
||||
return s;
|
||||
|
@ -183,7 +183,7 @@ static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic)
|
||||
for (i = 0; i < 2; i++) {
|
||||
s = (m5208_timer_state *)g_malloc0(sizeof(m5208_timer_state));
|
||||
bh = qemu_bh_new(m5208_timer_trigger, s);
|
||||
s->timer = ptimer_init(bh);
|
||||
s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s,
|
||||
"m5208-timer", 0x00004000);
|
||||
memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i,
|
||||
|
@ -387,7 +387,7 @@ static void etsec_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
|
||||
etsec->bh = qemu_bh_new(etsec_timer_hit, etsec);
|
||||
etsec->ptimer = ptimer_init(etsec->bh);
|
||||
etsec->ptimer = ptimer_init(etsec->bh, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_set_freq(etsec->ptimer, 100);
|
||||
}
|
||||
|
||||
|
@ -1345,7 +1345,7 @@ static int lan9118_init1(SysBusDevice *sbd)
|
||||
s->txp = &s->tx_packet;
|
||||
|
||||
bh = qemu_bh_new(lan9118_tick, s);
|
||||
s->timer = ptimer_init(bh);
|
||||
s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_set_freq(s->timer, 10000);
|
||||
ptimer_set_limit(s->timer, 0xffff, 1);
|
||||
|
||||
|
@ -267,7 +267,7 @@ static void a10_pit_init(Object *obj)
|
||||
tc->container = s;
|
||||
tc->index = i;
|
||||
bh[i] = qemu_bh_new(a10_pit_timer_cb, tc);
|
||||
s->timer[i] = ptimer_init(bh[i]);
|
||||
s->timer[i] = ptimer_init(bh[i], PTIMER_POLICY_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ static arm_timer_state *arm_timer_init(uint32_t freq)
|
||||
s->control = TIMER_CTRL_IE;
|
||||
|
||||
bh = qemu_bh_new(arm_timer_tick, s);
|
||||
s->timer = ptimer_init(bh);
|
||||
s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
vmstate_register(NULL, -1, &vmstate_arm_timer, s);
|
||||
return s;
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ static void digic_timer_init(Object *obj)
|
||||
{
|
||||
DigicTimerState *s = DIGIC_TIMER(obj);
|
||||
|
||||
s->ptimer = ptimer_init(NULL);
|
||||
s->ptimer = ptimer_init(NULL, PTIMER_POLICY_DEFAULT);
|
||||
|
||||
/*
|
||||
* FIXME: there is no documentation on Digic timer
|
||||
|
@ -322,9 +322,9 @@ static int etraxfs_timer_init(SysBusDevice *dev)
|
||||
t->bh_t0 = qemu_bh_new(timer0_hit, t);
|
||||
t->bh_t1 = qemu_bh_new(timer1_hit, t);
|
||||
t->bh_wd = qemu_bh_new(watchdog_hit, t);
|
||||
t->ptimer_t0 = ptimer_init(t->bh_t0);
|
||||
t->ptimer_t1 = ptimer_init(t->bh_t1);
|
||||
t->ptimer_wd = ptimer_init(t->bh_wd);
|
||||
t->ptimer_t0 = ptimer_init(t->bh_t0, PTIMER_POLICY_DEFAULT);
|
||||
t->ptimer_t1 = ptimer_init(t->bh_t1, PTIMER_POLICY_DEFAULT);
|
||||
t->ptimer_wd = ptimer_init(t->bh_wd, PTIMER_POLICY_DEFAULT);
|
||||
|
||||
sysbus_init_irq(dev, &t->irq);
|
||||
sysbus_init_irq(dev, &t->nmi);
|
||||
|
@ -1431,15 +1431,16 @@ static void exynos4210_mct_init(Object *obj)
|
||||
|
||||
/* Global timer */
|
||||
bh[0] = qemu_bh_new(exynos4210_gfrc_event, s);
|
||||
s->g_timer.ptimer_frc = ptimer_init(bh[0]);
|
||||
s->g_timer.ptimer_frc = ptimer_init(bh[0], PTIMER_POLICY_DEFAULT);
|
||||
memset(&s->g_timer.reg, 0, sizeof(struct gregs));
|
||||
|
||||
/* Local timers */
|
||||
for (i = 0; i < 2; i++) {
|
||||
bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]);
|
||||
bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]);
|
||||
s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]);
|
||||
s->l_timer[i].ptimer_frc = ptimer_init(bh[1]);
|
||||
s->l_timer[i].tick_timer.ptimer_tick =
|
||||
ptimer_init(bh[0], PTIMER_POLICY_DEFAULT);
|
||||
s->l_timer[i].ptimer_frc = ptimer_init(bh[1], PTIMER_POLICY_DEFAULT);
|
||||
s->l_timer[i].id = i;
|
||||
}
|
||||
|
||||
|
@ -390,7 +390,7 @@ static void exynos4210_pwm_init(Object *obj)
|
||||
for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
|
||||
bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]);
|
||||
sysbus_init_irq(dev, &s->timer[i].irq);
|
||||
s->timer[i].ptimer = ptimer_init(bh);
|
||||
s->timer[i].ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
s->timer[i].id = i;
|
||||
s->timer[i].parent = s;
|
||||
}
|
||||
|
@ -555,12 +555,12 @@ static void exynos4210_rtc_init(Object *obj)
|
||||
QEMUBH *bh;
|
||||
|
||||
bh = qemu_bh_new(exynos4210_rtc_tick, s);
|
||||
s->ptimer = ptimer_init(bh);
|
||||
s->ptimer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
|
||||
exynos4210_rtc_update_freq(s, 0);
|
||||
|
||||
bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
|
||||
s->ptimer_1Hz = ptimer_init(bh);
|
||||
s->ptimer_1Hz = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
|
||||
|
||||
sysbus_init_irq(dev, &s->alm_irq);
|
||||
|
@ -363,7 +363,7 @@ static int grlib_gptimer_init(SysBusDevice *dev)
|
||||
|
||||
timer->unit = unit;
|
||||
timer->bh = qemu_bh_new(grlib_gptimer_hit, timer);
|
||||
timer->ptimer = ptimer_init(timer->bh);
|
||||
timer->ptimer = ptimer_init(timer->bh, PTIMER_POLICY_DEFAULT);
|
||||
timer->id = i;
|
||||
|
||||
/* One IRQ line for each timer */
|
||||
|
@ -314,10 +314,10 @@ static void imx_epit_realize(DeviceState *dev, Error **errp)
|
||||
0x00001000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
s->timer_reload = ptimer_init(NULL);
|
||||
s->timer_reload = ptimer_init(NULL, PTIMER_POLICY_DEFAULT);
|
||||
|
||||
bh = qemu_bh_new(imx_epit_cmp, s);
|
||||
s->timer_cmp = ptimer_init(bh);
|
||||
s->timer_cmp = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
}
|
||||
|
||||
static void imx_epit_class_init(ObjectClass *klass, void *data)
|
||||
|
@ -461,7 +461,7 @@ static void imx_gpt_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
bh = qemu_bh_new(imx_gpt_timeout, s);
|
||||
s->timer = ptimer_init(bh);
|
||||
s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
}
|
||||
|
||||
static void imx_gpt_class_init(ObjectClass *klass, void *data)
|
||||
|
@ -184,7 +184,7 @@ static void lm32_timer_init(Object *obj)
|
||||
sysbus_init_irq(dev, &s->irq);
|
||||
|
||||
s->bh = qemu_bh_new(timer_hit, s);
|
||||
s->ptimer = ptimer_init(s->bh);
|
||||
s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &timer_ops, s,
|
||||
"timer", R_MAX * 4);
|
||||
|
@ -281,8 +281,8 @@ static void milkymist_sysctl_init(Object *obj)
|
||||
|
||||
s->bh0 = qemu_bh_new(timer0_hit, s);
|
||||
s->bh1 = qemu_bh_new(timer1_hit, s);
|
||||
s->ptimer0 = ptimer_init(s->bh0);
|
||||
s->ptimer1 = ptimer_init(s->bh1);
|
||||
s->ptimer0 = ptimer_init(s->bh0, PTIMER_POLICY_DEFAULT);
|
||||
s->ptimer1 = ptimer_init(s->bh1, PTIMER_POLICY_DEFAULT);
|
||||
|
||||
memory_region_init_io(&s->regs_region, obj, &sysctl_mmio_ops, s,
|
||||
"milkymist-sysctl", R_MAX * 4);
|
||||
|
@ -125,7 +125,7 @@ static int puv3_ost_init(SysBusDevice *dev)
|
||||
sysbus_init_irq(dev, &s->irq);
|
||||
|
||||
s->bh = qemu_bh_new(puv3_ost_tick, s);
|
||||
s->ptimer = ptimer_init(s->bh);
|
||||
s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_set_freq(s->ptimer, 50 * 1000 * 1000);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &puv3_ost_ops, s, "puv3_ost",
|
||||
|
@ -203,7 +203,7 @@ static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
|
||||
s->irq = irq;
|
||||
|
||||
bh = qemu_bh_new(sh_timer_tick, s);
|
||||
s->timer = ptimer_init(bh);
|
||||
s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
|
||||
sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
|
||||
sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
|
||||
|
@ -389,7 +389,7 @@ static int slavio_timer_init1(SysBusDevice *dev)
|
||||
tc->timer_index = i;
|
||||
|
||||
bh = qemu_bh_new(slavio_timer_irq, tc);
|
||||
s->cputimer[i].timer = ptimer_init(bh);
|
||||
s->cputimer[i].timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD);
|
||||
|
||||
size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE;
|
||||
|
@ -218,7 +218,7 @@ static void xilinx_timer_realize(DeviceState *dev, Error **errp)
|
||||
xt->parent = t;
|
||||
xt->nr = i;
|
||||
xt->bh = qemu_bh_new(timer_hit, xt);
|
||||
xt->ptimer = ptimer_init(xt->bh);
|
||||
xt->ptimer = ptimer_init(xt->bh, PTIMER_POLICY_DEFAULT);
|
||||
ptimer_set_freq(xt->ptimer, t->freq_hz);
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,34 @@
|
||||
#include "qemu/timer.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
/* The default ptimer policy retains backward compatibility with the legacy
|
||||
* timers. Custom policies are adjusting the default one. Consider providing
|
||||
* a correct policy for your timer.
|
||||
*
|
||||
* The rough edges of the default policy:
|
||||
* - Starting to run with a period = 0 emits error message and stops the
|
||||
* timer without a trigger.
|
||||
*
|
||||
* - Setting period to 0 of the running timer emits error message and
|
||||
* stops the timer without a trigger.
|
||||
*
|
||||
* - Starting to run with counter = 0 or setting it to "0" while timer
|
||||
* is running causes a trigger and reloads counter with a limit value.
|
||||
* If limit = 0, ptimer emits error message and stops the timer.
|
||||
*
|
||||
* - Counter value of the running timer is one less than the actual value.
|
||||
*
|
||||
* - Changing period/frequency of the running timer loses time elapsed
|
||||
* since the last period, effectively restarting the timer with a
|
||||
* counter = counter value at the moment of change (.i.e. one less).
|
||||
*/
|
||||
#define PTIMER_POLICY_DEFAULT 0
|
||||
|
||||
/* ptimer.c */
|
||||
typedef struct ptimer_state ptimer_state;
|
||||
typedef void (*ptimer_cb)(void *opaque);
|
||||
|
||||
ptimer_state *ptimer_init(QEMUBH *bh);
|
||||
ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask);
|
||||
void ptimer_set_period(ptimer_state *s, int64_t period);
|
||||
void ptimer_set_freq(ptimer_state *s, uint32_t freq);
|
||||
uint64_t ptimer_get_limit(ptimer_state *s);
|
||||
|
Loading…
Reference in New Issue
Block a user