hw/arm: add experimental iPhone 5 A6 platform
This commit is contained in:
@@ -10,6 +10,7 @@ system_ss.add(when: 'CONFIG_CMSDK_APB_TIMER', if_true: files('cmsdk-apb-timer.c'
|
||||
system_ss.add(when: 'CONFIG_RENESAS_TMR', if_true: files('renesas_tmr.c'))
|
||||
system_ss.add(when: 'CONFIG_RENESAS_CMT', if_true: files('renesas_cmt.c'))
|
||||
system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c'))
|
||||
system_ss.add(when: 'CONFIG_S5L8950X', if_true: files('s5l8950x-timer.c'))
|
||||
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c'))
|
||||
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.c'))
|
||||
system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_gptimer.c'))
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
#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)
|
||||
Reference in New Issue
Block a user