PERFORCE change 94609 for review

Warner Losh imp at FreeBSD.org
Tue Apr 4 16:56:04 UTC 2006


http://perforce.freebsd.org/chv.cgi?CH=94609

Change 94609 by imp at imp_Speedy on 2006/04/04 16:54:42

	Finish integrating to FreeBSD iicbus framework.  Eliminate old
	untested kludge.

Affected files ...

.. //depot/projects/arm/src/sys/arm/at91/at91_twi.c#10 edit

Differences ...

==== //depot/projects/arm/src/sys/arm/at91/at91_twi.c#10 (text+ko) ====

@@ -54,13 +54,14 @@
 	struct resource	*mem_res;	/* Memory resource */
 	struct mtx sc_mtx;		/* basically a perimeter lock */
 	int flags;
-#define XFER_PENDING	1		/* true when transfer taking place */
 #define OPENED		2		/* Device opened */
 #define RXRDY		4
-#define TXCOMP		8
+#define STOP_DONE	8
 #define TXRDY		0x10
 	struct cdev *cdev;
 	uint32_t cwgr;
+	int	sc_started;
+	int	twi_addr;
 };
 
 static inline uint32_t
@@ -225,7 +226,7 @@
 	if (status & TWI_SR_RXRDY)
 		sc->flags |= RXRDY;
 	if (status & TWI_SR_TXCOMP)
-		sc->flags |= TXCOMP;
+		sc->flags |= STOP_DONE;
 	if (status & TWI_SR_TXRDY)
 		sc->flags |= TXRDY;
 	AT91_TWI_UNLOCK(sc);
@@ -233,6 +234,20 @@
 	return;
 }
 
+static int
+at91_twi_wait_stop_done(struct at91_twi_softc *sc)
+{
+	int err = 0;
+
+	while (!(sc->flags & STOP_DONE)) {
+		err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwsd", 0);
+		if (err)
+			break;
+	}
+	sc->flags &= ~STOP_DONE;
+	return (err);
+}
+
 static int 
 at91_twi_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
 {
@@ -263,103 +278,7 @@
 	return (0);
 }
 
-
-static int
-at91_twi_read_master(struct at91_twi_softc *sc, struct at91_twi_io *xfr)
-{
-	uint8_t *walker;
-	uint8_t buffer[256];
-	size_t len;
-	int err = 0;
-
-	if (xfr->xfer_len > sizeof(buffer))
-		return (EINVAL);
-	walker = buffer;
-	len = xfr->xfer_len;
-	RD4(sc, TWI_RHR);
-	// Master mode, with the right address and interal addr size
-	WR4(sc, TWI_MMR, TWI_MMR_IADRSZ(xfr->iadrsz) | TWI_MMR_MREAD |
-	    TWI_MMR_DADR(xfr->dadr));
-	WR4(sc, TWI_IADR, xfr->iadr);
-	WR4(sc, TWI_CR, TWI_CR_START);
-	while (len-- > 1) {
-		while (!(sc->flags & RXRDY)) {
-			err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twird",
-			    0);
-			if (err)
-				return (err);
-		}
-		sc->flags &= ~RXRDY;
-		*walker++ = RD4(sc, TWI_RHR) & 0xff;
-	}
-	WR4(sc, TWI_CR, TWI_CR_STOP);
-	while (!(sc->flags & TXCOMP)) {
-		err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twird2", 0);
-		if (err)
-			return (err);
-	}
-	sc->flags &= ~TXCOMP;
-	*walker = RD4(sc, TWI_RHR) & 0xff;
-	if (xfr->xfer_buf) {
-		AT91_TWI_UNLOCK(sc);
-		err = copyout(buffer, xfr->xfer_buf, xfr->xfer_len);
-		AT91_TWI_LOCK(sc);
-	}
-	return (err);
-}
-
 static int
-at91_twi_write_master(struct at91_twi_softc *sc, struct at91_twi_io *xfr)
-{
-	uint8_t *walker;
-	uint8_t buffer[256];
-	size_t len;
-	int err;
-
-	if (xfr->xfer_len > sizeof(buffer))
-		return (EINVAL);
-	walker = buffer;
-	len = xfr->xfer_len;
-	AT91_TWI_UNLOCK(sc);
-	err = copyin(xfr->xfer_buf, buffer, xfr->xfer_len);
-	AT91_TWI_LOCK(sc);
-	if (err)
-		return (err);
-	/* Setup the xfr for later readback */
-	xfr->xfer_buf = 0;
-	xfr->xfer_len = 1;
-	while (len--) {
-		WR4(sc, TWI_MMR, TWI_MMR_IADRSZ(xfr->iadrsz) | TWI_MMR_MWRITE |
-		    TWI_MMR_DADR(xfr->dadr));
-		WR4(sc, TWI_IADR, xfr->iadr++);
-		WR4(sc, TWI_THR, *walker++);
-		WR4(sc, TWI_CR, TWI_CR_START);
-		/*
-		 * If we get signal while waiting for TXRDY, make sure we
-		 * try to stop this device
-		 */
-		while (!(sc->flags & TXRDY)) {
-			err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwr",
-			    0);
-			if (err)
-				break;
-		}
-		WR4(sc, TWI_CR, TWI_CR_STOP);
-		if (err)
-			return (err);
-		while (!(sc->flags & TXCOMP)) {
-			err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwr2",
-			    0);
-			if (err)
-				return (err);
-		}
-		/* Readback */
-		at91_twi_read_master(sc, xfr);
-	}
-	return (err);
-}
-
-static int
 at91_twi_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
     struct thread *td)
 {
@@ -368,36 +287,8 @@
 
 	sc = CDEV2SOFTC(dev);
 	AT91_TWI_LOCK(sc);
-	while (sc->flags & XFER_PENDING) {
-		err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH,
-		    "twiwait", 0);
-		if (err) {
-			AT91_TWI_UNLOCK(sc);
-			return (err);
-		}
-	}
-	sc->flags |= XFER_PENDING;
-
 	switch (cmd)
 	{
-	case TWIIOCXFER:
-	{
-		struct at91_twi_io *xfr = (struct at91_twi_io *)data;
-		switch (xfr->type)
-		{
-		case TWI_IO_READ_MASTER:
-			err = at91_twi_read_master(sc, xfr);
-			break;
-		case TWI_IO_WRITE_MASTER:
-			err = at91_twi_write_master(sc, xfr);
-			break;
-		default:
-			err = EINVAL;
-			break;
-		}
-		break;
-	}
-
 	case TWIIOCSETCLOCK:
 	{
 		struct at91_twi_clock *twick = (struct at91_twi_clock *)data;
@@ -414,46 +305,167 @@
 		err = ENOTTY;
 		break;
 	}
-	sc->flags &= ~XFER_PENDING;
 	AT91_TWI_UNLOCK(sc);
 	wakeup(sc);
 	return err;
 }
 
+/*
+ * Stop the transfer by entering a STOP state on the iic bus.  For read
+ * operations, we've already entered the STOP state, since we need to do
+ * that to read the last character.  For write operations, we need to
+ * wait for the TXCOMP bit to turn on before returning.
+ */
 static int
 at91_twi_stop(device_t dev)
 {
-	return (EIO);
+	struct at91_twi_softc *sc;
+	int err = 0;
+
+	sc = device_get_softc(dev);
+	if (sc->sc_started) {
+		WR4(sc, TWI_CR, TWI_CR_STOP);
+		err = at91_twi_wait_stop_done(sc);
+	}
+	return (err);
 }
 
+/*
+ * enter a START condition without requiring the device to be in a STOP
+ * state.
+ */
 static int
 at91_twi_repeated_start(device_t dev, u_char slave, int timeout)
 {
-	return EIO;
+	struct at91_twi_softc *sc;
+
+	sc = device_get_softc(dev);
+	WR4(sc, TWI_MMR, TWI_MMR_DADR(slave));
+	WR4(sc, TWI_CR, TWI_CR_START);
+	sc->sc_started = 1;
+	return (0);
 }
 
+/*
+ * enter a START condition from an idle state.
+ */
 static int
 at91_twi_start(device_t dev, u_char slave, int timeout)
 {
-	return EIO;
+	struct at91_twi_softc *sc;
+
+	sc = device_get_softc(dev);
+	WR4(sc, TWI_MMR, TWI_MMR_DADR(slave));
+	WR4(sc, TWI_CR, TWI_CR_START);
+	sc->sc_started = 1;
+	return (0);
 }
 
 static int
 at91_twi_write(device_t dev, char *buf, int len, int *sent, int timeout /* us */)
 {
-	return EIO;
+	struct at91_twi_softc *sc;
+	uint8_t *walker;
+	int err = 0;
+
+	walker = buf;
+	sc = device_get_softc(dev);
+	WR4(sc, TWI_MMR, TWI_MMR_MWRITE | RD4(sc, TWI_MMR));
+	AT91_TWI_LOCK(sc);
+	while (len--) {
+		WR4(sc, TWI_THR, *walker++);
+		while (!(sc->flags & TXRDY)) {
+			err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twiwr",
+			    0);
+			if (err)
+				goto errout;
+		}
+	}
+errout:;
+	AT91_TWI_UNLOCK(sc);
+	return (err);
 }
 
 static int
 at91_twi_read(device_t dev, char *buf, int len, int *read, int last,
 	 int delay /* us */)
 {
-	return EIO;
+	struct at91_twi_softc *sc;
+	char *walker;
+	int err = 0;
+
+	walker = buf;
+	sc = device_get_softc(dev);
+	AT91_TWI_LOCK(sc);
+	WR4(sc, TWI_MMR, ~TWI_MMR_MWRITE & RD4(sc, TWI_MMR));
+	while (len-- > 0) {
+		err = 0;
+		while (!(sc->flags & RXRDY)) {
+			err = msleep(sc, &sc->sc_mtx, PZERO | PCATCH, "twird",
+			    0);
+			if (err)
+				goto errout;
+		}
+		sc->flags &= ~RXRDY;
+		*walker++ = RD4(sc, TWI_RHR) & 0xff;
+		if (len == 1 && last)
+			break;
+	}
+	if (!last)
+		goto errout;
+	WR4(sc, TWI_CR, TWI_CR_STOP);
+	err = at91_twi_wait_stop_done(sc);
+	*walker = RD4(sc, TWI_RHR) & 0xff;
+	if (read)
+		*read = walker - buf;
+	sc->sc_started = 0;
+errout:;
+	AT91_TWI_UNLOCK(sc);
+	return (err);
 }
 
 static int
 at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
 {
+	struct at91_twi_softc *sc;
+	int ckdiv, rate;
+
+	sc = device_get_softc(dev);
+	if (oldaddr)
+		*oldaddr = sc->twi_addr;
+	if (addr != 0)
+		sc->twi_addr = 0;
+	else
+		sc->twi_addr = addr;
+
+	rate = 1;
+	
+	/*
+	 * 8 * is because "rate == 1" -> 4 clocks down, 4 clocks up.  The
+	 * speeds are for 1.5kb/s, 45kb/s and 90kb/s.
+	 */
+	switch (speed) {
+	case IIC_SLOW:
+		ckdiv = 8 * AT91C_MASTER_CLOCK / 1500;
+		break;
+
+	case IIC_FAST:
+		ckdiv = 8 * AT91C_MASTER_CLOCK / 45000;
+		break;
+
+	case IIC_UNKNOWN:
+	case IIC_FASTEST:
+	default:
+		ckdiv = 8 * AT91C_MASTER_CLOCK / 90000;
+		break;
+	}
+
+	sc->cwgr = TWI_CWGR_CKDIV(ckdiv) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(rate)) |
+	    TWI_CWGR_CLDIV(TWI_CWGR_DIV(rate));
+	WR4(sc, TWI_CR, TWI_CR_SWRST);
+	WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
+	WR4(sc, TWI_CWGR, sc->cwgr);
+
 	return EIO;
 }
 


More information about the p4-projects mailing list