diff --git a/drivers/firmware/scmi/CMakeLists.txt b/drivers/firmware/scmi/CMakeLists.txt index 68e4c7a787cb..59cb40d1b4a4 100644 --- a/drivers/firmware/scmi/CMakeLists.txt +++ b/drivers/firmware/scmi/CMakeLists.txt @@ -6,7 +6,10 @@ 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) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_CLK_HELPERS clk.c) zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_PINCTRL_HELPERS pinctrl.c) +zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_RESET_HELPERS reset.c) diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index f75583f21e9f..367dd19bc226 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -3,13 +3,6 @@ if ARM_SCMI -config ARM_SCMI_CLK_HELPERS - bool "Helper functions for SCMI clock protocol" - default y - depends on DT_HAS_ARM_SCMI_CLOCK_ENABLED - help - Enable support for SCMI clock protocol helper functions. - config ARM_SCMI_MAILBOX_TRANSPORT bool "SCMI transport based on shared memory and doorbells" default y @@ -20,13 +13,6 @@ config ARM_SCMI_MAILBOX_TRANSPORT Enable support for SCMI transport based on shared memory and doorbells. -config ARM_SCMI_PINCTRL_HELPERS - bool "Helper functions for SCMI pinctrl protocol" - default y - depends on DT_HAS_ARM_SCMI_PINCTRL_ENABLED - help - Enable support for SCMI pinctrl protocol helper functions. - config ARM_SCMI_SHMEM bool "SCMI shared memory (SHMEM) driver" default y @@ -56,4 +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 + 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 diff --git a/drivers/firmware/scmi/Kconfig.proto b/drivers/firmware/scmi/Kconfig.proto new file mode 100644 index 000000000000..d82bfaa05480 --- /dev/null +++ b/drivers/firmware/scmi/Kconfig.proto @@ -0,0 +1,47 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config ARM_SCMI_CLK_HELPERS + bool "Helper functions for SCMI clock protocol" + default y + depends on DT_HAS_ARM_SCMI_CLOCK_ENABLED + help + Enable support for SCMI clock protocol helper functions. + +config ARM_SCMI_PINCTRL_HELPERS + bool "Helper functions for SCMI pinctrl protocol" + default y + depends on DT_HAS_ARM_SCMI_PINCTRL_ENABLED + help + Enable support for SCMI pinctrl protocol helper functions. + +config ARM_SCMI_BASE_HELPERS + bool "Helper functions for SCMI base protocol" + default y + help + Enable support for SCMI base protocol helper functions. + +config ARM_SCMI_BASE_EXT_REV + bool "Select to report ARM SCMI base protocol extended revision" + depends on ARM_SCMI_BASE_HELPERS + default n + help + Enable report of ARM SCMI base extended revision information at boot time. + +config ARM_SCMI_BASE_AGENT_HELPERS + bool "Helper functions for SCMI base protocol agents management" + depends on ARM_SCMI_BASE_EXT_REV + default n + help + Enable support for SCMI base protocol agents management helper functions. + It enables support for SCMI base protocol messages: + - BASE_DISCOVER_AGENT + - BASE_SET_DEVICE_PERMISSIONS + - BASE_RESET_AGENT_CONFIGURATION + +config ARM_SCMI_RESET_HELPERS + bool "Helper functions for SCMI reset protocol" + default y + depends on DT_HAS_ARM_SCMI_RESET_ENABLED + help + Enable support for SCMI reset protocol helper functions. diff --git a/drivers/firmware/scmi/base.c b/drivers/firmware/scmi/base.c new file mode 100644 index 000000000000..2ea0bd4ca9db --- /dev/null +++ b/drivers/firmware/scmi/base.c @@ -0,0 +1,383 @@ +/* + * System Control and Management Interface (SCMI) Base Protocol + * + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +LOG_MODULE_REGISTER(arm_scmi_proto_base); + +#include + +#ifdef CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS +extern struct scmi_channel SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 0); +#endif /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */ + +STRUCT_SECTION_ITERABLE(scmi_protocol, SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_BASE)) = { + .id = SCMI_PROTOCOL_BASE, +#ifdef CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS + .tx = &SCMI_TRANSPORT_CHAN_NAME(SCMI_PROTOCOL_BASE, 0), +#endif /* CONFIG_ARM_SCMI_TRANSPORT_HAS_STATIC_CHANNELS */ + .data = NULL, +}; + +/* + * SCMI Base protocol + */ +enum scmi_base_protocol_cmd { + SCMI_BASE_PROTOCOL_VERSION = 0x0, + SCMI_BASE_PROTOCOL_ATTRIBUTES = 0x1, + SCMI_BASE_PROTOCOL_MESSAGE_ATTRIBUTES = 0x2, + SCMI_BASE_DISCOVER_VENDOR = 0x3, + SCMI_BASE_DISCOVER_SUB_VENDOR = 0x4, + SCMI_BASE_DISCOVER_IMPLEMENT_VERSION = 0x5, + SCMI_BASE_DISCOVER_LIST_PROTOCOLS = 0x6, + SCMI_BASE_DISCOVER_AGENT = 0x7, + SCMI_BASE_NOTIFY_ERRORS = 0x8, + SCMI_BASE_SET_DEVICE_PERMISSIONS = 0x9, + SCMI_BASE_SET_PROTOCOL_PERMISSIONS = 0xa, + SCMI_BASE_RESET_AGENT_CONFIGURATION = 0xb, +}; + +/* + * SCMI Base protocol version info + */ + +/* BASE PROTOCOL_ATTRIBUTES */ +struct scmi_msg_base_attributes_p2a { + uint8_t num_protocols; + uint8_t num_agents; + uint16_t reserved; +} __packed; + +/* BASE_DISCOVER_VENDOR */ +struct scmi_msg_base_vendor_id_p2a { + char vendor_id[SCMI_SHORT_NAME_MAX_SIZE]; +} __packed; + +/* BASE_DISCOVER_SUB_VENDOR */ +struct scmi_msg_base_subvendor_id_p2a { + char subvendor_id[SCMI_SHORT_NAME_MAX_SIZE]; +} __packed; + +/* BASE_DISCOVER_IMPLEMENTATION_VERSION */ +struct scmi_msg_base_impl_ver_p2a { + uint32_t impl_ver; +} __packed; + +/* + * BASE_DISCOVER_AGENT + */ +struct scmi_msg_base_discover_agent_a2p { + uint32_t agent_id; +} __packed; + +struct scmi_msg_base_discover_agent_p2a { + uint32_t agent_id; + char name[SCMI_SHORT_NAME_MAX_SIZE]; +} __packed; + +/* + * BASE_SET_DEVICE_PERMISSIONS + */ +#define SCMI_BASE_DEVICE_ACCESS_ALLOW BIT(0) + +struct scmi_msg_base_set_device_permissions_a2p { + uint32_t agent_id; + uint32_t device_id; + uint32_t flags; +} __packed; + +/* + * BASE_RESET_AGENT_CONFIGURATION + */ +#define SCMI_BASE_AGENT_PERMISSIONS_RESET BIT(0) + +struct scmi_msg_base_reset_agent_cfg_a2p { + uint32_t agent_id; + uint32_t flags; +} __packed; + +static int scmi_base_get_version(struct scmi_protocol_version *version) +{ + struct scmi_protocol *proto; + + proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_BASE); + if (!proto) { + return -EINVAL; + } + + return scmi_core_get_version(proto, version); +} + +#if defined(CONFIG_ARM_SCMI_BASE_EXT_REV) + +static int scmi_base_xfer_no_tx(uint8_t msg_id, void *rx_buf, size_t rx_len) +{ + struct scmi_message msg, reply; + struct scmi_protocol *proto; + int ret; + + proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_BASE); + if (!proto) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(msg_id, + SCMI_COMMAND, proto->id, 0x0); + msg.len = 0; + msg.content = NULL; + + reply.len = rx_len; + reply.content = rx_buf; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + LOG_ERR("base xfer failed (%d)", ret); + return ret; + } + + return 0; +} + +static int scmi_base_attributes_get(struct scmi_msg_base_attributes_p2a *attr) +{ + int ret; + + ret = scmi_base_xfer_no_tx(SCMI_BASE_PROTOCOL_ATTRIBUTES, attr, sizeof(*attr)); + if (ret) { + LOG_ERR("base get attributes failed (%d)", ret); + return ret; + } + + LOG_DBG("base attr num_protocols:0x%02x num_agents:0x%02x", attr->num_protocols, + attr->num_agents); + + return 0; +} + +static int scmi_base_vendor_id_get(struct scmi_msg_base_vendor_id_p2a *id) +{ + int ret; + + ret = scmi_base_xfer_no_tx(SCMI_BASE_DISCOVER_VENDOR, id, sizeof(*id)); + if (ret) { + LOG_ERR("base get vendor id failed (%d)", ret); + return ret; + } + + LOG_DBG("base vendor id:%s", id->vendor_id); + + return 0; +} + +static int scmi_base_subvendor_id_get(struct scmi_msg_base_subvendor_id_p2a *id) +{ + int ret; + + ret = scmi_base_xfer_no_tx(SCMI_BASE_DISCOVER_SUB_VENDOR, id, sizeof(*id)); + if (ret) { + LOG_ERR("base get subvendor id failed (%d)", ret); + return ret; + } + + LOG_DBG("base subvendor id:%s", id->subvendor_id); + + return 0; +} + +static int scmi_base_implementation_version_get(struct scmi_msg_base_impl_ver_p2a *impl_ver) +{ + int ret; + + ret = scmi_base_xfer_no_tx(SCMI_BASE_DISCOVER_IMPLEMENT_VERSION, impl_ver, + sizeof(*impl_ver)); + if (ret) { + LOG_ERR("base get impl_ver failed (%d)", ret); + return ret; + } + + LOG_DBG("base impl_ver:0x%08x", impl_ver->impl_ver); + + return ret; +} +#endif /* CONFIG_ARM_SCMI_BASE_EXT_REV */ + +union scmi_base_msgs_t { + struct scmi_msg_base_attributes_p2a attr; + struct scmi_msg_base_vendor_id_p2a vendor_id; + struct scmi_msg_base_subvendor_id_p2a subvendor_id; + struct scmi_msg_base_impl_ver_p2a impl_ver; +}; + +int scmi_base_get_revision_info(struct scmi_revision_info *rev) +{ + struct scmi_protocol_version ver; +#if defined(CONFIG_ARM_SCMI_BASE_EXT_REV) + union scmi_base_msgs_t msgs; +#endif /* CONFIG_ARM_SCMI_BASE_EXT_REV */ + int ret; + + ret = scmi_base_get_version(&ver); + if (ret) { + return ret; + } + + rev->major_ver = ver.major; + rev->minor_ver = ver.minor; + + LOG_DBG("scmi base protocol v%04x.%04x", rev->major_ver, rev->minor_ver); + +#if defined(CONFIG_ARM_SCMI_BASE_EXT_REV) + ret = scmi_base_attributes_get(&msgs.attr); + if (ret) { + return ret; + } + + rev->num_agents = msgs.attr.num_agents; + rev->num_protocols = msgs.attr.num_protocols; + + ret = scmi_base_vendor_id_get(&msgs.vendor_id); + if (ret) { + return ret; + } + + memcpy(rev->vendor_id, msgs.vendor_id.vendor_id, SCMI_SHORT_NAME_MAX_SIZE); + + ret = scmi_base_subvendor_id_get(&msgs.subvendor_id); + if (ret) { + return ret; + } + + memcpy(rev->sub_vendor_id, msgs.subvendor_id.subvendor_id, SCMI_SHORT_NAME_MAX_SIZE); + + ret = scmi_base_implementation_version_get(&msgs.impl_ver); + if (ret) { + return ret; + } + + LOG_DBG("scmi base revision info vendor '%s:%s' fw version 0x%x protocols:%d agents:%d", + rev->vendor_id, rev->sub_vendor_id, rev->impl_ver, rev->num_protocols, + rev->num_agents); +#endif /* CONFIG_ARM_SCMI_BASE_EXT_REV */ + + return 0; +} + +#if defined(CONFIG_ARM_SCMI_BASE_AGENT_HELPERS) +int scmi_base_discover_agent(uint32_t agent_id, struct scmi_agent_info *agent_inf) +{ + struct scmi_msg_base_discover_agent_a2p tx; + struct scmi_msg_base_discover_agent_p2a rx; + struct scmi_message msg, reply; + struct scmi_protocol *proto; + int ret; + + proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_BASE); + if (!proto) { + return -EINVAL; + } + + tx.agent_id = agent_id; + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_BASE_DISCOVER_AGENT, SCMI_COMMAND, proto->id, 0x0); + msg.len = sizeof(tx); + msg.content = &tx; + + reply.len = sizeof(rx); + reply.content = ℞ + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + LOG_ERR("base proto discover agent failed (%d)", ret); + return ret; + } + + agent_inf->agent_id = rx.agent_id; + strncpy(agent_inf->name, rx.name, SCMI_SHORT_NAME_MAX_SIZE); + + LOG_DBG("base discover agent agent_id:%u name:%s", agent_inf->agent_id, agent_inf->name); + + return 0; +} + +int scmi_base_device_permission(uint32_t agent_id, uint32_t device_id, bool allow) +{ + struct scmi_msg_base_set_device_permissions_a2p tx; + struct scmi_message msg, reply; + struct scmi_protocol *proto; + int ret; + + proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_BASE); + if (!proto) { + return -EINVAL; + } + + LOG_DBG("base proto agent:%u device:%u permission set allow:%d", agent_id, device_id, + allow); + + tx.agent_id = agent_id; + tx.device_id = device_id; + tx.flags = allow ? SCMI_BASE_DEVICE_ACCESS_ALLOW : 0; + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_BASE_SET_DEVICE_PERMISSIONS, SCMI_COMMAND, proto->id, + 0x0); + msg.len = sizeof(tx); + msg.content = &tx; + + reply.len = 0; + reply.content = NULL; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + LOG_ERR("base agent:%u device:%u permission allow:%d failed (%d)", agent_id, + device_id, allow, ret); + return ret; + } + + LOG_DBG("base agent:%u device:%u permission set allow:%d done", agent_id, device_id, + allow); + + return 0; +} + +int scmi_base_reset_agent_cfg(uint32_t agent_id, bool reset_perm) +{ + struct scmi_msg_base_reset_agent_cfg_a2p tx; + struct scmi_message msg, reply; + struct scmi_protocol *proto; + int ret; + + proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_BASE); + if (!proto) { + return -EINVAL; + } + + LOG_DBG("base agent:%u reset cfg reset_perm:%d", agent_id, reset_perm); + + tx.agent_id = agent_id; + tx.flags = reset_perm ? SCMI_BASE_AGENT_PERMISSIONS_RESET : 0; + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_BASE_RESET_AGENT_CONFIGURATION, SCMI_COMMAND, + proto->id, 0x0); + msg.len = sizeof(tx); + msg.content = &tx; + + reply.len = 0; + reply.content = NULL; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + LOG_ERR("base agent:%u reset cfg failed (%d)", agent_id, ret); + return ret; + } + + LOG_DBG("base agent:%u reset cfg reset_perm:%d done", agent_id, reset_perm); + + return 0; +} +#endif /* CONFIG_ARM_SCMI_BASE_AGENT_HELPERS */ diff --git a/drivers/firmware/scmi/clk.c b/drivers/firmware/scmi/clk.c index 415349586eeb..906a0e906d98 100644 --- a/drivers/firmware/scmi/clk.c +++ b/drivers/firmware/scmi/clk.c @@ -13,12 +13,10 @@ #define SCMI_CLK_CONFIG_EA_MASK GENMASK(23, 16) struct scmi_clock_attributes_reply { - int32_t status; uint32_t attributes; }; struct scmi_clock_rate_set_reply { - int32_t status; uint32_t rate[2]; }; @@ -52,10 +50,6 @@ int scmi_clock_rate_get(struct scmi_protocol *proto, return ret; } - if (reply_buffer.status != SCMI_SUCCESS) { - return scmi_status_to_errno(reply_buffer.status); - } - *rate = reply_buffer.rate[0]; return 0; @@ -65,7 +59,6 @@ int scmi_clock_config_set(struct scmi_protocol *proto, struct scmi_clock_config *cfg) { struct scmi_message msg, reply; - int status, ret; /* sanity checks */ if (!proto || !cfg) { @@ -97,19 +90,10 @@ int scmi_clock_config_set(struct scmi_protocol *proto, msg.content = cfg; reply.hdr = msg.hdr; - reply.len = sizeof(status); - reply.content = &status; - - ret = scmi_send_message(proto, &msg, &reply); - if (ret < 0) { - return ret; - } - - if (status != SCMI_SUCCESS) { - return scmi_status_to_errno(status); - } + reply.len = 0; + reply.content = NULL; - return 0; + return scmi_send_message(proto, &msg, &reply); } int scmi_clock_protocol_attributes(struct scmi_protocol *proto, uint32_t *attributes) @@ -142,10 +126,6 @@ int scmi_clock_protocol_attributes(struct scmi_protocol *proto, uint32_t *attrib return ret; } - if (reply_buffer.status != 0) { - return scmi_status_to_errno(reply_buffer.status); - } - *attributes = reply_buffer.attributes; return 0; diff --git a/drivers/firmware/scmi/core.c b/drivers/firmware/scmi/core.c index 58e354485ee5..ed2408c1619e 100644 --- a/drivers/firmware/scmi/core.c +++ b/drivers/firmware/scmi/core.c @@ -91,8 +91,13 @@ static int scmi_send_message_pre_kernel(struct scmi_protocol *proto, struct scmi_message *msg, struct scmi_message *reply) { + uint16_t token; int ret; + token = scmi_transport_channel_get_token(proto->transport, proto->tx); + msg->hdr |= FIELD_PREP(SCMI_MSG_TOKEN_ID_MASK, token); + reply->hdr = msg->hdr; + ret = scmi_transport_send_message(proto->transport, proto->tx, msg); if (ret < 0) { return ret; @@ -120,6 +125,7 @@ static int scmi_send_message_post_kernel(struct scmi_protocol *proto, struct scmi_message *msg, struct scmi_message *reply) { + uint16_t token; int ret = 0; if (!proto->tx) { @@ -133,17 +139,27 @@ static int scmi_send_message_post_kernel(struct scmi_protocol *proto, return ret; } + token = scmi_transport_channel_get_token(proto->transport, proto->tx); + msg->hdr |= FIELD_PREP(SCMI_MSG_TOKEN_ID_MASK, token); + reply->hdr = msg->hdr; + ret = scmi_transport_send_message(proto->transport, proto->tx, msg); if (ret < 0) { LOG_ERR("failed to send message"); 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)) { + /* 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(); + } } ret = scmi_transport_read_message(proto->transport, proto->tx, reply); @@ -212,3 +228,54 @@ int scmi_core_transport_init(const struct device *transport) return scmi_core_protocol_setup(transport); } + +#define SCMI_MSG_PROTOCOL_VERSION 0x0 + +/** + * @brief Response for a message SCMI_PROTOCOL_VERSION + * + * Protocol versioning uses a 32-bit unsigned integer, where + * - the upper 16 bits are the major revision; + * - the lower 16 bits are the minor revision. + * + */ +struct scmi_msg_prot_version_p2a { + union { + uint32_t version; + struct { + uint16_t minor; + uint16_t major; + } ver; + }; +} __packed; + +int scmi_core_get_version(struct scmi_protocol *proto, struct scmi_protocol_version *ver) +{ + struct scmi_msg_prot_version_p2a rx; + struct scmi_message msg, reply; + int ret; + + if (!proto || !ver) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_MSG_PROTOCOL_VERSION, + SCMI_COMMAND, proto->id, 0x0); + msg.len = 0; + msg.content = NULL; + + reply.len = sizeof(rx); + reply.content = ℞ + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + LOG_ERR("proto:%u get version failed (%d)", proto->id, ret); + return ret; + } + + LOG_DBG("proto:%u version 0x%08x", proto->id, rx.version); + ver->major = rx.ver.major; + ver->minor = rx.ver.minor; + + return 0; +} diff --git a/drivers/firmware/scmi/mailbox.c b/drivers/firmware/scmi/mailbox.c index 2c61afa9a87a..2b7ee996ebfa 100644 --- a/drivers/firmware/scmi/mailbox.c +++ b/drivers/firmware/scmi/mailbox.c @@ -5,6 +5,7 @@ */ #include +#include #include "mailbox.h" LOG_MODULE_REGISTER(scmi_mbox); @@ -49,10 +50,16 @@ static int scmi_mbox_read_message(const struct device *transport, struct scmi_message *msg) { struct scmi_mbox_channel *mbox_chan; + int ret; mbox_chan = chan->data; - return scmi_shmem_read_message(mbox_chan->shmem, msg); + ret = scmi_shmem_read_message(mbox_chan->shmem, msg); + if (!ret && msg->status != SCMI_SUCCESS) { + return scmi_status_to_errno(msg->status); + } + + return ret; } static bool scmi_mbox_channel_is_free(const struct device *transport, @@ -64,6 +71,12 @@ static bool scmi_mbox_channel_is_free(const struct device *transport, SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; } +static uint16_t scmi_mbox_channel_get_token(const struct device *transport, + struct scmi_channel *chan) +{ + return 0; +} + static int scmi_mbox_setup_chan(const struct device *transport, struct scmi_channel *chan, bool tx) @@ -108,6 +121,7 @@ static struct scmi_transport_api scmi_mbox_api = { .send_message = scmi_mbox_send_message, .read_message = scmi_mbox_read_message, .channel_is_free = scmi_mbox_channel_is_free, + .channel_get_token = scmi_mbox_channel_get_token, }; DT_INST_SCMI_MAILBOX_DEFINE(0, PRE_KERNEL_1, diff --git a/drivers/firmware/scmi/pinctrl.c b/drivers/firmware/scmi/pinctrl.c index fd0ef80d2ad5..25937a56eeca 100644 --- a/drivers/firmware/scmi/pinctrl.c +++ b/drivers/firmware/scmi/pinctrl.c @@ -13,7 +13,6 @@ int scmi_pinctrl_settings_configure(struct scmi_pinctrl_settings *settings) struct scmi_protocol *proto; struct scmi_message msg, reply; uint32_t config_num; - int32_t status, ret; proto = &SCMI_PROTOCOL_NAME(SCMI_PROTOCOL_PINCTRL); @@ -47,17 +46,8 @@ int scmi_pinctrl_settings_configure(struct scmi_pinctrl_settings *settings) msg.content = settings; reply.hdr = msg.hdr; - reply.len = sizeof(status); - reply.content = &status; + reply.len = 0; + reply.content = NULL; - ret = scmi_send_message(proto, &msg, &reply); - if (ret < 0) { - return ret; - } - - if (status != SCMI_SUCCESS) { - return scmi_status_to_errno(status); - } - - return 0; + return scmi_send_message(proto, &msg, &reply); } diff --git a/drivers/firmware/scmi/reset.c b/drivers/firmware/scmi/reset.c new file mode 100644 index 000000000000..e5a0eaafa9da --- /dev/null +++ b/drivers/firmware/scmi/reset.c @@ -0,0 +1,249 @@ +/* + * Copyright 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +LOG_MODULE_REGISTER(arm_scmi_reset); + +/* + * SCMI Reset protocol + */ +#define SCMI_PROTOCOL_RESET_REV_MAJOR 0x1 + +enum scmi_reset_protocol_cmd { + SCMI_RESET_MSG_PROTOCOL_VERSION = 0x0, + SCMI_RESET_MSG_PROTOCOL_ATTRIBUTES = 0x1, + SCMI_RESET_MSG_PROTOCOL_MESSAGE_ATTRIBUTES = 0x2, + SCMI_RESET_MSG_DOMAIN_ATTRIBUTES = 0x3, + SCMI_RESET_MSG = 0x4, + SCMI_RESET_MSG_NOTIFY = 0x5, + SCMI_RESET_MSG_DOMAIN_NAME_GET = 0x6, +}; + +/* Reset PROTOCOL_ATTRIBUTES */ +struct scmi_msg_reset_attributes_p2a { + uint32_t attributes; +#define SCMI_RESET_ATTR_NUM_DOMAINS GENMASK(15, 0) +#define SCMI_RESET_ATTR_GET_NUM_DOMAINS(attr) FIELD_GET(SCMI_RESET_ATTR_NUM_DOMAINS, (attr)) +} __packed; + +/* RESET_DOMAIN_ATTRIBUTES */ +struct scmi_msg_reset_domain_attr_a2p { + uint32_t domain_id; +} __packed; + +/* RESET_DOMAIN_ATTRIBUTES */ +struct scmi_msg_reset_domain_attr_p2a { + uint32_t attr; +#define SCMI_RESET_ATTR_SUPPORTS_ASYNC BIT(31) +#define SCMI_RESET_ATTR_SUPPORTS_NOTIFY BIT(30) +#define SCMI_RESET_ATTR_SUPPORTS_EXT_NAMES BIT(29) + uint32_t latency; +#define SCMI_RESET_ATTR_LATENCY_UNK1 0x7fffffff +#define SCMI_RESET_ATTR_LATENCY_UNK2 0xffffffff + char name[SCMI_SHORT_NAME_MAX_SIZE]; +} __packed; + +/* RESET */ +struct scmi_msg_reset_domain_reset_a2p { + uint32_t domain_id; + uint32_t flags; +#define SCMI_RESET_AUTONOMOUS BIT(0) +#define SCMI_RESET_EXPLICIT_ASSERT BIT(1) +#define SCMI_RESET_ASYNCHRONOUS_RESET BIT(2) + uint32_t reset_state; +#define SCMI_RESET_ARCH_COLD_RESET 0 +} __packed; + +int scmi_reset_get_attr(struct scmi_protocol *proto, uint16_t *num_domains) +{ + struct scmi_msg_reset_attributes_p2a attr; + struct scmi_message msg, reply; + int ret; + + if (!proto || !num_domains) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_RESET_DOMAIN) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_RESET_MSG_PROTOCOL_ATTRIBUTES, + SCMI_COMMAND, proto->id, 0x0); + msg.len = 0; + msg.content = NULL; + + reply.len = sizeof(attr); + reply.content = &attr; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + return ret; + } + + *num_domains = SCMI_RESET_ATTR_GET_NUM_DOMAINS(attr.attributes); + + return 0; +} + +int scmi_reset_domain_get_attr(struct scmi_protocol *proto, uint32_t id, + struct scmi_reset_domain_attr *dom_attr) +{ + struct scmi_msg_reset_domain_attr_a2p tx; + struct scmi_msg_reset_domain_attr_p2a rx; + struct scmi_message msg, reply; + int ret; + + if (!proto || !dom_attr) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_RESET_DOMAIN) { + return -EINVAL; + } + + tx.domain_id = id; + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_RESET_MSG_DOMAIN_ATTRIBUTES, SCMI_COMMAND, proto->id, + 0x0); + msg.len = sizeof(tx); + msg.content = &tx; + + reply.len = sizeof(rx); + reply.content = ℞ + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + return ret; + } + + LOG_DBG("scmi reset domain:%s get attributes attr:%x latency:%x", rx.name, rx.attr, + rx.latency); + + strncpy(dom_attr->name, rx.name, sizeof(dom_attr->name)); + dom_attr->latency = 0; + dom_attr->is_latency_valid = false; + if (rx.latency != SCMI_RESET_ATTR_LATENCY_UNK1 && + rx.latency == SCMI_RESET_ATTR_LATENCY_UNK1) { + dom_attr->latency = rx.latency; + dom_attr->is_latency_valid = true; + } + dom_attr->is_async_sup = !!(rx.attr & SCMI_RESET_ATTR_SUPPORTS_ASYNC); + dom_attr->is_notifications_sup = !!(rx.attr & SCMI_RESET_ATTR_SUPPORTS_NOTIFY); + + if (rx.attr & SCMI_RESET_ATTR_SUPPORTS_EXT_NAMES) { + /* TODO: get long name */ + } + + return 0; +} + +int scmi_reset_domain_assert(struct scmi_protocol *proto, uint32_t id) +{ + struct scmi_msg_reset_domain_reset_a2p tx; + struct scmi_message msg, reply; + int ret; + + if (!proto) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_RESET_DOMAIN) { + return -EINVAL; + } + + tx.domain_id = id; + tx.flags = SCMI_RESET_EXPLICIT_ASSERT; + tx.reset_state = SCMI_RESET_ARCH_COLD_RESET; + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_RESET_MSG, SCMI_COMMAND, proto->id, 0x0); + msg.len = sizeof(tx); + msg.content = &tx; + + reply.len = 0; + reply.content = NULL; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + LOG_ERR("scmi reset:%u assert failed (%d)", id, ret); + } + + LOG_DBG("scmi reset:%u assert", id); + + return ret; +} + +int scmi_reset_domain_deassert(struct scmi_protocol *proto, uint32_t id) +{ + struct scmi_msg_reset_domain_reset_a2p tx; + struct scmi_message msg, reply; + int ret; + + if (!proto) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_RESET_DOMAIN) { + return -EINVAL; + } + + tx.domain_id = id; + tx.flags = 0; + tx.reset_state = SCMI_RESET_ARCH_COLD_RESET; + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_RESET_MSG, SCMI_COMMAND, proto->id, 0x0); + msg.len = sizeof(tx); + msg.content = &tx; + + reply.len = 0; + reply.content = NULL; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + LOG_ERR("scmi reset:%d deassert failed (%d)", id, ret); + } + + LOG_DBG("scmi reset:%u deassert", id); + + return ret; +} + +int scmi_reset_domain_toggle(struct scmi_protocol *proto, uint32_t id) +{ + struct scmi_msg_reset_domain_reset_a2p tx; + struct scmi_message msg, reply; + int ret; + + if (!proto) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_RESET_DOMAIN) { + return -EINVAL; + } + + tx.domain_id = id; + tx.flags = SCMI_RESET_AUTONOMOUS; + tx.reset_state = SCMI_RESET_ARCH_COLD_RESET; + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_RESET_MSG, SCMI_COMMAND, proto->id, 0x0); + msg.len = sizeof(tx); + msg.content = &tx; + + reply.len = 0; + reply.content = NULL; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + LOG_ERR("scmi reset:%d toggle failed (%d)", id, ret); + } + + LOG_DBG("scmi reset:%u toggle", id); + + return ret; +} diff --git a/drivers/firmware/scmi/shmem.c b/drivers/firmware/scmi/shmem.c index 6ec645cbc309..659ec2377dc4 100644 --- a/drivers/firmware/scmi/shmem.c +++ b/drivers/firmware/scmi/shmem.c @@ -35,6 +35,9 @@ struct scmi_shmem_layout { volatile uint32_t msg_hdr; }; +#define SCMI_SHMEM_RX_DATA_LEN(layout) ((layout)->len - sizeof((layout)->msg_hdr) - sizeof(int32_t)) +#define SCMI_SHMEM_RX_DATA_OFS(layout) (sizeof(*(layout)) + sizeof(int32_t)) + int scmi_shmem_get_channel_status(const struct device *dev, uint32_t *status) { struct scmi_shmem_data *data; @@ -62,6 +65,7 @@ int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg struct scmi_shmem_layout *layout; struct scmi_shmem_data *data; const struct scmi_shmem_config *cfg; + size_t len; data = shmem->data; cfg = shmem->config; @@ -76,14 +80,15 @@ int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg return -EINVAL; } + LOG_HEXDUMP_DBG((void *)layout, 64, "shmem before read"); + if (cfg->size < (sizeof(*layout) + msg->len)) { LOG_ERR("message doesn't fit in shmem area"); return -EINVAL; } - /* mismatch between expected reply size and actual size? */ - if (msg->len != (layout->len - sizeof(layout->msg_hdr))) { - LOG_ERR("bad message len. Expected 0x%x, got 0x%x", + if (msg->len && msg->len < SCMI_SHMEM_RX_DATA_LEN(layout)) { + LOG_ERR("bad message len. max 0x%x, got 0x%x", msg->len, (uint32_t)(layout->len - sizeof(layout->msg_hdr))); return -EINVAL; @@ -96,9 +101,14 @@ int scmi_shmem_read_message(const struct device *shmem, struct scmi_message *msg return -EINVAL; } + msg->status = SCMI_SUCCESS; + len = SCMI_SHMEM_RX_DATA_LEN(layout); + msg->len = MIN(msg->len, len); + msg->status = *(uint32_t *)((uint8_t *)data->regmap + sizeof(*layout)); + if (msg->content) { scmi_shmem_memcpy(POINTER_TO_UINT(msg->content), - data->regmap + sizeof(*layout), msg->len); + data->regmap + SCMI_SHMEM_RX_DATA_OFS(layout), msg->len); } return 0; @@ -131,6 +141,7 @@ int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *ms return -EBUSY; } + msg->status = SCMI_SUCCESS; layout->len = sizeof(layout->msg_hdr) + msg->len; layout->msg_hdr = msg->hdr; @@ -142,6 +153,8 @@ int scmi_shmem_write_message(const struct device *shmem, struct scmi_message *ms /* done, mark channel as busy and proceed */ layout->chan_status &= ~SCMI_SHMEM_CHAN_STATUS_BUSY_BIT; + LOG_HEXDUMP_DBG((void *)layout, 64, "shmem after write"); + return 0; } @@ -180,6 +193,7 @@ static int scmi_shmem_init(const struct device *dev) } device_map(&data->regmap, cfg->phys_addr, cfg->size, K_MEM_CACHE_NONE); + LOG_DBG("shmem phys:%lx dev:%lx", cfg->phys_addr, data->regmap); return 0; } diff --git a/drivers/firmware/scmi/smc.c b/drivers/firmware/scmi/smc.c new file mode 100644 index 000000000000..cae6df143fe4 --- /dev/null +++ b/drivers/firmware/scmi/smc.c @@ -0,0 +1,172 @@ +/* + * Copyright 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +LOG_MODULE_REGISTER(arm_scmi_smc); + +#include +#include +#include + +#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"); +#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, + 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); diff --git a/drivers/reset/CMakeLists.txt b/drivers/reset/CMakeLists.txt index 84f0443cabb5..6f117d607c7a 100644 --- a/drivers/reset/CMakeLists.txt +++ b/drivers/reset/CMakeLists.txt @@ -12,3 +12,4 @@ zephyr_library_sources_ifdef(CONFIG_RESET_INTEL_SOCFPGA reset_intel_socfpga.c) zephyr_library_sources_ifdef(CONFIG_RESET_NPCX reset_npcx.c) zephyr_library_sources_ifdef(CONFIG_RESET_NXP_SYSCON reset_lpc_syscon.c) zephyr_library_sources_ifdef(CONFIG_RESET_NXP_RSTCTL reset_nxp_rstctl.c) +zephyr_library_sources_ifdef(CONFIG_RESET_ARM_SCMI reset_arm_scmi.c) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 87ba9c7576ca..1177f309d1bf 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -36,5 +36,6 @@ rsource "Kconfig.intel_socfpga" rsource "Kconfig.npcx" rsource "Kconfig.lpc_syscon" rsource "Kconfig.nxp_rstctl" +rsource "Kconfig.arm_scmi" endif # RESET diff --git a/drivers/reset/Kconfig.arm_scmi b/drivers/reset/Kconfig.arm_scmi new file mode 100644 index 000000000000..6e4ab5507f3e --- /dev/null +++ b/drivers/reset/Kconfig.arm_scmi @@ -0,0 +1,9 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +config RESET_ARM_SCMI + bool "ARM SCMI based reset driver" + depends on ARM_SCMI_RESET_HELPERS + default y + help + Enable ARM SCMI reset driver based on Reset domain management protocol. diff --git a/drivers/reset/reset_arm_scmi.c b/drivers/reset/reset_arm_scmi.c new file mode 100644 index 000000000000..0fa474e54d07 --- /dev/null +++ b/drivers/reset/reset_arm_scmi.c @@ -0,0 +1,106 @@ +/* + * Copyright 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +LOG_MODULE_REGISTER(reset_arm_scmi); + +#define DT_DRV_COMPAT arm_scmi_reset + +struct scmi_reset_drv_data { + struct scmi_protocol_version version; + uint16_t num_domains; +}; + +static int scmi_reset_line_assert(const struct device *dev, uint32_t id) +{ + struct scmi_reset_drv_data *data; + struct scmi_protocol *proto; + + proto = dev->data; + data = proto->data; + + if (id >= data->num_domains) { + return -EINVAL; + } + + return scmi_reset_domain_assert(proto, id); +} + +static int scmi_reset_line_deassert(const struct device *dev, uint32_t id) +{ + struct scmi_reset_drv_data *data; + struct scmi_protocol *proto; + + proto = dev->data; + data = proto->data; + + if (id >= data->num_domains) { + return -EINVAL; + } + + return scmi_reset_domain_deassert(proto, id); +} + +static int scmi_reset_line_toggle(const struct device *dev, uint32_t id) +{ + struct scmi_reset_drv_data *data; + struct scmi_protocol *proto; + + proto = dev->data; + data = proto->data; + + if (id >= data->num_domains) { + return -EINVAL; + } + + return scmi_reset_domain_toggle(proto, id); +} + +static int scmi_reset_init(const struct device *dev) +{ + struct scmi_protocol *proto; + struct scmi_reset_drv_data *data; + int ret; + + proto = dev->data; + data = proto->data; + + ret = scmi_core_get_version(proto, &data->version); + if (ret) { + return ret; + } + + if (data->version.major != SCMI_PROTOCOL_RESET_REV_MAJOR) { + LOG_ERR("unsupported reset protocol version 0x%04x.0x%04x ", data->version.major, + data->version.minor); + return -ENOTSUP; + } + + ret = scmi_reset_get_attr(proto, &data->num_domains); + if (ret) { + return ret; + } + + LOG_INF("scmi reset rotocol version 0x%04x.0x%04x num_domains:%u", data->version.major, + data->version.minor, data->num_domains); + + return 0; +} + +static const struct reset_driver_api scmi_reset_driver_api = { + .line_assert = scmi_reset_line_assert, + .line_deassert = scmi_reset_line_deassert, + .line_toggle = scmi_reset_line_toggle, +}; + +static struct scmi_reset_drv_data scmi_reset_data; + +DT_INST_SCMI_PROTOCOL_DEFINE(0, &scmi_reset_init, NULL, &scmi_reset_data, NULL, + PRE_KERNEL_1, CONFIG_RESET_INIT_PRIORITY, + &scmi_reset_driver_api); diff --git a/dts/bindings/firmware/arm,scmi-reset.yaml b/dts/bindings/firmware/arm,scmi-reset.yaml new file mode 100644 index 000000000000..af7cc83c805e --- /dev/null +++ b/dts/bindings/firmware/arm,scmi-reset.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +description: SCMI Reset domain management Interface + +compatible: "arm,scmi-reset" + +include: [base.yaml, reset-controller.yaml] + +properties: + "#reset-cells": + const: 1 + + shmem: + type: phandles + + reg: + required: true + const: [0x16] + +reset-cells: + - id diff --git a/dts/bindings/firmware/arm,scmi-smc-param.yaml b/dts/bindings/firmware/arm,scmi-smc-param.yaml new file mode 100644 index 000000000000..1b3026882e55 --- /dev/null +++ b/dts/bindings/firmware/arm,scmi-smc-param.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +description: | + SMC based System Control and Management Interface (SCMI) with + shmem address(4KB-page, offset) as parameters + +include: arm,scmi-smc.yaml + +compatible: "arm,scmi-smc-param" diff --git a/dts/bindings/firmware/arm,scmi-smc.yaml b/dts/bindings/firmware/arm,scmi-smc.yaml new file mode 100644 index 000000000000..fe66d1e9ac40 --- /dev/null +++ b/dts/bindings/firmware/arm,scmi-smc.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +description: SMC based System Control and Management Interface (SCMI) + +compatible: "arm,scmi-smc" + +include: base.yaml + +bus: scmi + +properties: + "#address-cells": + required: true + const: 1 + + "#size-cells": + required: true + const: 0 + + arm,smc-id: + required: true + type: int + description: + SMC id required when using smc or hvc transports + + shmem: + required: true + description: phandles to the shared memory region used for data transmission + type: phandles diff --git a/include/zephyr/drivers/firmware/scmi/base.h b/include/zephyr/drivers/firmware/scmi/base.h new file mode 100644 index 000000000000..607e3ecc54d3 --- /dev/null +++ b/include/zephyr/drivers/firmware/scmi/base.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef DRIVERS_ARM_SCMI_BASE_H_ +#define DRIVERS_ARM_SCMI_BASE_H_ + +#define SCMI_PROTOCOL_BASE_REV_MAJOR 0x2 + +/** + * @brief SCMI base protocol revision information + */ +struct scmi_revision_info { + /** Major ABI version. */ + uint16_t major_ver; + /** Minor ABI version. */ + uint16_t minor_ver; +#if defined(CONFIG_ARM_SCMI_BASE_EXT_REV) || defined(__DOXYGEN__) + /** Number of protocols that are implemented, excluding the base protocol. */ + uint8_t num_protocols; + /** Number of agents in the system. */ + uint8_t num_agents; + /** A vendor-specific implementation version. */ + uint32_t impl_ver; + /** A vendor identifier. */ + char vendor_id[SCMI_SHORT_NAME_MAX_SIZE]; + /** A sub-vendor identifier. */ + char sub_vendor_id[SCMI_SHORT_NAME_MAX_SIZE]; +#endif /* CONFIG_ARM_SCMI_BASE_EXT_REV) || __DOXYGEN__ */ +}; + +/** + * @brief SCMI base protocol agent info + * + * @agent_id: SCMI agent id. + * @name: SCMI agent name. + */ + +#define SCMI_BASE_AGENT_ID_OWN 0xFFFFFFFF + +struct scmi_agent_info { + uint32_t agent_id; + char name[SCMI_SHORT_NAME_MAX_SIZE]; +}; + +/** + * @brief SCMI base protocol get revision information. + * + * @param rev pointer on revision information struct scmi_revision_info. + * @retval 0 If successful. + */ +int scmi_base_get_revision_info(struct scmi_revision_info *rev); + +/** + * @brief SCMI base protocol discover the name of an agent and agent id. + * + * @param agent_id SCMI agent id. The platform will return caller SCMI agent id + * if set to SCMI_BASE_AGENT_ID_OWN. + * @param agent_inf pointer on SCMI agent information struct scmi_agent_info. + * @retval 0 If successful. + */ +int scmi_base_discover_agent(uint32_t agent_id, struct scmi_agent_info *agent_inf); + +/** + * @brief SCMI base protocol set an agent permissions to access devices. + * + * @param agent_id SCMI agent id. + * @param device_id SCMI device id. + * @param allow If set to true, allow agent access to the device. + * @retval 0 If successful. + */ +int scmi_base_device_permission(uint32_t agent_id, uint32_t device_id, bool allow); + +/** + * @brief SCMI base protocol reset platform resource settings that were configured by an agent. + * + * @param agent_id SCMI agent id. + * @param reset_perm If set to true, reset all access permission settings of the agent. + * @retval 0 If successful. + */ +int scmi_base_reset_agent_cfg(uint32_t agent_id, bool reset_perm); + +#endif /* DRIVERS_ARM_SCMI_BASE_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/pinctrl.h b/include/zephyr/drivers/firmware/scmi/pinctrl.h index 3906b91db681..9ec40029623a 100644 --- a/include/zephyr/drivers/firmware/scmi/pinctrl.h +++ b/include/zephyr/drivers/firmware/scmi/pinctrl.h @@ -18,10 +18,9 @@ #define SCMI_PINCTRL_NO_FUNCTION 0xFFFFFFFF -#define SCMI_PINCTRL_CONFIG_ATTRIBUTES(fid_valid, cfg_num, selector) \ - (SCMI_FIELD_MAKE(fid_valid, BIT(1), 10) | \ - SCMI_FIELD_MAKE(cfg_num, GENMASK(7, 0), 2) | \ - SCMI_FIELD_MAKE(selector, GENMASK(1, 0), 0)) +#define SCMI_PINCTRL_CONFIG_ATTRIBUTES(fid_valid, cfg_num, selector) \ + (FIELD_PREP(BIT(1), fid_valid) | FIELD_PREP(GENMASK(7, 0), cfg_num) | \ + FIELD_PREP(GENMASK(1, 0), selector)) #define SCMI_PINCTRL_SELECTOR_PIN 0x0 #define SCMI_PINCTRL_SELECTOR_GROUP 0x1 diff --git a/include/zephyr/drivers/firmware/scmi/protocol.h b/include/zephyr/drivers/firmware/scmi/protocol.h index 13ac8ba248c0..263786d0a875 100644 --- a/include/zephyr/drivers/firmware/scmi/protocol.h +++ b/include/zephyr/drivers/firmware/scmi/protocol.h @@ -17,6 +17,25 @@ #include #include +#define SCMI_MAX_STR_SIZE 64 +#define SCMI_SHORT_NAME_MAX_SIZE 16 + +/** + * @brief SCMI message header definitions + */ +#define SCMI_MSG_ID_MASK GENMASK(7, 0) +#define SCMI_MSG_XTRACT_ID(hdr) (uint8_t)FIELD_GET(SCMI_MSG_ID_MASK, (hdr)) +#define SCMI_MSG_TYPE_MASK GENMASK(9, 8) +#define SCMI_MSG_XTRACT_TYPE(hdr) (uint8_t)FIELD_GET(SCMI_MSG_TYPE_MASK, (hdr)) +#define SCMI_MSG_TYPE_COMMAND 0 +#define SCMI_MSG_TYPE_DELAYED_RESP 2 +#define SCMI_MSG_TYPE_NOTIFICATION 3 +#define SCMI_MSG_PROTOCOL_ID_MASK GENMASK(17, 10) +#define SCMI_MSG_XTRACT_PROT_ID(hdr) (uint8_t)FIELD_GET(SCMI_MSG_PROTOCOL_ID_MASK, (hdr)) +#define SCMI_MSG_TOKEN_ID_MASK GENMASK(27, 18) +#define SCMI_MSG_XTRACT_TOKEN(hdr) (uint16_t)FIELD_GET(SCMI_MSG_TOKEN_ID_MASK, (hdr)) +#define SCMI_MSG_TOKEN_MAX (SCMI_MSG_XTRACT_TOKEN(SCMI_MSG_TOKEN_ID_MASK) + 1) + /** * @brief Build an SCMI message header * @@ -28,11 +47,9 @@ * @param proto protocol ID * @param token message token */ -#define SCMI_MESSAGE_HDR_MAKE(id, type, proto, token) \ - (SCMI_FIELD_MAKE(id, GENMASK(7, 0), 0) | \ - SCMI_FIELD_MAKE(type, GENMASK(1, 0), 8) | \ - SCMI_FIELD_MAKE(proto, GENMASK(7, 0), 10) | \ - SCMI_FIELD_MAKE(token, GENMASK(9, 0), 18)) +#define SCMI_MESSAGE_HDR_MAKE(id, type, proto, token) \ + (FIELD_PREP(SCMI_MSG_ID_MASK, id) | FIELD_PREP(SCMI_MSG_TYPE_MASK, type) | \ + FIELD_PREP(SCMI_MSG_PROTOCOL_ID_MASK, proto) | FIELD_PREP(SCMI_MSG_TOKEN_ID_MASK, token)) struct scmi_channel; @@ -66,6 +83,21 @@ enum scmi_status_code { SCMI_IN_USE = -11, }; +/** + * @brief SCMI protocol version + * + * Protocol versioning uses a 32-bit unsigned integer, where + * - the upper 16 bits are the major revision; + * - the lower 16 bits are the minor revision. + * + */ +struct scmi_protocol_version { + /** major protocol revision */ + uint16_t minor; + /** minor protocol revision */ + uint16_t major; +}; + /** * @struct scmi_protocol * @@ -90,6 +122,7 @@ struct scmi_protocol { struct scmi_message { uint32_t hdr; uint32_t len; + int32_t status; void *content; }; @@ -119,4 +152,17 @@ int scmi_status_to_errno(int scmi_status); int scmi_send_message(struct scmi_protocol *proto, struct scmi_message *msg, struct scmi_message *reply); +/** + * @brief Get SCMI protocol version + * + * Generic function to get SCMI protocol version which is common operation for all protocols. + * + * @param proto pointer to SCMI protocol + * @param ver pointer to SCMI protocol version structure + * + * @retval 0 if successful + * @retval negative errno on failure + */ +int scmi_core_get_version(struct scmi_protocol *proto, struct scmi_protocol_version *ver); + #endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_PROTOCOL_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/reset.h b/include/zephyr/drivers/firmware/scmi/reset.h new file mode 100644 index 000000000000..fe1ba15c2813 --- /dev/null +++ b/include/zephyr/drivers/firmware/scmi/reset.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef DRIVERS_ARM_SCMI_RESET_H_ +#define DRIVERS_ARM_SCMI_RESET_H_ + +#include + +#define SCMI_PROTOCOL_RESET_REV_MAJOR 0x1 + +/** + * @brief SCMI Reset domain attributes + * + */ +struct scmi_reset_domain_attr { + /** Maximum time (in microseconds) required for the reset to take effect */ + uint32_t latency; + /** reset domain name */ + char name[SCMI_MAX_STR_SIZE]; + /** Asynchronous reset support */ + bool is_async_sup:1; + /** Reset notifications support */ + bool is_notifications_sup:1; + /** Reset latency is valid */ + bool is_latency_valid:1; +}; + +/** + * @brief SCMI reset domain protocol get protocol attributes + * + * @param proto pointer to SCMI clock protocol data + * @param num_domains pointer to return Number of reset domains + * + * @retval 0 if successful + * @retval negative errno on failure + */ +int scmi_reset_get_attr(struct scmi_protocol *proto, uint16_t *num_domains); + +/** + * @brief SCMI reset domain protocol get attributes of the reset domain + * + * @param proto pointer to SCMI clock protocol data + * @param id ID of the Reset domain for which the query is done + * @param dom_attr pointer to return Reset domain @p id attributes + * + * @retval 0 if successful + * @retval negative errno on failure + */ +int scmi_reset_domain_get_attr(struct scmi_protocol *proto, uint32_t id, + struct scmi_reset_domain_attr *dom_attr); + +/** + * @brief SCMI reset domain protocol assert reset signal for the reset domain + * + * @param proto pointer to SCMI clock protocol data + * @param id ID of the Reset domain for which the query is done + * + * @retval 0 if successful + * @retval negative errno on failure + */ +int scmi_reset_domain_assert(struct scmi_protocol *proto, uint32_t id); + +/** + * @brief SCMI reset domain protocol de-assert reset signal for the reset domain + * + * @param proto pointer to SCMI clock protocol data + * @param id ID of the Reset domain for which the query is done + * + * @retval 0 if successful + * @retval negative errno on failure + */ +int scmi_reset_domain_deassert(struct scmi_protocol *proto, uint32_t id); + +/** + * @brief SCMI reset domain protocol toggle reset signal for the reset domain + * + * In terms of SCMI, perform Autonomous Reset action. + * + * @param proto pointer to SCMI clock protocol data + * @param id ID of the Reset domain for which the query is done + * + * @retval 0 if successful + * @retval negative errno on failure + */ +int scmi_reset_domain_toggle(struct scmi_protocol *proto, uint32_t id); + +#endif /* DRIVERS_ARM_SCMI_RESET_H_ */ diff --git a/include/zephyr/drivers/firmware/scmi/transport.h b/include/zephyr/drivers/firmware/scmi/transport.h index 2cb72a2b9042..24e83d511bb7 100644 --- a/include/zephyr/drivers/firmware/scmi/transport.h +++ b/include/zephyr/drivers/firmware/scmi/transport.h @@ -82,8 +82,10 @@ 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); }; /** @@ -260,6 +262,51 @@ 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. + * + * @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 The token value to be used in SCMI message header. The each call should return value + * differ from previous one, for example continuously incremented. + */ +static inline uint16_t scmi_transport_channel_get_token(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_get_token) { + return -ENOSYS; + } + + return api->channel_get_token(transport, chan); +} + /** * @brief Perfrom SCMI core initialization * diff --git a/include/zephyr/drivers/firmware/scmi/util.h b/include/zephyr/drivers/firmware/scmi/util.h index ac22b10efc41..216876865756 100644 --- a/include/zephyr/drivers/firmware/scmi/util.h +++ b/include/zephyr/drivers/firmware/scmi/util.h @@ -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 @@ -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 */ @@ -243,21 +243,6 @@ DT_SCMI_TRANSPORT_CHANNELS_DECLARE(node_id) \ DT_SCMI_PROTOCOL_DATA_DEFINE(node_id, DT_REG_ADDR(node_id), data) -/** - * @brief Create an SCMI message field - * - * Data might not necessarily be encoded in the first - * x bits of an SCMI message parameter/return value. - * This comes in handy when building said parameters/ - * return values. - * - * @param x value to encode - * @param mask value to perform bitwise-and with `x` - * @param shift value to left-shift masked `x` - */ -#define SCMI_FIELD_MAKE(x, mask, shift)\ - (((uint32_t)(x) & (mask)) << (shift)) - /** * @brief SCMI protocol IDs * diff --git a/samples/drivers/firmware/arm_scmi/CMakeLists.txt b/samples/drivers/firmware/arm_scmi/CMakeLists.txt new file mode 100644 index 000000000000..66ef70be2971 --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright 2024 EPAM Systems +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(arm_scmi_shell) + +target_sources(app PRIVATE + src/main.c + src/shell.c +) + +target_sources_ifdef(CONFIG_ARM_SCMI_RESET_HELPERS app PRIVATE + src/shell_reset.c +) diff --git a/samples/drivers/firmware/arm_scmi/README.rst b/samples/drivers/firmware/arm_scmi/README.rst new file mode 100644 index 000000000000..ac5ba8af50bf --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/README.rst @@ -0,0 +1,147 @@ +.. _arm_scmi_shell: + +ARM SCMI shell sample +##################### + +Overview +******** + +This sample demonstrates the usage of ARM SCMI. It provides access to the +ARM SCMI shell interface for demo and testing purposes of different +ARM SCMI protocols. + +* Base protocol +* Reset domain management protocol + +Caveats +******* + +Zephyr ARM SCMI relies on the firmware running in EL3 layer to be compatible +with Arm System Control and Management Interface Platform Design Document +and ARM SCMI driver used by the sample. + +Building and Running +******************** + +For building the application: + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/firmware/arm_scmi + :board: rpi_5 + :goals: build + +Running on RPI5 +^^^^^^^^^^^^^^^ + +* The SCMI compatible Trusted Firmware-A (TF-A) binary should be placed + at RPI5 rootfs as `armstub8-2712.bin`. +* The Zephyr application binary should be placed at RPI5 rootfs + as `zephyr.bin`. +* Modify config.txt parameter `kernel=zephyr.bin` + +Sample Output +************* + +Base protocol +^^^^^^^^^^^^^ + +.. code-block:: console + + *** Booting Zephyr OS build v3.6.0-126-g9a3343971f49 *** + ARM SCMI shell sample + + uart:~$ arm_scmi base + base - SCMI Base proto commands. + Subcommands: + revision : SCMI Base get revision info + Usage: arm_scmi base revision + + discover_agent : SCMI Base discover an agent + Usage: arm_scmi base discover_agent [agent_id] + + device_permission : SCMI Base set an agent permissions to access device + Usage: arm_scmi base discover_agent + + + reset_agent_cfg : SCMI Base reset an agent configuration + Usage: arm_scmi base reset_agent_cfg + + + uart:~$ arm_scmi base revision + ARM SCMI base protocol v0002.0000 + vendor :EPAM + subvendor : + fw version :0x40 + protocols :2 + num_agents :8 + + uart:~$ arm_scmi base discover_agent + agent id :0 + agent name :agent-0 + + uart:~$ arm_scmi base device_permission 1 1 1 + agent:1 device:1 permission set to allow + + uart:~$ arm_scmi base device_permission 2 1 1 + E: failed to read reply + E: base agent:2 device:1 permission allow:1 failed (-13) + base proto device permission failed (-13) + + uart:~$ arm_scmi base reset_agent_cfg 1 1 + agent:1 reset cfg reset_perm:reset + + uart:~$ arm_scmi base device_permission 2 1 1 + agent:2 device:1 permission set to allow + +Reset domain management protocol +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: console + + *** Booting Zephyr OS build v3.6.0-131-g07152a092eaa *** + ARM SCMI shell sample + + uart:~$ arm_scmi reset + reset - SCMI Reset proto commands. + Subcommands: + revision : SCMI Reset proto show revision information + Usage: arm_scmi reset revision + + list : SCMI Reset domains list + Usage: arm_scmi reset list + + info : SCMI Reset domain show info + Usage: arm_scmi reset info + + assert : SCMI Reset domain assert + Usage: arm_scmi reset assert + + deassert : SCMI Reset domain de-assert + Usage: arm_scmi reset deassert + + autoreset : SCMI Reset domain Autonomous reset + Usage: arm_scmi reset autoreset + + ARM SCMI Reset protocol version 0x0001.0000 num_domains:4 + uart:~$ arm_scmi reset list + domain_id,name,latency,attributes + 0,swinit_pcie1,0x00000000,async=no,notify=no,latency=invalid + 1,bridge_pcie1,0x00000000,async=no,notify=no,latency=invalid + 2,swinit_pcie2,0x00000000,async=no,notify=no,latency=invalid + 3,bridge_pcie2,0x00000000,async=no,notify=no,latency=invalid + uart:~$ arm_scmi reset info 1 + ARM SCMI reset domain: 1 + name : bridge_pcie1 + latency : invalid + async : not supported + notifications : not supported + uart:~$ arm_scmi reset a + assert autoreset + uart:~$ arm_scmi reset assert 1 + reset domain:1 assert done + uart:~$ arm_scmi reset deassert 1 + reset domain:1 deassert done + uart:~$ arm_scmi reset autoreset 1 + reset domain:1 toggle done + uart:~$ arm_scmi reset autoreset 4 + reset domain:4 toggle failed (-22) diff --git a/samples/drivers/firmware/arm_scmi/boards/rpi_5.conf b/samples/drivers/firmware/arm_scmi/boards/rpi_5.conf new file mode 100644 index 000000000000..c9b166c74294 --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/boards/rpi_5.conf @@ -0,0 +1,5 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_ARM_SCMI_BASE_AGENT_HELPERS=y +CONFIG_RESET=y diff --git a/samples/drivers/firmware/arm_scmi/boards/rpi_5.overlay b/samples/drivers/firmware/arm_scmi/boards/rpi_5.overlay new file mode 100644 index 000000000000..f3983cae50e6 --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/boards/rpi_5.overlay @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + firmware { + scmi { + compatible = "arm,scmi-smc"; + arm,smc-id = <0x82000002>; + #address-cells = <1>; + #size-cells = <0>; + shmem = <&scmi_buf>; + + scmi_reset: protocol@16 { + compatible = "arm,scmi-reset"; + reg = <0x16>; + #reset-cells = <1>; + }; + }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <1>; + ranges; + + scmi_buf: scmi@3ff00000 { + compatible = "arm,scmi-shmem"; + reg = <0x0 0x3ff00000 4096>; + }; + }; +}; diff --git a/samples/drivers/firmware/arm_scmi/prj.conf b/samples/drivers/firmware/arm_scmi/prj.conf new file mode 100644 index 000000000000..16e39477dade --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/prj.conf @@ -0,0 +1,10 @@ +# Copyright 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y +CONFIG_PRINTK=y +CONFIG_SHELL=y + +CONFIG_ARM_SCMI=y +CONFIG_ARM_SCMI_BASE_EXT_REV=y diff --git a/samples/drivers/firmware/arm_scmi/sample.yml b/samples/drivers/firmware/arm_scmi/sample.yml new file mode 100644 index 000000000000..3a9254bcf6d6 --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/sample.yml @@ -0,0 +1,14 @@ +sample: + name: ARM SCMI shell sample +common: + filter: dt_compat_enabled("arm,scmi-smc") and dt_compat_enabled("arm,scmi-smc-param") + integration_platforms: + - rpi_5 +tests: + sample.firmware.arm_scmi_shell: + tags: + - drivers + - firmware + - arm_scmi + - shell + harness: console diff --git a/samples/drivers/firmware/arm_scmi/src/main.c b/samples/drivers/firmware/arm_scmi/src/main.c new file mode 100644 index 000000000000..1347c81fd3fa --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/src/main.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + * + * A sample application for demonstrating ARM SCMI Platform interface access using shell commands. + */ + +#include + +int main(void) +{ + printk("ARM SCMI shell sample\n"); +} diff --git a/samples/drivers/firmware/arm_scmi/src/shell.c b/samples/drivers/firmware/arm_scmi/src/shell.c new file mode 100644 index 000000000000..f00ca2ae3cbc --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/src/shell.c @@ -0,0 +1,154 @@ +/* + * System Control and Management Interface (SCMI) Shell interface + * + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +static int scmi_shell_base_revision(const struct shell *sh, size_t argc, char **argv) +{ + struct scmi_revision_info rev; + int ret; + + ret = scmi_base_get_revision_info(&rev); + if (ret) { + shell_error(sh, "base proto get revision failed (%d)", ret); + return ret; + } + + shell_print(sh, "ARM SCMI base protocol v%04x.%04x", rev.major_ver, rev.minor_ver); + +#if defined(CONFIG_ARM_SCMI_BASE_EXT_REV) + shell_print(sh, " vendor\t:%s", rev.vendor_id); + shell_print(sh, " subvendor\t:%s", rev.sub_vendor_id); + shell_print(sh, " fw version\t:0x%x", rev.impl_ver); + shell_print(sh, " protocols\t:%d", rev.num_protocols); + shell_print(sh, " num_agents\t:%d", rev.num_agents); +#endif /* CONFIG_ARM_SCMI_BASE_EXT_REV */ + + return 0; +} + +#if defined(CONFIG_ARM_SCMI_BASE_AGENT_HELPERS) +static int scmi_shell_base_discover_agent(const struct shell *sh, size_t argc, char **argv) +{ + struct scmi_revision_info rev; + struct scmi_agent_info agent_info; + uint32_t agent_id = SCMI_BASE_AGENT_ID_OWN; + int ret; + + if (argc > 2) { + return -EINVAL; + } + + if (argc == 2) { + agent_id = atoi(argv[1]); + } + + ret = scmi_base_get_revision_info(&rev); + if (ret) { + shell_error(sh, "base proto get revision failed (%d)", ret); + return ret; + } + + if (agent_id != SCMI_BASE_AGENT_ID_OWN && agent_id >= rev.num_agents) { + shell_error(sh, "base proto invalid agent id (%u)", agent_id); + return -EINVAL; + } + + ret = scmi_base_discover_agent(agent_id, &agent_info); + if (ret) { + shell_error(sh, "base proto discover agent failed (%d)", ret); + return ret; + } + + shell_print(sh, " agent id\t:%u", agent_info.agent_id); + shell_print(sh, " agent name\t:%s", agent_info.name); + return 0; +} + +static int scmi_shell_base_device_permission(const struct shell *sh, size_t argc, char **argv) +{ + uint32_t agent_id, device_id; + bool allow; + int ret; + + if (argc != 4) { + return -EINVAL; + } + + agent_id = atoi(argv[1]); + device_id = atoi(argv[2]); + allow = !!atoi(argv[3]); + + ret = scmi_base_device_permission(agent_id, device_id, allow); + if (ret) { + shell_error(sh, "base proto device permission failed (%d)", ret); + return ret; + } + + shell_print(sh, "agent:%u device:%u permission set to %s", agent_id, device_id, + allow ? "allow" : "deny"); + + return 0; +} + +static int scmi_shell_base_reset_agent_cfg(const struct shell *sh, size_t argc, char **argv) +{ + uint32_t agent_id; + bool reset_perm; + int ret; + + if (argc != 3) { + return -EINVAL; + } + + agent_id = atoi(argv[1]); + reset_perm = !!atoi(argv[2]); + + ret = scmi_base_reset_agent_cfg(agent_id, reset_perm); + if (ret) { + shell_error(sh, "base proto reset agent cfg failed (%d)", ret); + return ret; + } + + shell_print(sh, "agent:%u reset cfg reset_perm:%s", agent_id, + reset_perm ? "reset" : "none"); + + return 0; +} +#endif /* CONFIG_ARM_SCMI_BASE_AGENT_HELPERS */ + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_scmi_base_cmds, + SHELL_CMD(revision, NULL, + "SCMI Base get revision info\n" + "Usage: arm_scmi base revision\n", + scmi_shell_base_revision), +#if defined(CONFIG_ARM_SCMI_BASE_AGENT_HELPERS) + SHELL_CMD_ARG(discover_agent, NULL, + "SCMI Base discover an agent\n" + "Usage: arm_scmi base discover_agent [agent_id]\n", + scmi_shell_base_discover_agent, 1, 2), + SHELL_CMD_ARG(device_permission, NULL, + "SCMI Base set an agent permissions to access device\n" + "Usage: arm_scmi base discover_agent \n", + scmi_shell_base_device_permission, 4, 0), + SHELL_CMD_ARG(reset_agent_cfg, NULL, + "SCMI Base reset an agent configuration\n" + "Usage: arm_scmi base reset_agent_cfg \n", + scmi_shell_base_reset_agent_cfg, 3, 0), +#endif /* CONFIG_ARM_SCMI_BASE_AGENT_HELPERS */ + SHELL_SUBCMD_SET_END); + +SHELL_SUBCMD_ADD((arm_scmi), base, &sub_scmi_base_cmds, "SCMI Base proto commands.", + NULL, 1, 0); + +SHELL_SUBCMD_SET_CREATE(subcmd_arm_scmi, (arm_scmi)); + +SHELL_CMD_REGISTER(arm_scmi, &subcmd_arm_scmi, "ARM SCMI commands", NULL); diff --git a/samples/drivers/firmware/arm_scmi/src/shell_reset.c b/samples/drivers/firmware/arm_scmi/src/shell_reset.c new file mode 100644 index 000000000000..cc21be1b7847 --- /dev/null +++ b/samples/drivers/firmware/arm_scmi/src/shell_reset.c @@ -0,0 +1,202 @@ +/* + * SCMI Reset domain management protocol Shell interface + * + * Copyright (c) 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#if defined(CONFIG_DT_HAS_ARM_SCMI_RESET_ENABLED) +static const struct device *reset_dev = DEVICE_DT_GET_ANY(arm_scmi_reset); +#else +BUILD_ASSERT(1, "unsupported scmi reset interface"); +#endif + +static int scmi_shell_reset_revision(const struct shell *sh, size_t argc, char **argv) +{ + struct scmi_protocol_version ver; + struct scmi_protocol *proto; + uint16_t num_domains; + int ret; + + proto = reset_dev->data; + + ret = scmi_core_get_version(proto, &ver); + if (ret) { + shell_error(sh, "reset get version failed (%d)", ret); + return ret; + } + + ret = scmi_reset_get_attr(proto, &num_domains); + if (ret) { + shell_error(sh, "reset get attributes failed (%d)", ret); + return ret; + } + + shell_print(sh, "ARM SCMI Reset protocol version 0x%04x.%04x num_domains:%u", ver.major, + ver.minor, num_domains); + + return 0; +} + +static int scmi_shell_reset_dom_list(const struct shell *sh, size_t argc, char **argv) +{ + struct scmi_reset_domain_attr dom_attr; + struct scmi_protocol *proto; + uint16_t num_domains; + uint16_t i; + int ret; + + proto = reset_dev->data; + + ret = scmi_reset_get_attr(proto, &num_domains); + if (ret) { + shell_error(sh, "reset get attributes failed (%d)", ret); + return ret; + } + + shell_print(sh, "domain_id,name,latency,attributes"); + + for (i = 0; i < num_domains; i++) { + ret = scmi_reset_domain_get_attr(proto, i, &dom_attr); + if (ret) { + shell_error(sh, "reset domain:%u get attributes failed (%d)", ret, i); + return ret; + } + + shell_print(sh, "%u,%s,0x%08x,async=%s,notify=%s,latency=%s", i, dom_attr.name, + dom_attr.latency, dom_attr.is_async_sup ? "yes" : "no", + dom_attr.is_notifications_sup ? "yes" : "no", + dom_attr.is_latency_valid ? "valid" : "invalid"); + } + + return 0; +} + +static int scmi_shell_reset_info(const struct shell *sh, size_t argc, char **argv) +{ + struct scmi_reset_domain_attr dom_attr; + struct scmi_protocol *proto; + uint16_t num_domains; + uint32_t domain_id; + int ret; + + proto = reset_dev->data; + + ret = scmi_reset_get_attr(proto, &num_domains); + if (ret) { + shell_error(sh, "reset get attributes failed (%d)", ret); + return ret; + } + + domain_id = atoi(argv[1]); + if (domain_id >= num_domains) { + shell_error(sh, "invalid reset domain index %s\n", argv[1]); + return -ENOENT; + } + + ret = scmi_reset_domain_get_attr(proto, domain_id, &dom_attr); + if (ret) { + shell_error(sh, "reset domain get attributes failed (%d)", ret); + return ret; + } + + shell_print(sh, "ARM SCMI reset domain: %u", domain_id); + shell_print(sh, " name\t\t: %s", dom_attr.name); + if (dom_attr.is_latency_valid) { + shell_print(sh, " latency\t: %u", dom_attr.latency); + } else { + shell_print(sh, " latency\t: invalid"); + } + shell_print(sh, " async\t\t: %s", dom_attr.is_async_sup ? "supported" : "not supported"); + shell_print(sh, " notifications\t\t: %s", + dom_attr.is_notifications_sup ? "supported" : "not supported"); + + return 0; +} + +static int scmi_shell_reset_assert(const struct shell *sh, size_t argc, char **argv) +{ + uint32_t domain_id; + int ret; + + domain_id = atoi(argv[1]); + + ret = reset_line_assert(reset_dev, domain_id); + if (ret) { + shell_error(sh, "reset domain:%u assert failed (%d)", domain_id, ret); + } else { + shell_info(sh, "reset domain:%u assert done", domain_id); + } + + return ret; +} + +static int scmi_shell_reset_deassert(const struct shell *sh, size_t argc, char **argv) +{ + uint32_t domain_id; + int ret; + + domain_id = atoi(argv[1]); + + ret = reset_line_deassert(reset_dev, domain_id); + if (ret) { + shell_error(sh, "reset domain:%u deassert failed (%d)", domain_id, ret); + } else { + shell_info(sh, "reset domain:%u deassert done", domain_id); + } + + return ret; +} + +static int scmi_shell_reset_toggle(const struct shell *sh, size_t argc, char **argv) +{ + uint32_t domain_id; + int ret; + + domain_id = atoi(argv[1]); + + ret = reset_line_toggle(reset_dev, domain_id); + if (ret) { + shell_error(sh, "reset domain:%u toggle failed (%d)", domain_id, ret); + } else { + shell_info(sh, "reset domain:%u toggle done", domain_id); + } + + return ret; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(sub_scmi_reset_cmds, + SHELL_CMD_ARG(revision, NULL, + "SCMI Reset proto show revision information\n" + "Usage: arm_scmi reset revision\n", + scmi_shell_reset_revision, 1, 0), + SHELL_CMD_ARG(list, NULL, + "SCMI Reset domains list\n" + "Usage: arm_scmi reset list\n", + scmi_shell_reset_dom_list, 1, 0), + SHELL_CMD_ARG(info, NULL, + "SCMI Reset domain show info\n" + "Usage: arm_scmi reset info \n", + scmi_shell_reset_info, 2, 0), + SHELL_CMD_ARG(assert, NULL, + "SCMI Reset domain assert\n" + "Usage: arm_scmi reset assert \n", + scmi_shell_reset_assert, 2, 0), + SHELL_CMD_ARG(deassert, NULL, + "SCMI Reset domain de-assert\n" + "Usage: arm_scmi reset deassert \n", + scmi_shell_reset_deassert, 2, 0), + SHELL_CMD_ARG(autoreset, NULL, + "SCMI Reset domain Autonomous reset\n" + "Usage: arm_scmi reset autoreset \n", + scmi_shell_reset_toggle, 2, 0), + SHELL_SUBCMD_SET_END); + +SHELL_SUBCMD_ADD((arm_scmi), reset, &sub_scmi_reset_cmds, + "SCMI Reset proto commands.", + NULL, 1, 1);