aboutsummaryrefslogtreecommitdiff
path: root/drivers/clk/clk_scmi.c
diff options
context:
space:
mode:
authorEtienne Carriere2020-09-09 18:44:04 +0200
committerTom Rini2020-09-30 11:55:23 -0400
commit60388844836f5639e6c9a4331335ff22298128da (patch)
treeb5f9e60ab477d740c2542cd123d0d81eb0c862dd /drivers/clk/clk_scmi.c
parent4e5ce7ecd3ed734245b00a4068d6c86009e8b7ab (diff)
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>
Diffstat (limited to 'drivers/clk/clk_scmi.c')
-rw-r--r--drivers/clk/clk_scmi.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c
new file mode 100644
index 00000000000..93a4819501c
--- /dev/null
+++ b/drivers/clk/clk_scmi.c
@@ -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,
+};