[PATCH RFC 2/2] clk: scmi: Add support for two #clock-cells to pass rate rounding mode

Peng Fan (OSS)
2026-03-06T06:18:58Z

From: Peng Fan <peng.fan@nxp.com>

SCMI CLOCK_RATE_SET allows the caller to specify the rounding behaviour
when setting a clock rate. The previously added dt-bindings header
defines three modes:

ROUND_DOWN / ROUND_UP / ROUND_AUTO

To enable device tree clients to select a rounding mode, extend the
SCMI clock provider to support "#clock-cells = <2>", where the second
cell encodes the desired rounding mode. The default remains
ROUND_DOWN for backwards compatibility with existing device trees.

When two cells are used, scmi_clk_two_cells_get() extracts the rounding
mode and stores it per clock. The SCMI clk driver then passes this
value to the SCMI Clock protocol, which maps it to the appropriate
CLOCK_SET_* flag.

Existing DTs using "#clock-cells = <1>" are also being supported.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
drivers/clk/clk-scmi.c | 62 +++++++++++++++++++++++++++++++++++++--
drivers/firmware/arm_scmi/clock.c | 15 ++++++++--
include/linux/scmi_protocol.h | 8 ++++-
3 files changed, 79 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index 6b286ea6f1218c802d0ebb782c75a19057581c20..16547a1fa1a0f1595323b0f89753b38315743150 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -5,6 +5,7 @@
* Copyright (C) 2018-2024 ARM Ltd.
*/

+#include <dt-bindings/clock/scmi.h>
#include <linux/bits.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
@@ -32,6 +33,8 @@ static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;

struct scmi_clk {
u32 id;
+ u32 round;
+ bool round_set; /* policy latched once */
struct device *dev;
struct clk_hw hw;
const struct scmi_clock_info *info;
@@ -94,8 +97,20 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct scmi_clk *clk = to_scmi_clk(hw);
+ u32 round;
+
+ switch (clk->round) {
+ case ROUND_UP:
+ round = SCMI_CLOCK_ROUND_UP;
+ break;
+ case ROUND_AUTO:
+ round = SCMI_CLOCK_ROUND_AUTO;
+ break;
+ default:
+ round = SCMI_CLOCK_ROUND_DOWN;
+ }

- return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, rate);
+ return scmi_proto_clk_ops->rate_set(clk->ph, clk->id, round, rate);
}

static int scmi_clk_set_parent(struct clk_hw *hw, u8 parent_index)
@@ -396,6 +411,41 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
return ops;
}

+static struct clk_hw *
+scmi_clk_two_cells_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct clk_hw_onecell_data *hw_data = data;
+ unsigned int idx = clkspec->args[0];
+ u32 round = clkspec->args[1];
+ struct scmi_clk *clk;
+ struct clk_hw *hw;
+
+ if (idx >= hw_data->num) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (round > ROUND_AUTO) {
+ pr_err("%s: invalid round method %u\n", __func__, round);
+ return ERR_PTR(-EINVAL);
+ }
+
+ hw = hw_data->hws[idx];
+ clk = to_scmi_clk(hw);
+
+ /* per-clock policy: latch on first use, refuse conflicts */
+ if (clk->round_set && clk->round != round) {
+ pr_warn("%s: conflicting rounding mode for clk idx %u: %u != %u\n",
+ __func__, idx, clk->round, round);
+ return ERR_PTR(-EINVAL);
+ }
+
+ clk->round = round;
+ clk->round_set = true;
+
+ return hw;
+}
+
static int scmi_clocks_probe(struct scmi_device *sdev)
{
int idx, count, err;
@@ -409,6 +459,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
struct scmi_protocol_handle *ph;
const struct clk_ops *scmi_clk_ops_db[SCMI_MAX_CLK_OPS] = {};
struct scmi_clk *sclks;
+ u32 cells = 1;

if (!handle)
return -ENODEV;
@@ -456,6 +507,8 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
sclk->id = idx;
sclk->ph = ph;
sclk->dev = dev;
+ sclk->round = ROUND_DOWN;
+ sclk->round_set = false;

/*
* Note that the scmi_clk_ops_db is on the stack, not global,
@@ -495,8 +548,11 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
}
}

- return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
- clk_data);
+ of_property_read_u32(np, "#clock-cells", &cells);
+ if (cells == 2)
+ return devm_of_clk_add_hw_provider(dev, scmi_clk_two_cells_get, clk_data);
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
}

static const struct scmi_device_id scmi_id_table[] = {
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index ab36871650a1ff890c4cb7f67d3ded2622a72868..1548b6611f7f6c4ac60e740bb36f2377568d06dd 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -570,10 +570,10 @@ scmi_clock_rate_get(const struct scmi_protocol_handle *ph,
}

static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
- u32 clk_id, u64 rate)
+ u32 clk_id, u32 round, u64 rate)
{
int ret;
- u32 flags = 0;
+ u32 flags;
struct scmi_xfer *t;
struct scmi_clock_set_rate *cfg;
struct clock_info *ci = ph->get_priv(ph);
@@ -590,6 +590,17 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
if (ret)
return ret;

+ switch (round) {
+ case SCMI_CLOCK_ROUND_UP:
+ flags = CLOCK_SET_ROUND_UP;
+ break;
+ case SCMI_CLOCK_ROUND_AUTO:
+ flags = CLOCK_SET_ROUND_AUTO;
+ break;
+ default:
+ flags = 0;
+ }
+
if (ci->max_async_req &&
atomic_inc_return(&ci->cur_async_req) < ci->max_async_req)
flags |= CLOCK_SET_ASYNC;
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index aafaac1496b06a6e4f0ca32eee58a9edf7d4a70f..d0b7186177f49dea9c4b0030927782e6fd819ad0 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -83,6 +83,12 @@ enum scmi_clock_oem_config {
SCMI_CLOCK_CFG_OEM_END = 0xFF,
};

+enum scmi_clock_round {
+ SCMI_CLOCK_ROUND_DOWN = 0x0,
+ SCMI_CLOCK_ROUND_UP = 0x1,
+ SCMI_CLOCK_ROUND_AUTO = 0x2,
+};
+
/**
* struct scmi_clk_proto_ops - represents the various operations provided
* by SCMI Clock Protocol
@@ -107,7 +113,7 @@ struct scmi_clk_proto_ops {
int (*rate_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
u64 *rate);
int (*rate_set)(const struct scmi_protocol_handle *ph, u32 clk_id,
- u64 rate);
+ u32 round, u64 rate);
int (*enable)(const struct scmi_protocol_handle *ph, u32 clk_id,
bool atomic);
int (*disable)(const struct scmi_protocol_handle *ph, u32 clk_id,

--
2.37.1