18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ST's Remote Processor Control Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 STMicroelectronics - All Rights Reserved
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Ludovic Barre <ludovic.barre@st.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h>
168c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/of_address.h>
208c2ecf20Sopenharmony_ci#include <linux/of_device.h>
218c2ecf20Sopenharmony_ci#include <linux/of_reserved_mem.h>
228c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
238c2ecf20Sopenharmony_ci#include <linux/regmap.h>
248c2ecf20Sopenharmony_ci#include <linux/remoteproc.h>
258c2ecf20Sopenharmony_ci#include <linux/reset.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "remoteproc_internal.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define ST_RPROC_VQ0		0
308c2ecf20Sopenharmony_ci#define ST_RPROC_VQ1		1
318c2ecf20Sopenharmony_ci#define ST_RPROC_MAX_VRING	2
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define MBOX_RX			0
348c2ecf20Sopenharmony_ci#define MBOX_TX			1
358c2ecf20Sopenharmony_ci#define MBOX_MAX		2
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct st_rproc_config {
388c2ecf20Sopenharmony_ci	bool			sw_reset;
398c2ecf20Sopenharmony_ci	bool			pwr_reset;
408c2ecf20Sopenharmony_ci	unsigned long		bootaddr_mask;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct st_rproc {
448c2ecf20Sopenharmony_ci	struct st_rproc_config	*config;
458c2ecf20Sopenharmony_ci	struct reset_control	*sw_reset;
468c2ecf20Sopenharmony_ci	struct reset_control	*pwr_reset;
478c2ecf20Sopenharmony_ci	struct clk		*clk;
488c2ecf20Sopenharmony_ci	u32			clk_rate;
498c2ecf20Sopenharmony_ci	struct regmap		*boot_base;
508c2ecf20Sopenharmony_ci	u32			boot_offset;
518c2ecf20Sopenharmony_ci	struct mbox_chan	*mbox_chan[ST_RPROC_MAX_VRING * MBOX_MAX];
528c2ecf20Sopenharmony_ci	struct mbox_client mbox_client_vq0;
538c2ecf20Sopenharmony_ci	struct mbox_client mbox_client_vq1;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic void st_rproc_mbox_callback(struct device *dev, u32 msg)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct rproc *rproc = dev_get_drvdata(dev);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (rproc_vq_interrupt(rproc, msg) == IRQ_NONE)
618c2ecf20Sopenharmony_ci		dev_dbg(dev, "no message was found in vqid %d\n", msg);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic
658c2ecf20Sopenharmony_civoid st_rproc_mbox_callback_vq0(struct mbox_client *mbox_client, void *data)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	st_rproc_mbox_callback(mbox_client->dev, 0);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic
718c2ecf20Sopenharmony_civoid st_rproc_mbox_callback_vq1(struct mbox_client *mbox_client, void *data)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	st_rproc_mbox_callback(mbox_client->dev, 1);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void st_rproc_kick(struct rproc *rproc, int vqid)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct st_rproc *ddata = rproc->priv;
798c2ecf20Sopenharmony_ci	struct device *dev = rproc->dev.parent;
808c2ecf20Sopenharmony_ci	int ret;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* send the index of the triggered virtqueue in the mailbox payload */
838c2ecf20Sopenharmony_ci	if (WARN_ON(vqid >= ST_RPROC_MAX_VRING))
848c2ecf20Sopenharmony_ci		return;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ret = mbox_send_message(ddata->mbox_chan[vqid * MBOX_MAX + MBOX_TX],
878c2ecf20Sopenharmony_ci				(void *)&vqid);
888c2ecf20Sopenharmony_ci	if (ret < 0)
898c2ecf20Sopenharmony_ci		dev_err(dev, "failed to send message via mbox: %d\n", ret);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int st_rproc_mem_alloc(struct rproc *rproc,
938c2ecf20Sopenharmony_ci			      struct rproc_mem_entry *mem)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct device *dev = rproc->dev.parent;
968c2ecf20Sopenharmony_ci	void *va;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	va = ioremap_wc(mem->dma, mem->len);
998c2ecf20Sopenharmony_ci	if (!va) {
1008c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to map memory region: %pa+%zx\n",
1018c2ecf20Sopenharmony_ci			&mem->dma, mem->len);
1028c2ecf20Sopenharmony_ci		return -ENOMEM;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* Update memory entry va */
1068c2ecf20Sopenharmony_ci	mem->va = va;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int st_rproc_mem_release(struct rproc *rproc,
1128c2ecf20Sopenharmony_ci				struct rproc_mem_entry *mem)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	iounmap(mem->va);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return 0;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct device *dev = rproc->dev.parent;
1228c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
1238c2ecf20Sopenharmony_ci	struct rproc_mem_entry *mem;
1248c2ecf20Sopenharmony_ci	struct reserved_mem *rmem;
1258c2ecf20Sopenharmony_ci	struct of_phandle_iterator it;
1268c2ecf20Sopenharmony_ci	int index = 0;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	of_phandle_iterator_init(&it, np, "memory-region", NULL, 0);
1298c2ecf20Sopenharmony_ci	while (of_phandle_iterator_next(&it) == 0) {
1308c2ecf20Sopenharmony_ci		rmem = of_reserved_mem_lookup(it.node);
1318c2ecf20Sopenharmony_ci		if (!rmem) {
1328c2ecf20Sopenharmony_ci			of_node_put(it.node);
1338c2ecf20Sopenharmony_ci			dev_err(dev, "unable to acquire memory-region\n");
1348c2ecf20Sopenharmony_ci			return -EINVAL;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		/*  No need to map vdev buffer */
1388c2ecf20Sopenharmony_ci		if (strcmp(it.node->name, "vdev0buffer")) {
1398c2ecf20Sopenharmony_ci			/* Register memory region */
1408c2ecf20Sopenharmony_ci			mem = rproc_mem_entry_init(dev, NULL,
1418c2ecf20Sopenharmony_ci						   (dma_addr_t)rmem->base,
1428c2ecf20Sopenharmony_ci						   rmem->size, rmem->base,
1438c2ecf20Sopenharmony_ci						   st_rproc_mem_alloc,
1448c2ecf20Sopenharmony_ci						   st_rproc_mem_release,
1458c2ecf20Sopenharmony_ci						   it.node->name);
1468c2ecf20Sopenharmony_ci		} else {
1478c2ecf20Sopenharmony_ci			/* Register reserved memory for vdev buffer allocation */
1488c2ecf20Sopenharmony_ci			mem = rproc_of_resm_mem_entry_init(dev, index,
1498c2ecf20Sopenharmony_ci							   rmem->size,
1508c2ecf20Sopenharmony_ci							   rmem->base,
1518c2ecf20Sopenharmony_ci							   it.node->name);
1528c2ecf20Sopenharmony_ci		}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		if (!mem) {
1558c2ecf20Sopenharmony_ci			of_node_put(it.node);
1568c2ecf20Sopenharmony_ci			return -ENOMEM;
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		rproc_add_carveout(rproc, mem);
1608c2ecf20Sopenharmony_ci		index++;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return rproc_elf_load_rsc_table(rproc, fw);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int st_rproc_start(struct rproc *rproc)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct st_rproc *ddata = rproc->priv;
1698c2ecf20Sopenharmony_ci	int err;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	regmap_update_bits(ddata->boot_base, ddata->boot_offset,
1728c2ecf20Sopenharmony_ci			   ddata->config->bootaddr_mask, rproc->bootaddr);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	err = clk_enable(ddata->clk);
1758c2ecf20Sopenharmony_ci	if (err) {
1768c2ecf20Sopenharmony_ci		dev_err(&rproc->dev, "Failed to enable clock\n");
1778c2ecf20Sopenharmony_ci		return err;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (ddata->config->sw_reset) {
1818c2ecf20Sopenharmony_ci		err = reset_control_deassert(ddata->sw_reset);
1828c2ecf20Sopenharmony_ci		if (err) {
1838c2ecf20Sopenharmony_ci			dev_err(&rproc->dev, "Failed to deassert S/W Reset\n");
1848c2ecf20Sopenharmony_ci			goto sw_reset_fail;
1858c2ecf20Sopenharmony_ci		}
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (ddata->config->pwr_reset) {
1898c2ecf20Sopenharmony_ci		err = reset_control_deassert(ddata->pwr_reset);
1908c2ecf20Sopenharmony_ci		if (err) {
1918c2ecf20Sopenharmony_ci			dev_err(&rproc->dev, "Failed to deassert Power Reset\n");
1928c2ecf20Sopenharmony_ci			goto pwr_reset_fail;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	dev_info(&rproc->dev, "Started from 0x%llx\n", rproc->bootaddr);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cipwr_reset_fail:
2028c2ecf20Sopenharmony_ci	if (ddata->config->pwr_reset)
2038c2ecf20Sopenharmony_ci		reset_control_assert(ddata->sw_reset);
2048c2ecf20Sopenharmony_cisw_reset_fail:
2058c2ecf20Sopenharmony_ci	clk_disable(ddata->clk);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return err;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int st_rproc_stop(struct rproc *rproc)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct st_rproc *ddata = rproc->priv;
2138c2ecf20Sopenharmony_ci	int sw_err = 0, pwr_err = 0;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (ddata->config->sw_reset) {
2168c2ecf20Sopenharmony_ci		sw_err = reset_control_assert(ddata->sw_reset);
2178c2ecf20Sopenharmony_ci		if (sw_err)
2188c2ecf20Sopenharmony_ci			dev_err(&rproc->dev, "Failed to assert S/W Reset\n");
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (ddata->config->pwr_reset) {
2228c2ecf20Sopenharmony_ci		pwr_err = reset_control_assert(ddata->pwr_reset);
2238c2ecf20Sopenharmony_ci		if (pwr_err)
2248c2ecf20Sopenharmony_ci			dev_err(&rproc->dev, "Failed to assert Power Reset\n");
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	clk_disable(ddata->clk);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return sw_err ?: pwr_err;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic const struct rproc_ops st_rproc_ops = {
2338c2ecf20Sopenharmony_ci	.kick			= st_rproc_kick,
2348c2ecf20Sopenharmony_ci	.start			= st_rproc_start,
2358c2ecf20Sopenharmony_ci	.stop			= st_rproc_stop,
2368c2ecf20Sopenharmony_ci	.parse_fw		= st_rproc_parse_fw,
2378c2ecf20Sopenharmony_ci	.load			= rproc_elf_load_segments,
2388c2ecf20Sopenharmony_ci	.find_loaded_rsc_table	= rproc_elf_find_loaded_rsc_table,
2398c2ecf20Sopenharmony_ci	.sanity_check		= rproc_elf_sanity_check,
2408c2ecf20Sopenharmony_ci	.get_boot_addr		= rproc_elf_get_boot_addr,
2418c2ecf20Sopenharmony_ci};
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci/*
2448c2ecf20Sopenharmony_ci * Fetch state of the processor: 0 is off, 1 is on.
2458c2ecf20Sopenharmony_ci */
2468c2ecf20Sopenharmony_cistatic int st_rproc_state(struct platform_device *pdev)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct rproc *rproc = platform_get_drvdata(pdev);
2498c2ecf20Sopenharmony_ci	struct st_rproc *ddata = rproc->priv;
2508c2ecf20Sopenharmony_ci	int reset_sw = 0, reset_pwr = 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (ddata->config->sw_reset)
2538c2ecf20Sopenharmony_ci		reset_sw = reset_control_status(ddata->sw_reset);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (ddata->config->pwr_reset)
2568c2ecf20Sopenharmony_ci		reset_pwr = reset_control_status(ddata->pwr_reset);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (reset_sw < 0 || reset_pwr < 0)
2598c2ecf20Sopenharmony_ci		return -EINVAL;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	return !reset_sw && !reset_pwr;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic const struct st_rproc_config st40_rproc_cfg = {
2658c2ecf20Sopenharmony_ci	.sw_reset = true,
2668c2ecf20Sopenharmony_ci	.pwr_reset = true,
2678c2ecf20Sopenharmony_ci	.bootaddr_mask = GENMASK(28, 1),
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic const struct st_rproc_config st231_rproc_cfg = {
2718c2ecf20Sopenharmony_ci	.sw_reset = true,
2728c2ecf20Sopenharmony_ci	.pwr_reset = false,
2738c2ecf20Sopenharmony_ci	.bootaddr_mask = GENMASK(31, 6),
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic const struct of_device_id st_rproc_match[] = {
2778c2ecf20Sopenharmony_ci	{ .compatible = "st,st40-rproc", .data = &st40_rproc_cfg },
2788c2ecf20Sopenharmony_ci	{ .compatible = "st,st231-rproc", .data = &st231_rproc_cfg },
2798c2ecf20Sopenharmony_ci	{},
2808c2ecf20Sopenharmony_ci};
2818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, st_rproc_match);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int st_rproc_parse_dt(struct platform_device *pdev)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2868c2ecf20Sopenharmony_ci	struct rproc *rproc = platform_get_drvdata(pdev);
2878c2ecf20Sopenharmony_ci	struct st_rproc *ddata = rproc->priv;
2888c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
2898c2ecf20Sopenharmony_ci	int err;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (ddata->config->sw_reset) {
2928c2ecf20Sopenharmony_ci		ddata->sw_reset = devm_reset_control_get_exclusive(dev,
2938c2ecf20Sopenharmony_ci								   "sw_reset");
2948c2ecf20Sopenharmony_ci		if (IS_ERR(ddata->sw_reset)) {
2958c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to get S/W Reset\n");
2968c2ecf20Sopenharmony_ci			return PTR_ERR(ddata->sw_reset);
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (ddata->config->pwr_reset) {
3018c2ecf20Sopenharmony_ci		ddata->pwr_reset = devm_reset_control_get_exclusive(dev,
3028c2ecf20Sopenharmony_ci								    "pwr_reset");
3038c2ecf20Sopenharmony_ci		if (IS_ERR(ddata->pwr_reset)) {
3048c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to get Power Reset\n");
3058c2ecf20Sopenharmony_ci			return PTR_ERR(ddata->pwr_reset);
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	ddata->clk = devm_clk_get(dev, NULL);
3108c2ecf20Sopenharmony_ci	if (IS_ERR(ddata->clk)) {
3118c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to get clock\n");
3128c2ecf20Sopenharmony_ci		return PTR_ERR(ddata->clk);
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	err = of_property_read_u32(np, "clock-frequency", &ddata->clk_rate);
3168c2ecf20Sopenharmony_ci	if (err) {
3178c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get clock frequency\n");
3188c2ecf20Sopenharmony_ci		return err;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	ddata->boot_base = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
3228c2ecf20Sopenharmony_ci	if (IS_ERR(ddata->boot_base)) {
3238c2ecf20Sopenharmony_ci		dev_err(dev, "Boot base not found\n");
3248c2ecf20Sopenharmony_ci		return PTR_ERR(ddata->boot_base);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	err = of_property_read_u32_index(np, "st,syscfg", 1,
3288c2ecf20Sopenharmony_ci					 &ddata->boot_offset);
3298c2ecf20Sopenharmony_ci	if (err) {
3308c2ecf20Sopenharmony_ci		dev_err(dev, "Boot offset not found\n");
3318c2ecf20Sopenharmony_ci		return -EINVAL;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	err = clk_prepare(ddata->clk);
3358c2ecf20Sopenharmony_ci	if (err)
3368c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get clock\n");
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return err;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int st_rproc_probe(struct platform_device *pdev)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3448c2ecf20Sopenharmony_ci	const struct of_device_id *match;
3458c2ecf20Sopenharmony_ci	struct st_rproc *ddata;
3468c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
3478c2ecf20Sopenharmony_ci	struct rproc *rproc;
3488c2ecf20Sopenharmony_ci	struct mbox_chan *chan;
3498c2ecf20Sopenharmony_ci	int enabled;
3508c2ecf20Sopenharmony_ci	int ret, i;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	match = of_match_device(st_rproc_match, dev);
3538c2ecf20Sopenharmony_ci	if (!match || !match->data) {
3548c2ecf20Sopenharmony_ci		dev_err(dev, "No device match found\n");
3558c2ecf20Sopenharmony_ci		return -ENODEV;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	rproc = rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata));
3598c2ecf20Sopenharmony_ci	if (!rproc)
3608c2ecf20Sopenharmony_ci		return -ENOMEM;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	rproc->has_iommu = false;
3638c2ecf20Sopenharmony_ci	ddata = rproc->priv;
3648c2ecf20Sopenharmony_ci	ddata->config = (struct st_rproc_config *)match->data;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, rproc);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	ret = st_rproc_parse_dt(pdev);
3698c2ecf20Sopenharmony_ci	if (ret)
3708c2ecf20Sopenharmony_ci		goto free_rproc;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	enabled = st_rproc_state(pdev);
3738c2ecf20Sopenharmony_ci	if (enabled < 0) {
3748c2ecf20Sopenharmony_ci		ret = enabled;
3758c2ecf20Sopenharmony_ci		goto free_clk;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (enabled) {
3798c2ecf20Sopenharmony_ci		atomic_inc(&rproc->power);
3808c2ecf20Sopenharmony_ci		rproc->state = RPROC_RUNNING;
3818c2ecf20Sopenharmony_ci	} else {
3828c2ecf20Sopenharmony_ci		clk_set_rate(ddata->clk, ddata->clk_rate);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (of_get_property(np, "mbox-names", NULL)) {
3868c2ecf20Sopenharmony_ci		ddata->mbox_client_vq0.dev		= dev;
3878c2ecf20Sopenharmony_ci		ddata->mbox_client_vq0.tx_done		= NULL;
3888c2ecf20Sopenharmony_ci		ddata->mbox_client_vq0.tx_block	= false;
3898c2ecf20Sopenharmony_ci		ddata->mbox_client_vq0.knows_txdone	= false;
3908c2ecf20Sopenharmony_ci		ddata->mbox_client_vq0.rx_callback	= st_rproc_mbox_callback_vq0;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		ddata->mbox_client_vq1.dev		= dev;
3938c2ecf20Sopenharmony_ci		ddata->mbox_client_vq1.tx_done		= NULL;
3948c2ecf20Sopenharmony_ci		ddata->mbox_client_vq1.tx_block	= false;
3958c2ecf20Sopenharmony_ci		ddata->mbox_client_vq1.knows_txdone	= false;
3968c2ecf20Sopenharmony_ci		ddata->mbox_client_vq1.rx_callback	= st_rproc_mbox_callback_vq1;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		/*
3998c2ecf20Sopenharmony_ci		 * To control a co-processor without IPC mechanism.
4008c2ecf20Sopenharmony_ci		 * This driver can be used without mbox and rpmsg.
4018c2ecf20Sopenharmony_ci		 */
4028c2ecf20Sopenharmony_ci		chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_rx");
4038c2ecf20Sopenharmony_ci		if (IS_ERR(chan)) {
4048c2ecf20Sopenharmony_ci			dev_err(&rproc->dev, "failed to request mbox chan 0\n");
4058c2ecf20Sopenharmony_ci			ret = PTR_ERR(chan);
4068c2ecf20Sopenharmony_ci			goto free_clk;
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci		ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_RX] = chan;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_tx");
4118c2ecf20Sopenharmony_ci		if (IS_ERR(chan)) {
4128c2ecf20Sopenharmony_ci			dev_err(&rproc->dev, "failed to request mbox chan 0\n");
4138c2ecf20Sopenharmony_ci			ret = PTR_ERR(chan);
4148c2ecf20Sopenharmony_ci			goto free_mbox;
4158c2ecf20Sopenharmony_ci		}
4168c2ecf20Sopenharmony_ci		ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_TX] = chan;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_rx");
4198c2ecf20Sopenharmony_ci		if (IS_ERR(chan)) {
4208c2ecf20Sopenharmony_ci			dev_err(&rproc->dev, "failed to request mbox chan 1\n");
4218c2ecf20Sopenharmony_ci			ret = PTR_ERR(chan);
4228c2ecf20Sopenharmony_ci			goto free_mbox;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci		ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_RX] = chan;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_tx");
4278c2ecf20Sopenharmony_ci		if (IS_ERR(chan)) {
4288c2ecf20Sopenharmony_ci			dev_err(&rproc->dev, "failed to request mbox chan 1\n");
4298c2ecf20Sopenharmony_ci			ret = PTR_ERR(chan);
4308c2ecf20Sopenharmony_ci			goto free_mbox;
4318c2ecf20Sopenharmony_ci		}
4328c2ecf20Sopenharmony_ci		ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_TX] = chan;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	ret = rproc_add(rproc);
4368c2ecf20Sopenharmony_ci	if (ret)
4378c2ecf20Sopenharmony_ci		goto free_mbox;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return 0;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cifree_mbox:
4428c2ecf20Sopenharmony_ci	for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++)
4438c2ecf20Sopenharmony_ci		mbox_free_channel(ddata->mbox_chan[i]);
4448c2ecf20Sopenharmony_cifree_clk:
4458c2ecf20Sopenharmony_ci	clk_unprepare(ddata->clk);
4468c2ecf20Sopenharmony_cifree_rproc:
4478c2ecf20Sopenharmony_ci	rproc_free(rproc);
4488c2ecf20Sopenharmony_ci	return ret;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic int st_rproc_remove(struct platform_device *pdev)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct rproc *rproc = platform_get_drvdata(pdev);
4548c2ecf20Sopenharmony_ci	struct st_rproc *ddata = rproc->priv;
4558c2ecf20Sopenharmony_ci	int i;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	rproc_del(rproc);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	clk_disable_unprepare(ddata->clk);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++)
4628c2ecf20Sopenharmony_ci		mbox_free_channel(ddata->mbox_chan[i]);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	rproc_free(rproc);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return 0;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic struct platform_driver st_rproc_driver = {
4708c2ecf20Sopenharmony_ci	.probe = st_rproc_probe,
4718c2ecf20Sopenharmony_ci	.remove = st_rproc_remove,
4728c2ecf20Sopenharmony_ci	.driver = {
4738c2ecf20Sopenharmony_ci		.name = "st-rproc",
4748c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(st_rproc_match),
4758c2ecf20Sopenharmony_ci	},
4768c2ecf20Sopenharmony_ci};
4778c2ecf20Sopenharmony_cimodule_platform_driver(st_rproc_driver);
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ST Remote Processor Control Driver");
4808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ludovic Barre <ludovic.barre@st.com>");
4818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
482