xref: /openbmc/qemu/hw/sparc64/sparc64.c (revision 707ded20)
1 /*
2  * QEMU Sun4u/Sun4v System Emulator common routines
3  *
4  * Copyright (c) 2005 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 
26 #include "qemu/osdep.h"
27 #include "qapi/error.h"
28 #include "cpu.h"
29 #include "hw/boards.h"
30 #include "hw/sparc/sparc64.h"
31 #include "qemu/timer.h"
32 #include "sysemu/reset.h"
33 #include "trace.h"
34 
35 
36 #define TICK_MAX             0x7fffffffffffffffULL
37 
38 static void cpu_kick_irq(SPARCCPU *cpu)
39 {
40     CPUState *cs = CPU(cpu);
41     CPUSPARCState *env = &cpu->env;
42 
43     cs->halted = 0;
44     cpu_check_irqs(env);
45     qemu_cpu_kick(cs);
46 }
47 
48 void sparc64_cpu_set_ivec_irq(void *opaque, int irq, int level)
49 {
50     SPARCCPU *cpu = opaque;
51     CPUSPARCState *env = &cpu->env;
52     CPUState *cs;
53 
54     if (level) {
55         if (!(env->ivec_status & 0x20)) {
56             trace_sparc64_cpu_ivec_raise_irq(irq);
57             cs = CPU(cpu);
58             cs->halted = 0;
59             env->interrupt_index = TT_IVEC;
60             env->ivec_status |= 0x20;
61             env->ivec_data[0] = (0x1f << 6) | irq;
62             env->ivec_data[1] = 0;
63             env->ivec_data[2] = 0;
64             cpu_interrupt(cs, CPU_INTERRUPT_HARD);
65         }
66     } else {
67         if (env->ivec_status & 0x20) {
68             trace_sparc64_cpu_ivec_lower_irq(irq);
69             cs = CPU(cpu);
70             env->ivec_status &= ~0x20;
71             cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
72         }
73     }
74 }
75 
76 typedef struct ResetData {
77     SPARCCPU *cpu;
78     uint64_t prom_addr;
79 } ResetData;
80 
81 static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu,
82                                   QEMUBHFunc *cb, uint32_t frequency,
83                                   uint64_t disabled_mask, uint64_t npt_mask)
84 {
85     CPUTimer *timer = g_new0(CPUTimer, 1);
86 
87     timer->name = name;
88     timer->frequency = frequency;
89     timer->disabled_mask = disabled_mask;
90     timer->npt_mask = npt_mask;
91 
92     timer->disabled = 1;
93     timer->npt = 1;
94     timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
95 
96     timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu);
97 
98     return timer;
99 }
100 
101 static void cpu_timer_reset(CPUTimer *timer)
102 {
103     timer->disabled = 1;
104     timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
105 
106     timer_del(timer->qtimer);
107 }
108 
109 static void main_cpu_reset(void *opaque)
110 {
111     ResetData *s = (ResetData *)opaque;
112     CPUSPARCState *env = &s->cpu->env;
113     static unsigned int nr_resets;
114 
115     cpu_reset(CPU(s->cpu));
116 
117     cpu_timer_reset(env->tick);
118     cpu_timer_reset(env->stick);
119     cpu_timer_reset(env->hstick);
120 
121     env->gregs[1] = 0; /* Memory start */
122     env->gregs[2] = current_machine->ram_size; /* Memory size */
123     env->gregs[3] = 0; /* Machine description XXX */
124     if (nr_resets++ == 0) {
125         /* Power on reset */
126         env->pc = s->prom_addr + 0x20ULL;
127     } else {
128         env->pc = s->prom_addr + 0x40ULL;
129     }
130     env->npc = env->pc + 4;
131 }
132 
133 static void tick_irq(void *opaque)
134 {
135     SPARCCPU *cpu = opaque;
136     CPUSPARCState *env = &cpu->env;
137 
138     CPUTimer *timer = env->tick;
139 
140     if (timer->disabled) {
141         trace_sparc64_cpu_tick_irq_disabled();
142         return;
143     } else {
144         trace_sparc64_cpu_tick_irq_fire();
145     }
146 
147     env->softint |= SOFTINT_TIMER;
148     cpu_kick_irq(cpu);
149 }
150 
151 static void stick_irq(void *opaque)
152 {
153     SPARCCPU *cpu = opaque;
154     CPUSPARCState *env = &cpu->env;
155 
156     CPUTimer *timer = env->stick;
157 
158     if (timer->disabled) {
159         trace_sparc64_cpu_stick_irq_disabled();
160         return;
161     } else {
162         trace_sparc64_cpu_stick_irq_fire();
163     }
164 
165     env->softint |= SOFTINT_STIMER;
166     cpu_kick_irq(cpu);
167 }
168 
169 static void hstick_irq(void *opaque)
170 {
171     SPARCCPU *cpu = opaque;
172     CPUSPARCState *env = &cpu->env;
173 
174     CPUTimer *timer = env->hstick;
175 
176     if (timer->disabled) {
177         trace_sparc64_cpu_hstick_irq_disabled();
178         return;
179     } else {
180         trace_sparc64_cpu_hstick_irq_fire();
181     }
182 
183     env->softint |= SOFTINT_STIMER;
184     cpu_kick_irq(cpu);
185 }
186 
187 static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency)
188 {
189     return muldiv64(cpu_ticks, NANOSECONDS_PER_SECOND, frequency);
190 }
191 
192 static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency)
193 {
194     return muldiv64(timer_ticks, frequency, NANOSECONDS_PER_SECOND);
195 }
196 
197 void cpu_tick_set_count(CPUTimer *timer, uint64_t count)
198 {
199     uint64_t real_count = count & ~timer->npt_mask;
200     uint64_t npt_bit = count & timer->npt_mask;
201 
202     int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
203                     cpu_to_timer_ticks(real_count, timer->frequency);
204 
205     trace_sparc64_cpu_tick_set_count(timer->name, real_count,
206                                      timer->npt ? "disabled" : "enabled",
207                                      timer);
208 
209     timer->npt = npt_bit ? 1 : 0;
210     timer->clock_offset = vm_clock_offset;
211 }
212 
213 uint64_t cpu_tick_get_count(CPUTimer *timer)
214 {
215     uint64_t real_count = timer_to_cpu_ticks(
216                     qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset,
217                     timer->frequency);
218 
219     trace_sparc64_cpu_tick_get_count(timer->name, real_count,
220                                      timer->npt ? "disabled" : "enabled",
221                                      timer);
222 
223     if (timer->npt) {
224         real_count |= timer->npt_mask;
225     }
226 
227     return real_count;
228 }
229 
230 void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit)
231 {
232     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
233 
234     uint64_t real_limit = limit & ~timer->disabled_mask;
235     timer->disabled = (limit & timer->disabled_mask) ? 1 : 0;
236 
237     int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) +
238                     timer->clock_offset;
239 
240     if (expires < now) {
241         expires = now + 1;
242     }
243 
244     trace_sparc64_cpu_tick_set_limit(timer->name, real_limit,
245                                      timer->disabled ? "disabled" : "enabled",
246                                      timer, limit,
247                                      timer_to_cpu_ticks(
248                                          now - timer->clock_offset,
249                                          timer->frequency
250                                      ),
251                                      timer_to_cpu_ticks(
252                                          expires - now, timer->frequency
253                                      ));
254 
255     if (!real_limit) {
256         trace_sparc64_cpu_tick_set_limit_zero(timer->name);
257         timer_del(timer->qtimer);
258     } else if (timer->disabled) {
259         timer_del(timer->qtimer);
260     } else {
261         timer_mod(timer->qtimer, expires);
262     }
263 }
264 
265 SPARCCPU *sparc64_cpu_devinit(const char *cpu_type, uint64_t prom_addr)
266 {
267     SPARCCPU *cpu;
268     CPUSPARCState *env;
269     ResetData *reset_info;
270 
271     uint32_t   tick_frequency = 100 * 1000000;
272     uint32_t  stick_frequency = 100 * 1000000;
273     uint32_t hstick_frequency = 100 * 1000000;
274 
275     cpu = SPARC_CPU(object_new(cpu_type));
276     qdev_init_gpio_in_named(DEVICE(cpu), sparc64_cpu_set_ivec_irq,
277                             "ivec-irq", IVEC_MAX);
278     qdev_realize(DEVICE(cpu), NULL, &error_fatal);
279     env = &cpu->env;
280 
281     env->tick = cpu_timer_create("tick", cpu, tick_irq,
282                                   tick_frequency, TICK_INT_DIS,
283                                   TICK_NPT_MASK);
284 
285     env->stick = cpu_timer_create("stick", cpu, stick_irq,
286                                    stick_frequency, TICK_INT_DIS,
287                                    TICK_NPT_MASK);
288 
289     env->hstick = cpu_timer_create("hstick", cpu, hstick_irq,
290                                     hstick_frequency, TICK_INT_DIS,
291                                     TICK_NPT_MASK);
292 
293     reset_info = g_new0(ResetData, 1);
294     reset_info->cpu = cpu;
295     reset_info->prom_addr = prom_addr;
296     qemu_register_reset(main_cpu_reset, reset_info);
297 
298     return cpu;
299 }
300