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