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