xref: /openbmc/qemu/hw/xtensa/pic_cpu.c (revision 53018216)
1*53018216SPaolo Bonzini /*
2*53018216SPaolo Bonzini  * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
3*53018216SPaolo Bonzini  * All rights reserved.
4*53018216SPaolo Bonzini  *
5*53018216SPaolo Bonzini  * Redistribution and use in source and binary forms, with or without
6*53018216SPaolo Bonzini  * modification, are permitted provided that the following conditions are met:
7*53018216SPaolo Bonzini  *     * Redistributions of source code must retain the above copyright
8*53018216SPaolo Bonzini  *       notice, this list of conditions and the following disclaimer.
9*53018216SPaolo Bonzini  *     * Redistributions in binary form must reproduce the above copyright
10*53018216SPaolo Bonzini  *       notice, this list of conditions and the following disclaimer in the
11*53018216SPaolo Bonzini  *       documentation and/or other materials provided with the distribution.
12*53018216SPaolo Bonzini  *     * Neither the name of the Open Source and Linux Lab nor the
13*53018216SPaolo Bonzini  *       names of its contributors may be used to endorse or promote products
14*53018216SPaolo Bonzini  *       derived from this software without specific prior written permission.
15*53018216SPaolo Bonzini  *
16*53018216SPaolo Bonzini  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17*53018216SPaolo Bonzini  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*53018216SPaolo Bonzini  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*53018216SPaolo Bonzini  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20*53018216SPaolo Bonzini  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21*53018216SPaolo Bonzini  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22*53018216SPaolo Bonzini  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23*53018216SPaolo Bonzini  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24*53018216SPaolo Bonzini  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25*53018216SPaolo Bonzini  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*53018216SPaolo Bonzini  */
27*53018216SPaolo Bonzini 
28*53018216SPaolo Bonzini #include "hw/hw.h"
29*53018216SPaolo Bonzini #include "qemu/log.h"
30*53018216SPaolo Bonzini #include "qemu/timer.h"
31*53018216SPaolo Bonzini 
32*53018216SPaolo Bonzini void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d)
33*53018216SPaolo Bonzini {
34*53018216SPaolo Bonzini     uint32_t old_ccount = env->sregs[CCOUNT];
35*53018216SPaolo Bonzini 
36*53018216SPaolo Bonzini     env->sregs[CCOUNT] += d;
37*53018216SPaolo Bonzini 
38*53018216SPaolo Bonzini     if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
39*53018216SPaolo Bonzini         int i;
40*53018216SPaolo Bonzini         for (i = 0; i < env->config->nccompare; ++i) {
41*53018216SPaolo Bonzini             if (env->sregs[CCOMPARE + i] - old_ccount <= d) {
42*53018216SPaolo Bonzini                 xtensa_timer_irq(env, i, 1);
43*53018216SPaolo Bonzini             }
44*53018216SPaolo Bonzini         }
45*53018216SPaolo Bonzini     }
46*53018216SPaolo Bonzini }
47*53018216SPaolo Bonzini 
48*53018216SPaolo Bonzini void check_interrupts(CPUXtensaState *env)
49*53018216SPaolo Bonzini {
50*53018216SPaolo Bonzini     int minlevel = xtensa_get_cintlevel(env);
51*53018216SPaolo Bonzini     uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE];
52*53018216SPaolo Bonzini     int level;
53*53018216SPaolo Bonzini 
54*53018216SPaolo Bonzini     /* If the CPU is halted advance CCOUNT according to the vm_clock time
55*53018216SPaolo Bonzini      * elapsed since the moment when it was advanced last time.
56*53018216SPaolo Bonzini      */
57*53018216SPaolo Bonzini     if (env->halted) {
58*53018216SPaolo Bonzini         int64_t now = qemu_get_clock_ns(vm_clock);
59*53018216SPaolo Bonzini 
60*53018216SPaolo Bonzini         xtensa_advance_ccount(env,
61*53018216SPaolo Bonzini                 muldiv64(now - env->halt_clock,
62*53018216SPaolo Bonzini                     env->config->clock_freq_khz, 1000000));
63*53018216SPaolo Bonzini         env->halt_clock = now;
64*53018216SPaolo Bonzini     }
65*53018216SPaolo Bonzini     for (level = env->config->nlevel; level > minlevel; --level) {
66*53018216SPaolo Bonzini         if (env->config->level_mask[level] & int_set_enabled) {
67*53018216SPaolo Bonzini             env->pending_irq_level = level;
68*53018216SPaolo Bonzini             cpu_interrupt(env, CPU_INTERRUPT_HARD);
69*53018216SPaolo Bonzini             qemu_log_mask(CPU_LOG_INT,
70*53018216SPaolo Bonzini                     "%s level = %d, cintlevel = %d, "
71*53018216SPaolo Bonzini                     "pc = %08x, a0 = %08x, ps = %08x, "
72*53018216SPaolo Bonzini                     "intset = %08x, intenable = %08x, "
73*53018216SPaolo Bonzini                     "ccount = %08x\n",
74*53018216SPaolo Bonzini                     __func__, level, xtensa_get_cintlevel(env),
75*53018216SPaolo Bonzini                     env->pc, env->regs[0], env->sregs[PS],
76*53018216SPaolo Bonzini                     env->sregs[INTSET], env->sregs[INTENABLE],
77*53018216SPaolo Bonzini                     env->sregs[CCOUNT]);
78*53018216SPaolo Bonzini             return;
79*53018216SPaolo Bonzini         }
80*53018216SPaolo Bonzini     }
81*53018216SPaolo Bonzini     env->pending_irq_level = 0;
82*53018216SPaolo Bonzini     cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
83*53018216SPaolo Bonzini }
84*53018216SPaolo Bonzini 
85*53018216SPaolo Bonzini static void xtensa_set_irq(void *opaque, int irq, int active)
86*53018216SPaolo Bonzini {
87*53018216SPaolo Bonzini     CPUXtensaState *env = opaque;
88*53018216SPaolo Bonzini 
89*53018216SPaolo Bonzini     if (irq >= env->config->ninterrupt) {
90*53018216SPaolo Bonzini         qemu_log("%s: bad IRQ %d\n", __func__, irq);
91*53018216SPaolo Bonzini     } else {
92*53018216SPaolo Bonzini         uint32_t irq_bit = 1 << irq;
93*53018216SPaolo Bonzini 
94*53018216SPaolo Bonzini         if (active) {
95*53018216SPaolo Bonzini             env->sregs[INTSET] |= irq_bit;
96*53018216SPaolo Bonzini         } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) {
97*53018216SPaolo Bonzini             env->sregs[INTSET] &= ~irq_bit;
98*53018216SPaolo Bonzini         }
99*53018216SPaolo Bonzini 
100*53018216SPaolo Bonzini         check_interrupts(env);
101*53018216SPaolo Bonzini     }
102*53018216SPaolo Bonzini }
103*53018216SPaolo Bonzini 
104*53018216SPaolo Bonzini void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active)
105*53018216SPaolo Bonzini {
106*53018216SPaolo Bonzini     qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active);
107*53018216SPaolo Bonzini }
108*53018216SPaolo Bonzini 
109*53018216SPaolo Bonzini void xtensa_rearm_ccompare_timer(CPUXtensaState *env)
110*53018216SPaolo Bonzini {
111*53018216SPaolo Bonzini     int i;
112*53018216SPaolo Bonzini     uint32_t wake_ccount = env->sregs[CCOUNT] - 1;
113*53018216SPaolo Bonzini 
114*53018216SPaolo Bonzini     for (i = 0; i < env->config->nccompare; ++i) {
115*53018216SPaolo Bonzini         if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] <
116*53018216SPaolo Bonzini                 wake_ccount - env->sregs[CCOUNT]) {
117*53018216SPaolo Bonzini             wake_ccount = env->sregs[CCOMPARE + i];
118*53018216SPaolo Bonzini         }
119*53018216SPaolo Bonzini     }
120*53018216SPaolo Bonzini     env->wake_ccount = wake_ccount;
121*53018216SPaolo Bonzini     qemu_mod_timer(env->ccompare_timer, env->halt_clock +
122*53018216SPaolo Bonzini             muldiv64(wake_ccount - env->sregs[CCOUNT],
123*53018216SPaolo Bonzini                 1000000, env->config->clock_freq_khz));
124*53018216SPaolo Bonzini }
125*53018216SPaolo Bonzini 
126*53018216SPaolo Bonzini static void xtensa_ccompare_cb(void *opaque)
127*53018216SPaolo Bonzini {
128*53018216SPaolo Bonzini     XtensaCPU *cpu = opaque;
129*53018216SPaolo Bonzini     CPUXtensaState *env = &cpu->env;
130*53018216SPaolo Bonzini 
131*53018216SPaolo Bonzini     if (env->halted) {
132*53018216SPaolo Bonzini         env->halt_clock = qemu_get_clock_ns(vm_clock);
133*53018216SPaolo Bonzini         xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]);
134*53018216SPaolo Bonzini         if (!cpu_has_work(CPU(cpu))) {
135*53018216SPaolo Bonzini             env->sregs[CCOUNT] = env->wake_ccount + 1;
136*53018216SPaolo Bonzini             xtensa_rearm_ccompare_timer(env);
137*53018216SPaolo Bonzini         }
138*53018216SPaolo Bonzini     }
139*53018216SPaolo Bonzini }
140*53018216SPaolo Bonzini 
141*53018216SPaolo Bonzini void xtensa_irq_init(CPUXtensaState *env)
142*53018216SPaolo Bonzini {
143*53018216SPaolo Bonzini     XtensaCPU *cpu = xtensa_env_get_cpu(env);
144*53018216SPaolo Bonzini 
145*53018216SPaolo Bonzini     env->irq_inputs = (void **)qemu_allocate_irqs(
146*53018216SPaolo Bonzini             xtensa_set_irq, env, env->config->ninterrupt);
147*53018216SPaolo Bonzini     if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT) &&
148*53018216SPaolo Bonzini             env->config->nccompare > 0) {
149*53018216SPaolo Bonzini         env->ccompare_timer =
150*53018216SPaolo Bonzini             qemu_new_timer_ns(vm_clock, &xtensa_ccompare_cb, cpu);
151*53018216SPaolo Bonzini     }
152*53018216SPaolo Bonzini }
153*53018216SPaolo Bonzini 
154*53018216SPaolo Bonzini void *xtensa_get_extint(CPUXtensaState *env, unsigned extint)
155*53018216SPaolo Bonzini {
156*53018216SPaolo Bonzini     if (extint < env->config->nextint) {
157*53018216SPaolo Bonzini         unsigned irq = env->config->extint[extint];
158*53018216SPaolo Bonzini         return env->irq_inputs[irq];
159*53018216SPaolo Bonzini     } else {
160*53018216SPaolo Bonzini         qemu_log("%s: trying to acquire invalid external interrupt %d\n",
161*53018216SPaolo Bonzini                 __func__, extint);
162*53018216SPaolo Bonzini         return NULL;
163*53018216SPaolo Bonzini     }
164*53018216SPaolo Bonzini }
165