Browse Source

i2c: sh_mobile: terminate DMA reads properly

DMA read requests could miss proper termination, so two more bytes would
have been read via PIO overwriting the end of the buffer with wrong
data. Make DMA stop handling more readable while we are here.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Wolfram Sang 11 năm trước cách đây
mục cha
commit
32e224090f
1 tập tin đã thay đổi với 11 bổ sung1 xóa
  1. 11 1
      drivers/i2c/busses/i2c-sh_mobile.c

+ 11 - 1
drivers/i2c/busses/i2c-sh_mobile.c

@@ -139,6 +139,7 @@ struct sh_mobile_i2c_data {
 	int pos;
 	int pos;
 	int sr;
 	int sr;
 	bool send_stop;
 	bool send_stop;
+	bool stop_after_dma;
 
 
 	struct resource *res;
 	struct resource *res;
 	struct dma_chan *dma_tx;
 	struct dma_chan *dma_tx;
@@ -407,7 +408,7 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
 
 
 	if (pd->pos == pd->msg->len) {
 	if (pd->pos == pd->msg->len) {
 		/* Send stop if we haven't yet (DMA case) */
 		/* Send stop if we haven't yet (DMA case) */
-		if (pd->send_stop && (iic_rd(pd, ICCR) & ICCR_BBSY))
+		if (pd->send_stop && pd->stop_after_dma)
 			i2c_op(pd, OP_TX_STOP, 0);
 			i2c_op(pd, OP_TX_STOP, 0);
 		return 1;
 		return 1;
 	}
 	}
@@ -449,6 +450,13 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)
 		real_pos = pd->pos - 2;
 		real_pos = pd->pos - 2;
 
 
 		if (pd->pos == pd->msg->len) {
 		if (pd->pos == pd->msg->len) {
+			if (pd->stop_after_dma) {
+				/* Simulate PIO end condition after DMA transfer */
+				i2c_op(pd, OP_RX_STOP, 0);
+				pd->pos++;
+				break;
+			}
+
 			if (real_pos < 0) {
 			if (real_pos < 0) {
 				i2c_op(pd, OP_RX_STOP, 0);
 				i2c_op(pd, OP_RX_STOP, 0);
 				break;
 				break;
@@ -536,6 +544,7 @@ static void sh_mobile_i2c_dma_callback(void *data)
 
 
 	sh_mobile_i2c_dma_unmap(pd);
 	sh_mobile_i2c_dma_unmap(pd);
 	pd->pos = pd->msg->len;
 	pd->pos = pd->msg->len;
+	pd->stop_after_dma = true;
 
 
 	iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE);
 	iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE);
 }
 }
@@ -726,6 +735,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
 		bool do_start = pd->send_stop || !i;
 		bool do_start = pd->send_stop || !i;
 		msg = &msgs[i];
 		msg = &msgs[i];
 		pd->send_stop = i == num - 1 || msg->flags & I2C_M_STOP;
 		pd->send_stop = i == num - 1 || msg->flags & I2C_M_STOP;
+		pd->stop_after_dma = false;
 
 
 		err = start_ch(pd, msg, do_start);
 		err = start_ch(pd, msg, do_start);
 		if (err)
 		if (err)