diff --git a/drivers/i3c/master/i3c-master-msm-geni.c b/drivers/i3c/master/i3c-master-msm-geni.c index dca9365d6a25..cb4d4ea45e6f 100644 --- a/drivers/i3c/master/i3c-master-msm-geni.c +++ b/drivers/i3c/master/i3c-master-msm-geni.c @@ -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,