Hi,
a small series to review how the SCMI Clock driver chooses and sets up the
CLK operations to associate to a clock when registering with CLK framework.
SCMI clocks exposed by the platform sports a growing number of clock
properties since SCMI v3.2: discovered SCMI clocks could be restricted in
terms of capability to set state/rate/parent/duty_cycle and the platform
itself can have a varying support in terms of atomic support.
Knowing upfront which operations are NOT allowed on some clocks helps
avoiding needless message exchanges.
As a result, the SCMI Clock driver, when registering resources with the
CLK framework, aims to provide only the specific clk_ops as known to be
certainly supported by the specific SCMI clock resource.
Using static pre-compiled clk_ops structures to fulfill all the possible
(and possibly growing) combinations of clock features is cumbersome and
error-prone (there are 32 possible combinations as of now to account for
the above mentioned clock features variation).
This rework introduces a dynamic allocation mechanism to be able to
configure the required clk_ops at run-time when the SCMI clocks are
enumerated.
Only one single clk_ops is generated for each of the features combinations
effectively found in the set of returned SCMI resources.
Once this preliminary rework is done in 1/5, the following patches use this
new clk_ops schema to introduce a number of restricted clk_ops depending on
the specific retrieved SCMI clocks characteristics.
Based on v6.9-rc1
Thanks,
Cristian
V2
- rebased on v6.9-rc1
Cristian Marussi (5):
clk: scmi: Allocate CLK operations dynamically
clk: scmi: Add support for state control restricted clocks
clk: scmi: Add support for rate change restricted clocks
clk: scmi: Add support for re-parenting restricted clocks
clk: scmi: Add support for get/set duty_cycle operations
drivers/clk/clk-scmi.c | 226 ++++++++++++++++++++++++++++++++---------
1 file changed, 179 insertions(+), 47 deletions(-)
--
2.44.0
SCMI Clocks descriptors expose an increasing number of properties, thing
which, in turn, leads to a varying set of supported CLK operations to be
associated with each clock.
Providing statically pre-defined CLK operations structs for all the
possible combinations of allowed clock features is becoming cumbersome and
error-prone.
Allocate the per-clock operations descriptors dynamically and populate it
with the strictly needed set of operations depending on the advertised
clock properties: one descriptor is created for each distinct combination
of clock operations, so minimizing the number of clk_ops structures to the
strictly minimum needed.
CC: Michael Turquette <[email protected]>
CC: Stephen Boyd <[email protected]>
CC: [email protected]
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/clk/clk-scmi.c | 163 ++++++++++++++++++++++++++++-------------
1 file changed, 114 insertions(+), 49 deletions(-)
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index 8cbe24789c24..d5d369b052bd 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -2,7 +2,7 @@
/*
* System Control and Power Interface (SCMI) Protocol based clock driver
*
- * Copyright (C) 2018-2022 ARM Ltd.
+ * Copyright (C) 2018-2024 ARM Ltd.
*/
#include <linux/clk-provider.h>
@@ -16,6 +16,14 @@
#define NOT_ATOMIC false
#define ATOMIC true
+enum scmi_clk_feats {
+ SCMI_CLK_ATOMIC_SUPPORTED,
+ SCMI_CLK_MAX_FEATS
+};
+
+#define SCMI_MAX_CLK_OPS (1 << SCMI_CLK_MAX_FEATS)
+
+static const struct clk_ops *clk_ops_db[SCMI_MAX_CLK_OPS];
static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
struct scmi_clk {
@@ -158,42 +166,6 @@ static int scmi_clk_atomic_is_enabled(struct clk_hw *hw)
return !!enabled;
}
-/*
- * We can provide enable/disable/is_enabled atomic callbacks only if the
- * underlying SCMI transport for an SCMI instance is configured to handle
- * SCMI commands in an atomic manner.
- *
- * When no SCMI atomic transport support is available we instead provide only
- * the prepare/unprepare API, as allowed by the clock framework when atomic
- * calls are not available.
- *
- * Two distinct sets of clk_ops are provided since we could have multiple SCMI
- * instances with different underlying transport quality, so they cannot be
- * shared.
- */
-static const struct clk_ops scmi_clk_ops = {
- .recalc_rate = scmi_clk_recalc_rate,
- .round_rate = scmi_clk_round_rate,
- .set_rate = scmi_clk_set_rate,
- .prepare = scmi_clk_enable,
- .unprepare = scmi_clk_disable,
- .set_parent = scmi_clk_set_parent,
- .get_parent = scmi_clk_get_parent,
- .determine_rate = scmi_clk_determine_rate,
-};
-
-static const struct clk_ops scmi_atomic_clk_ops = {
- .recalc_rate = scmi_clk_recalc_rate,
- .round_rate = scmi_clk_round_rate,
- .set_rate = scmi_clk_set_rate,
- .enable = scmi_clk_atomic_enable,
- .disable = scmi_clk_atomic_disable,
- .is_enabled = scmi_clk_atomic_is_enabled,
- .set_parent = scmi_clk_set_parent,
- .get_parent = scmi_clk_get_parent,
- .determine_rate = scmi_clk_determine_rate,
-};
-
static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
const struct clk_ops *scmi_ops)
{
@@ -230,6 +202,106 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
return ret;
}
+/**
+ * scmi_clk_ops_alloc() - Alloc and configure clock operations
+ * @dev: A device reference for devres
+ * @feats_key: A bitmap representing the desired clk_ops capabilities.
+ *
+ * Allocate and configure a proper set of clock operations depending on the
+ * specifically required SCMI clock features.
+ *
+ * Return: A pointer to the allocated and configured clk_ops on Success,
+ * or NULL on allocation failure.
+ */
+static const struct clk_ops *
+scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
+{
+ struct clk_ops *ops;
+
+ ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return NULL;
+ /*
+ * We can provide enable/disable/is_enabled atomic callbacks only if the
+ * underlying SCMI transport for an SCMI instance is configured to
+ * handle SCMI commands in an atomic manner.
+ *
+ * When no SCMI atomic transport support is available we instead provide
+ * only the prepare/unprepare API, as allowed by the clock framework
+ * when atomic calls are not available.
+ */
+ if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) {
+ ops->enable = scmi_clk_atomic_enable;
+ ops->disable = scmi_clk_atomic_disable;
+ ops->is_enabled = scmi_clk_atomic_is_enabled;
+ } else {
+ ops->prepare = scmi_clk_enable;
+ ops->unprepare = scmi_clk_disable;
+ }
+
+ /* Rate ops */
+ ops->recalc_rate = scmi_clk_recalc_rate;
+ ops->round_rate = scmi_clk_round_rate;
+ ops->determine_rate = scmi_clk_determine_rate;
+ ops->set_rate = scmi_clk_set_rate;
+
+ /* Parent ops */
+ ops->get_parent = scmi_clk_get_parent;
+ ops->set_parent = scmi_clk_set_parent;
+
+ return ops;
+}
+
+/**
+ * scmi_clk_ops_select() - Select a proper set of clock operations
+ * @sclk: A reference to an SCMI clock descriptor
+ * @atomic_capable: A flag to indicate if atomic mode is supported by the
+ * transport
+ * @atomic_threshold: Platform atomic threshold value
+ *
+ * After having built a bitmap descriptor to represent the set of features
+ * needed by this SCMI clock, at first use it to lookup into the set of
+ * previously allocated clk_ops to check if a suitable combination of clock
+ * operations was already created; when no match is found allocate a brand new
+ * set of clk_ops satisfying the required combination of features and save it
+ * for future references.
+ *
+ * In this way only one set of clk_ops is ever created for each different
+ * combination that is effectively needed.
+ *
+ * Return: A pointer to the allocated and configured clk_ops on Success, or
+ * NULL otherwise.
+ */
+static const struct clk_ops *
+scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
+ unsigned int atomic_threshold)
+{
+ const struct scmi_clock_info *ci = sclk->info;
+ unsigned int feats_key = 0;
+ const struct clk_ops *ops;
+
+ /*
+ * Note that when transport is atomic but SCMI protocol did not
+ * specify (or support) an enable_latency associated with a
+ * clock, we default to use atomic operations mode.
+ */
+ if (atomic_capable && ci->enable_latency <= atomic_threshold)
+ feats_key |= BIT(SCMI_CLK_ATOMIC_SUPPORTED);
+
+ /* Lookup previously allocated ops */
+ ops = clk_ops_db[feats_key];
+ if (!ops) {
+ ops = scmi_clk_ops_alloc(sclk->dev, feats_key);
+ if (!ops)
+ return NULL;
+
+ /* Store new ops combinations */
+ clk_ops_db[feats_key] = ops;
+ }
+
+ return ops;
+}
+
static int scmi_clocks_probe(struct scmi_device *sdev)
{
int idx, count, err;
@@ -285,16 +357,10 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
sclk->ph = ph;
sclk->dev = dev;
- /*
- * Note that when transport is atomic but SCMI protocol did not
- * specify (or support) an enable_latency associated with a
- * clock, we default to use atomic operations mode.
- */
- if (is_atomic &&
- sclk->info->enable_latency <= atomic_threshold)
- scmi_ops = &scmi_atomic_clk_ops;
- else
- scmi_ops = &scmi_clk_ops;
+ scmi_ops = scmi_clk_ops_select(sclk, is_atomic,
+ atomic_threshold);
+ if (!scmi_ops)
+ return -ENOMEM;
/* Initialize clock parent data. */
if (sclk->info->num_parents > 0) {
@@ -318,8 +384,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
} else {
dev_dbg(dev, "Registered clock:%s%s\n",
sclk->info->name,
- scmi_ops == &scmi_atomic_clk_ops ?
- " (atomic ops)" : "");
+ scmi_ops->enable ? " (atomic ops)" : "");
hws[idx] = &sclk->hw;
}
}
--
2.44.0
Some exposed SCMI Clocks could be marked as non-supporting state changes.
Configure a clk_ops descriptor which does not provide the state change
callbacks for such clocks when registering with CLK framework.
CC: Michael Turquette <[email protected]>
CC: Stephen Boyd <[email protected]>
CC: [email protected]
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/clk/clk-scmi.c | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index d5d369b052bd..fc9603988d91 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -18,6 +18,7 @@
enum scmi_clk_feats {
SCMI_CLK_ATOMIC_SUPPORTED,
+ SCMI_CLK_STATE_CTRL_FORBIDDEN,
SCMI_CLK_MAX_FEATS
};
@@ -230,15 +231,19 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
* only the prepare/unprepare API, as allowed by the clock framework
* when atomic calls are not available.
*/
- if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) {
- ops->enable = scmi_clk_atomic_enable;
- ops->disable = scmi_clk_atomic_disable;
- ops->is_enabled = scmi_clk_atomic_is_enabled;
- } else {
- ops->prepare = scmi_clk_enable;
- ops->unprepare = scmi_clk_disable;
+ if (!(feats_key & BIT(SCMI_CLK_STATE_CTRL_FORBIDDEN))) {
+ if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) {
+ ops->enable = scmi_clk_atomic_enable;
+ ops->disable = scmi_clk_atomic_disable;
+ } else {
+ ops->prepare = scmi_clk_enable;
+ ops->unprepare = scmi_clk_disable;
+ }
}
+ if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED))
+ ops->is_enabled = scmi_clk_atomic_is_enabled;
+
/* Rate ops */
ops->recalc_rate = scmi_clk_recalc_rate;
ops->round_rate = scmi_clk_round_rate;
@@ -288,6 +293,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
if (atomic_capable && ci->enable_latency <= atomic_threshold)
feats_key |= BIT(SCMI_CLK_ATOMIC_SUPPORTED);
+ if (ci->state_ctrl_forbidden)
+ feats_key |= BIT(SCMI_CLK_STATE_CTRL_FORBIDDEN);
+
/* Lookup previously allocated ops */
ops = clk_ops_db[feats_key];
if (!ops) {
--
2.44.0
Some exposed SCMI Clocks could be marked as non-supporting re-parenting
changes.
Configure a clk_ops descriptor which does not provide the re-parenting
callbacks for such clocks when registering with CLK framework.
CC: Michael Turquette <[email protected]>
CC: Stephen Boyd <[email protected]>
CC: [email protected]
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/clk/clk-scmi.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index d20dcc60f9d1..87e968b6c095 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -20,6 +20,7 @@ enum scmi_clk_feats {
SCMI_CLK_ATOMIC_SUPPORTED,
SCMI_CLK_STATE_CTRL_FORBIDDEN,
SCMI_CLK_RATE_CTRL_FORBIDDEN,
+ SCMI_CLK_PARENT_CTRL_FORBIDDEN,
SCMI_CLK_MAX_FEATS
};
@@ -254,7 +255,8 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
/* Parent ops */
ops->get_parent = scmi_clk_get_parent;
- ops->set_parent = scmi_clk_set_parent;
+ if (!(feats_key & BIT(SCMI_CLK_PARENT_CTRL_FORBIDDEN)))
+ ops->set_parent = scmi_clk_set_parent;
return ops;
}
@@ -301,6 +303,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
if (ci->rate_ctrl_forbidden)
feats_key |= BIT(SCMI_CLK_RATE_CTRL_FORBIDDEN);
+ if (ci->parent_ctrl_forbidden)
+ feats_key |= BIT(SCMI_CLK_PARENT_CTRL_FORBIDDEN);
+
/* Lookup previously allocated ops */
ops = clk_ops_db[feats_key];
if (!ops) {
--
2.44.0
Provide the CLK framework callbacks related to get/set clock duty cycle if
the related SCMI clock supports OEM extended configurations.
CC: Michael Turquette <[email protected]>
CC: Stephen Boyd <[email protected]>
CC: [email protected]
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/clk/clk-scmi.c | 49 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index 87e968b6c095..86ef7c553ddd 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -21,6 +21,7 @@ enum scmi_clk_feats {
SCMI_CLK_STATE_CTRL_FORBIDDEN,
SCMI_CLK_RATE_CTRL_FORBIDDEN,
SCMI_CLK_PARENT_CTRL_FORBIDDEN,
+ SCMI_CLK_DUTY_CYCLE_SUPPORTED,
SCMI_CLK_MAX_FEATS
};
@@ -169,6 +170,45 @@ static int scmi_clk_atomic_is_enabled(struct clk_hw *hw)
return !!enabled;
}
+static int scmi_clk_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
+{
+ int ret;
+ u32 val;
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ ret = scmi_proto_clk_ops->config_oem_get(clk->ph, clk->id,
+ SCMI_CLOCK_CFG_DUTY_CYCLE,
+ &val, NULL, false);
+ if (!ret) {
+ duty->num = val;
+ duty->den = 100;
+ } else {
+ dev_warn(clk->dev,
+ "Failed to get duty cycle for clock ID %d\n", clk->id);
+ }
+
+ return ret;
+}
+
+static int scmi_clk_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
+{
+ int ret;
+ u32 val;
+ struct scmi_clk *clk = to_scmi_clk(hw);
+
+ /* SCMI OEM Duty Cycle is expressed as a percentage */
+ val = (duty->num * 100) / duty->den;
+ ret = scmi_proto_clk_ops->config_oem_set(clk->ph, clk->id,
+ SCMI_CLOCK_CFG_DUTY_CYCLE,
+ val, false);
+ if (ret)
+ dev_warn(clk->dev,
+ "Failed to set duty cycle(%u/%u) for clock ID %d\n",
+ duty->num, duty->den, clk->id);
+
+ return ret;
+}
+
static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
const struct clk_ops *scmi_ops)
{
@@ -258,6 +298,12 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
if (!(feats_key & BIT(SCMI_CLK_PARENT_CTRL_FORBIDDEN)))
ops->set_parent = scmi_clk_set_parent;
+ /* Duty cycle */
+ if (feats_key & BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED)) {
+ ops->get_duty_cycle = scmi_clk_get_duty_cycle;
+ ops->set_duty_cycle = scmi_clk_set_duty_cycle;
+ }
+
return ops;
}
@@ -306,6 +352,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
if (ci->parent_ctrl_forbidden)
feats_key |= BIT(SCMI_CLK_PARENT_CTRL_FORBIDDEN);
+ if (ci->extended_config)
+ feats_key |= BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED);
+
/* Lookup previously allocated ops */
ops = clk_ops_db[feats_key];
if (!ops) {
--
2.44.0
Some exposed SCMI Clocks could be marked as non-supporting rate changes.
Configure a clk_ops descriptors which does not provide the rate change
callbacks for such clocks when registering with CLK framework.
CC: Michael Turquette <[email protected]>
CC: Stephen Boyd <[email protected]>
CC: [email protected]
Signed-off-by: Cristian Marussi <[email protected]>
---
drivers/clk/clk-scmi.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index fc9603988d91..d20dcc60f9d1 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -19,6 +19,7 @@
enum scmi_clk_feats {
SCMI_CLK_ATOMIC_SUPPORTED,
SCMI_CLK_STATE_CTRL_FORBIDDEN,
+ SCMI_CLK_RATE_CTRL_FORBIDDEN,
SCMI_CLK_MAX_FEATS
};
@@ -248,7 +249,8 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
ops->recalc_rate = scmi_clk_recalc_rate;
ops->round_rate = scmi_clk_round_rate;
ops->determine_rate = scmi_clk_determine_rate;
- ops->set_rate = scmi_clk_set_rate;
+ if (!(feats_key & BIT(SCMI_CLK_RATE_CTRL_FORBIDDEN)))
+ ops->set_rate = scmi_clk_set_rate;
/* Parent ops */
ops->get_parent = scmi_clk_get_parent;
@@ -296,6 +298,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
if (ci->state_ctrl_forbidden)
feats_key |= BIT(SCMI_CLK_STATE_CTRL_FORBIDDEN);
+ if (ci->rate_ctrl_forbidden)
+ feats_key |= BIT(SCMI_CLK_RATE_CTRL_FORBIDDEN);
+
/* Lookup previously allocated ops */
ops = clk_ops_db[feats_key];
if (!ops) {
--
2.44.0
On 25/03/2024 14:00, Cristian Marussi wrote:
> SCMI Clocks descriptors expose an increasing number of properties, thing
> which, in turn, leads to a varying set of supported CLK operations to be
> associated with each clock.
>
> Providing statically pre-defined CLK operations structs for all the
> possible combinations of allowed clock features is becoming cumbersome and
> error-prone.
>
> Allocate the per-clock operations descriptors dynamically and populate it
> with the strictly needed set of operations depending on the advertised
> clock properties: one descriptor is created for each distinct combination
> of clock operations, so minimizing the number of clk_ops structures to the
> strictly minimum needed.
>
> CC: Michael Turquette <[email protected]>
> CC: Stephen Boyd <[email protected]>
> CC: [email protected]
> Signed-off-by: Cristian Marussi <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
--
Florian
On 25/03/2024 14:00, Cristian Marussi wrote:
> Some exposed SCMI Clocks could be marked as non-supporting state changes.
> Configure a clk_ops descriptor which does not provide the state change
> callbacks for such clocks when registering with CLK framework.
>
> CC: Michael Turquette <[email protected]>
> CC: Stephen Boyd <[email protected]>
> CC: [email protected]
> Signed-off-by: Cristian Marussi <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
--
Florian
On 25/03/2024 14:00, Cristian Marussi wrote:
> Some exposed SCMI Clocks could be marked as non-supporting re-parenting
> changes.
> Configure a clk_ops descriptor which does not provide the re-parenting
> callbacks for such clocks when registering with CLK framework.
>
> CC: Michael Turquette <[email protected]>
> CC: Stephen Boyd <[email protected]>
> CC: [email protected]
> Signed-off-by: Cristian Marussi <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
--
Florian
On 25/03/2024 14:00, Cristian Marussi wrote:
> Some exposed SCMI Clocks could be marked as non-supporting rate changes.
> Configure a clk_ops descriptors which does not provide the rate change
> callbacks for such clocks when registering with CLK framework.
>
> CC: Michael Turquette <[email protected]>
> CC: Stephen Boyd <[email protected]>
> CC: [email protected]
> Signed-off-by: Cristian Marussi <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
--
Florian
On 25/03/2024 14:00, Cristian Marussi wrote:
> Provide the CLK framework callbacks related to get/set clock duty cycle if
> the related SCMI clock supports OEM extended configurations.
>
> CC: Michael Turquette <[email protected]>
> CC: Stephen Boyd <[email protected]>
> CC: [email protected]
> Signed-off-by: Cristian Marussi <[email protected]>
Reviewed-by: Florian Fainelli <[email protected]>
--
Florian
Hi Stephen,
On Mon, Mar 25, 2024 at 09:00:20PM +0000, Cristian Marussi wrote:
> Hi,
>
[...]
>
> This rework introduces a dynamic allocation mechanism to be able to
> configure the required clk_ops at run-time when the SCMI clocks are
> enumerated.
>
> Only one single clk_ops is generated for each of the features combinations
> effectively found in the set of returned SCMI resources.
>
> Once this preliminary rework is done in 1/5, the following patches use this
> new clk_ops schema to introduce a number of restricted clk_ops depending on
> the specific retrieved SCMI clocks characteristics.
>
Let me know if you are happy with this changes. There is no strict
dependency on the changes in SCMI tree ATM, so I can provide reviewed-by
if you would like to take it via clk tree. If you prefer me to take it
via SCMI tree, please provide Ack if you are OK with the changes.
--
Regards,
Sudeep
Quoting Cristian Marussi (2024-03-25 14:00:21)
> diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
> index 8cbe24789c24..d5d369b052bd 100644
> --- a/drivers/clk/clk-scmi.c
> +++ b/drivers/clk/clk-scmi.c
> @@ -16,6 +16,14 @@
> #define NOT_ATOMIC false
> #define ATOMIC true
>
> +enum scmi_clk_feats {
> + SCMI_CLK_ATOMIC_SUPPORTED,
> + SCMI_CLK_MAX_FEATS
> +};
> +
> +#define SCMI_MAX_CLK_OPS (1 << SCMI_CLK_MAX_FEATS)
> +
> +static const struct clk_ops *clk_ops_db[SCMI_MAX_CLK_OPS];
Can it be 'scmi_clk_ops_db' for some name spacing?
> static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
>
> struct scmi_clk {
> @@ -230,6 +202,106 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
> return ret;
> }
>
> +/**
> + * scmi_clk_ops_alloc() - Alloc and configure clock operations
> + * @dev: A device reference for devres
> + * @feats_key: A bitmap representing the desired clk_ops capabilities.
Drop the period please because it's not consistent with the previous
argument descriptor.
> + *
> + * Allocate and configure a proper set of clock operations depending on the
> + * specifically required SCMI clock features.
> + *
> + * Return: A pointer to the allocated and configured clk_ops on Success,
Lowercase 'Success'.
> +
> +/**
> + * scmi_clk_ops_select() - Select a proper set of clock operations
> + * @sclk: A reference to an SCMI clock descriptor
> + * @atomic_capable: A flag to indicate if atomic mode is supported by the
> + * transport
> + * @atomic_threshold: Platform atomic threshold value
Is this in nanoseconds, microseconds, or ??? Maybe a better description is
"clk_ops are atomic when clk enable_latency is less than X [time unit]"
> + *
> + * After having built a bitmap descriptor to represent the set of features
> + * needed by this SCMI clock, at first use it to lookup into the set of
> + * previously allocated clk_ops to check if a suitable combination of clock
> + * operations was already created; when no match is found allocate a brand new
> + * set of clk_ops satisfying the required combination of features and save it
> + * for future references.
> + *
> + * In this way only one set of clk_ops is ever created for each different
> + * combination that is effectively needed.
> + *
> + * Return: A pointer to the allocated and configured clk_ops on Success, or
Lowercase 'Success'.
> + * NULL otherwise.
> + */
> +static const struct clk_ops *
> +scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
> + unsigned int atomic_threshold)
> +{
> + const struct scmi_clock_info *ci = sclk->info;
> + unsigned int feats_key = 0;
> + const struct clk_ops *ops;
> +
> + /*
> + * Note that when transport is atomic but SCMI protocol did not
> + * specify (or support) an enable_latency associated with a
> + * clock, we default to use atomic operations mode.
> + */
> + if (atomic_capable && ci->enable_latency <= atomic_threshold)
> + feats_key |= BIT(SCMI_CLK_ATOMIC_SUPPORTED);
> +
Can we have a static_assert() here that makes sure 'feats_key' isn't
larger than the size of clk_ops_db?
static_assert(ARRAY_SIZE(clk_ops_db) >= feats_key);
> + /* Lookup previously allocated ops */
> + ops = clk_ops_db[feats_key];
> + if (!ops) {
> + ops = scmi_clk_ops_alloc(sclk->dev, feats_key);
> + if (!ops)
> + return NULL;
This could be less nested if the first lookup is put in
scmi_clk_ops_alloc() and the store below is folded in. Or an early
return if found.
ops = clk_ops_db[feats_key];
if (ops)
return ops;
/* Didn't find one */
ops = scmi_clk_ops_alloc(...)
if (!ops)
return NULL;
clk_ops_db[feats_key] = ops;
return ops;
> +
> + /* Store new ops combinations */
> + clk_ops_db[feats_key] = ops;
> + }
> +
> + return ops;
> +}
> +
> static int scmi_clocks_probe(struct scmi_device *sdev)
> {
> int idx, count, err;
> @@ -285,16 +357,10 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
> sclk->ph = ph;
> sclk->dev = dev;
>
> - /*
> - * Note that when transport is atomic but SCMI protocol did not
> - * specify (or support) an enable_latency associated with a
> - * clock, we default to use atomic operations mode.
> - */
> - if (is_atomic &&
> - sclk->info->enable_latency <= atomic_threshold)
> - scmi_ops = &scmi_atomic_clk_ops;
> - else
> - scmi_ops = &scmi_clk_ops;
> + scmi_ops = scmi_clk_ops_select(sclk, is_atomic,
'is_atomic' should probably be 'transport_is_atomic' so this reads
easier.
> + atomic_threshold);
> + if (!scmi_ops)
> + return -ENOMEM;
>
> /* Initialize clock parent data. */
> if (sclk->info->num_parents > 0) {
Quoting Cristian Marussi (2024-03-25 14:00:22)
> Some exposed SCMI Clocks could be marked as non-supporting state changes.
> Configure a clk_ops descriptor which does not provide the state change
> callbacks for such clocks when registering with CLK framework.
>
> CC: Michael Turquette <[email protected]>
> CC: Stephen Boyd <[email protected]>
> CC: [email protected]
> Signed-off-by: Cristian Marussi <[email protected]>
> ---
> drivers/clk/clk-scmi.c | 22 +++++++++++++++-------
> 1 file changed, 15 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
> index d5d369b052bd..fc9603988d91 100644
> --- a/drivers/clk/clk-scmi.c
> +++ b/drivers/clk/clk-scmi.c
> @@ -18,6 +18,7 @@
>
> enum scmi_clk_feats {
> SCMI_CLK_ATOMIC_SUPPORTED,
> + SCMI_CLK_STATE_CTRL_FORBIDDEN,
Can it be positive, i.e. SCMI_CLK_STATE_CTRL_SUPPORTED?
> SCMI_CLK_MAX_FEATS
> };
>
> @@ -230,15 +231,19 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
> * only the prepare/unprepare API, as allowed by the clock framework
> * when atomic calls are not available.
> */
> - if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) {
> - ops->enable = scmi_clk_atomic_enable;
> - ops->disable = scmi_clk_atomic_disable;
> - ops->is_enabled = scmi_clk_atomic_is_enabled;
> - } else {
> - ops->prepare = scmi_clk_enable;
> - ops->unprepare = scmi_clk_disable;
> + if (!(feats_key & BIT(SCMI_CLK_STATE_CTRL_FORBIDDEN))) {
> + if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) {
> + ops->enable = scmi_clk_atomic_enable;
> + ops->disable = scmi_clk_atomic_disable;
> + } else {
> + ops->prepare = scmi_clk_enable;
> + ops->unprepare = scmi_clk_disable;
> + }
> }
>
> + if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED))
> + ops->is_enabled = scmi_clk_atomic_is_enabled;
> +
> /* Rate ops */
> ops->recalc_rate = scmi_clk_recalc_rate;
> ops->round_rate = scmi_clk_round_rate;
> @@ -288,6 +293,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
> if (atomic_capable && ci->enable_latency <= atomic_threshold)
> feats_key |= BIT(SCMI_CLK_ATOMIC_SUPPORTED);
>
> + if (ci->state_ctrl_forbidden)
Then this is negated.
> + feats_key |= BIT(SCMI_CLK_STATE_CTRL_FORBIDDEN);
> +
> /* Lookup previously allocated ops */
> ops = clk_ops_db[feats_key];
On Sun, Apr 07, 2024 at 09:38:46PM -0700, Stephen Boyd wrote:
> Quoting Cristian Marussi (2024-03-25 14:00:21)
> > diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
> > index 8cbe24789c24..d5d369b052bd 100644
> > --- a/drivers/clk/clk-scmi.c
> > +++ b/drivers/clk/clk-scmi.c
> > @@ -16,6 +16,14 @@
> > #define NOT_ATOMIC false
> > #define ATOMIC true
> >
Hi,
thanks for the review.
> > +enum scmi_clk_feats {
> > + SCMI_CLK_ATOMIC_SUPPORTED,
> > + SCMI_CLK_MAX_FEATS
> > +};
> > +
> > +#define SCMI_MAX_CLK_OPS (1 << SCMI_CLK_MAX_FEATS)
> > +
> > +static const struct clk_ops *clk_ops_db[SCMI_MAX_CLK_OPS];
>
> Can it be 'scmi_clk_ops_db' for some name spacing?
>
Yes.
> > static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
> >
> > struct scmi_clk {
> > @@ -230,6 +202,106 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
> > return ret;
> > }
> >
> > +/**
> > + * scmi_clk_ops_alloc() - Alloc and configure clock operations
> > + * @dev: A device reference for devres
> > + * @feats_key: A bitmap representing the desired clk_ops capabilities.
>
> Drop the period please because it's not consistent with the previous
> argument descriptor.
>
Ok.
> > + *
> > + * Allocate and configure a proper set of clock operations depending on the
> > + * specifically required SCMI clock features.
> > + *
> > + * Return: A pointer to the allocated and configured clk_ops on Success,
>
> Lowercase 'Success'.
>
Ok.
> > +
> > +/**
> > + * scmi_clk_ops_select() - Select a proper set of clock operations
> > + * @sclk: A reference to an SCMI clock descriptor
> > + * @atomic_capable: A flag to indicate if atomic mode is supported by the
> > + * transport
> > + * @atomic_threshold: Platform atomic threshold value
>
> Is this in nanoseconds, microseconds, or ??? Maybe a better description is
> "clk_ops are atomic when clk enable_latency is less than X [time unit]"
>
It is micro, I will comment better.
> > + *
> > + * After having built a bitmap descriptor to represent the set of features
> > + * needed by this SCMI clock, at first use it to lookup into the set of
> > + * previously allocated clk_ops to check if a suitable combination of clock
> > + * operations was already created; when no match is found allocate a brand new
> > + * set of clk_ops satisfying the required combination of features and save it
> > + * for future references.
> > + *
> > + * In this way only one set of clk_ops is ever created for each different
> > + * combination that is effectively needed.
> > + *
> > + * Return: A pointer to the allocated and configured clk_ops on Success, or
>
> Lowercase 'Success'.
>
Ok.
> > + * NULL otherwise.
> > + */
> > +static const struct clk_ops *
> > +scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
> > + unsigned int atomic_threshold)
> > +{
> > + const struct scmi_clock_info *ci = sclk->info;
> > + unsigned int feats_key = 0;
> > + const struct clk_ops *ops;
> > +
> > + /*
> > + * Note that when transport is atomic but SCMI protocol did not
> > + * specify (or support) an enable_latency associated with a
> > + * clock, we default to use atomic operations mode.
> > + */
> > + if (atomic_capable && ci->enable_latency <= atomic_threshold)
> > + feats_key |= BIT(SCMI_CLK_ATOMIC_SUPPORTED);
> > +
>
> Can we have a static_assert() here that makes sure 'feats_key' isn't
> larger than the size of clk_ops_db?
>
> static_assert(ARRAY_SIZE(clk_ops_db) >= feats_key);
>
Ok.
> > + /* Lookup previously allocated ops */
> > + ops = clk_ops_db[feats_key];
> > + if (!ops) {
> > + ops = scmi_clk_ops_alloc(sclk->dev, feats_key);
> > + if (!ops)
> > + return NULL;
>
> This could be less nested if the first lookup is put in
> scmi_clk_ops_alloc() and the store below is folded in. Or an early
> return if found.
>
> ops = clk_ops_db[feats_key];
> if (ops)
> return ops;
>
> /* Didn't find one */
> ops = scmi_clk_ops_alloc(...)
> if (!ops)
> return NULL;
>
> clk_ops_db[feats_key] = ops;
> return ops;
>
ok.
> > +
> > + /* Store new ops combinations */
> > + clk_ops_db[feats_key] = ops;
> > + }
> > +
> > + return ops;
> > +}
> > +
> > static int scmi_clocks_probe(struct scmi_device *sdev)
> > {
> > int idx, count, err;
> > @@ -285,16 +357,10 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
> > sclk->ph = ph;
> > sclk->dev = dev;
> >
> > - /*
> > - * Note that when transport is atomic but SCMI protocol did not
> > - * specify (or support) an enable_latency associated with a
> > - * clock, we default to use atomic operations mode.
> > - */
> > - if (is_atomic &&
> > - sclk->info->enable_latency <= atomic_threshold)
> > - scmi_ops = &scmi_atomic_clk_ops;
> > - else
> > - scmi_ops = &scmi_clk_ops;
> > + scmi_ops = scmi_clk_ops_select(sclk, is_atomic,
>
> 'is_atomic' should probably be 'transport_is_atomic' so this reads
> easier.
>
Ok.
Thanks,
Cristian
On Sun, Apr 07, 2024 at 09:48:59PM -0700, Stephen Boyd wrote:
> Quoting Cristian Marussi (2024-03-25 14:00:22)
> > Some exposed SCMI Clocks could be marked as non-supporting state changes.
> > Configure a clk_ops descriptor which does not provide the state change
> > callbacks for such clocks when registering with CLK framework.
> >
> > CC: Michael Turquette <[email protected]>
> > CC: Stephen Boyd <[email protected]>
> > CC: [email protected]
> > Signed-off-by: Cristian Marussi <[email protected]>
> > ---
> > drivers/clk/clk-scmi.c | 22 +++++++++++++++-------
> > 1 file changed, 15 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
> > index d5d369b052bd..fc9603988d91 100644
> > --- a/drivers/clk/clk-scmi.c
> > +++ b/drivers/clk/clk-scmi.c
> > @@ -18,6 +18,7 @@
> >
> > enum scmi_clk_feats {
> > SCMI_CLK_ATOMIC_SUPPORTED,
> > + SCMI_CLK_STATE_CTRL_FORBIDDEN,
>
> Can it be positive, i.e. SCMI_CLK_STATE_CTRL_SUPPORTED?
Yes of course.
>
> > SCMI_CLK_MAX_FEATS
> > };
> >
> > @@ -230,15 +231,19 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
> > * only the prepare/unprepare API, as allowed by the clock framework
> > * when atomic calls are not available.
> > */
> > - if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) {
> > - ops->enable = scmi_clk_atomic_enable;
> > - ops->disable = scmi_clk_atomic_disable;
> > - ops->is_enabled = scmi_clk_atomic_is_enabled;
> > - } else {
> > - ops->prepare = scmi_clk_enable;
> > - ops->unprepare = scmi_clk_disable;
> > + if (!(feats_key & BIT(SCMI_CLK_STATE_CTRL_FORBIDDEN))) {
> > + if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED)) {
> > + ops->enable = scmi_clk_atomic_enable;
> > + ops->disable = scmi_clk_atomic_disable;
> > + } else {
> > + ops->prepare = scmi_clk_enable;
> > + ops->unprepare = scmi_clk_disable;
> > + }
> > }
> >
> > + if (feats_key & BIT(SCMI_CLK_ATOMIC_SUPPORTED))
> > + ops->is_enabled = scmi_clk_atomic_is_enabled;
> > +
> > /* Rate ops */
> > ops->recalc_rate = scmi_clk_recalc_rate;
> > ops->round_rate = scmi_clk_round_rate;
> > @@ -288,6 +293,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
> > if (atomic_capable && ci->enable_latency <= atomic_threshold)
> > feats_key |= BIT(SCMI_CLK_ATOMIC_SUPPORTED);
> >
> > + if (ci->state_ctrl_forbidden)
>
> Then this is negated.
>
I will rework accordingly
Thanks,
Cristian