riscv/qemu-rv: add RPTUN support

This adds initial RPTUN support for qemu-rv and rv-virt device.

Signed-off-by: Yanfeng Liu <yfliu2008@qq.com>
This commit is contained in:
Yanfeng Liu 2024-08-17 12:08:22 +08:00 committed by Xiang Xiao
parent 2af5ad174f
commit b9fde453ce
5 changed files with 467 additions and 1 deletions

View File

@ -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

View File

@ -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);

View File

@ -0,0 +1,360 @@
/****************************************************************************
* 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 <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <nuttx/nuttx.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/kthread.h>
#include <nuttx/semaphore.h>
#include <nuttx/spi/spi.h>
#include <nuttx/wqueue.h>
#include <nuttx/rptun/openamp.h>
#include <nuttx/rptun/rptun.h>
#include <nuttx/drivers/addrenv.h>
#include <nuttx/list.h>
#include <arch/barriers.h>
#include <arch/board/board.h>
#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;
DEBUGASSERT(NUM_RPTUN_PEERS == rp_init_ipi());
/* master is responsible for initializing shmem */
#ifdef CONFIG_QEMU_RPTUN_MASTER
ret = CONFIG_QEMU_RPTUN_SHM_SIZE * NUM_RPTUN_PEERS;
memset((void *)SHMEM, 0, ret);
rpinfo("cleared %d @%p\n", ret, 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("%s failed %d!\n", dev->peername, ret);
return ret;
}
rpinfo("%s ok\n", dev->peername);
}
return OK;
}

View File

@ -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 <nuttx/config.h>
#include <sys/types.h>
/****************************************************************************
* 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 */

View File

@ -38,6 +38,7 @@
#include "hardware/qemu_rv_memorymap.h"
#include "qemu_rv_memorymap.h"
#include "qemu_rv_rptun.h"
#include "riscv_internal.h"
#include "romfs.h"
@ -139,6 +140,10 @@ int board_app_initialize(uintptr_t arg)
#endif
#endif
#ifdef CONFIG_RPTUN
qemu_rptun_init();
#endif
return OK;
#endif
}