brintos

brintos / linux-shallow public Read only

0
0
Text · 14.5 KiB · 21d3e08 Raw
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