153018216SPaolo Bonzini /* 253018216SPaolo Bonzini * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. 353018216SPaolo Bonzini * All rights reserved. 453018216SPaolo Bonzini * 553018216SPaolo Bonzini * Redistribution and use in source and binary forms, with or without 653018216SPaolo Bonzini * modification, are permitted provided that the following conditions are met: 753018216SPaolo Bonzini * * Redistributions of source code must retain the above copyright 853018216SPaolo Bonzini * notice, this list of conditions and the following disclaimer. 953018216SPaolo Bonzini * * Redistributions in binary form must reproduce the above copyright 1053018216SPaolo Bonzini * notice, this list of conditions and the following disclaimer in the 1153018216SPaolo Bonzini * documentation and/or other materials provided with the distribution. 1253018216SPaolo Bonzini * * Neither the name of the Open Source and Linux Lab nor the 1353018216SPaolo Bonzini * names of its contributors may be used to endorse or promote products 1453018216SPaolo Bonzini * derived from this software without specific prior written permission. 1553018216SPaolo Bonzini * 1653018216SPaolo Bonzini * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1753018216SPaolo Bonzini * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1853018216SPaolo Bonzini * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1953018216SPaolo Bonzini * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 2053018216SPaolo Bonzini * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 2153018216SPaolo Bonzini * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2253018216SPaolo Bonzini * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2353018216SPaolo Bonzini * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2453018216SPaolo Bonzini * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 2553018216SPaolo Bonzini * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2653018216SPaolo Bonzini */ 2753018216SPaolo Bonzini 2853018216SPaolo Bonzini #include "hw/hw.h" 2953018216SPaolo Bonzini #include "qemu/log.h" 3053018216SPaolo Bonzini #include "qemu/timer.h" 3153018216SPaolo Bonzini 3253018216SPaolo Bonzini void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d) 3353018216SPaolo Bonzini { 3453018216SPaolo Bonzini uint32_t old_ccount = env->sregs[CCOUNT]; 3553018216SPaolo Bonzini 3653018216SPaolo Bonzini env->sregs[CCOUNT] += d; 3753018216SPaolo Bonzini 3853018216SPaolo Bonzini if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { 3953018216SPaolo Bonzini int i; 4053018216SPaolo Bonzini for (i = 0; i < env->config->nccompare; ++i) { 4153018216SPaolo Bonzini if (env->sregs[CCOMPARE + i] - old_ccount <= d) { 4253018216SPaolo Bonzini xtensa_timer_irq(env, i, 1); 4353018216SPaolo Bonzini } 4453018216SPaolo Bonzini } 4553018216SPaolo Bonzini } 4653018216SPaolo Bonzini } 4753018216SPaolo Bonzini 4853018216SPaolo Bonzini void check_interrupts(CPUXtensaState *env) 4953018216SPaolo Bonzini { 50259186a7SAndreas Färber CPUState *cs = CPU(xtensa_env_get_cpu(env)); 5153018216SPaolo Bonzini int minlevel = xtensa_get_cintlevel(env); 5253018216SPaolo Bonzini uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE]; 5353018216SPaolo Bonzini int level; 5453018216SPaolo Bonzini 55*bc72ad67SAlex Bligh /* If the CPU is halted advance CCOUNT according to the QEMU_CLOCK_VIRTUAL time 5653018216SPaolo Bonzini * elapsed since the moment when it was advanced last time. 5753018216SPaolo Bonzini */ 58259186a7SAndreas Färber if (cs->halted) { 59*bc72ad67SAlex Bligh int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 6053018216SPaolo Bonzini 6153018216SPaolo Bonzini xtensa_advance_ccount(env, 6253018216SPaolo Bonzini muldiv64(now - env->halt_clock, 6353018216SPaolo Bonzini env->config->clock_freq_khz, 1000000)); 6453018216SPaolo Bonzini env->halt_clock = now; 6553018216SPaolo Bonzini } 6653018216SPaolo Bonzini for (level = env->config->nlevel; level > minlevel; --level) { 6753018216SPaolo Bonzini if (env->config->level_mask[level] & int_set_enabled) { 6853018216SPaolo Bonzini env->pending_irq_level = level; 69c3affe56SAndreas Färber cpu_interrupt(cs, CPU_INTERRUPT_HARD); 7053018216SPaolo Bonzini qemu_log_mask(CPU_LOG_INT, 7153018216SPaolo Bonzini "%s level = %d, cintlevel = %d, " 7253018216SPaolo Bonzini "pc = %08x, a0 = %08x, ps = %08x, " 7353018216SPaolo Bonzini "intset = %08x, intenable = %08x, " 7453018216SPaolo Bonzini "ccount = %08x\n", 7553018216SPaolo Bonzini __func__, level, xtensa_get_cintlevel(env), 7653018216SPaolo Bonzini env->pc, env->regs[0], env->sregs[PS], 7753018216SPaolo Bonzini env->sregs[INTSET], env->sregs[INTENABLE], 7853018216SPaolo Bonzini env->sregs[CCOUNT]); 7953018216SPaolo Bonzini return; 8053018216SPaolo Bonzini } 8153018216SPaolo Bonzini } 8253018216SPaolo Bonzini env->pending_irq_level = 0; 83d8ed887bSAndreas Färber cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); 8453018216SPaolo Bonzini } 8553018216SPaolo Bonzini 8653018216SPaolo Bonzini static void xtensa_set_irq(void *opaque, int irq, int active) 8753018216SPaolo Bonzini { 8853018216SPaolo Bonzini CPUXtensaState *env = opaque; 8953018216SPaolo Bonzini 9053018216SPaolo Bonzini if (irq >= env->config->ninterrupt) { 9153018216SPaolo Bonzini qemu_log("%s: bad IRQ %d\n", __func__, irq); 9253018216SPaolo Bonzini } else { 9353018216SPaolo Bonzini uint32_t irq_bit = 1 << irq; 9453018216SPaolo Bonzini 9553018216SPaolo Bonzini if (active) { 9653018216SPaolo Bonzini env->sregs[INTSET] |= irq_bit; 9753018216SPaolo Bonzini } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) { 9853018216SPaolo Bonzini env->sregs[INTSET] &= ~irq_bit; 9953018216SPaolo Bonzini } 10053018216SPaolo Bonzini 10153018216SPaolo Bonzini check_interrupts(env); 10253018216SPaolo Bonzini } 10353018216SPaolo Bonzini } 10453018216SPaolo Bonzini 10553018216SPaolo Bonzini void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active) 10653018216SPaolo Bonzini { 10753018216SPaolo Bonzini qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); 10853018216SPaolo Bonzini } 10953018216SPaolo Bonzini 11053018216SPaolo Bonzini void xtensa_rearm_ccompare_timer(CPUXtensaState *env) 11153018216SPaolo Bonzini { 11253018216SPaolo Bonzini int i; 11353018216SPaolo Bonzini uint32_t wake_ccount = env->sregs[CCOUNT] - 1; 11453018216SPaolo Bonzini 11553018216SPaolo Bonzini for (i = 0; i < env->config->nccompare; ++i) { 11653018216SPaolo Bonzini if (env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] < 11753018216SPaolo Bonzini wake_ccount - env->sregs[CCOUNT]) { 11853018216SPaolo Bonzini wake_ccount = env->sregs[CCOMPARE + i]; 11953018216SPaolo Bonzini } 12053018216SPaolo Bonzini } 12153018216SPaolo Bonzini env->wake_ccount = wake_ccount; 122*bc72ad67SAlex Bligh timer_mod(env->ccompare_timer, env->halt_clock + 12353018216SPaolo Bonzini muldiv64(wake_ccount - env->sregs[CCOUNT], 12453018216SPaolo Bonzini 1000000, env->config->clock_freq_khz)); 12553018216SPaolo Bonzini } 12653018216SPaolo Bonzini 12753018216SPaolo Bonzini static void xtensa_ccompare_cb(void *opaque) 12853018216SPaolo Bonzini { 12953018216SPaolo Bonzini XtensaCPU *cpu = opaque; 13053018216SPaolo Bonzini CPUXtensaState *env = &cpu->env; 131259186a7SAndreas Färber CPUState *cs = CPU(cpu); 13253018216SPaolo Bonzini 133259186a7SAndreas Färber if (cs->halted) { 134*bc72ad67SAlex Bligh env->halt_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 13553018216SPaolo Bonzini xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]); 136259186a7SAndreas Färber if (!cpu_has_work(cs)) { 13753018216SPaolo Bonzini env->sregs[CCOUNT] = env->wake_ccount + 1; 13853018216SPaolo Bonzini xtensa_rearm_ccompare_timer(env); 13953018216SPaolo Bonzini } 14053018216SPaolo Bonzini } 14153018216SPaolo Bonzini } 14253018216SPaolo Bonzini 14353018216SPaolo Bonzini void xtensa_irq_init(CPUXtensaState *env) 14453018216SPaolo Bonzini { 14553018216SPaolo Bonzini XtensaCPU *cpu = xtensa_env_get_cpu(env); 14653018216SPaolo Bonzini 14753018216SPaolo Bonzini env->irq_inputs = (void **)qemu_allocate_irqs( 14853018216SPaolo Bonzini xtensa_set_irq, env, env->config->ninterrupt); 14953018216SPaolo Bonzini if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT) && 15053018216SPaolo Bonzini env->config->nccompare > 0) { 15153018216SPaolo Bonzini env->ccompare_timer = 152*bc72ad67SAlex Bligh timer_new_ns(QEMU_CLOCK_VIRTUAL, &xtensa_ccompare_cb, cpu); 15353018216SPaolo Bonzini } 15453018216SPaolo Bonzini } 15553018216SPaolo Bonzini 15653018216SPaolo Bonzini void *xtensa_get_extint(CPUXtensaState *env, unsigned extint) 15753018216SPaolo Bonzini { 15853018216SPaolo Bonzini if (extint < env->config->nextint) { 15953018216SPaolo Bonzini unsigned irq = env->config->extint[extint]; 16053018216SPaolo Bonzini return env->irq_inputs[irq]; 16153018216SPaolo Bonzini } else { 16253018216SPaolo Bonzini qemu_log("%s: trying to acquire invalid external interrupt %d\n", 16353018216SPaolo Bonzini __func__, extint); 16453018216SPaolo Bonzini return NULL; 16553018216SPaolo Bonzini } 16653018216SPaolo Bonzini } 167