Merge tag 'pm-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
[sfrench/cifs-2.6.git] / drivers / firmware / arm_scmi / driver.c
index 1d38ecfafc59af6418cfbfa534fe1a2645f19ac8..2709598f3008908ecc7d377508bb99eedbb6f1b6 100644 (file)
@@ -86,6 +86,12 @@ struct scmi_xfers_info {
  * @users: A refcount to track effective users of this protocol.
  * @priv: Reference for optional protocol private data.
  * @version: Protocol version supported by the platform as detected at runtime.
+ * @negotiated_version: When the platform supports a newer protocol version,
+ *                     the agent will try to negotiate with the platform the
+ *                     usage of the newest version known to it, since
+ *                     backward compatibility is NOT automatically assured.
+ *                     This field is NON-zero when a successful negotiation
+ *                     has completed.
  * @ph: An embedded protocol handle that will be passed down to protocol
  *     initialization code to identify this instance.
  *
@@ -99,6 +105,7 @@ struct scmi_protocol_instance {
        refcount_t                      users;
        void                            *priv;
        unsigned int                    version;
+       unsigned int                    negotiated_version;
        struct scmi_protocol_handle     ph;
 };
 
@@ -1757,10 +1764,44 @@ static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db)
 #endif
 }
 
+/**
+ * scmi_protocol_msg_check  - Check protocol message attributes
+ *
+ * @ph: A reference to the protocol handle.
+ * @message_id: The ID of the message to check.
+ * @attributes: A parameter to optionally return the retrieved message
+ *             attributes, in case of Success.
+ *
+ * An helper to check protocol message attributes for a specific protocol
+ * and message pair.
+ *
+ * Return: 0 on SUCCESS
+ */
+static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,
+                                  u32 message_id, u32 *attributes)
+{
+       int ret;
+       struct scmi_xfer *t;
+
+       ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES,
+                           sizeof(__le32), 0, &t);
+       if (ret)
+               return ret;
+
+       put_unaligned_le32(message_id, t->tx.buf);
+       ret = do_xfer(ph, t);
+       if (!ret && attributes)
+               *attributes = get_unaligned_le32(t->rx.buf);
+       xfer_put(ph, t);
+
+       return ret;
+}
+
 static const struct scmi_proto_helpers_ops helpers_ops = {
        .extended_name_get = scmi_common_extended_name_get,
        .iter_response_init = scmi_iterator_init,
        .iter_response_run = scmi_iterator_run,
+       .protocol_msg_check = scmi_protocol_msg_check,
        .fastchannel_init = scmi_common_fastchannel_init,
        .fastchannel_db_ring = scmi_common_fastchannel_db_ring,
 };
@@ -1784,6 +1825,44 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph)
        return pi->handle->version;
 }
 
+/**
+ * scmi_protocol_version_negotiate  - Negotiate protocol version
+ *
+ * @ph: A reference to the protocol handle.
+ *
+ * An helper to negotiate a protocol version different from the latest
+ * advertised as supported from the platform: on Success backward
+ * compatibility is assured by the platform.
+ *
+ * Return: 0 on Success
+ */
+static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph)
+{
+       int ret;
+       struct scmi_xfer *t;
+       struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+       /* At first check if NEGOTIATE_PROTOCOL_VERSION is supported ... */
+       ret = scmi_protocol_msg_check(ph, NEGOTIATE_PROTOCOL_VERSION, NULL);
+       if (ret)
+               return ret;
+
+       /* ... then attempt protocol version negotiation */
+       ret = xfer_get_init(ph, NEGOTIATE_PROTOCOL_VERSION,
+                           sizeof(__le32), 0, &t);
+       if (ret)
+               return ret;
+
+       put_unaligned_le32(pi->proto->supported_version, t->tx.buf);
+       ret = do_xfer(ph, t);
+       if (!ret)
+               pi->negotiated_version = pi->proto->supported_version;
+
+       xfer_put(ph, t);
+
+       return ret;
+}
+
 /**
  * scmi_alloc_init_protocol_instance  - Allocate and initialize a protocol
  * instance descriptor.
@@ -1856,11 +1935,21 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
        devres_close_group(handle->dev, pi->gid);
        dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
 
-       if (pi->version > proto->supported_version)
-               dev_warn(handle->dev,
-                        "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X."
-                        "Backward compatibility is NOT assured.\n",
-                        pi->version, pi->proto->id);
+       if (pi->version > proto->supported_version) {
+               ret = scmi_protocol_version_negotiate(&pi->ph);
+               if (!ret) {
+                       dev_info(handle->dev,
+                                "Protocol 0x%X successfully negotiated version 0x%X\n",
+                                proto->id, pi->negotiated_version);
+               } else {
+                       dev_warn(handle->dev,
+                                "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
+                                pi->version, pi->proto->id);
+                       dev_warn(handle->dev,
+                                "Trying version 0x%X. Backward compatibility is NOT assured.\n",
+                                pi->proto->supported_version);
+               }
+       }
 
        return pi;