Commit a45af72a authored by Måns Rullgård's avatar Måns Rullgård Committed by Wolfram Sang

i2c: xlr: fix extra read/write at end of rx transfer

The BYTECNT register holds the transfer size minus one.  Setting it to
the correct value removes the need for a dummy read/write at the end of
each transfer.  As zero-length transfers are not supported, do not
advertise I2C_FUNC_SMBUS_QUICK.

In other words, this patch makes the driver transfer the number of bytes
requested unless this is zero, which is not supported by the hardware
and is thus refused.
Signed-off-by: default avatarMans Rullgard <mans@mansr.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 75d31c23
...@@ -89,38 +89,43 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len, ...@@ -89,38 +89,43 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
unsigned long timeout, stoptime, checktime; unsigned long timeout, stoptime, checktime;
u32 i2c_status; u32 i2c_status;
int pos, timedout; int pos, timedout;
u8 offset, byte; u8 offset;
u32 xfer;
if (!len)
return -EOPNOTSUPP;
offset = buf[0]; offset = buf[0];
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset); xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr); xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra); XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT); timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout; stoptime = jiffies + timeout;
timedout = 0; timedout = 0;
pos = 1;
retry:
if (len == 1) { if (len == 1) {
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
XLR_I2C_STARTXFR_ND); xfer = XLR_I2C_STARTXFR_ND;
pos = 1;
} else { } else {
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]); xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 2);
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[1]);
XLR_I2C_STARTXFR_WR); xfer = XLR_I2C_STARTXFR_WR;
pos = 2;
} }
retry:
/* retry can only happen on the first byte */
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
while (!timedout) { while (!timedout) {
checktime = jiffies; checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS); i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
if (i2c_status & XLR_I2C_SDOEMPTY) { if ((i2c_status & XLR_I2C_SDOEMPTY) && pos < len) {
pos++; xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos++]);
/* need to do a empty dataout after the last byte */
byte = (pos < len) ? buf[pos] : 0;
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
/* reset timeout on successful xmit */ /* reset timeout on successful xmit */
stoptime = jiffies + timeout; stoptime = jiffies + timeout;
...@@ -149,11 +154,13 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr) ...@@ -149,11 +154,13 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
u32 i2c_status; u32 i2c_status;
unsigned long timeout, stoptime, checktime; unsigned long timeout, stoptime, checktime;
int nbytes, timedout; int nbytes, timedout;
u8 byte;
if (!len)
return -EOPNOTSUPP;
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra); XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len); xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr); xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT); timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
...@@ -167,14 +174,11 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr) ...@@ -167,14 +174,11 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
checktime = jiffies; checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS); i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
if (i2c_status & XLR_I2C_RXRDY) { if (i2c_status & XLR_I2C_RXRDY) {
if (nbytes > len) if (nbytes >= len)
return -EIO; /* should not happen */ return -EIO; /* should not happen */
/* we need to do a dummy datain when nbytes == len */ buf[nbytes++] =
byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN); xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
if (nbytes < len)
buf[nbytes] = byte;
nbytes++;
/* reset timeout on successful read */ /* reset timeout on successful read */
stoptime = jiffies + timeout; stoptime = jiffies + timeout;
...@@ -228,7 +232,7 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap, ...@@ -228,7 +232,7 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
static u32 xlr_func(struct i2c_adapter *adap) static u32 xlr_func(struct i2c_adapter *adap)
{ {
/* Emulate SMBUS over I2C */ /* Emulate SMBUS over I2C */
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C;
} }
static struct i2c_algorithm xlr_i2c_algo = { static struct i2c_algorithm xlr_i2c_algo = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment