xref: /openbmc/qemu/hw/ppc/ppce500_spin.c (revision 6b829602e2f10f301ff8508f3a6850a0e913142c)
1c68c4a56SPaolo Bonzini /*
2c68c4a56SPaolo Bonzini  * QEMU PowerPC e500v2 ePAPR spinning code
3c68c4a56SPaolo Bonzini  *
4c68c4a56SPaolo Bonzini  * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved.
5c68c4a56SPaolo Bonzini  *
6c68c4a56SPaolo Bonzini  * Author: Alexander Graf, <agraf@suse.de>
7c68c4a56SPaolo Bonzini  *
8c68c4a56SPaolo Bonzini  * This library is free software; you can redistribute it and/or
9c68c4a56SPaolo Bonzini  * modify it under the terms of the GNU Lesser General Public
10c68c4a56SPaolo Bonzini  * License as published by the Free Software Foundation; either
116bd039cdSChetan Pant  * version 2.1 of the License, or (at your option) any later version.
12c68c4a56SPaolo Bonzini  *
13c68c4a56SPaolo Bonzini  * This library is distributed in the hope that it will be useful,
14c68c4a56SPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15c68c4a56SPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16c68c4a56SPaolo Bonzini  * Lesser General Public License for more details.
17c68c4a56SPaolo Bonzini  *
18c68c4a56SPaolo Bonzini  * You should have received a copy of the GNU Lesser General Public
19c68c4a56SPaolo Bonzini  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20c68c4a56SPaolo Bonzini  *
21c68c4a56SPaolo Bonzini  * This code is not really a device, but models an interface that usually
22c68c4a56SPaolo Bonzini  * firmware takes care of. It's used when QEMU plays the role of firmware.
23c68c4a56SPaolo Bonzini  *
24c68c4a56SPaolo Bonzini  * Specification:
25c68c4a56SPaolo Bonzini  *
26c68c4a56SPaolo Bonzini  * https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf
27c68c4a56SPaolo Bonzini  *
28c68c4a56SPaolo Bonzini  */
29c68c4a56SPaolo Bonzini 
300d75590dSPeter Maydell #include "qemu/osdep.h"
310b8fa32fSMarkus Armbruster #include "qemu/module.h"
32ab3dd749SPhilippe Mathieu-Daudé #include "qemu/units.h"
33c68c4a56SPaolo Bonzini #include "hw/hw.h"
34c68c4a56SPaolo Bonzini #include "hw/sysbus.h"
35b3946626SVincent Palatin #include "sysemu/hw_accel.h"
36*779a30dfSBALATON Zoltan #include "hw/ppc/ppc.h"
37a36848ffSAaron Larson #include "e500.h"
38db1015e9SEduardo Habkost #include "qom/object.h"
39c68c4a56SPaolo Bonzini 
40c68c4a56SPaolo Bonzini #define MAX_CPUS 32
41c68c4a56SPaolo Bonzini 
42c68c4a56SPaolo Bonzini typedef struct spin_info {
43c68c4a56SPaolo Bonzini     uint64_t addr;
44c68c4a56SPaolo Bonzini     uint64_t r3;
45c68c4a56SPaolo Bonzini     uint32_t resv;
46c68c4a56SPaolo Bonzini     uint32_t pir;
47c68c4a56SPaolo Bonzini     uint64_t reserved;
48c68c4a56SPaolo Bonzini } QEMU_PACKED SpinInfo;
49c68c4a56SPaolo Bonzini 
50880fc798SAndreas Färber #define TYPE_E500_SPIN "e500-spin"
518063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(SpinState, E500_SPIN)
52880fc798SAndreas Färber 
53db1015e9SEduardo Habkost struct SpinState {
54880fc798SAndreas Färber     SysBusDevice parent_obj;
55880fc798SAndreas Färber 
56c68c4a56SPaolo Bonzini     MemoryRegion iomem;
57c68c4a56SPaolo Bonzini     SpinInfo spin[MAX_CPUS];
58db1015e9SEduardo Habkost };
59c68c4a56SPaolo Bonzini 
spin_reset(DeviceState * dev)6009a7eb97Sxiaoqiang zhao static void spin_reset(DeviceState *dev)
61c68c4a56SPaolo Bonzini {
6209a7eb97Sxiaoqiang zhao     SpinState *s = E500_SPIN(dev);
63c68c4a56SPaolo Bonzini     int i;
64c68c4a56SPaolo Bonzini 
65c68c4a56SPaolo Bonzini     for (i = 0; i < MAX_CPUS; i++) {
66c68c4a56SPaolo Bonzini         SpinInfo *info = &s->spin[i];
67c68c4a56SPaolo Bonzini 
686a2b3d89SAlexander Graf         stl_p(&info->pir, i);
696a2b3d89SAlexander Graf         stq_p(&info->r3, i);
706a2b3d89SAlexander Graf         stq_p(&info->addr, 1);
71c68c4a56SPaolo Bonzini     }
72c68c4a56SPaolo Bonzini }
73c68c4a56SPaolo Bonzini 
spin_kick(CPUState * cs,run_on_cpu_data data)7414e6fe12SPaolo Bonzini static void spin_kick(CPUState *cs, run_on_cpu_data data)
75c68c4a56SPaolo Bonzini {
76794511bcSPhilippe Mathieu-Daudé     CPUPPCState *env = cpu_env(cs);
7714e6fe12SPaolo Bonzini     SpinInfo *curspin = data.host_ptr;
78*779a30dfSBALATON Zoltan     hwaddr map_start, map_size = 64 * MiB;
79*779a30dfSBALATON Zoltan     ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1);
80c68c4a56SPaolo Bonzini 
81e0eeb4a2SAlex Bennée     cpu_synchronize_state(cs);
826d18a7a1SAaron Larson     stl_p(&curspin->pir, env->spr[SPR_BOOKE_PIR]);
83c68c4a56SPaolo Bonzini     env->nip = ldq_p(&curspin->addr) & (map_size - 1);
84c68c4a56SPaolo Bonzini     env->gpr[3] = ldq_p(&curspin->r3);
85c68c4a56SPaolo Bonzini     env->gpr[4] = 0;
86c68c4a56SPaolo Bonzini     env->gpr[5] = 0;
87c68c4a56SPaolo Bonzini     env->gpr[6] = 0;
88c68c4a56SPaolo Bonzini     env->gpr[7] = map_size;
89c68c4a56SPaolo Bonzini     env->gpr[8] = 0;
90c68c4a56SPaolo Bonzini     env->gpr[9] = 0;
91c68c4a56SPaolo Bonzini 
92c68c4a56SPaolo Bonzini     map_start = ldq_p(&curspin->addr) & ~(map_size - 1);
93*779a30dfSBALATON Zoltan     /* create initial mapping */
94*779a30dfSBALATON Zoltan     booke206_set_tlb(tlb, 0, map_start, map_size);
95*779a30dfSBALATON Zoltan     tlb->mas2 |= MAS2_M;
96*779a30dfSBALATON Zoltan #ifdef CONFIG_KVM
97*779a30dfSBALATON Zoltan     env->tlb_dirty = true;
98*779a30dfSBALATON Zoltan #endif
99c68c4a56SPaolo Bonzini 
100e0eeb4a2SAlex Bennée     cs->halted = 0;
101e0eeb4a2SAlex Bennée     cs->exception_index = -1;
102e0eeb4a2SAlex Bennée     cs->stopped = false;
103e0eeb4a2SAlex Bennée     qemu_cpu_kick(cs);
104c68c4a56SPaolo Bonzini }
105c68c4a56SPaolo Bonzini 
spin_write(void * opaque,hwaddr addr,uint64_t value,unsigned len)106c68c4a56SPaolo Bonzini static void spin_write(void *opaque, hwaddr addr, uint64_t value,
107c68c4a56SPaolo Bonzini                        unsigned len)
108c68c4a56SPaolo Bonzini {
109c68c4a56SPaolo Bonzini     SpinState *s = opaque;
110c68c4a56SPaolo Bonzini     int env_idx = addr / sizeof(SpinInfo);
111c68c4a56SPaolo Bonzini     CPUState *cpu;
112c68c4a56SPaolo Bonzini     SpinInfo *curspin = &s->spin[env_idx];
113c68c4a56SPaolo Bonzini     uint8_t *curspin_p = (uint8_t*)curspin;
114c68c4a56SPaolo Bonzini 
115c68c4a56SPaolo Bonzini     cpu = qemu_get_cpu(env_idx);
116c68c4a56SPaolo Bonzini     if (cpu == NULL) {
117c68c4a56SPaolo Bonzini         /* Unknown CPU */
118c68c4a56SPaolo Bonzini         return;
119c68c4a56SPaolo Bonzini     }
120c68c4a56SPaolo Bonzini 
121c68c4a56SPaolo Bonzini     if (cpu->cpu_index == 0) {
122c68c4a56SPaolo Bonzini         /* primary CPU doesn't spin */
123c68c4a56SPaolo Bonzini         return;
124c68c4a56SPaolo Bonzini     }
125c68c4a56SPaolo Bonzini 
126c68c4a56SPaolo Bonzini     curspin_p = &curspin_p[addr % sizeof(SpinInfo)];
127c68c4a56SPaolo Bonzini     switch (len) {
128c68c4a56SPaolo Bonzini     case 1:
129c68c4a56SPaolo Bonzini         stb_p(curspin_p, value);
130c68c4a56SPaolo Bonzini         break;
131c68c4a56SPaolo Bonzini     case 2:
132c68c4a56SPaolo Bonzini         stw_p(curspin_p, value);
133c68c4a56SPaolo Bonzini         break;
134c68c4a56SPaolo Bonzini     case 4:
135c68c4a56SPaolo Bonzini         stl_p(curspin_p, value);
136c68c4a56SPaolo Bonzini         break;
137c68c4a56SPaolo Bonzini     }
138c68c4a56SPaolo Bonzini 
139c68c4a56SPaolo Bonzini     if (!(ldq_p(&curspin->addr) & 1)) {
140c68c4a56SPaolo Bonzini         /* run CPU */
14114e6fe12SPaolo Bonzini         run_on_cpu(cpu, spin_kick, RUN_ON_CPU_HOST_PTR(curspin));
142c68c4a56SPaolo Bonzini     }
143c68c4a56SPaolo Bonzini }
144c68c4a56SPaolo Bonzini 
spin_read(void * opaque,hwaddr addr,unsigned len)145c68c4a56SPaolo Bonzini static uint64_t spin_read(void *opaque, hwaddr addr, unsigned len)
146c68c4a56SPaolo Bonzini {
147c68c4a56SPaolo Bonzini     SpinState *s = opaque;
148c68c4a56SPaolo Bonzini     uint8_t *spin_p = &((uint8_t*)s->spin)[addr];
149c68c4a56SPaolo Bonzini 
150c68c4a56SPaolo Bonzini     switch (len) {
151c68c4a56SPaolo Bonzini     case 1:
152c68c4a56SPaolo Bonzini         return ldub_p(spin_p);
153c68c4a56SPaolo Bonzini     case 2:
154c68c4a56SPaolo Bonzini         return lduw_p(spin_p);
155c68c4a56SPaolo Bonzini     case 4:
156c68c4a56SPaolo Bonzini         return ldl_p(spin_p);
157c68c4a56SPaolo Bonzini     default:
158c68c4a56SPaolo Bonzini         hw_error("ppce500: unexpected %s with len = %u", __func__, len);
159c68c4a56SPaolo Bonzini     }
160c68c4a56SPaolo Bonzini }
161c68c4a56SPaolo Bonzini 
162c68c4a56SPaolo Bonzini static const MemoryRegionOps spin_rw_ops = {
163c68c4a56SPaolo Bonzini     .read = spin_read,
164c68c4a56SPaolo Bonzini     .write = spin_write,
165c68c4a56SPaolo Bonzini     .endianness = DEVICE_BIG_ENDIAN,
166c68c4a56SPaolo Bonzini };
167c68c4a56SPaolo Bonzini 
ppce500_spin_initfn(Object * obj)16809a7eb97Sxiaoqiang zhao static void ppce500_spin_initfn(Object *obj)
169c68c4a56SPaolo Bonzini {
17009a7eb97Sxiaoqiang zhao     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
171880fc798SAndreas Färber     SpinState *s = E500_SPIN(dev);
172c68c4a56SPaolo Bonzini 
17309a7eb97Sxiaoqiang zhao     memory_region_init_io(&s->iomem, obj, &spin_rw_ops, s,
17440c5dce9SPaolo Bonzini                           "e500 spin pv device", sizeof(SpinInfo) * MAX_CPUS);
175c68c4a56SPaolo Bonzini     sysbus_init_mmio(dev, &s->iomem);
176c68c4a56SPaolo Bonzini }
177c68c4a56SPaolo Bonzini 
ppce500_spin_class_init(ObjectClass * klass,void * data)178c68c4a56SPaolo Bonzini static void ppce500_spin_class_init(ObjectClass *klass, void *data)
179c68c4a56SPaolo Bonzini {
18009a7eb97Sxiaoqiang zhao     DeviceClass *dc = DEVICE_CLASS(klass);
181c68c4a56SPaolo Bonzini 
182e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, spin_reset);
183c68c4a56SPaolo Bonzini }
184c68c4a56SPaolo Bonzini 
185c68c4a56SPaolo Bonzini static const TypeInfo ppce500_spin_info = {
186880fc798SAndreas Färber     .name          = TYPE_E500_SPIN,
187c68c4a56SPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
188c68c4a56SPaolo Bonzini     .instance_size = sizeof(SpinState),
18909a7eb97Sxiaoqiang zhao     .instance_init = ppce500_spin_initfn,
190c68c4a56SPaolo Bonzini     .class_init    = ppce500_spin_class_init,
191c68c4a56SPaolo Bonzini };
192c68c4a56SPaolo Bonzini 
ppce500_spin_register_types(void)193c68c4a56SPaolo Bonzini static void ppce500_spin_register_types(void)
194c68c4a56SPaolo Bonzini {
195c68c4a56SPaolo Bonzini     type_register_static(&ppce500_spin_info);
196c68c4a56SPaolo Bonzini }
197c68c4a56SPaolo Bonzini 
198c68c4a56SPaolo Bonzini type_init(ppce500_spin_register_types)
199