Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: arm_scmi: Add support for asynchronous commands and delayed response

Messages that are sent to platform, also known as commands and can be:

1. Synchronous commands that block the channel until the requested work
has been completed. The platform responds to these commands over the
same channel and hence can't be used to send another command until the
previous command has completed.

2. Asynchronous commands on the other hand, the platform schedules the
requested work to complete later in time and returns almost immediately
freeing the channel for new commands. The response indicates the success
or failure in the ability to schedule the requested work. When the work
has completed, the platform sends an additional delayed response message.

Using the same transmit buffer used for sending the asynchronous command
even for the delayed response corresponding to it simplifies handling of
the delayed response. It's the caller of asynchronous command that is
responsible for allocating the completion flag that scmi driver can
complete to indicate the arrival of delayed response.

Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>

+46 -3
+5 -1
drivers/firmware/arm_scmi/common.h
··· 84 84 * @rx: Receive message, the buffer should be pre-allocated to store 85 85 * message. If request-ACK protocol is used, we can reuse the same 86 86 * buffer for the rx path as we use for the tx path. 87 - * @done: completion event 87 + * @done: command message transmit completion event 88 + * @async: pointer to delayed response message received event completion 88 89 */ 89 90 struct scmi_xfer { 90 91 struct scmi_msg_hdr hdr; 91 92 struct scmi_msg tx; 92 93 struct scmi_msg rx; 93 94 struct completion done; 95 + struct completion *async_done; 94 96 }; 95 97 96 98 void scmi_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer); 97 99 int scmi_do_xfer(const struct scmi_handle *h, struct scmi_xfer *xfer); 100 + int scmi_do_xfer_with_response(const struct scmi_handle *h, 101 + struct scmi_xfer *xfer); 98 102 int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id, 99 103 size_t tx_size, size_t rx_size, struct scmi_xfer **p); 100 104 int scmi_handle_put(const struct scmi_handle *handle);
+41 -2
drivers/firmware/arm_scmi/driver.c
··· 345 345 */ 346 346 static void scmi_rx_callback(struct mbox_client *cl, void *m) 347 347 { 348 + u8 msg_type; 349 + u32 msg_hdr; 348 350 u16 xfer_id; 349 351 struct scmi_xfer *xfer; 350 352 struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); ··· 355 353 struct scmi_xfers_info *minfo = &info->tx_minfo; 356 354 struct scmi_shared_mem __iomem *mem = cinfo->payload; 357 355 358 - xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); 356 + msg_hdr = ioread32(&mem->msg_header); 357 + msg_type = MSG_XTRACT_TYPE(msg_hdr); 358 + xfer_id = MSG_XTRACT_TOKEN(msg_hdr); 359 + 360 + if (msg_type == MSG_TYPE_NOTIFICATION) 361 + return; /* Notifications not yet supported */ 359 362 360 363 /* Are we even expecting this? */ 361 364 if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { ··· 373 366 scmi_dump_header_dbg(dev, &xfer->hdr); 374 367 375 368 scmi_fetch_response(xfer, mem); 376 - complete(&xfer->done); 369 + 370 + if (msg_type == MSG_TYPE_DELAYED_RESP) 371 + complete(xfer->async_done); 372 + else 373 + complete(&xfer->done); 377 374 } 378 375 379 376 /** ··· 478 467 */ 479 468 mbox_client_txdone(cinfo->chan, ret); 480 469 470 + return ret; 471 + } 472 + 473 + #define SCMI_MAX_RESPONSE_TIMEOUT (2 * MSEC_PER_SEC) 474 + 475 + /** 476 + * scmi_do_xfer_with_response() - Do one transfer and wait until the delayed 477 + * response is received 478 + * 479 + * @handle: Pointer to SCMI entity handle 480 + * @xfer: Transfer to initiate and wait for response 481 + * 482 + * Return: -ETIMEDOUT in case of no delayed response, if transmit error, 483 + * return corresponding error, else if all goes well, return 0. 484 + */ 485 + int scmi_do_xfer_with_response(const struct scmi_handle *handle, 486 + struct scmi_xfer *xfer) 487 + { 488 + int ret, timeout = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT); 489 + DECLARE_COMPLETION_ONSTACK(async_response); 490 + 491 + xfer->async_done = &async_response; 492 + 493 + ret = scmi_do_xfer(handle, xfer); 494 + if (!ret && !wait_for_completion_timeout(xfer->async_done, timeout)) 495 + ret = -ETIMEDOUT; 496 + 497 + xfer->async_done = NULL; 481 498 return ret; 482 499 } 483 500