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
next prev 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.