diff --git a/accel/split/split-accel-ops.c b/accel/split/split-accel-ops.c
new file mode 100644
index 00000000000..e5c1d51d426
--- /dev/null
+++ b/accel/split/split-accel-ops.c
@@ -0,0 +1,161 @@
+/*
+ * QEMU "split" accelerator (HW + SW) ops
+ *
+ * Copyright (c) 2025 Linaro Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "system/accel-ops.h"
+
+static void *split_cpu_thread_routine(void *arg)
+{
+    g_assert_not_reached();
+}
+
+static void split_ops_init(AccelClass *ac)
+{
+    g_assert_not_reached();
+}
+
+static bool split_cpu_common_realize(CPUState *cpu, Error **errp)
+{
+    g_assert_not_reached();
+}
+
+static void split_cpu_common_unrealize(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static void split_cpu_reset_hold(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static void split_kick_vcpu_thread(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static bool split_cpu_thread_is_idle(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static void split_synchronize_post_reset(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static void split_synchronize_post_init(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static void split_synchronize_state(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static void split_synchronize_pre_loadvm(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static void split_synchronize_pre_resume(bool step_pending)
+{
+    g_assert_not_reached();
+}
+
+static void split_handle_interrupt(CPUState *cpu, int mask)
+{
+    g_assert_not_reached();
+}
+
+static int64_t split_get_virtual_clock(void)
+{
+    g_assert_not_reached();
+}
+
+static void split_set_virtual_clock(int64_t time)
+{
+    g_assert_not_reached();
+}
+
+static int64_t split_get_elapsed_ticks(void)
+{
+    g_assert_not_reached();
+}
+
+static int split_update_guest_debug(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static int split_insert_breakpoint(CPUState *cpu, int type,
+                                   vaddr addr, vaddr len)
+{
+    g_assert_not_reached();
+}
+
+static int split_remove_breakpoint(CPUState *cpu, int type,
+                                   vaddr addr, vaddr len)
+{
+    g_assert_not_reached();
+}
+
+static void split_remove_all_breakpoints(CPUState *cpu)
+{
+    g_assert_not_reached();
+}
+
+static void split_get_vcpu_stats(CPUState *cpu, GString *buf)
+{
+    g_assert_not_reached();
+}
+
+static void split_accel_ops_class_init(ObjectClass *oc, const void *data)
+{
+    AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
+
+    ops->ops_init = split_ops_init;
+    ops->cpu_common_realize = split_cpu_common_realize;
+    ops->cpu_common_unrealize = split_cpu_common_unrealize;
+    ops->cpu_reset_hold = split_cpu_reset_hold;
+    ops->cpu_thread_routine = split_cpu_thread_routine;
+    ops->kick_vcpu_thread = split_kick_vcpu_thread;
+    ops->cpu_thread_is_idle = split_cpu_thread_is_idle;
+
+    ops->synchronize_post_reset = split_synchronize_post_reset;
+    ops->synchronize_post_init = split_synchronize_post_init;
+    ops->synchronize_state = split_synchronize_state;
+    ops->synchronize_pre_loadvm = split_synchronize_pre_loadvm;
+    ops->synchronize_pre_resume = split_synchronize_pre_resume;
+
+    ops->handle_interrupt = split_handle_interrupt;
+    ops->get_vcpu_stats = split_get_vcpu_stats;
+
+    ops->get_virtual_clock = split_get_virtual_clock;
+    ops->set_virtual_clock = split_set_virtual_clock;
+    ops->get_elapsed_ticks = split_get_elapsed_ticks;
+
+    ops->update_guest_debug = split_update_guest_debug;
+    ops->insert_breakpoint = split_insert_breakpoint;
+    ops->remove_breakpoint = split_remove_breakpoint;
+    ops->remove_all_breakpoints = split_remove_all_breakpoints;
+}
+
+static const TypeInfo split_accel_ops_type = {
+    .name = ACCEL_OPS_NAME("split"),
+    .parent = TYPE_ACCEL_OPS,
+    .class_init = split_accel_ops_class_init,
+    .abstract = true,
+};
+
+static void split_accel_ops_register_types(void)
+{
+    type_register_static(&split_accel_ops_type);
+}
+type_init(split_accel_ops_register_types);
diff --git a/accel/split/split-all.c b/accel/split/split-all.c
new file mode 100644
index 00000000000..7e308f3c1e7
--- /dev/null
+++ b/accel/split/split-all.c
@@ -0,0 +1,77 @@
+/*
+ * QEMU "split" accelerator (HW + SW emulator)
+ *
+ * Copyright (c) 2025 Linaro Ltd.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/accel.h"
+
+bool split_allowed;
+
+static int split_accel_init_machine(MachineState *ms, AccelState *as)
+{
+    g_assert_not_reached();
+}
+
+static void split_setup_post(MachineState *ms, AccelState *accel)
+{
+    g_assert_not_reached();
+}
+
+static bool split_has_memory(MachineState *ms, AddressSpace *as,
+                             hwaddr start_addr, hwaddr size)
+{
+    g_assert_not_reached();
+}
+
+static bool split_cpus_are_resettable(AccelState *as)
+{
+    g_assert_not_reached();
+}
+
+static bool split_supports_guest_debug(AccelState *as)
+{
+    g_assert_not_reached();
+}
+
+static int split_gdbstub_supported_sstep_flags(AccelState *as)
+{
+    g_assert_not_reached();
+}
+
+static void split_get_stats(AccelState *as, GString *buf)
+{
+    g_assert_not_reached();
+}
+
+static void split_accel_class_init(ObjectClass *oc, const void *data)
+{
+    AccelClass *ac = ACCEL_CLASS(oc);
+
+    ac->name = "split";
+    ac->init_machine = split_accel_init_machine;
+    ac->setup_post = split_setup_post;
+    ac->has_memory = split_has_memory;
+    ac->cpus_are_resettable = split_cpus_are_resettable;
+    ac->supports_guest_debug = split_supports_guest_debug;
+    ac->gdbstub_supported_sstep_flags = split_gdbstub_supported_sstep_flags;
+    ac->get_stats = split_get_stats;
+    ac->allowed = &split_allowed;
+    ac->compat_props = NULL;
+}
+
+static const TypeInfo split_accel_type = {
+    .name = ACCEL_CLASS_NAME("split"),
+    .parent = TYPE_ACCEL,
+    .class_init = split_accel_class_init,
+};
+
+static void register_accel_types(void)
+{
+    type_register_static(&split_accel_type);
+}
+
+type_init(register_accel_types);
diff --git a/system/vl.c b/system/vl.c
index 3b7057e6c66..c7111d743ab 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -2433,6 +2433,7 @@ static void configure_accelerators(const char *progname)
             bool have_tcg = accel_find("tcg");
             bool have_kvm = accel_find("kvm");
             bool have_hvf = accel_find("hvf");
+            bool have_split = accel_find("split");
 
             if (have_tcg && have_kvm) {
                 if (g_str_has_suffix(progname, "kvm")) {
@@ -2447,6 +2448,9 @@ static void configure_accelerators(const char *progname)
                 accelerators = "tcg";
             } else if (have_hvf) {
                 accelerators = "hvf";
+            } else if (have_split) {
+                assert(have_tcg);
+                accelerators = "split";
             } else {
                 error_report("No accelerator selected and"
                              " no default accelerator available");
diff --git a/accel/Kconfig b/accel/Kconfig
index 4263cab7227..122ca9945ad 100644
--- a/accel/Kconfig
+++ b/accel/Kconfig
@@ -10,6 +10,12 @@ config HVF
 config TCG
     bool
 
+config SPLIT
+    bool
+    default y
+    depends on TCG
+    depends on HVF
+
 config KVM
     bool
 
diff --git a/accel/meson.build b/accel/meson.build
index 52909314bfa..b6d4740725f 100644
--- a/accel/meson.build
+++ b/accel/meson.build
@@ -9,6 +9,7 @@ if have_system
   subdir('qtest')
   subdir('kvm')
   subdir('xen')
+  subdir('split')
   subdir('stubs')
 endif
 
diff --git a/accel/split/meson.build b/accel/split/meson.build
new file mode 100644
index 00000000000..fbc68593180
--- /dev/null
+++ b/accel/split/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+split_ss = ss.source_set()
+split_ss.add(files(
+  'split-all.c',
+  'split-accel-ops.c',
+))
+
+specific_ss.add_all(when: 'CONFIG_SPLIT', if_true: split_ss)
