]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
drivers: spmi: msm: add support for multi register operations
authorLothar Waßmann <LW@KARO-electronics.de>
Mon, 13 Mar 2017 15:37:59 +0000 (16:37 +0100)
committerLothar Waßmann <LW@KARO-electronics.de>
Tue, 14 Mar 2017 16:41:23 +0000 (17:41 +0100)
The SPMI protocol supports reading/writing multiple registers in one
bus access. Add the byte count to be transferred to the generic
read/write functions in dm_spmi_ops and create new functions
spmi_read()/spmi_write() to utilize this new functionality.

cmd/spmi.c
drivers/power/pmic/pm8916.c
drivers/spmi/spmi-msm.c
drivers/spmi/spmi-sandbox.c
drivers/spmi/spmi-uclass.c
include/spmi/spmi.h

index fd80b3a7d8cbe19503f35f8d41f1e50d4703b96f..9792fa9ff93c0553e574d369b6b3cdca9ac2d762 100644 (file)
@@ -54,11 +54,13 @@ static int spmi_report_err(int ret, enum spmi_err_op op)
 static int do_spmi_read(cmd_tbl_t *cmdtp, int flag, int argc,
                        char *const argv[])
 {
-       uint sid, pid, reg;
+       uint sid, pid, reg, cnt = 1;
+       uint8_t buf[8];
        int ret;
+       int i;
        struct udevice *dev;
 
-       if (argc != 4)
+       if (argc < 4 || argc > 5)
                return cmd_usage(cmdtp);
 
        ret = uclass_get_device_by_name(UCLASS_SPMI, "spmi", &dev);
@@ -67,23 +69,46 @@ static int do_spmi_read(cmd_tbl_t *cmdtp, int flag, int argc,
                return CMD_RET_FAILURE;
        }
 
-       /*
-        * SPMI chip address
-        */
+       /* SPMI slave ID */
        sid = simple_strtoul(argv[1], NULL, 16);
+       if (sid > 5) {
+               printf("SID %02x out of range 0..5\n", sid);
+               return CMD_RET_FAILURE;
+       }
 
-       /*
-        * SPMI data address within the chip.  This can be 1 or
-        * 2 bytes long.  Some day it might be 3 bytes long :-).
-        */
+       /* SPMI peripheral ID */
        pid = simple_strtoul(argv[2], NULL, 16);
+       if (pid > 255) {
+               printf("PID %02x out of range 0x00..0xff\n", pid);
+               return CMD_RET_FAILURE;
+       }
+
+       /* SPMI register offset */
        reg = simple_strtoul(argv[3], NULL, 16);
+       if (reg > 255) {
+               printf("REG offset %02x out of range 0x00..0xff\n", reg);
+               return CMD_RET_FAILURE;
+       }
+
+       /* SPMI byte count */
+       if (argc > 4) {
+               cnt = simple_strtoul(argv[4], NULL, 16);
+               if (cnt == 0 || cnt > sizeof(buf)) {
+                       printf("Byte count %u out of range 1..%lu\n",
+                               cnt, sizeof(buf));
+                       return CMD_RET_FAILURE;
+               }
+       }
 
-       ret = spmi_reg_read(dev, sid, pid, reg);
+       debug("%s@%d: Reading SID %02x PID %02x REG %02x\n", __func__, __LINE__,
+               sid, pid, reg);
+       ret = spmi_read(dev, buf, sid, pid, reg, cnt);
        if (ret < 0)
                return spmi_report_err(ret, SPMI_ERR_READ);
 
-       printf("%02x\n", ret);
+       for (i = 0; i < cnt; i++)
+               printf("%02x\n", buf[i]);
+
        return CMD_RET_SUCCESS;
 }
 
@@ -115,6 +140,8 @@ static int do_spmi_write(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[
                return CMD_RET_FAILURE;
        }
 
+       debug("%s@%d: Writing SID %02x PID %02x REG %02x value %02x\n",
+               __func__, __LINE__, sid, pid, reg, val);
        ret = spmi_reg_write(dev, sid, pid, reg, val);
        if (ret)
                return spmi_report_err(ret, SPMI_ERR_WRITE);
index 2b65c697ec9431a87497b29011638464fcef628e..456005fcd02e87a866bc3d0ea97049c65bf9629e 100644 (file)
@@ -30,29 +30,18 @@ static int pm8916_write(struct udevice *dev, uint reg, const uint8_t *buff,
 {
        struct pm8916_priv *priv = dev_get_priv(dev);
 
-       if (len != 1)
-               return -EINVAL;
-
-       return spmi_reg_write(dev->parent, priv->usid,
-                             (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK,
-                             *buff);
+       return spmi_write(dev->parent, buff, priv->usid,
+                         (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK,
+                         len);
 }
 
 static int pm8916_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
 {
        struct pm8916_priv *priv = dev_get_priv(dev);
-       int val;
-
-       if (len != 1)
-               return -EINVAL;
-
-       val = spmi_reg_read(dev->parent, priv->usid,
-                           (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK);
 
-       if (val < 0)
-               return val;
-       *buff = val;
-       return 0;
+       return spmi_read(dev->parent, buff, priv->usid,
+                       (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK,
+                       len);
 }
 
 static struct dm_pmic_ops pm8916_ops = {
index 44062c46d083d014710a414411215eabb21f7bd6..41cb8c31c8c0263ddc3cc5ba489a10347463a428 100644 (file)
@@ -26,8 +26,10 @@ DECLARE_GLOBAL_DATA_PTR;
 #define SPMI_REG_CMD0                  0x0
 #define SPMI_REG_CONFIG                        0x4
 #define SPMI_REG_STATUS                        0x8
-#define SPMI_REG_WDATA                 0x10
-#define SPMI_REG_RDATA                 0x18
+#define SPMI_REG_WDATA0                        0x10
+#define SPMI_REG_WDATA1                        0x14
+#define SPMI_REG_RDATA0                        0x18
+#define SPMI_REG_RDATA1                        0x1c
 
 #define SPMI_CMD_OPCODE_SHIFT          27
 #define SPMI_CMD_SLAVE_ID_SHIFT                20
@@ -47,6 +49,7 @@ DECLARE_GLOBAL_DATA_PTR;
 #define SPMI_MAX_CHANNELS              128
 #define SPMI_MAX_SLAVES                        16
 #define SPMI_MAX_PERIPH                        256
+#define SPMI_MAX_ARB_TRANS_BYTES       8
 
 #define SPMI_READ_TIMEOUT              100
 #define SPMI_WRITE_TIMEOUT             100
@@ -67,18 +70,21 @@ struct msm_spmi_priv {
        uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH];
 };
 
-static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
-                         uint8_t val)
+static int msm_spmi_write(struct udevice *dev, const void *buf, int usid,
+                         int pid, int off, uint8_t bc)
 {
        struct msm_spmi_priv *priv = dev_get_priv(dev);
        unsigned channel;
        uint32_t reg = 0;
        int timeout = SPMI_WRITE_TIMEOUT;
+       uint32_t val;
 
        if (usid >= SPMI_MAX_SLAVES)
                return -EINVAL;
        if (pid >= SPMI_MAX_PERIPH)
                return -EINVAL;
+       if (bc > SPMI_MAX_ARB_TRANS_BYTES)
+               return -EINVAL;
 
        channel = priv->channel_map[usid][pid];
 
@@ -87,8 +93,12 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
               SPMI_REG_CONFIG);
 
        /* Write single byte */
-       writel(val, priv->spmi_chnls + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA);
-
+       memcpy(&val, buf, min_t(uint8_t, bc, sizeof(val)));
+       writel(val, priv->spmi_chnls + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA0);
+       if (bc > sizeof(val)) {
+               memcpy(&val, buf + sizeof(val), bc - sizeof(val));
+               writel(val, priv->spmi_chnls + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA1);
+       }
        /* Prepare write command */
        if (pmic_arb_is_v1()) {
                reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT;
@@ -99,7 +109,7 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
        } 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 */
+               reg |= bc - 1; /* byte count - 1 */
        }
        /* Send write command */
        writel(reg, priv->spmi_chnls + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0);
@@ -125,7 +135,8 @@ static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off,
        return 0;
 }
 
-static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
+static int msm_spmi_read(struct udevice *dev, void *buf, int usid, int pid, int off,
+                        uint8_t bc)
 {
        struct msm_spmi_priv *priv = dev_get_priv(dev);
        unsigned channel;
@@ -135,6 +146,8 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
                return -EINVAL;
        if (pid >= SPMI_MAX_PERIPH)
                return -EINVAL;
+       if (bc > SPMI_MAX_ARB_TRANS_BYTES)
+               return -EINVAL;
 
        channel = priv->channel_map[usid][pid];
 
@@ -151,7 +164,7 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
        } 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 */
+               reg |= bc - 1; /* byte count - 1 */
        }
 
        /* Request read */
@@ -170,8 +183,16 @@ static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off)
        }
 
        /* Read the data */
-       return readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
-                    SPMI_REG_RDATA) & 0xFF;
+       reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
+                    SPMI_REG_RDATA0);
+       memcpy(buf, &reg, min_t(uint8_t, bc, sizeof(reg)));
+       if (bc > sizeof(reg)) {
+               reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) +
+                       SPMI_REG_RDATA1);
+               memcpy(buf + sizeof(reg), &reg, bc - sizeof(reg));
+       }
+
+       return 0;
 }
 
 static struct dm_spmi_ops msm_spmi_ops = {
index 980aff2063a5308dc89647865caa7d0fd3dbbf4a..c20acd66e67799b7ef196d92569d4e74682d4efb 100644 (file)
@@ -55,15 +55,17 @@ static bool check_address_valid(int usid, int pid, int off)
        return true;
 }
 
-static int sandbox_spmi_write(struct udevice *dev, int usid, int pid, int off,
-                         uint8_t val)
+static int sandbox_spmi_write(struct udevice *dev, const void *buf, int usid,
+                         int pid, int off, uint8_t bc)
 {
        struct sandbox_spmi_priv *priv = dev_get_priv(dev);
        struct sandbox_emul_fake_regs *regs;
+       uint8_t val;
 
        if (!check_address_valid(usid, pid, off))
                return -EIO;
 
+       memcpy(&val, buf, 1);
        regs = priv->gpios[pid & 0x3].r; /* Last 3 bits of pid are gpio # */
 
        switch (off) {
@@ -82,7 +84,8 @@ static int sandbox_spmi_write(struct udevice *dev, int usid, int pid, int off,
        return 0;
 }
 
-static int sandbox_spmi_read(struct udevice *dev, int usid, int pid, int off)
+static int sandbox_spmi_read(struct udevice *dev, void *buf, int usid, int pid,
+                       int off, uint8_t bc)
 {
        struct sandbox_spmi_priv *priv = dev_get_priv(dev);
        struct sandbox_emul_fake_regs *regs;
@@ -99,13 +102,13 @@ static int sandbox_spmi_read(struct udevice *dev, int usid, int pid, int off)
        case 0x8: /* Status */
                if (regs[0x46].value == 0) /* Block disabled */
                        return 0;
-               return regs[off].value;
+               memcpy(buf, &regs[off].value, 1);
+               break;
        default:
                if (regs[off].perms & EMUL_PERM_R)
-                       return regs[off].value;
-               else
-                       return 0;
+                       memcpy(buf, &regs[off].value, 1);
        }
+       return 0;
 }
 
 static struct dm_spmi_ops sandbox_spmi_ops = {
index 6edece23d82bf991d0cf0d484cbd60c2b2142475..8171691013ed9d64e7963459fa148c7b0f55990e 100644 (file)
@@ -16,12 +16,15 @@ DECLARE_GLOBAL_DATA_PTR;
 
 int spmi_reg_read(struct udevice *dev, int usid, int pid, int reg)
 {
+       int ret;
        const struct dm_spmi_ops *ops = dev_get_driver_ops(dev);
+       uint8_t value;
 
        if (!ops || !ops->read)
                return -ENOSYS;
 
-       return ops->read(dev, usid, pid, reg);
+       ret = ops->read(dev, &value, usid, pid, reg, sizeof(value));
+       return ret < 0 ? ret : value;
 }
 
 int spmi_reg_write(struct udevice *dev, int usid, int pid, int reg,
@@ -32,7 +35,29 @@ int spmi_reg_write(struct udevice *dev, int usid, int pid, int reg,
        if (!ops || !ops->write)
                return -ENOSYS;
 
-       return ops->write(dev, usid, pid, reg, value);
+       return ops->write(dev, &value, usid, pid, reg, sizeof(value));
+}
+
+int spmi_read(struct udevice *dev, void *buf, int usid, int pid, int reg,
+             uint8_t bc)
+{
+       const struct dm_spmi_ops *ops = dev_get_driver_ops(dev);
+
+       if (!ops || !ops->read)
+               return -ENOSYS;
+
+       return ops->read(dev, buf, usid, pid, reg, bc);
+}
+
+int spmi_write(struct udevice *dev, const void *buf, int usid, int pid,
+              int reg, uint8_t bc)
+{
+       const struct dm_spmi_ops *ops = dev_get_driver_ops(dev);
+
+       if (!ops || !ops->write)
+               return -ENOSYS;
+
+       return ops->write(dev, buf, usid, pid, reg, bc);
 }
 
 UCLASS_DRIVER(spmi) = {
index 3242e6bbd07f8b93da694fa9f8916a2fc87cc99a..d59b2201f4d5ddfb4605734b6e3741c9105f5a13 100644 (file)
  * on error.
  */
 struct dm_spmi_ops {
-       int (*read)(struct udevice *dev, int usid, int pid, int reg);
-       int (*write)(struct udevice *dev, int usid, int pid, int reg,
-                    uint8_t value);
+       int (*read)(struct udevice *dev, void *buf, int usid, int pid, int reg,
+                   uint8_t bc);
+       int (*write)(struct udevice *dev, const void *buf, int usid, int pid,
+                    int reg, uint8_t bc);
 };
 
 /**
@@ -43,4 +44,31 @@ int spmi_reg_read(struct udevice *dev, int usid, int pid, int reg);
 int spmi_reg_write(struct udevice *dev, int usid, int pid, int reg,
                   uint8_t value);
 
+/**
+ * spmi_read() - read a range of registers from specific slave/peripheral
+ *
+ * @dev:       SPMI bus to read
+ * @usid       SlaveID
+ * @pid                Peripheral ID
+ * @reg:       Register to read
+ * @len:       Byte count to read
+ * @return value read on success or negative value of errno.
+ */
+int spmi_read(struct udevice *dev, void *buf, int usid, int pid, int reg,
+             uint8_t len);
+
+/**
+ * spmi_write() - write to a range of registers of specific slave/peripheral
+ *
+ * @dev:       SPMI bus to write
+ * @usid       SlaveID
+ * @pid                Peripheral ID
+ * @reg:       Register to write
+ * @value:     Value to write
+ * @len:       Byte count to write
+ * @return 0 on success or negative value of errno.
+ */
+int spmi_write(struct udevice *dev, const void *buf, int usid, int pid,
+              int reg, uint8_t len);
+
 #endif