From fec1e5dff2e66c319667d24e5334549c5c5da7c4 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 6 Aug 2024 14:51:51 +0300 Subject: [PATCH] drivers: reset: scmi: add arm scmi reset driver This patch adds reset driver which implements ARM SCMI Reset domain management protocol and exposes Zephyr Reset API. The current implementation supports only sync operations. Signed-off-by: Grygorii Strashko --- drivers/reset/CMakeLists.txt | 1 + drivers/reset/Kconfig | 1 + drivers/reset/Kconfig.arm_scmi | 9 ++ drivers/reset/reset_arm_scmi.c | 192 +++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+) create mode 100644 drivers/reset/Kconfig.arm_scmi create mode 100644 drivers/reset/reset_arm_scmi.c diff --git a/drivers/reset/CMakeLists.txt b/drivers/reset/CMakeLists.txt index 36c68367d39c806..d446f5747c78dd5 100644 --- a/drivers/reset/CMakeLists.txt +++ b/drivers/reset/CMakeLists.txt @@ -9,3 +9,4 @@ zephyr_library_sources_ifdef(CONFIG_RESET_AST10X0 reset_ast10x0.c) zephyr_library_sources_ifdef(CONFIG_RESET_STM32 reset_stm32.c) zephyr_library_sources_ifdef(CONFIG_RESET_NUMAKER reset_numaker.c) zephyr_library_sources_ifdef(CONFIG_RESET_INTEL_SOCFPGA reset_intel_socfpga.c) +zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_RESET reset_arm_scmi.c) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index f940583c9749322..b5e21a512c52b75 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -33,5 +33,6 @@ rsource "Kconfig.aspeed" rsource "Kconfig.stm32" rsource "Kconfig.numaker" rsource "Kconfig.intel_socfpga" +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 000000000000000..38ad688933836e6 --- /dev/null +++ b/drivers/reset/Kconfig.arm_scmi @@ -0,0 +1,9 @@ +# Copyright (c) 2024 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +config ARM_SCMI_RESET + bool "ARM SCMI based reset driver" + depends on DT_HAS_ARM_SCMI_RESET_ENABLED + 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 000000000000000..cdbd6c6815ea323 --- /dev/null +++ b/drivers/reset/reset_arm_scmi.c @@ -0,0 +1,192 @@ +/* + * Copyright 2024 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +LOG_MODULE_REGISTER(arm_scmi_reset, CONFIG_ARM_SCMI_LOG_LEVEL); + +#define DT_DRV_COMPAT arm_scmi_reset + +/* + * SCMI Reset protocol + */ +#define SCMI_PROTOCOL_RESET_REV_MAJOR 0x1 + +enum scmi_reset_protocol_cmd { + SCMI_RESET_DOMAIN_ATTRIBUTES = 0x3, + SCMI_RESET = 0x4, + SCMI_RESET_NOTIFY = 0x5, + SCMI_RESET_DOMAIN_NAME_GET = 0x6, +}; + +/* Reset PROTOCOL_ATTRIBUTES cmd response */ +struct scmi_msg_reset_attributes_resp { + 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 cmd request */ +struct scmi_msg_reset_domain_attr_req { + uint32_t domain_id; +} __packed; + +/* RESET_DOMAIN_ATTRIBUTES cmd response */ +struct scmi_msg_reset_domain_attr_resp { + 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 cmd response */ +struct scmi_msg_reset_domain_reset_req { + 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; + +/* scmi reset driver definitions */ +struct scmi_reset_config { + const struct device *scmi_dev; +}; + +struct scmi_reset_drv_data { + struct scmi_msg_prot_version_resp version; + uint16_t num_domains; +}; + +static int scmi_reset_proto_attr_get(const struct device *scmi_dev, uint16_t *num_domains) +{ + struct scmi_msg_reset_attributes_resp attr = {0}; + int ret; + + ret = scmi_xfer_no_tx(scmi_dev, SCMI_PROTOCOL_RESET, SCMI_PROTOCOL_ATTRIBUTES, + (uint8_t *)&attr, sizeof(struct scmi_msg_reset_attributes_resp)); + if (ret) { + LOG_ERR("reset proto get attributes failed (%d)", ret); + return ret; + } + + *num_domains = SCMI_RESET_ATTR_GET_NUM_DOMAINS(sys_le32_to_cpu(attr.attributes)); + + return 0; +} + +static int scmi_reset_line_assert(const struct device *dev, uint32_t id) +{ + const struct scmi_reset_config *cfg = dev->config; + struct scmi_msg_reset_domain_reset_req reset_cmd; + int ret; + + reset_cmd.domain_id = sys_cpu_to_le32(id); + reset_cmd.flags = sys_cpu_to_le32(SCMI_RESET_EXPLICIT_ASSERT); + reset_cmd.reset_state = sys_cpu_to_le32(SCMI_RESET_ARCH_COLD_RESET); + + ret = scmi_xfer_no_rx(cfg->scmi_dev, SCMI_PROTOCOL_RESET, SCMI_RESET, (uint8_t *)&reset_cmd, + sizeof(struct scmi_msg_reset_domain_reset_req)); + if (ret) { + LOG_ERR("scmi reset:%u assert failed (%d)", id, ret); + } + + LOG_DBG("scmi reset:%u assert", id); + + return ret; +} + +static int scmi_reset_line_deassert(const struct device *dev, uint32_t id) +{ + const struct scmi_reset_config *cfg = dev->config; + struct scmi_msg_reset_domain_reset_req reset_cmd; + int ret; + + reset_cmd.domain_id = sys_cpu_to_le32(id); + reset_cmd.flags = 0; + reset_cmd.reset_state = sys_cpu_to_le32(SCMI_RESET_ARCH_COLD_RESET); + + ret = scmi_xfer_no_rx(cfg->scmi_dev, SCMI_PROTOCOL_RESET, SCMI_RESET, (uint8_t *)&reset_cmd, + sizeof(struct scmi_msg_reset_domain_reset_req)); + if (ret) { + LOG_ERR("scmi reset deassert failed (%d)", ret); + } + + LOG_DBG("scmi reset:%u deassert", id); + + return ret; +} + +static int scmi_reset_line_toggle(const struct device *dev, uint32_t id) +{ + const struct scmi_reset_config *cfg = dev->config; + struct scmi_msg_reset_domain_reset_req reset_cmd; + int ret; + + reset_cmd.domain_id = sys_cpu_to_le32(id); + reset_cmd.flags = sys_cpu_to_le32(SCMI_RESET_AUTONOMOUS); + reset_cmd.reset_state = sys_cpu_to_le32(SCMI_RESET_ARCH_COLD_RESET); + + ret = scmi_xfer_no_rx(cfg->scmi_dev, SCMI_PROTOCOL_RESET, SCMI_RESET, (uint8_t *)&reset_cmd, + sizeof(struct scmi_msg_reset_domain_reset_req)); + if (ret) { + LOG_ERR("scmi reset toggle failed (%d)", ret); + } + + LOG_DBG("scmi reset:%u toggle", id); + + return ret; +} + +static int scmi_reset_init(const struct device *dev) +{ + const struct scmi_reset_config *cfg = dev->config; + struct scmi_reset_drv_data *data = dev->data; + int ret; + + ret = scmi_proto_get_version(cfg->scmi_dev, SCMI_PROTOCOL_RESET, &data->version); + if (ret) { + return ret; + } + + if (data->version.ver.major != SCMI_PROTOCOL_RESET_REV_MAJOR) { + LOG_ERR("unsupported reset protocol version 0x%08x", data->version.version); + return -ENOTSUP; + } + + ret = scmi_reset_proto_attr_get(cfg->scmi_dev, &data->num_domains); + if (ret) { + return ret; + } + + LOG_INF("scmi reset rotocol version 0x%04x.%04x num_domains:%u", data->version.ver.major, + data->version.ver.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; + +static const struct scmi_reset_config scmi_reset_config = { + .scmi_dev = DEVICE_DT_GET(DT_INST_PARENT(0)), +}; + +DEVICE_DT_INST_DEFINE(0, scmi_reset_init, NULL, &scmi_reset_data, &scmi_reset_config, PRE_KERNEL_2, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &scmi_reset_driver_api);