[PATCH 3/5] hw/misc: add K230 IOMUX and mailbox/IPCM.
Leo Cheng <[email protected]>
| Newsgroups | org.nongnu.qemu-devel,org.nongnu.qemu-riscv |
|---|---|
| Message-ID | <[email protected]> |
IOMUX is the K230 pin-configuration controller (64 pins, one 32-bit register per pin) with the field layout of the upstream Linux pinctrl-k230 driver. The mailbox/IPCM block at 0x91104000 is an inter-core interrupt-notify mailbox plus a hardware spinlock array: a write to a CPU2DSP/DSP2CPU SET register rings the doorbell IRQ, CLEAR acks, and the 0xA0 array is 128 test-and-set locks. Message payloads travel through shared memory, so no data registers are modelled. Register semantics from the Canaan k230_sdk. Covered by the k230-iomux and k230-mailbox qtests. Signed-off-by: Leo Cheng <[email protected]> --- hw/misc/Kconfig | 6 + hw/misc/k230_iomux.c | 101 ++++++++++++++++ hw/misc/k230_mailbox.c | 206 ++++++++++++++++++++++++++++++++ hw/misc/meson.build | 2 + hw/misc/trace-events | 10 ++ include/hw/misc/k230_iomux.h | 44 +++++++ include/hw/misc/k230_mailbox.h | 61 ++++++++++ tests/qtest/k230-iomux-test.c | 62 ++++++++++ tests/qtest/k230-mailbox-test.c | 89 ++++++++++++++ 9 files changed, 581 insertions(+) create mode 100644 hw/misc/k230_iomux.c create mode 100644 hw/misc/k230_mailbox.c create mode 100644 include/hw/misc/k230_iomux.h create mode 100644 include/hw/misc/k230_mailbox.h create mode 100644 tests/qtest/k230-iomux-test.c create mode 100644 tests/qtest/k230-mailbox-test.c diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 1543ee6..742ea2f 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -186,6 +186,12 @@ config AUX config UNIMP bool +config K230_IOMUX + bool + +config K230_MAILBOX + bool + config LED bool diff --git a/hw/misc/k230_iomux.c b/hw/misc/k230_iomux.c new file mode 100644 index 0000000..87e7587 --- /dev/null +++ b/hw/misc/k230_iomux.c @@ -0,0 +1,101 @@ +/* + * K230 IOMUX (pin configuration) controller + * + * Copyright (c) 2026 Leo Cheng <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18); register layout matches + * the upstream Linux driver drivers/pinctrl/pinctrl-k230.c. + */ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/core/sysbus.h" +#include "migration/vmstate.h" +#include "hw/misc/k230_iomux.h" +#include "trace.h" + +static uint64_t k230_iomux_read(void *opaque, hwaddr addr, unsigned int size) +{ + K230IomuxState *s = K230_IOMUX(opaque); + uint32_t value = 0; + + if (addr / 4 < K230_IOMUX_NPINS) { + value = s->pin[addr / 4]; + } + + trace_k230_iomux_read(addr, value); + return value; +} + +static void k230_iomux_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + K230IomuxState *s = K230_IOMUX(opaque); + + trace_k230_iomux_write(addr, value); + + if (addr / 4 < K230_IOMUX_NPINS) { + s->pin[addr / 4] = value & K230_PC_MASK; + } +} + +static const MemoryRegionOps k230_iomux_ops = { + .read = k230_iomux_read, + .write = k230_iomux_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void k230_iomux_reset(DeviceState *dev) +{ + K230IomuxState *s = K230_IOMUX(dev); + + memset(s->pin, 0, sizeof(s->pin)); +} + +static const VMStateDescription vmstate_k230_iomux = { + .name = "k230.iomux", + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(pin, K230IomuxState, K230_IOMUX_NPINS), + VMSTATE_END_OF_LIST() + } +}; + +static void k230_iomux_realize(DeviceState *dev, Error **errp) +{ + K230IomuxState *s = K230_IOMUX(dev); + + memory_region_init_io(&s->mmio, OBJECT(dev), &k230_iomux_ops, s, + TYPE_K230_IOMUX, K230_IOMUX_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); +} + +static void k230_iomux_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = k230_iomux_realize; + device_class_set_legacy_reset(dc, k230_iomux_reset); + dc->vmsd = &vmstate_k230_iomux; + dc->desc = "K230 IOMUX"; +} + +static const TypeInfo k230_iomux_info = { + .name = TYPE_K230_IOMUX, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230IomuxState), + .class_init = k230_iomux_class_init, +}; + +static void k230_iomux_register_type(void) +{ + type_register_static(&k230_iomux_info); +} + +type_init(k230_iomux_register_type) diff --git a/hw/misc/k230_mailbox.c b/hw/misc/k230_mailbox.c new file mode 100644 index 0000000..efcb843 --- /dev/null +++ b/hw/misc/k230_mailbox.c @@ -0,0 +1,206 @@ +/* + * K230 mailbox / IPCM block (inter-core interrupt notify + hardware spinlock) + * + * Copyright (c) 2026 Leo Cheng <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Register semantics from the Canaan k230_sdk: ipcm_platform.h (interrupt + * register offsets/bits), platform_riscv_rtsmart.c (SET rings the doorbell, + * CLEAR acks), drv_hardlock.c (test-and-set array at 0xA0). Message payloads + * are not carried by any register; they travel through shared memory. + */ +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "hw/core/sysbus.h" +#include "hw/core/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/k230_mailbox.h" +#include "trace.h" + +static void k230_mailbox_update_irq(K230MailboxState *s, int ch) +{ + bool level = s->status[ch] && (s->en[ch] & K230_MB_INT_EN); + + qemu_set_irq(s->irq[ch], level); +} + +static int k230_mailbox_lock_num(hwaddr addr) +{ + if (addr < K230_MB_HARDLOCK_BASE) { + return -1; + } + addr -= K230_MB_HARDLOCK_BASE; + if (addr / 4 >= K230_MB_HARDLOCK_MAX) { + return -1; + } + return addr / 4; +} + +static uint64_t k230_mailbox_read(void *opaque, hwaddr addr, unsigned int size) +{ + K230MailboxState *s = K230_MAILBOX(opaque); + int num = k230_mailbox_lock_num(addr); + uint32_t value = 0; + + if (num >= 0) { + value = s->lock[num]; /* 0 = free (now taken), 1 = already held */ + s->lock[num] = 1; + trace_k230_mailbox_lock_read(num, value); + return value; + } + + switch (addr) { + case K230_MB_CPU2DSP_EN: + value = s->en[0]; + break; + case K230_MB_CPU2DSP_STATUS: + value = s->status[0]; + break; + case K230_MB_CPU2DSP_ERR: + value = s->err[0]; + break; + case K230_MB_DSP2CPU_EN: + value = s->en[1]; + break; + case K230_MB_DSP2CPU_STATUS: + value = s->status[1]; + break; + case K230_MB_DSP2CPU_ERR: + value = s->err[1]; + break; + default: + break; + } + + trace_k230_mailbox_read(addr, value); + return value; +} + +static void k230_mailbox_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + K230MailboxState *s = K230_MAILBOX(opaque); + int num = k230_mailbox_lock_num(addr); + + if (num >= 0) { + s->lock[num] = value & 1; + trace_k230_mailbox_lock_write(num, value); + return; + } + + trace_k230_mailbox_write(addr, value); + + switch (addr) { + case K230_MB_CPU2DSP_EN: + s->en[0] = value; + if (value & K230_MB_INT_SOFTRST) { + s->status[0] = 0; + } + k230_mailbox_update_irq(s, 0); + break; + case K230_MB_CPU2DSP_SET: + s->status[0] = 1; + k230_mailbox_update_irq(s, 0); + break; + case K230_MB_CPU2DSP_CLEAR: + s->status[0] = 0; + k230_mailbox_update_irq(s, 0); + break; + case K230_MB_CPU2DSP_ERR: + s->err[0] = value; + break; + case K230_MB_DSP2CPU_EN: + s->en[1] = value; + if (value & K230_MB_INT_SOFTRST) { + s->status[1] = 0; + } + k230_mailbox_update_irq(s, 1); + break; + case K230_MB_DSP2CPU_SET: + s->status[1] = 1; + k230_mailbox_update_irq(s, 1); + break; + case K230_MB_DSP2CPU_CLEAR: + s->status[1] = 0; + k230_mailbox_update_irq(s, 1); + break; + case K230_MB_DSP2CPU_ERR: + s->err[1] = value; + break; + default: + break; + } +} + +static const MemoryRegionOps k230_mailbox_ops = { + .read = k230_mailbox_read, + .write = k230_mailbox_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void k230_mailbox_reset(DeviceState *dev) +{ + K230MailboxState *s = K230_MAILBOX(dev); + + memset(s->en, 0, sizeof(s->en)); + memset(s->status, 0, sizeof(s->status)); + memset(s->err, 0, sizeof(s->err)); + memset(s->lock, 0, sizeof(s->lock)); + for (int i = 0; i < K230_MB_CHANNELS; i++) { + qemu_set_irq(s->irq[i], 0); + } +} + +static const VMStateDescription vmstate_k230_mailbox = { + .name = "k230.mailbox", + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(en, K230MailboxState, K230_MB_CHANNELS), + VMSTATE_UINT32_ARRAY(status, K230MailboxState, K230_MB_CHANNELS), + VMSTATE_UINT32_ARRAY(err, K230MailboxState, K230_MB_CHANNELS), + VMSTATE_UINT8_ARRAY(lock, K230MailboxState, K230_MB_HARDLOCK_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static void k230_mailbox_realize(DeviceState *dev, Error **errp) +{ + K230MailboxState *s = K230_MAILBOX(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->mmio, OBJECT(dev), &k230_mailbox_ops, s, + TYPE_K230_MAILBOX, K230_MAILBOX_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + for (int i = 0; i < K230_MB_CHANNELS; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } +} + +static void k230_mailbox_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = k230_mailbox_realize; + device_class_set_legacy_reset(dc, k230_mailbox_reset); + dc->vmsd = &vmstate_k230_mailbox; + dc->desc = "K230 mailbox/IPCM"; +} + +static const TypeInfo k230_mailbox_info = { + .name = TYPE_K230_MAILBOX, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230MailboxState), + .class_init = k230_mailbox_class_init, +}; + +static void k230_mailbox_register_type(void) +{ + type_register_static(&k230_mailbox_info); +} + +type_init(k230_mailbox_register_type) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 23265f6..c0a678b 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -6,6 +6,8 @@ system_ss.add(when: 'CONFIG_ISA_TESTDEV', if_true: files('pc-testdev.c')) system_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c')) system_ss.add(when: 'CONFIG_IOMMU_TESTDEV', if_true: files('iommu-testdev.c')) system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) +system_ss.add(when: 'CONFIG_K230_IOMUX', if_true: files('k230_iomux.c')) +system_ss.add(when: 'CONFIG_K230_MAILBOX', if_true: files('k230_mailbox.c')) system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) system_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) system_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c9a868b..5bf28dd 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -442,3 +442,13 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u" iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x" iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x" iommu_testdev_dma_armed(bool armed) "armed=%d" + +# k230_iomux.c +k230_iomux_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " val 0x%08x" +k230_iomux_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 + +# k230_mailbox.c +k230_mailbox_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " val 0x%08x" +k230_mailbox_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +k230_mailbox_lock_read(int num, uint32_t old) "lock %d old %u" +k230_mailbox_lock_write(int num, uint64_t val) "lock %d val 0x%" PRIx64 diff --git a/include/hw/misc/k230_iomux.h b/include/hw/misc/k230_iomux.h new file mode 100644 index 0000000..83c3acf --- /dev/null +++ b/include/hw/misc/k230_iomux.h @@ -0,0 +1,44 @@ +/* + * K230 IOMUX (pin configuration) controller + * + * Copyright (c) 2026 Leo Cheng <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Register layout follows the upstream Linux pinctrl-k230 driver. + * (64 pins, one 32-bit config register per pin at pin*4). + */ +#ifndef HW_MISC_K230_IOMUX_H +#define HW_MISC_K230_IOMUX_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" + +#define TYPE_K230_IOMUX "riscv.k230.iomux" +OBJECT_DECLARE_SIMPLE_TYPE(K230IomuxState, K230_IOMUX) + +#define K230_IOMUX_MMIO_SIZE 0x800 +#define K230_IOMUX_NPINS 64 + +/* Per-pin config bits (pinctrl-k230.c) */ +#define K230_PC_ST (1u << 0) /* schmitt trigger */ +#define K230_PC_DS (0xfu << 1) /* drive strength [4:1] */ +#define K230_PC_PD (1u << 5) /* pull-down */ +#define K230_PC_PU (1u << 6) /* pull-up */ +#define K230_PC_OE (1u << 7) /* output enable */ +#define K230_PC_IE (1u << 8) /* input enable */ +#define K230_PC_MSC (1u << 9) /* power/misc select */ +#define K230_PC_SL (1u << 10) /* slew rate */ +#define K230_PC_SEL (0x7u << 11) /* function/mux select [13:11] */ +#define K230_PC_MASK (K230_PC_ST | K230_PC_DS | K230_PC_PD | K230_PC_PU | \ + K230_PC_OE | K230_PC_IE | K230_PC_MSC | K230_PC_SL | \ + K230_PC_SEL) + +struct K230IomuxState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + uint32_t pin[K230_IOMUX_NPINS]; +}; + +#endif diff --git a/include/hw/misc/k230_mailbox.h b/include/hw/misc/k230_mailbox.h new file mode 100644 index 0000000..f939637 --- /dev/null +++ b/include/hw/misc/k230_mailbox.h @@ -0,0 +1,61 @@ +/* + * K230 mailbox / IPCM block (inter-core interrupt notify + hardware spinlock) + * + * Copyright (c) 2026 Leo Cheng <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * The K230 mailbox at 0x91104000 is an interrupt-notification block, not a + * data FIFO: payloads travel through shared memory, the mailbox only rings a + * doorbell interrupt. Two sub-blocks share the 4KB page: + * - IPCM interrupt registers 0x00-0x24 (CPU2DSP / DSP2CPU EN/SET/CLEAR/ + * STATUS/ERR), from Canaan k230_sdk ipcm_platform.h. + * - hardware spinlock array at 0xA0 (128 test-and-set locks), from the + * Canaan k230_sdk drv_hardlock. + */ +#ifndef HW_MISC_K230_MAILBOX_H +#define HW_MISC_K230_MAILBOX_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" + +#define TYPE_K230_MAILBOX "riscv.k230.mailbox" +OBJECT_DECLARE_SIMPLE_TYPE(K230MailboxState, K230_MAILBOX) + +#define K230_MAILBOX_MMIO_SIZE 0x1000 + +/* IPCM interrupt block (ipcm_platform.h) */ +#define K230_MB_CPU2DSP_EN 0x00 +#define K230_MB_CPU2DSP_SET 0x04 +#define K230_MB_CPU2DSP_CLEAR 0x08 +#define K230_MB_CPU2DSP_STATUS 0x0c +#define K230_MB_CPU2DSP_ERR 0x10 +#define K230_MB_DSP2CPU_EN 0x14 +#define K230_MB_DSP2CPU_SET 0x18 +#define K230_MB_DSP2CPU_CLEAR 0x1c +#define K230_MB_DSP2CPU_STATUS 0x20 +#define K230_MB_DSP2CPU_ERR 0x24 + +#define K230_MB_INT_EN (1u << 0) /* INTR_EN_EABLE */ +#define K230_MB_INT_SOFTRST (1u << 1) /* INTR_SOFT_RST_VALID */ +#define K230_MB_INT_RAW_EN (1u << 16) /* INTR_RAW_EN_EABLE */ + +/* hardware spinlock array */ +#define K230_MB_HARDLOCK_BASE 0xa0 +#define K230_MB_HARDLOCK_MAX 128 + +#define K230_MB_CHANNELS 2 /* CPU2DSP, DSP2CPU */ + +struct K230MailboxState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + qemu_irq irq[K230_MB_CHANNELS]; + + uint32_t en[K230_MB_CHANNELS]; + uint32_t status[K230_MB_CHANNELS]; + uint32_t err[K230_MB_CHANNELS]; + uint8_t lock[K230_MB_HARDLOCK_MAX]; +}; + +#endif diff --git a/tests/qtest/k230-iomux-test.c b/tests/qtest/k230-iomux-test.c new file mode 100644 index 0000000..855d993 --- /dev/null +++ b/tests/qtest/k230-iomux-test.c @@ -0,0 +1,62 @@ +/* + * QTest testcase for K230 IOMUX + * + * Copyright (c) 2026 Leo Cheng <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" +#include "hw/misc/k230_iomux.h" + +#define IOMUX_BASE 0x91105000 + +static void test_reset_values(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + g_assert_cmphex(qtest_readl(qts, IOMUX_BASE + 0), ==, 0); + g_assert_cmphex(qtest_readl(qts, IOMUX_BASE + 63 * 4), ==, 0); + + qtest_quit(qts); +} + +/* Each pin config register is read/write; reserved bits above [13:0] drop */ +static void test_pin_rw(void) +{ + QTestState *qts = qtest_init("-machine k230"); + uint32_t cfg = K230_PC_IE | K230_PC_PU | (3u << 11); /* IE, PU, SEL=3 */ + + qtest_writel(qts, IOMUX_BASE + 5 * 4, cfg); + g_assert_cmphex(qtest_readl(qts, IOMUX_BASE + 5 * 4), ==, cfg); + + qtest_writel(qts, IOMUX_BASE + 10 * 4, 0xFFFFFFFF); + g_assert_cmphex(qtest_readl(qts, IOMUX_BASE + 10 * 4), ==, K230_PC_MASK); + + /* pin 5 unaffected by pin 10 */ + g_assert_cmphex(qtest_readl(qts, IOMUX_BASE + 5 * 4), ==, cfg); + + qtest_quit(qts); +} + +/* Registers past the 64-pin window read back zero and ignore writes */ +static void test_out_of_range(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + qtest_writel(qts, IOMUX_BASE + 64 * 4, 0x1234); + g_assert_cmphex(qtest_readl(qts, IOMUX_BASE + 64 * 4), ==, 0); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-iomux/reset_values", test_reset_values); + qtest_add_func("/k230-iomux/pin_rw", test_pin_rw); + qtest_add_func("/k230-iomux/out_of_range", test_out_of_range); + + return g_test_run(); +} diff --git a/tests/qtest/k230-mailbox-test.c b/tests/qtest/k230-mailbox-test.c new file mode 100644 index 0000000..c3869f0 --- /dev/null +++ b/tests/qtest/k230-mailbox-test.c @@ -0,0 +1,89 @@ +/* + * QTest testcase for K230 mailbox / IPCM block + * + * Copyright (c) 2026 Leo Cheng <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" +#include "hw/misc/k230_mailbox.h" + +#define MB_BASE 0x91104000 +#define PLIC_BASE 0xF00000000ULL +#define PLIC_PENDING (PLIC_BASE + 0x1000) +#define MB_IRQ0 109 +#define MB_IRQ1 110 + +static bool plic_pending(QTestState *qts, int irq) +{ + uint32_t word = qtest_readl(qts, PLIC_PENDING + (irq / 32) * 4); + return (word >> (irq % 32)) & 1; +} + +/* SET rings the doorbell only when the channel interrupt is enabled */ +static void test_doorbell_cpu2dsp(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + /* SET with interrupt disabled: no doorbell */ + qtest_writel(qts, MB_BASE + K230_MB_CPU2DSP_SET, 0); + g_assert_false(plic_pending(qts, MB_IRQ0)); + qtest_writel(qts, MB_BASE + K230_MB_CPU2DSP_CLEAR, 0); + + /* enable, then SET rings the doorbell and latches STATUS */ + qtest_writel(qts, MB_BASE + K230_MB_CPU2DSP_EN, K230_MB_INT_EN); + qtest_writel(qts, MB_BASE + K230_MB_CPU2DSP_SET, 0); + g_assert_true(plic_pending(qts, MB_IRQ0)); + g_assert_cmpuint(qtest_readl(qts, MB_BASE + K230_MB_CPU2DSP_STATUS), ==, 1); + + /* CLEAR acks and drops STATUS */ + qtest_writel(qts, MB_BASE + K230_MB_CPU2DSP_CLEAR, 0); + g_assert_cmpuint(qtest_readl(qts, MB_BASE + K230_MB_CPU2DSP_STATUS), ==, 0); + + qtest_quit(qts); +} + +/* The reverse channel rings its own doorbell line */ +static void test_doorbell_dsp2cpu(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + qtest_writel(qts, MB_BASE + K230_MB_DSP2CPU_EN, K230_MB_INT_EN); + qtest_writel(qts, MB_BASE + K230_MB_DSP2CPU_SET, 0); + g_assert_true(plic_pending(qts, MB_IRQ1)); + g_assert_false(plic_pending(qts, MB_IRQ0)); /* independent of ch0 */ + + qtest_quit(qts); +} + +/* Hardlock array at 0xA0: read = test-and-set, write 0 = release */ +static void test_hardlock(void) +{ + QTestState *qts = qtest_init("-machine k230"); + hwaddr l0 = MB_BASE + K230_MB_HARDLOCK_BASE; + + g_assert_cmpuint(qtest_readl(qts, l0), ==, 0); /* acquired */ + g_assert_cmpuint(qtest_readl(qts, l0), ==, 1); /* busy */ + qtest_writel(qts, l0, 0); /* release */ + g_assert_cmpuint(qtest_readl(qts, l0), ==, 0); /* acquired again */ + + /* independence + last lock */ + g_assert_cmpuint(qtest_readl(qts, l0 + 4), ==, 0); + g_assert_cmpuint(qtest_readl(qts, l0), ==, 1); + g_assert_cmpuint(qtest_readl(qts, + l0 + (K230_MB_HARDLOCK_MAX - 1) * 4), ==, 0); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-mailbox/doorbell_cpu2dsp", test_doorbell_cpu2dsp); + qtest_add_func("/k230-mailbox/doorbell_dsp2cpu", test_doorbell_dsp2cpu); + qtest_add_func("/k230-mailbox/hardlock", test_hardlock); + + return g_test_run(); +} -- 2.43.0