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);
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;
}
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);
{
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 = {
#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
#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
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];
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;
} 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);
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;
return -EINVAL;
if (pid >= SPMI_MAX_PERIPH)
return -EINVAL;
+ if (bc > SPMI_MAX_ARB_TRANS_BYTES)
+ return -EINVAL;
channel = priv->channel_map[usid][pid];
} 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 */
}
/* 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, ®, 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), ®, bc - sizeof(reg));
+ }
+
+ return 0;
}
static struct dm_spmi_ops msm_spmi_ops = {
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) {
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;
case 0x8: /* Status */
if (regs[0x46].value == 0) /* Block disabled */
return 0;
- return regs[off].value;
+ memcpy(buf, ®s[off].value, 1);
+ break;
default:
if (regs[off].perms & EMUL_PERM_R)
- return regs[off].value;
- else
- return 0;
+ memcpy(buf, ®s[off].value, 1);
}
+ return 0;
}
static struct dm_spmi_ops sandbox_spmi_ops = {
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,
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) = {
* 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);
};
/**
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