zephyr/subsys/ipc/ipc_service/lib/ipc_static_vrings.c

203 lines
4.4 KiB
C

/*
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ipc/ipc_static_vrings.h>
#include <zephyr/cache.h>
#define SHM_DEVICE_DEFAULT_NAME "sram0.shm"
#define RPMSG_VQ_0 (0) /* TX virtqueue queue index */
#define RPMSG_VQ_1 (1) /* RX virtqueue queue index */
static void ipc_virtio_notify(struct virtqueue *vq)
{
struct ipc_static_vrings *vr;
vr = CONTAINER_OF(vq->vq_dev, struct ipc_static_vrings, vdev);
if (vr->notify_cb) {
vr->notify_cb(vq, vr->priv);
}
}
static void ipc_virtio_set_features(struct virtio_device *vdev, uint32_t features)
{
/* No need for implementation */
}
static void ipc_virtio_set_status(struct virtio_device *p_vdev, unsigned char status)
{
struct ipc_static_vrings *vr;
if (p_vdev->role != VIRTIO_DEV_DRIVER) {
return;
}
vr = CONTAINER_OF(p_vdev, struct ipc_static_vrings, vdev);
sys_write8(status, vr->status_reg_addr);
sys_cache_data_flush_range((void *) vr->status_reg_addr, sizeof(status));
}
static uint32_t ipc_virtio_get_features(struct virtio_device *vdev)
{
return BIT(VIRTIO_RPMSG_F_NS);
}
static unsigned char ipc_virtio_get_status(struct virtio_device *p_vdev)
{
struct ipc_static_vrings *vr;
uint8_t ret;
vr = CONTAINER_OF(p_vdev, struct ipc_static_vrings, vdev);
ret = VIRTIO_CONFIG_STATUS_DRIVER_OK;
if (p_vdev->role == VIRTIO_DEV_DEVICE) {
sys_cache_data_invd_range((void *) vr->status_reg_addr, sizeof(ret));
ret = sys_read8(vr->status_reg_addr);
}
return ret;
}
const static struct virtio_dispatch dispatch = {
.get_status = ipc_virtio_get_status,
.get_features = ipc_virtio_get_features,
.set_status = ipc_virtio_set_status,
.set_features = ipc_virtio_set_features,
.notify = ipc_virtio_notify,
};
static int libmetal_setup(struct ipc_static_vrings *vr)
{
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
struct metal_device *device;
int err;
err = metal_init(&metal_params);
if (err != 0) {
return err;
}
err = metal_register_generic_device(&vr->shm_device);
if (err != 0) {
return err;
}
err = metal_device_open("generic", vr->shm_device.name, &device);
if (err != 0) {
return err;
}
vr->shm_io = metal_device_io_region(device, 0);
if (vr->shm_io == NULL) {
return err;
}
return 0;
}
static int libmetal_teardown(struct ipc_static_vrings *vr)
{
vr->shm_io = 0;
metal_device_close(&vr->shm_device);
metal_finish();
return 0;
}
static int vq_setup(struct ipc_static_vrings *vr, unsigned int role)
{
vr->vq[RPMSG_VQ_0] = virtqueue_allocate(vr->vring_size);
if (vr->vq[RPMSG_VQ_0] == NULL) {
return -ENOMEM;
}
vr->vq[RPMSG_VQ_1] = virtqueue_allocate(vr->vring_size);
if (vr->vq[RPMSG_VQ_1] == NULL) {
return -ENOMEM;
}
vr->rvrings[RPMSG_VQ_0].io = vr->shm_io;
vr->rvrings[RPMSG_VQ_0].info.vaddr = (void *) vr->tx_addr;
vr->rvrings[RPMSG_VQ_0].info.num_descs = vr->vring_size;
vr->rvrings[RPMSG_VQ_0].info.align = MEM_ALIGNMENT;
vr->rvrings[RPMSG_VQ_0].vq = vr->vq[RPMSG_VQ_0];
vr->rvrings[RPMSG_VQ_1].io = vr->shm_io;
vr->rvrings[RPMSG_VQ_1].info.vaddr = (void *) vr->rx_addr;
vr->rvrings[RPMSG_VQ_1].info.num_descs = vr->vring_size;
vr->rvrings[RPMSG_VQ_1].info.align = MEM_ALIGNMENT;
vr->rvrings[RPMSG_VQ_1].vq = vr->vq[RPMSG_VQ_1];
vr->vdev.role = role;
vr->vdev.vrings_num = VRING_COUNT;
vr->vdev.func = &dispatch;
vr->vdev.vrings_info = &vr->rvrings[0];
return 0;
}
static int vq_teardown(struct ipc_static_vrings *vr, unsigned int role)
{
memset(&vr->vdev, 0, sizeof(struct virtio_device));
memset(&(vr->rvrings[RPMSG_VQ_1]), 0, sizeof(struct virtio_vring_info));
memset(&(vr->rvrings[RPMSG_VQ_0]), 0, sizeof(struct virtio_vring_info));
virtqueue_free(vr->vq[RPMSG_VQ_1]);
virtqueue_free(vr->vq[RPMSG_VQ_0]);
return 0;
}
int ipc_static_vrings_init(struct ipc_static_vrings *vr, unsigned int role)
{
int err = 0;
if (!vr) {
return -EINVAL;
}
if (!vr->shm_device.name)
vr->shm_device.name = SHM_DEVICE_DEFAULT_NAME;
vr->shm_device.num_regions = 1;
vr->shm_physmap[0] = vr->shm_addr;
metal_io_init(vr->shm_device.regions, (void *) vr->shm_addr,
vr->shm_physmap, vr->shm_size, -1, 0, NULL);
err = libmetal_setup(vr);
if (err != 0) {
return err;
}
return vq_setup(vr, role);
}
int ipc_static_vrings_deinit(struct ipc_static_vrings *vr, unsigned int role)
{
int err;
err = vq_teardown(vr, role);
if (err != 0) {
return err;
}
err = libmetal_teardown(vr);
if (err != 0) {
return err;
}
metal_io_finish(vr->shm_device.regions);
return 0;
}