
From: Ye Li ye.li@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@nxp.com Reviewed-by: Peng Fan peng.fan@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 */