2 * SiFive CLINT (Core Local Interruptor)
4 * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
5 * Copyright (c) 2017 SiFive, Inc.
7 * This provides real-time clock, timer and interprocessor interrupts.
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.
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
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/>.
22 #include "qemu/osdep.h"
23 #include "qapi/error.h"
24 #include "qemu/error-report.h"
25 #include "qemu/module.h"
26 #include "hw/sysbus.h"
27 #include "target/riscv/cpu.h"
28 #include "hw/qdev-properties.h"
29 #include "hw/intc/sifive_clint.h"
30 #include "qemu/timer.h"
32 static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq
)
34 return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
),
35 timebase_freq
, NANOSECONDS_PER_SECOND
);
39 * Called when timecmp is written to update the QEMU timer or immediately
40 * trigger timer interrupt if mtimecmp <= current timer value.
42 static void sifive_clint_write_timecmp(RISCVCPU
*cpu
, uint64_t value
,
43 uint32_t timebase_freq
)
48 uint64_t rtc_r
= cpu_riscv_read_rtc(timebase_freq
);
50 cpu
->env
.timecmp
= value
;
51 if (cpu
->env
.timecmp
<= rtc_r
) {
52 /* if we're setting an MTIMECMP value in the "past",
53 immediately raise the timer interrupt */
54 riscv_cpu_update_mip(cpu
, MIP_MTIP
, BOOL_TO_MASK(1));
58 /* otherwise, set up the future timer interrupt */
59 riscv_cpu_update_mip(cpu
, MIP_MTIP
, BOOL_TO_MASK(0));
60 diff
= cpu
->env
.timecmp
- rtc_r
;
61 /* back to ns (note args switched in muldiv64) */
62 next
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) +
63 muldiv64(diff
, NANOSECONDS_PER_SECOND
, timebase_freq
);
64 timer_mod(cpu
->env
.timer
, next
);
68 * Callback used when the timer set using timer_mod expires.
69 * Should raise the timer interrupt line
71 static void sifive_clint_timer_cb(void *opaque
)
73 RISCVCPU
*cpu
= opaque
;
74 riscv_cpu_update_mip(cpu
, MIP_MTIP
, BOOL_TO_MASK(1));
77 /* CPU wants to read rtc or timecmp register */
78 static uint64_t sifive_clint_read(void *opaque
, hwaddr addr
, unsigned size
)
80 SiFiveCLINTState
*clint
= opaque
;
81 if (addr
>= clint
->sip_base
&&
82 addr
< clint
->sip_base
+ (clint
->num_harts
<< 2)) {
83 size_t hartid
= clint
->hartid_base
+ ((addr
- clint
->sip_base
) >> 2);
84 CPUState
*cpu
= qemu_get_cpu(hartid
);
85 CPURISCVState
*env
= cpu
? cpu
->env_ptr
: NULL
;
87 error_report("clint: invalid timecmp hartid: %zu", hartid
);
88 } else if ((addr
& 0x3) == 0) {
89 return (env
->mip
& MIP_MSIP
) > 0;
91 error_report("clint: invalid read: %08x", (uint32_t)addr
);
94 } else if (addr
>= clint
->timecmp_base
&&
95 addr
< clint
->timecmp_base
+ (clint
->num_harts
<< 3)) {
96 size_t hartid
= clint
->hartid_base
+
97 ((addr
- clint
->timecmp_base
) >> 3);
98 CPUState
*cpu
= qemu_get_cpu(hartid
);
99 CPURISCVState
*env
= cpu
? cpu
->env_ptr
: NULL
;
101 error_report("clint: invalid timecmp hartid: %zu", hartid
);
102 } else if ((addr
& 0x7) == 0) {
104 uint64_t timecmp
= env
->timecmp
;
105 return timecmp
& 0xFFFFFFFF;
106 } else if ((addr
& 0x7) == 4) {
108 uint64_t timecmp
= env
->timecmp
;
109 return (timecmp
>> 32) & 0xFFFFFFFF;
111 error_report("clint: invalid read: %08x", (uint32_t)addr
);
114 } else if (addr
== clint
->time_base
) {
116 return cpu_riscv_read_rtc(clint
->timebase_freq
) & 0xFFFFFFFF;
117 } else if (addr
== clint
->time_base
+ 4) {
119 return (cpu_riscv_read_rtc(clint
->timebase_freq
) >> 32) & 0xFFFFFFFF;
122 error_report("clint: invalid read: %08x", (uint32_t)addr
);
126 /* CPU wrote to rtc or timecmp register */
127 static void sifive_clint_write(void *opaque
, hwaddr addr
, uint64_t value
,
130 SiFiveCLINTState
*clint
= opaque
;
132 if (addr
>= clint
->sip_base
&&
133 addr
< clint
->sip_base
+ (clint
->num_harts
<< 2)) {
134 size_t hartid
= clint
->hartid_base
+ ((addr
- clint
->sip_base
) >> 2);
135 CPUState
*cpu
= qemu_get_cpu(hartid
);
136 CPURISCVState
*env
= cpu
? cpu
->env_ptr
: NULL
;
138 error_report("clint: invalid timecmp hartid: %zu", hartid
);
139 } else if ((addr
& 0x3) == 0) {
140 riscv_cpu_update_mip(RISCV_CPU(cpu
), MIP_MSIP
, BOOL_TO_MASK(value
));
142 error_report("clint: invalid sip write: %08x", (uint32_t)addr
);
145 } else if (addr
>= clint
->timecmp_base
&&
146 addr
< clint
->timecmp_base
+ (clint
->num_harts
<< 3)) {
147 size_t hartid
= clint
->hartid_base
+
148 ((addr
- clint
->timecmp_base
) >> 3);
149 CPUState
*cpu
= qemu_get_cpu(hartid
);
150 CPURISCVState
*env
= cpu
? cpu
->env_ptr
: NULL
;
152 error_report("clint: invalid timecmp hartid: %zu", hartid
);
153 } else if ((addr
& 0x7) == 0) {
155 uint64_t timecmp_hi
= env
->timecmp
>> 32;
156 sifive_clint_write_timecmp(RISCV_CPU(cpu
),
157 timecmp_hi
<< 32 | (value
& 0xFFFFFFFF), clint
->timebase_freq
);
159 } else if ((addr
& 0x7) == 4) {
161 uint64_t timecmp_lo
= env
->timecmp
;
162 sifive_clint_write_timecmp(RISCV_CPU(cpu
),
163 value
<< 32 | (timecmp_lo
& 0xFFFFFFFF), clint
->timebase_freq
);
165 error_report("clint: invalid timecmp write: %08x", (uint32_t)addr
);
168 } else if (addr
== clint
->time_base
) {
170 error_report("clint: time_lo write not implemented");
172 } else if (addr
== clint
->time_base
+ 4) {
174 error_report("clint: time_hi write not implemented");
178 error_report("clint: invalid write: %08x", (uint32_t)addr
);
181 static const MemoryRegionOps sifive_clint_ops
= {
182 .read
= sifive_clint_read
,
183 .write
= sifive_clint_write
,
184 .endianness
= DEVICE_LITTLE_ENDIAN
,
186 .min_access_size
= 4,
191 static Property sifive_clint_properties
[] = {
192 DEFINE_PROP_UINT32("hartid-base", SiFiveCLINTState
, hartid_base
, 0),
193 DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState
, num_harts
, 0),
194 DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState
, sip_base
, 0),
195 DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState
, timecmp_base
, 0),
196 DEFINE_PROP_UINT32("time-base", SiFiveCLINTState
, time_base
, 0),
197 DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState
, aperture_size
, 0),
198 DEFINE_PROP_UINT32("timebase-freq", SiFiveCLINTState
, timebase_freq
, 0),
199 DEFINE_PROP_END_OF_LIST(),
202 static void sifive_clint_realize(DeviceState
*dev
, Error
**errp
)
204 SiFiveCLINTState
*s
= SIFIVE_CLINT(dev
);
205 memory_region_init_io(&s
->mmio
, OBJECT(dev
), &sifive_clint_ops
, s
,
206 TYPE_SIFIVE_CLINT
, s
->aperture_size
);
207 sysbus_init_mmio(SYS_BUS_DEVICE(dev
), &s
->mmio
);
210 static void sifive_clint_class_init(ObjectClass
*klass
, void *data
)
212 DeviceClass
*dc
= DEVICE_CLASS(klass
);
213 dc
->realize
= sifive_clint_realize
;
214 device_class_set_props(dc
, sifive_clint_properties
);
217 static const TypeInfo sifive_clint_info
= {
218 .name
= TYPE_SIFIVE_CLINT
,
219 .parent
= TYPE_SYS_BUS_DEVICE
,
220 .instance_size
= sizeof(SiFiveCLINTState
),
221 .class_init
= sifive_clint_class_init
,
224 static void sifive_clint_register_types(void)
226 type_register_static(&sifive_clint_info
);
229 type_init(sifive_clint_register_types
)
233 * Create CLINT device.
235 DeviceState
*sifive_clint_create(hwaddr addr
, hwaddr size
,
236 uint32_t hartid_base
, uint32_t num_harts
, uint32_t sip_base
,
237 uint32_t timecmp_base
, uint32_t time_base
, uint32_t timebase_freq
,
241 for (i
= 0; i
< num_harts
; i
++) {
242 CPUState
*cpu
= qemu_get_cpu(hartid_base
+ i
);
243 CPURISCVState
*env
= cpu
? cpu
->env_ptr
: NULL
;
247 if (provide_rdtime
) {
248 riscv_cpu_set_rdtime_fn(env
, cpu_riscv_read_rtc
, timebase_freq
);
250 env
->timer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
,
251 &sifive_clint_timer_cb
, cpu
);
255 DeviceState
*dev
= qdev_new(TYPE_SIFIVE_CLINT
);
256 qdev_prop_set_uint32(dev
, "hartid-base", hartid_base
);
257 qdev_prop_set_uint32(dev
, "num-harts", num_harts
);
258 qdev_prop_set_uint32(dev
, "sip-base", sip_base
);
259 qdev_prop_set_uint32(dev
, "timecmp-base", timecmp_base
);
260 qdev_prop_set_uint32(dev
, "time-base", time_base
);
261 qdev_prop_set_uint32(dev
, "aperture-size", size
);
262 qdev_prop_set_uint32(dev
, "timebase-freq", timebase_freq
);
263 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev
), &error_fatal
);
264 sysbus_mmio_map(SYS_BUS_DEVICE(dev
), 0, addr
);