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:
Laurentiu Mihalcea 2024-08-07 11:05:12 +03:00 committed by Anas Nashif
parent 413c77cf4e
commit 350e36a47a
9 changed files with 398 additions and 0 deletions

View file

@ -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)

View file

@ -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

View 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.

View 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);

View file

@ -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)

View file

@ -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
View 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;
}

View 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

View 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_ */