Merge tag 'zonefs-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal...
[sfrench/cifs-2.6.git] / drivers / firmware / arm_scmi / clock.c
index e2050adbf85c6a125fc5ba241fb0c6b133466bfe..134019297d08b1bd9e4b02e69a1cc16a3dc429b2 100644 (file)
@@ -13,7 +13,7 @@
 #include "notify.h"
 
 /* Updated only after ALL the mandatory features for that version are merged */
-#define SCMI_PROTOCOL_SUPPORTED_VERSION                0x20000
+#define SCMI_PROTOCOL_SUPPORTED_VERSION                0x30000
 
 enum scmi_clock_protocol_cmd {
        CLOCK_ATTRIBUTES = 0x3,
@@ -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,8 @@ 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_EXTENDED_CONFIG(x)            ((x) & BIT(27))
+#define SUPPORTS_GET_PERMISSIONS(x)            ((x) & BIT(1))
        u8 name[SCMI_SHORT_NAME_MAX_SIZE];
        __le32 clock_enable_latency;
 };
@@ -152,14 +159,18 @@ struct clock_info {
        u32 version;
        int num_clocks;
        int max_async_req;
+       bool notify_rate_changed_cmd;
+       bool notify_rate_change_requested_cmd;
        atomic_t cur_async_req;
        struct scmi_clock_info *clk;
        int (*clock_config_set)(const struct scmi_protocol_handle *ph,
                                u32 clk_id, enum clk_state state,
-                               u8 oem_type, u32 oem_val, bool atomic);
+                               enum scmi_clock_oem_config oem_type,
+                               u32 oem_val, bool atomic);
        int (*clock_config_get)(const struct scmi_protocol_handle *ph,
-                               u32 clk_id, u8 oem_type, u32 *attributes,
-                               bool *enabled, u32 *oem_val, bool atomic);
+                               u32 clk_id, enum scmi_clock_oem_config oem_type,
+                               u32 *attributes, bool *enabled, u32 *oem_val,
+                               bool atomic);
 };
 
 static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
@@ -167,6 +178,15 @@ static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
        CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
 };
 
+static inline struct scmi_clock_info *
+scmi_clock_domain_lookup(struct clock_info *ci, u32 clk_id)
+{
+       if (clk_id >= ci->num_clocks)
+               return ERR_PTR(-EINVAL);
+
+       return ci->clk + clk_id;
+}
+
 static int
 scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
                                   struct clock_info *ci)
@@ -189,6 +209,17 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
        }
 
        ph->xops->xfer_put(ph, t);
+
+       if (!ret) {
+               if (!ph->hops->protocol_msg_check(ph, CLOCK_RATE_NOTIFY, NULL))
+                       ci->notify_rate_changed_cmd = true;
+
+               if (!ph->hops->protocol_msg_check(ph,
+                                                 CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
+                                                 NULL))
+                       ci->notify_rate_change_requested_cmd = true;
+       }
+
        return ret;
 }
 
@@ -284,14 +315,44 @@ 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 clk_id, struct clock_info *cinfo,
                                     u32 version)
 {
        int ret;
        u32 attributes;
        struct scmi_xfer *t;
        struct scmi_msg_resp_clock_attributes *attr;
+       struct scmi_clock_info *clk = cinfo->clk + clk_id;
 
        ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
                                      sizeof(clk_id), sizeof(*attr), &t);
@@ -324,12 +385,20 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
                                                    NULL, clk->name,
                                                    SCMI_MAX_STR_SIZE);
 
-               if (SUPPORTS_RATE_CHANGED_NOTIF(attributes))
+               if (cinfo->notify_rate_changed_cmd &&
+                   SUPPORTS_RATE_CHANGED_NOTIF(attributes))
                        clk->rate_changed_notifications = true;
-               if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
+               if (cinfo->notify_rate_change_requested_cmd &&
+                   SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
                        clk->rate_change_requested_notifications = true;
-               if (SUPPORTS_PARENT_CLOCK(attributes))
-                       scmi_clock_possible_parents(ph, clk_id, clk);
+               if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
+                       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);
+                       if (SUPPORTS_EXTENDED_CONFIG(attributes))
+                               clk->extended_config = true;
+               }
        }
 
        return ret;
@@ -502,6 +571,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)
@@ -543,7 +620,8 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph,
 
 static int
 scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
-                     enum clk_state state, u8 __unused0, u32 __unused1,
+                     enum clk_state state,
+                     enum scmi_clock_oem_config __unused0, u32 __unused1,
                      bool atomic)
 {
        int ret;
@@ -580,14 +658,16 @@ scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
        struct clock_info *ci = ph->get_priv(ph);
        struct scmi_clock_info *clk;
 
-       if (clk_id >= ci->num_clocks)
-               return -EINVAL;
-
-       clk = ci->clk + clk_id;
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
 
        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)
@@ -628,10 +708,11 @@ scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id,
        return ret;
 }
 
-/* For SCMI clock v2.1 and onwards */
+/* For SCMI clock v3.0 and onwards */
 static int
 scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
-                        enum clk_state state, u8 oem_type, u32 oem_val,
+                        enum clk_state state,
+                        enum scmi_clock_oem_config oem_type, u32 oem_val,
                         bool atomic)
 {
        int ret;
@@ -671,6 +752,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);
@@ -680,16 +769,24 @@ 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);
 }
 
-/* For SCMI clock v2.1 and onwards */
+/* For SCMI clock v3.0 and onwards */
 static int
 scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
-                        u8 oem_type, u32 *attributes, bool *enabled,
-                        u32 *oem_val, bool atomic)
+                        enum scmi_clock_oem_config oem_type, u32 *attributes,
+                        bool *enabled, u32 *oem_val, bool atomic)
 {
        int ret;
        u32 flags;
@@ -730,8 +827,8 @@ scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
 
 static int
 scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id,
-                     u8 oem_type, u32 *attributes, bool *enabled,
-                     u32 *oem_val, bool atomic)
+                     enum scmi_clock_oem_config oem_type, u32 *attributes,
+                     bool *enabled, u32 *oem_val, bool atomic)
 {
        int ret;
        struct scmi_xfer *t;
@@ -768,20 +865,38 @@ static int scmi_clock_state_get(const struct scmi_protocol_handle *ph,
 }
 
 static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph,
-                                    u32 clk_id, u8 oem_type, u32 oem_val,
-                                    bool atomic)
+                                    u32 clk_id,
+                                    enum scmi_clock_oem_config oem_type,
+                                    u32 oem_val, 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->extended_config)
+               return -EOPNOTSUPP;
 
        return ci->clock_config_set(ph, clk_id, CLK_STATE_UNCHANGED,
                                    oem_type, oem_val, atomic);
 }
 
 static int scmi_clock_config_oem_get(const struct scmi_protocol_handle *ph,
-                                    u32 clk_id, u8 oem_type, u32 *oem_val,
-                                    u32 *attributes, bool atomic)
+                                    u32 clk_id,
+                                    enum scmi_clock_oem_config oem_type,
+                                    u32 *oem_val, u32 *attributes, 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->extended_config)
+               return -EOPNOTSUPP;
 
        return ci->clock_config_get(ph, clk_id, oem_type, attributes,
                                    NULL, oem_val, atomic);
@@ -800,10 +915,10 @@ scmi_clock_info_get(const struct scmi_protocol_handle *ph, u32 clk_id)
        struct scmi_clock_info *clk;
        struct clock_info *ci = ph->get_priv(ph);
 
-       if (clk_id >= ci->num_clocks)
+       clk = scmi_clock_domain_lookup(ci, clk_id);
+       if (IS_ERR(clk))
                return NULL;
 
-       clk = ci->clk + clk_id;
        if (!clk->name[0])
                return NULL;
 
@@ -824,6 +939,28 @@ static const struct scmi_clk_proto_ops clk_proto_ops = {
        .parent_get = scmi_clock_get_parent,
 };
 
+static bool scmi_clk_notify_supported(const struct scmi_protocol_handle *ph,
+                                     u8 evt_id, u32 src_id)
+{
+       bool supported;
+       struct scmi_clock_info *clk;
+       struct clock_info *ci = ph->get_priv(ph);
+
+       if (evt_id >= ARRAY_SIZE(evt_2_cmd))
+               return false;
+
+       clk = scmi_clock_domain_lookup(ci, src_id);
+       if (IS_ERR(clk))
+               return false;
+
+       if (evt_id == SCMI_EVENT_CLOCK_RATE_CHANGED)
+               supported = clk->rate_changed_notifications;
+       else
+               supported = clk->rate_change_requested_notifications;
+
+       return supported;
+}
+
 static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph,
                                u32 clk_id, int message_id, bool enable)
 {
@@ -908,6 +1045,7 @@ static const struct scmi_event clk_events[] = {
 };
 
 static const struct scmi_event_ops clk_event_ops = {
+       .is_notify_supported = scmi_clk_notify_supported,
        .get_num_sources = scmi_clk_get_num_sources,
        .set_notify_enabled = scmi_clk_set_notify_enabled,
        .fill_custom_report = scmi_clk_fill_custom_report,
@@ -949,7 +1087,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
        for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
                struct scmi_clock_info *clk = cinfo->clk + clkid;
 
-               ret = scmi_clock_attributes_get(ph, clkid, clk, version);
+               ret = scmi_clock_attributes_get(ph, clkid, cinfo, version);
                if (!ret)
                        scmi_clock_describe_rates_get(ph, clkid, clk);
        }