[PATCH v4 05/27] clk: add clk_round_rate()

Dario Binacchi
2020-10-19T16:46:36Z

It returns the rate which will be set if you ask clk_set_rate() to set
that rate. It provides a way to query exactly what rate you'll get if
you call clk_set_rate() with that same argument.
So essentially, clk_round_rate() and clk_set_rate() are equivalent
except the former does not modify the clock hardware in any way.

Signed-off-by: Dario Binacchi <dariobin@libero.it>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Sean Anderson <seanga2@gmail.com>

---

Changes in v4:
- Update clk_round_rate description.
- Add Sean Anderson review.

arch/sandbox/include/asm/clk.h | 9 +++++++++
drivers/clk/clk-uclass.c | 15 +++++++++++++++
drivers/clk/clk_sandbox.c | 17 +++++++++++++++++
drivers/clk/clk_sandbox_test.c | 10 ++++++++++
include/clk-uclass.h | 8 ++++++++
include/clk.h | 28 ++++++++++++++++++++++++++++
test/dm/clk.c | 22 ++++++++++++++++++++++
7 files changed, 109 insertions(+)

diff --git a/arch/sandbox/include/asm/clk.h b/arch/sandbox/include/asm/clk.h
index c184c4bffc..0294baee27 100644
--- a/arch/sandbox/include/asm/clk.h
+++ b/arch/sandbox/include/asm/clk.h
@@ -105,6 +105,15 @@ int sandbox_clk_test_get_bulk(struct udevice *dev);
* @return: The rate of the clock.
*/
ulong sandbox_clk_test_get_rate(struct udevice *dev, int id);
+/**
+ * sandbox_clk_test_round_rate - Ask the sandbox clock test device to round a
+ * clock's rate.
+ *
+ * @dev: The sandbox clock test (client) device.
+ * @id: The test device's clock ID to configure.
+ * @return: The rounded rate of the clock.
+ */
+ulong sandbox_clk_test_round_rate(struct udevice *dev, int id, ulong rate);
/**
* sandbox_clk_test_set_rate - Ask the sandbox clock test device to set a
* clock's rate.
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 31c5997aea..c16a8b120c 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -524,6 +524,21 @@ long long clk_get_parent_rate(struct clk *clk)
return pclk->rate;
}

+ulong clk_round_rate(struct clk *clk, ulong rate)
+{
+ const struct clk_ops *ops;
+
+ debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate);
+ if (!clk_valid(clk))
+ return 0;
+
+ ops = clk_dev_ops(clk->dev);
+ if (!ops->round_rate)
+ return -ENOSYS;
+
+ return ops->round_rate(clk, rate);
+}
+
ulong clk_set_rate(struct clk *clk, ulong rate)
{
const struct clk_ops *ops;
diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c
index 768fbb7c52..8361b930df 100644
--- a/drivers/clk/clk_sandbox.c
+++ b/drivers/clk/clk_sandbox.c
@@ -30,6 +30,22 @@ static ulong sandbox_clk_get_rate(struct clk *clk)
return priv->rate[clk->id];
}

+static ulong sandbox_clk_round_rate(struct clk *clk, ulong rate)
+{
+ struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
+
+ if (!priv->probed)
+ return -ENODEV;
+
+ if (clk->id >= SANDBOX_CLK_ID_COUNT)
+ return -EINVAL;
+
+ if (!rate)
+ return -EINVAL;
+
+ return rate;
+}
+
static ulong sandbox_clk_set_rate(struct clk *clk, ulong rate)
{
struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
@@ -103,6 +119,7 @@ static int sandbox_clk_free(struct clk *clk)
}

static struct clk_ops sandbox_clk_ops = {
+ .round_rate = sandbox_clk_round_rate,
.get_rate = sandbox_clk_get_rate,
.set_rate = sandbox_clk_set_rate,
.enable = sandbox_clk_enable,
diff --git a/drivers/clk/clk_sandbox_test.c b/drivers/clk/clk_sandbox_test.c
index 873383856f..f7b77aa674 100644
--- a/drivers/clk/clk_sandbox_test.c
+++ b/drivers/clk/clk_sandbox_test.c
@@ -86,6 +86,16 @@ ulong sandbox_clk_test_get_rate(struct udevice *dev, int id)
return clk_get_rate(sbct->clkps[id]);
}

+ulong sandbox_clk_test_round_rate(struct udevice *dev, int id, ulong rate)
+{
+ struct sandbox_clk_test *sbct = dev_get_priv(dev);
+
+ if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
+ return -EINVAL;
+
+ return clk_round_rate(sbct->clkps[id], rate);
+}
+
ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate)
{
struct sandbox_clk_test *sbct = dev_get_priv(dev);
diff --git a/include/clk-uclass.h b/include/clk-uclass.h
index dac42dab36..50e8681b55 100644
--- a/include/clk-uclass.h
+++ b/include/clk-uclass.h
@@ -61,6 +61,14 @@ struct clk_ops {
* @return 0 if OK, or a negative error code.
*/
int (*rfree)(struct clk *clock);
+ /**
+ * round_rate() - Adjust a rate to the exact rate a clock can provide.
+ *
+ * @clk: The clock to manipulate.
+ * @rate: Desidered clock rate in Hz.
+ * @return rounded rate in Hz, or -ve error code.
+ */
+ ulong (*round_rate)(struct clk *clk, ulong rate);
/**
* get_rate() - Get current clock rate.
*
diff --git a/include/clk.h b/include/clk.h
index a62e2efa2c..ca6b85fa6f 100644
--- a/include/clk.h
+++ b/include/clk.h
@@ -366,6 +366,29 @@ struct clk *clk_get_parent(struct clk *clk);
*/
long long clk_get_parent_rate(struct clk *clk);

+/**
+ * clk_round_rate() - Adjust a rate to the exact rate a clock can provide
+ *
+ * This answers the question "if I were to pass @rate to clk_set_rate(),
+ * what clock rate would I end up with?" without changing the hardware
+ * in any way. In other words:
+ *
+ * rate = clk_round_rate(clk, r);
+ *
+ * and:
+ *
+ * rate = clk_set_rate(clk, r);
+ *
+ * are equivalent except the former does not modify the clock hardware
+ * in any way.
+ *
+ * @clk: A clock struct that was previously successfully requested by
+ * clk_request/get_by_*().
+ * @rate: desired clock rate in Hz.
+ * @return rounded rate in Hz, or -ve error code.
+ */
+ulong clk_round_rate(struct clk *clk, ulong rate);
+
/**
* clk_set_rate() - Set current clock rate.
*
@@ -482,6 +505,11 @@ static inline long long clk_get_parent_rate(struct clk *clk)
return -ENOSYS;
}

+static inline ulong clk_round_rate(struct clk *clk, ulong rate)
+{
+ return -ENOSYS;
+}
+
static inline ulong clk_set_rate(struct clk *clk, ulong rate)
{
return -ENOSYS;
diff --git a/test/dm/clk.c b/test/dm/clk.c
index edca3b49f6..21997ed892 100644
--- a/test/dm/clk.c
+++ b/test/dm/clk.c
@@ -112,6 +112,28 @@ static int dm_test_clk(struct unit_test_state *uts)
rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0);
ut_assert(IS_ERR_VALUE(rate));

+ ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C));
+
+ ut_asserteq(5000, sandbox_clk_test_round_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI,
+ 5000));
+ ut_asserteq(7000, sandbox_clk_test_round_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C,
+ 7000));
+
+ ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C));
+
+ rate = sandbox_clk_test_round_rate(dev_test, SANDBOX_CLK_TEST_ID_SPI, 0);
+ ut_assert(IS_ERR_VALUE(rate));
+ rate = sandbox_clk_test_round_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0);
+ ut_assert(IS_ERR_VALUE(rate));
+
ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test,
SANDBOX_CLK_TEST_ID_SPI));
ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test,
--
2.17.1