spmi: msm: correctly handle multiple mapping entries
authorNeil Armstrong <neil.armstrong@linaro.org>
Fri, 28 Mar 2025 08:53:24 +0000 (09:53 +0100)
committerCaleb Connolly <caleb.connolly@linaro.org>
Thu, 10 Apr 2025 13:43:10 +0000 (15:43 +0200)
On v5 & v7 controllers, multiple mapping for different
Execution Environment exists, if the mapping owner is for
a different Execution Environment we can only read and
not write any data.

To allow us to find a Write mapping for our Execution
Environment, we can overwritte a mapping if we encounter
a new one which we own.

Implement this logic, the result is the same mapping
table as in Linux.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Tested-by: caleb.connolly@linaro.org # sdm845
Link: https://lore.kernel.org/r/20250328-topic-sm8x50-spmi-fix-v1-4-a7548d3aef0d@linaro.org
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
drivers/spmi/spmi-msm.c

index ee3feaf..faae54e 100644 (file)
@@ -254,16 +254,30 @@ static struct dm_spmi_ops msm_spmi_ops = {
        .write = msm_spmi_write,
 };
 
+/*
+ * In order to allow multiple EEs to write to a single PPID in arbiter
+ * version 5 and 7, there is more than one APID mapped to each PPID.
+ * The owner field for each of these mappings specifies the EE which is
+ * allowed to write to the APID.
+ */
 static void msm_spmi_channel_map_v5(struct msm_spmi_priv *priv, unsigned int i,
                                    uint8_t slave_id, uint8_t pid)
 {
        /* Mark channels read-only when from different owner */
        uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i));
        uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg);
-
-       priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID;
-       if (owner != priv->owner)
-               priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY;
+       bool prev_valid = priv->channel_map[slave_id][pid] & SPMI_CHANNEL_VALID;
+       uint32_t prev_read_only = priv->channel_map[slave_id][pid] & SPMI_CHANNEL_READ_ONLY;
+
+       if (!prev_valid) {
+               /* First PPID mapping */
+               priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID;
+               if (owner != priv->owner)
+                       priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY;
+       } else if ((owner == priv->owner) && prev_read_only) {
+               /* Read only and we found one we own, switch */
+               priv->channel_map[slave_id][pid] = i | SPMI_CHANNEL_VALID;
+       }
 }
 
 static int msm_spmi_probe(struct udevice *dev)