diff options
author | imp <imp@FreeBSD.org> | 2006-04-06 04:31:19 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2006-04-06 04:31:19 +0000 |
commit | 000b33975fdda376e2248023d10bd0bfab55b5f8 (patch) | |
tree | dcb3cbab5be2a58506060fc4c6bbd8bed949a2d4 /sys/arm | |
parent | b8ec0fd91c0b84f95fa850acec5879b0464a65cf (diff) | |
download | FreeBSD-src-000b33975fdda376e2248023d10bd0bfab55b5f8.zip FreeBSD-src-000b33975fdda376e2248023d10bd0bfab55b5f8.tar.gz |
Connect twi to the FreeBSD iicbus infrastructure.
Diffstat (limited to 'sys/arm')
-rw-r--r-- | sys/arm/at91/at91_twi.c | 343 |
1 files changed, 177 insertions, 166 deletions
diff --git a/sys/arm/at91/at91_twi.c b/sys/arm/at91/at91_twi.c index 142f1f9..65b1a8d 100644 --- a/sys/arm/at91/at91_twi.c +++ b/sys/arm/at91/at91_twi.c @@ -40,7 +40,10 @@ __FBSDID("$FreeBSD$"); #include <arm/at91/at91rm92reg.h> #include <arm/at91/at91_twireg.h> -#include <arm/at91/at91_twiio.h> + +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> +#include "iicbus_if.h" struct at91_twi_softc { @@ -50,13 +53,12 @@ struct at91_twi_softc 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; + int sc_started; + int twi_addr; + device_t iicbus; }; static inline uint32_t @@ -79,7 +81,6 @@ WR4(struct at91_twi_softc *sc, bus_size_t off, uint32_t val) #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) #define TWI_DEF_CLK 100000 static devclass_t at91_twi_devclass; @@ -95,19 +96,6 @@ static void at91_twi_intr(void *); 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) { @@ -137,20 +125,23 @@ at91_twi_attach(device_t dev) 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 = TWI_CWGR_CKDIV(1) | + sc->cwgr = TWI_CWGR_CKDIV(8 * AT91C_MASTER_CLOCK / 90000) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(TWI_DEF_CLK)) | TWI_CWGR_CLDIV(TWI_CWGR_DIV(TWI_DEF_CLK)); WR4(sc, TWI_CR, TWI_CR_SWRST); WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); WR4(sc, TWI_CWGR, sc->cwgr); + + WR4(sc, TWI_IER, TWI_SR_RXRDY | TWI_SR_OVRE | TWI_SR_UNRE | + TWI_SR_NACK); + + if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL) + device_printf(dev, "could not allocate iicbus instance\n"); + + /* probe and attach the iicbus */ + bus_generic_attach(dev); + out:; if (err) at91_twi_deactivate(dev); @@ -160,7 +151,15 @@ out:; static int at91_twi_detach(device_t dev) { - return (EBUSY); /* XXX */ + struct at91_twi_softc *sc; + int rv; + + sc = device_get_softc(dev); + at91_twi_deactivate(dev); + if (sc->iicbus && (rv = device_delete_child(dev, sc->iicbus)) != 0) + return (rv); + + return (0); } static int @@ -215,13 +214,12 @@ at91_twi_intr(void *xsc) /* Reading the status also clears the interrupt */ status = RD4(sc, TWI_SR); + printf("status %x\n", status); if (status == 0) return; AT91_TWI_LOCK(sc); if (status & TWI_SR_RXRDY) sc->flags |= RXRDY; - if (status & TWI_SR_TXCOMP) - sc->flags |= TXCOMP; if (status & TWI_SR_TXRDY) sc->flags |= TXRDY; AT91_TWI_UNLOCK(sc); @@ -229,191 +227,196 @@ at91_twi_intr(void *xsc) return; } -static int -at91_twi_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +static int +at91_twi_wait_stop_done(struct at91_twi_softc *sc) +{ + int err = 0; + + while (!(RD4(sc, TWI_SR) & TWI_SR_TXCOMP)) + continue; + 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) { struct at91_twi_softc *sc; + int err = 0; - sc = CDEV2SOFTC(dev); - AT91_TWI_LOCK(sc); - if (!(sc->flags & OPENED)) { - sc->flags |= OPENED; - WR4(sc, TWI_IER, TWI_SR_TXCOMP | TWI_SR_RXRDY | TWI_SR_TXRDY | - TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK); + sc = device_get_softc(dev); + if (sc->sc_started) { + WR4(sc, TWI_CR, TWI_CR_STOP); + err = at91_twi_wait_stop_done(sc); } - AT91_TWI_UNLOCK(sc); - return (0); + return (err); } +/* + * enter a START condition without requiring the device to be in a STOP + * state. + */ static int -at91_twi_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +at91_twi_repeated_start(device_t dev, u_char slave, int timeout) { struct at91_twi_softc *sc; - sc = CDEV2SOFTC(dev); - AT91_TWI_LOCK(sc); - sc->flags &= ~OPENED; - WR4(sc, TWI_IDR, TWI_SR_TXCOMP | TWI_SR_RXRDY | TWI_SR_TXRDY | - TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK); - AT91_TWI_UNLOCK(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_read_master(struct at91_twi_softc *sc, struct at91_twi_io *xfr) +at91_twi_start(device_t dev, u_char slave, int timeout) { - uint8_t *walker; - uint8_t buffer[256]; - size_t len; - int err = 0; + struct at91_twi_softc *sc; - 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); + sc = device_get_softc(dev); + WR4(sc, TWI_MMR, TWI_MMR_DADR(slave)); 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); + sc->sc_started = 1; + return (0); } static int -at91_twi_write_master(struct at91_twi_softc *sc, struct at91_twi_io *xfr) +at91_twi_write(device_t dev, char *buf, int len, int *sent, int timeout /* us */) { + struct at91_twi_softc *sc; uint8_t *walker; - uint8_t buffer[256]; - size_t len; - int err; + int err = 0; - 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); + walker = buf; + sc = device_get_softc(dev); + WR4(sc, TWI_MMR, TWI_MMR_MWRITE | RD4(sc, TWI_MMR)); AT91_TWI_LOCK(sc); - if (err) - return (err); - /* Setup the xfr for later readback */ - xfr->xfer_buf = 0; - xfr->xfer_len = 1; + WR4(sc, TWI_IER, TWI_SR_TXRDY); 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); + goto errout; } - /* Readback */ - at91_twi_read_master(sc, xfr); } +errout:; + WR4(sc, TWI_IDR, TWI_SR_TXRDY); + AT91_TWI_UNLOCK(sc); return (err); } static int -at91_twi_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, - struct thread *td) +at91_twi_read(device_t dev, char *buf, int len, int *read, int last, + int delay /* us */) { - int err = 0; struct at91_twi_softc *sc; + char *walker; + int err = 0; - sc = CDEV2SOFTC(dev); + walker = buf; + sc = device_get_softc(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); + WR4(sc, TWI_MMR, ~TWI_MMR_MWRITE & RD4(sc, TWI_MMR)); + WR4(sc, TWI_IER, TWI_SR_RXRDY); + 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 |= 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; + sc->flags &= ~RXRDY; + *walker++ = RD4(sc, TWI_RHR) & 0xff; + if (len == 1 && last) break; - } - 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:; + WR4(sc, TWI_IDR, TWI_SR_RXRDY); + AT91_TWI_UNLOCK(sc); + return (err); +} - case TWIIOCSETCLOCK: - { - struct at91_twi_clock *twick = (struct at91_twi_clock *)data; +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->cwgr = TWI_CWGR_CKDIV(twick->ckdiv) | - TWI_CWGR_CHDIV(TWI_CWGR_DIV(twick->high_rate)) | - TWI_CWGR_CLDIV(TWI_CWGR_DIV(twick->low_rate)); - WR4(sc, TWI_CR, TWI_CR_SWRST); - WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); - WR4(sc, TWI_CWGR, sc->cwgr); + 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: - err = ENOTTY; + ckdiv = 8 * AT91C_MASTER_CLOCK / 90000; break; } - sc->flags &= ~XFER_PENDING; - AT91_TWI_UNLOCK(sc); - wakeup(sc); - return err; + + 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; +} + +static int +at91_twi_callback(device_t dev, int index, caddr_t *data) +{ + int error = 0; + + switch (index) { + case IIC_REQUEST_BUS: + break; + + case IIC_RELEASE_BUS: + break; + + default: + error = EINVAL; + } + + return (error); } static device_method_t at91_twi_methods[] = { @@ -422,6 +425,14 @@ static device_method_t at91_twi_methods[] = { DEVMETHOD(device_attach, at91_twi_attach), DEVMETHOD(device_detach, at91_twi_detach), + /* iicbus interface */ + DEVMETHOD(iicbus_callback, at91_twi_callback), + DEVMETHOD(iicbus_repeated_start, at91_twi_repeated_start), + DEVMETHOD(iicbus_start, at91_twi_start), + DEVMETHOD(iicbus_stop, at91_twi_stop), + DEVMETHOD(iicbus_write, at91_twi_write), + DEVMETHOD(iicbus_read, at91_twi_read), + DEVMETHOD(iicbus_reset, at91_twi_rst_card), { 0, 0 } }; |