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"
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
--- /dev/null
+// 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,
+};
{
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))
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;
}
#define _SCMI_PROTOCOLS_H
#include <linux/bitops.h>
+#include <asm/types.h>
/*
* Subset the SCMI protocols definition
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 */