/* * 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 #include #include #include #include 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; } }