@@ -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;
}
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(-)