diff options
Diffstat (limited to 'hw/misc/allwinner-h3-sysctrl.c')
-rw-r--r-- | hw/misc/allwinner-h3-sysctrl.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/hw/misc/allwinner-h3-sysctrl.c b/hw/misc/allwinner-h3-sysctrl.c new file mode 100644 index 000000000..1d07efa88 --- /dev/null +++ b/hw/misc/allwinner-h3-sysctrl.c @@ -0,0 +1,140 @@ +/* + * Allwinner H3 System Control emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/allwinner-h3-sysctrl.h" + +/* System Control register offsets */ +enum { + REG_VER = 0x24, /* Version */ + REG_EMAC_PHY_CLK = 0x30, /* EMAC PHY Clock */ +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* System Control register reset values */ +enum { + REG_VER_RST = 0x0, + REG_EMAC_PHY_CLK_RST = 0x58000, +}; + +static uint64_t allwinner_h3_sysctrl_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3SysCtrlState *s = AW_H3_SYSCTRL(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_SYSCTRL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_h3_sysctrl_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3SysCtrlState *s = AW_H3_SYSCTRL(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_SYSCTRL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_VER: /* Version */ + break; + default: + s->regs[idx] = (uint32_t) val; + break; + } +} + +static const MemoryRegionOps allwinner_h3_sysctrl_ops = { + .read = allwinner_h3_sysctrl_read, + .write = allwinner_h3_sysctrl_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_h3_sysctrl_reset(DeviceState *dev) +{ + AwH3SysCtrlState *s = AW_H3_SYSCTRL(dev); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_VER)] = REG_VER_RST; + s->regs[REG_INDEX(REG_EMAC_PHY_CLK)] = REG_EMAC_PHY_CLK_RST; +} + +static void allwinner_h3_sysctrl_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3SysCtrlState *s = AW_H3_SYSCTRL(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_sysctrl_ops, s, + TYPE_AW_H3_SYSCTRL, 4 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_sysctrl_vmstate = { + .name = "allwinner-h3-sysctrl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwH3SysCtrlState, AW_H3_SYSCTRL_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_sysctrl_reset; + dc->vmsd = &allwinner_h3_sysctrl_vmstate; +} + +static const TypeInfo allwinner_h3_sysctrl_info = { + .name = TYPE_AW_H3_SYSCTRL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_sysctrl_init, + .instance_size = sizeof(AwH3SysCtrlState), + .class_init = allwinner_h3_sysctrl_class_init, +}; + +static void allwinner_h3_sysctrl_register(void) +{ + type_register_static(&allwinner_h3_sysctrl_info); +} + +type_init(allwinner_h3_sysctrl_register) |