new file mode 100644
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Loongson ipi interrupt support
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "hw/sysbus.h"
+#include "hw/intc/loongson_ipi_common.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "exec/address-spaces.h"
+#include "exec/memory.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr,
+ uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
+{
+ IPICore *s = opaque;
+ uint64_t ret = 0;
+ int index = 0;
+
+ addr &= 0xff;
+ switch (addr) {
+ case CORE_STATUS_OFF:
+ ret = s->status;
+ break;
+ case CORE_EN_OFF:
+ ret = s->en;
+ break;
+ case CORE_SET_OFF:
+ ret = 0;
+ break;
+ case CORE_CLEAR_OFF:
+ ret = 0;
+ break;
+ case CORE_BUF_20 ... CORE_BUF_38 + 4:
+ index = (addr - CORE_BUF_20) >> 2;
+ ret = s->buf[index];
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr);
+ break;
+ }
+
+ trace_loongson_ipi_read(size, (uint64_t)addr, ret);
+ *data = ret;
+ return MEMTX_OK;
+}
+
+static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr,
+ uint64_t *data,
+ unsigned size, MemTxAttrs attrs)
+{
+ LoongsonIPICommonState *ipi = opaque;
+ IPICore *s;
+
+ if (attrs.requester_id >= ipi->num_cpu) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ s = &ipi->cpu[attrs.requester_id];
+ return loongson_ipi_core_readl(s, addr, data, size, attrs);
+}
+
+static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu,
+ uint64_t val,
+ hwaddr addr, MemTxAttrs attrs)
+{
+ int i, mask = 0, data = 0;
+ AddressSpace *iocsr_as;
+ LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
+
+ iocsr_as = NULL;
+ if (licc->get_iocsr_as) {
+ iocsr_as = licc->get_iocsr_as(cpu);
+ }
+
+ if (!iocsr_as) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ /*
+ * bit 27-30 is mask for byte writing,
+ * if the mask is 0, we need not to do anything.
+ */
+ if ((val >> 27) & 0xf) {
+ data = address_space_ldl_le(iocsr_as, addr, attrs, NULL);
+ for (i = 0; i < 4; i++) {
+ /* get mask for byte writing */
+ if (val & (0x1 << (27 + i))) {
+ mask |= 0xff << (i * 8);
+ }
+ }
+ }
+
+ data &= mask;
+ data |= (val >> 32) & ~mask;
+ address_space_stl_le(iocsr_as, addr, data, attrs, NULL);
+
+ return MEMTX_OK;
+}
+
+static CPUState *get_cpu_by_arch_id(LoongsonIPICommonState *ipi, uint32_t cpuid)
+{
+ LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi);
+
+ if (licc->cpu_by_arch_id) {
+ return licc->cpu_by_arch_id(cpuid);
+ }
+
+ return cpu_by_arch_id(cpuid);
+}
+
+static MemTxResult mail_send(LoongsonIPICommonState *ipi, uint64_t val,
+ MemTxAttrs attrs)
+{
+ uint32_t cpuid;
+ hwaddr addr;
+ CPUState *cs;
+
+ cpuid = extract32(val, 16, 10);
+ cs = get_cpu_by_arch_id(ipi, cpuid);
+ if (cs == NULL) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ /* override requester_id */
+ addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c);
+ attrs.requester_id = cs->cpu_index;
+ return send_ipi_data(ipi, cs, val, addr, attrs);
+}
+
+static MemTxResult any_send(LoongsonIPICommonState *ipi, uint64_t val,
+ MemTxAttrs attrs)
+{
+ uint32_t cpuid;
+ hwaddr addr;
+ CPUState *cs;
+
+ cpuid = extract32(val, 16, 10);
+ cs = get_cpu_by_arch_id(ipi, cpuid);
+ if (cs == NULL) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ /* override requester_id */
+ addr = val & 0xffff;
+ attrs.requester_id = cs->cpu_index;
+ return send_ipi_data(ipi, cs, val, addr, attrs);
+}
+
+MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs)
+{
+ IPICore *s = opaque;
+ LoongsonIPICommonState *ipi = s->ipi;
+ int index = 0;
+ uint32_t cpuid;
+ uint8_t vector;
+ CPUState *cs;
+
+ addr &= 0xff;
+ trace_loongson_ipi_write(size, (uint64_t)addr, val);
+ switch (addr) {
+ case CORE_STATUS_OFF:
+ qemu_log_mask(LOG_GUEST_ERROR, "can not be written");
+ break;
+ case CORE_EN_OFF:
+ s->en = val;
+ break;
+ case CORE_SET_OFF:
+ s->status |= val;
+ if (s->status != 0 && (s->status & s->en) != 0) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+ case CORE_CLEAR_OFF:
+ s->status &= ~val;
+ if (s->status == 0 && s->en != 0) {
+ qemu_irq_lower(s->irq);
+ }
+ break;
+ case CORE_BUF_20 ... CORE_BUF_38 + 4:
+ index = (addr - CORE_BUF_20) >> 2;
+ s->buf[index] = val;
+ break;
+ case IOCSR_IPI_SEND:
+ cpuid = extract32(val, 16, 10);
+ /* IPI status vector */
+ vector = extract8(val, 0, 5);
+ cs = get_cpu_by_arch_id(ipi, cpuid);
+ if (cs == NULL || cs->cpu_index >= ipi->num_cpu) {
+ return MEMTX_DECODE_ERROR;
+ }
+ loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], CORE_SET_OFF,
+ BIT(vector), 4, attrs);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr);
+ break;
+ }
+
+ return MEMTX_OK;
+}
+
+static MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs)
+{
+ LoongsonIPICommonState *ipi = opaque;
+ IPICore *s;
+
+ if (attrs.requester_id >= ipi->num_cpu) {
+ return MEMTX_DECODE_ERROR;
+ }
+
+ s = &ipi->cpu[attrs.requester_id];
+ return loongson_ipi_core_writel(s, addr, val, size, attrs);
+}
+
+static const MemoryRegionOps loongson_ipi_iocsr_ops = {
+ .read_with_attrs = loongson_ipi_iocsr_readl,
+ .write_with_attrs = loongson_ipi_iocsr_writel,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 8,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/* mail send and any send only support writeq */
+static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size, MemTxAttrs attrs)
+{
+ MemTxResult ret = MEMTX_OK;
+ LoongsonIPICommonState *ipi = opaque;
+
+ addr &= 0xfff;
+ switch (addr) {
+ case MAIL_SEND_OFFSET:
+ ret = mail_send(ipi, val, attrs);
+ break;
+ case ANY_SEND_OFFSET:
+ ret = any_send(ipi, val, attrs);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const MemoryRegionOps loongson_ipi64_ops = {
+ .write_with_attrs = loongson_ipi_writeq,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+ .valid.min_access_size = 8,
+ .valid.max_access_size = 8,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void loongson_ipi_common_realize(DeviceState *dev, Error **errp)
+{
+ LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ int i;
+
+ if (s->num_cpu == 0) {
+ error_setg(errp, "num-cpu must be at least 1");
+ return;
+ }
+
+ memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev),
+ &loongson_ipi_iocsr_ops,
+ s, "loongson_ipi_iocsr", 0x48);
+
+ /* loongson_ipi_iocsr performs re-entrant IO through ipi_send */
+ s->ipi_iocsr_mem.disable_reentrancy_guard = true;
+
+ sysbus_init_mmio(sbd, &s->ipi_iocsr_mem);
+
+ memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev),
+ &loongson_ipi64_ops,
+ s, "loongson_ipi64_iocsr", 0x118);
+ sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem);
+
+ s->cpu = g_new0(IPICore, s->num_cpu);
+ if (s->cpu == NULL) {
+ error_setg(errp, "Memory allocation for IPICore faile");
+ return;
+ }
+
+ for (i = 0; i < s->num_cpu; i++) {
+ s->cpu[i].ipi = s;
+ qdev_init_gpio_out(dev, &s->cpu[i].irq, 1);
+ }
+}
+
+static int loongson_ipi_pre_save(void *opaque)
+{
+ LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
+ LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
+
+ if (c->pre_save) {
+ c->pre_save(s);
+ }
+
+ return 0;
+}
+
+static int loongson_ipi_post_load(void *opaque, int version_id)
+{
+ LoongsonIPICommonState *s = (LoongsonIPICommonState *)opaque;
+ LoongsonIPICommonClass *c = LOONGSON_IPI_COMMON_GET_CLASS(s);
+
+ if (c->post_load) {
+ c->post_load(s);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_ipi_core = {
+ .name = "ipi-single",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(status, IPICore),
+ VMSTATE_UINT32(en, IPICore),
+ VMSTATE_UINT32(set, IPICore),
+ VMSTATE_UINT32(clear, IPICore),
+ VMSTATE_UINT32_ARRAY(buf, IPICore, IPI_MBX_NUM * 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_loongson_ipi = {
+ /* Fixed name to keep compatible */
+ .name = "loongson_ipi",
+ .pre_save = loongson_ipi_pre_save,
+ .post_load = loongson_ipi_post_load,
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (const VMStateField[]) {
+ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState,
+ num_cpu, vmstate_ipi_core, IPICore),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property ipi_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void loongson_ipi_common_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = loongson_ipi_common_realize;
+ device_class_set_props(dc, ipi_properties);
+ dc->vmsd = &vmstate_loongson_ipi;
+}
+
+static void loongson_ipi_common_finalize(Object *obj)
+{
+ LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(obj);
+
+ g_free(s->cpu);
+}
+
+static const TypeInfo loongson_ipi_common_info = {
+ .name = TYPE_LOONGSON_IPI_COMMON,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(LoongsonIPICommonState),
+ .class_size = sizeof(LoongsonIPICommonClass),
+ .class_init = loongson_ipi_common_class_init,
+ .instance_finalize = loongson_ipi_common_finalize,
+ .abstract = true,
+};
+
+static void loongson_ipi_common_register_types(void)
+{
+ type_register_static(&loongson_ipi_common_info);
+}
+
+type_init(loongson_ipi_common_register_types)
new file mode 100644
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Loongson ipi interrupt header files
+ *
+ * Copyright (C) 2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef HW_LOONGSON_IPI_COMMON_H
+#define HW_LOONGSON_IPI_COMMON_H
+
+#include "hw/sysbus.h"
+#include "hw/core/cpu.h"
+#include "qom/object.h"
+
+/* Mainy used by iocsr read and write */
+#define SMP_IPI_MAILBOX 0x1000ULL
+#define CORE_STATUS_OFF 0x0
+#define CORE_EN_OFF 0x4
+#define CORE_SET_OFF 0x8
+#define CORE_CLEAR_OFF 0xc
+#define CORE_BUF_20 0x20
+#define CORE_BUF_28 0x28
+#define CORE_BUF_30 0x30
+#define CORE_BUF_38 0x38
+#define IOCSR_IPI_SEND 0x40
+#define IOCSR_MAIL_SEND 0x48
+#define IOCSR_ANY_SEND 0x158
+
+#define MAIL_SEND_ADDR (SMP_IPI_MAILBOX + IOCSR_MAIL_SEND)
+#define MAIL_SEND_OFFSET 0
+#define ANY_SEND_OFFSET (IOCSR_ANY_SEND - IOCSR_MAIL_SEND)
+
+#define IPI_MBX_NUM 4
+
+#define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common"
+typedef struct LoongsonIPICommonClass LoongsonIPICommonClass;
+typedef struct LoongsonIPICommonState LoongsonIPICommonState;
+DECLARE_OBJ_CHECKERS(LoongsonIPICommonState, LoongsonIPICommonClass,
+ LOONGSON_IPI_COMMON, TYPE_LOONGSON_IPI_COMMON)
+
+typedef struct IPICore {
+ LoongsonIPICommonState *ipi;
+ uint32_t status;
+ uint32_t en;
+ uint32_t set;
+ uint32_t clear;
+ /* 64bit buf divide into 2 32bit buf */
+ uint32_t buf[IPI_MBX_NUM * 2];
+ qemu_irq irq;
+} IPICore;
+
+struct LoongsonIPICommonState {
+ SysBusDevice parent_obj;
+ MemoryRegion ipi_iocsr_mem;
+ MemoryRegion ipi64_iocsr_mem;
+ uint32_t num_cpu;
+ IPICore *cpu;
+};
+
+struct LoongsonIPICommonClass {
+ /*< private >*/
+ SysBusDeviceClass parent_class;
+ /*< public >*/
+
+ void (*pre_save)(LoongsonIPICommonState *s);
+ void (*post_load)(LoongsonIPICommonState *s);
+ AddressSpace *(*get_iocsr_as)(CPUState *cpu);
+ CPUState *(*cpu_by_arch_id)(int64_t id);
+};
+
+MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr,
+ uint64_t *data,
+ unsigned size, MemTxAttrs attrs);
+MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size,
+ MemTxAttrs attrs);
+#endif
Loongson ipi common class and instance is created here, it comes from file loongson_ipi mostly. For the new added loongson ipi common class, there is four interfaces defined here: 1. Interfaces pre_save/post_load are used for future kvm child class 2. Interface get_iocsr_as can be used for different architectures, now MIPS 3A4000 and LoongArch 3A5000 machine use this ip, can inherit this common class. 3. Interace cpu_by_arch_id is added, by default generic function cpu_by_arch_id() is used to search vcpu from physical cpuid, it is generic searching method. Different machine may define other search methods such binary searching method. Signed-off-by: Bibo Mao <maobibo@loongson.cn> --- hw/intc/loongson_ipi_common.c | 394 ++++++++++++++++++++++++++ include/hw/intc/loongson_ipi_common.h | 77 +++++ 2 files changed, 471 insertions(+) create mode 100644 hw/intc/loongson_ipi_common.c create mode 100644 include/hw/intc/loongson_ipi_common.h