592 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STMicroelectronics SA 20154 * Author: Hugues Fruchet <hugues.fruchet@st.com> for STMicroelectronics.5 */6 7#include <linux/rpmsg.h>8 9#include "delta.h"10#include "delta-ipc.h"11#include "delta-mem.h"12 13#define IPC_TIMEOUT 10014#define IPC_SANITY_TAG 0xDEADBEEF15 16enum delta_ipc_fw_command {17 DELTA_IPC_OPEN,18 DELTA_IPC_SET_STREAM,19 DELTA_IPC_DECODE,20 DELTA_IPC_CLOSE21};22 23#define to_rpmsg_driver(__drv) container_of(__drv, struct rpmsg_driver, drv)24#define to_delta(__d) container_of(__d, struct delta_dev, rpmsg_driver)25 26#define to_ctx(hdl) ((struct delta_ipc_ctx *)hdl)27#define to_pctx(ctx) container_of(ctx, struct delta_ctx, ipc_ctx)28 29struct delta_ipc_header_msg {30 u32 tag;31 void *host_hdl;32 u32 copro_hdl;33 u32 command;34};35 36#define to_host_hdl(ctx) ((void *)ctx)37 38#define msg_to_ctx(msg) ((struct delta_ipc_ctx *)(msg)->header.host_hdl)39#define msg_to_copro_hdl(msg) ((msg)->header.copro_hdl)40 41static inline dma_addr_t to_paddr(struct delta_ipc_ctx *ctx, void *vaddr)42{43 return (ctx->ipc_buf->paddr + (vaddr - ctx->ipc_buf->vaddr));44}45 46static inline bool is_valid_data(struct delta_ipc_ctx *ctx,47 void *data, u32 size)48{49 return ((data >= ctx->ipc_buf->vaddr) &&50 ((data + size) <= (ctx->ipc_buf->vaddr + ctx->ipc_buf->size)));51}52 53/*54 * IPC shared memory (@ipc_buf_size, @ipc_buf_paddr) is sent to copro55 * at each instance opening. This memory is allocated by IPC client56 * and given through delta_ipc_open(). All messages parameters57 * (open, set_stream, decode) will have their phy address within58 * this IPC shared memory, avoiding de-facto recopies inside delta-ipc.59 * All the below messages structures are used on both host and firmware60 * side and are packed (use only of 32 bits size fields in messages61 * structures to ensure packing):62 * - struct delta_ipc_open_msg63 * - struct delta_ipc_set_stream_msg64 * - struct delta_ipc_decode_msg65 * - struct delta_ipc_close_msg66 * - struct delta_ipc_cb_msg67 */68struct delta_ipc_open_msg {69 struct delta_ipc_header_msg header;70 u32 ipc_buf_size;71 dma_addr_t ipc_buf_paddr;72 char name[32];73 u32 param_size;74 dma_addr_t param_paddr;75};76 77struct delta_ipc_set_stream_msg {78 struct delta_ipc_header_msg header;79 u32 param_size;80 dma_addr_t param_paddr;81};82 83struct delta_ipc_decode_msg {84 struct delta_ipc_header_msg header;85 u32 param_size;86 dma_addr_t param_paddr;87 u32 status_size;88 dma_addr_t status_paddr;89};90 91struct delta_ipc_close_msg {92 struct delta_ipc_header_msg header;93};94 95struct delta_ipc_cb_msg {96 struct delta_ipc_header_msg header;97 int err;98};99 100static void build_msg_header(struct delta_ipc_ctx *ctx,101 enum delta_ipc_fw_command command,102 struct delta_ipc_header_msg *header)103{104 header->tag = IPC_SANITY_TAG;105 header->host_hdl = to_host_hdl(ctx);106 header->copro_hdl = ctx->copro_hdl;107 header->command = command;108}109 110int delta_ipc_open(struct delta_ctx *pctx, const char *name,111 struct delta_ipc_param *param, u32 ipc_buf_size,112 struct delta_buf **ipc_buf, void **hdl)113{114 struct delta_dev *delta = pctx->dev;115 struct rpmsg_device *rpmsg_device = delta->rpmsg_device;116 struct delta_ipc_ctx *ctx = &pctx->ipc_ctx;117 struct delta_ipc_open_msg msg;118 struct delta_buf *buf = &ctx->ipc_buf_struct;119 int ret;120 121 if (!rpmsg_device) {122 dev_err(delta->dev,123 "%s ipc: failed to open, rpmsg is not initialized\n",124 pctx->name);125 pctx->sys_errors++;126 return -EINVAL;127 }128 129 if (!name) {130 dev_err(delta->dev,131 "%s ipc: failed to open, no name given\n",132 pctx->name);133 return -EINVAL;134 }135 136 if (!param || !param->data || !param->size) {137 dev_err(delta->dev,138 "%s ipc: failed to open, empty parameter\n",139 pctx->name);140 return -EINVAL;141 }142 143 if (!ipc_buf_size) {144 dev_err(delta->dev,145 "%s ipc: failed to open, no size given for ipc buffer\n",146 pctx->name);147 return -EINVAL;148 }149 150 if (param->size > ipc_buf_size) {151 dev_err(delta->dev,152 "%s ipc: failed to open, too large ipc parameter (%d bytes while max %d expected)\n",153 pctx->name,154 param->size, ctx->ipc_buf->size);155 return -EINVAL;156 }157 158 /* init */159 init_completion(&ctx->done);160 161 /*162 * allocation of contiguous buffer for163 * data of commands exchanged between164 * host and firmware coprocessor165 */166 ret = hw_alloc(pctx, ipc_buf_size,167 "ipc data buffer", buf);168 if (ret)169 return ret;170 ctx->ipc_buf = buf;171 172 /* build rpmsg message */173 build_msg_header(ctx, DELTA_IPC_OPEN, &msg.header);174 175 msg.ipc_buf_size = ipc_buf_size;176 msg.ipc_buf_paddr = ctx->ipc_buf->paddr;177 178 strscpy(msg.name, name, sizeof(msg.name));179 180 msg.param_size = param->size;181 memcpy(ctx->ipc_buf->vaddr, param->data, msg.param_size);182 msg.param_paddr = ctx->ipc_buf->paddr;183 184 /* send it */185 ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));186 if (ret) {187 dev_err(delta->dev,188 "%s ipc: failed to open, rpmsg_send failed (%d) for DELTA_IPC_OPEN (name=%s, size=%d, data=%p)\n",189 pctx->name,190 ret, name, param->size, param->data);191 goto err;192 }193 194 /* wait for acknowledge */195 if (!wait_for_completion_timeout196 (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {197 dev_err(delta->dev,198 "%s ipc: failed to open, timeout waiting for DELTA_IPC_OPEN callback (name=%s, size=%d, data=%p)\n",199 pctx->name,200 name, param->size, param->data);201 ret = -ETIMEDOUT;202 goto err;203 }204 205 /* command completed, check error */206 if (ctx->cb_err) {207 dev_err(delta->dev,208 "%s ipc: failed to open, DELTA_IPC_OPEN completed but with error (%d) (name=%s, size=%d, data=%p)\n",209 pctx->name,210 ctx->cb_err, name, param->size, param->data);211 ret = -EIO;212 goto err;213 }214 215 *ipc_buf = ctx->ipc_buf;216 *hdl = (void *)ctx;217 218 return 0;219 220err:221 pctx->sys_errors++;222 hw_free(pctx, ctx->ipc_buf);223 ctx->ipc_buf = NULL;224 225 return ret;226};227 228int delta_ipc_set_stream(void *hdl, struct delta_ipc_param *param)229{230 struct delta_ipc_ctx *ctx = to_ctx(hdl);231 struct delta_ctx *pctx = to_pctx(ctx);232 struct delta_dev *delta = pctx->dev;233 struct rpmsg_device *rpmsg_device = delta->rpmsg_device;234 struct delta_ipc_set_stream_msg msg;235 int ret;236 237 if (!hdl) {238 dev_err(delta->dev,239 "%s ipc: failed to set stream, invalid ipc handle\n",240 pctx->name);241 return -EINVAL;242 }243 244 if (!rpmsg_device) {245 dev_err(delta->dev,246 "%s ipc: failed to set stream, rpmsg is not initialized\n",247 pctx->name);248 return -EINVAL;249 }250 251 if (!param || !param->data || !param->size) {252 dev_err(delta->dev,253 "%s ipc: failed to set stream, empty parameter\n",254 pctx->name);255 return -EINVAL;256 }257 258 if (param->size > ctx->ipc_buf->size) {259 dev_err(delta->dev,260 "%s ipc: failed to set stream, too large ipc parameter(%d bytes while max %d expected)\n",261 pctx->name,262 param->size, ctx->ipc_buf->size);263 return -EINVAL;264 }265 266 if (!is_valid_data(ctx, param->data, param->size)) {267 dev_err(delta->dev,268 "%s ipc: failed to set stream, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",269 pctx->name,270 param->size,271 param->data,272 ctx->ipc_buf->vaddr,273 ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);274 return -EINVAL;275 }276 277 /* build rpmsg message */278 build_msg_header(ctx, DELTA_IPC_SET_STREAM, &msg.header);279 280 msg.param_size = param->size;281 msg.param_paddr = to_paddr(ctx, param->data);282 283 /* send it */284 ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));285 if (ret) {286 dev_err(delta->dev,287 "%s ipc: failed to set stream, rpmsg_send failed (%d) for DELTA_IPC_SET_STREAM (size=%d, data=%p)\n",288 pctx->name,289 ret, param->size, param->data);290 pctx->sys_errors++;291 return ret;292 }293 294 /* wait for acknowledge */295 if (!wait_for_completion_timeout296 (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {297 dev_err(delta->dev,298 "%s ipc: failed to set stream, timeout waiting for DELTA_IPC_SET_STREAM callback (size=%d, data=%p)\n",299 pctx->name,300 param->size, param->data);301 pctx->sys_errors++;302 return -ETIMEDOUT;303 }304 305 /* command completed, check status */306 if (ctx->cb_err) {307 dev_err(delta->dev,308 "%s ipc: failed to set stream, DELTA_IPC_SET_STREAM completed but with error (%d) (size=%d, data=%p)\n",309 pctx->name,310 ctx->cb_err, param->size, param->data);311 pctx->sys_errors++;312 return -EIO;313 }314 315 return 0;316}317 318int delta_ipc_decode(void *hdl, struct delta_ipc_param *param,319 struct delta_ipc_param *status)320{321 struct delta_ipc_ctx *ctx = to_ctx(hdl);322 struct delta_ctx *pctx = to_pctx(ctx);323 struct delta_dev *delta = pctx->dev;324 struct rpmsg_device *rpmsg_device = delta->rpmsg_device;325 struct delta_ipc_decode_msg msg;326 int ret;327 328 if (!hdl) {329 dev_err(delta->dev,330 "%s ipc: failed to decode, invalid ipc handle\n",331 pctx->name);332 return -EINVAL;333 }334 335 if (!rpmsg_device) {336 dev_err(delta->dev,337 "%s ipc: failed to decode, rpmsg is not initialized\n",338 pctx->name);339 return -EINVAL;340 }341 342 if (!param || !param->data || !param->size) {343 dev_err(delta->dev,344 "%s ipc: failed to decode, empty parameter\n",345 pctx->name);346 return -EINVAL;347 }348 349 if (!status || !status->data || !status->size) {350 dev_err(delta->dev,351 "%s ipc: failed to decode, empty status\n",352 pctx->name);353 return -EINVAL;354 }355 356 if (param->size + status->size > ctx->ipc_buf->size) {357 dev_err(delta->dev,358 "%s ipc: failed to decode, too large ipc parameter (%d bytes (param) + %d bytes (status) while max %d expected)\n",359 pctx->name,360 param->size,361 status->size,362 ctx->ipc_buf->size);363 return -EINVAL;364 }365 366 if (!is_valid_data(ctx, param->data, param->size)) {367 dev_err(delta->dev,368 "%s ipc: failed to decode, parameter is not in expected address range (size=%d, data=%p not in %p..%p)\n",369 pctx->name,370 param->size,371 param->data,372 ctx->ipc_buf->vaddr,373 ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);374 return -EINVAL;375 }376 377 if (!is_valid_data(ctx, status->data, status->size)) {378 dev_err(delta->dev,379 "%s ipc: failed to decode, status is not in expected address range (size=%d, data=%p not in %p..%p)\n",380 pctx->name,381 status->size,382 status->data,383 ctx->ipc_buf->vaddr,384 ctx->ipc_buf->vaddr + ctx->ipc_buf->size - 1);385 return -EINVAL;386 }387 388 /* build rpmsg message */389 build_msg_header(ctx, DELTA_IPC_DECODE, &msg.header);390 391 msg.param_size = param->size;392 msg.param_paddr = to_paddr(ctx, param->data);393 394 msg.status_size = status->size;395 msg.status_paddr = to_paddr(ctx, status->data);396 397 /* send it */398 ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));399 if (ret) {400 dev_err(delta->dev,401 "%s ipc: failed to decode, rpmsg_send failed (%d) for DELTA_IPC_DECODE (size=%d, data=%p)\n",402 pctx->name,403 ret, param->size, param->data);404 pctx->sys_errors++;405 return ret;406 }407 408 /* wait for acknowledge */409 if (!wait_for_completion_timeout410 (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {411 dev_err(delta->dev,412 "%s ipc: failed to decode, timeout waiting for DELTA_IPC_DECODE callback (size=%d, data=%p)\n",413 pctx->name,414 param->size, param->data);415 pctx->sys_errors++;416 return -ETIMEDOUT;417 }418 419 /* command completed, check status */420 if (ctx->cb_err) {421 dev_err(delta->dev,422 "%s ipc: failed to decode, DELTA_IPC_DECODE completed but with error (%d) (size=%d, data=%p)\n",423 pctx->name,424 ctx->cb_err, param->size, param->data);425 pctx->sys_errors++;426 return -EIO;427 }428 429 return 0;430};431 432void delta_ipc_close(void *hdl)433{434 struct delta_ipc_ctx *ctx = to_ctx(hdl);435 struct delta_ctx *pctx = to_pctx(ctx);436 struct delta_dev *delta = pctx->dev;437 struct rpmsg_device *rpmsg_device = delta->rpmsg_device;438 struct delta_ipc_close_msg msg;439 int ret;440 441 if (!hdl) {442 dev_err(delta->dev,443 "%s ipc: failed to close, invalid ipc handle\n",444 pctx->name);445 return;446 }447 448 if (ctx->ipc_buf) {449 hw_free(pctx, ctx->ipc_buf);450 ctx->ipc_buf = NULL;451 }452 453 if (!rpmsg_device) {454 dev_err(delta->dev,455 "%s ipc: failed to close, rpmsg is not initialized\n",456 pctx->name);457 return;458 }459 460 /* build rpmsg message */461 build_msg_header(ctx, DELTA_IPC_CLOSE, &msg.header);462 463 /* send it */464 ret = rpmsg_send(rpmsg_device->ept, &msg, sizeof(msg));465 if (ret) {466 dev_err(delta->dev,467 "%s ipc: failed to close, rpmsg_send failed (%d) for DELTA_IPC_CLOSE\n",468 pctx->name, ret);469 pctx->sys_errors++;470 return;471 }472 473 /* wait for acknowledge */474 if (!wait_for_completion_timeout475 (&ctx->done, msecs_to_jiffies(IPC_TIMEOUT))) {476 dev_err(delta->dev,477 "%s ipc: failed to close, timeout waiting for DELTA_IPC_CLOSE callback\n",478 pctx->name);479 pctx->sys_errors++;480 return;481 }482 483 /* command completed, check status */484 if (ctx->cb_err) {485 dev_err(delta->dev,486 "%s ipc: failed to close, DELTA_IPC_CLOSE completed but with error (%d)\n",487 pctx->name, ctx->cb_err);488 pctx->sys_errors++;489 }490};491 492static int delta_ipc_cb(struct rpmsg_device *rpdev, void *data,493 int len, void *priv, u32 src)494{495 struct delta_ipc_ctx *ctx;496 struct delta_ipc_cb_msg *msg;497 498 /* sanity check */499 if (!rpdev) {500 dev_err(NULL, "rpdev is NULL\n");501 return -EINVAL;502 }503 504 if (!data || !len) {505 dev_err(&rpdev->dev,506 "unexpected empty message received from src=%d\n", src);507 return -EINVAL;508 }509 510 if (len != sizeof(*msg)) {511 dev_err(&rpdev->dev,512 "unexpected message length received from src=%d (received %d bytes while %zu bytes expected)\n",513 len, src, sizeof(*msg));514 return -EINVAL;515 }516 517 msg = (struct delta_ipc_cb_msg *)data;518 if (msg->header.tag != IPC_SANITY_TAG) {519 dev_err(&rpdev->dev,520 "unexpected message tag received from src=%d (received %x tag while %x expected)\n",521 src, msg->header.tag, IPC_SANITY_TAG);522 return -EINVAL;523 }524 525 ctx = msg_to_ctx(msg);526 if (!ctx) {527 dev_err(&rpdev->dev,528 "unexpected message with NULL host_hdl received from src=%d\n",529 src);530 return -EINVAL;531 }532 533 /*534 * if not already known, save copro instance context535 * to ensure re-entrance on copro side536 */537 if (!ctx->copro_hdl)538 ctx->copro_hdl = msg_to_copro_hdl(msg);539 540 /*541 * all is fine,542 * update status & complete command543 */544 ctx->cb_err = msg->err;545 complete(&ctx->done);546 547 return 0;548}549 550static int delta_ipc_probe(struct rpmsg_device *rpmsg_device)551{552 struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpmsg_device->dev.driver);553 struct delta_dev *delta = to_delta(rpdrv);554 555 delta->rpmsg_device = rpmsg_device;556 557 return 0;558}559 560static void delta_ipc_remove(struct rpmsg_device *rpmsg_device)561{562 struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpmsg_device->dev.driver);563 struct delta_dev *delta = to_delta(rpdrv);564 565 delta->rpmsg_device = NULL;566}567 568static struct rpmsg_device_id delta_ipc_device_id_table[] = {569 {.name = "rpmsg-delta"},570 {},571};572 573static struct rpmsg_driver delta_rpmsg_driver = {574 .drv = {.name = KBUILD_MODNAME},575 .id_table = delta_ipc_device_id_table,576 .probe = delta_ipc_probe,577 .callback = delta_ipc_cb,578 .remove = delta_ipc_remove,579};580 581int delta_ipc_init(struct delta_dev *delta)582{583 delta->rpmsg_driver = delta_rpmsg_driver;584 585 return register_rpmsg_driver(&delta->rpmsg_driver);586}587 588void delta_ipc_exit(struct delta_dev *delta)589{590 unregister_rpmsg_driver(&delta->rpmsg_driver);591}592