Path: blob/master/drivers/firmware/arm_scmi/transports/mailbox.c
29282 views
// SPDX-License-Identifier: GPL-2.01/*2* System Control and Management Interface (SCMI) Message Mailbox Transport3* driver.4*5* Copyright (C) 2019-2024 ARM Ltd.6*/78#include <linux/err.h>9#include <linux/device.h>10#include <linux/mailbox_client.h>11#include <linux/of.h>12#include <linux/of_address.h>13#include <linux/platform_device.h>14#include <linux/slab.h>1516#include "../common.h"1718/**19* struct scmi_mailbox - Structure representing a SCMI mailbox transport20*21* @cl: Mailbox Client22* @chan: Transmit/Receive mailbox uni/bi-directional channel23* @chan_receiver: Optional Receiver mailbox unidirectional channel24* @chan_platform_receiver: Optional Platform Receiver mailbox unidirectional channel25* @cinfo: SCMI channel info26* @shmem: Transmit/Receive shared memory area27* @chan_lock: Lock that prevents multiple xfers from being queued28* @io_ops: Transport specific I/O operations29*/30struct scmi_mailbox {31struct mbox_client cl;32struct mbox_chan *chan;33struct mbox_chan *chan_receiver;34struct mbox_chan *chan_platform_receiver;35struct scmi_chan_info *cinfo;36struct scmi_shared_mem __iomem *shmem;37struct mutex chan_lock;38struct scmi_shmem_io_ops *io_ops;39};4041#define client_to_scmi_mailbox(c) container_of(c, struct scmi_mailbox, cl)4243static struct scmi_transport_core_operations *core;4445static void tx_prepare(struct mbox_client *cl, void *m)46{47struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);4849core->shmem->tx_prepare(smbox->shmem, m, smbox->cinfo,50smbox->io_ops->toio);51}5253static void rx_callback(struct mbox_client *cl, void *m)54{55struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);5657/*58* An A2P IRQ is NOT valid when received while the platform still has59* the ownership of the channel, because the platform at first releases60* the SMT channel and then sends the completion interrupt.61*62* This addresses a possible race condition in which a spurious IRQ from63* a previous timed-out reply which arrived late could be wrongly64* associated with the next pending transaction.65*/66if (cl->knows_txdone &&67!core->shmem->channel_free(smbox->shmem)) {68dev_warn(smbox->cinfo->dev, "Ignoring spurious A2P IRQ !\n");69core->bad_message_trace(smbox->cinfo,70core->shmem->read_header(smbox->shmem),71MSG_MBOX_SPURIOUS);72return;73}7475core->rx_callback(smbox->cinfo,76core->shmem->read_header(smbox->shmem), NULL);77}7879static bool mailbox_chan_available(struct device_node *of_node, int idx)80{81int num_mb;8283/*84* Just check if bidirrectional channels are involved, and check the85* index accordingly; proper full validation will be made later86* in mailbox_chan_setup().87*/88num_mb = of_count_phandle_with_args(of_node, "mboxes", "#mbox-cells");89if (num_mb == 3 && idx == 1)90idx = 2;9192return !of_parse_phandle_with_args(of_node, "mboxes",93"#mbox-cells", idx, NULL);94}9596/**97* mailbox_chan_validate - Validate transport configuration and map channels98*99* @cdev: Reference to the underlying transport device carrying the100* of_node descriptor to analyze.101* @a2p_rx_chan: A reference to an optional unidirectional channel to use102* for replies on the a2p channel. Set as zero if not present.103* @p2a_chan: A reference to the optional p2a channel.104* Set as zero if not present.105* @p2a_rx_chan: A reference to the optional p2a completion channel.106* Set as zero if not present.107*108* At first, validate the transport configuration as described in terms of109* 'mboxes' and 'shmem', then determin which mailbox channel indexes are110* appropriate to be use in the current configuration.111*112* Return: 0 on Success or error113*/114static int mailbox_chan_validate(struct device *cdev, int *a2p_rx_chan,115int *p2a_chan, int *p2a_rx_chan)116{117int num_mb, num_sh, ret = 0;118struct device_node *np = cdev->of_node;119120num_mb = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");121num_sh = of_count_phandle_with_args(np, "shmem", NULL);122dev_dbg(cdev, "Found %d mboxes and %d shmems !\n", num_mb, num_sh);123124/* Bail out if mboxes and shmem descriptors are inconsistent */125if (num_mb <= 0 || num_sh <= 0 || num_sh > 2 || num_mb > 4 ||126(num_mb == 1 && num_sh != 1) || (num_mb == 3 && num_sh != 2) ||127(num_mb == 4 && num_sh != 2)) {128dev_warn(cdev,129"Invalid channel descriptor for '%pOF' - mbs:%d shm:%d\n",130np, num_mb, num_sh);131return -EINVAL;132}133134/* Bail out if provided shmem descriptors do not refer distinct areas */135if (num_sh > 1) {136struct device_node *np_tx __free(device_node) =137of_parse_phandle(np, "shmem", 0);138struct device_node *np_rx __free(device_node) =139of_parse_phandle(np, "shmem", 1);140141if (!np_tx || !np_rx || np_tx == np_rx) {142dev_warn(cdev, "Invalid shmem descriptor for '%pOF'\n", np);143ret = -EINVAL;144}145}146147/* Calculate channels IDs to use depending on mboxes/shmem layout */148if (!ret) {149switch (num_mb) {150case 1:151*a2p_rx_chan = 0;152*p2a_chan = 0;153*p2a_rx_chan = 0;154break;155case 2:156if (num_sh == 2) {157*a2p_rx_chan = 0;158*p2a_chan = 1;159} else {160*a2p_rx_chan = 1;161*p2a_chan = 0;162}163*p2a_rx_chan = 0;164break;165case 3:166*a2p_rx_chan = 1;167*p2a_chan = 2;168*p2a_rx_chan = 0;169break;170case 4:171*a2p_rx_chan = 1;172*p2a_chan = 2;173*p2a_rx_chan = 3;174break;175}176}177178return ret;179}180181static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,182bool tx)183{184const char *desc = tx ? "Tx" : "Rx";185struct device *cdev = cinfo->dev;186struct scmi_mailbox *smbox;187int ret, a2p_rx_chan, p2a_chan, p2a_rx_chan;188struct mbox_client *cl;189190ret = mailbox_chan_validate(cdev, &a2p_rx_chan, &p2a_chan, &p2a_rx_chan);191if (ret)192return ret;193194if (!tx && !p2a_chan)195return -ENODEV;196197smbox = devm_kzalloc(dev, sizeof(*smbox), GFP_KERNEL);198if (!smbox)199return -ENOMEM;200201smbox->shmem = core->shmem->setup_iomap(cinfo, dev, tx, NULL,202&smbox->io_ops);203if (IS_ERR(smbox->shmem))204return PTR_ERR(smbox->shmem);205206cl = &smbox->cl;207cl->dev = cdev;208cl->tx_prepare = tx ? tx_prepare : NULL;209cl->rx_callback = rx_callback;210cl->tx_block = false;211cl->knows_txdone = tx;212213smbox->chan = mbox_request_channel(cl, tx ? 0 : p2a_chan);214if (IS_ERR(smbox->chan)) {215ret = PTR_ERR(smbox->chan);216if (ret != -EPROBE_DEFER)217dev_err(cdev,218"failed to request SCMI %s mailbox\n", desc);219return ret;220}221222/* Additional unidirectional channel for TX if needed */223if (tx && a2p_rx_chan) {224smbox->chan_receiver = mbox_request_channel(cl, a2p_rx_chan);225if (IS_ERR(smbox->chan_receiver)) {226ret = PTR_ERR(smbox->chan_receiver);227if (ret != -EPROBE_DEFER)228dev_err(cdev, "failed to request SCMI Tx Receiver mailbox\n");229return ret;230}231}232233if (!tx && p2a_rx_chan) {234smbox->chan_platform_receiver = mbox_request_channel(cl, p2a_rx_chan);235if (IS_ERR(smbox->chan_platform_receiver)) {236ret = PTR_ERR(smbox->chan_platform_receiver);237if (ret != -EPROBE_DEFER)238dev_err(cdev, "failed to request SCMI P2A Receiver mailbox\n");239return ret;240}241}242243cinfo->transport_info = smbox;244smbox->cinfo = cinfo;245mutex_init(&smbox->chan_lock);246247return 0;248}249250static int mailbox_chan_free(int id, void *p, void *data)251{252struct scmi_chan_info *cinfo = p;253struct scmi_mailbox *smbox = cinfo->transport_info;254255if (smbox && !IS_ERR(smbox->chan)) {256mbox_free_channel(smbox->chan);257mbox_free_channel(smbox->chan_receiver);258mbox_free_channel(smbox->chan_platform_receiver);259cinfo->transport_info = NULL;260smbox->chan = NULL;261smbox->chan_receiver = NULL;262smbox->chan_platform_receiver = NULL;263smbox->cinfo = NULL;264}265266return 0;267}268269static int mailbox_send_message(struct scmi_chan_info *cinfo,270struct scmi_xfer *xfer)271{272struct scmi_mailbox *smbox = cinfo->transport_info;273int ret;274275/*276* The mailbox layer has its own queue. However the mailbox queue277* confuses the per message SCMI timeouts since the clock starts when278* the message is submitted into the mailbox queue. So when multiple279* messages are queued up the clock starts on all messages instead of280* only the one inflight.281*/282mutex_lock(&smbox->chan_lock);283284ret = mbox_send_message(smbox->chan, xfer);285/* mbox_send_message returns non-negative value on success */286if (ret < 0) {287mutex_unlock(&smbox->chan_lock);288return ret;289}290291return 0;292}293294static void mailbox_mark_txdone(struct scmi_chan_info *cinfo, int ret,295struct scmi_xfer *__unused)296{297struct scmi_mailbox *smbox = cinfo->transport_info;298299mbox_client_txdone(smbox->chan, ret);300301/* Release channel */302mutex_unlock(&smbox->chan_lock);303}304305static void mailbox_fetch_response(struct scmi_chan_info *cinfo,306struct scmi_xfer *xfer)307{308struct scmi_mailbox *smbox = cinfo->transport_info;309310core->shmem->fetch_response(smbox->shmem, xfer, smbox->io_ops->fromio);311}312313static void mailbox_fetch_notification(struct scmi_chan_info *cinfo,314size_t max_len, struct scmi_xfer *xfer)315{316struct scmi_mailbox *smbox = cinfo->transport_info;317318core->shmem->fetch_notification(smbox->shmem, max_len, xfer,319smbox->io_ops->fromio);320}321322static void mailbox_clear_channel(struct scmi_chan_info *cinfo)323{324struct scmi_mailbox *smbox = cinfo->transport_info;325struct mbox_chan *intr_chan;326int ret;327328core->shmem->clear_channel(smbox->shmem);329330if (!core->shmem->channel_intr_enabled(smbox->shmem))331return;332333if (smbox->chan_platform_receiver)334intr_chan = smbox->chan_platform_receiver;335else if (smbox->chan)336intr_chan = smbox->chan;337else338return;339340ret = mbox_send_message(intr_chan, NULL);341/* mbox_send_message returns non-negative value on success, so reset */342if (ret > 0)343ret = 0;344345mbox_client_txdone(intr_chan, ret);346}347348static bool349mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)350{351struct scmi_mailbox *smbox = cinfo->transport_info;352353return core->shmem->poll_done(smbox->shmem, xfer);354}355356static const struct scmi_transport_ops scmi_mailbox_ops = {357.chan_available = mailbox_chan_available,358.chan_setup = mailbox_chan_setup,359.chan_free = mailbox_chan_free,360.send_message = mailbox_send_message,361.mark_txdone = mailbox_mark_txdone,362.fetch_response = mailbox_fetch_response,363.fetch_notification = mailbox_fetch_notification,364.clear_channel = mailbox_clear_channel,365.poll_done = mailbox_poll_done,366};367368static struct scmi_desc scmi_mailbox_desc = {369.ops = &scmi_mailbox_ops,370.max_rx_timeout_ms = 30, /* We may increase this if required */371.max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */372.max_msg_size = SCMI_SHMEM_MAX_PAYLOAD_SIZE,373};374375static const struct of_device_id scmi_of_match[] = {376{ .compatible = "arm,scmi" },377{ /* Sentinel */ },378};379MODULE_DEVICE_TABLE(of, scmi_of_match);380381DEFINE_SCMI_TRANSPORT_DRIVER(scmi_mailbox, scmi_mailbox_driver,382scmi_mailbox_desc, scmi_of_match, core);383module_platform_driver(scmi_mailbox_driver);384385MODULE_AUTHOR("Sudeep Holla <[email protected]>");386MODULE_DESCRIPTION("SCMI Mailbox Transport driver");387MODULE_LICENSE("GPL");388389390