-
Notifications
You must be signed in to change notification settings - Fork 7k
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
Changes from 1 commit
b5473c0
5be7738
1b40c07
68f6abf
e352152
bfc95ec
c3e2c9e
a4fa16f
11c3fb1
f9bbef1
fa943c5
008590c
d455d4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All transport channels should support polling mode because the |
||
/* 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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
} | ||
} | ||
|
||
ret = scmi_transport_read_message(proto->transport, proto->tx, reply); | ||
|
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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can probably remove this if we add a dependency on |
||
#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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can just:
|
||
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); |
There was a problem hiding this comment.
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`