Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

firmware: scmi: add base proto, scmi sample, smc/hvc transport and reset proto #78293

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/firmware/scmi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ zephyr_library()
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI core.c)
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_MAILBOX_TRANSPORT mailbox.c)
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SHMEM shmem.c)
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_SMC_TRANSPORT smc.c)

# SCMI protocol helper files
zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_BASE_HELPERS base.c)
Expand Down
26 changes: 26 additions & 0 deletions drivers/firmware/scmi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,32 @@ config ARM_SCMI_TRANSPORT_INIT_PRIORITY
help
SCMI transport driver device initialization priority.

config ARM_SCMI_SMC_TRANSPORT
bool
depends on ARM_SCMI_SHMEM
select ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS
default y if DT_HAS_ARM_SCMI_SMC_ENABLED
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can probably simplify to default y if DT_HAS_ARM_SCMI_SMC_ENABLED||DT_HAS_ARM_SCMI_SMC_PARAM_ENABLED`

default y if DT_HAS_ARM_SCMI_SMC_PARAM_ENABLED
help
Enable support of SCMI compliant firmware with ARM SMC/HVC transport
with/without shmem address(4KB-page, offset) as parameters

choice ARM_SCMI_SMC_METHOD
bool "Select ARM SCMI SMC Calling Convention method"
depends on ARM_SCMI_SMC_TRANSPORT
default ARM_SCMI_SMC_METHOD_SMC

config ARM_SCMI_SMC_METHOD_SMC
bool "Use ARM SCMI Secure Monitor Call (SMC) method"
help
Use ARM Secure Monitor Call (SMC) method to access SCMI.

config ARM_SCMI_SMC_METHOD_HVC
bool "Use ARM SCMI Hypervisor Call (HVC) method"
help
Use ARM Hypervisor Call (HVC) method to access SCMI.
endchoice

rsource "Kconfig.proto"

endif # ARM_SCMI
16 changes: 11 additions & 5 deletions drivers/firmware/scmi/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,17 @@ static int scmi_send_message_post_kernel(struct scmi_protocol *proto,
goto out_release_mutex;
}

/* only one protocol instance can wait for a message reply at a time */
ret = k_sem_take(&proto->tx->sem, K_USEC(SCMI_CHAN_SEM_TIMEOUT_USEC));
if (ret < 0) {
LOG_ERR("failed to wait for msg reply");
goto out_release_mutex;
if (!scmi_transport_channel_is_polling(proto->transport, proto->tx)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All transport channels should support polling mode because the send_message function is forced to poll in PRE_KERNEL so this function should always return true. As such, I suggest renaming to scmi_transport_channel_is_polling_only.

/* only one protocol instance can wait for a message reply at a time */
ret = k_sem_take(&proto->tx->sem, K_USEC(SCMI_CHAN_SEM_TIMEOUT_USEC));
if (ret < 0) {
LOG_ERR("failed to wait for msg reply");
goto out_release_mutex;
}
} else {
while (!scmi_transport_channel_is_free(proto->transport, proto->tx)) {
k_cpu_idle();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why go into idle mode? What if there's more work to do? I'd suggest using k_yield.

}
}

ret = scmi_transport_read_message(proto->transport, proto->tx, reply);
Expand Down
172 changes: 172 additions & 0 deletions drivers/firmware/scmi/smc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright 2024 EPAM Systems
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/arch/arm64/arm-smccc.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(arm_scmi_smc);

#include <zephyr/drivers/firmware/scmi/shmem.h>
#include <zephyr/drivers/firmware/scmi/protocol.h>
#include <zephyr/drivers/firmware/scmi/transport.h>

#if defined(CONFIG_DT_HAS_ARM_SCMI_SMC_ENABLED)
#define DT_DRV_COMPAT arm_scmi_smc
#elif defined(CONFIG_DT_HAS_ARM_SCMI_SMC_PARAM_ENABLED)
#define DT_DRV_COMPAT arm_scmi_smc_param
#else
BUILD_ASSERT(0, "unsupported scmi interface");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably remove this if we add a dependency on DT_HAS_ARM_SCMI_SMC_ENABLED or DT_HAS_ARM_SCMI_SMC_ENABLED. Something like: depends on DT_HAS_ARM_SCMI_SMC_ENABLED || DT_HAS_ARM_SCMI_SMC_ENABLED

#endif

struct scmi_smc_channel {
/* SHMEM area bound to the channel */
const struct device *shmem;
/* ARM smc-id */
uint32_t smc_func_id;
uint32_t param_page;
uint32_t param_offset;
uint16_t xfer_seq;
};

#define SHMEM_SIZE (4096)
#define SHMEM_SHIFT 12
#define SHMEM_PAGE(x) ((x) >> SHMEM_SHIFT)
#define SHMEM_OFFSET(x) ((x) & (SHMEM_SIZE - 1))

#if defined(CONFIG_ARM_SCMI_SMC_METHOD_SMC)
static void scmi_smccc_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can just:

static void scmi_smccc_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
{
#ifdef CONFIG_ARM_SCMI_SMC_METHOD_SMC
    arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
#else
    arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
#endif /* CONFIG_ARM_SCMI_SMC_METHOD_SMC* / 
}

unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res)
{
arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
}
#else
static void scmi_smccc_call(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3,
unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res)
{
arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
}
#endif /* CONFIG_ARM_SCMI_SMC_METHOD_SMC */

static int scmi_smc_send_message(const struct device *transport, struct scmi_channel *chan,
struct scmi_message *msg)
{
struct scmi_smc_channel *smc_chan;
struct arm_smccc_res res;
unsigned long offset;
unsigned long page;
int ret;

smc_chan = chan->data;
page = smc_chan->param_page;
offset = smc_chan->param_offset;

LOG_DBG("smc send seq:%u prot:%02x msg:%02x", SCMI_MSG_XTRACT_TOKEN(msg->hdr),
SCMI_MSG_XTRACT_PROT_ID(msg->hdr), SCMI_MSG_XTRACT_ID(msg->hdr));

ret = scmi_shmem_write_message(smc_chan->shmem, msg);
if (ret) {
LOG_ERR("failed to write message to shmem: %d", ret);
return ret;
}

scmi_smccc_call(smc_chan->smc_func_id, page, offset, 0, 0, 0, 0, 0, &res);

/* Only SMCCC_RET_NOT_SUPPORTED is valid error code */
if (res.a0) {
ret = -EOPNOTSUPP;
}

return ret;
}

static int scmi_smc_read_message(const struct device *transport, struct scmi_channel *chan,
struct scmi_message *msg)
{
struct scmi_smc_channel *smc_chan;
int ret;

smc_chan = chan->data;

ret = scmi_shmem_read_message(smc_chan->shmem, msg);
LOG_DBG("smc done seq:%u prot:%02x msg:%02x status:%d (%d)",
SCMI_MSG_XTRACT_TOKEN(msg->hdr), SCMI_MSG_XTRACT_PROT_ID(msg->hdr),
SCMI_MSG_XTRACT_ID(msg->hdr), msg->status, ret);

if (!ret && msg->status != SCMI_SUCCESS) {
return scmi_status_to_errno(msg->status);
}

return ret;
}

static bool scmi_smc_channel_is_free(const struct device *transport, struct scmi_channel *chan)
{
struct scmi_smc_channel *smc_chan = chan->data;

return scmi_shmem_channel_status(smc_chan->shmem) & SCMI_SHMEM_CHAN_STATUS_BUSY_BIT;
}

static bool scmi_smc_channel_is_polling(const struct device *transport, struct scmi_channel *chan)
{
return true;
}

static uint16_t scmi_smc_channel_get_token(const struct device *transport,
struct scmi_channel *chan)
{
struct scmi_smc_channel *smc_chan = chan->data;

return smc_chan->xfer_seq++;
}

static int scmi_smc_setup_chan(const struct device *transport, struct scmi_channel *chan, bool tx)
{
struct scmi_smc_channel *smc_chan;

smc_chan = chan->data;
#if !defined(CONFIG_DT_HAS_ARM_SCMI_SMC_PARAM_ENABLED)
smc_chan->param_page = 0;
smc_chan->param_offset = 0;
#endif /* CONFIG_DT_HAS_ARM_SCMI_SMC_PARAM_ENABLED */

/* disable interrupt-based communication */
scmi_shmem_update_flags(smc_chan->shmem, SCMI_SHMEM_CHAN_FLAG_IRQ_BIT, 0);

return 0;
}

static const struct scmi_transport_api scmi_smc_api = {
.setup_chan = scmi_smc_setup_chan,
.send_message = scmi_smc_send_message,
.read_message = scmi_smc_read_message,
.channel_is_free = scmi_smc_channel_is_free,
.channel_get_token = scmi_smc_channel_get_token,
.channel_is_polling = scmi_smc_channel_is_polling,
};

#define _SCMI_SMC_CHAN_NAME(proto, idx) CONCAT(SCMI_TRANSPORT_CHAN_NAME(proto, idx), _, priv)

#define _SCMI_SMC_CHAN_DEFINE_PRIV_TX(node_id, proto) \
static struct scmi_smc_channel _SCMI_SMC_CHAN_NAME(proto, 0) = { \
.shmem = DEVICE_DT_GET(DT_PROP_BY_IDX(node_id, shmem, 0)), \
.smc_func_id = DT_PROP(node_id, arm_smc_id), \
.param_page = SHMEM_PAGE(DT_REG_ADDR(DT_PHANDLE_BY_IDX(node_id, shmem, 0))), \
.param_offset = SHMEM_OFFSET(DT_REG_ADDR(DT_PHANDLE_BY_IDX(node_id, shmem, 0))), \
.xfer_seq = 1, \
}

#define _SCMI_SMC_CHAN_DEFINE(node_id, proto, idx) \
_SCMI_SMC_CHAN_DEFINE_PRIV_TX(node_id, proto); \
DT_SCMI_TRANSPORT_CHAN_DEFINE(node_id, idx, proto, &(_SCMI_SMC_CHAN_NAME(proto, idx)));

#define DT_INST_SCMI_SMC_BASE_CHAN_DEFINE(inst) \
_SCMI_SMC_CHAN_DEFINE(DT_DRV_INST(inst), SCMI_PROTOCOL_BASE, 0)

DT_INST_SCMI_SMC_BASE_CHAN_DEFINE(0);
DT_INST_SCMI_TRANSPORT_DEFINE(0, NULL, NULL, NULL, PRE_KERNEL_1,
CONFIG_ARM_SCMI_TRANSPORT_INIT_PRIORITY, &scmi_smc_api);
23 changes: 23 additions & 0 deletions include/zephyr/drivers/firmware/scmi/transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ struct scmi_transport_api {
struct scmi_message *msg);
bool (*channel_is_free)(const struct device *transport,
struct scmi_channel *chan);
bool (*channel_is_polling)(const struct device *transport, struct scmi_channel *chan);
struct scmi_channel *(*request_channel)(const struct device *transport,
uint32_t proto, bool tx);
uint16_t (*channel_get_token)(const struct device *transport, struct scmi_channel *chan);
Expand Down Expand Up @@ -261,6 +262,28 @@ static inline bool scmi_transport_channel_is_free(const struct device *transport
return api->channel_is_free(transport, chan);
}

/**
* @brief Check if an SCMI channel is in polling mode
*
* @param transport pointer to the device structure for
* the transport layer
* @param chan pointer to SCMI channel the query is to be
* performed on
*
* @retval true if channel is in polling mode
*/
static inline bool scmi_transport_channel_is_polling(const struct device *transport,
struct scmi_channel *chan)
{
const struct scmi_transport_api *api = (const struct scmi_transport_api *)transport->api;

if (!api || !api->channel_is_polling) {
return false;
}

return api->channel_is_polling(transport, chan);
}

/**
* @brief Get actual token value from channel.
*
Expand Down
4 changes: 2 additions & 2 deletions include/zephyr/drivers/firmware/scmi/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

#ifdef CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS

#ifdef CONFIG_ARM_SCMI_MAILBOX_TRANSPORT
#if defined(CONFIG_ARM_SCMI_MAILBOX_TRANSPORT) || defined(CONFIG_ARM_SCMI_SMC_TRANSPORT)
/** @brief Check if a protocol node has an associated channel
*
* This macro, when applied to a protocol node, checks if
Expand All @@ -46,7 +46,7 @@
*/
#define DT_SCMI_TRANSPORT_PROTO_HAS_CHAN(node_id, idx)\
DT_PROP_HAS_IDX(node_id, shmem, idx)
#else /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT */
#else /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT || CONFIG_ARM_SCMI_SMC_TRANSPORT */
#error "Transport with static channels needs to define HAS_CHAN macro"
#endif /* CONFIG_ARM_SCMI_MAILBOX_TRANSPORT */

Expand Down