]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - drivers/spmi/spmi-msm.c
drivers: spmi: msm: add timeout to function waiting for SPMI_STATUS_DONE
[karo-tx-uboot.git] / drivers / spmi / spmi-msm.c
index 41541875de71dad4325c10e3d24307170064b26e..44062c46d083d014710a414411215eabb21f7bd6 100644 (file)
@@ -20,6 +20,9 @@ DECLARE_GLOBAL_DATA_PTR;
 #define ARB_CHANNEL_OFFSET(n)          (0x4 * (n))
 #define SPMI_CH_OFFSET(chnl)           ((chnl) * 0x8000)
 
+#define PMIC_ARB_VERSION               0x0000
+#define PMIC_ARB_VERSION_V2_MIN                0x20010000
+
 #define SPMI_REG_CMD0                  0x0
 #define SPMI_REG_CONFIG                        0x4
 #define SPMI_REG_STATUS                        0x8
@@ -32,18 +35,33 @@ DECLARE_GLOBAL_DATA_PTR;
 #define SPMI_CMD_ADDR_OFFSET_SHIFT     4
 #define SPMI_CMD_BYTE_CNT_SHIFT                0
 
+#define SPMI_V2_CMD_OPCODE_SHIFT       27
+#define SPMI_V2_CMD_ADDR_OFFSET_SHIFT  4
+#define SPMI_V2_CMD_BYTE_CNT_SHIFT     0
+
 #define SPMI_CMD_EXT_REG_WRITE_LONG    0x00
 #define SPMI_CMD_EXT_REG_READ_LONG     0x01
 
 #define SPMI_STATUS_DONE               0x1
 
-#define SPMI_MAX_CHANNELS      128
-#define SPMI_MAX_SLAVES                16
-#define SPMI_MAX_PERIPH                256
+#define SPMI_MAX_CHANNELS              128
+#define SPMI_MAX_SLAVES                        16
+#define SPMI_MAX_PERIPH                        256
+
+#define SPMI_READ_TIMEOUT              100
+#define SPMI_WRITE_TIMEOUT             100
+
+static int pmic_arb_ver;
+
+static inline int pmic_arb_is_v1(void)
+{
+       return pmic_arb_ver < PMIC_ARB_VERSION_V2_MIN;
+}
 
 struct msm_spmi_priv {
        phys_addr_t arb_chnl; /* ARB channel mapping base */
        phys_addr_t spmi_core; /* SPMI core */
+       phys_addr_t spmi_chnls; /* SPMI chnls */
        phys_addr_t spmi_obs; /* SPMI observer */
        /* SPMI channel map */
        uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
@@ -55,6 +73,7 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
        struct msm_spmi_priv *priv = dev_get_priv(dev);
        unsigned channel;
        uint32_t reg = 0;
+       int timeout = SPMI_WRITE_TIMEOUT;
 
        if (usid >= SPMI_MAX_SLAVES)
                return -EINVAL;
@@ -63,28 +82,39 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
 
        channel = priv->channel_map[usid][pid];
 
-       /* Disable IRQ mode for the current channel*/
-       writel(0x0, priv->spmi_core + SPMI_CH_OFFSET(channel) +
+       /* Disable IRQ mode for the current channel */
+       writel(0x0, priv->spmi_chnls + SPMI_CH_OFFSET(channel) +
               SPMI_REG_CONFIG);
 
        /* Write single byte */
-       writel(val, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA);
+       writel(val, priv->spmi_chnls + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA);
 
        /* Prepare write command */
-       reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT;
-       reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
-       reg |= (pid << SPMI_CMD_ADDR_SHIFT);
-       reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
-       reg |= 1; /* byte count */
-
+       if (pmic_arb_is_v1()) {
+               reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT;
+               reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
+               reg |= (pid << SPMI_CMD_ADDR_SHIFT);
+               reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
+               reg |= 1; /* byte count */
+       } else {
+               reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT;
+               reg |= ((off & 0xff) << SPMI_CMD_ADDR_OFFSET_SHIFT);
+               reg |= 0; /* byte count - 1 */
+       }
        /* Send write command */
-       writel(reg, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
+       writel(reg, priv->spmi_chnls + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
 
        /* Wait till CMD DONE status */
-       reg = 0;
-       while (!reg) {
-               reg = readl(priv->spmi_core + SPMI_CH_OFFSET(channel) +
+       do {
+               reg = readl(priv->spmi_chnls + SPMI_CH_OFFSET(channel) +
                            SPMI_REG_STATUS);
+               if (reg)
+                       break;
+               udelay(1);
+       } while (timeout-- > 0);
+       if (!(reg & SPMI_STATUS_DONE)) {
+               printf("SPMI write timed out\n");
+               return -ETIMEDOUT;
        }
 
        if (reg ^ SPMI_STATUS_DONE) {
@@ -112,11 +142,17 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
        writel(0x0, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG);
 
        /* Prepare read command */
-       reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT;
-       reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
-       reg |= (pid << SPMI_CMD_ADDR_SHIFT);
-       reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
-       reg |= 1; /* byte count */
+       if (pmic_arb_is_v1()) {
+               reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT;
+               reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT);
+               reg |= (pid << SPMI_CMD_ADDR_SHIFT);
+               reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT);
+               reg |= 1; /* byte count */
+       } else {
+               reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT;
+               reg |= ((off & 0xff) << SPMI_CMD_ADDR_OFFSET_SHIFT);
+               reg |= 0; /* byte count - 1 */
+       }
 
        /* Request read */
        writel(reg, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
@@ -149,8 +185,8 @@ static int msm_spmi_probe(struct udevice *dev)
        struct msm_spmi_priv *priv = dev_get_priv(dev);
        int i;
 
-       priv->arb_chnl = dev_get_addr(dev);
-       priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
+       priv->spmi_core = dev_get_addr(dev);
+       priv->spmi_chnls = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
                                                           parent->of_offset,
                                                           dev->of_offset,
                                                           "reg", 1, NULL,
@@ -159,11 +195,13 @@ static int msm_spmi_probe(struct udevice *dev)
                                                          parent->of_offset,
                                                          dev->of_offset, "reg",
                                                          2, NULL, false);
-       if (priv->arb_chnl == FDT_ADDR_T_NONE ||
-           priv->spmi_core == FDT_ADDR_T_NONE ||
+       if (priv->spmi_core == FDT_ADDR_T_NONE ||
+           priv->spmi_chnls == FDT_ADDR_T_NONE ||
            priv->spmi_obs == FDT_ADDR_T_NONE)
                return -EINVAL;
 
+       priv->arb_chnl = priv->spmi_core + 0x800;
+
        /* Scan peripherals connected to each SPMI channel */
        for (i = 0; i < SPMI_MAX_CHANNELS ; i++) {
                uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i));
@@ -172,6 +210,8 @@ static int msm_spmi_probe(struct udevice *dev)
 
                priv->channel_map[slave_id][pid] = i;
        }
+       pmic_arb_ver = readl(priv->spmi_core + PMIC_ARB_VERSION);
+       printf("PMIC: PM8916 ARB version %d\n", pmic_arb_is_v1() ? 1 : 2);
        return 0;
 }