diff options
author | Noralf Trønnes <noralf@tronnes.org> | 2016-10-03 16:06:08 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-04-18 01:11:47 -0400 |
commit | 938f8e856064c0a7890ab998ec66abf1d093fd18 (patch) | |
tree | 137ecd6501a24594e65999487f1825d520cb834a | |
parent | f7513c9165bfe9671d83b940780602980e4ac913 (diff) |
i2c: bcm2835: Fix hang for writing messages larger than 16 bytes
commit e2474541032db65d02bf88b6a8c2f954654b443f upstream.
Writing messages larger than the FIFO size results in a hang, rendering
the machine unusable. This is because the RXD status flag is set on the
first interrupt which results in bcm2835_drain_rxfifo() stealing bytes
from the buffer. The controller continues to trigger interrupts waiting
for the missing bytes, but bcm2835_fill_txfifo() has none to give.
In this situation wait_for_completion_timeout() apparently is unable to
stop the madness.
The BCM2835 ARM Peripherals datasheet has this to say about the flags:
TXD: is set when the FIFO has space for at least one byte of data.
RXD: is set when the FIFO contains at least one byte of data.
TXW: is set during a write transfer and the FIFO is less than full.
RXR: is set during a read transfer and the FIFO is or more full.
Implementing the logic from the downstream i2c-bcm2708 driver solved
the hang problem.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Martin Sperl <kernel@martin.sperl.org>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/i2c/busses/i2c-bcm2835.c | 22 |
1 files changed, 14 insertions, 8 deletions
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index d4f3239b5686..f283b714aa79 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c | |||
@@ -64,6 +64,7 @@ struct bcm2835_i2c_dev { | |||
64 | int irq; | 64 | int irq; |
65 | struct i2c_adapter adapter; | 65 | struct i2c_adapter adapter; |
66 | struct completion completion; | 66 | struct completion completion; |
67 | struct i2c_msg *curr_msg; | ||
67 | u32 msg_err; | 68 | u32 msg_err; |
68 | u8 *msg_buf; | 69 | u8 *msg_buf; |
69 | size_t msg_buf_remaining; | 70 | size_t msg_buf_remaining; |
@@ -126,14 +127,13 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) | |||
126 | return IRQ_HANDLED; | 127 | return IRQ_HANDLED; |
127 | } | 128 | } |
128 | 129 | ||
129 | if (val & BCM2835_I2C_S_RXD) { | ||
130 | bcm2835_drain_rxfifo(i2c_dev); | ||
131 | if (!(val & BCM2835_I2C_S_DONE)) | ||
132 | return IRQ_HANDLED; | ||
133 | } | ||
134 | |||
135 | if (val & BCM2835_I2C_S_DONE) { | 130 | if (val & BCM2835_I2C_S_DONE) { |
136 | if (i2c_dev->msg_buf_remaining) | 131 | if (i2c_dev->curr_msg->flags & I2C_M_RD) { |
132 | bcm2835_drain_rxfifo(i2c_dev); | ||
133 | val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); | ||
134 | } | ||
135 | |||
136 | if ((val & BCM2835_I2C_S_RXD) || i2c_dev->msg_buf_remaining) | ||
137 | i2c_dev->msg_err = BCM2835_I2C_S_LEN; | 137 | i2c_dev->msg_err = BCM2835_I2C_S_LEN; |
138 | else | 138 | else |
139 | i2c_dev->msg_err = 0; | 139 | i2c_dev->msg_err = 0; |
@@ -141,11 +141,16 @@ static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) | |||
141 | return IRQ_HANDLED; | 141 | return IRQ_HANDLED; |
142 | } | 142 | } |
143 | 143 | ||
144 | if (val & BCM2835_I2C_S_TXD) { | 144 | if (val & BCM2835_I2C_S_TXW) { |
145 | bcm2835_fill_txfifo(i2c_dev); | 145 | bcm2835_fill_txfifo(i2c_dev); |
146 | return IRQ_HANDLED; | 146 | return IRQ_HANDLED; |
147 | } | 147 | } |
148 | 148 | ||
149 | if (val & BCM2835_I2C_S_RXR) { | ||
150 | bcm2835_drain_rxfifo(i2c_dev); | ||
151 | return IRQ_HANDLED; | ||
152 | } | ||
153 | |||
149 | return IRQ_NONE; | 154 | return IRQ_NONE; |
150 | } | 155 | } |
151 | 156 | ||
@@ -155,6 +160,7 @@ static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev, | |||
155 | u32 c; | 160 | u32 c; |
156 | unsigned long time_left; | 161 | unsigned long time_left; |
157 | 162 | ||
163 | i2c_dev->curr_msg = msg; | ||
158 | i2c_dev->msg_buf = msg->buf; | 164 | i2c_dev->msg_buf = msg->buf; |
159 | i2c_dev->msg_buf_remaining = msg->len; | 165 | i2c_dev->msg_buf_remaining = msg->len; |
160 | reinit_completion(&i2c_dev->completion); | 166 | reinit_completion(&i2c_dev->completion); |