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:
Dmitry Osipenko 2016-09-22 18:13:06 +01:00 committed by Peter Maydell
parent 780d23e54e
commit e7ea81c37d
24 changed files with 57 additions and 31 deletions

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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 */

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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",

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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);