mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
firmware: arm_scmi: Add support for unidirectional mailbox channels
Extend the SCMI transport layer to support mailbox controllers that expose communication channels that are unidirectional by nature. Signed-off-by: Cristian Marussi <cristian.marussi@arm.com> Link: https://lore.kernel.org/r/20230404115026.2828149-3-cristian.marussi@arm.com Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
This commit is contained in:
parent
92ac94f7e1
commit
9f68ff79ec
1 changed files with 84 additions and 11 deletions
|
@ -19,13 +19,15 @@
|
||||||
* struct scmi_mailbox - Structure representing a SCMI mailbox transport
|
* struct scmi_mailbox - Structure representing a SCMI mailbox transport
|
||||||
*
|
*
|
||||||
* @cl: Mailbox Client
|
* @cl: Mailbox Client
|
||||||
* @chan: Transmit/Receive mailbox channel
|
* @chan: Transmit/Receive mailbox uni/bi-directional channel
|
||||||
|
* @chan_receiver: Optional Receiver mailbox unidirectional channel
|
||||||
* @cinfo: SCMI channel info
|
* @cinfo: SCMI channel info
|
||||||
* @shmem: Transmit/Receive shared memory area
|
* @shmem: Transmit/Receive shared memory area
|
||||||
*/
|
*/
|
||||||
struct scmi_mailbox {
|
struct scmi_mailbox {
|
||||||
struct mbox_client cl;
|
struct mbox_client cl;
|
||||||
struct mbox_chan *chan;
|
struct mbox_chan *chan;
|
||||||
|
struct mbox_chan *chan_receiver;
|
||||||
struct scmi_chan_info *cinfo;
|
struct scmi_chan_info *cinfo;
|
||||||
struct scmi_shared_mem __iomem *shmem;
|
struct scmi_shared_mem __iomem *shmem;
|
||||||
};
|
};
|
||||||
|
@ -48,30 +50,62 @@ static void rx_callback(struct mbox_client *cl, void *m)
|
||||||
|
|
||||||
static bool mailbox_chan_available(struct device_node *of_node, int idx)
|
static bool mailbox_chan_available(struct device_node *of_node, int idx)
|
||||||
{
|
{
|
||||||
|
int num_mb;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Just check if bidirrectional channels are involved, and check the
|
||||||
|
* index accordingly; proper full validation will be made later
|
||||||
|
* in mailbox_chan_setup().
|
||||||
|
*/
|
||||||
|
num_mb = of_count_phandle_with_args(of_node, "mboxes", "#mbox-cells");
|
||||||
|
if (num_mb == 3 && idx == 1)
|
||||||
|
idx = 2;
|
||||||
|
|
||||||
return !of_parse_phandle_with_args(of_node, "mboxes",
|
return !of_parse_phandle_with_args(of_node, "mboxes",
|
||||||
"#mbox-cells", idx, NULL);
|
"#mbox-cells", idx, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mailbox_chan_validate(struct device *cdev)
|
/**
|
||||||
|
* mailbox_chan_validate - Validate transport configuration and map channels
|
||||||
|
*
|
||||||
|
* @cdev: Reference to the underlying transport device carrying the
|
||||||
|
* of_node descriptor to analyze.
|
||||||
|
* @a2p_rx_chan: A reference to an optional unidirectional channel to use
|
||||||
|
* for replies on the a2p channel. Set as zero if not present.
|
||||||
|
* @p2a_chan: A reference to the optional p2a channel.
|
||||||
|
* Set as zero if not present.
|
||||||
|
*
|
||||||
|
* At first, validate the transport configuration as described in terms of
|
||||||
|
* 'mboxes' and 'shmem', then determin which mailbox channel indexes are
|
||||||
|
* appropriate to be use in the current configuration.
|
||||||
|
*
|
||||||
|
* Return: 0 on Success or error
|
||||||
|
*/
|
||||||
|
static int mailbox_chan_validate(struct device *cdev,
|
||||||
|
int *a2p_rx_chan, int *p2a_chan)
|
||||||
{
|
{
|
||||||
int num_mb, num_sh, ret = 0;
|
int num_mb, num_sh, ret = 0;
|
||||||
struct device_node *np = cdev->of_node;
|
struct device_node *np = cdev->of_node;
|
||||||
|
|
||||||
num_mb = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");
|
num_mb = of_count_phandle_with_args(np, "mboxes", "#mbox-cells");
|
||||||
num_sh = of_count_phandle_with_args(np, "shmem", NULL);
|
num_sh = of_count_phandle_with_args(np, "shmem", NULL);
|
||||||
|
dev_dbg(cdev, "Found %d mboxes and %d shmems !\n", num_mb, num_sh);
|
||||||
|
|
||||||
/* Bail out if mboxes and shmem descriptors are inconsistent */
|
/* Bail out if mboxes and shmem descriptors are inconsistent */
|
||||||
if (num_mb <= 0 || num_sh > 2 || num_mb != num_sh) {
|
if (num_mb <= 0 || num_sh <= 0 || num_sh > 2 || num_mb > 3 ||
|
||||||
dev_warn(cdev, "Invalid channel descriptor for '%s'\n",
|
(num_mb == 1 && num_sh != 1) || (num_mb == 3 && num_sh != 2)) {
|
||||||
of_node_full_name(np));
|
dev_warn(cdev,
|
||||||
|
"Invalid channel descriptor for '%s' - mbs:%d shm:%d\n",
|
||||||
|
of_node_full_name(np), num_mb, num_sh);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bail out if provided shmem descriptors do not refer distinct areas */
|
||||||
if (num_sh > 1) {
|
if (num_sh > 1) {
|
||||||
struct device_node *np_tx, *np_rx;
|
struct device_node *np_tx, *np_rx;
|
||||||
|
|
||||||
np_tx = of_parse_phandle(np, "shmem", 0);
|
np_tx = of_parse_phandle(np, "shmem", 0);
|
||||||
np_rx = of_parse_phandle(np, "shmem", 1);
|
np_rx = of_parse_phandle(np, "shmem", 1);
|
||||||
/* SCMI Tx and Rx shared mem areas have to be distinct */
|
|
||||||
if (!np_tx || !np_rx || np_tx == np_rx) {
|
if (!np_tx || !np_rx || np_tx == np_rx) {
|
||||||
dev_warn(cdev, "Invalid shmem descriptor for '%s'\n",
|
dev_warn(cdev, "Invalid shmem descriptor for '%s'\n",
|
||||||
of_node_full_name(np));
|
of_node_full_name(np));
|
||||||
|
@ -82,6 +116,29 @@ static int mailbox_chan_validate(struct device *cdev)
|
||||||
of_node_put(np_rx);
|
of_node_put(np_rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate channels IDs to use depending on mboxes/shmem layout */
|
||||||
|
if (!ret) {
|
||||||
|
switch (num_mb) {
|
||||||
|
case 1:
|
||||||
|
*a2p_rx_chan = 0;
|
||||||
|
*p2a_chan = 0;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (num_sh == 2) {
|
||||||
|
*a2p_rx_chan = 0;
|
||||||
|
*p2a_chan = 1;
|
||||||
|
} else {
|
||||||
|
*a2p_rx_chan = 1;
|
||||||
|
*p2a_chan = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
*a2p_rx_chan = 1;
|
||||||
|
*p2a_chan = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,15 +149,18 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||||
struct device *cdev = cinfo->dev;
|
struct device *cdev = cinfo->dev;
|
||||||
struct scmi_mailbox *smbox;
|
struct scmi_mailbox *smbox;
|
||||||
struct device_node *shmem;
|
struct device_node *shmem;
|
||||||
int ret, idx = tx ? 0 : 1;
|
int ret, a2p_rx_chan, p2a_chan, idx = tx ? 0 : 1;
|
||||||
struct mbox_client *cl;
|
struct mbox_client *cl;
|
||||||
resource_size_t size;
|
resource_size_t size;
|
||||||
struct resource res;
|
struct resource res;
|
||||||
|
|
||||||
ret = mailbox_chan_validate(cdev);
|
ret = mailbox_chan_validate(cdev, &a2p_rx_chan, &p2a_chan);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
if (!tx && !p2a_chan)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
smbox = devm_kzalloc(dev, sizeof(*smbox), GFP_KERNEL);
|
smbox = devm_kzalloc(dev, sizeof(*smbox), GFP_KERNEL);
|
||||||
if (!smbox)
|
if (!smbox)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -130,15 +190,26 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
|
||||||
cl->tx_block = false;
|
cl->tx_block = false;
|
||||||
cl->knows_txdone = tx;
|
cl->knows_txdone = tx;
|
||||||
|
|
||||||
smbox->chan = mbox_request_channel(cl, tx ? 0 : 1);
|
smbox->chan = mbox_request_channel(cl, tx ? 0 : p2a_chan);
|
||||||
if (IS_ERR(smbox->chan)) {
|
if (IS_ERR(smbox->chan)) {
|
||||||
ret = PTR_ERR(smbox->chan);
|
ret = PTR_ERR(smbox->chan);
|
||||||
if (ret != -EPROBE_DEFER)
|
if (ret != -EPROBE_DEFER)
|
||||||
dev_err(cdev, "failed to request SCMI %s mailbox\n",
|
dev_err(cdev,
|
||||||
tx ? "Tx" : "Rx");
|
"failed to request SCMI %s mailbox\n", desc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Additional unidirectional channel for TX if needed */
|
||||||
|
if (tx && a2p_rx_chan) {
|
||||||
|
smbox->chan_receiver = mbox_request_channel(cl, a2p_rx_chan);
|
||||||
|
if (IS_ERR(smbox->chan_receiver)) {
|
||||||
|
ret = PTR_ERR(smbox->chan_receiver);
|
||||||
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_err(cdev, "failed to request SCMI Tx Receiver mailbox\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cinfo->transport_info = smbox;
|
cinfo->transport_info = smbox;
|
||||||
smbox->cinfo = cinfo;
|
smbox->cinfo = cinfo;
|
||||||
|
|
||||||
|
@ -152,8 +223,10 @@ static int mailbox_chan_free(int id, void *p, void *data)
|
||||||
|
|
||||||
if (smbox && !IS_ERR(smbox->chan)) {
|
if (smbox && !IS_ERR(smbox->chan)) {
|
||||||
mbox_free_channel(smbox->chan);
|
mbox_free_channel(smbox->chan);
|
||||||
|
mbox_free_channel(smbox->chan_receiver);
|
||||||
cinfo->transport_info = NULL;
|
cinfo->transport_info = NULL;
|
||||||
smbox->chan = NULL;
|
smbox->chan = NULL;
|
||||||
|
smbox->chan_receiver = NULL;
|
||||||
smbox->cinfo = NULL;
|
smbox->cinfo = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue