svn commit: r363021 - head/sys/dev/iicbus/twsi

Emmanuel Vadot manu at FreeBSD.org
Wed Jul 8 19:14:49 UTC 2020


Author: manu
Date: Wed Jul  8 19:14:44 2020
New Revision: 363021
URL: https://svnweb.freebsd.org/changeset/base/363021

Log:
  twsi: Fix for > Allwinner A20
  
  Every revision of twsi after the A20 have a bug where we need to
  write again the control register after each interrupts. We also need
  to add some delay before writing to this register, a simple read of the
  same register does the job so do that.
  Also fix the case when we have finish sending all the bytes, it only worked
  for 1 byte transfer (the same kind that we do for talking to the PMIC on A20
  boards).
  While here add more debug messages and rework some of them.
  
  This was tested by talking to a AT23C32 eeprom and a DS3231 RTC from an
  H3 and A20 board.
  
  PR:		247576
  Reported by:	Manuel Stühn (freebsd at justmail.de)
  MFC after:	1 week

Modified:
  head/sys/dev/iicbus/twsi/twsi.c

Modified: head/sys/dev/iicbus/twsi/twsi.c
==============================================================================
--- head/sys/dev/iicbus/twsi/twsi.c	Wed Jul  8 18:58:44 2020	(r363020)
+++ head/sys/dev/iicbus/twsi/twsi.c	Wed Jul  8 19:14:44 2020	(r363021)
@@ -499,6 +499,10 @@ twsi_transfer(device_t dev, struct iic_msg *msgs, uint
 	sc->msg_idx = 0;
 	sc->transfer = 1;
 
+#ifdef TWSI_DEBUG
+	for (int i = 0; i < nmsgs; i++)
+		debugf(sc->dev, "msg %d is %d bytes long\n", i, msgs[i].len);
+#endif
 	/* Send start and re-enable interrupts */
 	sc->control_val = TWSI_CONTROL_TWSIEN |
 		TWSI_CONTROL_INTEN | TWSI_CONTROL_ACK;
@@ -535,13 +539,13 @@ twsi_intr(void *arg)
 	debugf(sc->dev, "Got interrupt Current msg=%x\n", sc->msg_idx);
 
 	status = TWSI_READ(sc, sc->reg_status);
-	debugf(sc->dev, "initial status=%x\n", status);
+	debugf(sc->dev, "reg control=%x\n", TWSI_READ(sc, sc->reg_control));
 
 	switch (status) {
 	case TWSI_STATUS_START:
 	case TWSI_STATUS_RPTD_START:
 		/* Transmit the address */
-		debugf(sc->dev, "Send the address\n");
+		debugf(sc->dev, "Send the address (%x)", sc->msgs[sc->msg_idx].slave);
 
 		if (sc->msgs[sc->msg_idx].flags & IIC_M_RD)
 			TWSI_WRITE(sc, sc->reg_data,
@@ -555,8 +559,10 @@ twsi_intr(void *arg)
 	case TWSI_STATUS_ADDR_W_ACK:
 		debugf(sc->dev, "Ack received after transmitting the address (write)\n");
 		/* Directly send the first byte */
-		sc->sent_bytes = 0;
-		debugf(sc->dev, "Sending byte 0 = %x\n", sc->msgs[sc->msg_idx].buf[0]);
+		sc->sent_bytes = 1;
+		debugf(sc->dev, "Sending byte 0 (of %d) = %x\n",
+		    sc->msgs[sc->msg_idx].len,
+		    sc->msgs[sc->msg_idx].buf[0]);
 		TWSI_WRITE(sc, sc->reg_data, sc->msgs[sc->msg_idx].buf[0]);
 
 		TWSI_WRITE(sc, sc->reg_control, sc->control_val);
@@ -580,7 +586,7 @@ twsi_intr(void *arg)
 
 	case TWSI_STATUS_DATA_WR_ACK:
 		debugf(sc->dev, "Ack received after transmitting data\n");
-		if (sc->sent_bytes++ == (sc->msgs[sc->msg_idx].len - 1)) {
+		if (sc->sent_bytes == sc->msgs[sc->msg_idx].len) {
 			debugf(sc->dev, "Done sending all the bytes for msg %d\n", sc->msg_idx);
 			/* Send stop, no interrupts on stop */
 			if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP)) {
@@ -595,30 +601,40 @@ twsi_intr(void *arg)
 			if (sc->msg_idx == sc->nmsgs) {
 				debugf(sc->dev, "transfer_done=1\n");
 				transfer_done = 1;
+				sc->error = 0;
+			} else {
+				debugf(sc->dev, "Send repeated start\n");
+				TWSI_WRITE(sc, sc->reg_control, sc->control_val | TWSI_CONTROL_START);
 			}
 		} else {
-			debugf(sc->dev, "Sending byte %d = %x\n",
+			debugf(sc->dev, "Sending byte %d (of %d) = %x\n",
 			    sc->sent_bytes,
+			    sc->msgs[sc->msg_idx].len,
 			    sc->msgs[sc->msg_idx].buf[sc->sent_bytes]);
 			TWSI_WRITE(sc, sc->reg_data,
 			    sc->msgs[sc->msg_idx].buf[sc->sent_bytes]);
 			TWSI_WRITE(sc, sc->reg_control,
 			    sc->control_val);
+			sc->sent_bytes++;
 		}
 		break;
 
 	case TWSI_STATUS_DATA_RD_ACK:
 		debugf(sc->dev, "Ack received after receiving data\n");
-		debugf(sc->dev, "msg_len=%d recv_bytes=%d\n", sc->msgs[sc->msg_idx].len, sc->recv_bytes);
 		sc->msgs[sc->msg_idx].buf[sc->recv_bytes++] = TWSI_READ(sc, sc->reg_data);
+		debugf(sc->dev, "msg_len=%d recv_bytes=%d\n", sc->msgs[sc->msg_idx].len, sc->recv_bytes);
 
 		/* If we only have one byte left, disable ACK */
 		if (sc->msgs[sc->msg_idx].len - sc->recv_bytes == 1)
 			sc->control_val &= ~TWSI_CONTROL_ACK;
-		if (sc->msgs[sc->msg_idx].len - sc->recv_bytes) {
+		if (sc->msgs[sc->msg_idx].len == sc->recv_bytes) {
+			debugf(sc->dev, "Done with msg %d\n", sc->msg_idx);
 			sc->msg_idx++;
-			if (sc->msg_idx == sc->nmsgs - 1)
+			if (sc->msg_idx == sc->nmsgs - 1) {
+				debugf(sc->dev, "No more msgs\n");
 				transfer_done = 1;
+				sc->error = 0;
+			}
 		}
 		TWSI_WRITE(sc, sc->reg_control, sc->control_val);
 		break;
@@ -631,12 +647,14 @@ twsi_intr(void *arg)
 				TWSI_WRITE(sc, sc->reg_control,
 				    sc->control_val | TWSI_CONTROL_STOP);
 		} else {
-			debugf(sc->dev, "No ack when receiving data\n");
-			sc->error = ENXIO;
-			sc->control_val = 0;
+			debugf(sc->dev, "No ack when receiving data, sending stop anyway\n");
+			if (!(sc->msgs[sc->msg_idx].flags & IIC_M_NOSTOP))
+				TWSI_WRITE(sc, sc->reg_control,
+				    sc->control_val | TWSI_CONTROL_STOP);
 		}
 		sc->transfer = 0;
 		transfer_done = 1;
+		sc->error = 0;
 		break;
 
 	default:
@@ -647,6 +665,15 @@ twsi_intr(void *arg)
 		wakeup(sc);
 		break;
 	}
+	debugf(sc->dev, "Refresh reg_control\n");
+
+	/* 
+	 * Fix silicon bug on > Allwinner A20 by doing a read and writing
+	 * again to the control register
+	 */
+	status = TWSI_READ(sc, sc->reg_status);
+	TWSI_WRITE(sc, sc->reg_control,
+	  sc->control_val | TWSI_CONTROL_IFLG);
 
 	debugf(sc->dev, "Done with interrupts\n\n");
 	if (transfer_done == 1) {


More information about the svn-src-all mailing list