[PATCH v4 08/20] clk: scmi: check the clock state/parent/rate control permissions
Alice Guo
alice.guo at oss.nxp.com
Wed Jan 15 14:28:55 CET 2025
From: Alice Guo <alice.guo at nxp.com>
Clock driver based on SCMI clock management protocol in Linux checks
clock state, parent and rate control permissions. To be consistent with
the kernel driver, add this check here.
When using common clock framework (CCF), use the clock signal ID to get
the clock registered by clk_register() in scmi_clk_probe(), and then
obatin the struct clk_scmi variable with container_of().
Signed-off-by: Alice Guo <alice.guo at nxp.com>
Signed-off-by: Ye Li <ye.li at nxp.com>
Reviewed-by: Peng Fan <peng.fan at nxp.com>
---
drivers/clk/clk_scmi.c | 173 ++++++++++++++++++++++++++++++++++++++++++++---
include/scmi_protocols.h | 26 ++++++-
2 files changed, 188 insertions(+), 11 deletions(-)
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c
index 84333cdd0ccfe566c269f776f39725c69883c25c..0654f0cbf5fcca9c82b840c2fabd057ce1907979 100644
--- a/drivers/clk/clk_scmi.c
+++ b/drivers/clk/clk_scmi.c
@@ -12,6 +12,56 @@
#include <asm/types.h>
#include <linux/clk-provider.h>
+struct clk_scmi {
+ struct clk clk;
+ u32 ctrl_flags;
+};
+
+static int scmi_clk_get_permissions(struct udevice *dev, int clkid, u32 *perm)
+{
+ u32 version;
+ int ret;
+
+ ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_CLOCK, &version);
+ if (ret) {
+ debug("get SCMI clock management protocol version failed\n");
+ return ret;
+ }
+
+ if (version >= CLOCK_PROTOCOL_VERSION_3_0) {
+ struct scmi_clk_get_permissions_in in = {
+ .clock_id = clkid,
+ };
+ struct scmi_clk_get_permissions_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
+ .message_id = SCMI_CLOCK_GET_PERMISSIONS,
+ .in_msg = (u8 *)&in,
+ .in_msg_sz = sizeof(in),
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret) {
+ debug("get SCMI clock management protocol permissions failed\n");
+ return ret;
+ }
+
+ ret = scmi_to_linux_errno(out.status);
+ if (ret < 0) {
+ debug("the status code of getting permissions: %d\n", ret);
+ return ret;
+ }
+
+ *perm = out.permissions;
+ return 0;
+ }
+
+ debug("SCMI clock management protocol version is less than 3.0.\n");
+ return -EINVAL;
+}
+
static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
{
struct scmi_clk_protocol_attr_out out;
@@ -32,7 +82,8 @@ static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
return 0;
}
-static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
+static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name,
+ u32 *attr)
{
struct scmi_clk_attribute_in in = {
.clock_id = clkid,
@@ -53,6 +104,7 @@ static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
return ret;
*name = strdup(out.clock_name);
+ *attr = out.attributes;
return 0;
}
@@ -78,11 +130,49 @@ static int scmi_clk_gate(struct clk *clk, int enable)
static int scmi_clk_enable(struct clk *clk)
{
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (CONFIG_IS_ENABLED(CLK_CCF)) {
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
+ return scmi_clk_gate(clk, 1);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ debug("SCMI CLOCK: the clock cannot be enabled by the agent.\n");
+ return 0;
+ }
+
return scmi_clk_gate(clk, 1);
}
static int scmi_clk_disable(struct clk *clk)
{
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (CONFIG_IS_ENABLED(CLK_CCF)) {
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
+ return scmi_clk_gate(clk, 0);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ debug("SCMI CLOCK: the clock cannot be disabled by the agent.\n");
+ return 0;
+ }
+
return scmi_clk_gate(clk, 0);
}
@@ -108,7 +198,7 @@ static ulong scmi_clk_get_rate(struct clk *clk)
return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
}
-static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
+static ulong __scmi_clk_set_rate(struct clk *clk, ulong rate)
{
struct scmi_clk_rate_set_in in = {
.clock_id = clk->id,
@@ -133,9 +223,33 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
return scmi_clk_get_rate(clk);
}
+static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (CONFIG_IS_ENABLED(CLK_CCF)) {
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_RATE_CONTROL)
+ return __scmi_clk_set_rate(clk, rate);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ debug("SCMI CLOCK: the clock rate cannot be changed by the agent.\n");
+ return 0;
+ }
+
+ return __scmi_clk_set_rate(clk, rate);
+}
+
static int scmi_clk_probe(struct udevice *dev)
{
- struct clk *clk;
+ struct clk_scmi *clk_scmi;
size_t num_clocks, i;
int ret;
@@ -156,29 +270,44 @@ static int scmi_clk_probe(struct udevice *dev)
for (i = 0; i < num_clocks; i++) {
char *clock_name;
+ u32 attributes;
- if (!scmi_clk_get_attibute(dev, i, &clock_name)) {
- clk = kzalloc(sizeof(*clk), GFP_KERNEL);
- if (!clk || !clock_name)
+ if (!scmi_clk_get_attibute(dev, i, &clock_name, &attributes)) {
+ clk_scmi = kzalloc(sizeof(*clk_scmi), GFP_KERNEL);
+ if (!clk_scmi || !clock_name)
ret = -ENOMEM;
else
- ret = clk_register(clk, dev->driver->name,
+ ret = clk_register(&clk_scmi->clk, dev->driver->name,
clock_name, dev->name);
if (ret) {
- free(clk);
+ free(clk_scmi);
free(clock_name);
+ free(&attributes);
return ret;
}
- clk_dm(i, clk);
+ clk_dm(i, &clk_scmi->clk);
+
+ if (CLK_HAS_RESTRICTIONS(attributes)) {
+ u32 perm;
+
+ ret = scmi_clk_get_permissions(dev, i, &perm);
+ if (ret < 0)
+ clk_scmi->ctrl_flags = 0;
+ else
+ clk_scmi->ctrl_flags = perm;
+ } else {
+ clk_scmi->ctrl_flags = SUPPORT_CLK_STAT_CONTROL | SUPPORT_CLK_PARENT_CONTROL |
+ SUPPORT_CLK_RATE_CONTROL;
+ }
}
}
return 0;
}
-static int scmi_clk_set_parent(struct clk *clk, struct clk *parent)
+static int __scmi_clk_set_parent(struct clk *clk, struct clk *parent)
{
struct scmi_clk_parent_set_in in = {
.clock_id = clk->id,
@@ -197,6 +326,30 @@ static int scmi_clk_set_parent(struct clk *clk, struct clk *parent)
return scmi_to_linux_errno(out.status);
}
+static int scmi_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (CONFIG_IS_ENABLED(CLK_CCF)) {
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_PARENT_CONTROL)
+ return __scmi_clk_set_parent(clk, parent);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ debug("SCMI CLOCK: the clock's parent cannot be changed by the agent.\n");
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
static const struct clk_ops scmi_clk_ops = {
.enable = scmi_clk_enable,
.disable = scmi_clk_disable,
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h
index d529f8e2697472e60d0cb9c275f34ef0ecaed3ca..9607433c3276f9aa18044c414a5bde3143726cf4 100644
--- a/include/scmi_protocols.h
+++ b/include/scmi_protocols.h
@@ -731,13 +731,15 @@ int scmi_pwd_name_get(struct udevice *dev, u32 domain_id, u8 **name);
/*
* SCMI Clock Protocol
*/
+#define CLOCK_PROTOCOL_VERSION_3_0 0x30000
enum scmi_clock_message_id {
SCMI_CLOCK_ATTRIBUTES = 0x3,
SCMI_CLOCK_RATE_SET = 0x5,
SCMI_CLOCK_RATE_GET = 0x6,
SCMI_CLOCK_CONFIG_SET = 0x7,
- SCMI_CLOCK_PARENT_SET = 0xD
+ SCMI_CLOCK_PARENT_SET = 0xD,
+ SCMI_CLOCK_GET_PERMISSIONS = 0xF
};
#define SCMI_CLK_PROTO_ATTR_COUNT_MASK GENMASK(15, 0)
@@ -776,6 +778,7 @@ struct scmi_clk_attribute_in {
struct scmi_clk_attribute_out {
s32 status;
u32 attributes;
+#define CLK_HAS_RESTRICTIONS(x) ((x) & BIT(1))
char clock_name[SCMI_CLOCK_NAME_LENGTH_MAX];
};
@@ -858,6 +861,27 @@ struct scmi_clk_parent_set_out {
s32 status;
};
+/**
+ * @clock_id: Identifier for the clock device.
+ */
+struct scmi_clk_get_permissions_in {
+ u32 clock_id;
+};
+
+/**
+ * @status: Negative 32-bit integers are used to return error status codes.
+ * @permissions: Bit[31] Clock state control, Bit[30] Clock parent control,
+ * Bit[29] Clock rate control, Bits[28:0] Reserved, must be zero
+ */
+struct scmi_clk_get_permissions_out {
+ s32 status;
+ u32 permissions;
+};
+
+#define SUPPORT_CLK_STAT_CONTROL BIT(31)
+#define SUPPORT_CLK_PARENT_CONTROL BIT(30)
+#define SUPPORT_CLK_RATE_CONTROL BIT(29)
+
/*
* SCMI Reset Domain Protocol
*/
--
2.34.1
More information about the U-Boot
mailing list