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