11c77c410SMichael Clark /* 21c77c410SMichael Clark * SiFive CLINT (Core Local Interruptor) 31c77c410SMichael Clark * 41c77c410SMichael Clark * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu 51c77c410SMichael Clark * Copyright (c) 2017 SiFive, Inc. 61c77c410SMichael Clark * 71c77c410SMichael Clark * This provides real-time clock, timer and interprocessor interrupts. 81c77c410SMichael Clark * 91c77c410SMichael Clark * This program is free software; you can redistribute it and/or modify it 101c77c410SMichael Clark * under the terms and conditions of the GNU General Public License, 111c77c410SMichael Clark * version 2 or later, as published by the Free Software Foundation. 121c77c410SMichael Clark * 131c77c410SMichael Clark * This program is distributed in the hope it will be useful, but WITHOUT 141c77c410SMichael Clark * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 151c77c410SMichael Clark * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 161c77c410SMichael Clark * more details. 171c77c410SMichael Clark * 181c77c410SMichael Clark * You should have received a copy of the GNU General Public License along with 191c77c410SMichael Clark * this program. If not, see <http://www.gnu.org/licenses/>. 201c77c410SMichael Clark */ 211c77c410SMichael Clark 221c77c410SMichael Clark #include "qemu/osdep.h" 23*3e80f690SMarkus Armbruster #include "qapi/error.h" 241c77c410SMichael Clark #include "qemu/error-report.h" 250b8fa32fSMarkus Armbruster #include "qemu/module.h" 261c77c410SMichael Clark #include "hw/sysbus.h" 271c77c410SMichael Clark #include "target/riscv/cpu.h" 28a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 291c77c410SMichael Clark #include "hw/riscv/sifive_clint.h" 301c77c410SMichael Clark #include "qemu/timer.h" 311c77c410SMichael Clark 321c77c410SMichael Clark static uint64_t cpu_riscv_read_rtc(void) 331c77c410SMichael Clark { 342a8756edSMichael Clark return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 352a8756edSMichael Clark SIFIVE_CLINT_TIMEBASE_FREQ, NANOSECONDS_PER_SECOND); 361c77c410SMichael Clark } 371c77c410SMichael Clark 381c77c410SMichael Clark /* 391c77c410SMichael Clark * Called when timecmp is written to update the QEMU timer or immediately 401c77c410SMichael Clark * trigger timer interrupt if mtimecmp <= current timer value. 411c77c410SMichael Clark */ 421c77c410SMichael Clark static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) 431c77c410SMichael Clark { 441c77c410SMichael Clark uint64_t next; 451c77c410SMichael Clark uint64_t diff; 461c77c410SMichael Clark 471c77c410SMichael Clark uint64_t rtc_r = cpu_riscv_read_rtc(); 481c77c410SMichael Clark 491c77c410SMichael Clark cpu->env.timecmp = value; 501c77c410SMichael Clark if (cpu->env.timecmp <= rtc_r) { 511c77c410SMichael Clark /* if we're setting an MTIMECMP value in the "past", 521c77c410SMichael Clark immediately raise the timer interrupt */ 5385ba724fSMichael Clark riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); 541c77c410SMichael Clark return; 551c77c410SMichael Clark } 561c77c410SMichael Clark 571c77c410SMichael Clark /* otherwise, set up the future timer interrupt */ 5885ba724fSMichael Clark riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0)); 591c77c410SMichael Clark diff = cpu->env.timecmp - rtc_r; 601c77c410SMichael Clark /* back to ns (note args switched in muldiv64) */ 611c77c410SMichael Clark next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 622a8756edSMichael Clark muldiv64(diff, NANOSECONDS_PER_SECOND, SIFIVE_CLINT_TIMEBASE_FREQ); 631c77c410SMichael Clark timer_mod(cpu->env.timer, next); 641c77c410SMichael Clark } 651c77c410SMichael Clark 661c77c410SMichael Clark /* 671c77c410SMichael Clark * Callback used when the timer set using timer_mod expires. 681c77c410SMichael Clark * Should raise the timer interrupt line 691c77c410SMichael Clark */ 701c77c410SMichael Clark static void sifive_clint_timer_cb(void *opaque) 711c77c410SMichael Clark { 721c77c410SMichael Clark RISCVCPU *cpu = opaque; 7385ba724fSMichael Clark riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); 741c77c410SMichael Clark } 751c77c410SMichael Clark 761c77c410SMichael Clark /* CPU wants to read rtc or timecmp register */ 771c77c410SMichael Clark static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size) 781c77c410SMichael Clark { 791c77c410SMichael Clark SiFiveCLINTState *clint = opaque; 801c77c410SMichael Clark if (addr >= clint->sip_base && 811c77c410SMichael Clark addr < clint->sip_base + (clint->num_harts << 2)) { 821c77c410SMichael Clark size_t hartid = (addr - clint->sip_base) >> 2; 831c77c410SMichael Clark CPUState *cpu = qemu_get_cpu(hartid); 841c77c410SMichael Clark CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 851c77c410SMichael Clark if (!env) { 861c77c410SMichael Clark error_report("clint: invalid timecmp hartid: %zu", hartid); 871c77c410SMichael Clark } else if ((addr & 0x3) == 0) { 881c77c410SMichael Clark return (env->mip & MIP_MSIP) > 0; 891c77c410SMichael Clark } else { 901c77c410SMichael Clark error_report("clint: invalid read: %08x", (uint32_t)addr); 911c77c410SMichael Clark return 0; 921c77c410SMichael Clark } 931c77c410SMichael Clark } else if (addr >= clint->timecmp_base && 941c77c410SMichael Clark addr < clint->timecmp_base + (clint->num_harts << 3)) { 951c77c410SMichael Clark size_t hartid = (addr - clint->timecmp_base) >> 3; 961c77c410SMichael Clark CPUState *cpu = qemu_get_cpu(hartid); 971c77c410SMichael Clark CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 981c77c410SMichael Clark if (!env) { 991c77c410SMichael Clark error_report("clint: invalid timecmp hartid: %zu", hartid); 1001c77c410SMichael Clark } else if ((addr & 0x7) == 0) { 1011c77c410SMichael Clark /* timecmp_lo */ 1021c77c410SMichael Clark uint64_t timecmp = env->timecmp; 1031c77c410SMichael Clark return timecmp & 0xFFFFFFFF; 1041c77c410SMichael Clark } else if ((addr & 0x7) == 4) { 1051c77c410SMichael Clark /* timecmp_hi */ 1061c77c410SMichael Clark uint64_t timecmp = env->timecmp; 1071c77c410SMichael Clark return (timecmp >> 32) & 0xFFFFFFFF; 1081c77c410SMichael Clark } else { 1091c77c410SMichael Clark error_report("clint: invalid read: %08x", (uint32_t)addr); 1101c77c410SMichael Clark return 0; 1111c77c410SMichael Clark } 1121c77c410SMichael Clark } else if (addr == clint->time_base) { 1131c77c410SMichael Clark /* time_lo */ 1141c77c410SMichael Clark return cpu_riscv_read_rtc() & 0xFFFFFFFF; 1151c77c410SMichael Clark } else if (addr == clint->time_base + 4) { 1161c77c410SMichael Clark /* time_hi */ 1171c77c410SMichael Clark return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF; 1181c77c410SMichael Clark } 1191c77c410SMichael Clark 1201c77c410SMichael Clark error_report("clint: invalid read: %08x", (uint32_t)addr); 1211c77c410SMichael Clark return 0; 1221c77c410SMichael Clark } 1231c77c410SMichael Clark 1241c77c410SMichael Clark /* CPU wrote to rtc or timecmp register */ 1251c77c410SMichael Clark static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, 1261c77c410SMichael Clark unsigned size) 1271c77c410SMichael Clark { 1281c77c410SMichael Clark SiFiveCLINTState *clint = opaque; 1291c77c410SMichael Clark 1301c77c410SMichael Clark if (addr >= clint->sip_base && 1311c77c410SMichael Clark addr < clint->sip_base + (clint->num_harts << 2)) { 1321c77c410SMichael Clark size_t hartid = (addr - clint->sip_base) >> 2; 1331c77c410SMichael Clark CPUState *cpu = qemu_get_cpu(hartid); 1341c77c410SMichael Clark CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 1351c77c410SMichael Clark if (!env) { 1361c77c410SMichael Clark error_report("clint: invalid timecmp hartid: %zu", hartid); 1371c77c410SMichael Clark } else if ((addr & 0x3) == 0) { 13885ba724fSMichael Clark riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MSIP, BOOL_TO_MASK(value)); 1391c77c410SMichael Clark } else { 1401c77c410SMichael Clark error_report("clint: invalid sip write: %08x", (uint32_t)addr); 1411c77c410SMichael Clark } 1421c77c410SMichael Clark return; 1431c77c410SMichael Clark } else if (addr >= clint->timecmp_base && 1441c77c410SMichael Clark addr < clint->timecmp_base + (clint->num_harts << 3)) { 1451c77c410SMichael Clark size_t hartid = (addr - clint->timecmp_base) >> 3; 1461c77c410SMichael Clark CPUState *cpu = qemu_get_cpu(hartid); 1471c77c410SMichael Clark CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 1481c77c410SMichael Clark if (!env) { 1491c77c410SMichael Clark error_report("clint: invalid timecmp hartid: %zu", hartid); 1501c77c410SMichael Clark } else if ((addr & 0x7) == 0) { 1511c77c410SMichael Clark /* timecmp_lo */ 152ef9e41dfSMichael Clark uint64_t timecmp_hi = env->timecmp >> 32; 1531c77c410SMichael Clark sifive_clint_write_timecmp(RISCV_CPU(cpu), 154ef9e41dfSMichael Clark timecmp_hi << 32 | (value & 0xFFFFFFFF)); 1551c77c410SMichael Clark return; 1561c77c410SMichael Clark } else if ((addr & 0x7) == 4) { 1571c77c410SMichael Clark /* timecmp_hi */ 158ef9e41dfSMichael Clark uint64_t timecmp_lo = env->timecmp; 1591c77c410SMichael Clark sifive_clint_write_timecmp(RISCV_CPU(cpu), 160ef9e41dfSMichael Clark value << 32 | (timecmp_lo & 0xFFFFFFFF)); 1611c77c410SMichael Clark } else { 1621c77c410SMichael Clark error_report("clint: invalid timecmp write: %08x", (uint32_t)addr); 1631c77c410SMichael Clark } 1641c77c410SMichael Clark return; 1651c77c410SMichael Clark } else if (addr == clint->time_base) { 1661c77c410SMichael Clark /* time_lo */ 1671c77c410SMichael Clark error_report("clint: time_lo write not implemented"); 1681c77c410SMichael Clark return; 1691c77c410SMichael Clark } else if (addr == clint->time_base + 4) { 1701c77c410SMichael Clark /* time_hi */ 1711c77c410SMichael Clark error_report("clint: time_hi write not implemented"); 1721c77c410SMichael Clark return; 1731c77c410SMichael Clark } 1741c77c410SMichael Clark 1751c77c410SMichael Clark error_report("clint: invalid write: %08x", (uint32_t)addr); 1761c77c410SMichael Clark } 1771c77c410SMichael Clark 1781c77c410SMichael Clark static const MemoryRegionOps sifive_clint_ops = { 1791c77c410SMichael Clark .read = sifive_clint_read, 1801c77c410SMichael Clark .write = sifive_clint_write, 1811c77c410SMichael Clark .endianness = DEVICE_LITTLE_ENDIAN, 1821c77c410SMichael Clark .valid = { 1831c77c410SMichael Clark .min_access_size = 4, 1841c77c410SMichael Clark .max_access_size = 4 1851c77c410SMichael Clark } 1861c77c410SMichael Clark }; 1871c77c410SMichael Clark 1881c77c410SMichael Clark static Property sifive_clint_properties[] = { 1891c77c410SMichael Clark DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0), 1901c77c410SMichael Clark DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0), 1911c77c410SMichael Clark DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0), 1921c77c410SMichael Clark DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0), 1931c77c410SMichael Clark DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0), 1941c77c410SMichael Clark DEFINE_PROP_END_OF_LIST(), 1951c77c410SMichael Clark }; 1961c77c410SMichael Clark 1971c77c410SMichael Clark static void sifive_clint_realize(DeviceState *dev, Error **errp) 1981c77c410SMichael Clark { 1991c77c410SMichael Clark SiFiveCLINTState *s = SIFIVE_CLINT(dev); 2001c77c410SMichael Clark memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s, 2011c77c410SMichael Clark TYPE_SIFIVE_CLINT, s->aperture_size); 2021c77c410SMichael Clark sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); 2031c77c410SMichael Clark } 2041c77c410SMichael Clark 2051c77c410SMichael Clark static void sifive_clint_class_init(ObjectClass *klass, void *data) 2061c77c410SMichael Clark { 2071c77c410SMichael Clark DeviceClass *dc = DEVICE_CLASS(klass); 2081c77c410SMichael Clark dc->realize = sifive_clint_realize; 2094f67d30bSMarc-André Lureau device_class_set_props(dc, sifive_clint_properties); 2101c77c410SMichael Clark } 2111c77c410SMichael Clark 2121c77c410SMichael Clark static const TypeInfo sifive_clint_info = { 2131c77c410SMichael Clark .name = TYPE_SIFIVE_CLINT, 2141c77c410SMichael Clark .parent = TYPE_SYS_BUS_DEVICE, 2151c77c410SMichael Clark .instance_size = sizeof(SiFiveCLINTState), 2161c77c410SMichael Clark .class_init = sifive_clint_class_init, 2171c77c410SMichael Clark }; 2181c77c410SMichael Clark 2191c77c410SMichael Clark static void sifive_clint_register_types(void) 2201c77c410SMichael Clark { 2211c77c410SMichael Clark type_register_static(&sifive_clint_info); 2221c77c410SMichael Clark } 2231c77c410SMichael Clark 2241c77c410SMichael Clark type_init(sifive_clint_register_types) 2251c77c410SMichael Clark 2261c77c410SMichael Clark 2271c77c410SMichael Clark /* 2281c77c410SMichael Clark * Create CLINT device. 2291c77c410SMichael Clark */ 2301c77c410SMichael Clark DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts, 2315f3616ccSAnup Patel uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base, 2325f3616ccSAnup Patel bool provide_rdtime) 2331c77c410SMichael Clark { 2341c77c410SMichael Clark int i; 2351c77c410SMichael Clark for (i = 0; i < num_harts; i++) { 2361c77c410SMichael Clark CPUState *cpu = qemu_get_cpu(i); 2371c77c410SMichael Clark CPURISCVState *env = cpu ? cpu->env_ptr : NULL; 2381c77c410SMichael Clark if (!env) { 2391c77c410SMichael Clark continue; 2401c77c410SMichael Clark } 2415f3616ccSAnup Patel if (provide_rdtime) { 2425f3616ccSAnup Patel riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc); 2435f3616ccSAnup Patel } 2441c77c410SMichael Clark env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 2451c77c410SMichael Clark &sifive_clint_timer_cb, cpu); 2461c77c410SMichael Clark env->timecmp = 0; 2471c77c410SMichael Clark } 2481c77c410SMichael Clark 249*3e80f690SMarkus Armbruster DeviceState *dev = qdev_new(TYPE_SIFIVE_CLINT); 2501c77c410SMichael Clark qdev_prop_set_uint32(dev, "num-harts", num_harts); 2511c77c410SMichael Clark qdev_prop_set_uint32(dev, "sip-base", sip_base); 2521c77c410SMichael Clark qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base); 2531c77c410SMichael Clark qdev_prop_set_uint32(dev, "time-base", time_base); 2541c77c410SMichael Clark qdev_prop_set_uint32(dev, "aperture-size", size); 255*3e80f690SMarkus Armbruster qdev_realize_and_unref(dev, NULL, &error_fatal); 2561c77c410SMichael Clark sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); 2571c77c410SMichael Clark return dev; 2581c77c410SMichael Clark } 259