svn commit: r354314 - head/sys/dev/ichiic

Vladimir Kondratyev wulf at FreeBSD.org
Sun Nov 3 21:07:13 UTC 2019


Author: wulf
Date: Sun Nov  3 21:07:12 2019
New Revision: 354314
URL: https://svnweb.freebsd.org/changeset/base/354314

Log:
  [ig4] Improve error detection
  
  Handle error bits of INTR_STAT and TX_ABORT registers.
  
  Move interrupt clearing from interrupt handler to polling loop to get
  common execution path with polled mode.
  
  Do not clear interrupts with reading of IG4_REG_CLR_INTR register as
  interrupts, triggered during the period from reg_read(IG4_REG_INTR_STAT)
  to reg_read(IG4_REG_CLR_INTR) will be missed.
  Instead, read each IG4_REG_CLR_* register separately.

Modified:
  head/sys/dev/ichiic/ig4_iic.c
  head/sys/dev/ichiic/ig4_reg.h

Modified: head/sys/dev/ichiic/ig4_iic.c
==============================================================================
--- head/sys/dev/ichiic/ig4_iic.c	Sun Nov  3 21:06:06 2019	(r354313)
+++ head/sys/dev/ichiic/ig4_iic.c	Sun Nov  3 21:07:12 2019	(r354314)
@@ -164,6 +164,55 @@ set_intr_mask(ig4iic_softc_t *sc, uint32_t val)
 	}
 }
 
+static int
+intrstat2iic(ig4iic_softc_t *sc, uint32_t val)
+{
+	uint32_t src;
+
+	if (val & IG4_INTR_RX_UNDER)
+		reg_read(sc, IG4_REG_CLR_RX_UNDER);
+	if (val & IG4_INTR_RX_OVER)
+		reg_read(sc, IG4_REG_CLR_RX_OVER);
+	if (val & IG4_INTR_TX_OVER)
+		reg_read(sc, IG4_REG_CLR_TX_OVER);
+
+	if (val & IG4_INTR_TX_ABRT) {
+		src = reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
+		reg_read(sc, IG4_REG_CLR_TX_ABORT);
+		/* User-requested abort. Not really a error */
+		if (src & IG4_ABRTSRC_TRANSFER)
+			return (IIC_ESTATUS);
+		/* Master has lost arbitration */
+		if (src & IG4_ABRTSRC_ARBLOST)
+			return (IIC_EBUSBSY);
+		/* Did not receive an acknowledge from the remote slave */
+		if (src & (IG4_ABRTSRC_TXNOACK_ADDR7 |
+			   IG4_ABRTSRC_TXNOACK_ADDR10_1 |
+			   IG4_ABRTSRC_TXNOACK_ADDR10_2 |
+			   IG4_ABRTSRC_TXNOACK_DATA |
+			   IG4_ABRTSRC_GENCALL_NOACK))
+			return (IIC_ENOACK);
+		/* Programming errors */
+		if (src & (IG4_ABRTSRC_GENCALL_READ |
+			   IG4_ABRTSRC_NORESTART_START |
+			   IG4_ABRTSRC_NORESTART_10))
+			return (IIC_ENOTSUPP);
+		/* Other errors */
+		if (src & IG4_ABRTSRC_ACKED_START)
+			return (IIC_EBUSERR);
+	}
+	/*
+	 * TX_OVER, RX_OVER and RX_UNDER are caused by wrong RX/TX FIFO depth
+	 * detection or driver's read/write pipelining errors.
+	 */
+	if (val & (IG4_INTR_TX_OVER | IG4_INTR_RX_OVER))
+		return (IIC_EOVERFLOW);
+	if (val & IG4_INTR_RX_UNDER)
+		return (IIC_EUNDERFLOW);
+
+	return (IIC_NOERR);
+}
+
 /*
  * Enable or disable the controller and wait for the controller to acknowledge
  * the state change.
@@ -209,17 +258,14 @@ wait_intr(ig4iic_softc_t *sc, uint32_t intr)
 	u_int count_us = 0;
 	u_int limit_us = 25000; /* 25ms */
 
-	error = IIC_ETIMEOUT;
-
 	for (;;) {
 		/*
 		 * Check requested status
 		 */
 		v = reg_read(sc, IG4_REG_RAW_INTR_STAT);
-		if (v & intr) {
-			error = 0;
+		error = intrstat2iic(sc, v & IG4_INTR_ERR_MASK);
+		if (error || (v & intr))
 			break;
-		}
 
 		/*
 		 * When waiting for the transmit FIFO to become empty,
@@ -237,15 +283,17 @@ wait_intr(ig4iic_softc_t *sc, uint32_t intr)
 		/*
 		 * Stop if we've run out of time.
 		 */
-		if (count_us >= limit_us)
+		if (count_us >= limit_us) {
+			error = IIC_ETIMEOUT;
 			break;
+		}
 
 		/*
 		 * When polling is not requested let the interrupt do its work.
 		 */
 		if (!DO_POLL(sc)) {
 			mtx_lock(&sc->io_lock);
-			set_intr_mask(sc, intr);
+			set_intr_mask(sc, intr | IG4_INTR_ERR_MASK);
 			mtx_sleep(sc, &sc->io_lock, 0, "i2cwait",
 				  (hz + 99) / 100); /* sleep up to 10ms */
 			set_intr_mask(sc, 0);
@@ -307,9 +355,17 @@ set_slave_addr(ig4iic_softc_t *sc, uint8_t slave)
  *				IICBUS API FUNCTIONS
  */
 static int
-ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave)
+ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave, bool repeated_start)
 {
 	set_slave_addr(sc, slave >> 1);
+
+	if (!repeated_start) {
+		/*
+		 * Clear any previous TX/RX FIFOs overflow/underflow bits.
+		 */
+		reg_read(sc, IG4_REG_CLR_INTR);
+	}
+
 	return (0);
 }
 
@@ -374,7 +430,6 @@ ig4iic_read(ig4iic_softc_t *sc, uint8_t *buf, uint16_t
 		}
 	}
 out:
-	(void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
 	return (error);
 }
 
@@ -410,7 +465,6 @@ ig4iic_write(ig4iic_softc_t *sc, uint8_t *buf, uint16_
 		}
 	}
 
-	(void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
 	return (error);
 }
 
@@ -509,7 +563,7 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, ui
 	error = 0;
 	for (i = 0; i < nmsgs; i++) {
 		if ((msgs[i].flags & IIC_M_NOSTART) == 0) {
-			error = ig4iic_xfer_start(sc, msgs[i].slave);
+			error = ig4iic_xfer_start(sc, msgs[i].slave, rpstart);
 		} else {
 			if (!sc->slave_valid ||
 			    (msgs[i].slave >> 1) != sc->last_slave) {
@@ -1019,8 +1073,8 @@ ig4iic_intr(void *cookie)
 	mtx_lock(&sc->io_lock);
 	/* Ignore stray interrupts */
 	if (sc->intr_mask != 0 && reg_read(sc, IG4_REG_INTR_STAT) != 0) {
+		/* Interrupt bits are cleared in wait_intr() loop */
 		set_intr_mask(sc, 0);
-		reg_read(sc, IG4_REG_CLR_INTR);
 		wakeup(sc);
 	}
 	mtx_unlock(&sc->io_lock);

Modified: head/sys/dev/ichiic/ig4_reg.h
==============================================================================
--- head/sys/dev/ichiic/ig4_reg.h	Sun Nov  3 21:06:06 2019	(r354313)
+++ head/sys/dev/ichiic/ig4_reg.h	Sun Nov  3 21:07:12 2019	(r354314)
@@ -328,6 +328,9 @@
 #define IG4_INTR_RX_OVER	0x0002
 #define IG4_INTR_RX_UNDER	0x0001
 
+#define IG4_INTR_ERR_MASK	(IG4_INTR_TX_ABRT | IG4_INTR_TX_OVER | \
+				 IG4_INTR_RX_OVER | IG4_INTR_RX_UNDER)
+
 /*
  * RX_TL	- (RW) Receive FIFO Threshold Register		22.2.11
  * TX_TL	- (RW) Transmit FIFO Threshold Register		22.2.12
@@ -435,8 +438,8 @@
 #define IG4_ABRTSRC_NORESTART_10	0x00000400 /* RESTART disabled */
 #define IG4_ABRTSRC_NORESTART_START	0x00000200 /* RESTART disabled */
 #define IG4_ABRTSRC_ACKED_START		0x00000080 /* Improper acked START */
-#define IG4_ABRTSRC_GENCALL_NOACK	0x00000020 /* Improper GENCALL */
-#define IG4_ABRTSRC_GENCALL_READ	0x00000010 /* Nobody acked GENCALL */
+#define IG4_ABRTSRC_GENCALL_READ	0x00000020 /* Improper GENCALL */
+#define IG4_ABRTSRC_GENCALL_NOACK	0x00000010 /* Nobody acked GENCALL */
 #define IG4_ABRTSRC_TXNOACK_DATA	0x00000008 /* data phase no ACK */
 #define IG4_ABRTSRC_TXNOACK_ADDR10_2	0x00000004 /* addr10/1 phase no ACK */
 #define IG4_ABRTSRC_TXNOACK_ADDR10_1	0x00000002 /* addr10/2 phase no ACK */


More information about the svn-src-head mailing list