18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2015
48c2ecf20Sopenharmony_ci * Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/rpmsg.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "delta.h"
108c2ecf20Sopenharmony_ci#include "delta-ipc.h"
118c2ecf20Sopenharmony_ci#include "delta-mem.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define IPC_TIMEOUT 100
148c2ecf20Sopenharmony_ci#define IPC_SANITY_TAG 0xDEADBEEF
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cienum delta_ipc_fw_command {
178c2ecf20Sopenharmony_ci	DELTA_IPC_OPEN,
188c2ecf20Sopenharmony_ci	DELTA_IPC_SET_STREAM,
198c2ecf20Sopenharmony_ci	DELTA_IPC_DECODE,
208c2ecf20Sopenharmony_ci	DELTA_IPC_CLOSE
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define to_rpmsg_driver(__drv) container_of(__drv, struct rpmsg_driver, drv)
248c2ecf20Sopenharmony_ci#define to_delta(__d) container_of(__d, struct delta_dev, rpmsg_driver)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define to_ctx(hdl) ((struct delta_ipc_ctx *)hdl)
278c2ecf20Sopenharmony_ci#define to_pctx(ctx) container_of(ctx, struct delta_ctx, ipc_ctx)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct delta_ipc_header_msg {
308c2ecf20Sopenharmony_ci	u32 tag;
318c2ecf20Sopenharmony_ci	void *host_hdl;
328c2ecf20Sopenharmony_ci	u32 copro_hdl;
338c2ecf20Sopenharmony_ci	u32 command;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define to_host_hdl(ctx) ((void *)ctx)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define msg_to_ctx(msg) ((struct delta_ipc_ctx *)(msg)->header.host_hdl)
398c2ecf20Sopenharmony_ci#define msg_to_copro_hdl(msg) ((msg)->header.copro_hdl)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline dma_addr_t to_paddr(struct delta_ipc_ctx *ctx, void *vaddr)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return (ctx->ipc_buf->paddr + (vaddr - ctx->ipc_buf->vaddr));
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline bool is_valid_data(struct delta_ipc_ctx *ctx,
478c2ecf20Sopenharmony_ci				 void *data, u32 size)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	return ((data >= ctx->ipc_buf->vaddr) &&
508c2ecf20Sopenharmony_ci		((data + size) <= (ctx->ipc_buf->vaddr + ctx->ipc_buf->size)));
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * IPC shared memory (@ipc_buf_size, @ipc_buf_paddr) is sent to copro
558c2ecf20Sopenharmony_ci * at each instance opening. This memory is allocated by IPC client
568c2ecf20Sopenharmony_ci * and given through delta_ipc_open(). All messages parameters
578c2ecf20Sopenharmony_ci * (open, set_stream, decode) will have their phy address within
588c2ecf20Sopenharmony_ci * this IPC shared memory, avoiding de-facto recopies inside delta-ipc.
598c2ecf20Sopenharmony_ci * All the below messages structures are used on both host and firmware
608c2ecf20Sopenharmony_ci * side and are packed (use only of 32 bits size fields in messages
618c2ecf20Sopenharmony_ci * structures to ensure packing):
628c2ecf20Sopenharmony_ci * - struct delta_ipc_open_msg
638c2ecf20Sopenharmony_ci * - struct delta_ipc_set_stream_msg
648c2ecf20Sopenharmony_ci * - struct delta_ipc_decode_msg
658c2ecf20Sopenharmony_ci * - struct delta_ipc_close_msg
668c2ecf20Sopenharmony_ci * - struct delta_ipc_cb_msg
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_cistruct delta_ipc_open_msg {
698c2ecf20Sopenharmony_ci	struct delta_ipc_header_msg header;
708c2ecf20Sopenharmony_ci	u32 ipc_buf_size;
718c2ecf20Sopenharmony_ci	dma_addr_t ipc_buf_paddr;
728c2ecf20Sopenharmony_ci	char name[32];
738c2ecf20Sopenharmony_ci	u32 param_size;
748c2ecf20Sopenharmony_ci	dma_addr_t param_paddr;
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistruct delta_ipc_set_stream_msg {
788c2ecf20Sopenharmony_ci	struct delta_ipc_header_msg header;
798c2ecf20Sopenharmony_ci	u32 param_size;
808c2ecf20Sopenharmony_ci	dma_addr_t param_paddr;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct delta_ipc_decode_msg {
848c2ecf20Sopenharmony_ci	struct delta_ipc_header_msg header;
858c2ecf20Sopenharmony_ci	u32 param_size;
868c2ecf20Sopenharmony_ci	dma_addr_t param_paddr;
878c2ecf20Sopenharmony_ci	u32 status_size;
888c2ecf20Sopenharmony_ci	dma_addr_t status_paddr;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistruct delta_ipc_close_msg {
928c2ecf20Sopenharmony_ci	struct delta_ipc_header_msg header;
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistruct delta_ipc_cb_msg {
968c2ecf20Sopenharmony_ci	struct delta_ipc_header_msg header;
978c2ecf20Sopenharmony_ci	int err;
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic void build_msg_header(struct delta_ipc_ctx *ctx,
1018c2ecf20Sopenharmony_ci			     enum delta_ipc_fw_command command,
1028c2ecf20Sopenharmony_ci			     struct delta_ipc_header_msg *header)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	header->tag = IPC_SANITY_TAG;
1058c2ecf20Sopenharmony_ci	header->host_hdl = to_host_hdl(ctx);
1068c2ecf20Sopenharmony_ci	header->copro_hdl = ctx->copro_hdl;
1078c2ecf20Sopenharmony_ci	header->command = command;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ciint delta_ipc_open(struct delta_ctx *pctx, const char *name,
1118c2ecf20Sopenharmony_ci		   struct delta_ipc_param *param, u32 ipc_buf_size,
1128c2ecf20Sopenharmony_ci		   struct delta_buf **ipc_buf, void **hdl)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct delta_dev *delta = pctx->dev;
1158c2ecf20Sopenharmony_ci	struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
1168c2ecf20Sopenharmony_ci	struct delta_ipc_ctx *ctx = &pctx->ipc_ctx;
1178c2ecf20Sopenharmony_ci	struct delta_ipc_open_msg msg;
1188c2ecf20Sopenharmony_ci	struct delta_buf *buf = &ctx->ipc_buf_struct;
1198c2ecf20Sopenharmony_ci	int ret;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (!rpmsg_device) {
1228c2ecf20Sopenharmony_ci		dev_err(delta->dev,
1238c2ecf20Sopenharmony_ci			"%s   ipc: failed to open, rpmsg is not initialized\n",
1248c2ecf20Sopenharmony_ci			pctx->name);
1258c2ecf20Sopenharmony_ci		pctx->sys_errors++;
1268c2ecf20Sopenharmony_ci		return -EINVAL;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (!name) {
1308c2ecf20Sopenharmony_ci		dev_err(delta->dev,
1318c2ecf20Sopenharmony_ci			"%s   ipc: failed to open, no name given\n",
1328c2ecf20Sopenharmony_ci			pctx->name);
1338c2ecf20Sopenharmony_ci		return -EINVAL;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (!param || !param->data || !param->size) {
1378c2ecf20Sopenharmony_ci		dev_err(delta->dev,
1388c2ecf20Sopenharmony_ci			"%s  ipc: failed to open, empty parameter\n",
1398c2ecf20Sopenharmony_ci			pctx->name);
1408c2ecf20Sopenharmony_ci		return -EINVAL;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (!ipc_buf_size) {
1448c2ecf20Sopenharmony_ci		dev_err(delta->dev,
1458c2ecf20Sopenharmony_ci			"%s   ipc: failed to open, no size given for ipc buffer\n",
1468c2ecf20Sopenharmony_ci			pctx->name);
1478c2ecf20Sopenharmony_ci		return -EINVAL;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (param->size > ipc_buf_size) {
1518c2ecf20Sopenharmony_ci		dev_err(delta->dev,
1528c2ecf20Sopenharmony_ci			"%s   ipc: failed to open, too large ipc parameter (%d bytes while max %d expected)\n",
1538c2ecf20Sopenharmony_ci			pctx->name,
1548c2ecf20Sopenharmony_ci			param->size, ctx->ipc_buf->size);
1558c2ecf20Sopenharmony_ci		return -EINVAL;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* init */
1598c2ecf20Sopenharmony_ci	init_completion(&ctx->done);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/*
1628c2ecf20Sopenharmony_ci	 * allocation of contiguous buffer for
1638c2ecf20Sopenharmony_ci	 * data of commands exchanged between
1648c2ecf20Sopenharmony_ci	 * host and firmware coprocessor
1658c2ecf20Sopenharmony_ci	 */
1668c2ecf20Sopenharmony_ci	ret = hw_alloc(pctx, ipc_buf_size,
1678c2ecf20Sopenharmony_ci		       "ipc data buffer", buf);
1688c2ecf20Sopenharmony_ci	if (ret)
1698c2ecf20Sopenharmony_ci		return ret;
1708c2ecf20Sopenharmony_ci	ctx->ipc_buf = buf;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* build rpmsg message */
1738c2ecf20Sopenharmony_ci	build_msg_header(ctx, DELTA_IPC_OPEN, &msg.header);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	msg.ipc_buf_size = ipc_buf_size;
1768c2ecf20Sopenharmony_ci	msg.ipc_buf_paddr = ctx->ipc_buf->paddr;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	memcpy(msg.name, name, sizeof(msg.name));
1798c2ecf20Sopenharmony_ci	msg.name[sizeof(msg.name) - 1] = 0;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	msg.param_size = param->size;
1828c2ecf20Sopenharmony_ci	memcpy(ctx->ipc_buf->vaddr, param->data, msg.param_size);
1838c2ecf20Sopenharmony_ci	msg.param_paddr = ctx->ipc_buf->paddr;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* send it */
1868c2ecf20Sopenharmony_ci	ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
1878c2ecf20Sopenharmony_ci	if (ret) {
1888c2ecf20Sopenharmony_ci		dev_err(delta->dev,
1898c2ecf20Sopenharmony_ci			"%s   ipc: failed to open, rpmsg_send failed (%d) for DELTA_IPC_OPEN (name=%s, size=%d, data=%p)\n",
1908c2ecf20Sopenharmony_ci			pctx->name,
1918c2ecf20Sopenharmony_ci			ret, name, param->size, param->data);
1928c2ecf20Sopenharmony_ci		goto err;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/* wait for acknowledge */
1968c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout
1978c2ecf20Sopenharmony_ci	    (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
1988c2ecf20Sopenharmony_ci		dev_err(delta->dev,
1998c2ecf20Sopenharmony_ci			"%s   ipc: failed to open, timeout waiting for DELTA_IPC_OPEN callback (name=%s, size=%d, data=%p)\n",
2008c2ecf20Sopenharmony_ci			pctx->name,
2018c2ecf20Sopenharmony_ci			name, param->size, param->data);
2028c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
2038c2ecf20Sopenharmony_ci		goto err;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* command completed, check error */
2078c2ecf20Sopenharmony_ci	if (ctx->cb_err) {
2088c2ecf20Sopenharmony_ci		dev_err(delta->dev,
2098c2ecf20Sopenharmony_ci			"%s   ipc: failed to open, DELTA_IPC_OPEN completed but with error (%d) (name=%s, size=%d, data=%p)\n",
2108c2ecf20Sopenharmony_ci			pctx->name,
2118c2ecf20Sopenharmony_ci			ctx->cb_err, name, param->size, param->data);
2128c2ecf20Sopenharmony_ci		ret = -EIO;
2138c2ecf20Sopenharmony_ci		goto err;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	*ipc_buf = ctx->ipc_buf;
2178c2ecf20Sopenharmony_ci	*hdl = (void *)ctx;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cierr:
2228c2ecf20Sopenharmony_ci	pctx->sys_errors++;
2238c2ecf20Sopenharmony_ci	hw_free(pctx, ctx->ipc_buf);
2248c2ecf20Sopenharmony_ci	ctx->ipc_buf = NULL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return ret;
2278c2ecf20Sopenharmony_ci};
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ciint delta_ipc_set_stream(void *hdl, struct delta_ipc_param *param)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct delta_ipc_ctx *ctx = to_ctx(hdl);
2328c2ecf20Sopenharmony_ci	struct delta_ctx *pctx = to_pctx(ctx);
2338c2ecf20Sopenharmony_ci	struct delta_dev *delta = pctx->dev;
2348c2ecf20Sopenharmony_ci	struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
2358c2ecf20Sopenharmony_ci	struct delta_ipc_set_stream_msg msg;
2368c2ecf20Sopenharmony_ci	int ret;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (!hdl) {
2398c2ecf20Sopenharmony_ci		dev_err(delta->dev,
2408c2ecf20Sopenharmony_ci			"%s   ipc: failed to set stream, invalid ipc handle\n",
2418c2ecf20Sopenharmony_ci			pctx->name);
2428c2ecf20Sopenharmony_ci		return -EINVAL;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (!rpmsg_device) {
2468c2ecf20Sopenharmony_ci		dev_err(delta->dev,
2478c2ecf20Sopenharmony_ci			"%s   ipc: failed to set stream, rpmsg is not initialized\n",
2488c2ecf20Sopenharmony_ci			pctx->name);
2498c2ecf20Sopenharmony_ci		return -EINVAL;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (!param || !param->data || !param->size) {
2538c2ecf20Sopenharmony_ci		dev_err(delta->dev,
2548c2ecf20Sopenharmony_ci			"%s  ipc: failed to set stream, empty parameter\n",
2558c2ecf20Sopenharmony_ci			pctx->name);
2568c2ecf20Sopenharmony_ci		return -EINVAL;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (param->size > ctx->ipc_buf->size) {
2608c2ecf20Sopenharmony_ci		dev_err(delta->dev,
2618c2ecf20Sopenharmony_ci			"%s   ipc: failed to set stream, too large ipc parameter(%d bytes while max %d expected)\n",
2628c2ecf20Sopenharmony_ci			pctx->name,
2638c2ecf20Sopenharmony_ci			param->size, ctx->ipc_buf->size);
2648c2ecf20Sopenharmony_ci		return -EINVAL;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (!is_valid_data(ctx, param->data, param->size)) {
2688c2ecf20Sopenharmony_ci		dev_err(delta->dev,
2698c2ecf20Sopenharmony_ci			"%s   ipc: failed to set stream, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",
2708c2ecf20Sopenharmony_ci			pctx->name,
2718c2ecf20Sopenharmony_ci			param->size,
2728c2ecf20Sopenharmony_ci			param->data,
2738c2ecf20Sopenharmony_ci			ctx->ipc_buf->vaddr,
2748c2ecf20Sopenharmony_ci			ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
2758c2ecf20Sopenharmony_ci		return -EINVAL;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* build rpmsg message */
2798c2ecf20Sopenharmony_ci	build_msg_header(ctx, DELTA_IPC_SET_STREAM, &msg.header);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	msg.param_size = param->size;
2828c2ecf20Sopenharmony_ci	msg.param_paddr = to_paddr(ctx, param->data);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* send it */
2858c2ecf20Sopenharmony_ci	ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
2868c2ecf20Sopenharmony_ci	if (ret) {
2878c2ecf20Sopenharmony_ci		dev_err(delta->dev,
2888c2ecf20Sopenharmony_ci			"%s   ipc: failed to set stream, rpmsg_send failed (%d) for DELTA_IPC_SET_STREAM (size=%d, data=%p)\n",
2898c2ecf20Sopenharmony_ci			pctx->name,
2908c2ecf20Sopenharmony_ci			ret, param->size, param->data);
2918c2ecf20Sopenharmony_ci		pctx->sys_errors++;
2928c2ecf20Sopenharmony_ci		return ret;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* wait for acknowledge */
2968c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout
2978c2ecf20Sopenharmony_ci	    (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
2988c2ecf20Sopenharmony_ci		dev_err(delta->dev,
2998c2ecf20Sopenharmony_ci			"%s   ipc: failed to set stream, timeout waiting for DELTA_IPC_SET_STREAM callback (size=%d, data=%p)\n",
3008c2ecf20Sopenharmony_ci			pctx->name,
3018c2ecf20Sopenharmony_ci			param->size, param->data);
3028c2ecf20Sopenharmony_ci		pctx->sys_errors++;
3038c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* command completed, check status */
3078c2ecf20Sopenharmony_ci	if (ctx->cb_err) {
3088c2ecf20Sopenharmony_ci		dev_err(delta->dev,
3098c2ecf20Sopenharmony_ci			"%s   ipc: failed to set stream, DELTA_IPC_SET_STREAM completed but with error (%d) (size=%d, data=%p)\n",
3108c2ecf20Sopenharmony_ci			pctx->name,
3118c2ecf20Sopenharmony_ci			ctx->cb_err, param->size, param->data);
3128c2ecf20Sopenharmony_ci		pctx->sys_errors++;
3138c2ecf20Sopenharmony_ci		return -EIO;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ciint delta_ipc_decode(void *hdl, struct delta_ipc_param *param,
3208c2ecf20Sopenharmony_ci		     struct delta_ipc_param *status)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct delta_ipc_ctx *ctx = to_ctx(hdl);
3238c2ecf20Sopenharmony_ci	struct delta_ctx *pctx = to_pctx(ctx);
3248c2ecf20Sopenharmony_ci	struct delta_dev *delta = pctx->dev;
3258c2ecf20Sopenharmony_ci	struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
3268c2ecf20Sopenharmony_ci	struct delta_ipc_decode_msg msg;
3278c2ecf20Sopenharmony_ci	int ret;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (!hdl) {
3308c2ecf20Sopenharmony_ci		dev_err(delta->dev,
3318c2ecf20Sopenharmony_ci			"%s   ipc: failed to decode, invalid ipc handle\n",
3328c2ecf20Sopenharmony_ci			pctx->name);
3338c2ecf20Sopenharmony_ci		return -EINVAL;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (!rpmsg_device) {
3378c2ecf20Sopenharmony_ci		dev_err(delta->dev,
3388c2ecf20Sopenharmony_ci			"%s   ipc: failed to decode, rpmsg is not initialized\n",
3398c2ecf20Sopenharmony_ci			pctx->name);
3408c2ecf20Sopenharmony_ci		return -EINVAL;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (!param || !param->data || !param->size) {
3448c2ecf20Sopenharmony_ci		dev_err(delta->dev,
3458c2ecf20Sopenharmony_ci			"%s  ipc: failed to decode, empty parameter\n",
3468c2ecf20Sopenharmony_ci			pctx->name);
3478c2ecf20Sopenharmony_ci		return -EINVAL;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (!status || !status->data || !status->size) {
3518c2ecf20Sopenharmony_ci		dev_err(delta->dev,
3528c2ecf20Sopenharmony_ci			"%s  ipc: failed to decode, empty status\n",
3538c2ecf20Sopenharmony_ci			pctx->name);
3548c2ecf20Sopenharmony_ci		return -EINVAL;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (param->size + status->size > ctx->ipc_buf->size) {
3588c2ecf20Sopenharmony_ci		dev_err(delta->dev,
3598c2ecf20Sopenharmony_ci			"%s   ipc: failed to decode, too large ipc parameter (%d bytes (param) + %d bytes (status) while max %d expected)\n",
3608c2ecf20Sopenharmony_ci			pctx->name,
3618c2ecf20Sopenharmony_ci			param->size,
3628c2ecf20Sopenharmony_ci			status->size,
3638c2ecf20Sopenharmony_ci			ctx->ipc_buf->size);
3648c2ecf20Sopenharmony_ci		return -EINVAL;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (!is_valid_data(ctx, param->data, param->size)) {
3688c2ecf20Sopenharmony_ci		dev_err(delta->dev,
3698c2ecf20Sopenharmony_ci			"%s   ipc: failed to decode, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",
3708c2ecf20Sopenharmony_ci			pctx->name,
3718c2ecf20Sopenharmony_ci			param->size,
3728c2ecf20Sopenharmony_ci			param->data,
3738c2ecf20Sopenharmony_ci			ctx->ipc_buf->vaddr,
3748c2ecf20Sopenharmony_ci			ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
3758c2ecf20Sopenharmony_ci		return -EINVAL;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (!is_valid_data(ctx, status->data, status->size)) {
3798c2ecf20Sopenharmony_ci		dev_err(delta->dev,
3808c2ecf20Sopenharmony_ci			"%s   ipc: failed to decode, status is not in expected address range (size=%d, data=%p not in %p..%p)\n",
3818c2ecf20Sopenharmony_ci			pctx->name,
3828c2ecf20Sopenharmony_ci			status->size,
3838c2ecf20Sopenharmony_ci			status->data,
3848c2ecf20Sopenharmony_ci			ctx->ipc_buf->vaddr,
3858c2ecf20Sopenharmony_ci			ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);
3868c2ecf20Sopenharmony_ci		return -EINVAL;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* build rpmsg message */
3908c2ecf20Sopenharmony_ci	build_msg_header(ctx, DELTA_IPC_DECODE, &msg.header);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	msg.param_size = param->size;
3938c2ecf20Sopenharmony_ci	msg.param_paddr = to_paddr(ctx, param->data);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	msg.status_size = status->size;
3968c2ecf20Sopenharmony_ci	msg.status_paddr = to_paddr(ctx, status->data);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* send it */
3998c2ecf20Sopenharmony_ci	ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
4008c2ecf20Sopenharmony_ci	if (ret) {
4018c2ecf20Sopenharmony_ci		dev_err(delta->dev,
4028c2ecf20Sopenharmony_ci			"%s   ipc: failed to decode, rpmsg_send failed (%d) for DELTA_IPC_DECODE (size=%d, data=%p)\n",
4038c2ecf20Sopenharmony_ci			pctx->name,
4048c2ecf20Sopenharmony_ci			ret, param->size, param->data);
4058c2ecf20Sopenharmony_ci		pctx->sys_errors++;
4068c2ecf20Sopenharmony_ci		return ret;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/* wait for acknowledge */
4108c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout
4118c2ecf20Sopenharmony_ci	    (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
4128c2ecf20Sopenharmony_ci		dev_err(delta->dev,
4138c2ecf20Sopenharmony_ci			"%s   ipc: failed to decode, timeout waiting for DELTA_IPC_DECODE callback (size=%d, data=%p)\n",
4148c2ecf20Sopenharmony_ci			pctx->name,
4158c2ecf20Sopenharmony_ci			param->size, param->data);
4168c2ecf20Sopenharmony_ci		pctx->sys_errors++;
4178c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* command completed, check status */
4218c2ecf20Sopenharmony_ci	if (ctx->cb_err) {
4228c2ecf20Sopenharmony_ci		dev_err(delta->dev,
4238c2ecf20Sopenharmony_ci			"%s   ipc: failed to decode, DELTA_IPC_DECODE completed but with error (%d) (size=%d, data=%p)\n",
4248c2ecf20Sopenharmony_ci			pctx->name,
4258c2ecf20Sopenharmony_ci			ctx->cb_err, param->size, param->data);
4268c2ecf20Sopenharmony_ci		pctx->sys_errors++;
4278c2ecf20Sopenharmony_ci		return -EIO;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci};
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_civoid delta_ipc_close(void *hdl)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	struct delta_ipc_ctx *ctx = to_ctx(hdl);
4368c2ecf20Sopenharmony_ci	struct delta_ctx *pctx = to_pctx(ctx);
4378c2ecf20Sopenharmony_ci	struct delta_dev *delta = pctx->dev;
4388c2ecf20Sopenharmony_ci	struct rpmsg_device *rpmsg_device = delta->rpmsg_device;
4398c2ecf20Sopenharmony_ci	struct delta_ipc_close_msg msg;
4408c2ecf20Sopenharmony_ci	int ret;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (!hdl) {
4438c2ecf20Sopenharmony_ci		dev_err(delta->dev,
4448c2ecf20Sopenharmony_ci			"%s   ipc: failed to close, invalid ipc handle\n",
4458c2ecf20Sopenharmony_ci			pctx->name);
4468c2ecf20Sopenharmony_ci		return;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (ctx->ipc_buf) {
4508c2ecf20Sopenharmony_ci		hw_free(pctx, ctx->ipc_buf);
4518c2ecf20Sopenharmony_ci		ctx->ipc_buf = NULL;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (!rpmsg_device) {
4558c2ecf20Sopenharmony_ci		dev_err(delta->dev,
4568c2ecf20Sopenharmony_ci			"%s   ipc: failed to close, rpmsg is not initialized\n",
4578c2ecf20Sopenharmony_ci			pctx->name);
4588c2ecf20Sopenharmony_ci		return;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/* build rpmsg message */
4628c2ecf20Sopenharmony_ci	build_msg_header(ctx, DELTA_IPC_CLOSE, &msg.header);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* send it */
4658c2ecf20Sopenharmony_ci	ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));
4668c2ecf20Sopenharmony_ci	if (ret) {
4678c2ecf20Sopenharmony_ci		dev_err(delta->dev,
4688c2ecf20Sopenharmony_ci			"%s   ipc: failed to close, rpmsg_send failed (%d) for DELTA_IPC_CLOSE\n",
4698c2ecf20Sopenharmony_ci			pctx->name, ret);
4708c2ecf20Sopenharmony_ci		pctx->sys_errors++;
4718c2ecf20Sopenharmony_ci		return;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* wait for acknowledge */
4758c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout
4768c2ecf20Sopenharmony_ci	    (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {
4778c2ecf20Sopenharmony_ci		dev_err(delta->dev,
4788c2ecf20Sopenharmony_ci			"%s   ipc: failed to close, timeout waiting for DELTA_IPC_CLOSE callback\n",
4798c2ecf20Sopenharmony_ci			pctx->name);
4808c2ecf20Sopenharmony_ci		pctx->sys_errors++;
4818c2ecf20Sopenharmony_ci		return;
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* command completed, check status */
4858c2ecf20Sopenharmony_ci	if (ctx->cb_err) {
4868c2ecf20Sopenharmony_ci		dev_err(delta->dev,
4878c2ecf20Sopenharmony_ci			"%s   ipc: failed to close, DELTA_IPC_CLOSE completed but with error (%d)\n",
4888c2ecf20Sopenharmony_ci			pctx->name, ctx->cb_err);
4898c2ecf20Sopenharmony_ci		pctx->sys_errors++;
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci};
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic int delta_ipc_cb(struct rpmsg_device *rpdev, void *data,
4948c2ecf20Sopenharmony_ci			int len, void *priv, u32 src)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	struct delta_ipc_ctx *ctx;
4978c2ecf20Sopenharmony_ci	struct delta_ipc_cb_msg *msg;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* sanity check */
5008c2ecf20Sopenharmony_ci	if (!rpdev) {
5018c2ecf20Sopenharmony_ci		dev_err(NULL, "rpdev is NULL\n");
5028c2ecf20Sopenharmony_ci		return -EINVAL;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (!data || !len) {
5068c2ecf20Sopenharmony_ci		dev_err(&rpdev->dev,
5078c2ecf20Sopenharmony_ci			"unexpected empty message received from src=%d\n", src);
5088c2ecf20Sopenharmony_ci		return -EINVAL;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (len != sizeof(*msg)) {
5128c2ecf20Sopenharmony_ci		dev_err(&rpdev->dev,
5138c2ecf20Sopenharmony_ci			"unexpected message length received from src=%d (received %d bytes while %zu bytes expected)\n",
5148c2ecf20Sopenharmony_ci			len, src, sizeof(*msg));
5158c2ecf20Sopenharmony_ci		return -EINVAL;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	msg = (struct delta_ipc_cb_msg *)data;
5198c2ecf20Sopenharmony_ci	if (msg->header.tag != IPC_SANITY_TAG) {
5208c2ecf20Sopenharmony_ci		dev_err(&rpdev->dev,
5218c2ecf20Sopenharmony_ci			"unexpected message tag received from src=%d (received %x tag while %x expected)\n",
5228c2ecf20Sopenharmony_ci			src, msg->header.tag, IPC_SANITY_TAG);
5238c2ecf20Sopenharmony_ci		return -EINVAL;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	ctx = msg_to_ctx(msg);
5278c2ecf20Sopenharmony_ci	if (!ctx) {
5288c2ecf20Sopenharmony_ci		dev_err(&rpdev->dev,
5298c2ecf20Sopenharmony_ci			"unexpected message with NULL host_hdl received from src=%d\n",
5308c2ecf20Sopenharmony_ci			src);
5318c2ecf20Sopenharmony_ci		return -EINVAL;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/*
5358c2ecf20Sopenharmony_ci	 * if not already known, save copro instance context
5368c2ecf20Sopenharmony_ci	 * to ensure re-entrance on copro side
5378c2ecf20Sopenharmony_ci	 */
5388c2ecf20Sopenharmony_ci	if (!ctx->copro_hdl)
5398c2ecf20Sopenharmony_ci		ctx->copro_hdl = msg_to_copro_hdl(msg);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/*
5428c2ecf20Sopenharmony_ci	 * all is fine,
5438c2ecf20Sopenharmony_ci	 * update status & complete command
5448c2ecf20Sopenharmony_ci	 */
5458c2ecf20Sopenharmony_ci	ctx->cb_err = msg->err;
5468c2ecf20Sopenharmony_ci	complete(&ctx->done);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	return 0;
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cistatic int delta_ipc_probe(struct rpmsg_device *rpmsg_device)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpmsg_device->dev.driver);
5548c2ecf20Sopenharmony_ci	struct delta_dev *delta = to_delta(rpdrv);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	delta->rpmsg_device = rpmsg_device;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	return 0;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void delta_ipc_remove(struct rpmsg_device *rpmsg_device)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpmsg_device->dev.driver);
5648c2ecf20Sopenharmony_ci	struct delta_dev *delta = to_delta(rpdrv);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	delta->rpmsg_device = NULL;
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic struct rpmsg_device_id delta_ipc_device_id_table[] = {
5708c2ecf20Sopenharmony_ci	{.name = "rpmsg-delta"},
5718c2ecf20Sopenharmony_ci	{},
5728c2ecf20Sopenharmony_ci};
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic struct rpmsg_driver delta_rpmsg_driver = {
5758c2ecf20Sopenharmony_ci	.drv = {.name = KBUILD_MODNAME},
5768c2ecf20Sopenharmony_ci	.id_table = delta_ipc_device_id_table,
5778c2ecf20Sopenharmony_ci	.probe = delta_ipc_probe,
5788c2ecf20Sopenharmony_ci	.callback = delta_ipc_cb,
5798c2ecf20Sopenharmony_ci	.remove = delta_ipc_remove,
5808c2ecf20Sopenharmony_ci};
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ciint delta_ipc_init(struct delta_dev *delta)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	delta->rpmsg_driver = delta_rpmsg_driver;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return register_rpmsg_driver(&delta->rpmsg_driver);
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_civoid delta_ipc_exit(struct delta_dev *delta)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	unregister_rpmsg_driver(&delta->rpmsg_driver);
5928c2ecf20Sopenharmony_ci}
593