@@ -1620,6 +1620,17 @@ gpi_peripheral_config(struct dma_chan *chan, struct dma_slave_config *config)
return 0;
}
+static void gpi_create_i2c_go_recovery_tre(struct gpi_i2c_config *i2c,
+ struct gpi_tre *tre)
+{
+ tre->dword[0] = u32_encode_bits(i2c->op, TRE_I2C_GO_CMD);
+ tre->dword[1] = 0;
+ tre->dword[2] = 0;
+
+ tre->dword[3] = u32_encode_bits(TRE_TYPE_GO, TRE_FLAGS_TYPE);
+ tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOT);
+}
+
static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
struct scatterlist *sgl, enum dma_transfer_direction direction,
unsigned long flags)
@@ -1650,6 +1661,12 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
}
+ if (i2c->op == I2C_BUS_CLEAR || i2c->op == I2C_STOP_ON_BUS) {
+ gpi_create_i2c_go_recovery_tre(i2c, &desc->tre[tre_idx]);
+ tre_idx++;
+ goto log_tre;
+ }
+
/* create the GO tre for Tx */
if (i2c->op == I2C_WRITE) {
tre = &desc->tre[tre_idx];
@@ -1692,6 +1709,7 @@ static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc,
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_BEI);
}
+log_tre:
for (i = 0; i < tre_idx; i++)
dev_dbg(dev, "TRE:%d %x:%x:%x:%x\n", i, desc->tre[i].dword[0],
desc->tre[i].dword[1], desc->tre[i].dword[2], desc->tre[i].dword[3]);
@@ -1825,6 +1843,18 @@ gpi_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
nr_tre = 2;
if (direction == DMA_DEV_TO_MEM) /* rx */
nr_tre = 1;
+ /*
+ * Recovery opcodes do not require DMA data TREs, only CONFIG
+ * (for set_config) and GO TREs. Since gpi_prep_slave_sg() is
+ * shared with SPI, verify the channel is I2C before accessing
+ * the configuration data.
+ */
+ if (gchan->protocol == QCOM_GPI_I2C) {
+ struct gpi_i2c_config *i2c = gchan->config;
+
+ if (i2c->op == I2C_BUS_CLEAR || i2c->op == I2C_STOP_ON_BUS)
+ nr_tre = set_config ? 2 : 1;
+ }
/* calculate # of elements required & available */
nr = gpi_ring_num_elements_avail(ch_ring);
@@ -52,6 +52,8 @@ struct gpi_spi_config {
enum i2c_op {
I2C_WRITE = 1,
I2C_READ,
+ I2C_BUS_CLEAR = 6,
+ I2C_STOP_ON_BUS,
};
/**
The I2C_BUS_CLEAR and I2C_STOP_ON_BUS GENI sequencer opcodes release a stuck I2C bus, but the GPI DMA driver has no way to emit these zero-payload control commands, so an I2C controller using GPI DMA cannot recover the bus. Add I2C_BUS_CLEAR and I2C_STOP_ON_BUS to enum i2c_op so I2C drivers can request them through gpi_i2c_config.op. Handle them in gpi_create_i2c_tre() by emitting a CONFIG TRE (when set_config is set) followed by a GO TRE with IEOT set and no DMA TRE, and reserve the matching number of ring entries in gpi_prep_slave_sg(). Signed-off-by: Aniket Randive <aniket.randive@oss.qualcomm.com> --- drivers/dma/qcom/gpi.c | 30 ++++++++++++++++++++++++++++++ include/linux/dma/qcom-gpi-dma.h | 2 ++ 2 files changed, 32 insertions(+)