Skip to content

Commit 2e829cc

Browse files
Hans HuAndi Shyti
authored andcommitted
i2c: wmt: fix a bug when thread blocked
During each byte access, the host performs clock stretching. To reduce the host performs clock stretching, move most of the per-msg processing to the interrupt context. Suggested-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Signed-off-by: Hans Hu <hanshu-oc@zhaoxin.com> Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
1 parent 013fa16 commit 2e829cc

2 files changed

Lines changed: 80 additions & 71 deletions

File tree

drivers/i2c/busses/i2c-viai2c-common.c

Lines changed: 76 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,18 @@ int viai2c_wait_bus_not_busy(struct viai2c *i2c)
1818
return 0;
1919
}
2020

21-
int viai2c_check_status(struct viai2c *i2c)
22-
{
23-
int ret = 0;
24-
unsigned long time_left;
25-
26-
time_left = wait_for_completion_timeout(&i2c->complete,
27-
msecs_to_jiffies(500));
28-
if (!time_left)
29-
return -ETIMEDOUT;
30-
31-
if (i2c->cmd_status & VIAI2C_ISR_NACK_ADDR)
32-
ret = -EIO;
33-
34-
if (i2c->cmd_status & VIAI2C_ISR_SCL_TIMEOUT)
35-
ret = -ETIMEDOUT;
36-
37-
return ret;
38-
}
39-
4021
static int viai2c_write(struct viai2c *i2c, struct i2c_msg *pmsg, int last)
4122
{
4223
u16 val, tcr_val = i2c->tcr;
43-
int ret;
44-
int xfer_len = 0;
24+
25+
i2c->last = last;
4526

4627
if (pmsg->len == 0) {
4728
/*
4829
* We still need to run through the while (..) once, so
4930
* start at -1 and break out early from the loop
5031
*/
51-
xfer_len = -1;
32+
i2c->xfered_len = -1;
5233
writew(0, i2c->base + VIAI2C_REG_CDR);
5334
} else {
5435
writew(pmsg->buf[0] & 0xFF, i2c->base + VIAI2C_REG_CDR);
@@ -73,42 +54,15 @@ static int viai2c_write(struct viai2c *i2c, struct i2c_msg *pmsg, int last)
7354
writew(val, i2c->base + VIAI2C_REG_CR);
7455
}
7556

76-
while (xfer_len < pmsg->len) {
77-
ret = viai2c_check_status(i2c);
78-
if (ret)
79-
return ret;
80-
81-
xfer_len++;
82-
83-
val = readw(i2c->base + VIAI2C_REG_CSR);
84-
if (val & VIAI2C_CSR_RCV_NOT_ACK) {
85-
dev_dbg(i2c->dev, "write RCV NACK error\n");
86-
return -EIO;
87-
}
88-
89-
if (pmsg->len == 0) {
90-
val = VIAI2C_CR_TX_END | VIAI2C_CR_CPU_RDY | VIAI2C_CR_ENABLE;
91-
writew(val, i2c->base + VIAI2C_REG_CR);
92-
break;
93-
}
94-
95-
if (xfer_len == pmsg->len) {
96-
if (last != 1)
97-
writew(VIAI2C_CR_ENABLE, i2c->base + VIAI2C_REG_CR);
98-
} else {
99-
writew(pmsg->buf[xfer_len] & 0xFF, i2c->base + VIAI2C_REG_CDR);
100-
writew(VIAI2C_CR_CPU_RDY | VIAI2C_CR_ENABLE, i2c->base + VIAI2C_REG_CR);
101-
}
102-
}
57+
if (!wait_for_completion_timeout(&i2c->complete, VIAI2C_TIMEOUT))
58+
return -ETIMEDOUT;
10359

104-
return 0;
60+
return i2c->ret;
10561
}
10662

10763
static int viai2c_read(struct viai2c *i2c, struct i2c_msg *pmsg)
10864
{
10965
u16 val, tcr_val = i2c->tcr;
110-
int ret;
111-
u32 xfer_len = 0;
11266

11367
val = readw(i2c->base + VIAI2C_REG_CR);
11468
val &= ~(VIAI2C_CR_TX_END | VIAI2C_CR_RX_END);
@@ -133,21 +87,10 @@ static int viai2c_read(struct viai2c *i2c, struct i2c_msg *pmsg)
13387
writew(val, i2c->base + VIAI2C_REG_CR);
13488
}
13589

136-
while (xfer_len < pmsg->len) {
137-
ret = viai2c_check_status(i2c);
138-
if (ret)
139-
return ret;
140-
141-
pmsg->buf[xfer_len] = readw(i2c->base + VIAI2C_REG_CDR) >> 8;
142-
xfer_len++;
143-
144-
val = readw(i2c->base + VIAI2C_REG_CR) | VIAI2C_CR_CPU_RDY;
145-
if (xfer_len == pmsg->len - 1)
146-
val |= VIAI2C_CR_RX_END;
147-
writew(val, i2c->base + VIAI2C_REG_CR);
148-
}
90+
if (!wait_for_completion_timeout(&i2c->complete, VIAI2C_TIMEOUT))
91+
return -ETIMEDOUT;
14992

150-
return 0;
93+
return i2c->ret;
15194
}
15295

15396
int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
@@ -165,6 +108,9 @@ int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
165108
return ret;
166109
}
167110

111+
i2c->msg = pmsg;
112+
i2c->xfered_len = 0;
113+
168114
if (pmsg->flags & I2C_M_RD)
169115
ret = viai2c_read(i2c, pmsg);
170116
else
@@ -174,15 +120,76 @@ int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
174120
return (ret < 0) ? ret : i;
175121
}
176122

123+
/*
124+
* Main process of the byte mode xfer
125+
*
126+
* Return value indicates whether the transfer is complete
127+
* 1: all the data has been successfully transferred
128+
* 0: there is still data that needs to be transferred
129+
* -EIO: error occurred
130+
*/
131+
static int viai2c_irq_xfer(struct viai2c *i2c)
132+
{
133+
u16 val;
134+
struct i2c_msg *msg = i2c->msg;
135+
u8 read = msg->flags & I2C_M_RD;
136+
void __iomem *base = i2c->base;
137+
138+
if (read) {
139+
msg->buf[i2c->xfered_len] = readw(base + VIAI2C_REG_CDR) >> 8;
140+
141+
val = readw(base + VIAI2C_REG_CR) | VIAI2C_CR_CPU_RDY;
142+
if (i2c->xfered_len == msg->len - 2)
143+
val |= VIAI2C_CR_RX_END;
144+
writew(val, base + VIAI2C_REG_CR);
145+
} else {
146+
val = readw(base + VIAI2C_REG_CSR);
147+
if (val & VIAI2C_CSR_RCV_NOT_ACK)
148+
return -EIO;
149+
150+
/* I2C_SMBUS_QUICK */
151+
if (msg->len == 0) {
152+
val = VIAI2C_CR_TX_END | VIAI2C_CR_CPU_RDY | VIAI2C_CR_ENABLE;
153+
writew(val, base + VIAI2C_REG_CR);
154+
return 1;
155+
}
156+
157+
if ((i2c->xfered_len + 1) == msg->len) {
158+
if (!i2c->last)
159+
writew(VIAI2C_CR_ENABLE, base + VIAI2C_REG_CR);
160+
} else {
161+
writew(msg->buf[i2c->xfered_len + 1] & 0xFF, base + VIAI2C_REG_CDR);
162+
writew(VIAI2C_CR_CPU_RDY | VIAI2C_CR_ENABLE, base + VIAI2C_REG_CR);
163+
}
164+
}
165+
166+
i2c->xfered_len++;
167+
168+
return i2c->xfered_len == msg->len;
169+
}
170+
177171
static irqreturn_t viai2c_isr(int irq, void *data)
178172
{
179173
struct viai2c *i2c = data;
174+
u8 status;
180175

181176
/* save the status and write-clear it */
182-
i2c->cmd_status = readw(i2c->base + VIAI2C_REG_ISR);
183-
writew(i2c->cmd_status, i2c->base + VIAI2C_REG_ISR);
177+
status = readw(i2c->base + VIAI2C_REG_ISR);
178+
writew(status, i2c->base + VIAI2C_REG_ISR);
179+
180+
i2c->ret = 0;
181+
if (status & VIAI2C_ISR_NACK_ADDR)
182+
i2c->ret = -EIO;
183+
184+
if (status & VIAI2C_ISR_SCL_TIMEOUT)
185+
i2c->ret = -ETIMEDOUT;
186+
187+
if (!i2c->ret)
188+
i2c->ret = viai2c_irq_xfer(i2c);
184189

185-
complete(&i2c->complete);
190+
/* All the data has been successfully transferred or error occurred */
191+
if (i2c->ret)
192+
complete(&i2c->complete);
186193

187194
return IRQ_HANDLED;
188195
}

drivers/i2c/busses/i2c-viai2c-common.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ struct viai2c {
5858
struct clk *clk;
5959
u16 tcr;
6060
int irq;
61-
u16 cmd_status;
61+
u16 xfered_len;
62+
struct i2c_msg *msg;
63+
int ret;
64+
bool last;
6265
};
6366

6467
int viai2c_wait_bus_not_busy(struct viai2c *i2c);
65-
int viai2c_check_status(struct viai2c *i2c);
6668
int viai2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num);
6769
int viai2c_init(struct platform_device *pdev, struct viai2c **pi2c);
6870

0 commit comments

Comments
 (0)