Skip to content

Commit a0fb48c

Browse files
Wolfram Sangwsakernel
authored andcommitted
i2c: rcar: avoid race condition with SMIs
A customer experienced a race condition with 'repeated starts' when a System Management Interrupt took over for 30us and more. The problem was that during the SMI a new MAT interrupt came in because we set up the 'repeated start' condition. But the old one was not acknowledged yet. So, when it was acknowledged after the SMI, the new MAT interrupt was lost, confusing the state machine of the driver. The fix consists of two parts. First, we do not clear the status register for 'repeated starts' when preparing the next message anymore. The interrupt handlers for sending and receiving data is now solely responsible for that and it makes the code easier to follow, in fact. Secondly, clearing the status register is now split up to handle MAT interrupts independently. This avoids the race condition because the old MAT interrupt will be now cleared before we initiate the "repeated start" condition. Reported-by: Yoshihiro Shimoda <[email protected]> Signed-off-by: Wolfram Sang <[email protected]> Signed-off-by: Wolfram Sang <[email protected]>
1 parent 3c9fedf commit a0fb48c

File tree

1 file changed

+17
-11
lines changed

1 file changed

+17
-11
lines changed

drivers/i2c/busses/i2c-rcar.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,6 @@
9797
#define RCAR_IRQ_RECV (MNR | MAL | MST | MAT | MDR)
9898
#define RCAR_IRQ_STOP (MST)
9999

100-
#define RCAR_IRQ_ACK_SEND (~(MAT | MDE) & 0x7F)
101-
#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0x7F)
102-
103100
#define ID_LAST_MSG (1 << 0)
104101
#define ID_FIRST_MSG (1 << 1)
105102
#define ID_DONE (1 << 2)
@@ -161,6 +158,11 @@ static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg)
161158
return readl(priv->io + reg);
162159
}
163160

161+
static void rcar_i2c_clear_irq(struct rcar_i2c_priv *priv, u32 val)
162+
{
163+
writel(~val & 0x7f, priv->io + ICMSR);
164+
}
165+
164166
static int rcar_i2c_get_scl(struct i2c_adapter *adap)
165167
{
166168
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
@@ -356,7 +358,7 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
356358
priv->flags &= ~ID_P_REP_AFTER_RD;
357359
else
358360
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
359-
rcar_i2c_write(priv, ICMSR, 0);
361+
/* ICMSR is cleared in interrupt handlers */
360362
}
361363
}
362364

@@ -476,11 +478,15 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
476478
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
477479
{
478480
struct i2c_msg *msg = priv->msg;
481+
u32 irqs_to_clear = MDE;
479482

480483
/* FIXME: sometimes, unknown interrupt happened. Do nothing */
481484
if (!(msr & MDE))
482485
return;
483486

487+
if (msr & MAT)
488+
irqs_to_clear |= MAT;
489+
484490
/* Check if DMA can be enabled and take over */
485491
if (priv->pos == 1 && rcar_i2c_dma(priv))
486492
return;
@@ -504,32 +510,32 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
504510
* [ICRXTX] -> [SHIFT] -> [I2C bus]
505511
*/
506512

507-
if (priv->flags & ID_LAST_MSG) {
513+
if (priv->flags & ID_LAST_MSG)
508514
/*
509515
* If current msg is the _LAST_ msg,
510516
* prepare stop condition here.
511517
* ID_DONE will be set on STOP irq.
512518
*/
513519
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
514-
} else {
520+
else
515521
rcar_i2c_next_msg(priv);
516-
return;
517-
}
518522
}
519523

520-
rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_SEND);
524+
rcar_i2c_clear_irq(priv, irqs_to_clear);
521525
}
522526

523527
static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
524528
{
525529
struct i2c_msg *msg = priv->msg;
526530
bool recv_len_init = priv->pos == 0 && msg->flags & I2C_M_RECV_LEN;
531+
u32 irqs_to_clear = MDR;
527532

528533
/* FIXME: sometimes, unknown interrupt happened. Do nothing */
529534
if (!(msr & MDR))
530535
return;
531536

532537
if (msr & MAT) {
538+
irqs_to_clear |= MAT;
533539
/*
534540
* Address transfer phase finished, but no data at this point.
535541
* Try to use DMA to receive data.
@@ -570,8 +576,8 @@ static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
570576

571577
if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG))
572578
rcar_i2c_next_msg(priv);
573-
else
574-
rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV);
579+
580+
rcar_i2c_clear_irq(priv, irqs_to_clear);
575581
}
576582

577583
static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)

0 commit comments

Comments
 (0)