firmware: arm_scmi: Implement clock get permissions
authorPeng Fan <peng.fan@nxp.com>
Sun, 21 Jan 2024 11:09:00 +0000 (19:09 +0800)
committerSudeep Holla <sudeep.holla@arm.com>
Tue, 20 Feb 2024 06:29:43 +0000 (06:29 +0000)
ARM SCMI v3.2 introduces clock get permission command. To implement the
same let us stash the values of those permissions in the scmi_clock_info.
They indicate if the operation is forbidden or not.

If the CLOCK_GET_PERMISSIONS command is not supported, the default
permissions are set to allow the operations, otherwise they will be set
according to the response of CLOCK_GET_PERMISSIONS from the SCMI
platform firmware.

Reviewed-by: Cristian Marussi <cristian.marussi@arm.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20240121110901.1414856-1-peng.fan@oss.nxp.com
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/firmware/arm_scmi/clock.c
include/linux/scmi_protocol.h

index 2e4d6479a639527fb4fd763cded63bf08c8408d2..959e48aba1b54b1bb3037e11a99bd9e692ecddda 100644 (file)
@@ -28,8 +28,13 @@ enum scmi_clock_protocol_cmd {
        CLOCK_POSSIBLE_PARENTS_GET = 0xC,
        CLOCK_PARENT_SET = 0xD,
        CLOCK_PARENT_GET = 0xE,
+       CLOCK_GET_PERMISSIONS = 0xF,
 };
 
+#define CLOCK_STATE_CONTROL_ALLOWED    BIT(31)
+#define CLOCK_PARENT_CONTROL_ALLOWED   BIT(30)
+#define CLOCK_RATE_CONTROL_ALLOWED     BIT(29)
+
 enum clk_state {
        CLK_STATE_DISABLE,
        CLK_STATE_ENABLE,
@@ -49,6 +54,7 @@ struct scmi_msg_resp_clock_attributes {
 #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x)        ((x) & BIT(30))
 #define SUPPORTS_EXTENDED_NAMES(x)             ((x) & BIT(29))
 #define SUPPORTS_PARENT_CLOCK(x)               ((x) & BIT(28))
+#define SUPPORTS_GET_PERMISSIONS(x)            ((x) & BIT(1))
        u8 name[SCMI_SHORT_NAME_MAX_SIZE];
        __le32 clock_enable_latency;
 };
@@ -293,6 +299,35 @@ static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u3
        return ret;
 }
 
+static int
+scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
+                          struct scmi_clock_info *clk)
+{
+       struct scmi_xfer *t;
+       u32 perm;
+       int ret;
+
+       ret = ph->xops->xfer_get_init(ph, CLOCK_GET_PERMISSIONS,
+                                     sizeof(clk_id), sizeof(perm), &t);
+       if (ret)
+               return ret;
+
+       put_unaligned_le32(clk_id, t->tx.buf);
+
+       ret = ph->xops->do_xfer(ph, t);
+       if (!ret) {
+               perm = get_unaligned_le32(t->rx.buf);
+
+               clk->state_ctrl_forbidden = !(perm & CLOCK_STATE_CONTROL_ALLOWED);
+               clk->rate_ctrl_forbidden = !(perm & CLOCK_RATE_CONTROL_ALLOWED);
+               clk->parent_ctrl_forbidden = !(perm & CLOCK_PARENT_CONTROL_ALLOWED);
+       }
+
+       ph->xops->xfer_put(ph, t);
+
+       return ret;
+}
+
 static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
                                     u32 clk_id, struct scmi_clock_info *clk,
                                     u32 version)
@@ -339,6 +374,8 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
                        clk->rate_change_requested_notifications = true;
                if (SUPPORTS_PARENT_CLOCK(attributes))
                        scmi_clock_possible_parents(ph, clk_id, clk);
+               if (SUPPORTS_GET_PERMISSIONS(attributes))
+                       scmi_clock_get_permissions(ph, clk_id, clk);
        }
 
        return ret;
@@ -511,6 +548,14 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
        struct scmi_xfer *t;
        struct scmi_clock_set_rate *cfg;
        struct clock_info *ci = ph->get_priv(ph);
+       struct scmi_clock_info *clk;
+
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (clk->rate_ctrl_forbidden)
+               return -EACCES;
 
        ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
        if (ret)
@@ -596,6 +641,9 @@ scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
        if (parent_id >= clk->num_parents)
                return -EINVAL;
 
+       if (clk->parent_ctrl_forbidden)
+               return -EACCES;
+
        ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET,
                                      sizeof(*cfg), 0, &t);
        if (ret)
@@ -679,6 +727,14 @@ static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id,
                             bool atomic)
 {
        struct clock_info *ci = ph->get_priv(ph);
+       struct scmi_clock_info *clk;
+
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (clk->state_ctrl_forbidden)
+               return -EACCES;
 
        return ci->clock_config_set(ph, clk_id, CLK_STATE_ENABLE,
                                    NULL_OEM_TYPE, 0, atomic);
@@ -688,6 +744,14 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id,
                              bool atomic)
 {
        struct clock_info *ci = ph->get_priv(ph);
+       struct scmi_clock_info *clk;
+
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       if (clk->state_ctrl_forbidden)
+               return -EACCES;
 
        return ci->clock_config_set(ph, clk_id, CLK_STATE_DISABLE,
                                    NULL_OEM_TYPE, 0, atomic);
index f2f05fb42d284034a94ba61d99e0e6d4fc523a1b..0cc40af5519a2bf0fd18c803a40bc01d4bbe7973 100644 (file)
@@ -47,6 +47,9 @@ struct scmi_clock_info {
        bool rate_discrete;
        bool rate_changed_notifications;
        bool rate_change_requested_notifications;
+       bool state_ctrl_forbidden;
+       bool rate_ctrl_forbidden;
+       bool parent_ctrl_forbidden;
        union {
                struct {
                        int num_rates;