mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
i2c: qcom-geni: use dedicated completions for abort and reset events
The driver uses a common completion event for transfer, abort and DMA reset operations. This allows unrelated completion events to prematurely wake abort and reset waiters, leading to incorrect synchronization. Introduce dedicated completions for abort, TX reset, and RX reset operations, and signal them only from their respective interrupt events. This removes the dependency on shared completion state and eliminates the abort_done flag-based synchronization. Co-developed-by: Naresh Maramaina <naresh.maramaina@oss.qualcomm.com> Signed-off-by: Naresh Maramaina <naresh.maramaina@oss.qualcomm.com> Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com> Reviewed-by: Mukesh Kumar Savaliya <mukesh.savaliya@oss.qualcomm.com> Signed-off-by: Andi Shyti <andi.shyti@kernel.org> Link: https://lore.kernel.org/r/20260716-fix_cancel_sequence_on_failure_for_i2c-v3-2-9091315a33a0@oss.qualcomm.com
This commit is contained in:
parent
3275b99ecb
commit
b64f1a0709
|
|
@ -113,7 +113,10 @@ struct geni_i2c_dev {
|
|||
int err;
|
||||
struct i2c_adapter adap;
|
||||
struct completion done;
|
||||
struct completion abort_done;
|
||||
struct completion cancel_done;
|
||||
struct completion tx_reset_done;
|
||||
struct completion rx_reset_done;
|
||||
struct i2c_msg *cur;
|
||||
int cur_wr;
|
||||
int cur_rd;
|
||||
|
|
@ -127,7 +130,6 @@ struct geni_i2c_dev {
|
|||
struct dma_chan *rx_c;
|
||||
bool no_dma;
|
||||
bool gpi_mode;
|
||||
bool abort_done;
|
||||
bool is_tx_multi_desc_xfer;
|
||||
u32 num_msgs;
|
||||
struct geni_i2c_gpi_multi_desc_xfer i2c_multi_desc_config;
|
||||
|
|
@ -256,8 +258,6 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
|
|||
|
||||
switch (err) {
|
||||
case GENI_ABORT_DONE:
|
||||
gi2c->abort_done = true;
|
||||
break;
|
||||
case NACK:
|
||||
case GENI_TIMEOUT:
|
||||
dev_dbg(gi2c->se.dev, "%s\n", gi2c_log[err].msg);
|
||||
|
|
@ -359,12 +359,18 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
|||
writel_relaxed(dm_rx_st, base + SE_DMA_RX_IRQ_CLR);
|
||||
|
||||
/* if this is err with done-bit not set, handle that through timeout. */
|
||||
if (m_stat & M_CMD_DONE_EN || m_stat & M_CMD_ABORT_EN ||
|
||||
dm_tx_st & TX_DMA_DONE || dm_tx_st & TX_RESET_DONE ||
|
||||
dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
|
||||
if (m_stat & M_CMD_DONE_EN ||
|
||||
dm_tx_st & TX_DMA_DONE ||
|
||||
dm_rx_st & RX_DMA_DONE)
|
||||
complete(&gi2c->done);
|
||||
if (m_stat & M_CMD_CANCEL_EN)
|
||||
complete(&gi2c->cancel_done);
|
||||
if (m_stat & M_CMD_ABORT_EN)
|
||||
complete(&gi2c->abort_done);
|
||||
if (dm_tx_st & TX_RESET_DONE)
|
||||
complete(&gi2c->tx_reset_done);
|
||||
if (dm_rx_st & RX_RESET_DONE)
|
||||
complete(&gi2c->rx_reset_done);
|
||||
|
||||
spin_unlock(&gi2c->lock);
|
||||
|
||||
|
|
@ -376,17 +382,13 @@ static void geni_i2c_abort_xfer(struct geni_i2c_dev *gi2c)
|
|||
unsigned long time_left = ABORT_TIMEOUT;
|
||||
unsigned long flags;
|
||||
|
||||
reinit_completion(&gi2c->abort_done);
|
||||
|
||||
spin_lock_irqsave(&gi2c->lock, flags);
|
||||
geni_i2c_err(gi2c, GENI_TIMEOUT);
|
||||
gi2c->cur = NULL;
|
||||
gi2c->abort_done = false;
|
||||
geni_se_abort_m_cmd(&gi2c->se);
|
||||
spin_unlock_irqrestore(&gi2c->lock, flags);
|
||||
|
||||
do {
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||
} while (!gi2c->abort_done && time_left);
|
||||
|
||||
time_left = wait_for_completion_timeout(&gi2c->abort_done, time_left);
|
||||
if (!time_left)
|
||||
dev_err(gi2c->se.dev, "Timeout abort_m_cmd\n");
|
||||
}
|
||||
|
|
@ -414,31 +416,25 @@ static void geni_i2c_cancel_xfer(struct geni_i2c_dev *gi2c)
|
|||
|
||||
static void geni_i2c_rx_fsm_rst(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long time_left = RST_TIMEOUT;
|
||||
|
||||
reinit_completion(&gi2c->rx_reset_done);
|
||||
writel_relaxed(1, gi2c->se.base + SE_DMA_RX_FSM_RST);
|
||||
do {
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||
val = readl_relaxed(gi2c->se.base + SE_DMA_RX_IRQ_STAT);
|
||||
} while (!(val & RX_RESET_DONE) && time_left);
|
||||
|
||||
if (!(val & RX_RESET_DONE))
|
||||
time_left = wait_for_completion_timeout(&gi2c->rx_reset_done, time_left);
|
||||
if (!time_left)
|
||||
dev_err(gi2c->se.dev, "Timeout resetting RX_FSM\n");
|
||||
}
|
||||
|
||||
static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long time_left = RST_TIMEOUT;
|
||||
|
||||
reinit_completion(&gi2c->tx_reset_done);
|
||||
writel_relaxed(1, gi2c->se.base + SE_DMA_TX_FSM_RST);
|
||||
do {
|
||||
time_left = wait_for_completion_timeout(&gi2c->done, time_left);
|
||||
val = readl_relaxed(gi2c->se.base + SE_DMA_TX_IRQ_STAT);
|
||||
} while (!(val & TX_RESET_DONE) && time_left);
|
||||
|
||||
if (!(val & TX_RESET_DONE))
|
||||
time_left = wait_for_completion_timeout(&gi2c->tx_reset_done, time_left);
|
||||
if (!time_left)
|
||||
dev_err(gi2c->se.dev, "Timeout resetting TX_FSM\n");
|
||||
}
|
||||
|
||||
|
|
@ -851,6 +847,7 @@ static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], i
|
|||
for (i = 0; i < num; i++) {
|
||||
gi2c->cur = &msgs[i];
|
||||
gi2c->err = 0;
|
||||
reinit_completion(&gi2c->done);
|
||||
dev_dbg(gi2c->se.dev, "msg[%d].len:%d\n", i, gi2c->cur->len);
|
||||
|
||||
peripheral.stretch = 0;
|
||||
|
|
@ -920,6 +917,8 @@ static int geni_i2c_fifo_xfer(struct geni_i2c_dev *gi2c,
|
|||
m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK);
|
||||
|
||||
gi2c->cur = &msgs[i];
|
||||
gi2c->err = 0;
|
||||
reinit_completion(&gi2c->done);
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param);
|
||||
else
|
||||
|
|
@ -939,8 +938,6 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
|
|||
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
|
||||
int ret;
|
||||
|
||||
gi2c->err = 0;
|
||||
reinit_completion(&gi2c->done);
|
||||
ret = pm_runtime_get_sync(gi2c->se.dev);
|
||||
if (ret < 0) {
|
||||
dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
|
||||
|
|
@ -1132,7 +1129,10 @@ static int geni_i2c_probe(struct platform_device *pdev)
|
|||
|
||||
gi2c->adap.algo = &geni_i2c_algo;
|
||||
init_completion(&gi2c->done);
|
||||
init_completion(&gi2c->abort_done);
|
||||
init_completion(&gi2c->cancel_done);
|
||||
init_completion(&gi2c->tx_reset_done);
|
||||
init_completion(&gi2c->rx_reset_done);
|
||||
spin_lock_init(&gi2c->lock);
|
||||
platform_set_drvdata(pdev, gi2c);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user