19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 204a9016eSOhad Ben-Cohen /* 304a9016eSOhad Ben-Cohen * Remote processor messaging transport (OMAP platform-specific bits) 404a9016eSOhad Ben-Cohen * 504a9016eSOhad Ben-Cohen * Copyright (C) 2011 Texas Instruments, Inc. 604a9016eSOhad Ben-Cohen * Copyright (C) 2011 Google, Inc. 704a9016eSOhad Ben-Cohen * 804a9016eSOhad Ben-Cohen * Ohad Ben-Cohen <ohad@wizery.com> 904a9016eSOhad Ben-Cohen * Brian Swetland <swetland@google.com> 1004a9016eSOhad Ben-Cohen */ 1104a9016eSOhad Ben-Cohen 129c31255cSArnaud Pouliquen #include <linux/dma-direct.h> 130a0f0d8bSChristoph Hellwig #include <linux/dma-map-ops.h> 149c31255cSArnaud Pouliquen #include <linux/dma-mapping.h> 1504a9016eSOhad Ben-Cohen #include <linux/export.h> 16086d0872SLoic Pallardy #include <linux/of_reserved_mem.h> 17ccf22a48SArnaud Pouliquen #include <linux/platform_device.h> 1804a9016eSOhad Ben-Cohen #include <linux/remoteproc.h> 1904a9016eSOhad Ben-Cohen #include <linux/virtio.h> 2004a9016eSOhad Ben-Cohen #include <linux/virtio_config.h> 2104a9016eSOhad Ben-Cohen #include <linux/virtio_ids.h> 2204a9016eSOhad Ben-Cohen #include <linux/virtio_ring.h> 2304a9016eSOhad Ben-Cohen #include <linux/err.h> 2404a9016eSOhad Ben-Cohen #include <linux/kref.h> 2504a9016eSOhad Ben-Cohen #include <linux/slab.h> 2604a9016eSOhad Ben-Cohen 2704a9016eSOhad Ben-Cohen #include "remoteproc_internal.h" 2804a9016eSOhad Ben-Cohen 299c31255cSArnaud Pouliquen static int copy_dma_range_map(struct device *to, struct device *from) 309c31255cSArnaud Pouliquen { 319c31255cSArnaud Pouliquen const struct bus_dma_region *map = from->dma_range_map, *new_map, *r; 329c31255cSArnaud Pouliquen int num_ranges = 0; 339c31255cSArnaud Pouliquen 349c31255cSArnaud Pouliquen if (!map) 359c31255cSArnaud Pouliquen return 0; 369c31255cSArnaud Pouliquen 379c31255cSArnaud Pouliquen for (r = map; r->size; r++) 389c31255cSArnaud Pouliquen num_ranges++; 399c31255cSArnaud Pouliquen 409c31255cSArnaud Pouliquen new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)), 419c31255cSArnaud Pouliquen GFP_KERNEL); 429c31255cSArnaud Pouliquen if (!new_map) 439c31255cSArnaud Pouliquen return -ENOMEM; 449c31255cSArnaud Pouliquen to->dma_range_map = new_map; 459c31255cSArnaud Pouliquen return 0; 469c31255cSArnaud Pouliquen } 479c31255cSArnaud Pouliquen 4899555489SArnaud Pouliquen static struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) 4999555489SArnaud Pouliquen { 501d7b61c0SArnaud Pouliquen struct platform_device *pdev; 511d7b61c0SArnaud Pouliquen 521d7b61c0SArnaud Pouliquen pdev = container_of(vdev->dev.parent, struct platform_device, dev); 531d7b61c0SArnaud Pouliquen 541d7b61c0SArnaud Pouliquen return platform_get_drvdata(pdev); 5599555489SArnaud Pouliquen } 5699555489SArnaud Pouliquen 5799555489SArnaud Pouliquen static struct rproc *vdev_to_rproc(struct virtio_device *vdev) 5899555489SArnaud Pouliquen { 5999555489SArnaud Pouliquen struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 6099555489SArnaud Pouliquen 6199555489SArnaud Pouliquen return rvdev->rproc; 6299555489SArnaud Pouliquen } 6399555489SArnaud Pouliquen 6404a9016eSOhad Ben-Cohen /* kick the remote processor, and let it know which virtqueue to poke at */ 6546f9c2b9SHeinz Graalfs static bool rproc_virtio_notify(struct virtqueue *vq) 6604a9016eSOhad Ben-Cohen { 677a186941SOhad Ben-Cohen struct rproc_vring *rvring = vq->priv; 687a186941SOhad Ben-Cohen struct rproc *rproc = rvring->rvdev->rproc; 697a186941SOhad Ben-Cohen int notifyid = rvring->notifyid; 7004a9016eSOhad Ben-Cohen 71b5ab5e24SOhad Ben-Cohen dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid); 7204a9016eSOhad Ben-Cohen 737a186941SOhad Ben-Cohen rproc->ops->kick(rproc, notifyid); 7446f9c2b9SHeinz Graalfs return true; 7504a9016eSOhad Ben-Cohen } 7604a9016eSOhad Ben-Cohen 7704a9016eSOhad Ben-Cohen /** 7804a9016eSOhad Ben-Cohen * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted 7904a9016eSOhad Ben-Cohen * @rproc: handle to the remote processor 807a186941SOhad Ben-Cohen * @notifyid: index of the signalled virtqueue (unique per this @rproc) 8104a9016eSOhad Ben-Cohen * 8204a9016eSOhad Ben-Cohen * This function should be called by the platform-specific rproc driver, 8304a9016eSOhad Ben-Cohen * when the remote processor signals that a specific virtqueue has pending 8404a9016eSOhad Ben-Cohen * messages available. 8504a9016eSOhad Ben-Cohen * 86f2867434SSuman Anna * Return: IRQ_NONE if no message was found in the @notifyid virtqueue, 8704a9016eSOhad Ben-Cohen * and otherwise returns IRQ_HANDLED. 8804a9016eSOhad Ben-Cohen */ 897a186941SOhad Ben-Cohen irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid) 9004a9016eSOhad Ben-Cohen { 917a186941SOhad Ben-Cohen struct rproc_vring *rvring; 927a186941SOhad Ben-Cohen 93b5ab5e24SOhad Ben-Cohen dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid); 947a186941SOhad Ben-Cohen 957a186941SOhad Ben-Cohen rvring = idr_find(&rproc->notifyids, notifyid); 967a186941SOhad Ben-Cohen if (!rvring || !rvring->vq) 977a186941SOhad Ben-Cohen return IRQ_NONE; 987a186941SOhad Ben-Cohen 997a186941SOhad Ben-Cohen return vring_interrupt(0, rvring->vq); 10004a9016eSOhad Ben-Cohen } 10104a9016eSOhad Ben-Cohen EXPORT_SYMBOL(rproc_vq_interrupt); 10204a9016eSOhad Ben-Cohen 10304a9016eSOhad Ben-Cohen static struct virtqueue *rp_find_vq(struct virtio_device *vdev, 104f145928dSAnna, Suman unsigned int id, 10504a9016eSOhad Ben-Cohen void (*callback)(struct virtqueue *vq), 106f94682ddSMichael S. Tsirkin const char *name, bool ctx) 10704a9016eSOhad Ben-Cohen { 1087a186941SOhad Ben-Cohen struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 10904a9016eSOhad Ben-Cohen struct rproc *rproc = vdev_to_rproc(vdev); 110b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 111c6aed238SLoic Pallardy struct rproc_mem_entry *mem; 1127a186941SOhad Ben-Cohen struct rproc_vring *rvring; 113c6aed238SLoic Pallardy struct fw_rsc_vdev *rsc; 11404a9016eSOhad Ben-Cohen struct virtqueue *vq; 11504a9016eSOhad Ben-Cohen void *addr; 116c2a052a4SXuan Zhuo int num, size; 11704a9016eSOhad Ben-Cohen 1187a186941SOhad Ben-Cohen /* we're temporarily limited to two virtqueues per rvdev */ 1197a186941SOhad Ben-Cohen if (id >= ARRAY_SIZE(rvdev->vring)) 1207a186941SOhad Ben-Cohen return ERR_PTR(-EINVAL); 12104a9016eSOhad Ben-Cohen 1226457f126SMichael S. Tsirkin if (!name) 1236457f126SMichael S. Tsirkin return NULL; 1246457f126SMichael S. Tsirkin 125c6aed238SLoic Pallardy /* Search allocated memory region by name */ 126c6aed238SLoic Pallardy mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index, 127c6aed238SLoic Pallardy id); 128c6aed238SLoic Pallardy if (!mem || !mem->va) 129c6aed238SLoic Pallardy return ERR_PTR(-ENOMEM); 130c6aed238SLoic Pallardy 1316db20ea8SOhad Ben-Cohen rvring = &rvdev->vring[id]; 132c6aed238SLoic Pallardy addr = mem->va; 133c2a052a4SXuan Zhuo num = rvring->num; 13404a9016eSOhad Ben-Cohen 1357a186941SOhad Ben-Cohen /* zero vring */ 136c2a052a4SXuan Zhuo size = vring_size(num, rvring->align); 1377a186941SOhad Ben-Cohen memset(addr, 0, size); 1387a186941SOhad Ben-Cohen 139276ec993SLoic Pallardy dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d\n", 140c2a052a4SXuan Zhuo id, addr, num, rvring->notifyid); 14104a9016eSOhad Ben-Cohen 14204a9016eSOhad Ben-Cohen /* 14304a9016eSOhad Ben-Cohen * Create the new vq, and tell virtio we're not interested in 14404a9016eSOhad Ben-Cohen * the 'weak' smp barriers, since we're talking with a real device. 14504a9016eSOhad Ben-Cohen */ 146c2a052a4SXuan Zhuo vq = vring_new_virtqueue(id, num, rvring->align, vdev, false, ctx, 147f94682ddSMichael S. Tsirkin addr, rproc_virtio_notify, callback, name); 14804a9016eSOhad Ben-Cohen if (!vq) { 149b5ab5e24SOhad Ben-Cohen dev_err(dev, "vring_new_virtqueue %s failed\n", name); 1506db20ea8SOhad Ben-Cohen rproc_free_vring(rvring); 1517a186941SOhad Ben-Cohen return ERR_PTR(-ENOMEM); 15204a9016eSOhad Ben-Cohen } 15304a9016eSOhad Ben-Cohen 154da802961SXuan Zhuo vq->num_max = num; 155da802961SXuan Zhuo 1567a186941SOhad Ben-Cohen rvring->vq = vq; 1577a186941SOhad Ben-Cohen vq->priv = rvring; 15804a9016eSOhad Ben-Cohen 159c6aed238SLoic Pallardy /* Update vring in resource table */ 160c6aed238SLoic Pallardy rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; 161c6aed238SLoic Pallardy rsc->vring[id].da = mem->da; 162c6aed238SLoic Pallardy 16304a9016eSOhad Ben-Cohen return vq; 16404a9016eSOhad Ben-Cohen } 16504a9016eSOhad Ben-Cohen 166dab55bbaSOhad Ben-Cohen static void __rproc_virtio_del_vqs(struct virtio_device *vdev) 16704a9016eSOhad Ben-Cohen { 16804a9016eSOhad Ben-Cohen struct virtqueue *vq, *n; 1697a186941SOhad Ben-Cohen struct rproc_vring *rvring; 17004a9016eSOhad Ben-Cohen 17104a9016eSOhad Ben-Cohen list_for_each_entry_safe(vq, n, &vdev->vqs, list) { 1727a186941SOhad Ben-Cohen rvring = vq->priv; 1737a186941SOhad Ben-Cohen rvring->vq = NULL; 17404a9016eSOhad Ben-Cohen vring_del_virtqueue(vq); 17504a9016eSOhad Ben-Cohen } 17604a9016eSOhad Ben-Cohen } 17704a9016eSOhad Ben-Cohen 178dab55bbaSOhad Ben-Cohen static void rproc_virtio_del_vqs(struct virtio_device *vdev) 179dab55bbaSOhad Ben-Cohen { 180dab55bbaSOhad Ben-Cohen __rproc_virtio_del_vqs(vdev); 181dab55bbaSOhad Ben-Cohen } 182dab55bbaSOhad Ben-Cohen 183f145928dSAnna, Suman static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, 18404a9016eSOhad Ben-Cohen struct virtqueue *vqs[], 18504a9016eSOhad Ben-Cohen vq_callback_t *callbacks[], 186fb5e31d9SChristoph Hellwig const char * const names[], 187f94682ddSMichael S. Tsirkin const bool * ctx, 188fb5e31d9SChristoph Hellwig struct irq_affinity *desc) 18904a9016eSOhad Ben-Cohen { 190a229989dSWei Wang int i, ret, queue_idx = 0; 19104a9016eSOhad Ben-Cohen 19204a9016eSOhad Ben-Cohen for (i = 0; i < nvqs; ++i) { 193a229989dSWei Wang if (!names[i]) { 194a229989dSWei Wang vqs[i] = NULL; 195a229989dSWei Wang continue; 196a229989dSWei Wang } 197a229989dSWei Wang 198a229989dSWei Wang vqs[i] = rp_find_vq(vdev, queue_idx++, callbacks[i], names[i], 199f94682ddSMichael S. Tsirkin ctx ? ctx[i] : false); 20004a9016eSOhad Ben-Cohen if (IS_ERR(vqs[i])) { 20104a9016eSOhad Ben-Cohen ret = PTR_ERR(vqs[i]); 20204a9016eSOhad Ben-Cohen goto error; 20304a9016eSOhad Ben-Cohen } 20404a9016eSOhad Ben-Cohen } 20504a9016eSOhad Ben-Cohen 20604a9016eSOhad Ben-Cohen return 0; 20704a9016eSOhad Ben-Cohen 20804a9016eSOhad Ben-Cohen error: 209dab55bbaSOhad Ben-Cohen __rproc_virtio_del_vqs(vdev); 21004a9016eSOhad Ben-Cohen return ret; 21104a9016eSOhad Ben-Cohen } 21204a9016eSOhad Ben-Cohen 21304a9016eSOhad Ben-Cohen static u8 rproc_virtio_get_status(struct virtio_device *vdev) 21404a9016eSOhad Ben-Cohen { 21592b38f85SSjur Brændeland struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 21692b38f85SSjur Brændeland struct fw_rsc_vdev *rsc; 21792b38f85SSjur Brændeland 21892b38f85SSjur Brændeland rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 21992b38f85SSjur Brændeland 22092b38f85SSjur Brændeland return rsc->status; 22104a9016eSOhad Ben-Cohen } 22204a9016eSOhad Ben-Cohen 22304a9016eSOhad Ben-Cohen static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status) 22404a9016eSOhad Ben-Cohen { 22592b38f85SSjur Brændeland struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 22692b38f85SSjur Brændeland struct fw_rsc_vdev *rsc; 22792b38f85SSjur Brændeland 22892b38f85SSjur Brændeland rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 22992b38f85SSjur Brændeland 23092b38f85SSjur Brændeland rsc->status = status; 2317a186941SOhad Ben-Cohen dev_dbg(&vdev->dev, "status: %d\n", status); 23204a9016eSOhad Ben-Cohen } 23304a9016eSOhad Ben-Cohen 23404a9016eSOhad Ben-Cohen static void rproc_virtio_reset(struct virtio_device *vdev) 23504a9016eSOhad Ben-Cohen { 23692b38f85SSjur Brændeland struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 23792b38f85SSjur Brændeland struct fw_rsc_vdev *rsc; 23892b38f85SSjur Brændeland 23992b38f85SSjur Brændeland rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 24092b38f85SSjur Brændeland 24192b38f85SSjur Brændeland rsc->status = 0; 24204a9016eSOhad Ben-Cohen dev_dbg(&vdev->dev, "reset !\n"); 24304a9016eSOhad Ben-Cohen } 24404a9016eSOhad Ben-Cohen 24504a9016eSOhad Ben-Cohen /* provide the vdev features as retrieved from the firmware */ 246d0254773SMichael S. Tsirkin static u64 rproc_virtio_get_features(struct virtio_device *vdev) 24704a9016eSOhad Ben-Cohen { 2487a186941SOhad Ben-Cohen struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 24992b38f85SSjur Brændeland struct fw_rsc_vdev *rsc; 25004a9016eSOhad Ben-Cohen 25192b38f85SSjur Brændeland rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 25292b38f85SSjur Brændeland 25392b38f85SSjur Brændeland return rsc->dfeatures; 25404a9016eSOhad Ben-Cohen } 25504a9016eSOhad Ben-Cohen 2563a814fdfSTiwei Bie static void rproc_transport_features(struct virtio_device *vdev) 2573a814fdfSTiwei Bie { 2583a814fdfSTiwei Bie /* 2593a814fdfSTiwei Bie * Packed ring isn't enabled on remoteproc for now, 2603a814fdfSTiwei Bie * because remoteproc uses vring_new_virtqueue() which 2613a814fdfSTiwei Bie * creates virtio rings on preallocated memory. 2623a814fdfSTiwei Bie */ 2633a814fdfSTiwei Bie __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED); 2643a814fdfSTiwei Bie } 2653a814fdfSTiwei Bie 2665c609a5eSMichael S. Tsirkin static int rproc_virtio_finalize_features(struct virtio_device *vdev) 26704a9016eSOhad Ben-Cohen { 2687a186941SOhad Ben-Cohen struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 26992b38f85SSjur Brændeland struct fw_rsc_vdev *rsc; 27092b38f85SSjur Brændeland 27192b38f85SSjur Brændeland rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 27204a9016eSOhad Ben-Cohen 27304a9016eSOhad Ben-Cohen /* Give virtio_ring a chance to accept features */ 27404a9016eSOhad Ben-Cohen vring_transport_features(vdev); 27504a9016eSOhad Ben-Cohen 2763a814fdfSTiwei Bie /* Give virtio_rproc a chance to accept features. */ 2773a814fdfSTiwei Bie rproc_transport_features(vdev); 2783a814fdfSTiwei Bie 27993d389f8SMichael S. Tsirkin /* Make sure we don't have any features > 32 bits! */ 28093d389f8SMichael S. Tsirkin BUG_ON((u32)vdev->features != vdev->features); 28193d389f8SMichael S. Tsirkin 28204a9016eSOhad Ben-Cohen /* 28304a9016eSOhad Ben-Cohen * Remember the finalized features of our vdev, and provide it 28404a9016eSOhad Ben-Cohen * to the remote processor once it is powered on. 28504a9016eSOhad Ben-Cohen */ 286e16e12beSMichael S. Tsirkin rsc->gfeatures = vdev->features; 2875c609a5eSMichael S. Tsirkin 2885c609a5eSMichael S. Tsirkin return 0; 28992b38f85SSjur Brændeland } 29092b38f85SSjur Brændeland 291f145928dSAnna, Suman static void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset, 292f145928dSAnna, Suman void *buf, unsigned int len) 29392b38f85SSjur Brændeland { 29492b38f85SSjur Brændeland struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 29592b38f85SSjur Brændeland struct fw_rsc_vdev *rsc; 29692b38f85SSjur Brændeland void *cfg; 29792b38f85SSjur Brændeland 29892b38f85SSjur Brændeland rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 29992b38f85SSjur Brændeland cfg = &rsc->vring[rsc->num_of_vrings]; 30092b38f85SSjur Brændeland 30192b38f85SSjur Brændeland if (offset + len > rsc->config_len || offset + len < len) { 30292b38f85SSjur Brændeland dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n"); 30392b38f85SSjur Brændeland return; 30492b38f85SSjur Brændeland } 30592b38f85SSjur Brændeland 30692b38f85SSjur Brændeland memcpy(buf, cfg + offset, len); 30792b38f85SSjur Brændeland } 30892b38f85SSjur Brændeland 309f145928dSAnna, Suman static void rproc_virtio_set(struct virtio_device *vdev, unsigned int offset, 310f145928dSAnna, Suman const void *buf, unsigned int len) 31192b38f85SSjur Brændeland { 31292b38f85SSjur Brændeland struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 31392b38f85SSjur Brændeland struct fw_rsc_vdev *rsc; 31492b38f85SSjur Brændeland void *cfg; 31592b38f85SSjur Brændeland 31692b38f85SSjur Brændeland rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; 31792b38f85SSjur Brændeland cfg = &rsc->vring[rsc->num_of_vrings]; 31892b38f85SSjur Brændeland 31992b38f85SSjur Brændeland if (offset + len > rsc->config_len || offset + len < len) { 32092b38f85SSjur Brændeland dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n"); 32192b38f85SSjur Brændeland return; 32292b38f85SSjur Brændeland } 32392b38f85SSjur Brændeland 32492b38f85SSjur Brændeland memcpy(cfg + offset, buf, len); 32504a9016eSOhad Ben-Cohen } 32604a9016eSOhad Ben-Cohen 32793503932SStephen Hemminger static const struct virtio_config_ops rproc_virtio_config_ops = { 32804a9016eSOhad Ben-Cohen .get_features = rproc_virtio_get_features, 32904a9016eSOhad Ben-Cohen .finalize_features = rproc_virtio_finalize_features, 33004a9016eSOhad Ben-Cohen .find_vqs = rproc_virtio_find_vqs, 33104a9016eSOhad Ben-Cohen .del_vqs = rproc_virtio_del_vqs, 33204a9016eSOhad Ben-Cohen .reset = rproc_virtio_reset, 33304a9016eSOhad Ben-Cohen .set_status = rproc_virtio_set_status, 33404a9016eSOhad Ben-Cohen .get_status = rproc_virtio_get_status, 33592b38f85SSjur Brændeland .get = rproc_virtio_get, 33692b38f85SSjur Brændeland .set = rproc_virtio_set, 33704a9016eSOhad Ben-Cohen }; 33804a9016eSOhad Ben-Cohen 33904a9016eSOhad Ben-Cohen /* 34004a9016eSOhad Ben-Cohen * This function is called whenever vdev is released, and is responsible 3417183a2a7SOhad Ben-Cohen * to decrement the remote processor's refcount which was taken when vdev was 34204a9016eSOhad Ben-Cohen * added. 34304a9016eSOhad Ben-Cohen * 34404a9016eSOhad Ben-Cohen * Never call this function directly; it will be called by the driver 34504a9016eSOhad Ben-Cohen * core when needed. 34604a9016eSOhad Ben-Cohen */ 347aab8d802SBjorn Andersson static void rproc_virtio_dev_release(struct device *dev) 34804a9016eSOhad Ben-Cohen { 34904a9016eSOhad Ben-Cohen struct virtio_device *vdev = dev_to_virtio(dev); 3506db20ea8SOhad Ben-Cohen struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); 35104a9016eSOhad Ben-Cohen 352d4c036feSLoic Pallardy kfree(vdev); 353d4c036feSLoic Pallardy 354*b327c727SJoakim Zhang of_reserved_mem_device_release(&rvdev->pdev->dev); 355*b327c727SJoakim Zhang dma_release_coherent_memory(&rvdev->pdev->dev); 356*b327c727SJoakim Zhang 3571d7b61c0SArnaud Pouliquen put_device(&rvdev->pdev->dev); 35804a9016eSOhad Ben-Cohen } 35904a9016eSOhad Ben-Cohen 36004a9016eSOhad Ben-Cohen /** 3617a186941SOhad Ben-Cohen * rproc_add_virtio_dev() - register an rproc-induced virtio device 3627a186941SOhad Ben-Cohen * @rvdev: the remote vdev 3632e7d4c2cSArnaud Pouliquen * @id: the device type identification (used to match it with a driver). 36404a9016eSOhad Ben-Cohen * 3657a186941SOhad Ben-Cohen * This function registers a virtio device. This vdev's partent is 3667a186941SOhad Ben-Cohen * the rproc device. 36704a9016eSOhad Ben-Cohen * 368f2867434SSuman Anna * Return: 0 on success or an appropriate error value otherwise 36904a9016eSOhad Ben-Cohen */ 3709c31255cSArnaud Pouliquen static int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) 37104a9016eSOhad Ben-Cohen { 3727a186941SOhad Ben-Cohen struct rproc *rproc = rvdev->rproc; 3731d7b61c0SArnaud Pouliquen struct device *dev = &rvdev->pdev->dev; 374d4c036feSLoic Pallardy struct virtio_device *vdev; 375086d0872SLoic Pallardy struct rproc_mem_entry *mem; 37604a9016eSOhad Ben-Cohen int ret; 37704a9016eSOhad Ben-Cohen 378791c13b7SNikita Shubin if (rproc->ops->kick == NULL) { 379791c13b7SNikita Shubin ret = -EINVAL; 3802fb75ceaSChristophe JAILLET dev_err(dev, ".kick method not defined for %s\n", rproc->name); 381791c13b7SNikita Shubin goto out; 382791c13b7SNikita Shubin } 383791c13b7SNikita Shubin 384086d0872SLoic Pallardy /* Try to find dedicated vdev buffer carveout */ 385086d0872SLoic Pallardy mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index); 386086d0872SLoic Pallardy if (mem) { 387086d0872SLoic Pallardy phys_addr_t pa; 388086d0872SLoic Pallardy 389086d0872SLoic Pallardy if (mem->of_resm_idx != -1) { 390086d0872SLoic Pallardy struct device_node *np = rproc->dev.parent->of_node; 391086d0872SLoic Pallardy 392086d0872SLoic Pallardy /* Associate reserved memory to vdev device */ 393086d0872SLoic Pallardy ret = of_reserved_mem_device_init_by_idx(dev, np, 394086d0872SLoic Pallardy mem->of_resm_idx); 395086d0872SLoic Pallardy if (ret) { 396086d0872SLoic Pallardy dev_err(dev, "Can't associate reserved memory\n"); 397086d0872SLoic Pallardy goto out; 398086d0872SLoic Pallardy } 399086d0872SLoic Pallardy } else { 400086d0872SLoic Pallardy if (mem->va) { 401086d0872SLoic Pallardy dev_warn(dev, "vdev %d buffer already mapped\n", 402086d0872SLoic Pallardy rvdev->index); 403086d0872SLoic Pallardy pa = rproc_va_to_pa(mem->va); 404086d0872SLoic Pallardy } else { 405086d0872SLoic Pallardy /* Use dma address as carveout no memmapped yet */ 406086d0872SLoic Pallardy pa = (phys_addr_t)mem->dma; 407086d0872SLoic Pallardy } 408086d0872SLoic Pallardy 409086d0872SLoic Pallardy /* Associate vdev buffer memory pool to vdev subdev */ 410086d0872SLoic Pallardy ret = dma_declare_coherent_memory(dev, pa, 411086d0872SLoic Pallardy mem->da, 412d664ce75SStephen Rothwell mem->len); 413086d0872SLoic Pallardy if (ret < 0) { 414086d0872SLoic Pallardy dev_err(dev, "Failed to associate buffer\n"); 415086d0872SLoic Pallardy goto out; 416086d0872SLoic Pallardy } 417086d0872SLoic Pallardy } 418db9178a4STero Kristo } else { 419db9178a4STero Kristo struct device_node *np = rproc->dev.parent->of_node; 420db9178a4STero Kristo 421db9178a4STero Kristo /* 422db9178a4STero Kristo * If we don't have dedicated buffer, just attempt to re-assign 423db9178a4STero Kristo * the reserved memory from our parent. A default memory-region 424db9178a4STero Kristo * at index 0 from the parent's memory-regions is assigned for 425db9178a4STero Kristo * the rvdev dev to allocate from. Failure is non-critical and 426db9178a4STero Kristo * the allocations will fall back to global pools, so don't 427db9178a4STero Kristo * check return value either. 428db9178a4STero Kristo */ 429db9178a4STero Kristo of_reserved_mem_device_init_by_idx(dev, np, 0); 430086d0872SLoic Pallardy } 431086d0872SLoic Pallardy 432d4c036feSLoic Pallardy /* Allocate virtio device */ 433d4c036feSLoic Pallardy vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); 434d4c036feSLoic Pallardy if (!vdev) { 435d4c036feSLoic Pallardy ret = -ENOMEM; 436d4c036feSLoic Pallardy goto out; 437d4c036feSLoic Pallardy } 4387a186941SOhad Ben-Cohen vdev->id.device = id, 4397a186941SOhad Ben-Cohen vdev->config = &rproc_virtio_config_ops, 4407a186941SOhad Ben-Cohen vdev->dev.parent = dev; 441aab8d802SBjorn Andersson vdev->dev.release = rproc_virtio_dev_release; 44204a9016eSOhad Ben-Cohen 443aab8d802SBjorn Andersson /* Reference the vdev and vring allocations */ 4441d7b61c0SArnaud Pouliquen get_device(dev); 445aab8d802SBjorn Andersson 4467a186941SOhad Ben-Cohen ret = register_virtio_device(vdev); 44704a9016eSOhad Ben-Cohen if (ret) { 448900a163eSweiping zhang put_device(&vdev->dev); 44904a9016eSOhad Ben-Cohen dev_err(dev, "failed to register vdev: %d\n", ret); 4507a186941SOhad Ben-Cohen goto out; 45104a9016eSOhad Ben-Cohen } 45204a9016eSOhad Ben-Cohen 4537a186941SOhad Ben-Cohen dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id); 4547a186941SOhad Ben-Cohen 4557a186941SOhad Ben-Cohen out: 45604a9016eSOhad Ben-Cohen return ret; 45704a9016eSOhad Ben-Cohen } 45804a9016eSOhad Ben-Cohen 45904a9016eSOhad Ben-Cohen /** 4607a186941SOhad Ben-Cohen * rproc_remove_virtio_dev() - remove an rproc-induced virtio device 461d4c036feSLoic Pallardy * @dev: the virtio device 462d4c036feSLoic Pallardy * @data: must be null 46304a9016eSOhad Ben-Cohen * 4647a186941SOhad Ben-Cohen * This function unregisters an existing virtio device. 465f2867434SSuman Anna * 466f2867434SSuman Anna * Return: 0 46704a9016eSOhad Ben-Cohen */ 4689c31255cSArnaud Pouliquen static int rproc_remove_virtio_dev(struct device *dev, void *data) 46904a9016eSOhad Ben-Cohen { 470d4c036feSLoic Pallardy struct virtio_device *vdev = dev_to_virtio(dev); 471d4c036feSLoic Pallardy 472d4c036feSLoic Pallardy unregister_virtio_device(vdev); 473d4c036feSLoic Pallardy return 0; 47404a9016eSOhad Ben-Cohen } 4759c31255cSArnaud Pouliquen 4769c31255cSArnaud Pouliquen static int rproc_vdev_do_start(struct rproc_subdev *subdev) 4779c31255cSArnaud Pouliquen { 4789c31255cSArnaud Pouliquen struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); 4799c31255cSArnaud Pouliquen 4809c31255cSArnaud Pouliquen return rproc_add_virtio_dev(rvdev, rvdev->id); 4819c31255cSArnaud Pouliquen } 4829c31255cSArnaud Pouliquen 4839c31255cSArnaud Pouliquen static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) 4849c31255cSArnaud Pouliquen { 4859c31255cSArnaud Pouliquen struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); 4861d7b61c0SArnaud Pouliquen struct device *dev = &rvdev->pdev->dev; 4879c31255cSArnaud Pouliquen int ret; 4889c31255cSArnaud Pouliquen 4891d7b61c0SArnaud Pouliquen ret = device_for_each_child(dev, NULL, rproc_remove_virtio_dev); 4909c31255cSArnaud Pouliquen if (ret) 4911d7b61c0SArnaud Pouliquen dev_warn(dev, "can't remove vdev child device: %d\n", ret); 4929c31255cSArnaud Pouliquen } 4939c31255cSArnaud Pouliquen 4941d7b61c0SArnaud Pouliquen static int rproc_virtio_probe(struct platform_device *pdev) 4959c31255cSArnaud Pouliquen { 4961d7b61c0SArnaud Pouliquen struct device *dev = &pdev->dev; 4971d7b61c0SArnaud Pouliquen struct rproc_vdev_data *rvdev_data = dev->platform_data; 4989c31255cSArnaud Pouliquen struct rproc_vdev *rvdev; 4991d7b61c0SArnaud Pouliquen struct rproc *rproc = container_of(dev->parent, struct rproc, dev); 5001d7b61c0SArnaud Pouliquen struct fw_rsc_vdev *rsc; 5019c31255cSArnaud Pouliquen int i, ret; 5029c31255cSArnaud Pouliquen 5031d7b61c0SArnaud Pouliquen if (!rvdev_data) 5041d7b61c0SArnaud Pouliquen return -EINVAL; 5059c31255cSArnaud Pouliquen 5061d7b61c0SArnaud Pouliquen rvdev = devm_kzalloc(dev, sizeof(*rvdev), GFP_KERNEL); 5071d7b61c0SArnaud Pouliquen if (!rvdev) 5081d7b61c0SArnaud Pouliquen return -ENOMEM; 5099c31255cSArnaud Pouliquen 5109c31255cSArnaud Pouliquen rvdev->id = rvdev_data->id; 5119c31255cSArnaud Pouliquen rvdev->rproc = rproc; 5129c31255cSArnaud Pouliquen rvdev->index = rvdev_data->index; 5139c31255cSArnaud Pouliquen 5141d7b61c0SArnaud Pouliquen ret = copy_dma_range_map(dev, rproc->dev.parent); 5159c31255cSArnaud Pouliquen if (ret) 5161d7b61c0SArnaud Pouliquen return ret; 5179c31255cSArnaud Pouliquen 5189c31255cSArnaud Pouliquen /* Make device dma capable by inheriting from parent's capabilities */ 5191d7b61c0SArnaud Pouliquen set_dma_ops(dev, get_dma_ops(rproc->dev.parent)); 5209c31255cSArnaud Pouliquen 5211d7b61c0SArnaud Pouliquen ret = dma_coerce_mask_and_coherent(dev, dma_get_mask(rproc->dev.parent)); 5229c31255cSArnaud Pouliquen if (ret) { 5231d7b61c0SArnaud Pouliquen dev_warn(dev, "Failed to set DMA mask %llx. Trying to continue... (%pe)\n", 5249c31255cSArnaud Pouliquen dma_get_mask(rproc->dev.parent), ERR_PTR(ret)); 5259c31255cSArnaud Pouliquen } 5269c31255cSArnaud Pouliquen 5271d7b61c0SArnaud Pouliquen platform_set_drvdata(pdev, rvdev); 5281d7b61c0SArnaud Pouliquen rvdev->pdev = pdev; 5291d7b61c0SArnaud Pouliquen 5301d7b61c0SArnaud Pouliquen rsc = rvdev_data->rsc; 5311d7b61c0SArnaud Pouliquen 5329c31255cSArnaud Pouliquen /* parse the vrings */ 5339c31255cSArnaud Pouliquen for (i = 0; i < rsc->num_of_vrings; i++) { 5349c31255cSArnaud Pouliquen ret = rproc_parse_vring(rvdev, rsc, i); 5359c31255cSArnaud Pouliquen if (ret) 5361d7b61c0SArnaud Pouliquen return ret; 5379c31255cSArnaud Pouliquen } 5389c31255cSArnaud Pouliquen 5399c31255cSArnaud Pouliquen /* remember the resource offset*/ 5409c31255cSArnaud Pouliquen rvdev->rsc_offset = rvdev_data->rsc_offset; 5419c31255cSArnaud Pouliquen 5429c31255cSArnaud Pouliquen /* allocate the vring resources */ 5439c31255cSArnaud Pouliquen for (i = 0; i < rsc->num_of_vrings; i++) { 5449c31255cSArnaud Pouliquen ret = rproc_alloc_vring(rvdev, i); 5459c31255cSArnaud Pouliquen if (ret) 5469c31255cSArnaud Pouliquen goto unwind_vring_allocations; 5479c31255cSArnaud Pouliquen } 5489c31255cSArnaud Pouliquen 5499c31255cSArnaud Pouliquen rproc_add_rvdev(rproc, rvdev); 5509c31255cSArnaud Pouliquen 5519c31255cSArnaud Pouliquen rvdev->subdev.start = rproc_vdev_do_start; 5529c31255cSArnaud Pouliquen rvdev->subdev.stop = rproc_vdev_do_stop; 5539c31255cSArnaud Pouliquen 5549c31255cSArnaud Pouliquen rproc_add_subdev(rproc, &rvdev->subdev); 5559c31255cSArnaud Pouliquen 5561d7b61c0SArnaud Pouliquen /* 5571d7b61c0SArnaud Pouliquen * We're indirectly making a non-temporary copy of the rproc pointer 5581d7b61c0SArnaud Pouliquen * here, because the platform device or the vdev device will indirectly 5591d7b61c0SArnaud Pouliquen * access the wrapping rproc. 5601d7b61c0SArnaud Pouliquen * 5611d7b61c0SArnaud Pouliquen * Therefore we must increment the rproc refcount here, and decrement 5621d7b61c0SArnaud Pouliquen * it _only_ on platform remove. 5631d7b61c0SArnaud Pouliquen */ 5641d7b61c0SArnaud Pouliquen get_device(&rproc->dev); 5651d7b61c0SArnaud Pouliquen 5661d7b61c0SArnaud Pouliquen return 0; 5679c31255cSArnaud Pouliquen 5689c31255cSArnaud Pouliquen unwind_vring_allocations: 5699c31255cSArnaud Pouliquen for (i--; i >= 0; i--) 5709c31255cSArnaud Pouliquen rproc_free_vring(&rvdev->vring[i]); 5711d7b61c0SArnaud Pouliquen 5721d7b61c0SArnaud Pouliquen return ret; 5739c31255cSArnaud Pouliquen } 5749c31255cSArnaud Pouliquen 575d1d8d442SUwe Kleine-König static void rproc_virtio_remove(struct platform_device *pdev) 5769c31255cSArnaud Pouliquen { 5771d7b61c0SArnaud Pouliquen struct rproc_vdev *rvdev = dev_get_drvdata(&pdev->dev); 5789c31255cSArnaud Pouliquen struct rproc *rproc = rvdev->rproc; 5791d7b61c0SArnaud Pouliquen struct rproc_vring *rvring; 5809c31255cSArnaud Pouliquen int id; 5819c31255cSArnaud Pouliquen 5829c31255cSArnaud Pouliquen for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) { 5839c31255cSArnaud Pouliquen rvring = &rvdev->vring[id]; 5849c31255cSArnaud Pouliquen rproc_free_vring(rvring); 5859c31255cSArnaud Pouliquen } 5869c31255cSArnaud Pouliquen 5879c31255cSArnaud Pouliquen rproc_remove_subdev(rproc, &rvdev->subdev); 5889c31255cSArnaud Pouliquen rproc_remove_rvdev(rvdev); 5891d7b61c0SArnaud Pouliquen 5901d7b61c0SArnaud Pouliquen put_device(&rproc->dev); 5919c31255cSArnaud Pouliquen } 5921d7b61c0SArnaud Pouliquen 5931d7b61c0SArnaud Pouliquen /* Platform driver */ 5941d7b61c0SArnaud Pouliquen static struct platform_driver rproc_virtio_driver = { 5951d7b61c0SArnaud Pouliquen .probe = rproc_virtio_probe, 596d1d8d442SUwe Kleine-König .remove_new = rproc_virtio_remove, 5971d7b61c0SArnaud Pouliquen .driver = { 5981d7b61c0SArnaud Pouliquen .name = "rproc-virtio", 5991d7b61c0SArnaud Pouliquen }, 6001d7b61c0SArnaud Pouliquen }; 6011d7b61c0SArnaud Pouliquen builtin_platform_driver(rproc_virtio_driver); 602