162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Remote Processor Framework
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc.
662306a36Sopenharmony_ci * Copyright (C) 2011 Google, Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Ohad Ben-Cohen <ohad@wizery.com>
962306a36Sopenharmony_ci * Brian Swetland <swetland@google.com>
1062306a36Sopenharmony_ci * Mark Grosen <mgrosen@ti.com>
1162306a36Sopenharmony_ci * Fernando Guzman Lugo <fernando.lugo@ti.com>
1262306a36Sopenharmony_ci * Suman Anna <s-anna@ti.com>
1362306a36Sopenharmony_ci * Robert Tivy <rtivy@ti.com>
1462306a36Sopenharmony_ci * Armando Uribe De Leon <x0095078@ti.com>
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define pr_fmt(fmt)    "%s: " fmt, __func__
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/device.h>
2362306a36Sopenharmony_ci#include <linux/panic_notifier.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci#include <linux/mutex.h>
2662306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2762306a36Sopenharmony_ci#include <linux/firmware.h>
2862306a36Sopenharmony_ci#include <linux/string.h>
2962306a36Sopenharmony_ci#include <linux/debugfs.h>
3062306a36Sopenharmony_ci#include <linux/rculist.h>
3162306a36Sopenharmony_ci#include <linux/remoteproc.h>
3262306a36Sopenharmony_ci#include <linux/iommu.h>
3362306a36Sopenharmony_ci#include <linux/idr.h>
3462306a36Sopenharmony_ci#include <linux/elf.h>
3562306a36Sopenharmony_ci#include <linux/crc32.h>
3662306a36Sopenharmony_ci#include <linux/of_reserved_mem.h>
3762306a36Sopenharmony_ci#include <linux/virtio_ids.h>
3862306a36Sopenharmony_ci#include <linux/virtio_ring.h>
3962306a36Sopenharmony_ci#include <asm/byteorder.h>
4062306a36Sopenharmony_ci#include <linux/platform_device.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include "remoteproc_internal.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic DEFINE_MUTEX(rproc_list_mutex);
4762306a36Sopenharmony_cistatic LIST_HEAD(rproc_list);
4862306a36Sopenharmony_cistatic struct notifier_block rproc_panic_nb;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_citypedef int (*rproc_handle_resource_t)(struct rproc *rproc,
5162306a36Sopenharmony_ci				 void *, int offset, int avail);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int rproc_alloc_carveout(struct rproc *rproc,
5462306a36Sopenharmony_ci				struct rproc_mem_entry *mem);
5562306a36Sopenharmony_cistatic int rproc_release_carveout(struct rproc *rproc,
5662306a36Sopenharmony_ci				  struct rproc_mem_entry *mem);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Unique indices for remoteproc devices */
5962306a36Sopenharmony_cistatic DEFINE_IDA(rproc_dev_index);
6062306a36Sopenharmony_cistatic struct workqueue_struct *rproc_recovery_wq;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const char * const rproc_crash_names[] = {
6362306a36Sopenharmony_ci	[RPROC_MMUFAULT]	= "mmufault",
6462306a36Sopenharmony_ci	[RPROC_WATCHDOG]	= "watchdog",
6562306a36Sopenharmony_ci	[RPROC_FATAL_ERROR]	= "fatal error",
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* translate rproc_crash_type to string */
6962306a36Sopenharmony_cistatic const char *rproc_crash_to_string(enum rproc_crash_type type)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	if (type < ARRAY_SIZE(rproc_crash_names))
7262306a36Sopenharmony_ci		return rproc_crash_names[type];
7362306a36Sopenharmony_ci	return "unknown";
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * This is the IOMMU fault handler we register with the IOMMU API
7862306a36Sopenharmony_ci * (when relevant; not all remote processors access memory through
7962306a36Sopenharmony_ci * an IOMMU).
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * IOMMU core will invoke this handler whenever the remote processor
8262306a36Sopenharmony_ci * will try to access an unmapped device address.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_cistatic int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
8562306a36Sopenharmony_ci			     unsigned long iova, int flags, void *token)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct rproc *rproc = token;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	rproc_report_crash(rproc, RPROC_MMUFAULT);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/*
9462306a36Sopenharmony_ci	 * Let the iommu core know we're not really handling this fault;
9562306a36Sopenharmony_ci	 * we just used it as a recovery trigger.
9662306a36Sopenharmony_ci	 */
9762306a36Sopenharmony_ci	return -ENOSYS;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int rproc_enable_iommu(struct rproc *rproc)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct iommu_domain *domain;
10362306a36Sopenharmony_ci	struct device *dev = rproc->dev.parent;
10462306a36Sopenharmony_ci	int ret;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (!rproc->has_iommu) {
10762306a36Sopenharmony_ci		dev_dbg(dev, "iommu not present\n");
10862306a36Sopenharmony_ci		return 0;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	domain = iommu_domain_alloc(dev->bus);
11262306a36Sopenharmony_ci	if (!domain) {
11362306a36Sopenharmony_ci		dev_err(dev, "can't alloc iommu domain\n");
11462306a36Sopenharmony_ci		return -ENOMEM;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	iommu_set_fault_handler(domain, rproc_iommu_fault, rproc);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	ret = iommu_attach_device(domain, dev);
12062306a36Sopenharmony_ci	if (ret) {
12162306a36Sopenharmony_ci		dev_err(dev, "can't attach iommu device: %d\n", ret);
12262306a36Sopenharmony_ci		goto free_domain;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	rproc->domain = domain;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cifree_domain:
13062306a36Sopenharmony_ci	iommu_domain_free(domain);
13162306a36Sopenharmony_ci	return ret;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic void rproc_disable_iommu(struct rproc *rproc)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct iommu_domain *domain = rproc->domain;
13762306a36Sopenharmony_ci	struct device *dev = rproc->dev.parent;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (!domain)
14062306a36Sopenharmony_ci		return;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	iommu_detach_device(domain, dev);
14362306a36Sopenharmony_ci	iommu_domain_free(domain);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciphys_addr_t rproc_va_to_pa(void *cpu_addr)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	/*
14962306a36Sopenharmony_ci	 * Return physical address according to virtual address location
15062306a36Sopenharmony_ci	 * - in vmalloc: if region ioremapped or defined as dma_alloc_coherent
15162306a36Sopenharmony_ci	 * - in kernel: if region allocated in generic dma memory pool
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	if (is_vmalloc_addr(cpu_addr)) {
15462306a36Sopenharmony_ci		return page_to_phys(vmalloc_to_page(cpu_addr)) +
15562306a36Sopenharmony_ci				    offset_in_page(cpu_addr);
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	WARN_ON(!virt_addr_valid(cpu_addr));
15962306a36Sopenharmony_ci	return virt_to_phys(cpu_addr);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_va_to_pa);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/**
16462306a36Sopenharmony_ci * rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address
16562306a36Sopenharmony_ci * @rproc: handle of a remote processor
16662306a36Sopenharmony_ci * @da: remoteproc device address to translate
16762306a36Sopenharmony_ci * @len: length of the memory region @da is pointing to
16862306a36Sopenharmony_ci * @is_iomem: optional pointer filled in to indicate if @da is iomapped memory
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * Some remote processors will ask us to allocate them physically contiguous
17162306a36Sopenharmony_ci * memory regions (which we call "carveouts"), and map them to specific
17262306a36Sopenharmony_ci * device addresses (which are hardcoded in the firmware). They may also have
17362306a36Sopenharmony_ci * dedicated memory regions internal to the processors, and use them either
17462306a36Sopenharmony_ci * exclusively or alongside carveouts.
17562306a36Sopenharmony_ci *
17662306a36Sopenharmony_ci * They may then ask us to copy objects into specific device addresses (e.g.
17762306a36Sopenharmony_ci * code/data sections) or expose us certain symbols in other device address
17862306a36Sopenharmony_ci * (e.g. their trace buffer).
17962306a36Sopenharmony_ci *
18062306a36Sopenharmony_ci * This function is a helper function with which we can go over the allocated
18162306a36Sopenharmony_ci * carveouts and translate specific device addresses to kernel virtual addresses
18262306a36Sopenharmony_ci * so we can access the referenced memory. This function also allows to perform
18362306a36Sopenharmony_ci * translations on the internal remoteproc memory regions through a platform
18462306a36Sopenharmony_ci * implementation specific da_to_va ops, if present.
18562306a36Sopenharmony_ci *
18662306a36Sopenharmony_ci * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too,
18762306a36Sopenharmony_ci * but only on kernel direct mapped RAM memory. Instead, we're just using
18862306a36Sopenharmony_ci * here the output of the DMA API for the carveouts, which should be more
18962306a36Sopenharmony_ci * correct.
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * Return: a valid kernel address on success or NULL on failure
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_civoid *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct rproc_mem_entry *carveout;
19662306a36Sopenharmony_ci	void *ptr = NULL;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (rproc->ops->da_to_va) {
19962306a36Sopenharmony_ci		ptr = rproc->ops->da_to_va(rproc, da, len, is_iomem);
20062306a36Sopenharmony_ci		if (ptr)
20162306a36Sopenharmony_ci			goto out;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	list_for_each_entry(carveout, &rproc->carveouts, node) {
20562306a36Sopenharmony_ci		int offset = da - carveout->da;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		/*  Verify that carveout is allocated */
20862306a36Sopenharmony_ci		if (!carveout->va)
20962306a36Sopenharmony_ci			continue;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		/* try next carveout if da is too small */
21262306a36Sopenharmony_ci		if (offset < 0)
21362306a36Sopenharmony_ci			continue;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		/* try next carveout if da is too large */
21662306a36Sopenharmony_ci		if (offset + len > carveout->len)
21762306a36Sopenharmony_ci			continue;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		ptr = carveout->va + offset;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		if (is_iomem)
22262306a36Sopenharmony_ci			*is_iomem = carveout->is_iomem;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		break;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ciout:
22862306a36Sopenharmony_ci	return ptr;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_da_to_va);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/**
23362306a36Sopenharmony_ci * rproc_find_carveout_by_name() - lookup the carveout region by a name
23462306a36Sopenharmony_ci * @rproc: handle of a remote processor
23562306a36Sopenharmony_ci * @name: carveout name to find (format string)
23662306a36Sopenharmony_ci * @...: optional parameters matching @name string
23762306a36Sopenharmony_ci *
23862306a36Sopenharmony_ci * Platform driver has the capability to register some pre-allacoted carveout
23962306a36Sopenharmony_ci * (physically contiguous memory regions) before rproc firmware loading and
24062306a36Sopenharmony_ci * associated resource table analysis. These regions may be dedicated memory
24162306a36Sopenharmony_ci * regions internal to the coprocessor or specified DDR region with specific
24262306a36Sopenharmony_ci * attributes
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci * This function is a helper function with which we can go over the
24562306a36Sopenharmony_ci * allocated carveouts and return associated region characteristics like
24662306a36Sopenharmony_ci * coprocessor address, length or processor virtual address.
24762306a36Sopenharmony_ci *
24862306a36Sopenharmony_ci * Return: a valid pointer on carveout entry on success or NULL on failure.
24962306a36Sopenharmony_ci */
25062306a36Sopenharmony_ci__printf(2, 3)
25162306a36Sopenharmony_cistruct rproc_mem_entry *
25262306a36Sopenharmony_cirproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	va_list args;
25562306a36Sopenharmony_ci	char _name[32];
25662306a36Sopenharmony_ci	struct rproc_mem_entry *carveout, *mem = NULL;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!name)
25962306a36Sopenharmony_ci		return NULL;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	va_start(args, name);
26262306a36Sopenharmony_ci	vsnprintf(_name, sizeof(_name), name, args);
26362306a36Sopenharmony_ci	va_end(args);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	list_for_each_entry(carveout, &rproc->carveouts, node) {
26662306a36Sopenharmony_ci		/* Compare carveout and requested names */
26762306a36Sopenharmony_ci		if (!strcmp(carveout->name, _name)) {
26862306a36Sopenharmony_ci			mem = carveout;
26962306a36Sopenharmony_ci			break;
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return mem;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/**
27762306a36Sopenharmony_ci * rproc_check_carveout_da() - Check specified carveout da configuration
27862306a36Sopenharmony_ci * @rproc: handle of a remote processor
27962306a36Sopenharmony_ci * @mem: pointer on carveout to check
28062306a36Sopenharmony_ci * @da: area device address
28162306a36Sopenharmony_ci * @len: associated area size
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * This function is a helper function to verify requested device area (couple
28462306a36Sopenharmony_ci * da, len) is part of specified carveout.
28562306a36Sopenharmony_ci * If da is not set (defined as FW_RSC_ADDR_ANY), only requested length is
28662306a36Sopenharmony_ci * checked.
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * Return: 0 if carveout matches request else error
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic int rproc_check_carveout_da(struct rproc *rproc,
29162306a36Sopenharmony_ci				   struct rproc_mem_entry *mem, u32 da, u32 len)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
29462306a36Sopenharmony_ci	int delta;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* Check requested resource length */
29762306a36Sopenharmony_ci	if (len > mem->len) {
29862306a36Sopenharmony_ci		dev_err(dev, "Registered carveout doesn't fit len request\n");
29962306a36Sopenharmony_ci		return -EINVAL;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) {
30362306a36Sopenharmony_ci		/* Address doesn't match registered carveout configuration */
30462306a36Sopenharmony_ci		return -EINVAL;
30562306a36Sopenharmony_ci	} else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) {
30662306a36Sopenharmony_ci		delta = da - mem->da;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		/* Check requested resource belongs to registered carveout */
30962306a36Sopenharmony_ci		if (delta < 0) {
31062306a36Sopenharmony_ci			dev_err(dev,
31162306a36Sopenharmony_ci				"Registered carveout doesn't fit da request\n");
31262306a36Sopenharmony_ci			return -EINVAL;
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		if (delta + len > mem->len) {
31662306a36Sopenharmony_ci			dev_err(dev,
31762306a36Sopenharmony_ci				"Registered carveout doesn't fit len request\n");
31862306a36Sopenharmony_ci			return -EINVAL;
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ciint rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct rproc *rproc = rvdev->rproc;
32862306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
32962306a36Sopenharmony_ci	struct rproc_vring *rvring = &rvdev->vring[i];
33062306a36Sopenharmony_ci	struct fw_rsc_vdev *rsc;
33162306a36Sopenharmony_ci	int ret, notifyid;
33262306a36Sopenharmony_ci	struct rproc_mem_entry *mem;
33362306a36Sopenharmony_ci	size_t size;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* actual size of vring (in bytes) */
33662306a36Sopenharmony_ci	size = PAGE_ALIGN(vring_size(rvring->num, rvring->align));
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* Search for pre-registered carveout */
34162306a36Sopenharmony_ci	mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index,
34262306a36Sopenharmony_ci					  i);
34362306a36Sopenharmony_ci	if (mem) {
34462306a36Sopenharmony_ci		if (rproc_check_carveout_da(rproc, mem, rsc->vring[i].da, size))
34562306a36Sopenharmony_ci			return -ENOMEM;
34662306a36Sopenharmony_ci	} else {
34762306a36Sopenharmony_ci		/* Register carveout in list */
34862306a36Sopenharmony_ci		mem = rproc_mem_entry_init(dev, NULL, 0,
34962306a36Sopenharmony_ci					   size, rsc->vring[i].da,
35062306a36Sopenharmony_ci					   rproc_alloc_carveout,
35162306a36Sopenharmony_ci					   rproc_release_carveout,
35262306a36Sopenharmony_ci					   "vdev%dvring%d",
35362306a36Sopenharmony_ci					   rvdev->index, i);
35462306a36Sopenharmony_ci		if (!mem) {
35562306a36Sopenharmony_ci			dev_err(dev, "Can't allocate memory entry structure\n");
35662306a36Sopenharmony_ci			return -ENOMEM;
35762306a36Sopenharmony_ci		}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		rproc_add_carveout(rproc, mem);
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/*
36362306a36Sopenharmony_ci	 * Assign an rproc-wide unique index for this vring
36462306a36Sopenharmony_ci	 * TODO: assign a notifyid for rvdev updates as well
36562306a36Sopenharmony_ci	 * TODO: support predefined notifyids (via resource table)
36662306a36Sopenharmony_ci	 */
36762306a36Sopenharmony_ci	ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL);
36862306a36Sopenharmony_ci	if (ret < 0) {
36962306a36Sopenharmony_ci		dev_err(dev, "idr_alloc failed: %d\n", ret);
37062306a36Sopenharmony_ci		return ret;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci	notifyid = ret;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* Potentially bump max_notifyid */
37562306a36Sopenharmony_ci	if (notifyid > rproc->max_notifyid)
37662306a36Sopenharmony_ci		rproc->max_notifyid = notifyid;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	rvring->notifyid = notifyid;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Let the rproc know the notifyid of this vring.*/
38162306a36Sopenharmony_ci	rsc->vring[i].notifyid = notifyid;
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ciint
38662306a36Sopenharmony_cirproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct rproc *rproc = rvdev->rproc;
38962306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
39062306a36Sopenharmony_ci	struct fw_rsc_vdev_vring *vring = &rsc->vring[i];
39162306a36Sopenharmony_ci	struct rproc_vring *rvring = &rvdev->vring[i];
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	dev_dbg(dev, "vdev rsc: vring%d: da 0x%x, qsz %d, align %d\n",
39462306a36Sopenharmony_ci		i, vring->da, vring->num, vring->align);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* verify queue size and vring alignment are sane */
39762306a36Sopenharmony_ci	if (!vring->num || !vring->align) {
39862306a36Sopenharmony_ci		dev_err(dev, "invalid qsz (%d) or alignment (%d)\n",
39962306a36Sopenharmony_ci			vring->num, vring->align);
40062306a36Sopenharmony_ci		return -EINVAL;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	rvring->num = vring->num;
40462306a36Sopenharmony_ci	rvring->align = vring->align;
40562306a36Sopenharmony_ci	rvring->rvdev = rvdev;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_civoid rproc_free_vring(struct rproc_vring *rvring)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct rproc *rproc = rvring->rvdev->rproc;
41362306a36Sopenharmony_ci	int idx = rvring - rvring->rvdev->vring;
41462306a36Sopenharmony_ci	struct fw_rsc_vdev *rsc;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	idr_remove(&rproc->notifyids, rvring->notifyid);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	/*
41962306a36Sopenharmony_ci	 * At this point rproc_stop() has been called and the installed resource
42062306a36Sopenharmony_ci	 * table in the remote processor memory may no longer be accessible. As
42162306a36Sopenharmony_ci	 * such and as per rproc_stop(), rproc->table_ptr points to the cached
42262306a36Sopenharmony_ci	 * resource table (rproc->cached_table).  The cached resource table is
42362306a36Sopenharmony_ci	 * only available when a remote processor has been booted by the
42462306a36Sopenharmony_ci	 * remoteproc core, otherwise it is NULL.
42562306a36Sopenharmony_ci	 *
42662306a36Sopenharmony_ci	 * Based on the above, reset the virtio device section in the cached
42762306a36Sopenharmony_ci	 * resource table only if there is one to work with.
42862306a36Sopenharmony_ci	 */
42962306a36Sopenharmony_ci	if (rproc->table_ptr) {
43062306a36Sopenharmony_ci		rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset;
43162306a36Sopenharmony_ci		rsc->vring[idx].da = 0;
43262306a36Sopenharmony_ci		rsc->vring[idx].notifyid = -1;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_civoid rproc_add_rvdev(struct rproc *rproc, struct rproc_vdev *rvdev)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	if (rvdev && rproc)
43962306a36Sopenharmony_ci		list_add_tail(&rvdev->node, &rproc->rvdevs);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_civoid rproc_remove_rvdev(struct rproc_vdev *rvdev)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	if (rvdev)
44562306a36Sopenharmony_ci		list_del(&rvdev->node);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci/**
44862306a36Sopenharmony_ci * rproc_handle_vdev() - handle a vdev fw resource
44962306a36Sopenharmony_ci * @rproc: the remote processor
45062306a36Sopenharmony_ci * @ptr: the vring resource descriptor
45162306a36Sopenharmony_ci * @offset: offset of the resource entry
45262306a36Sopenharmony_ci * @avail: size of available data (for sanity checking the image)
45362306a36Sopenharmony_ci *
45462306a36Sopenharmony_ci * This resource entry requests the host to statically register a virtio
45562306a36Sopenharmony_ci * device (vdev), and setup everything needed to support it. It contains
45662306a36Sopenharmony_ci * everything needed to make it possible: the virtio device id, virtio
45762306a36Sopenharmony_ci * device features, vrings information, virtio config space, etc...
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * Before registering the vdev, the vrings are allocated from non-cacheable
46062306a36Sopenharmony_ci * physically contiguous memory. Currently we only support two vrings per
46162306a36Sopenharmony_ci * remote processor (temporary limitation). We might also want to consider
46262306a36Sopenharmony_ci * doing the vring allocation only later when ->find_vqs() is invoked, and
46362306a36Sopenharmony_ci * then release them upon ->del_vqs().
46462306a36Sopenharmony_ci *
46562306a36Sopenharmony_ci * Note: @da is currently not really handled correctly: we dynamically
46662306a36Sopenharmony_ci * allocate it using the DMA API, ignoring requested hard coded addresses,
46762306a36Sopenharmony_ci * and we don't take care of any required IOMMU programming. This is all
46862306a36Sopenharmony_ci * going to be taken care of when the generic iommu-based DMA API will be
46962306a36Sopenharmony_ci * merged. Meanwhile, statically-addressed iommu-based firmware images should
47062306a36Sopenharmony_ci * use RSC_DEVMEM resource entries to map their required @da to the physical
47162306a36Sopenharmony_ci * address of their base CMA region (ouch, hacky!).
47262306a36Sopenharmony_ci *
47362306a36Sopenharmony_ci * Return: 0 on success, or an appropriate error code otherwise
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_cistatic int rproc_handle_vdev(struct rproc *rproc, void *ptr,
47662306a36Sopenharmony_ci			     int offset, int avail)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct fw_rsc_vdev *rsc = ptr;
47962306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
48062306a36Sopenharmony_ci	struct rproc_vdev *rvdev;
48162306a36Sopenharmony_ci	size_t rsc_size;
48262306a36Sopenharmony_ci	struct rproc_vdev_data rvdev_data;
48362306a36Sopenharmony_ci	struct platform_device *pdev;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* make sure resource isn't truncated */
48662306a36Sopenharmony_ci	rsc_size = struct_size(rsc, vring, rsc->num_of_vrings);
48762306a36Sopenharmony_ci	if (size_add(rsc_size, rsc->config_len) > avail) {
48862306a36Sopenharmony_ci		dev_err(dev, "vdev rsc is truncated\n");
48962306a36Sopenharmony_ci		return -EINVAL;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* make sure reserved bytes are zeroes */
49362306a36Sopenharmony_ci	if (rsc->reserved[0] || rsc->reserved[1]) {
49462306a36Sopenharmony_ci		dev_err(dev, "vdev rsc has non zero reserved bytes\n");
49562306a36Sopenharmony_ci		return -EINVAL;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	dev_dbg(dev, "vdev rsc: id %d, dfeatures 0x%x, cfg len %d, %d vrings\n",
49962306a36Sopenharmony_ci		rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* we currently support only two vrings per rvdev */
50262306a36Sopenharmony_ci	if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) {
50362306a36Sopenharmony_ci		dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings);
50462306a36Sopenharmony_ci		return -EINVAL;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	rvdev_data.id = rsc->id;
50862306a36Sopenharmony_ci	rvdev_data.index = rproc->nb_vdev++;
50962306a36Sopenharmony_ci	rvdev_data.rsc_offset = offset;
51062306a36Sopenharmony_ci	rvdev_data.rsc = rsc;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/*
51362306a36Sopenharmony_ci	 * When there is more than one remote processor, rproc->nb_vdev number is
51462306a36Sopenharmony_ci	 * same for each separate instances of "rproc". If rvdev_data.index is used
51562306a36Sopenharmony_ci	 * as device id, then we get duplication in sysfs, so need to use
51662306a36Sopenharmony_ci	 * PLATFORM_DEVID_AUTO to auto select device id.
51762306a36Sopenharmony_ci	 */
51862306a36Sopenharmony_ci	pdev = platform_device_register_data(dev, "rproc-virtio", PLATFORM_DEVID_AUTO, &rvdev_data,
51962306a36Sopenharmony_ci					     sizeof(rvdev_data));
52062306a36Sopenharmony_ci	if (IS_ERR(pdev)) {
52162306a36Sopenharmony_ci		dev_err(dev, "failed to create rproc-virtio device\n");
52262306a36Sopenharmony_ci		return PTR_ERR(pdev);
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return 0;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/**
52962306a36Sopenharmony_ci * rproc_handle_trace() - handle a shared trace buffer resource
53062306a36Sopenharmony_ci * @rproc: the remote processor
53162306a36Sopenharmony_ci * @ptr: the trace resource descriptor
53262306a36Sopenharmony_ci * @offset: offset of the resource entry
53362306a36Sopenharmony_ci * @avail: size of available data (for sanity checking the image)
53462306a36Sopenharmony_ci *
53562306a36Sopenharmony_ci * In case the remote processor dumps trace logs into memory,
53662306a36Sopenharmony_ci * export it via debugfs.
53762306a36Sopenharmony_ci *
53862306a36Sopenharmony_ci * Currently, the 'da' member of @rsc should contain the device address
53962306a36Sopenharmony_ci * where the remote processor is dumping the traces. Later we could also
54062306a36Sopenharmony_ci * support dynamically allocating this address using the generic
54162306a36Sopenharmony_ci * DMA API (but currently there isn't a use case for that).
54262306a36Sopenharmony_ci *
54362306a36Sopenharmony_ci * Return: 0 on success, or an appropriate error code otherwise
54462306a36Sopenharmony_ci */
54562306a36Sopenharmony_cistatic int rproc_handle_trace(struct rproc *rproc, void *ptr,
54662306a36Sopenharmony_ci			      int offset, int avail)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct fw_rsc_trace *rsc = ptr;
54962306a36Sopenharmony_ci	struct rproc_debug_trace *trace;
55062306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
55162306a36Sopenharmony_ci	char name[15];
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (sizeof(*rsc) > avail) {
55462306a36Sopenharmony_ci		dev_err(dev, "trace rsc is truncated\n");
55562306a36Sopenharmony_ci		return -EINVAL;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* make sure reserved bytes are zeroes */
55962306a36Sopenharmony_ci	if (rsc->reserved) {
56062306a36Sopenharmony_ci		dev_err(dev, "trace rsc has non zero reserved bytes\n");
56162306a36Sopenharmony_ci		return -EINVAL;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	trace = kzalloc(sizeof(*trace), GFP_KERNEL);
56562306a36Sopenharmony_ci	if (!trace)
56662306a36Sopenharmony_ci		return -ENOMEM;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* set the trace buffer dma properties */
56962306a36Sopenharmony_ci	trace->trace_mem.len = rsc->len;
57062306a36Sopenharmony_ci	trace->trace_mem.da = rsc->da;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* set pointer on rproc device */
57362306a36Sopenharmony_ci	trace->rproc = rproc;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* make sure snprintf always null terminates, even if truncating */
57662306a36Sopenharmony_ci	snprintf(name, sizeof(name), "trace%d", rproc->num_traces);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* create the debugfs entry */
57962306a36Sopenharmony_ci	trace->tfile = rproc_create_trace_file(name, rproc, trace);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	list_add_tail(&trace->node, &rproc->traces);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	rproc->num_traces++;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	dev_dbg(dev, "%s added: da 0x%x, len 0x%x\n",
58662306a36Sopenharmony_ci		name, rsc->da, rsc->len);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return 0;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci/**
59262306a36Sopenharmony_ci * rproc_handle_devmem() - handle devmem resource entry
59362306a36Sopenharmony_ci * @rproc: remote processor handle
59462306a36Sopenharmony_ci * @ptr: the devmem resource entry
59562306a36Sopenharmony_ci * @offset: offset of the resource entry
59662306a36Sopenharmony_ci * @avail: size of available data (for sanity checking the image)
59762306a36Sopenharmony_ci *
59862306a36Sopenharmony_ci * Remote processors commonly need to access certain on-chip peripherals.
59962306a36Sopenharmony_ci *
60062306a36Sopenharmony_ci * Some of these remote processors access memory via an iommu device,
60162306a36Sopenharmony_ci * and might require us to configure their iommu before they can access
60262306a36Sopenharmony_ci * the on-chip peripherals they need.
60362306a36Sopenharmony_ci *
60462306a36Sopenharmony_ci * This resource entry is a request to map such a peripheral device.
60562306a36Sopenharmony_ci *
60662306a36Sopenharmony_ci * These devmem entries will contain the physical address of the device in
60762306a36Sopenharmony_ci * the 'pa' member. If a specific device address is expected, then 'da' will
60862306a36Sopenharmony_ci * contain it (currently this is the only use case supported). 'len' will
60962306a36Sopenharmony_ci * contain the size of the physical region we need to map.
61062306a36Sopenharmony_ci *
61162306a36Sopenharmony_ci * Currently we just "trust" those devmem entries to contain valid physical
61262306a36Sopenharmony_ci * addresses, but this is going to change: we want the implementations to
61362306a36Sopenharmony_ci * tell us ranges of physical addresses the firmware is allowed to request,
61462306a36Sopenharmony_ci * and not allow firmwares to request access to physical addresses that
61562306a36Sopenharmony_ci * are outside those ranges.
61662306a36Sopenharmony_ci *
61762306a36Sopenharmony_ci * Return: 0 on success, or an appropriate error code otherwise
61862306a36Sopenharmony_ci */
61962306a36Sopenharmony_cistatic int rproc_handle_devmem(struct rproc *rproc, void *ptr,
62062306a36Sopenharmony_ci			       int offset, int avail)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct fw_rsc_devmem *rsc = ptr;
62362306a36Sopenharmony_ci	struct rproc_mem_entry *mapping;
62462306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
62562306a36Sopenharmony_ci	int ret;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* no point in handling this resource without a valid iommu domain */
62862306a36Sopenharmony_ci	if (!rproc->domain)
62962306a36Sopenharmony_ci		return -EINVAL;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (sizeof(*rsc) > avail) {
63262306a36Sopenharmony_ci		dev_err(dev, "devmem rsc is truncated\n");
63362306a36Sopenharmony_ci		return -EINVAL;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/* make sure reserved bytes are zeroes */
63762306a36Sopenharmony_ci	if (rsc->reserved) {
63862306a36Sopenharmony_ci		dev_err(dev, "devmem rsc has non zero reserved bytes\n");
63962306a36Sopenharmony_ci		return -EINVAL;
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
64362306a36Sopenharmony_ci	if (!mapping)
64462306a36Sopenharmony_ci		return -ENOMEM;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags,
64762306a36Sopenharmony_ci			GFP_KERNEL);
64862306a36Sopenharmony_ci	if (ret) {
64962306a36Sopenharmony_ci		dev_err(dev, "failed to map devmem: %d\n", ret);
65062306a36Sopenharmony_ci		goto out;
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	/*
65462306a36Sopenharmony_ci	 * We'll need this info later when we'll want to unmap everything
65562306a36Sopenharmony_ci	 * (e.g. on shutdown).
65662306a36Sopenharmony_ci	 *
65762306a36Sopenharmony_ci	 * We can't trust the remote processor not to change the resource
65862306a36Sopenharmony_ci	 * table, so we must maintain this info independently.
65962306a36Sopenharmony_ci	 */
66062306a36Sopenharmony_ci	mapping->da = rsc->da;
66162306a36Sopenharmony_ci	mapping->len = rsc->len;
66262306a36Sopenharmony_ci	list_add_tail(&mapping->node, &rproc->mappings);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	dev_dbg(dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n",
66562306a36Sopenharmony_ci		rsc->pa, rsc->da, rsc->len);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	return 0;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ciout:
67062306a36Sopenharmony_ci	kfree(mapping);
67162306a36Sopenharmony_ci	return ret;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci/**
67562306a36Sopenharmony_ci * rproc_alloc_carveout() - allocated specified carveout
67662306a36Sopenharmony_ci * @rproc: rproc handle
67762306a36Sopenharmony_ci * @mem: the memory entry to allocate
67862306a36Sopenharmony_ci *
67962306a36Sopenharmony_ci * This function allocate specified memory entry @mem using
68062306a36Sopenharmony_ci * dma_alloc_coherent() as default allocator
68162306a36Sopenharmony_ci *
68262306a36Sopenharmony_ci * Return: 0 on success, or an appropriate error code otherwise
68362306a36Sopenharmony_ci */
68462306a36Sopenharmony_cistatic int rproc_alloc_carveout(struct rproc *rproc,
68562306a36Sopenharmony_ci				struct rproc_mem_entry *mem)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	struct rproc_mem_entry *mapping = NULL;
68862306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
68962306a36Sopenharmony_ci	dma_addr_t dma;
69062306a36Sopenharmony_ci	void *va;
69162306a36Sopenharmony_ci	int ret;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	va = dma_alloc_coherent(dev->parent, mem->len, &dma, GFP_KERNEL);
69462306a36Sopenharmony_ci	if (!va) {
69562306a36Sopenharmony_ci		dev_err(dev->parent,
69662306a36Sopenharmony_ci			"failed to allocate dma memory: len 0x%zx\n",
69762306a36Sopenharmony_ci			mem->len);
69862306a36Sopenharmony_ci		return -ENOMEM;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%zx\n",
70262306a36Sopenharmony_ci		va, &dma, mem->len);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) {
70562306a36Sopenharmony_ci		/*
70662306a36Sopenharmony_ci		 * Check requested da is equal to dma address
70762306a36Sopenharmony_ci		 * and print a warn message in case of missalignment.
70862306a36Sopenharmony_ci		 * Don't stop rproc_start sequence as coprocessor may
70962306a36Sopenharmony_ci		 * build pa to da translation on its side.
71062306a36Sopenharmony_ci		 */
71162306a36Sopenharmony_ci		if (mem->da != (u32)dma)
71262306a36Sopenharmony_ci			dev_warn(dev->parent,
71362306a36Sopenharmony_ci				 "Allocated carveout doesn't fit device address request\n");
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/*
71762306a36Sopenharmony_ci	 * Ok, this is non-standard.
71862306a36Sopenharmony_ci	 *
71962306a36Sopenharmony_ci	 * Sometimes we can't rely on the generic iommu-based DMA API
72062306a36Sopenharmony_ci	 * to dynamically allocate the device address and then set the IOMMU
72162306a36Sopenharmony_ci	 * tables accordingly, because some remote processors might
72262306a36Sopenharmony_ci	 * _require_ us to use hard coded device addresses that their
72362306a36Sopenharmony_ci	 * firmware was compiled with.
72462306a36Sopenharmony_ci	 *
72562306a36Sopenharmony_ci	 * In this case, we must use the IOMMU API directly and map
72662306a36Sopenharmony_ci	 * the memory to the device address as expected by the remote
72762306a36Sopenharmony_ci	 * processor.
72862306a36Sopenharmony_ci	 *
72962306a36Sopenharmony_ci	 * Obviously such remote processor devices should not be configured
73062306a36Sopenharmony_ci	 * to use the iommu-based DMA API: we expect 'dma' to contain the
73162306a36Sopenharmony_ci	 * physical address in this case.
73262306a36Sopenharmony_ci	 */
73362306a36Sopenharmony_ci	if (mem->da != FW_RSC_ADDR_ANY && rproc->domain) {
73462306a36Sopenharmony_ci		mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
73562306a36Sopenharmony_ci		if (!mapping) {
73662306a36Sopenharmony_ci			ret = -ENOMEM;
73762306a36Sopenharmony_ci			goto dma_free;
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		ret = iommu_map(rproc->domain, mem->da, dma, mem->len,
74162306a36Sopenharmony_ci				mem->flags, GFP_KERNEL);
74262306a36Sopenharmony_ci		if (ret) {
74362306a36Sopenharmony_ci			dev_err(dev, "iommu_map failed: %d\n", ret);
74462306a36Sopenharmony_ci			goto free_mapping;
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci		/*
74862306a36Sopenharmony_ci		 * We'll need this info later when we'll want to unmap
74962306a36Sopenharmony_ci		 * everything (e.g. on shutdown).
75062306a36Sopenharmony_ci		 *
75162306a36Sopenharmony_ci		 * We can't trust the remote processor not to change the
75262306a36Sopenharmony_ci		 * resource table, so we must maintain this info independently.
75362306a36Sopenharmony_ci		 */
75462306a36Sopenharmony_ci		mapping->da = mem->da;
75562306a36Sopenharmony_ci		mapping->len = mem->len;
75662306a36Sopenharmony_ci		list_add_tail(&mapping->node, &rproc->mappings);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		dev_dbg(dev, "carveout mapped 0x%x to %pad\n",
75962306a36Sopenharmony_ci			mem->da, &dma);
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (mem->da == FW_RSC_ADDR_ANY) {
76362306a36Sopenharmony_ci		/* Update device address as undefined by requester */
76462306a36Sopenharmony_ci		if ((u64)dma & HIGH_BITS_MASK)
76562306a36Sopenharmony_ci			dev_warn(dev, "DMA address cast in 32bit to fit resource table format\n");
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		mem->da = (u32)dma;
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	mem->dma = dma;
77162306a36Sopenharmony_ci	mem->va = va;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	return 0;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cifree_mapping:
77662306a36Sopenharmony_ci	kfree(mapping);
77762306a36Sopenharmony_cidma_free:
77862306a36Sopenharmony_ci	dma_free_coherent(dev->parent, mem->len, va, dma);
77962306a36Sopenharmony_ci	return ret;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci/**
78362306a36Sopenharmony_ci * rproc_release_carveout() - release acquired carveout
78462306a36Sopenharmony_ci * @rproc: rproc handle
78562306a36Sopenharmony_ci * @mem: the memory entry to release
78662306a36Sopenharmony_ci *
78762306a36Sopenharmony_ci * This function releases specified memory entry @mem allocated via
78862306a36Sopenharmony_ci * rproc_alloc_carveout() function by @rproc.
78962306a36Sopenharmony_ci *
79062306a36Sopenharmony_ci * Return: 0 on success, or an appropriate error code otherwise
79162306a36Sopenharmony_ci */
79262306a36Sopenharmony_cistatic int rproc_release_carveout(struct rproc *rproc,
79362306a36Sopenharmony_ci				  struct rproc_mem_entry *mem)
79462306a36Sopenharmony_ci{
79562306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/* clean up carveout allocations */
79862306a36Sopenharmony_ci	dma_free_coherent(dev->parent, mem->len, mem->va, mem->dma);
79962306a36Sopenharmony_ci	return 0;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci/**
80362306a36Sopenharmony_ci * rproc_handle_carveout() - handle phys contig memory allocation requests
80462306a36Sopenharmony_ci * @rproc: rproc handle
80562306a36Sopenharmony_ci * @ptr: the resource entry
80662306a36Sopenharmony_ci * @offset: offset of the resource entry
80762306a36Sopenharmony_ci * @avail: size of available data (for image validation)
80862306a36Sopenharmony_ci *
80962306a36Sopenharmony_ci * This function will handle firmware requests for allocation of physically
81062306a36Sopenharmony_ci * contiguous memory regions.
81162306a36Sopenharmony_ci *
81262306a36Sopenharmony_ci * These request entries should come first in the firmware's resource table,
81362306a36Sopenharmony_ci * as other firmware entries might request placing other data objects inside
81462306a36Sopenharmony_ci * these memory regions (e.g. data/code segments, trace resource entries, ...).
81562306a36Sopenharmony_ci *
81662306a36Sopenharmony_ci * Allocating memory this way helps utilizing the reserved physical memory
81762306a36Sopenharmony_ci * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
81862306a36Sopenharmony_ci * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
81962306a36Sopenharmony_ci * pressure is important; it may have a substantial impact on performance.
82062306a36Sopenharmony_ci *
82162306a36Sopenharmony_ci * Return: 0 on success, or an appropriate error code otherwise
82262306a36Sopenharmony_ci */
82362306a36Sopenharmony_cistatic int rproc_handle_carveout(struct rproc *rproc,
82462306a36Sopenharmony_ci				 void *ptr, int offset, int avail)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct fw_rsc_carveout *rsc = ptr;
82762306a36Sopenharmony_ci	struct rproc_mem_entry *carveout;
82862306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (sizeof(*rsc) > avail) {
83162306a36Sopenharmony_ci		dev_err(dev, "carveout rsc is truncated\n");
83262306a36Sopenharmony_ci		return -EINVAL;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* make sure reserved bytes are zeroes */
83662306a36Sopenharmony_ci	if (rsc->reserved) {
83762306a36Sopenharmony_ci		dev_err(dev, "carveout rsc has non zero reserved bytes\n");
83862306a36Sopenharmony_ci		return -EINVAL;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	dev_dbg(dev, "carveout rsc: name: %s, da 0x%x, pa 0x%x, len 0x%x, flags 0x%x\n",
84262306a36Sopenharmony_ci		rsc->name, rsc->da, rsc->pa, rsc->len, rsc->flags);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/*
84562306a36Sopenharmony_ci	 * Check carveout rsc already part of a registered carveout,
84662306a36Sopenharmony_ci	 * Search by name, then check the da and length
84762306a36Sopenharmony_ci	 */
84862306a36Sopenharmony_ci	carveout = rproc_find_carveout_by_name(rproc, rsc->name);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (carveout) {
85162306a36Sopenharmony_ci		if (carveout->rsc_offset != FW_RSC_ADDR_ANY) {
85262306a36Sopenharmony_ci			dev_err(dev,
85362306a36Sopenharmony_ci				"Carveout already associated to resource table\n");
85462306a36Sopenharmony_ci			return -ENOMEM;
85562306a36Sopenharmony_ci		}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		if (rproc_check_carveout_da(rproc, carveout, rsc->da, rsc->len))
85862306a36Sopenharmony_ci			return -ENOMEM;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci		/* Update memory carveout with resource table info */
86162306a36Sopenharmony_ci		carveout->rsc_offset = offset;
86262306a36Sopenharmony_ci		carveout->flags = rsc->flags;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		return 0;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	/* Register carveout in list */
86862306a36Sopenharmony_ci	carveout = rproc_mem_entry_init(dev, NULL, 0, rsc->len, rsc->da,
86962306a36Sopenharmony_ci					rproc_alloc_carveout,
87062306a36Sopenharmony_ci					rproc_release_carveout, rsc->name);
87162306a36Sopenharmony_ci	if (!carveout) {
87262306a36Sopenharmony_ci		dev_err(dev, "Can't allocate memory entry structure\n");
87362306a36Sopenharmony_ci		return -ENOMEM;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	carveout->flags = rsc->flags;
87762306a36Sopenharmony_ci	carveout->rsc_offset = offset;
87862306a36Sopenharmony_ci	rproc_add_carveout(rproc, carveout);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	return 0;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci/**
88462306a36Sopenharmony_ci * rproc_add_carveout() - register an allocated carveout region
88562306a36Sopenharmony_ci * @rproc: rproc handle
88662306a36Sopenharmony_ci * @mem: memory entry to register
88762306a36Sopenharmony_ci *
88862306a36Sopenharmony_ci * This function registers specified memory entry in @rproc carveouts list.
88962306a36Sopenharmony_ci * Specified carveout should have been allocated before registering.
89062306a36Sopenharmony_ci */
89162306a36Sopenharmony_civoid rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	list_add_tail(&mem->node, &rproc->carveouts);
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_add_carveout);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci/**
89862306a36Sopenharmony_ci * rproc_mem_entry_init() - allocate and initialize rproc_mem_entry struct
89962306a36Sopenharmony_ci * @dev: pointer on device struct
90062306a36Sopenharmony_ci * @va: virtual address
90162306a36Sopenharmony_ci * @dma: dma address
90262306a36Sopenharmony_ci * @len: memory carveout length
90362306a36Sopenharmony_ci * @da: device address
90462306a36Sopenharmony_ci * @alloc: memory carveout allocation function
90562306a36Sopenharmony_ci * @release: memory carveout release function
90662306a36Sopenharmony_ci * @name: carveout name
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * This function allocates a rproc_mem_entry struct and fill it with parameters
90962306a36Sopenharmony_ci * provided by client.
91062306a36Sopenharmony_ci *
91162306a36Sopenharmony_ci * Return: a valid pointer on success, or NULL on failure
91262306a36Sopenharmony_ci */
91362306a36Sopenharmony_ci__printf(8, 9)
91462306a36Sopenharmony_cistruct rproc_mem_entry *
91562306a36Sopenharmony_cirproc_mem_entry_init(struct device *dev,
91662306a36Sopenharmony_ci		     void *va, dma_addr_t dma, size_t len, u32 da,
91762306a36Sopenharmony_ci		     int (*alloc)(struct rproc *, struct rproc_mem_entry *),
91862306a36Sopenharmony_ci		     int (*release)(struct rproc *, struct rproc_mem_entry *),
91962306a36Sopenharmony_ci		     const char *name, ...)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct rproc_mem_entry *mem;
92262306a36Sopenharmony_ci	va_list args;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
92562306a36Sopenharmony_ci	if (!mem)
92662306a36Sopenharmony_ci		return mem;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	mem->va = va;
92962306a36Sopenharmony_ci	mem->dma = dma;
93062306a36Sopenharmony_ci	mem->da = da;
93162306a36Sopenharmony_ci	mem->len = len;
93262306a36Sopenharmony_ci	mem->alloc = alloc;
93362306a36Sopenharmony_ci	mem->release = release;
93462306a36Sopenharmony_ci	mem->rsc_offset = FW_RSC_ADDR_ANY;
93562306a36Sopenharmony_ci	mem->of_resm_idx = -1;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	va_start(args, name);
93862306a36Sopenharmony_ci	vsnprintf(mem->name, sizeof(mem->name), name, args);
93962306a36Sopenharmony_ci	va_end(args);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	return mem;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_mem_entry_init);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci/**
94662306a36Sopenharmony_ci * rproc_of_resm_mem_entry_init() - allocate and initialize rproc_mem_entry struct
94762306a36Sopenharmony_ci * from a reserved memory phandle
94862306a36Sopenharmony_ci * @dev: pointer on device struct
94962306a36Sopenharmony_ci * @of_resm_idx: reserved memory phandle index in "memory-region"
95062306a36Sopenharmony_ci * @len: memory carveout length
95162306a36Sopenharmony_ci * @da: device address
95262306a36Sopenharmony_ci * @name: carveout name
95362306a36Sopenharmony_ci *
95462306a36Sopenharmony_ci * This function allocates a rproc_mem_entry struct and fill it with parameters
95562306a36Sopenharmony_ci * provided by client.
95662306a36Sopenharmony_ci *
95762306a36Sopenharmony_ci * Return: a valid pointer on success, or NULL on failure
95862306a36Sopenharmony_ci */
95962306a36Sopenharmony_ci__printf(5, 6)
96062306a36Sopenharmony_cistruct rproc_mem_entry *
96162306a36Sopenharmony_cirproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len,
96262306a36Sopenharmony_ci			     u32 da, const char *name, ...)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct rproc_mem_entry *mem;
96562306a36Sopenharmony_ci	va_list args;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
96862306a36Sopenharmony_ci	if (!mem)
96962306a36Sopenharmony_ci		return mem;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	mem->da = da;
97262306a36Sopenharmony_ci	mem->len = len;
97362306a36Sopenharmony_ci	mem->rsc_offset = FW_RSC_ADDR_ANY;
97462306a36Sopenharmony_ci	mem->of_resm_idx = of_resm_idx;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	va_start(args, name);
97762306a36Sopenharmony_ci	vsnprintf(mem->name, sizeof(mem->name), name, args);
97862306a36Sopenharmony_ci	va_end(args);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	return mem;
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_of_resm_mem_entry_init);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci/**
98562306a36Sopenharmony_ci * rproc_of_parse_firmware() - parse and return the firmware-name
98662306a36Sopenharmony_ci * @dev: pointer on device struct representing a rproc
98762306a36Sopenharmony_ci * @index: index to use for the firmware-name retrieval
98862306a36Sopenharmony_ci * @fw_name: pointer to a character string, in which the firmware
98962306a36Sopenharmony_ci *           name is returned on success and unmodified otherwise.
99062306a36Sopenharmony_ci *
99162306a36Sopenharmony_ci * This is an OF helper function that parses a device's DT node for
99262306a36Sopenharmony_ci * the "firmware-name" property and returns the firmware name pointer
99362306a36Sopenharmony_ci * in @fw_name on success.
99462306a36Sopenharmony_ci *
99562306a36Sopenharmony_ci * Return: 0 on success, or an appropriate failure.
99662306a36Sopenharmony_ci */
99762306a36Sopenharmony_ciint rproc_of_parse_firmware(struct device *dev, int index, const char **fw_name)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	int ret;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	ret = of_property_read_string_index(dev->of_node, "firmware-name",
100262306a36Sopenharmony_ci					    index, fw_name);
100362306a36Sopenharmony_ci	return ret ? ret : 0;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_of_parse_firmware);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci/*
100862306a36Sopenharmony_ci * A lookup table for resource handlers. The indices are defined in
100962306a36Sopenharmony_ci * enum fw_resource_type.
101062306a36Sopenharmony_ci */
101162306a36Sopenharmony_cistatic rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
101262306a36Sopenharmony_ci	[RSC_CARVEOUT] = rproc_handle_carveout,
101362306a36Sopenharmony_ci	[RSC_DEVMEM] = rproc_handle_devmem,
101462306a36Sopenharmony_ci	[RSC_TRACE] = rproc_handle_trace,
101562306a36Sopenharmony_ci	[RSC_VDEV] = rproc_handle_vdev,
101662306a36Sopenharmony_ci};
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci/* handle firmware resource entries before booting the remote processor */
101962306a36Sopenharmony_cistatic int rproc_handle_resources(struct rproc *rproc,
102062306a36Sopenharmony_ci				  rproc_handle_resource_t handlers[RSC_LAST])
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
102362306a36Sopenharmony_ci	rproc_handle_resource_t handler;
102462306a36Sopenharmony_ci	int ret = 0, i;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if (!rproc->table_ptr)
102762306a36Sopenharmony_ci		return 0;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	for (i = 0; i < rproc->table_ptr->num; i++) {
103062306a36Sopenharmony_ci		int offset = rproc->table_ptr->offset[i];
103162306a36Sopenharmony_ci		struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;
103262306a36Sopenharmony_ci		int avail = rproc->table_sz - offset - sizeof(*hdr);
103362306a36Sopenharmony_ci		void *rsc = (void *)hdr + sizeof(*hdr);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		/* make sure table isn't truncated */
103662306a36Sopenharmony_ci		if (avail < 0) {
103762306a36Sopenharmony_ci			dev_err(dev, "rsc table is truncated\n");
103862306a36Sopenharmony_ci			return -EINVAL;
103962306a36Sopenharmony_ci		}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci		dev_dbg(dev, "rsc: type %d\n", hdr->type);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci		if (hdr->type >= RSC_VENDOR_START &&
104462306a36Sopenharmony_ci		    hdr->type <= RSC_VENDOR_END) {
104562306a36Sopenharmony_ci			ret = rproc_handle_rsc(rproc, hdr->type, rsc,
104662306a36Sopenharmony_ci					       offset + sizeof(*hdr), avail);
104762306a36Sopenharmony_ci			if (ret == RSC_HANDLED)
104862306a36Sopenharmony_ci				continue;
104962306a36Sopenharmony_ci			else if (ret < 0)
105062306a36Sopenharmony_ci				break;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci			dev_warn(dev, "unsupported vendor resource %d\n",
105362306a36Sopenharmony_ci				 hdr->type);
105462306a36Sopenharmony_ci			continue;
105562306a36Sopenharmony_ci		}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci		if (hdr->type >= RSC_LAST) {
105862306a36Sopenharmony_ci			dev_warn(dev, "unsupported resource %d\n", hdr->type);
105962306a36Sopenharmony_ci			continue;
106062306a36Sopenharmony_ci		}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		handler = handlers[hdr->type];
106362306a36Sopenharmony_ci		if (!handler)
106462306a36Sopenharmony_ci			continue;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci		ret = handler(rproc, rsc, offset + sizeof(*hdr), avail);
106762306a36Sopenharmony_ci		if (ret)
106862306a36Sopenharmony_ci			break;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	return ret;
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic int rproc_prepare_subdevices(struct rproc *rproc)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct rproc_subdev *subdev;
107762306a36Sopenharmony_ci	int ret;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	list_for_each_entry(subdev, &rproc->subdevs, node) {
108062306a36Sopenharmony_ci		if (subdev->prepare) {
108162306a36Sopenharmony_ci			ret = subdev->prepare(subdev);
108262306a36Sopenharmony_ci			if (ret)
108362306a36Sopenharmony_ci				goto unroll_preparation;
108462306a36Sopenharmony_ci		}
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	return 0;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ciunroll_preparation:
109062306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) {
109162306a36Sopenharmony_ci		if (subdev->unprepare)
109262306a36Sopenharmony_ci			subdev->unprepare(subdev);
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return ret;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic int rproc_start_subdevices(struct rproc *rproc)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	struct rproc_subdev *subdev;
110162306a36Sopenharmony_ci	int ret;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	list_for_each_entry(subdev, &rproc->subdevs, node) {
110462306a36Sopenharmony_ci		if (subdev->start) {
110562306a36Sopenharmony_ci			ret = subdev->start(subdev);
110662306a36Sopenharmony_ci			if (ret)
110762306a36Sopenharmony_ci				goto unroll_registration;
110862306a36Sopenharmony_ci		}
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	return 0;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ciunroll_registration:
111462306a36Sopenharmony_ci	list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) {
111562306a36Sopenharmony_ci		if (subdev->stop)
111662306a36Sopenharmony_ci			subdev->stop(subdev, true);
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	return ret;
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic void rproc_stop_subdevices(struct rproc *rproc, bool crashed)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	struct rproc_subdev *subdev;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	list_for_each_entry_reverse(subdev, &rproc->subdevs, node) {
112762306a36Sopenharmony_ci		if (subdev->stop)
112862306a36Sopenharmony_ci			subdev->stop(subdev, crashed);
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic void rproc_unprepare_subdevices(struct rproc *rproc)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	struct rproc_subdev *subdev;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	list_for_each_entry_reverse(subdev, &rproc->subdevs, node) {
113762306a36Sopenharmony_ci		if (subdev->unprepare)
113862306a36Sopenharmony_ci			subdev->unprepare(subdev);
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci/**
114362306a36Sopenharmony_ci * rproc_alloc_registered_carveouts() - allocate all carveouts registered
114462306a36Sopenharmony_ci * in the list
114562306a36Sopenharmony_ci * @rproc: the remote processor handle
114662306a36Sopenharmony_ci *
114762306a36Sopenharmony_ci * This function parses registered carveout list, performs allocation
114862306a36Sopenharmony_ci * if alloc() ops registered and updates resource table information
114962306a36Sopenharmony_ci * if rsc_offset set.
115062306a36Sopenharmony_ci *
115162306a36Sopenharmony_ci * Return: 0 on success
115262306a36Sopenharmony_ci */
115362306a36Sopenharmony_cistatic int rproc_alloc_registered_carveouts(struct rproc *rproc)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct rproc_mem_entry *entry, *tmp;
115662306a36Sopenharmony_ci	struct fw_rsc_carveout *rsc;
115762306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
115862306a36Sopenharmony_ci	u64 pa;
115962306a36Sopenharmony_ci	int ret;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
116262306a36Sopenharmony_ci		if (entry->alloc) {
116362306a36Sopenharmony_ci			ret = entry->alloc(rproc, entry);
116462306a36Sopenharmony_ci			if (ret) {
116562306a36Sopenharmony_ci				dev_err(dev, "Unable to allocate carveout %s: %d\n",
116662306a36Sopenharmony_ci					entry->name, ret);
116762306a36Sopenharmony_ci				return -ENOMEM;
116862306a36Sopenharmony_ci			}
116962306a36Sopenharmony_ci		}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		if (entry->rsc_offset != FW_RSC_ADDR_ANY) {
117262306a36Sopenharmony_ci			/* update resource table */
117362306a36Sopenharmony_ci			rsc = (void *)rproc->table_ptr + entry->rsc_offset;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci			/*
117662306a36Sopenharmony_ci			 * Some remote processors might need to know the pa
117762306a36Sopenharmony_ci			 * even though they are behind an IOMMU. E.g., OMAP4's
117862306a36Sopenharmony_ci			 * remote M3 processor needs this so it can control
117962306a36Sopenharmony_ci			 * on-chip hardware accelerators that are not behind
118062306a36Sopenharmony_ci			 * the IOMMU, and therefor must know the pa.
118162306a36Sopenharmony_ci			 *
118262306a36Sopenharmony_ci			 * Generally we don't want to expose physical addresses
118362306a36Sopenharmony_ci			 * if we don't have to (remote processors are generally
118462306a36Sopenharmony_ci			 * _not_ trusted), so we might want to do this only for
118562306a36Sopenharmony_ci			 * remote processor that _must_ have this (e.g. OMAP4's
118662306a36Sopenharmony_ci			 * dual M3 subsystem).
118762306a36Sopenharmony_ci			 *
118862306a36Sopenharmony_ci			 * Non-IOMMU processors might also want to have this info.
118962306a36Sopenharmony_ci			 * In this case, the device address and the physical address
119062306a36Sopenharmony_ci			 * are the same.
119162306a36Sopenharmony_ci			 */
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci			/* Use va if defined else dma to generate pa */
119462306a36Sopenharmony_ci			if (entry->va)
119562306a36Sopenharmony_ci				pa = (u64)rproc_va_to_pa(entry->va);
119662306a36Sopenharmony_ci			else
119762306a36Sopenharmony_ci				pa = (u64)entry->dma;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci			if (((u64)pa) & HIGH_BITS_MASK)
120062306a36Sopenharmony_ci				dev_warn(dev,
120162306a36Sopenharmony_ci					 "Physical address cast in 32bit to fit resource table format\n");
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci			rsc->pa = (u32)pa;
120462306a36Sopenharmony_ci			rsc->da = entry->da;
120562306a36Sopenharmony_ci			rsc->len = entry->len;
120662306a36Sopenharmony_ci		}
120762306a36Sopenharmony_ci	}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	return 0;
121062306a36Sopenharmony_ci}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci/**
121462306a36Sopenharmony_ci * rproc_resource_cleanup() - clean up and free all acquired resources
121562306a36Sopenharmony_ci * @rproc: rproc handle
121662306a36Sopenharmony_ci *
121762306a36Sopenharmony_ci * This function will free all resources acquired for @rproc, and it
121862306a36Sopenharmony_ci * is called whenever @rproc either shuts down or fails to boot.
121962306a36Sopenharmony_ci */
122062306a36Sopenharmony_civoid rproc_resource_cleanup(struct rproc *rproc)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	struct rproc_mem_entry *entry, *tmp;
122362306a36Sopenharmony_ci	struct rproc_debug_trace *trace, *ttmp;
122462306a36Sopenharmony_ci	struct rproc_vdev *rvdev, *rvtmp;
122562306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	/* clean up debugfs trace entries */
122862306a36Sopenharmony_ci	list_for_each_entry_safe(trace, ttmp, &rproc->traces, node) {
122962306a36Sopenharmony_ci		rproc_remove_trace_file(trace->tfile);
123062306a36Sopenharmony_ci		rproc->num_traces--;
123162306a36Sopenharmony_ci		list_del(&trace->node);
123262306a36Sopenharmony_ci		kfree(trace);
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	/* clean up iommu mapping entries */
123662306a36Sopenharmony_ci	list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) {
123762306a36Sopenharmony_ci		size_t unmapped;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci		unmapped = iommu_unmap(rproc->domain, entry->da, entry->len);
124062306a36Sopenharmony_ci		if (unmapped != entry->len) {
124162306a36Sopenharmony_ci			/* nothing much to do besides complaining */
124262306a36Sopenharmony_ci			dev_err(dev, "failed to unmap %zx/%zu\n", entry->len,
124362306a36Sopenharmony_ci				unmapped);
124462306a36Sopenharmony_ci		}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		list_del(&entry->node);
124762306a36Sopenharmony_ci		kfree(entry);
124862306a36Sopenharmony_ci	}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	/* clean up carveout allocations */
125162306a36Sopenharmony_ci	list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
125262306a36Sopenharmony_ci		if (entry->release)
125362306a36Sopenharmony_ci			entry->release(rproc, entry);
125462306a36Sopenharmony_ci		list_del(&entry->node);
125562306a36Sopenharmony_ci		kfree(entry);
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	/* clean up remote vdev entries */
125962306a36Sopenharmony_ci	list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
126062306a36Sopenharmony_ci		platform_device_unregister(rvdev->pdev);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	rproc_coredump_cleanup(rproc);
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_resource_cleanup);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_cistatic int rproc_start(struct rproc *rproc, const struct firmware *fw)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	struct resource_table *loaded_table;
126962306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
127062306a36Sopenharmony_ci	int ret;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	/* load the ELF segments to memory */
127362306a36Sopenharmony_ci	ret = rproc_load_segments(rproc, fw);
127462306a36Sopenharmony_ci	if (ret) {
127562306a36Sopenharmony_ci		dev_err(dev, "Failed to load program segments: %d\n", ret);
127662306a36Sopenharmony_ci		return ret;
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	/*
128062306a36Sopenharmony_ci	 * The starting device has been given the rproc->cached_table as the
128162306a36Sopenharmony_ci	 * resource table. The address of the vring along with the other
128262306a36Sopenharmony_ci	 * allocated resources (carveouts etc) is stored in cached_table.
128362306a36Sopenharmony_ci	 * In order to pass this information to the remote device we must copy
128462306a36Sopenharmony_ci	 * this information to device memory. We also update the table_ptr so
128562306a36Sopenharmony_ci	 * that any subsequent changes will be applied to the loaded version.
128662306a36Sopenharmony_ci	 */
128762306a36Sopenharmony_ci	loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
128862306a36Sopenharmony_ci	if (loaded_table) {
128962306a36Sopenharmony_ci		memcpy(loaded_table, rproc->cached_table, rproc->table_sz);
129062306a36Sopenharmony_ci		rproc->table_ptr = loaded_table;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	ret = rproc_prepare_subdevices(rproc);
129462306a36Sopenharmony_ci	if (ret) {
129562306a36Sopenharmony_ci		dev_err(dev, "failed to prepare subdevices for %s: %d\n",
129662306a36Sopenharmony_ci			rproc->name, ret);
129762306a36Sopenharmony_ci		goto reset_table_ptr;
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	/* power up the remote processor */
130162306a36Sopenharmony_ci	ret = rproc->ops->start(rproc);
130262306a36Sopenharmony_ci	if (ret) {
130362306a36Sopenharmony_ci		dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret);
130462306a36Sopenharmony_ci		goto unprepare_subdevices;
130562306a36Sopenharmony_ci	}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	/* Start any subdevices for the remote processor */
130862306a36Sopenharmony_ci	ret = rproc_start_subdevices(rproc);
130962306a36Sopenharmony_ci	if (ret) {
131062306a36Sopenharmony_ci		dev_err(dev, "failed to probe subdevices for %s: %d\n",
131162306a36Sopenharmony_ci			rproc->name, ret);
131262306a36Sopenharmony_ci		goto stop_rproc;
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	rproc->state = RPROC_RUNNING;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	dev_info(dev, "remote processor %s is now up\n", rproc->name);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	return 0;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistop_rproc:
132262306a36Sopenharmony_ci	rproc->ops->stop(rproc);
132362306a36Sopenharmony_ciunprepare_subdevices:
132462306a36Sopenharmony_ci	rproc_unprepare_subdevices(rproc);
132562306a36Sopenharmony_cireset_table_ptr:
132662306a36Sopenharmony_ci	rproc->table_ptr = rproc->cached_table;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	return ret;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic int __rproc_attach(struct rproc *rproc)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
133462306a36Sopenharmony_ci	int ret;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	ret = rproc_prepare_subdevices(rproc);
133762306a36Sopenharmony_ci	if (ret) {
133862306a36Sopenharmony_ci		dev_err(dev, "failed to prepare subdevices for %s: %d\n",
133962306a36Sopenharmony_ci			rproc->name, ret);
134062306a36Sopenharmony_ci		goto out;
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	/* Attach to the remote processor */
134462306a36Sopenharmony_ci	ret = rproc_attach_device(rproc);
134562306a36Sopenharmony_ci	if (ret) {
134662306a36Sopenharmony_ci		dev_err(dev, "can't attach to rproc %s: %d\n",
134762306a36Sopenharmony_ci			rproc->name, ret);
134862306a36Sopenharmony_ci		goto unprepare_subdevices;
134962306a36Sopenharmony_ci	}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/* Start any subdevices for the remote processor */
135262306a36Sopenharmony_ci	ret = rproc_start_subdevices(rproc);
135362306a36Sopenharmony_ci	if (ret) {
135462306a36Sopenharmony_ci		dev_err(dev, "failed to probe subdevices for %s: %d\n",
135562306a36Sopenharmony_ci			rproc->name, ret);
135662306a36Sopenharmony_ci		goto stop_rproc;
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	rproc->state = RPROC_ATTACHED;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	dev_info(dev, "remote processor %s is now attached\n", rproc->name);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	return 0;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_cistop_rproc:
136662306a36Sopenharmony_ci	rproc->ops->stop(rproc);
136762306a36Sopenharmony_ciunprepare_subdevices:
136862306a36Sopenharmony_ci	rproc_unprepare_subdevices(rproc);
136962306a36Sopenharmony_ciout:
137062306a36Sopenharmony_ci	return ret;
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci/*
137462306a36Sopenharmony_ci * take a firmware and boot a remote processor with it.
137562306a36Sopenharmony_ci */
137662306a36Sopenharmony_cistatic int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
137762306a36Sopenharmony_ci{
137862306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
137962306a36Sopenharmony_ci	const char *name = rproc->firmware;
138062306a36Sopenharmony_ci	int ret;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	ret = rproc_fw_sanity_check(rproc, fw);
138362306a36Sopenharmony_ci	if (ret)
138462306a36Sopenharmony_ci		return ret;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	/*
138962306a36Sopenharmony_ci	 * if enabling an IOMMU isn't relevant for this rproc, this is
139062306a36Sopenharmony_ci	 * just a nop
139162306a36Sopenharmony_ci	 */
139262306a36Sopenharmony_ci	ret = rproc_enable_iommu(rproc);
139362306a36Sopenharmony_ci	if (ret) {
139462306a36Sopenharmony_ci		dev_err(dev, "can't enable iommu: %d\n", ret);
139562306a36Sopenharmony_ci		return ret;
139662306a36Sopenharmony_ci	}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	/* Prepare rproc for firmware loading if needed */
139962306a36Sopenharmony_ci	ret = rproc_prepare_device(rproc);
140062306a36Sopenharmony_ci	if (ret) {
140162306a36Sopenharmony_ci		dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
140262306a36Sopenharmony_ci		goto disable_iommu;
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	rproc->bootaddr = rproc_get_boot_addr(rproc, fw);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	/* Load resource table, core dump segment list etc from the firmware */
140862306a36Sopenharmony_ci	ret = rproc_parse_fw(rproc, fw);
140962306a36Sopenharmony_ci	if (ret)
141062306a36Sopenharmony_ci		goto unprepare_rproc;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	/* reset max_notifyid */
141362306a36Sopenharmony_ci	rproc->max_notifyid = -1;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	/* reset handled vdev */
141662306a36Sopenharmony_ci	rproc->nb_vdev = 0;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	/* handle fw resources which are required to boot rproc */
141962306a36Sopenharmony_ci	ret = rproc_handle_resources(rproc, rproc_loading_handlers);
142062306a36Sopenharmony_ci	if (ret) {
142162306a36Sopenharmony_ci		dev_err(dev, "Failed to process resources: %d\n", ret);
142262306a36Sopenharmony_ci		goto clean_up_resources;
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	/* Allocate carveout resources associated to rproc */
142662306a36Sopenharmony_ci	ret = rproc_alloc_registered_carveouts(rproc);
142762306a36Sopenharmony_ci	if (ret) {
142862306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate associated carveouts: %d\n",
142962306a36Sopenharmony_ci			ret);
143062306a36Sopenharmony_ci		goto clean_up_resources;
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	ret = rproc_start(rproc, fw);
143462306a36Sopenharmony_ci	if (ret)
143562306a36Sopenharmony_ci		goto clean_up_resources;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	return 0;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ciclean_up_resources:
144062306a36Sopenharmony_ci	rproc_resource_cleanup(rproc);
144162306a36Sopenharmony_ci	kfree(rproc->cached_table);
144262306a36Sopenharmony_ci	rproc->cached_table = NULL;
144362306a36Sopenharmony_ci	rproc->table_ptr = NULL;
144462306a36Sopenharmony_ciunprepare_rproc:
144562306a36Sopenharmony_ci	/* release HW resources if needed */
144662306a36Sopenharmony_ci	rproc_unprepare_device(rproc);
144762306a36Sopenharmony_cidisable_iommu:
144862306a36Sopenharmony_ci	rproc_disable_iommu(rproc);
144962306a36Sopenharmony_ci	return ret;
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cistatic int rproc_set_rsc_table(struct rproc *rproc)
145362306a36Sopenharmony_ci{
145462306a36Sopenharmony_ci	struct resource_table *table_ptr;
145562306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
145662306a36Sopenharmony_ci	size_t table_sz;
145762306a36Sopenharmony_ci	int ret;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	table_ptr = rproc_get_loaded_rsc_table(rproc, &table_sz);
146062306a36Sopenharmony_ci	if (!table_ptr) {
146162306a36Sopenharmony_ci		/* Not having a resource table is acceptable */
146262306a36Sopenharmony_ci		return 0;
146362306a36Sopenharmony_ci	}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	if (IS_ERR(table_ptr)) {
146662306a36Sopenharmony_ci		ret = PTR_ERR(table_ptr);
146762306a36Sopenharmony_ci		dev_err(dev, "can't load resource table: %d\n", ret);
146862306a36Sopenharmony_ci		return ret;
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	/*
147262306a36Sopenharmony_ci	 * If it is possible to detach the remote processor, keep an untouched
147362306a36Sopenharmony_ci	 * copy of the resource table.  That way we can start fresh again when
147462306a36Sopenharmony_ci	 * the remote processor is re-attached, that is:
147562306a36Sopenharmony_ci	 *
147662306a36Sopenharmony_ci	 *      DETACHED -> ATTACHED -> DETACHED -> ATTACHED
147762306a36Sopenharmony_ci	 *
147862306a36Sopenharmony_ci	 * Free'd in rproc_reset_rsc_table_on_detach() and
147962306a36Sopenharmony_ci	 * rproc_reset_rsc_table_on_stop().
148062306a36Sopenharmony_ci	 */
148162306a36Sopenharmony_ci	if (rproc->ops->detach) {
148262306a36Sopenharmony_ci		rproc->clean_table = kmemdup(table_ptr, table_sz, GFP_KERNEL);
148362306a36Sopenharmony_ci		if (!rproc->clean_table)
148462306a36Sopenharmony_ci			return -ENOMEM;
148562306a36Sopenharmony_ci	} else {
148662306a36Sopenharmony_ci		rproc->clean_table = NULL;
148762306a36Sopenharmony_ci	}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	rproc->cached_table = NULL;
149062306a36Sopenharmony_ci	rproc->table_ptr = table_ptr;
149162306a36Sopenharmony_ci	rproc->table_sz = table_sz;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	return 0;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic int rproc_reset_rsc_table_on_detach(struct rproc *rproc)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	struct resource_table *table_ptr;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	/* A resource table was never retrieved, nothing to do here */
150162306a36Sopenharmony_ci	if (!rproc->table_ptr)
150262306a36Sopenharmony_ci		return 0;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	/*
150562306a36Sopenharmony_ci	 * If we made it to this point a clean_table _must_ have been
150662306a36Sopenharmony_ci	 * allocated in rproc_set_rsc_table().  If one isn't present
150762306a36Sopenharmony_ci	 * something went really wrong and we must complain.
150862306a36Sopenharmony_ci	 */
150962306a36Sopenharmony_ci	if (WARN_ON(!rproc->clean_table))
151062306a36Sopenharmony_ci		return -EINVAL;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	/* Remember where the external entity installed the resource table */
151362306a36Sopenharmony_ci	table_ptr = rproc->table_ptr;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	/*
151662306a36Sopenharmony_ci	 * If we made it here the remote processor was started by another
151762306a36Sopenharmony_ci	 * entity and a cache table doesn't exist.  As such make a copy of
151862306a36Sopenharmony_ci	 * the resource table currently used by the remote processor and
151962306a36Sopenharmony_ci	 * use that for the rest of the shutdown process.  The memory
152062306a36Sopenharmony_ci	 * allocated here is free'd in rproc_detach().
152162306a36Sopenharmony_ci	 */
152262306a36Sopenharmony_ci	rproc->cached_table = kmemdup(rproc->table_ptr,
152362306a36Sopenharmony_ci				      rproc->table_sz, GFP_KERNEL);
152462306a36Sopenharmony_ci	if (!rproc->cached_table)
152562306a36Sopenharmony_ci		return -ENOMEM;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	/*
152862306a36Sopenharmony_ci	 * Use a copy of the resource table for the remainder of the
152962306a36Sopenharmony_ci	 * shutdown process.
153062306a36Sopenharmony_ci	 */
153162306a36Sopenharmony_ci	rproc->table_ptr = rproc->cached_table;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	/*
153462306a36Sopenharmony_ci	 * Reset the memory area where the firmware loaded the resource table
153562306a36Sopenharmony_ci	 * to its original value.  That way when we re-attach the remote
153662306a36Sopenharmony_ci	 * processor the resource table is clean and ready to be used again.
153762306a36Sopenharmony_ci	 */
153862306a36Sopenharmony_ci	memcpy(table_ptr, rproc->clean_table, rproc->table_sz);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	/*
154162306a36Sopenharmony_ci	 * The clean resource table is no longer needed.  Allocated in
154262306a36Sopenharmony_ci	 * rproc_set_rsc_table().
154362306a36Sopenharmony_ci	 */
154462306a36Sopenharmony_ci	kfree(rproc->clean_table);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	return 0;
154762306a36Sopenharmony_ci}
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_cistatic int rproc_reset_rsc_table_on_stop(struct rproc *rproc)
155062306a36Sopenharmony_ci{
155162306a36Sopenharmony_ci	/* A resource table was never retrieved, nothing to do here */
155262306a36Sopenharmony_ci	if (!rproc->table_ptr)
155362306a36Sopenharmony_ci		return 0;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	/*
155662306a36Sopenharmony_ci	 * If a cache table exists the remote processor was started by
155762306a36Sopenharmony_ci	 * the remoteproc core.  That cache table should be used for
155862306a36Sopenharmony_ci	 * the rest of the shutdown process.
155962306a36Sopenharmony_ci	 */
156062306a36Sopenharmony_ci	if (rproc->cached_table)
156162306a36Sopenharmony_ci		goto out;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	/*
156462306a36Sopenharmony_ci	 * If we made it here the remote processor was started by another
156562306a36Sopenharmony_ci	 * entity and a cache table doesn't exist.  As such make a copy of
156662306a36Sopenharmony_ci	 * the resource table currently used by the remote processor and
156762306a36Sopenharmony_ci	 * use that for the rest of the shutdown process.  The memory
156862306a36Sopenharmony_ci	 * allocated here is free'd in rproc_shutdown().
156962306a36Sopenharmony_ci	 */
157062306a36Sopenharmony_ci	rproc->cached_table = kmemdup(rproc->table_ptr,
157162306a36Sopenharmony_ci				      rproc->table_sz, GFP_KERNEL);
157262306a36Sopenharmony_ci	if (!rproc->cached_table)
157362306a36Sopenharmony_ci		return -ENOMEM;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	/*
157662306a36Sopenharmony_ci	 * Since the remote processor is being switched off the clean table
157762306a36Sopenharmony_ci	 * won't be needed.  Allocated in rproc_set_rsc_table().
157862306a36Sopenharmony_ci	 */
157962306a36Sopenharmony_ci	kfree(rproc->clean_table);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ciout:
158262306a36Sopenharmony_ci	/*
158362306a36Sopenharmony_ci	 * Use a copy of the resource table for the remainder of the
158462306a36Sopenharmony_ci	 * shutdown process.
158562306a36Sopenharmony_ci	 */
158662306a36Sopenharmony_ci	rproc->table_ptr = rproc->cached_table;
158762306a36Sopenharmony_ci	return 0;
158862306a36Sopenharmony_ci}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci/*
159162306a36Sopenharmony_ci * Attach to remote processor - similar to rproc_fw_boot() but without
159262306a36Sopenharmony_ci * the steps that deal with the firmware image.
159362306a36Sopenharmony_ci */
159462306a36Sopenharmony_cistatic int rproc_attach(struct rproc *rproc)
159562306a36Sopenharmony_ci{
159662306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
159762306a36Sopenharmony_ci	int ret;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	/*
160062306a36Sopenharmony_ci	 * if enabling an IOMMU isn't relevant for this rproc, this is
160162306a36Sopenharmony_ci	 * just a nop
160262306a36Sopenharmony_ci	 */
160362306a36Sopenharmony_ci	ret = rproc_enable_iommu(rproc);
160462306a36Sopenharmony_ci	if (ret) {
160562306a36Sopenharmony_ci		dev_err(dev, "can't enable iommu: %d\n", ret);
160662306a36Sopenharmony_ci		return ret;
160762306a36Sopenharmony_ci	}
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	/* Do anything that is needed to boot the remote processor */
161062306a36Sopenharmony_ci	ret = rproc_prepare_device(rproc);
161162306a36Sopenharmony_ci	if (ret) {
161262306a36Sopenharmony_ci		dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
161362306a36Sopenharmony_ci		goto disable_iommu;
161462306a36Sopenharmony_ci	}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	ret = rproc_set_rsc_table(rproc);
161762306a36Sopenharmony_ci	if (ret) {
161862306a36Sopenharmony_ci		dev_err(dev, "can't load resource table: %d\n", ret);
161962306a36Sopenharmony_ci		goto unprepare_device;
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	/* reset max_notifyid */
162362306a36Sopenharmony_ci	rproc->max_notifyid = -1;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	/* reset handled vdev */
162662306a36Sopenharmony_ci	rproc->nb_vdev = 0;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	/*
162962306a36Sopenharmony_ci	 * Handle firmware resources required to attach to a remote processor.
163062306a36Sopenharmony_ci	 * Because we are attaching rather than booting the remote processor,
163162306a36Sopenharmony_ci	 * we expect the platform driver to properly set rproc->table_ptr.
163262306a36Sopenharmony_ci	 */
163362306a36Sopenharmony_ci	ret = rproc_handle_resources(rproc, rproc_loading_handlers);
163462306a36Sopenharmony_ci	if (ret) {
163562306a36Sopenharmony_ci		dev_err(dev, "Failed to process resources: %d\n", ret);
163662306a36Sopenharmony_ci		goto unprepare_device;
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	/* Allocate carveout resources associated to rproc */
164062306a36Sopenharmony_ci	ret = rproc_alloc_registered_carveouts(rproc);
164162306a36Sopenharmony_ci	if (ret) {
164262306a36Sopenharmony_ci		dev_err(dev, "Failed to allocate associated carveouts: %d\n",
164362306a36Sopenharmony_ci			ret);
164462306a36Sopenharmony_ci		goto clean_up_resources;
164562306a36Sopenharmony_ci	}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	ret = __rproc_attach(rproc);
164862306a36Sopenharmony_ci	if (ret)
164962306a36Sopenharmony_ci		goto clean_up_resources;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	return 0;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ciclean_up_resources:
165462306a36Sopenharmony_ci	rproc_resource_cleanup(rproc);
165562306a36Sopenharmony_ciunprepare_device:
165662306a36Sopenharmony_ci	/* release HW resources if needed */
165762306a36Sopenharmony_ci	rproc_unprepare_device(rproc);
165862306a36Sopenharmony_cidisable_iommu:
165962306a36Sopenharmony_ci	rproc_disable_iommu(rproc);
166062306a36Sopenharmony_ci	return ret;
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci/*
166462306a36Sopenharmony_ci * take a firmware and boot it up.
166562306a36Sopenharmony_ci *
166662306a36Sopenharmony_ci * Note: this function is called asynchronously upon registration of the
166762306a36Sopenharmony_ci * remote processor (so we must wait until it completes before we try
166862306a36Sopenharmony_ci * to unregister the device. one other option is just to use kref here,
166962306a36Sopenharmony_ci * that might be cleaner).
167062306a36Sopenharmony_ci */
167162306a36Sopenharmony_cistatic void rproc_auto_boot_callback(const struct firmware *fw, void *context)
167262306a36Sopenharmony_ci{
167362306a36Sopenharmony_ci	struct rproc *rproc = context;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	rproc_boot(rproc);
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	release_firmware(fw);
167862306a36Sopenharmony_ci}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_cistatic int rproc_trigger_auto_boot(struct rproc *rproc)
168162306a36Sopenharmony_ci{
168262306a36Sopenharmony_ci	int ret;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	/*
168562306a36Sopenharmony_ci	 * Since the remote processor is in a detached state, it has already
168662306a36Sopenharmony_ci	 * been booted by another entity.  As such there is no point in waiting
168762306a36Sopenharmony_ci	 * for a firmware image to be loaded, we can simply initiate the process
168862306a36Sopenharmony_ci	 * of attaching to it immediately.
168962306a36Sopenharmony_ci	 */
169062306a36Sopenharmony_ci	if (rproc->state == RPROC_DETACHED)
169162306a36Sopenharmony_ci		return rproc_boot(rproc);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	/*
169462306a36Sopenharmony_ci	 * We're initiating an asynchronous firmware loading, so we can
169562306a36Sopenharmony_ci	 * be built-in kernel code, without hanging the boot process.
169662306a36Sopenharmony_ci	 */
169762306a36Sopenharmony_ci	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
169862306a36Sopenharmony_ci				      rproc->firmware, &rproc->dev, GFP_KERNEL,
169962306a36Sopenharmony_ci				      rproc, rproc_auto_boot_callback);
170062306a36Sopenharmony_ci	if (ret < 0)
170162306a36Sopenharmony_ci		dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	return ret;
170462306a36Sopenharmony_ci}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_cistatic int rproc_stop(struct rproc *rproc, bool crashed)
170762306a36Sopenharmony_ci{
170862306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
170962306a36Sopenharmony_ci	int ret;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	/* No need to continue if a stop() operation has not been provided */
171262306a36Sopenharmony_ci	if (!rproc->ops->stop)
171362306a36Sopenharmony_ci		return -EINVAL;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	/* Stop any subdevices for the remote processor */
171662306a36Sopenharmony_ci	rproc_stop_subdevices(rproc, crashed);
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	/* the installed resource table is no longer accessible */
171962306a36Sopenharmony_ci	ret = rproc_reset_rsc_table_on_stop(rproc);
172062306a36Sopenharmony_ci	if (ret) {
172162306a36Sopenharmony_ci		dev_err(dev, "can't reset resource table: %d\n", ret);
172262306a36Sopenharmony_ci		return ret;
172362306a36Sopenharmony_ci	}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	/* power off the remote processor */
172762306a36Sopenharmony_ci	ret = rproc->ops->stop(rproc);
172862306a36Sopenharmony_ci	if (ret) {
172962306a36Sopenharmony_ci		dev_err(dev, "can't stop rproc: %d\n", ret);
173062306a36Sopenharmony_ci		return ret;
173162306a36Sopenharmony_ci	}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	rproc_unprepare_subdevices(rproc);
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	rproc->state = RPROC_OFFLINE;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	dev_info(dev, "stopped remote processor %s\n", rproc->name);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	return 0;
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci/*
174362306a36Sopenharmony_ci * __rproc_detach(): Does the opposite of __rproc_attach()
174462306a36Sopenharmony_ci */
174562306a36Sopenharmony_cistatic int __rproc_detach(struct rproc *rproc)
174662306a36Sopenharmony_ci{
174762306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
174862306a36Sopenharmony_ci	int ret;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	/* No need to continue if a detach() operation has not been provided */
175162306a36Sopenharmony_ci	if (!rproc->ops->detach)
175262306a36Sopenharmony_ci		return -EINVAL;
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	/* Stop any subdevices for the remote processor */
175562306a36Sopenharmony_ci	rproc_stop_subdevices(rproc, false);
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	/* the installed resource table is no longer accessible */
175862306a36Sopenharmony_ci	ret = rproc_reset_rsc_table_on_detach(rproc);
175962306a36Sopenharmony_ci	if (ret) {
176062306a36Sopenharmony_ci		dev_err(dev, "can't reset resource table: %d\n", ret);
176162306a36Sopenharmony_ci		return ret;
176262306a36Sopenharmony_ci	}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	/* Tell the remote processor the core isn't available anymore */
176562306a36Sopenharmony_ci	ret = rproc->ops->detach(rproc);
176662306a36Sopenharmony_ci	if (ret) {
176762306a36Sopenharmony_ci		dev_err(dev, "can't detach from rproc: %d\n", ret);
176862306a36Sopenharmony_ci		return ret;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	rproc_unprepare_subdevices(rproc);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	rproc->state = RPROC_DETACHED;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	dev_info(dev, "detached remote processor %s\n", rproc->name);
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	return 0;
177862306a36Sopenharmony_ci}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_cistatic int rproc_attach_recovery(struct rproc *rproc)
178162306a36Sopenharmony_ci{
178262306a36Sopenharmony_ci	int ret;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	ret = __rproc_detach(rproc);
178562306a36Sopenharmony_ci	if (ret)
178662306a36Sopenharmony_ci		return ret;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	return __rproc_attach(rproc);
178962306a36Sopenharmony_ci}
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_cistatic int rproc_boot_recovery(struct rproc *rproc)
179262306a36Sopenharmony_ci{
179362306a36Sopenharmony_ci	const struct firmware *firmware_p;
179462306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
179562306a36Sopenharmony_ci	int ret;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	ret = rproc_stop(rproc, true);
179862306a36Sopenharmony_ci	if (ret)
179962306a36Sopenharmony_ci		return ret;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	/* generate coredump */
180262306a36Sopenharmony_ci	rproc->ops->coredump(rproc);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	/* load firmware */
180562306a36Sopenharmony_ci	ret = request_firmware(&firmware_p, rproc->firmware, dev);
180662306a36Sopenharmony_ci	if (ret < 0) {
180762306a36Sopenharmony_ci		dev_err(dev, "request_firmware failed: %d\n", ret);
180862306a36Sopenharmony_ci		return ret;
180962306a36Sopenharmony_ci	}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	/* boot the remote processor up again */
181262306a36Sopenharmony_ci	ret = rproc_start(rproc, firmware_p);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	release_firmware(firmware_p);
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	return ret;
181762306a36Sopenharmony_ci}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci/**
182062306a36Sopenharmony_ci * rproc_trigger_recovery() - recover a remoteproc
182162306a36Sopenharmony_ci * @rproc: the remote processor
182262306a36Sopenharmony_ci *
182362306a36Sopenharmony_ci * The recovery is done by resetting all the virtio devices, that way all the
182462306a36Sopenharmony_ci * rpmsg drivers will be reseted along with the remote processor making the
182562306a36Sopenharmony_ci * remoteproc functional again.
182662306a36Sopenharmony_ci *
182762306a36Sopenharmony_ci * This function can sleep, so it cannot be called from atomic context.
182862306a36Sopenharmony_ci *
182962306a36Sopenharmony_ci * Return: 0 on success or a negative value upon failure
183062306a36Sopenharmony_ci */
183162306a36Sopenharmony_ciint rproc_trigger_recovery(struct rproc *rproc)
183262306a36Sopenharmony_ci{
183362306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
183462306a36Sopenharmony_ci	int ret;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&rproc->lock);
183762306a36Sopenharmony_ci	if (ret)
183862306a36Sopenharmony_ci		return ret;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	/* State could have changed before we got the mutex */
184162306a36Sopenharmony_ci	if (rproc->state != RPROC_CRASHED)
184262306a36Sopenharmony_ci		goto unlock_mutex;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	dev_err(dev, "recovering %s\n", rproc->name);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	if (rproc_has_feature(rproc, RPROC_FEAT_ATTACH_ON_RECOVERY))
184762306a36Sopenharmony_ci		ret = rproc_attach_recovery(rproc);
184862306a36Sopenharmony_ci	else
184962306a36Sopenharmony_ci		ret = rproc_boot_recovery(rproc);
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ciunlock_mutex:
185262306a36Sopenharmony_ci	mutex_unlock(&rproc->lock);
185362306a36Sopenharmony_ci	return ret;
185462306a36Sopenharmony_ci}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci/**
185762306a36Sopenharmony_ci * rproc_crash_handler_work() - handle a crash
185862306a36Sopenharmony_ci * @work: work treating the crash
185962306a36Sopenharmony_ci *
186062306a36Sopenharmony_ci * This function needs to handle everything related to a crash, like cpu
186162306a36Sopenharmony_ci * registers and stack dump, information to help to debug the fatal error, etc.
186262306a36Sopenharmony_ci */
186362306a36Sopenharmony_cistatic void rproc_crash_handler_work(struct work_struct *work)
186462306a36Sopenharmony_ci{
186562306a36Sopenharmony_ci	struct rproc *rproc = container_of(work, struct rproc, crash_handler);
186662306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	dev_dbg(dev, "enter %s\n", __func__);
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	mutex_lock(&rproc->lock);
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	if (rproc->state == RPROC_CRASHED) {
187362306a36Sopenharmony_ci		/* handle only the first crash detected */
187462306a36Sopenharmony_ci		mutex_unlock(&rproc->lock);
187562306a36Sopenharmony_ci		return;
187662306a36Sopenharmony_ci	}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	if (rproc->state == RPROC_OFFLINE) {
187962306a36Sopenharmony_ci		/* Don't recover if the remote processor was stopped */
188062306a36Sopenharmony_ci		mutex_unlock(&rproc->lock);
188162306a36Sopenharmony_ci		goto out;
188262306a36Sopenharmony_ci	}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	rproc->state = RPROC_CRASHED;
188562306a36Sopenharmony_ci	dev_err(dev, "handling crash #%u in %s\n", ++rproc->crash_cnt,
188662306a36Sopenharmony_ci		rproc->name);
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	mutex_unlock(&rproc->lock);
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	if (!rproc->recovery_disabled)
189162306a36Sopenharmony_ci		rproc_trigger_recovery(rproc);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ciout:
189462306a36Sopenharmony_ci	pm_relax(rproc->dev.parent);
189562306a36Sopenharmony_ci}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci/**
189862306a36Sopenharmony_ci * rproc_boot() - boot a remote processor
189962306a36Sopenharmony_ci * @rproc: handle of a remote processor
190062306a36Sopenharmony_ci *
190162306a36Sopenharmony_ci * Boot a remote processor (i.e. load its firmware, power it on, ...).
190262306a36Sopenharmony_ci *
190362306a36Sopenharmony_ci * If the remote processor is already powered on, this function immediately
190462306a36Sopenharmony_ci * returns (successfully).
190562306a36Sopenharmony_ci *
190662306a36Sopenharmony_ci * Return: 0 on success, and an appropriate error value otherwise
190762306a36Sopenharmony_ci */
190862306a36Sopenharmony_ciint rproc_boot(struct rproc *rproc)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	const struct firmware *firmware_p;
191162306a36Sopenharmony_ci	struct device *dev;
191262306a36Sopenharmony_ci	int ret;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	if (!rproc) {
191562306a36Sopenharmony_ci		pr_err("invalid rproc handle\n");
191662306a36Sopenharmony_ci		return -EINVAL;
191762306a36Sopenharmony_ci	}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	dev = &rproc->dev;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&rproc->lock);
192262306a36Sopenharmony_ci	if (ret) {
192362306a36Sopenharmony_ci		dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
192462306a36Sopenharmony_ci		return ret;
192562306a36Sopenharmony_ci	}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (rproc->state == RPROC_DELETED) {
192862306a36Sopenharmony_ci		ret = -ENODEV;
192962306a36Sopenharmony_ci		dev_err(dev, "can't boot deleted rproc %s\n", rproc->name);
193062306a36Sopenharmony_ci		goto unlock_mutex;
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	/* skip the boot or attach process if rproc is already powered up */
193462306a36Sopenharmony_ci	if (atomic_inc_return(&rproc->power) > 1) {
193562306a36Sopenharmony_ci		ret = 0;
193662306a36Sopenharmony_ci		goto unlock_mutex;
193762306a36Sopenharmony_ci	}
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	if (rproc->state == RPROC_DETACHED) {
194062306a36Sopenharmony_ci		dev_info(dev, "attaching to %s\n", rproc->name);
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci		ret = rproc_attach(rproc);
194362306a36Sopenharmony_ci	} else {
194462306a36Sopenharmony_ci		dev_info(dev, "powering up %s\n", rproc->name);
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci		/* load firmware */
194762306a36Sopenharmony_ci		ret = request_firmware(&firmware_p, rproc->firmware, dev);
194862306a36Sopenharmony_ci		if (ret < 0) {
194962306a36Sopenharmony_ci			dev_err(dev, "request_firmware failed: %d\n", ret);
195062306a36Sopenharmony_ci			goto downref_rproc;
195162306a36Sopenharmony_ci		}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci		ret = rproc_fw_boot(rproc, firmware_p);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci		release_firmware(firmware_p);
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_cidownref_rproc:
195962306a36Sopenharmony_ci	if (ret)
196062306a36Sopenharmony_ci		atomic_dec(&rproc->power);
196162306a36Sopenharmony_ciunlock_mutex:
196262306a36Sopenharmony_ci	mutex_unlock(&rproc->lock);
196362306a36Sopenharmony_ci	return ret;
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_boot);
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci/**
196862306a36Sopenharmony_ci * rproc_shutdown() - power off the remote processor
196962306a36Sopenharmony_ci * @rproc: the remote processor
197062306a36Sopenharmony_ci *
197162306a36Sopenharmony_ci * Power off a remote processor (previously booted with rproc_boot()).
197262306a36Sopenharmony_ci *
197362306a36Sopenharmony_ci * In case @rproc is still being used by an additional user(s), then
197462306a36Sopenharmony_ci * this function will just decrement the power refcount and exit,
197562306a36Sopenharmony_ci * without really powering off the device.
197662306a36Sopenharmony_ci *
197762306a36Sopenharmony_ci * Every call to rproc_boot() must (eventually) be accompanied by a call
197862306a36Sopenharmony_ci * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug.
197962306a36Sopenharmony_ci *
198062306a36Sopenharmony_ci * Notes:
198162306a36Sopenharmony_ci * - we're not decrementing the rproc's refcount, only the power refcount.
198262306a36Sopenharmony_ci *   which means that the @rproc handle stays valid even after rproc_shutdown()
198362306a36Sopenharmony_ci *   returns, and users can still use it with a subsequent rproc_boot(), if
198462306a36Sopenharmony_ci *   needed.
198562306a36Sopenharmony_ci *
198662306a36Sopenharmony_ci * Return: 0 on success, and an appropriate error value otherwise
198762306a36Sopenharmony_ci */
198862306a36Sopenharmony_ciint rproc_shutdown(struct rproc *rproc)
198962306a36Sopenharmony_ci{
199062306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
199162306a36Sopenharmony_ci	int ret = 0;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&rproc->lock);
199462306a36Sopenharmony_ci	if (ret) {
199562306a36Sopenharmony_ci		dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
199662306a36Sopenharmony_ci		return ret;
199762306a36Sopenharmony_ci	}
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	if (rproc->state != RPROC_RUNNING &&
200062306a36Sopenharmony_ci	    rproc->state != RPROC_ATTACHED) {
200162306a36Sopenharmony_ci		ret = -EINVAL;
200262306a36Sopenharmony_ci		goto out;
200362306a36Sopenharmony_ci	}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	/* if the remote proc is still needed, bail out */
200662306a36Sopenharmony_ci	if (!atomic_dec_and_test(&rproc->power))
200762306a36Sopenharmony_ci		goto out;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	ret = rproc_stop(rproc, false);
201062306a36Sopenharmony_ci	if (ret) {
201162306a36Sopenharmony_ci		atomic_inc(&rproc->power);
201262306a36Sopenharmony_ci		goto out;
201362306a36Sopenharmony_ci	}
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	/* clean up all acquired resources */
201662306a36Sopenharmony_ci	rproc_resource_cleanup(rproc);
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	/* release HW resources if needed */
201962306a36Sopenharmony_ci	rproc_unprepare_device(rproc);
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	rproc_disable_iommu(rproc);
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	/* Free the copy of the resource table */
202462306a36Sopenharmony_ci	kfree(rproc->cached_table);
202562306a36Sopenharmony_ci	rproc->cached_table = NULL;
202662306a36Sopenharmony_ci	rproc->table_ptr = NULL;
202762306a36Sopenharmony_ciout:
202862306a36Sopenharmony_ci	mutex_unlock(&rproc->lock);
202962306a36Sopenharmony_ci	return ret;
203062306a36Sopenharmony_ci}
203162306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_shutdown);
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci/**
203462306a36Sopenharmony_ci * rproc_detach() - Detach the remote processor from the
203562306a36Sopenharmony_ci * remoteproc core
203662306a36Sopenharmony_ci *
203762306a36Sopenharmony_ci * @rproc: the remote processor
203862306a36Sopenharmony_ci *
203962306a36Sopenharmony_ci * Detach a remote processor (previously attached to with rproc_attach()).
204062306a36Sopenharmony_ci *
204162306a36Sopenharmony_ci * In case @rproc is still being used by an additional user(s), then
204262306a36Sopenharmony_ci * this function will just decrement the power refcount and exit,
204362306a36Sopenharmony_ci * without disconnecting the device.
204462306a36Sopenharmony_ci *
204562306a36Sopenharmony_ci * Function rproc_detach() calls __rproc_detach() in order to let a remote
204662306a36Sopenharmony_ci * processor know that services provided by the application processor are
204762306a36Sopenharmony_ci * no longer available.  From there it should be possible to remove the
204862306a36Sopenharmony_ci * platform driver and even power cycle the application processor (if the HW
204962306a36Sopenharmony_ci * supports it) without needing to switch off the remote processor.
205062306a36Sopenharmony_ci *
205162306a36Sopenharmony_ci * Return: 0 on success, and an appropriate error value otherwise
205262306a36Sopenharmony_ci */
205362306a36Sopenharmony_ciint rproc_detach(struct rproc *rproc)
205462306a36Sopenharmony_ci{
205562306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
205662306a36Sopenharmony_ci	int ret;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&rproc->lock);
205962306a36Sopenharmony_ci	if (ret) {
206062306a36Sopenharmony_ci		dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
206162306a36Sopenharmony_ci		return ret;
206262306a36Sopenharmony_ci	}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	if (rproc->state != RPROC_ATTACHED) {
206562306a36Sopenharmony_ci		ret = -EINVAL;
206662306a36Sopenharmony_ci		goto out;
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	/* if the remote proc is still needed, bail out */
207062306a36Sopenharmony_ci	if (!atomic_dec_and_test(&rproc->power)) {
207162306a36Sopenharmony_ci		ret = 0;
207262306a36Sopenharmony_ci		goto out;
207362306a36Sopenharmony_ci	}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	ret = __rproc_detach(rproc);
207662306a36Sopenharmony_ci	if (ret) {
207762306a36Sopenharmony_ci		atomic_inc(&rproc->power);
207862306a36Sopenharmony_ci		goto out;
207962306a36Sopenharmony_ci	}
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	/* clean up all acquired resources */
208262306a36Sopenharmony_ci	rproc_resource_cleanup(rproc);
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	/* release HW resources if needed */
208562306a36Sopenharmony_ci	rproc_unprepare_device(rproc);
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	rproc_disable_iommu(rproc);
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	/* Free the copy of the resource table */
209062306a36Sopenharmony_ci	kfree(rproc->cached_table);
209162306a36Sopenharmony_ci	rproc->cached_table = NULL;
209262306a36Sopenharmony_ci	rproc->table_ptr = NULL;
209362306a36Sopenharmony_ciout:
209462306a36Sopenharmony_ci	mutex_unlock(&rproc->lock);
209562306a36Sopenharmony_ci	return ret;
209662306a36Sopenharmony_ci}
209762306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_detach);
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci/**
210062306a36Sopenharmony_ci * rproc_get_by_phandle() - find a remote processor by phandle
210162306a36Sopenharmony_ci * @phandle: phandle to the rproc
210262306a36Sopenharmony_ci *
210362306a36Sopenharmony_ci * Finds an rproc handle using the remote processor's phandle, and then
210462306a36Sopenharmony_ci * return a handle to the rproc.
210562306a36Sopenharmony_ci *
210662306a36Sopenharmony_ci * This function increments the remote processor's refcount, so always
210762306a36Sopenharmony_ci * use rproc_put() to decrement it back once rproc isn't needed anymore.
210862306a36Sopenharmony_ci *
210962306a36Sopenharmony_ci * Return: rproc handle on success, and NULL on failure
211062306a36Sopenharmony_ci */
211162306a36Sopenharmony_ci#ifdef CONFIG_OF
211262306a36Sopenharmony_cistruct rproc *rproc_get_by_phandle(phandle phandle)
211362306a36Sopenharmony_ci{
211462306a36Sopenharmony_ci	struct rproc *rproc = NULL, *r;
211562306a36Sopenharmony_ci	struct device_node *np;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	np = of_find_node_by_phandle(phandle);
211862306a36Sopenharmony_ci	if (!np)
211962306a36Sopenharmony_ci		return NULL;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	rcu_read_lock();
212262306a36Sopenharmony_ci	list_for_each_entry_rcu(r, &rproc_list, node) {
212362306a36Sopenharmony_ci		if (r->dev.parent && device_match_of_node(r->dev.parent, np)) {
212462306a36Sopenharmony_ci			/* prevent underlying implementation from being removed */
212562306a36Sopenharmony_ci			if (!try_module_get(r->dev.parent->driver->owner)) {
212662306a36Sopenharmony_ci				dev_err(&r->dev, "can't get owner\n");
212762306a36Sopenharmony_ci				break;
212862306a36Sopenharmony_ci			}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci			rproc = r;
213162306a36Sopenharmony_ci			get_device(&rproc->dev);
213262306a36Sopenharmony_ci			break;
213362306a36Sopenharmony_ci		}
213462306a36Sopenharmony_ci	}
213562306a36Sopenharmony_ci	rcu_read_unlock();
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	of_node_put(np);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	return rproc;
214062306a36Sopenharmony_ci}
214162306a36Sopenharmony_ci#else
214262306a36Sopenharmony_cistruct rproc *rproc_get_by_phandle(phandle phandle)
214362306a36Sopenharmony_ci{
214462306a36Sopenharmony_ci	return NULL;
214562306a36Sopenharmony_ci}
214662306a36Sopenharmony_ci#endif
214762306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_get_by_phandle);
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci/**
215062306a36Sopenharmony_ci * rproc_set_firmware() - assign a new firmware
215162306a36Sopenharmony_ci * @rproc: rproc handle to which the new firmware is being assigned
215262306a36Sopenharmony_ci * @fw_name: new firmware name to be assigned
215362306a36Sopenharmony_ci *
215462306a36Sopenharmony_ci * This function allows remoteproc drivers or clients to configure a custom
215562306a36Sopenharmony_ci * firmware name that is different from the default name used during remoteproc
215662306a36Sopenharmony_ci * registration. The function does not trigger a remote processor boot,
215762306a36Sopenharmony_ci * only sets the firmware name used for a subsequent boot. This function
215862306a36Sopenharmony_ci * should also be called only when the remote processor is offline.
215962306a36Sopenharmony_ci *
216062306a36Sopenharmony_ci * This allows either the userspace to configure a different name through
216162306a36Sopenharmony_ci * sysfs or a kernel-level remoteproc or a remoteproc client driver to set
216262306a36Sopenharmony_ci * a specific firmware when it is controlling the boot and shutdown of the
216362306a36Sopenharmony_ci * remote processor.
216462306a36Sopenharmony_ci *
216562306a36Sopenharmony_ci * Return: 0 on success or a negative value upon failure
216662306a36Sopenharmony_ci */
216762306a36Sopenharmony_ciint rproc_set_firmware(struct rproc *rproc, const char *fw_name)
216862306a36Sopenharmony_ci{
216962306a36Sopenharmony_ci	struct device *dev;
217062306a36Sopenharmony_ci	int ret, len;
217162306a36Sopenharmony_ci	char *p;
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	if (!rproc || !fw_name)
217462306a36Sopenharmony_ci		return -EINVAL;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	dev = rproc->dev.parent;
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&rproc->lock);
217962306a36Sopenharmony_ci	if (ret) {
218062306a36Sopenharmony_ci		dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
218162306a36Sopenharmony_ci		return -EINVAL;
218262306a36Sopenharmony_ci	}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	if (rproc->state != RPROC_OFFLINE) {
218562306a36Sopenharmony_ci		dev_err(dev, "can't change firmware while running\n");
218662306a36Sopenharmony_ci		ret = -EBUSY;
218762306a36Sopenharmony_ci		goto out;
218862306a36Sopenharmony_ci	}
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	len = strcspn(fw_name, "\n");
219162306a36Sopenharmony_ci	if (!len) {
219262306a36Sopenharmony_ci		dev_err(dev, "can't provide empty string for firmware name\n");
219362306a36Sopenharmony_ci		ret = -EINVAL;
219462306a36Sopenharmony_ci		goto out;
219562306a36Sopenharmony_ci	}
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	p = kstrndup(fw_name, len, GFP_KERNEL);
219862306a36Sopenharmony_ci	if (!p) {
219962306a36Sopenharmony_ci		ret = -ENOMEM;
220062306a36Sopenharmony_ci		goto out;
220162306a36Sopenharmony_ci	}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	kfree_const(rproc->firmware);
220462306a36Sopenharmony_ci	rproc->firmware = p;
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ciout:
220762306a36Sopenharmony_ci	mutex_unlock(&rproc->lock);
220862306a36Sopenharmony_ci	return ret;
220962306a36Sopenharmony_ci}
221062306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_set_firmware);
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_cistatic int rproc_validate(struct rproc *rproc)
221362306a36Sopenharmony_ci{
221462306a36Sopenharmony_ci	switch (rproc->state) {
221562306a36Sopenharmony_ci	case RPROC_OFFLINE:
221662306a36Sopenharmony_ci		/*
221762306a36Sopenharmony_ci		 * An offline processor without a start()
221862306a36Sopenharmony_ci		 * function makes no sense.
221962306a36Sopenharmony_ci		 */
222062306a36Sopenharmony_ci		if (!rproc->ops->start)
222162306a36Sopenharmony_ci			return -EINVAL;
222262306a36Sopenharmony_ci		break;
222362306a36Sopenharmony_ci	case RPROC_DETACHED:
222462306a36Sopenharmony_ci		/*
222562306a36Sopenharmony_ci		 * A remote processor in a detached state without an
222662306a36Sopenharmony_ci		 * attach() function makes not sense.
222762306a36Sopenharmony_ci		 */
222862306a36Sopenharmony_ci		if (!rproc->ops->attach)
222962306a36Sopenharmony_ci			return -EINVAL;
223062306a36Sopenharmony_ci		/*
223162306a36Sopenharmony_ci		 * When attaching to a remote processor the device memory
223262306a36Sopenharmony_ci		 * is already available and as such there is no need to have a
223362306a36Sopenharmony_ci		 * cached table.
223462306a36Sopenharmony_ci		 */
223562306a36Sopenharmony_ci		if (rproc->cached_table)
223662306a36Sopenharmony_ci			return -EINVAL;
223762306a36Sopenharmony_ci		break;
223862306a36Sopenharmony_ci	default:
223962306a36Sopenharmony_ci		/*
224062306a36Sopenharmony_ci		 * When adding a remote processor, the state of the device
224162306a36Sopenharmony_ci		 * can be offline or detached, nothing else.
224262306a36Sopenharmony_ci		 */
224362306a36Sopenharmony_ci		return -EINVAL;
224462306a36Sopenharmony_ci	}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	return 0;
224762306a36Sopenharmony_ci}
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci/**
225062306a36Sopenharmony_ci * rproc_add() - register a remote processor
225162306a36Sopenharmony_ci * @rproc: the remote processor handle to register
225262306a36Sopenharmony_ci *
225362306a36Sopenharmony_ci * Registers @rproc with the remoteproc framework, after it has been
225462306a36Sopenharmony_ci * allocated with rproc_alloc().
225562306a36Sopenharmony_ci *
225662306a36Sopenharmony_ci * This is called by the platform-specific rproc implementation, whenever
225762306a36Sopenharmony_ci * a new remote processor device is probed.
225862306a36Sopenharmony_ci *
225962306a36Sopenharmony_ci * Note: this function initiates an asynchronous firmware loading
226062306a36Sopenharmony_ci * context, which will look for virtio devices supported by the rproc's
226162306a36Sopenharmony_ci * firmware.
226262306a36Sopenharmony_ci *
226362306a36Sopenharmony_ci * If found, those virtio devices will be created and added, so as a result
226462306a36Sopenharmony_ci * of registering this remote processor, additional virtio drivers might be
226562306a36Sopenharmony_ci * probed.
226662306a36Sopenharmony_ci *
226762306a36Sopenharmony_ci * Return: 0 on success and an appropriate error code otherwise
226862306a36Sopenharmony_ci */
226962306a36Sopenharmony_ciint rproc_add(struct rproc *rproc)
227062306a36Sopenharmony_ci{
227162306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
227262306a36Sopenharmony_ci	int ret;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	ret = rproc_validate(rproc);
227562306a36Sopenharmony_ci	if (ret < 0)
227662306a36Sopenharmony_ci		return ret;
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	/* add char device for this remoteproc */
227962306a36Sopenharmony_ci	ret = rproc_char_device_add(rproc);
228062306a36Sopenharmony_ci	if (ret < 0)
228162306a36Sopenharmony_ci		return ret;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	ret = device_add(dev);
228462306a36Sopenharmony_ci	if (ret < 0) {
228562306a36Sopenharmony_ci		put_device(dev);
228662306a36Sopenharmony_ci		goto rproc_remove_cdev;
228762306a36Sopenharmony_ci	}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	dev_info(dev, "%s is available\n", rproc->name);
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	/* create debugfs entries */
229262306a36Sopenharmony_ci	rproc_create_debug_dir(rproc);
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	/* if rproc is marked always-on, request it to boot */
229562306a36Sopenharmony_ci	if (rproc->auto_boot) {
229662306a36Sopenharmony_ci		ret = rproc_trigger_auto_boot(rproc);
229762306a36Sopenharmony_ci		if (ret < 0)
229862306a36Sopenharmony_ci			goto rproc_remove_dev;
229962306a36Sopenharmony_ci	}
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	/* expose to rproc_get_by_phandle users */
230262306a36Sopenharmony_ci	mutex_lock(&rproc_list_mutex);
230362306a36Sopenharmony_ci	list_add_rcu(&rproc->node, &rproc_list);
230462306a36Sopenharmony_ci	mutex_unlock(&rproc_list_mutex);
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	return 0;
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_cirproc_remove_dev:
230962306a36Sopenharmony_ci	rproc_delete_debug_dir(rproc);
231062306a36Sopenharmony_ci	device_del(dev);
231162306a36Sopenharmony_cirproc_remove_cdev:
231262306a36Sopenharmony_ci	rproc_char_device_remove(rproc);
231362306a36Sopenharmony_ci	return ret;
231462306a36Sopenharmony_ci}
231562306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_add);
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_cistatic void devm_rproc_remove(void *rproc)
231862306a36Sopenharmony_ci{
231962306a36Sopenharmony_ci	rproc_del(rproc);
232062306a36Sopenharmony_ci}
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_ci/**
232362306a36Sopenharmony_ci * devm_rproc_add() - resource managed rproc_add()
232462306a36Sopenharmony_ci * @dev: the underlying device
232562306a36Sopenharmony_ci * @rproc: the remote processor handle to register
232662306a36Sopenharmony_ci *
232762306a36Sopenharmony_ci * This function performs like rproc_add() but the registered rproc device will
232862306a36Sopenharmony_ci * automatically be removed on driver detach.
232962306a36Sopenharmony_ci *
233062306a36Sopenharmony_ci * Return: 0 on success, negative errno on failure
233162306a36Sopenharmony_ci */
233262306a36Sopenharmony_ciint devm_rproc_add(struct device *dev, struct rproc *rproc)
233362306a36Sopenharmony_ci{
233462306a36Sopenharmony_ci	int err;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	err = rproc_add(rproc);
233762306a36Sopenharmony_ci	if (err)
233862306a36Sopenharmony_ci		return err;
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci	return devm_add_action_or_reset(dev, devm_rproc_remove, rproc);
234162306a36Sopenharmony_ci}
234262306a36Sopenharmony_ciEXPORT_SYMBOL(devm_rproc_add);
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci/**
234562306a36Sopenharmony_ci * rproc_type_release() - release a remote processor instance
234662306a36Sopenharmony_ci * @dev: the rproc's device
234762306a36Sopenharmony_ci *
234862306a36Sopenharmony_ci * This function should _never_ be called directly.
234962306a36Sopenharmony_ci *
235062306a36Sopenharmony_ci * It will be called by the driver core when no one holds a valid pointer
235162306a36Sopenharmony_ci * to @dev anymore.
235262306a36Sopenharmony_ci */
235362306a36Sopenharmony_cistatic void rproc_type_release(struct device *dev)
235462306a36Sopenharmony_ci{
235562306a36Sopenharmony_ci	struct rproc *rproc = container_of(dev, struct rproc, dev);
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	dev_info(&rproc->dev, "releasing %s\n", rproc->name);
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	idr_destroy(&rproc->notifyids);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	if (rproc->index >= 0)
236262306a36Sopenharmony_ci		ida_free(&rproc_dev_index, rproc->index);
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	kfree_const(rproc->firmware);
236562306a36Sopenharmony_ci	kfree_const(rproc->name);
236662306a36Sopenharmony_ci	kfree(rproc->ops);
236762306a36Sopenharmony_ci	kfree(rproc);
236862306a36Sopenharmony_ci}
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_cistatic const struct device_type rproc_type = {
237162306a36Sopenharmony_ci	.name		= "remoteproc",
237262306a36Sopenharmony_ci	.release	= rproc_type_release,
237362306a36Sopenharmony_ci};
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_cistatic int rproc_alloc_firmware(struct rproc *rproc,
237662306a36Sopenharmony_ci				const char *name, const char *firmware)
237762306a36Sopenharmony_ci{
237862306a36Sopenharmony_ci	const char *p;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	/*
238162306a36Sopenharmony_ci	 * Allocate a firmware name if the caller gave us one to work
238262306a36Sopenharmony_ci	 * with.  Otherwise construct a new one using a default pattern.
238362306a36Sopenharmony_ci	 */
238462306a36Sopenharmony_ci	if (firmware)
238562306a36Sopenharmony_ci		p = kstrdup_const(firmware, GFP_KERNEL);
238662306a36Sopenharmony_ci	else
238762306a36Sopenharmony_ci		p = kasprintf(GFP_KERNEL, "rproc-%s-fw", name);
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	if (!p)
239062306a36Sopenharmony_ci		return -ENOMEM;
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	rproc->firmware = p;
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	return 0;
239562306a36Sopenharmony_ci}
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_cistatic int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
239862306a36Sopenharmony_ci{
239962306a36Sopenharmony_ci	rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL);
240062306a36Sopenharmony_ci	if (!rproc->ops)
240162306a36Sopenharmony_ci		return -ENOMEM;
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	/* Default to rproc_coredump if no coredump function is specified */
240462306a36Sopenharmony_ci	if (!rproc->ops->coredump)
240562306a36Sopenharmony_ci		rproc->ops->coredump = rproc_coredump;
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	if (rproc->ops->load)
240862306a36Sopenharmony_ci		return 0;
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	/* Default to ELF loader if no load function is specified */
241162306a36Sopenharmony_ci	rproc->ops->load = rproc_elf_load_segments;
241262306a36Sopenharmony_ci	rproc->ops->parse_fw = rproc_elf_load_rsc_table;
241362306a36Sopenharmony_ci	rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table;
241462306a36Sopenharmony_ci	rproc->ops->sanity_check = rproc_elf_sanity_check;
241562306a36Sopenharmony_ci	rproc->ops->get_boot_addr = rproc_elf_get_boot_addr;
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	return 0;
241862306a36Sopenharmony_ci}
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci/**
242162306a36Sopenharmony_ci * rproc_alloc() - allocate a remote processor handle
242262306a36Sopenharmony_ci * @dev: the underlying device
242362306a36Sopenharmony_ci * @name: name of this remote processor
242462306a36Sopenharmony_ci * @ops: platform-specific handlers (mainly start/stop)
242562306a36Sopenharmony_ci * @firmware: name of firmware file to load, can be NULL
242662306a36Sopenharmony_ci * @len: length of private data needed by the rproc driver (in bytes)
242762306a36Sopenharmony_ci *
242862306a36Sopenharmony_ci * Allocates a new remote processor handle, but does not register
242962306a36Sopenharmony_ci * it yet. if @firmware is NULL, a default name is used.
243062306a36Sopenharmony_ci *
243162306a36Sopenharmony_ci * This function should be used by rproc implementations during initialization
243262306a36Sopenharmony_ci * of the remote processor.
243362306a36Sopenharmony_ci *
243462306a36Sopenharmony_ci * After creating an rproc handle using this function, and when ready,
243562306a36Sopenharmony_ci * implementations should then call rproc_add() to complete
243662306a36Sopenharmony_ci * the registration of the remote processor.
243762306a36Sopenharmony_ci *
243862306a36Sopenharmony_ci * Note: _never_ directly deallocate @rproc, even if it was not registered
243962306a36Sopenharmony_ci * yet. Instead, when you need to unroll rproc_alloc(), use rproc_free().
244062306a36Sopenharmony_ci *
244162306a36Sopenharmony_ci * Return: new rproc pointer on success, and NULL on failure
244262306a36Sopenharmony_ci */
244362306a36Sopenharmony_cistruct rproc *rproc_alloc(struct device *dev, const char *name,
244462306a36Sopenharmony_ci			  const struct rproc_ops *ops,
244562306a36Sopenharmony_ci			  const char *firmware, int len)
244662306a36Sopenharmony_ci{
244762306a36Sopenharmony_ci	struct rproc *rproc;
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	if (!dev || !name || !ops)
245062306a36Sopenharmony_ci		return NULL;
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
245362306a36Sopenharmony_ci	if (!rproc)
245462306a36Sopenharmony_ci		return NULL;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	rproc->priv = &rproc[1];
245762306a36Sopenharmony_ci	rproc->auto_boot = true;
245862306a36Sopenharmony_ci	rproc->elf_class = ELFCLASSNONE;
245962306a36Sopenharmony_ci	rproc->elf_machine = EM_NONE;
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci	device_initialize(&rproc->dev);
246262306a36Sopenharmony_ci	rproc->dev.parent = dev;
246362306a36Sopenharmony_ci	rproc->dev.type = &rproc_type;
246462306a36Sopenharmony_ci	rproc->dev.class = &rproc_class;
246562306a36Sopenharmony_ci	rproc->dev.driver_data = rproc;
246662306a36Sopenharmony_ci	idr_init(&rproc->notifyids);
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	rproc->name = kstrdup_const(name, GFP_KERNEL);
246962306a36Sopenharmony_ci	if (!rproc->name)
247062306a36Sopenharmony_ci		goto put_device;
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	if (rproc_alloc_firmware(rproc, name, firmware))
247362306a36Sopenharmony_ci		goto put_device;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	if (rproc_alloc_ops(rproc, ops))
247662306a36Sopenharmony_ci		goto put_device;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	/* Assign a unique device index and name */
247962306a36Sopenharmony_ci	rproc->index = ida_alloc(&rproc_dev_index, GFP_KERNEL);
248062306a36Sopenharmony_ci	if (rproc->index < 0) {
248162306a36Sopenharmony_ci		dev_err(dev, "ida_alloc failed: %d\n", rproc->index);
248262306a36Sopenharmony_ci		goto put_device;
248362306a36Sopenharmony_ci	}
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	dev_set_name(&rproc->dev, "remoteproc%d", rproc->index);
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	atomic_set(&rproc->power, 0);
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	mutex_init(&rproc->lock);
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	INIT_LIST_HEAD(&rproc->carveouts);
249262306a36Sopenharmony_ci	INIT_LIST_HEAD(&rproc->mappings);
249362306a36Sopenharmony_ci	INIT_LIST_HEAD(&rproc->traces);
249462306a36Sopenharmony_ci	INIT_LIST_HEAD(&rproc->rvdevs);
249562306a36Sopenharmony_ci	INIT_LIST_HEAD(&rproc->subdevs);
249662306a36Sopenharmony_ci	INIT_LIST_HEAD(&rproc->dump_segments);
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	rproc->state = RPROC_OFFLINE;
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	return rproc;
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ciput_device:
250562306a36Sopenharmony_ci	put_device(&rproc->dev);
250662306a36Sopenharmony_ci	return NULL;
250762306a36Sopenharmony_ci}
250862306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_alloc);
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci/**
251162306a36Sopenharmony_ci * rproc_free() - unroll rproc_alloc()
251262306a36Sopenharmony_ci * @rproc: the remote processor handle
251362306a36Sopenharmony_ci *
251462306a36Sopenharmony_ci * This function decrements the rproc dev refcount.
251562306a36Sopenharmony_ci *
251662306a36Sopenharmony_ci * If no one holds any reference to rproc anymore, then its refcount would
251762306a36Sopenharmony_ci * now drop to zero, and it would be freed.
251862306a36Sopenharmony_ci */
251962306a36Sopenharmony_civoid rproc_free(struct rproc *rproc)
252062306a36Sopenharmony_ci{
252162306a36Sopenharmony_ci	put_device(&rproc->dev);
252262306a36Sopenharmony_ci}
252362306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_free);
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci/**
252662306a36Sopenharmony_ci * rproc_put() - release rproc reference
252762306a36Sopenharmony_ci * @rproc: the remote processor handle
252862306a36Sopenharmony_ci *
252962306a36Sopenharmony_ci * This function decrements the rproc dev refcount.
253062306a36Sopenharmony_ci *
253162306a36Sopenharmony_ci * If no one holds any reference to rproc anymore, then its refcount would
253262306a36Sopenharmony_ci * now drop to zero, and it would be freed.
253362306a36Sopenharmony_ci */
253462306a36Sopenharmony_civoid rproc_put(struct rproc *rproc)
253562306a36Sopenharmony_ci{
253662306a36Sopenharmony_ci	module_put(rproc->dev.parent->driver->owner);
253762306a36Sopenharmony_ci	put_device(&rproc->dev);
253862306a36Sopenharmony_ci}
253962306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_put);
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci/**
254262306a36Sopenharmony_ci * rproc_del() - unregister a remote processor
254362306a36Sopenharmony_ci * @rproc: rproc handle to unregister
254462306a36Sopenharmony_ci *
254562306a36Sopenharmony_ci * This function should be called when the platform specific rproc
254662306a36Sopenharmony_ci * implementation decides to remove the rproc device. it should
254762306a36Sopenharmony_ci * _only_ be called if a previous invocation of rproc_add()
254862306a36Sopenharmony_ci * has completed successfully.
254962306a36Sopenharmony_ci *
255062306a36Sopenharmony_ci * After rproc_del() returns, @rproc isn't freed yet, because
255162306a36Sopenharmony_ci * of the outstanding reference created by rproc_alloc. To decrement that
255262306a36Sopenharmony_ci * one last refcount, one still needs to call rproc_free().
255362306a36Sopenharmony_ci *
255462306a36Sopenharmony_ci * Return: 0 on success and -EINVAL if @rproc isn't valid
255562306a36Sopenharmony_ci */
255662306a36Sopenharmony_ciint rproc_del(struct rproc *rproc)
255762306a36Sopenharmony_ci{
255862306a36Sopenharmony_ci	if (!rproc)
255962306a36Sopenharmony_ci		return -EINVAL;
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	/* TODO: make sure this works with rproc->power > 1 */
256262306a36Sopenharmony_ci	rproc_shutdown(rproc);
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	mutex_lock(&rproc->lock);
256562306a36Sopenharmony_ci	rproc->state = RPROC_DELETED;
256662306a36Sopenharmony_ci	mutex_unlock(&rproc->lock);
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	rproc_delete_debug_dir(rproc);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	/* the rproc is downref'ed as soon as it's removed from the klist */
257162306a36Sopenharmony_ci	mutex_lock(&rproc_list_mutex);
257262306a36Sopenharmony_ci	list_del_rcu(&rproc->node);
257362306a36Sopenharmony_ci	mutex_unlock(&rproc_list_mutex);
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	/* Ensure that no readers of rproc_list are still active */
257662306a36Sopenharmony_ci	synchronize_rcu();
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	device_del(&rproc->dev);
257962306a36Sopenharmony_ci	rproc_char_device_remove(rproc);
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	return 0;
258262306a36Sopenharmony_ci}
258362306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_del);
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_cistatic void devm_rproc_free(struct device *dev, void *res)
258662306a36Sopenharmony_ci{
258762306a36Sopenharmony_ci	rproc_free(*(struct rproc **)res);
258862306a36Sopenharmony_ci}
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci/**
259162306a36Sopenharmony_ci * devm_rproc_alloc() - resource managed rproc_alloc()
259262306a36Sopenharmony_ci * @dev: the underlying device
259362306a36Sopenharmony_ci * @name: name of this remote processor
259462306a36Sopenharmony_ci * @ops: platform-specific handlers (mainly start/stop)
259562306a36Sopenharmony_ci * @firmware: name of firmware file to load, can be NULL
259662306a36Sopenharmony_ci * @len: length of private data needed by the rproc driver (in bytes)
259762306a36Sopenharmony_ci *
259862306a36Sopenharmony_ci * This function performs like rproc_alloc() but the acquired rproc device will
259962306a36Sopenharmony_ci * automatically be released on driver detach.
260062306a36Sopenharmony_ci *
260162306a36Sopenharmony_ci * Return: new rproc instance, or NULL on failure
260262306a36Sopenharmony_ci */
260362306a36Sopenharmony_cistruct rproc *devm_rproc_alloc(struct device *dev, const char *name,
260462306a36Sopenharmony_ci			       const struct rproc_ops *ops,
260562306a36Sopenharmony_ci			       const char *firmware, int len)
260662306a36Sopenharmony_ci{
260762306a36Sopenharmony_ci	struct rproc **ptr, *rproc;
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	ptr = devres_alloc(devm_rproc_free, sizeof(*ptr), GFP_KERNEL);
261062306a36Sopenharmony_ci	if (!ptr)
261162306a36Sopenharmony_ci		return NULL;
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	rproc = rproc_alloc(dev, name, ops, firmware, len);
261462306a36Sopenharmony_ci	if (rproc) {
261562306a36Sopenharmony_ci		*ptr = rproc;
261662306a36Sopenharmony_ci		devres_add(dev, ptr);
261762306a36Sopenharmony_ci	} else {
261862306a36Sopenharmony_ci		devres_free(ptr);
261962306a36Sopenharmony_ci	}
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci	return rproc;
262262306a36Sopenharmony_ci}
262362306a36Sopenharmony_ciEXPORT_SYMBOL(devm_rproc_alloc);
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci/**
262662306a36Sopenharmony_ci * rproc_add_subdev() - add a subdevice to a remoteproc
262762306a36Sopenharmony_ci * @rproc: rproc handle to add the subdevice to
262862306a36Sopenharmony_ci * @subdev: subdev handle to register
262962306a36Sopenharmony_ci *
263062306a36Sopenharmony_ci * Caller is responsible for populating optional subdevice function pointers.
263162306a36Sopenharmony_ci */
263262306a36Sopenharmony_civoid rproc_add_subdev(struct rproc *rproc, struct rproc_subdev *subdev)
263362306a36Sopenharmony_ci{
263462306a36Sopenharmony_ci	list_add_tail(&subdev->node, &rproc->subdevs);
263562306a36Sopenharmony_ci}
263662306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_add_subdev);
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci/**
263962306a36Sopenharmony_ci * rproc_remove_subdev() - remove a subdevice from a remoteproc
264062306a36Sopenharmony_ci * @rproc: rproc handle to remove the subdevice from
264162306a36Sopenharmony_ci * @subdev: subdev handle, previously registered with rproc_add_subdev()
264262306a36Sopenharmony_ci */
264362306a36Sopenharmony_civoid rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev)
264462306a36Sopenharmony_ci{
264562306a36Sopenharmony_ci	list_del(&subdev->node);
264662306a36Sopenharmony_ci}
264762306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_remove_subdev);
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci/**
265062306a36Sopenharmony_ci * rproc_get_by_child() - acquire rproc handle of @dev's ancestor
265162306a36Sopenharmony_ci * @dev:	child device to find ancestor of
265262306a36Sopenharmony_ci *
265362306a36Sopenharmony_ci * Return: the ancestor rproc instance, or NULL if not found
265462306a36Sopenharmony_ci */
265562306a36Sopenharmony_cistruct rproc *rproc_get_by_child(struct device *dev)
265662306a36Sopenharmony_ci{
265762306a36Sopenharmony_ci	for (dev = dev->parent; dev; dev = dev->parent) {
265862306a36Sopenharmony_ci		if (dev->type == &rproc_type)
265962306a36Sopenharmony_ci			return dev->driver_data;
266062306a36Sopenharmony_ci	}
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ci	return NULL;
266362306a36Sopenharmony_ci}
266462306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_get_by_child);
266562306a36Sopenharmony_ci
266662306a36Sopenharmony_ci/**
266762306a36Sopenharmony_ci * rproc_report_crash() - rproc crash reporter function
266862306a36Sopenharmony_ci * @rproc: remote processor
266962306a36Sopenharmony_ci * @type: crash type
267062306a36Sopenharmony_ci *
267162306a36Sopenharmony_ci * This function must be called every time a crash is detected by the low-level
267262306a36Sopenharmony_ci * drivers implementing a specific remoteproc. This should not be called from a
267362306a36Sopenharmony_ci * non-remoteproc driver.
267462306a36Sopenharmony_ci *
267562306a36Sopenharmony_ci * This function can be called from atomic/interrupt context.
267662306a36Sopenharmony_ci */
267762306a36Sopenharmony_civoid rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
267862306a36Sopenharmony_ci{
267962306a36Sopenharmony_ci	if (!rproc) {
268062306a36Sopenharmony_ci		pr_err("NULL rproc pointer\n");
268162306a36Sopenharmony_ci		return;
268262306a36Sopenharmony_ci	}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	/* Prevent suspend while the remoteproc is being recovered */
268562306a36Sopenharmony_ci	pm_stay_awake(rproc->dev.parent);
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	dev_err(&rproc->dev, "crash detected in %s: type %s\n",
268862306a36Sopenharmony_ci		rproc->name, rproc_crash_to_string(type));
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	queue_work(rproc_recovery_wq, &rproc->crash_handler);
269162306a36Sopenharmony_ci}
269262306a36Sopenharmony_ciEXPORT_SYMBOL(rproc_report_crash);
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_cistatic int rproc_panic_handler(struct notifier_block *nb, unsigned long event,
269562306a36Sopenharmony_ci			       void *ptr)
269662306a36Sopenharmony_ci{
269762306a36Sopenharmony_ci	unsigned int longest = 0;
269862306a36Sopenharmony_ci	struct rproc *rproc;
269962306a36Sopenharmony_ci	unsigned int d;
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	rcu_read_lock();
270262306a36Sopenharmony_ci	list_for_each_entry_rcu(rproc, &rproc_list, node) {
270362306a36Sopenharmony_ci		if (!rproc->ops->panic)
270462306a36Sopenharmony_ci			continue;
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci		if (rproc->state != RPROC_RUNNING &&
270762306a36Sopenharmony_ci		    rproc->state != RPROC_ATTACHED)
270862306a36Sopenharmony_ci			continue;
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci		d = rproc->ops->panic(rproc);
271162306a36Sopenharmony_ci		longest = max(longest, d);
271262306a36Sopenharmony_ci	}
271362306a36Sopenharmony_ci	rcu_read_unlock();
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	/*
271662306a36Sopenharmony_ci	 * Delay for the longest requested duration before returning. This can
271762306a36Sopenharmony_ci	 * be used by the remoteproc drivers to give the remote processor time
271862306a36Sopenharmony_ci	 * to perform any requested operations (such as flush caches), when
271962306a36Sopenharmony_ci	 * it's not possible to signal the Linux side due to the panic.
272062306a36Sopenharmony_ci	 */
272162306a36Sopenharmony_ci	mdelay(longest);
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	return NOTIFY_DONE;
272462306a36Sopenharmony_ci}
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_cistatic void __init rproc_init_panic(void)
272762306a36Sopenharmony_ci{
272862306a36Sopenharmony_ci	rproc_panic_nb.notifier_call = rproc_panic_handler;
272962306a36Sopenharmony_ci	atomic_notifier_chain_register(&panic_notifier_list, &rproc_panic_nb);
273062306a36Sopenharmony_ci}
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_cistatic void __exit rproc_exit_panic(void)
273362306a36Sopenharmony_ci{
273462306a36Sopenharmony_ci	atomic_notifier_chain_unregister(&panic_notifier_list, &rproc_panic_nb);
273562306a36Sopenharmony_ci}
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_cistatic int __init remoteproc_init(void)
273862306a36Sopenharmony_ci{
273962306a36Sopenharmony_ci	rproc_recovery_wq = alloc_workqueue("rproc_recovery_wq",
274062306a36Sopenharmony_ci						WQ_UNBOUND | WQ_FREEZABLE, 0);
274162306a36Sopenharmony_ci	if (!rproc_recovery_wq) {
274262306a36Sopenharmony_ci		pr_err("remoteproc: creation of rproc_recovery_wq failed\n");
274362306a36Sopenharmony_ci		return -ENOMEM;
274462306a36Sopenharmony_ci	}
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	rproc_init_sysfs();
274762306a36Sopenharmony_ci	rproc_init_debugfs();
274862306a36Sopenharmony_ci	rproc_init_cdev();
274962306a36Sopenharmony_ci	rproc_init_panic();
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	return 0;
275262306a36Sopenharmony_ci}
275362306a36Sopenharmony_cisubsys_initcall(remoteproc_init);
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_cistatic void __exit remoteproc_exit(void)
275662306a36Sopenharmony_ci{
275762306a36Sopenharmony_ci	ida_destroy(&rproc_dev_index);
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	if (!rproc_recovery_wq)
276062306a36Sopenharmony_ci		return;
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	rproc_exit_panic();
276362306a36Sopenharmony_ci	rproc_exit_debugfs();
276462306a36Sopenharmony_ci	rproc_exit_sysfs();
276562306a36Sopenharmony_ci	destroy_workqueue(rproc_recovery_wq);
276662306a36Sopenharmony_ci}
276762306a36Sopenharmony_cimodule_exit(remoteproc_exit);
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic Remote Processor Framework");
2770