diff options
author | imp <imp@FreeBSD.org> | 2006-09-07 21:53:28 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2006-09-07 21:53:28 +0000 |
commit | 6af2f13b581b00dd371a88e4b18cf5c73e878e37 (patch) | |
tree | c9725493d85e97a28fe696518630f8d21d7f1a5e /sys/arm | |
parent | 423b53ad2f9481c4fcd10a06c6d25126976dcf10 (diff) | |
download | FreeBSD-src-6af2f13b581b00dd371a88e4b18cf5c73e878e37.zip FreeBSD-src-6af2f13b581b00dd371a88e4b18cf5c73e878e37.tar.gz |
MFp4: first cut at getting I2C transfers working (generically). I'm
unsure if this driver correctly implements all the start/stop junk
right (but it did or didn't before I made this commit).
Diffstat (limited to 'sys/arm')
-rw-r--r-- | sys/arm/at91/at91_twi.c | 63 |
1 files changed, 55 insertions, 8 deletions
diff --git a/sys/arm/at91/at91_twi.c b/sys/arm/at91/at91_twi.c index 56dd244..84db6b2 100644 --- a/sys/arm/at91/at91_twi.c +++ b/sys/arm/at91/at91_twi.c @@ -128,20 +128,16 @@ at91_twi_attach(device_t dev) 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); @@ -228,12 +224,15 @@ at91_twi_intr(void *xsc) } static int -at91_twi_wait_stop_done(struct at91_twi_softc *sc) +at91_twi_wait(struct at91_twi_softc *sc, uint32_t bit) { int err = 0; + int counter = 10000; - while (!(RD4(sc, TWI_SR) & TWI_SR_TXCOMP)) + while (!(RD4(sc, TWI_SR) & bit) && counter-- != 0) continue; + if (counter == 0) + err = EIO; return (err); } @@ -252,7 +251,7 @@ at91_twi_stop(device_t dev) sc = device_get_softc(dev); if (sc->sc_started) { WR4(sc, TWI_CR, TWI_CR_STOP); - err = at91_twi_wait_stop_done(sc); + err = at91_twi_wait(sc, TWI_SR_TXCOMP); } return (err); } @@ -345,7 +344,7 @@ at91_twi_read(device_t dev, char *buf, int len, int *read, int last, if (!last) goto errout; WR4(sc, TWI_CR, TWI_CR_STOP); - err = at91_twi_wait_stop_done(sc); + err = at91_twi_wait(sc, TWI_SR_TXCOMP); *walker = RD4(sc, TWI_RHR) & 0xff; if (read) *read = walker - buf; @@ -419,6 +418,53 @@ at91_twi_callback(device_t dev, int index, caddr_t *data) return (error); } +static int +at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + struct at91_twi_softc *sc; + int i, len; + uint32_t rdwr; + uint8_t *buf; + + sc = device_get_softc(dev); + for (i = 0; i < nmsgs; i++) { + /* + * The linux atmel driver doesn't use the internal device + * address feature of twi. A separate i2c message needs to + * be written to use this. + * See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html + * for details. + */ + rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0; + WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr); + len = msgs[i].len; + buf = msgs[i].buf; + if (len != 0 || buf == NULL) + return EINVAL; + WR4(sc, TWI_CR, TWI_CR_START); + if (msgs[i].flags & IIC_M_RD) { + while (len--) { + if (len == 0) + WR4(sc, TWI_CR, TWI_CR_STOP); + if (!at91_twi_wait(sc, TWI_SR_RXRDY)) + return EIO; + *buf++ = RD4(sc, TWI_RHR) & 0xff; + } + } else { + while (len--) { + WR4(sc, TWI_THR, *buf++); + if (len == 0) + WR4(sc, TWI_CR, TWI_CR_STOP); + if (!at91_twi_wait(sc, TWI_SR_TXRDY)) + return EIO; + } + } + if (!at91_twi_wait(sc, TWI_SR_TXCOMP)) + return EIO; + } + return 0; +} + static device_method_t at91_twi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, at91_twi_probe), @@ -433,6 +479,7 @@ static device_method_t at91_twi_methods[] = { DEVMETHOD(iicbus_write, at91_twi_write), DEVMETHOD(iicbus_read, at91_twi_read), DEVMETHOD(iicbus_reset, at91_twi_rst_card), + DEVMETHOD(iicbus_transfer, at91_twi_transfer), { 0, 0 } }; |