18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Remote Processor Framework 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc. 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Google, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Ohad Ben-Cohen <ohad@wizery.com> 98c2ecf20Sopenharmony_ci * Brian Swetland <swetland@google.com> 108c2ecf20Sopenharmony_ci * Mark Grosen <mgrosen@ti.com> 118c2ecf20Sopenharmony_ci * Fernando Guzman Lugo <fernando.lugo@ti.com> 128c2ecf20Sopenharmony_ci * Suman Anna <s-anna@ti.com> 138c2ecf20Sopenharmony_ci * Robert Tivy <rtivy@ti.com> 148c2ecf20Sopenharmony_ci * Armando Uribe De Leon <x0095078@ti.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/device.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/mutex.h> 258c2ecf20Sopenharmony_ci#include <linux/dma-map-ops.h> 268c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 278c2ecf20Sopenharmony_ci#include <linux/dma-direct.h> /* XXX: pokes into bus_dma_range */ 288c2ecf20Sopenharmony_ci#include <linux/firmware.h> 298c2ecf20Sopenharmony_ci#include <linux/string.h> 308c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 318c2ecf20Sopenharmony_ci#include <linux/rculist.h> 328c2ecf20Sopenharmony_ci#include <linux/remoteproc.h> 338c2ecf20Sopenharmony_ci#include <linux/iommu.h> 348c2ecf20Sopenharmony_ci#include <linux/idr.h> 358c2ecf20Sopenharmony_ci#include <linux/elf.h> 368c2ecf20Sopenharmony_ci#include <linux/crc32.h> 378c2ecf20Sopenharmony_ci#include <linux/of_reserved_mem.h> 388c2ecf20Sopenharmony_ci#include <linux/virtio_ids.h> 398c2ecf20Sopenharmony_ci#include <linux/virtio_ring.h> 408c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 418c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include "remoteproc_internal.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(rproc_list_mutex); 488c2ecf20Sopenharmony_cistatic LIST_HEAD(rproc_list); 498c2ecf20Sopenharmony_cistatic struct notifier_block rproc_panic_nb; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_citypedef int (*rproc_handle_resource_t)(struct rproc *rproc, 528c2ecf20Sopenharmony_ci void *, int offset, int avail); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int rproc_alloc_carveout(struct rproc *rproc, 558c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem); 568c2ecf20Sopenharmony_cistatic int rproc_release_carveout(struct rproc *rproc, 578c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Unique indices for remoteproc devices */ 608c2ecf20Sopenharmony_cistatic DEFINE_IDA(rproc_dev_index); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const char * const rproc_crash_names[] = { 638c2ecf20Sopenharmony_ci [RPROC_MMUFAULT] = "mmufault", 648c2ecf20Sopenharmony_ci [RPROC_WATCHDOG] = "watchdog", 658c2ecf20Sopenharmony_ci [RPROC_FATAL_ERROR] = "fatal error", 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* translate rproc_crash_type to string */ 698c2ecf20Sopenharmony_cistatic const char *rproc_crash_to_string(enum rproc_crash_type type) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci if (type < ARRAY_SIZE(rproc_crash_names)) 728c2ecf20Sopenharmony_ci return rproc_crash_names[type]; 738c2ecf20Sopenharmony_ci return "unknown"; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* 778c2ecf20Sopenharmony_ci * This is the IOMMU fault handler we register with the IOMMU API 788c2ecf20Sopenharmony_ci * (when relevant; not all remote processors access memory through 798c2ecf20Sopenharmony_ci * an IOMMU). 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * IOMMU core will invoke this handler whenever the remote processor 828c2ecf20Sopenharmony_ci * will try to access an unmapped device address. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev, 858c2ecf20Sopenharmony_ci unsigned long iova, int flags, void *token) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct rproc *rproc = token; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci rproc_report_crash(rproc, RPROC_MMUFAULT); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Let the iommu core know we're not really handling this fault; 958c2ecf20Sopenharmony_ci * we just used it as a recovery trigger. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci return -ENOSYS; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int rproc_enable_iommu(struct rproc *rproc) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct iommu_domain *domain; 1038c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!rproc->has_iommu) { 1078c2ecf20Sopenharmony_ci dev_dbg(dev, "iommu not present\n"); 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci domain = iommu_domain_alloc(dev->bus); 1128c2ecf20Sopenharmony_ci if (!domain) { 1138c2ecf20Sopenharmony_ci dev_err(dev, "can't alloc iommu domain\n"); 1148c2ecf20Sopenharmony_ci return -ENOMEM; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci iommu_set_fault_handler(domain, rproc_iommu_fault, rproc); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ret = iommu_attach_device(domain, dev); 1208c2ecf20Sopenharmony_ci if (ret) { 1218c2ecf20Sopenharmony_ci dev_err(dev, "can't attach iommu device: %d\n", ret); 1228c2ecf20Sopenharmony_ci goto free_domain; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci rproc->domain = domain; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cifree_domain: 1308c2ecf20Sopenharmony_ci iommu_domain_free(domain); 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void rproc_disable_iommu(struct rproc *rproc) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct iommu_domain *domain = rproc->domain; 1378c2ecf20Sopenharmony_ci struct device *dev = rproc->dev.parent; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!domain) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci iommu_detach_device(domain, dev); 1438c2ecf20Sopenharmony_ci iommu_domain_free(domain); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ciphys_addr_t rproc_va_to_pa(void *cpu_addr) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * Return physical address according to virtual address location 1508c2ecf20Sopenharmony_ci * - in vmalloc: if region ioremapped or defined as dma_alloc_coherent 1518c2ecf20Sopenharmony_ci * - in kernel: if region allocated in generic dma memory pool 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci if (is_vmalloc_addr(cpu_addr)) { 1548c2ecf20Sopenharmony_ci return page_to_phys(vmalloc_to_page(cpu_addr)) + 1558c2ecf20Sopenharmony_ci offset_in_page(cpu_addr); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci WARN_ON(!virt_addr_valid(cpu_addr)); 1598c2ecf20Sopenharmony_ci return virt_to_phys(cpu_addr); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_va_to_pa); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/** 1648c2ecf20Sopenharmony_ci * rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address 1658c2ecf20Sopenharmony_ci * @rproc: handle of a remote processor 1668c2ecf20Sopenharmony_ci * @da: remoteproc device address to translate 1678c2ecf20Sopenharmony_ci * @len: length of the memory region @da is pointing to 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * Some remote processors will ask us to allocate them physically contiguous 1708c2ecf20Sopenharmony_ci * memory regions (which we call "carveouts"), and map them to specific 1718c2ecf20Sopenharmony_ci * device addresses (which are hardcoded in the firmware). They may also have 1728c2ecf20Sopenharmony_ci * dedicated memory regions internal to the processors, and use them either 1738c2ecf20Sopenharmony_ci * exclusively or alongside carveouts. 1748c2ecf20Sopenharmony_ci * 1758c2ecf20Sopenharmony_ci * They may then ask us to copy objects into specific device addresses (e.g. 1768c2ecf20Sopenharmony_ci * code/data sections) or expose us certain symbols in other device address 1778c2ecf20Sopenharmony_ci * (e.g. their trace buffer). 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * This function is a helper function with which we can go over the allocated 1808c2ecf20Sopenharmony_ci * carveouts and translate specific device addresses to kernel virtual addresses 1818c2ecf20Sopenharmony_ci * so we can access the referenced memory. This function also allows to perform 1828c2ecf20Sopenharmony_ci * translations on the internal remoteproc memory regions through a platform 1838c2ecf20Sopenharmony_ci * implementation specific da_to_va ops, if present. 1848c2ecf20Sopenharmony_ci * 1858c2ecf20Sopenharmony_ci * The function returns a valid kernel address on success or NULL on failure. 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too, 1888c2ecf20Sopenharmony_ci * but only on kernel direct mapped RAM memory. Instead, we're just using 1898c2ecf20Sopenharmony_ci * here the output of the DMA API for the carveouts, which should be more 1908c2ecf20Sopenharmony_ci * correct. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_civoid *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct rproc_mem_entry *carveout; 1958c2ecf20Sopenharmony_ci void *ptr = NULL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (rproc->ops->da_to_va) { 1988c2ecf20Sopenharmony_ci ptr = rproc->ops->da_to_va(rproc, da, len); 1998c2ecf20Sopenharmony_ci if (ptr) 2008c2ecf20Sopenharmony_ci goto out; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci list_for_each_entry(carveout, &rproc->carveouts, node) { 2048c2ecf20Sopenharmony_ci int offset = da - carveout->da; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Verify that carveout is allocated */ 2078c2ecf20Sopenharmony_ci if (!carveout->va) 2088c2ecf20Sopenharmony_ci continue; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* try next carveout if da is too small */ 2118c2ecf20Sopenharmony_ci if (offset < 0) 2128c2ecf20Sopenharmony_ci continue; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* try next carveout if da is too large */ 2158c2ecf20Sopenharmony_ci if (offset + len > carveout->len) 2168c2ecf20Sopenharmony_ci continue; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ptr = carveout->va + offset; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ciout: 2248c2ecf20Sopenharmony_ci return ptr; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_da_to_va); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/** 2298c2ecf20Sopenharmony_ci * rproc_find_carveout_by_name() - lookup the carveout region by a name 2308c2ecf20Sopenharmony_ci * @rproc: handle of a remote processor 2318c2ecf20Sopenharmony_ci * @name: carveout name to find (format string) 2328c2ecf20Sopenharmony_ci * @...: optional parameters matching @name string 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * Platform driver has the capability to register some pre-allacoted carveout 2358c2ecf20Sopenharmony_ci * (physically contiguous memory regions) before rproc firmware loading and 2368c2ecf20Sopenharmony_ci * associated resource table analysis. These regions may be dedicated memory 2378c2ecf20Sopenharmony_ci * regions internal to the coprocessor or specified DDR region with specific 2388c2ecf20Sopenharmony_ci * attributes 2398c2ecf20Sopenharmony_ci * 2408c2ecf20Sopenharmony_ci * This function is a helper function with which we can go over the 2418c2ecf20Sopenharmony_ci * allocated carveouts and return associated region characteristics like 2428c2ecf20Sopenharmony_ci * coprocessor address, length or processor virtual address. 2438c2ecf20Sopenharmony_ci * 2448c2ecf20Sopenharmony_ci * Return: a valid pointer on carveout entry on success or NULL on failure. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci__printf(2, 3) 2478c2ecf20Sopenharmony_cistruct rproc_mem_entry * 2488c2ecf20Sopenharmony_cirproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci va_list args; 2518c2ecf20Sopenharmony_ci char _name[32]; 2528c2ecf20Sopenharmony_ci struct rproc_mem_entry *carveout, *mem = NULL; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!name) 2558c2ecf20Sopenharmony_ci return NULL; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci va_start(args, name); 2588c2ecf20Sopenharmony_ci vsnprintf(_name, sizeof(_name), name, args); 2598c2ecf20Sopenharmony_ci va_end(args); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci list_for_each_entry(carveout, &rproc->carveouts, node) { 2628c2ecf20Sopenharmony_ci /* Compare carveout and requested names */ 2638c2ecf20Sopenharmony_ci if (!strcmp(carveout->name, _name)) { 2648c2ecf20Sopenharmony_ci mem = carveout; 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return mem; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/** 2738c2ecf20Sopenharmony_ci * rproc_check_carveout_da() - Check specified carveout da configuration 2748c2ecf20Sopenharmony_ci * @rproc: handle of a remote processor 2758c2ecf20Sopenharmony_ci * @mem: pointer on carveout to check 2768c2ecf20Sopenharmony_ci * @da: area device address 2778c2ecf20Sopenharmony_ci * @len: associated area size 2788c2ecf20Sopenharmony_ci * 2798c2ecf20Sopenharmony_ci * This function is a helper function to verify requested device area (couple 2808c2ecf20Sopenharmony_ci * da, len) is part of specified carveout. 2818c2ecf20Sopenharmony_ci * If da is not set (defined as FW_RSC_ADDR_ANY), only requested length is 2828c2ecf20Sopenharmony_ci * checked. 2838c2ecf20Sopenharmony_ci * 2848c2ecf20Sopenharmony_ci * Return: 0 if carveout matches request else error 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cistatic int rproc_check_carveout_da(struct rproc *rproc, 2878c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem, u32 da, u32 len) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 2908c2ecf20Sopenharmony_ci int delta; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Check requested resource length */ 2938c2ecf20Sopenharmony_ci if (len > mem->len) { 2948c2ecf20Sopenharmony_ci dev_err(dev, "Registered carveout doesn't fit len request\n"); 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) { 2998c2ecf20Sopenharmony_ci /* Address doesn't match registered carveout configuration */ 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci } else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) { 3028c2ecf20Sopenharmony_ci delta = da - mem->da; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Check requested resource belongs to registered carveout */ 3058c2ecf20Sopenharmony_ci if (delta < 0) { 3068c2ecf20Sopenharmony_ci dev_err(dev, 3078c2ecf20Sopenharmony_ci "Registered carveout doesn't fit da request\n"); 3088c2ecf20Sopenharmony_ci return -EINVAL; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (delta + len > mem->len) { 3128c2ecf20Sopenharmony_ci dev_err(dev, 3138c2ecf20Sopenharmony_ci "Registered carveout doesn't fit len request\n"); 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ciint rproc_alloc_vring(struct rproc_vdev *rvdev, int i) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct rproc *rproc = rvdev->rproc; 3248c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 3258c2ecf20Sopenharmony_ci struct rproc_vring *rvring = &rvdev->vring[i]; 3268c2ecf20Sopenharmony_ci struct fw_rsc_vdev *rsc; 3278c2ecf20Sopenharmony_ci int ret, notifyid; 3288c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem; 3298c2ecf20Sopenharmony_ci size_t size; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* actual size of vring (in bytes) */ 3328c2ecf20Sopenharmony_ci size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Search for pre-registered carveout */ 3378c2ecf20Sopenharmony_ci mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index, 3388c2ecf20Sopenharmony_ci i); 3398c2ecf20Sopenharmony_ci if (mem) { 3408c2ecf20Sopenharmony_ci if (rproc_check_carveout_da(rproc, mem, rsc->vring[i].da, size)) 3418c2ecf20Sopenharmony_ci return -ENOMEM; 3428c2ecf20Sopenharmony_ci } else { 3438c2ecf20Sopenharmony_ci /* Register carveout in in list */ 3448c2ecf20Sopenharmony_ci mem = rproc_mem_entry_init(dev, NULL, 0, 3458c2ecf20Sopenharmony_ci size, rsc->vring[i].da, 3468c2ecf20Sopenharmony_ci rproc_alloc_carveout, 3478c2ecf20Sopenharmony_ci rproc_release_carveout, 3488c2ecf20Sopenharmony_ci "vdev%dvring%d", 3498c2ecf20Sopenharmony_ci rvdev->index, i); 3508c2ecf20Sopenharmony_ci if (!mem) { 3518c2ecf20Sopenharmony_ci dev_err(dev, "Can't allocate memory entry structure\n"); 3528c2ecf20Sopenharmony_ci return -ENOMEM; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci rproc_add_carveout(rproc, mem); 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* 3598c2ecf20Sopenharmony_ci * Assign an rproc-wide unique index for this vring 3608c2ecf20Sopenharmony_ci * TODO: assign a notifyid for rvdev updates as well 3618c2ecf20Sopenharmony_ci * TODO: support predefined notifyids (via resource table) 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL); 3648c2ecf20Sopenharmony_ci if (ret < 0) { 3658c2ecf20Sopenharmony_ci dev_err(dev, "idr_alloc failed: %d\n", ret); 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci notifyid = ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Potentially bump max_notifyid */ 3718c2ecf20Sopenharmony_ci if (notifyid > rproc->max_notifyid) 3728c2ecf20Sopenharmony_ci rproc->max_notifyid = notifyid; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci rvring->notifyid = notifyid; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Let the rproc know the notifyid of this vring.*/ 3778c2ecf20Sopenharmony_ci rsc->vring[i].notifyid = notifyid; 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int 3828c2ecf20Sopenharmony_cirproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct rproc *rproc = rvdev->rproc; 3858c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 3868c2ecf20Sopenharmony_ci struct fw_rsc_vdev_vring *vring = &rsc->vring[i]; 3878c2ecf20Sopenharmony_ci struct rproc_vring *rvring = &rvdev->vring[i]; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci dev_dbg(dev, "vdev rsc: vring%d: da 0x%x, qsz %d, align %d\n", 3908c2ecf20Sopenharmony_ci i, vring->da, vring->num, vring->align); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* verify queue size and vring alignment are sane */ 3938c2ecf20Sopenharmony_ci if (!vring->num || !vring->align) { 3948c2ecf20Sopenharmony_ci dev_err(dev, "invalid qsz (%d) or alignment (%d)\n", 3958c2ecf20Sopenharmony_ci vring->num, vring->align); 3968c2ecf20Sopenharmony_ci return -EINVAL; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci rvring->len = vring->num; 4008c2ecf20Sopenharmony_ci rvring->align = vring->align; 4018c2ecf20Sopenharmony_ci rvring->rvdev = rvdev; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_civoid rproc_free_vring(struct rproc_vring *rvring) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct rproc *rproc = rvring->rvdev->rproc; 4098c2ecf20Sopenharmony_ci int idx = rvring - rvring->rvdev->vring; 4108c2ecf20Sopenharmony_ci struct fw_rsc_vdev *rsc; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci idr_remove(&rproc->notifyids, rvring->notifyid); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * At this point rproc_stop() has been called and the installed resource 4168c2ecf20Sopenharmony_ci * table in the remote processor memory may no longer be accessible. As 4178c2ecf20Sopenharmony_ci * such and as per rproc_stop(), rproc->table_ptr points to the cached 4188c2ecf20Sopenharmony_ci * resource table (rproc->cached_table). The cached resource table is 4198c2ecf20Sopenharmony_ci * only available when a remote processor has been booted by the 4208c2ecf20Sopenharmony_ci * remoteproc core, otherwise it is NULL. 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * Based on the above, reset the virtio device section in the cached 4238c2ecf20Sopenharmony_ci * resource table only if there is one to work with. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci if (rproc->table_ptr) { 4268c2ecf20Sopenharmony_ci rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset; 4278c2ecf20Sopenharmony_ci rsc->vring[idx].da = 0; 4288c2ecf20Sopenharmony_ci rsc->vring[idx].notifyid = -1; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int rproc_vdev_do_start(struct rproc_subdev *subdev) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return rproc_add_virtio_dev(rvdev, rvdev->id); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); 4428c2ecf20Sopenharmony_ci int ret; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ret = device_for_each_child(&rvdev->dev, NULL, rproc_remove_virtio_dev); 4458c2ecf20Sopenharmony_ci if (ret) 4468c2ecf20Sopenharmony_ci dev_warn(&rvdev->dev, "can't remove vdev child device: %d\n", ret); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/** 4508c2ecf20Sopenharmony_ci * rproc_rvdev_release() - release the existence of a rvdev 4518c2ecf20Sopenharmony_ci * 4528c2ecf20Sopenharmony_ci * @dev: the subdevice's dev 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic void rproc_rvdev_release(struct device *dev) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci of_reserved_mem_device_release(dev); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci kfree(rvdev); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int copy_dma_range_map(struct device *to, struct device *from) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci const struct bus_dma_region *map = from->dma_range_map, *new_map, *r; 4668c2ecf20Sopenharmony_ci int num_ranges = 0; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (!map) 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci for (r = map; r->size; r++) 4728c2ecf20Sopenharmony_ci num_ranges++; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)), 4758c2ecf20Sopenharmony_ci GFP_KERNEL); 4768c2ecf20Sopenharmony_ci if (!new_map) 4778c2ecf20Sopenharmony_ci return -ENOMEM; 4788c2ecf20Sopenharmony_ci to->dma_range_map = new_map; 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci/** 4838c2ecf20Sopenharmony_ci * rproc_handle_vdev() - handle a vdev fw resource 4848c2ecf20Sopenharmony_ci * @rproc: the remote processor 4858c2ecf20Sopenharmony_ci * @rsc: the vring resource descriptor 4868c2ecf20Sopenharmony_ci * @offset: offset of the resource entry 4878c2ecf20Sopenharmony_ci * @avail: size of available data (for sanity checking the image) 4888c2ecf20Sopenharmony_ci * 4898c2ecf20Sopenharmony_ci * This resource entry requests the host to statically register a virtio 4908c2ecf20Sopenharmony_ci * device (vdev), and setup everything needed to support it. It contains 4918c2ecf20Sopenharmony_ci * everything needed to make it possible: the virtio device id, virtio 4928c2ecf20Sopenharmony_ci * device features, vrings information, virtio config space, etc... 4938c2ecf20Sopenharmony_ci * 4948c2ecf20Sopenharmony_ci * Before registering the vdev, the vrings are allocated from non-cacheable 4958c2ecf20Sopenharmony_ci * physically contiguous memory. Currently we only support two vrings per 4968c2ecf20Sopenharmony_ci * remote processor (temporary limitation). We might also want to consider 4978c2ecf20Sopenharmony_ci * doing the vring allocation only later when ->find_vqs() is invoked, and 4988c2ecf20Sopenharmony_ci * then release them upon ->del_vqs(). 4998c2ecf20Sopenharmony_ci * 5008c2ecf20Sopenharmony_ci * Note: @da is currently not really handled correctly: we dynamically 5018c2ecf20Sopenharmony_ci * allocate it using the DMA API, ignoring requested hard coded addresses, 5028c2ecf20Sopenharmony_ci * and we don't take care of any required IOMMU programming. This is all 5038c2ecf20Sopenharmony_ci * going to be taken care of when the generic iommu-based DMA API will be 5048c2ecf20Sopenharmony_ci * merged. Meanwhile, statically-addressed iommu-based firmware images should 5058c2ecf20Sopenharmony_ci * use RSC_DEVMEM resource entries to map their required @da to the physical 5068c2ecf20Sopenharmony_ci * address of their base CMA region (ouch, hacky!). 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * Returns 0 on success, or an appropriate error code otherwise 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_cistatic int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, 5118c2ecf20Sopenharmony_ci int offset, int avail) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 5148c2ecf20Sopenharmony_ci struct rproc_vdev *rvdev; 5158c2ecf20Sopenharmony_ci int i, ret; 5168c2ecf20Sopenharmony_ci char name[16]; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* make sure resource isn't truncated */ 5198c2ecf20Sopenharmony_ci if (struct_size(rsc, vring, rsc->num_of_vrings) + rsc->config_len > 5208c2ecf20Sopenharmony_ci avail) { 5218c2ecf20Sopenharmony_ci dev_err(dev, "vdev rsc is truncated\n"); 5228c2ecf20Sopenharmony_ci return -EINVAL; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* make sure reserved bytes are zeroes */ 5268c2ecf20Sopenharmony_ci if (rsc->reserved[0] || rsc->reserved[1]) { 5278c2ecf20Sopenharmony_ci dev_err(dev, "vdev rsc has non zero reserved bytes\n"); 5288c2ecf20Sopenharmony_ci return -EINVAL; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci dev_dbg(dev, "vdev rsc: id %d, dfeatures 0x%x, cfg len %d, %d vrings\n", 5328c2ecf20Sopenharmony_ci rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* we currently support only two vrings per rvdev */ 5358c2ecf20Sopenharmony_ci if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) { 5368c2ecf20Sopenharmony_ci dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings); 5378c2ecf20Sopenharmony_ci return -EINVAL; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci rvdev = kzalloc(sizeof(*rvdev), GFP_KERNEL); 5418c2ecf20Sopenharmony_ci if (!rvdev) 5428c2ecf20Sopenharmony_ci return -ENOMEM; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci kref_init(&rvdev->refcount); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci rvdev->id = rsc->id; 5478c2ecf20Sopenharmony_ci rvdev->rproc = rproc; 5488c2ecf20Sopenharmony_ci rvdev->index = rproc->nb_vdev++; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Initialise vdev subdevice */ 5518c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); 5528c2ecf20Sopenharmony_ci rvdev->dev.parent = &rproc->dev; 5538c2ecf20Sopenharmony_ci rvdev->dev.release = rproc_rvdev_release; 5548c2ecf20Sopenharmony_ci dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); 5558c2ecf20Sopenharmony_ci dev_set_drvdata(&rvdev->dev, rvdev); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ret = device_register(&rvdev->dev); 5588c2ecf20Sopenharmony_ci if (ret) { 5598c2ecf20Sopenharmony_ci put_device(&rvdev->dev); 5608c2ecf20Sopenharmony_ci return ret; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ret = copy_dma_range_map(&rvdev->dev, rproc->dev.parent); 5648c2ecf20Sopenharmony_ci if (ret) 5658c2ecf20Sopenharmony_ci goto free_rvdev; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Make device dma capable by inheriting from parent's capabilities */ 5688c2ecf20Sopenharmony_ci set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent)); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci ret = dma_coerce_mask_and_coherent(&rvdev->dev, 5718c2ecf20Sopenharmony_ci dma_get_mask(rproc->dev.parent)); 5728c2ecf20Sopenharmony_ci if (ret) { 5738c2ecf20Sopenharmony_ci dev_warn(dev, 5748c2ecf20Sopenharmony_ci "Failed to set DMA mask %llx. Trying to continue... %x\n", 5758c2ecf20Sopenharmony_ci dma_get_mask(rproc->dev.parent), ret); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* parse the vrings */ 5798c2ecf20Sopenharmony_ci for (i = 0; i < rsc->num_of_vrings; i++) { 5808c2ecf20Sopenharmony_ci ret = rproc_parse_vring(rvdev, rsc, i); 5818c2ecf20Sopenharmony_ci if (ret) 5828c2ecf20Sopenharmony_ci goto free_rvdev; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* remember the resource offset*/ 5868c2ecf20Sopenharmony_ci rvdev->rsc_offset = offset; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* allocate the vring resources */ 5898c2ecf20Sopenharmony_ci for (i = 0; i < rsc->num_of_vrings; i++) { 5908c2ecf20Sopenharmony_ci ret = rproc_alloc_vring(rvdev, i); 5918c2ecf20Sopenharmony_ci if (ret) 5928c2ecf20Sopenharmony_ci goto unwind_vring_allocations; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci list_add_tail(&rvdev->node, &rproc->rvdevs); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci rvdev->subdev.start = rproc_vdev_do_start; 5988c2ecf20Sopenharmony_ci rvdev->subdev.stop = rproc_vdev_do_stop; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci rproc_add_subdev(rproc, &rvdev->subdev); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci return 0; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ciunwind_vring_allocations: 6058c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) 6068c2ecf20Sopenharmony_ci rproc_free_vring(&rvdev->vring[i]); 6078c2ecf20Sopenharmony_cifree_rvdev: 6088c2ecf20Sopenharmony_ci device_unregister(&rvdev->dev); 6098c2ecf20Sopenharmony_ci return ret; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_civoid rproc_vdev_release(struct kref *ref) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci struct rproc_vdev *rvdev = container_of(ref, struct rproc_vdev, refcount); 6158c2ecf20Sopenharmony_ci struct rproc_vring *rvring; 6168c2ecf20Sopenharmony_ci struct rproc *rproc = rvdev->rproc; 6178c2ecf20Sopenharmony_ci int id; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) { 6208c2ecf20Sopenharmony_ci rvring = &rvdev->vring[id]; 6218c2ecf20Sopenharmony_ci rproc_free_vring(rvring); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci rproc_remove_subdev(rproc, &rvdev->subdev); 6258c2ecf20Sopenharmony_ci list_del(&rvdev->node); 6268c2ecf20Sopenharmony_ci device_unregister(&rvdev->dev); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/** 6308c2ecf20Sopenharmony_ci * rproc_handle_trace() - handle a shared trace buffer resource 6318c2ecf20Sopenharmony_ci * @rproc: the remote processor 6328c2ecf20Sopenharmony_ci * @rsc: the trace resource descriptor 6338c2ecf20Sopenharmony_ci * @offset: offset of the resource entry 6348c2ecf20Sopenharmony_ci * @avail: size of available data (for sanity checking the image) 6358c2ecf20Sopenharmony_ci * 6368c2ecf20Sopenharmony_ci * In case the remote processor dumps trace logs into memory, 6378c2ecf20Sopenharmony_ci * export it via debugfs. 6388c2ecf20Sopenharmony_ci * 6398c2ecf20Sopenharmony_ci * Currently, the 'da' member of @rsc should contain the device address 6408c2ecf20Sopenharmony_ci * where the remote processor is dumping the traces. Later we could also 6418c2ecf20Sopenharmony_ci * support dynamically allocating this address using the generic 6428c2ecf20Sopenharmony_ci * DMA API (but currently there isn't a use case for that). 6438c2ecf20Sopenharmony_ci * 6448c2ecf20Sopenharmony_ci * Returns 0 on success, or an appropriate error code otherwise 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_cistatic int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, 6478c2ecf20Sopenharmony_ci int offset, int avail) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct rproc_debug_trace *trace; 6508c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 6518c2ecf20Sopenharmony_ci char name[15]; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (sizeof(*rsc) > avail) { 6548c2ecf20Sopenharmony_ci dev_err(dev, "trace rsc is truncated\n"); 6558c2ecf20Sopenharmony_ci return -EINVAL; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* make sure reserved bytes are zeroes */ 6598c2ecf20Sopenharmony_ci if (rsc->reserved) { 6608c2ecf20Sopenharmony_ci dev_err(dev, "trace rsc has non zero reserved bytes\n"); 6618c2ecf20Sopenharmony_ci return -EINVAL; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci trace = kzalloc(sizeof(*trace), GFP_KERNEL); 6658c2ecf20Sopenharmony_ci if (!trace) 6668c2ecf20Sopenharmony_ci return -ENOMEM; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* set the trace buffer dma properties */ 6698c2ecf20Sopenharmony_ci trace->trace_mem.len = rsc->len; 6708c2ecf20Sopenharmony_ci trace->trace_mem.da = rsc->da; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* set pointer on rproc device */ 6738c2ecf20Sopenharmony_ci trace->rproc = rproc; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* make sure snprintf always null terminates, even if truncating */ 6768c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "trace%d", rproc->num_traces); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* create the debugfs entry */ 6798c2ecf20Sopenharmony_ci trace->tfile = rproc_create_trace_file(name, rproc, trace); 6808c2ecf20Sopenharmony_ci if (!trace->tfile) { 6818c2ecf20Sopenharmony_ci kfree(trace); 6828c2ecf20Sopenharmony_ci return -EINVAL; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci list_add_tail(&trace->node, &rproc->traces); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci rproc->num_traces++; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci dev_dbg(dev, "%s added: da 0x%x, len 0x%x\n", 6908c2ecf20Sopenharmony_ci name, rsc->da, rsc->len); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci/** 6968c2ecf20Sopenharmony_ci * rproc_handle_devmem() - handle devmem resource entry 6978c2ecf20Sopenharmony_ci * @rproc: remote processor handle 6988c2ecf20Sopenharmony_ci * @rsc: the devmem resource entry 6998c2ecf20Sopenharmony_ci * @offset: offset of the resource entry 7008c2ecf20Sopenharmony_ci * @avail: size of available data (for sanity checking the image) 7018c2ecf20Sopenharmony_ci * 7028c2ecf20Sopenharmony_ci * Remote processors commonly need to access certain on-chip peripherals. 7038c2ecf20Sopenharmony_ci * 7048c2ecf20Sopenharmony_ci * Some of these remote processors access memory via an iommu device, 7058c2ecf20Sopenharmony_ci * and might require us to configure their iommu before they can access 7068c2ecf20Sopenharmony_ci * the on-chip peripherals they need. 7078c2ecf20Sopenharmony_ci * 7088c2ecf20Sopenharmony_ci * This resource entry is a request to map such a peripheral device. 7098c2ecf20Sopenharmony_ci * 7108c2ecf20Sopenharmony_ci * These devmem entries will contain the physical address of the device in 7118c2ecf20Sopenharmony_ci * the 'pa' member. If a specific device address is expected, then 'da' will 7128c2ecf20Sopenharmony_ci * contain it (currently this is the only use case supported). 'len' will 7138c2ecf20Sopenharmony_ci * contain the size of the physical region we need to map. 7148c2ecf20Sopenharmony_ci * 7158c2ecf20Sopenharmony_ci * Currently we just "trust" those devmem entries to contain valid physical 7168c2ecf20Sopenharmony_ci * addresses, but this is going to change: we want the implementations to 7178c2ecf20Sopenharmony_ci * tell us ranges of physical addresses the firmware is allowed to request, 7188c2ecf20Sopenharmony_ci * and not allow firmwares to request access to physical addresses that 7198c2ecf20Sopenharmony_ci * are outside those ranges. 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_cistatic int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, 7228c2ecf20Sopenharmony_ci int offset, int avail) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci struct rproc_mem_entry *mapping; 7258c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 7268c2ecf20Sopenharmony_ci int ret; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* no point in handling this resource without a valid iommu domain */ 7298c2ecf20Sopenharmony_ci if (!rproc->domain) 7308c2ecf20Sopenharmony_ci return -EINVAL; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (sizeof(*rsc) > avail) { 7338c2ecf20Sopenharmony_ci dev_err(dev, "devmem rsc is truncated\n"); 7348c2ecf20Sopenharmony_ci return -EINVAL; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* make sure reserved bytes are zeroes */ 7388c2ecf20Sopenharmony_ci if (rsc->reserved) { 7398c2ecf20Sopenharmony_ci dev_err(dev, "devmem rsc has non zero reserved bytes\n"); 7408c2ecf20Sopenharmony_ci return -EINVAL; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); 7448c2ecf20Sopenharmony_ci if (!mapping) 7458c2ecf20Sopenharmony_ci return -ENOMEM; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags); 7488c2ecf20Sopenharmony_ci if (ret) { 7498c2ecf20Sopenharmony_ci dev_err(dev, "failed to map devmem: %d\n", ret); 7508c2ecf20Sopenharmony_ci goto out; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* 7548c2ecf20Sopenharmony_ci * We'll need this info later when we'll want to unmap everything 7558c2ecf20Sopenharmony_ci * (e.g. on shutdown). 7568c2ecf20Sopenharmony_ci * 7578c2ecf20Sopenharmony_ci * We can't trust the remote processor not to change the resource 7588c2ecf20Sopenharmony_ci * table, so we must maintain this info independently. 7598c2ecf20Sopenharmony_ci */ 7608c2ecf20Sopenharmony_ci mapping->da = rsc->da; 7618c2ecf20Sopenharmony_ci mapping->len = rsc->len; 7628c2ecf20Sopenharmony_ci list_add_tail(&mapping->node, &rproc->mappings); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci dev_dbg(dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n", 7658c2ecf20Sopenharmony_ci rsc->pa, rsc->da, rsc->len); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ciout: 7708c2ecf20Sopenharmony_ci kfree(mapping); 7718c2ecf20Sopenharmony_ci return ret; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/** 7758c2ecf20Sopenharmony_ci * rproc_alloc_carveout() - allocated specified carveout 7768c2ecf20Sopenharmony_ci * @rproc: rproc handle 7778c2ecf20Sopenharmony_ci * @mem: the memory entry to allocate 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * This function allocate specified memory entry @mem using 7808c2ecf20Sopenharmony_ci * dma_alloc_coherent() as default allocator 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_cistatic int rproc_alloc_carveout(struct rproc *rproc, 7838c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct rproc_mem_entry *mapping = NULL; 7868c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 7878c2ecf20Sopenharmony_ci dma_addr_t dma; 7888c2ecf20Sopenharmony_ci void *va; 7898c2ecf20Sopenharmony_ci int ret; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci va = dma_alloc_coherent(dev->parent, mem->len, &dma, GFP_KERNEL); 7928c2ecf20Sopenharmony_ci if (!va) { 7938c2ecf20Sopenharmony_ci dev_err(dev->parent, 7948c2ecf20Sopenharmony_ci "failed to allocate dma memory: len 0x%zx\n", 7958c2ecf20Sopenharmony_ci mem->len); 7968c2ecf20Sopenharmony_ci return -ENOMEM; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%zx\n", 8008c2ecf20Sopenharmony_ci va, &dma, mem->len); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) { 8038c2ecf20Sopenharmony_ci /* 8048c2ecf20Sopenharmony_ci * Check requested da is equal to dma address 8058c2ecf20Sopenharmony_ci * and print a warn message in case of missalignment. 8068c2ecf20Sopenharmony_ci * Don't stop rproc_start sequence as coprocessor may 8078c2ecf20Sopenharmony_ci * build pa to da translation on its side. 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_ci if (mem->da != (u32)dma) 8108c2ecf20Sopenharmony_ci dev_warn(dev->parent, 8118c2ecf20Sopenharmony_ci "Allocated carveout doesn't fit device address request\n"); 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* 8158c2ecf20Sopenharmony_ci * Ok, this is non-standard. 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci * Sometimes we can't rely on the generic iommu-based DMA API 8188c2ecf20Sopenharmony_ci * to dynamically allocate the device address and then set the IOMMU 8198c2ecf20Sopenharmony_ci * tables accordingly, because some remote processors might 8208c2ecf20Sopenharmony_ci * _require_ us to use hard coded device addresses that their 8218c2ecf20Sopenharmony_ci * firmware was compiled with. 8228c2ecf20Sopenharmony_ci * 8238c2ecf20Sopenharmony_ci * In this case, we must use the IOMMU API directly and map 8248c2ecf20Sopenharmony_ci * the memory to the device address as expected by the remote 8258c2ecf20Sopenharmony_ci * processor. 8268c2ecf20Sopenharmony_ci * 8278c2ecf20Sopenharmony_ci * Obviously such remote processor devices should not be configured 8288c2ecf20Sopenharmony_ci * to use the iommu-based DMA API: we expect 'dma' to contain the 8298c2ecf20Sopenharmony_ci * physical address in this case. 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_ci if (mem->da != FW_RSC_ADDR_ANY && rproc->domain) { 8328c2ecf20Sopenharmony_ci mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); 8338c2ecf20Sopenharmony_ci if (!mapping) { 8348c2ecf20Sopenharmony_ci ret = -ENOMEM; 8358c2ecf20Sopenharmony_ci goto dma_free; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci ret = iommu_map(rproc->domain, mem->da, dma, mem->len, 8398c2ecf20Sopenharmony_ci mem->flags); 8408c2ecf20Sopenharmony_ci if (ret) { 8418c2ecf20Sopenharmony_ci dev_err(dev, "iommu_map failed: %d\n", ret); 8428c2ecf20Sopenharmony_ci goto free_mapping; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* 8468c2ecf20Sopenharmony_ci * We'll need this info later when we'll want to unmap 8478c2ecf20Sopenharmony_ci * everything (e.g. on shutdown). 8488c2ecf20Sopenharmony_ci * 8498c2ecf20Sopenharmony_ci * We can't trust the remote processor not to change the 8508c2ecf20Sopenharmony_ci * resource table, so we must maintain this info independently. 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ci mapping->da = mem->da; 8538c2ecf20Sopenharmony_ci mapping->len = mem->len; 8548c2ecf20Sopenharmony_ci list_add_tail(&mapping->node, &rproc->mappings); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci dev_dbg(dev, "carveout mapped 0x%x to %pad\n", 8578c2ecf20Sopenharmony_ci mem->da, &dma); 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (mem->da == FW_RSC_ADDR_ANY) { 8618c2ecf20Sopenharmony_ci /* Update device address as undefined by requester */ 8628c2ecf20Sopenharmony_ci if ((u64)dma & HIGH_BITS_MASK) 8638c2ecf20Sopenharmony_ci dev_warn(dev, "DMA address cast in 32bit to fit resource table format\n"); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci mem->da = (u32)dma; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci mem->dma = dma; 8698c2ecf20Sopenharmony_ci mem->va = va; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci return 0; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cifree_mapping: 8748c2ecf20Sopenharmony_ci kfree(mapping); 8758c2ecf20Sopenharmony_cidma_free: 8768c2ecf20Sopenharmony_ci dma_free_coherent(dev->parent, mem->len, va, dma); 8778c2ecf20Sopenharmony_ci return ret; 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci/** 8818c2ecf20Sopenharmony_ci * rproc_release_carveout() - release acquired carveout 8828c2ecf20Sopenharmony_ci * @rproc: rproc handle 8838c2ecf20Sopenharmony_ci * @mem: the memory entry to release 8848c2ecf20Sopenharmony_ci * 8858c2ecf20Sopenharmony_ci * This function releases specified memory entry @mem allocated via 8868c2ecf20Sopenharmony_ci * rproc_alloc_carveout() function by @rproc. 8878c2ecf20Sopenharmony_ci */ 8888c2ecf20Sopenharmony_cistatic int rproc_release_carveout(struct rproc *rproc, 8898c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* clean up carveout allocations */ 8948c2ecf20Sopenharmony_ci dma_free_coherent(dev->parent, mem->len, mem->va, mem->dma); 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/** 8998c2ecf20Sopenharmony_ci * rproc_handle_carveout() - handle phys contig memory allocation requests 9008c2ecf20Sopenharmony_ci * @rproc: rproc handle 9018c2ecf20Sopenharmony_ci * @rsc: the resource entry 9028c2ecf20Sopenharmony_ci * @offset: offset of the resource entry 9038c2ecf20Sopenharmony_ci * @avail: size of available data (for image validation) 9048c2ecf20Sopenharmony_ci * 9058c2ecf20Sopenharmony_ci * This function will handle firmware requests for allocation of physically 9068c2ecf20Sopenharmony_ci * contiguous memory regions. 9078c2ecf20Sopenharmony_ci * 9088c2ecf20Sopenharmony_ci * These request entries should come first in the firmware's resource table, 9098c2ecf20Sopenharmony_ci * as other firmware entries might request placing other data objects inside 9108c2ecf20Sopenharmony_ci * these memory regions (e.g. data/code segments, trace resource entries, ...). 9118c2ecf20Sopenharmony_ci * 9128c2ecf20Sopenharmony_ci * Allocating memory this way helps utilizing the reserved physical memory 9138c2ecf20Sopenharmony_ci * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries 9148c2ecf20Sopenharmony_ci * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB 9158c2ecf20Sopenharmony_ci * pressure is important; it may have a substantial impact on performance. 9168c2ecf20Sopenharmony_ci */ 9178c2ecf20Sopenharmony_cistatic int rproc_handle_carveout(struct rproc *rproc, 9188c2ecf20Sopenharmony_ci struct fw_rsc_carveout *rsc, 9198c2ecf20Sopenharmony_ci int offset, int avail) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct rproc_mem_entry *carveout; 9228c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (sizeof(*rsc) > avail) { 9258c2ecf20Sopenharmony_ci dev_err(dev, "carveout rsc is truncated\n"); 9268c2ecf20Sopenharmony_ci return -EINVAL; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* make sure reserved bytes are zeroes */ 9308c2ecf20Sopenharmony_ci if (rsc->reserved) { 9318c2ecf20Sopenharmony_ci dev_err(dev, "carveout rsc has non zero reserved bytes\n"); 9328c2ecf20Sopenharmony_ci return -EINVAL; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci dev_dbg(dev, "carveout rsc: name: %s, da 0x%x, pa 0x%x, len 0x%x, flags 0x%x\n", 9368c2ecf20Sopenharmony_ci rsc->name, rsc->da, rsc->pa, rsc->len, rsc->flags); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci /* 9398c2ecf20Sopenharmony_ci * Check carveout rsc already part of a registered carveout, 9408c2ecf20Sopenharmony_ci * Search by name, then check the da and length 9418c2ecf20Sopenharmony_ci */ 9428c2ecf20Sopenharmony_ci carveout = rproc_find_carveout_by_name(rproc, rsc->name); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (carveout) { 9458c2ecf20Sopenharmony_ci if (carveout->rsc_offset != FW_RSC_ADDR_ANY) { 9468c2ecf20Sopenharmony_ci dev_err(dev, 9478c2ecf20Sopenharmony_ci "Carveout already associated to resource table\n"); 9488c2ecf20Sopenharmony_ci return -ENOMEM; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (rproc_check_carveout_da(rproc, carveout, rsc->da, rsc->len)) 9528c2ecf20Sopenharmony_ci return -ENOMEM; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci /* Update memory carveout with resource table info */ 9558c2ecf20Sopenharmony_ci carveout->rsc_offset = offset; 9568c2ecf20Sopenharmony_ci carveout->flags = rsc->flags; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci return 0; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* Register carveout in in list */ 9628c2ecf20Sopenharmony_ci carveout = rproc_mem_entry_init(dev, NULL, 0, rsc->len, rsc->da, 9638c2ecf20Sopenharmony_ci rproc_alloc_carveout, 9648c2ecf20Sopenharmony_ci rproc_release_carveout, rsc->name); 9658c2ecf20Sopenharmony_ci if (!carveout) { 9668c2ecf20Sopenharmony_ci dev_err(dev, "Can't allocate memory entry structure\n"); 9678c2ecf20Sopenharmony_ci return -ENOMEM; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci carveout->flags = rsc->flags; 9718c2ecf20Sopenharmony_ci carveout->rsc_offset = offset; 9728c2ecf20Sopenharmony_ci rproc_add_carveout(rproc, carveout); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci return 0; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci/** 9788c2ecf20Sopenharmony_ci * rproc_add_carveout() - register an allocated carveout region 9798c2ecf20Sopenharmony_ci * @rproc: rproc handle 9808c2ecf20Sopenharmony_ci * @mem: memory entry to register 9818c2ecf20Sopenharmony_ci * 9828c2ecf20Sopenharmony_ci * This function registers specified memory entry in @rproc carveouts list. 9838c2ecf20Sopenharmony_ci * Specified carveout should have been allocated before registering. 9848c2ecf20Sopenharmony_ci */ 9858c2ecf20Sopenharmony_civoid rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci list_add_tail(&mem->node, &rproc->carveouts); 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_add_carveout); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci/** 9928c2ecf20Sopenharmony_ci * rproc_mem_entry_init() - allocate and initialize rproc_mem_entry struct 9938c2ecf20Sopenharmony_ci * @dev: pointer on device struct 9948c2ecf20Sopenharmony_ci * @va: virtual address 9958c2ecf20Sopenharmony_ci * @dma: dma address 9968c2ecf20Sopenharmony_ci * @len: memory carveout length 9978c2ecf20Sopenharmony_ci * @da: device address 9988c2ecf20Sopenharmony_ci * @alloc: memory carveout allocation function 9998c2ecf20Sopenharmony_ci * @release: memory carveout release function 10008c2ecf20Sopenharmony_ci * @name: carveout name 10018c2ecf20Sopenharmony_ci * 10028c2ecf20Sopenharmony_ci * This function allocates a rproc_mem_entry struct and fill it with parameters 10038c2ecf20Sopenharmony_ci * provided by client. 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_ci__printf(8, 9) 10068c2ecf20Sopenharmony_cistruct rproc_mem_entry * 10078c2ecf20Sopenharmony_cirproc_mem_entry_init(struct device *dev, 10088c2ecf20Sopenharmony_ci void *va, dma_addr_t dma, size_t len, u32 da, 10098c2ecf20Sopenharmony_ci int (*alloc)(struct rproc *, struct rproc_mem_entry *), 10108c2ecf20Sopenharmony_ci int (*release)(struct rproc *, struct rproc_mem_entry *), 10118c2ecf20Sopenharmony_ci const char *name, ...) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem; 10148c2ecf20Sopenharmony_ci va_list args; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci mem = kzalloc(sizeof(*mem), GFP_KERNEL); 10178c2ecf20Sopenharmony_ci if (!mem) 10188c2ecf20Sopenharmony_ci return mem; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci mem->va = va; 10218c2ecf20Sopenharmony_ci mem->dma = dma; 10228c2ecf20Sopenharmony_ci mem->da = da; 10238c2ecf20Sopenharmony_ci mem->len = len; 10248c2ecf20Sopenharmony_ci mem->alloc = alloc; 10258c2ecf20Sopenharmony_ci mem->release = release; 10268c2ecf20Sopenharmony_ci mem->rsc_offset = FW_RSC_ADDR_ANY; 10278c2ecf20Sopenharmony_ci mem->of_resm_idx = -1; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci va_start(args, name); 10308c2ecf20Sopenharmony_ci vsnprintf(mem->name, sizeof(mem->name), name, args); 10318c2ecf20Sopenharmony_ci va_end(args); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci return mem; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_mem_entry_init); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci/** 10388c2ecf20Sopenharmony_ci * rproc_of_resm_mem_entry_init() - allocate and initialize rproc_mem_entry struct 10398c2ecf20Sopenharmony_ci * from a reserved memory phandle 10408c2ecf20Sopenharmony_ci * @dev: pointer on device struct 10418c2ecf20Sopenharmony_ci * @of_resm_idx: reserved memory phandle index in "memory-region" 10428c2ecf20Sopenharmony_ci * @len: memory carveout length 10438c2ecf20Sopenharmony_ci * @da: device address 10448c2ecf20Sopenharmony_ci * @name: carveout name 10458c2ecf20Sopenharmony_ci * 10468c2ecf20Sopenharmony_ci * This function allocates a rproc_mem_entry struct and fill it with parameters 10478c2ecf20Sopenharmony_ci * provided by client. 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci__printf(5, 6) 10508c2ecf20Sopenharmony_cistruct rproc_mem_entry * 10518c2ecf20Sopenharmony_cirproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len, 10528c2ecf20Sopenharmony_ci u32 da, const char *name, ...) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci struct rproc_mem_entry *mem; 10558c2ecf20Sopenharmony_ci va_list args; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci mem = kzalloc(sizeof(*mem), GFP_KERNEL); 10588c2ecf20Sopenharmony_ci if (!mem) 10598c2ecf20Sopenharmony_ci return mem; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci mem->da = da; 10628c2ecf20Sopenharmony_ci mem->len = len; 10638c2ecf20Sopenharmony_ci mem->rsc_offset = FW_RSC_ADDR_ANY; 10648c2ecf20Sopenharmony_ci mem->of_resm_idx = of_resm_idx; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci va_start(args, name); 10678c2ecf20Sopenharmony_ci vsnprintf(mem->name, sizeof(mem->name), name, args); 10688c2ecf20Sopenharmony_ci va_end(args); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci return mem; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_of_resm_mem_entry_init); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci/** 10758c2ecf20Sopenharmony_ci * rproc_of_parse_firmware() - parse and return the firmware-name 10768c2ecf20Sopenharmony_ci * @dev: pointer on device struct representing a rproc 10778c2ecf20Sopenharmony_ci * @index: index to use for the firmware-name retrieval 10788c2ecf20Sopenharmony_ci * @fw_name: pointer to a character string, in which the firmware 10798c2ecf20Sopenharmony_ci * name is returned on success and unmodified otherwise. 10808c2ecf20Sopenharmony_ci * 10818c2ecf20Sopenharmony_ci * This is an OF helper function that parses a device's DT node for 10828c2ecf20Sopenharmony_ci * the "firmware-name" property and returns the firmware name pointer 10838c2ecf20Sopenharmony_ci * in @fw_name on success. 10848c2ecf20Sopenharmony_ci * 10858c2ecf20Sopenharmony_ci * Return: 0 on success, or an appropriate failure. 10868c2ecf20Sopenharmony_ci */ 10878c2ecf20Sopenharmony_ciint rproc_of_parse_firmware(struct device *dev, int index, const char **fw_name) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci int ret; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci ret = of_property_read_string_index(dev->of_node, "firmware-name", 10928c2ecf20Sopenharmony_ci index, fw_name); 10938c2ecf20Sopenharmony_ci return ret ? ret : 0; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_of_parse_firmware); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci/* 10988c2ecf20Sopenharmony_ci * A lookup table for resource handlers. The indices are defined in 10998c2ecf20Sopenharmony_ci * enum fw_resource_type. 11008c2ecf20Sopenharmony_ci */ 11018c2ecf20Sopenharmony_cistatic rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = { 11028c2ecf20Sopenharmony_ci [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, 11038c2ecf20Sopenharmony_ci [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, 11048c2ecf20Sopenharmony_ci [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, 11058c2ecf20Sopenharmony_ci [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev, 11068c2ecf20Sopenharmony_ci}; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci/* handle firmware resource entries before booting the remote processor */ 11098c2ecf20Sopenharmony_cistatic int rproc_handle_resources(struct rproc *rproc, 11108c2ecf20Sopenharmony_ci rproc_handle_resource_t handlers[RSC_LAST]) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 11138c2ecf20Sopenharmony_ci rproc_handle_resource_t handler; 11148c2ecf20Sopenharmony_ci int ret = 0, i; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (!rproc->table_ptr) 11178c2ecf20Sopenharmony_ci return 0; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci for (i = 0; i < rproc->table_ptr->num; i++) { 11208c2ecf20Sopenharmony_ci int offset = rproc->table_ptr->offset[i]; 11218c2ecf20Sopenharmony_ci struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset; 11228c2ecf20Sopenharmony_ci int avail = rproc->table_sz - offset - sizeof(*hdr); 11238c2ecf20Sopenharmony_ci void *rsc = (void *)hdr + sizeof(*hdr); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* make sure table isn't truncated */ 11268c2ecf20Sopenharmony_ci if (avail < 0) { 11278c2ecf20Sopenharmony_ci dev_err(dev, "rsc table is truncated\n"); 11288c2ecf20Sopenharmony_ci return -EINVAL; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci dev_dbg(dev, "rsc: type %d\n", hdr->type); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if (hdr->type >= RSC_VENDOR_START && 11348c2ecf20Sopenharmony_ci hdr->type <= RSC_VENDOR_END) { 11358c2ecf20Sopenharmony_ci ret = rproc_handle_rsc(rproc, hdr->type, rsc, 11368c2ecf20Sopenharmony_ci offset + sizeof(*hdr), avail); 11378c2ecf20Sopenharmony_ci if (ret == RSC_HANDLED) 11388c2ecf20Sopenharmony_ci continue; 11398c2ecf20Sopenharmony_ci else if (ret < 0) 11408c2ecf20Sopenharmony_ci break; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci dev_warn(dev, "unsupported vendor resource %d\n", 11438c2ecf20Sopenharmony_ci hdr->type); 11448c2ecf20Sopenharmony_ci continue; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (hdr->type >= RSC_LAST) { 11488c2ecf20Sopenharmony_ci dev_warn(dev, "unsupported resource %d\n", hdr->type); 11498c2ecf20Sopenharmony_ci continue; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci handler = handlers[hdr->type]; 11538c2ecf20Sopenharmony_ci if (!handler) 11548c2ecf20Sopenharmony_ci continue; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci ret = handler(rproc, rsc, offset + sizeof(*hdr), avail); 11578c2ecf20Sopenharmony_ci if (ret) 11588c2ecf20Sopenharmony_ci break; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return ret; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_cistatic int rproc_prepare_subdevices(struct rproc *rproc) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci struct rproc_subdev *subdev; 11678c2ecf20Sopenharmony_ci int ret; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci list_for_each_entry(subdev, &rproc->subdevs, node) { 11708c2ecf20Sopenharmony_ci if (subdev->prepare) { 11718c2ecf20Sopenharmony_ci ret = subdev->prepare(subdev); 11728c2ecf20Sopenharmony_ci if (ret) 11738c2ecf20Sopenharmony_ci goto unroll_preparation; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return 0; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ciunroll_preparation: 11808c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) { 11818c2ecf20Sopenharmony_ci if (subdev->unprepare) 11828c2ecf20Sopenharmony_ci subdev->unprepare(subdev); 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return ret; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistatic int rproc_start_subdevices(struct rproc *rproc) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct rproc_subdev *subdev; 11918c2ecf20Sopenharmony_ci int ret; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci list_for_each_entry(subdev, &rproc->subdevs, node) { 11948c2ecf20Sopenharmony_ci if (subdev->start) { 11958c2ecf20Sopenharmony_ci ret = subdev->start(subdev); 11968c2ecf20Sopenharmony_ci if (ret) 11978c2ecf20Sopenharmony_ci goto unroll_registration; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci return 0; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ciunroll_registration: 12048c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) { 12058c2ecf20Sopenharmony_ci if (subdev->stop) 12068c2ecf20Sopenharmony_ci subdev->stop(subdev, true); 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci return ret; 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_cistatic void rproc_stop_subdevices(struct rproc *rproc, bool crashed) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci struct rproc_subdev *subdev; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci list_for_each_entry_reverse(subdev, &rproc->subdevs, node) { 12178c2ecf20Sopenharmony_ci if (subdev->stop) 12188c2ecf20Sopenharmony_ci subdev->stop(subdev, crashed); 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic void rproc_unprepare_subdevices(struct rproc *rproc) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci struct rproc_subdev *subdev; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci list_for_each_entry_reverse(subdev, &rproc->subdevs, node) { 12278c2ecf20Sopenharmony_ci if (subdev->unprepare) 12288c2ecf20Sopenharmony_ci subdev->unprepare(subdev); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci/** 12338c2ecf20Sopenharmony_ci * rproc_alloc_registered_carveouts() - allocate all carveouts registered 12348c2ecf20Sopenharmony_ci * in the list 12358c2ecf20Sopenharmony_ci * @rproc: the remote processor handle 12368c2ecf20Sopenharmony_ci * 12378c2ecf20Sopenharmony_ci * This function parses registered carveout list, performs allocation 12388c2ecf20Sopenharmony_ci * if alloc() ops registered and updates resource table information 12398c2ecf20Sopenharmony_ci * if rsc_offset set. 12408c2ecf20Sopenharmony_ci * 12418c2ecf20Sopenharmony_ci * Return: 0 on success 12428c2ecf20Sopenharmony_ci */ 12438c2ecf20Sopenharmony_cistatic int rproc_alloc_registered_carveouts(struct rproc *rproc) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct rproc_mem_entry *entry, *tmp; 12468c2ecf20Sopenharmony_ci struct fw_rsc_carveout *rsc; 12478c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 12488c2ecf20Sopenharmony_ci u64 pa; 12498c2ecf20Sopenharmony_ci int ret; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { 12528c2ecf20Sopenharmony_ci if (entry->alloc) { 12538c2ecf20Sopenharmony_ci ret = entry->alloc(rproc, entry); 12548c2ecf20Sopenharmony_ci if (ret) { 12558c2ecf20Sopenharmony_ci dev_err(dev, "Unable to allocate carveout %s: %d\n", 12568c2ecf20Sopenharmony_ci entry->name, ret); 12578c2ecf20Sopenharmony_ci return -ENOMEM; 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (entry->rsc_offset != FW_RSC_ADDR_ANY) { 12628c2ecf20Sopenharmony_ci /* update resource table */ 12638c2ecf20Sopenharmony_ci rsc = (void *)rproc->table_ptr + entry->rsc_offset; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* 12668c2ecf20Sopenharmony_ci * Some remote processors might need to know the pa 12678c2ecf20Sopenharmony_ci * even though they are behind an IOMMU. E.g., OMAP4's 12688c2ecf20Sopenharmony_ci * remote M3 processor needs this so it can control 12698c2ecf20Sopenharmony_ci * on-chip hardware accelerators that are not behind 12708c2ecf20Sopenharmony_ci * the IOMMU, and therefor must know the pa. 12718c2ecf20Sopenharmony_ci * 12728c2ecf20Sopenharmony_ci * Generally we don't want to expose physical addresses 12738c2ecf20Sopenharmony_ci * if we don't have to (remote processors are generally 12748c2ecf20Sopenharmony_ci * _not_ trusted), so we might want to do this only for 12758c2ecf20Sopenharmony_ci * remote processor that _must_ have this (e.g. OMAP4's 12768c2ecf20Sopenharmony_ci * dual M3 subsystem). 12778c2ecf20Sopenharmony_ci * 12788c2ecf20Sopenharmony_ci * Non-IOMMU processors might also want to have this info. 12798c2ecf20Sopenharmony_ci * In this case, the device address and the physical address 12808c2ecf20Sopenharmony_ci * are the same. 12818c2ecf20Sopenharmony_ci */ 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci /* Use va if defined else dma to generate pa */ 12848c2ecf20Sopenharmony_ci if (entry->va) 12858c2ecf20Sopenharmony_ci pa = (u64)rproc_va_to_pa(entry->va); 12868c2ecf20Sopenharmony_ci else 12878c2ecf20Sopenharmony_ci pa = (u64)entry->dma; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (((u64)pa) & HIGH_BITS_MASK) 12908c2ecf20Sopenharmony_ci dev_warn(dev, 12918c2ecf20Sopenharmony_ci "Physical address cast in 32bit to fit resource table format\n"); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci rsc->pa = (u32)pa; 12948c2ecf20Sopenharmony_ci rsc->da = entry->da; 12958c2ecf20Sopenharmony_ci rsc->len = entry->len; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci return 0; 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci/** 13048c2ecf20Sopenharmony_ci * rproc_resource_cleanup() - clean up and free all acquired resources 13058c2ecf20Sopenharmony_ci * @rproc: rproc handle 13068c2ecf20Sopenharmony_ci * 13078c2ecf20Sopenharmony_ci * This function will free all resources acquired for @rproc, and it 13088c2ecf20Sopenharmony_ci * is called whenever @rproc either shuts down or fails to boot. 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_civoid rproc_resource_cleanup(struct rproc *rproc) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci struct rproc_mem_entry *entry, *tmp; 13138c2ecf20Sopenharmony_ci struct rproc_debug_trace *trace, *ttmp; 13148c2ecf20Sopenharmony_ci struct rproc_vdev *rvdev, *rvtmp; 13158c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci /* clean up debugfs trace entries */ 13188c2ecf20Sopenharmony_ci list_for_each_entry_safe(trace, ttmp, &rproc->traces, node) { 13198c2ecf20Sopenharmony_ci rproc_remove_trace_file(trace->tfile); 13208c2ecf20Sopenharmony_ci rproc->num_traces--; 13218c2ecf20Sopenharmony_ci list_del(&trace->node); 13228c2ecf20Sopenharmony_ci kfree(trace); 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* clean up iommu mapping entries */ 13268c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) { 13278c2ecf20Sopenharmony_ci size_t unmapped; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci unmapped = iommu_unmap(rproc->domain, entry->da, entry->len); 13308c2ecf20Sopenharmony_ci if (unmapped != entry->len) { 13318c2ecf20Sopenharmony_ci /* nothing much to do besides complaining */ 13328c2ecf20Sopenharmony_ci dev_err(dev, "failed to unmap %zx/%zu\n", entry->len, 13338c2ecf20Sopenharmony_ci unmapped); 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci list_del(&entry->node); 13378c2ecf20Sopenharmony_ci kfree(entry); 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* clean up carveout allocations */ 13418c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { 13428c2ecf20Sopenharmony_ci if (entry->release) 13438c2ecf20Sopenharmony_ci entry->release(rproc, entry); 13448c2ecf20Sopenharmony_ci list_del(&entry->node); 13458c2ecf20Sopenharmony_ci kfree(entry); 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* clean up remote vdev entries */ 13498c2ecf20Sopenharmony_ci list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) 13508c2ecf20Sopenharmony_ci kref_put(&rvdev->refcount, rproc_vdev_release); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci rproc_coredump_cleanup(rproc); 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_resource_cleanup); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic int rproc_start(struct rproc *rproc, const struct firmware *fw) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct resource_table *loaded_table; 13598c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 13608c2ecf20Sopenharmony_ci int ret; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci /* load the ELF segments to memory */ 13638c2ecf20Sopenharmony_ci ret = rproc_load_segments(rproc, fw); 13648c2ecf20Sopenharmony_ci if (ret) { 13658c2ecf20Sopenharmony_ci dev_err(dev, "Failed to load program segments: %d\n", ret); 13668c2ecf20Sopenharmony_ci return ret; 13678c2ecf20Sopenharmony_ci } 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci /* 13708c2ecf20Sopenharmony_ci * The starting device has been given the rproc->cached_table as the 13718c2ecf20Sopenharmony_ci * resource table. The address of the vring along with the other 13728c2ecf20Sopenharmony_ci * allocated resources (carveouts etc) is stored in cached_table. 13738c2ecf20Sopenharmony_ci * In order to pass this information to the remote device we must copy 13748c2ecf20Sopenharmony_ci * this information to device memory. We also update the table_ptr so 13758c2ecf20Sopenharmony_ci * that any subsequent changes will be applied to the loaded version. 13768c2ecf20Sopenharmony_ci */ 13778c2ecf20Sopenharmony_ci loaded_table = rproc_find_loaded_rsc_table(rproc, fw); 13788c2ecf20Sopenharmony_ci if (loaded_table) { 13798c2ecf20Sopenharmony_ci memcpy(loaded_table, rproc->cached_table, rproc->table_sz); 13808c2ecf20Sopenharmony_ci rproc->table_ptr = loaded_table; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci ret = rproc_prepare_subdevices(rproc); 13848c2ecf20Sopenharmony_ci if (ret) { 13858c2ecf20Sopenharmony_ci dev_err(dev, "failed to prepare subdevices for %s: %d\n", 13868c2ecf20Sopenharmony_ci rproc->name, ret); 13878c2ecf20Sopenharmony_ci goto reset_table_ptr; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* power up the remote processor */ 13918c2ecf20Sopenharmony_ci ret = rproc->ops->start(rproc); 13928c2ecf20Sopenharmony_ci if (ret) { 13938c2ecf20Sopenharmony_ci dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret); 13948c2ecf20Sopenharmony_ci goto unprepare_subdevices; 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci /* Start any subdevices for the remote processor */ 13988c2ecf20Sopenharmony_ci ret = rproc_start_subdevices(rproc); 13998c2ecf20Sopenharmony_ci if (ret) { 14008c2ecf20Sopenharmony_ci dev_err(dev, "failed to probe subdevices for %s: %d\n", 14018c2ecf20Sopenharmony_ci rproc->name, ret); 14028c2ecf20Sopenharmony_ci goto stop_rproc; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci rproc->state = RPROC_RUNNING; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci dev_info(dev, "remote processor %s is now up\n", rproc->name); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci return 0; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistop_rproc: 14128c2ecf20Sopenharmony_ci rproc->ops->stop(rproc); 14138c2ecf20Sopenharmony_ciunprepare_subdevices: 14148c2ecf20Sopenharmony_ci rproc_unprepare_subdevices(rproc); 14158c2ecf20Sopenharmony_cireset_table_ptr: 14168c2ecf20Sopenharmony_ci rproc->table_ptr = rproc->cached_table; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci return ret; 14198c2ecf20Sopenharmony_ci} 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_cistatic int rproc_attach(struct rproc *rproc) 14228c2ecf20Sopenharmony_ci{ 14238c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 14248c2ecf20Sopenharmony_ci int ret; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci ret = rproc_prepare_subdevices(rproc); 14278c2ecf20Sopenharmony_ci if (ret) { 14288c2ecf20Sopenharmony_ci dev_err(dev, "failed to prepare subdevices for %s: %d\n", 14298c2ecf20Sopenharmony_ci rproc->name, ret); 14308c2ecf20Sopenharmony_ci goto out; 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci /* Attach to the remote processor */ 14348c2ecf20Sopenharmony_ci ret = rproc_attach_device(rproc); 14358c2ecf20Sopenharmony_ci if (ret) { 14368c2ecf20Sopenharmony_ci dev_err(dev, "can't attach to rproc %s: %d\n", 14378c2ecf20Sopenharmony_ci rproc->name, ret); 14388c2ecf20Sopenharmony_ci goto unprepare_subdevices; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* Start any subdevices for the remote processor */ 14428c2ecf20Sopenharmony_ci ret = rproc_start_subdevices(rproc); 14438c2ecf20Sopenharmony_ci if (ret) { 14448c2ecf20Sopenharmony_ci dev_err(dev, "failed to probe subdevices for %s: %d\n", 14458c2ecf20Sopenharmony_ci rproc->name, ret); 14468c2ecf20Sopenharmony_ci goto stop_rproc; 14478c2ecf20Sopenharmony_ci } 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci rproc->state = RPROC_RUNNING; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci dev_info(dev, "remote processor %s is now attached\n", rproc->name); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci return 0; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cistop_rproc: 14568c2ecf20Sopenharmony_ci rproc->ops->stop(rproc); 14578c2ecf20Sopenharmony_ciunprepare_subdevices: 14588c2ecf20Sopenharmony_ci rproc_unprepare_subdevices(rproc); 14598c2ecf20Sopenharmony_ciout: 14608c2ecf20Sopenharmony_ci return ret; 14618c2ecf20Sopenharmony_ci} 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci/* 14648c2ecf20Sopenharmony_ci * take a firmware and boot a remote processor with it. 14658c2ecf20Sopenharmony_ci */ 14668c2ecf20Sopenharmony_cistatic int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) 14678c2ecf20Sopenharmony_ci{ 14688c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 14698c2ecf20Sopenharmony_ci const char *name = rproc->firmware; 14708c2ecf20Sopenharmony_ci int ret; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci ret = rproc_fw_sanity_check(rproc, fw); 14738c2ecf20Sopenharmony_ci if (ret) 14748c2ecf20Sopenharmony_ci return ret; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci /* 14798c2ecf20Sopenharmony_ci * if enabling an IOMMU isn't relevant for this rproc, this is 14808c2ecf20Sopenharmony_ci * just a nop 14818c2ecf20Sopenharmony_ci */ 14828c2ecf20Sopenharmony_ci ret = rproc_enable_iommu(rproc); 14838c2ecf20Sopenharmony_ci if (ret) { 14848c2ecf20Sopenharmony_ci dev_err(dev, "can't enable iommu: %d\n", ret); 14858c2ecf20Sopenharmony_ci return ret; 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* Prepare rproc for firmware loading if needed */ 14898c2ecf20Sopenharmony_ci ret = rproc_prepare_device(rproc); 14908c2ecf20Sopenharmony_ci if (ret) { 14918c2ecf20Sopenharmony_ci dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret); 14928c2ecf20Sopenharmony_ci goto disable_iommu; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci rproc->bootaddr = rproc_get_boot_addr(rproc, fw); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci /* Load resource table, core dump segment list etc from the firmware */ 14988c2ecf20Sopenharmony_ci ret = rproc_parse_fw(rproc, fw); 14998c2ecf20Sopenharmony_ci if (ret) 15008c2ecf20Sopenharmony_ci goto unprepare_rproc; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci /* reset max_notifyid */ 15038c2ecf20Sopenharmony_ci rproc->max_notifyid = -1; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci /* reset handled vdev */ 15068c2ecf20Sopenharmony_ci rproc->nb_vdev = 0; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci /* handle fw resources which are required to boot rproc */ 15098c2ecf20Sopenharmony_ci ret = rproc_handle_resources(rproc, rproc_loading_handlers); 15108c2ecf20Sopenharmony_ci if (ret) { 15118c2ecf20Sopenharmony_ci dev_err(dev, "Failed to process resources: %d\n", ret); 15128c2ecf20Sopenharmony_ci goto clean_up_resources; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci /* Allocate carveout resources associated to rproc */ 15168c2ecf20Sopenharmony_ci ret = rproc_alloc_registered_carveouts(rproc); 15178c2ecf20Sopenharmony_ci if (ret) { 15188c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate associated carveouts: %d\n", 15198c2ecf20Sopenharmony_ci ret); 15208c2ecf20Sopenharmony_ci goto clean_up_resources; 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci ret = rproc_start(rproc, fw); 15248c2ecf20Sopenharmony_ci if (ret) 15258c2ecf20Sopenharmony_ci goto clean_up_resources; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci return 0; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ciclean_up_resources: 15308c2ecf20Sopenharmony_ci rproc_resource_cleanup(rproc); 15318c2ecf20Sopenharmony_ci kfree(rproc->cached_table); 15328c2ecf20Sopenharmony_ci rproc->cached_table = NULL; 15338c2ecf20Sopenharmony_ci rproc->table_ptr = NULL; 15348c2ecf20Sopenharmony_ciunprepare_rproc: 15358c2ecf20Sopenharmony_ci /* release HW resources if needed */ 15368c2ecf20Sopenharmony_ci rproc_unprepare_device(rproc); 15378c2ecf20Sopenharmony_cidisable_iommu: 15388c2ecf20Sopenharmony_ci rproc_disable_iommu(rproc); 15398c2ecf20Sopenharmony_ci return ret; 15408c2ecf20Sopenharmony_ci} 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci/* 15438c2ecf20Sopenharmony_ci * Attach to remote processor - similar to rproc_fw_boot() but without 15448c2ecf20Sopenharmony_ci * the steps that deal with the firmware image. 15458c2ecf20Sopenharmony_ci */ 15468c2ecf20Sopenharmony_cistatic int rproc_actuate(struct rproc *rproc) 15478c2ecf20Sopenharmony_ci{ 15488c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 15498c2ecf20Sopenharmony_ci int ret; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci /* 15528c2ecf20Sopenharmony_ci * if enabling an IOMMU isn't relevant for this rproc, this is 15538c2ecf20Sopenharmony_ci * just a nop 15548c2ecf20Sopenharmony_ci */ 15558c2ecf20Sopenharmony_ci ret = rproc_enable_iommu(rproc); 15568c2ecf20Sopenharmony_ci if (ret) { 15578c2ecf20Sopenharmony_ci dev_err(dev, "can't enable iommu: %d\n", ret); 15588c2ecf20Sopenharmony_ci return ret; 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci /* reset max_notifyid */ 15628c2ecf20Sopenharmony_ci rproc->max_notifyid = -1; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci /* reset handled vdev */ 15658c2ecf20Sopenharmony_ci rproc->nb_vdev = 0; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci /* 15688c2ecf20Sopenharmony_ci * Handle firmware resources required to attach to a remote processor. 15698c2ecf20Sopenharmony_ci * Because we are attaching rather than booting the remote processor, 15708c2ecf20Sopenharmony_ci * we expect the platform driver to properly set rproc->table_ptr. 15718c2ecf20Sopenharmony_ci */ 15728c2ecf20Sopenharmony_ci ret = rproc_handle_resources(rproc, rproc_loading_handlers); 15738c2ecf20Sopenharmony_ci if (ret) { 15748c2ecf20Sopenharmony_ci dev_err(dev, "Failed to process resources: %d\n", ret); 15758c2ecf20Sopenharmony_ci goto disable_iommu; 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci /* Allocate carveout resources associated to rproc */ 15798c2ecf20Sopenharmony_ci ret = rproc_alloc_registered_carveouts(rproc); 15808c2ecf20Sopenharmony_ci if (ret) { 15818c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate associated carveouts: %d\n", 15828c2ecf20Sopenharmony_ci ret); 15838c2ecf20Sopenharmony_ci goto clean_up_resources; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci ret = rproc_attach(rproc); 15878c2ecf20Sopenharmony_ci if (ret) 15888c2ecf20Sopenharmony_ci goto clean_up_resources; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci return 0; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ciclean_up_resources: 15938c2ecf20Sopenharmony_ci rproc_resource_cleanup(rproc); 15948c2ecf20Sopenharmony_cidisable_iommu: 15958c2ecf20Sopenharmony_ci rproc_disable_iommu(rproc); 15968c2ecf20Sopenharmony_ci return ret; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci/* 16008c2ecf20Sopenharmony_ci * take a firmware and boot it up. 16018c2ecf20Sopenharmony_ci * 16028c2ecf20Sopenharmony_ci * Note: this function is called asynchronously upon registration of the 16038c2ecf20Sopenharmony_ci * remote processor (so we must wait until it completes before we try 16048c2ecf20Sopenharmony_ci * to unregister the device. one other option is just to use kref here, 16058c2ecf20Sopenharmony_ci * that might be cleaner). 16068c2ecf20Sopenharmony_ci */ 16078c2ecf20Sopenharmony_cistatic void rproc_auto_boot_callback(const struct firmware *fw, void *context) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct rproc *rproc = context; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci rproc_boot(rproc); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci release_firmware(fw); 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic int rproc_trigger_auto_boot(struct rproc *rproc) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci int ret; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci /* 16218c2ecf20Sopenharmony_ci * Since the remote processor is in a detached state, it has already 16228c2ecf20Sopenharmony_ci * been booted by another entity. As such there is no point in waiting 16238c2ecf20Sopenharmony_ci * for a firmware image to be loaded, we can simply initiate the process 16248c2ecf20Sopenharmony_ci * of attaching to it immediately. 16258c2ecf20Sopenharmony_ci */ 16268c2ecf20Sopenharmony_ci if (rproc->state == RPROC_DETACHED) 16278c2ecf20Sopenharmony_ci return rproc_boot(rproc); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* 16308c2ecf20Sopenharmony_ci * We're initiating an asynchronous firmware loading, so we can 16318c2ecf20Sopenharmony_ci * be built-in kernel code, without hanging the boot process. 16328c2ecf20Sopenharmony_ci */ 16338c2ecf20Sopenharmony_ci ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, 16348c2ecf20Sopenharmony_ci rproc->firmware, &rproc->dev, GFP_KERNEL, 16358c2ecf20Sopenharmony_ci rproc, rproc_auto_boot_callback); 16368c2ecf20Sopenharmony_ci if (ret < 0) 16378c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret); 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci return ret; 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_cistatic int rproc_stop(struct rproc *rproc, bool crashed) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 16458c2ecf20Sopenharmony_ci int ret; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci /* Stop any subdevices for the remote processor */ 16488c2ecf20Sopenharmony_ci rproc_stop_subdevices(rproc, crashed); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci /* the installed resource table is no longer accessible */ 16518c2ecf20Sopenharmony_ci rproc->table_ptr = rproc->cached_table; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci /* power off the remote processor */ 16548c2ecf20Sopenharmony_ci ret = rproc->ops->stop(rproc); 16558c2ecf20Sopenharmony_ci if (ret) { 16568c2ecf20Sopenharmony_ci dev_err(dev, "can't stop rproc: %d\n", ret); 16578c2ecf20Sopenharmony_ci return ret; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci rproc_unprepare_subdevices(rproc); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci rproc->state = RPROC_OFFLINE; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci /* 16658c2ecf20Sopenharmony_ci * The remote processor has been stopped and is now offline, which means 16668c2ecf20Sopenharmony_ci * that the next time it is brought back online the remoteproc core will 16678c2ecf20Sopenharmony_ci * be responsible to load its firmware. As such it is no longer 16688c2ecf20Sopenharmony_ci * autonomous. 16698c2ecf20Sopenharmony_ci */ 16708c2ecf20Sopenharmony_ci rproc->autonomous = false; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci dev_info(dev, "stopped remote processor %s\n", rproc->name); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci return 0; 16758c2ecf20Sopenharmony_ci} 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci/** 16798c2ecf20Sopenharmony_ci * rproc_trigger_recovery() - recover a remoteproc 16808c2ecf20Sopenharmony_ci * @rproc: the remote processor 16818c2ecf20Sopenharmony_ci * 16828c2ecf20Sopenharmony_ci * The recovery is done by resetting all the virtio devices, that way all the 16838c2ecf20Sopenharmony_ci * rpmsg drivers will be reseted along with the remote processor making the 16848c2ecf20Sopenharmony_ci * remoteproc functional again. 16858c2ecf20Sopenharmony_ci * 16868c2ecf20Sopenharmony_ci * This function can sleep, so it cannot be called from atomic context. 16878c2ecf20Sopenharmony_ci */ 16888c2ecf20Sopenharmony_ciint rproc_trigger_recovery(struct rproc *rproc) 16898c2ecf20Sopenharmony_ci{ 16908c2ecf20Sopenharmony_ci const struct firmware *firmware_p; 16918c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 16928c2ecf20Sopenharmony_ci int ret; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&rproc->lock); 16958c2ecf20Sopenharmony_ci if (ret) 16968c2ecf20Sopenharmony_ci return ret; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci /* State could have changed before we got the mutex */ 16998c2ecf20Sopenharmony_ci if (rproc->state != RPROC_CRASHED) 17008c2ecf20Sopenharmony_ci goto unlock_mutex; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci dev_err(dev, "recovering %s\n", rproc->name); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci ret = rproc_stop(rproc, true); 17058c2ecf20Sopenharmony_ci if (ret) 17068c2ecf20Sopenharmony_ci goto unlock_mutex; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci /* generate coredump */ 17098c2ecf20Sopenharmony_ci rproc_coredump(rproc); 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci /* load firmware */ 17128c2ecf20Sopenharmony_ci ret = request_firmware(&firmware_p, rproc->firmware, dev); 17138c2ecf20Sopenharmony_ci if (ret < 0) { 17148c2ecf20Sopenharmony_ci dev_err(dev, "request_firmware failed: %d\n", ret); 17158c2ecf20Sopenharmony_ci goto unlock_mutex; 17168c2ecf20Sopenharmony_ci } 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci /* boot the remote processor up again */ 17198c2ecf20Sopenharmony_ci ret = rproc_start(rproc, firmware_p); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci release_firmware(firmware_p); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ciunlock_mutex: 17248c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 17258c2ecf20Sopenharmony_ci return ret; 17268c2ecf20Sopenharmony_ci} 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci/** 17298c2ecf20Sopenharmony_ci * rproc_crash_handler_work() - handle a crash 17308c2ecf20Sopenharmony_ci * @work: work treating the crash 17318c2ecf20Sopenharmony_ci * 17328c2ecf20Sopenharmony_ci * This function needs to handle everything related to a crash, like cpu 17338c2ecf20Sopenharmony_ci * registers and stack dump, information to help to debug the fatal error, etc. 17348c2ecf20Sopenharmony_ci */ 17358c2ecf20Sopenharmony_cistatic void rproc_crash_handler_work(struct work_struct *work) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci struct rproc *rproc = container_of(work, struct rproc, crash_handler); 17388c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci dev_dbg(dev, "enter %s\n", __func__); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci mutex_lock(&rproc->lock); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (rproc->state == RPROC_CRASHED) { 17458c2ecf20Sopenharmony_ci /* handle only the first crash detected */ 17468c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 17478c2ecf20Sopenharmony_ci return; 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci if (rproc->state == RPROC_OFFLINE) { 17518c2ecf20Sopenharmony_ci /* Don't recover if the remote processor was stopped */ 17528c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 17538c2ecf20Sopenharmony_ci goto out; 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci rproc->state = RPROC_CRASHED; 17578c2ecf20Sopenharmony_ci dev_err(dev, "handling crash #%u in %s\n", ++rproc->crash_cnt, 17588c2ecf20Sopenharmony_ci rproc->name); 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (!rproc->recovery_disabled) 17638c2ecf20Sopenharmony_ci rproc_trigger_recovery(rproc); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ciout: 17668c2ecf20Sopenharmony_ci pm_relax(rproc->dev.parent); 17678c2ecf20Sopenharmony_ci} 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci/** 17708c2ecf20Sopenharmony_ci * rproc_boot() - boot a remote processor 17718c2ecf20Sopenharmony_ci * @rproc: handle of a remote processor 17728c2ecf20Sopenharmony_ci * 17738c2ecf20Sopenharmony_ci * Boot a remote processor (i.e. load its firmware, power it on, ...). 17748c2ecf20Sopenharmony_ci * 17758c2ecf20Sopenharmony_ci * If the remote processor is already powered on, this function immediately 17768c2ecf20Sopenharmony_ci * returns (successfully). 17778c2ecf20Sopenharmony_ci * 17788c2ecf20Sopenharmony_ci * Returns 0 on success, and an appropriate error value otherwise. 17798c2ecf20Sopenharmony_ci */ 17808c2ecf20Sopenharmony_ciint rproc_boot(struct rproc *rproc) 17818c2ecf20Sopenharmony_ci{ 17828c2ecf20Sopenharmony_ci const struct firmware *firmware_p; 17838c2ecf20Sopenharmony_ci struct device *dev; 17848c2ecf20Sopenharmony_ci int ret; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci if (!rproc) { 17878c2ecf20Sopenharmony_ci pr_err("invalid rproc handle\n"); 17888c2ecf20Sopenharmony_ci return -EINVAL; 17898c2ecf20Sopenharmony_ci } 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci dev = &rproc->dev; 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&rproc->lock); 17948c2ecf20Sopenharmony_ci if (ret) { 17958c2ecf20Sopenharmony_ci dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); 17968c2ecf20Sopenharmony_ci return ret; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci if (rproc->state == RPROC_DELETED) { 18008c2ecf20Sopenharmony_ci ret = -ENODEV; 18018c2ecf20Sopenharmony_ci dev_err(dev, "can't boot deleted rproc %s\n", rproc->name); 18028c2ecf20Sopenharmony_ci goto unlock_mutex; 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci /* skip the boot or attach process if rproc is already powered up */ 18068c2ecf20Sopenharmony_ci if (atomic_inc_return(&rproc->power) > 1) { 18078c2ecf20Sopenharmony_ci ret = 0; 18088c2ecf20Sopenharmony_ci goto unlock_mutex; 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci if (rproc->state == RPROC_DETACHED) { 18128c2ecf20Sopenharmony_ci dev_info(dev, "attaching to %s\n", rproc->name); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci ret = rproc_actuate(rproc); 18158c2ecf20Sopenharmony_ci } else { 18168c2ecf20Sopenharmony_ci dev_info(dev, "powering up %s\n", rproc->name); 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci /* load firmware */ 18198c2ecf20Sopenharmony_ci ret = request_firmware(&firmware_p, rproc->firmware, dev); 18208c2ecf20Sopenharmony_ci if (ret < 0) { 18218c2ecf20Sopenharmony_ci dev_err(dev, "request_firmware failed: %d\n", ret); 18228c2ecf20Sopenharmony_ci goto downref_rproc; 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci ret = rproc_fw_boot(rproc, firmware_p); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci release_firmware(firmware_p); 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_cidownref_rproc: 18318c2ecf20Sopenharmony_ci if (ret) 18328c2ecf20Sopenharmony_ci atomic_dec(&rproc->power); 18338c2ecf20Sopenharmony_ciunlock_mutex: 18348c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 18358c2ecf20Sopenharmony_ci return ret; 18368c2ecf20Sopenharmony_ci} 18378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_boot); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci/** 18408c2ecf20Sopenharmony_ci * rproc_shutdown() - power off the remote processor 18418c2ecf20Sopenharmony_ci * @rproc: the remote processor 18428c2ecf20Sopenharmony_ci * 18438c2ecf20Sopenharmony_ci * Power off a remote processor (previously booted with rproc_boot()). 18448c2ecf20Sopenharmony_ci * 18458c2ecf20Sopenharmony_ci * In case @rproc is still being used by an additional user(s), then 18468c2ecf20Sopenharmony_ci * this function will just decrement the power refcount and exit, 18478c2ecf20Sopenharmony_ci * without really powering off the device. 18488c2ecf20Sopenharmony_ci * 18498c2ecf20Sopenharmony_ci * Every call to rproc_boot() must (eventually) be accompanied by a call 18508c2ecf20Sopenharmony_ci * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug. 18518c2ecf20Sopenharmony_ci * 18528c2ecf20Sopenharmony_ci * Notes: 18538c2ecf20Sopenharmony_ci * - we're not decrementing the rproc's refcount, only the power refcount. 18548c2ecf20Sopenharmony_ci * which means that the @rproc handle stays valid even after rproc_shutdown() 18558c2ecf20Sopenharmony_ci * returns, and users can still use it with a subsequent rproc_boot(), if 18568c2ecf20Sopenharmony_ci * needed. 18578c2ecf20Sopenharmony_ci */ 18588c2ecf20Sopenharmony_civoid rproc_shutdown(struct rproc *rproc) 18598c2ecf20Sopenharmony_ci{ 18608c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 18618c2ecf20Sopenharmony_ci int ret; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&rproc->lock); 18648c2ecf20Sopenharmony_ci if (ret) { 18658c2ecf20Sopenharmony_ci dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); 18668c2ecf20Sopenharmony_ci return; 18678c2ecf20Sopenharmony_ci } 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci /* if the remote proc is still needed, bail out */ 18708c2ecf20Sopenharmony_ci if (!atomic_dec_and_test(&rproc->power)) 18718c2ecf20Sopenharmony_ci goto out; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci ret = rproc_stop(rproc, false); 18748c2ecf20Sopenharmony_ci if (ret) { 18758c2ecf20Sopenharmony_ci atomic_inc(&rproc->power); 18768c2ecf20Sopenharmony_ci goto out; 18778c2ecf20Sopenharmony_ci } 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci /* clean up all acquired resources */ 18808c2ecf20Sopenharmony_ci rproc_resource_cleanup(rproc); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci /* release HW resources if needed */ 18838c2ecf20Sopenharmony_ci rproc_unprepare_device(rproc); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci rproc_disable_iommu(rproc); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci /* Free the copy of the resource table */ 18888c2ecf20Sopenharmony_ci kfree(rproc->cached_table); 18898c2ecf20Sopenharmony_ci rproc->cached_table = NULL; 18908c2ecf20Sopenharmony_ci rproc->table_ptr = NULL; 18918c2ecf20Sopenharmony_ciout: 18928c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 18938c2ecf20Sopenharmony_ci} 18948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_shutdown); 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci/** 18978c2ecf20Sopenharmony_ci * rproc_get_by_phandle() - find a remote processor by phandle 18988c2ecf20Sopenharmony_ci * @phandle: phandle to the rproc 18998c2ecf20Sopenharmony_ci * 19008c2ecf20Sopenharmony_ci * Finds an rproc handle using the remote processor's phandle, and then 19018c2ecf20Sopenharmony_ci * return a handle to the rproc. 19028c2ecf20Sopenharmony_ci * 19038c2ecf20Sopenharmony_ci * This function increments the remote processor's refcount, so always 19048c2ecf20Sopenharmony_ci * use rproc_put() to decrement it back once rproc isn't needed anymore. 19058c2ecf20Sopenharmony_ci * 19068c2ecf20Sopenharmony_ci * Returns the rproc handle on success, and NULL on failure. 19078c2ecf20Sopenharmony_ci */ 19088c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 19098c2ecf20Sopenharmony_cistruct rproc *rproc_get_by_phandle(phandle phandle) 19108c2ecf20Sopenharmony_ci{ 19118c2ecf20Sopenharmony_ci struct rproc *rproc = NULL, *r; 19128c2ecf20Sopenharmony_ci struct device_node *np; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci np = of_find_node_by_phandle(phandle); 19158c2ecf20Sopenharmony_ci if (!np) 19168c2ecf20Sopenharmony_ci return NULL; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci rcu_read_lock(); 19198c2ecf20Sopenharmony_ci list_for_each_entry_rcu(r, &rproc_list, node) { 19208c2ecf20Sopenharmony_ci if (r->dev.parent && r->dev.parent->of_node == np) { 19218c2ecf20Sopenharmony_ci /* prevent underlying implementation from being removed */ 19228c2ecf20Sopenharmony_ci if (!try_module_get(r->dev.parent->driver->owner)) { 19238c2ecf20Sopenharmony_ci dev_err(&r->dev, "can't get owner\n"); 19248c2ecf20Sopenharmony_ci break; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci rproc = r; 19288c2ecf20Sopenharmony_ci get_device(&rproc->dev); 19298c2ecf20Sopenharmony_ci break; 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci } 19328c2ecf20Sopenharmony_ci rcu_read_unlock(); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci of_node_put(np); 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci return rproc; 19378c2ecf20Sopenharmony_ci} 19388c2ecf20Sopenharmony_ci#else 19398c2ecf20Sopenharmony_cistruct rproc *rproc_get_by_phandle(phandle phandle) 19408c2ecf20Sopenharmony_ci{ 19418c2ecf20Sopenharmony_ci return NULL; 19428c2ecf20Sopenharmony_ci} 19438c2ecf20Sopenharmony_ci#endif 19448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_get_by_phandle); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_cistatic int rproc_validate(struct rproc *rproc) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci switch (rproc->state) { 19498c2ecf20Sopenharmony_ci case RPROC_OFFLINE: 19508c2ecf20Sopenharmony_ci /* 19518c2ecf20Sopenharmony_ci * An offline processor without a start() 19528c2ecf20Sopenharmony_ci * function makes no sense. 19538c2ecf20Sopenharmony_ci */ 19548c2ecf20Sopenharmony_ci if (!rproc->ops->start) 19558c2ecf20Sopenharmony_ci return -EINVAL; 19568c2ecf20Sopenharmony_ci break; 19578c2ecf20Sopenharmony_ci case RPROC_DETACHED: 19588c2ecf20Sopenharmony_ci /* 19598c2ecf20Sopenharmony_ci * A remote processor in a detached state without an 19608c2ecf20Sopenharmony_ci * attach() function makes not sense. 19618c2ecf20Sopenharmony_ci */ 19628c2ecf20Sopenharmony_ci if (!rproc->ops->attach) 19638c2ecf20Sopenharmony_ci return -EINVAL; 19648c2ecf20Sopenharmony_ci /* 19658c2ecf20Sopenharmony_ci * When attaching to a remote processor the device memory 19668c2ecf20Sopenharmony_ci * is already available and as such there is no need to have a 19678c2ecf20Sopenharmony_ci * cached table. 19688c2ecf20Sopenharmony_ci */ 19698c2ecf20Sopenharmony_ci if (rproc->cached_table) 19708c2ecf20Sopenharmony_ci return -EINVAL; 19718c2ecf20Sopenharmony_ci break; 19728c2ecf20Sopenharmony_ci default: 19738c2ecf20Sopenharmony_ci /* 19748c2ecf20Sopenharmony_ci * When adding a remote processor, the state of the device 19758c2ecf20Sopenharmony_ci * can be offline or detached, nothing else. 19768c2ecf20Sopenharmony_ci */ 19778c2ecf20Sopenharmony_ci return -EINVAL; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci return 0; 19818c2ecf20Sopenharmony_ci} 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci/** 19848c2ecf20Sopenharmony_ci * rproc_add() - register a remote processor 19858c2ecf20Sopenharmony_ci * @rproc: the remote processor handle to register 19868c2ecf20Sopenharmony_ci * 19878c2ecf20Sopenharmony_ci * Registers @rproc with the remoteproc framework, after it has been 19888c2ecf20Sopenharmony_ci * allocated with rproc_alloc(). 19898c2ecf20Sopenharmony_ci * 19908c2ecf20Sopenharmony_ci * This is called by the platform-specific rproc implementation, whenever 19918c2ecf20Sopenharmony_ci * a new remote processor device is probed. 19928c2ecf20Sopenharmony_ci * 19938c2ecf20Sopenharmony_ci * Returns 0 on success and an appropriate error code otherwise. 19948c2ecf20Sopenharmony_ci * 19958c2ecf20Sopenharmony_ci * Note: this function initiates an asynchronous firmware loading 19968c2ecf20Sopenharmony_ci * context, which will look for virtio devices supported by the rproc's 19978c2ecf20Sopenharmony_ci * firmware. 19988c2ecf20Sopenharmony_ci * 19998c2ecf20Sopenharmony_ci * If found, those virtio devices will be created and added, so as a result 20008c2ecf20Sopenharmony_ci * of registering this remote processor, additional virtio drivers might be 20018c2ecf20Sopenharmony_ci * probed. 20028c2ecf20Sopenharmony_ci */ 20038c2ecf20Sopenharmony_ciint rproc_add(struct rproc *rproc) 20048c2ecf20Sopenharmony_ci{ 20058c2ecf20Sopenharmony_ci struct device *dev = &rproc->dev; 20068c2ecf20Sopenharmony_ci int ret; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci ret = device_add(dev); 20098c2ecf20Sopenharmony_ci if (ret < 0) 20108c2ecf20Sopenharmony_ci return ret; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci ret = rproc_validate(rproc); 20138c2ecf20Sopenharmony_ci if (ret < 0) 20148c2ecf20Sopenharmony_ci return ret; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci dev_info(dev, "%s is available\n", rproc->name); 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci /* create debugfs entries */ 20198c2ecf20Sopenharmony_ci rproc_create_debug_dir(rproc); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci /* add char device for this remoteproc */ 20228c2ecf20Sopenharmony_ci ret = rproc_char_device_add(rproc); 20238c2ecf20Sopenharmony_ci if (ret < 0) 20248c2ecf20Sopenharmony_ci return ret; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci /* 20278c2ecf20Sopenharmony_ci * Remind ourselves the remote processor has been attached to rather 20288c2ecf20Sopenharmony_ci * than booted by the remoteproc core. This is important because the 20298c2ecf20Sopenharmony_ci * RPROC_DETACHED state will be lost as soon as the remote processor 20308c2ecf20Sopenharmony_ci * has been attached to. Used in firmware_show() and reset in 20318c2ecf20Sopenharmony_ci * rproc_stop(). 20328c2ecf20Sopenharmony_ci */ 20338c2ecf20Sopenharmony_ci if (rproc->state == RPROC_DETACHED) 20348c2ecf20Sopenharmony_ci rproc->autonomous = true; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci /* if rproc is marked always-on, request it to boot */ 20378c2ecf20Sopenharmony_ci if (rproc->auto_boot) { 20388c2ecf20Sopenharmony_ci ret = rproc_trigger_auto_boot(rproc); 20398c2ecf20Sopenharmony_ci if (ret < 0) 20408c2ecf20Sopenharmony_ci return ret; 20418c2ecf20Sopenharmony_ci } 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci /* expose to rproc_get_by_phandle users */ 20448c2ecf20Sopenharmony_ci mutex_lock(&rproc_list_mutex); 20458c2ecf20Sopenharmony_ci list_add_rcu(&rproc->node, &rproc_list); 20468c2ecf20Sopenharmony_ci mutex_unlock(&rproc_list_mutex); 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci return 0; 20498c2ecf20Sopenharmony_ci} 20508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_add); 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cistatic void devm_rproc_remove(void *rproc) 20538c2ecf20Sopenharmony_ci{ 20548c2ecf20Sopenharmony_ci rproc_del(rproc); 20558c2ecf20Sopenharmony_ci} 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci/** 20588c2ecf20Sopenharmony_ci * devm_rproc_add() - resource managed rproc_add() 20598c2ecf20Sopenharmony_ci * @dev: the underlying device 20608c2ecf20Sopenharmony_ci * @rproc: the remote processor handle to register 20618c2ecf20Sopenharmony_ci * 20628c2ecf20Sopenharmony_ci * This function performs like rproc_add() but the registered rproc device will 20638c2ecf20Sopenharmony_ci * automatically be removed on driver detach. 20648c2ecf20Sopenharmony_ci * 20658c2ecf20Sopenharmony_ci * Returns: 0 on success, negative errno on failure 20668c2ecf20Sopenharmony_ci */ 20678c2ecf20Sopenharmony_ciint devm_rproc_add(struct device *dev, struct rproc *rproc) 20688c2ecf20Sopenharmony_ci{ 20698c2ecf20Sopenharmony_ci int err; 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci err = rproc_add(rproc); 20728c2ecf20Sopenharmony_ci if (err) 20738c2ecf20Sopenharmony_ci return err; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci return devm_add_action_or_reset(dev, devm_rproc_remove, rproc); 20768c2ecf20Sopenharmony_ci} 20778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_rproc_add); 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci/** 20808c2ecf20Sopenharmony_ci * rproc_type_release() - release a remote processor instance 20818c2ecf20Sopenharmony_ci * @dev: the rproc's device 20828c2ecf20Sopenharmony_ci * 20838c2ecf20Sopenharmony_ci * This function should _never_ be called directly. 20848c2ecf20Sopenharmony_ci * 20858c2ecf20Sopenharmony_ci * It will be called by the driver core when no one holds a valid pointer 20868c2ecf20Sopenharmony_ci * to @dev anymore. 20878c2ecf20Sopenharmony_ci */ 20888c2ecf20Sopenharmony_cistatic void rproc_type_release(struct device *dev) 20898c2ecf20Sopenharmony_ci{ 20908c2ecf20Sopenharmony_ci struct rproc *rproc = container_of(dev, struct rproc, dev); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci dev_info(&rproc->dev, "releasing %s\n", rproc->name); 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci idr_destroy(&rproc->notifyids); 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci if (rproc->index >= 0) 20978c2ecf20Sopenharmony_ci ida_simple_remove(&rproc_dev_index, rproc->index); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci kfree_const(rproc->firmware); 21008c2ecf20Sopenharmony_ci kfree_const(rproc->name); 21018c2ecf20Sopenharmony_ci kfree(rproc->ops); 21028c2ecf20Sopenharmony_ci kfree(rproc); 21038c2ecf20Sopenharmony_ci} 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_cistatic const struct device_type rproc_type = { 21068c2ecf20Sopenharmony_ci .name = "remoteproc", 21078c2ecf20Sopenharmony_ci .release = rproc_type_release, 21088c2ecf20Sopenharmony_ci}; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_cistatic int rproc_alloc_firmware(struct rproc *rproc, 21118c2ecf20Sopenharmony_ci const char *name, const char *firmware) 21128c2ecf20Sopenharmony_ci{ 21138c2ecf20Sopenharmony_ci const char *p; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci /* 21168c2ecf20Sopenharmony_ci * Allocate a firmware name if the caller gave us one to work 21178c2ecf20Sopenharmony_ci * with. Otherwise construct a new one using a default pattern. 21188c2ecf20Sopenharmony_ci */ 21198c2ecf20Sopenharmony_ci if (firmware) 21208c2ecf20Sopenharmony_ci p = kstrdup_const(firmware, GFP_KERNEL); 21218c2ecf20Sopenharmony_ci else 21228c2ecf20Sopenharmony_ci p = kasprintf(GFP_KERNEL, "rproc-%s-fw", name); 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci if (!p) 21258c2ecf20Sopenharmony_ci return -ENOMEM; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci rproc->firmware = p; 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci return 0; 21308c2ecf20Sopenharmony_ci} 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_cistatic int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops) 21338c2ecf20Sopenharmony_ci{ 21348c2ecf20Sopenharmony_ci rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL); 21358c2ecf20Sopenharmony_ci if (!rproc->ops) 21368c2ecf20Sopenharmony_ci return -ENOMEM; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci if (rproc->ops->load) 21398c2ecf20Sopenharmony_ci return 0; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci /* Default to ELF loader if no load function is specified */ 21428c2ecf20Sopenharmony_ci rproc->ops->load = rproc_elf_load_segments; 21438c2ecf20Sopenharmony_ci rproc->ops->parse_fw = rproc_elf_load_rsc_table; 21448c2ecf20Sopenharmony_ci rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; 21458c2ecf20Sopenharmony_ci rproc->ops->sanity_check = rproc_elf_sanity_check; 21468c2ecf20Sopenharmony_ci rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci return 0; 21498c2ecf20Sopenharmony_ci} 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci/** 21528c2ecf20Sopenharmony_ci * rproc_alloc() - allocate a remote processor handle 21538c2ecf20Sopenharmony_ci * @dev: the underlying device 21548c2ecf20Sopenharmony_ci * @name: name of this remote processor 21558c2ecf20Sopenharmony_ci * @ops: platform-specific handlers (mainly start/stop) 21568c2ecf20Sopenharmony_ci * @firmware: name of firmware file to load, can be NULL 21578c2ecf20Sopenharmony_ci * @len: length of private data needed by the rproc driver (in bytes) 21588c2ecf20Sopenharmony_ci * 21598c2ecf20Sopenharmony_ci * Allocates a new remote processor handle, but does not register 21608c2ecf20Sopenharmony_ci * it yet. if @firmware is NULL, a default name is used. 21618c2ecf20Sopenharmony_ci * 21628c2ecf20Sopenharmony_ci * This function should be used by rproc implementations during initialization 21638c2ecf20Sopenharmony_ci * of the remote processor. 21648c2ecf20Sopenharmony_ci * 21658c2ecf20Sopenharmony_ci * After creating an rproc handle using this function, and when ready, 21668c2ecf20Sopenharmony_ci * implementations should then call rproc_add() to complete 21678c2ecf20Sopenharmony_ci * the registration of the remote processor. 21688c2ecf20Sopenharmony_ci * 21698c2ecf20Sopenharmony_ci * On success the new rproc is returned, and on failure, NULL. 21708c2ecf20Sopenharmony_ci * 21718c2ecf20Sopenharmony_ci * Note: _never_ directly deallocate @rproc, even if it was not registered 21728c2ecf20Sopenharmony_ci * yet. Instead, when you need to unroll rproc_alloc(), use rproc_free(). 21738c2ecf20Sopenharmony_ci */ 21748c2ecf20Sopenharmony_cistruct rproc *rproc_alloc(struct device *dev, const char *name, 21758c2ecf20Sopenharmony_ci const struct rproc_ops *ops, 21768c2ecf20Sopenharmony_ci const char *firmware, int len) 21778c2ecf20Sopenharmony_ci{ 21788c2ecf20Sopenharmony_ci struct rproc *rproc; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci if (!dev || !name || !ops) 21818c2ecf20Sopenharmony_ci return NULL; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL); 21848c2ecf20Sopenharmony_ci if (!rproc) 21858c2ecf20Sopenharmony_ci return NULL; 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci rproc->priv = &rproc[1]; 21888c2ecf20Sopenharmony_ci rproc->auto_boot = true; 21898c2ecf20Sopenharmony_ci rproc->elf_class = ELFCLASSNONE; 21908c2ecf20Sopenharmony_ci rproc->elf_machine = EM_NONE; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci device_initialize(&rproc->dev); 21938c2ecf20Sopenharmony_ci rproc->dev.parent = dev; 21948c2ecf20Sopenharmony_ci rproc->dev.type = &rproc_type; 21958c2ecf20Sopenharmony_ci rproc->dev.class = &rproc_class; 21968c2ecf20Sopenharmony_ci rproc->dev.driver_data = rproc; 21978c2ecf20Sopenharmony_ci idr_init(&rproc->notifyids); 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci rproc->name = kstrdup_const(name, GFP_KERNEL); 22008c2ecf20Sopenharmony_ci if (!rproc->name) 22018c2ecf20Sopenharmony_ci goto put_device; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci if (rproc_alloc_firmware(rproc, name, firmware)) 22048c2ecf20Sopenharmony_ci goto put_device; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci if (rproc_alloc_ops(rproc, ops)) 22078c2ecf20Sopenharmony_ci goto put_device; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci /* Assign a unique device index and name */ 22108c2ecf20Sopenharmony_ci rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); 22118c2ecf20Sopenharmony_ci if (rproc->index < 0) { 22128c2ecf20Sopenharmony_ci dev_err(dev, "ida_simple_get failed: %d\n", rproc->index); 22138c2ecf20Sopenharmony_ci goto put_device; 22148c2ecf20Sopenharmony_ci } 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci dev_set_name(&rproc->dev, "remoteproc%d", rproc->index); 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci atomic_set(&rproc->power, 0); 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci mutex_init(&rproc->lock); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rproc->carveouts); 22238c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rproc->mappings); 22248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rproc->traces); 22258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rproc->rvdevs); 22268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rproc->subdevs); 22278c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rproc->dump_segments); 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work); 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci rproc->state = RPROC_OFFLINE; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci return rproc; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ciput_device: 22368c2ecf20Sopenharmony_ci put_device(&rproc->dev); 22378c2ecf20Sopenharmony_ci return NULL; 22388c2ecf20Sopenharmony_ci} 22398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_alloc); 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci/** 22428c2ecf20Sopenharmony_ci * rproc_free() - unroll rproc_alloc() 22438c2ecf20Sopenharmony_ci * @rproc: the remote processor handle 22448c2ecf20Sopenharmony_ci * 22458c2ecf20Sopenharmony_ci * This function decrements the rproc dev refcount. 22468c2ecf20Sopenharmony_ci * 22478c2ecf20Sopenharmony_ci * If no one holds any reference to rproc anymore, then its refcount would 22488c2ecf20Sopenharmony_ci * now drop to zero, and it would be freed. 22498c2ecf20Sopenharmony_ci */ 22508c2ecf20Sopenharmony_civoid rproc_free(struct rproc *rproc) 22518c2ecf20Sopenharmony_ci{ 22528c2ecf20Sopenharmony_ci put_device(&rproc->dev); 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_free); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci/** 22578c2ecf20Sopenharmony_ci * rproc_put() - release rproc reference 22588c2ecf20Sopenharmony_ci * @rproc: the remote processor handle 22598c2ecf20Sopenharmony_ci * 22608c2ecf20Sopenharmony_ci * This function decrements the rproc dev refcount. 22618c2ecf20Sopenharmony_ci * 22628c2ecf20Sopenharmony_ci * If no one holds any reference to rproc anymore, then its refcount would 22638c2ecf20Sopenharmony_ci * now drop to zero, and it would be freed. 22648c2ecf20Sopenharmony_ci */ 22658c2ecf20Sopenharmony_civoid rproc_put(struct rproc *rproc) 22668c2ecf20Sopenharmony_ci{ 22678c2ecf20Sopenharmony_ci module_put(rproc->dev.parent->driver->owner); 22688c2ecf20Sopenharmony_ci put_device(&rproc->dev); 22698c2ecf20Sopenharmony_ci} 22708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_put); 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci/** 22738c2ecf20Sopenharmony_ci * rproc_del() - unregister a remote processor 22748c2ecf20Sopenharmony_ci * @rproc: rproc handle to unregister 22758c2ecf20Sopenharmony_ci * 22768c2ecf20Sopenharmony_ci * This function should be called when the platform specific rproc 22778c2ecf20Sopenharmony_ci * implementation decides to remove the rproc device. it should 22788c2ecf20Sopenharmony_ci * _only_ be called if a previous invocation of rproc_add() 22798c2ecf20Sopenharmony_ci * has completed successfully. 22808c2ecf20Sopenharmony_ci * 22818c2ecf20Sopenharmony_ci * After rproc_del() returns, @rproc isn't freed yet, because 22828c2ecf20Sopenharmony_ci * of the outstanding reference created by rproc_alloc. To decrement that 22838c2ecf20Sopenharmony_ci * one last refcount, one still needs to call rproc_free(). 22848c2ecf20Sopenharmony_ci * 22858c2ecf20Sopenharmony_ci * Returns 0 on success and -EINVAL if @rproc isn't valid. 22868c2ecf20Sopenharmony_ci */ 22878c2ecf20Sopenharmony_ciint rproc_del(struct rproc *rproc) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci if (!rproc) 22908c2ecf20Sopenharmony_ci return -EINVAL; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci /* if rproc is marked always-on, rproc_add() booted it */ 22938c2ecf20Sopenharmony_ci /* TODO: make sure this works with rproc->power > 1 */ 22948c2ecf20Sopenharmony_ci if (rproc->auto_boot) 22958c2ecf20Sopenharmony_ci rproc_shutdown(rproc); 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci mutex_lock(&rproc->lock); 22988c2ecf20Sopenharmony_ci rproc->state = RPROC_DELETED; 22998c2ecf20Sopenharmony_ci mutex_unlock(&rproc->lock); 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci rproc_delete_debug_dir(rproc); 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci /* the rproc is downref'ed as soon as it's removed from the klist */ 23048c2ecf20Sopenharmony_ci mutex_lock(&rproc_list_mutex); 23058c2ecf20Sopenharmony_ci list_del_rcu(&rproc->node); 23068c2ecf20Sopenharmony_ci mutex_unlock(&rproc_list_mutex); 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci /* Ensure that no readers of rproc_list are still active */ 23098c2ecf20Sopenharmony_ci synchronize_rcu(); 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci device_del(&rproc->dev); 23128c2ecf20Sopenharmony_ci rproc_char_device_remove(rproc); 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci return 0; 23158c2ecf20Sopenharmony_ci} 23168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_del); 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_cistatic void devm_rproc_free(struct device *dev, void *res) 23198c2ecf20Sopenharmony_ci{ 23208c2ecf20Sopenharmony_ci rproc_free(*(struct rproc **)res); 23218c2ecf20Sopenharmony_ci} 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci/** 23248c2ecf20Sopenharmony_ci * devm_rproc_alloc() - resource managed rproc_alloc() 23258c2ecf20Sopenharmony_ci * @dev: the underlying device 23268c2ecf20Sopenharmony_ci * @name: name of this remote processor 23278c2ecf20Sopenharmony_ci * @ops: platform-specific handlers (mainly start/stop) 23288c2ecf20Sopenharmony_ci * @firmware: name of firmware file to load, can be NULL 23298c2ecf20Sopenharmony_ci * @len: length of private data needed by the rproc driver (in bytes) 23308c2ecf20Sopenharmony_ci * 23318c2ecf20Sopenharmony_ci * This function performs like rproc_alloc() but the acquired rproc device will 23328c2ecf20Sopenharmony_ci * automatically be released on driver detach. 23338c2ecf20Sopenharmony_ci * 23348c2ecf20Sopenharmony_ci * Returns: new rproc instance, or NULL on failure 23358c2ecf20Sopenharmony_ci */ 23368c2ecf20Sopenharmony_cistruct rproc *devm_rproc_alloc(struct device *dev, const char *name, 23378c2ecf20Sopenharmony_ci const struct rproc_ops *ops, 23388c2ecf20Sopenharmony_ci const char *firmware, int len) 23398c2ecf20Sopenharmony_ci{ 23408c2ecf20Sopenharmony_ci struct rproc **ptr, *rproc; 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_rproc_free, sizeof(*ptr), GFP_KERNEL); 23438c2ecf20Sopenharmony_ci if (!ptr) 23448c2ecf20Sopenharmony_ci return NULL; 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci rproc = rproc_alloc(dev, name, ops, firmware, len); 23478c2ecf20Sopenharmony_ci if (rproc) { 23488c2ecf20Sopenharmony_ci *ptr = rproc; 23498c2ecf20Sopenharmony_ci devres_add(dev, ptr); 23508c2ecf20Sopenharmony_ci } else { 23518c2ecf20Sopenharmony_ci devres_free(ptr); 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci return rproc; 23558c2ecf20Sopenharmony_ci} 23568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_rproc_alloc); 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci/** 23598c2ecf20Sopenharmony_ci * rproc_add_subdev() - add a subdevice to a remoteproc 23608c2ecf20Sopenharmony_ci * @rproc: rproc handle to add the subdevice to 23618c2ecf20Sopenharmony_ci * @subdev: subdev handle to register 23628c2ecf20Sopenharmony_ci * 23638c2ecf20Sopenharmony_ci * Caller is responsible for populating optional subdevice function pointers. 23648c2ecf20Sopenharmony_ci */ 23658c2ecf20Sopenharmony_civoid rproc_add_subdev(struct rproc *rproc, struct rproc_subdev *subdev) 23668c2ecf20Sopenharmony_ci{ 23678c2ecf20Sopenharmony_ci list_add_tail(&subdev->node, &rproc->subdevs); 23688c2ecf20Sopenharmony_ci} 23698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_add_subdev); 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci/** 23728c2ecf20Sopenharmony_ci * rproc_remove_subdev() - remove a subdevice from a remoteproc 23738c2ecf20Sopenharmony_ci * @rproc: rproc handle to remove the subdevice from 23748c2ecf20Sopenharmony_ci * @subdev: subdev handle, previously registered with rproc_add_subdev() 23758c2ecf20Sopenharmony_ci */ 23768c2ecf20Sopenharmony_civoid rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev) 23778c2ecf20Sopenharmony_ci{ 23788c2ecf20Sopenharmony_ci list_del(&subdev->node); 23798c2ecf20Sopenharmony_ci} 23808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_remove_subdev); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci/** 23838c2ecf20Sopenharmony_ci * rproc_get_by_child() - acquire rproc handle of @dev's ancestor 23848c2ecf20Sopenharmony_ci * @dev: child device to find ancestor of 23858c2ecf20Sopenharmony_ci * 23868c2ecf20Sopenharmony_ci * Returns the ancestor rproc instance, or NULL if not found. 23878c2ecf20Sopenharmony_ci */ 23888c2ecf20Sopenharmony_cistruct rproc *rproc_get_by_child(struct device *dev) 23898c2ecf20Sopenharmony_ci{ 23908c2ecf20Sopenharmony_ci for (dev = dev->parent; dev; dev = dev->parent) { 23918c2ecf20Sopenharmony_ci if (dev->type == &rproc_type) 23928c2ecf20Sopenharmony_ci return dev->driver_data; 23938c2ecf20Sopenharmony_ci } 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci return NULL; 23968c2ecf20Sopenharmony_ci} 23978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_get_by_child); 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci/** 24008c2ecf20Sopenharmony_ci * rproc_report_crash() - rproc crash reporter function 24018c2ecf20Sopenharmony_ci * @rproc: remote processor 24028c2ecf20Sopenharmony_ci * @type: crash type 24038c2ecf20Sopenharmony_ci * 24048c2ecf20Sopenharmony_ci * This function must be called every time a crash is detected by the low-level 24058c2ecf20Sopenharmony_ci * drivers implementing a specific remoteproc. This should not be called from a 24068c2ecf20Sopenharmony_ci * non-remoteproc driver. 24078c2ecf20Sopenharmony_ci * 24088c2ecf20Sopenharmony_ci * This function can be called from atomic/interrupt context. 24098c2ecf20Sopenharmony_ci */ 24108c2ecf20Sopenharmony_civoid rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type) 24118c2ecf20Sopenharmony_ci{ 24128c2ecf20Sopenharmony_ci if (!rproc) { 24138c2ecf20Sopenharmony_ci pr_err("NULL rproc pointer\n"); 24148c2ecf20Sopenharmony_ci return; 24158c2ecf20Sopenharmony_ci } 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci /* Prevent suspend while the remoteproc is being recovered */ 24188c2ecf20Sopenharmony_ci pm_stay_awake(rproc->dev.parent); 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci dev_err(&rproc->dev, "crash detected in %s: type %s\n", 24218c2ecf20Sopenharmony_ci rproc->name, rproc_crash_to_string(type)); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci /* create a new task to handle the error */ 24248c2ecf20Sopenharmony_ci schedule_work(&rproc->crash_handler); 24258c2ecf20Sopenharmony_ci} 24268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rproc_report_crash); 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_cistatic int rproc_panic_handler(struct notifier_block *nb, unsigned long event, 24298c2ecf20Sopenharmony_ci void *ptr) 24308c2ecf20Sopenharmony_ci{ 24318c2ecf20Sopenharmony_ci unsigned int longest = 0; 24328c2ecf20Sopenharmony_ci struct rproc *rproc; 24338c2ecf20Sopenharmony_ci unsigned int d; 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci rcu_read_lock(); 24368c2ecf20Sopenharmony_ci list_for_each_entry_rcu(rproc, &rproc_list, node) { 24378c2ecf20Sopenharmony_ci if (!rproc->ops->panic || rproc->state != RPROC_RUNNING) 24388c2ecf20Sopenharmony_ci continue; 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci d = rproc->ops->panic(rproc); 24418c2ecf20Sopenharmony_ci longest = max(longest, d); 24428c2ecf20Sopenharmony_ci } 24438c2ecf20Sopenharmony_ci rcu_read_unlock(); 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci /* 24468c2ecf20Sopenharmony_ci * Delay for the longest requested duration before returning. This can 24478c2ecf20Sopenharmony_ci * be used by the remoteproc drivers to give the remote processor time 24488c2ecf20Sopenharmony_ci * to perform any requested operations (such as flush caches), when 24498c2ecf20Sopenharmony_ci * it's not possible to signal the Linux side due to the panic. 24508c2ecf20Sopenharmony_ci */ 24518c2ecf20Sopenharmony_ci mdelay(longest); 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci return NOTIFY_DONE; 24548c2ecf20Sopenharmony_ci} 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_cistatic void __init rproc_init_panic(void) 24578c2ecf20Sopenharmony_ci{ 24588c2ecf20Sopenharmony_ci rproc_panic_nb.notifier_call = rproc_panic_handler; 24598c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, &rproc_panic_nb); 24608c2ecf20Sopenharmony_ci} 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_cistatic void __exit rproc_exit_panic(void) 24638c2ecf20Sopenharmony_ci{ 24648c2ecf20Sopenharmony_ci atomic_notifier_chain_unregister(&panic_notifier_list, &rproc_panic_nb); 24658c2ecf20Sopenharmony_ci} 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_cistatic int __init remoteproc_init(void) 24688c2ecf20Sopenharmony_ci{ 24698c2ecf20Sopenharmony_ci rproc_init_sysfs(); 24708c2ecf20Sopenharmony_ci rproc_init_debugfs(); 24718c2ecf20Sopenharmony_ci rproc_init_cdev(); 24728c2ecf20Sopenharmony_ci rproc_init_panic(); 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci return 0; 24758c2ecf20Sopenharmony_ci} 24768c2ecf20Sopenharmony_cisubsys_initcall(remoteproc_init); 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_cistatic void __exit remoteproc_exit(void) 24798c2ecf20Sopenharmony_ci{ 24808c2ecf20Sopenharmony_ci ida_destroy(&rproc_dev_index); 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci rproc_exit_panic(); 24838c2ecf20Sopenharmony_ci rproc_exit_debugfs(); 24848c2ecf20Sopenharmony_ci rproc_exit_sysfs(); 24858c2ecf20Sopenharmony_ci} 24868c2ecf20Sopenharmony_cimodule_exit(remoteproc_exit); 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 24898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic Remote Processor Framework"); 2490