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:
parent
1f4f6f1f92
commit
c24a464fd4
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user