i3c-master-msm-geni: add changes for ibi stuck in gsi mode

If i3c geni master driver receives Nack or timeout, then
driver does gsi stop sequence, if i3c bus is idle
stop command is executed by IBI controller. During the
execution of stop command, IBI can be received from the slave.
IBI controller FSM cannot handle stop command execution
when BUS is not IDLE, so IBI is stuck never recovered.
To solve this added stall bit for every transfer, it will
stretch the clock line, so bus will not release, will release
the bus by generating stop command after last transfer done.
This complete sequence is as per the HW suggested SW workaround.

Change-Id: I45a95f992ff263351090401ae5e07e7f37e2e29d
Signed-off-by: Anil Veshala Veshala <quic_aveshala@quicinc.com>
This commit is contained in:
Anil Veshala Veshala 2023-09-14 02:32:07 -07:00
parent 1f4f6f1f92
commit c24a464fd4

View File

@ -381,6 +381,7 @@ struct geni_i3c_clk_fld {
u32 i2c_t_cycle_cnt;
};
static int geni_i3c_gsi_stop_on_bus(struct geni_i3c_dev *gi3c);
static void geni_i3c_enable_ibi_ctrl(struct geni_i3c_dev *gi3c, bool enable);
static void geni_i3c_enable_ibi_irq(struct geni_i3c_dev *gi3c, bool enable);
static int geni_i3c_enable_naon_ibi_clks(struct geni_i3c_dev *gi3c, bool enable);
@ -794,7 +795,6 @@ static void i3c_setup_go_tre(struct geni_i3c_dev *gi3c, struct geni_i3c_xfer_par
{
struct msm_gpi_tre *go_t = &gi3c->gsi.tx.tre.go_t;
struct gsi_tre_queue *tx_tre_q = &gi3c->gsi.tx.tre_queue;
bool stretch = (xfer->m_param & STOP_STRETCH) ? 1 : 0;
bool use_7e = (xfer->m_param & USE_7E) ? 1 : 0;
bool nack_ibi = (xfer->m_param & IBI_NACK_TBL_CTRL) ? 1 : 0;
bool cont_mode = (xfer->m_param & CONTINUOUS_MODE_DAA) ? 1 : 0;
@ -808,7 +808,7 @@ static void i3c_setup_go_tre(struct geni_i3c_dev *gi3c, struct geni_i3c_xfer_par
else
cur_len = gi3c->cur_len;
go_t->dword[0] = MSM_GPI_I3C_GO_TRE_DWORD0((stretch << 2 | bypass_addrspace << 7), ccc,
go_t->dword[0] = MSM_GPI_I3C_GO_TRE_DWORD0((1 << 2 | bypass_addrspace << 7), ccc,
addr, xfer->m_cmd);
go_t->dword[1] = MSM_GPI_I3C_GO_TRE_DWORD1(use_7e << 0 | nack_ibi << 1 | cont_mode << 2);
if (gi3c->cur_rnw == READ_TRANSACTION) {
@ -1850,6 +1850,7 @@ geni_i3c_master_gsi_priv_xfers(struct geni_i3c_dev *gi3c, struct i3c_priv_xfer *
I3C_LOG_DBG(gi3c->ipcl, false, gi3c->se.dev, "%s Time took for %d xfers = %llu nsecs\n",
__func__, num_xfers, (sched_clock() - start_time));
geni_i3c_gsi_stop_on_bus(gi3c);
return ret;
}
@ -2135,6 +2136,55 @@ static void geni_i3c_perform_daa(struct geni_i3c_dev *gi3c)
kfree(rx_buf);
}
/*
* geni_i3c_gsi_stop_on_bus() - Does gsi i3c stop command on the bus
*
* @gi3c: i3c master device handle
*
* Return: 0 on success, error code on failure
*/
static int geni_i3c_gsi_stop_on_bus(struct geni_i3c_dev *gi3c)
{
struct msm_gpi_tre *go_t = &gi3c->gsi.tx.tre.go_t;
int tre_cnt = 0, ret = 0, time_remaining = 0;
bool tx_chan = true;
gi3c->err = 0;
gi3c->gsi_err = false;
gi3c->gsi.tx.tre.flags = 0;
reinit_completion(&gi3c->done);
go_t->dword[0] = MSM_GPI_I3C_GO_TRE_DWORD0(0, 0, 0, I2C_STOP_ON_BUS);
go_t->dword[1] = 0x0;
go_t->dword[2] = 0x0;
go_t->dword[3] = MSM_GPI_I3C_GO_TRE_DWORD3(0, 0, 1, 0, 0);
I3C_LOG_DBG(gi3c->ipcl, false, gi3c->se.dev,
"%s: dword[0]:0x%x dword[1]:0x%x dword[2]:0x%x dword[3]:0x%x\n",
__func__, go_t->dword[0], go_t->dword[1], go_t->dword[2], go_t->dword[3]);
gi3c->gsi.tx.tre.flags |= GO_TRE_SET;
tre_cnt = gsi_common_fill_tre_buf(&gi3c->gsi, tx_chan);
ret = gsi_common_prep_desc_and_submit(&gi3c->gsi, tre_cnt, tx_chan, false);
if (ret < 0)
gi3c->err = ret;
time_remaining = wait_for_completion_timeout(&gi3c->done, XFER_TIMEOUT);
if (!time_remaining) {
I3C_LOG_ERR(gi3c->ipcl, false, gi3c->se.dev,
"%s:wait_for_completion timed out\n", __func__);
geni_i3c_err(gi3c, GENI_TIMEOUT);
gi3c->cur_buf = NULL;
gi3c->cur_len = 0;
gi3c->cur_idx = 0;
gi3c->cur_rnw = 0;
reinit_completion(&gi3c->done);
}
I3C_LOG_DBG(gi3c->ipcl, false, gi3c->se.dev, "%s: ret:%d\n", __func__, ret);
return ret;
}
/*
* geni_i3c_master_send_ccc_cmd() - Does i3c master send ccc commands
*
@ -3423,6 +3473,14 @@ static int geni_i3c_probe(struct platform_device *pdev)
"I3C bus freq:%ld, I2C bus fres:%ld\n",
gi3c->ctrlr.bus.scl_rate.i3c, gi3c->ctrlr.bus.scl_rate.i2c);
if (gi3c->se_mode == GENI_GPI_DMA) {
if (geni_i3c_gsi_stop_on_bus(gi3c)) {
I3C_LOG_ERR(gi3c->ipcl, false, gi3c->se.dev,
"I3C gsi stop on bus failed\n");
return -EINVAL;
}
}
// hot-join
gi3c->hj_wl = wakeup_source_register(gi3c->se.dev,
dev_name(gi3c->se.dev));
@ -3542,6 +3600,7 @@ static int geni_i3c_runtime_suspend(struct device *dev)
if (gi3c->se_mode != GENI_GPI_DMA) {
disable_irq(gi3c->irq);
} else {
geni_i3c_gsi_stop_on_bus(gi3c);
ret = geni_i3c_gpi_pause_resume(gi3c, true);
if (ret) {
I3C_LOG_ERR(gi3c->ipcl, false, gi3c->se.dev,