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" 2303dd024fSPaolo Bonzini #include "qemu/log.h" 240b8fa32fSMarkus Armbruster #include "qemu/module.h" 251f93a6e4SLeon Alrae #include "hw/sysbus.h" 26d6454270SMarkus Armbruster #include "migration/vmstate.h" 271f93a6e4SLeon Alrae 281f93a6e4SLeon Alrae #include "hw/misc/mips_cpc.h" 29a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 301f93a6e4SLeon Alrae 311f93a6e4SLeon Alrae static inline uint64_t cpc_vp_run_mask(MIPSCPCState *cpc) 321f93a6e4SLeon Alrae { 331f93a6e4SLeon Alrae return (1ULL << cpc->num_vp) - 1; 341f93a6e4SLeon Alrae } 351f93a6e4SLeon Alrae 36f5117fd2SMiodrag Dinic static void mips_cpu_reset_async_work(CPUState *cs, run_on_cpu_data data) 37f5117fd2SMiodrag Dinic { 38f5117fd2SMiodrag Dinic MIPSCPCState *cpc = (MIPSCPCState *) data.host_ptr; 39f5117fd2SMiodrag Dinic 40f5117fd2SMiodrag Dinic cpu_reset(cs); 41*42a05233SPhilippe Mathieu-Daudé cs->halted = 0; 42f5117fd2SMiodrag Dinic cpc->vp_running |= 1ULL << cs->cpu_index; 43f5117fd2SMiodrag Dinic } 44f5117fd2SMiodrag Dinic 451f93a6e4SLeon Alrae static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run) 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_run & ~cpc->vp_running) { 52f5117fd2SMiodrag Dinic /* 53f5117fd2SMiodrag Dinic * To avoid racing with a CPU we are just kicking off. 54f5117fd2SMiodrag Dinic * We do the final bit of preparation for the work in 55f5117fd2SMiodrag Dinic * the target CPUs context. 56f5117fd2SMiodrag Dinic */ 57f5117fd2SMiodrag Dinic async_safe_run_on_cpu(cs, mips_cpu_reset_async_work, 58f5117fd2SMiodrag Dinic RUN_ON_CPU_HOST_PTR(cpc)); 591f93a6e4SLeon Alrae } 601f93a6e4SLeon Alrae } 611f93a6e4SLeon Alrae } 621f93a6e4SLeon Alrae 631f93a6e4SLeon Alrae static void cpc_stop_vp(MIPSCPCState *cpc, uint64_t vp_stop) 641f93a6e4SLeon Alrae { 651f93a6e4SLeon Alrae CPUState *cs = first_cpu; 661f93a6e4SLeon Alrae 671f93a6e4SLeon Alrae CPU_FOREACH(cs) { 681f93a6e4SLeon Alrae uint64_t i = 1ULL << cs->cpu_index; 691f93a6e4SLeon Alrae if (i & vp_stop & cpc->vp_running) { 70dff94251SLeon Alrae cpu_interrupt(cs, CPU_INTERRUPT_HALT); 711f93a6e4SLeon Alrae cpc->vp_running &= ~i; 721f93a6e4SLeon Alrae } 731f93a6e4SLeon Alrae } 741f93a6e4SLeon Alrae } 751f93a6e4SLeon Alrae 761f93a6e4SLeon Alrae static void cpc_write(void *opaque, hwaddr offset, uint64_t data, 771f93a6e4SLeon Alrae unsigned size) 781f93a6e4SLeon Alrae { 791f93a6e4SLeon Alrae MIPSCPCState *s = opaque; 801f93a6e4SLeon Alrae 811f93a6e4SLeon Alrae switch (offset) { 821f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_RUN_OFS: 831f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_RUN_OFS: 841f93a6e4SLeon Alrae cpc_run_vp(s, data & cpc_vp_run_mask(s)); 851f93a6e4SLeon Alrae break; 861f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_STOP_OFS: 871f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_STOP_OFS: 881f93a6e4SLeon Alrae cpc_stop_vp(s, data & cpc_vp_run_mask(s)); 891f93a6e4SLeon Alrae break; 901f93a6e4SLeon Alrae default: 911f93a6e4SLeon Alrae qemu_log_mask(LOG_UNIMP, 921f93a6e4SLeon Alrae "%s: Bad offset 0x%x\n", __func__, (int)offset); 931f93a6e4SLeon Alrae break; 941f93a6e4SLeon Alrae } 951f93a6e4SLeon Alrae 961f93a6e4SLeon Alrae return; 971f93a6e4SLeon Alrae } 981f93a6e4SLeon Alrae 991f93a6e4SLeon Alrae static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size) 1001f93a6e4SLeon Alrae { 1011f93a6e4SLeon Alrae MIPSCPCState *s = opaque; 1021f93a6e4SLeon Alrae 1031f93a6e4SLeon Alrae switch (offset) { 1041f93a6e4SLeon Alrae case CPC_CL_BASE_OFS + CPC_VP_RUNNING_OFS: 1051f93a6e4SLeon Alrae case CPC_CO_BASE_OFS + CPC_VP_RUNNING_OFS: 1061f93a6e4SLeon Alrae return s->vp_running; 1071f93a6e4SLeon Alrae default: 1081f93a6e4SLeon Alrae qemu_log_mask(LOG_UNIMP, 1091f93a6e4SLeon Alrae "%s: Bad offset 0x%x\n", __func__, (int)offset); 1101f93a6e4SLeon Alrae return 0; 1111f93a6e4SLeon Alrae } 1121f93a6e4SLeon Alrae } 1131f93a6e4SLeon Alrae 1141f93a6e4SLeon Alrae static const MemoryRegionOps cpc_ops = { 1151f93a6e4SLeon Alrae .read = cpc_read, 1161f93a6e4SLeon Alrae .write = cpc_write, 1171f93a6e4SLeon Alrae .endianness = DEVICE_NATIVE_ENDIAN, 1181f93a6e4SLeon Alrae .impl = { 1191f93a6e4SLeon Alrae .max_access_size = 8, 1201f93a6e4SLeon Alrae }, 1211f93a6e4SLeon Alrae }; 1221f93a6e4SLeon Alrae 1231f93a6e4SLeon Alrae static void mips_cpc_init(Object *obj) 1241f93a6e4SLeon Alrae { 1251f93a6e4SLeon Alrae SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 1261f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(obj); 1271f93a6e4SLeon Alrae 1281f93a6e4SLeon Alrae memory_region_init_io(&s->mr, OBJECT(s), &cpc_ops, s, "mips-cpc", 1291f93a6e4SLeon Alrae CPC_ADDRSPACE_SZ); 1301f93a6e4SLeon Alrae sysbus_init_mmio(sbd, &s->mr); 1311f93a6e4SLeon Alrae } 1321f93a6e4SLeon Alrae 1331f93a6e4SLeon Alrae static void mips_cpc_realize(DeviceState *dev, Error **errp) 1341f93a6e4SLeon Alrae { 1351f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(dev); 1361f93a6e4SLeon Alrae 1371f93a6e4SLeon Alrae if (s->vp_start_running > cpc_vp_run_mask(s)) { 1381f93a6e4SLeon Alrae error_setg(errp, 1391f93a6e4SLeon Alrae "incorrect vp_start_running 0x%" PRIx64 " for num_vp = %d", 1401f93a6e4SLeon Alrae s->vp_running, s->num_vp); 1411f93a6e4SLeon Alrae return; 1421f93a6e4SLeon Alrae } 1431f93a6e4SLeon Alrae } 1441f93a6e4SLeon Alrae 1451f93a6e4SLeon Alrae static void mips_cpc_reset(DeviceState *dev) 1461f93a6e4SLeon Alrae { 1471f93a6e4SLeon Alrae MIPSCPCState *s = MIPS_CPC(dev); 1481f93a6e4SLeon Alrae 1491f93a6e4SLeon Alrae /* Reflect the fact that all VPs are halted on reset */ 1501f93a6e4SLeon Alrae s->vp_running = 0; 1511f93a6e4SLeon Alrae 1521f93a6e4SLeon Alrae /* Put selected VPs into run state */ 1531f93a6e4SLeon Alrae cpc_run_vp(s, s->vp_start_running); 1541f93a6e4SLeon Alrae } 1551f93a6e4SLeon Alrae 1561f93a6e4SLeon Alrae static const VMStateDescription vmstate_mips_cpc = { 1571f93a6e4SLeon Alrae .name = "mips-cpc", 1581f93a6e4SLeon Alrae .version_id = 0, 1591f93a6e4SLeon Alrae .minimum_version_id = 0, 1601f93a6e4SLeon Alrae .fields = (VMStateField[]) { 1611f93a6e4SLeon Alrae VMSTATE_UINT64(vp_running, MIPSCPCState), 1621f93a6e4SLeon Alrae VMSTATE_END_OF_LIST() 1631f93a6e4SLeon Alrae }, 1641f93a6e4SLeon Alrae }; 1651f93a6e4SLeon Alrae 1661f93a6e4SLeon Alrae static Property mips_cpc_properties[] = { 1671f93a6e4SLeon Alrae DEFINE_PROP_UINT32("num-vp", MIPSCPCState, num_vp, 0x1), 1681f93a6e4SLeon Alrae DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1), 1691f93a6e4SLeon Alrae DEFINE_PROP_END_OF_LIST(), 1701f93a6e4SLeon Alrae }; 1711f93a6e4SLeon Alrae 1721f93a6e4SLeon Alrae static void mips_cpc_class_init(ObjectClass *klass, void *data) 1731f93a6e4SLeon Alrae { 1741f93a6e4SLeon Alrae DeviceClass *dc = DEVICE_CLASS(klass); 1751f93a6e4SLeon Alrae 1761f93a6e4SLeon Alrae dc->realize = mips_cpc_realize; 1771f93a6e4SLeon Alrae dc->reset = mips_cpc_reset; 1781f93a6e4SLeon Alrae dc->vmsd = &vmstate_mips_cpc; 1794f67d30bSMarc-André Lureau device_class_set_props(dc, mips_cpc_properties); 1801f93a6e4SLeon Alrae } 1811f93a6e4SLeon Alrae 1821f93a6e4SLeon Alrae static const TypeInfo mips_cpc_info = { 1831f93a6e4SLeon Alrae .name = TYPE_MIPS_CPC, 1841f93a6e4SLeon Alrae .parent = TYPE_SYS_BUS_DEVICE, 1851f93a6e4SLeon Alrae .instance_size = sizeof(MIPSCPCState), 1861f93a6e4SLeon Alrae .instance_init = mips_cpc_init, 1871f93a6e4SLeon Alrae .class_init = mips_cpc_class_init, 1881f93a6e4SLeon Alrae }; 1891f93a6e4SLeon Alrae 1901f93a6e4SLeon Alrae static void mips_cpc_register_types(void) 1911f93a6e4SLeon Alrae { 1921f93a6e4SLeon Alrae type_register_static(&mips_cpc_info); 1931f93a6e4SLeon Alrae } 1941f93a6e4SLeon Alrae 1951f93a6e4SLeon Alrae type_init(mips_cpc_register_types) 196