1 /* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 7 * Authors: Sanjay Lal <sanjayl@kymasys.com> 8 * 9 * Copyright (C) 2015 Imagination Technologies 10 */ 11 12 #include "qemu/osdep.h" 13 #include "qapi/error.h" 14 #include "qemu/log.h" 15 #include "hw/hw.h" 16 #include "hw/sysbus.h" 17 #include "sysemu/sysemu.h" 18 #include "hw/misc/mips_cmgcr.h" 19 #include "hw/misc/mips_cpc.h" 20 #include "hw/intc/mips_gic.h" 21 22 static inline bool is_cpc_connected(MIPSGCRState *s) 23 { 24 return s->cpc_mr != NULL; 25 } 26 27 static inline bool is_gic_connected(MIPSGCRState *s) 28 { 29 return s->gic_mr != NULL; 30 } 31 32 static inline void update_gcr_base(MIPSGCRState *gcr, uint64_t val) 33 { 34 CPUState *cpu; 35 MIPSCPU *mips_cpu; 36 37 gcr->gcr_base = val & GCR_BASE_GCRBASE_MSK; 38 memory_region_set_address(&gcr->iomem, gcr->gcr_base); 39 40 CPU_FOREACH(cpu) { 41 mips_cpu = MIPS_CPU(cpu); 42 mips_cpu->env.CP0_CMGCRBase = gcr->gcr_base >> 4; 43 } 44 } 45 46 static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val) 47 { 48 if (is_cpc_connected(gcr)) { 49 gcr->cpc_base = val & GCR_CPC_BASE_MSK; 50 memory_region_transaction_begin(); 51 memory_region_set_address(gcr->cpc_mr, 52 gcr->cpc_base & GCR_CPC_BASE_CPCBASE_MSK); 53 memory_region_set_enabled(gcr->cpc_mr, 54 gcr->cpc_base & GCR_CPC_BASE_CPCEN_MSK); 55 memory_region_transaction_commit(); 56 } 57 } 58 59 static inline void update_gic_base(MIPSGCRState *gcr, uint64_t val) 60 { 61 if (is_gic_connected(gcr)) { 62 gcr->gic_base = val & GCR_GIC_BASE_MSK; 63 memory_region_transaction_begin(); 64 memory_region_set_address(gcr->gic_mr, 65 gcr->gic_base & GCR_GIC_BASE_GICBASE_MSK); 66 memory_region_set_enabled(gcr->gic_mr, 67 gcr->gic_base & GCR_GIC_BASE_GICEN_MSK); 68 memory_region_transaction_commit(); 69 } 70 } 71 72 /* Read GCR registers */ 73 static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size) 74 { 75 MIPSGCRState *gcr = (MIPSGCRState *) opaque; 76 MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index]; 77 MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other]; 78 79 switch (addr) { 80 /* Global Control Block Register */ 81 case GCR_CONFIG_OFS: 82 /* Set PCORES to 0 */ 83 return 0; 84 case GCR_BASE_OFS: 85 return gcr->gcr_base; 86 case GCR_REV_OFS: 87 return gcr->gcr_rev; 88 case GCR_GIC_BASE_OFS: 89 return gcr->gic_base; 90 case GCR_CPC_BASE_OFS: 91 return gcr->cpc_base; 92 case GCR_GIC_STATUS_OFS: 93 return is_gic_connected(gcr); 94 case GCR_CPC_STATUS_OFS: 95 return is_cpc_connected(gcr); 96 case GCR_L2_CONFIG_OFS: 97 /* L2 BYPASS */ 98 return GCR_L2_CONFIG_BYPASS_MSK; 99 /* Core-Local and Core-Other Control Blocks */ 100 case MIPS_CLCB_OFS + GCR_CL_CONFIG_OFS: 101 case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS: 102 /* Set PVP to # of VPs - 1 */ 103 return gcr->num_vps - 1; 104 case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS: 105 return current_vps->reset_base; 106 case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS: 107 return other_vps->reset_base; 108 case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS: 109 return current_vps->other; 110 case MIPS_COCB_OFS + GCR_CL_OTHER_OFS: 111 return other_vps->other; 112 default: 113 qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx 114 "\n", size, addr); 115 return 0; 116 } 117 return 0; 118 } 119 120 static inline target_ulong get_exception_base(MIPSGCRVPState *vps) 121 { 122 /* TODO: BEV_BASE and SELECT_BEV */ 123 return (int32_t)(vps->reset_base & GCR_CL_RESET_BASE_RESETBASE_MSK); 124 } 125 126 /* Write GCR registers */ 127 static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) 128 { 129 MIPSGCRState *gcr = (MIPSGCRState *)opaque; 130 MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index]; 131 MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other]; 132 133 switch (addr) { 134 case GCR_BASE_OFS: 135 update_gcr_base(gcr, data); 136 break; 137 case GCR_GIC_BASE_OFS: 138 update_gic_base(gcr, data); 139 break; 140 case GCR_CPC_BASE_OFS: 141 update_cpc_base(gcr, data); 142 break; 143 case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS: 144 current_vps->reset_base = data & GCR_CL_RESET_BASE_MSK; 145 cpu_set_exception_base(current_cpu->cpu_index, 146 get_exception_base(current_vps)); 147 break; 148 case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS: 149 other_vps->reset_base = data & GCR_CL_RESET_BASE_MSK; 150 cpu_set_exception_base(current_vps->other, 151 get_exception_base(other_vps)); 152 break; 153 case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS: 154 if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) { 155 current_vps->other = data & GCR_CL_OTHER_MSK; 156 } 157 break; 158 case MIPS_COCB_OFS + GCR_CL_OTHER_OFS: 159 if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) { 160 other_vps->other = data & GCR_CL_OTHER_MSK; 161 } 162 break; 163 default: 164 qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx 165 " 0x%" PRIx64 "\n", size, addr, data); 166 break; 167 } 168 } 169 170 static const MemoryRegionOps gcr_ops = { 171 .read = gcr_read, 172 .write = gcr_write, 173 .endianness = DEVICE_NATIVE_ENDIAN, 174 .impl = { 175 .max_access_size = 8, 176 }, 177 }; 178 179 static void mips_gcr_init(Object *obj) 180 { 181 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 182 MIPSGCRState *s = MIPS_GCR(obj); 183 184 object_property_add_link(obj, "gic", TYPE_MEMORY_REGION, 185 (Object **)&s->gic_mr, 186 qdev_prop_allow_set_link_before_realize, 187 OBJ_PROP_LINK_UNREF_ON_RELEASE, 188 &error_abort); 189 190 object_property_add_link(obj, "cpc", TYPE_MEMORY_REGION, 191 (Object **)&s->cpc_mr, 192 qdev_prop_allow_set_link_before_realize, 193 OBJ_PROP_LINK_UNREF_ON_RELEASE, 194 &error_abort); 195 196 memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s, 197 "mips-gcr", GCR_ADDRSPACE_SZ); 198 sysbus_init_mmio(sbd, &s->iomem); 199 } 200 201 static void mips_gcr_reset(DeviceState *dev) 202 { 203 MIPSGCRState *s = MIPS_GCR(dev); 204 int i; 205 206 update_gic_base(s, 0); 207 update_cpc_base(s, 0); 208 209 for (i = 0; i < s->num_vps; i++) { 210 s->vps[i].other = 0; 211 s->vps[i].reset_base = 0xBFC00000 & GCR_CL_RESET_BASE_MSK; 212 cpu_set_exception_base(i, get_exception_base(&s->vps[i])); 213 } 214 } 215 216 static const VMStateDescription vmstate_mips_gcr = { 217 .name = "mips-gcr", 218 .version_id = 0, 219 .minimum_version_id = 0, 220 .fields = (VMStateField[]) { 221 VMSTATE_UINT64(cpc_base, MIPSGCRState), 222 VMSTATE_END_OF_LIST() 223 }, 224 }; 225 226 static Property mips_gcr_properties[] = { 227 DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1), 228 DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800), 229 DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR), 230 DEFINE_PROP_END_OF_LIST(), 231 }; 232 233 static void mips_gcr_realize(DeviceState *dev, Error **errp) 234 { 235 MIPSGCRState *s = MIPS_GCR(dev); 236 237 /* Create local set of registers for each VP */ 238 s->vps = g_new(MIPSGCRVPState, s->num_vps); 239 } 240 241 static void mips_gcr_class_init(ObjectClass *klass, void *data) 242 { 243 DeviceClass *dc = DEVICE_CLASS(klass); 244 dc->props = mips_gcr_properties; 245 dc->vmsd = &vmstate_mips_gcr; 246 dc->reset = mips_gcr_reset; 247 dc->realize = mips_gcr_realize; 248 } 249 250 static const TypeInfo mips_gcr_info = { 251 .name = TYPE_MIPS_GCR, 252 .parent = TYPE_SYS_BUS_DEVICE, 253 .instance_size = sizeof(MIPSGCRState), 254 .instance_init = mips_gcr_init, 255 .class_init = mips_gcr_class_init, 256 }; 257 258 static void mips_gcr_register_types(void) 259 { 260 type_register_static(&mips_gcr_info); 261 } 262 263 type_init(mips_gcr_register_types) 264