From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id CD081C43458 for ; Fri, 26 Jun 2026 21:20:53 +0000 (UTC) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id BB8BE849C5; Fri, 26 Jun 2026 23:20:22 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=reject dis=none) header.from=microchip.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=microchip.com header.i=@microchip.com header.b="jTFaT2XD"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 4AA548404D; Fri, 26 Jun 2026 22:17:25 +0200 (CEST) Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.153.233]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id CBD6084105 for ; Fri, 26 Jun 2026 22:17:21 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=reject dis=none) header.from=microchip.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=prvs=6300e006a=Charles.Perry@microchip.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1782505042; x=1814041042; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=birHorcVCgZ283r+3wH/o/OBGTwG5trx3rqsjdeiLQI=; b=jTFaT2XDdUn+/yD7dK9aYy+pw7ss0OfvxtTOWvu5yPnXK+V72kF2X9c3 dbX5mi32JNj+YMnfkdZgqOsLlGpCIzupJgI+quEf4oz87zCFLKIbRJRUA h3VR2ttMA9UaR+d59jO++NssOBh7YvkBz0+svD+ytzVTsesZgYWgopcL2 9TX33zGx8Y4zK7J4R6O38CtsiZn3reTXd06lSPx/Y2CohV2+SIiqiaw1h LIkHd7hM/qMGagodFd4JfGe1c9fGjdSxfk9kUcucxKVrcK2bTRafUDsHk WTw97FxN6CiqdX48kcQbDWgocf8/DBzW0dllxb4wsZXqumHh+HJ8p/TgB Q==; X-CSE-ConnectionGUID: 6ecAlqwPRLGKg2Ko+L2LyA== X-CSE-MsgGUID: wJ8jkaeRTWeEH7qiGo/H+w== X-IronPort-AV: E=Sophos;i="6.24,227,1774335600"; d="scan'208";a="59747553" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa3.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Jun 2026 13:17:19 -0700 Received: from chn-vm-ex04.mchp-main.com (10.10.87.151) by chn-vm-ex2.mchp-main.com (10.10.87.31) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.2.2562.43; Fri, 26 Jun 2026 13:17:19 -0700 Received: from bby-cbu-swbuild03.eng.microchip.com (10.10.85.11) by chn-vm-ex04.mchp-main.com (10.10.85.152) with Microsoft SMTP Server id 15.1.2507.58 via Frontend Transport; Fri, 26 Jun 2026 13:17:18 -0700 From: Charles Perry To: CC: Rahul Pathak , Anup Patel , Charles Perry , Tom Rini , Lukasz Majewski , Neil Armstrong , Simon Glass , Kory Maincent , Peng Fan , Kuan-Wei Chiu , "Raymond Mao" , Quentin Schulz , Stefan Roese , Philip Molloy , Jerome Forissier , Michal Simek , Michael Trimarchi , Peter Korsgaard Subject: [PATCH 4/7] drivers: clk: add support for RPMI clocks Date: Fri, 26 Jun 2026 13:15:45 -0700 Message-ID: <20260626201613.1035208-5-charles.perry@microchip.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260626201613.1035208-1-charles.perry@microchip.com> References: <20260626201613.1035208-1-charles.perry@microchip.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Mailman-Approved-At: Fri, 26 Jun 2026 23:20:19 +0200 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean 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 --- 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 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 +#include +#include +#include +#include +#include +#include + +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