xref: /qemu/hw/timer/mips_gictimer.c (revision f6527ead)
140514051SYongbok Kim /*
240514051SYongbok Kim  * This file is subject to the terms and conditions of the GNU General Public
340514051SYongbok Kim  * License.  See the file "COPYING" in the main directory of this archive
440514051SYongbok Kim  * for more details.
540514051SYongbok Kim  *
640514051SYongbok Kim  * Copyright (C) 2016 Imagination Technologies
740514051SYongbok Kim  */
840514051SYongbok Kim 
940514051SYongbok Kim #include "qemu/osdep.h"
1040514051SYongbok Kim #include "qemu/timer.h"
1140514051SYongbok Kim #include "hw/timer/mips_gictimer.h"
1240514051SYongbok Kim 
1340514051SYongbok Kim #define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
1440514051SYongbok Kim 
mips_gictimer_get_freq(MIPSGICTimerState * gic)15*eb90ab94SPaul Burton uint32_t mips_gictimer_get_freq(MIPSGICTimerState *gic)
16*eb90ab94SPaul Burton {
17*eb90ab94SPaul Burton     return NANOSECONDS_PER_SECOND / TIMER_PERIOD;
18*eb90ab94SPaul Burton }
19*eb90ab94SPaul Burton 
gic_vptimer_update(MIPSGICTimerState * gictimer,uint32_t vp_index,uint64_t now)2040514051SYongbok Kim static void gic_vptimer_update(MIPSGICTimerState *gictimer,
2140514051SYongbok Kim                                    uint32_t vp_index, uint64_t now)
2240514051SYongbok Kim {
2340514051SYongbok Kim     uint64_t next;
2440514051SYongbok Kim     uint32_t wait;
2540514051SYongbok Kim 
2640514051SYongbok Kim     wait = gictimer->vptimers[vp_index].comparelo - gictimer->sh_counterlo -
2740514051SYongbok Kim            (uint32_t)(now / TIMER_PERIOD);
2840514051SYongbok Kim     next = now + (uint64_t)wait * TIMER_PERIOD;
2940514051SYongbok Kim 
3040514051SYongbok Kim     timer_mod(gictimer->vptimers[vp_index].qtimer, next);
3140514051SYongbok Kim }
3240514051SYongbok Kim 
gic_vptimer_expire(MIPSGICTimerState * gictimer,uint32_t vp_index,uint64_t now)3340514051SYongbok Kim static void gic_vptimer_expire(MIPSGICTimerState *gictimer, uint32_t vp_index,
3440514051SYongbok Kim                                uint64_t now)
3540514051SYongbok Kim {
3640514051SYongbok Kim     if (gictimer->countstop) {
3740514051SYongbok Kim         /* timer stopped */
3840514051SYongbok Kim         return;
3940514051SYongbok Kim     }
4040514051SYongbok Kim     gictimer->cb(gictimer->opaque, vp_index);
4140514051SYongbok Kim     gic_vptimer_update(gictimer, vp_index, now);
4240514051SYongbok Kim }
4340514051SYongbok Kim 
gic_vptimer_cb(void * opaque)4440514051SYongbok Kim static void gic_vptimer_cb(void *opaque)
4540514051SYongbok Kim {
4640514051SYongbok Kim     MIPSGICTimerVPState *vptimer = opaque;
4740514051SYongbok Kim     MIPSGICTimerState *gictimer = vptimer->gictimer;
4840514051SYongbok Kim     gic_vptimer_expire(gictimer, vptimer->vp_index,
4940514051SYongbok Kim                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
5040514051SYongbok Kim }
5140514051SYongbok Kim 
mips_gictimer_get_sh_count(MIPSGICTimerState * gictimer)5240514051SYongbok Kim uint32_t mips_gictimer_get_sh_count(MIPSGICTimerState *gictimer)
5340514051SYongbok Kim {
5440514051SYongbok Kim     int i;
5540514051SYongbok Kim     if (gictimer->countstop) {
5640514051SYongbok Kim         return gictimer->sh_counterlo;
5740514051SYongbok Kim     } else {
5840514051SYongbok Kim         uint64_t now;
5940514051SYongbok Kim         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
6040514051SYongbok Kim         for (i = 0; i < gictimer->num_vps; i++) {
6140514051SYongbok Kim             if (timer_pending(gictimer->vptimers[i].qtimer)
6240514051SYongbok Kim                 && timer_expired(gictimer->vptimers[i].qtimer, now)) {
6340514051SYongbok Kim                 /* The timer has already expired.  */
6440514051SYongbok Kim                 gic_vptimer_expire(gictimer, i, now);
6540514051SYongbok Kim             }
6640514051SYongbok Kim         }
6740514051SYongbok Kim         return gictimer->sh_counterlo + (uint32_t)(now / TIMER_PERIOD);
6840514051SYongbok Kim     }
6940514051SYongbok Kim }
7040514051SYongbok Kim 
mips_gictimer_store_sh_count(MIPSGICTimerState * gictimer,uint64_t count)7140514051SYongbok Kim void mips_gictimer_store_sh_count(MIPSGICTimerState *gictimer, uint64_t count)
7240514051SYongbok Kim {
7340514051SYongbok Kim     int i;
7440514051SYongbok Kim     uint64_t now;
7540514051SYongbok Kim 
7640514051SYongbok Kim     if (gictimer->countstop || !gictimer->vptimers[0].qtimer) {
7740514051SYongbok Kim         gictimer->sh_counterlo = count;
7840514051SYongbok Kim     } else {
7940514051SYongbok Kim         /* Store new count register */
8040514051SYongbok Kim         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
8140514051SYongbok Kim         gictimer->sh_counterlo = count - (uint32_t)(now / TIMER_PERIOD);
8240514051SYongbok Kim         /* Update timer timer */
8340514051SYongbok Kim         for (i = 0; i < gictimer->num_vps; i++) {
8440514051SYongbok Kim             gic_vptimer_update(gictimer, i, now);
8540514051SYongbok Kim         }
8640514051SYongbok Kim     }
8740514051SYongbok Kim }
8840514051SYongbok Kim 
mips_gictimer_get_vp_compare(MIPSGICTimerState * gictimer,uint32_t vp_index)8940514051SYongbok Kim uint32_t mips_gictimer_get_vp_compare(MIPSGICTimerState *gictimer,
9040514051SYongbok Kim                                       uint32_t vp_index)
9140514051SYongbok Kim {
9240514051SYongbok Kim     return gictimer->vptimers[vp_index].comparelo;
9340514051SYongbok Kim }
9440514051SYongbok Kim 
mips_gictimer_store_vp_compare(MIPSGICTimerState * gictimer,uint32_t vp_index,uint64_t compare)9540514051SYongbok Kim void mips_gictimer_store_vp_compare(MIPSGICTimerState *gictimer,
9640514051SYongbok Kim                                     uint32_t vp_index, uint64_t compare)
9740514051SYongbok Kim {
9840514051SYongbok Kim     gictimer->vptimers[vp_index].comparelo = (uint32_t) compare;
9940514051SYongbok Kim     gic_vptimer_update(gictimer, vp_index,
10040514051SYongbok Kim                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
10140514051SYongbok Kim }
10240514051SYongbok Kim 
mips_gictimer_get_countstop(MIPSGICTimerState * gictimer)10340514051SYongbok Kim uint8_t mips_gictimer_get_countstop(MIPSGICTimerState *gictimer)
10440514051SYongbok Kim {
10540514051SYongbok Kim     return gictimer->countstop;
10640514051SYongbok Kim }
10740514051SYongbok Kim 
mips_gictimer_start_count(MIPSGICTimerState * gictimer)10840514051SYongbok Kim void mips_gictimer_start_count(MIPSGICTimerState *gictimer)
10940514051SYongbok Kim {
11040514051SYongbok Kim     gictimer->countstop = 0;
11140514051SYongbok Kim     mips_gictimer_store_sh_count(gictimer, gictimer->sh_counterlo);
11240514051SYongbok Kim }
11340514051SYongbok Kim 
mips_gictimer_stop_count(MIPSGICTimerState * gictimer)11440514051SYongbok Kim void mips_gictimer_stop_count(MIPSGICTimerState *gictimer)
11540514051SYongbok Kim {
11640514051SYongbok Kim     int i;
11740514051SYongbok Kim 
11840514051SYongbok Kim     gictimer->countstop = 1;
11940514051SYongbok Kim     /* Store the current value */
12040514051SYongbok Kim     gictimer->sh_counterlo +=
12140514051SYongbok Kim         (uint32_t)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD);
12240514051SYongbok Kim     for (i = 0; i < gictimer->num_vps; i++) {
12340514051SYongbok Kim         timer_del(gictimer->vptimers[i].qtimer);
12440514051SYongbok Kim     }
12540514051SYongbok Kim }
12640514051SYongbok Kim 
mips_gictimer_init(void * opaque,uint32_t nvps,MIPSGICTimerCB * cb)12740514051SYongbok Kim MIPSGICTimerState *mips_gictimer_init(void *opaque, uint32_t nvps,
12840514051SYongbok Kim                                       MIPSGICTimerCB *cb)
12940514051SYongbok Kim {
13040514051SYongbok Kim     int i;
13140514051SYongbok Kim     MIPSGICTimerState *gictimer = g_new(MIPSGICTimerState, 1);
13240514051SYongbok Kim     gictimer->vptimers = g_new(MIPSGICTimerVPState, nvps);
13340514051SYongbok Kim     gictimer->countstop = 1;
13440514051SYongbok Kim     gictimer->num_vps = nvps;
13540514051SYongbok Kim     gictimer->opaque = opaque;
13640514051SYongbok Kim     gictimer->cb = cb;
13740514051SYongbok Kim     for (i = 0; i < nvps; i++) {
13840514051SYongbok Kim         gictimer->vptimers[i].gictimer = gictimer;
13940514051SYongbok Kim         gictimer->vptimers[i].vp_index = i;
14040514051SYongbok Kim         gictimer->vptimers[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
14140514051SYongbok Kim                                             &gic_vptimer_cb,
14240514051SYongbok Kim                                             &gictimer->vptimers[i]);
14340514051SYongbok Kim     }
14440514051SYongbok Kim     return gictimer;
14540514051SYongbok Kim }
146