13994215dSYongbok Kim /*
23994215dSYongbok Kim * This file is subject to the terms and conditions of the GNU General Public
33994215dSYongbok Kim * License. See the file "COPYING" in the main directory of this archive
43994215dSYongbok Kim * for more details.
53994215dSYongbok Kim *
63994215dSYongbok Kim * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
73994215dSYongbok Kim * Authors: Sanjay Lal <sanjayl@kymasys.com>
83994215dSYongbok Kim *
93994215dSYongbok Kim * Copyright (C) 2015 Imagination Technologies
103994215dSYongbok Kim */
113994215dSYongbok Kim
123994215dSYongbok Kim #include "qemu/osdep.h"
1303dd024fSPaolo Bonzini #include "qemu/log.h"
140b8fa32fSMarkus Armbruster #include "qemu/module.h"
153994215dSYongbok Kim #include "hw/sysbus.h"
16d6454270SMarkus Armbruster #include "migration/vmstate.h"
173994215dSYongbok Kim #include "hw/misc/mips_cmgcr.h"
182edd5261SLeon Alrae #include "hw/misc/mips_cpc.h"
19a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
2019494f81SLeon Alrae #include "hw/intc/mips_gic.h"
212edd5261SLeon Alrae
is_cpc_connected(MIPSGCRState * s)222edd5261SLeon Alrae static inline bool is_cpc_connected(MIPSGCRState *s)
232edd5261SLeon Alrae {
242edd5261SLeon Alrae return s->cpc_mr != NULL;
252edd5261SLeon Alrae }
262edd5261SLeon Alrae
is_gic_connected(MIPSGCRState * s)2719494f81SLeon Alrae static inline bool is_gic_connected(MIPSGCRState *s)
2819494f81SLeon Alrae {
2919494f81SLeon Alrae return s->gic_mr != NULL;
3019494f81SLeon Alrae }
3119494f81SLeon Alrae
update_gcr_base(MIPSGCRState * gcr,uint64_t val)3208944be1SPaul Burton static inline void update_gcr_base(MIPSGCRState *gcr, uint64_t val)
3308944be1SPaul Burton {
3408944be1SPaul Burton CPUState *cpu;
3508944be1SPaul Burton MIPSCPU *mips_cpu;
3608944be1SPaul Burton
3708944be1SPaul Burton gcr->gcr_base = val & GCR_BASE_GCRBASE_MSK;
3808944be1SPaul Burton memory_region_set_address(&gcr->iomem, gcr->gcr_base);
3908944be1SPaul Burton
4008944be1SPaul Burton CPU_FOREACH(cpu) {
4108944be1SPaul Burton mips_cpu = MIPS_CPU(cpu);
4208944be1SPaul Burton mips_cpu->env.CP0_CMGCRBase = gcr->gcr_base >> 4;
4308944be1SPaul Burton }
4408944be1SPaul Burton }
4508944be1SPaul Burton
update_cpc_base(MIPSGCRState * gcr,uint64_t val)462edd5261SLeon Alrae static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val)
472edd5261SLeon Alrae {
482edd5261SLeon Alrae if (is_cpc_connected(gcr)) {
492edd5261SLeon Alrae gcr->cpc_base = val & GCR_CPC_BASE_MSK;
502edd5261SLeon Alrae memory_region_transaction_begin();
512edd5261SLeon Alrae memory_region_set_address(gcr->cpc_mr,
522edd5261SLeon Alrae gcr->cpc_base & GCR_CPC_BASE_CPCBASE_MSK);
532edd5261SLeon Alrae memory_region_set_enabled(gcr->cpc_mr,
542edd5261SLeon Alrae gcr->cpc_base & GCR_CPC_BASE_CPCEN_MSK);
552edd5261SLeon Alrae memory_region_transaction_commit();
562edd5261SLeon Alrae }
572edd5261SLeon Alrae }
583994215dSYongbok Kim
update_gic_base(MIPSGCRState * gcr,uint64_t val)5919494f81SLeon Alrae static inline void update_gic_base(MIPSGCRState *gcr, uint64_t val)
6019494f81SLeon Alrae {
6119494f81SLeon Alrae if (is_gic_connected(gcr)) {
6219494f81SLeon Alrae gcr->gic_base = val & GCR_GIC_BASE_MSK;
6319494f81SLeon Alrae memory_region_transaction_begin();
6419494f81SLeon Alrae memory_region_set_address(gcr->gic_mr,
6519494f81SLeon Alrae gcr->gic_base & GCR_GIC_BASE_GICBASE_MSK);
6619494f81SLeon Alrae memory_region_set_enabled(gcr->gic_mr,
6719494f81SLeon Alrae gcr->gic_base & GCR_GIC_BASE_GICEN_MSK);
6819494f81SLeon Alrae memory_region_transaction_commit();
6919494f81SLeon Alrae }
7019494f81SLeon Alrae }
7119494f81SLeon Alrae
723994215dSYongbok Kim /* Read GCR registers */
gcr_read(void * opaque,hwaddr addr,unsigned size)733994215dSYongbok Kim static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size)
743994215dSYongbok Kim {
753994215dSYongbok Kim MIPSGCRState *gcr = (MIPSGCRState *) opaque;
76c09199feSLeon Alrae MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index];
77c09199feSLeon Alrae MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
783994215dSYongbok Kim
793994215dSYongbok Kim switch (addr) {
803994215dSYongbok Kim /* Global Control Block Register */
813994215dSYongbok Kim case GCR_CONFIG_OFS:
823994215dSYongbok Kim /* Set PCORES to 0 */
833994215dSYongbok Kim return 0;
843994215dSYongbok Kim case GCR_BASE_OFS:
853994215dSYongbok Kim return gcr->gcr_base;
863994215dSYongbok Kim case GCR_REV_OFS:
873994215dSYongbok Kim return gcr->gcr_rev;
8819494f81SLeon Alrae case GCR_GIC_BASE_OFS:
8919494f81SLeon Alrae return gcr->gic_base;
902edd5261SLeon Alrae case GCR_CPC_BASE_OFS:
912edd5261SLeon Alrae return gcr->cpc_base;
9219494f81SLeon Alrae case GCR_GIC_STATUS_OFS:
9319494f81SLeon Alrae return is_gic_connected(gcr);
942edd5261SLeon Alrae case GCR_CPC_STATUS_OFS:
952edd5261SLeon Alrae return is_cpc_connected(gcr);
963994215dSYongbok Kim case GCR_L2_CONFIG_OFS:
973994215dSYongbok Kim /* L2 BYPASS */
983994215dSYongbok Kim return GCR_L2_CONFIG_BYPASS_MSK;
993994215dSYongbok Kim /* Core-Local and Core-Other Control Blocks */
1003994215dSYongbok Kim case MIPS_CLCB_OFS + GCR_CL_CONFIG_OFS:
1013994215dSYongbok Kim case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS:
1023994215dSYongbok Kim /* Set PVP to # of VPs - 1 */
1033994215dSYongbok Kim return gcr->num_vps - 1;
104c09199feSLeon Alrae case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS:
105c09199feSLeon Alrae return current_vps->reset_base;
106c09199feSLeon Alrae case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS:
107c09199feSLeon Alrae return other_vps->reset_base;
1083994215dSYongbok Kim case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
109c09199feSLeon Alrae return current_vps->other;
110c09199feSLeon Alrae case MIPS_COCB_OFS + GCR_CL_OTHER_OFS:
111c09199feSLeon Alrae return other_vps->other;
1123994215dSYongbok Kim default:
1133994215dSYongbok Kim qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx
1143994215dSYongbok Kim "\n", size, addr);
1153994215dSYongbok Kim return 0;
1163994215dSYongbok Kim }
1173994215dSYongbok Kim return 0;
1183994215dSYongbok Kim }
1193994215dSYongbok Kim
get_exception_base(MIPSGCRVPState * vps)120c09199feSLeon Alrae static inline target_ulong get_exception_base(MIPSGCRVPState *vps)
121c09199feSLeon Alrae {
122c09199feSLeon Alrae /* TODO: BEV_BASE and SELECT_BEV */
123c09199feSLeon Alrae return (int32_t)(vps->reset_base & GCR_CL_RESET_BASE_RESETBASE_MSK);
124c09199feSLeon Alrae }
125c09199feSLeon Alrae
1263994215dSYongbok Kim /* Write GCR registers */
gcr_write(void * opaque,hwaddr addr,uint64_t data,unsigned size)1273994215dSYongbok Kim static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
1283994215dSYongbok Kim {
1292edd5261SLeon Alrae MIPSGCRState *gcr = (MIPSGCRState *)opaque;
130c09199feSLeon Alrae MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index];
131c09199feSLeon Alrae MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
1322edd5261SLeon Alrae
1333994215dSYongbok Kim switch (addr) {
13408944be1SPaul Burton case GCR_BASE_OFS:
13508944be1SPaul Burton update_gcr_base(gcr, data);
13608944be1SPaul Burton break;
13719494f81SLeon Alrae case GCR_GIC_BASE_OFS:
13819494f81SLeon Alrae update_gic_base(gcr, data);
13919494f81SLeon Alrae break;
1402edd5261SLeon Alrae case GCR_CPC_BASE_OFS:
1412edd5261SLeon Alrae update_cpc_base(gcr, data);
1422edd5261SLeon Alrae break;
143c09199feSLeon Alrae case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS:
144c09199feSLeon Alrae current_vps->reset_base = data & GCR_CL_RESET_BASE_MSK;
145c09199feSLeon Alrae cpu_set_exception_base(current_cpu->cpu_index,
146c09199feSLeon Alrae get_exception_base(current_vps));
147c09199feSLeon Alrae break;
148c09199feSLeon Alrae case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS:
149c09199feSLeon Alrae other_vps->reset_base = data & GCR_CL_RESET_BASE_MSK;
150c09199feSLeon Alrae cpu_set_exception_base(current_vps->other,
151c09199feSLeon Alrae get_exception_base(other_vps));
152c09199feSLeon Alrae break;
153c09199feSLeon Alrae case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
154c09199feSLeon Alrae if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) {
155c09199feSLeon Alrae current_vps->other = data & GCR_CL_OTHER_MSK;
156c09199feSLeon Alrae }
157c09199feSLeon Alrae break;
158c09199feSLeon Alrae case MIPS_COCB_OFS + GCR_CL_OTHER_OFS:
159c09199feSLeon Alrae if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) {
160c09199feSLeon Alrae other_vps->other = data & GCR_CL_OTHER_MSK;
161c09199feSLeon Alrae }
162c09199feSLeon Alrae break;
1633994215dSYongbok Kim default:
1643994215dSYongbok Kim qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx
1653994215dSYongbok Kim " 0x%" PRIx64 "\n", size, addr, data);
1663994215dSYongbok Kim break;
1673994215dSYongbok Kim }
1683994215dSYongbok Kim }
1693994215dSYongbok Kim
1703994215dSYongbok Kim static const MemoryRegionOps gcr_ops = {
1713994215dSYongbok Kim .read = gcr_read,
1723994215dSYongbok Kim .write = gcr_write,
1733994215dSYongbok Kim .endianness = DEVICE_NATIVE_ENDIAN,
1743994215dSYongbok Kim .impl = {
1753994215dSYongbok Kim .max_access_size = 8,
1763994215dSYongbok Kim },
1773994215dSYongbok Kim };
1783994215dSYongbok Kim
mips_gcr_init(Object * obj)1793994215dSYongbok Kim static void mips_gcr_init(Object *obj)
1803994215dSYongbok Kim {
1813994215dSYongbok Kim SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1823994215dSYongbok Kim MIPSGCRState *s = MIPS_GCR(obj);
1833994215dSYongbok Kim
1843994215dSYongbok Kim memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s,
1853994215dSYongbok Kim "mips-gcr", GCR_ADDRSPACE_SZ);
1863994215dSYongbok Kim sysbus_init_mmio(sbd, &s->iomem);
1873994215dSYongbok Kim }
1883994215dSYongbok Kim
mips_gcr_reset(DeviceState * dev)1892edd5261SLeon Alrae static void mips_gcr_reset(DeviceState *dev)
1902edd5261SLeon Alrae {
1912edd5261SLeon Alrae MIPSGCRState *s = MIPS_GCR(dev);
192c09199feSLeon Alrae int i;
1932edd5261SLeon Alrae
19419494f81SLeon Alrae update_gic_base(s, 0);
1952edd5261SLeon Alrae update_cpc_base(s, 0);
196c09199feSLeon Alrae
197c09199feSLeon Alrae for (i = 0; i < s->num_vps; i++) {
198c09199feSLeon Alrae s->vps[i].other = 0;
199c09199feSLeon Alrae s->vps[i].reset_base = 0xBFC00000 & GCR_CL_RESET_BASE_MSK;
200c09199feSLeon Alrae cpu_set_exception_base(i, get_exception_base(&s->vps[i]));
201c09199feSLeon Alrae }
2022edd5261SLeon Alrae }
2032edd5261SLeon Alrae
2042edd5261SLeon Alrae static const VMStateDescription vmstate_mips_gcr = {
2052edd5261SLeon Alrae .name = "mips-gcr",
2062edd5261SLeon Alrae .version_id = 0,
2072edd5261SLeon Alrae .minimum_version_id = 0,
208e4ea952fSRichard Henderson .fields = (const VMStateField[]) {
2092edd5261SLeon Alrae VMSTATE_UINT64(cpc_base, MIPSGCRState),
2102edd5261SLeon Alrae VMSTATE_END_OF_LIST()
2112edd5261SLeon Alrae },
2122edd5261SLeon Alrae };
2132edd5261SLeon Alrae
2143994215dSYongbok Kim static Property mips_gcr_properties[] = {
21510997f2dSPhilippe Mathieu-Daudé DEFINE_PROP_UINT32("num-vp", MIPSGCRState, num_vps, 1),
2163994215dSYongbok Kim DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800),
2173994215dSYongbok Kim DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR),
218e4934bb3SFam Zheng DEFINE_PROP_LINK("gic", MIPSGCRState, gic_mr, TYPE_MEMORY_REGION,
219e4934bb3SFam Zheng MemoryRegion *),
220e4934bb3SFam Zheng DEFINE_PROP_LINK("cpc", MIPSGCRState, cpc_mr, TYPE_MEMORY_REGION,
221e4934bb3SFam Zheng MemoryRegion *),
2223994215dSYongbok Kim DEFINE_PROP_END_OF_LIST(),
2233994215dSYongbok Kim };
2243994215dSYongbok Kim
mips_gcr_realize(DeviceState * dev,Error ** errp)225c09199feSLeon Alrae static void mips_gcr_realize(DeviceState *dev, Error **errp)
226c09199feSLeon Alrae {
227c09199feSLeon Alrae MIPSGCRState *s = MIPS_GCR(dev);
228c09199feSLeon Alrae
229c09199feSLeon Alrae /* Create local set of registers for each VP */
230c09199feSLeon Alrae s->vps = g_new(MIPSGCRVPState, s->num_vps);
231c09199feSLeon Alrae }
232c09199feSLeon Alrae
mips_gcr_class_init(ObjectClass * klass,void * data)2333994215dSYongbok Kim static void mips_gcr_class_init(ObjectClass *klass, void *data)
2343994215dSYongbok Kim {
2353994215dSYongbok Kim DeviceClass *dc = DEVICE_CLASS(klass);
2364f67d30bSMarc-André Lureau device_class_set_props(dc, mips_gcr_properties);
2372edd5261SLeon Alrae dc->vmsd = &vmstate_mips_gcr;
238*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, mips_gcr_reset);
239c09199feSLeon Alrae dc->realize = mips_gcr_realize;
2403994215dSYongbok Kim }
2413994215dSYongbok Kim
2423994215dSYongbok Kim static const TypeInfo mips_gcr_info = {
2433994215dSYongbok Kim .name = TYPE_MIPS_GCR,
2443994215dSYongbok Kim .parent = TYPE_SYS_BUS_DEVICE,
2453994215dSYongbok Kim .instance_size = sizeof(MIPSGCRState),
2463994215dSYongbok Kim .instance_init = mips_gcr_init,
2473994215dSYongbok Kim .class_init = mips_gcr_class_init,
2483994215dSYongbok Kim };
2493994215dSYongbok Kim
mips_gcr_register_types(void)2503994215dSYongbok Kim static void mips_gcr_register_types(void)
2513994215dSYongbok Kim {
2523994215dSYongbok Kim type_register_static(&mips_gcr_info);
2533994215dSYongbok Kim }
2543994215dSYongbok Kim
2553994215dSYongbok Kim type_init(mips_gcr_register_types)
256