]> git.proxmox.com Git - mirror_qemu.git/blame - hw/intc/riscv_aclint.c
hw/intc: Rename sifive_clint sources to riscv_aclint sources
[mirror_qemu.git] / hw / intc / riscv_aclint.c
CommitLineData
1c77c410
MC
1/*
2 * SiFive CLINT (Core Local Interruptor)
3 *
4 * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
5 * Copyright (c) 2017 SiFive, Inc.
6 *
7 * This provides real-time clock, timer and interprocessor interrupts.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms and conditions of the GNU General Public License,
11 * version 2 or later, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "qemu/osdep.h"
3e80f690 23#include "qapi/error.h"
1c77c410 24#include "qemu/error-report.h"
0b8fa32f 25#include "qemu/module.h"
1c77c410
MC
26#include "hw/sysbus.h"
27#include "target/riscv/cpu.h"
a27bd6c7 28#include "hw/qdev-properties.h"
cc63a182 29#include "hw/intc/riscv_aclint.h"
1c77c410 30#include "qemu/timer.h"
a714b8aa
AF
31#include "hw/irq.h"
32
33typedef struct sifive_clint_callback {
34 SiFiveCLINTState *s;
35 int num;
36} sifive_clint_callback;
1c77c410 37
a47ef6e9 38static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
1c77c410 39{
2a8756ed 40 return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
a47ef6e9 41 timebase_freq, NANOSECONDS_PER_SECOND);
1c77c410
MC
42}
43
44/*
45 * Called when timecmp is written to update the QEMU timer or immediately
46 * trigger timer interrupt if mtimecmp <= current timer value.
47 */
a714b8aa
AF
48static void sifive_clint_write_timecmp(SiFiveCLINTState *s, RISCVCPU *cpu,
49 int hartid,
50 uint64_t value,
a47ef6e9 51 uint32_t timebase_freq)
1c77c410
MC
52{
53 uint64_t next;
54 uint64_t diff;
55
a47ef6e9 56 uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq);
1c77c410
MC
57
58 cpu->env.timecmp = value;
59 if (cpu->env.timecmp <= rtc_r) {
60 /* if we're setting an MTIMECMP value in the "past",
61 immediately raise the timer interrupt */
a714b8aa 62 qemu_irq_raise(s->timer_irqs[hartid - s->hartid_base]);
1c77c410
MC
63 return;
64 }
65
66 /* otherwise, set up the future timer interrupt */
a714b8aa 67 qemu_irq_lower(s->timer_irqs[hartid - s->hartid_base]);
1c77c410
MC
68 diff = cpu->env.timecmp - rtc_r;
69 /* back to ns (note args switched in muldiv64) */
4dc06bb8
DH
70 uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq);
71
72 /*
73 * check if ns_diff overflowed and check if the addition would potentially
74 * overflow
75 */
76 if ((NANOSECONDS_PER_SECOND > timebase_freq && ns_diff < diff) ||
77 ns_diff > INT64_MAX) {
78 next = INT64_MAX;
79 } else {
80 /*
81 * as it is very unlikely qemu_clock_get_ns will return a value
82 * greater than INT64_MAX, no additional check is needed for an
83 * unsigned integer overflow.
84 */
85 next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns_diff;
86 /*
87 * if ns_diff is INT64_MAX next may still be outside the range
88 * of a signed integer.
89 */
90 next = MIN(next, INT64_MAX);
91 }
92
1c77c410
MC
93 timer_mod(cpu->env.timer, next);
94}
95
96/*
97 * Callback used when the timer set using timer_mod expires.
98 * Should raise the timer interrupt line
99 */
100static void sifive_clint_timer_cb(void *opaque)
101{
a714b8aa
AF
102 sifive_clint_callback *state = opaque;
103
104 qemu_irq_raise(state->s->timer_irqs[state->num]);
1c77c410
MC
105}
106
107/* CPU wants to read rtc or timecmp register */
108static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size)
109{
110 SiFiveCLINTState *clint = opaque;
111 if (addr >= clint->sip_base &&
112 addr < clint->sip_base + (clint->num_harts << 2)) {
3bf03f08 113 size_t hartid = clint->hartid_base + ((addr - clint->sip_base) >> 2);
1c77c410
MC
114 CPUState *cpu = qemu_get_cpu(hartid);
115 CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
116 if (!env) {
117 error_report("clint: invalid timecmp hartid: %zu", hartid);
118 } else if ((addr & 0x3) == 0) {
119 return (env->mip & MIP_MSIP) > 0;
120 } else {
121 error_report("clint: invalid read: %08x", (uint32_t)addr);
122 return 0;
123 }
124 } else if (addr >= clint->timecmp_base &&
125 addr < clint->timecmp_base + (clint->num_harts << 3)) {
3bf03f08
AP
126 size_t hartid = clint->hartid_base +
127 ((addr - clint->timecmp_base) >> 3);
1c77c410
MC
128 CPUState *cpu = qemu_get_cpu(hartid);
129 CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
130 if (!env) {
131 error_report("clint: invalid timecmp hartid: %zu", hartid);
132 } else if ((addr & 0x7) == 0) {
133 /* timecmp_lo */
134 uint64_t timecmp = env->timecmp;
135 return timecmp & 0xFFFFFFFF;
136 } else if ((addr & 0x7) == 4) {
137 /* timecmp_hi */
138 uint64_t timecmp = env->timecmp;
139 return (timecmp >> 32) & 0xFFFFFFFF;
140 } else {
141 error_report("clint: invalid read: %08x", (uint32_t)addr);
142 return 0;
143 }
144 } else if (addr == clint->time_base) {
145 /* time_lo */
a47ef6e9 146 return cpu_riscv_read_rtc(clint->timebase_freq) & 0xFFFFFFFF;
1c77c410
MC
147 } else if (addr == clint->time_base + 4) {
148 /* time_hi */
a47ef6e9 149 return (cpu_riscv_read_rtc(clint->timebase_freq) >> 32) & 0xFFFFFFFF;
1c77c410
MC
150 }
151
152 error_report("clint: invalid read: %08x", (uint32_t)addr);
153 return 0;
154}
155
156/* CPU wrote to rtc or timecmp register */
157static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
158 unsigned size)
159{
160 SiFiveCLINTState *clint = opaque;
161
162 if (addr >= clint->sip_base &&
163 addr < clint->sip_base + (clint->num_harts << 2)) {
3bf03f08 164 size_t hartid = clint->hartid_base + ((addr - clint->sip_base) >> 2);
1c77c410
MC
165 CPUState *cpu = qemu_get_cpu(hartid);
166 CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
167 if (!env) {
168 error_report("clint: invalid timecmp hartid: %zu", hartid);
169 } else if ((addr & 0x3) == 0) {
a714b8aa 170 qemu_set_irq(clint->soft_irqs[hartid - clint->hartid_base], value);
1c77c410
MC
171 } else {
172 error_report("clint: invalid sip write: %08x", (uint32_t)addr);
173 }
174 return;
175 } else if (addr >= clint->timecmp_base &&
176 addr < clint->timecmp_base + (clint->num_harts << 3)) {
3bf03f08
AP
177 size_t hartid = clint->hartid_base +
178 ((addr - clint->timecmp_base) >> 3);
1c77c410
MC
179 CPUState *cpu = qemu_get_cpu(hartid);
180 CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
181 if (!env) {
182 error_report("clint: invalid timecmp hartid: %zu", hartid);
183 } else if ((addr & 0x7) == 0) {
184 /* timecmp_lo */
ef9e41df 185 uint64_t timecmp_hi = env->timecmp >> 32;
a714b8aa 186 sifive_clint_write_timecmp(clint, RISCV_CPU(cpu), hartid,
a47ef6e9 187 timecmp_hi << 32 | (value & 0xFFFFFFFF), clint->timebase_freq);
1c77c410
MC
188 return;
189 } else if ((addr & 0x7) == 4) {
190 /* timecmp_hi */
ef9e41df 191 uint64_t timecmp_lo = env->timecmp;
a714b8aa 192 sifive_clint_write_timecmp(clint, RISCV_CPU(cpu), hartid,
a47ef6e9 193 value << 32 | (timecmp_lo & 0xFFFFFFFF), clint->timebase_freq);
1c77c410
MC
194 } else {
195 error_report("clint: invalid timecmp write: %08x", (uint32_t)addr);
196 }
197 return;
198 } else if (addr == clint->time_base) {
199 /* time_lo */
200 error_report("clint: time_lo write not implemented");
201 return;
202 } else if (addr == clint->time_base + 4) {
203 /* time_hi */
204 error_report("clint: time_hi write not implemented");
205 return;
206 }
207
208 error_report("clint: invalid write: %08x", (uint32_t)addr);
209}
210
211static const MemoryRegionOps sifive_clint_ops = {
212 .read = sifive_clint_read,
213 .write = sifive_clint_write,
214 .endianness = DEVICE_LITTLE_ENDIAN,
215 .valid = {
216 .min_access_size = 4,
70b78d4e 217 .max_access_size = 8
1c77c410
MC
218 }
219};
220
221static Property sifive_clint_properties[] = {
3bf03f08 222 DEFINE_PROP_UINT32("hartid-base", SiFiveCLINTState, hartid_base, 0),
1c77c410
MC
223 DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0),
224 DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0),
225 DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0),
226 DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0),
227 DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0),
a47ef6e9 228 DEFINE_PROP_UINT32("timebase-freq", SiFiveCLINTState, timebase_freq, 0),
1c77c410
MC
229 DEFINE_PROP_END_OF_LIST(),
230};
231
232static void sifive_clint_realize(DeviceState *dev, Error **errp)
233{
234 SiFiveCLINTState *s = SIFIVE_CLINT(dev);
235 memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s,
236 TYPE_SIFIVE_CLINT, s->aperture_size);
237 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
a714b8aa
AF
238
239 s->timer_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts);
240 qdev_init_gpio_out(dev, s->timer_irqs, s->num_harts);
241
242 s->soft_irqs = g_malloc(sizeof(qemu_irq) * s->num_harts);
243 qdev_init_gpio_out(dev, s->soft_irqs, s->num_harts);
1c77c410
MC
244}
245
246static void sifive_clint_class_init(ObjectClass *klass, void *data)
247{
248 DeviceClass *dc = DEVICE_CLASS(klass);
249 dc->realize = sifive_clint_realize;
4f67d30b 250 device_class_set_props(dc, sifive_clint_properties);
1c77c410
MC
251}
252
253static const TypeInfo sifive_clint_info = {
254 .name = TYPE_SIFIVE_CLINT,
255 .parent = TYPE_SYS_BUS_DEVICE,
256 .instance_size = sizeof(SiFiveCLINTState),
257 .class_init = sifive_clint_class_init,
258};
259
260static void sifive_clint_register_types(void)
261{
262 type_register_static(&sifive_clint_info);
263}
264
265type_init(sifive_clint_register_types)
266
1c77c410
MC
267/*
268 * Create CLINT device.
269 */
3bf03f08
AP
270DeviceState *sifive_clint_create(hwaddr addr, hwaddr size,
271 uint32_t hartid_base, uint32_t num_harts, uint32_t sip_base,
a47ef6e9
BM
272 uint32_t timecmp_base, uint32_t time_base, uint32_t timebase_freq,
273 bool provide_rdtime)
1c77c410
MC
274{
275 int i;
a714b8aa
AF
276
277 DeviceState *dev = qdev_new(TYPE_SIFIVE_CLINT);
278 qdev_prop_set_uint32(dev, "hartid-base", hartid_base);
279 qdev_prop_set_uint32(dev, "num-harts", num_harts);
280 qdev_prop_set_uint32(dev, "sip-base", sip_base);
281 qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
282 qdev_prop_set_uint32(dev, "time-base", time_base);
283 qdev_prop_set_uint32(dev, "aperture-size", size);
284 qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq);
285 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
286 sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
287
1c77c410 288 for (i = 0; i < num_harts; i++) {
3bf03f08 289 CPUState *cpu = qemu_get_cpu(hartid_base + i);
a714b8aa 290 RISCVCPU *rvcpu = RISCV_CPU(cpu);
1c77c410 291 CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
a714b8aa
AF
292 sifive_clint_callback *cb = g_malloc0(sizeof(sifive_clint_callback));
293
1c77c410 294 if (!env) {
a714b8aa 295 g_free(cb);
1c77c410
MC
296 continue;
297 }
5f3616cc 298 if (provide_rdtime) {
a47ef6e9 299 riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq);
5f3616cc 300 }
a714b8aa
AF
301
302 cb->s = SIFIVE_CLINT(dev);
303 cb->num = i;
1c77c410 304 env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
a714b8aa 305 &sifive_clint_timer_cb, cb);
1c77c410 306 env->timecmp = 0;
a714b8aa
AF
307
308 qdev_connect_gpio_out(dev, i,
309 qdev_get_gpio_in(DEVICE(rvcpu), IRQ_M_TIMER));
310 qdev_connect_gpio_out(dev, num_harts + i,
311 qdev_get_gpio_in(DEVICE(rvcpu), IRQ_M_SOFT));
1c77c410
MC
312 }
313
1c77c410
MC
314 return dev;
315}