diff options
| author | Federico Vaga <federico.vaga@cern.ch> | 2019-02-14 03:51:30 -0500 |
|---|---|---|
| committer | Wolfram Sang <wsa@the-dreams.de> | 2019-02-14 11:55:20 -0500 |
| commit | e7663ef5ae0f02e3b902eb0305dec981333eb3e1 (patch) | |
| tree | c2186d1e3285863a6e136daa472b4e58ce7b81c6 /drivers/i2c | |
| parent | 0940d24912e9256fdf172f84c54ffd91680f05d0 (diff) | |
i2c: ocores: stop transfer on timeout
Detecting a timeout is ok, but we also need to assert a STOP command on
the bus in order to prevent it from generating interrupts when there are
no on going transfers.
Example: very long transmission.
1. ocores_xfer: START a transfer
2. ocores_isr : handle byte by byte the transfer
3. ocores_xfer: goes in timeout [[bugfix here]]
4. ocores_xfer: return to I2C subsystem and to the I2C driver
5. I2C driver : it may clean up the i2c_msg memory
6. ocores_isr : receives another interrupt (pending bytes to be
transferred) but the i2c_msg memory is invalid now
So, since the transfer was too long, we have to detect the timeout and
STOP the transfer.
Another point is that we have a critical region here. When handling the
timeout condition we may have a running IRQ handler. For this reason I
introduce a spinlock.
In order to make easier to understan locking I have:
- added a new function to handle timeout
- modified the current ocores_process() function in order to be protected
by the new spinlock
Like this it is obvious at first sight that this locking serializes
the execution of ocores_process() and ocores_process_timeout()
Signed-off-by: Federico Vaga <federico.vaga@cern.ch>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Diffstat (limited to 'drivers/i2c')
| -rw-r--r-- | drivers/i2c/busses/i2c-ocores.c | 54 |
1 files changed, 45 insertions, 9 deletions
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 87f9caacba85..aa852028d8c1 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c | |||
| @@ -25,7 +25,12 @@ | |||
| 25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
| 26 | #include <linux/io.h> | 26 | #include <linux/io.h> |
| 27 | #include <linux/log2.h> | 27 | #include <linux/log2.h> |
| 28 | #include <linux/spinlock.h> | ||
| 28 | 29 | ||
| 30 | /** | ||
| 31 | * @process_lock: protect I2C transfer process. | ||
| 32 | * ocores_process() and ocores_process_timeout() can't run in parallel. | ||
| 33 | */ | ||
| 29 | struct ocores_i2c { | 34 | struct ocores_i2c { |
| 30 | void __iomem *base; | 35 | void __iomem *base; |
| 31 | u32 reg_shift; | 36 | u32 reg_shift; |
| @@ -36,6 +41,7 @@ struct ocores_i2c { | |||
| 36 | int pos; | 41 | int pos; |
| 37 | int nmsgs; | 42 | int nmsgs; |
| 38 | int state; /* see STATE_ */ | 43 | int state; /* see STATE_ */ |
| 44 | spinlock_t process_lock; | ||
| 39 | struct clk *clk; | 45 | struct clk *clk; |
| 40 | int ip_clock_khz; | 46 | int ip_clock_khz; |
| 41 | int bus_clock_khz; | 47 | int bus_clock_khz; |
| @@ -141,19 +147,26 @@ static void ocores_process(struct ocores_i2c *i2c) | |||
| 141 | { | 147 | { |
| 142 | struct i2c_msg *msg = i2c->msg; | 148 | struct i2c_msg *msg = i2c->msg; |
| 143 | u8 stat = oc_getreg(i2c, OCI2C_STATUS); | 149 | u8 stat = oc_getreg(i2c, OCI2C_STATUS); |
| 150 | unsigned long flags; | ||
| 151 | |||
| 152 | /* | ||
| 153 | * If we spin here is because we are in timeout, so we are going | ||
| 154 | * to be in STATE_ERROR. See ocores_process_timeout() | ||
| 155 | */ | ||
| 156 | spin_lock_irqsave(&i2c->process_lock, flags); | ||
| 144 | 157 | ||
| 145 | if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { | 158 | if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { |
| 146 | /* stop has been sent */ | 159 | /* stop has been sent */ |
| 147 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); | 160 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); |
| 148 | wake_up(&i2c->wait); | 161 | wake_up(&i2c->wait); |
| 149 | return; | 162 | goto out; |
| 150 | } | 163 | } |
| 151 | 164 | ||
| 152 | /* error? */ | 165 | /* error? */ |
| 153 | if (stat & OCI2C_STAT_ARBLOST) { | 166 | if (stat & OCI2C_STAT_ARBLOST) { |
| 154 | i2c->state = STATE_ERROR; | 167 | i2c->state = STATE_ERROR; |
| 155 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); | 168 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); |
| 156 | return; | 169 | goto out; |
| 157 | } | 170 | } |
| 158 | 171 | ||
| 159 | if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { | 172 | if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { |
| @@ -163,7 +176,7 @@ static void ocores_process(struct ocores_i2c *i2c) | |||
| 163 | if (stat & OCI2C_STAT_NACK) { | 176 | if (stat & OCI2C_STAT_NACK) { |
| 164 | i2c->state = STATE_ERROR; | 177 | i2c->state = STATE_ERROR; |
| 165 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); | 178 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); |
| 166 | return; | 179 | goto out; |
| 167 | } | 180 | } |
| 168 | } else | 181 | } else |
| 169 | msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); | 182 | msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); |
| @@ -184,14 +197,14 @@ static void ocores_process(struct ocores_i2c *i2c) | |||
| 184 | 197 | ||
| 185 | oc_setreg(i2c, OCI2C_DATA, addr); | 198 | oc_setreg(i2c, OCI2C_DATA, addr); |
| 186 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); | 199 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); |
| 187 | return; | 200 | goto out; |
| 188 | } else | 201 | } else |
| 189 | i2c->state = (msg->flags & I2C_M_RD) | 202 | i2c->state = (msg->flags & I2C_M_RD) |
| 190 | ? STATE_READ : STATE_WRITE; | 203 | ? STATE_READ : STATE_WRITE; |
| 191 | } else { | 204 | } else { |
| 192 | i2c->state = STATE_DONE; | 205 | i2c->state = STATE_DONE; |
| 193 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); | 206 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); |
| 194 | return; | 207 | goto out; |
| 195 | } | 208 | } |
| 196 | } | 209 | } |
| 197 | 210 | ||
| @@ -202,6 +215,9 @@ static void ocores_process(struct ocores_i2c *i2c) | |||
| 202 | oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); | 215 | oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); |
| 203 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); | 216 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); |
| 204 | } | 217 | } |
| 218 | |||
| 219 | out: | ||
| 220 | spin_unlock_irqrestore(&i2c->process_lock, flags); | ||
| 205 | } | 221 | } |
| 206 | 222 | ||
| 207 | static irqreturn_t ocores_isr(int irq, void *dev_id) | 223 | static irqreturn_t ocores_isr(int irq, void *dev_id) |
| @@ -213,9 +229,24 @@ static irqreturn_t ocores_isr(int irq, void *dev_id) | |||
| 213 | return IRQ_HANDLED; | 229 | return IRQ_HANDLED; |
| 214 | } | 230 | } |
| 215 | 231 | ||
| 232 | /** | ||
| 233 | * Process timeout event | ||
| 234 | * @i2c: ocores I2C device instance | ||
| 235 | */ | ||
| 236 | static void ocores_process_timeout(struct ocores_i2c *i2c) | ||
| 237 | { | ||
| 238 | unsigned long flags; | ||
| 239 | |||
| 240 | spin_lock_irqsave(&i2c->process_lock, flags); | ||
| 241 | i2c->state = STATE_ERROR; | ||
| 242 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); | ||
| 243 | spin_unlock_irqrestore(&i2c->process_lock, flags); | ||
| 244 | } | ||
| 245 | |||
| 216 | static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | 246 | static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) |
| 217 | { | 247 | { |
| 218 | struct ocores_i2c *i2c = i2c_get_adapdata(adap); | 248 | struct ocores_i2c *i2c = i2c_get_adapdata(adap); |
| 249 | int ret; | ||
| 219 | 250 | ||
| 220 | i2c->msg = msgs; | 251 | i2c->msg = msgs; |
| 221 | i2c->pos = 0; | 252 | i2c->pos = 0; |
| @@ -225,11 +256,14 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | |||
| 225 | oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); | 256 | oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg)); |
| 226 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); | 257 | oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); |
| 227 | 258 | ||
| 228 | if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || | 259 | ret = wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || |
| 229 | (i2c->state == STATE_DONE), HZ)) | 260 | (i2c->state == STATE_DONE), HZ); |
| 230 | return (i2c->state == STATE_DONE) ? num : -EIO; | 261 | if (ret == 0) { |
| 231 | else | 262 | ocores_process_timeout(i2c); |
| 232 | return -ETIMEDOUT; | 263 | return -ETIMEDOUT; |
| 264 | } | ||
| 265 | |||
| 266 | return (i2c->state == STATE_DONE) ? num : -EIO; | ||
| 233 | } | 267 | } |
| 234 | 268 | ||
| 235 | static int ocores_init(struct device *dev, struct ocores_i2c *i2c) | 269 | static int ocores_init(struct device *dev, struct ocores_i2c *i2c) |
| @@ -422,6 +456,8 @@ static int ocores_i2c_probe(struct platform_device *pdev) | |||
| 422 | if (!i2c) | 456 | if (!i2c) |
| 423 | return -ENOMEM; | 457 | return -ENOMEM; |
| 424 | 458 | ||
| 459 | spin_lock_init(&i2c->process_lock); | ||
| 460 | |||
| 425 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 461 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 426 | i2c->base = devm_ioremap_resource(&pdev->dev, res); | 462 | i2c->base = devm_ioremap_resource(&pdev->dev, res); |
| 427 | if (IS_ERR(i2c->base)) | 463 | if (IS_ERR(i2c->base)) |
