diff options
Diffstat (limited to 'hw/timer/aspeed_timer.c')
-rw-r--r-- | hw/timer/aspeed_timer.c | 273 |
1 files changed, 253 insertions, 20 deletions
diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 4868651..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; } @@ -431,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 */ @@ -451,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, @@ -475,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; } @@ -495,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; } } @@ -514,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; } @@ -548,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; } } @@ -564,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; } @@ -586,10 +609,203 @@ static void aspeed_2600_timer_write(AspeedTimerCtrlState *s, hwaddr offset, 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; } } @@ -679,7 +895,7 @@ static const Property aspeed_timer_properties[] = { AspeedSCUState *), }; -static void timer_class_init(ObjectClass *klass, void *data) +static void timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -699,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); @@ -715,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); @@ -731,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); @@ -747,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); @@ -763,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); @@ -770,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) |