From 4bc4533c598cbbbc220713c77d52a159d984ee43 Mon Sep 17 00:00:00 2001 From: Yanfeng Liu Date: Sat, 17 Aug 2024 12:08:22 +0800 Subject: [PATCH] riscv/qemu-rv: add RPTUN support This adds initial RPTUN support for qemu-rv chip. Signed-off-by: Yanfeng Liu --- arch/risc-v/src/qemu-rv/Make.defs | 4 + arch/risc-v/src/qemu-rv/qemu_rv_irq.c | 32 ++- arch/risc-v/src/qemu-rv/qemu_rv_rptun.c | 357 ++++++++++++++++++++++++ arch/risc-v/src/qemu-rv/qemu_rv_rptun.h | 67 +++++ 4 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 arch/risc-v/src/qemu-rv/qemu_rv_rptun.c create mode 100644 arch/risc-v/src/qemu-rv/qemu_rv_rptun.h diff --git a/arch/risc-v/src/qemu-rv/Make.defs b/arch/risc-v/src/qemu-rv/Make.defs index 07ad8f22a845f..7ccc334767a3e 100644 --- a/arch/risc-v/src/qemu-rv/Make.defs +++ b/arch/risc-v/src/qemu-rv/Make.defs @@ -38,3 +38,7 @@ endif ifeq ($(CONFIG_BUILD_PROTECTED),y) CHIP_CSRCS += qemu_rv_userspace.c endif + +ifeq ($(CONFIG_RPTUN),y) +CHIP_CSRCS += qemu_rv_rptun.c +endif diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_irq.c b/arch/risc-v/src/qemu-rv/qemu_rv_irq.c index 145a56d591d86..71c4b05fac8d9 100644 --- a/arch/risc-v/src/qemu-rv/qemu_rv_irq.c +++ b/arch/risc-v/src/qemu-rv/qemu_rv_irq.c @@ -37,6 +37,30 @@ #include "riscv_aia.h" #include "chip.h" +#ifdef CONFIG_RPTUN +#include "qemu_rv_rptun.h" +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef CONFIG_RPTUN +static int qemu_ipi_handler(int mcause, FAR void *regs, FAR void *args) +{ + /* Clear IPI (Inter-Processor-Interrupt) */ + + riscv_ipi_clear(up_cpu_index()); + +#ifdef CONFIG_SMP + riscv_pause_handler(mcause, regs, args); +#endif + + qemu_rptun_ipi(); + return OK; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -106,7 +130,13 @@ void up_irqinitialize(void) riscv_exception_attach(); -#ifdef CONFIG_SMP +#ifdef CONFIG_RPTUN + /* Replace default IRQ_SOFT handler */ + + irq_attach(RISCV_IRQ_SOFT, qemu_ipi_handler, NULL); +#endif + +#if defined(CONFIG_SMP) || defined(CONFIG_RPTUN) /* Clear IPI for CPU0 */ riscv_ipi_clear(0); diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_rptun.c b/arch/risc-v/src/qemu-rv/qemu_rv_rptun.c new file mode 100644 index 0000000000000..958ec85c2a1a3 --- /dev/null +++ b/arch/risc-v/src/qemu-rv/qemu_rv_rptun.c @@ -0,0 +1,357 @@ +/**************************************************************************** + * arch/risc-v/src/qemu-rv/qemu_rv_rptun.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include "hardware/qemu_rv_memorymap.h" +#include "riscv_internal.h" + +#include "qemu_rv_rptun.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define rpinfo rpmsginfo +#define rpwarn rpmsgwarn +#define rperr rpmsgerr + +/* Vring config parameters taken from nrf53_rptun */ + +#define VRINGS 2 /* Number of vrings */ +#define VRING_ALIGN 8 /* Vring alignment */ +#define VRING_NR 8 /* Number of descriptors */ +#define VRING_SIZE 512 /* Size of one descriptor */ + +/* The RPMSG default channel used with only one RPMSG channel */ + +#define VRING_SHMEM (CONFIG_QEMU_RPTUN_SHM_BASE) /* Vring addr */ +#define VRING0_NOTIFYID (RSC_NOTIFY_ID_ANY) /* Vring0 id */ +#define VRING1_NOTIFYID (RSC_NOTIFY_ID_ANY) /* Vring1 id */ + +#define VRING_SHMEM_END (VRING_SHMEM + CONFIG_QEMU_RPTUN_SHM_SIZE) + +/* Number of rptun peers */ + +#ifdef CONFIG_QEMU_RPTUN_MASTER +#define NUM_RPTUN_PEERS (CONFIG_QEMU_RPTUN_REMOTE_NUM) +#else +#define NUM_RPTUN_PEERS (1) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct qemu_rptun_shmem_s +{ + volatile uintptr_t base; + struct rptun_rsc_s rsc; +}; + +struct qemu_rptun_dev_s +{ + struct rptun_dev_s rptun; + rptun_callback_t callback; + void *arg; + bool master; + struct qemu_rptun_shmem_s *shmem; + struct simple_addrenv_s addrenv[VRINGS]; + uintreg_t peeripi; + char peername[RPMSG_NAME_SIZE + 1]; + uint8_t ndx; +}; + +#define as_qemu_rptun_dev(d) container_of(d, struct qemu_rptun_dev_s, rptun) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static const char *rp_get_cpuname(struct rptun_dev_s *dev); +static struct rptun_rsc_s *rp_get_resource(struct rptun_dev_s *dev); +static bool rp_is_autostart(struct rptun_dev_s *dev); +static bool rp_is_master(struct rptun_dev_s *dev); +static int rp_start(struct rptun_dev_s *dev); +static int rp_stop(struct rptun_dev_s *dev); +static int rp_notify(struct rptun_dev_s *dev, uint32_t notifyid); +static int rp_set_callback(struct rptun_dev_s *, rptun_callback_t, void *); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct rptun_ops_s g_rptun_ops = +{ + .get_cpuname = rp_get_cpuname, + .get_resource = rp_get_resource, + .is_autostart = rp_is_autostart, + .is_master = rp_is_master, + .start = rp_start, + .stop = rp_stop, + .notify = rp_notify, + .register_callback = rp_set_callback, +}; + +#define SHMEM (struct qemu_rptun_shmem_s*)VRING_SHMEM +#define SHMEM_SIZE sizeof(struct qemu_rptun_shmem_s) +#define SHMEM_END (VRING_SHMEM + SHMEM_SIZE) + +static struct qemu_rptun_dev_s g_rptun_devs[NUM_RPTUN_PEERS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +int rp_init_ipi(void) +{ +#ifdef CONFIG_QEMU_RPTUN_MASTER + char *ptr = CONFIG_QEMU_RPTUN_REMOTE_IPIS; + int i = 0; + + while (*ptr != '\0') + { + if (i < CONFIG_QEMU_RPTUN_REMOTE_NUM) + { +#ifdef CONFIG_ARCH_RV64 + g_rptun_devs[i].peeripi = strtoll(ptr, &ptr, 0); +#else + g_rptun_devs[i].peeripi = strtol(ptr, &ptr, 0); +#endif + if (!g_rptun_devs[i].peeripi) + { + break; + } + } + + while (*ptr != '\0' && *ptr != ',') + { + ptr++; /* seek delimeter */ + } + + if (*ptr) + { + ptr++; /* skip delimeter */ + } + + i++; + } + + return i; + +#else + + g_rptun_devs[0].peeripi = CONFIG_QEMU_RPTUN_MASTER_IPI; + return 1; + +#endif +} + +static const char *rp_get_cpuname(struct rptun_dev_s *dev) +{ + struct qemu_rptun_dev_s *priv = as_qemu_rptun_dev(dev); + return priv->peername; +} + +static struct rptun_rsc_s *rp_get_resource(struct rptun_dev_s *dev) +{ + struct qemu_rptun_dev_s *priv = as_qemu_rptun_dev(dev); + struct rptun_rsc_s *rsc; + + if (priv->shmem != NULL) + { + return &priv->shmem->rsc; + } + + priv->shmem = SHMEM + CONFIG_QEMU_RPTUN_SHM_SIZE * priv->ndx; + + if (priv->master) + { + /* Perform initial setup */ + + rsc = &priv->shmem->rsc; + + rsc->rsc_tbl_hdr.ver = 1; + rsc->rsc_tbl_hdr.num = 1; + rsc->rsc_tbl_hdr.reserved[0] = 0; + rsc->rsc_tbl_hdr.reserved[1] = 0; + rsc->offset[0] = offsetof(struct rptun_rsc_s, + rpmsg_vdev); + + rsc->rpmsg_vdev.type = RSC_VDEV; + rsc->rpmsg_vdev.id = VIRTIO_ID_RPMSG; + rsc->rpmsg_vdev.dfeatures = 1 << VIRTIO_RPMSG_F_NS + | 1 << VIRTIO_RPMSG_F_ACK + | 1 << VIRTIO_RPMSG_F_BUFSZ; + rsc->rpmsg_vdev.config_len = sizeof(struct fw_rsc_config); + rsc->rpmsg_vdev.num_of_vrings = VRINGS; + + rsc->rpmsg_vring0.align = VRING_ALIGN; + rsc->rpmsg_vring0.num = VRING_NR; + rsc->rpmsg_vring0.notifyid = VRING0_NOTIFYID; + rsc->rpmsg_vring1.align = VRING_ALIGN; + rsc->rpmsg_vring1.num = VRING_NR; + rsc->rpmsg_vring1.notifyid = VRING1_NOTIFYID; + rsc->config.r2h_buf_size = VRING_SIZE; + rsc->config.h2r_buf_size = VRING_SIZE; + + priv->shmem->base = (uintptr_t)priv->shmem; + } + else + { + /* TODO: use IPI later, polling now. */ + + rpinfo("wait for shmem %p...\n", priv->shmem); + + while (priv->shmem->base == 0) + { + nxsig_usleep(100); + } + } + + rpinfo("shmem:%p, dev:%p\n", (void *)priv->shmem->base, dev); + return &priv->shmem->rsc; +} + +static bool rp_is_autostart(struct rptun_dev_s *dev) +{ + return true; +} + +static bool rp_is_master(struct rptun_dev_s *dev) +{ + struct qemu_rptun_dev_s *priv = as_qemu_rptun_dev(dev); + return priv->master; +} + +static int rp_start(struct rptun_dev_s *dev) +{ + rpinfo("%p\n", dev); + return 0; +} + +static int rp_stop(struct rptun_dev_s *dev) +{ + rpinfo("%p\n", dev); + return 0; +} + +static int rp_notify(struct rptun_dev_s *dev, uint32_t vqid) +{ + struct qemu_rptun_dev_s *priv = as_qemu_rptun_dev(dev); + UNUSED(vqid); + putreg32(1, priv->peeripi); + return 0; +} + +static int rp_set_callback(struct rptun_dev_s *dev, rptun_callback_t cb, + void *arg) +{ + struct qemu_rptun_dev_s *priv = as_qemu_rptun_dev(dev); + + priv->callback = cb; + priv->arg = arg; + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void qemu_rptun_ipi() +{ + for (int i = 0; i < NUM_RPTUN_PEERS ; i++) + { + if (g_rptun_devs[i].callback) + { + g_rptun_devs[i].callback(g_rptun_devs[i].arg, RPTUN_NOTIFY_ALL); + } + } +} + +int qemu_rptun_init() +{ + struct qemu_rptun_dev_s *dev; + int ret = OK; + + DEBUGASSERT(NUM_RPTUN_PEERS == rp_init_ipi()); + + /* master is responsible for initializing shmem */ + +#ifdef CONFIG_QEMU_RPTUN_MASTER + memset((void *)SHMEM, 0, SHMEM_SIZE * NUM_RPTUN_PEERS); + rpinfo("cleared %"PRIuPTR" @%p\n", SHMEM_SIZE * NUM_RPTUN_PEERS, SHMEM); +#endif + + for (int i = 0; i < NUM_RPTUN_PEERS; i++) + { + dev = &g_rptun_devs[i]; + +#ifdef CONFIG_QEMU_RPTUN_MASTER + dev->master = true; + dev->ndx = i; + snprintf(dev->peername, sizeof(dev->peername), "remote%d", i + 1); +#else + dev->master = false; + strncpy(dev->peername, "master", sizeof(dev->peername) - 1); +#endif + + dev->rptun.ops = &g_rptun_ops; + + ret = rptun_initialize(&dev->rptun); + if (ret < 0) + { + rperr("rptun %d failed %d!\n", i, ret); + return ret; + } + } + + return OK; +} diff --git a/arch/risc-v/src/qemu-rv/qemu_rv_rptun.h b/arch/risc-v/src/qemu-rv/qemu_rv_rptun.h new file mode 100644 index 0000000000000..4689ab5ae00ce --- /dev/null +++ b/arch/risc-v/src/qemu-rv/qemu_rv_rptun.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * arch/risc-v/src/qemu-rv/qemu_rv_rptun.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_QEMU_RV_QEMU_RV_RPTUN_H +#define __ARCH_RISCV_SRC_QEMU_RV_QEMU_RV_RPTUN_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: qemu_rptun_init + * Description: initializes QEMU-RV RPTUN devices. + * Returns: OK on success, or negated number on error + ****************************************************************************/ + +int qemu_rptun_init(void); + +/**************************************************************************** + * Name: qemu_rptun_ipi + * Description: hook called from IRQ_SOFT handler. + ****************************************************************************/ + +void qemu_rptun_ipi(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_RISCV_SRC_QEMU_RV_QEMU_RV_RPTUN_H */