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

Peng Fan (OSS) posted 2 patches 1 month ago
[PATCH RFC 2/2] clk: scmi: Add support for two #clock-cells to pass rate rounding mode
Posted by Peng Fan (OSS) 1 month ago
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