]>
Commit | Line | Data |
---|---|---|
ecf3b270 SL |
1 | From 79077ec79eb807b6409189f5c67a6fb063692d3e Mon Sep 17 00:00:00 2001 |
2 | From: Paul Kocialkowski <paul.kocialkowski@bootlin.com> | |
3 | Date: Thu, 27 Dec 2018 16:42:25 +0100 | |
4 | Subject: i2c: bcm2835: Clear current buffer pointers and counts after a | |
5 | transfer | |
6 | ||
7 | [ Upstream commit f275a4659484716259cc46268d9043424e51cf0f ] | |
8 | ||
9 | The driver's interrupt handler checks whether a message is currently | |
10 | being handled with the curr_msg pointer. When it is NULL, the interrupt | |
11 | is considered to be unexpected. Similarly, the i2c_start_transfer | |
12 | routine checks for the remaining number of messages to handle in | |
13 | num_msgs. | |
14 | ||
15 | However, these values are never cleared and always keep the message and | |
16 | number relevant to the latest transfer (which might be done already and | |
17 | the underlying message memory might have been freed). | |
18 | ||
19 | When an unexpected interrupt hits with the DONE bit set, the isr will | |
20 | then try to access the flags field of the curr_msg structure, leading | |
21 | to a fatal page fault. | |
22 | ||
23 | The msg_buf and msg_buf_remaining fields are also never cleared at the | |
24 | end of the transfer, which can lead to similar pitfalls. | |
25 | ||
26 | Fix these issues by introducing a cleanup function and always calling | |
27 | it after a transfer is finished. | |
28 | ||
29 | Fixes: e2474541032d ("i2c: bcm2835: Fix hang for writing messages larger than 16 bytes") | |
30 | Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com> | |
31 | Acked-by: Stefan Wahren <stefan.wahren@i2se.com> | |
32 | Signed-off-by: Wolfram Sang <wsa@the-dreams.de> | |
33 | Signed-off-by: Sasha Levin <sashal@kernel.org> | |
34 | --- | |
35 | drivers/i2c/busses/i2c-bcm2835.c | 12 ++++++++++++ | |
36 | 1 file changed, 12 insertions(+) | |
37 | ||
38 | diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c | |
39 | index 44deae78913e..4d19254f78c8 100644 | |
40 | --- a/drivers/i2c/busses/i2c-bcm2835.c | |
41 | +++ b/drivers/i2c/busses/i2c-bcm2835.c | |
42 | @@ -191,6 +191,15 @@ static void bcm2835_i2c_start_transfer(struct bcm2835_i2c_dev *i2c_dev) | |
43 | bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c); | |
44 | } | |
45 | ||
46 | +static void bcm2835_i2c_finish_transfer(struct bcm2835_i2c_dev *i2c_dev) | |
47 | +{ | |
48 | + i2c_dev->curr_msg = NULL; | |
49 | + i2c_dev->num_msgs = 0; | |
50 | + | |
51 | + i2c_dev->msg_buf = NULL; | |
52 | + i2c_dev->msg_buf_remaining = 0; | |
53 | +} | |
54 | + | |
55 | /* | |
56 | * Note about I2C_C_CLEAR on error: | |
57 | * The I2C_C_CLEAR on errors will take some time to resolve -- if you were in | |
58 | @@ -291,6 +300,9 @@ static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], | |
59 | ||
60 | time_left = wait_for_completion_timeout(&i2c_dev->completion, | |
61 | adap->timeout); | |
62 | + | |
63 | + bcm2835_i2c_finish_transfer(i2c_dev); | |
64 | + | |
65 | if (!time_left) { | |
66 | bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, | |
67 | BCM2835_I2C_C_CLEAR); | |
68 | -- | |
69 | 2.19.1 | |
70 |