162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Remote processor messaging transport (OMAP platform-specific bits) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2011 Google, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Ohad Ben-Cohen <ohad@wizery.com> 962306a36Sopenharmony_ci * Brian Swetland <swetland@google.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/dma-direct.h> 1362306a36Sopenharmony_ci#include <linux/dma-map-ops.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci#include <linux/export.h> 1662306a36Sopenharmony_ci#include <linux/of_reserved_mem.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/remoteproc.h> 1962306a36Sopenharmony_ci#include <linux/virtio.h> 2062306a36Sopenharmony_ci#include <linux/virtio_config.h> 2162306a36Sopenharmony_ci#include <linux/virtio_ids.h> 2262306a36Sopenharmony_ci#include <linux/virtio_ring.h> 2362306a36Sopenharmony_ci#include <linux/err.h> 2462306a36Sopenharmony_ci#include <linux/kref.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "remoteproc_internal.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int copy_dma_range_map(struct device *to, struct device *from) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci const struct bus_dma_region *map = from->dma_range_map, *new_map, *r; 3262306a36Sopenharmony_ci int num_ranges = 0; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (!map) 3562306a36Sopenharmony_ci return 0; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for (r = map; r->size; r++) 3862306a36Sopenharmony_ci num_ranges++; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)), 4162306a36Sopenharmony_ci GFP_KERNEL); 4262306a36Sopenharmony_ci if (!new_map) 4362306a36Sopenharmony_ci return -ENOMEM; 4462306a36Sopenharmony_ci to->dma_range_map = new_map; 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct platform_device *pdev; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci pdev = container_of(vdev->dev.parent, struct platform_device, dev); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return platform_get_drvdata(pdev); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct rproc *vdev_to_rproc(struct virtio_device *vdev) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return rvdev->rproc; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* kick the remote processor, and let it know which virtqueue to poke at */ 6562306a36Sopenharmony_cistatic bool rproc_virtio_notify(struct virtqueue *vq) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct rproc_vring *rvring = vq->priv; 6862306a36Sopenharmony_ci struct rproc *rproc = rvring->rvdev->rproc; 6962306a36Sopenharmony_ci int notifyid = rvring->notifyid; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci rproc->ops->kick(rproc, notifyid); 7462306a36Sopenharmony_ci return true; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/** 7862306a36Sopenharmony_ci * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted 7962306a36Sopenharmony_ci * @rproc: handle to the remote processor 8062306a36Sopenharmony_ci * @notifyid: index of the signalled virtqueue (unique per this @rproc) 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * This function should be called by the platform-specific rproc driver, 8362306a36Sopenharmony_ci * when the remote processor signals that a specific virtqueue has pending 8462306a36Sopenharmony_ci * messages available. 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * Return: IRQ_NONE if no message was found in the @notifyid virtqueue, 8762306a36Sopenharmony_ci * and otherwise returns IRQ_HANDLED. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ciirqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct rproc_vring *rvring; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci rvring = idr_find(&rproc->notifyids, notifyid); 9662306a36Sopenharmony_ci if (!rvring || !rvring->vq) 9762306a36Sopenharmony_ci return IRQ_NONE; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return vring_interrupt(0, rvring->vq); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_vq_interrupt); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic struct virtqueue *rp_find_vq(struct virtio_device *vdev, 10462306a36Sopenharmony_ci unsigned int id, 10562306a36Sopenharmony_ci void (*callback)(struct virtqueue *vq), 10662306a36Sopenharmony_ci const char *name, bool ctx) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 10962306a36Sopenharmony_ci struct rproc *rproc = vdev_to_rproc(vdev); 11062306a36Sopenharmony_ci struct device *dev = &rproc->dev; 11162306a36Sopenharmony_ci struct rproc_mem_entry *mem; 11262306a36Sopenharmony_ci struct rproc_vring *rvring; 11362306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 11462306a36Sopenharmony_ci struct virtqueue *vq; 11562306a36Sopenharmony_ci void *addr; 11662306a36Sopenharmony_ci int num, size; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* we're temporarily limited to two virtqueues per rvdev */ 11962306a36Sopenharmony_ci if (id >= ARRAY_SIZE(rvdev->vring)) 12062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!name) 12362306a36Sopenharmony_ci return NULL; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Search allocated memory region by name */ 12662306a36Sopenharmony_ci mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index, 12762306a36Sopenharmony_ci id); 12862306a36Sopenharmony_ci if (!mem || !mem->va) 12962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci rvring = &rvdev->vring[id]; 13262306a36Sopenharmony_ci addr = mem->va; 13362306a36Sopenharmony_ci num = rvring->num; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* zero vring */ 13662306a36Sopenharmony_ci size = vring_size(num, rvring->align); 13762306a36Sopenharmony_ci memset(addr, 0, size); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d\n", 14062306a36Sopenharmony_ci id, addr, num, rvring->notifyid); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * Create the new vq, and tell virtio we're not interested in 14462306a36Sopenharmony_ci * the 'weak' smp barriers, since we're talking with a real device. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci vq = vring_new_virtqueue(id, num, rvring->align, vdev, false, ctx, 14762306a36Sopenharmony_ci addr, rproc_virtio_notify, callback, name); 14862306a36Sopenharmony_ci if (!vq) { 14962306a36Sopenharmony_ci dev_err(dev, "vring_new_virtqueue %s failed\n", name); 15062306a36Sopenharmony_ci rproc_free_vring(rvring); 15162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci vq->num_max = num; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci rvring->vq = vq; 15762306a36Sopenharmony_ci vq->priv = rvring; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* Update vring in resource table */ 16062306a36Sopenharmony_ci rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; 16162306a36Sopenharmony_ci rsc->vring[id].da = mem->da; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return vq; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void __rproc_virtio_del_vqs(struct virtio_device *vdev) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct virtqueue *vq, *n; 16962306a36Sopenharmony_ci struct rproc_vring *rvring; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci list_for_each_entry_safe(vq, n, &vdev->vqs, list) { 17262306a36Sopenharmony_ci rvring = vq->priv; 17362306a36Sopenharmony_ci rvring->vq = NULL; 17462306a36Sopenharmony_ci vring_del_virtqueue(vq); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void rproc_virtio_del_vqs(struct virtio_device *vdev) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci __rproc_virtio_del_vqs(vdev); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, 18462306a36Sopenharmony_ci struct virtqueue *vqs[], 18562306a36Sopenharmony_ci vq_callback_t *callbacks[], 18662306a36Sopenharmony_ci const char * const names[], 18762306a36Sopenharmony_ci const bool * ctx, 18862306a36Sopenharmony_ci struct irq_affinity *desc) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int i, ret, queue_idx = 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci for (i = 0; i < nvqs; ++i) { 19362306a36Sopenharmony_ci if (!names[i]) { 19462306a36Sopenharmony_ci vqs[i] = NULL; 19562306a36Sopenharmony_ci continue; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci vqs[i] = rp_find_vq(vdev, queue_idx++, callbacks[i], names[i], 19962306a36Sopenharmony_ci ctx ? ctx[i] : false); 20062306a36Sopenharmony_ci if (IS_ERR(vqs[i])) { 20162306a36Sopenharmony_ci ret = PTR_ERR(vqs[i]); 20262306a36Sopenharmony_ci goto error; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cierror: 20962306a36Sopenharmony_ci __rproc_virtio_del_vqs(vdev); 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic u8 rproc_virtio_get_status(struct virtio_device *vdev) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 21662306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return rsc->status; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void rproc_virtio_set_status(struct virtio_device *vdev, u8 status) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 22662306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci rsc->status = status; 23162306a36Sopenharmony_ci dev_dbg(&vdev->dev, "status: %d\n", status); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic void rproc_virtio_reset(struct virtio_device *vdev) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 23762306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci rsc->status = 0; 24262306a36Sopenharmony_ci dev_dbg(&vdev->dev, "reset !\n"); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* provide the vdev features as retrieved from the firmware */ 24662306a36Sopenharmony_cistatic u64 rproc_virtio_get_features(struct virtio_device *vdev) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 24962306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return rsc->dfeatures; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void rproc_transport_features(struct virtio_device *vdev) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * Packed ring isn't enabled on remoteproc for now, 26062306a36Sopenharmony_ci * because remoteproc uses vring_new_virtqueue() which 26162306a36Sopenharmony_ci * creates virtio rings on preallocated memory. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic int rproc_virtio_finalize_features(struct virtio_device *vdev) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 26962306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Give virtio_ring a chance to accept features */ 27462306a36Sopenharmony_ci vring_transport_features(vdev); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Give virtio_rproc a chance to accept features. */ 27762306a36Sopenharmony_ci rproc_transport_features(vdev); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Make sure we don't have any features > 32 bits! */ 28062306a36Sopenharmony_ci BUG_ON((u32)vdev->features != vdev->features); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Remember the finalized features of our vdev, and provide it 28462306a36Sopenharmony_ci * to the remote processor once it is powered on. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci rsc->gfeatures = vdev->features; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset, 29262306a36Sopenharmony_ci void *buf, unsigned int len) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 29562306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 29662306a36Sopenharmony_ci void *cfg; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 29962306a36Sopenharmony_ci cfg = &rsc->vring[rsc->num_of_vrings]; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (offset + len > rsc->config_len || offset + len < len) { 30262306a36Sopenharmony_ci dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n"); 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci memcpy(buf, cfg + offset, len); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void rproc_virtio_set(struct virtio_device *vdev, unsigned int offset, 31062306a36Sopenharmony_ci const void *buf, unsigned int len) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 31362306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 31462306a36Sopenharmony_ci void *cfg; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 31762306a36Sopenharmony_ci cfg = &rsc->vring[rsc->num_of_vrings]; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (offset + len > rsc->config_len || offset + len < len) { 32062306a36Sopenharmony_ci dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n"); 32162306a36Sopenharmony_ci return; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci memcpy(cfg + offset, buf, len); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic const struct virtio_config_ops rproc_virtio_config_ops = { 32862306a36Sopenharmony_ci .get_features = rproc_virtio_get_features, 32962306a36Sopenharmony_ci .finalize_features = rproc_virtio_finalize_features, 33062306a36Sopenharmony_ci .find_vqs = rproc_virtio_find_vqs, 33162306a36Sopenharmony_ci .del_vqs = rproc_virtio_del_vqs, 33262306a36Sopenharmony_ci .reset = rproc_virtio_reset, 33362306a36Sopenharmony_ci .set_status = rproc_virtio_set_status, 33462306a36Sopenharmony_ci .get_status = rproc_virtio_get_status, 33562306a36Sopenharmony_ci .get = rproc_virtio_get, 33662306a36Sopenharmony_ci .set = rproc_virtio_set, 33762306a36Sopenharmony_ci}; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* 34062306a36Sopenharmony_ci * This function is called whenever vdev is released, and is responsible 34162306a36Sopenharmony_ci * to decrement the remote processor's refcount which was taken when vdev was 34262306a36Sopenharmony_ci * added. 34362306a36Sopenharmony_ci * 34462306a36Sopenharmony_ci * Never call this function directly; it will be called by the driver 34562306a36Sopenharmony_ci * core when needed. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_cistatic void rproc_virtio_dev_release(struct device *dev) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct virtio_device *vdev = dev_to_virtio(dev); 35062306a36Sopenharmony_ci struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci kfree(vdev); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci of_reserved_mem_device_release(&rvdev->pdev->dev); 35562306a36Sopenharmony_ci dma_release_coherent_memory(&rvdev->pdev->dev); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci put_device(&rvdev->pdev->dev); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/** 36162306a36Sopenharmony_ci * rproc_add_virtio_dev() - register an rproc-induced virtio device 36262306a36Sopenharmony_ci * @rvdev: the remote vdev 36362306a36Sopenharmony_ci * @id: the device type identification (used to match it with a driver). 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * This function registers a virtio device. This vdev's partent is 36662306a36Sopenharmony_ci * the rproc device. 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci * Return: 0 on success or an appropriate error value otherwise 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_cistatic int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct rproc *rproc = rvdev->rproc; 37362306a36Sopenharmony_ci struct device *dev = &rvdev->pdev->dev; 37462306a36Sopenharmony_ci struct virtio_device *vdev; 37562306a36Sopenharmony_ci struct rproc_mem_entry *mem; 37662306a36Sopenharmony_ci int ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (rproc->ops->kick == NULL) { 37962306a36Sopenharmony_ci ret = -EINVAL; 38062306a36Sopenharmony_ci dev_err(dev, ".kick method not defined for %s\n", rproc->name); 38162306a36Sopenharmony_ci goto out; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Try to find dedicated vdev buffer carveout */ 38562306a36Sopenharmony_ci mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index); 38662306a36Sopenharmony_ci if (mem) { 38762306a36Sopenharmony_ci phys_addr_t pa; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (mem->of_resm_idx != -1) { 39062306a36Sopenharmony_ci struct device_node *np = rproc->dev.parent->of_node; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Associate reserved memory to vdev device */ 39362306a36Sopenharmony_ci ret = of_reserved_mem_device_init_by_idx(dev, np, 39462306a36Sopenharmony_ci mem->of_resm_idx); 39562306a36Sopenharmony_ci if (ret) { 39662306a36Sopenharmony_ci dev_err(dev, "Can't associate reserved memory\n"); 39762306a36Sopenharmony_ci goto out; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci } else { 40062306a36Sopenharmony_ci if (mem->va) { 40162306a36Sopenharmony_ci dev_warn(dev, "vdev %d buffer already mapped\n", 40262306a36Sopenharmony_ci rvdev->index); 40362306a36Sopenharmony_ci pa = rproc_va_to_pa(mem->va); 40462306a36Sopenharmony_ci } else { 40562306a36Sopenharmony_ci /* Use dma address as carveout no memmapped yet */ 40662306a36Sopenharmony_ci pa = (phys_addr_t)mem->dma; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Associate vdev buffer memory pool to vdev subdev */ 41062306a36Sopenharmony_ci ret = dma_declare_coherent_memory(dev, pa, 41162306a36Sopenharmony_ci mem->da, 41262306a36Sopenharmony_ci mem->len); 41362306a36Sopenharmony_ci if (ret < 0) { 41462306a36Sopenharmony_ci dev_err(dev, "Failed to associate buffer\n"); 41562306a36Sopenharmony_ci goto out; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci struct device_node *np = rproc->dev.parent->of_node; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * If we don't have dedicated buffer, just attempt to re-assign 42362306a36Sopenharmony_ci * the reserved memory from our parent. A default memory-region 42462306a36Sopenharmony_ci * at index 0 from the parent's memory-regions is assigned for 42562306a36Sopenharmony_ci * the rvdev dev to allocate from. Failure is non-critical and 42662306a36Sopenharmony_ci * the allocations will fall back to global pools, so don't 42762306a36Sopenharmony_ci * check return value either. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci of_reserved_mem_device_init_by_idx(dev, np, 0); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Allocate virtio device */ 43362306a36Sopenharmony_ci vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); 43462306a36Sopenharmony_ci if (!vdev) { 43562306a36Sopenharmony_ci ret = -ENOMEM; 43662306a36Sopenharmony_ci goto out; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci vdev->id.device = id, 43962306a36Sopenharmony_ci vdev->config = &rproc_virtio_config_ops, 44062306a36Sopenharmony_ci vdev->dev.parent = dev; 44162306a36Sopenharmony_ci vdev->dev.release = rproc_virtio_dev_release; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Reference the vdev and vring allocations */ 44462306a36Sopenharmony_ci get_device(dev); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci ret = register_virtio_device(vdev); 44762306a36Sopenharmony_ci if (ret) { 44862306a36Sopenharmony_ci put_device(&vdev->dev); 44962306a36Sopenharmony_ci dev_err(dev, "failed to register vdev: %d\n", ret); 45062306a36Sopenharmony_ci goto out; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciout: 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/** 46062306a36Sopenharmony_ci * rproc_remove_virtio_dev() - remove an rproc-induced virtio device 46162306a36Sopenharmony_ci * @dev: the virtio device 46262306a36Sopenharmony_ci * @data: must be null 46362306a36Sopenharmony_ci * 46462306a36Sopenharmony_ci * This function unregisters an existing virtio device. 46562306a36Sopenharmony_ci * 46662306a36Sopenharmony_ci * Return: 0 46762306a36Sopenharmony_ci */ 46862306a36Sopenharmony_cistatic int rproc_remove_virtio_dev(struct device *dev, void *data) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct virtio_device *vdev = dev_to_virtio(dev); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci unregister_virtio_device(vdev); 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int rproc_vdev_do_start(struct rproc_subdev *subdev) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return rproc_add_virtio_dev(rvdev, rvdev->id); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); 48662306a36Sopenharmony_ci struct device *dev = &rvdev->pdev->dev; 48762306a36Sopenharmony_ci int ret; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ret = device_for_each_child(dev, NULL, rproc_remove_virtio_dev); 49062306a36Sopenharmony_ci if (ret) 49162306a36Sopenharmony_ci dev_warn(dev, "can't remove vdev child device: %d\n", ret); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int rproc_virtio_probe(struct platform_device *pdev) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 49762306a36Sopenharmony_ci struct rproc_vdev_data *rvdev_data = dev->platform_data; 49862306a36Sopenharmony_ci struct rproc_vdev *rvdev; 49962306a36Sopenharmony_ci struct rproc *rproc = container_of(dev->parent, struct rproc, dev); 50062306a36Sopenharmony_ci struct fw_rsc_vdev *rsc; 50162306a36Sopenharmony_ci int i, ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (!rvdev_data) 50462306a36Sopenharmony_ci return -EINVAL; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci rvdev = devm_kzalloc(dev, sizeof(*rvdev), GFP_KERNEL); 50762306a36Sopenharmony_ci if (!rvdev) 50862306a36Sopenharmony_ci return -ENOMEM; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci rvdev->id = rvdev_data->id; 51162306a36Sopenharmony_ci rvdev->rproc = rproc; 51262306a36Sopenharmony_ci rvdev->index = rvdev_data->index; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci ret = copy_dma_range_map(dev, rproc->dev.parent); 51562306a36Sopenharmony_ci if (ret) 51662306a36Sopenharmony_ci return ret; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* Make device dma capable by inheriting from parent's capabilities */ 51962306a36Sopenharmony_ci set_dma_ops(dev, get_dma_ops(rproc->dev.parent)); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci ret = dma_coerce_mask_and_coherent(dev, dma_get_mask(rproc->dev.parent)); 52262306a36Sopenharmony_ci if (ret) { 52362306a36Sopenharmony_ci dev_warn(dev, "Failed to set DMA mask %llx. Trying to continue... (%pe)\n", 52462306a36Sopenharmony_ci dma_get_mask(rproc->dev.parent), ERR_PTR(ret)); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci platform_set_drvdata(pdev, rvdev); 52862306a36Sopenharmony_ci rvdev->pdev = pdev; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci rsc = rvdev_data->rsc; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* parse the vrings */ 53362306a36Sopenharmony_ci for (i = 0; i < rsc->num_of_vrings; i++) { 53462306a36Sopenharmony_ci ret = rproc_parse_vring(rvdev, rsc, i); 53562306a36Sopenharmony_ci if (ret) 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* remember the resource offset*/ 54062306a36Sopenharmony_ci rvdev->rsc_offset = rvdev_data->rsc_offset; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* allocate the vring resources */ 54362306a36Sopenharmony_ci for (i = 0; i < rsc->num_of_vrings; i++) { 54462306a36Sopenharmony_ci ret = rproc_alloc_vring(rvdev, i); 54562306a36Sopenharmony_ci if (ret) 54662306a36Sopenharmony_ci goto unwind_vring_allocations; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci rproc_add_rvdev(rproc, rvdev); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci rvdev->subdev.start = rproc_vdev_do_start; 55262306a36Sopenharmony_ci rvdev->subdev.stop = rproc_vdev_do_stop; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci rproc_add_subdev(rproc, &rvdev->subdev); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* 55762306a36Sopenharmony_ci * We're indirectly making a non-temporary copy of the rproc pointer 55862306a36Sopenharmony_ci * here, because the platform device or the vdev device will indirectly 55962306a36Sopenharmony_ci * access the wrapping rproc. 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Therefore we must increment the rproc refcount here, and decrement 56262306a36Sopenharmony_ci * it _only_ on platform remove. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci get_device(&rproc->dev); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci return 0; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ciunwind_vring_allocations: 56962306a36Sopenharmony_ci for (i--; i >= 0; i--) 57062306a36Sopenharmony_ci rproc_free_vring(&rvdev->vring[i]); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return ret; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic void rproc_virtio_remove(struct platform_device *pdev) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct rproc_vdev *rvdev = dev_get_drvdata(&pdev->dev); 57862306a36Sopenharmony_ci struct rproc *rproc = rvdev->rproc; 57962306a36Sopenharmony_ci struct rproc_vring *rvring; 58062306a36Sopenharmony_ci int id; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) { 58362306a36Sopenharmony_ci rvring = &rvdev->vring[id]; 58462306a36Sopenharmony_ci rproc_free_vring(rvring); 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci rproc_remove_subdev(rproc, &rvdev->subdev); 58862306a36Sopenharmony_ci rproc_remove_rvdev(rvdev); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci put_device(&rproc->dev); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* Platform driver */ 59462306a36Sopenharmony_cistatic struct platform_driver rproc_virtio_driver = { 59562306a36Sopenharmony_ci .probe = rproc_virtio_probe, 59662306a36Sopenharmony_ci .remove_new = rproc_virtio_remove, 59762306a36Sopenharmony_ci .driver = { 59862306a36Sopenharmony_ci .name = "rproc-virtio", 59962306a36Sopenharmony_ci }, 60062306a36Sopenharmony_ci}; 60162306a36Sopenharmony_cibuiltin_platform_driver(rproc_virtio_driver); 602