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" 2233c11879SPaolo Bonzini #include "cpu.h" 23*03dd024fSPaolo Bonzini #include "qemu/log.h" 241f93a6e4SLeon Alrae #include "hw/sysbus.h" 251f93a6e4SLeon Alrae 261f93a6e4SLeon Alrae #include "hw/misc/mips_cpc.h" 271f93a6e4SLeon Alrae 281f93a6e4SLeon Alrae static inline uint64_t cpc_vp_run_mask(MIPSCPCState *cpc) 291f93a6e4SLeon Alrae { 301f93a6e4SLeon Alrae return (1ULL << cpc->num_vp) - 1; 311f93a6e4SLeon Alrae } 321f93a6e4SLeon Alrae 331f93a6e4SLeon Alrae static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run) 341f93a6e4SLeon Alrae { 351f93a6e4SLeon Alrae CPUState *cs = first_cpu; 361f93a6e4SLeon Alrae 371f93a6e4SLeon Alrae CPU_FOREACH(cs) { 381f93a6e4SLeon Alrae uint64_t i = 1ULL << cs->cpu_index; 391f93a6e4SLeon Alrae if (i & vp_run & ~cpc->vp_running) { 401f93a6e4SLeon Alrae cpu_interrupt(cs, CPU_INTERRUPT_WAKE); 411f93a6e4SLeon Alrae cpc->vp_running |= i; 421f93a6e4SLeon Alrae } 431f93a6e4SLeon Alrae } 441f93a6e4SLeon Alrae } 451f93a6e4SLeon Alrae 461f93a6e4SLeon Alrae static void cpc_stop_vp(MIPSCPCState *cpc, uint64_t vp_stop) 471f93a6e4SLeon Alrae { 481f93a6e4SLeon Alrae CPUState *cs = first_cpu; 491f93a6e4SLeon Alrae 501f93a6e4SLeon Alrae CPU_FOREACH(cs) { 511f93a6e4SLeon Alrae uint64_t i = 1ULL << cs->cpu_index; 521f93a6e4SLeon Alrae if (i & vp_stop & cpc->vp_running) { 531f93a6e4SLeon Alrae cs->halted = 1; 541f93a6e4SLeon Alrae cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); 551f93a6e4SLeon Alrae cpc->vp_running &= ~i; 561f93a6e4SLeon Alrae } 571f93a6e4SLeon Alrae } 581f93a6e4SLeon Alrae } 591f93a6e4SLeon Alrae 601f93a6e4SLeon Alrae static void cpc_write(void *opaque, hwaddr offset, uint64_t data, 611f93a6e4SLeon Alrae unsigned size) 621f93a6e4SLeon Alrae { 631f93a6e4SLeon Alrae MIPSCPCState *s = opaque; 641f93a6e4SLeon Alrae 651f93a6e4SLeon Alrae switch (offset) { 661f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_RUN_OFS: 671f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_RUN_OFS: 681f93a6e4SLeon Alrae cpc_run_vp(s, data & cpc_vp_run_mask(s)); 691f93a6e4SLeon Alrae break; 701f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_STOP_OFS: 711f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_STOP_OFS: 721f93a6e4SLeon Alrae cpc_stop_vp(s, data & cpc_vp_run_mask(s)); 731f93a6e4SLeon Alrae break; 741f93a6e4SLeon Alrae default: 751f93a6e4SLeon Alrae qemu_log_mask(LOG_UNIMP, 761f93a6e4SLeon Alrae "%s: Bad offset 0x%x\n", __func__, (int)offset); 771f93a6e4SLeon Alrae break; 781f93a6e4SLeon Alrae } 791f93a6e4SLeon Alrae 801f93a6e4SLeon Alrae return; 811f93a6e4SLeon Alrae } 821f93a6e4SLeon Alrae 831f93a6e4SLeon Alrae static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size) 841f93a6e4SLeon Alrae { 851f93a6e4SLeon Alrae MIPSCPCState *s = opaque; 861f93a6e4SLeon Alrae 871f93a6e4SLeon Alrae switch (offset) { 881f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_RUNNING_OFS: 891f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_RUNNING_OFS: 901f93a6e4SLeon Alrae return s->vp_running; 911f93a6e4SLeon Alrae default: 921f93a6e4SLeon Alrae qemu_log_mask(LOG_UNIMP, 931f93a6e4SLeon Alrae "%s: Bad offset 0x%x\n", __func__, (int)offset); 941f93a6e4SLeon Alrae return 0; 951f93a6e4SLeon Alrae } 961f93a6e4SLeon Alrae } 971f93a6e4SLeon Alrae 981f93a6e4SLeon Alrae static const MemoryRegionOps cpc_ops = { 991f93a6e4SLeon Alrae .read = cpc_read, 1001f93a6e4SLeon Alrae .write = cpc_write, 1011f93a6e4SLeon Alrae .endianness = DEVICE_NATIVE_ENDIAN, 1021f93a6e4SLeon Alrae .impl = { 1031f93a6e4SLeon Alrae .max_access_size = 8, 1041f93a6e4SLeon Alrae }, 1051f93a6e4SLeon Alrae }; 1061f93a6e4SLeon Alrae 1071f93a6e4SLeon Alrae static void mips_cpc_init(Object *obj) 1081f93a6e4SLeon Alrae { 1091f93a6e4SLeon Alrae SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 1101f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(obj); 1111f93a6e4SLeon Alrae 1121f93a6e4SLeon Alrae memory_region_init_io(&s->mr, OBJECT(s), &cpc_ops, s, "mips-cpc", 1131f93a6e4SLeon Alrae CPC_ADDRSPACE_SZ); 1141f93a6e4SLeon Alrae sysbus_init_mmio(sbd, &s->mr); 1151f93a6e4SLeon Alrae } 1161f93a6e4SLeon Alrae 1171f93a6e4SLeon Alrae static void mips_cpc_realize(DeviceState *dev, Error **errp) 1181f93a6e4SLeon Alrae { 1191f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(dev); 1201f93a6e4SLeon Alrae 1211f93a6e4SLeon Alrae if (s->vp_start_running > cpc_vp_run_mask(s)) { 1221f93a6e4SLeon Alrae error_setg(errp, 1231f93a6e4SLeon Alrae "incorrect vp_start_running 0x%" PRIx64 " for num_vp = %d", 1241f93a6e4SLeon Alrae s->vp_running, s->num_vp); 1251f93a6e4SLeon Alrae return; 1261f93a6e4SLeon Alrae } 1271f93a6e4SLeon Alrae } 1281f93a6e4SLeon Alrae 1291f93a6e4SLeon Alrae static void mips_cpc_reset(DeviceState *dev) 1301f93a6e4SLeon Alrae { 1311f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(dev); 1321f93a6e4SLeon Alrae 1331f93a6e4SLeon Alrae /* Reflect the fact that all VPs are halted on reset */ 1341f93a6e4SLeon Alrae s->vp_running = 0; 1351f93a6e4SLeon Alrae 1361f93a6e4SLeon Alrae /* Put selected VPs into run state */ 1371f93a6e4SLeon Alrae cpc_run_vp(s, s->vp_start_running); 1381f93a6e4SLeon Alrae } 1391f93a6e4SLeon Alrae 1401f93a6e4SLeon Alrae static const VMStateDescription vmstate_mips_cpc = { 1411f93a6e4SLeon Alrae .name = "mips-cpc", 1421f93a6e4SLeon Alrae .version_id = 0, 1431f93a6e4SLeon Alrae .minimum_version_id = 0, 1441f93a6e4SLeon Alrae .fields = (VMStateField[]) { 1451f93a6e4SLeon Alrae VMSTATE_UINT64(vp_running, MIPSCPCState), 1461f93a6e4SLeon Alrae VMSTATE_END_OF_LIST() 1471f93a6e4SLeon Alrae }, 1481f93a6e4SLeon Alrae }; 1491f93a6e4SLeon Alrae 1501f93a6e4SLeon Alrae static Property mips_cpc_properties[] = { 1511f93a6e4SLeon Alrae DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1), 1521f93a6e4SLeon Alrae DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1), 1531f93a6e4SLeon Alrae DEFINE_PROP_END_OF_LIST(), 1541f93a6e4SLeon Alrae }; 1551f93a6e4SLeon Alrae 1561f93a6e4SLeon Alrae static void mips_cpc_class_init(ObjectClass *klass, void *data) 1571f93a6e4SLeon Alrae { 1581f93a6e4SLeon Alrae DeviceClass *dc = DEVICE_CLASS(klass); 1591f93a6e4SLeon Alrae 1601f93a6e4SLeon Alrae dc->realize = mips_cpc_realize; 1611f93a6e4SLeon Alrae dc->reset = mips_cpc_reset; 1621f93a6e4SLeon Alrae dc->vmsd = &vmstate_mips_cpc; 1631f93a6e4SLeon Alrae dc->props = mips_cpc_properties; 1641f93a6e4SLeon Alrae } 1651f93a6e4SLeon Alrae 1661f93a6e4SLeon Alrae static const TypeInfo mips_cpc_info = { 1671f93a6e4SLeon Alrae .name = TYPE_MIPS_CPC, 1681f93a6e4SLeon Alrae .parent = TYPE_SYS_BUS_DEVICE, 1691f93a6e4SLeon Alrae .instance_size = sizeof(MIPSCPCState), 1701f93a6e4SLeon Alrae .instance_init = mips_cpc_init, 1711f93a6e4SLeon Alrae .class_init = mips_cpc_class_init, 1721f93a6e4SLeon Alrae }; 1731f93a6e4SLeon Alrae 1741f93a6e4SLeon Alrae static void mips_cpc_register_types(void) 1751f93a6e4SLeon Alrae { 1761f93a6e4SLeon Alrae type_register_static(&mips_cpc_info); 1771f93a6e4SLeon Alrae } 1781f93a6e4SLeon Alrae 1791f93a6e4SLeon Alrae type_init(mips_cpc_register_types) 180