diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index 80b004aceb3cbb..caa240b0e6d679 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -4,6 +4,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_BEETLE beetle_clock_control.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_ADSP clock_control_adsp.c) +zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_ARM_SCMI clock_control_arm_scmi.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_ESP32 clock_control_esp32.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_FIXED_RATE_CLOCK clock_control_fixed_rate.c) zephyr_library_sources_ifdef(CONFIG_CLOCK_CONTROL_GD32 clock_control_gd32.c) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index c7f4493ac0ae8c..7d661a1cffe7eb 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -94,4 +94,6 @@ source "drivers/clock_control/Kconfig.rpi_pico" source "drivers/clock_control/Kconfig.nrf_auxpll" +source "drivers/clock_control/Kconfig.arm_scmi" + endif # CLOCK_CONTROL diff --git a/drivers/clock_control/Kconfig.arm_scmi b/drivers/clock_control/Kconfig.arm_scmi new file mode 100644 index 00000000000000..d8c7de05dae951 --- /dev/null +++ b/drivers/clock_control/Kconfig.arm_scmi @@ -0,0 +1,8 @@ +# Copyright 2024 NXP + +config CLOCK_CONTROL_ARM_SCMI + bool "SCMI clock protocol clock controller driver" + default y + depends on ARM_SCMI_CLK_HELPERS + help + Enable support for SCMI-based clock control. diff --git a/drivers/clock_control/clock_control_arm_scmi.c b/drivers/clock_control/clock_control_arm_scmi.c new file mode 100644 index 00000000000000..202f7eb05a8bb2 --- /dev/null +++ b/drivers/clock_control/clock_control_arm_scmi.c @@ -0,0 +1,102 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +LOG_MODULE_REGISTER(arm_scmi_clock); + +#define DT_DRV_COMPAT arm_scmi_clock + +struct scmi_clock_data { + uint32_t clk_num; +}; + +static int scmi_clock_on_off(const struct device *dev, + clock_control_subsys_t clk, bool on) +{ + struct scmi_clock_data *data; + struct scmi_protocol *proto; + uint32_t clk_id; + struct scmi_clock_config cfg; + + proto = dev->data; + data = proto->data; + clk_id = POINTER_TO_UINT(clk); + + if (clk_id >= data->clk_num) { + return -EINVAL; + } + + memset(&cfg, 0, sizeof(cfg)); + + cfg.attributes = SCMI_CLK_CONFIG_ENABLE_DISABLE(on); + cfg.clk_id = clk_id; + + return scmi_clock_config_set(proto, &cfg); +} + +static int scmi_clock_on(const struct device *dev, clock_control_subsys_t clk) +{ + return scmi_clock_on_off(dev, clk, true); +} + +static int scmi_clock_off(const struct device *dev, clock_control_subsys_t clk) +{ + return scmi_clock_on_off(dev, clk, false); +} + +static int scmi_clock_get_rate(const struct device *dev, + clock_control_subsys_t clk, uint32_t *rate) +{ + struct scmi_clock_data *data; + struct scmi_protocol *proto; + uint32_t clk_id; + + proto = dev->data; + data = proto->data; + clk_id = POINTER_TO_UINT(clk); + + if (clk_id >= data->clk_num) { + return -EINVAL; + } + + return scmi_clock_rate_get(proto, clk_id, rate); +} + +static struct clock_control_driver_api scmi_clock_api = { + .on = scmi_clock_on, + .off = scmi_clock_off, + .get_rate = scmi_clock_get_rate, +}; + +static int scmi_clock_init(const struct device *dev) +{ + struct scmi_protocol *proto; + struct scmi_clock_data *data; + int ret; + uint32_t attributes; + + proto = dev->data; + data = proto->data; + + ret = scmi_clock_protocol_attributes(proto, &attributes); + if (ret < 0) { + LOG_ERR("failed to fetch clock attributes: %d", ret); + return ret; + } + + data->clk_num = SCMI_CLK_ATTRIBUTES_CLK_NUM(attributes); + + return 0; +} + +static struct scmi_clock_data data; + +DT_INST_SCMI_PROTOCOL_DEFINE(0, &scmi_clock_init, NULL, &data, NULL, + PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, + &scmi_clock_api); diff --git a/drivers/firmware/scmi/CMakeLists.txt b/drivers/firmware/scmi/CMakeLists.txt index 09febb5d72130a..956bbf0aa82e37 100644 --- a/drivers/firmware/scmi/CMakeLists.txt +++ b/drivers/firmware/scmi/CMakeLists.txt @@ -6,3 +6,6 @@ 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) + +# SCMI protocol helper files +zephyr_library_sources_ifdef(CONFIG_ARM_SCMI_CLK_HELPERS clk.c) diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index 924dd3a8ded9ed..87b46250cc1609 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -3,6 +3,13 @@ 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 diff --git a/drivers/firmware/scmi/clk.c b/drivers/firmware/scmi/clk.c new file mode 100644 index 00000000000000..415349586eeb17 --- /dev/null +++ b/drivers/firmware/scmi/clk.c @@ -0,0 +1,152 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* TODO: if extended attributes are supported this should be moved + * to the header file so that users will have access to it. + */ +#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]; +}; + +int scmi_clock_rate_get(struct scmi_protocol *proto, + uint32_t clk_id, uint32_t *rate) +{ + struct scmi_message msg, reply; + int ret; + struct scmi_clock_rate_set_reply reply_buffer; + + /* sanity checks */ + if (!proto || !rate) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_CLOCK) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_CLOCK_RATE_GET, + SCMI_COMMAND, proto->id, 0x0); + msg.len = sizeof(clk_id); + msg.content = &clk_id; + + reply.hdr = msg.hdr; + reply.len = sizeof(reply_buffer); + reply.content = &reply_buffer; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + return ret; + } + + if (reply_buffer.status != SCMI_SUCCESS) { + return scmi_status_to_errno(reply_buffer.status); + } + + *rate = reply_buffer.rate[0]; + + return 0; +} + +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) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_CLOCK) { + return -EINVAL; + } + + /* extended attributes currently not supported */ + if (cfg->attributes & SCMI_CLK_CONFIG_EA_MASK) { + return -ENOTSUP; + } + + /* invalid because extended attributes are not supported */ + if (SCMI_CLK_CONFIG_ENABLE_DISABLE(cfg->attributes) == 3) { + return -ENOTSUP; + } + + /* this is a reserved value */ + if (SCMI_CLK_CONFIG_ENABLE_DISABLE(cfg->attributes) == 2) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_CLOCK_CONFIG_SET, + SCMI_COMMAND, proto->id, 0x0); + msg.len = sizeof(*cfg); + 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); + } + + return 0; +} + +int scmi_clock_protocol_attributes(struct scmi_protocol *proto, uint32_t *attributes) +{ + struct scmi_message msg, reply; + struct scmi_clock_attributes_reply reply_buffer; + int ret; + + /* sanity checks */ + if (!proto || !attributes) { + return -EINVAL; + } + + if (proto->id != SCMI_PROTOCOL_CLOCK) { + return -EINVAL; + } + + msg.hdr = SCMI_MESSAGE_HDR_MAKE(SCMI_CLK_MSG_PROTOCOL_ATTRIBUTES, + SCMI_COMMAND, proto->id, 0x0); + /* command has no parameters */ + msg.len = 0x0; + msg.content = NULL; + + reply.hdr = msg.hdr; + reply.len = sizeof(reply_buffer); + reply.content = &reply_buffer; + + ret = scmi_send_message(proto, &msg, &reply); + if (ret < 0) { + return ret; + } + + if (reply_buffer.status != 0) { + return scmi_status_to_errno(reply_buffer.status); + } + + *attributes = reply_buffer.attributes; + + return 0; +} diff --git a/dts/bindings/firmware/arm,scmi-clock.yaml b/dts/bindings/firmware/arm,scmi-clock.yaml new file mode 100644 index 00000000000000..5f0aa160d3fc7e --- /dev/null +++ b/dts/bindings/firmware/arm,scmi-clock.yaml @@ -0,0 +1,27 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +description: System Control and Management Interface (SCMI) clock protocol + +compatible: "arm,scmi-clock" + +include: [base.yaml, clock-controller.yaml] + +properties: + shmem: + type: phandles + + reg: + required: true + const: [0x14] + + "#clock-cells": + required: true + const: 1 + description: | + Vendor-specific clock identifier. Needs to match identifier + expected by the SCMI platform as this is directly used in SCMI + clock management messages. + +clock-cells: + - name diff --git a/include/zephyr/drivers/firmware/scmi/clk.h b/include/zephyr/drivers/firmware/scmi/clk.h new file mode 100644 index 00000000000000..e56fc9df24431c --- /dev/null +++ b/include/zephyr/drivers/firmware/scmi/clk.h @@ -0,0 +1,96 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief SCMI clock protocol helpers + */ + +#ifndef _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_CLK_H_ +#define _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_CLK_H_ + +#include + +#define SCMI_CLK_CONFIG_DISABLE_ENABLE_MASK GENMASK(1, 0) +#define SCMI_CLK_CONFIG_ENABLE_DISABLE(x)\ + ((uint32_t)(x) & SCMI_CLK_CONFIG_DISABLE_ENABLE_MASK) + +#define SCMI_CLK_ATTRIBUTES_CLK_NUM(x) ((x) & GENMASK(15, 0)) + +/** + * @struct scmi_clock_config + * + * @brief Describes the parameters for the CLOCK_CONFIG_SET + * command + */ +struct scmi_clock_config { + uint32_t clk_id; + uint32_t attributes; + uint32_t extended_cfg_val; +}; + +/** + * @brief Clock protocol command message IDs + */ +enum scmi_clock_message { + SCMI_CLK_MSG_PROTOCOL_VERSION = 0x0, + SCMI_CLK_MSG_PROTOCOL_ATTRIBUTES = 0x1, + SCMI_CLK_MSG_PROTOCOL_MESSAGE_ATTRIBUTES = 0x2, + SCMI_CLK_MSG_CLOCK_ATTRIBUTES = 0x3, + SCMI_CLK_MSG_CLOCK_DESCRIBE_RATES = 0x4, + SCMI_CLK_MSG_CLOCK_RATE_SET = 0x5, + SCMI_CLK_MSG_CLOCK_RATE_GET = 0x6, + SCMI_CLK_MSG_CLOCK_CONFIG_SET = 0x7, + SCMI_CLK_MSG_CLOCK_NAME_GET = 0x8, + SCMI_CLK_MSG_CLOCK_RATE_NOTIFY = 0x9, + SCMI_CLK_MSG_CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xa, + SCMI_CLK_MSG_CLOCK_CONFIG_GET = 0xb, + SCMI_CLK_MSG_CLOCK_POSSIBLE_PARENTS_GET = 0xc, + SCMI_CLK_MSG_CLOCK_PARENT_SET = 0xd, + SCMI_CLK_MSG_CLOCK_PARENT_GET = 0xe, + SCMI_CLK_MSG_CLOCK_GET_PERMISSIONS = 0xf, + SCMI_CLK_MSG_NEGOTIATE_PROTOCOL_VERSION = 0x10, +}; + +/** + * @brief Send the PROTOCOL_ATTRIBUTES command and get its reply + * + * @param proto pointer to SCMI clock protocol data + * @param attributes pointer to attributes to be set via + * this command + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_clock_protocol_attributes(struct scmi_protocol *proto, + uint32_t *attributes); + +/** + * @brief Send the CLOCK_CONFIG_SET command and get its reply + * + * @param proto pointer to SCMI clock protocol data + * @param cfg pointer to structure containing configuration + * to be set + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_clock_config_set(struct scmi_protocol *proto, + struct scmi_clock_config *cfg); +/** + * @brief Query the rate of a clock + * + * @param proto pointer to SCMI clock protocol data + * @param clk_id ID of the clock for which the query is done + * @param rate pointer to rate to be set via this command + * + * @retval 0 if successful + * @retval negative errno if failure + */ +int scmi_clock_rate_get(struct scmi_protocol *proto, + uint32_t clk_id, uint32_t *rate); + +#endif /* _INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_SCMI_CLK_H_ */