mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-11 07:34:31 +00:00
firmware: scmi: mailbox/smt agent device
This change implements a mailbox transport using SMT format for SCMI exchanges. This implementation follows the Linux kernel and SCP-firmware [1] as references implementation for SCMI message processing using SMT format for communication channel meta-data. Use of mailboxes in SCMI FDT bindings are defined in the Linux kernel DT bindings since v4.17. Links: [1] https://github.com/ARM-software/SCP-firmware Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org> Cc: Simon Glass <sjg@chromium.org> Cc: Peng Fan <peng.fan@nxp.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
358599efd8
commit
240720e905
5 changed files with 333 additions and 2 deletions
|
@ -2,7 +2,7 @@ config SCMI_FIRMWARE
|
|||
bool "Enable SCMI support"
|
||||
select FIRMWARE
|
||||
select OF_TRANSLATE
|
||||
depends on SANDBOX
|
||||
depends on SANDBOX || DM_MAILBOX
|
||||
help
|
||||
System Control and Management Interface (SCMI) is a communication
|
||||
protocol that defines standard interfaces for power, performance
|
||||
|
@ -14,4 +14,6 @@ config SCMI_FIRMWARE
|
|||
or a companion host in the CPU system.
|
||||
|
||||
Communications between agent (client) and the SCMI server are
|
||||
based on message exchange.
|
||||
based on message exchange. Messages can be exchange over tranport
|
||||
channels as a mailbox device with some piece of identified shared
|
||||
memory.
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
obj-y += scmi_agent-uclass.o
|
||||
obj-y += smt.o
|
||||
obj-$(CONFIG_DM_MAILBOX) += mailbox_agent.o
|
||||
obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o
|
||||
|
|
102
drivers/firmware/scmi/mailbox_agent.c
Normal file
102
drivers/firmware/scmi/mailbox_agent.c
Normal file
|
@ -0,0 +1,102 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2020 Linaro Limited.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <mailbox.h>
|
||||
#include <scmi_agent.h>
|
||||
#include <scmi_agent-uclass.h>
|
||||
#include <dm/devres.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include "smt.h"
|
||||
|
||||
#define TIMEOUT_US_10MS 10000
|
||||
|
||||
/**
|
||||
* struct scmi_mbox_channel - Description of an SCMI mailbox transport
|
||||
* @smt: Shared memory buffer
|
||||
* @mbox: Mailbox channel description
|
||||
* @timeout_us: Timeout in microseconds for the mailbox transfer
|
||||
*/
|
||||
struct scmi_mbox_channel {
|
||||
struct scmi_smt smt;
|
||||
struct mbox_chan mbox;
|
||||
ulong timeout_us;
|
||||
};
|
||||
|
||||
static int scmi_mbox_process_msg(struct udevice *dev, struct scmi_msg *msg)
|
||||
{
|
||||
struct scmi_mbox_channel *chan = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = scmi_write_msg_to_smt(dev, &chan->smt, msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Give shm addr to mbox in case it is meaningful */
|
||||
ret = mbox_send(&chan->mbox, chan->smt.buf);
|
||||
if (ret) {
|
||||
dev_err(dev, "Message send failed: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Receive the response */
|
||||
ret = mbox_recv(&chan->mbox, chan->smt.buf, chan->timeout_us);
|
||||
if (ret) {
|
||||
dev_err(dev, "Response failed: %d, abort\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = scmi_read_resp_from_smt(dev, &chan->smt, msg);
|
||||
|
||||
out:
|
||||
scmi_clear_smt_channel(&chan->smt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int scmi_mbox_probe(struct udevice *dev)
|
||||
{
|
||||
struct scmi_mbox_channel *chan = dev_get_priv(dev);
|
||||
int ret;
|
||||
|
||||
chan->timeout_us = TIMEOUT_US_10MS;
|
||||
|
||||
ret = mbox_get_by_index(dev, 0, &chan->mbox);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to find mailbox: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = scmi_dt_get_smt_buffer(dev, &chan->smt);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to get shm resources: %d\n", ret);
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
devm_kfree(dev, chan);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct udevice_id scmi_mbox_ids[] = {
|
||||
{ .compatible = "arm,scmi" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct scmi_agent_ops scmi_mbox_ops = {
|
||||
.process_msg = scmi_mbox_process_msg,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(scmi_mbox) = {
|
||||
.name = "scmi-over-mailbox",
|
||||
.id = UCLASS_SCMI_AGENT,
|
||||
.of_match = scmi_mbox_ids,
|
||||
.priv_auto_alloc_size = sizeof(struct scmi_mbox_channel),
|
||||
.probe = scmi_mbox_probe,
|
||||
.ops = &scmi_mbox_ops,
|
||||
};
|
139
drivers/firmware/scmi/smt.c
Normal file
139
drivers/firmware/scmi/smt.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
|
||||
* Copyright (C) 2019-2020 Linaro Limited.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <scmi_agent.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/system.h>
|
||||
#include <dm/ofnode.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#include "smt.h"
|
||||
|
||||
/**
|
||||
* Get shared memory configuration defined by the referred DT phandle
|
||||
* Return with a errno compliant value.
|
||||
*/
|
||||
int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt)
|
||||
{
|
||||
int ret;
|
||||
struct ofnode_phandle_args args;
|
||||
struct resource resource;
|
||||
fdt32_t faddr;
|
||||
phys_addr_t paddr;
|
||||
|
||||
ret = dev_read_phandle_with_args(dev, "shmem", NULL, 0, 0, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ofnode_read_resource(args.node, 0, &resource);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
faddr = cpu_to_fdt32(resource.start);
|
||||
paddr = ofnode_translate_address(args.node, &faddr);
|
||||
|
||||
smt->size = resource_size(&resource);
|
||||
if (smt->size < sizeof(struct scmi_smt_header)) {
|
||||
dev_err(dev, "Shared memory buffer too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
smt->buf = devm_ioremap(dev, paddr, smt->size);
|
||||
if (!smt->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
if (dcache_status())
|
||||
mmu_set_region_dcache_behaviour((uintptr_t)smt->buf,
|
||||
smt->size, DCACHE_OFF);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write SCMI message @msg into a SMT shared buffer @smt.
|
||||
* Return 0 on success and with a negative errno in case of error.
|
||||
*/
|
||||
int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
|
||||
struct scmi_msg *msg)
|
||||
{
|
||||
struct scmi_smt_header *hdr = (void *)smt->buf;
|
||||
|
||||
if ((!msg->in_msg && msg->in_msg_sz) ||
|
||||
(!msg->out_msg && msg->out_msg_sz))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
|
||||
dev_dbg(dev, "Channel busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) ||
|
||||
smt->size < (sizeof(*hdr) + msg->out_msg_sz)) {
|
||||
dev_dbg(dev, "Buffer too small\n");
|
||||
return -ETOOSMALL;
|
||||
}
|
||||
|
||||
/* Load message in shared memory */
|
||||
hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
|
||||
hdr->length = msg->in_msg_sz + sizeof(hdr->msg_header);
|
||||
hdr->msg_header = SMT_HEADER_TOKEN(0) |
|
||||
SMT_HEADER_MESSAGE_TYPE(0) |
|
||||
SMT_HEADER_PROTOCOL_ID(msg->protocol_id) |
|
||||
SMT_HEADER_MESSAGE_ID(msg->message_id);
|
||||
|
||||
memcpy_toio(hdr->msg_payload, msg->in_msg, msg->in_msg_sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SCMI message from a SMT shared buffer @smt and copy it into @msg.
|
||||
* Return 0 on success and with a negative errno in case of error.
|
||||
*/
|
||||
int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt,
|
||||
struct scmi_msg *msg)
|
||||
{
|
||||
struct scmi_smt_header *hdr = (void *)smt->buf;
|
||||
|
||||
if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
|
||||
dev_err(dev, "Channel unexpectedly busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR) {
|
||||
dev_err(dev, "Channel error reported, reset channel\n");
|
||||
return -ECOMM;
|
||||
}
|
||||
|
||||
if (hdr->length > msg->out_msg_sz + sizeof(hdr->msg_header)) {
|
||||
dev_err(dev, "Buffer to small\n");
|
||||
return -ETOOSMALL;
|
||||
}
|
||||
|
||||
/* Get the data */
|
||||
msg->out_msg_sz = hdr->length - sizeof(hdr->msg_header);
|
||||
memcpy_fromio(msg->out_msg, hdr->msg_payload, msg->out_msg_sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear SMT flags in shared buffer to allow further message exchange
|
||||
*/
|
||||
void scmi_clear_smt_channel(struct scmi_smt *smt)
|
||||
{
|
||||
struct scmi_smt_header *hdr = (void *)smt->buf;
|
||||
|
||||
hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
|
||||
}
|
86
drivers/firmware/scmi/smt.h
Normal file
86
drivers/firmware/scmi/smt.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
|
||||
* Copyright (C) 2019-2020 Linaro Limited.
|
||||
*/
|
||||
#ifndef SCMI_SMT_H
|
||||
#define SCMI_SMT_H
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
/**
|
||||
* struct scmi_smt_header - Description of the shared memory message buffer
|
||||
*
|
||||
* SMT stands for Shared Memory based Transport.
|
||||
* SMT uses 28 byte header prior message payload to handle the state of
|
||||
* the communication channel realized by the shared memory area and
|
||||
* to define SCMI protocol information the payload relates to.
|
||||
*/
|
||||
struct scmi_smt_header {
|
||||
__le32 reserved;
|
||||
__le32 channel_status;
|
||||
#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1)
|
||||
#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE BIT(0)
|
||||
__le32 reserved1[2];
|
||||
__le32 flags;
|
||||
#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT(0)
|
||||
__le32 length;
|
||||
__le32 msg_header;
|
||||
u8 msg_payload[0];
|
||||
};
|
||||
|
||||
#define SMT_HEADER_TOKEN(token) (((token) << 18) & GENMASK(31, 18))
|
||||
#define SMT_HEADER_PROTOCOL_ID(proto) (((proto) << 10) & GENMASK(17, 10))
|
||||
#define SMT_HEADER_MESSAGE_TYPE(type) (((type) << 18) & GENMASK(9, 8))
|
||||
#define SMT_HEADER_MESSAGE_ID(id) ((id) & GENMASK(7, 0))
|
||||
|
||||
/**
|
||||
* struct scmi_smt - Description of a SMT memory buffer
|
||||
* @buf: Shared memory base address
|
||||
* @size: Shared memory byte size
|
||||
*/
|
||||
struct scmi_smt {
|
||||
u8 *buf;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static inline bool scmi_smt_channel_is_free(struct scmi_smt *smt)
|
||||
{
|
||||
struct scmi_smt_header *hdr = (void *)smt->buf;
|
||||
|
||||
return hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
|
||||
}
|
||||
|
||||
static inline bool scmi_smt_channel_reports_error(struct scmi_smt *smt)
|
||||
{
|
||||
struct scmi_smt_header *hdr = (void *)smt->buf;
|
||||
|
||||
return hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
|
||||
}
|
||||
|
||||
static inline void scmi_smt_get_channel(struct scmi_smt *smt)
|
||||
{
|
||||
struct scmi_smt_header *hdr = (void *)smt->buf;
|
||||
|
||||
hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
|
||||
}
|
||||
|
||||
static inline void scmi_smt_put_channel(struct scmi_smt *smt)
|
||||
{
|
||||
struct scmi_smt_header *hdr = (void *)smt->buf;
|
||||
|
||||
hdr->channel_status |= SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
|
||||
hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR;
|
||||
}
|
||||
|
||||
int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt);
|
||||
|
||||
int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt,
|
||||
struct scmi_msg *msg);
|
||||
|
||||
int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt,
|
||||
struct scmi_msg *msg);
|
||||
|
||||
void scmi_clear_smt_channel(struct scmi_smt *smt);
|
||||
|
||||
#endif /* SCMI_SMT_H */
|
Loading…
Reference in a new issue