]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
firmware: arm_scmi: Rework protocol version negotiation logic
authorCristian Marussi <cristian.marussi@arm.com>
Sat, 27 Dec 2025 16:41:31 +0000 (16:41 +0000)
committerSudeep Holla <sudeep.holla@arm.com>
Sat, 27 Dec 2025 20:09:30 +0000 (20:09 +0000)
Protocol version negotiation can be used by an agent to request the server
to downgrade the version effectively utilized by a specific protocol
during the current session, if the latest version used by the server is
newer than the latest version known to the client.

In order for the negotiation process to be fully effective at preventing
any possible version incompatibility, it must happen early on, well before
the specific protocol initialization phase takes place.

Delegate protocol version querying to the core SCMI stack and rework the
protocol negotiation logic in order to execute the needed negotiation
exchanges upfront, right before the initialization phase takes place.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
Message-Id: <20251227164132.1311988-2-cristian.marussi@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
drivers/firmware/arm_scmi/driver.c
drivers/firmware/arm_scmi/protocols.h

index 5caa9191a8d1a36354afcb0fe2ee4f760fffbf82..094cfcf51d289a1a0dc79bc81f25fa5fb987e1c4 100644 (file)
@@ -2112,6 +2112,76 @@ static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph)
        return ret;
 }
 
+/**
+ * scmi_protocol_version_initialize  - Initialize protocol version
+ * @dev: A device reference.
+ * @pi: A reference to the protocol instance being initialized
+ *
+ * At first retrieve the newest protocol version supported by the platform for
+ * this specific protoocol.
+ *
+ * Negotiation is attempted only when the platform advertised a protocol
+ * version newer than the most recent version known to this agent, since
+ * backward compatibility is NOT assured in general between versions.
+ *
+ * Failing to negotiate a fallback version or to query supported version at
+ * all will result in an attempt to use the newest version known to this agent
+ * even though compatibility is NOT assured.
+ *
+ * Versions are defined as:
+ *
+ * pi->version: the version supported by the platform as returned by the query.
+ * pi->proto->supported_version: the newest version supported by this agent
+ *                              for this protocol.
+ * pi->negotiated_version: The version successfully negotiated with the platform.
+ * ph->version: The final version effectively chosen for this session.
+ */
+static void scmi_protocol_version_initialize(struct device *dev,
+                                            struct scmi_protocol_instance *pi)
+{
+       struct scmi_protocol_handle *ph = &pi->ph;
+       int ret;
+
+       /*
+        * Query and store platform supported protocol version: this is usually
+        * the newest version the platfom can support.
+        */
+       ret = version_get(ph, &pi->version);
+       if (ret) {
+               dev_warn(dev,
+                        "Failed to query supported version for protocol 0x%X.\n",
+                        pi->proto->id);
+               goto best_effort;
+       }
+
+       /* Need to negotiate at all ? */
+       if (pi->version <= pi->proto->supported_version) {
+               ph->version = pi->version;
+               return;
+       }
+
+       /* Attempt negotiation */
+       ret = scmi_protocol_version_negotiate(ph);
+       if (!ret) {
+               ph->version = pi->negotiated_version;
+               dev_info(dev,
+                        "Protocol 0x%X successfully negotiated version 0x%X\n",
+                        pi->proto->id, ph->version);
+               return;
+       }
+
+       dev_warn(dev,
+                "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
+                pi->version, pi->proto->id);
+
+best_effort:
+       /* Fallback to use newest version known to this agent */
+       ph->version = pi->proto->supported_version;
+       dev_warn(dev,
+                "Trying version 0x%X. Backward compatibility is NOT assured.\n",
+                ph->version);
+}
+
 /**
  * scmi_alloc_init_protocol_instance  - Allocate and initialize a protocol
  * instance descriptor.
@@ -2157,6 +2227,13 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
        pi->ph.set_priv = scmi_set_protocol_priv;
        pi->ph.get_priv = scmi_get_protocol_priv;
        refcount_set(&pi->users, 1);
+
+       /*
+        * Initialize effectively used protocol version performing any
+        * possibly needed negotiations.
+        */
+       scmi_protocol_version_initialize(handle->dev, pi);
+
        /* proto->init is assured NON NULL by scmi_protocol_register */
        ret = pi->proto->instance_init(&pi->ph);
        if (ret)
@@ -2184,22 +2261,6 @@ 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) {
-               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;
 
 clean:
index d62c4469d1fd9f1f3eb9f09327d1dcb4c18b2f7b..2766c2b822b5666ba884e9ecfcccdc4963ab203f 100644 (file)
@@ -159,6 +159,9 @@ struct scmi_proto_helpers_ops;
  * struct scmi_protocol_handle  - Reference to an initialized protocol instance
  *
  * @dev: A reference to the associated SCMI instance device (handle->dev).
+ * @version: The protocol version currently effectively in use by this
+ *          initialized instance of the protocol as determined at the end of
+ *          any possibly needed negotiations performed by the core.
  * @xops: A reference to a struct holding refs to the core xfer operations that
  *       can be used by the protocol implementation to generate SCMI messages.
  * @set_priv: A method to set protocol private data for this instance.
@@ -177,6 +180,7 @@ struct scmi_proto_helpers_ops;
  */
 struct scmi_protocol_handle {
        struct device *dev;
+       unsigned int version;
        const struct scmi_xfer_ops *xops;
        const struct scmi_proto_helpers_ops *hops;
        int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv,