qemu/hw/openrisc/cputimer.c
<<
>>
Prefs
   1/*
   2 * QEMU OpenRISC timer support
   3 *
   4 * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
   5 *                         Zhizhou Zhang <etouzh@gmail.com>
   6 *
   7 * This library is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU Lesser General Public
   9 * License as published by the Free Software Foundation; either
  10 * version 2.1 of the License, or (at your option) any later version.
  11 *
  12 * This library is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * Lesser General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU Lesser General Public
  18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "cpu.h"
  23#include "migration/vmstate.h"
  24#include "qemu/timer.h"
  25
  26#define TIMER_PERIOD 50 /* 50 ns period for 20 MHz timer */
  27
  28/* Tick Timer global state to allow all cores to be in sync */
  29typedef struct OR1KTimerState {
  30    uint32_t ttcr;
  31    uint64_t last_clk;
  32} OR1KTimerState;
  33
  34static OR1KTimerState *or1k_timer;
  35
  36void cpu_openrisc_count_set(OpenRISCCPU *cpu, uint32_t val)
  37{
  38    or1k_timer->ttcr = val;
  39}
  40
  41uint32_t cpu_openrisc_count_get(OpenRISCCPU *cpu)
  42{
  43    return or1k_timer->ttcr;
  44}
  45
  46/* Add elapsed ticks to ttcr */
  47void cpu_openrisc_count_update(OpenRISCCPU *cpu)
  48{
  49    uint64_t now;
  50
  51    if (!cpu->env.is_counting) {
  52        return;
  53    }
  54    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  55    or1k_timer->ttcr += (uint32_t)((now - or1k_timer->last_clk)
  56                                    / TIMER_PERIOD);
  57    or1k_timer->last_clk = now;
  58}
  59
  60/* Update the next timeout time as difference between ttmr and ttcr */
  61void cpu_openrisc_timer_update(OpenRISCCPU *cpu)
  62{
  63    uint32_t wait;
  64    uint64_t now, next;
  65
  66    if (!cpu->env.is_counting) {
  67        return;
  68    }
  69
  70    cpu_openrisc_count_update(cpu);
  71    now = or1k_timer->last_clk;
  72
  73    if ((cpu->env.ttmr & TTMR_TP) <= (or1k_timer->ttcr & TTMR_TP)) {
  74        wait = TTMR_TP - (or1k_timer->ttcr & TTMR_TP) + 1;
  75        wait += cpu->env.ttmr & TTMR_TP;
  76    } else {
  77        wait = (cpu->env.ttmr & TTMR_TP) - (or1k_timer->ttcr & TTMR_TP);
  78    }
  79    next = now + (uint64_t)wait * TIMER_PERIOD;
  80    timer_mod(cpu->env.timer, next);
  81}
  82
  83void cpu_openrisc_count_start(OpenRISCCPU *cpu)
  84{
  85    cpu->env.is_counting = 1;
  86    cpu_openrisc_count_update(cpu);
  87}
  88
  89void cpu_openrisc_count_stop(OpenRISCCPU *cpu)
  90{
  91    timer_del(cpu->env.timer);
  92    cpu_openrisc_count_update(cpu);
  93    cpu->env.is_counting = 0;
  94}
  95
  96static void openrisc_timer_cb(void *opaque)
  97{
  98    OpenRISCCPU *cpu = opaque;
  99
 100    if ((cpu->env.ttmr & TTMR_IE) &&
 101         timer_expired(cpu->env.timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL))) {
 102        CPUState *cs = CPU(cpu);
 103
 104        cpu->env.ttmr |= TTMR_IP;
 105        cs->interrupt_request |= CPU_INTERRUPT_TIMER;
 106    }
 107
 108    switch (cpu->env.ttmr & TTMR_M) {
 109    case TIMER_NONE:
 110        break;
 111    case TIMER_INTR:
 112        or1k_timer->ttcr = 0;
 113        break;
 114    case TIMER_SHOT:
 115        cpu_openrisc_count_stop(cpu);
 116        break;
 117    case TIMER_CONT:
 118        break;
 119    }
 120
 121    cpu_openrisc_timer_update(cpu);
 122    qemu_cpu_kick(CPU(cpu));
 123}
 124
 125static const VMStateDescription vmstate_or1k_timer = {
 126    .name = "or1k_timer",
 127    .version_id = 1,
 128    .minimum_version_id = 1,
 129    .fields = (VMStateField[]) {
 130        VMSTATE_UINT32(ttcr, OR1KTimerState),
 131        VMSTATE_UINT64(last_clk, OR1KTimerState),
 132        VMSTATE_END_OF_LIST()
 133    }
 134};
 135
 136void cpu_openrisc_clock_init(OpenRISCCPU *cpu)
 137{
 138    cpu->env.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &openrisc_timer_cb, cpu);
 139    cpu->env.ttmr = 0x00000000;
 140
 141    if (or1k_timer == NULL) {
 142        or1k_timer = g_new0(OR1KTimerState, 1);
 143        vmstate_register(NULL, 0, &vmstate_or1k_timer, or1k_timer);
 144    }
 145}
 146