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