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