Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 18 Jan 2006 09:04:47 GMT
From:      Warner Losh <imp@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 89895 for review
Message-ID:  <200601180904.k0I94l9O039631@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=89895

Change 89895 by imp@imp_hammer on 2006/01/18 09:04:47

	First cut at implementing two ioctls for setting the speed of
	the device, as well as doing the actual transfer.  Sadly, we have
	to do the xfers as ioctls since we need much more data than the
	traditional read call can handle.  Since we're going to be using
	either no threads or libpthread/libthr, we don't have to worry about
	blocking in the ioctl anymore.

Affected files ...

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

Differences ...

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

@@ -38,6 +38,7 @@
 #include <machine/bus.h>
 
 #include <arm/at91/at91_twireg.h>
+#include <arm/at91/at91_twiio.h>
 
 struct at91_twi_softc
 {
@@ -46,6 +47,14 @@
 	struct resource *irq_res;	/* IRQ resource */
 	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 TXRDY		0x10
+	struct cdev *cdev;
+	uint32_t cwgr;
 };
 
 static inline uint32_t
@@ -60,14 +69,15 @@
 	bus_write_4(sc->mem_res, off, val);
 }
 
-#define AT19_TWI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
-#define	AT19_TWI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
-#define AT19_TWI_LOCK_INIT(_sc) \
+#define AT91_TWI_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
+#define	AT91_TWI_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
+#define AT91_TWI_LOCK_INIT(_sc) \
 	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
 	    "twi", MTX_DEF)
-#define AT19_TWI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
-#define AT19_TWI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
-#define AT19_TWI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+#define AT91_TWI_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
+#define AT91_TWI_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
+#define AT91_TWI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+#define CDEV2SOFTC(dev)		((dev)->si_drv1)
 
 static devclass_t at91_twi_devclass;
 
@@ -82,6 +92,19 @@
 static int at91_twi_activate(device_t dev);
 static void at91_twi_deactivate(device_t dev);
 
+/* cdev routines */
+static d_open_t at91_twi_open;
+static d_close_t at91_twi_close;
+static d_ioctl_t at91_twi_ioctl;
+
+static struct cdevsw at91_twi_cdevsw =
+{
+	.d_version = D_VERSION,
+	.d_open = at91_twi_open,
+	.d_close = at91_twi_close,
+	.d_ioctl = at91_twi_ioctl
+};
+
 static int
 at91_twi_probe(device_t dev)
 {
@@ -100,15 +123,31 @@
 	if (err)
 		goto out;
 
-	AT19_TWI_LOCK_INIT(sc);
+	AT91_TWI_LOCK_INIT(sc);
 
 	/*
 	 * Activate the interrupt
 	 */
-	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
+	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
 	    at91_twi_intr, sc, &sc->intrhand);
-	if (err) 
-		AT19_TWI_LOCK_DESTROY(sc);
+	if (err) {
+		AT91_TWI_LOCK_DESTROY(sc);
+		goto out;
+	}
+	sc->cdev = make_dev(at91_twi_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
+	    "twi%d", device_get_unit(dev));
+	if (sc->cdev == NULL) {
+		err = ENOMEM;
+		goto out;
+	}
+	sc->cdev->si_drv1 = sc;
+	sc->cwgr = TWT_CWGR_CKDIV(1) |
+	    TWT_CWGR_CHDIV(TWT_CWGR_DIV(TWI_DEF_CLK)) |
+	    TWT_CWGR_CLDIV(TWT_CWGR_DIV(TWI_DEF_CLK));
+
+	WR4(sc, TWT_CR, TWI_CR_SWRST);
+	WR4(sc, TWT_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
+	WR4(sc, TWT_CWGR, sc->csgr);
 out:;
 	if (err)
 		at91_twi_deactivate(dev);
@@ -118,7 +157,7 @@
 static int
 at91_twi_detach(device_t dev)
 {
-	return EBUSY;	/* XXX */
+	return (EBUSY);	/* XXX */
 }
 
 static int
@@ -169,8 +208,207 @@
 at91_twi_intr(void *xsc)
 {
 	struct at91_twi_softc *sc = xsc;
+	uint32_t status;
+
+	/* Reading the status also clears the interrupt */
+	status = RD4(SC, TWI_SR);
+	if (status == 0)
+		return;
+	AT91_TWI_LOCK(sc);
+	if (status & TWI_SR_RXRDY)
+		sc->flags |= RXRDY;
+	if (statys & TWI_SR_TXCOMP)
+		sc->flags |= TXCOMP;
+	if (statys & TWI_SR_TXRDY)
+		sc->flags |= TXRDY;
+	AT91_TWI_UNLOCK(sc);
+	wakeup(sc);
+	return;
+}
+
+static int 
+at91_twi_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+	struct at91_twi_softc *sc;
+
+	sc = CDEV2SOFTC(dev);
+	AT91_TWI_LOCK(sc);
+	if (!(sc->flags & OPENED)) {
+		sc->flags |= OPENED;
+		WR4(sc, TWI_IER, TWT_SR_TXCOMP | TWT_SR_RXRDY | TWT_SR_TXRDY |
+		    TWT_SR_OVRE | TWT_SR_UNRE | TWT_SR_NACK);
+	}
+	AT91_TWI_UNLOCK(sc);
+    	return (0);
+}
+
+static int
+at91_twi_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
+{
+	struct at91_twi_softc *sc;
+
+	sc = CDEV2SOFTC(dev);
+	AT91_TWI_LOCK(sc);
+	sc->flags &= ~OPENED;
+	WR4(sc, TWI_IDR, TWT_SR_TXCOMP | TWT_SR_RXRDY | TWT_SR_TXRDY |
+	    TWT_SR_OVRE | TWT_SR_UNRE | TWT_SR_NACK);
+	AT91_TWI_UNLOCK(sc);
+	return (0);
+}
+
+
+static int
+at91_twi_read_master(struct at91_twi_softc *sc, struct at91_twi_io *xfr)
+{
+	uint8_t buffer[256];
+	size_t len;
+	int err;
+
+	if (xfr->xfer_len > sizeof(buffer))
+		return (EINVAL);
+	walker = buffer;
+	len = xfr->xfer_len;
+	RD4(sc, TWT_RHR);
+	// Master mode, with the right address and interal addr size
+	WR4(sc, TWT_MMR, TWT_MMR_IADRSZ(xfr->iadrsz) | TWT_MMR_MREAD |
+	    TWT_MMR_DADR(xfr->xadr));
+	WR4(sc, TWT_IADR, xfr->iadr);
+	WR4(sc, TWT_CR, TWT_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, TWT_CR, TWT_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 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, TWT_MMR, TWT_MMR_IADRSZ(xfr->iadrsz) | TWT_MMR_MWRITE |
+		    TWT_MMR_DADR(xfr->xadr));
+		WR4(sc, TWT_IADR, xfr->iadr++);
+		WR4(sc, TWI_THR, *walker++);
+		WR4(sc, TWT_CR, TWT_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, TWT_CR, TWT_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)
+{
+	int err = 0;
+	struct at91_twi_softc *sc;
+
+	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_clock *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;
+
+		sc->cwgr = TWT_CWGR_CKDIV(twick->ckdiv) |
+		    TWT_CWGR_CHDIV(TWT_CWGR_DIV(twick->high_rate)) |
+		    TWT_CWGR_CLDIV(TWT_CWGR_DIV(twick->low_rate));
+		WR4(sc, TWT_CR, TWI_CR_SWRST);
+		WR4(sc, TWT_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
+		WR4(sc, TWT_CWGR, sc->csgr);
+		break;
+	}
+	default:
+		err = ENOTTY;
+		break;
+	}
+	sc->flags &= ~XFER_PENDING;
+	AT91_TWI_UNLOCK(sc);
 	wakeup(sc);
+	return err;
 }
 
 static device_method_t at91_twi_methods[] = {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200601180904.k0I94l9O039631>