clk: add clock driver for SCMI agents

This change introduces a clock driver for SCMI agent devices. When
SCMI agent and SCMI clock drivers are enabled, SCMI agent binds a
clock device for each SCMI clock protocol devices enabled in the FDT.

SCMI clock driver is embedded upon CONFIG_CLK_SCMI=y. If enabled,
CONFIG_SCMI_AGENT is also enabled.

SCMI Clock protocol is defined in the SCMI specification [1].

Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi
Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
Cc: Lukasz Majewski <lukma@denx.de>
Cc: Simon Glass <sjg@chromium.org>
Cc: Peng Fan <peng.fan@nxp.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Etienne Carriere 2020-09-09 18:44:04 +02:00 committed by Tom Rini
parent 4e5ce7ecd3
commit 6038884483
5 changed files with 197 additions and 3 deletions

View File

@ -159,6 +159,14 @@ config CLK_CDCE9XX
Enable the clock synthesizer driver for CDCE913/925/937/949
series of chips.
config CLK_SCMI
bool "Enable SCMI clock driver"
depends on SCMI_FIRMWARE
help
Enable this option if you want to support clock devices exposed
by a SCMI agent based on SCMI clock protocol communication
with a SCMI server.
source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/at91/Kconfig"
source "drivers/clk/exynos/Kconfig"

View File

@ -32,6 +32,7 @@ obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o
obj-$(CONFIG_CLK_OWL) += owl/
obj-$(CONFIG_CLK_RENESAS) += renesas/
obj-$(CONFIG_CLK_SCMI) += clk_scmi.o
obj-$(CONFIG_CLK_SIFIVE) += sifive/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o

99
drivers/clk/clk_scmi.c Normal file
View File

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2019-2020 Linaro Limited
*/
#include <common.h>
#include <clk-uclass.h>
#include <dm.h>
#include <scmi_agent.h>
#include <scmi_protocols.h>
#include <asm/types.h>
static int scmi_clk_gate(struct clk *clk, int enable)
{
struct scmi_clk_state_in in = {
.clock_id = clk->id,
.attributes = enable,
};
struct scmi_clk_state_out out;
struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
SCMI_CLOCK_CONFIG_SET,
in, out);
int ret;
ret = devm_scmi_process_msg(clk->dev->parent, &msg);
if (ret)
return ret;
return scmi_to_linux_errno(out.status);
}
static int scmi_clk_enable(struct clk *clk)
{
return scmi_clk_gate(clk, 1);
}
static int scmi_clk_disable(struct clk *clk)
{
return scmi_clk_gate(clk, 0);
}
static ulong scmi_clk_get_rate(struct clk *clk)
{
struct scmi_clk_rate_get_in in = {
.clock_id = clk->id,
};
struct scmi_clk_rate_get_out out;
struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
SCMI_CLOCK_RATE_GET,
in, out);
int ret;
ret = devm_scmi_process_msg(clk->dev->parent, &msg);
if (ret < 0)
return ret;
ret = scmi_to_linux_errno(out.status);
if (ret < 0)
return ret;
return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
}
static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
{
struct scmi_clk_rate_set_in in = {
.clock_id = clk->id,
.flags = SCMI_CLK_RATE_ROUND_CLOSEST,
.rate_lsb = (u32)rate,
.rate_msb = (u32)((u64)rate >> 32),
};
struct scmi_clk_rate_set_out out;
struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
SCMI_CLOCK_RATE_SET,
in, out);
int ret;
ret = devm_scmi_process_msg(clk->dev->parent, &msg);
if (ret < 0)
return ret;
ret = scmi_to_linux_errno(out.status);
if (ret < 0)
return ret;
return scmi_clk_get_rate(clk);
}
static const struct clk_ops scmi_clk_ops = {
.enable = scmi_clk_enable,
.disable = scmi_clk_disable,
.get_rate = scmi_clk_get_rate,
.set_rate = scmi_clk_set_rate,
};
U_BOOT_DRIVER(scmi_clock) = {
.name = "scmi_clk",
.id = UCLASS_CLK,
.ops = &scmi_clk_ops,
};

View File

@ -58,9 +58,9 @@ static int scmi_bind_protocols(struct udevice *dev)
{
int ret = 0;
ofnode node;
struct driver *drv;
dev_for_each_subnode(node, dev) {
struct driver *drv = NULL;
u32 protocol_id;
if (!ofnode_is_available(node))
@ -70,9 +70,17 @@ static int scmi_bind_protocols(struct udevice *dev)
continue;
switch (protocol_id) {
case SCMI_PROTOCOL_ID_CLOCK:
if (IS_ENABLED(CONFIG_CLK_SCMI))
drv = DM_GET_DRIVER(scmi_clock);
break;
default:
dev_info(dev, "Ignore unsupported SCMI protocol %#x\n",
protocol_id);
break;
}
if (!drv) {
dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n",
protocol_id);
continue;
}

View File

@ -7,6 +7,7 @@
#define _SCMI_PROTOCOLS_H
#include <linux/bitops.h>
#include <asm/types.h>
/*
* Subset the SCMI protocols definition
@ -38,4 +39,81 @@ enum scmi_status_code {
SCMI_PROTOCOL_ERROR = -10,
};
/*
* SCMI Clock Protocol
*/
enum scmi_clock_message_id {
SCMI_CLOCK_RATE_SET = 0x5,
SCMI_CLOCK_RATE_GET = 0x6,
SCMI_CLOCK_CONFIG_SET = 0x7,
};
#define SCMI_CLK_RATE_ASYNC_NOTIFY BIT(0)
#define SCMI_CLK_RATE_ASYNC_NORESP (BIT(0) | BIT(1))
#define SCMI_CLK_RATE_ROUND_DOWN 0
#define SCMI_CLK_RATE_ROUND_UP BIT(2)
#define SCMI_CLK_RATE_ROUND_CLOSEST BIT(3)
/**
* struct scmi_clk_state_in - Message payload for CLOCK_CONFIG_SET command
* @clock_id: SCMI clock ID
* @attributes: Attributes of the targets clock state
*/
struct scmi_clk_state_in {
u32 clock_id;
u32 attributes;
};
/**
* struct scmi_clk_state_out - Response payload for CLOCK_CONFIG_SET command
* @status: SCMI command status
*/
struct scmi_clk_state_out {
s32 status;
};
/**
* struct scmi_clk_state_in - Message payload for CLOCK_RATE_GET command
* @clock_id: SCMI clock ID
* @attributes: Attributes of the targets clock state
*/
struct scmi_clk_rate_get_in {
u32 clock_id;
};
/**
* struct scmi_clk_rate_get_out - Response payload for CLOCK_RATE_GET command
* @status: SCMI command status
* @rate_lsb: 32bit LSB of the clock rate in Hertz
* @rate_msb: 32bit MSB of the clock rate in Hertz
*/
struct scmi_clk_rate_get_out {
s32 status;
u32 rate_lsb;
u32 rate_msb;
};
/**
* struct scmi_clk_state_in - Message payload for CLOCK_RATE_SET command
* @clock_id: SCMI clock ID
* @flags: Flags for the clock rate set request
* @rate_lsb: 32bit LSB of the clock rate in Hertz
* @rate_msb: 32bit MSB of the clock rate in Hertz
*/
struct scmi_clk_rate_set_in {
u32 clock_id;
u32 flags;
u32 rate_lsb;
u32 rate_msb;
};
/**
* struct scmi_clk_rate_set_out - Response payload for CLOCK_RATE_SET command
* @status: SCMI command status
*/
struct scmi_clk_rate_set_out {
s32 status;
};
#endif /* _SCMI_PROTOCOLS_H */