aboutsummaryrefslogtreecommitdiff
path: root/hw/timer/aspeed_timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/timer/aspeed_timer.c')
-rw-r--r--hw/timer/aspeed_timer.c293
1 files changed, 264 insertions, 29 deletions
diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
index fc5c94b..57db035 100644
--- a/hw/timer/aspeed_timer.c
+++ b/hw/timer/aspeed_timer.c
@@ -239,9 +239,8 @@ static uint64_t aspeed_timer_get_value(AspeedTimer *t, int reg)
return value;
}
-static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size)
+static uint64_t aspeed_timer_read_common(AspeedTimerCtrlState *s, hwaddr offset)
{
- AspeedTimerCtrlState *s = opaque;
const int reg = (offset & 0xf) / 4;
uint64_t value;
@@ -256,10 +255,11 @@ static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size)
value = aspeed_timer_get_value(&s->timers[(offset >> 4) - 1], reg);
break;
default:
- value = ASPEED_TIMER_GET_CLASS(s)->read(s, offset);
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ value = 0;
break;
}
- trace_aspeed_timer_read(offset, size, value);
return value;
}
@@ -276,7 +276,8 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
old_reload = t->reload;
t->reload = calculate_min_ticks(t, value);
- /* If the reload value was not previously set, or zero, and
+ /*
+ * If the reload value was not previously set, or zero, and
* the current value is valid, try to start the timer if it is
* enabled.
*/
@@ -312,7 +313,8 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg,
}
}
-/* Control register operations are broken out into helpers that can be
+/*
+ * Control register operations are broken out into helpers that can be
* explicitly called on aspeed_timer_reset(), but also from
* aspeed_timer_ctrl_op().
*/
@@ -396,7 +398,8 @@ static void aspeed_timer_set_ctrl(AspeedTimerCtrlState *s, uint32_t reg)
AspeedTimer *t;
const uint8_t enable_mask = BIT(op_enable);
- /* Handle a dependency between the 'enable' and remaining three
+ /*
+ * Handle a dependency between the 'enable' and remaining three
* configuration bits - i.e. if more than one bit in the control set has
* changed, including the 'enable' bit, then we want either disable the
* timer and perform configuration, or perform configuration and then
@@ -428,12 +431,11 @@ static void aspeed_timer_set_ctrl2(AspeedTimerCtrlState *s, uint32_t value)
trace_aspeed_timer_set_ctrl2(value);
}
-static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value,
- unsigned size)
+static void aspeed_timer_write_common(AspeedTimerCtrlState *s, hwaddr offset,
+ uint64_t value)
{
const uint32_t tv = (uint32_t)(value & 0xFFFFFFFF);
const int reg = (offset & 0xf) / 4;
- AspeedTimerCtrlState *s = opaque;
switch (offset) {
/* Control Registers */
@@ -448,11 +450,25 @@ static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value,
aspeed_timer_set_value(s, (offset >> TIMER_NR_REGS) - 1, reg, tv);
break;
default:
- ASPEED_TIMER_GET_CLASS(s)->write(s, offset, value);
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
break;
}
}
+static uint64_t aspeed_timer_read(void *opaque, hwaddr offset, unsigned size)
+{
+ AspeedTimerCtrlState *s = ASPEED_TIMER(opaque);
+ return ASPEED_TIMER_GET_CLASS(s)->read(s, offset);
+}
+
+static void aspeed_timer_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ AspeedTimerCtrlState *s = ASPEED_TIMER(opaque);
+ ASPEED_TIMER_GET_CLASS(s)->write(s, offset, value);
+}
+
static const MemoryRegionOps aspeed_timer_ops = {
.read = aspeed_timer_read,
.write = aspeed_timer_write,
@@ -472,12 +488,15 @@ static uint64_t aspeed_2400_timer_read(AspeedTimerCtrlState *s, hwaddr offset)
break;
case 0x38:
case 0x3C:
- default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
value = 0;
break;
+ default:
+ value = aspeed_timer_read_common(s, offset);
+ break;
}
+ trace_aspeed_timer_read(offset, value);
return value;
}
@@ -492,10 +511,12 @@ static void aspeed_2400_timer_write(AspeedTimerCtrlState *s, hwaddr offset,
break;
case 0x38:
case 0x3C:
- default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
break;
+ default:
+ aspeed_timer_write_common(s, offset, value);
+ break;
}
}
@@ -511,12 +532,15 @@ static uint64_t aspeed_2500_timer_read(AspeedTimerCtrlState *s, hwaddr offset)
value = s->ctrl3 & BIT(0);
break;
case 0x3C:
- default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
value = 0;
break;
+ default:
+ value = aspeed_timer_read_common(s, offset);
+ break;
}
+ trace_aspeed_timer_read(offset, value);
return value;
}
@@ -545,8 +569,7 @@ static void aspeed_2500_timer_write(AspeedTimerCtrlState *s, hwaddr offset,
break;
default:
- qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
- __func__, offset);
+ aspeed_timer_write_common(s, offset, value);
break;
}
}
@@ -561,12 +584,15 @@ static uint64_t aspeed_2600_timer_read(AspeedTimerCtrlState *s, hwaddr offset)
break;
case 0x38:
case 0x3C:
- default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
value = 0;
break;
+ default:
+ value = aspeed_timer_read_common(s, offset);
+ break;
}
+ trace_aspeed_timer_read(offset, value);
return value;
}
@@ -577,17 +603,209 @@ static void aspeed_2600_timer_write(AspeedTimerCtrlState *s, hwaddr offset,
switch (offset) {
case 0x34:
- s->irq_sts &= tv;
+ s->irq_sts &= ~tv;
break;
case 0x3C:
aspeed_timer_set_ctrl(s, s->ctrl & ~tv);
break;
-
case 0x38:
- default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
break;
+ default:
+ aspeed_timer_write_common(s, offset, value);
+ break;
+ }
+}
+
+static void aspeed_2700_timer_set_ctrl(AspeedTimerCtrlState *s, int index,
+ uint32_t reg)
+{
+ const uint8_t overflow_interrupt_mask = BIT(op_overflow_interrupt);
+ const uint8_t external_clock_mask = BIT(op_external_clock);
+ const uint8_t pulse_enable_mask = BIT(op_pulse_enable);
+ const uint8_t enable_mask = BIT(op_enable);
+ AspeedTimer *t;
+ uint8_t t_old;
+ uint8_t t_new;
+ int shift;
+
+ /*
+ * Only 1 will set the specific bits to 1
+ * Handle a dependency between the 'enable' and remaining three
+ * configuration bits - i.e. if more than one bit in the control set has
+ * set, including the 'enable' bit, perform configuration and then
+ * enable the timer.
+ * Interrupt Status bit should not be set.
+ */
+
+ t = &s->timers[index];
+ shift = index * TIMER_CTRL_BITS;
+
+ t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK;
+ t_new = reg & TIMER_CTRL_MASK;
+
+ if (!(t_old & external_clock_mask) &&
+ (t_new & external_clock_mask)) {
+ aspeed_timer_ctrl_external_clock(t, true);
+ s->ctrl = deposit32(s->ctrl, shift + op_external_clock, 1, 1);
+ }
+
+ if (!(t_old & overflow_interrupt_mask) &&
+ (t_new & overflow_interrupt_mask)) {
+ aspeed_timer_ctrl_overflow_interrupt(t, true);
+ s->ctrl = deposit32(s->ctrl, shift + op_overflow_interrupt, 1, 1);
+ }
+
+
+ if (!(t_old & pulse_enable_mask) &&
+ (t_new & pulse_enable_mask)) {
+ aspeed_timer_ctrl_pulse_enable(t, true);
+ s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 1);
+ }
+
+ /* If we are enabling, do so last */
+ if (!(t_old & enable_mask) &&
+ (t_new & enable_mask)) {
+ aspeed_timer_ctrl_enable(t, true);
+ s->ctrl = deposit32(s->ctrl, shift + op_enable, 1, 1);
+ }
+}
+
+static void aspeed_2700_timer_clear_ctrl(AspeedTimerCtrlState *s, int index,
+ uint32_t reg)
+{
+ const uint8_t overflow_interrupt_mask = BIT(op_overflow_interrupt);
+ const uint8_t external_clock_mask = BIT(op_external_clock);
+ const uint8_t pulse_enable_mask = BIT(op_pulse_enable);
+ const uint8_t enable_mask = BIT(op_enable);
+ AspeedTimer *t;
+ uint8_t t_old;
+ uint8_t t_new;
+ int shift;
+
+ /*
+ * Only 1 will clear the specific bits to 0
+ * Handle a dependency between the 'enable' and remaining three
+ * configuration bits - i.e. if more than one bit in the control set has
+ * clear, including the 'enable' bit, then disable the timer and perform
+ * configuration
+ */
+
+ t = &s->timers[index];
+ shift = index * TIMER_CTRL_BITS;
+
+ t_old = (s->ctrl >> shift) & TIMER_CTRL_MASK;
+ t_new = reg & TIMER_CTRL_MASK;
+
+ /* If we are disabling, do so first */
+ if ((t_old & enable_mask) &&
+ (t_new & enable_mask)) {
+ aspeed_timer_ctrl_enable(t, false);
+ s->ctrl = deposit32(s->ctrl, shift + op_enable, 1, 0);
+ }
+
+ if ((t_old & external_clock_mask) &&
+ (t_new & external_clock_mask)) {
+ aspeed_timer_ctrl_external_clock(t, false);
+ s->ctrl = deposit32(s->ctrl, shift + op_external_clock, 1, 0);
+ }
+
+ if ((t_old & overflow_interrupt_mask) &&
+ (t_new & overflow_interrupt_mask)) {
+ aspeed_timer_ctrl_overflow_interrupt(t, false);
+ s->ctrl = deposit32(s->ctrl, shift + op_overflow_interrupt, 1, 0);
+ }
+
+ if ((t_old & pulse_enable_mask) &&
+ (t_new & pulse_enable_mask)) {
+ aspeed_timer_ctrl_pulse_enable(t, false);
+ s->ctrl = deposit32(s->ctrl, shift + op_pulse_enable, 1, 0);
+ }
+
+ /* Clear interrupt status */
+ if (reg & 0x10000) {
+ s->irq_sts = deposit32(s->irq_sts, index, 1, 0);
+ }
+}
+
+static uint64_t aspeed_2700_timer_read(AspeedTimerCtrlState *s, hwaddr offset)
+{
+ uint32_t timer_offset = offset & 0x3f;
+ int timer_index = offset >> 6;
+ uint64_t value = 0;
+
+ if (timer_index >= ASPEED_TIMER_NR_TIMERS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: offset 0x%" PRIx64 " out of bounds\n",
+ __func__, offset);
+ return 0;
+ }
+
+ switch (timer_offset) {
+ /*
+ * Counter Status
+ * Counter Reload
+ * Counter First Matching
+ * Counter Second Matching
+ */
+ case 0x00 ... 0x0C:
+ value = aspeed_timer_get_value(&s->timers[timer_index],
+ timer_offset >> 2);
+ break;
+ /* Counter Control and Interrupt Status */
+ case 0x10:
+ value = deposit64(value, 0, 4,
+ extract32(s->ctrl, timer_index * 4, 4));
+ value = deposit64(value, 16, 1,
+ extract32(s->irq_sts, timer_index, 1));
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
+ PRIx64"\n", __func__, offset);
+ value = 0;
+ break;
+ }
+ trace_aspeed_timer_read(offset, value);
+ return value;
+}
+
+static void aspeed_2700_timer_write(AspeedTimerCtrlState *s, hwaddr offset,
+ uint64_t value)
+{
+ const uint32_t timer_value = (uint32_t)(value & 0xFFFFFFFF);
+ uint32_t timer_offset = offset & 0x3f;
+ int timer_index = offset >> 6;
+
+ if (timer_index >= ASPEED_TIMER_NR_TIMERS) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: offset 0x%" PRIx64 " out of bounds\n",
+ __func__, offset);
+ }
+
+ switch (timer_offset) {
+ /*
+ * Counter Status
+ * Counter Reload
+ * Counter First Matching
+ * Counter Second Matching
+ */
+ case 0x00 ... 0x0C:
+ aspeed_timer_set_value(s, timer_index, timer_offset >> 2,
+ timer_value);
+ break;
+ /* Counter Control Set and Interrupt Status */
+ case 0x10:
+ aspeed_2700_timer_set_ctrl(s, timer_index, timer_value);
+ break;
+ /* Counter Control Clear and Interrupr Status */
+ case 0x14:
+ aspeed_2700_timer_clear_ctrl(s, timer_index, timer_value);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
+ PRIx64"\n", __func__, offset);
+ break;
}
}
@@ -623,7 +841,8 @@ static void aspeed_timer_reset(DeviceState *dev)
for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) {
AspeedTimer *t = &s->timers[i];
- /* Explicitly call helpers to avoid any conditional behaviour through
+ /*
+ * Explicitly call helpers to avoid any conditional behaviour through
* aspeed_timer_set_ctrl().
*/
aspeed_timer_ctrl_enable(t, false);
@@ -671,18 +890,17 @@ static const VMStateDescription vmstate_aspeed_timer_state = {
}
};
-static Property aspeed_timer_properties[] = {
+static const Property aspeed_timer_properties[] = {
DEFINE_PROP_LINK("scu", AspeedTimerCtrlState, scu, TYPE_ASPEED_SCU,
AspeedSCUState *),
- DEFINE_PROP_END_OF_LIST(),
};
-static void timer_class_init(ObjectClass *klass, void *data)
+static void timer_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = aspeed_timer_realize;
- dc->reset = aspeed_timer_reset;
+ device_class_set_legacy_reset(dc, aspeed_timer_reset);
dc->desc = "ASPEED Timer";
dc->vmsd = &vmstate_aspeed_timer_state;
device_class_set_props(dc, aspeed_timer_properties);
@@ -697,7 +915,7 @@ static const TypeInfo aspeed_timer_info = {
.abstract = true,
};
-static void aspeed_2400_timer_class_init(ObjectClass *klass, void *data)
+static void aspeed_2400_timer_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
@@ -713,7 +931,7 @@ static const TypeInfo aspeed_2400_timer_info = {
.class_init = aspeed_2400_timer_class_init,
};
-static void aspeed_2500_timer_class_init(ObjectClass *klass, void *data)
+static void aspeed_2500_timer_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
@@ -729,7 +947,7 @@ static const TypeInfo aspeed_2500_timer_info = {
.class_init = aspeed_2500_timer_class_init,
};
-static void aspeed_2600_timer_class_init(ObjectClass *klass, void *data)
+static void aspeed_2600_timer_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
@@ -745,7 +963,7 @@ static const TypeInfo aspeed_2600_timer_info = {
.class_init = aspeed_2600_timer_class_init,
};
-static void aspeed_1030_timer_class_init(ObjectClass *klass, void *data)
+static void aspeed_1030_timer_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
@@ -761,6 +979,22 @@ static const TypeInfo aspeed_1030_timer_info = {
.class_init = aspeed_1030_timer_class_init,
};
+static void aspeed_2700_timer_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass);
+
+ dc->desc = "ASPEED 2700 Timer";
+ awc->read = aspeed_2700_timer_read;
+ awc->write = aspeed_2700_timer_write;
+}
+
+static const TypeInfo aspeed_2700_timer_info = {
+ .name = TYPE_ASPEED_2700_TIMER,
+ .parent = TYPE_ASPEED_TIMER,
+ .class_init = aspeed_2700_timer_class_init,
+};
+
static void aspeed_timer_register_types(void)
{
type_register_static(&aspeed_timer_info);
@@ -768,6 +1002,7 @@ static void aspeed_timer_register_types(void)
type_register_static(&aspeed_2500_timer_info);
type_register_static(&aspeed_2600_timer_info);
type_register_static(&aspeed_1030_timer_info);
+ type_register_static(&aspeed_2700_timer_info);
}
type_init(aspeed_timer_register_types)