mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-17 07:54:54 +08:00
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>
This commit is contained in:
parent
aafced673c
commit
a49cc1fe9d
@ -129,6 +129,7 @@ struct sh_mobile_i2c_data {
|
|||||||
int sr;
|
int sr;
|
||||||
bool send_stop;
|
bool send_stop;
|
||||||
bool stop_after_dma;
|
bool stop_after_dma;
|
||||||
|
bool atomic_xfer;
|
||||||
|
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct dma_chan *dma_tx;
|
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
|
|||||||
ret = iic_rd(pd, ICDR);
|
ret = iic_rd(pd, ICDR);
|
||||||
break;
|
break;
|
||||||
case OP_RX_STOP: /* enable DTE interrupt, issue stop */
|
case OP_RX_STOP: /* enable DTE interrupt, issue stop */
|
||||||
iic_wr(pd, ICIC,
|
if (!pd->atomic_xfer)
|
||||||
ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
|
iic_wr(pd, ICIC,
|
||||||
|
ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
|
||||||
iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
|
iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
|
||||||
break;
|
break;
|
||||||
case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
|
case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
|
||||||
iic_wr(pd, ICIC,
|
if (!pd->atomic_xfer)
|
||||||
ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
|
iic_wr(pd, ICIC,
|
||||||
|
ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
|
||||||
ret = iic_rd(pd, ICDR);
|
ret = iic_rd(pd, ICDR);
|
||||||
iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
|
iic_wr(pd, ICCR, ICCR_ICE | ICCR_RACK);
|
||||||
break;
|
break;
|
||||||
@ -429,7 +432,8 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id)
|
|||||||
|
|
||||||
if (wakeup) {
|
if (wakeup) {
|
||||||
pd->sr |= SW_DONE;
|
pd->sr |= SW_DONE;
|
||||||
wake_up(&pd->wait);
|
if (!pd->atomic_xfer)
|
||||||
|
wake_up(&pd->wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* defeat write posting to avoid spurious WAIT interrupts */
|
/* 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,
|
|||||||
pd->pos = -1;
|
pd->pos = -1;
|
||||||
pd->sr = 0;
|
pd->sr = 0;
|
||||||
|
|
||||||
|
if (pd->atomic_xfer)
|
||||||
|
return;
|
||||||
|
|
||||||
pd->dma_buf = i2c_get_dma_safe_msg_buf(pd->msg, 8);
|
pd->dma_buf = i2c_get_dma_safe_msg_buf(pd->msg, 8);
|
||||||
if (pd->dma_buf)
|
if (pd->dma_buf)
|
||||||
sh_mobile_i2c_xfer_dma(pd);
|
sh_mobile_i2c_xfer_dma(pd);
|
||||||
@ -637,15 +644,13 @@ static int poll_busy(struct sh_mobile_i2c_data *pd)
|
|||||||
return i ? 0 : -ETIMEDOUT;
|
return i ? 0 : -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
|
static int sh_mobile_xfer(struct sh_mobile_i2c_data *pd,
|
||||||
struct i2c_msg *msgs,
|
struct i2c_msg *msgs, int num)
|
||||||
int num)
|
|
||||||
{
|
{
|
||||||
struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
|
|
||||||
struct i2c_msg *msg;
|
struct i2c_msg *msg;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
int i;
|
int i;
|
||||||
long timeout;
|
long time_left;
|
||||||
|
|
||||||
/* Wake up device and enable clock */
|
/* Wake up device and enable clock */
|
||||||
pm_runtime_get_sync(pd->dev);
|
pm_runtime_get_sync(pd->dev);
|
||||||
@ -662,15 +667,35 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
|
|||||||
if (do_start)
|
if (do_start)
|
||||||
i2c_op(pd, OP_START);
|
i2c_op(pd, OP_START);
|
||||||
|
|
||||||
/* The interrupt handler takes care of the rest... */
|
if (pd->atomic_xfer) {
|
||||||
timeout = wait_event_timeout(pd->wait,
|
unsigned long j = jiffies + pd->adap.timeout;
|
||||||
pd->sr & (ICSR_TACK | SW_DONE),
|
|
||||||
adapter->timeout);
|
|
||||||
|
|
||||||
/* 'stop_after_dma' tells if DMA transfer was complete */
|
time_left = time_before_eq(jiffies, j);
|
||||||
i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg, pd->stop_after_dma);
|
while (time_left &&
|
||||||
|
!(pd->sr & (ICSR_TACK | SW_DONE))) {
|
||||||
|
unsigned char sr = iic_rd(pd, ICSR);
|
||||||
|
|
||||||
if (!timeout) {
|
if (sr & (ICSR_AL | ICSR_TACK |
|
||||||
|
ICSR_WAIT | ICSR_DTE)) {
|
||||||
|
sh_mobile_i2c_isr(0, pd);
|
||||||
|
udelay(150);
|
||||||
|
} else {
|
||||||
|
cpu_relax();
|
||||||
|
}
|
||||||
|
time_left = time_before_eq(jiffies, j);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* The interrupt handler takes care of the rest... */
|
||||||
|
time_left = wait_event_timeout(pd->wait,
|
||||||
|
pd->sr & (ICSR_TACK | SW_DONE),
|
||||||
|
pd->adap.timeout);
|
||||||
|
|
||||||
|
/* 'stop_after_dma' tells if DMA xfer was complete */
|
||||||
|
i2c_put_dma_safe_msg_buf(pd->dma_buf, pd->msg,
|
||||||
|
pd->stop_after_dma);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!time_left) {
|
||||||
dev_err(pd->dev, "Transfer request timed out\n");
|
dev_err(pd->dev, "Transfer request timed out\n");
|
||||||
if (pd->dma_direction != DMA_NONE)
|
if (pd->dma_direction != DMA_NONE)
|
||||||
sh_mobile_i2c_cleanup_dma(pd);
|
sh_mobile_i2c_cleanup_dma(pd);
|
||||||
@ -696,14 +721,35 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
|
|||||||
return err ?: num;
|
return err ?: num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
|
struct i2c_msg *msgs,
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
|
||||||
|
|
||||||
|
pd->atomic_xfer = false;
|
||||||
|
return sh_mobile_xfer(pd, msgs, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sh_mobile_i2c_xfer_atomic(struct i2c_adapter *adapter,
|
||||||
|
struct i2c_msg *msgs,
|
||||||
|
int num)
|
||||||
|
{
|
||||||
|
struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
|
||||||
|
|
||||||
|
pd->atomic_xfer = true;
|
||||||
|
return sh_mobile_xfer(pd, msgs, num);
|
||||||
|
}
|
||||||
|
|
||||||
static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
|
static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
|
||||||
{
|
{
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
|
static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
|
||||||
.functionality = sh_mobile_i2c_func,
|
.functionality = sh_mobile_i2c_func,
|
||||||
.master_xfer = sh_mobile_i2c_xfer,
|
.master_xfer = sh_mobile_i2c_xfer,
|
||||||
|
.master_xfer_atomic = sh_mobile_i2c_xfer_atomic,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {
|
static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {
|
||||||
|
Loading…
Reference in New Issue
Block a user