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