153018216SPaolo Bonzini /* 253018216SPaolo Bonzini * QEMU OpenRISC timer support 353018216SPaolo Bonzini * 453018216SPaolo Bonzini * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com> 553018216SPaolo Bonzini * Zhizhou Zhang <etouzh@gmail.com> 653018216SPaolo Bonzini * 753018216SPaolo Bonzini * This library is free software; you can redistribute it and/or 853018216SPaolo Bonzini * modify it under the terms of the GNU Lesser General Public 953018216SPaolo Bonzini * License as published by the Free Software Foundation; either 1053018216SPaolo Bonzini * version 2 of the License, or (at your option) any later version. 1153018216SPaolo Bonzini * 1253018216SPaolo Bonzini * This library is distributed in the hope that it will be useful, 1353018216SPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of 1453018216SPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553018216SPaolo Bonzini * Lesser General Public License for more details. 1653018216SPaolo Bonzini * 1753018216SPaolo Bonzini * You should have received a copy of the GNU Lesser General Public 1853018216SPaolo Bonzini * License along with this library; if not, see <http://www.gnu.org/licenses/>. 1953018216SPaolo Bonzini */ 2053018216SPaolo Bonzini 2153018216SPaolo Bonzini #include "cpu.h" 2253018216SPaolo Bonzini #include "hw/hw.h" 2353018216SPaolo Bonzini #include "qemu/timer.h" 2453018216SPaolo Bonzini 2553018216SPaolo Bonzini #define TIMER_FREQ (20 * 1000 * 1000) /* 20MHz */ 2653018216SPaolo Bonzini 2753018216SPaolo Bonzini /* The time when TTCR changes */ 2853018216SPaolo Bonzini static uint64_t last_clk; 2953018216SPaolo Bonzini static int is_counting; 3053018216SPaolo Bonzini 3153018216SPaolo Bonzini void cpu_openrisc_count_update(OpenRISCCPU *cpu) 3253018216SPaolo Bonzini { 33*d5155217SSebastian Macke uint64_t now; 3453018216SPaolo Bonzini 3553018216SPaolo Bonzini if (!is_counting) { 3653018216SPaolo Bonzini return; 3753018216SPaolo Bonzini } 38*d5155217SSebastian Macke now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 3953018216SPaolo Bonzini cpu->env.ttcr += (uint32_t)muldiv64(now - last_clk, TIMER_FREQ, 4053018216SPaolo Bonzini get_ticks_per_sec()); 4153018216SPaolo Bonzini last_clk = now; 42*d5155217SSebastian Macke } 43*d5155217SSebastian Macke 44*d5155217SSebastian Macke void cpu_openrisc_timer_update(OpenRISCCPU *cpu) 45*d5155217SSebastian Macke { 46*d5155217SSebastian Macke uint32_t wait; 47*d5155217SSebastian Macke uint64_t now, next; 48*d5155217SSebastian Macke 49*d5155217SSebastian Macke if (!is_counting) { 50*d5155217SSebastian Macke return; 51*d5155217SSebastian Macke } 52*d5155217SSebastian Macke 53*d5155217SSebastian Macke cpu_openrisc_count_update(cpu); 54*d5155217SSebastian Macke now = last_clk; 5553018216SPaolo Bonzini 5653018216SPaolo Bonzini if ((cpu->env.ttmr & TTMR_TP) <= (cpu->env.ttcr & TTMR_TP)) { 5753018216SPaolo Bonzini wait = TTMR_TP - (cpu->env.ttcr & TTMR_TP) + 1; 5853018216SPaolo Bonzini wait += cpu->env.ttmr & TTMR_TP; 5953018216SPaolo Bonzini } else { 6053018216SPaolo Bonzini wait = (cpu->env.ttmr & TTMR_TP) - (cpu->env.ttcr & TTMR_TP); 6153018216SPaolo Bonzini } 6253018216SPaolo Bonzini next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ); 63bc72ad67SAlex Bligh timer_mod(cpu->env.timer, next); 6453018216SPaolo Bonzini } 6553018216SPaolo Bonzini 6653018216SPaolo Bonzini void cpu_openrisc_count_start(OpenRISCCPU *cpu) 6753018216SPaolo Bonzini { 6853018216SPaolo Bonzini is_counting = 1; 6953018216SPaolo Bonzini cpu_openrisc_count_update(cpu); 7053018216SPaolo Bonzini } 7153018216SPaolo Bonzini 7253018216SPaolo Bonzini void cpu_openrisc_count_stop(OpenRISCCPU *cpu) 7353018216SPaolo Bonzini { 74*d5155217SSebastian Macke timer_del(cpu->env.timer); 7553018216SPaolo Bonzini cpu_openrisc_count_update(cpu); 76*d5155217SSebastian Macke is_counting = 0; 7753018216SPaolo Bonzini } 7853018216SPaolo Bonzini 7953018216SPaolo Bonzini static void openrisc_timer_cb(void *opaque) 8053018216SPaolo Bonzini { 8153018216SPaolo Bonzini OpenRISCCPU *cpu = opaque; 8253018216SPaolo Bonzini 8353018216SPaolo Bonzini if ((cpu->env.ttmr & TTMR_IE) && 84bc72ad67SAlex Bligh timer_expired(cpu->env.timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))) { 85259186a7SAndreas Färber CPUState *cs = CPU(cpu); 86259186a7SAndreas Färber 8753018216SPaolo Bonzini cpu->env.ttmr |= TTMR_IP; 88259186a7SAndreas Färber cs->interrupt_request |= CPU_INTERRUPT_TIMER; 8953018216SPaolo Bonzini } 9053018216SPaolo Bonzini 9153018216SPaolo Bonzini switch (cpu->env.ttmr & TTMR_M) { 9253018216SPaolo Bonzini case TIMER_NONE: 9353018216SPaolo Bonzini break; 9453018216SPaolo Bonzini case TIMER_INTR: 9553018216SPaolo Bonzini cpu->env.ttcr = 0; 9653018216SPaolo Bonzini break; 9753018216SPaolo Bonzini case TIMER_SHOT: 9853018216SPaolo Bonzini cpu_openrisc_count_stop(cpu); 9953018216SPaolo Bonzini break; 10053018216SPaolo Bonzini case TIMER_CONT: 10153018216SPaolo Bonzini break; 10253018216SPaolo Bonzini } 103*d5155217SSebastian Macke 104*d5155217SSebastian Macke cpu_openrisc_timer_update(cpu); 10553018216SPaolo Bonzini } 10653018216SPaolo Bonzini 10753018216SPaolo Bonzini void cpu_openrisc_clock_init(OpenRISCCPU *cpu) 10853018216SPaolo Bonzini { 109bc72ad67SAlex Bligh cpu->env.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &openrisc_timer_cb, cpu); 11053018216SPaolo Bonzini cpu->env.ttmr = 0x00000000; 11153018216SPaolo Bonzini cpu->env.ttcr = 0x00000000; 11253018216SPaolo Bonzini } 113