Skip to content

Commit a49cc1f

Browse files
Ulrich Hechtwsakernel
authored andcommitted
i2c: sh_mobile: implement atomic transfers
Implements atomic transfers to fix reboot/shutdown on r8a7790 Lager and similar boards. Signed-off-by: Ulrich Hecht <uli+renesas@fpond.eu> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> [wsa: some whitespace fixing] Signed-off-by: Wolfram Sang <wsa@kernel.org>
1 parent aafced6 commit a49cc1f

1 file changed

Lines changed: 66 additions & 20 deletions

File tree

drivers/i2c/busses/i2c-sh_mobile.c

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ struct sh_mobile_i2c_data {
129129
int sr;
130130
bool send_stop;
131131
bool stop_after_dma;
132+
bool atomic_xfer;
132133

133134
struct resource *res;
134135
struct dma_chan *dma_tx;
@@ -330,13 +331,15 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op
330331
ret = iic_rd(pd, ICDR);
331332
break;
332333
case OP_RX_STOP: /* enable DTE interrupt, issue stop */
333-
iic_wr(pd, ICIC,
334-
ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
334+
if (!pd->atomic_xfer)
335+
iic_wr(pd, ICIC,
336+
ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
335337
iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
336338
break;
337339
case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
338-
iic_wr(pd, ICIC,
339-
ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
340+
if (!pd->atomic_xfer)
341+
iic_wr(pd, ICIC,
342+
ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
340343
ret = iic_rd(pd, ICDR);
341344
iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
342345
break;
@@ -429,7 +432,8 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
429432

430433
if (wakeup) {
431434
pd->sr |= SW_DONE;
432-
wake_up(&pd->wait);
435+
if (!pd->atomic_xfer)
436+
wake_up(&pd->wait);
433437
}
434438

435439
/* defeat write posting to avoid spurious WAIT interrupts */
@@ -581,6 +585,9 @@ static void start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
581585
pd->pos = -1;
582586
pd->sr = 0;
583587

588+
if (pd->atomic_xfer)
589+
return;
590+
584591
pd->dma_buf = i2c_get_dma_safe_msg_buf(pd->msg, 8);
585592
if (pd->dma_buf)
586593
sh_mobile_i2c_xfer_dma(pd);
@@ -637,15 +644,13 @@ static int poll_busy(struct sh_mobile_i2c_data *pd)
637644
return i ? 0 : -ETIMEDOUT;
638645
}
639646

640-
static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
641-
struct i2c_msg *msgs,
642-
int num)
647+
static int sh_mobile_xfer(struct sh_mobile_i2c_data *pd,
648+
struct i2c_msg *msgs, int num)
643649
{
644-
struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
645650
struct i2c_msg *msg;
646651
int err = 0;
647652
int i;
648-
long timeout;
653+
long time_left;
649654

650655
/* Wake up device and enable clock */
651656
pm_runtime_get_sync(pd->dev);
@@ -662,15 +667,35 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
662667
if (do_start)
663668
i2c_op(pd, OP_START);
664669

665-
/* The interrupt handler takes care of the rest... */
666-
timeout = wait_event_timeout(pd->wait,
667-
pd->sr & (ICSR_TACK | SW_DONE),
668-
adapter->timeout);
669-
670-
/* 'stop_after_dma' tells if DMA transfer was complete */
671-
i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg, pd->stop_after_dma);
670+
if (pd->atomic_xfer) {
671+
unsigned long j = jiffies + pd->adap.timeout;
672+
673+
time_left = time_before_eq(jiffies, j);
674+
while (time_left &&
675+
!(pd->sr & (ICSR_TACK | SW_DONE))) {
676+
unsigned char sr = iic_rd(pd, ICSR);
677+
678+
if (sr & (ICSR_AL | ICSR_TACK |
679+
ICSR_WAIT | ICSR_DTE)) {
680+
sh_mobile_i2c_isr(0, pd);
681+
udelay(150);
682+
} else {
683+
cpu_relax();
684+
}
685+
time_left = time_before_eq(jiffies, j);
686+
}
687+
} else {
688+
/* The interrupt handler takes care of the rest... */
689+
time_left = wait_event_timeout(pd->wait,
690+
pd->sr & (ICSR_TACK | SW_DONE),
691+
pd->adap.timeout);
692+
693+
/* 'stop_after_dma' tells if DMA xfer was complete */
694+
i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg,
695+
pd->stop_after_dma);
696+
}
672697

673-
if (!timeout) {
698+
if (!time_left) {
674699
dev_err(pd->dev, "Transfer request timed out\n");
675700
if (pd->dma_direction != DMA_NONE)
676701
sh_mobile_i2c_cleanup_dma(pd);
@@ -696,14 +721,35 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
696721
return err ?: num;
697722
}
698723

724+
static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
725+
struct i2c_msg *msgs,
726+
int num)
727+
{
728+
struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
729+
730+
pd->atomic_xfer = false;
731+
return sh_mobile_xfer(pd, msgs, num);
732+
}
733+
734+
static int sh_mobile_i2c_xfer_atomic(struct i2c_adapter *adapter,
735+
struct i2c_msg *msgs,
736+
int num)
737+
{
738+
struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
739+
740+
pd->atomic_xfer = true;
741+
return sh_mobile_xfer(pd, msgs, num);
742+
}
743+
699744
static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
700745
{
701746
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
702747
}
703748

704749
static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
705-
.functionality = sh_mobile_i2c_func,
706-
.master_xfer = sh_mobile_i2c_xfer,
750+
.functionality = sh_mobile_i2c_func,
751+
.master_xfer = sh_mobile_i2c_xfer,
752+
.master_xfer_atomic = sh_mobile_i2c_xfer_atomic,
707753
};
708754

709755
static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {

0 commit comments

Comments
 (0)