203 lines
4.4 KiB
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;
|
|
}
|