U-Boot Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Charles Perry <charles.perry@microchip.com>
To: <u-boot@lists.denx.de>
Cc: Rahul Pathak <rahul@summations.net>,
	Anup Patel <anup@brainfault.org>,
	Charles Perry <charles.perry@microchip.com>,
	Tom Rini <trini@konsulko.com>, Lukasz Majewski <lukma@denx.de>,
	Neil Armstrong <neil.armstrong@linaro.org>,
	Simon Glass <sjg@chromium.org>,
	Kory Maincent <kory.maincent@bootlin.com>,
	Peng Fan <peng.fan@nxp.com>, Kuan-Wei Chiu <visitorckw@gmail.com>,
	"Raymond Mao" <raymond.mao@riscstar.com>,
	Quentin Schulz <quentin.schulz@cherry.de>,
	Stefan Roese <stefan.roese@mailbox.org>,
	Philip Molloy <philip.molloy@analog.com>,
	Jerome Forissier <jerome.forissier@arm.com>,
	Michal Simek <michal.simek@amd.com>,
	Michael Trimarchi <michael@amarulasolutions.com>,
	Peter Korsgaard <peter@korsgaard.com>
Subject: [PATCH 4/7] drivers: clk: add support for RPMI clocks
Date: Fri, 26 Jun 2026 13:15:45 -0700	[thread overview]
Message-ID: <20260626201613.1035208-5-charles.perry@microchip.com> (raw)
In-Reply-To: <20260626201613.1035208-1-charles.perry@microchip.com>

The RISC-V Platform Management Interface (RPMI) defines a service group
for control and monitoring of clocks [1]. This can be exposed as a
UCLASS_CLK driver.

[1]: https://github.com/riscv-non-isa/riscv-rpmi (chapter 4.8)

Signed-off-by: Charles Perry <charles.perry@microchip.com>
---
 MAINTAINERS            |   1 +
 drivers/clk/Kconfig    |   7 +
 drivers/clk/Makefile   |   1 +
 drivers/clk/clk_rpmi.c | 346 +++++++++++++++++++++++++++++++++++++++++
 include/rpmi_proto.h   |  39 +++++
 5 files changed, 394 insertions(+)
 create mode 100644 drivers/clk/clk_rpmi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 8b8ae8e6cc9f..0bf6582ef493 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1692,6 +1692,7 @@ F:	doc/README.rockusb
 RPMI
 M:	Charles Perry <charles.perry@microchip.com>
 S:	Maintained
+F:	drivers/clk/clk_rpmi.c
 F:	drivers/firmware/rpmi/
 F:	include/rpmi*
 
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c2da7b3938b6..c8cb4a4a71cd 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -181,6 +181,13 @@ config CLK_OCTEON
 	help
 	  Enable this to support the clocks on Octeon MIPS platforms.
 
+config CLK_RPMI
+	bool "Enable RPMI clock driver"
+	depends on CLK && RPMI_FIRMWARE
+	help
+	  Enable this option if you want to support clock devices exposed
+	  by RISC-V Platform Management Interface (RPMI).
+
 config SANDBOX_CLK_CCF
 	bool "Sandbox Common Clock Framework [CCF] support"
 	depends on SANDBOX
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 5f0c0d8a5c28..b0d9ca3cf9bb 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o
 obj-$(CONFIG_CLK_OWL) += owl/
 obj-$(CONFIG_CLK_QCOM) += qcom/
 obj-$(CONFIG_CLK_RENESAS) += renesas/
+obj-$(CONFIG_CLK_RPMI) += clk_rpmi.o
 obj-$(CONFIG_$(PHASE_)CLK_SCMI) += clk_scmi.o
 obj-$(CONFIG_CLK_SIFIVE) += sifive/
 obj-$(CONFIG_CLK_SOPHGO) += sophgo/
diff --git a/drivers/clk/clk_rpmi.c b/drivers/clk/clk_rpmi.c
new file mode 100644
index 000000000000..d2efb057600b
--- /dev/null
+++ b/drivers/clk/clk_rpmi.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Microchip Technology Inc. All rights reserved.
+ */
+
+#define LOG_CATEGORY UCLASS_CLK
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <asm/types.h>
+#include <linux/clk-provider.h>
+#include <rpmi.h>
+
+struct rpmi_get_num_clocks_rx {
+	__le32 status;
+	__le32 num_clocks;
+};
+
+struct rpmi_get_rate_rx {
+	__le32 status;
+	__le32 lo;
+	__le32 hi;
+};
+
+struct rpmi_get_config_rx {
+	__le32 status;
+	__le32 state;
+};
+
+struct rpmi_set_rate_tx {
+	__le32 clkid;
+	__le32 flags;
+	__le32 lo;
+	__le32 hi;
+};
+
+struct rpmi_set_config_tx {
+	__le32 clkid;
+	__le32 config;
+};
+
+struct rpmi_get_attrs_rx {
+	__le32 status;
+#define RPMI_CLK_TYPE_MASK GENMASK(1, 0)
+	__le32 flags;
+	__le32 num_rates;
+	__le32 transition_latency;
+	char name[RPMI_CLK_NAME_LEN];
+};
+
+struct rpmi_clk_def {
+	u32 num_rates;
+	u32 transition_latency;
+	enum rpmi_clock_type type;
+	char name[RPMI_CLK_NAME_LEN + 1];
+};
+
+struct rpmi_clk_priv {
+	struct rpmi_chan chan;
+	u32 num_clocks;
+	struct rpmi_clk_def *defs;
+};
+
+static int _rpmi_clk_enable_disable(struct rpmi_clk_priv *priv, u32 clkid,
+				    bool ena)
+{
+	struct rpmi_set_config_tx tx = {
+		.clkid = cpu_to_le32(clkid),
+		.config = cpu_to_le32(ena ? RPMI_CLK_STATE_ENABLED :
+					    RPMI_CLK_STATE_DISABLED),
+	};
+	__le32 rx;
+	int ret, status;
+
+	ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_SET_CONFIG, &tx,
+				  sizeof(tx), &rx, sizeof(rx), NULL);
+	if (ret)
+		return ret;
+
+	status = le32_to_cpu(rx);
+	if (status)
+		return rpmi_to_linux_error(status);
+
+	return 0;
+}
+
+static ulong _rpmi_clk_get_rate(struct rpmi_clk_priv *priv, u32 clkid)
+{
+	__le32 tx = cpu_to_le32(clkid);
+	struct rpmi_get_rate_rx rx;
+	int ret, status;
+
+	ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_GET_RATE, &tx,
+				  sizeof(tx), &rx, sizeof(rx), NULL);
+	if (ret)
+		return ret;
+
+	status = le32_to_cpu(rx.status);
+	if (status)
+		return rpmi_to_linux_error(status);
+
+	return (((u64)(le32_to_cpu(rx.hi)) << 32) | (u32)(le32_to_cpu(rx.lo)));
+}
+
+static __maybe_unused int _rpmi_clk_get_config(struct rpmi_clk_priv *priv,
+					       u32 clkid)
+{
+	__le32 tx = cpu_to_le32(clkid);
+	struct rpmi_get_config_rx rx;
+	int ret, status;
+
+	ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_GET_CONFIG, &tx,
+				  sizeof(tx), &rx, sizeof(rx), NULL);
+	if (ret)
+		return ret;
+
+	status = le32_to_cpu(rx.status);
+	if (status)
+		return rpmi_to_linux_error(status);
+
+	return le32_to_cpu(rx.state);
+}
+
+static int _rpmi_clk_set_rate(struct rpmi_clk_priv *priv, u32 clkid,
+			      unsigned long rate)
+{
+	struct rpmi_set_rate_tx tx = {
+		.clkid = cpu_to_le32(clkid),
+		.flags = 0,
+		.lo = cpu_to_le32(lower_32_bits(rate)),
+		.hi = cpu_to_le32(upper_32_bits(rate)),
+	};
+	__le32 rx;
+	int ret, status;
+
+	ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_SET_RATE, &tx,
+				  sizeof(tx), &rx, sizeof(rx), NULL);
+	if (ret)
+		return ret;
+
+	status = le32_to_cpu(rx);
+	if (status)
+		return rpmi_to_linux_error(status);
+
+	return _rpmi_clk_get_rate(priv, clkid);
+}
+
+static int _rpmi_clk_get_num_clocks(struct rpmi_clk_priv *priv)
+{
+	struct rpmi_get_num_clocks_rx rx;
+	int ret, status;
+
+	ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_GET_NUM_CLOCKS,
+				  NULL, 0, &rx, sizeof(rx), NULL);
+	if (ret)
+		return ret;
+
+	status = le32_to_cpu(rx.status);
+	if (status)
+		return rpmi_to_linux_error(status);
+
+	priv->num_clocks = le32_to_cpu(rx.num_clocks);
+
+	return 0;
+}
+
+static int rpmi_clk_enable(struct clk *clk)
+{
+	return _rpmi_clk_enable_disable(dev_get_plat(clk->dev),
+					clk->id & CLK_ID_MSK, true);
+}
+
+static int rpmi_clk_disable(struct clk *clk)
+{
+	return _rpmi_clk_enable_disable(dev_get_plat(clk->dev),
+					clk->id & CLK_ID_MSK, false);
+}
+
+static ulong rpmi_clk_get_rate(struct clk *clk)
+{
+	return _rpmi_clk_get_rate(dev_get_plat(clk->dev), clk->id & CLK_ID_MSK);
+}
+
+static ulong rpmi_clk_set_rate(struct clk *clk, ulong rate)
+{
+	return _rpmi_clk_set_rate(dev_get_plat(clk->dev), clk->id & CLK_ID_MSK,
+				  rate);
+}
+
+static __maybe_unused void rpmi_clk_dump(struct udevice *dev)
+{
+	struct rpmi_clk_priv *priv = (struct rpmi_clk_priv *)dev_get_plat(dev);
+	struct rpmi_clk_def *def;
+	u64 clk_rate = 0;
+	int i, state;
+
+	printf("              Rate State  Id Name\n");
+	printf("--------------------------------------\n");
+
+	for (i = 0; i < priv->num_clocks; i++) {
+		def = &priv->defs[i];
+		clk_rate = _rpmi_clk_get_rate(priv, i);
+		state = _rpmi_clk_get_config(priv, i);
+		printf("%18lld %s %4i %s\n", clk_rate,
+		       (state == RPMI_CLK_STATE_ENABLED) ? "   ON" : "  OFF", i,
+		       def->name);
+	}
+}
+
+static int rpmi_clk_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+	struct rpmi_clk_priv *priv = dev_get_plat(clk->dev);
+
+	if (args->args_count != 1)
+		return -EINVAL;
+
+	if (args->args[0] >= priv->num_clocks)
+		return -EINVAL;
+
+	clk->id = CLK_ID(clk->dev, args->args[0]);
+	clk->data = 0;
+
+	return 0;
+}
+
+static int rpmi_clk_get_attrs(struct rpmi_clk_priv *priv, u32 clkid,
+			      struct rpmi_clk_def *def)
+{
+	__le32 tx = cpu_to_le32(clkid);
+	struct rpmi_get_attrs_rx rx;
+	u8 format;
+	int ret, status;
+
+	ret = rpmi_send_with_resp(&priv->chan, RPMI_CLK_SRV_GET_ATTRIBUTES, &tx,
+				  sizeof(tx), &rx, sizeof(rx), NULL);
+	if (ret)
+		return ret;
+
+	status = le32_to_cpu(rx.status);
+	if (status)
+		return rpmi_to_linux_error(status);
+
+	format = le32_to_cpu(rx.flags) & RPMI_CLK_TYPE_MASK;
+	if (format >= RPMI_CLK_TYPE_MAX)
+		return -EINVAL;
+
+	def->type = format;
+	def->num_rates = le32_to_cpu(rx.num_rates);
+	def->transition_latency = le32_to_cpu(rx.transition_latency);
+	memcpy(def->name, rx.name, RPMI_CLK_NAME_LEN);
+	def->name[RPMI_CLK_NAME_LEN] = '\0';
+
+	return 0;
+}
+
+static int rpmi_clk_probe(struct udevice *dev)
+{
+	struct rpmi_clk_priv *priv = (struct rpmi_clk_priv *)dev_get_plat(dev);
+	struct rpmi_chan *chan = &priv->chan;
+	int ret, i;
+
+	ret = rpmi_open(chan);
+	if (ret)
+		return ret;
+
+	ret = rpmi_check_versions(chan, RPMI_MKVER(1, 0), RPMI_SRVGRP_CLOCK,
+				  RPMI_MKVER(1, 0));
+	if (ret)
+		goto err_out;
+
+	ret = _rpmi_clk_get_num_clocks(priv);
+	if (ret)
+		goto err_out;
+
+	priv->defs = calloc(priv->num_clocks, sizeof(struct rpmi_clk_def));
+	if (!priv->defs) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+
+	for (i = 0; i < priv->num_clocks; i++) {
+		ret = rpmi_clk_get_attrs(priv, i, &priv->defs[i]);
+		if (ret)
+			goto err_out;
+	}
+
+	dev_dbg(dev, "num clock = %i\n", priv->num_clocks);
+
+	return 0;
+
+err_out:
+	if (priv->defs)
+		free(priv->defs);
+	rpmi_close(chan);
+
+	return ret;
+}
+
+static int rpmi_clk_remove(struct udevice *dev)
+{
+	struct rpmi_clk_priv *priv = (struct rpmi_clk_priv *)dev_get_plat(dev);
+	struct rpmi_chan *chan = &priv->chan;
+
+	if (priv->defs)
+		free(priv->defs);
+	rpmi_close(chan);
+
+	return 0;
+}
+
+static int rpmi_clk_to_plat(struct udevice *dev)
+{
+	struct rpmi_clk_priv *priv = (struct rpmi_clk_priv *)dev_get_plat(dev);
+
+	return rpmi_get_by_index(dev, 0, &priv->chan);
+}
+
+static struct clk_ops rpmi_clk_ops = {
+	.of_xlate = rpmi_clk_xlate,
+	.set_rate = rpmi_clk_set_rate,
+	.get_rate = rpmi_clk_get_rate,
+	.enable = rpmi_clk_enable,
+	.disable = rpmi_clk_disable,
+#if IS_ENABLED(CONFIG_CMD_CLK)
+	.dump = rpmi_clk_dump,
+#endif
+};
+
+static const struct udevice_id rpmi_clk_ids[] = {
+	{ .compatible = "riscv,rpmi-clock" },
+	{}
+};
+
+U_BOOT_DRIVER(rpmi_clk) = {
+	.name = "rpmi-clk",
+	.id = UCLASS_CLK,
+	.of_match = rpmi_clk_ids,
+	.probe = rpmi_clk_probe,
+	.remove = rpmi_clk_remove,
+	.ops = &rpmi_clk_ops,
+	.of_to_plat = rpmi_clk_to_plat,
+	.plat_auto = sizeof(struct rpmi_clk_priv),
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/rpmi_proto.h b/include/rpmi_proto.h
index 4b557bfeedf5..dc0937ce7053 100644
--- a/include/rpmi_proto.h
+++ b/include/rpmi_proto.h
@@ -83,4 +83,43 @@ enum rpmi_message_type {
 	RPMI_MSG_NOTIFICATION = 0x3,
 };
 
+/* RPMI clock service IDs */
+enum rpmi_clock_service_id {
+	RPMI_CLK_SRV_ENABLE_NOTIFICATION = 0x01,
+	RPMI_CLK_SRV_GET_NUM_CLOCKS = 0x02,
+	RPMI_CLK_SRV_GET_ATTRIBUTES = 0x03,
+	RPMI_CLK_SRV_GET_SUPPORTED_RATES = 0x04,
+	RPMI_CLK_SRV_SET_CONFIG = 0x05,
+	RPMI_CLK_SRV_GET_CONFIG = 0x06,
+	RPMI_CLK_SRV_SET_RATE = 0x07,
+	RPMI_CLK_SRV_GET_RATE = 0x08,
+	RPMI_CLK_SRV_ID_MAX_COUNT
+};
+
+/** Clock rate match mode */
+enum rpmi_clock_rate_match {
+	RPMI_CLK_RATE_MATCH_PLATFORM	= 0,
+	RPMI_CLK_RATE_MATCH_ROUND_DOWN	= 1,
+	RPMI_CLK_RATE_MATCH_ROUND_UP	= 2,
+	RPMI_CLK_RATE_MATCH_MAX
+};
+
+/** Supported clock states */
+enum rpmi_clock_state {
+	RPMI_CLK_STATE_DISABLED	= 0,
+	RPMI_CLK_STATE_ENABLED	= 1,
+	RPMI_CLK_STATE_MAX
+};
+
+/** Clock type based on rate format */
+enum rpmi_clock_type {
+	RPMI_CLK_TYPE_DISCRETE	= 0,
+	RPMI_CLK_TYPE_LINEAR	= 1,
+	RPMI_CLK_TYPE_MAX
+};
+
+#define RPMI_CLK_NAME_LEN 16
+
+#define RPMI_CLOCK_RATE_INVALID		(-1ULL)
+
 #endif
-- 
2.47.3


  parent reply	other threads:[~2026-06-26 21:20 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-26 20:15 [PATCH 0/7] Add support for RPMI to U-Boot Charles Perry
2026-06-26 20:15 ` [PATCH 1/7] firmware: add support for RPMI Charles Perry
2026-06-27  1:09   ` Yao Zi
2026-06-26 20:15 ` [PATCH 2/7] firmware: rpmi: add support for the SBI MPXY transport Charles Perry
2026-06-27  1:30   ` Yao Zi
2026-06-26 20:15 ` [PATCH 3/7] firmware: rpmi: add support for shared memory transport Charles Perry
2026-06-26 20:15 ` Charles Perry [this message]
2026-06-27  1:47   ` [PATCH 4/7] drivers: clk: add support for RPMI clocks Yao Zi
2026-06-26 20:15 ` [PATCH 5/7] drivers: power: add support for RPMI power domains Charles Perry
2026-06-26 20:15 ` [PATCH 6/7] firmware: rpmi: add a test and a sandbox driver Charles Perry
2026-06-26 20:15 ` [PATCH 7/7] firmware: rpmi: add a test and sandbox for device power Charles Perry

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260626201613.1035208-5-charles.perry@microchip.com \
    --to=charles.perry@microchip.com \
    --cc=anup@brainfault.org \
    --cc=jerome.forissier@arm.com \
    --cc=kory.maincent@bootlin.com \
    --cc=lukma@denx.de \
    --cc=michael@amarulasolutions.com \
    --cc=michal.simek@amd.com \
    --cc=neil.armstrong@linaro.org \
    --cc=peng.fan@nxp.com \
    --cc=peter@korsgaard.com \
    --cc=philip.molloy@analog.com \
    --cc=quentin.schulz@cherry.de \
    --cc=rahul@summations.net \
    --cc=raymond.mao@riscstar.com \
    --cc=sjg@chromium.org \
    --cc=stefan.roese@mailbox.org \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.denx.de \
    --cc=visitorckw@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox