i2c: i2c-qcom-geni: Correct the IEOB/IEOT sequence for I2C GSI tres

Correct the IEOB/IEOT sequence of I2C GSI tres handling the functionality
and memory leak cases.

Change-Id: I001381bc1a35667b16b603854257e210d38723ff
Signed-off-by: Vipin Deep Kaur <vkaur@codeaurora.org>
This commit is contained in:
Vipin Deep Kaur 2020-03-27 17:27:26 +05:30
parent 84b56d3ab5
commit d06ac0169f
2 changed files with 104 additions and 36 deletions
drivers
dma/qcom
i2c/busses

View file

@ -582,7 +582,7 @@ struct gpii {
struct gpi_reg_table dbg_reg_table;
bool reg_table_dump;
u32 dbg_gpi_irq_cnt;
bool ieob_set;
bool unlock_tre_set;
};
struct gpi_desc {
@ -1449,6 +1449,22 @@ static void gpi_process_qup_notif_event(struct gpii_chan *gpii_chan,
client_info->cb_param);
}
/* free gpi_desc for the specified channel */
static void gpi_free_chan_desc(struct gpii_chan *gpii_chan)
{
struct virt_dma_desc *vd;
struct gpi_desc *gpi_desc;
unsigned long flags;
spin_lock_irqsave(&gpii_chan->vc.lock, flags);
vd = vchan_next_desc(&gpii_chan->vc);
gpi_desc = to_gpi_desc(vd);
list_del(&vd->node);
spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
kfree(gpi_desc);
gpi_desc = NULL;
}
/* process DMA Immediate completion data events */
static void gpi_process_imed_data_event(struct gpii_chan *gpii_chan,
struct immediate_data_event *imed_event)
@ -1462,6 +1478,7 @@ static void gpi_process_imed_data_event(struct gpii_chan *gpii_chan,
struct msm_gpi_dma_async_tx_cb_param *tx_cb_param;
unsigned long flags;
u32 chid;
struct gpii_chan *gpii_tx_chan = &gpii->gpii_chan[GPI_TX_CHAN];
/*
* If channel not active don't process event but let
@ -1514,12 +1531,33 @@ static void gpi_process_imed_data_event(struct gpii_chan *gpii_chan,
/* make sure rp updates are immediately visible to all cores */
smp_wmb();
/*
* If unlock tre is present, don't send transfer callback on
* on IEOT, wait for unlock IEOB. Free the respective channel
* descriptors.
* If unlock is not present, IEOB indicates freeing the descriptor
* and IEOT indicates channel transfer completion.
*/
chid = imed_event->chid;
if (imed_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) {
if (chid == GPI_RX_CHAN)
goto gpi_free_desc;
else
if (gpii->unlock_tre_set) {
if (chid == GPI_RX_CHAN) {
if (imed_event->code == MSM_GPI_TCE_EOT)
goto gpi_free_desc;
else if (imed_event->code == MSM_GPI_TCE_UNEXP_ERR)
/*
* In case of an error in a read transfer on a
* shared se, unlock tre will not be processed
* as channels go to bad state so tx desc should
* be freed manually.
*/
gpi_free_chan_desc(gpii_tx_chan);
else
return;
} else if (imed_event->code == MSM_GPI_TCE_EOT) {
return;
}
} else if (imed_event->code == MSM_GPI_TCE_EOB) {
goto gpi_free_desc;
}
tx_cb_param = vd->tx.callback_param;
@ -1539,11 +1577,7 @@ static void gpi_process_imed_data_event(struct gpii_chan *gpii_chan,
}
gpi_free_desc:
spin_lock_irqsave(&gpii_chan->vc.lock, flags);
list_del(&vd->node);
spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
kfree(gpi_desc);
gpi_desc = NULL;
gpi_free_chan_desc(gpii_chan);
}
/* processing transfer completion events */
@ -1558,6 +1592,7 @@ static void gpi_process_xfer_compl_event(struct gpii_chan *gpii_chan,
struct gpi_desc *gpi_desc;
unsigned long flags;
u32 chid;
struct gpii_chan *gpii_tx_chan = &gpii->gpii_chan[GPI_TX_CHAN];
/* only process events on active channel */
if (unlikely(gpii_chan->pm_state != ACTIVE_STATE)) {
@ -1602,12 +1637,33 @@ static void gpi_process_xfer_compl_event(struct gpii_chan *gpii_chan,
/* update must be visible to other cores */
smp_wmb();
/*
* If unlock tre is present, don't send transfer callback on
* on IEOT, wait for unlock IEOB. Free the respective channel
* descriptors.
* If unlock is not present, IEOB indicates freeing the descriptor
* and IEOT indicates channel transfer completion.
*/
chid = compl_event->chid;
if (compl_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) {
if (chid == GPI_RX_CHAN)
goto gpi_free_desc;
else
if (gpii->unlock_tre_set) {
if (chid == GPI_RX_CHAN) {
if (compl_event->code == MSM_GPI_TCE_EOT)
goto gpi_free_desc;
else if (compl_event->code == MSM_GPI_TCE_UNEXP_ERR)
/*
* In case of an error in a read transfer on a
* shared se, unlock tre will not be processed
* as channels go to bad state so tx desc should
* be freed manually.
*/
gpi_free_chan_desc(gpii_tx_chan);
else
return;
} else if (compl_event->code == MSM_GPI_TCE_EOT) {
return;
}
} else if (compl_event->code == MSM_GPI_TCE_EOB) {
goto gpi_free_desc;
}
tx_cb_param = vd->tx.callback_param;
@ -1623,11 +1679,7 @@ static void gpi_process_xfer_compl_event(struct gpii_chan *gpii_chan,
}
gpi_free_desc:
spin_lock_irqsave(&gpii_chan->vc.lock, flags);
list_del(&vd->node);
spin_unlock_irqrestore(&gpii_chan->vc.lock, flags);
kfree(gpi_desc);
gpi_desc = NULL;
gpi_free_chan_desc(gpii_chan);
}
@ -2325,7 +2377,7 @@ struct dma_async_tx_descriptor *gpi_prep_slave_sg(struct dma_chan *chan,
void *tre, *wp = NULL;
const gfp_t gfp = GFP_ATOMIC;
struct gpi_desc *gpi_desc;
gpii->ieob_set = false;
u32 tre_type;
GPII_VERB(gpii, gpii_chan->chid, "enter\n");
@ -2362,13 +2414,12 @@ struct dma_async_tx_descriptor *gpi_prep_slave_sg(struct dma_chan *chan,
for_each_sg(sgl, sg, sg_len, i) {
tre = sg_virt(sg);
/* Check if last tre has ieob set */
/* Check if last tre is an unlock tre */
if (i == sg_len - 1) {
if ((((struct msm_gpi_tre *)tre)->dword[3] &
GPI_IEOB_BMSK) >> GPI_IEOB_BMSK_SHIFT)
gpii->ieob_set = true;
else
gpii->ieob_set = false;
tre_type =
MSM_GPI_TRE_TYPE(((struct msm_gpi_tre *)tre));
gpii->unlock_tre_set =
tre_type == MSM_GPI_TRE_UNLOCK ? true : false;
}
for (j = 0; j < sg->length;

View file

@ -405,12 +405,8 @@ static void gi2c_gsi_tx_cb(void *ptr)
struct msm_gpi_dma_async_tx_cb_param *tx_cb = ptr;
struct geni_i2c_dev *gi2c = tx_cb->userdata;
if (tx_cb->completion_code == MSM_GPI_TCE_EOB) {
complete(&gi2c->xfer);
} else if (!(gi2c->cur->flags & I2C_M_RD)) {
gi2c_gsi_cb_err(tx_cb, "TX");
complete(&gi2c->xfer);
}
gi2c_gsi_cb_err(tx_cb, "TX");
complete(&gi2c->xfer);
}
static void gi2c_gsi_rx_cb(void *ptr)
@ -480,7 +476,7 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
lock_t->dword[2] = MSM_GPI_LOCK_TRE_DWORD2;
lock_t->dword[3] = MSM_GPI_LOCK_TRE_DWORD3(0, 0, 0, 0, 1);
/* unlock */
/* unlock tre: ieob set */
unlock_t->dword[0] = MSM_GPI_UNLOCK_TRE_DWORD0;
unlock_t->dword[1] = MSM_GPI_UNLOCK_TRE_DWORD1;
unlock_t->dword[2] = MSM_GPI_UNLOCK_TRE_DWORD2;
@ -535,12 +531,14 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
segs++;
sg_init_table(gi2c->tx_sg, segs);
if (i == 0)
/* Send lock tre for first transfer in a msg */
sg_set_buf(&gi2c->tx_sg[index++], &gi2c->lock_t,
sizeof(gi2c->lock_t));
} else {
sg_init_table(gi2c->tx_sg, segs);
}
/* Send cfg tre when cfg not sent already */
if (!gi2c->cfg_sent) {
sg_set_buf(&gi2c->tx_sg[index++], &gi2c->cfg0_t,
sizeof(gi2c->cfg0_t));
@ -553,12 +551,21 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
if (msgs[i].flags & I2C_M_RD) {
go_t->dword[2] = MSM_GPI_I2C_GO_TRE_DWORD2(msgs[i].len);
go_t->dword[3] = MSM_GPI_I2C_GO_TRE_DWORD3(1, 0, 0, 0,
0);
/*
* For Rx Go tre: Set ieob for non-shared se and for all
* but last transfer in shared se
*/
if (!gi2c->is_shared || (gi2c->is_shared && i != num-1))
go_t->dword[3] = MSM_GPI_I2C_GO_TRE_DWORD3(1, 0,
0, 1, 0);
else
go_t->dword[3] = MSM_GPI_I2C_GO_TRE_DWORD3(1, 0,
0, 0, 0);
} else {
/* For Tx Go tre: ieob is not set, chain bit is set */
go_t->dword[2] = MSM_GPI_I2C_GO_TRE_DWORD2(0);
go_t->dword[3] = MSM_GPI_I2C_GO_TRE_DWORD3(0, 0, 0, 0,
1);
1);
}
sg_set_buf(&gi2c->tx_sg[index++], &gi2c->go_t,
@ -591,6 +598,7 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
MSM_GPI_DMA_W_BUFFER_TRE_DWORD1(gi2c->rx_ph);
gi2c->rx_t.dword[2] =
MSM_GPI_DMA_W_BUFFER_TRE_DWORD2(msgs[i].len);
/* Set ieot for all Rx/Tx DMA tres */
gi2c->rx_t.dword[3] =
MSM_GPI_DMA_W_BUFFER_TRE_DWORD3(0, 0, 1, 0, 0);
@ -641,6 +649,10 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
gi2c->tx_t.dword[2] =
MSM_GPI_DMA_W_BUFFER_TRE_DWORD2(msgs[i].len);
if (gi2c->is_shared && i == num-1)
/*
* For Tx: unlock tre is send for last transfer
* so set chain bit for last transfer DMA tre.
*/
gi2c->tx_t.dword[3] =
MSM_GPI_DMA_W_BUFFER_TRE_DWORD3(0, 0, 1, 0, 1);
else
@ -652,6 +664,7 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
}
if (gi2c->is_shared && i == num-1) {
/* Send unlock tre at the end of last transfer */
sg_set_buf(&gi2c->tx_sg[index++],
&gi2c->unlock_t, sizeof(gi2c->unlock_t));
}
@ -689,6 +702,10 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
dmaengine_terminate_all(gi2c->tx_c);
gi2c->cfg_sent = 0;
}
if (gi2c->is_shared)
/* Resend cfg tre for every new message on shared se */
gi2c->cfg_sent = 0;
if (msgs[i].flags & I2C_M_RD)
geni_se_iommu_unmap_buf(rx_dev, &gi2c->rx_ph,
msgs[i].len, DMA_FROM_DEVICE);