11f93a6e4SLeon Alrae /* 21f93a6e4SLeon Alrae * Cluster Power Controller emulation 31f93a6e4SLeon Alrae * 41f93a6e4SLeon Alrae * Copyright (c) 2016 Imagination Technologies 51f93a6e4SLeon Alrae * 61f93a6e4SLeon Alrae * This library is free software; you can redistribute it and/or 71f93a6e4SLeon Alrae * modify it under the terms of the GNU Lesser General Public 81f93a6e4SLeon Alrae * License as published by the Free Software Foundation; either 91f93a6e4SLeon Alrae * version 2 of the License, or (at your option) any later version. 101f93a6e4SLeon Alrae * 111f93a6e4SLeon Alrae * This library is distributed in the hope that it will be useful, 121f93a6e4SLeon Alrae * but WITHOUT ANY WARRANTY; without even the implied warranty of 131f93a6e4SLeon Alrae * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 141f93a6e4SLeon Alrae * Lesser General Public License for more details. 151f93a6e4SLeon Alrae * 161f93a6e4SLeon Alrae * You should have received a copy of the GNU Lesser General Public 171f93a6e4SLeon Alrae * License along with this library; if not, see <http://www.gnu.org/licenses/>. 181f93a6e4SLeon Alrae */ 191f93a6e4SLeon Alrae 201f93a6e4SLeon Alrae #include "qemu/osdep.h" 211f93a6e4SLeon Alrae #include "qapi/error.h" 22*33c11879SPaolo Bonzini #include "cpu.h" 231f93a6e4SLeon Alrae #include "hw/sysbus.h" 241f93a6e4SLeon Alrae 251f93a6e4SLeon Alrae #include "hw/misc/mips_cpc.h" 261f93a6e4SLeon Alrae 271f93a6e4SLeon Alrae static inline uint64_t cpc_vp_run_mask(MIPSCPCState *cpc) 281f93a6e4SLeon Alrae { 291f93a6e4SLeon Alrae return (1ULL << cpc->num_vp) - 1; 301f93a6e4SLeon Alrae } 311f93a6e4SLeon Alrae 321f93a6e4SLeon Alrae static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run) 331f93a6e4SLeon Alrae { 341f93a6e4SLeon Alrae CPUState *cs = first_cpu; 351f93a6e4SLeon Alrae 361f93a6e4SLeon Alrae CPU_FOREACH(cs) { 371f93a6e4SLeon Alrae uint64_t i = 1ULL << cs->cpu_index; 381f93a6e4SLeon Alrae if (i & vp_run & ~cpc->vp_running) { 391f93a6e4SLeon Alrae cpu_interrupt(cs, CPU_INTERRUPT_WAKE); 401f93a6e4SLeon Alrae cpc->vp_running |= i; 411f93a6e4SLeon Alrae } 421f93a6e4SLeon Alrae } 431f93a6e4SLeon Alrae } 441f93a6e4SLeon Alrae 451f93a6e4SLeon Alrae static void cpc_stop_vp(MIPSCPCState *cpc, uint64_t vp_stop) 461f93a6e4SLeon Alrae { 471f93a6e4SLeon Alrae CPUState *cs = first_cpu; 481f93a6e4SLeon Alrae 491f93a6e4SLeon Alrae CPU_FOREACH(cs) { 501f93a6e4SLeon Alrae uint64_t i = 1ULL << cs->cpu_index; 511f93a6e4SLeon Alrae if (i & vp_stop & cpc->vp_running) { 521f93a6e4SLeon Alrae cs->halted = 1; 531f93a6e4SLeon Alrae cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); 541f93a6e4SLeon Alrae cpc->vp_running &= ~i; 551f93a6e4SLeon Alrae } 561f93a6e4SLeon Alrae } 571f93a6e4SLeon Alrae } 581f93a6e4SLeon Alrae 591f93a6e4SLeon Alrae static void cpc_write(void *opaque, hwaddr offset, uint64_t data, 601f93a6e4SLeon Alrae unsigned size) 611f93a6e4SLeon Alrae { 621f93a6e4SLeon Alrae MIPSCPCState *s = opaque; 631f93a6e4SLeon Alrae 641f93a6e4SLeon Alrae switch (offset) { 651f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_RUN_OFS: 661f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_RUN_OFS: 671f93a6e4SLeon Alrae cpc_run_vp(s, data & cpc_vp_run_mask(s)); 681f93a6e4SLeon Alrae break; 691f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_STOP_OFS: 701f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_STOP_OFS: 711f93a6e4SLeon Alrae cpc_stop_vp(s, data & cpc_vp_run_mask(s)); 721f93a6e4SLeon Alrae break; 731f93a6e4SLeon Alrae default: 741f93a6e4SLeon Alrae qemu_log_mask(LOG_UNIMP, 751f93a6e4SLeon Alrae "%s: Bad offset 0x%x\n", __func__, (int)offset); 761f93a6e4SLeon Alrae break; 771f93a6e4SLeon Alrae } 781f93a6e4SLeon Alrae 791f93a6e4SLeon Alrae return; 801f93a6e4SLeon Alrae } 811f93a6e4SLeon Alrae 821f93a6e4SLeon Alrae static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size) 831f93a6e4SLeon Alrae { 841f93a6e4SLeon Alrae MIPSCPCState *s = opaque; 851f93a6e4SLeon Alrae 861f93a6e4SLeon Alrae switch (offset) { 871f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_RUNNING_OFS: 881f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_RUNNING_OFS: 891f93a6e4SLeon Alrae return s->vp_running; 901f93a6e4SLeon Alrae default: 911f93a6e4SLeon Alrae qemu_log_mask(LOG_UNIMP, 921f93a6e4SLeon Alrae "%s: Bad offset 0x%x\n", __func__, (int)offset); 931f93a6e4SLeon Alrae return 0; 941f93a6e4SLeon Alrae } 951f93a6e4SLeon Alrae } 961f93a6e4SLeon Alrae 971f93a6e4SLeon Alrae static const MemoryRegionOps cpc_ops = { 981f93a6e4SLeon Alrae .read = cpc_read, 991f93a6e4SLeon Alrae .write = cpc_write, 1001f93a6e4SLeon Alrae .endianness = DEVICE_NATIVE_ENDIAN, 1011f93a6e4SLeon Alrae .impl = { 1021f93a6e4SLeon Alrae .max_access_size = 8, 1031f93a6e4SLeon Alrae }, 1041f93a6e4SLeon Alrae }; 1051f93a6e4SLeon Alrae 1061f93a6e4SLeon Alrae static void mips_cpc_init(Object *obj) 1071f93a6e4SLeon Alrae { 1081f93a6e4SLeon Alrae SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 1091f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(obj); 1101f93a6e4SLeon Alrae 1111f93a6e4SLeon Alrae memory_region_init_io(&s->mr, OBJECT(s), &cpc_ops, s, "mips-cpc", 1121f93a6e4SLeon Alrae CPC_ADDRSPACE_SZ); 1131f93a6e4SLeon Alrae sysbus_init_mmio(sbd, &s->mr); 1141f93a6e4SLeon Alrae } 1151f93a6e4SLeon Alrae 1161f93a6e4SLeon Alrae static void mips_cpc_realize(DeviceState *dev, Error **errp) 1171f93a6e4SLeon Alrae { 1181f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(dev); 1191f93a6e4SLeon Alrae 1201f93a6e4SLeon Alrae if (s->vp_start_running > cpc_vp_run_mask(s)) { 1211f93a6e4SLeon Alrae error_setg(errp, 1221f93a6e4SLeon Alrae "incorrect vp_start_running 0x%" PRIx64 " for num_vp = %d", 1231f93a6e4SLeon Alrae s->vp_running, s->num_vp); 1241f93a6e4SLeon Alrae return; 1251f93a6e4SLeon Alrae } 1261f93a6e4SLeon Alrae } 1271f93a6e4SLeon Alrae 1281f93a6e4SLeon Alrae static void mips_cpc_reset(DeviceState *dev) 1291f93a6e4SLeon Alrae { 1301f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(dev); 1311f93a6e4SLeon Alrae 1321f93a6e4SLeon Alrae /* Reflect the fact that all VPs are halted on reset */ 1331f93a6e4SLeon Alrae s->vp_running = 0; 1341f93a6e4SLeon Alrae 1351f93a6e4SLeon Alrae /* Put selected VPs into run state */ 1361f93a6e4SLeon Alrae cpc_run_vp(s, s->vp_start_running); 1371f93a6e4SLeon Alrae } 1381f93a6e4SLeon Alrae 1391f93a6e4SLeon Alrae static const VMStateDescription vmstate_mips_cpc = { 1401f93a6e4SLeon Alrae .name = "mips-cpc", 1411f93a6e4SLeon Alrae .version_id = 0, 1421f93a6e4SLeon Alrae .minimum_version_id = 0, 1431f93a6e4SLeon Alrae .fields = (VMStateField[]) { 1441f93a6e4SLeon Alrae VMSTATE_UINT64(vp_running, MIPSCPCState), 1451f93a6e4SLeon Alrae VMSTATE_END_OF_LIST() 1461f93a6e4SLeon Alrae }, 1471f93a6e4SLeon Alrae }; 1481f93a6e4SLeon Alrae 1491f93a6e4SLeon Alrae static Property mips_cpc_properties[] = { 1501f93a6e4SLeon Alrae DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1), 1511f93a6e4SLeon Alrae DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1), 1521f93a6e4SLeon Alrae DEFINE_PROP_END_OF_LIST(), 1531f93a6e4SLeon Alrae }; 1541f93a6e4SLeon Alrae 1551f93a6e4SLeon Alrae static void mips_cpc_class_init(ObjectClass *klass, void *data) 1561f93a6e4SLeon Alrae { 1571f93a6e4SLeon Alrae DeviceClass *dc = DEVICE_CLASS(klass); 1581f93a6e4SLeon Alrae 1591f93a6e4SLeon Alrae dc->realize = mips_cpc_realize; 1601f93a6e4SLeon Alrae dc->reset = mips_cpc_reset; 1611f93a6e4SLeon Alrae dc->vmsd = &vmstate_mips_cpc; 1621f93a6e4SLeon Alrae dc->props = mips_cpc_properties; 1631f93a6e4SLeon Alrae } 1641f93a6e4SLeon Alrae 1651f93a6e4SLeon Alrae static const TypeInfo mips_cpc_info = { 1661f93a6e4SLeon Alrae .name = TYPE_MIPS_CPC, 1671f93a6e4SLeon Alrae .parent = TYPE_SYS_BUS_DEVICE, 1681f93a6e4SLeon Alrae .instance_size = sizeof(MIPSCPCState), 1691f93a6e4SLeon Alrae .instance_init = mips_cpc_init, 1701f93a6e4SLeon Alrae .class_init = mips_cpc_class_init, 1711f93a6e4SLeon Alrae }; 1721f93a6e4SLeon Alrae 1731f93a6e4SLeon Alrae static void mips_cpc_register_types(void) 1741f93a6e4SLeon Alrae { 1751f93a6e4SLeon Alrae type_register_static(&mips_cpc_info); 1761f93a6e4SLeon Alrae } 1771f93a6e4SLeon Alrae 1781f93a6e4SLeon Alrae type_init(mips_cpc_register_types) 179