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

Reply via email to