162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ZynqMP R5 Remote Processor driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <dt-bindings/power/xlnx-zynqmp-power.h>
862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
962306a36Sopenharmony_ci#include <linux/firmware/xlnx-zynqmp.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/mailbox_client.h>
1262306a36Sopenharmony_ci#include <linux/mailbox/zynqmp-ipi-message.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of_address.h>
1562306a36Sopenharmony_ci#include <linux/of_platform.h>
1662306a36Sopenharmony_ci#include <linux/of_reserved_mem.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/remoteproc.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "remoteproc_internal.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* IPI buffer MAX length */
2362306a36Sopenharmony_ci#define IPI_BUF_LEN_MAX	32U
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* RX mailbox client buffer max length */
2662306a36Sopenharmony_ci#define MBOX_CLIENT_BUF_MAX	(IPI_BUF_LEN_MAX + \
2762306a36Sopenharmony_ci				 sizeof(struct zynqmp_ipi_message))
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * settings for RPU cluster mode which
3062306a36Sopenharmony_ci * reflects possible values of xlnx,cluster-mode dt-property
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_cienum zynqmp_r5_cluster_mode {
3362306a36Sopenharmony_ci	SPLIT_MODE = 0, /* When cores run as separate processor */
3462306a36Sopenharmony_ci	LOCKSTEP_MODE = 1, /* cores execute same code in lockstep,clk-for-clk */
3562306a36Sopenharmony_ci	SINGLE_CPU_MODE = 2, /* core0 is held in reset and only core1 runs */
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/**
3962306a36Sopenharmony_ci * struct mem_bank_data - Memory Bank description
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * @addr: Start address of memory bank
4262306a36Sopenharmony_ci * @size: Size of Memory bank
4362306a36Sopenharmony_ci * @pm_domain_id: Power-domains id of memory bank for firmware to turn on/off
4462306a36Sopenharmony_ci * @bank_name: name of the bank for remoteproc framework
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_cistruct mem_bank_data {
4762306a36Sopenharmony_ci	phys_addr_t addr;
4862306a36Sopenharmony_ci	size_t size;
4962306a36Sopenharmony_ci	u32 pm_domain_id;
5062306a36Sopenharmony_ci	char *bank_name;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/**
5462306a36Sopenharmony_ci * struct mbox_info
5562306a36Sopenharmony_ci *
5662306a36Sopenharmony_ci * @rx_mc_buf: to copy data from mailbox rx channel
5762306a36Sopenharmony_ci * @tx_mc_buf: to copy data to mailbox tx channel
5862306a36Sopenharmony_ci * @r5_core: this mailbox's corresponding r5_core pointer
5962306a36Sopenharmony_ci * @mbox_work: schedule work after receiving data from mailbox
6062306a36Sopenharmony_ci * @mbox_cl: mailbox client
6162306a36Sopenharmony_ci * @tx_chan: mailbox tx channel
6262306a36Sopenharmony_ci * @rx_chan: mailbox rx channel
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistruct mbox_info {
6562306a36Sopenharmony_ci	unsigned char rx_mc_buf[MBOX_CLIENT_BUF_MAX];
6662306a36Sopenharmony_ci	unsigned char tx_mc_buf[MBOX_CLIENT_BUF_MAX];
6762306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
6862306a36Sopenharmony_ci	struct work_struct mbox_work;
6962306a36Sopenharmony_ci	struct mbox_client mbox_cl;
7062306a36Sopenharmony_ci	struct mbox_chan *tx_chan;
7162306a36Sopenharmony_ci	struct mbox_chan *rx_chan;
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * Hardcoded TCM bank values. This will be removed once TCM bindings are
7662306a36Sopenharmony_ci * accepted for system-dt specifications and upstreamed in linux kernel
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_cistatic const struct mem_bank_data zynqmp_tcm_banks[] = {
7962306a36Sopenharmony_ci	{0xffe00000UL, 0x10000UL, PD_R5_0_ATCM, "atcm0"}, /* TCM 64KB each */
8062306a36Sopenharmony_ci	{0xffe20000UL, 0x10000UL, PD_R5_0_BTCM, "btcm0"},
8162306a36Sopenharmony_ci	{0xffe90000UL, 0x10000UL, PD_R5_1_ATCM, "atcm1"},
8262306a36Sopenharmony_ci	{0xffeb0000UL, 0x10000UL, PD_R5_1_BTCM, "btcm1"},
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/**
8662306a36Sopenharmony_ci * struct zynqmp_r5_core
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * @dev: device of RPU instance
8962306a36Sopenharmony_ci * @np: device node of RPU instance
9062306a36Sopenharmony_ci * @tcm_bank_count: number TCM banks accessible to this RPU
9162306a36Sopenharmony_ci * @tcm_banks: array of each TCM bank data
9262306a36Sopenharmony_ci * @rproc: rproc handle
9362306a36Sopenharmony_ci * @pm_domain_id: RPU CPU power domain id
9462306a36Sopenharmony_ci * @ipi: pointer to mailbox information
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_cistruct zynqmp_r5_core {
9762306a36Sopenharmony_ci	struct device *dev;
9862306a36Sopenharmony_ci	struct device_node *np;
9962306a36Sopenharmony_ci	int tcm_bank_count;
10062306a36Sopenharmony_ci	struct mem_bank_data **tcm_banks;
10162306a36Sopenharmony_ci	struct rproc *rproc;
10262306a36Sopenharmony_ci	u32 pm_domain_id;
10362306a36Sopenharmony_ci	struct mbox_info *ipi;
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/**
10762306a36Sopenharmony_ci * struct zynqmp_r5_cluster
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * @dev: r5f subsystem cluster device node
11062306a36Sopenharmony_ci * @mode: cluster mode of type zynqmp_r5_cluster_mode
11162306a36Sopenharmony_ci * @core_count: number of r5 cores used for this cluster mode
11262306a36Sopenharmony_ci * @r5_cores: Array of pointers pointing to r5 core
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cistruct zynqmp_r5_cluster {
11562306a36Sopenharmony_ci	struct device *dev;
11662306a36Sopenharmony_ci	enum  zynqmp_r5_cluster_mode mode;
11762306a36Sopenharmony_ci	int core_count;
11862306a36Sopenharmony_ci	struct zynqmp_r5_core **r5_cores;
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/**
12262306a36Sopenharmony_ci * event_notified_idr_cb() - callback for vq_interrupt per notifyid
12362306a36Sopenharmony_ci * @id: rproc->notify id
12462306a36Sopenharmony_ci * @ptr: pointer to idr private data
12562306a36Sopenharmony_ci * @data: data passed to idr_for_each callback
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci * Pass notification to remoteproc virtio
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * Return: 0. having return is to satisfy the idr_for_each() function
13062306a36Sopenharmony_ci *          pointer input argument requirement.
13162306a36Sopenharmony_ci **/
13262306a36Sopenharmony_cistatic int event_notified_idr_cb(int id, void *ptr, void *data)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct rproc *rproc = data;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (rproc_vq_interrupt(rproc, id) == IRQ_NONE)
13762306a36Sopenharmony_ci		dev_dbg(&rproc->dev, "data not found for vqid=%d\n", id);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/**
14362306a36Sopenharmony_ci * handle_event_notified() - remoteproc notification work function
14462306a36Sopenharmony_ci * @work: pointer to the work structure
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * It checks each registered remoteproc notify IDs.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_cistatic void handle_event_notified(struct work_struct *work)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct mbox_info *ipi;
15162306a36Sopenharmony_ci	struct rproc *rproc;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ipi = container_of(work, struct mbox_info, mbox_work);
15462306a36Sopenharmony_ci	rproc = ipi->r5_core->rproc;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * We only use IPI for interrupt. The RPU firmware side may or may
15862306a36Sopenharmony_ci	 * not write the notifyid when it trigger IPI.
15962306a36Sopenharmony_ci	 * And thus, we scan through all the registered notifyids and
16062306a36Sopenharmony_ci	 * find which one is valid to get the message.
16162306a36Sopenharmony_ci	 * Even if message from firmware is NULL, we attempt to get vqid
16262306a36Sopenharmony_ci	 */
16362306a36Sopenharmony_ci	idr_for_each(&rproc->notifyids, event_notified_idr_cb, rproc);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/**
16762306a36Sopenharmony_ci * zynqmp_r5_mb_rx_cb() - receive channel mailbox callback
16862306a36Sopenharmony_ci * @cl: mailbox client
16962306a36Sopenharmony_ci * @msg: message pointer
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci * Receive data from ipi buffer, ack interrupt and then
17262306a36Sopenharmony_ci * it will schedule the R5 notification work.
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_cistatic void zynqmp_r5_mb_rx_cb(struct mbox_client *cl, void *msg)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct zynqmp_ipi_message *ipi_msg, *buf_msg;
17762306a36Sopenharmony_ci	struct mbox_info *ipi;
17862306a36Sopenharmony_ci	size_t len;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ipi = container_of(cl, struct mbox_info, mbox_cl);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* copy data from ipi buffer to r5_core */
18362306a36Sopenharmony_ci	ipi_msg = (struct zynqmp_ipi_message *)msg;
18462306a36Sopenharmony_ci	buf_msg = (struct zynqmp_ipi_message *)ipi->rx_mc_buf;
18562306a36Sopenharmony_ci	len = ipi_msg->len;
18662306a36Sopenharmony_ci	if (len > IPI_BUF_LEN_MAX) {
18762306a36Sopenharmony_ci		dev_warn(cl->dev, "msg size exceeded than %d\n",
18862306a36Sopenharmony_ci			 IPI_BUF_LEN_MAX);
18962306a36Sopenharmony_ci		len = IPI_BUF_LEN_MAX;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci	buf_msg->len = len;
19262306a36Sopenharmony_ci	memcpy(buf_msg->data, ipi_msg->data, len);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* received and processed interrupt ack */
19562306a36Sopenharmony_ci	if (mbox_send_message(ipi->rx_chan, NULL) < 0)
19662306a36Sopenharmony_ci		dev_err(cl->dev, "ack failed to mbox rx_chan\n");
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	schedule_work(&ipi->mbox_work);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/**
20262306a36Sopenharmony_ci * zynqmp_r5_setup_mbox() - Setup mailboxes related properties
20362306a36Sopenharmony_ci *			    this is used for each individual R5 core
20462306a36Sopenharmony_ci *
20562306a36Sopenharmony_ci * @cdev: child node device
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci * Function to setup mailboxes related properties
20862306a36Sopenharmony_ci * return : NULL if failed else pointer to mbox_info
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_cistatic struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct mbox_client *mbox_cl;
21362306a36Sopenharmony_ci	struct mbox_info *ipi;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ipi = kzalloc(sizeof(*ipi), GFP_KERNEL);
21662306a36Sopenharmony_ci	if (!ipi)
21762306a36Sopenharmony_ci		return NULL;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	mbox_cl = &ipi->mbox_cl;
22062306a36Sopenharmony_ci	mbox_cl->rx_callback = zynqmp_r5_mb_rx_cb;
22162306a36Sopenharmony_ci	mbox_cl->tx_block = false;
22262306a36Sopenharmony_ci	mbox_cl->knows_txdone = false;
22362306a36Sopenharmony_ci	mbox_cl->tx_done = NULL;
22462306a36Sopenharmony_ci	mbox_cl->dev = cdev;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Request TX and RX channels */
22762306a36Sopenharmony_ci	ipi->tx_chan = mbox_request_channel_byname(mbox_cl, "tx");
22862306a36Sopenharmony_ci	if (IS_ERR(ipi->tx_chan)) {
22962306a36Sopenharmony_ci		ipi->tx_chan = NULL;
23062306a36Sopenharmony_ci		kfree(ipi);
23162306a36Sopenharmony_ci		dev_warn(cdev, "mbox tx channel request failed\n");
23262306a36Sopenharmony_ci		return NULL;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	ipi->rx_chan = mbox_request_channel_byname(mbox_cl, "rx");
23662306a36Sopenharmony_ci	if (IS_ERR(ipi->rx_chan)) {
23762306a36Sopenharmony_ci		mbox_free_channel(ipi->tx_chan);
23862306a36Sopenharmony_ci		ipi->rx_chan = NULL;
23962306a36Sopenharmony_ci		ipi->tx_chan = NULL;
24062306a36Sopenharmony_ci		kfree(ipi);
24162306a36Sopenharmony_ci		dev_warn(cdev, "mbox rx channel request failed\n");
24262306a36Sopenharmony_ci		return NULL;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	INIT_WORK(&ipi->mbox_work, handle_event_notified);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return ipi;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void zynqmp_r5_free_mbox(struct mbox_info *ipi)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	if (!ipi)
25362306a36Sopenharmony_ci		return;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (ipi->tx_chan) {
25662306a36Sopenharmony_ci		mbox_free_channel(ipi->tx_chan);
25762306a36Sopenharmony_ci		ipi->tx_chan = NULL;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (ipi->rx_chan) {
26162306a36Sopenharmony_ci		mbox_free_channel(ipi->rx_chan);
26262306a36Sopenharmony_ci		ipi->rx_chan = NULL;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	kfree(ipi);
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/*
26962306a36Sopenharmony_ci * zynqmp_r5_core_kick() - kick a firmware if mbox is provided
27062306a36Sopenharmony_ci * @rproc: r5 core's corresponding rproc structure
27162306a36Sopenharmony_ci * @vqid: virtqueue ID
27262306a36Sopenharmony_ci */
27362306a36Sopenharmony_cistatic void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core = rproc->priv;
27662306a36Sopenharmony_ci	struct device *dev = r5_core->dev;
27762306a36Sopenharmony_ci	struct zynqmp_ipi_message *mb_msg;
27862306a36Sopenharmony_ci	struct mbox_info *ipi;
27962306a36Sopenharmony_ci	int ret;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	ipi = r5_core->ipi;
28262306a36Sopenharmony_ci	if (!ipi)
28362306a36Sopenharmony_ci		return;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	mb_msg = (struct zynqmp_ipi_message *)ipi->tx_mc_buf;
28662306a36Sopenharmony_ci	memcpy(mb_msg->data, &vqid, sizeof(vqid));
28762306a36Sopenharmony_ci	mb_msg->len = sizeof(vqid);
28862306a36Sopenharmony_ci	ret = mbox_send_message(ipi->tx_chan, mb_msg);
28962306a36Sopenharmony_ci	if (ret < 0)
29062306a36Sopenharmony_ci		dev_warn(dev, "failed to send message\n");
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/*
29462306a36Sopenharmony_ci * zynqmp_r5_set_mode()
29562306a36Sopenharmony_ci *
29662306a36Sopenharmony_ci * set RPU cluster and TCM operation mode
29762306a36Sopenharmony_ci *
29862306a36Sopenharmony_ci * @r5_core: pointer to zynqmp_r5_core type object
29962306a36Sopenharmony_ci * @fw_reg_val: value expected by firmware to configure RPU cluster mode
30062306a36Sopenharmony_ci * @tcm_mode: value expected by fw to configure TCM mode (lockstep or split)
30162306a36Sopenharmony_ci *
30262306a36Sopenharmony_ci * Return: 0 for success and < 0 for failure
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_cistatic int zynqmp_r5_set_mode(struct zynqmp_r5_core *r5_core,
30562306a36Sopenharmony_ci			      enum rpu_oper_mode fw_reg_val,
30662306a36Sopenharmony_ci			      enum rpu_tcm_comb tcm_mode)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	int ret;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	ret = zynqmp_pm_set_rpu_mode(r5_core->pm_domain_id, fw_reg_val);
31162306a36Sopenharmony_ci	if (ret < 0) {
31262306a36Sopenharmony_ci		dev_err(r5_core->dev, "failed to set RPU mode\n");
31362306a36Sopenharmony_ci		return ret;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	ret = zynqmp_pm_set_tcm_config(r5_core->pm_domain_id, tcm_mode);
31762306a36Sopenharmony_ci	if (ret < 0)
31862306a36Sopenharmony_ci		dev_err(r5_core->dev, "failed to configure TCM\n");
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return ret;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/*
32462306a36Sopenharmony_ci * zynqmp_r5_rproc_start()
32562306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
32662306a36Sopenharmony_ci *
32762306a36Sopenharmony_ci * Start R5 Core from designated boot address.
32862306a36Sopenharmony_ci *
32962306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
33062306a36Sopenharmony_ci */
33162306a36Sopenharmony_cistatic int zynqmp_r5_rproc_start(struct rproc *rproc)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core = rproc->priv;
33462306a36Sopenharmony_ci	enum rpu_boot_mem bootmem;
33562306a36Sopenharmony_ci	int ret;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/*
33862306a36Sopenharmony_ci	 * The exception vector pointers (EVP) refer to the base-address of
33962306a36Sopenharmony_ci	 * exception vectors (for reset, IRQ, FIQ, etc). The reset-vector
34062306a36Sopenharmony_ci	 * starts at the base-address and subsequent vectors are on 4-byte
34162306a36Sopenharmony_ci	 * boundaries.
34262306a36Sopenharmony_ci	 *
34362306a36Sopenharmony_ci	 * Exception vectors can start either from 0x0000_0000 (LOVEC) or
34462306a36Sopenharmony_ci	 * from 0xFFFF_0000 (HIVEC) which is mapped in the OCM (On-Chip Memory)
34562306a36Sopenharmony_ci	 *
34662306a36Sopenharmony_ci	 * Usually firmware will put Exception vectors at LOVEC.
34762306a36Sopenharmony_ci	 *
34862306a36Sopenharmony_ci	 * It is not recommend that you change the exception vector.
34962306a36Sopenharmony_ci	 * Changing the EVP to HIVEC will result in increased interrupt latency
35062306a36Sopenharmony_ci	 * and jitter. Also, if the OCM is secured and the Cortex-R5F processor
35162306a36Sopenharmony_ci	 * is non-secured, then the Cortex-R5F processor cannot access the
35262306a36Sopenharmony_ci	 * HIVEC exception vectors in the OCM.
35362306a36Sopenharmony_ci	 */
35462306a36Sopenharmony_ci	bootmem = (rproc->bootaddr >= 0xFFFC0000) ?
35562306a36Sopenharmony_ci		   PM_RPU_BOOTMEM_HIVEC : PM_RPU_BOOTMEM_LOVEC;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	dev_dbg(r5_core->dev, "RPU boot addr 0x%llx from %s.", rproc->bootaddr,
35862306a36Sopenharmony_ci		bootmem == PM_RPU_BOOTMEM_HIVEC ? "OCM" : "TCM");
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	ret = zynqmp_pm_request_wake(r5_core->pm_domain_id, 1,
36162306a36Sopenharmony_ci				     bootmem, ZYNQMP_PM_REQUEST_ACK_NO);
36262306a36Sopenharmony_ci	if (ret)
36362306a36Sopenharmony_ci		dev_err(r5_core->dev,
36462306a36Sopenharmony_ci			"failed to start RPU = 0x%x\n", r5_core->pm_domain_id);
36562306a36Sopenharmony_ci	return ret;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/*
36962306a36Sopenharmony_ci * zynqmp_r5_rproc_stop()
37062306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
37162306a36Sopenharmony_ci *
37262306a36Sopenharmony_ci * Power down  R5 Core.
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
37562306a36Sopenharmony_ci */
37662306a36Sopenharmony_cistatic int zynqmp_r5_rproc_stop(struct rproc *rproc)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core = rproc->priv;
37962306a36Sopenharmony_ci	int ret;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	ret = zynqmp_pm_force_pwrdwn(r5_core->pm_domain_id,
38262306a36Sopenharmony_ci				     ZYNQMP_PM_REQUEST_ACK_BLOCKING);
38362306a36Sopenharmony_ci	if (ret)
38462306a36Sopenharmony_ci		dev_err(r5_core->dev, "failed to stop remoteproc RPU %d\n", ret);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return ret;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci/*
39062306a36Sopenharmony_ci * zynqmp_r5_mem_region_map()
39162306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
39262306a36Sopenharmony_ci * @mem: mem descriptor to map reserved memory-regions
39362306a36Sopenharmony_ci *
39462306a36Sopenharmony_ci * Callback to map va for memory-region's carveout.
39562306a36Sopenharmony_ci *
39662306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cistatic int zynqmp_r5_mem_region_map(struct rproc *rproc,
39962306a36Sopenharmony_ci				    struct rproc_mem_entry *mem)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	void __iomem *va;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	va = ioremap_wc(mem->dma, mem->len);
40462306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(va))
40562306a36Sopenharmony_ci		return -ENOMEM;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	mem->va = (void *)va;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return 0;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci/*
41362306a36Sopenharmony_ci * zynqmp_r5_rproc_mem_unmap
41462306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
41562306a36Sopenharmony_ci * @mem: mem entry to unmap
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * Unmap memory-region carveout
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * return: always returns 0
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_cistatic int zynqmp_r5_mem_region_unmap(struct rproc *rproc,
42262306a36Sopenharmony_ci				      struct rproc_mem_entry *mem)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	iounmap((void __iomem *)mem->va);
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/*
42962306a36Sopenharmony_ci * add_mem_regions_carveout()
43062306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * Construct rproc mem carveouts from memory-region property nodes
43362306a36Sopenharmony_ci *
43462306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_cistatic int add_mem_regions_carveout(struct rproc *rproc)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct rproc_mem_entry *rproc_mem;
43962306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
44062306a36Sopenharmony_ci	struct of_phandle_iterator it;
44162306a36Sopenharmony_ci	struct reserved_mem *rmem;
44262306a36Sopenharmony_ci	int i = 0;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	r5_core = rproc->priv;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* Register associated reserved memory regions */
44762306a36Sopenharmony_ci	of_phandle_iterator_init(&it, r5_core->np, "memory-region", NULL, 0);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	while (of_phandle_iterator_next(&it) == 0) {
45062306a36Sopenharmony_ci		rmem = of_reserved_mem_lookup(it.node);
45162306a36Sopenharmony_ci		if (!rmem) {
45262306a36Sopenharmony_ci			of_node_put(it.node);
45362306a36Sopenharmony_ci			dev_err(&rproc->dev, "unable to acquire memory-region\n");
45462306a36Sopenharmony_ci			return -EINVAL;
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		if (!strcmp(it.node->name, "vdev0buffer")) {
45862306a36Sopenharmony_ci			/* Init reserved memory for vdev buffer */
45962306a36Sopenharmony_ci			rproc_mem = rproc_of_resm_mem_entry_init(&rproc->dev, i,
46062306a36Sopenharmony_ci								 rmem->size,
46162306a36Sopenharmony_ci								 rmem->base,
46262306a36Sopenharmony_ci								 it.node->name);
46362306a36Sopenharmony_ci		} else {
46462306a36Sopenharmony_ci			/* Register associated reserved memory regions */
46562306a36Sopenharmony_ci			rproc_mem = rproc_mem_entry_init(&rproc->dev, NULL,
46662306a36Sopenharmony_ci							 (dma_addr_t)rmem->base,
46762306a36Sopenharmony_ci							 rmem->size, rmem->base,
46862306a36Sopenharmony_ci							 zynqmp_r5_mem_region_map,
46962306a36Sopenharmony_ci							 zynqmp_r5_mem_region_unmap,
47062306a36Sopenharmony_ci							 it.node->name);
47162306a36Sopenharmony_ci		}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		if (!rproc_mem) {
47462306a36Sopenharmony_ci			of_node_put(it.node);
47562306a36Sopenharmony_ci			return -ENOMEM;
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		rproc_add_carveout(rproc, rproc_mem);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		dev_dbg(&rproc->dev, "reserved mem carveout %s addr=%llx, size=0x%llx",
48162306a36Sopenharmony_ci			it.node->name, rmem->base, rmem->size);
48262306a36Sopenharmony_ci		i++;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return 0;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci/*
48962306a36Sopenharmony_ci * tcm_mem_unmap()
49062306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
49162306a36Sopenharmony_ci * @mem: tcm mem entry to unmap
49262306a36Sopenharmony_ci *
49362306a36Sopenharmony_ci * Unmap TCM banks when powering down R5 core.
49462306a36Sopenharmony_ci *
49562306a36Sopenharmony_ci * return always 0
49662306a36Sopenharmony_ci */
49762306a36Sopenharmony_cistatic int tcm_mem_unmap(struct rproc *rproc, struct rproc_mem_entry *mem)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	iounmap((void __iomem *)mem->va);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return 0;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/*
50562306a36Sopenharmony_ci * tcm_mem_map()
50662306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
50762306a36Sopenharmony_ci * @mem: tcm memory entry descriptor
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci * Given TCM bank entry, this func setup virtual address for TCM bank
51062306a36Sopenharmony_ci * remoteproc carveout. It also takes care of va to da address translation
51162306a36Sopenharmony_ci *
51262306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
51362306a36Sopenharmony_ci */
51462306a36Sopenharmony_cistatic int tcm_mem_map(struct rproc *rproc,
51562306a36Sopenharmony_ci		       struct rproc_mem_entry *mem)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	void __iomem *va;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	va = ioremap_wc(mem->dma, mem->len);
52062306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(va))
52162306a36Sopenharmony_ci		return -ENOMEM;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Update memory entry va */
52462306a36Sopenharmony_ci	mem->va = (void *)va;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* clear TCMs */
52762306a36Sopenharmony_ci	memset_io(va, 0, mem->len);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/*
53062306a36Sopenharmony_ci	 * The R5s expect their TCM banks to be at address 0x0 and 0x2000,
53162306a36Sopenharmony_ci	 * while on the Linux side they are at 0xffexxxxx.
53262306a36Sopenharmony_ci	 *
53362306a36Sopenharmony_ci	 * Zero out the high 12 bits of the address. This will give
53462306a36Sopenharmony_ci	 * expected values for TCM Banks 0A and 0B (0x0 and 0x20000).
53562306a36Sopenharmony_ci	 */
53662306a36Sopenharmony_ci	mem->da &= 0x000fffff;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/*
53962306a36Sopenharmony_ci	 * TCM Banks 1A and 1B still have to be translated.
54062306a36Sopenharmony_ci	 *
54162306a36Sopenharmony_ci	 * Below handle these two banks' absolute addresses (0xffe90000 and
54262306a36Sopenharmony_ci	 * 0xffeb0000) and convert to the expected relative addresses
54362306a36Sopenharmony_ci	 * (0x0 and 0x20000).
54462306a36Sopenharmony_ci	 */
54562306a36Sopenharmony_ci	if (mem->da == 0x90000 || mem->da == 0xB0000)
54662306a36Sopenharmony_ci		mem->da -= 0x90000;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* if translated TCM bank address is not valid report error */
54962306a36Sopenharmony_ci	if (mem->da != 0x0 && mem->da != 0x20000) {
55062306a36Sopenharmony_ci		dev_err(&rproc->dev, "invalid TCM address: %x\n", mem->da);
55162306a36Sopenharmony_ci		return -EINVAL;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci	return 0;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/*
55762306a36Sopenharmony_ci * add_tcm_carveout_split_mode()
55862306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
55962306a36Sopenharmony_ci *
56062306a36Sopenharmony_ci * allocate and add remoteproc carveout for TCM memory in split mode
56162306a36Sopenharmony_ci *
56262306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
56362306a36Sopenharmony_ci */
56462306a36Sopenharmony_cistatic int add_tcm_carveout_split_mode(struct rproc *rproc)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct rproc_mem_entry *rproc_mem;
56762306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
56862306a36Sopenharmony_ci	int i, num_banks, ret;
56962306a36Sopenharmony_ci	phys_addr_t bank_addr;
57062306a36Sopenharmony_ci	struct device *dev;
57162306a36Sopenharmony_ci	u32 pm_domain_id;
57262306a36Sopenharmony_ci	size_t bank_size;
57362306a36Sopenharmony_ci	char *bank_name;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	r5_core = rproc->priv;
57662306a36Sopenharmony_ci	dev = r5_core->dev;
57762306a36Sopenharmony_ci	num_banks = r5_core->tcm_bank_count;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/*
58062306a36Sopenharmony_ci	 * Power-on Each 64KB TCM,
58162306a36Sopenharmony_ci	 * register its address space, map and unmap functions
58262306a36Sopenharmony_ci	 * and add carveouts accordingly
58362306a36Sopenharmony_ci	 */
58462306a36Sopenharmony_ci	for (i = 0; i < num_banks; i++) {
58562306a36Sopenharmony_ci		bank_addr = r5_core->tcm_banks[i]->addr;
58662306a36Sopenharmony_ci		bank_name = r5_core->tcm_banks[i]->bank_name;
58762306a36Sopenharmony_ci		bank_size = r5_core->tcm_banks[i]->size;
58862306a36Sopenharmony_ci		pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		ret = zynqmp_pm_request_node(pm_domain_id,
59162306a36Sopenharmony_ci					     ZYNQMP_PM_CAPABILITY_ACCESS, 0,
59262306a36Sopenharmony_ci					     ZYNQMP_PM_REQUEST_ACK_BLOCKING);
59362306a36Sopenharmony_ci		if (ret < 0) {
59462306a36Sopenharmony_ci			dev_err(dev, "failed to turn on TCM 0x%x", pm_domain_id);
59562306a36Sopenharmony_ci			goto release_tcm_split;
59662306a36Sopenharmony_ci		}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		dev_dbg(dev, "TCM carveout split mode %s addr=%llx, size=0x%lx",
59962306a36Sopenharmony_ci			bank_name, bank_addr, bank_size);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		rproc_mem = rproc_mem_entry_init(dev, NULL, bank_addr,
60262306a36Sopenharmony_ci						 bank_size, bank_addr,
60362306a36Sopenharmony_ci						 tcm_mem_map, tcm_mem_unmap,
60462306a36Sopenharmony_ci						 bank_name);
60562306a36Sopenharmony_ci		if (!rproc_mem) {
60662306a36Sopenharmony_ci			ret = -ENOMEM;
60762306a36Sopenharmony_ci			zynqmp_pm_release_node(pm_domain_id);
60862306a36Sopenharmony_ci			goto release_tcm_split;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		rproc_add_carveout(rproc, rproc_mem);
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	return 0;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cirelease_tcm_split:
61762306a36Sopenharmony_ci	/* If failed, Turn off all TCM banks turned on before */
61862306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
61962306a36Sopenharmony_ci		pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id;
62062306a36Sopenharmony_ci		zynqmp_pm_release_node(pm_domain_id);
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci	return ret;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/*
62662306a36Sopenharmony_ci * add_tcm_carveout_lockstep_mode()
62762306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
62862306a36Sopenharmony_ci *
62962306a36Sopenharmony_ci * allocate and add remoteproc carveout for TCM memory in lockstep mode
63062306a36Sopenharmony_ci *
63162306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
63262306a36Sopenharmony_ci */
63362306a36Sopenharmony_cistatic int add_tcm_carveout_lockstep_mode(struct rproc *rproc)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct rproc_mem_entry *rproc_mem;
63662306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
63762306a36Sopenharmony_ci	int i, num_banks, ret;
63862306a36Sopenharmony_ci	phys_addr_t bank_addr;
63962306a36Sopenharmony_ci	size_t bank_size = 0;
64062306a36Sopenharmony_ci	struct device *dev;
64162306a36Sopenharmony_ci	u32 pm_domain_id;
64262306a36Sopenharmony_ci	char *bank_name;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	r5_core = rproc->priv;
64562306a36Sopenharmony_ci	dev = r5_core->dev;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/* Go through zynqmp banks for r5 node */
64862306a36Sopenharmony_ci	num_banks = r5_core->tcm_bank_count;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/*
65162306a36Sopenharmony_ci	 * In lockstep mode, TCM is contiguous memory block
65262306a36Sopenharmony_ci	 * However, each TCM block still needs to be enabled individually.
65362306a36Sopenharmony_ci	 * So, Enable each TCM block individually, but add their size
65462306a36Sopenharmony_ci	 * to create contiguous memory region.
65562306a36Sopenharmony_ci	 */
65662306a36Sopenharmony_ci	bank_addr = r5_core->tcm_banks[0]->addr;
65762306a36Sopenharmony_ci	bank_name = r5_core->tcm_banks[0]->bank_name;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	for (i = 0; i < num_banks; i++) {
66062306a36Sopenharmony_ci		bank_size += r5_core->tcm_banks[i]->size;
66162306a36Sopenharmony_ci		pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		/* Turn on each TCM bank individually */
66462306a36Sopenharmony_ci		ret = zynqmp_pm_request_node(pm_domain_id,
66562306a36Sopenharmony_ci					     ZYNQMP_PM_CAPABILITY_ACCESS, 0,
66662306a36Sopenharmony_ci					     ZYNQMP_PM_REQUEST_ACK_BLOCKING);
66762306a36Sopenharmony_ci		if (ret < 0) {
66862306a36Sopenharmony_ci			dev_err(dev, "failed to turn on TCM 0x%x", pm_domain_id);
66962306a36Sopenharmony_ci			goto release_tcm_lockstep;
67062306a36Sopenharmony_ci		}
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	dev_dbg(dev, "TCM add carveout lockstep mode %s addr=0x%llx, size=0x%lx",
67462306a36Sopenharmony_ci		bank_name, bank_addr, bank_size);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	/* Register TCM address range, TCM map and unmap functions */
67762306a36Sopenharmony_ci	rproc_mem = rproc_mem_entry_init(dev, NULL, bank_addr,
67862306a36Sopenharmony_ci					 bank_size, bank_addr,
67962306a36Sopenharmony_ci					 tcm_mem_map, tcm_mem_unmap,
68062306a36Sopenharmony_ci					 bank_name);
68162306a36Sopenharmony_ci	if (!rproc_mem) {
68262306a36Sopenharmony_ci		ret = -ENOMEM;
68362306a36Sopenharmony_ci		goto release_tcm_lockstep;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* If registration is success, add carveouts */
68762306a36Sopenharmony_ci	rproc_add_carveout(rproc, rproc_mem);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cirelease_tcm_lockstep:
69262306a36Sopenharmony_ci	/* If failed, Turn off all TCM banks turned on before */
69362306a36Sopenharmony_ci	for (i--; i >= 0; i--) {
69462306a36Sopenharmony_ci		pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id;
69562306a36Sopenharmony_ci		zynqmp_pm_release_node(pm_domain_id);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci	return ret;
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci/*
70162306a36Sopenharmony_ci * add_tcm_banks()
70262306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
70362306a36Sopenharmony_ci *
70462306a36Sopenharmony_ci * allocate and add remoteproc carveouts for TCM memory based on cluster mode
70562306a36Sopenharmony_ci *
70662306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
70762306a36Sopenharmony_ci */
70862306a36Sopenharmony_cistatic int add_tcm_banks(struct rproc *rproc)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct zynqmp_r5_cluster *cluster;
71162306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
71262306a36Sopenharmony_ci	struct device *dev;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	r5_core = rproc->priv;
71562306a36Sopenharmony_ci	if (!r5_core)
71662306a36Sopenharmony_ci		return -EINVAL;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	dev = r5_core->dev;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	cluster = dev_get_drvdata(dev->parent);
72162306a36Sopenharmony_ci	if (!cluster) {
72262306a36Sopenharmony_ci		dev_err(dev->parent, "Invalid driver data\n");
72362306a36Sopenharmony_ci		return -EINVAL;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/*
72762306a36Sopenharmony_ci	 * In lockstep mode TCM banks are one contiguous memory region of 256Kb
72862306a36Sopenharmony_ci	 * In split mode, each TCM bank is 64Kb and not contiguous.
72962306a36Sopenharmony_ci	 * We add memory carveouts accordingly.
73062306a36Sopenharmony_ci	 */
73162306a36Sopenharmony_ci	if (cluster->mode == SPLIT_MODE)
73262306a36Sopenharmony_ci		return add_tcm_carveout_split_mode(rproc);
73362306a36Sopenharmony_ci	else if (cluster->mode == LOCKSTEP_MODE)
73462306a36Sopenharmony_ci		return add_tcm_carveout_lockstep_mode(rproc);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	return -EINVAL;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci/*
74062306a36Sopenharmony_ci * zynqmp_r5_parse_fw()
74162306a36Sopenharmony_ci * @rproc: single R5 core's corresponding rproc instance
74262306a36Sopenharmony_ci * @fw: ptr to firmware to be loaded onto r5 core
74362306a36Sopenharmony_ci *
74462306a36Sopenharmony_ci * get resource table if available
74562306a36Sopenharmony_ci *
74662306a36Sopenharmony_ci * return 0 on success, otherwise non-zero value on failure
74762306a36Sopenharmony_ci */
74862306a36Sopenharmony_cistatic int zynqmp_r5_parse_fw(struct rproc *rproc, const struct firmware *fw)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	int ret;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	ret = rproc_elf_load_rsc_table(rproc, fw);
75362306a36Sopenharmony_ci	if (ret == -EINVAL) {
75462306a36Sopenharmony_ci		/*
75562306a36Sopenharmony_ci		 * resource table only required for IPC.
75662306a36Sopenharmony_ci		 * if not present, this is not necessarily an error;
75762306a36Sopenharmony_ci		 * for example, loading r5 hello world application
75862306a36Sopenharmony_ci		 * so simply inform user and keep going.
75962306a36Sopenharmony_ci		 */
76062306a36Sopenharmony_ci		dev_info(&rproc->dev, "no resource table found.\n");
76162306a36Sopenharmony_ci		ret = 0;
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci	return ret;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci/**
76762306a36Sopenharmony_ci * zynqmp_r5_rproc_prepare()
76862306a36Sopenharmony_ci * adds carveouts for TCM bank and reserved memory regions
76962306a36Sopenharmony_ci *
77062306a36Sopenharmony_ci * @rproc: Device node of each rproc
77162306a36Sopenharmony_ci *
77262306a36Sopenharmony_ci * Return: 0 for success else < 0 error code
77362306a36Sopenharmony_ci */
77462306a36Sopenharmony_cistatic int zynqmp_r5_rproc_prepare(struct rproc *rproc)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	int ret;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	ret = add_tcm_banks(rproc);
77962306a36Sopenharmony_ci	if (ret) {
78062306a36Sopenharmony_ci		dev_err(&rproc->dev, "failed to get TCM banks, err %d\n", ret);
78162306a36Sopenharmony_ci		return ret;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	ret = add_mem_regions_carveout(rproc);
78562306a36Sopenharmony_ci	if (ret) {
78662306a36Sopenharmony_ci		dev_err(&rproc->dev, "failed to get reserve mem regions %d\n", ret);
78762306a36Sopenharmony_ci		return ret;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	return 0;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci/**
79462306a36Sopenharmony_ci * zynqmp_r5_rproc_unprepare()
79562306a36Sopenharmony_ci * Turns off TCM banks using power-domain id
79662306a36Sopenharmony_ci *
79762306a36Sopenharmony_ci * @rproc: Device node of each rproc
79862306a36Sopenharmony_ci *
79962306a36Sopenharmony_ci * Return: always 0
80062306a36Sopenharmony_ci */
80162306a36Sopenharmony_cistatic int zynqmp_r5_rproc_unprepare(struct rproc *rproc)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
80462306a36Sopenharmony_ci	u32 pm_domain_id;
80562306a36Sopenharmony_ci	int i;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	r5_core = rproc->priv;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	for (i = 0; i < r5_core->tcm_bank_count; i++) {
81062306a36Sopenharmony_ci		pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id;
81162306a36Sopenharmony_ci		if (zynqmp_pm_release_node(pm_domain_id))
81262306a36Sopenharmony_ci			dev_warn(r5_core->dev,
81362306a36Sopenharmony_ci				 "can't turn off TCM bank 0x%x", pm_domain_id);
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return 0;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic const struct rproc_ops zynqmp_r5_rproc_ops = {
82062306a36Sopenharmony_ci	.prepare	= zynqmp_r5_rproc_prepare,
82162306a36Sopenharmony_ci	.unprepare	= zynqmp_r5_rproc_unprepare,
82262306a36Sopenharmony_ci	.start		= zynqmp_r5_rproc_start,
82362306a36Sopenharmony_ci	.stop		= zynqmp_r5_rproc_stop,
82462306a36Sopenharmony_ci	.load		= rproc_elf_load_segments,
82562306a36Sopenharmony_ci	.parse_fw	= zynqmp_r5_parse_fw,
82662306a36Sopenharmony_ci	.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
82762306a36Sopenharmony_ci	.sanity_check	= rproc_elf_sanity_check,
82862306a36Sopenharmony_ci	.get_boot_addr	= rproc_elf_get_boot_addr,
82962306a36Sopenharmony_ci	.kick		= zynqmp_r5_rproc_kick,
83062306a36Sopenharmony_ci};
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci/**
83362306a36Sopenharmony_ci * zynqmp_r5_add_rproc_core()
83462306a36Sopenharmony_ci * Allocate and add struct rproc object for each r5f core
83562306a36Sopenharmony_ci * This is called for each individual r5f core
83662306a36Sopenharmony_ci *
83762306a36Sopenharmony_ci * @cdev: Device node of each r5 core
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci * Return: zynqmp_r5_core object for success else error code pointer
84062306a36Sopenharmony_ci */
84162306a36Sopenharmony_cistatic struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
84462306a36Sopenharmony_ci	struct rproc *r5_rproc;
84562306a36Sopenharmony_ci	int ret;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* Set up DMA mask */
84862306a36Sopenharmony_ci	ret = dma_set_coherent_mask(cdev, DMA_BIT_MASK(32));
84962306a36Sopenharmony_ci	if (ret)
85062306a36Sopenharmony_ci		return ERR_PTR(ret);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* Allocate remoteproc instance */
85362306a36Sopenharmony_ci	r5_rproc = rproc_alloc(cdev, dev_name(cdev),
85462306a36Sopenharmony_ci			       &zynqmp_r5_rproc_ops,
85562306a36Sopenharmony_ci			       NULL, sizeof(struct zynqmp_r5_core));
85662306a36Sopenharmony_ci	if (!r5_rproc) {
85762306a36Sopenharmony_ci		dev_err(cdev, "failed to allocate memory for rproc instance\n");
85862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	r5_rproc->auto_boot = false;
86262306a36Sopenharmony_ci	r5_core = r5_rproc->priv;
86362306a36Sopenharmony_ci	r5_core->dev = cdev;
86462306a36Sopenharmony_ci	r5_core->np = dev_of_node(cdev);
86562306a36Sopenharmony_ci	if (!r5_core->np) {
86662306a36Sopenharmony_ci		dev_err(cdev, "can't get device node for r5 core\n");
86762306a36Sopenharmony_ci		ret = -EINVAL;
86862306a36Sopenharmony_ci		goto free_rproc;
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* Add R5 remoteproc core */
87262306a36Sopenharmony_ci	ret = rproc_add(r5_rproc);
87362306a36Sopenharmony_ci	if (ret) {
87462306a36Sopenharmony_ci		dev_err(cdev, "failed to add r5 remoteproc\n");
87562306a36Sopenharmony_ci		goto free_rproc;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	r5_core->rproc = r5_rproc;
87962306a36Sopenharmony_ci	return r5_core;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cifree_rproc:
88262306a36Sopenharmony_ci	rproc_free(r5_rproc);
88362306a36Sopenharmony_ci	return ERR_PTR(ret);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci/**
88762306a36Sopenharmony_ci * zynqmp_r5_get_tcm_node()
88862306a36Sopenharmony_ci * Ideally this function should parse tcm node and store information
88962306a36Sopenharmony_ci * in r5_core instance. For now, Hardcoded TCM information is used.
89062306a36Sopenharmony_ci * This approach is used as TCM bindings for system-dt is being developed
89162306a36Sopenharmony_ci *
89262306a36Sopenharmony_ci * @cluster: pointer to zynqmp_r5_cluster type object
89362306a36Sopenharmony_ci *
89462306a36Sopenharmony_ci * Return: 0 for success and < 0 error code for failure.
89562306a36Sopenharmony_ci */
89662306a36Sopenharmony_cistatic int zynqmp_r5_get_tcm_node(struct zynqmp_r5_cluster *cluster)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct device *dev = cluster->dev;
89962306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
90062306a36Sopenharmony_ci	int tcm_bank_count, tcm_node;
90162306a36Sopenharmony_ci	int i, j;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	tcm_bank_count = ARRAY_SIZE(zynqmp_tcm_banks);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/* count per core tcm banks */
90662306a36Sopenharmony_ci	tcm_bank_count = tcm_bank_count / cluster->core_count;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/*
90962306a36Sopenharmony_ci	 * r5 core 0 will use all of TCM banks in lockstep mode.
91062306a36Sopenharmony_ci	 * In split mode, r5 core0 will use 128k and r5 core1 will use another
91162306a36Sopenharmony_ci	 * 128k. Assign TCM banks to each core accordingly
91262306a36Sopenharmony_ci	 */
91362306a36Sopenharmony_ci	tcm_node = 0;
91462306a36Sopenharmony_ci	for (i = 0; i < cluster->core_count; i++) {
91562306a36Sopenharmony_ci		r5_core = cluster->r5_cores[i];
91662306a36Sopenharmony_ci		r5_core->tcm_banks = devm_kcalloc(dev, tcm_bank_count,
91762306a36Sopenharmony_ci						  sizeof(struct mem_bank_data *),
91862306a36Sopenharmony_ci						  GFP_KERNEL);
91962306a36Sopenharmony_ci		if (!r5_core->tcm_banks)
92062306a36Sopenharmony_ci			return -ENOMEM;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		for (j = 0; j < tcm_bank_count; j++) {
92362306a36Sopenharmony_ci			/*
92462306a36Sopenharmony_ci			 * Use pre-defined TCM reg values.
92562306a36Sopenharmony_ci			 * Eventually this should be replaced by values
92662306a36Sopenharmony_ci			 * parsed from dts.
92762306a36Sopenharmony_ci			 */
92862306a36Sopenharmony_ci			r5_core->tcm_banks[j] =
92962306a36Sopenharmony_ci				(struct mem_bank_data *)&zynqmp_tcm_banks[tcm_node];
93062306a36Sopenharmony_ci			tcm_node++;
93162306a36Sopenharmony_ci		}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		r5_core->tcm_bank_count = tcm_bank_count;
93462306a36Sopenharmony_ci	}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	return 0;
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci/*
94062306a36Sopenharmony_ci * zynqmp_r5_core_init()
94162306a36Sopenharmony_ci * Create and initialize zynqmp_r5_core type object
94262306a36Sopenharmony_ci *
94362306a36Sopenharmony_ci * @cluster: pointer to zynqmp_r5_cluster type object
94462306a36Sopenharmony_ci * @fw_reg_val: value expected by firmware to configure RPU cluster mode
94562306a36Sopenharmony_ci * @tcm_mode: value expected by fw to configure TCM mode (lockstep or split)
94662306a36Sopenharmony_ci *
94762306a36Sopenharmony_ci * Return: 0 for success and error code for failure.
94862306a36Sopenharmony_ci */
94962306a36Sopenharmony_cistatic int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster,
95062306a36Sopenharmony_ci			       enum rpu_oper_mode fw_reg_val,
95162306a36Sopenharmony_ci			       enum rpu_tcm_comb tcm_mode)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	struct device *dev = cluster->dev;
95462306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
95562306a36Sopenharmony_ci	int ret, i;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	ret = zynqmp_r5_get_tcm_node(cluster);
95862306a36Sopenharmony_ci	if (ret < 0) {
95962306a36Sopenharmony_ci		dev_err(dev, "can't get tcm node, err %d\n", ret);
96062306a36Sopenharmony_ci		return ret;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	for (i = 0; i < cluster->core_count; i++) {
96462306a36Sopenharmony_ci		r5_core = cluster->r5_cores[i];
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci		/* Initialize r5 cores with power-domains parsed from dts */
96762306a36Sopenharmony_ci		ret = of_property_read_u32_index(r5_core->np, "power-domains",
96862306a36Sopenharmony_ci						 1, &r5_core->pm_domain_id);
96962306a36Sopenharmony_ci		if (ret) {
97062306a36Sopenharmony_ci			dev_err(dev, "failed to get power-domains property\n");
97162306a36Sopenharmony_ci			return ret;
97262306a36Sopenharmony_ci		}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		ret = zynqmp_r5_set_mode(r5_core, fw_reg_val, tcm_mode);
97562306a36Sopenharmony_ci		if (ret) {
97662306a36Sopenharmony_ci			dev_err(dev, "failed to set r5 cluster mode %d, err %d\n",
97762306a36Sopenharmony_ci				cluster->mode, ret);
97862306a36Sopenharmony_ci			return ret;
97962306a36Sopenharmony_ci		}
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	return 0;
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci/*
98662306a36Sopenharmony_ci * zynqmp_r5_cluster_init()
98762306a36Sopenharmony_ci * Create and initialize zynqmp_r5_cluster type object
98862306a36Sopenharmony_ci *
98962306a36Sopenharmony_ci * @cluster: pointer to zynqmp_r5_cluster type object
99062306a36Sopenharmony_ci *
99162306a36Sopenharmony_ci * Return: 0 for success and error code for failure.
99262306a36Sopenharmony_ci */
99362306a36Sopenharmony_cistatic int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	enum zynqmp_r5_cluster_mode cluster_mode = LOCKSTEP_MODE;
99662306a36Sopenharmony_ci	struct device *dev = cluster->dev;
99762306a36Sopenharmony_ci	struct device_node *dev_node = dev_of_node(dev);
99862306a36Sopenharmony_ci	struct platform_device *child_pdev;
99962306a36Sopenharmony_ci	struct zynqmp_r5_core **r5_cores;
100062306a36Sopenharmony_ci	enum rpu_oper_mode fw_reg_val;
100162306a36Sopenharmony_ci	struct device **child_devs;
100262306a36Sopenharmony_ci	struct device_node *child;
100362306a36Sopenharmony_ci	enum rpu_tcm_comb tcm_mode;
100462306a36Sopenharmony_ci	int core_count, ret, i;
100562306a36Sopenharmony_ci	struct mbox_info *ipi;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	ret = of_property_read_u32(dev_node, "xlnx,cluster-mode", &cluster_mode);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	/*
101062306a36Sopenharmony_ci	 * on success returns 0, if not defined then returns -EINVAL,
101162306a36Sopenharmony_ci	 * In that case, default is LOCKSTEP mode. Other than that
101262306a36Sopenharmony_ci	 * returns relative error code < 0.
101362306a36Sopenharmony_ci	 */
101462306a36Sopenharmony_ci	if (ret != -EINVAL && ret != 0) {
101562306a36Sopenharmony_ci		dev_err(dev, "Invalid xlnx,cluster-mode property\n");
101662306a36Sopenharmony_ci		return ret;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/*
102062306a36Sopenharmony_ci	 * For now driver only supports split mode and lockstep mode.
102162306a36Sopenharmony_ci	 * fail driver probe if either of that is not set in dts.
102262306a36Sopenharmony_ci	 */
102362306a36Sopenharmony_ci	if (cluster_mode == LOCKSTEP_MODE) {
102462306a36Sopenharmony_ci		tcm_mode = PM_RPU_TCM_COMB;
102562306a36Sopenharmony_ci		fw_reg_val = PM_RPU_MODE_LOCKSTEP;
102662306a36Sopenharmony_ci	} else if (cluster_mode == SPLIT_MODE) {
102762306a36Sopenharmony_ci		tcm_mode = PM_RPU_TCM_SPLIT;
102862306a36Sopenharmony_ci		fw_reg_val = PM_RPU_MODE_SPLIT;
102962306a36Sopenharmony_ci	} else {
103062306a36Sopenharmony_ci		dev_err(dev, "driver does not support cluster mode %d\n", cluster_mode);
103162306a36Sopenharmony_ci		return -EINVAL;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	/*
103562306a36Sopenharmony_ci	 * Number of cores is decided by number of child nodes of
103662306a36Sopenharmony_ci	 * r5f subsystem node in dts. If Split mode is used in dts
103762306a36Sopenharmony_ci	 * 2 child nodes are expected.
103862306a36Sopenharmony_ci	 * In lockstep mode if two child nodes are available,
103962306a36Sopenharmony_ci	 * only use first child node and consider it as core0
104062306a36Sopenharmony_ci	 * and ignore core1 dt node.
104162306a36Sopenharmony_ci	 */
104262306a36Sopenharmony_ci	core_count = of_get_available_child_count(dev_node);
104362306a36Sopenharmony_ci	if (core_count == 0) {
104462306a36Sopenharmony_ci		dev_err(dev, "Invalid number of r5 cores %d", core_count);
104562306a36Sopenharmony_ci		return -EINVAL;
104662306a36Sopenharmony_ci	} else if (cluster_mode == SPLIT_MODE && core_count != 2) {
104762306a36Sopenharmony_ci		dev_err(dev, "Invalid number of r5 cores for split mode\n");
104862306a36Sopenharmony_ci		return -EINVAL;
104962306a36Sopenharmony_ci	} else if (cluster_mode == LOCKSTEP_MODE && core_count == 2) {
105062306a36Sopenharmony_ci		dev_warn(dev, "Only r5 core0 will be used\n");
105162306a36Sopenharmony_ci		core_count = 1;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	child_devs = kcalloc(core_count, sizeof(struct device *), GFP_KERNEL);
105562306a36Sopenharmony_ci	if (!child_devs)
105662306a36Sopenharmony_ci		return -ENOMEM;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	r5_cores = kcalloc(core_count,
105962306a36Sopenharmony_ci			   sizeof(struct zynqmp_r5_core *), GFP_KERNEL);
106062306a36Sopenharmony_ci	if (!r5_cores) {
106162306a36Sopenharmony_ci		kfree(child_devs);
106262306a36Sopenharmony_ci		return -ENOMEM;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	i = 0;
106662306a36Sopenharmony_ci	for_each_available_child_of_node(dev_node, child) {
106762306a36Sopenharmony_ci		child_pdev = of_find_device_by_node(child);
106862306a36Sopenharmony_ci		if (!child_pdev) {
106962306a36Sopenharmony_ci			of_node_put(child);
107062306a36Sopenharmony_ci			ret = -ENODEV;
107162306a36Sopenharmony_ci			goto release_r5_cores;
107262306a36Sopenharmony_ci		}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci		child_devs[i] = &child_pdev->dev;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		/* create and add remoteproc instance of type struct rproc */
107762306a36Sopenharmony_ci		r5_cores[i] = zynqmp_r5_add_rproc_core(&child_pdev->dev);
107862306a36Sopenharmony_ci		if (IS_ERR(r5_cores[i])) {
107962306a36Sopenharmony_ci			of_node_put(child);
108062306a36Sopenharmony_ci			ret = PTR_ERR(r5_cores[i]);
108162306a36Sopenharmony_ci			r5_cores[i] = NULL;
108262306a36Sopenharmony_ci			goto release_r5_cores;
108362306a36Sopenharmony_ci		}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		/*
108662306a36Sopenharmony_ci		 * If mailbox nodes are disabled using "status" property then
108762306a36Sopenharmony_ci		 * setting up mailbox channels will fail.
108862306a36Sopenharmony_ci		 */
108962306a36Sopenharmony_ci		ipi = zynqmp_r5_setup_mbox(&child_pdev->dev);
109062306a36Sopenharmony_ci		if (ipi) {
109162306a36Sopenharmony_ci			r5_cores[i]->ipi = ipi;
109262306a36Sopenharmony_ci			ipi->r5_core = r5_cores[i];
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		/*
109662306a36Sopenharmony_ci		 * If two child nodes are available in dts in lockstep mode,
109762306a36Sopenharmony_ci		 * then ignore second child node.
109862306a36Sopenharmony_ci		 */
109962306a36Sopenharmony_ci		if (cluster_mode == LOCKSTEP_MODE) {
110062306a36Sopenharmony_ci			of_node_put(child);
110162306a36Sopenharmony_ci			break;
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci		i++;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	cluster->mode = cluster_mode;
110862306a36Sopenharmony_ci	cluster->core_count = core_count;
110962306a36Sopenharmony_ci	cluster->r5_cores = r5_cores;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	ret = zynqmp_r5_core_init(cluster, fw_reg_val, tcm_mode);
111262306a36Sopenharmony_ci	if (ret < 0) {
111362306a36Sopenharmony_ci		dev_err(dev, "failed to init r5 core err %d\n", ret);
111462306a36Sopenharmony_ci		cluster->core_count = 0;
111562306a36Sopenharmony_ci		cluster->r5_cores = NULL;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		/*
111862306a36Sopenharmony_ci		 * at this point rproc resources for each core are allocated.
111962306a36Sopenharmony_ci		 * adjust index to free resources in reverse order
112062306a36Sopenharmony_ci		 */
112162306a36Sopenharmony_ci		i = core_count - 1;
112262306a36Sopenharmony_ci		goto release_r5_cores;
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	kfree(child_devs);
112662306a36Sopenharmony_ci	return 0;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cirelease_r5_cores:
112962306a36Sopenharmony_ci	while (i >= 0) {
113062306a36Sopenharmony_ci		put_device(child_devs[i]);
113162306a36Sopenharmony_ci		if (r5_cores[i]) {
113262306a36Sopenharmony_ci			zynqmp_r5_free_mbox(r5_cores[i]->ipi);
113362306a36Sopenharmony_ci			of_reserved_mem_device_release(r5_cores[i]->dev);
113462306a36Sopenharmony_ci			rproc_del(r5_cores[i]->rproc);
113562306a36Sopenharmony_ci			rproc_free(r5_cores[i]->rproc);
113662306a36Sopenharmony_ci		}
113762306a36Sopenharmony_ci		i--;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci	kfree(r5_cores);
114062306a36Sopenharmony_ci	kfree(child_devs);
114162306a36Sopenharmony_ci	return ret;
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_cistatic void zynqmp_r5_cluster_exit(void *data)
114562306a36Sopenharmony_ci{
114662306a36Sopenharmony_ci	struct platform_device *pdev = data;
114762306a36Sopenharmony_ci	struct zynqmp_r5_cluster *cluster;
114862306a36Sopenharmony_ci	struct zynqmp_r5_core *r5_core;
114962306a36Sopenharmony_ci	int i;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	cluster = platform_get_drvdata(pdev);
115262306a36Sopenharmony_ci	if (!cluster)
115362306a36Sopenharmony_ci		return;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	for (i = 0; i < cluster->core_count; i++) {
115662306a36Sopenharmony_ci		r5_core = cluster->r5_cores[i];
115762306a36Sopenharmony_ci		zynqmp_r5_free_mbox(r5_core->ipi);
115862306a36Sopenharmony_ci		of_reserved_mem_device_release(r5_core->dev);
115962306a36Sopenharmony_ci		put_device(r5_core->dev);
116062306a36Sopenharmony_ci		rproc_del(r5_core->rproc);
116162306a36Sopenharmony_ci		rproc_free(r5_core->rproc);
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	kfree(cluster->r5_cores);
116562306a36Sopenharmony_ci	kfree(cluster);
116662306a36Sopenharmony_ci	platform_set_drvdata(pdev, NULL);
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci/*
117062306a36Sopenharmony_ci * zynqmp_r5_remoteproc_probe()
117162306a36Sopenharmony_ci * parse device-tree, initialize hardware and allocate required resources
117262306a36Sopenharmony_ci * and remoteproc ops
117362306a36Sopenharmony_ci *
117462306a36Sopenharmony_ci * @pdev: domain platform device for R5 cluster
117562306a36Sopenharmony_ci *
117662306a36Sopenharmony_ci * Return: 0 for success and < 0 for failure.
117762306a36Sopenharmony_ci */
117862306a36Sopenharmony_cistatic int zynqmp_r5_remoteproc_probe(struct platform_device *pdev)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	struct zynqmp_r5_cluster *cluster;
118162306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
118262306a36Sopenharmony_ci	int ret;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	cluster = kzalloc(sizeof(*cluster), GFP_KERNEL);
118562306a36Sopenharmony_ci	if (!cluster)
118662306a36Sopenharmony_ci		return -ENOMEM;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	cluster->dev = dev;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	ret = devm_of_platform_populate(dev);
119162306a36Sopenharmony_ci	if (ret) {
119262306a36Sopenharmony_ci		dev_err_probe(dev, ret, "failed to populate platform dev\n");
119362306a36Sopenharmony_ci		kfree(cluster);
119462306a36Sopenharmony_ci		return ret;
119562306a36Sopenharmony_ci	}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	/* wire in so each core can be cleaned up at driver remove */
119862306a36Sopenharmony_ci	platform_set_drvdata(pdev, cluster);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	ret = zynqmp_r5_cluster_init(cluster);
120162306a36Sopenharmony_ci	if (ret) {
120262306a36Sopenharmony_ci		kfree(cluster);
120362306a36Sopenharmony_ci		platform_set_drvdata(pdev, NULL);
120462306a36Sopenharmony_ci		dev_err_probe(dev, ret, "Invalid r5f subsystem device tree\n");
120562306a36Sopenharmony_ci		return ret;
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, zynqmp_r5_cluster_exit, pdev);
120962306a36Sopenharmony_ci	if (ret)
121062306a36Sopenharmony_ci		return ret;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return 0;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci/* Match table for OF platform binding */
121662306a36Sopenharmony_cistatic const struct of_device_id zynqmp_r5_remoteproc_match[] = {
121762306a36Sopenharmony_ci	{ .compatible = "xlnx,zynqmp-r5fss", },
121862306a36Sopenharmony_ci	{ /* end of list */ },
121962306a36Sopenharmony_ci};
122062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, zynqmp_r5_remoteproc_match);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_cistatic struct platform_driver zynqmp_r5_remoteproc_driver = {
122362306a36Sopenharmony_ci	.probe = zynqmp_r5_remoteproc_probe,
122462306a36Sopenharmony_ci	.driver = {
122562306a36Sopenharmony_ci		.name = "zynqmp_r5_remoteproc",
122662306a36Sopenharmony_ci		.of_match_table = zynqmp_r5_remoteproc_match,
122762306a36Sopenharmony_ci	},
122862306a36Sopenharmony_ci};
122962306a36Sopenharmony_cimodule_platform_driver(zynqmp_r5_remoteproc_driver);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ciMODULE_DESCRIPTION("Xilinx R5F remote processor driver");
123262306a36Sopenharmony_ciMODULE_AUTHOR("Xilinx Inc.");
123362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1234