diff mbox series

[10/11,v2] hw/riscv: add CLIC into virt machine

Message ID 20240819160742.27586-14-Ian.Brockbank@cirrus.com (mailing list archive)
State New, archived
Headers show
Series RISC-V: support CLIC v0.9 specification | expand

Commit Message

Ian Brockbank Aug. 19, 2024, 4:02 p.m. UTC
Signed-off-by: Ian Brockbank<ian.brockbank@cirrus.com>
---
 hw/riscv/virt.c         | 235 +++++++++++++++++++++++++++++++++++++++-
 include/hw/riscv/virt.h |  35 ++++++
 2 files changed, 267 insertions(+), 3 deletions(-)

--
2.46.0.windows.1
This message and any attachments may contain privileged and confidential information that is intended solely for the person(s) to whom it is addressed. If you are not an intended recipient you must not: read; copy; distribute; discuss; take any action in or make any reliance upon the contents of this message; nor open or read any attachment. If you have received this message in error, please notify us as soon as possible on the following telephone number and destroy this message including any attachments. Thank you. Cirrus Logic International (UK) Ltd and Cirrus Logic International Semiconductor Ltd are companies registered in Scotland, with registered numbers SC089839 and SC495735 respectively. Our registered office is at 7B Nightingale Way, Quartermile, Edinburgh, EH3 9EG, UK. Tel: +44 (0)131 272 7000. www.cirrus.com
diff mbox series

Patch

diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index cef41c150a..68d614ad5c 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -1,4 +1,4 @@ 
-/*
+ /*
  * QEMU RISC-V VirtIO Board
  *
  * Copyright (c) 2017 SiFive, Inc.
@@ -39,6 +39,7 @@ 
 #include "hw/firmware/smbios.h"
 #include "hw/intc/riscv_aclint.h"
 #include "hw/intc/riscv_aplic.h"
+#include "hw/intc/riscv_clic.h"
 #include "hw/intc/sifive_plic.h"
 #include "hw/misc/sifive_test.h"
 #include "hw/platform-bus.h"
@@ -72,6 +73,7 @@  static const MemMapEntry virt_memmap[] = {
     [VIRT_MROM] =         {     0x1000,        0xf000 },
     [VIRT_TEST] =         {   0x100000,        0x1000 },
     [VIRT_RTC] =          {   0x101000,        0x1000 },
+    [VIRT_CLIC] =         {  0x2000000, VIRT_CLIC_MAX_SIZE(VIRT_CPUS_MAX)},
     [VIRT_CLINT] =        {  0x2000000,       0x10000 },
     [VIRT_ACLINT_SSWI] =  {  0x2F00000,        0x4000 },
     [VIRT_PCIE_PIO] =     {  0x3000000,       0x10000 },
@@ -424,6 +426,37 @@  static void create_fdt_socket_aclint(RISCVVirtState *s,
     }
 }

+static void create_fdt_socket_clic(RISCVVirtState *s,
+                                   const MemMapEntry *memmap, int socket)
+{
+    g_autofree char *clic_name = NULL;
+    g_autofree uint32_t *clic_cells = NULL;
+    unsigned long mclicbase;
+    MachineState *ms = MACHINE(s);
+    static const char * const clic_compat[1] = {
+        "riscv,clic-0.9"
+    };
+
+    /*
+     * The spec doesn't define a memory layout, other than to say that each
+     * CLIC should be on a 4KiB boundary if memory-mapped.
+     * This implementation makes all the CLICs contiguous, in the order M, S, U,
+     * and assumes the worst-case size.
+     * TODO: create entries for each CLIC on the system.
+     */
+    mclicbase = memmap[VIRT_CLIC].base;
+    clic_name = g_strdup_printf("/soc/clic@%lx", mclicbase);
+    qemu_fdt_add_subnode(ms->fdt, clic_name);
+    qemu_fdt_setprop_string_array(ms->fdt, clic_name, "compatible",
+                                  (char **)&clic_compat,
+                                  ARRAY_SIZE(clic_compat));
+    qemu_fdt_setprop_cells(ms->fdt, clic_name, "regs",
+        0x0, mclicbase, 0x0, memmap[VIRT_CLIC].size);
+    qemu_fdt_setprop_cell(ms->fdt, clic_name, "riscv,num-sources",
+                          VIRT_IRQCHIP_NUM_SOURCES);
+    riscv_socket_fdt_write_id(ms, clic_name, socket);
+}
+
 static void create_fdt_socket_plic(RISCVVirtState *s,
                                    const MemMapEntry *memmap, int socket,
                                    uint32_t *phandle, uint32_t *intc_phandles,
@@ -759,7 +792,10 @@  static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,

         create_fdt_socket_memory(s, memmap, socket);

-        if (virt_aclint_allowed() && s->have_aclint) {
+
+        if (s->have_clic) {
+            create_fdt_socket_clic(s, memmap, socket);
+        } else if (virt_aclint_allowed() && s->have_aclint) {
             create_fdt_socket_aclint(s, memmap, socket,
                                      &intc_phandles[phandle_pos]);
         } else if (tcg_enabled()) {
@@ -1206,6 +1242,37 @@  static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket,
     return ret;
 }

+static DeviceState *virt_create_clic(RISCVVirtState *s, uint64_t clic_base,
+                                     int hartid)
+{
+    DeviceState *ret;
+    uint32_t block_size = VIRT_CLIC_HART_SIZE(s->clic_prv_s, s->clic_prv_u);
+    uint64_t mclicbase = clic_base + hartid * block_size;
+    uint64_t sclicbase = 0;
+    uint64_t uclicbase = 0;
+
+    /*
+     * The spec doesn't define a memory layout, other than to say that each
+     * CLIC should be on a 4KiB boundary if memory-mapped.
+     * This implementation makes all the CLICs contiguous, in the order M, S, U.
+     */
+    if (s->clic_prv_s) {
+        sclicbase = mclicbase + VIRT_CLIC_BLOCK_SIZE;
+    }
+    if (s->clic_prv_u) {
+        uclicbase = mclicbase + VIRT_CLIC_BLOCK_SIZE;
+        if (s->clic_prv_s) {
+            uclicbase += VIRT_CLIC_BLOCK_SIZE;
+        }
+    }
+    ret = riscv_clic_create(mclicbase, sclicbase, uclicbase,
+                            hartid, VIRT_IRQCHIP_NUM_SOURCES,
+                            s->clic_intctlbits,
+                            s->clic_version);
+
+    return ret;
+}
+
 static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
                                     const MemMapEntry *memmap, int socket,
                                     int base_hartid, int hart_count)
@@ -1505,7 +1572,7 @@  static void virt_machine_init(MachineState *machine)
                             i * memmap[VIRT_ACLINT_SSWI].size,
                         base_hartid, hart_count, true);
             }
-        } else if (tcg_enabled()) {
+        } else if (tcg_enabled() && !s->have_clic) {
             /* Per-socket SiFive CLINT */
             riscv_aclint_swi_create(
                     memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
@@ -1527,6 +1594,12 @@  static void virt_machine_init(MachineState *machine)
                                             hart_count);
         }

+        /* CLIC if present - needs to come after PLIC */
+        if (s->have_clic) {
+            s->irqchip[i] = virt_create_clic(s, memmap[VIRT_CLIC].base,
+                                             base_hartid);
+        }
+
         /* Try to use different IRQCHIP instance based device type */
         if (i == 0) {
             mmio_irqchip = s->irqchip[i];
@@ -1710,6 +1783,134 @@  static void virt_set_aclint(Object *obj, bool value, Error **errp)
     s->have_aclint = value;
 }

+static bool virt_get_clic(Object *obj, Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+
+    return s->have_clic;
+}
+
+static void virt_set_clic(Object *obj, bool value, Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+
+    s->have_clic = value;
+}
+
+static char *virt_get_clic_mode(Object *obj, Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+    const char *val;
+
+    if (s->have_clic) {
+        if (s->clic_prv_s && s->clic_prv_u) {
+            val = "msu";
+        } else if (s->clic_prv_s) {
+            val = "ms";
+        } else if (s->clic_prv_u) {
+            val = "mu";
+        } else {
+            val = "m";
+        }
+    } else {
+        val = "none";
+    }
+
+    return g_strdup(val);
+}
+
+static void virt_set_clic_mode(Object *obj, const char *val, Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+    const char *c = val;
+
+    s->have_clic = true;
+
+    s->clic_prv_s = false;
+    s->clic_prv_u = false;
+
+    /* The mode is encoded with m = machine, s = PRV_S, u = PRV_U */
+    while (*c && !*errp) {
+        switch (*c) {
+        case 'm':
+        case 'M':
+            /* Machine mode is implicit and always enabled */
+            break;
+        case 's':
+        case 'S':
+            s->clic_prv_s = true;
+            break;
+        case 'u':
+        case 'U':
+            s->clic_prv_u = true;
+            break;
+        default:
+            error_setg(errp, "Invalid CLIC interrupt mode %c in %s", *c, val);
+            error_append_hint(errp, "Valid values are m, s and u.\n");
+            break;
+        }
+        ++c;
+    }
+}
+
+static char *virt_get_clic_version(Object *obj, Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+    return g_strdup(s->clic_version);
+}
+
+static void virt_set_clic_version(Object *obj, const char *val, Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+    g_autofree char **tokens = g_strsplit(val, "-", 2);
+
+    if (0 != strcmp(tokens[0], "v0.9")) {
+        error_setg(errp, "Invalid CLIC version %s", tokens[0]);
+        error_append_hint(errp,
+                          "Only v0.9 is supported. The 'v' is required.\n");
+        return;
+    }
+
+    if (tokens[1]) {
+        if (0 != strcmp(tokens[1], "jmp")) {
+            error_setg(errp, "Invalid CLIC version suffix -%s", tokens[1]);
+            error_append_hint(errp, "Only -jmp is supported.\n");
+            return;
+        }
+    }
+
+    s->clic_version = g_strdup(val);
+    s->have_clic = true;
+}
+
+static void virt_get_clicintctlbits(Object *obj, Visitor *v,
+                                    const char *name, void *opaque,
+                                    Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+    uint8_t value = s->clic_intctlbits;
+
+    visit_type_uint8(v, name, &value, errp);
+}
+
+static void virt_set_clicintctlbits(Object *obj, Visitor *v,
+                                    const char *name, void *opaque,
+                                    Error **errp)
+{
+    RISCVVirtState *s = RISCV_VIRT_MACHINE(obj);
+    uint8_t value;
+
+    if (!visit_type_uint8(v, name, &value, errp)) {
+        return;
+    }
+    if (value > MAX_CLIC_INTCTLBITS) {
+        error_setg(errp, "CLIC intctlbits must be <= %d; was %d",
+                   MAX_CLIC_INTCTLBITS, value);
+    }
+
+    s->clic_intctlbits = value;
+}
+
 bool virt_is_acpi_enabled(RISCVVirtState *s)
 {
     return s->acpi != ON_OFF_AUTO_OFF;
@@ -1767,6 +1968,7 @@  static void virt_machine_class_init(ObjectClass *oc, void *data)
 {
     MachineClass *mc = MACHINE_CLASS(oc);
     HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+    ObjectProperty *op = NULL;

     mc->desc = "RISC-V VirtIO board";
     mc->init = virt_machine_init;
@@ -1799,6 +2001,33 @@  static void virt_machine_class_init(ObjectClass *oc, void *data)
                                           "enable/disable emulating "
                                           "ACLINT devices");

+    object_class_property_add_bool(oc, "clic", virt_get_clic,
+                                   virt_set_clic);
+    object_class_property_set_description(oc, "clic",
+                                          "Set on/off to enable/disable "
+                                          "emulating CLIC devices");
+    object_class_property_add_str(oc, "clic-mode", virt_get_clic_mode,
+                                   virt_set_clic_mode);
+    object_class_property_set_description(oc, "clic-mode",
+                                          "Specify supported CLIC modes: "
+                                          "m = machine (always enabled), "
+                                          "s = PRV_S, u = PRV_U");
+    op = object_class_property_add_str(oc, "clic-version",
+                                       virt_get_clic_version,
+                                       virt_set_clic_version);
+    object_property_set_default_str(op, VIRT_CLIC_VERSION);
+    object_class_property_set_description(oc, "clic-version",
+                                          "Set the CLIC version to use. "
+                                          "Valid values are v0.9 and v0.9-jmp");
+    op = object_class_property_add(oc, "clic-intctlbits", "uint8",
+                                   virt_get_clicintctlbits,
+                                   virt_set_clicintctlbits,
+                                   NULL, NULL);
+    object_property_set_default_uint(op, 8);
+    object_class_property_set_description(oc, "clic-intctlbits",
+                                          "The number of intctl bits used in "
+                                          "this CLIC implementation");
+
     object_class_property_add_str(oc, "aia", virt_get_aia,
                                   virt_set_aia);
     object_class_property_set_description(oc, "aia",
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index c0dc41ff9a..d0a245608c 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -55,6 +55,11 @@  struct RISCVVirtState {

     int fdt_size;
     bool have_aclint;
+    bool have_clic;
+    bool clic_prv_s;
+    bool clic_prv_u;
+    uint8_t clic_intctlbits;
+    char *clic_version;
     RISCVVirtAIAType aia_type;
     int aia_guests;
     char *oem_id;
@@ -71,6 +76,7 @@  enum {
     VIRT_RTC,
     VIRT_CLINT,
     VIRT_ACLINT_SSWI,
+    VIRT_CLIC,
     VIRT_PLIC,
     VIRT_APLIC_M,
     VIRT_APLIC_S,
@@ -113,6 +119,35 @@  enum {
 #define VIRT_PLIC_SIZE(__num_context) \
     (VIRT_PLIC_CONTEXT_BASE + (__num_context) * VIRT_PLIC_CONTEXT_STRIDE)

+#define VIRT_CLIC_INTCLTBITS    3
+#define VIRT_CLIC_VERSION       "v0.9"
+#define VIRT_CLIC_MAX_IRQS      0x1000
+#define VIRT_CLIC_CONTEXT_BASE  0x1000
+#define VIRT_CLIC_CONTEXT_COUNT(_prv_s, _prv_u) \
+    (1 + ((_prv_s) ? 1 : 0) + ((_prv_u) ? 1 : 0))
+#define VIRT_CLIC_FULL_CONTEXT_COUNT VIRT_CLIC_CONTEXT_COUNT(1, 1)
+#define VIRT_CLIC_ALIGN_BITS    12
+#define VIRT_CLIC_ALIGN_MASK    ((1U << VIRT_CLIC_ALIGN_BITS) - 1)
+/* Round up to next 4KiB alignment boundary */
+#define VIRT_CLIC_ALIGN(_base_addr) \
+    (((_base_addr) + VIRT_CLIC_ALIGN_MASK) & VIRT_CLIC_ALIGN_MASK)
+#define VIRT_CLIC_INT_SIZE(_irq_count) ((_irq_count) * 4)
+/*
+ * The spec doesn't define a memory layout, other than to say that each
+ * CLIC should be on a 4KiB boundary if memory-mapped.
+ * This implementation makes all the CLICs contiguous, in the order M, S, U,
+ * and assumes the worst-case size.
+ */
+#define VIRT_CLIC_BLOCK_SIZE \
+    (VIRT_CLIC_CONTEXT_BASE + VIRT_CLIC_INT_SIZE(VIRT_CLIC_MAX_IRQS))
+#define VIRT_CLIC_HART_SIZE(_prv_s, _prv_u) \
+    (VIRT_CLIC_CONTEXT_COUNT(_prv_s, _prv_u) * VIRT_CLIC_BLOCK_SIZE)
+#define VIRT_CLIC_SIZE(_hart_count, _prv_s, _prv_u) \
+    ((_hart_count) * VIRT_CLIC_HART_SIZE(_prv_s, _prv_u))
+#define VIRT_CLIC_MAX_HART_SIZE VIRT_CLIC_HART_SIZE(1, 1)
+#define VIRT_CLIC_MAX_SIZE(_hart_count) \
+    ((_hart_count) * VIRT_CLIC_MAX_HART_SIZE)
+
 #define FDT_PCI_ADDR_CELLS    3
 #define FDT_PCI_INT_CELLS     1
 #define FDT_PLIC_ADDR_CELLS   0