diff mbox series

[3/4] spmi: msm: handle peripheral ownership

Message ID 20240405-topic-sm8x50-spmi-fixes-v1-3-031a9e5efab4@linaro.org
State Accepted
Delegated to: Caleb Connolly
Headers show
Series smpi: msm: fix version 5 and add version 7 support | expand

Commit Message

Neil Armstrong April 5, 2024, 8:21 a.m. UTC
The cnfg registers provides the owner id for each peripheral,
so we can use this id to check if we're allowed to write register
to each peripherals.

Since the v5 can handle more peripherals, add the max_channels to
scan more starting from version 5, make the channel_map store
32bit values and introduce the SPMI_CHANNEL_READ_ONLY flag to
mark a peripheral as read-only.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 drivers/spmi/spmi-msm.c | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/drivers/spmi/spmi-msm.c b/drivers/spmi/spmi-msm.c
index 68bb8a38c3c..46e2e09dc26 100644
--- a/drivers/spmi/spmi-msm.c
+++ b/drivers/spmi/spmi-msm.c
@@ -31,6 +31,8 @@  DECLARE_GLOBAL_DATA_PTR;
 #define SPMI_V5_OBS_CH_OFFSET(chnl) ((chnl) * 0x80)
 #define SPMI_V5_RW_CH_OFFSET(chnl) ((chnl) * 0x10000)
 
+#define SPMI_OWNERSHIP_PERIPH2OWNER(x)	((x) & 0x7)
+
 #define SPMI_REG_CMD0 0x0
 #define SPMI_REG_CONFIG 0x4
 #define SPMI_REG_STATUS 0x8
@@ -49,9 +51,13 @@  DECLARE_GLOBAL_DATA_PTR;
 #define SPMI_STATUS_DONE 0x1
 
 #define SPMI_MAX_CHANNELS 128
+#define SPMI_MAX_CHANNELS_V5	512
 #define SPMI_MAX_SLAVES 16
 #define SPMI_MAX_PERIPH 256
 
+#define SPMI_CHANNEL_READ_ONLY	BIT(31)
+#define SPMI_CHANNEL_MASK	0xffff
+
 enum arb_ver {
 	V1 = 1,
 	V2,
@@ -72,8 +78,11 @@  struct msm_spmi_priv {
 	phys_addr_t arb_chnl;  /* ARB channel mapping base */
 	phys_addr_t spmi_chnls; /* SPMI channels */
 	phys_addr_t spmi_obs;  /* SPMI observer */
+	phys_addr_t spmi_cnfg;  /* SPMI config */
+	u32 owner;	/* Current owner */
+	unsigned int max_channels; /* Max channels */
 	/* SPMI channel map */
-	uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
+	uint32_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
 	/* SPMI bus arbiter version */
 	u32 arb_ver;
 };
@@ -100,8 +109,10 @@  static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
 		return -EIO;
 	if (pid >= SPMI_MAX_PERIPH)
 		return -EIO;
+	if (priv->channel_map[usid][pid] & SPMI_CHANNEL_READ_ONLY)
+		return -EPERM;
 
-	channel = priv->channel_map[usid][pid];
+	channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
 
 	dev_dbg(dev, "[%d:%d] %s: channel %d\n", usid, pid, __func__, channel);
 
@@ -162,7 +173,7 @@  static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
 	if (pid >= SPMI_MAX_PERIPH)
 		return -EIO;
 
-	channel = priv->channel_map[usid][pid];
+	channel = priv->channel_map[usid][pid] & SPMI_CHANNEL_MASK;
 
 	dev_dbg(dev, "[%d:%d] %s: channel %d\n", usid, pid, __func__, channel);
 
@@ -227,18 +238,23 @@  static int msm_spmi_probe(struct udevice *dev)
 	core_addr = dev_read_addr_name(dev, "core");
 	priv->spmi_chnls = dev_read_addr_name(dev, "chnls");
 	priv->spmi_obs = dev_read_addr_name(dev, "obsrvr");
+	dev_read_u32(dev, "qcom,ee", &priv->owner);
 
 	hw_ver = readl(core_addr + PMIC_ARB_VERSION);
 
 	if (hw_ver < PMIC_ARB_VERSION_V3_MIN) {
 		priv->arb_ver = V2;
 		priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
+		priv->max_channels = SPMI_MAX_CHANNELS;
 	} else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) {
 		priv->arb_ver = V3;
 		priv->arb_chnl = core_addr + APID_MAP_OFFSET_V1_V2_V3;
+		priv->max_channels = SPMI_MAX_CHANNELS;
 	} else {
 		priv->arb_ver = V5;
 		priv->arb_chnl = core_addr + APID_MAP_OFFSET_V5;
+		priv->max_channels = SPMI_MAX_CHANNELS_V5;
+		priv->spmi_cnfg = dev_read_addr_name(dev, "cnfg");
 	}
 
 	dev_dbg(dev, "PMIC Arb Version-%d (%#x)\n", hw_ver >> 28, hw_ver);
@@ -252,12 +268,21 @@  static int msm_spmi_probe(struct udevice *dev)
 	dev_dbg(dev, "priv->spmi_chnls address (%#08llx)\n", priv->spmi_chnls);
 	dev_dbg(dev, "priv->spmi_obs address (%#08llx)\n", priv->spmi_obs);
 	/* Scan peripherals connected to each SPMI channel */
-	for (i = 0; i < SPMI_MAX_PERIPH; i++) {
+	for (i = 0; i < priv->max_channels; i++) {
 		uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
 		uint8_t slave_id = (periph & 0xf0000) >> 16;
 		uint8_t pid = (periph & 0xff00) >> 8;
 
 		priv->channel_map[slave_id][pid] = i;
+
+		/* Mark channels read-only when from different owner */
+		if (priv->arb_ver == V5) {
+			uint32_t cnfg = readl(priv->spmi_cnfg + ARB_CHANNEL_OFFSET(i));
+			uint8_t owner = SPMI_OWNERSHIP_PERIPH2OWNER(cnfg);
+
+			if (owner != priv->owner)
+				priv->channel_map[slave_id][pid] |= SPMI_CHANNEL_READ_ONLY;
+		}
 	}
 	return 0;
 }