firmware: scmi: add support for clock management protocol
This includes: 1) Source containing helper functions, each implementing a command from the clock management protocol. 2) A clock controller driver making use of said helper functions and implementing the clock subsystem API. 3) A DT binding for clock protocol node. Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
This commit is contained in:
parent
413c77cf4e
commit
350e36a47a
9 changed files with 398 additions and 0 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
8
drivers/clock_control/Kconfig.arm_scmi
Normal file
8
drivers/clock_control/Kconfig.arm_scmi
Normal file
|
|
@ -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.
|
||||
102
drivers/clock_control/clock_control_arm_scmi.c
Normal file
102
drivers/clock_control/clock_control_arm_scmi.c
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/firmware/scmi/clk.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
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);
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
152
drivers/firmware/scmi/clk.c
Normal file
152
drivers/firmware/scmi/clk.c
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/firmware/scmi/clk.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
27
dts/bindings/firmware/arm,scmi-clock.yaml
Normal file
27
dts/bindings/firmware/arm,scmi-clock.yaml
Normal file
|
|
@ -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
|
||||
96
include/zephyr/drivers/firmware/scmi/clk.h
Normal file
96
include/zephyr/drivers/firmware/scmi/clk.h
Normal file
|
|
@ -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 <zephyr/drivers/firmware/scmi/protocol.h>
|
||||
|
||||
#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_ */
|
||||
Loading…
Reference in a new issue