allwinner-a10-pit: implement prescaler and source selection
This implements the prescaler and source fields of the timer control register. The source for each timer can be selected among 4 clock inputs whose frequencies are set through model properties. Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com> Message-id: 1395771730-16882-6-git-send-email-b.galvani@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
a63f9f85e3
commit
286226a479
|
@ -43,6 +43,19 @@ static void cubieboard_init(QEMUMachineInitArgs *args)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_property_set_int(OBJECT(&s->a10->timer), 32768, "clk0-freq", &err);
|
||||||
|
if (err != NULL) {
|
||||||
|
error_report("Couldn't set clk0 frequency: %s", error_get_pretty(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_property_set_int(OBJECT(&s->a10->timer), 24000000, "clk1-freq",
|
||||||
|
&err);
|
||||||
|
if (err != NULL) {
|
||||||
|
error_report("Couldn't set clk1 frequency: %s", error_get_pretty(err));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
|
object_property_set_bool(OBJECT(s->a10), true, "realized", &err);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
error_report("Couldn't realize Allwinner A10: %s",
|
error_report("Couldn't realize Allwinner A10: %s",
|
||||||
|
|
|
@ -74,6 +74,22 @@ static uint64_t a10_pit_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void a10_pit_set_freq(AwA10PITState *s, int index)
|
||||||
|
{
|
||||||
|
uint32_t prescaler, source, source_freq;
|
||||||
|
|
||||||
|
prescaler = 1 << extract32(s->control[index], 4, 3);
|
||||||
|
source = extract32(s->control[index], 2, 2);
|
||||||
|
source_freq = s->clk_freq[source];
|
||||||
|
|
||||||
|
if (source_freq) {
|
||||||
|
ptimer_set_freq(s->timer[index], source_freq / prescaler);
|
||||||
|
} else {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid clock source %u\n",
|
||||||
|
__func__, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
|
static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
unsigned size)
|
unsigned size)
|
||||||
{
|
{
|
||||||
|
@ -96,6 +112,7 @@ static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
switch (offset & 0x0f) {
|
switch (offset & 0x0f) {
|
||||||
case AW_A10_PIT_TIMER_CONTROL:
|
case AW_A10_PIT_TIMER_CONTROL:
|
||||||
s->control[index] = value;
|
s->control[index] = value;
|
||||||
|
a10_pit_set_freq(s, index);
|
||||||
if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
|
if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) {
|
||||||
ptimer_set_count(s->timer[index], s->interval[index]);
|
ptimer_set_count(s->timer[index], s->interval[index]);
|
||||||
}
|
}
|
||||||
|
@ -161,6 +178,14 @@ static const MemoryRegionOps a10_pit_ops = {
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Property a10_pit_properties[] = {
|
||||||
|
DEFINE_PROP_UINT32("clk0-freq", AwA10PITState, clk_freq[0], 0),
|
||||||
|
DEFINE_PROP_UINT32("clk1-freq", AwA10PITState, clk_freq[1], 0),
|
||||||
|
DEFINE_PROP_UINT32("clk2-freq", AwA10PITState, clk_freq[2], 0),
|
||||||
|
DEFINE_PROP_UINT32("clk3-freq", AwA10PITState, clk_freq[3], 0),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
static const VMStateDescription vmstate_a10_pit = {
|
static const VMStateDescription vmstate_a10_pit = {
|
||||||
.name = "a10.pit",
|
.name = "a10.pit",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
|
@ -196,6 +221,7 @@ static void a10_pit_reset(DeviceState *dev)
|
||||||
s->interval[i] = 0;
|
s->interval[i] = 0;
|
||||||
s->count[i] = 0;
|
s->count[i] = 0;
|
||||||
ptimer_stop(s->timer[i]);
|
ptimer_stop(s->timer[i]);
|
||||||
|
a10_pit_set_freq(s, i);
|
||||||
}
|
}
|
||||||
s->watch_dog_mode = 0;
|
s->watch_dog_mode = 0;
|
||||||
s->watch_dog_control = 0;
|
s->watch_dog_control = 0;
|
||||||
|
@ -241,7 +267,6 @@ static void a10_pit_init(Object *obj)
|
||||||
tc->index = i;
|
tc->index = i;
|
||||||
bh[i] = qemu_bh_new(a10_pit_timer_cb, tc);
|
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_set_freq(s->timer[i], 240000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +275,7 @@ static void a10_pit_class_init(ObjectClass *klass, void *data)
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
dc->reset = a10_pit_reset;
|
dc->reset = a10_pit_reset;
|
||||||
|
dc->props = a10_pit_properties;
|
||||||
dc->desc = "allwinner a10 timer";
|
dc->desc = "allwinner a10 timer";
|
||||||
dc->vmsd = &vmstate_a10_pit;
|
dc->vmsd = &vmstate_a10_pit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ struct AwA10PITState {
|
||||||
ptimer_state * timer[AW_A10_PIT_TIMER_NR];
|
ptimer_state * timer[AW_A10_PIT_TIMER_NR];
|
||||||
AwA10TimerContext timer_context[AW_A10_PIT_TIMER_NR];
|
AwA10TimerContext timer_context[AW_A10_PIT_TIMER_NR];
|
||||||
MemoryRegion iomem;
|
MemoryRegion iomem;
|
||||||
|
uint32_t clk_freq[4];
|
||||||
|
|
||||||
uint32_t irq_enable;
|
uint32_t irq_enable;
|
||||||
uint32_t irq_status;
|
uint32_t irq_status;
|
||||||
|
|
Loading…
Reference in a new issue