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