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