331 lines
9.2 KiB
C
331 lines
9.2 KiB
C
/*
|
|
* Remoteproc Virtio Framework Implementation
|
|
*
|
|
* Copyright(c) 2018 Xilinx Ltd.
|
|
* Copyright(c) 2011 Texas Instruments, Inc.
|
|
* Copyright(c) 2011 Google, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name Texas Instruments nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <openamp/remoteproc.h>
|
|
#include <openamp/remoteproc_virtio.h>
|
|
#include <openamp/virtqueue.h>
|
|
#include <metal/utilities.h>
|
|
#include <metal/alloc.h>
|
|
|
|
static void rproc_virtio_virtqueue_notify(struct virtqueue *vq)
|
|
{
|
|
struct remoteproc_virtio *rpvdev;
|
|
struct virtio_vring_info *vring_info;
|
|
struct virtio_device *vdev;
|
|
unsigned int vq_id = vq->vq_queue_index;
|
|
|
|
vdev = vq->vq_dev;
|
|
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
|
metal_assert(vq_id <= vdev->vrings_num);
|
|
vring_info = &vdev->vrings_info[vq_id];
|
|
rpvdev->notify(rpvdev->priv, vring_info->notifyid);
|
|
}
|
|
|
|
static unsigned char rproc_virtio_get_status(struct virtio_device *vdev)
|
|
{
|
|
struct remoteproc_virtio *rpvdev;
|
|
struct fw_rsc_vdev *vdev_rsc;
|
|
struct metal_io_region *io;
|
|
char status;
|
|
|
|
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
|
vdev_rsc = rpvdev->vdev_rsc;
|
|
io = rpvdev->vdev_rsc_io;
|
|
status = metal_io_read8(io,
|
|
metal_io_virt_to_offset(io, &vdev_rsc->status));
|
|
return status;
|
|
}
|
|
|
|
#ifndef VIRTIO_SLAVE_ONLY
|
|
static void rproc_virtio_set_status(struct virtio_device *vdev,
|
|
unsigned char status)
|
|
{
|
|
struct remoteproc_virtio *rpvdev;
|
|
struct fw_rsc_vdev *vdev_rsc;
|
|
struct metal_io_region *io;
|
|
|
|
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
|
vdev_rsc = rpvdev->vdev_rsc;
|
|
io = rpvdev->vdev_rsc_io;
|
|
metal_io_write8(io,
|
|
metal_io_virt_to_offset(io, &vdev_rsc->status),
|
|
status);
|
|
rpvdev->notify(rpvdev->priv, vdev->index);
|
|
}
|
|
#endif
|
|
|
|
static uint32_t rproc_virtio_get_features(struct virtio_device *vdev)
|
|
{
|
|
struct remoteproc_virtio *rpvdev;
|
|
struct fw_rsc_vdev *vdev_rsc;
|
|
struct metal_io_region *io;
|
|
uint32_t features;
|
|
|
|
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
|
vdev_rsc = rpvdev->vdev_rsc;
|
|
io = rpvdev->vdev_rsc_io;
|
|
/* TODO: shall we get features based on the role ? */
|
|
features = metal_io_read32(io,
|
|
metal_io_virt_to_offset(io, &vdev_rsc->dfeatures));
|
|
|
|
return features;
|
|
}
|
|
|
|
#ifndef VIRTIO_SLAVE_ONLY
|
|
static void rproc_virtio_set_features(struct virtio_device *vdev,
|
|
uint32_t features)
|
|
{
|
|
struct remoteproc_virtio *rpvdev;
|
|
struct fw_rsc_vdev *vdev_rsc;
|
|
struct metal_io_region *io;
|
|
|
|
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
|
vdev_rsc = rpvdev->vdev_rsc;
|
|
io = rpvdev->vdev_rsc_io;
|
|
/* TODO: shall we set features based on the role ? */
|
|
metal_io_write32(io,
|
|
metal_io_virt_to_offset(io, &vdev_rsc->dfeatures),
|
|
features);
|
|
rpvdev->notify(rpvdev->priv, vdev->index);
|
|
}
|
|
#endif
|
|
|
|
static uint32_t rproc_virtio_negotiate_features(struct virtio_device *vdev,
|
|
uint32_t features)
|
|
{
|
|
(void)vdev;
|
|
(void)features;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rproc_virtio_read_config(struct virtio_device *vdev,
|
|
uint32_t offset, void *dst, int length)
|
|
{
|
|
(void)vdev;
|
|
(void)offset;
|
|
(void)dst;
|
|
(void)length;
|
|
}
|
|
|
|
#ifndef VIRTIO_SLAVE_ONLY
|
|
static void rproc_virtio_write_config(struct virtio_device *vdev,
|
|
uint32_t offset, void *src, int length)
|
|
{
|
|
(void)vdev;
|
|
(void)offset;
|
|
(void)src;
|
|
(void)length;
|
|
}
|
|
|
|
static void rproc_virtio_reset_device(struct virtio_device *vdev)
|
|
{
|
|
if (vdev->role == VIRTIO_DEV_MASTER)
|
|
rproc_virtio_set_status(vdev,
|
|
VIRTIO_CONFIG_STATUS_NEEDS_RESET);
|
|
}
|
|
#endif
|
|
|
|
const struct virtio_dispatch remoteproc_virtio_dispatch_funcs = {
|
|
.get_status = rproc_virtio_get_status,
|
|
.get_features = rproc_virtio_get_features,
|
|
.read_config = rproc_virtio_read_config,
|
|
.notify = rproc_virtio_virtqueue_notify,
|
|
.negotiate_features = rproc_virtio_negotiate_features,
|
|
#ifndef VIRTIO_SLAVE_ONLY
|
|
/*
|
|
* We suppose here that the vdev is in a shared memory so that can
|
|
* be access only by one core: the master. In this case salve core has
|
|
* only read access right.
|
|
*/
|
|
.set_status = rproc_virtio_set_status,
|
|
.set_features = rproc_virtio_set_features,
|
|
.write_config = rproc_virtio_write_config,
|
|
.reset_device = rproc_virtio_reset_device,
|
|
#endif
|
|
};
|
|
|
|
struct virtio_device *
|
|
rproc_virtio_create_vdev(unsigned int role, unsigned int notifyid,
|
|
void *rsc, struct metal_io_region *rsc_io,
|
|
void *priv,
|
|
rpvdev_notify_func notify,
|
|
virtio_dev_reset_cb rst_cb)
|
|
{
|
|
struct remoteproc_virtio *rpvdev;
|
|
struct virtio_vring_info *vrings_info;
|
|
struct fw_rsc_vdev *vdev_rsc = rsc;
|
|
struct virtio_device *vdev;
|
|
unsigned int num_vrings = vdev_rsc->num_of_vrings;
|
|
unsigned int i;
|
|
|
|
rpvdev = metal_allocate_memory(sizeof(*rpvdev));
|
|
if (!rpvdev)
|
|
return NULL;
|
|
vrings_info = metal_allocate_memory(sizeof(*vrings_info) * num_vrings);
|
|
if (!vrings_info)
|
|
goto err0;
|
|
memset(rpvdev, 0, sizeof(*rpvdev));
|
|
memset(vrings_info, 0, sizeof(*vrings_info));
|
|
vdev = &rpvdev->vdev;
|
|
|
|
for (i = 0; i < num_vrings; i++) {
|
|
struct virtqueue *vq;
|
|
struct fw_rsc_vdev_vring *vring_rsc;
|
|
unsigned int num_extra_desc = 0;
|
|
|
|
vring_rsc = &vdev_rsc->vring[i];
|
|
if (role == VIRTIO_DEV_MASTER) {
|
|
num_extra_desc = vring_rsc->num;
|
|
}
|
|
vq = virtqueue_allocate(num_extra_desc);
|
|
if (!vq)
|
|
goto err1;
|
|
vrings_info[i].vq = vq;
|
|
}
|
|
|
|
/* FIXME commended as seems not nedded, already stored in vdev */
|
|
//rpvdev->notifyid = notifyid;
|
|
rpvdev->notify = notify;
|
|
rpvdev->priv = priv;
|
|
vdev->vrings_info = vrings_info;
|
|
/* Assuming the shared memory has been mapped and registered if
|
|
* necessary
|
|
*/
|
|
rpvdev->vdev_rsc = vdev_rsc;
|
|
rpvdev->vdev_rsc_io = rsc_io;
|
|
|
|
vdev->index = notifyid;
|
|
vdev->role = role;
|
|
vdev->reset_cb = rst_cb;
|
|
vdev->vrings_num = num_vrings;
|
|
vdev->func = &remoteproc_virtio_dispatch_funcs;
|
|
/* TODO: Shall we set features here ? */
|
|
|
|
return &rpvdev->vdev;
|
|
|
|
err1:
|
|
for (i = 0; i < num_vrings; i++) {
|
|
if (vrings_info[i].vq)
|
|
metal_free_memory(vrings_info[i].vq);
|
|
}
|
|
metal_free_memory(vrings_info);
|
|
err0:
|
|
metal_free_memory(rpvdev);
|
|
return NULL;
|
|
}
|
|
|
|
void rproc_virtio_remove_vdev(struct virtio_device *vdev)
|
|
{
|
|
struct remoteproc_virtio *rpvdev;
|
|
unsigned int i;
|
|
|
|
if (!vdev)
|
|
return;
|
|
rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev);
|
|
for (i = 0; i < vdev->vrings_num; i++) {
|
|
struct virtqueue *vq;
|
|
|
|
vq = vdev->vrings_info[i].vq;
|
|
if (vq)
|
|
metal_free_memory(vq);
|
|
}
|
|
metal_free_memory(vdev->vrings_info);
|
|
metal_free_memory(rpvdev);
|
|
}
|
|
|
|
int rproc_virtio_init_vring(struct virtio_device *vdev, unsigned int index,
|
|
unsigned int notifyid, void *va,
|
|
struct metal_io_region *io,
|
|
unsigned int num_descs, unsigned int align)
|
|
{
|
|
struct virtio_vring_info *vring_info;
|
|
unsigned int num_vrings;
|
|
|
|
num_vrings = vdev->vrings_num;
|
|
if (index >= num_vrings)
|
|
return -RPROC_EINVAL;
|
|
vring_info = &vdev->vrings_info[index];
|
|
vring_info->io = io;
|
|
vring_info->notifyid = notifyid;
|
|
vring_info->info.vaddr = va;
|
|
vring_info->info.num_descs = num_descs;
|
|
vring_info->info.align = align;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rproc_virtio_notified(struct virtio_device *vdev, uint32_t notifyid)
|
|
{
|
|
unsigned int num_vrings, i;
|
|
struct virtio_vring_info *vring_info;
|
|
struct virtqueue *vq;
|
|
|
|
if (!vdev)
|
|
return -EINVAL;
|
|
/* We do nothing for vdev notification in this implementation */
|
|
if (vdev->index == notifyid)
|
|
return 0;
|
|
num_vrings = vdev->vrings_num;
|
|
for (i = 0; i < num_vrings; i++) {
|
|
vring_info = &vdev->vrings_info[i];
|
|
if (vring_info->notifyid == notifyid ||
|
|
notifyid == RSC_NOTIFY_ID_ANY) {
|
|
vq = vring_info->vq;
|
|
virtqueue_notification(vq);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void rproc_virtio_wait_remote_ready(struct virtio_device *vdev)
|
|
{
|
|
uint8_t status;
|
|
|
|
/*
|
|
* No status available for slave. As Master has not to wait
|
|
* slave action, we can return. Behavior should be updated
|
|
* in future if a slave status is added.
|
|
*/
|
|
if (vdev->role == VIRTIO_DEV_MASTER)
|
|
return;
|
|
|
|
while (1) {
|
|
status = rproc_virtio_get_status(vdev);
|
|
if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
|
|
return;
|
|
}
|
|
}
|