diff options
author | Måns Rullgård <mans@mansr.com> | 2015-12-15 18:15:05 -0500 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2015-12-16 05:42:30 -0500 |
commit | a45af72a609b095820feab26c49286337301377e (patch) | |
tree | c347d0cd0b11d7f4b0872d02aefea64d5786c1bd | |
parent | 75d31c2372e4a08319919b14bd160c48305373a1 (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.c | 50 |
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 | |
105 | retry: | ||
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 | ||
119 | retry: | ||
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, | |||
228 | static u32 xlr_func(struct i2c_adapter *adap) | 232 | static 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 | ||
234 | static struct i2c_algorithm xlr_i2c_algo = { | 238 | static struct i2c_algorithm xlr_i2c_algo = { |