| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * System Control and Management Interface (SCMI) Raw mode support |
| * |
| * Copyright (C) 2022 ARM Ltd. |
| */ |
| /** |
| * DOC: Theory of operation |
| * |
| * When enabled the SCMI Raw mode support exposes a userspace API which allows |
| * to send and receive SCMI commands, replies and notifications from a user |
| * application through injection and snooping of bare SCMI messages in binary |
| * little-endian format. |
| * |
| * Such injected SCMI transactions will then be routed through the SCMI core |
| * stack towards the SCMI backend server using whatever SCMI transport is |
| * currently configured on the system under test. |
| * |
| * It is meant to help in running any sort of SCMI backend server testing, no |
| * matter where the server is placed, as long as it is normally reachable via |
| * the transport configured on the system. |
| * |
| * It is activated by a Kernel configuration option since it is NOT meant to |
| * be used in production but only during development and in CI deployments. |
| * |
| * In order to avoid possible interferences between the SCMI Raw transactions |
| * originated from a test-suite and the normal operations of the SCMI drivers, |
| * when Raw mode is enabled, by default, all the regular SCMI drivers are |
| * inhibited, unless CONFIG_ARM_SCMI_RAW_MODE_SUPPORT_COEX is enabled: in this |
| * latter case the regular SCMI stack drivers will be loaded as usual and it is |
| * up to the user of this interface to take care of manually inhibiting the |
| * regular SCMI drivers in order to avoid interferences during the test runs. |
| * |
| * The exposed API is as follows. |
| * |
| * All SCMI Raw entries are rooted under a common top /raw debugfs top directory |
| * which in turn is rooted under the corresponding underlying SCMI instance. |
| * |
| * /sys/kernel/debug/scmi/ |
| * `-- 0 |
| * |-- atomic_threshold_us |
| * |-- instance_name |
| * |-- raw |
| * | |-- channels |
| * | | |-- 0x10 |
| * | | | |-- message |
| * | | | `-- message_async |
| * | | `-- 0x13 |
| * | | |-- message |
| * | | `-- message_async |
| * | |-- errors |
| * | |-- message |
| * | |-- message_async |
| * | |-- notification |
| * | `-- reset |
| * `-- transport |
| * |-- is_atomic |
| * |-- max_msg_size |
| * |-- max_rx_timeout_ms |
| * |-- rx_max_msg |
| * |-- tx_max_msg |
| * `-- type |
| * |
| * where: |
| * |
| * - errors: used to read back timed-out and unexpected replies |
| * - message*: used to send sync/async commands and read back immediate and |
| * delayed reponses (if any) |
| * - notification: used to read any notification being emitted by the system |
| * (if previously enabled by the user app) |
| * - reset: used to flush the queues of messages (of any kind) still pending |
| * to be read; this is useful at test-suite start/stop to get |
| * rid of any unread messages from the previous run. |
| * |
| * with the per-channel entries rooted at /channels being present only on a |
| * system where multiple transport channels have been configured. |
| * |
| * Such per-channel entries can be used to explicitly choose a specific channel |
| * for SCMI bare message injection, in contrast with the general entries above |
| * where, instead, the selection of the proper channel to use is automatically |
| * performed based the protocol embedded in the injected message and on how the |
| * transport is configured on the system. |
| * |
| * Note that other common general entries are available under transport/ to let |
| * the user applications properly make up their expectations in terms of |
| * timeouts and message characteristics. |
| * |
| * Each write to the message* entries causes one command request to be built |
| * and sent while the replies or delayed response are read back from those same |
| * entries one message at time (receiving an EOF at each message boundary). |
| * |
| * The user application running the test is in charge of handling timeouts |
| * on replies and properly choosing SCMI sequence numbers for the outgoing |
| * requests (using the same sequence number is supported but discouraged). |
| * |
| * Injection of multiple in-flight requests is supported as long as the user |
| * application uses properly distinct sequence numbers for concurrent requests |
| * and takes care to properly manage all the related issues about concurrency |
| * and command/reply pairing. Keep in mind that, anyway, the real level of |
| * parallelism attainable in such scenario is dependent on the characteristics |
| * of the underlying transport being used. |
| * |
| * Since the SCMI core regular stack is partially used to deliver and collect |
| * the messages, late replies arrived after timeouts and any other sort of |
| * unexpected message can be identified by the SCMI core as usual and they will |
| * be reported as messages under "errors" for later analysis. |
| */ |
| |
| #include <linux/bitmap.h> |
| #include <linux/debugfs.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/export.h> |
| #include <linux/io.h> |
| #include <linux/kernel.h> |
| #include <linux/fs.h> |
| #include <linux/list.h> |
| #include <linux/module.h> |
| #include <linux/poll.h> |
| #include <linux/of.h> |
| #include <linux/slab.h> |
| #include <linux/xarray.h> |
| |
| #include "common.h" |
| |
| #include "raw_mode.h" |
| |
| #include <trace/events/scmi.h> |
| |
| #define SCMI_XFER_RAW_MAX_RETRIES 10 |
| |
| /** |
| * struct scmi_raw_queue - Generic Raw queue descriptor |
| * |
| * @free_bufs: A freelists listhead used to keep unused raw buffers |
| * @free_bufs_lock: Spinlock used to protect access to @free_bufs |
| * @msg_q: A listhead to a queue of snooped messages waiting to be read out |
| * @msg_q_lock: Spinlock used to protect access to @msg_q |
| * @wq: A waitqueue used to wait and poll on related @msg_q |
| */ |
| struct scmi_raw_queue { |
| struct list_head free_bufs; |
| /* Protect free_bufs[] lists */ |
| spinlock_t free_bufs_lock; |
| struct list_head msg_q; |
| /* Protect msg_q[] lists */ |
| spinlock_t msg_q_lock; |
| wait_queue_head_t wq; |
| }; |
| |
| /** |
| * struct scmi_raw_mode_info - Structure holding SCMI Raw instance data |
| * |
| * @id: Sequential Raw instance ID. |
| * @handle: Pointer to SCMI entity handle to use |
| * @desc: Pointer to the transport descriptor to use |
| * @tx_max_msg: Maximum number of concurrent TX in-flight messages |
| * @q: An array of Raw queue descriptors |
| * @chans_q: An XArray mapping optional additional per-channel queues |
| * @free_waiters: Head of freelist for unused waiters |
| * @free_mtx: A mutex to protect the waiters freelist |
| * @active_waiters: Head of list for currently active and used waiters |
| * @active_mtx: A mutex to protect the active waiters list |
| * @waiters_work: A work descriptor to be used with the workqueue machinery |
| * @wait_wq: A workqueue reference to the created workqueue |
| * @dentry: Top debugfs root dentry for SCMI Raw |
| * @gid: A group ID used for devres accounting |
| * |
| * Note that this descriptor is passed back to the core after SCMI Raw is |
| * initialized as an opaque handle to use by subsequent SCMI Raw call hooks. |
| * |
| */ |
| struct scmi_raw_mode_info { |
| unsigned int id; |
| const struct scmi_handle *handle; |
| const struct scmi_desc *desc; |
| int tx_max_msg; |
| struct scmi_raw_queue *q[SCMI_RAW_MAX_QUEUE]; |
| struct xarray chans_q; |
| struct list_head free_waiters; |
| /* Protect free_waiters list */ |
| struct mutex free_mtx; |
| struct list_head active_waiters; |
| /* Protect active_waiters list */ |
| struct mutex active_mtx; |
| struct work_struct waiters_work; |
| struct workqueue_struct *wait_wq; |
| struct dentry *dentry; |
| void *gid; |
| }; |
| |
| /** |
| * struct scmi_xfer_raw_waiter - Structure to describe an xfer to be waited for |
| * |
| * @start_jiffies: The timestamp in jiffies of when this structure was queued. |
| * @cinfo: A reference to the channel to use for this transaction |
| * @xfer: A reference to the xfer to be waited for |
| * @async_response: A completion to be, optionally, used for async waits: it |
| * will be setup by @scmi_do_xfer_raw_start, if needed, to be |
| * pointed at by xfer->async_done. |
| * @node: A list node. |
| */ |
| struct scmi_xfer_raw_waiter { |
| unsigned long start_jiffies; |
| struct scmi_chan_info *cinfo; |
| struct scmi_xfer *xfer; |
| struct completion async_response; |
| struct list_head node; |
| }; |
| |
| /** |
| * struct scmi_raw_buffer - Structure to hold a full SCMI message |
| * |
| * @max_len: The maximum allowed message size (header included) that can be |
| * stored into @msg |
| * @msg: A message buffer used to collect a full message grabbed from an xfer. |
| * @node: A list node. |
| */ |
| struct scmi_raw_buffer { |
| size_t max_len; |
| struct scmi_msg msg; |
| struct list_head node; |
| }; |
| |
| /** |
| * struct scmi_dbg_raw_data - Structure holding data needed by the debugfs |
| * layer |
| * |
| * @chan_id: The preferred channel to use: if zero the channel is automatically |
| * selected based on protocol. |
| * @raw: A reference to the Raw instance. |
| * @tx: A message buffer used to collect TX message on write. |
| * @tx_size: The effective size of the TX message. |
| * @tx_req_size: The final expected size of the complete TX message. |
| * @rx: A message buffer to collect RX message on read. |
| * @rx_size: The effective size of the RX message. |
| */ |
| struct scmi_dbg_raw_data { |
| u8 chan_id; |
| struct scmi_raw_mode_info *raw; |
| struct scmi_msg tx; |
| size_t tx_size; |
| size_t tx_req_size; |
| struct scmi_msg rx; |
| size_t rx_size; |
| }; |
| |
| static struct scmi_raw_queue * |
| scmi_raw_queue_select(struct scmi_raw_mode_info *raw, unsigned int idx, |
| unsigned int chan_id) |
| { |
| if (!chan_id) |
| return raw->q[idx]; |
| |
| return xa_load(&raw->chans_q, chan_id); |
| } |
| |
| static struct scmi_raw_buffer *scmi_raw_buffer_get(struct scmi_raw_queue *q) |
| { |
| unsigned long flags; |
| struct scmi_raw_buffer *rb = NULL; |
| struct list_head *head = &q->free_bufs; |
| |
| spin_lock_irqsave(&q->free_bufs_lock, flags); |
| if (!list_empty(head)) { |
| rb = list_first_entry(head, struct scmi_raw_buffer, node); |
| list_del_init(&rb->node); |
| } |
| spin_unlock_irqrestore(&q->free_bufs_lock, flags); |
| |
| return rb; |
| } |
| |
| static void scmi_raw_buffer_put(struct scmi_raw_queue *q, |
| struct scmi_raw_buffer *rb) |
| { |
| unsigned long flags; |
| |
| /* Reset to full buffer length */ |
| rb->msg.len = rb->max_len; |
| |
| spin_lock_irqsave(&q->free_bufs_lock, flags); |
| list_add_tail(&rb->node, &q->free_bufs); |
| spin_unlock_irqrestore(&q->free_bufs_lock, flags); |
| } |
| |
| static void scmi_raw_buffer_enqueue(struct scmi_raw_queue *q, |
| struct scmi_raw_buffer *rb) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&q->msg_q_lock, flags); |
| list_add_tail(&rb->node, &q->msg_q); |
| spin_unlock_irqrestore(&q->msg_q_lock, flags); |
| |
| wake_up_interruptible(&q->wq); |
| } |
| |
| static struct scmi_raw_buffer* |
| scmi_raw_buffer_dequeue_unlocked(struct scmi_raw_queue *q) |
| { |
| struct scmi_raw_buffer *rb = NULL; |
| |
| if (!list_empty(&q->msg_q)) { |
| rb = list_first_entry(&q->msg_q, struct scmi_raw_buffer, node); |
| list_del_init(&rb->node); |
| } |
| |
| return rb; |
| } |
| |
| static struct scmi_raw_buffer *scmi_raw_buffer_dequeue(struct scmi_raw_queue *q) |
| { |
| unsigned long flags; |
| struct scmi_raw_buffer *rb; |
| |
| spin_lock_irqsave(&q->msg_q_lock, flags); |
| rb = scmi_raw_buffer_dequeue_unlocked(q); |
| spin_unlock_irqrestore(&q->msg_q_lock, flags); |
| |
| return rb; |
| } |
| |
| static void scmi_raw_buffer_queue_flush(struct scmi_raw_queue *q) |
| { |
| struct scmi_raw_buffer *rb; |
| |
| do { |
| rb = scmi_raw_buffer_dequeue(q); |
| if (rb) |
| scmi_raw_buffer_put(q, rb); |
| } while (rb); |
| } |
| |
| static struct scmi_xfer_raw_waiter * |
| scmi_xfer_raw_waiter_get(struct scmi_raw_mode_info *raw, struct scmi_xfer *xfer, |
| struct scmi_chan_info *cinfo, bool async) |
| { |
| struct scmi_xfer_raw_waiter *rw = NULL; |
| |
| mutex_lock(&raw->free_mtx); |
| if (!list_empty(&raw->free_waiters)) { |
| rw = list_first_entry(&raw->free_waiters, |
| struct scmi_xfer_raw_waiter, node); |
| list_del_init(&rw->node); |
| |
| if (async) { |
| reinit_completion(&rw->async_response); |
| xfer->async_done = &rw->async_response; |
| } |
| |
| rw->cinfo = cinfo; |
| rw->xfer = xfer; |
| } |
| mutex_unlock(&raw->free_mtx); |
| |
| return rw; |
| } |
| |
| static void scmi_xfer_raw_waiter_put(struct scmi_raw_mode_info *raw, |
| struct scmi_xfer_raw_waiter *rw) |
| { |
| if (rw->xfer) { |
| rw->xfer->async_done = NULL; |
| rw->xfer = NULL; |
| } |
| |
| mutex_lock(&raw->free_mtx); |
| list_add_tail(&rw->node, &raw->free_waiters); |
| mutex_unlock(&raw->free_mtx); |
| } |
| |
| static void scmi_xfer_raw_waiter_enqueue(struct scmi_raw_mode_info *raw, |
| struct scmi_xfer_raw_waiter *rw) |
| { |
| /* A timestamp for the deferred worker to know how much this has aged */ |
| rw->start_jiffies = jiffies; |
| |
| trace_scmi_xfer_response_wait(rw->xfer->transfer_id, rw->xfer->hdr.id, |
| rw->xfer->hdr.protocol_id, |
| rw->xfer->hdr.seq, |
| raw->desc->max_rx_timeout_ms, |
| rw->xfer->hdr.poll_completion); |
| |
| mutex_lock(&raw->active_mtx); |
| list_add_tail(&rw->node, &raw->active_waiters); |
| mutex_unlock(&raw->active_mtx); |
| |
| /* kick waiter work */ |
| queue_work(raw->wait_wq, &raw->waiters_work); |
| } |
| |
| static struct scmi_xfer_raw_waiter * |
| scmi_xfer_raw_waiter_dequeue(struct scmi_raw_mode_info *raw) |
| { |
| struct scmi_xfer_raw_waiter *rw = NULL; |
| |
| mutex_lock(&raw->active_mtx); |
| if (!list_empty(&raw->active_waiters)) { |
| rw = list_first_entry(&raw->active_waiters, |
| struct scmi_xfer_raw_waiter, node); |
| list_del_init(&rw->node); |
| } |
| mutex_unlock(&raw->active_mtx); |
| |
| return rw; |
| } |
| |
| /** |
| * scmi_xfer_raw_worker - Work function to wait for Raw xfers completions |
| * |
| * @work: A reference to the work. |
| * |
| * In SCMI Raw mode, once a user-provided injected SCMI message is sent, we |
| * cannot wait to receive its response (if any) in the context of the injection |
| * routines so as not to leave the userspace write syscall, which delivered the |
| * SCMI message to send, pending till eventually a reply is received. |
| * Userspace should and will poll/wait instead on the read syscalls which will |
| * be in charge of reading a received reply (if any). |
| * |
| * Even though reply messages are collected and reported into the SCMI Raw layer |
| * on the RX path, nonetheless we have to properly wait for their completion as |
| * usual (and async_completion too if needed) in order to properly release the |
| * xfer structure at the end: to do this out of the context of the write/send |
| * these waiting jobs are delegated to this deferred worker. |
| * |
| * Any sent xfer, to be waited for, is timestamped and queued for later |
| * consumption by this worker: queue aging is accounted for while choosing a |
| * timeout for the completion, BUT we do not really care here if we end up |
| * accidentally waiting for a bit too long. |
| */ |
| static void scmi_xfer_raw_worker(struct work_struct *work) |
| { |
| struct scmi_raw_mode_info *raw; |
| struct device *dev; |
| unsigned long max_tmo; |
| |
| raw = container_of(work, struct scmi_raw_mode_info, waiters_work); |
| dev = raw->handle->dev; |
| max_tmo = msecs_to_jiffies(raw->desc->max_rx_timeout_ms); |
| |
| do { |
| int ret = 0; |
| unsigned int timeout_ms; |
| unsigned long aging; |
| struct scmi_xfer *xfer; |
| struct scmi_xfer_raw_waiter *rw; |
| struct scmi_chan_info *cinfo; |
| |
| rw = scmi_xfer_raw_waiter_dequeue(raw); |
| if (!rw) |
| return; |
| |
| cinfo = rw->cinfo; |
| xfer = rw->xfer; |
| /* |
| * Waiters are queued by wait-deadline at the end, so some of |
| * them could have been already expired when processed, BUT we |
| * have to check the completion status anyway just in case a |
| * virtually expired (aged) transaction was indeed completed |
| * fine and we'll have to wait for the asynchronous part (if |
| * any): for this reason a 1 ms timeout is used for already |
| * expired/aged xfers. |
| */ |
| aging = jiffies - rw->start_jiffies; |
| timeout_ms = max_tmo > aging ? |
| jiffies_to_msecs(max_tmo - aging) : 1; |
| |
| ret = scmi_xfer_raw_wait_for_message_response(cinfo, xfer, |
| timeout_ms); |
| if (!ret && xfer->hdr.status) |
| ret = scmi_to_linux_errno(xfer->hdr.status); |
| |
| if (raw->desc->ops->mark_txdone) |
| raw->desc->ops->mark_txdone(rw->cinfo, ret, xfer); |
| |
| trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id, |
| xfer->hdr.protocol_id, xfer->hdr.seq, ret); |
| |
| /* Wait also for an async delayed response if needed */ |
| if (!ret && xfer->async_done) { |
| unsigned long tmo = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT); |
| |
| if (!wait_for_completion_timeout(xfer->async_done, tmo)) |
| dev_err(dev, |
| "timed out in RAW delayed resp - HDR:%08X\n", |
| pack_scmi_header(&xfer->hdr)); |
| } |
| |
| /* Release waiter and xfer */ |
| scmi_xfer_raw_put(raw->handle, xfer); |
| scmi_xfer_raw_waiter_put(raw, rw); |
| } while (1); |
| } |
| |
| static void scmi_xfer_raw_reset(struct scmi_raw_mode_info *raw) |
| { |
| int i; |
| |
| dev_info(raw->handle->dev, "Resetting SCMI Raw stack.\n"); |
| |
| for (i = 0; i < SCMI_RAW_MAX_QUEUE; i++) |
| scmi_raw_buffer_queue_flush(raw->q[i]); |
| } |
| |
| /** |
| * scmi_xfer_raw_get_init - An helper to build a valid xfer from the provided |
| * bare SCMI message. |
| * |
| * @raw: A reference to the Raw instance. |
| * @buf: A buffer containing the whole SCMI message to send (including the |
| * header) in little-endian binary formmat. |
| * @len: Length of the message in @buf. |
| * @p: A pointer to return the initialized Raw xfer. |
| * |
| * After an xfer is picked from the TX pool and filled in with the message |
| * content, the xfer is registered as pending with the core in the usual way |
| * using the original sequence number provided by the user with the message. |
| * |
| * Note that, in case the testing user application is NOT using distinct |
| * sequence-numbers between successive SCMI messages such registration could |
| * fail temporarily if the previous message, using the same sequence number, |
| * had still not released; in such a case we just wait and retry. |
| * |
| * Return: 0 on Success |
| */ |
| static int scmi_xfer_raw_get_init(struct scmi_raw_mode_info *raw, void *buf, |
| size_t len, struct scmi_xfer **p) |
| { |
| u32 msg_hdr; |
| size_t tx_size; |
| struct scmi_xfer *xfer; |
| int ret, retry = SCMI_XFER_RAW_MAX_RETRIES; |
| struct device *dev = raw->handle->dev; |
| |
| if (!buf || len < sizeof(u32)) |
| return -EINVAL; |
| |
| tx_size = len - sizeof(u32); |
| /* Ensure we have sane transfer sizes */ |
| if (tx_size > raw->desc->max_msg_size) |
| return -ERANGE; |
| |
| xfer = scmi_xfer_raw_get(raw->handle); |
| if (IS_ERR(xfer)) { |
| dev_warn(dev, "RAW - Cannot get a free RAW xfer !\n"); |
| return PTR_ERR(xfer); |
| } |
| |
| /* Build xfer from the provided SCMI bare LE message */ |
| msg_hdr = le32_to_cpu(*((__le32 *)buf)); |
| unpack_scmi_header(msg_hdr, &xfer->hdr); |
| xfer->hdr.seq = (u16)MSG_XTRACT_TOKEN(msg_hdr); |
| /* Polling not supported */ |
| xfer->hdr.poll_completion = false; |
| xfer->hdr.status = SCMI_SUCCESS; |
| xfer->tx.len = tx_size; |
| xfer->rx.len = raw->desc->max_msg_size; |
| /* Clear the whole TX buffer */ |
| memset(xfer->tx.buf, 0x00, raw->desc->max_msg_size); |
| if (xfer->tx.len) |
| memcpy(xfer->tx.buf, (u8 *)buf + sizeof(msg_hdr), xfer->tx.len); |
| *p = xfer; |
| |
| /* |
| * In flight registration can temporarily fail in case of Raw messages |
| * if the user injects messages without using monotonically increasing |
| * sequence numbers since, in Raw mode, the xfer (and the token) is |
| * finally released later by a deferred worker. Just retry for a while. |
| */ |
| do { |
| ret = scmi_xfer_raw_inflight_register(raw->handle, xfer); |
| if (ret) { |
| dev_dbg(dev, |
| "...retrying[%d] inflight registration\n", |
| retry); |
| msleep(raw->desc->max_rx_timeout_ms / |
| SCMI_XFER_RAW_MAX_RETRIES); |
| } |
| } while (ret && --retry); |
| |
| if (ret) { |
| dev_warn(dev, |
| "RAW - Could NOT register xfer %d in-flight HDR:0x%08X\n", |
| xfer->hdr.seq, msg_hdr); |
| scmi_xfer_raw_put(raw->handle, xfer); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * scmi_do_xfer_raw_start - An helper to send a valid raw xfer |
| * |
| * @raw: A reference to the Raw instance. |
| * @xfer: The xfer to send |
| * @chan_id: The channel ID to use, if zero the channels is automatically |
| * selected based on the protocol used. |
| * @async: A flag stating if an asynchronous command is required. |
| * |
| * This function send a previously built raw xfer using an appropriate channel |
| * and queues the related waiting work. |
| * |
| * Note that we need to know explicitly if the required command is meant to be |
| * asynchronous in kind since we have to properly setup the waiter. |
| * (and deducing this from the payload is weak and do not scale given there is |
| * NOT a common header-flag stating if the command is asynchronous or not) |
| * |
| * Return: 0 on Success |
| */ |
| static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw, |
| struct scmi_xfer *xfer, u8 chan_id, |
| bool async) |
| { |
| int ret; |
| struct scmi_chan_info *cinfo; |
| struct scmi_xfer_raw_waiter *rw; |
| struct device *dev = raw->handle->dev; |
| |
| if (!chan_id) |
| chan_id = xfer->hdr.protocol_id; |
| else |
| xfer->flags |= SCMI_XFER_FLAG_CHAN_SET; |
| |
| cinfo = scmi_xfer_raw_channel_get(raw->handle, chan_id); |
| if (IS_ERR(cinfo)) |
| return PTR_ERR(cinfo); |
| |
| rw = scmi_xfer_raw_waiter_get(raw, xfer, cinfo, async); |
| if (!rw) { |
| dev_warn(dev, "RAW - Cannot get a free waiter !\n"); |
| return -ENOMEM; |
| } |
| |
| /* True ONLY if also supported by transport. */ |
| if (is_polling_enabled(cinfo, raw->desc)) |
| xfer->hdr.poll_completion = true; |
| |
| reinit_completion(&xfer->done); |
| /* Make sure xfer state update is visible before sending */ |
| smp_store_mb(xfer->state, SCMI_XFER_SENT_OK); |
| |
| trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id, |
| xfer->hdr.protocol_id, xfer->hdr.seq, |
| xfer->hdr.poll_completion); |
| |
| ret = raw->desc->ops->send_message(rw->cinfo, xfer); |
| if (ret) { |
| dev_err(dev, "Failed to send RAW message %d\n", ret); |
| scmi_xfer_raw_waiter_put(raw, rw); |
| return ret; |
| } |
| |
| trace_scmi_msg_dump(raw->id, cinfo->id, xfer->hdr.protocol_id, |
| xfer->hdr.id, "cmnd", xfer->hdr.seq, |
| xfer->hdr.status, |
| xfer->tx.buf, xfer->tx.len); |
| |
| scmi_xfer_raw_waiter_enqueue(raw, rw); |
| |
| return ret; |
| } |
| |
| /** |
| * scmi_raw_message_send - An helper to build and send an SCMI command using |
| * the provided SCMI bare message buffer |
| * |
| * @raw: A reference to the Raw instance. |
| * @buf: A buffer containing the whole SCMI message to send (including the |
| * header) in little-endian binary format. |
| * @len: Length of the message in @buf. |
| * @chan_id: The channel ID to use. |
| * @async: A flag stating if an asynchronous command is required. |
| * |
| * Return: 0 on Success |
| */ |
| static int scmi_raw_message_send(struct scmi_raw_mode_info *raw, |
| void *buf, size_t len, u8 chan_id, bool async) |
| { |
| int ret; |
| struct scmi_xfer *xfer; |
| |
| ret = scmi_xfer_raw_get_init(raw, buf, len, &xfer); |
| if (ret) |
| return ret; |
| |
| ret = scmi_do_xfer_raw_start(raw, xfer, chan_id, async); |
| if (ret) |
| scmi_xfer_raw_put(raw->handle, xfer); |
| |
| return ret; |
| } |
| |
| static struct scmi_raw_buffer * |
| scmi_raw_message_dequeue(struct scmi_raw_queue *q, bool o_nonblock) |
| { |
| unsigned long flags; |
| struct scmi_raw_buffer *rb; |
| |
| spin_lock_irqsave(&q->msg_q_lock, flags); |
| while (list_empty(&q->msg_q)) { |
| spin_unlock_irqrestore(&q->msg_q_lock, flags); |
| |
| if (o_nonblock) |
| return ERR_PTR(-EAGAIN); |
| |
| if (wait_event_interruptible(q->wq, !list_empty(&q->msg_q))) |
| return ERR_PTR(-ERESTARTSYS); |
| |
| spin_lock_irqsave(&q->msg_q_lock, flags); |
| } |
| |
| rb = scmi_raw_buffer_dequeue_unlocked(q); |
| |
| spin_unlock_irqrestore(&q->msg_q_lock, flags); |
| |
| return rb; |
| } |
| |
| /** |
| * scmi_raw_message_receive - An helper to dequeue and report the next |
| * available enqueued raw message payload that has been collected. |
| * |
| * @raw: A reference to the Raw instance. |
| * @buf: A buffer to get hold of the whole SCMI message received and represented |
| * in little-endian binary format. |
| * @len: Length of @buf. |
| * @size: The effective size of the message copied into @buf |
| * @idx: The index of the queue to pick the next queued message from. |
| * @chan_id: The channel ID to use. |
| * @o_nonblock: A flag to request a non-blocking message dequeue. |
| * |
| * Return: 0 on Success |
| */ |
| static int scmi_raw_message_receive(struct scmi_raw_mode_info *raw, |
| void *buf, size_t len, size_t *size, |
| unsigned int idx, unsigned int chan_id, |
| bool o_nonblock) |
| { |
| int ret = 0; |
| struct scmi_raw_buffer *rb; |
| struct scmi_raw_queue *q; |
| |
| q = scmi_raw_queue_select(raw, idx, chan_id); |
| if (!q) |
| return -ENODEV; |
| |
| rb = scmi_raw_message_dequeue(q, o_nonblock); |
| if (IS_ERR(rb)) { |
| dev_dbg(raw->handle->dev, "RAW - No message available!\n"); |
| return PTR_ERR(rb); |
| } |
| |
| if (rb->msg.len <= len) { |
| memcpy(buf, rb->msg.buf, rb->msg.len); |
| *size = rb->msg.len; |
| } else { |
| ret = -ENOSPC; |
| } |
| |
| scmi_raw_buffer_put(q, rb); |
| |
| return ret; |
| } |
| |
| /* SCMI Raw debugfs helpers */ |
| |
| static ssize_t scmi_dbg_raw_mode_common_read(struct file *filp, |
| char __user *buf, |
| size_t count, loff_t *ppos, |
| unsigned int idx) |
| { |
| ssize_t cnt; |
| struct scmi_dbg_raw_data *rd = filp->private_data; |
| |
| if (!rd->rx_size) { |
| int ret; |
| |
| ret = scmi_raw_message_receive(rd->raw, rd->rx.buf, rd->rx.len, |
| &rd->rx_size, idx, rd->chan_id, |
| filp->f_flags & O_NONBLOCK); |
| if (ret) { |
| rd->rx_size = 0; |
| return ret; |
| } |
| |
| /* Reset any previous filepos change, including writes */ |
| *ppos = 0; |
| } else if (*ppos == rd->rx_size) { |
| /* Return EOF once all the message has been read-out */ |
| rd->rx_size = 0; |
| return 0; |
| } |
| |
| cnt = simple_read_from_buffer(buf, count, ppos, |
| rd->rx.buf, rd->rx_size); |
| |
| return cnt; |
| } |
| |
| static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp, |
| const char __user *buf, |
| size_t count, loff_t *ppos, |
| bool async) |
| { |
| int ret; |
| struct scmi_dbg_raw_data *rd = filp->private_data; |
| |
| if (count > rd->tx.len - rd->tx_size) |
| return -ENOSPC; |
| |
| /* On first write attempt @count carries the total full message size. */ |
| if (!rd->tx_size) |
| rd->tx_req_size = count; |
| |
| /* |
| * Gather a full message, possibly across multiple interrupted wrrtes, |
| * before sending it with a single RAW xfer. |
| */ |
| if (rd->tx_size < rd->tx_req_size) { |
| ssize_t cnt; |
| |
| cnt = simple_write_to_buffer(rd->tx.buf, rd->tx.len, ppos, |
| buf, count); |
| if (cnt < 0) |
| return cnt; |
| |
| rd->tx_size += cnt; |
| if (cnt < count) |
| return cnt; |
| } |
| |
| ret = scmi_raw_message_send(rd->raw, rd->tx.buf, rd->tx_size, |
| rd->chan_id, async); |
| |
| /* Reset ppos for next message ... */ |
| rd->tx_size = 0; |
| *ppos = 0; |
| |
| return ret ?: count; |
| } |
| |
| static __poll_t scmi_test_dbg_raw_common_poll(struct file *filp, |
| struct poll_table_struct *wait, |
| unsigned int idx) |
| { |
| unsigned long flags; |
| struct scmi_dbg_raw_data *rd = filp->private_data; |
| struct scmi_raw_queue *q; |
| __poll_t mask = 0; |
| |
| q = scmi_raw_queue_select(rd->raw, idx, rd->chan_id); |
| if (!q) |
| return mask; |
| |
| poll_wait(filp, &q->wq, wait); |
| |
| spin_lock_irqsave(&q->msg_q_lock, flags); |
| if (!list_empty(&q->msg_q)) |
| mask = EPOLLIN | EPOLLRDNORM; |
| spin_unlock_irqrestore(&q->msg_q_lock, flags); |
| |
| return mask; |
| } |
| |
| static ssize_t scmi_dbg_raw_mode_message_read(struct file *filp, |
| char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos, |
| SCMI_RAW_REPLY_QUEUE); |
| } |
| |
| static ssize_t scmi_dbg_raw_mode_message_write(struct file *filp, |
| const char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, false); |
| } |
| |
| static __poll_t scmi_dbg_raw_mode_message_poll(struct file *filp, |
| struct poll_table_struct *wait) |
| { |
| return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_REPLY_QUEUE); |
| } |
| |
| static int scmi_dbg_raw_mode_open(struct inode *inode, struct file *filp) |
| { |
| u8 id; |
| struct scmi_raw_mode_info *raw; |
| struct scmi_dbg_raw_data *rd; |
| const char *id_str = filp->f_path.dentry->d_parent->d_name.name; |
| |
| if (!inode->i_private) |
| return -ENODEV; |
| |
| raw = inode->i_private; |
| rd = kzalloc(sizeof(*rd), GFP_KERNEL); |
| if (!rd) |
| return -ENOMEM; |
| |
| rd->rx.len = raw->desc->max_msg_size + sizeof(u32); |
| rd->rx.buf = kzalloc(rd->rx.len, GFP_KERNEL); |
| if (!rd->rx.buf) { |
| kfree(rd); |
| return -ENOMEM; |
| } |
| |
| rd->tx.len = raw->desc->max_msg_size + sizeof(u32); |
| rd->tx.buf = kzalloc(rd->tx.len, GFP_KERNEL); |
| if (!rd->tx.buf) { |
| kfree(rd->rx.buf); |
| kfree(rd); |
| return -ENOMEM; |
| } |
| |
| /* Grab channel ID from debugfs entry naming if any */ |
| if (!kstrtou8(id_str, 16, &id)) |
| rd->chan_id = id; |
| |
| rd->raw = raw; |
| filp->private_data = rd; |
| |
| return 0; |
| } |
| |
| static int scmi_dbg_raw_mode_release(struct inode *inode, struct file *filp) |
| { |
| struct scmi_dbg_raw_data *rd = filp->private_data; |
| |
| kfree(rd->rx.buf); |
| kfree(rd->tx.buf); |
| kfree(rd); |
| |
| return 0; |
| } |
| |
| static ssize_t scmi_dbg_raw_mode_reset_write(struct file *filp, |
| const char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| struct scmi_dbg_raw_data *rd = filp->private_data; |
| |
| scmi_xfer_raw_reset(rd->raw); |
| |
| return count; |
| } |
| |
| static const struct file_operations scmi_dbg_raw_mode_reset_fops = { |
| .open = scmi_dbg_raw_mode_open, |
| .release = scmi_dbg_raw_mode_release, |
| .write = scmi_dbg_raw_mode_reset_write, |
| .owner = THIS_MODULE, |
| }; |
| |
| static const struct file_operations scmi_dbg_raw_mode_message_fops = { |
| .open = scmi_dbg_raw_mode_open, |
| .release = scmi_dbg_raw_mode_release, |
| .read = scmi_dbg_raw_mode_message_read, |
| .write = scmi_dbg_raw_mode_message_write, |
| .poll = scmi_dbg_raw_mode_message_poll, |
| .owner = THIS_MODULE, |
| }; |
| |
| static ssize_t scmi_dbg_raw_mode_message_async_write(struct file *filp, |
| const char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, true); |
| } |
| |
| static const struct file_operations scmi_dbg_raw_mode_message_async_fops = { |
| .open = scmi_dbg_raw_mode_open, |
| .release = scmi_dbg_raw_mode_release, |
| .read = scmi_dbg_raw_mode_message_read, |
| .write = scmi_dbg_raw_mode_message_async_write, |
| .poll = scmi_dbg_raw_mode_message_poll, |
| .owner = THIS_MODULE, |
| }; |
| |
| static ssize_t scmi_test_dbg_raw_mode_notif_read(struct file *filp, |
| char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos, |
| SCMI_RAW_NOTIF_QUEUE); |
| } |
| |
| static __poll_t |
| scmi_test_dbg_raw_mode_notif_poll(struct file *filp, |
| struct poll_table_struct *wait) |
| { |
| return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_NOTIF_QUEUE); |
| } |
| |
| static const struct file_operations scmi_dbg_raw_mode_notification_fops = { |
| .open = scmi_dbg_raw_mode_open, |
| .release = scmi_dbg_raw_mode_release, |
| .read = scmi_test_dbg_raw_mode_notif_read, |
| .poll = scmi_test_dbg_raw_mode_notif_poll, |
| .owner = THIS_MODULE, |
| }; |
| |
| static ssize_t scmi_test_dbg_raw_mode_errors_read(struct file *filp, |
| char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos, |
| SCMI_RAW_ERRS_QUEUE); |
| } |
| |
| static __poll_t |
| scmi_test_dbg_raw_mode_errors_poll(struct file *filp, |
| struct poll_table_struct *wait) |
| { |
| return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_ERRS_QUEUE); |
| } |
| |
| static const struct file_operations scmi_dbg_raw_mode_errors_fops = { |
| .open = scmi_dbg_raw_mode_open, |
| .release = scmi_dbg_raw_mode_release, |
| .read = scmi_test_dbg_raw_mode_errors_read, |
| .poll = scmi_test_dbg_raw_mode_errors_poll, |
| .owner = THIS_MODULE, |
| }; |
| |
| static struct scmi_raw_queue * |
| scmi_raw_queue_init(struct scmi_raw_mode_info *raw) |
| { |
| int i; |
| struct scmi_raw_buffer *rb; |
| struct device *dev = raw->handle->dev; |
| struct scmi_raw_queue *q; |
| |
| q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL); |
| if (!q) |
| return ERR_PTR(-ENOMEM); |
| |
| rb = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rb), GFP_KERNEL); |
| if (!rb) |
| return ERR_PTR(-ENOMEM); |
| |
| spin_lock_init(&q->free_bufs_lock); |
| INIT_LIST_HEAD(&q->free_bufs); |
| for (i = 0; i < raw->tx_max_msg; i++, rb++) { |
| rb->max_len = raw->desc->max_msg_size + sizeof(u32); |
| rb->msg.buf = devm_kzalloc(dev, rb->max_len, GFP_KERNEL); |
| if (!rb->msg.buf) |
| return ERR_PTR(-ENOMEM); |
| scmi_raw_buffer_put(q, rb); |
| } |
| |
| spin_lock_init(&q->msg_q_lock); |
| INIT_LIST_HEAD(&q->msg_q); |
| init_waitqueue_head(&q->wq); |
| |
| return q; |
| } |
| |
| static int scmi_xfer_raw_worker_init(struct scmi_raw_mode_info *raw) |
| { |
| int i; |
| struct scmi_xfer_raw_waiter *rw; |
| struct device *dev = raw->handle->dev; |
| |
| rw = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rw), GFP_KERNEL); |
| if (!rw) |
| return -ENOMEM; |
| |
| raw->wait_wq = alloc_workqueue("scmi-raw-wait-wq-%d", |
| WQ_UNBOUND | WQ_FREEZABLE | |
| WQ_HIGHPRI | WQ_SYSFS, 0, raw->id); |
| if (!raw->wait_wq) |
| return -ENOMEM; |
| |
| mutex_init(&raw->free_mtx); |
| INIT_LIST_HEAD(&raw->free_waiters); |
| mutex_init(&raw->active_mtx); |
| INIT_LIST_HEAD(&raw->active_waiters); |
| |
| for (i = 0; i < raw->tx_max_msg; i++, rw++) { |
| init_completion(&rw->async_response); |
| scmi_xfer_raw_waiter_put(raw, rw); |
| } |
| INIT_WORK(&raw->waiters_work, scmi_xfer_raw_worker); |
| |
| return 0; |
| } |
| |
| static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw, |
| u8 *channels, int num_chans) |
| { |
| int ret, idx; |
| void *gid; |
| struct device *dev = raw->handle->dev; |
| |
| gid = devres_open_group(dev, NULL, GFP_KERNEL); |
| if (!gid) |
| return -ENOMEM; |
| |
| for (idx = 0; idx < SCMI_RAW_MAX_QUEUE; idx++) { |
| raw->q[idx] = scmi_raw_queue_init(raw); |
| if (IS_ERR(raw->q[idx])) { |
| ret = PTR_ERR(raw->q[idx]); |
| goto err; |
| } |
| } |
| |
| xa_init(&raw->chans_q); |
| if (num_chans > 1) { |
| int i; |
| |
| for (i = 0; i < num_chans; i++) { |
| struct scmi_raw_queue *q; |
| |
| q = scmi_raw_queue_init(raw); |
| if (IS_ERR(q)) { |
| ret = PTR_ERR(q); |
| goto err_xa; |
| } |
| |
| ret = xa_insert(&raw->chans_q, channels[i], q, |
| GFP_KERNEL); |
| if (ret) { |
| dev_err(dev, |
| "Fail to allocate Raw queue 0x%02X\n", |
| channels[i]); |
| goto err_xa; |
| } |
| } |
| } |
| |
| ret = scmi_xfer_raw_worker_init(raw); |
| if (ret) |
| goto err_xa; |
| |
| devres_close_group(dev, gid); |
| raw->gid = gid; |
| |
| return 0; |
| |
| err_xa: |
| xa_destroy(&raw->chans_q); |
| err: |
| devres_release_group(dev, gid); |
| return ret; |
| } |
| |
| /** |
| * scmi_raw_mode_init - Function to initialize the SCMI Raw stack |
| * |
| * @handle: Pointer to SCMI entity handle |
| * @top_dentry: A reference to the top Raw debugfs dentry |
| * @instance_id: The ID of the underlying SCMI platform instance represented by |
| * this Raw instance |
| * @channels: The list of the existing channels |
| * @num_chans: The number of entries in @channels |
| * @desc: Reference to the transport operations |
| * @tx_max_msg: Max number of in-flight messages allowed by the transport |
| * |
| * This function prepare the SCMI Raw stack and creates the debugfs API. |
| * |
| * Return: An opaque handle to the Raw instance on Success, an ERR_PTR otherwise |
| */ |
| void *scmi_raw_mode_init(const struct scmi_handle *handle, |
| struct dentry *top_dentry, int instance_id, |
| u8 *channels, int num_chans, |
| const struct scmi_desc *desc, int tx_max_msg) |
| { |
| int ret; |
| struct scmi_raw_mode_info *raw; |
| struct device *dev; |
| |
| if (!handle || !desc) |
| return ERR_PTR(-EINVAL); |
| |
| dev = handle->dev; |
| raw = devm_kzalloc(dev, sizeof(*raw), GFP_KERNEL); |
| if (!raw) |
| return ERR_PTR(-ENOMEM); |
| |
| raw->handle = handle; |
| raw->desc = desc; |
| raw->tx_max_msg = tx_max_msg; |
| raw->id = instance_id; |
| |
| ret = scmi_raw_mode_setup(raw, channels, num_chans); |
| if (ret) { |
| devm_kfree(dev, raw); |
| return ERR_PTR(ret); |
| } |
| |
| raw->dentry = debugfs_create_dir("raw", top_dentry); |
| |
| debugfs_create_file("reset", 0200, raw->dentry, raw, |
| &scmi_dbg_raw_mode_reset_fops); |
| |
| debugfs_create_file("message", 0600, raw->dentry, raw, |
| &scmi_dbg_raw_mode_message_fops); |
| |
| debugfs_create_file("message_async", 0600, raw->dentry, raw, |
| &scmi_dbg_raw_mode_message_async_fops); |
| |
| debugfs_create_file("notification", 0400, raw->dentry, raw, |
| &scmi_dbg_raw_mode_notification_fops); |
| |
| debugfs_create_file("errors", 0400, raw->dentry, raw, |
| &scmi_dbg_raw_mode_errors_fops); |
| |
| /* |
| * Expose per-channel entries if multiple channels available. |
| * Just ignore errors while setting up these interfaces since we |
| * have anyway already a working core Raw support. |
| */ |
| if (num_chans > 1) { |
| int i; |
| struct dentry *top_chans; |
| |
| top_chans = debugfs_create_dir("channels", raw->dentry); |
| |
| for (i = 0; i < num_chans; i++) { |
| char cdir[8]; |
| struct dentry *chd; |
| |
| snprintf(cdir, 8, "0x%02X", channels[i]); |
| chd = debugfs_create_dir(cdir, top_chans); |
| |
| debugfs_create_file("message", 0600, chd, raw, |
| &scmi_dbg_raw_mode_message_fops); |
| |
| debugfs_create_file("message_async", 0600, chd, raw, |
| &scmi_dbg_raw_mode_message_async_fops); |
| } |
| } |
| |
| dev_info(dev, "SCMI RAW Mode initialized for instance %d\n", raw->id); |
| |
| return raw; |
| } |
| |
| /** |
| * scmi_raw_mode_cleanup - Function to cleanup the SCMI Raw stack |
| * |
| * @r: An opaque handle to an initialized SCMI Raw instance |
| */ |
| void scmi_raw_mode_cleanup(void *r) |
| { |
| struct scmi_raw_mode_info *raw = r; |
| |
| if (!raw) |
| return; |
| |
| debugfs_remove_recursive(raw->dentry); |
| |
| cancel_work_sync(&raw->waiters_work); |
| destroy_workqueue(raw->wait_wq); |
| xa_destroy(&raw->chans_q); |
| } |
| |
| static int scmi_xfer_raw_collect(void *msg, size_t *msg_len, |
| struct scmi_xfer *xfer) |
| { |
| __le32 *m; |
| size_t msg_size; |
| |
| if (!xfer || !msg || !msg_len) |
| return -EINVAL; |
| |
| /* Account for hdr ...*/ |
| msg_size = xfer->rx.len + sizeof(u32); |
| /* ... and status if needed */ |
| if (xfer->hdr.type != MSG_TYPE_NOTIFICATION) |
| msg_size += sizeof(u32); |
| |
| if (msg_size > *msg_len) |
| return -ENOSPC; |
| |
| m = msg; |
| *m = cpu_to_le32(pack_scmi_header(&xfer->hdr)); |
| if (xfer->hdr.type != MSG_TYPE_NOTIFICATION) |
| *++m = cpu_to_le32(xfer->hdr.status); |
| |
| memcpy(++m, xfer->rx.buf, xfer->rx.len); |
| |
| *msg_len = msg_size; |
| |
| return 0; |
| } |
| |
| /** |
| * scmi_raw_message_report - Helper to report back valid reponses/notifications |
| * to raw message requests. |
| * |
| * @r: An opaque reference to the raw instance configuration |
| * @xfer: The xfer containing the message to be reported |
| * @idx: The index of the queue. |
| * @chan_id: The channel ID to use. |
| * |
| * If Raw mode is enabled, this is called from the SCMI core on the regular RX |
| * path to save and enqueue the response/notification payload carried by this |
| * xfer into a dedicated scmi_raw_buffer for later consumption by the user. |
| * |
| * This way the caller can free the related xfer immediately afterwards and the |
| * user can read back the raw message payload at its own pace (if ever) without |
| * holding an xfer for too long. |
| */ |
| void scmi_raw_message_report(void *r, struct scmi_xfer *xfer, |
| unsigned int idx, unsigned int chan_id) |
| { |
| int ret; |
| unsigned long flags; |
| struct scmi_raw_buffer *rb; |
| struct device *dev; |
| struct scmi_raw_queue *q; |
| struct scmi_raw_mode_info *raw = r; |
| |
| if (!raw || (idx == SCMI_RAW_REPLY_QUEUE && !SCMI_XFER_IS_RAW(xfer))) |
| return; |
| |
| dev = raw->handle->dev; |
| q = scmi_raw_queue_select(raw, idx, |
| SCMI_XFER_IS_CHAN_SET(xfer) ? chan_id : 0); |
| if (!q) { |
| dev_warn(dev, |
| "RAW[%d] - NO queue for chan 0x%X. Dropping report.\n", |
| idx, chan_id); |
| return; |
| } |
| |
| /* |
| * Grab the msg_q_lock upfront to avoid a possible race between |
| * realizing the free list was empty and effectively picking the next |
| * buffer to use from the oldest one enqueued and still unread on this |
| * msg_q. |
| * |
| * Note that nowhere else these locks are taken together, so no risk of |
| * deadlocks du eto inversion. |
| */ |
| spin_lock_irqsave(&q->msg_q_lock, flags); |
| rb = scmi_raw_buffer_get(q); |
| if (!rb) { |
| /* |
| * Immediate and delayed replies to previously injected Raw |
| * commands MUST be read back from userspace to free the buffers: |
| * if this is not happening something is seriously broken and |
| * must be fixed at the application level: complain loudly. |
| */ |
| if (idx == SCMI_RAW_REPLY_QUEUE) { |
| spin_unlock_irqrestore(&q->msg_q_lock, flags); |
| dev_warn(dev, |
| "RAW[%d] - Buffers exhausted. Dropping report.\n", |
| idx); |
| return; |
| } |
| |
| /* |
| * Notifications and errors queues are instead handled in a |
| * circular manner: unread old buffers are just overwritten by |
| * newer ones. |
| * |
| * The main reason for this is that notifications originated |
| * by Raw requests cannot be distinguished from normal ones, so |
| * your Raw buffers queues risk to be flooded and depleted by |
| * notifications if you left it mistakenly enabled or when in |
| * coexistence mode. |
| */ |
| rb = scmi_raw_buffer_dequeue_unlocked(q); |
| if (WARN_ON(!rb)) { |
| spin_unlock_irqrestore(&q->msg_q_lock, flags); |
| return; |
| } |
| |
| /* Reset to full buffer length */ |
| rb->msg.len = rb->max_len; |
| |
| dev_warn_once(dev, |
| "RAW[%d] - Buffers exhausted. Re-using oldest.\n", |
| idx); |
| } |
| spin_unlock_irqrestore(&q->msg_q_lock, flags); |
| |
| ret = scmi_xfer_raw_collect(rb->msg.buf, &rb->msg.len, xfer); |
| if (ret) { |
| dev_warn(dev, "RAW - Cannot collect xfer into buffer !\n"); |
| scmi_raw_buffer_put(q, rb); |
| return; |
| } |
| |
| scmi_raw_buffer_enqueue(q, rb); |
| } |
| |
| static void scmi_xfer_raw_fill(struct scmi_raw_mode_info *raw, |
| struct scmi_chan_info *cinfo, |
| struct scmi_xfer *xfer, u32 msg_hdr) |
| { |
| /* Unpack received HDR as it is */ |
| unpack_scmi_header(msg_hdr, &xfer->hdr); |
| xfer->hdr.seq = MSG_XTRACT_TOKEN(msg_hdr); |
| |
| memset(xfer->rx.buf, 0x00, xfer->rx.len); |
| |
| raw->desc->ops->fetch_response(cinfo, xfer); |
| } |
| |
| /** |
| * scmi_raw_error_report - Helper to report back timed-out or generally |
| * unexpected replies. |
| * |
| * @r: An opaque reference to the raw instance configuration |
| * @cinfo: A reference to the channel to use to retrieve the broken xfer |
| * @msg_hdr: The SCMI message header of the message to fetch and report |
| * @priv: Any private data related to the xfer. |
| * |
| * If Raw mode is enabled, this is called from the SCMI core on the RX path in |
| * case of errors to save and enqueue the bad message payload carried by the |
| * message that has just been received. |
| * |
| * Note that we have to manually fetch any available payload into a temporary |
| * xfer to be able to save and enqueue the message, since the regular RX error |
| * path which had called this would have not fetched the message payload having |
| * classified it as an error. |
| */ |
| void scmi_raw_error_report(void *r, struct scmi_chan_info *cinfo, |
| u32 msg_hdr, void *priv) |
| { |
| struct scmi_xfer xfer; |
| struct scmi_raw_mode_info *raw = r; |
| |
| if (!raw) |
| return; |
| |
| xfer.rx.len = raw->desc->max_msg_size; |
| xfer.rx.buf = kzalloc(xfer.rx.len, GFP_ATOMIC); |
| if (!xfer.rx.buf) { |
| dev_info(raw->handle->dev, |
| "Cannot report Raw error for HDR:0x%X - ENOMEM\n", |
| msg_hdr); |
| return; |
| } |
| |
| /* Any transport-provided priv must be passed back down to transport */ |
| if (priv) |
| /* Ensure priv is visible */ |
| smp_store_mb(xfer.priv, priv); |
| |
| scmi_xfer_raw_fill(raw, cinfo, &xfer, msg_hdr); |
| scmi_raw_message_report(raw, &xfer, SCMI_RAW_ERRS_QUEUE, 0); |
| |
| kfree(xfer.rx.buf); |
| } |