1df41cbd6SAlistair Francis /*
2df41cbd6SAlistair Francis * QEMU lowRISC Ibex Timer device
3df41cbd6SAlistair Francis *
4df41cbd6SAlistair Francis * Copyright (c) 2021 Western Digital
5df41cbd6SAlistair Francis *
6df41cbd6SAlistair Francis * For details check the documentation here:
7df41cbd6SAlistair Francis * https://docs.opentitan.org/hw/ip/rv_timer/doc/
8df41cbd6SAlistair Francis *
9df41cbd6SAlistair Francis * Permission is hereby granted, free of charge, to any person obtaining a copy
10df41cbd6SAlistair Francis * of this software and associated documentation files (the "Software"), to deal
11df41cbd6SAlistair Francis * in the Software without restriction, including without limitation the rights
12df41cbd6SAlistair Francis * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13df41cbd6SAlistair Francis * copies of the Software, and to permit persons to whom the Software is
14df41cbd6SAlistair Francis * furnished to do so, subject to the following conditions:
15df41cbd6SAlistair Francis *
16df41cbd6SAlistair Francis * The above copyright notice and this permission notice shall be included in
17df41cbd6SAlistair Francis * all copies or substantial portions of the Software.
18df41cbd6SAlistair Francis *
19df41cbd6SAlistair Francis * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20df41cbd6SAlistair Francis * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21df41cbd6SAlistair Francis * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22df41cbd6SAlistair Francis * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23df41cbd6SAlistair Francis * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24df41cbd6SAlistair Francis * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25df41cbd6SAlistair Francis * THE SOFTWARE.
26df41cbd6SAlistair Francis */
27df41cbd6SAlistair Francis
28df41cbd6SAlistair Francis #include "qemu/osdep.h"
29df41cbd6SAlistair Francis #include "qemu/log.h"
30df41cbd6SAlistair Francis #include "qemu/timer.h"
31df41cbd6SAlistair Francis #include "hw/timer/ibex_timer.h"
32df41cbd6SAlistair Francis #include "hw/irq.h"
33df41cbd6SAlistair Francis #include "hw/qdev-properties.h"
34df41cbd6SAlistair Francis #include "target/riscv/cpu.h"
35df41cbd6SAlistair Francis #include "migration/vmstate.h"
36df41cbd6SAlistair Francis
37dda94e5cSWilfred Mallawa REG32(ALERT_TEST, 0x00)
38dda94e5cSWilfred Mallawa FIELD(ALERT_TEST, FATAL_FAULT, 0, 1)
39dda94e5cSWilfred Mallawa REG32(CTRL, 0x04)
40df41cbd6SAlistair Francis FIELD(CTRL, ACTIVE, 0, 1)
41df41cbd6SAlistair Francis REG32(CFG0, 0x100)
42df41cbd6SAlistair Francis FIELD(CFG0, PRESCALE, 0, 12)
43df41cbd6SAlistair Francis FIELD(CFG0, STEP, 16, 8)
44df41cbd6SAlistair Francis REG32(LOWER0, 0x104)
45df41cbd6SAlistair Francis REG32(UPPER0, 0x108)
46df41cbd6SAlistair Francis REG32(COMPARE_LOWER0, 0x10C)
47df41cbd6SAlistair Francis REG32(COMPARE_UPPER0, 0x110)
48df41cbd6SAlistair Francis REG32(INTR_ENABLE, 0x114)
49df41cbd6SAlistair Francis FIELD(INTR_ENABLE, IE_0, 0, 1)
50df41cbd6SAlistair Francis REG32(INTR_STATE, 0x118)
51df41cbd6SAlistair Francis FIELD(INTR_STATE, IS_0, 0, 1)
52df41cbd6SAlistair Francis REG32(INTR_TEST, 0x11C)
53df41cbd6SAlistair Francis FIELD(INTR_TEST, T_0, 0, 1)
54df41cbd6SAlistair Francis
cpu_riscv_read_rtc(uint32_t timebase_freq)55df41cbd6SAlistair Francis static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
56df41cbd6SAlistair Francis {
57df41cbd6SAlistair Francis return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
58df41cbd6SAlistair Francis timebase_freq, NANOSECONDS_PER_SECOND);
59df41cbd6SAlistair Francis }
60df41cbd6SAlistair Francis
ibex_timer_update_irqs(IbexTimerState * s)61df41cbd6SAlistair Francis static void ibex_timer_update_irqs(IbexTimerState *s)
62df41cbd6SAlistair Francis {
63df41cbd6SAlistair Francis uint64_t value = s->timer_compare_lower0 |
64df41cbd6SAlistair Francis ((uint64_t)s->timer_compare_upper0 << 32);
65df41cbd6SAlistair Francis uint64_t next, diff;
66df41cbd6SAlistair Francis uint64_t now = cpu_riscv_read_rtc(s->timebase_freq);
67df41cbd6SAlistair Francis
68df41cbd6SAlistair Francis if (!(s->timer_ctrl & R_CTRL_ACTIVE_MASK)) {
69df41cbd6SAlistair Francis /* Timer isn't active */
70df41cbd6SAlistair Francis return;
71df41cbd6SAlistair Francis }
72df41cbd6SAlistair Francis
73df41cbd6SAlistair Francis /* Update the CPUs mtimecmp */
747cbcc538SAtish Patra s->mtimecmp = value;
75df41cbd6SAlistair Francis
767cbcc538SAtish Patra if (s->mtimecmp <= now) {
77df41cbd6SAlistair Francis /*
78df41cbd6SAlistair Francis * If the mtimecmp was in the past raise the interrupt now.
79df41cbd6SAlistair Francis */
8057a3a622SAlistair Francis qemu_irq_raise(s->m_timer_irq);
81df41cbd6SAlistair Francis if (s->timer_intr_enable & R_INTR_ENABLE_IE_0_MASK) {
82df41cbd6SAlistair Francis s->timer_intr_state |= R_INTR_STATE_IS_0_MASK;
83df41cbd6SAlistair Francis qemu_set_irq(s->irq, true);
84df41cbd6SAlistair Francis }
85df41cbd6SAlistair Francis return;
86df41cbd6SAlistair Francis }
87df41cbd6SAlistair Francis
88df41cbd6SAlistair Francis /* Setup a timer to trigger the interrupt in the future */
8957a3a622SAlistair Francis qemu_irq_lower(s->m_timer_irq);
90df41cbd6SAlistair Francis qemu_set_irq(s->irq, false);
91df41cbd6SAlistair Francis
927cbcc538SAtish Patra diff = s->mtimecmp - now;
93df41cbd6SAlistair Francis next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
94df41cbd6SAlistair Francis muldiv64(diff,
95df41cbd6SAlistair Francis NANOSECONDS_PER_SECOND,
96df41cbd6SAlistair Francis s->timebase_freq);
97df41cbd6SAlistair Francis
98df41cbd6SAlistair Francis if (next < qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
99df41cbd6SAlistair Francis /* We overflowed the timer, just set it as large as we can */
1007cbcc538SAtish Patra timer_mod(s->mtimer, 0x7FFFFFFFFFFFFFFF);
101df41cbd6SAlistair Francis } else {
1027cbcc538SAtish Patra timer_mod(s->mtimer, next);
103df41cbd6SAlistair Francis }
104df41cbd6SAlistair Francis }
105df41cbd6SAlistair Francis
ibex_timer_cb(void * opaque)106df41cbd6SAlistair Francis static void ibex_timer_cb(void *opaque)
107df41cbd6SAlistair Francis {
108df41cbd6SAlistair Francis IbexTimerState *s = opaque;
109df41cbd6SAlistair Francis
11057a3a622SAlistair Francis qemu_irq_raise(s->m_timer_irq);
111df41cbd6SAlistair Francis if (s->timer_intr_enable & R_INTR_ENABLE_IE_0_MASK) {
112df41cbd6SAlistair Francis s->timer_intr_state |= R_INTR_STATE_IS_0_MASK;
113df41cbd6SAlistair Francis qemu_set_irq(s->irq, true);
114df41cbd6SAlistair Francis }
115df41cbd6SAlistair Francis }
116df41cbd6SAlistair Francis
ibex_timer_reset(DeviceState * dev)117df41cbd6SAlistair Francis static void ibex_timer_reset(DeviceState *dev)
118df41cbd6SAlistair Francis {
119df41cbd6SAlistair Francis IbexTimerState *s = IBEX_TIMER(dev);
120df41cbd6SAlistair Francis
1217cbcc538SAtish Patra s->mtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
122df41cbd6SAlistair Francis &ibex_timer_cb, s);
1237cbcc538SAtish Patra s->mtimecmp = 0;
124df41cbd6SAlistair Francis
125df41cbd6SAlistair Francis s->timer_ctrl = 0x00000000;
126df41cbd6SAlistair Francis s->timer_cfg0 = 0x00010000;
127df41cbd6SAlistair Francis s->timer_compare_lower0 = 0xFFFFFFFF;
128df41cbd6SAlistair Francis s->timer_compare_upper0 = 0xFFFFFFFF;
129df41cbd6SAlistair Francis s->timer_intr_enable = 0x00000000;
130df41cbd6SAlistair Francis s->timer_intr_state = 0x00000000;
131df41cbd6SAlistair Francis
132df41cbd6SAlistair Francis ibex_timer_update_irqs(s);
133df41cbd6SAlistair Francis }
134df41cbd6SAlistair Francis
ibex_timer_read(void * opaque,hwaddr addr,unsigned int size)135df41cbd6SAlistair Francis static uint64_t ibex_timer_read(void *opaque, hwaddr addr,
136df41cbd6SAlistair Francis unsigned int size)
137df41cbd6SAlistair Francis {
138df41cbd6SAlistair Francis IbexTimerState *s = opaque;
139df41cbd6SAlistair Francis uint64_t now = cpu_riscv_read_rtc(s->timebase_freq);
140df41cbd6SAlistair Francis uint64_t retvalue = 0;
141df41cbd6SAlistair Francis
142df41cbd6SAlistair Francis switch (addr >> 2) {
143dda94e5cSWilfred Mallawa case R_ALERT_TEST:
144dda94e5cSWilfred Mallawa qemu_log_mask(LOG_GUEST_ERROR,
145dda94e5cSWilfred Mallawa "Attempted to read ALERT_TEST, a write only register");
146dda94e5cSWilfred Mallawa break;
147df41cbd6SAlistair Francis case R_CTRL:
148df41cbd6SAlistair Francis retvalue = s->timer_ctrl;
149df41cbd6SAlistair Francis break;
150df41cbd6SAlistair Francis case R_CFG0:
151df41cbd6SAlistair Francis retvalue = s->timer_cfg0;
152df41cbd6SAlistair Francis break;
153df41cbd6SAlistair Francis case R_LOWER0:
154df41cbd6SAlistair Francis retvalue = now;
155df41cbd6SAlistair Francis break;
156df41cbd6SAlistair Francis case R_UPPER0:
157df41cbd6SAlistair Francis retvalue = now >> 32;
158df41cbd6SAlistair Francis break;
159df41cbd6SAlistair Francis case R_COMPARE_LOWER0:
160df41cbd6SAlistair Francis retvalue = s->timer_compare_lower0;
161df41cbd6SAlistair Francis break;
162df41cbd6SAlistair Francis case R_COMPARE_UPPER0:
163df41cbd6SAlistair Francis retvalue = s->timer_compare_upper0;
164df41cbd6SAlistair Francis break;
165df41cbd6SAlistair Francis case R_INTR_ENABLE:
166df41cbd6SAlistair Francis retvalue = s->timer_intr_enable;
167df41cbd6SAlistair Francis break;
168df41cbd6SAlistair Francis case R_INTR_STATE:
169df41cbd6SAlistair Francis retvalue = s->timer_intr_state;
170df41cbd6SAlistair Francis break;
171df41cbd6SAlistair Francis case R_INTR_TEST:
17228ca4689SWilfred Mallawa qemu_log_mask(LOG_GUEST_ERROR,
17328ca4689SWilfred Mallawa "Attempted to read INTR_TEST, a write only register");
174df41cbd6SAlistair Francis break;
175df41cbd6SAlistair Francis default:
176df41cbd6SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR,
177df41cbd6SAlistair Francis "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
178df41cbd6SAlistair Francis return 0;
179df41cbd6SAlistair Francis }
180df41cbd6SAlistair Francis
181df41cbd6SAlistair Francis return retvalue;
182df41cbd6SAlistair Francis }
183df41cbd6SAlistair Francis
ibex_timer_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)184df41cbd6SAlistair Francis static void ibex_timer_write(void *opaque, hwaddr addr,
185df41cbd6SAlistair Francis uint64_t val64, unsigned int size)
186df41cbd6SAlistair Francis {
187df41cbd6SAlistair Francis IbexTimerState *s = opaque;
188df41cbd6SAlistair Francis uint32_t val = val64;
189df41cbd6SAlistair Francis
190df41cbd6SAlistair Francis switch (addr >> 2) {
191dda94e5cSWilfred Mallawa case R_ALERT_TEST:
192dda94e5cSWilfred Mallawa qemu_log_mask(LOG_UNIMP, "Alert triggering not supported");
193dda94e5cSWilfred Mallawa break;
194df41cbd6SAlistair Francis case R_CTRL:
195df41cbd6SAlistair Francis s->timer_ctrl = val;
196df41cbd6SAlistair Francis break;
197df41cbd6SAlistair Francis case R_CFG0:
198df41cbd6SAlistair Francis qemu_log_mask(LOG_UNIMP, "Changing prescale or step not supported");
199df41cbd6SAlistair Francis s->timer_cfg0 = val;
200df41cbd6SAlistair Francis break;
201df41cbd6SAlistair Francis case R_LOWER0:
202df41cbd6SAlistair Francis qemu_log_mask(LOG_UNIMP, "Changing timer value is not supported");
203df41cbd6SAlistair Francis break;
204df41cbd6SAlistair Francis case R_UPPER0:
205df41cbd6SAlistair Francis qemu_log_mask(LOG_UNIMP, "Changing timer value is not supported");
206df41cbd6SAlistair Francis break;
207df41cbd6SAlistair Francis case R_COMPARE_LOWER0:
208df41cbd6SAlistair Francis s->timer_compare_lower0 = val;
209df41cbd6SAlistair Francis ibex_timer_update_irqs(s);
210df41cbd6SAlistair Francis break;
211df41cbd6SAlistair Francis case R_COMPARE_UPPER0:
212df41cbd6SAlistair Francis s->timer_compare_upper0 = val;
213df41cbd6SAlistair Francis ibex_timer_update_irqs(s);
214df41cbd6SAlistair Francis break;
215df41cbd6SAlistair Francis case R_INTR_ENABLE:
216df41cbd6SAlistair Francis s->timer_intr_enable = val;
217df41cbd6SAlistair Francis break;
218df41cbd6SAlistair Francis case R_INTR_STATE:
219df41cbd6SAlistair Francis /* Write 1 to clear */
220df41cbd6SAlistair Francis s->timer_intr_state &= ~val;
221df41cbd6SAlistair Francis break;
222df41cbd6SAlistair Francis case R_INTR_TEST:
22328ca4689SWilfred Mallawa if (s->timer_intr_enable & val & R_INTR_ENABLE_IE_0_MASK) {
224df41cbd6SAlistair Francis s->timer_intr_state |= R_INTR_STATE_IS_0_MASK;
225df41cbd6SAlistair Francis qemu_set_irq(s->irq, true);
226df41cbd6SAlistair Francis }
227df41cbd6SAlistair Francis break;
228df41cbd6SAlistair Francis default:
229df41cbd6SAlistair Francis qemu_log_mask(LOG_GUEST_ERROR,
230df41cbd6SAlistair Francis "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
231df41cbd6SAlistair Francis }
232df41cbd6SAlistair Francis }
233df41cbd6SAlistair Francis
234df41cbd6SAlistair Francis static const MemoryRegionOps ibex_timer_ops = {
235df41cbd6SAlistair Francis .read = ibex_timer_read,
236df41cbd6SAlistair Francis .write = ibex_timer_write,
237df41cbd6SAlistair Francis .endianness = DEVICE_NATIVE_ENDIAN,
238df41cbd6SAlistair Francis .impl.min_access_size = 4,
239df41cbd6SAlistair Francis .impl.max_access_size = 4,
240df41cbd6SAlistair Francis };
241df41cbd6SAlistair Francis
ibex_timer_post_load(void * opaque,int version_id)242df41cbd6SAlistair Francis static int ibex_timer_post_load(void *opaque, int version_id)
243df41cbd6SAlistair Francis {
244df41cbd6SAlistair Francis IbexTimerState *s = opaque;
245df41cbd6SAlistair Francis
246df41cbd6SAlistair Francis ibex_timer_update_irqs(s);
247df41cbd6SAlistair Francis return 0;
248df41cbd6SAlistair Francis }
249df41cbd6SAlistair Francis
250df41cbd6SAlistair Francis static const VMStateDescription vmstate_ibex_timer = {
251df41cbd6SAlistair Francis .name = TYPE_IBEX_TIMER,
25228ca4689SWilfred Mallawa .version_id = 2,
25328ca4689SWilfred Mallawa .minimum_version_id = 2,
254df41cbd6SAlistair Francis .post_load = ibex_timer_post_load,
255ba324b3fSRichard Henderson .fields = (const VMStateField[]) {
256df41cbd6SAlistair Francis VMSTATE_UINT32(timer_ctrl, IbexTimerState),
257df41cbd6SAlistair Francis VMSTATE_UINT32(timer_cfg0, IbexTimerState),
258df41cbd6SAlistair Francis VMSTATE_UINT32(timer_compare_lower0, IbexTimerState),
259df41cbd6SAlistair Francis VMSTATE_UINT32(timer_compare_upper0, IbexTimerState),
260df41cbd6SAlistair Francis VMSTATE_UINT32(timer_intr_enable, IbexTimerState),
261df41cbd6SAlistair Francis VMSTATE_UINT32(timer_intr_state, IbexTimerState),
262df41cbd6SAlistair Francis VMSTATE_END_OF_LIST()
263df41cbd6SAlistair Francis }
264df41cbd6SAlistair Francis };
265df41cbd6SAlistair Francis
266df41cbd6SAlistair Francis static Property ibex_timer_properties[] = {
267df41cbd6SAlistair Francis DEFINE_PROP_UINT32("timebase-freq", IbexTimerState, timebase_freq, 10000),
268df41cbd6SAlistair Francis DEFINE_PROP_END_OF_LIST(),
269df41cbd6SAlistair Francis };
270df41cbd6SAlistair Francis
ibex_timer_init(Object * obj)271df41cbd6SAlistair Francis static void ibex_timer_init(Object *obj)
272df41cbd6SAlistair Francis {
273df41cbd6SAlistair Francis IbexTimerState *s = IBEX_TIMER(obj);
274df41cbd6SAlistair Francis
275df41cbd6SAlistair Francis sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
276df41cbd6SAlistair Francis
277df41cbd6SAlistair Francis memory_region_init_io(&s->mmio, obj, &ibex_timer_ops, s,
278df41cbd6SAlistair Francis TYPE_IBEX_TIMER, 0x400);
279df41cbd6SAlistair Francis sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
280df41cbd6SAlistair Francis }
281df41cbd6SAlistair Francis
ibex_timer_realize(DeviceState * dev,Error ** errp)28257a3a622SAlistair Francis static void ibex_timer_realize(DeviceState *dev, Error **errp)
28357a3a622SAlistair Francis {
28457a3a622SAlistair Francis IbexTimerState *s = IBEX_TIMER(dev);
28557a3a622SAlistair Francis
28657a3a622SAlistair Francis qdev_init_gpio_out(dev, &s->m_timer_irq, 1);
28757a3a622SAlistair Francis }
28857a3a622SAlistair Francis
28957a3a622SAlistair Francis
ibex_timer_class_init(ObjectClass * klass,void * data)290df41cbd6SAlistair Francis static void ibex_timer_class_init(ObjectClass *klass, void *data)
291df41cbd6SAlistair Francis {
292df41cbd6SAlistair Francis DeviceClass *dc = DEVICE_CLASS(klass);
293df41cbd6SAlistair Francis
294*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, ibex_timer_reset);
295df41cbd6SAlistair Francis dc->vmsd = &vmstate_ibex_timer;
29657a3a622SAlistair Francis dc->realize = ibex_timer_realize;
297df41cbd6SAlistair Francis device_class_set_props(dc, ibex_timer_properties);
298df41cbd6SAlistair Francis }
299df41cbd6SAlistair Francis
300df41cbd6SAlistair Francis static const TypeInfo ibex_timer_info = {
301df41cbd6SAlistair Francis .name = TYPE_IBEX_TIMER,
302df41cbd6SAlistair Francis .parent = TYPE_SYS_BUS_DEVICE,
303df41cbd6SAlistair Francis .instance_size = sizeof(IbexTimerState),
304df41cbd6SAlistair Francis .instance_init = ibex_timer_init,
305df41cbd6SAlistair Francis .class_init = ibex_timer_class_init,
306df41cbd6SAlistair Francis };
307df41cbd6SAlistair Francis
ibex_timer_register_types(void)308df41cbd6SAlistair Francis static void ibex_timer_register_types(void)
309df41cbd6SAlistair Francis {
310df41cbd6SAlistair Francis type_register_static(&ibex_timer_info);
311df41cbd6SAlistair Francis }
312df41cbd6SAlistair Francis
313df41cbd6SAlistair Francis type_init(ibex_timer_register_types)
314