1400e64dfSOhad Ben-Cohen /* 2400e64dfSOhad Ben-Cohen * Remote Processor Framework 3400e64dfSOhad Ben-Cohen * 4400e64dfSOhad Ben-Cohen * Copyright (C) 2011 Texas Instruments, Inc. 5400e64dfSOhad Ben-Cohen * Copyright (C) 2011 Google, Inc. 6400e64dfSOhad Ben-Cohen * 7400e64dfSOhad Ben-Cohen * Ohad Ben-Cohen <ohad@wizery.com> 8400e64dfSOhad Ben-Cohen * Brian Swetland <swetland@google.com> 9400e64dfSOhad Ben-Cohen * Mark Grosen <mgrosen@ti.com> 10400e64dfSOhad Ben-Cohen * Fernando Guzman Lugo <fernando.lugo@ti.com> 11400e64dfSOhad Ben-Cohen * Suman Anna <s-anna@ti.com> 12400e64dfSOhad Ben-Cohen * Robert Tivy <rtivy@ti.com> 13400e64dfSOhad Ben-Cohen * Armando Uribe De Leon <x0095078@ti.com> 14400e64dfSOhad Ben-Cohen * 15400e64dfSOhad Ben-Cohen * This program is free software; you can redistribute it and/or 16400e64dfSOhad Ben-Cohen * modify it under the terms of the GNU General Public License 17400e64dfSOhad Ben-Cohen * version 2 as published by the Free Software Foundation. 18400e64dfSOhad Ben-Cohen * 19400e64dfSOhad Ben-Cohen * This program is distributed in the hope that it will be useful, 20400e64dfSOhad Ben-Cohen * but WITHOUT ANY WARRANTY; without even the implied warranty of 21400e64dfSOhad Ben-Cohen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22400e64dfSOhad Ben-Cohen * GNU General Public License for more details. 23400e64dfSOhad Ben-Cohen */ 24400e64dfSOhad Ben-Cohen 25400e64dfSOhad Ben-Cohen #define pr_fmt(fmt) "%s: " fmt, __func__ 26400e64dfSOhad Ben-Cohen 27400e64dfSOhad Ben-Cohen #include <linux/kernel.h> 28400e64dfSOhad Ben-Cohen #include <linux/module.h> 29400e64dfSOhad Ben-Cohen #include <linux/device.h> 30400e64dfSOhad Ben-Cohen #include <linux/slab.h> 31400e64dfSOhad Ben-Cohen #include <linux/mutex.h> 32400e64dfSOhad Ben-Cohen #include <linux/dma-mapping.h> 33400e64dfSOhad Ben-Cohen #include <linux/firmware.h> 34400e64dfSOhad Ben-Cohen #include <linux/string.h> 35400e64dfSOhad Ben-Cohen #include <linux/debugfs.h> 36400e64dfSOhad Ben-Cohen #include <linux/remoteproc.h> 37400e64dfSOhad Ben-Cohen #include <linux/iommu.h> 38b5ab5e24SOhad Ben-Cohen #include <linux/idr.h> 39400e64dfSOhad Ben-Cohen #include <linux/elf.h> 40400e64dfSOhad Ben-Cohen #include <linux/virtio_ids.h> 41400e64dfSOhad Ben-Cohen #include <linux/virtio_ring.h> 42cf59d3e9SOhad Ben-Cohen #include <asm/byteorder.h> 43400e64dfSOhad Ben-Cohen 44400e64dfSOhad Ben-Cohen #include "remoteproc_internal.h" 45400e64dfSOhad Ben-Cohen 46400e64dfSOhad Ben-Cohen typedef int (*rproc_handle_resources_t)(struct rproc *rproc, 47fd2c15ecSOhad Ben-Cohen struct resource_table *table, int len); 48fd2c15ecSOhad Ben-Cohen typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); 49400e64dfSOhad Ben-Cohen 50b5ab5e24SOhad Ben-Cohen /* Unique indices for remoteproc devices */ 51b5ab5e24SOhad Ben-Cohen static DEFINE_IDA(rproc_dev_index); 52b5ab5e24SOhad Ben-Cohen 53400e64dfSOhad Ben-Cohen /* 54400e64dfSOhad Ben-Cohen * This is the IOMMU fault handler we register with the IOMMU API 55400e64dfSOhad Ben-Cohen * (when relevant; not all remote processors access memory through 56400e64dfSOhad Ben-Cohen * an IOMMU). 57400e64dfSOhad Ben-Cohen * 58400e64dfSOhad Ben-Cohen * IOMMU core will invoke this handler whenever the remote processor 59400e64dfSOhad Ben-Cohen * will try to access an unmapped device address. 60400e64dfSOhad Ben-Cohen * 61400e64dfSOhad Ben-Cohen * Currently this is mostly a stub, but it will be later used to trigger 62400e64dfSOhad Ben-Cohen * the recovery of the remote processor. 63400e64dfSOhad Ben-Cohen */ 64400e64dfSOhad Ben-Cohen static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev, 6577ca2332SOhad Ben-Cohen unsigned long iova, int flags, void *token) 66400e64dfSOhad Ben-Cohen { 67400e64dfSOhad Ben-Cohen dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags); 68400e64dfSOhad Ben-Cohen 69400e64dfSOhad Ben-Cohen /* 70400e64dfSOhad Ben-Cohen * Let the iommu core know we're not really handling this fault; 71400e64dfSOhad Ben-Cohen * we just plan to use this as a recovery trigger. 72400e64dfSOhad Ben-Cohen */ 73400e64dfSOhad Ben-Cohen return -ENOSYS; 74400e64dfSOhad Ben-Cohen } 75400e64dfSOhad Ben-Cohen 76400e64dfSOhad Ben-Cohen static int rproc_enable_iommu(struct rproc *rproc) 77400e64dfSOhad Ben-Cohen { 78400e64dfSOhad Ben-Cohen struct iommu_domain *domain; 79b5ab5e24SOhad Ben-Cohen struct device *dev = rproc->dev.parent; 80400e64dfSOhad Ben-Cohen int ret; 81400e64dfSOhad Ben-Cohen 82400e64dfSOhad Ben-Cohen /* 83400e64dfSOhad Ben-Cohen * We currently use iommu_present() to decide if an IOMMU 84400e64dfSOhad Ben-Cohen * setup is needed. 85400e64dfSOhad Ben-Cohen * 86400e64dfSOhad Ben-Cohen * This works for simple cases, but will easily fail with 87400e64dfSOhad Ben-Cohen * platforms that do have an IOMMU, but not for this specific 88400e64dfSOhad Ben-Cohen * rproc. 89400e64dfSOhad Ben-Cohen * 90400e64dfSOhad Ben-Cohen * This will be easily solved by introducing hw capabilities 91400e64dfSOhad Ben-Cohen * that will be set by the remoteproc driver. 92400e64dfSOhad Ben-Cohen */ 93400e64dfSOhad Ben-Cohen if (!iommu_present(dev->bus)) { 940798e1daSMark Grosen dev_dbg(dev, "iommu not found\n"); 950798e1daSMark Grosen return 0; 96400e64dfSOhad Ben-Cohen } 97400e64dfSOhad Ben-Cohen 98400e64dfSOhad Ben-Cohen domain = iommu_domain_alloc(dev->bus); 99400e64dfSOhad Ben-Cohen if (!domain) { 100400e64dfSOhad Ben-Cohen dev_err(dev, "can't alloc iommu domain\n"); 101400e64dfSOhad Ben-Cohen return -ENOMEM; 102400e64dfSOhad Ben-Cohen } 103400e64dfSOhad Ben-Cohen 10477ca2332SOhad Ben-Cohen iommu_set_fault_handler(domain, rproc_iommu_fault, rproc); 105400e64dfSOhad Ben-Cohen 106400e64dfSOhad Ben-Cohen ret = iommu_attach_device(domain, dev); 107400e64dfSOhad Ben-Cohen if (ret) { 108400e64dfSOhad Ben-Cohen dev_err(dev, "can't attach iommu device: %d\n", ret); 109400e64dfSOhad Ben-Cohen goto free_domain; 110400e64dfSOhad Ben-Cohen } 111400e64dfSOhad Ben-Cohen 112400e64dfSOhad Ben-Cohen rproc->domain = domain; 113400e64dfSOhad Ben-Cohen 114400e64dfSOhad Ben-Cohen return 0; 115400e64dfSOhad Ben-Cohen 116400e64dfSOhad Ben-Cohen free_domain: 117400e64dfSOhad Ben-Cohen iommu_domain_free(domain); 118400e64dfSOhad Ben-Cohen return ret; 119400e64dfSOhad Ben-Cohen } 120400e64dfSOhad Ben-Cohen 121400e64dfSOhad Ben-Cohen static void rproc_disable_iommu(struct rproc *rproc) 122400e64dfSOhad Ben-Cohen { 123400e64dfSOhad Ben-Cohen struct iommu_domain *domain = rproc->domain; 124b5ab5e24SOhad Ben-Cohen struct device *dev = rproc->dev.parent; 125400e64dfSOhad Ben-Cohen 126400e64dfSOhad Ben-Cohen if (!domain) 127400e64dfSOhad Ben-Cohen return; 128400e64dfSOhad Ben-Cohen 129400e64dfSOhad Ben-Cohen iommu_detach_device(domain, dev); 130400e64dfSOhad Ben-Cohen iommu_domain_free(domain); 131400e64dfSOhad Ben-Cohen 132400e64dfSOhad Ben-Cohen return; 133400e64dfSOhad Ben-Cohen } 134400e64dfSOhad Ben-Cohen 135400e64dfSOhad Ben-Cohen /* 136400e64dfSOhad Ben-Cohen * Some remote processors will ask us to allocate them physically contiguous 137400e64dfSOhad Ben-Cohen * memory regions (which we call "carveouts"), and map them to specific 138400e64dfSOhad Ben-Cohen * device addresses (which are hardcoded in the firmware). 139400e64dfSOhad Ben-Cohen * 140400e64dfSOhad Ben-Cohen * They may then ask us to copy objects into specific device addresses (e.g. 141400e64dfSOhad Ben-Cohen * code/data sections) or expose us certain symbols in other device address 142400e64dfSOhad Ben-Cohen * (e.g. their trace buffer). 143400e64dfSOhad Ben-Cohen * 144400e64dfSOhad Ben-Cohen * This function is an internal helper with which we can go over the allocated 145400e64dfSOhad Ben-Cohen * carveouts and translate specific device address to kernel virtual addresses 146400e64dfSOhad Ben-Cohen * so we can access the referenced memory. 147400e64dfSOhad Ben-Cohen * 148400e64dfSOhad Ben-Cohen * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too, 149400e64dfSOhad Ben-Cohen * but only on kernel direct mapped RAM memory. Instead, we're just using 150400e64dfSOhad Ben-Cohen * here the output of the DMA API, which should be more correct. 151400e64dfSOhad Ben-Cohen */ 152*72854fb0SSjur Brændeland void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) 153400e64dfSOhad Ben-Cohen { 154400e64dfSOhad Ben-Cohen struct rproc_mem_entry *carveout; 155400e64dfSOhad Ben-Cohen void *ptr = NULL; 156400e64dfSOhad Ben-Cohen 157400e64dfSOhad Ben-Cohen list_for_each_entry(carveout, &rproc->carveouts, node) { 158400e64dfSOhad Ben-Cohen int offset = da - carveout->da; 159400e64dfSOhad Ben-Cohen 160400e64dfSOhad Ben-Cohen /* try next carveout if da is too small */ 161400e64dfSOhad Ben-Cohen if (offset < 0) 162400e64dfSOhad Ben-Cohen continue; 163400e64dfSOhad Ben-Cohen 164400e64dfSOhad Ben-Cohen /* try next carveout if da is too large */ 165400e64dfSOhad Ben-Cohen if (offset + len > carveout->len) 166400e64dfSOhad Ben-Cohen continue; 167400e64dfSOhad Ben-Cohen 168400e64dfSOhad Ben-Cohen ptr = carveout->va + offset; 169400e64dfSOhad Ben-Cohen 170400e64dfSOhad Ben-Cohen break; 171400e64dfSOhad Ben-Cohen } 172400e64dfSOhad Ben-Cohen 173400e64dfSOhad Ben-Cohen return ptr; 174400e64dfSOhad Ben-Cohen } 175400e64dfSOhad Ben-Cohen 1766db20ea8SOhad Ben-Cohen int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) 1776db20ea8SOhad Ben-Cohen { 1786db20ea8SOhad Ben-Cohen struct rproc *rproc = rvdev->rproc; 179b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 1806db20ea8SOhad Ben-Cohen struct rproc_vring *rvring = &rvdev->vring[i]; 1816db20ea8SOhad Ben-Cohen dma_addr_t dma; 1826db20ea8SOhad Ben-Cohen void *va; 1836db20ea8SOhad Ben-Cohen int ret, size, notifyid; 1846db20ea8SOhad Ben-Cohen 1856db20ea8SOhad Ben-Cohen /* actual size of vring (in bytes) */ 1866db20ea8SOhad Ben-Cohen size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); 1876db20ea8SOhad Ben-Cohen 1886db20ea8SOhad Ben-Cohen if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) { 1896db20ea8SOhad Ben-Cohen dev_err(dev, "idr_pre_get failed\n"); 1906db20ea8SOhad Ben-Cohen return -ENOMEM; 1916db20ea8SOhad Ben-Cohen } 1926db20ea8SOhad Ben-Cohen 1936db20ea8SOhad Ben-Cohen /* 1946db20ea8SOhad Ben-Cohen * Allocate non-cacheable memory for the vring. In the future 1956db20ea8SOhad Ben-Cohen * this call will also configure the IOMMU for us 1966db20ea8SOhad Ben-Cohen * TODO: let the rproc know the da of this vring 1976db20ea8SOhad Ben-Cohen */ 198b5ab5e24SOhad Ben-Cohen va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); 1996db20ea8SOhad Ben-Cohen if (!va) { 200b5ab5e24SOhad Ben-Cohen dev_err(dev->parent, "dma_alloc_coherent failed\n"); 2016db20ea8SOhad Ben-Cohen return -EINVAL; 2026db20ea8SOhad Ben-Cohen } 2036db20ea8SOhad Ben-Cohen 2046db20ea8SOhad Ben-Cohen /* 2056db20ea8SOhad Ben-Cohen * Assign an rproc-wide unique index for this vring 2066db20ea8SOhad Ben-Cohen * TODO: assign a notifyid for rvdev updates as well 2076db20ea8SOhad Ben-Cohen * TODO: let the rproc know the notifyid of this vring 2086db20ea8SOhad Ben-Cohen * TODO: support predefined notifyids (via resource table) 2096db20ea8SOhad Ben-Cohen */ 2106db20ea8SOhad Ben-Cohen ret = idr_get_new(&rproc->notifyids, rvring, ¬ifyid); 2116db20ea8SOhad Ben-Cohen if (ret) { 2126db20ea8SOhad Ben-Cohen dev_err(dev, "idr_get_new failed: %d\n", ret); 213b5ab5e24SOhad Ben-Cohen dma_free_coherent(dev->parent, size, va, dma); 2146db20ea8SOhad Ben-Cohen return ret; 2156db20ea8SOhad Ben-Cohen } 2166db20ea8SOhad Ben-Cohen 2176db20ea8SOhad Ben-Cohen dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va, 2186db20ea8SOhad Ben-Cohen dma, size, notifyid); 2196db20ea8SOhad Ben-Cohen 2206db20ea8SOhad Ben-Cohen rvring->va = va; 2216db20ea8SOhad Ben-Cohen rvring->dma = dma; 2226db20ea8SOhad Ben-Cohen rvring->notifyid = notifyid; 2236db20ea8SOhad Ben-Cohen 2246db20ea8SOhad Ben-Cohen return 0; 2256db20ea8SOhad Ben-Cohen } 2266db20ea8SOhad Ben-Cohen 2277a186941SOhad Ben-Cohen static int 2286db20ea8SOhad Ben-Cohen rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) 229400e64dfSOhad Ben-Cohen { 2307a186941SOhad Ben-Cohen struct rproc *rproc = rvdev->rproc; 231b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 2327a186941SOhad Ben-Cohen struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; 2336db20ea8SOhad Ben-Cohen struct rproc_vring *rvring = &rvdev->vring[i]; 234400e64dfSOhad Ben-Cohen 2357a186941SOhad Ben-Cohen dev_dbg(dev, "vdev rsc: vring%d: da %x, qsz %d, align %d\n", 2367a186941SOhad Ben-Cohen i, vring->da, vring->num, vring->align); 2377a186941SOhad Ben-Cohen 2387a186941SOhad Ben-Cohen /* make sure reserved bytes are zeroes */ 2397a186941SOhad Ben-Cohen if (vring->reserved) { 2407a186941SOhad Ben-Cohen dev_err(dev, "vring rsc has non zero reserved bytes\n"); 241fd2c15ecSOhad Ben-Cohen return -EINVAL; 242fd2c15ecSOhad Ben-Cohen } 243fd2c15ecSOhad Ben-Cohen 24463140e0eSOhad Ben-Cohen /* verify queue size and vring alignment are sane */ 24563140e0eSOhad Ben-Cohen if (!vring->num || !vring->align) { 24663140e0eSOhad Ben-Cohen dev_err(dev, "invalid qsz (%d) or alignment (%d)\n", 24763140e0eSOhad Ben-Cohen vring->num, vring->align); 248400e64dfSOhad Ben-Cohen return -EINVAL; 249400e64dfSOhad Ben-Cohen } 250400e64dfSOhad Ben-Cohen 2516db20ea8SOhad Ben-Cohen rvring->len = vring->num; 2526db20ea8SOhad Ben-Cohen rvring->align = vring->align; 2536db20ea8SOhad Ben-Cohen rvring->rvdev = rvdev; 254400e64dfSOhad Ben-Cohen 255400e64dfSOhad Ben-Cohen return 0; 256400e64dfSOhad Ben-Cohen } 257400e64dfSOhad Ben-Cohen 2586db20ea8SOhad Ben-Cohen void rproc_free_vring(struct rproc_vring *rvring) 2597a186941SOhad Ben-Cohen { 26063140e0eSOhad Ben-Cohen int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); 2616db20ea8SOhad Ben-Cohen struct rproc *rproc = rvring->rvdev->rproc; 2627a186941SOhad Ben-Cohen 263b5ab5e24SOhad Ben-Cohen dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); 2647a186941SOhad Ben-Cohen idr_remove(&rproc->notifyids, rvring->notifyid); 2657a186941SOhad Ben-Cohen } 2667a186941SOhad Ben-Cohen 267400e64dfSOhad Ben-Cohen /** 268fd2c15ecSOhad Ben-Cohen * rproc_handle_vdev() - handle a vdev fw resource 269400e64dfSOhad Ben-Cohen * @rproc: the remote processor 270400e64dfSOhad Ben-Cohen * @rsc: the vring resource descriptor 271fd2c15ecSOhad Ben-Cohen * @avail: size of available data (for sanity checking the image) 272400e64dfSOhad Ben-Cohen * 2737a186941SOhad Ben-Cohen * This resource entry requests the host to statically register a virtio 2747a186941SOhad Ben-Cohen * device (vdev), and setup everything needed to support it. It contains 2757a186941SOhad Ben-Cohen * everything needed to make it possible: the virtio device id, virtio 2767a186941SOhad Ben-Cohen * device features, vrings information, virtio config space, etc... 277400e64dfSOhad Ben-Cohen * 2787a186941SOhad Ben-Cohen * Before registering the vdev, the vrings are allocated from non-cacheable 2797a186941SOhad Ben-Cohen * physically contiguous memory. Currently we only support two vrings per 2807a186941SOhad Ben-Cohen * remote processor (temporary limitation). We might also want to consider 2817a186941SOhad Ben-Cohen * doing the vring allocation only later when ->find_vqs() is invoked, and 2827a186941SOhad Ben-Cohen * then release them upon ->del_vqs(). 283400e64dfSOhad Ben-Cohen * 2847a186941SOhad Ben-Cohen * Note: @da is currently not really handled correctly: we dynamically 2857a186941SOhad Ben-Cohen * allocate it using the DMA API, ignoring requested hard coded addresses, 2867a186941SOhad Ben-Cohen * and we don't take care of any required IOMMU programming. This is all 2877a186941SOhad Ben-Cohen * going to be taken care of when the generic iommu-based DMA API will be 2887a186941SOhad Ben-Cohen * merged. Meanwhile, statically-addressed iommu-based firmware images should 2897a186941SOhad Ben-Cohen * use RSC_DEVMEM resource entries to map their required @da to the physical 2907a186941SOhad Ben-Cohen * address of their base CMA region (ouch, hacky!). 291400e64dfSOhad Ben-Cohen * 292400e64dfSOhad Ben-Cohen * Returns 0 on success, or an appropriate error code otherwise 293400e64dfSOhad Ben-Cohen */ 294fd2c15ecSOhad Ben-Cohen static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, 295fd2c15ecSOhad Ben-Cohen int avail) 296400e64dfSOhad Ben-Cohen { 297b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 2987a186941SOhad Ben-Cohen struct rproc_vdev *rvdev; 2997a186941SOhad Ben-Cohen int i, ret; 300fd2c15ecSOhad Ben-Cohen 301fd2c15ecSOhad Ben-Cohen /* make sure resource isn't truncated */ 302fd2c15ecSOhad Ben-Cohen if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) 303fd2c15ecSOhad Ben-Cohen + rsc->config_len > avail) { 304b5ab5e24SOhad Ben-Cohen dev_err(dev, "vdev rsc is truncated\n"); 305fd2c15ecSOhad Ben-Cohen return -EINVAL; 306fd2c15ecSOhad Ben-Cohen } 307fd2c15ecSOhad Ben-Cohen 308fd2c15ecSOhad Ben-Cohen /* make sure reserved bytes are zeroes */ 309fd2c15ecSOhad Ben-Cohen if (rsc->reserved[0] || rsc->reserved[1]) { 310fd2c15ecSOhad Ben-Cohen dev_err(dev, "vdev rsc has non zero reserved bytes\n"); 311fd2c15ecSOhad Ben-Cohen return -EINVAL; 312fd2c15ecSOhad Ben-Cohen } 313fd2c15ecSOhad Ben-Cohen 314fd2c15ecSOhad Ben-Cohen dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n", 315fd2c15ecSOhad Ben-Cohen rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings); 316400e64dfSOhad Ben-Cohen 3177a186941SOhad Ben-Cohen /* we currently support only two vrings per rvdev */ 3187a186941SOhad Ben-Cohen if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) { 319fd2c15ecSOhad Ben-Cohen dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings); 320400e64dfSOhad Ben-Cohen return -EINVAL; 321400e64dfSOhad Ben-Cohen } 322400e64dfSOhad Ben-Cohen 3237a186941SOhad Ben-Cohen rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL); 3247a186941SOhad Ben-Cohen if (!rvdev) 3257a186941SOhad Ben-Cohen return -ENOMEM; 3267a186941SOhad Ben-Cohen 3277a186941SOhad Ben-Cohen rvdev->rproc = rproc; 3287a186941SOhad Ben-Cohen 3296db20ea8SOhad Ben-Cohen /* parse the vrings */ 330fd2c15ecSOhad Ben-Cohen for (i = 0; i < rsc->num_of_vrings; i++) { 3316db20ea8SOhad Ben-Cohen ret = rproc_parse_vring(rvdev, rsc, i); 3327a186941SOhad Ben-Cohen if (ret) 3336db20ea8SOhad Ben-Cohen goto free_rvdev; 334fd2c15ecSOhad Ben-Cohen } 335fd2c15ecSOhad Ben-Cohen 3367a186941SOhad Ben-Cohen /* remember the device features */ 3377a186941SOhad Ben-Cohen rvdev->dfeatures = rsc->dfeatures; 338400e64dfSOhad Ben-Cohen 3397a186941SOhad Ben-Cohen list_add_tail(&rvdev->node, &rproc->rvdevs); 340400e64dfSOhad Ben-Cohen 3417a186941SOhad Ben-Cohen /* it is now safe to add the virtio device */ 3427a186941SOhad Ben-Cohen ret = rproc_add_virtio_dev(rvdev, rsc->id); 3437a186941SOhad Ben-Cohen if (ret) 3446db20ea8SOhad Ben-Cohen goto free_rvdev; 345400e64dfSOhad Ben-Cohen 346400e64dfSOhad Ben-Cohen return 0; 3477a186941SOhad Ben-Cohen 3486db20ea8SOhad Ben-Cohen free_rvdev: 3497a186941SOhad Ben-Cohen kfree(rvdev); 3507a186941SOhad Ben-Cohen return ret; 351400e64dfSOhad Ben-Cohen } 352400e64dfSOhad Ben-Cohen 353400e64dfSOhad Ben-Cohen /** 354400e64dfSOhad Ben-Cohen * rproc_handle_trace() - handle a shared trace buffer resource 355400e64dfSOhad Ben-Cohen * @rproc: the remote processor 356400e64dfSOhad Ben-Cohen * @rsc: the trace resource descriptor 357fd2c15ecSOhad Ben-Cohen * @avail: size of available data (for sanity checking the image) 358400e64dfSOhad Ben-Cohen * 359400e64dfSOhad Ben-Cohen * In case the remote processor dumps trace logs into memory, 360400e64dfSOhad Ben-Cohen * export it via debugfs. 361400e64dfSOhad Ben-Cohen * 362400e64dfSOhad Ben-Cohen * Currently, the 'da' member of @rsc should contain the device address 363400e64dfSOhad Ben-Cohen * where the remote processor is dumping the traces. Later we could also 364400e64dfSOhad Ben-Cohen * support dynamically allocating this address using the generic 365400e64dfSOhad Ben-Cohen * DMA API (but currently there isn't a use case for that). 366400e64dfSOhad Ben-Cohen * 367400e64dfSOhad Ben-Cohen * Returns 0 on success, or an appropriate error code otherwise 368400e64dfSOhad Ben-Cohen */ 369fd2c15ecSOhad Ben-Cohen static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, 370fd2c15ecSOhad Ben-Cohen int avail) 371400e64dfSOhad Ben-Cohen { 372400e64dfSOhad Ben-Cohen struct rproc_mem_entry *trace; 373b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 374400e64dfSOhad Ben-Cohen void *ptr; 375400e64dfSOhad Ben-Cohen char name[15]; 376400e64dfSOhad Ben-Cohen 377fd2c15ecSOhad Ben-Cohen if (sizeof(*rsc) > avail) { 378b5ab5e24SOhad Ben-Cohen dev_err(dev, "trace rsc is truncated\n"); 379fd2c15ecSOhad Ben-Cohen return -EINVAL; 380fd2c15ecSOhad Ben-Cohen } 381fd2c15ecSOhad Ben-Cohen 382fd2c15ecSOhad Ben-Cohen /* make sure reserved bytes are zeroes */ 383fd2c15ecSOhad Ben-Cohen if (rsc->reserved) { 384fd2c15ecSOhad Ben-Cohen dev_err(dev, "trace rsc has non zero reserved bytes\n"); 385fd2c15ecSOhad Ben-Cohen return -EINVAL; 386fd2c15ecSOhad Ben-Cohen } 387fd2c15ecSOhad Ben-Cohen 388400e64dfSOhad Ben-Cohen /* what's the kernel address of this resource ? */ 389400e64dfSOhad Ben-Cohen ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); 390400e64dfSOhad Ben-Cohen if (!ptr) { 391400e64dfSOhad Ben-Cohen dev_err(dev, "erroneous trace resource entry\n"); 392400e64dfSOhad Ben-Cohen return -EINVAL; 393400e64dfSOhad Ben-Cohen } 394400e64dfSOhad Ben-Cohen 395400e64dfSOhad Ben-Cohen trace = kzalloc(sizeof(*trace), GFP_KERNEL); 396400e64dfSOhad Ben-Cohen if (!trace) { 397400e64dfSOhad Ben-Cohen dev_err(dev, "kzalloc trace failed\n"); 398400e64dfSOhad Ben-Cohen return -ENOMEM; 399400e64dfSOhad Ben-Cohen } 400400e64dfSOhad Ben-Cohen 401400e64dfSOhad Ben-Cohen /* set the trace buffer dma properties */ 402400e64dfSOhad Ben-Cohen trace->len = rsc->len; 403400e64dfSOhad Ben-Cohen trace->va = ptr; 404400e64dfSOhad Ben-Cohen 405400e64dfSOhad Ben-Cohen /* make sure snprintf always null terminates, even if truncating */ 406400e64dfSOhad Ben-Cohen snprintf(name, sizeof(name), "trace%d", rproc->num_traces); 407400e64dfSOhad Ben-Cohen 408400e64dfSOhad Ben-Cohen /* create the debugfs entry */ 409400e64dfSOhad Ben-Cohen trace->priv = rproc_create_trace_file(name, rproc, trace); 410400e64dfSOhad Ben-Cohen if (!trace->priv) { 411400e64dfSOhad Ben-Cohen trace->va = NULL; 412400e64dfSOhad Ben-Cohen kfree(trace); 413400e64dfSOhad Ben-Cohen return -EINVAL; 414400e64dfSOhad Ben-Cohen } 415400e64dfSOhad Ben-Cohen 416400e64dfSOhad Ben-Cohen list_add_tail(&trace->node, &rproc->traces); 417400e64dfSOhad Ben-Cohen 418400e64dfSOhad Ben-Cohen rproc->num_traces++; 419400e64dfSOhad Ben-Cohen 420fd2c15ecSOhad Ben-Cohen dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", name, ptr, 421400e64dfSOhad Ben-Cohen rsc->da, rsc->len); 422400e64dfSOhad Ben-Cohen 423400e64dfSOhad Ben-Cohen return 0; 424400e64dfSOhad Ben-Cohen } 425400e64dfSOhad Ben-Cohen 426400e64dfSOhad Ben-Cohen /** 427400e64dfSOhad Ben-Cohen * rproc_handle_devmem() - handle devmem resource entry 428400e64dfSOhad Ben-Cohen * @rproc: remote processor handle 429400e64dfSOhad Ben-Cohen * @rsc: the devmem resource entry 430fd2c15ecSOhad Ben-Cohen * @avail: size of available data (for sanity checking the image) 431400e64dfSOhad Ben-Cohen * 432400e64dfSOhad Ben-Cohen * Remote processors commonly need to access certain on-chip peripherals. 433400e64dfSOhad Ben-Cohen * 434400e64dfSOhad Ben-Cohen * Some of these remote processors access memory via an iommu device, 435400e64dfSOhad Ben-Cohen * and might require us to configure their iommu before they can access 436400e64dfSOhad Ben-Cohen * the on-chip peripherals they need. 437400e64dfSOhad Ben-Cohen * 438400e64dfSOhad Ben-Cohen * This resource entry is a request to map such a peripheral device. 439400e64dfSOhad Ben-Cohen * 440400e64dfSOhad Ben-Cohen * These devmem entries will contain the physical address of the device in 441400e64dfSOhad Ben-Cohen * the 'pa' member. If a specific device address is expected, then 'da' will 442400e64dfSOhad Ben-Cohen * contain it (currently this is the only use case supported). 'len' will 443400e64dfSOhad Ben-Cohen * contain the size of the physical region we need to map. 444400e64dfSOhad Ben-Cohen * 445400e64dfSOhad Ben-Cohen * Currently we just "trust" those devmem entries to contain valid physical 446400e64dfSOhad Ben-Cohen * addresses, but this is going to change: we want the implementations to 447400e64dfSOhad Ben-Cohen * tell us ranges of physical addresses the firmware is allowed to request, 448400e64dfSOhad Ben-Cohen * and not allow firmwares to request access to physical addresses that 449400e64dfSOhad Ben-Cohen * are outside those ranges. 450400e64dfSOhad Ben-Cohen */ 451fd2c15ecSOhad Ben-Cohen static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, 452fd2c15ecSOhad Ben-Cohen int avail) 453400e64dfSOhad Ben-Cohen { 454400e64dfSOhad Ben-Cohen struct rproc_mem_entry *mapping; 455b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 456400e64dfSOhad Ben-Cohen int ret; 457400e64dfSOhad Ben-Cohen 458400e64dfSOhad Ben-Cohen /* no point in handling this resource without a valid iommu domain */ 459400e64dfSOhad Ben-Cohen if (!rproc->domain) 460400e64dfSOhad Ben-Cohen return -EINVAL; 461400e64dfSOhad Ben-Cohen 462fd2c15ecSOhad Ben-Cohen if (sizeof(*rsc) > avail) { 463b5ab5e24SOhad Ben-Cohen dev_err(dev, "devmem rsc is truncated\n"); 464fd2c15ecSOhad Ben-Cohen return -EINVAL; 465fd2c15ecSOhad Ben-Cohen } 466fd2c15ecSOhad Ben-Cohen 467fd2c15ecSOhad Ben-Cohen /* make sure reserved bytes are zeroes */ 468fd2c15ecSOhad Ben-Cohen if (rsc->reserved) { 469b5ab5e24SOhad Ben-Cohen dev_err(dev, "devmem rsc has non zero reserved bytes\n"); 470fd2c15ecSOhad Ben-Cohen return -EINVAL; 471fd2c15ecSOhad Ben-Cohen } 472fd2c15ecSOhad Ben-Cohen 473400e64dfSOhad Ben-Cohen mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); 474400e64dfSOhad Ben-Cohen if (!mapping) { 475b5ab5e24SOhad Ben-Cohen dev_err(dev, "kzalloc mapping failed\n"); 476400e64dfSOhad Ben-Cohen return -ENOMEM; 477400e64dfSOhad Ben-Cohen } 478400e64dfSOhad Ben-Cohen 479400e64dfSOhad Ben-Cohen ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags); 480400e64dfSOhad Ben-Cohen if (ret) { 481b5ab5e24SOhad Ben-Cohen dev_err(dev, "failed to map devmem: %d\n", ret); 482400e64dfSOhad Ben-Cohen goto out; 483400e64dfSOhad Ben-Cohen } 484400e64dfSOhad Ben-Cohen 485400e64dfSOhad Ben-Cohen /* 486400e64dfSOhad Ben-Cohen * We'll need this info later when we'll want to unmap everything 487400e64dfSOhad Ben-Cohen * (e.g. on shutdown). 488400e64dfSOhad Ben-Cohen * 489400e64dfSOhad Ben-Cohen * We can't trust the remote processor not to change the resource 490400e64dfSOhad Ben-Cohen * table, so we must maintain this info independently. 491400e64dfSOhad Ben-Cohen */ 492400e64dfSOhad Ben-Cohen mapping->da = rsc->da; 493400e64dfSOhad Ben-Cohen mapping->len = rsc->len; 494400e64dfSOhad Ben-Cohen list_add_tail(&mapping->node, &rproc->mappings); 495400e64dfSOhad Ben-Cohen 496b5ab5e24SOhad Ben-Cohen dev_dbg(dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n", 497400e64dfSOhad Ben-Cohen rsc->pa, rsc->da, rsc->len); 498400e64dfSOhad Ben-Cohen 499400e64dfSOhad Ben-Cohen return 0; 500400e64dfSOhad Ben-Cohen 501400e64dfSOhad Ben-Cohen out: 502400e64dfSOhad Ben-Cohen kfree(mapping); 503400e64dfSOhad Ben-Cohen return ret; 504400e64dfSOhad Ben-Cohen } 505400e64dfSOhad Ben-Cohen 506400e64dfSOhad Ben-Cohen /** 507400e64dfSOhad Ben-Cohen * rproc_handle_carveout() - handle phys contig memory allocation requests 508400e64dfSOhad Ben-Cohen * @rproc: rproc handle 509400e64dfSOhad Ben-Cohen * @rsc: the resource entry 510fd2c15ecSOhad Ben-Cohen * @avail: size of available data (for image validation) 511400e64dfSOhad Ben-Cohen * 512400e64dfSOhad Ben-Cohen * This function will handle firmware requests for allocation of physically 513400e64dfSOhad Ben-Cohen * contiguous memory regions. 514400e64dfSOhad Ben-Cohen * 515400e64dfSOhad Ben-Cohen * These request entries should come first in the firmware's resource table, 516400e64dfSOhad Ben-Cohen * as other firmware entries might request placing other data objects inside 517400e64dfSOhad Ben-Cohen * these memory regions (e.g. data/code segments, trace resource entries, ...). 518400e64dfSOhad Ben-Cohen * 519400e64dfSOhad Ben-Cohen * Allocating memory this way helps utilizing the reserved physical memory 520400e64dfSOhad Ben-Cohen * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries 521400e64dfSOhad Ben-Cohen * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB 522400e64dfSOhad Ben-Cohen * pressure is important; it may have a substantial impact on performance. 523400e64dfSOhad Ben-Cohen */ 524fd2c15ecSOhad Ben-Cohen static int rproc_handle_carveout(struct rproc *rproc, 525fd2c15ecSOhad Ben-Cohen struct fw_rsc_carveout *rsc, int avail) 526400e64dfSOhad Ben-Cohen { 527400e64dfSOhad Ben-Cohen struct rproc_mem_entry *carveout, *mapping; 528b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 529400e64dfSOhad Ben-Cohen dma_addr_t dma; 530400e64dfSOhad Ben-Cohen void *va; 531400e64dfSOhad Ben-Cohen int ret; 532400e64dfSOhad Ben-Cohen 533fd2c15ecSOhad Ben-Cohen if (sizeof(*rsc) > avail) { 534b5ab5e24SOhad Ben-Cohen dev_err(dev, "carveout rsc is truncated\n"); 535fd2c15ecSOhad Ben-Cohen return -EINVAL; 536fd2c15ecSOhad Ben-Cohen } 537fd2c15ecSOhad Ben-Cohen 538fd2c15ecSOhad Ben-Cohen /* make sure reserved bytes are zeroes */ 539fd2c15ecSOhad Ben-Cohen if (rsc->reserved) { 540fd2c15ecSOhad Ben-Cohen dev_err(dev, "carveout rsc has non zero reserved bytes\n"); 541fd2c15ecSOhad Ben-Cohen return -EINVAL; 542fd2c15ecSOhad Ben-Cohen } 543fd2c15ecSOhad Ben-Cohen 544fd2c15ecSOhad Ben-Cohen dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n", 545fd2c15ecSOhad Ben-Cohen rsc->da, rsc->pa, rsc->len, rsc->flags); 546fd2c15ecSOhad Ben-Cohen 547400e64dfSOhad Ben-Cohen mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); 548400e64dfSOhad Ben-Cohen if (!mapping) { 549400e64dfSOhad Ben-Cohen dev_err(dev, "kzalloc mapping failed\n"); 550400e64dfSOhad Ben-Cohen return -ENOMEM; 551400e64dfSOhad Ben-Cohen } 552400e64dfSOhad Ben-Cohen 553400e64dfSOhad Ben-Cohen carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); 554400e64dfSOhad Ben-Cohen if (!carveout) { 555400e64dfSOhad Ben-Cohen dev_err(dev, "kzalloc carveout failed\n"); 556400e64dfSOhad Ben-Cohen ret = -ENOMEM; 557400e64dfSOhad Ben-Cohen goto free_mapping; 558400e64dfSOhad Ben-Cohen } 559400e64dfSOhad Ben-Cohen 560b5ab5e24SOhad Ben-Cohen va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); 561400e64dfSOhad Ben-Cohen if (!va) { 562b5ab5e24SOhad Ben-Cohen dev_err(dev->parent, "dma_alloc_coherent err: %d\n", rsc->len); 563400e64dfSOhad Ben-Cohen ret = -ENOMEM; 564400e64dfSOhad Ben-Cohen goto free_carv; 565400e64dfSOhad Ben-Cohen } 566400e64dfSOhad Ben-Cohen 567400e64dfSOhad Ben-Cohen dev_dbg(dev, "carveout va %p, dma %x, len 0x%x\n", va, dma, rsc->len); 568400e64dfSOhad Ben-Cohen 569400e64dfSOhad Ben-Cohen /* 570400e64dfSOhad Ben-Cohen * Ok, this is non-standard. 571400e64dfSOhad Ben-Cohen * 572400e64dfSOhad Ben-Cohen * Sometimes we can't rely on the generic iommu-based DMA API 573400e64dfSOhad Ben-Cohen * to dynamically allocate the device address and then set the IOMMU 574400e64dfSOhad Ben-Cohen * tables accordingly, because some remote processors might 575400e64dfSOhad Ben-Cohen * _require_ us to use hard coded device addresses that their 576400e64dfSOhad Ben-Cohen * firmware was compiled with. 577400e64dfSOhad Ben-Cohen * 578400e64dfSOhad Ben-Cohen * In this case, we must use the IOMMU API directly and map 579400e64dfSOhad Ben-Cohen * the memory to the device address as expected by the remote 580400e64dfSOhad Ben-Cohen * processor. 581400e64dfSOhad Ben-Cohen * 582400e64dfSOhad Ben-Cohen * Obviously such remote processor devices should not be configured 583400e64dfSOhad Ben-Cohen * to use the iommu-based DMA API: we expect 'dma' to contain the 584400e64dfSOhad Ben-Cohen * physical address in this case. 585400e64dfSOhad Ben-Cohen */ 586400e64dfSOhad Ben-Cohen if (rproc->domain) { 587400e64dfSOhad Ben-Cohen ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len, 588400e64dfSOhad Ben-Cohen rsc->flags); 589400e64dfSOhad Ben-Cohen if (ret) { 590400e64dfSOhad Ben-Cohen dev_err(dev, "iommu_map failed: %d\n", ret); 591400e64dfSOhad Ben-Cohen goto dma_free; 592400e64dfSOhad Ben-Cohen } 593400e64dfSOhad Ben-Cohen 594400e64dfSOhad Ben-Cohen /* 595400e64dfSOhad Ben-Cohen * We'll need this info later when we'll want to unmap 596400e64dfSOhad Ben-Cohen * everything (e.g. on shutdown). 597400e64dfSOhad Ben-Cohen * 598400e64dfSOhad Ben-Cohen * We can't trust the remote processor not to change the 599400e64dfSOhad Ben-Cohen * resource table, so we must maintain this info independently. 600400e64dfSOhad Ben-Cohen */ 601400e64dfSOhad Ben-Cohen mapping->da = rsc->da; 602400e64dfSOhad Ben-Cohen mapping->len = rsc->len; 603400e64dfSOhad Ben-Cohen list_add_tail(&mapping->node, &rproc->mappings); 604400e64dfSOhad Ben-Cohen 605fd2c15ecSOhad Ben-Cohen dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma); 6060e49b72cSOhad Ben-Cohen } 607400e64dfSOhad Ben-Cohen 608400e64dfSOhad Ben-Cohen /* 609400e64dfSOhad Ben-Cohen * Some remote processors might need to know the pa 610400e64dfSOhad Ben-Cohen * even though they are behind an IOMMU. E.g., OMAP4's 611400e64dfSOhad Ben-Cohen * remote M3 processor needs this so it can control 612400e64dfSOhad Ben-Cohen * on-chip hardware accelerators that are not behind 613400e64dfSOhad Ben-Cohen * the IOMMU, and therefor must know the pa. 614400e64dfSOhad Ben-Cohen * 615400e64dfSOhad Ben-Cohen * Generally we don't want to expose physical addresses 616400e64dfSOhad Ben-Cohen * if we don't have to (remote processors are generally 617400e64dfSOhad Ben-Cohen * _not_ trusted), so we might want to do this only for 618400e64dfSOhad Ben-Cohen * remote processor that _must_ have this (e.g. OMAP4's 619400e64dfSOhad Ben-Cohen * dual M3 subsystem). 6200e49b72cSOhad Ben-Cohen * 6210e49b72cSOhad Ben-Cohen * Non-IOMMU processors might also want to have this info. 6220e49b72cSOhad Ben-Cohen * In this case, the device address and the physical address 6230e49b72cSOhad Ben-Cohen * are the same. 624400e64dfSOhad Ben-Cohen */ 625400e64dfSOhad Ben-Cohen rsc->pa = dma; 626400e64dfSOhad Ben-Cohen 627400e64dfSOhad Ben-Cohen carveout->va = va; 628400e64dfSOhad Ben-Cohen carveout->len = rsc->len; 629400e64dfSOhad Ben-Cohen carveout->dma = dma; 630400e64dfSOhad Ben-Cohen carveout->da = rsc->da; 631400e64dfSOhad Ben-Cohen 632400e64dfSOhad Ben-Cohen list_add_tail(&carveout->node, &rproc->carveouts); 633400e64dfSOhad Ben-Cohen 634400e64dfSOhad Ben-Cohen return 0; 635400e64dfSOhad Ben-Cohen 636400e64dfSOhad Ben-Cohen dma_free: 637b5ab5e24SOhad Ben-Cohen dma_free_coherent(dev->parent, rsc->len, va, dma); 638400e64dfSOhad Ben-Cohen free_carv: 639400e64dfSOhad Ben-Cohen kfree(carveout); 640400e64dfSOhad Ben-Cohen free_mapping: 641400e64dfSOhad Ben-Cohen kfree(mapping); 642400e64dfSOhad Ben-Cohen return ret; 643400e64dfSOhad Ben-Cohen } 644400e64dfSOhad Ben-Cohen 645e12bc14bSOhad Ben-Cohen /* 646e12bc14bSOhad Ben-Cohen * A lookup table for resource handlers. The indices are defined in 647e12bc14bSOhad Ben-Cohen * enum fw_resource_type. 648e12bc14bSOhad Ben-Cohen */ 649e12bc14bSOhad Ben-Cohen static rproc_handle_resource_t rproc_handle_rsc[] = { 650fd2c15ecSOhad Ben-Cohen [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, 651fd2c15ecSOhad Ben-Cohen [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, 652fd2c15ecSOhad Ben-Cohen [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, 6537a186941SOhad Ben-Cohen [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */ 654e12bc14bSOhad Ben-Cohen }; 655e12bc14bSOhad Ben-Cohen 656400e64dfSOhad Ben-Cohen /* handle firmware resource entries before booting the remote processor */ 657400e64dfSOhad Ben-Cohen static int 658fd2c15ecSOhad Ben-Cohen rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len) 659400e64dfSOhad Ben-Cohen { 660b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 661e12bc14bSOhad Ben-Cohen rproc_handle_resource_t handler; 662fd2c15ecSOhad Ben-Cohen int ret = 0, i; 663400e64dfSOhad Ben-Cohen 664fd2c15ecSOhad Ben-Cohen for (i = 0; i < table->num; i++) { 665fd2c15ecSOhad Ben-Cohen int offset = table->offset[i]; 666fd2c15ecSOhad Ben-Cohen struct fw_rsc_hdr *hdr = (void *)table + offset; 667fd2c15ecSOhad Ben-Cohen int avail = len - offset - sizeof(*hdr); 668fd2c15ecSOhad Ben-Cohen void *rsc = (void *)hdr + sizeof(*hdr); 669400e64dfSOhad Ben-Cohen 670fd2c15ecSOhad Ben-Cohen /* make sure table isn't truncated */ 671fd2c15ecSOhad Ben-Cohen if (avail < 0) { 672fd2c15ecSOhad Ben-Cohen dev_err(dev, "rsc table is truncated\n"); 673fd2c15ecSOhad Ben-Cohen return -EINVAL; 674fd2c15ecSOhad Ben-Cohen } 675fd2c15ecSOhad Ben-Cohen 676fd2c15ecSOhad Ben-Cohen dev_dbg(dev, "rsc: type %d\n", hdr->type); 677fd2c15ecSOhad Ben-Cohen 678fd2c15ecSOhad Ben-Cohen if (hdr->type >= RSC_LAST) { 679fd2c15ecSOhad Ben-Cohen dev_warn(dev, "unsupported resource %d\n", hdr->type); 680e12bc14bSOhad Ben-Cohen continue; 681400e64dfSOhad Ben-Cohen } 682400e64dfSOhad Ben-Cohen 683fd2c15ecSOhad Ben-Cohen handler = rproc_handle_rsc[hdr->type]; 684e12bc14bSOhad Ben-Cohen if (!handler) 685e12bc14bSOhad Ben-Cohen continue; 686e12bc14bSOhad Ben-Cohen 687fd2c15ecSOhad Ben-Cohen ret = handler(rproc, rsc, avail); 688400e64dfSOhad Ben-Cohen if (ret) 689400e64dfSOhad Ben-Cohen break; 690400e64dfSOhad Ben-Cohen } 691400e64dfSOhad Ben-Cohen 692400e64dfSOhad Ben-Cohen return ret; 693400e64dfSOhad Ben-Cohen } 694400e64dfSOhad Ben-Cohen 695400e64dfSOhad Ben-Cohen /* handle firmware resource entries while registering the remote processor */ 696400e64dfSOhad Ben-Cohen static int 697fd2c15ecSOhad Ben-Cohen rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len) 698400e64dfSOhad Ben-Cohen { 699b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 700fd2c15ecSOhad Ben-Cohen int ret = 0, i; 701400e64dfSOhad Ben-Cohen 702fd2c15ecSOhad Ben-Cohen for (i = 0; i < table->num; i++) { 703fd2c15ecSOhad Ben-Cohen int offset = table->offset[i]; 704fd2c15ecSOhad Ben-Cohen struct fw_rsc_hdr *hdr = (void *)table + offset; 705fd2c15ecSOhad Ben-Cohen int avail = len - offset - sizeof(*hdr); 7067a186941SOhad Ben-Cohen struct fw_rsc_vdev *vrsc; 707fd2c15ecSOhad Ben-Cohen 708fd2c15ecSOhad Ben-Cohen /* make sure table isn't truncated */ 709fd2c15ecSOhad Ben-Cohen if (avail < 0) { 710fd2c15ecSOhad Ben-Cohen dev_err(dev, "rsc table is truncated\n"); 711fd2c15ecSOhad Ben-Cohen return -EINVAL; 712fd2c15ecSOhad Ben-Cohen } 713fd2c15ecSOhad Ben-Cohen 714fd2c15ecSOhad Ben-Cohen dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type); 715fd2c15ecSOhad Ben-Cohen 7167a186941SOhad Ben-Cohen if (hdr->type != RSC_VDEV) 7177a186941SOhad Ben-Cohen continue; 7187a186941SOhad Ben-Cohen 7197a186941SOhad Ben-Cohen vrsc = (struct fw_rsc_vdev *)hdr->data; 7207a186941SOhad Ben-Cohen 7217a186941SOhad Ben-Cohen ret = rproc_handle_vdev(rproc, vrsc, avail); 7227a186941SOhad Ben-Cohen if (ret) 723400e64dfSOhad Ben-Cohen break; 724400e64dfSOhad Ben-Cohen } 725400e64dfSOhad Ben-Cohen 726400e64dfSOhad Ben-Cohen return ret; 727400e64dfSOhad Ben-Cohen } 728400e64dfSOhad Ben-Cohen 729400e64dfSOhad Ben-Cohen /** 730400e64dfSOhad Ben-Cohen * rproc_resource_cleanup() - clean up and free all acquired resources 731400e64dfSOhad Ben-Cohen * @rproc: rproc handle 732400e64dfSOhad Ben-Cohen * 733400e64dfSOhad Ben-Cohen * This function will free all resources acquired for @rproc, and it 7347a186941SOhad Ben-Cohen * is called whenever @rproc either shuts down or fails to boot. 735400e64dfSOhad Ben-Cohen */ 736400e64dfSOhad Ben-Cohen static void rproc_resource_cleanup(struct rproc *rproc) 737400e64dfSOhad Ben-Cohen { 738400e64dfSOhad Ben-Cohen struct rproc_mem_entry *entry, *tmp; 739b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 740400e64dfSOhad Ben-Cohen 741400e64dfSOhad Ben-Cohen /* clean up debugfs trace entries */ 742400e64dfSOhad Ben-Cohen list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { 743400e64dfSOhad Ben-Cohen rproc_remove_trace_file(entry->priv); 744400e64dfSOhad Ben-Cohen rproc->num_traces--; 745400e64dfSOhad Ben-Cohen list_del(&entry->node); 746400e64dfSOhad Ben-Cohen kfree(entry); 747400e64dfSOhad Ben-Cohen } 748400e64dfSOhad Ben-Cohen 749400e64dfSOhad Ben-Cohen /* clean up carveout allocations */ 750400e64dfSOhad Ben-Cohen list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { 751b5ab5e24SOhad Ben-Cohen dma_free_coherent(dev->parent, entry->len, entry->va, entry->dma); 752400e64dfSOhad Ben-Cohen list_del(&entry->node); 753400e64dfSOhad Ben-Cohen kfree(entry); 754400e64dfSOhad Ben-Cohen } 755400e64dfSOhad Ben-Cohen 756400e64dfSOhad Ben-Cohen /* clean up iommu mapping entries */ 757400e64dfSOhad Ben-Cohen list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) { 758400e64dfSOhad Ben-Cohen size_t unmapped; 759400e64dfSOhad Ben-Cohen 760400e64dfSOhad Ben-Cohen unmapped = iommu_unmap(rproc->domain, entry->da, entry->len); 761400e64dfSOhad Ben-Cohen if (unmapped != entry->len) { 762400e64dfSOhad Ben-Cohen /* nothing much to do besides complaining */ 763400e64dfSOhad Ben-Cohen dev_err(dev, "failed to unmap %u/%u\n", entry->len, 764400e64dfSOhad Ben-Cohen unmapped); 765400e64dfSOhad Ben-Cohen } 766400e64dfSOhad Ben-Cohen 767400e64dfSOhad Ben-Cohen list_del(&entry->node); 768400e64dfSOhad Ben-Cohen kfree(entry); 769400e64dfSOhad Ben-Cohen } 770400e64dfSOhad Ben-Cohen } 771400e64dfSOhad Ben-Cohen 772400e64dfSOhad Ben-Cohen /* 773400e64dfSOhad Ben-Cohen * take a firmware and boot a remote processor with it. 774400e64dfSOhad Ben-Cohen */ 775400e64dfSOhad Ben-Cohen static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) 776400e64dfSOhad Ben-Cohen { 777b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 778400e64dfSOhad Ben-Cohen const char *name = rproc->firmware; 7791e3e2c7cSOhad Ben-Cohen struct resource_table *table; 7801e3e2c7cSOhad Ben-Cohen int ret, tablesz; 781400e64dfSOhad Ben-Cohen 782400e64dfSOhad Ben-Cohen ret = rproc_fw_sanity_check(rproc, fw); 783400e64dfSOhad Ben-Cohen if (ret) 784400e64dfSOhad Ben-Cohen return ret; 785400e64dfSOhad Ben-Cohen 786400e64dfSOhad Ben-Cohen dev_info(dev, "Booting fw image %s, size %d\n", name, fw->size); 787400e64dfSOhad Ben-Cohen 788400e64dfSOhad Ben-Cohen /* 789400e64dfSOhad Ben-Cohen * if enabling an IOMMU isn't relevant for this rproc, this is 790400e64dfSOhad Ben-Cohen * just a nop 791400e64dfSOhad Ben-Cohen */ 792400e64dfSOhad Ben-Cohen ret = rproc_enable_iommu(rproc); 793400e64dfSOhad Ben-Cohen if (ret) { 794400e64dfSOhad Ben-Cohen dev_err(dev, "can't enable iommu: %d\n", ret); 795400e64dfSOhad Ben-Cohen return ret; 796400e64dfSOhad Ben-Cohen } 797400e64dfSOhad Ben-Cohen 7983e5f9eb5SSjur Brændeland rproc->bootaddr = rproc_get_boot_addr(rproc, fw); 799400e64dfSOhad Ben-Cohen 8001e3e2c7cSOhad Ben-Cohen /* look for the resource table */ 801bd484984SSjur Brændeland table = rproc_find_rsc_table(rproc, fw, &tablesz); 8021e3e2c7cSOhad Ben-Cohen if (!table) 8031e3e2c7cSOhad Ben-Cohen goto clean_up; 8041e3e2c7cSOhad Ben-Cohen 805400e64dfSOhad Ben-Cohen /* handle fw resources which are required to boot rproc */ 8061e3e2c7cSOhad Ben-Cohen ret = rproc_handle_boot_rsc(rproc, table, tablesz); 807400e64dfSOhad Ben-Cohen if (ret) { 808400e64dfSOhad Ben-Cohen dev_err(dev, "Failed to process resources: %d\n", ret); 809400e64dfSOhad Ben-Cohen goto clean_up; 810400e64dfSOhad Ben-Cohen } 811400e64dfSOhad Ben-Cohen 812400e64dfSOhad Ben-Cohen /* load the ELF segments to memory */ 813bd484984SSjur Brændeland ret = rproc_load_segments(rproc, fw); 814400e64dfSOhad Ben-Cohen if (ret) { 815400e64dfSOhad Ben-Cohen dev_err(dev, "Failed to load program segments: %d\n", ret); 816400e64dfSOhad Ben-Cohen goto clean_up; 817400e64dfSOhad Ben-Cohen } 818400e64dfSOhad Ben-Cohen 819400e64dfSOhad Ben-Cohen /* power up the remote processor */ 820400e64dfSOhad Ben-Cohen ret = rproc->ops->start(rproc); 821400e64dfSOhad Ben-Cohen if (ret) { 822400e64dfSOhad Ben-Cohen dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret); 823400e64dfSOhad Ben-Cohen goto clean_up; 824400e64dfSOhad Ben-Cohen } 825400e64dfSOhad Ben-Cohen 826400e64dfSOhad Ben-Cohen rproc->state = RPROC_RUNNING; 827400e64dfSOhad Ben-Cohen 828400e64dfSOhad Ben-Cohen dev_info(dev, "remote processor %s is now up\n", rproc->name); 829400e64dfSOhad Ben-Cohen 830400e64dfSOhad Ben-Cohen return 0; 831400e64dfSOhad Ben-Cohen 832400e64dfSOhad Ben-Cohen clean_up: 833400e64dfSOhad Ben-Cohen rproc_resource_cleanup(rproc); 834400e64dfSOhad Ben-Cohen rproc_disable_iommu(rproc); 835400e64dfSOhad Ben-Cohen return ret; 836400e64dfSOhad Ben-Cohen } 837400e64dfSOhad Ben-Cohen 838400e64dfSOhad Ben-Cohen /* 839400e64dfSOhad Ben-Cohen * take a firmware and look for virtio devices to register. 840400e64dfSOhad Ben-Cohen * 841400e64dfSOhad Ben-Cohen * Note: this function is called asynchronously upon registration of the 842400e64dfSOhad Ben-Cohen * remote processor (so we must wait until it completes before we try 843400e64dfSOhad Ben-Cohen * to unregister the device. one other option is just to use kref here, 844400e64dfSOhad Ben-Cohen * that might be cleaner). 845400e64dfSOhad Ben-Cohen */ 846400e64dfSOhad Ben-Cohen static void rproc_fw_config_virtio(const struct firmware *fw, void *context) 847400e64dfSOhad Ben-Cohen { 848400e64dfSOhad Ben-Cohen struct rproc *rproc = context; 8491e3e2c7cSOhad Ben-Cohen struct resource_table *table; 8501e3e2c7cSOhad Ben-Cohen int ret, tablesz; 851400e64dfSOhad Ben-Cohen 852400e64dfSOhad Ben-Cohen if (rproc_fw_sanity_check(rproc, fw) < 0) 853400e64dfSOhad Ben-Cohen goto out; 854400e64dfSOhad Ben-Cohen 8551e3e2c7cSOhad Ben-Cohen /* look for the resource table */ 856bd484984SSjur Brændeland table = rproc_find_rsc_table(rproc, fw, &tablesz); 8571e3e2c7cSOhad Ben-Cohen if (!table) 858400e64dfSOhad Ben-Cohen goto out; 8591e3e2c7cSOhad Ben-Cohen 8601e3e2c7cSOhad Ben-Cohen /* look for virtio devices and register them */ 8611e3e2c7cSOhad Ben-Cohen ret = rproc_handle_virtio_rsc(rproc, table, tablesz); 8621e3e2c7cSOhad Ben-Cohen if (ret) 8631e3e2c7cSOhad Ben-Cohen goto out; 864400e64dfSOhad Ben-Cohen 865400e64dfSOhad Ben-Cohen out: 866400e64dfSOhad Ben-Cohen release_firmware(fw); 867160e7c84SOhad Ben-Cohen /* allow rproc_del() contexts, if any, to proceed */ 868400e64dfSOhad Ben-Cohen complete_all(&rproc->firmware_loading_complete); 869400e64dfSOhad Ben-Cohen } 870400e64dfSOhad Ben-Cohen 871400e64dfSOhad Ben-Cohen /** 872400e64dfSOhad Ben-Cohen * rproc_boot() - boot a remote processor 873400e64dfSOhad Ben-Cohen * @rproc: handle of a remote processor 874400e64dfSOhad Ben-Cohen * 875400e64dfSOhad Ben-Cohen * Boot a remote processor (i.e. load its firmware, power it on, ...). 876400e64dfSOhad Ben-Cohen * 877400e64dfSOhad Ben-Cohen * If the remote processor is already powered on, this function immediately 878400e64dfSOhad Ben-Cohen * returns (successfully). 879400e64dfSOhad Ben-Cohen * 880400e64dfSOhad Ben-Cohen * Returns 0 on success, and an appropriate error value otherwise. 881400e64dfSOhad Ben-Cohen */ 882400e64dfSOhad Ben-Cohen int rproc_boot(struct rproc *rproc) 883400e64dfSOhad Ben-Cohen { 884400e64dfSOhad Ben-Cohen const struct firmware *firmware_p; 885400e64dfSOhad Ben-Cohen struct device *dev; 886400e64dfSOhad Ben-Cohen int ret; 887400e64dfSOhad Ben-Cohen 888400e64dfSOhad Ben-Cohen if (!rproc) { 889400e64dfSOhad Ben-Cohen pr_err("invalid rproc handle\n"); 890400e64dfSOhad Ben-Cohen return -EINVAL; 891400e64dfSOhad Ben-Cohen } 892400e64dfSOhad Ben-Cohen 893b5ab5e24SOhad Ben-Cohen dev = &rproc->dev; 894400e64dfSOhad Ben-Cohen 895400e64dfSOhad Ben-Cohen ret = mutex_lock_interruptible(&rproc->lock); 896400e64dfSOhad Ben-Cohen if (ret) { 897400e64dfSOhad Ben-Cohen dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); 898400e64dfSOhad Ben-Cohen return ret; 899400e64dfSOhad Ben-Cohen } 900400e64dfSOhad Ben-Cohen 901400e64dfSOhad Ben-Cohen /* loading a firmware is required */ 902400e64dfSOhad Ben-Cohen if (!rproc->firmware) { 903400e64dfSOhad Ben-Cohen dev_err(dev, "%s: no firmware to load\n", __func__); 904400e64dfSOhad Ben-Cohen ret = -EINVAL; 905400e64dfSOhad Ben-Cohen goto unlock_mutex; 906400e64dfSOhad Ben-Cohen } 907400e64dfSOhad Ben-Cohen 908400e64dfSOhad Ben-Cohen /* prevent underlying implementation from being removed */ 909b5ab5e24SOhad Ben-Cohen if (!try_module_get(dev->parent->driver->owner)) { 910400e64dfSOhad Ben-Cohen dev_err(dev, "%s: can't get owner\n", __func__); 911400e64dfSOhad Ben-Cohen ret = -EINVAL; 912400e64dfSOhad Ben-Cohen goto unlock_mutex; 913400e64dfSOhad Ben-Cohen } 914400e64dfSOhad Ben-Cohen 915400e64dfSOhad Ben-Cohen /* skip the boot process if rproc is already powered up */ 916400e64dfSOhad Ben-Cohen if (atomic_inc_return(&rproc->power) > 1) { 917400e64dfSOhad Ben-Cohen ret = 0; 918400e64dfSOhad Ben-Cohen goto unlock_mutex; 919400e64dfSOhad Ben-Cohen } 920400e64dfSOhad Ben-Cohen 921400e64dfSOhad Ben-Cohen dev_info(dev, "powering up %s\n", rproc->name); 922400e64dfSOhad Ben-Cohen 923400e64dfSOhad Ben-Cohen /* load firmware */ 924400e64dfSOhad Ben-Cohen ret = request_firmware(&firmware_p, rproc->firmware, dev); 925400e64dfSOhad Ben-Cohen if (ret < 0) { 926400e64dfSOhad Ben-Cohen dev_err(dev, "request_firmware failed: %d\n", ret); 927400e64dfSOhad Ben-Cohen goto downref_rproc; 928400e64dfSOhad Ben-Cohen } 929400e64dfSOhad Ben-Cohen 930400e64dfSOhad Ben-Cohen ret = rproc_fw_boot(rproc, firmware_p); 931400e64dfSOhad Ben-Cohen 932400e64dfSOhad Ben-Cohen release_firmware(firmware_p); 933400e64dfSOhad Ben-Cohen 934400e64dfSOhad Ben-Cohen downref_rproc: 935400e64dfSOhad Ben-Cohen if (ret) { 936b5ab5e24SOhad Ben-Cohen module_put(dev->parent->driver->owner); 937400e64dfSOhad Ben-Cohen atomic_dec(&rproc->power); 938400e64dfSOhad Ben-Cohen } 939400e64dfSOhad Ben-Cohen unlock_mutex: 940400e64dfSOhad Ben-Cohen mutex_unlock(&rproc->lock); 941400e64dfSOhad Ben-Cohen return ret; 942400e64dfSOhad Ben-Cohen } 943400e64dfSOhad Ben-Cohen EXPORT_SYMBOL(rproc_boot); 944400e64dfSOhad Ben-Cohen 945400e64dfSOhad Ben-Cohen /** 946400e64dfSOhad Ben-Cohen * rproc_shutdown() - power off the remote processor 947400e64dfSOhad Ben-Cohen * @rproc: the remote processor 948400e64dfSOhad Ben-Cohen * 949400e64dfSOhad Ben-Cohen * Power off a remote processor (previously booted with rproc_boot()). 950400e64dfSOhad Ben-Cohen * 951400e64dfSOhad Ben-Cohen * In case @rproc is still being used by an additional user(s), then 952400e64dfSOhad Ben-Cohen * this function will just decrement the power refcount and exit, 953400e64dfSOhad Ben-Cohen * without really powering off the device. 954400e64dfSOhad Ben-Cohen * 955400e64dfSOhad Ben-Cohen * Every call to rproc_boot() must (eventually) be accompanied by a call 956400e64dfSOhad Ben-Cohen * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug. 957400e64dfSOhad Ben-Cohen * 958400e64dfSOhad Ben-Cohen * Notes: 959400e64dfSOhad Ben-Cohen * - we're not decrementing the rproc's refcount, only the power refcount. 960400e64dfSOhad Ben-Cohen * which means that the @rproc handle stays valid even after rproc_shutdown() 961400e64dfSOhad Ben-Cohen * returns, and users can still use it with a subsequent rproc_boot(), if 962400e64dfSOhad Ben-Cohen * needed. 963400e64dfSOhad Ben-Cohen */ 964400e64dfSOhad Ben-Cohen void rproc_shutdown(struct rproc *rproc) 965400e64dfSOhad Ben-Cohen { 966b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 967400e64dfSOhad Ben-Cohen int ret; 968400e64dfSOhad Ben-Cohen 969400e64dfSOhad Ben-Cohen ret = mutex_lock_interruptible(&rproc->lock); 970400e64dfSOhad Ben-Cohen if (ret) { 971400e64dfSOhad Ben-Cohen dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); 972400e64dfSOhad Ben-Cohen return; 973400e64dfSOhad Ben-Cohen } 974400e64dfSOhad Ben-Cohen 975400e64dfSOhad Ben-Cohen /* if the remote proc is still needed, bail out */ 976400e64dfSOhad Ben-Cohen if (!atomic_dec_and_test(&rproc->power)) 977400e64dfSOhad Ben-Cohen goto out; 978400e64dfSOhad Ben-Cohen 979400e64dfSOhad Ben-Cohen /* power off the remote processor */ 980400e64dfSOhad Ben-Cohen ret = rproc->ops->stop(rproc); 981400e64dfSOhad Ben-Cohen if (ret) { 982400e64dfSOhad Ben-Cohen atomic_inc(&rproc->power); 983400e64dfSOhad Ben-Cohen dev_err(dev, "can't stop rproc: %d\n", ret); 984400e64dfSOhad Ben-Cohen goto out; 985400e64dfSOhad Ben-Cohen } 986400e64dfSOhad Ben-Cohen 987400e64dfSOhad Ben-Cohen /* clean up all acquired resources */ 988400e64dfSOhad Ben-Cohen rproc_resource_cleanup(rproc); 989400e64dfSOhad Ben-Cohen 990400e64dfSOhad Ben-Cohen rproc_disable_iommu(rproc); 991400e64dfSOhad Ben-Cohen 992400e64dfSOhad Ben-Cohen rproc->state = RPROC_OFFLINE; 993400e64dfSOhad Ben-Cohen 994400e64dfSOhad Ben-Cohen dev_info(dev, "stopped remote processor %s\n", rproc->name); 995400e64dfSOhad Ben-Cohen 996400e64dfSOhad Ben-Cohen out: 997400e64dfSOhad Ben-Cohen mutex_unlock(&rproc->lock); 998400e64dfSOhad Ben-Cohen if (!ret) 999b5ab5e24SOhad Ben-Cohen module_put(dev->parent->driver->owner); 1000400e64dfSOhad Ben-Cohen } 1001400e64dfSOhad Ben-Cohen EXPORT_SYMBOL(rproc_shutdown); 1002400e64dfSOhad Ben-Cohen 1003400e64dfSOhad Ben-Cohen /** 1004160e7c84SOhad Ben-Cohen * rproc_add() - register a remote processor 1005400e64dfSOhad Ben-Cohen * @rproc: the remote processor handle to register 1006400e64dfSOhad Ben-Cohen * 1007400e64dfSOhad Ben-Cohen * Registers @rproc with the remoteproc framework, after it has been 1008400e64dfSOhad Ben-Cohen * allocated with rproc_alloc(). 1009400e64dfSOhad Ben-Cohen * 1010400e64dfSOhad Ben-Cohen * This is called by the platform-specific rproc implementation, whenever 1011400e64dfSOhad Ben-Cohen * a new remote processor device is probed. 1012400e64dfSOhad Ben-Cohen * 1013400e64dfSOhad Ben-Cohen * Returns 0 on success and an appropriate error code otherwise. 1014400e64dfSOhad Ben-Cohen * 1015400e64dfSOhad Ben-Cohen * Note: this function initiates an asynchronous firmware loading 1016400e64dfSOhad Ben-Cohen * context, which will look for virtio devices supported by the rproc's 1017400e64dfSOhad Ben-Cohen * firmware. 1018400e64dfSOhad Ben-Cohen * 1019400e64dfSOhad Ben-Cohen * If found, those virtio devices will be created and added, so as a result 10207a186941SOhad Ben-Cohen * of registering this remote processor, additional virtio drivers might be 1021400e64dfSOhad Ben-Cohen * probed. 1022400e64dfSOhad Ben-Cohen */ 1023160e7c84SOhad Ben-Cohen int rproc_add(struct rproc *rproc) 1024400e64dfSOhad Ben-Cohen { 1025b5ab5e24SOhad Ben-Cohen struct device *dev = &rproc->dev; 1026400e64dfSOhad Ben-Cohen int ret = 0; 1027400e64dfSOhad Ben-Cohen 1028b5ab5e24SOhad Ben-Cohen ret = device_add(dev); 1029b5ab5e24SOhad Ben-Cohen if (ret < 0) 1030b5ab5e24SOhad Ben-Cohen return ret; 1031b5ab5e24SOhad Ben-Cohen 1032b5ab5e24SOhad Ben-Cohen dev_info(dev, "%s is available\n", rproc->name); 1033400e64dfSOhad Ben-Cohen 1034489d129aSOhad Ben-Cohen dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n"); 1035489d129aSOhad Ben-Cohen dev_info(dev, "THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn't yet guaranteed.\n"); 1036489d129aSOhad Ben-Cohen 1037400e64dfSOhad Ben-Cohen /* create debugfs entries */ 1038400e64dfSOhad Ben-Cohen rproc_create_debug_dir(rproc); 1039400e64dfSOhad Ben-Cohen 1040160e7c84SOhad Ben-Cohen /* rproc_del() calls must wait until async loader completes */ 1041400e64dfSOhad Ben-Cohen init_completion(&rproc->firmware_loading_complete); 1042400e64dfSOhad Ben-Cohen 1043400e64dfSOhad Ben-Cohen /* 1044400e64dfSOhad Ben-Cohen * We must retrieve early virtio configuration info from 10457a186941SOhad Ben-Cohen * the firmware (e.g. whether to register a virtio device, 1046400e64dfSOhad Ben-Cohen * what virtio features does it support, ...). 1047400e64dfSOhad Ben-Cohen * 1048400e64dfSOhad Ben-Cohen * We're initiating an asynchronous firmware loading, so we can 1049400e64dfSOhad Ben-Cohen * be built-in kernel code, without hanging the boot process. 1050400e64dfSOhad Ben-Cohen */ 1051400e64dfSOhad Ben-Cohen ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, 1052400e64dfSOhad Ben-Cohen rproc->firmware, dev, GFP_KERNEL, 1053400e64dfSOhad Ben-Cohen rproc, rproc_fw_config_virtio); 1054400e64dfSOhad Ben-Cohen if (ret < 0) { 1055400e64dfSOhad Ben-Cohen dev_err(dev, "request_firmware_nowait failed: %d\n", ret); 1056400e64dfSOhad Ben-Cohen complete_all(&rproc->firmware_loading_complete); 1057400e64dfSOhad Ben-Cohen } 1058400e64dfSOhad Ben-Cohen 1059400e64dfSOhad Ben-Cohen return ret; 1060400e64dfSOhad Ben-Cohen } 1061160e7c84SOhad Ben-Cohen EXPORT_SYMBOL(rproc_add); 1062400e64dfSOhad Ben-Cohen 1063400e64dfSOhad Ben-Cohen /** 1064b5ab5e24SOhad Ben-Cohen * rproc_type_release() - release a remote processor instance 1065b5ab5e24SOhad Ben-Cohen * @dev: the rproc's device 1066b5ab5e24SOhad Ben-Cohen * 1067b5ab5e24SOhad Ben-Cohen * This function should _never_ be called directly. 1068b5ab5e24SOhad Ben-Cohen * 1069b5ab5e24SOhad Ben-Cohen * It will be called by the driver core when no one holds a valid pointer 1070b5ab5e24SOhad Ben-Cohen * to @dev anymore. 1071b5ab5e24SOhad Ben-Cohen */ 1072b5ab5e24SOhad Ben-Cohen static void rproc_type_release(struct device *dev) 1073b5ab5e24SOhad Ben-Cohen { 1074b5ab5e24SOhad Ben-Cohen struct rproc *rproc = container_of(dev, struct rproc, dev); 1075b5ab5e24SOhad Ben-Cohen 10767183a2a7SOhad Ben-Cohen dev_info(&rproc->dev, "releasing %s\n", rproc->name); 10777183a2a7SOhad Ben-Cohen 10787183a2a7SOhad Ben-Cohen rproc_delete_debug_dir(rproc); 10797183a2a7SOhad Ben-Cohen 1080b5ab5e24SOhad Ben-Cohen idr_remove_all(&rproc->notifyids); 1081b5ab5e24SOhad Ben-Cohen idr_destroy(&rproc->notifyids); 1082b5ab5e24SOhad Ben-Cohen 1083b5ab5e24SOhad Ben-Cohen if (rproc->index >= 0) 1084b5ab5e24SOhad Ben-Cohen ida_simple_remove(&rproc_dev_index, rproc->index); 1085b5ab5e24SOhad Ben-Cohen 1086b5ab5e24SOhad Ben-Cohen kfree(rproc); 1087b5ab5e24SOhad Ben-Cohen } 1088b5ab5e24SOhad Ben-Cohen 1089b5ab5e24SOhad Ben-Cohen static struct device_type rproc_type = { 1090b5ab5e24SOhad Ben-Cohen .name = "remoteproc", 1091b5ab5e24SOhad Ben-Cohen .release = rproc_type_release, 1092b5ab5e24SOhad Ben-Cohen }; 1093b5ab5e24SOhad Ben-Cohen 1094b5ab5e24SOhad Ben-Cohen /** 1095400e64dfSOhad Ben-Cohen * rproc_alloc() - allocate a remote processor handle 1096400e64dfSOhad Ben-Cohen * @dev: the underlying device 1097400e64dfSOhad Ben-Cohen * @name: name of this remote processor 1098400e64dfSOhad Ben-Cohen * @ops: platform-specific handlers (mainly start/stop) 1099400e64dfSOhad Ben-Cohen * @firmware: name of firmware file to load 1100400e64dfSOhad Ben-Cohen * @len: length of private data needed by the rproc driver (in bytes) 1101400e64dfSOhad Ben-Cohen * 1102400e64dfSOhad Ben-Cohen * Allocates a new remote processor handle, but does not register 1103400e64dfSOhad Ben-Cohen * it yet. 1104400e64dfSOhad Ben-Cohen * 1105400e64dfSOhad Ben-Cohen * This function should be used by rproc implementations during initialization 1106400e64dfSOhad Ben-Cohen * of the remote processor. 1107400e64dfSOhad Ben-Cohen * 1108400e64dfSOhad Ben-Cohen * After creating an rproc handle using this function, and when ready, 1109160e7c84SOhad Ben-Cohen * implementations should then call rproc_add() to complete 1110400e64dfSOhad Ben-Cohen * the registration of the remote processor. 1111400e64dfSOhad Ben-Cohen * 1112400e64dfSOhad Ben-Cohen * On success the new rproc is returned, and on failure, NULL. 1113400e64dfSOhad Ben-Cohen * 1114400e64dfSOhad Ben-Cohen * Note: _never_ directly deallocate @rproc, even if it was not registered 1115160e7c84SOhad Ben-Cohen * yet. Instead, when you need to unroll rproc_alloc(), use rproc_put(). 1116400e64dfSOhad Ben-Cohen */ 1117400e64dfSOhad Ben-Cohen struct rproc *rproc_alloc(struct device *dev, const char *name, 1118400e64dfSOhad Ben-Cohen const struct rproc_ops *ops, 1119400e64dfSOhad Ben-Cohen const char *firmware, int len) 1120400e64dfSOhad Ben-Cohen { 1121400e64dfSOhad Ben-Cohen struct rproc *rproc; 1122400e64dfSOhad Ben-Cohen 1123400e64dfSOhad Ben-Cohen if (!dev || !name || !ops) 1124400e64dfSOhad Ben-Cohen return NULL; 1125400e64dfSOhad Ben-Cohen 1126400e64dfSOhad Ben-Cohen rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL); 1127400e64dfSOhad Ben-Cohen if (!rproc) { 1128400e64dfSOhad Ben-Cohen dev_err(dev, "%s: kzalloc failed\n", __func__); 1129400e64dfSOhad Ben-Cohen return NULL; 1130400e64dfSOhad Ben-Cohen } 1131400e64dfSOhad Ben-Cohen 1132400e64dfSOhad Ben-Cohen rproc->name = name; 1133400e64dfSOhad Ben-Cohen rproc->ops = ops; 1134400e64dfSOhad Ben-Cohen rproc->firmware = firmware; 1135400e64dfSOhad Ben-Cohen rproc->priv = &rproc[1]; 1136400e64dfSOhad Ben-Cohen 1137b5ab5e24SOhad Ben-Cohen device_initialize(&rproc->dev); 1138b5ab5e24SOhad Ben-Cohen rproc->dev.parent = dev; 1139b5ab5e24SOhad Ben-Cohen rproc->dev.type = &rproc_type; 1140b5ab5e24SOhad Ben-Cohen 1141b5ab5e24SOhad Ben-Cohen /* Assign a unique device index and name */ 1142b5ab5e24SOhad Ben-Cohen rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); 1143b5ab5e24SOhad Ben-Cohen if (rproc->index < 0) { 1144b5ab5e24SOhad Ben-Cohen dev_err(dev, "ida_simple_get failed: %d\n", rproc->index); 1145b5ab5e24SOhad Ben-Cohen put_device(&rproc->dev); 1146b5ab5e24SOhad Ben-Cohen return NULL; 1147b5ab5e24SOhad Ben-Cohen } 1148b5ab5e24SOhad Ben-Cohen 1149b5ab5e24SOhad Ben-Cohen dev_set_name(&rproc->dev, "remoteproc%d", rproc->index); 1150b5ab5e24SOhad Ben-Cohen 1151400e64dfSOhad Ben-Cohen atomic_set(&rproc->power, 0); 1152400e64dfSOhad Ben-Cohen 1153400e64dfSOhad Ben-Cohen mutex_init(&rproc->lock); 1154400e64dfSOhad Ben-Cohen 11557a186941SOhad Ben-Cohen idr_init(&rproc->notifyids); 11567a186941SOhad Ben-Cohen 1157400e64dfSOhad Ben-Cohen INIT_LIST_HEAD(&rproc->carveouts); 1158400e64dfSOhad Ben-Cohen INIT_LIST_HEAD(&rproc->mappings); 1159400e64dfSOhad Ben-Cohen INIT_LIST_HEAD(&rproc->traces); 11607a186941SOhad Ben-Cohen INIT_LIST_HEAD(&rproc->rvdevs); 1161400e64dfSOhad Ben-Cohen 1162400e64dfSOhad Ben-Cohen rproc->state = RPROC_OFFLINE; 1163400e64dfSOhad Ben-Cohen 1164400e64dfSOhad Ben-Cohen return rproc; 1165400e64dfSOhad Ben-Cohen } 1166400e64dfSOhad Ben-Cohen EXPORT_SYMBOL(rproc_alloc); 1167400e64dfSOhad Ben-Cohen 1168400e64dfSOhad Ben-Cohen /** 1169160e7c84SOhad Ben-Cohen * rproc_put() - unroll rproc_alloc() 1170400e64dfSOhad Ben-Cohen * @rproc: the remote processor handle 1171400e64dfSOhad Ben-Cohen * 1172c6b5a276SOhad Ben-Cohen * This function decrements the rproc dev refcount. 1173400e64dfSOhad Ben-Cohen * 1174c6b5a276SOhad Ben-Cohen * If no one holds any reference to rproc anymore, then its refcount would 1175c6b5a276SOhad Ben-Cohen * now drop to zero, and it would be freed. 1176400e64dfSOhad Ben-Cohen */ 1177160e7c84SOhad Ben-Cohen void rproc_put(struct rproc *rproc) 1178400e64dfSOhad Ben-Cohen { 1179b5ab5e24SOhad Ben-Cohen put_device(&rproc->dev); 1180400e64dfSOhad Ben-Cohen } 1181160e7c84SOhad Ben-Cohen EXPORT_SYMBOL(rproc_put); 1182400e64dfSOhad Ben-Cohen 1183400e64dfSOhad Ben-Cohen /** 1184160e7c84SOhad Ben-Cohen * rproc_del() - unregister a remote processor 1185400e64dfSOhad Ben-Cohen * @rproc: rproc handle to unregister 1186400e64dfSOhad Ben-Cohen * 1187400e64dfSOhad Ben-Cohen * This function should be called when the platform specific rproc 1188400e64dfSOhad Ben-Cohen * implementation decides to remove the rproc device. it should 1189160e7c84SOhad Ben-Cohen * _only_ be called if a previous invocation of rproc_add() 1190400e64dfSOhad Ben-Cohen * has completed successfully. 1191400e64dfSOhad Ben-Cohen * 1192160e7c84SOhad Ben-Cohen * After rproc_del() returns, @rproc isn't freed yet, because 1193c6b5a276SOhad Ben-Cohen * of the outstanding reference created by rproc_alloc. To decrement that 1194160e7c84SOhad Ben-Cohen * one last refcount, one still needs to call rproc_put(). 1195400e64dfSOhad Ben-Cohen * 1196400e64dfSOhad Ben-Cohen * Returns 0 on success and -EINVAL if @rproc isn't valid. 1197400e64dfSOhad Ben-Cohen */ 1198160e7c84SOhad Ben-Cohen int rproc_del(struct rproc *rproc) 1199400e64dfSOhad Ben-Cohen { 12006db20ea8SOhad Ben-Cohen struct rproc_vdev *rvdev, *tmp; 12017a186941SOhad Ben-Cohen 1202400e64dfSOhad Ben-Cohen if (!rproc) 1203400e64dfSOhad Ben-Cohen return -EINVAL; 1204400e64dfSOhad Ben-Cohen 1205400e64dfSOhad Ben-Cohen /* if rproc is just being registered, wait */ 1206400e64dfSOhad Ben-Cohen wait_for_completion(&rproc->firmware_loading_complete); 1207400e64dfSOhad Ben-Cohen 12087a186941SOhad Ben-Cohen /* clean up remote vdev entries */ 12096db20ea8SOhad Ben-Cohen list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node) 12107a186941SOhad Ben-Cohen rproc_remove_virtio_dev(rvdev); 1211400e64dfSOhad Ben-Cohen 1212b5ab5e24SOhad Ben-Cohen device_del(&rproc->dev); 1213b5ab5e24SOhad Ben-Cohen 1214400e64dfSOhad Ben-Cohen return 0; 1215400e64dfSOhad Ben-Cohen } 1216160e7c84SOhad Ben-Cohen EXPORT_SYMBOL(rproc_del); 1217400e64dfSOhad Ben-Cohen 1218400e64dfSOhad Ben-Cohen static int __init remoteproc_init(void) 1219400e64dfSOhad Ben-Cohen { 1220400e64dfSOhad Ben-Cohen rproc_init_debugfs(); 1221b5ab5e24SOhad Ben-Cohen 1222400e64dfSOhad Ben-Cohen return 0; 1223400e64dfSOhad Ben-Cohen } 1224400e64dfSOhad Ben-Cohen module_init(remoteproc_init); 1225400e64dfSOhad Ben-Cohen 1226400e64dfSOhad Ben-Cohen static void __exit remoteproc_exit(void) 1227400e64dfSOhad Ben-Cohen { 1228400e64dfSOhad Ben-Cohen rproc_exit_debugfs(); 1229400e64dfSOhad Ben-Cohen } 1230400e64dfSOhad Ben-Cohen module_exit(remoteproc_exit); 1231400e64dfSOhad Ben-Cohen 1232400e64dfSOhad Ben-Cohen MODULE_LICENSE("GPL v2"); 1233400e64dfSOhad Ben-Cohen MODULE_DESCRIPTION("Generic Remote Processor Framework"); 1234