diff options
Diffstat (limited to 'hw/timer/aspeed_timer.c')
-rw-r--r-- | hw/timer/aspeed_timer.c | 293 |
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) |