summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/xtensa/Makefile.objs1
-rw-r--r--hw/xtensa/mx_pic.c354
-rw-r--r--hw/xtensa/pic_cpu.c47
-rw-r--r--hw/xtensa/xtfpga.c65
4 files changed, 429 insertions, 38 deletions
diff --git a/hw/xtensa/Makefile.objs b/hw/xtensa/Makefile.objs
index cb4998d2bf..f30e4a7e07 100644
--- a/hw/xtensa/Makefile.objs
+++ b/hw/xtensa/Makefile.objs
@@ -1,3 +1,4 @@
+obj-y += mx_pic.o
 obj-y += pic_cpu.o
 obj-y += sim.o
 obj-y += xtensa_memory.o
diff --git a/hw/xtensa/mx_pic.c b/hw/xtensa/mx_pic.c
new file mode 100644
index 0000000000..7075db9d4b
--- /dev/null
+++ b/hw/xtensa/mx_pic.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2013 - 2019, Max Filippov, Open Source and Linux Lab.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Open Source and Linux Lab nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/xtensa/mx_pic.h"
+#include "qemu/log.h"
+
+#define MX_MAX_CPU 32
+#define MX_MAX_IRQ 32
+
+#define MIROUT 0x0
+#define MIPICAUSE 0x100
+#define MIPISET 0x140
+#define MIENG 0x180
+#define MIENGSET 0x184
+#define MIASG 0x188
+#define MIASGSET 0x18c
+#define MIPIPART 0x190
+#define SYSCFGID 0x1a0
+#define MPSCORE 0x200
+#define CCON 0x220
+
+struct XtensaMxPic {
+    unsigned n_cpu;
+    unsigned n_irq;
+
+    uint32_t ext_irq_state;
+    uint32_t mieng;
+    uint32_t miasg;
+    uint32_t mirout[MX_MAX_IRQ];
+    uint32_t mipipart;
+    uint32_t runstall;
+
+    qemu_irq *irq_inputs;
+    struct XtensaMxPicCpu {
+        XtensaMxPic *mx;
+        qemu_irq *irq;
+        qemu_irq runstall;
+        uint32_t mipicause;
+        uint32_t mirout_cache;
+        uint32_t irq_state_cache;
+        uint32_t ccon;
+        MemoryRegion reg;
+    } cpu[MX_MAX_CPU];
+};
+
+static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset,
+                                           unsigned size)
+{
+    struct XtensaMxPicCpu *mx_cpu = opaque;
+    struct XtensaMxPic *mx = mx_cpu->mx;
+
+    if (offset < MIROUT + MX_MAX_IRQ) {
+        return mx->mirout[offset - MIROUT];
+    } else if (offset >= MIPICAUSE && offset < MIPICAUSE + MX_MAX_CPU) {
+        return mx->cpu[offset - MIPICAUSE].mipicause;
+    } else {
+        switch (offset) {
+        case MIENG:
+            return mx->mieng;
+
+        case MIASG:
+            return mx->miasg;
+
+        case MIPIPART:
+            return mx->mipipart;
+
+        case SYSCFGID:
+            return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu);
+
+        case MPSCORE:
+            return mx->runstall;
+
+        case CCON:
+            return mx_cpu->ccon;
+
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "unknown RER in MX PIC range: 0x%08x\n",
+                          (uint32_t)offset);
+            return 0;
+        }
+    }
+}
+
+static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx,
+                                              unsigned cpu)
+{
+    uint32_t mipicause = mx->cpu[cpu].mipicause;
+    uint32_t mipipart = mx->mipipart;
+
+    return (((mipicause & 1) << (mipipart & 3)) |
+            ((mipicause & 0x000e) != 0) << ((mipipart >> 2) & 3) |
+            ((mipicause & 0x00f0) != 0) << ((mipipart >> 4) & 3) |
+            ((mipicause & 0xff00) != 0) << ((mipipart >> 6) & 3)) & 0x7;
+}
+
+static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx,
+                                                  unsigned cpu)
+{
+    return ((((mx->ext_irq_state & mx->mieng) | mx->miasg) &
+             mx->cpu[cpu].mirout_cache) << 2) |
+        xtensa_mx_pic_get_ipi_for_cpu(mx, cpu);
+}
+
+static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu)
+{
+    uint32_t irq = xtensa_mx_pic_get_ext_irq_for_cpu(mx, cpu);
+    uint32_t changed_irq = mx->cpu[cpu].irq_state_cache ^ irq;
+    unsigned i;
+
+    qemu_log_mask(CPU_LOG_INT, "%s: CPU %d, irq: %08x, changed_irq: %08x\n",
+                  __func__, cpu, irq, changed_irq);
+    mx->cpu[cpu].irq_state_cache = irq;
+    for (i = 0; changed_irq; ++i) {
+        uint32_t mask = 1u << i;
+
+        if (changed_irq & mask) {
+            changed_irq ^= mask;
+            qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask);
+        }
+    }
+}
+
+static void xtensa_mx_pic_update_all(XtensaMxPic *mx)
+{
+    unsigned cpu;
+
+    for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
+        xtensa_mx_pic_update_cpu(mx, cpu);
+    }
+}
+
+static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset,
+                                        uint64_t v, unsigned size)
+{
+    struct XtensaMxPicCpu *mx_cpu = opaque;
+    struct XtensaMxPic *mx = mx_cpu->mx;
+    unsigned cpu;
+
+    if (offset < MIROUT + mx->n_irq) {
+        mx->mirout[offset - MIROUT] = v;
+        for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
+            uint32_t mask = 1u << (offset - MIROUT);
+
+            if (!(mx->cpu[cpu].mirout_cache & mask) != !(v & (1u << cpu))) {
+                mx->cpu[cpu].mirout_cache ^= mask;
+                xtensa_mx_pic_update_cpu(mx, cpu);
+            }
+        }
+    } else if (offset >= MIPICAUSE && offset < MIPICAUSE + mx->n_cpu) {
+        cpu = offset - MIPICAUSE;
+        mx->cpu[cpu].mipicause &= ~v;
+        xtensa_mx_pic_update_cpu(mx, cpu);
+    } else if (offset >= MIPISET && offset < MIPISET + 16) {
+        for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
+            if (v & (1u << cpu)) {
+                mx->cpu[cpu].mipicause |= 1u << (offset - MIPISET);
+                xtensa_mx_pic_update_cpu(mx, cpu);
+            }
+        }
+    } else {
+        uint32_t change = 0;
+        uint32_t oldv, newv;
+        const char *name = "???";
+
+        switch (offset) {
+        case MIENG:
+            change = mx->mieng & v;
+            oldv = mx->mieng;
+            mx->mieng &= ~v;
+            newv = mx->mieng;
+            name = "MIENG";
+            break;
+
+        case MIENGSET:
+            change = ~mx->mieng & v;
+            oldv = mx->mieng;
+            mx->mieng |= v;
+            newv = mx->mieng;
+            name = "MIENG";
+            break;
+
+        case MIASG:
+            change = mx->miasg & v;
+            oldv = mx->miasg;
+            mx->miasg &= ~v;
+            newv = mx->miasg;
+            name = "MIASG";
+            break;
+
+        case MIASGSET:
+            change = ~mx->miasg & v;
+            oldv = mx->miasg;
+            mx->miasg |= v;
+            newv = mx->miasg;
+            name = "MIASG";
+            break;
+
+        case MIPIPART:
+            change = mx->mipipart ^ v;
+            oldv = mx->mipipart;
+            mx->mipipart = v;
+            newv = mx->mipipart;
+            name = "MIPIPART";
+            break;
+
+        case MPSCORE:
+            change = mx->runstall ^ v;
+            oldv = mx->runstall;
+            mx->runstall = v;
+            newv = mx->runstall;
+            name = "RUNSTALL";
+            for (cpu = 0; cpu < mx->n_cpu; ++cpu) {
+                if (change & (1u << cpu)) {
+                    qemu_set_irq(mx->cpu[cpu].runstall, v & (1u << cpu));
+                }
+            }
+            break;
+
+        case CCON:
+            mx_cpu->ccon = v & 0x1;
+            break;
+
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "unknown WER in MX PIC range: 0x%08x = 0x%08x\n",
+                          (uint32_t)offset, (uint32_t)v);
+            break;
+        }
+        if (change) {
+            qemu_log_mask(CPU_LOG_INT,
+                          "%s: %s changed by CPU %d: %08x -> %08x\n",
+                          __func__, name, (int)(mx_cpu - mx->cpu),
+                          oldv, newv);
+            xtensa_mx_pic_update_all(mx);
+        }
+    }
+}
+
+static const MemoryRegionOps xtensa_mx_pic_ops = {
+    .read = xtensa_mx_pic_ext_reg_read,
+    .write = xtensa_mx_pic_ext_reg_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .unaligned = true,
+    },
+};
+
+MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx,
+                                         qemu_irq *irq,
+                                         qemu_irq runstall)
+{
+    struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu;
+
+    mx_cpu->mx = mx;
+    mx_cpu->irq = irq;
+    mx_cpu->runstall = runstall;
+
+    memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu,
+                          "mx_pic", 0x280);
+
+    ++mx->n_cpu;
+    return &mx_cpu->reg;
+}
+
+static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active)
+{
+    XtensaMxPic *mx = opaque;
+
+    if (irq < mx->n_irq) {
+        uint32_t old_irq_state = mx->ext_irq_state;
+
+        if (active) {
+            mx->ext_irq_state |= 1u << irq;
+        } else {
+            mx->ext_irq_state &= ~(1u << irq);
+        }
+        if (old_irq_state != mx->ext_irq_state) {
+            qemu_log_mask(CPU_LOG_INT,
+                          "%s: IRQ %d, active: %d, ext_irq_state: %08x -> %08x\n",
+                          __func__, irq, active,
+                          old_irq_state, mx->ext_irq_state);
+            xtensa_mx_pic_update_all(mx);
+        }
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n",
+                      __func__, irq);
+    }
+}
+
+XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq)
+{
+    XtensaMxPic *mx = calloc(1, sizeof(XtensaMxPic));
+
+    mx->n_irq = n_irq + 1;
+    mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx,
+                                        mx->n_irq);
+    return mx;
+}
+
+void xtensa_mx_pic_reset(void *opaque)
+{
+    XtensaMxPic *mx = opaque;
+    unsigned i;
+
+    mx->ext_irq_state = 0;
+    mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u;
+    mx->miasg = 0;
+    mx->mipipart = 0;
+    for (i = 0; i < mx->n_irq; ++i) {
+        mx->mirout[i] = 1;
+    }
+    for (i = 0; i < mx->n_cpu; ++i) {
+        mx->cpu[i].mipicause = 0;
+        mx->cpu[i].mirout_cache = i ? 0 : mx->mieng;
+        mx->cpu[i].irq_state_cache = 0;
+        mx->cpu[i].ccon = 0;
+    }
+    mx->runstall = (1u << mx->n_cpu) - 2;
+    for (i = 0; i < mx->n_cpu; ++i) {
+        qemu_set_irq(mx->cpu[i].runstall, i > 0);
+    }
+}
+
+qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx)
+{
+    return mx->irq_inputs + 1;
+}
diff --git a/hw/xtensa/pic_cpu.c b/hw/xtensa/pic_cpu.c
index 0e812d7f06..a8939f5e58 100644
--- a/hw/xtensa/pic_cpu.c
+++ b/hw/xtensa/pic_cpu.c
@@ -68,36 +68,37 @@ static void xtensa_set_irq(void *opaque, int irq, int active)
         uint32_t irq_bit = 1 << irq;
 
         if (active) {
-            env->sregs[INTSET] |= irq_bit;
+            atomic_or(&env->sregs[INTSET], irq_bit);
         } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) {
-            env->sregs[INTSET] &= ~irq_bit;
+            atomic_and(&env->sregs[INTSET], ~irq_bit);
         }
 
         check_interrupts(env);
     }
 }
 
-void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active)
-{
-    qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active);
-}
-
 static void xtensa_ccompare_cb(void *opaque)
 {
     XtensaCcompareTimer *ccompare = opaque;
     CPUXtensaState *env = ccompare->env;
     unsigned i = ccompare - env->ccompare;
 
-    xtensa_timer_irq(env, i, 1);
+    qemu_set_irq(env->irq_inputs[env->config->timerint[i]], 1);
+}
+
+static void xtensa_set_runstall(void *opaque, int irq, int active)
+{
+    CPUXtensaState *env = opaque;
+    xtensa_runstall(env, active);
 }
 
 void xtensa_irq_init(CPUXtensaState *env)
 {
-    env->irq_inputs = (void **)qemu_allocate_irqs(
-            xtensa_set_irq, env, env->config->ninterrupt);
-    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
-        unsigned i;
+    unsigned i;
 
+    env->irq_inputs = qemu_allocate_irqs(xtensa_set_irq, env,
+                                         env->config->ninterrupt);
+    if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) {
         env->time_base = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
         env->ccount_base = env->sregs[CCOUNT];
         for (i = 0; i < env->config->nccompare; ++i) {
@@ -106,16 +107,20 @@ void xtensa_irq_init(CPUXtensaState *env)
                     xtensa_ccompare_cb, env->ccompare + i);
         }
     }
+    for (i = 0; i < env->config->nextint; ++i) {
+        unsigned irq = env->config->extint[i];
+
+        env->ext_irq_inputs[i] = env->irq_inputs[irq];
+    }
+    env->runstall_irq = qemu_allocate_irq(xtensa_set_runstall, env, 0);
 }
 
-void *xtensa_get_extint(CPUXtensaState *env, unsigned extint)
+qemu_irq *xtensa_get_extints(CPUXtensaState *env)
 {
-    if (extint < env->config->nextint) {
-        unsigned irq = env->config->extint[extint];
-        return env->irq_inputs[irq];
-    } else {
-        qemu_log("%s: trying to acquire invalid external interrupt %d\n",
-                __func__, extint);
-        return NULL;
-    }
+    return env->ext_irq_inputs;
+}
+
+qemu_irq xtensa_get_runstall(CPUXtensaState *env)
+{
+    return env->runstall_irq;
 }
diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c
index 21094319a6..1d21162a27 100644
--- a/hw/xtensa/xtfpga.c
+++ b/hw/xtensa/xtfpga.c
@@ -45,6 +45,7 @@
 #include "qemu/option.h"
 #include "bootparam.h"
 #include "xtensa_memory.h"
+#include "hw/xtensa/mx_pic.h"
 
 typedef struct XtfpgaFlashDesc {
     hwaddr base;
@@ -61,6 +62,7 @@ typedef struct XtfpgaBoardDesc {
 
 typedef struct XtfpgaFpgaState {
     MemoryRegion iomem;
+    uint32_t freq;
     uint32_t leds;
     uint32_t switches;
 } XtfpgaFpgaState;
@@ -83,7 +85,7 @@ static uint64_t xtfpga_fpga_read(void *opaque, hwaddr addr,
         return 0x09272011;
 
     case 0x4: /*processor clock frequency, Hz*/
-        return 10000000;
+        return s->freq;
 
     case 0x8: /*LEDs (off = 0, on = 1)*/
         return s->leds;
@@ -119,13 +121,14 @@ static const MemoryRegionOps xtfpga_fpga_ops = {
 };
 
 static XtfpgaFpgaState *xtfpga_fpga_init(MemoryRegion *address_space,
-        hwaddr base)
+                                         hwaddr base, uint32_t freq)
 {
     XtfpgaFpgaState *s = g_malloc(sizeof(XtfpgaFpgaState));
 
     memory_region_init_io(&s->iomem, NULL, &xtfpga_fpga_ops, s,
-            "xtfpga.fpga", 0x10000);
+                          "xtfpga.fpga", 0x10000);
     memory_region_add_subregion(address_space, base, &s->iomem);
+    s->freq = freq;
     xtfpga_fpga_reset(s);
     qemu_register_reset(xtfpga_fpga_reset, s);
     return s;
@@ -223,6 +226,8 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine)
     XtensaCPU *cpu = NULL;
     CPUXtensaState *env = NULL;
     MemoryRegion *system_io;
+    XtensaMxPic *mx_pic = NULL;
+    qemu_irq *extints;
     DriveInfo *dinfo;
     pflash_t *flash = NULL;
     QemuOpts *machine_opts = qemu_get_machine_opts();
@@ -231,19 +236,45 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine)
     const char *dtb_filename = qemu_opt_get(machine_opts, "dtb");
     const char *initrd_filename = qemu_opt_get(machine_opts, "initrd");
     const unsigned system_io_size = 224 * MiB;
+    uint32_t freq = 10000000;
     int n;
 
+    if (smp_cpus > 1) {
+        mx_pic = xtensa_mx_pic_init(31);
+        qemu_register_reset(xtensa_mx_pic_reset, mx_pic);
+    }
     for (n = 0; n < smp_cpus; n++) {
+        CPUXtensaState *cenv = NULL;
+
         cpu = XTENSA_CPU(cpu_create(machine->cpu_type));
-        env = &cpu->env;
+        cenv = &cpu->env;
+        if (!env) {
+            env = cenv;
+            freq = env->config->clock_freq_khz * 1000;
+        }
+
+        if (mx_pic) {
+            MemoryRegion *mx_eri;
 
-        env->sregs[PRID] = n;
+            mx_eri = xtensa_mx_pic_register_cpu(mx_pic,
+                                                xtensa_get_extints(cenv),
+                                                xtensa_get_runstall(cenv));
+            memory_region_add_subregion(xtensa_get_er_region(cenv),
+                                        0, mx_eri);
+        }
+        cenv->sregs[PRID] = n;
+        xtensa_select_static_vectors(cenv, n != 0);
         qemu_register_reset(xtfpga_reset, cpu);
         /* Need MMU initialized prior to ELF loading,
          * so that ELF gets loaded into virtual addresses
          */
         cpu_reset(CPU(cpu));
     }
+    if (smp_cpus > 1) {
+        extints = xtensa_mx_pic_get_extints(mx_pic);
+    } else {
+        extints = xtensa_get_extints(env);
+    }
 
     if (env) {
         XtensaMemory sysram = env->config->sysram;
@@ -272,14 +303,14 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine)
                                  system_io, 0, system_io_size);
         memory_region_add_subregion(system_memory, board->io[1], io);
     }
-    xtfpga_fpga_init(system_io, 0x0d020000);
+    xtfpga_fpga_init(system_io, 0x0d020000, freq);
     if (nd_table[0].used) {
         xtfpga_net_init(system_io, 0x0d030000, 0x0d030400, 0x0d800000,
-                xtensa_get_extint(env, 1), nd_table);
+                        extints[1], nd_table);
     }
 
-    serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0),
-            115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
+    serial_mm_init(system_io, 0x0d050020, 2, extints[0],
+                   115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
 
     dinfo = drive_get(IF_PFLASH, 0, 0);
     if (dinfo) {
@@ -568,7 +599,7 @@ static void xtfpga_lx60_class_init(ObjectClass *oc, void *data)
 
     mc->desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
     mc->init = xtfpga_lx60_init;
-    mc->max_cpus = 4;
+    mc->max_cpus = 32;
     mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE;
     mc->default_ram_size = 64 * MiB;
 }
@@ -585,7 +616,7 @@ static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, void *data)
 
     mc->desc = "lx60 noMMU EVB (" XTENSA_DEFAULT_CPU_NOMMU_MODEL ")";
     mc->init = xtfpga_lx60_nommu_init;
-    mc->max_cpus = 4;
+    mc->max_cpus = 32;
     mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE;
     mc->default_ram_size = 64 * MiB;
 }
@@ -602,7 +633,7 @@ static void xtfpga_lx200_class_init(ObjectClass *oc, void *data)
 
     mc->desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
     mc->init = xtfpga_lx200_init;
-    mc->max_cpus = 4;
+    mc->max_cpus = 32;
     mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE;
     mc->default_ram_size = 96 * MiB;
 }
@@ -619,7 +650,7 @@ static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, void *data)
 
     mc->desc = "lx200 noMMU EVB (" XTENSA_DEFAULT_CPU_NOMMU_MODEL ")";
     mc->init = xtfpga_lx200_nommu_init;
-    mc->max_cpus = 4;
+    mc->max_cpus = 32;
     mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE;
     mc->default_ram_size = 96 * MiB;
 }
@@ -636,7 +667,7 @@ static void xtfpga_ml605_class_init(ObjectClass *oc, void *data)
 
     mc->desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
     mc->init = xtfpga_ml605_init;
-    mc->max_cpus = 4;
+    mc->max_cpus = 32;
     mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE;
     mc->default_ram_size = 512 * MiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE;
 }
@@ -653,7 +684,7 @@ static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, void *data)
 
     mc->desc = "ml605 noMMU EVB (" XTENSA_DEFAULT_CPU_NOMMU_MODEL ")";
     mc->init = xtfpga_ml605_nommu_init;
-    mc->max_cpus = 4;
+    mc->max_cpus = 32;
     mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE;
     mc->default_ram_size = 256 * MiB;
 }
@@ -670,7 +701,7 @@ static void xtfpga_kc705_class_init(ObjectClass *oc, void *data)
 
     mc->desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")";
     mc->init = xtfpga_kc705_init;
-    mc->max_cpus = 4;
+    mc->max_cpus = 32;
     mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE;
     mc->default_ram_size = 1 * GiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE;
 }
@@ -687,7 +718,7 @@ static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, void *data)
 
     mc->desc = "kc705 noMMU EVB (" XTENSA_DEFAULT_CPU_NOMMU_MODEL ")";
     mc->init = xtfpga_kc705_nommu_init;
-    mc->max_cpus = 4;
+    mc->max_cpus = 32;
     mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE;
     mc->default_ram_size = 256 * MiB;
 }