qemu/hw/timer/mips_gictimer.c
<<
>>
Prefs
   1/*
   2 * This file is subject to the terms and conditions of the GNU General Public
   3 * License.  See the file "COPYING" in the main directory of this archive
   4 * for more details.
   5 *
   6 * Copyright (C) 2016 Imagination Technologies
   7 */
   8
   9#include "qemu/osdep.h"
  10#include "hw/sysbus.h"
  11#include "qemu/timer.h"
  12#include "hw/timer/mips_gictimer.h"
  13
  14#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
  15
  16uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic)
  17{
  18    return NANOSECONDS_PER_SECOND / TIMER_PERIOD;
  19}
  20
  21static void gic_vptimer_update(MIPSGICTimerState *gictimer,
  22                                   uint32_t vp_index, uint64_t now)
  23{
  24    uint64_t next;
  25    uint32_t wait;
  26
  27    wait = gictimer->vptimers[vp_index].comparelo - gictimer->sh_counterlo -
  28           (uint32_t)(now / TIMER_PERIOD);
  29    next = now + (uint64_t)wait * TIMER_PERIOD;
  30
  31    timer_mod(gictimer->vptimers[vp_index].qtimer, next);
  32}
  33
  34static void gic_vptimer_expire(MIPSGICTimerState *gictimer, uint32_t vp_index,
  35                               uint64_t now)
  36{
  37    if (gictimer->countstop) {
  38        /* timer stopped */
  39        return;
  40    }
  41    gictimer->cb(gictimer->opaque, vp_index);
  42    gic_vptimer_update(gictimer, vp_index, now);
  43}
  44
  45static void gic_vptimer_cb(void *opaque)
  46{
  47    MIPSGICTimerVPState *vptimer = opaque;
  48    MIPSGICTimerState *gictimer = vptimer->gictimer;
  49    gic_vptimer_expire(gictimer, vptimer->vp_index,
  50                       qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
  51}
  52
  53uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gictimer)
  54{
  55    int i;
  56    if (gictimer->countstop) {
  57        return gictimer->sh_counterlo;
  58    } else {
  59        uint64_t now;
  60        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  61        for (i = 0; i < gictimer->num_vps; i++) {
  62            if (timer_pending(gictimer->vptimers[i].qtimer)
  63                && timer_expired(gictimer->vptimers[i].qtimer, now)) {
  64                /* The timer has already expired.  */
  65                gic_vptimer_expire(gictimer, i, now);
  66            }
  67        }
  68        return gictimer->sh_counterlo + (uint32_t)(now / TIMER_PERIOD);
  69    }
  70}
  71
  72void mips_gictimer_store_sh_count(MIPSGICTimerState *gictimer, uint64_t count)
  73{
  74    int i;
  75    uint64_t now;
  76
  77    if (gictimer->countstop || !gictimer->vptimers[0].qtimer) {
  78        gictimer->sh_counterlo = count;
  79    } else {
  80        /* Store new count register */
  81        now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  82        gictimer->sh_counterlo = count - (uint32_t)(now / TIMER_PERIOD);
  83        /* Update timer timer */
  84        for (i = 0; i < gictimer->num_vps; i++) {
  85            gic_vptimer_update(gictimer, i, now);
  86        }
  87    }
  88}
  89
  90uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
  91                                      uint32_t vp_index)
  92{
  93    return gictimer->vptimers[vp_index].comparelo;
  94}
  95
  96void mips_gictimer_store_vp_compare(MIPSGICTimerState *gictimer,
  97                                    uint32_t vp_index, uint64_t compare)
  98{
  99    gictimer->vptimers[vp_index].comparelo = (uint32_t) compare;
 100    gic_vptimer_update(gictimer, vp_index,
 101                       qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
 102}
 103
 104uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer)
 105{
 106    return gictimer->countstop;
 107}
 108
 109void mips_gictimer_start_count(MIPSGICTimerState *gictimer)
 110{
 111    gictimer->countstop = 0;
 112    mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo);
 113}
 114
 115void mips_gictimer_stop_count(MIPSGICTimerState *gictimer)
 116{
 117    int i;
 118
 119    gictimer->countstop = 1;
 120    /* Store the current value */
 121    gictimer->sh_counterlo +=
 122        (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
 123    for (i = 0; i < gictimer->num_vps; i++) {
 124        timer_del(gictimer->vptimers[i].qtimer);
 125    }
 126}
 127
 128MIPSGICTimerState *mips_gictimer_init(void *opaque, uint32_t nvps,
 129                                      MIPSGICTimerCB *cb)
 130{
 131    int i;
 132    MIPSGICTimerState *gictimer = g_new(MIPSGICTimerState, 1);
 133    gictimer->vptimers = g_new(MIPSGICTimerVPState, nvps);
 134    gictimer->countstop = 1;
 135    gictimer->num_vps = nvps;
 136    gictimer->opaque = opaque;
 137    gictimer->cb = cb;
 138    for (i = 0; i < nvps; i++) {
 139        gictimer->vptimers[i].gictimer = gictimer;
 140        gictimer->vptimers[i].vp_index = i;
 141        gictimer->vptimers[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
 142                                            &gic_vptimer_cb,
 143                                            &gictimer->vptimers[i]);
 144    }
 145    return gictimer;
 146}
 147