xref: /openbmc/qemu/hw/misc/mips_cmgcr.c (revision d6454270575da1f16a8923c7cb240e46ef243f72)
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/hw.h"
163994215dSYongbok Kim #include "hw/sysbus.h"
17*d6454270SMarkus Armbruster #include "migration/vmstate.h"
183994215dSYongbok Kim #include "sysemu/sysemu.h"
193994215dSYongbok Kim #include "hw/misc/mips_cmgcr.h"
202edd5261SLeon Alrae #include "hw/misc/mips_cpc.h"
2119494f81SLeon Alrae #include "hw/intc/mips_gic.h"
222edd5261SLeon Alrae 
232edd5261SLeon Alrae static inline bool is_cpc_connected(MIPSGCRState *s)
242edd5261SLeon Alrae {
252edd5261SLeon Alrae     return s->cpc_mr != NULL;
262edd5261SLeon Alrae }
272edd5261SLeon Alrae 
2819494f81SLeon Alrae static inline bool is_gic_connected(MIPSGCRState *s)
2919494f81SLeon Alrae {
3019494f81SLeon Alrae     return s->gic_mr != NULL;
3119494f81SLeon Alrae }
3219494f81SLeon Alrae 
3308944be1SPaul Burton static inline void update_gcr_base(MIPSGCRState *gcr, uint64_t val)
3408944be1SPaul Burton {
3508944be1SPaul Burton     CPUState *cpu;
3608944be1SPaul Burton     MIPSCPU *mips_cpu;
3708944be1SPaul Burton 
3808944be1SPaul Burton     gcr->gcr_base = val & GCR_BASE_GCRBASE_MSK;
3908944be1SPaul Burton     memory_region_set_address(&gcr->iomem, gcr->gcr_base);
4008944be1SPaul Burton 
4108944be1SPaul Burton     CPU_FOREACH(cpu) {
4208944be1SPaul Burton         mips_cpu = MIPS_CPU(cpu);
4308944be1SPaul Burton         mips_cpu->env.CP0_CMGCRBase = gcr->gcr_base >> 4;
4408944be1SPaul Burton     }
4508944be1SPaul Burton }
4608944be1SPaul Burton 
472edd5261SLeon Alrae static inline void update_cpc_base(MIPSGCRState *gcr, uint64_t val)
482edd5261SLeon Alrae {
492edd5261SLeon Alrae     if (is_cpc_connected(gcr)) {
502edd5261SLeon Alrae         gcr->cpc_base = val & GCR_CPC_BASE_MSK;
512edd5261SLeon Alrae         memory_region_transaction_begin();
522edd5261SLeon Alrae         memory_region_set_address(gcr->cpc_mr,
532edd5261SLeon Alrae                                   gcr->cpc_base & GCR_CPC_BASE_CPCBASE_MSK);
542edd5261SLeon Alrae         memory_region_set_enabled(gcr->cpc_mr,
552edd5261SLeon Alrae                                   gcr->cpc_base & GCR_CPC_BASE_CPCEN_MSK);
562edd5261SLeon Alrae         memory_region_transaction_commit();
572edd5261SLeon Alrae     }
582edd5261SLeon Alrae }
593994215dSYongbok Kim 
6019494f81SLeon Alrae static inline void update_gic_base(MIPSGCRState *gcr, uint64_t val)
6119494f81SLeon Alrae {
6219494f81SLeon Alrae     if (is_gic_connected(gcr)) {
6319494f81SLeon Alrae         gcr->gic_base = val & GCR_GIC_BASE_MSK;
6419494f81SLeon Alrae         memory_region_transaction_begin();
6519494f81SLeon Alrae         memory_region_set_address(gcr->gic_mr,
6619494f81SLeon Alrae                                   gcr->gic_base & GCR_GIC_BASE_GICBASE_MSK);
6719494f81SLeon Alrae         memory_region_set_enabled(gcr->gic_mr,
6819494f81SLeon Alrae                                   gcr->gic_base & GCR_GIC_BASE_GICEN_MSK);
6919494f81SLeon Alrae         memory_region_transaction_commit();
7019494f81SLeon Alrae     }
7119494f81SLeon Alrae }
7219494f81SLeon Alrae 
733994215dSYongbok Kim /* Read GCR registers */
743994215dSYongbok Kim static uint64_t gcr_read(void *opaque, hwaddr addr, unsigned size)
753994215dSYongbok Kim {
763994215dSYongbok Kim     MIPSGCRState *gcr = (MIPSGCRState *) opaque;
77c09199feSLeon Alrae     MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index];
78c09199feSLeon Alrae     MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
793994215dSYongbok Kim 
803994215dSYongbok Kim     switch (addr) {
813994215dSYongbok Kim     /* Global Control Block Register */
823994215dSYongbok Kim     case GCR_CONFIG_OFS:
833994215dSYongbok Kim         /* Set PCORES to 0 */
843994215dSYongbok Kim         return 0;
853994215dSYongbok Kim     case GCR_BASE_OFS:
863994215dSYongbok Kim         return gcr->gcr_base;
873994215dSYongbok Kim     case GCR_REV_OFS:
883994215dSYongbok Kim         return gcr->gcr_rev;
8919494f81SLeon Alrae     case GCR_GIC_BASE_OFS:
9019494f81SLeon Alrae         return gcr->gic_base;
912edd5261SLeon Alrae     case GCR_CPC_BASE_OFS:
922edd5261SLeon Alrae         return gcr->cpc_base;
9319494f81SLeon Alrae     case GCR_GIC_STATUS_OFS:
9419494f81SLeon Alrae         return is_gic_connected(gcr);
952edd5261SLeon Alrae     case GCR_CPC_STATUS_OFS:
962edd5261SLeon Alrae         return is_cpc_connected(gcr);
973994215dSYongbok Kim     case GCR_L2_CONFIG_OFS:
983994215dSYongbok Kim         /* L2 BYPASS */
993994215dSYongbok Kim         return GCR_L2_CONFIG_BYPASS_MSK;
1003994215dSYongbok Kim         /* Core-Local and Core-Other Control Blocks */
1013994215dSYongbok Kim     case MIPS_CLCB_OFS + GCR_CL_CONFIG_OFS:
1023994215dSYongbok Kim     case MIPS_COCB_OFS + GCR_CL_CONFIG_OFS:
1033994215dSYongbok Kim         /* Set PVP to # of VPs - 1 */
1043994215dSYongbok Kim         return gcr->num_vps - 1;
105c09199feSLeon Alrae     case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS:
106c09199feSLeon Alrae         return current_vps->reset_base;
107c09199feSLeon Alrae     case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS:
108c09199feSLeon Alrae         return other_vps->reset_base;
1093994215dSYongbok Kim     case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
110c09199feSLeon Alrae         return current_vps->other;
111c09199feSLeon Alrae     case MIPS_COCB_OFS + GCR_CL_OTHER_OFS:
112c09199feSLeon Alrae         return other_vps->other;
1133994215dSYongbok Kim     default:
1143994215dSYongbok Kim         qemu_log_mask(LOG_UNIMP, "Read %d bytes at GCR offset 0x%" HWADDR_PRIx
1153994215dSYongbok Kim                       "\n", size, addr);
1163994215dSYongbok Kim         return 0;
1173994215dSYongbok Kim     }
1183994215dSYongbok Kim     return 0;
1193994215dSYongbok Kim }
1203994215dSYongbok Kim 
121c09199feSLeon Alrae static inline target_ulong get_exception_base(MIPSGCRVPState *vps)
122c09199feSLeon Alrae {
123c09199feSLeon Alrae     /* TODO: BEV_BASE and SELECT_BEV */
124c09199feSLeon Alrae     return (int32_t)(vps->reset_base & GCR_CL_RESET_BASE_RESETBASE_MSK);
125c09199feSLeon Alrae }
126c09199feSLeon Alrae 
1273994215dSYongbok Kim /* Write GCR registers */
1283994215dSYongbok Kim static void gcr_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
1293994215dSYongbok Kim {
1302edd5261SLeon Alrae     MIPSGCRState *gcr = (MIPSGCRState *)opaque;
131c09199feSLeon Alrae     MIPSGCRVPState *current_vps = &gcr->vps[current_cpu->cpu_index];
132c09199feSLeon Alrae     MIPSGCRVPState *other_vps = &gcr->vps[current_vps->other];
1332edd5261SLeon Alrae 
1343994215dSYongbok Kim     switch (addr) {
13508944be1SPaul Burton     case GCR_BASE_OFS:
13608944be1SPaul Burton         update_gcr_base(gcr, data);
13708944be1SPaul Burton         break;
13819494f81SLeon Alrae     case GCR_GIC_BASE_OFS:
13919494f81SLeon Alrae         update_gic_base(gcr, data);
14019494f81SLeon Alrae         break;
1412edd5261SLeon Alrae     case GCR_CPC_BASE_OFS:
1422edd5261SLeon Alrae         update_cpc_base(gcr, data);
1432edd5261SLeon Alrae         break;
144c09199feSLeon Alrae     case MIPS_CLCB_OFS + GCR_CL_RESETBASE_OFS:
145c09199feSLeon Alrae         current_vps->reset_base = data & GCR_CL_RESET_BASE_MSK;
146c09199feSLeon Alrae         cpu_set_exception_base(current_cpu->cpu_index,
147c09199feSLeon Alrae                                get_exception_base(current_vps));
148c09199feSLeon Alrae         break;
149c09199feSLeon Alrae     case MIPS_COCB_OFS + GCR_CL_RESETBASE_OFS:
150c09199feSLeon Alrae         other_vps->reset_base = data & GCR_CL_RESET_BASE_MSK;
151c09199feSLeon Alrae         cpu_set_exception_base(current_vps->other,
152c09199feSLeon Alrae                                get_exception_base(other_vps));
153c09199feSLeon Alrae         break;
154c09199feSLeon Alrae     case MIPS_CLCB_OFS + GCR_CL_OTHER_OFS:
155c09199feSLeon Alrae         if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) {
156c09199feSLeon Alrae             current_vps->other = data & GCR_CL_OTHER_MSK;
157c09199feSLeon Alrae         }
158c09199feSLeon Alrae         break;
159c09199feSLeon Alrae     case MIPS_COCB_OFS + GCR_CL_OTHER_OFS:
160c09199feSLeon Alrae         if ((data & GCR_CL_OTHER_MSK) < gcr->num_vps) {
161c09199feSLeon Alrae             other_vps->other = data & GCR_CL_OTHER_MSK;
162c09199feSLeon Alrae         }
163c09199feSLeon Alrae         break;
1643994215dSYongbok Kim     default:
1653994215dSYongbok Kim         qemu_log_mask(LOG_UNIMP, "Write %d bytes at GCR offset 0x%" HWADDR_PRIx
1663994215dSYongbok Kim                       " 0x%" PRIx64 "\n", size, addr, data);
1673994215dSYongbok Kim         break;
1683994215dSYongbok Kim     }
1693994215dSYongbok Kim }
1703994215dSYongbok Kim 
1713994215dSYongbok Kim static const MemoryRegionOps gcr_ops = {
1723994215dSYongbok Kim     .read = gcr_read,
1733994215dSYongbok Kim     .write = gcr_write,
1743994215dSYongbok Kim     .endianness = DEVICE_NATIVE_ENDIAN,
1753994215dSYongbok Kim     .impl = {
1763994215dSYongbok Kim         .max_access_size = 8,
1773994215dSYongbok Kim     },
1783994215dSYongbok Kim };
1793994215dSYongbok Kim 
1803994215dSYongbok Kim static void mips_gcr_init(Object *obj)
1813994215dSYongbok Kim {
1823994215dSYongbok Kim     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1833994215dSYongbok Kim     MIPSGCRState *s = MIPS_GCR(obj);
1843994215dSYongbok Kim 
1853994215dSYongbok Kim     memory_region_init_io(&s->iomem, OBJECT(s), &gcr_ops, s,
1863994215dSYongbok Kim                           "mips-gcr", GCR_ADDRSPACE_SZ);
1873994215dSYongbok Kim     sysbus_init_mmio(sbd, &s->iomem);
1883994215dSYongbok Kim }
1893994215dSYongbok Kim 
1902edd5261SLeon Alrae static void mips_gcr_reset(DeviceState *dev)
1912edd5261SLeon Alrae {
1922edd5261SLeon Alrae     MIPSGCRState *s = MIPS_GCR(dev);
193c09199feSLeon Alrae     int i;
1942edd5261SLeon Alrae 
19519494f81SLeon Alrae     update_gic_base(s, 0);
1962edd5261SLeon Alrae     update_cpc_base(s, 0);
197c09199feSLeon Alrae 
198c09199feSLeon Alrae     for (i = 0; i < s->num_vps; i++) {
199c09199feSLeon Alrae         s->vps[i].other = 0;
200c09199feSLeon Alrae         s->vps[i].reset_base = 0xBFC00000 & GCR_CL_RESET_BASE_MSK;
201c09199feSLeon Alrae         cpu_set_exception_base(i, get_exception_base(&s->vps[i]));
202c09199feSLeon Alrae     }
2032edd5261SLeon Alrae }
2042edd5261SLeon Alrae 
2052edd5261SLeon Alrae static const VMStateDescription vmstate_mips_gcr = {
2062edd5261SLeon Alrae     .name = "mips-gcr",
2072edd5261SLeon Alrae     .version_id = 0,
2082edd5261SLeon Alrae     .minimum_version_id = 0,
2092edd5261SLeon Alrae     .fields = (VMStateField[]) {
2102edd5261SLeon Alrae         VMSTATE_UINT64(cpc_base, MIPSGCRState),
2112edd5261SLeon Alrae         VMSTATE_END_OF_LIST()
2122edd5261SLeon Alrae     },
2132edd5261SLeon Alrae };
2142edd5261SLeon Alrae 
2153994215dSYongbok Kim static Property mips_gcr_properties[] = {
2163994215dSYongbok Kim     DEFINE_PROP_INT32("num-vp", MIPSGCRState, num_vps, 1),
2173994215dSYongbok Kim     DEFINE_PROP_INT32("gcr-rev", MIPSGCRState, gcr_rev, 0x800),
2183994215dSYongbok Kim     DEFINE_PROP_UINT64("gcr-base", MIPSGCRState, gcr_base, GCR_BASE_ADDR),
219e4934bb3SFam Zheng     DEFINE_PROP_LINK("gic", MIPSGCRState, gic_mr, TYPE_MEMORY_REGION,
220e4934bb3SFam Zheng                      MemoryRegion *),
221e4934bb3SFam Zheng     DEFINE_PROP_LINK("cpc", MIPSGCRState, cpc_mr, TYPE_MEMORY_REGION,
222e4934bb3SFam Zheng                      MemoryRegion *),
2233994215dSYongbok Kim     DEFINE_PROP_END_OF_LIST(),
2243994215dSYongbok Kim };
2253994215dSYongbok Kim 
226c09199feSLeon Alrae static void mips_gcr_realize(DeviceState *dev, Error **errp)
227c09199feSLeon Alrae {
228c09199feSLeon Alrae     MIPSGCRState *s = MIPS_GCR(dev);
229c09199feSLeon Alrae 
230c09199feSLeon Alrae     /* Create local set of registers for each VP */
231c09199feSLeon Alrae     s->vps = g_new(MIPSGCRVPState, s->num_vps);
232c09199feSLeon Alrae }
233c09199feSLeon Alrae 
2343994215dSYongbok Kim static void mips_gcr_class_init(ObjectClass *klass, void *data)
2353994215dSYongbok Kim {
2363994215dSYongbok Kim     DeviceClass *dc = DEVICE_CLASS(klass);
2373994215dSYongbok Kim     dc->props = mips_gcr_properties;
2382edd5261SLeon Alrae     dc->vmsd = &vmstate_mips_gcr;
2392edd5261SLeon Alrae     dc->reset = mips_gcr_reset;
240c09199feSLeon Alrae     dc->realize = mips_gcr_realize;
2413994215dSYongbok Kim }
2423994215dSYongbok Kim 
2433994215dSYongbok Kim static const TypeInfo mips_gcr_info = {
2443994215dSYongbok Kim     .name          = TYPE_MIPS_GCR,
2453994215dSYongbok Kim     .parent        = TYPE_SYS_BUS_DEVICE,
2463994215dSYongbok Kim     .instance_size = sizeof(MIPSGCRState),
2473994215dSYongbok Kim     .instance_init = mips_gcr_init,
2483994215dSYongbok Kim     .class_init    = mips_gcr_class_init,
2493994215dSYongbok Kim };
2503994215dSYongbok Kim 
2513994215dSYongbok Kim static void mips_gcr_register_types(void)
2523994215dSYongbok Kim {
2533994215dSYongbok Kim     type_register_static(&mips_gcr_info);
2543994215dSYongbok Kim }
2553994215dSYongbok Kim 
2563994215dSYongbok Kim type_init(mips_gcr_register_types)
257