#include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/timer.h" #include "hw/core/sysbus.h" #include "qom/object.h" #define TYPE_S5L8950X_TIMER "s5l8950x-timer" OBJECT_DECLARE_SIMPLE_TYPE(S5L8950XTimerState, S5L8950X_TIMER) struct S5L8950XTimerState { SysBusDevice parent_obj; MemoryRegion iomem; uint32_t tcon[4]; uint32_t tcnt[4]; uint32_t tdata[4]; uint32_t tstat[4]; }; #define TIMER_64BIT_LO 0x20 #define TIMER_64BIT_HI 0x28 static uint64_t s5l8950x_timer_read(void *opaque, hwaddr offset, unsigned size) { S5L8950XTimerState *s = opaque; uint64_t ticks; ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 24000000, NANOSECONDS_PER_SECOND); if (offset == TIMER_64BIT_LO) { return (uint32_t)ticks; } if (offset == TIMER_64BIT_HI) { return (uint32_t)(ticks >> 32); } switch (offset) { case 0x00 ... 0x7C: { int channel = offset / 0x20; int reg = offset % 0x20; if (channel >= 4) { break; } switch (reg) { case 0x00: return s->tcon[channel]; case 0x08: return s->tdata[channel]; case 0x10: return s->tcnt[channel]; case 0x14: return s->tstat[channel]; } break; } default: qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read at offset 0x%" HWADDR_PRIx "\n", __func__, offset); return 0; } qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read at offset 0x%" HWADDR_PRIx "\n", __func__, offset); return 0; } static void s5l8950x_timer_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { S5L8950XTimerState *s = opaque; switch (offset) { case 0x00 ... 0x7C: { int channel = offset / 0x20; int reg = offset % 0x20; if (channel >= 4) { break; } switch (reg) { case 0x00: s->tcon[channel] = val; return; case 0x08: s->tdata[channel] = val; return; case 0x10: s->tcnt[channel] = val; return; case 0x14: s->tstat[channel] = val; return; case 0x04: /* TCMD */ if (val == 1) { s->tstat[channel] &= ~1; /* Clear stop bit or whatever */ } else if (val == 2) { /* Stop */ } return; } break; } default: qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); return; } qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); } static const MemoryRegionOps s5l8950x_timer_ops = { .read = s5l8950x_timer_read, .write = s5l8950x_timer_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 4, .max_access_size = 4, }, }; static void s5l8950x_timer_init(Object *obj) { S5L8950XTimerState *s = S5L8950X_TIMER(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); memory_region_init_io(&s->iomem, obj, &s5l8950x_timer_ops, s, "s5l8950x-timer", 0x1000); sysbus_init_mmio(sbd, &s->iomem); } static void s5l8950x_timer_class_init(ObjectClass *klass, const void *data) { } static const TypeInfo s5l8950x_timer_info = { .name = TYPE_S5L8950X_TIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(S5L8950XTimerState), .instance_init = s5l8950x_timer_init, .class_init = s5l8950x_timer_class_init, }; static void s5l8950x_timer_register_types(void) { type_register_static(&s5l8950x_timer_info); } type_init(s5l8950x_timer_register_types)