aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMåns Rullgård <mans@mansr.com>2015-12-15 18:15:05 -0500
committerWolfram Sang <wsa@the-dreams.de>2015-12-16 05:42:30 -0500
commita45af72a609b095820feab26c49286337301377e (patch)
treec347d0cd0b11d7f4b0872d02aefea64d5786c1bd
parent75d31c2372e4a08319919b14bd160c48305373a1 (diff)
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: Mans Rullgard <mans@mansr.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--drivers/i2c/busses/i2c-xlr.c50
1 files changed, 27 insertions, 23 deletions
diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index 10fb916fa8c7..2cfaba7c1f92 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -89,38 +89,43 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
89 unsigned long timeout, stoptime, checktime; 89 unsigned long timeout, stoptime, checktime;
90 u32 i2c_status; 90 u32 i2c_status;
91 int pos, timedout; 91 int pos, timedout;
92 u8 offset, byte; 92 u8 offset;
93 u32 xfer;
94
95 if (!len)
96 return -EOPNOTSUPP;
93 97
94 offset = buf[0]; 98 offset = buf[0];
95 xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset); 99 xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
96 xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr); 100 xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
97 xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, 101 xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
98 XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra); 102 XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
99 xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
100 103
101 timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT); 104 timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
102 stoptime = jiffies + timeout; 105 stoptime = jiffies + timeout;
103 timedout = 0; 106 timedout = 0;
104 pos = 1; 107
105retry:
106 if (len == 1) { 108 if (len == 1) {
107 xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, 109 xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
108 XLR_I2C_STARTXFR_ND); 110 xfer = XLR_I2C_STARTXFR_ND;
111 pos = 1;
109 } else { 112 } else {
110 xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]); 113 xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 2);
111 xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, 114 xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[1]);
112 XLR_I2C_STARTXFR_WR); 115 xfer = XLR_I2C_STARTXFR_WR;
116 pos = 2;
113 } 117 }
114 118
119retry:
120 /* retry can only happen on the first byte */
121 xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
122
115 while (!timedout) { 123 while (!timedout) {
116 checktime = jiffies; 124 checktime = jiffies;
117 i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS); 125 i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
118 126
119 if (i2c_status & XLR_I2C_SDOEMPTY) { 127 if ((i2c_status & XLR_I2C_SDOEMPTY) && pos < len) {
120 pos++; 128 xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos++]);
121 /* need to do a empty dataout after the last byte */
122 byte = (pos < len) ? buf[pos] : 0;
123 xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
124 129
125 /* reset timeout on successful xmit */ 130 /* reset timeout on successful xmit */
126 stoptime = jiffies + timeout; 131 stoptime = jiffies + timeout;
@@ -149,11 +154,13 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
149 u32 i2c_status; 154 u32 i2c_status;
150 unsigned long timeout, stoptime, checktime; 155 unsigned long timeout, stoptime, checktime;
151 int nbytes, timedout; 156 int nbytes, timedout;
152 u8 byte; 157
158 if (!len)
159 return -EOPNOTSUPP;
153 160
154 xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, 161 xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
155 XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra); 162 XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
156 xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len); 163 xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
157 xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr); 164 xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
158 165
159 timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT); 166 timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
@@ -167,14 +174,11 @@ retry:
167 checktime = jiffies; 174 checktime = jiffies;
168 i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS); 175 i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
169 if (i2c_status & XLR_I2C_RXRDY) { 176 if (i2c_status & XLR_I2C_RXRDY) {
170 if (nbytes > len) 177 if (nbytes >= len)
171 return -EIO; /* should not happen */ 178 return -EIO; /* should not happen */
172 179
173 /* we need to do a dummy datain when nbytes == len */ 180 buf[nbytes++] =
174 byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN); 181 xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
175 if (nbytes < len)
176 buf[nbytes] = byte;
177 nbytes++;
178 182
179 /* reset timeout on successful read */ 183 /* reset timeout on successful read */
180 stoptime = jiffies + timeout; 184 stoptime = jiffies + timeout;
@@ -228,7 +232,7 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
228static u32 xlr_func(struct i2c_adapter *adap) 232static u32 xlr_func(struct i2c_adapter *adap)
229{ 233{
230 /* Emulate SMBUS over I2C */ 234 /* Emulate SMBUS over I2C */
231 return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; 235 return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C;
232} 236}
233 237
234static struct i2c_algorithm xlr_i2c_algo = { 238static struct i2c_algorithm xlr_i2c_algo = {