[PATCH 07/17] clk: scmi: check the clock state/parent/rate control permissions
Alice Guo
alice.guo at oss.nxp.com
Wed Oct 16 09:18:00 CEST 2024
From: Ye Li <ye.li at nxp.com>
SCMI clock management protocol driver in Linux checks clock state,
parent and rate control permissions. To be consistent with the kernel
driver, add this check here.
Signed-off-by: Alice Guo <alice.guo at nxp.com>
Reviewed-by: Peng Fan <peng.fan at nxp.com>
---
drivers/clk/clk_scmi.c | 116 +++++++++++++++++++++++++++++++++++++++++++----
include/scmi_protocols.h | 25 +++++++++-
2 files changed, 130 insertions(+), 11 deletions(-)
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c
index 84333cdd0ccfe566c269f776f39725c69883c25c..cbc7be718a5e123be8cd0865d71cff3577d506a2 100644
--- a/drivers/clk/clk_scmi.c
+++ b/drivers/clk/clk_scmi.c
@@ -12,6 +12,53 @@
#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 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)
+ return ret;
+
+ return out.permissions;
+ } else {
+ debug("SCMI clock management protocol version is less than 3.0.\n");
+ return -EOPNOTSUPP;
+ }
+}
+
static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
{
struct scmi_clk_protocol_attr_out out;
@@ -78,12 +125,26 @@ static int scmi_clk_gate(struct clk *clk, int enable)
static int scmi_clk_enable(struct clk *clk)
{
- return scmi_clk_gate(clk, 1);
+ struct clk_scmi *clkscmi = container_of(clk, 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 not have permission */
+ debug("SCMI CLOCK: the clock cannot be enabled by the agent.\n");
+ return 0;
}
static int scmi_clk_disable(struct clk *clk)
{
- return scmi_clk_gate(clk, 0);
+ struct clk_scmi *clkscmi = container_of(clk, 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 not have permission */
+ debug("SCMI CLOCK: the clock cannot be disabled by the agent.\n");
+ return 0;
}
static ulong scmi_clk_get_rate(struct clk *clk)
@@ -108,7 +169,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 +194,21 @@ 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 = container_of(clk, 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 not have permission */
+ debug("SCMI CLOCK: the clock rate cannot be changed by the agent.\n");
+ return 0;
+}
+
static int scmi_clk_probe(struct udevice *dev)
{
- struct clk *clk;
+ struct clk_scmi *clk_scmi;
size_t num_clocks, i;
int ret;
@@ -158,27 +231,38 @@ static int scmi_clk_probe(struct udevice *dev)
char *clock_name;
if (!scmi_clk_get_attibute(dev, i, &clock_name)) {
- clk = kzalloc(sizeof(*clk), GFP_KERNEL);
- if (!clk || !clock_name)
+ 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);
return ret;
}
- clk_dm(i, clk);
+ clk_dm(i, &clk_scmi->clk);
+
+ ret = scmi_clk_get_permissions(dev, i);
+ if (ret > 0) {
+ clk_scmi->ctrl_flags = ret;
+ } else if (ret == -EOPNOTSUPP) {
+ clk_scmi->ctrl_flags = SUPPORT_CLK_STAT_CONTROL |
+ SUPPORT_CLK_RATE_CONTROL;
+ } else {
+ debug("SCMI CLOCK: getting permissions failed.\n");
+ clk_scmi->ctrl_flags = 0;
+ }
}
}
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 +281,18 @@ 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 = container_of(clk, 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 not have permission */
+ debug("SCMI CLOCK: the clock's parent cannot be changed by the agent.\n");
+ return 0;
+}
+
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..8a5830d582d8baf4b4781ab860f09f49d335a023 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)
@@ -858,6 +860,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