diff options
author | imp <imp@FreeBSD.org> | 2007-03-23 22:57:24 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2007-03-23 22:57:24 +0000 |
commit | 9e26c8685091c80a47fa747bd0b1e03549ec64dc (patch) | |
tree | f67ea993070dc4893349acd95dc68912e4903ad6 | |
parent | 12863b36f7ecab73d884b29ba3ac3b6187f1ec08 (diff) | |
download | FreeBSD-src-9e26c8685091c80a47fa747bd0b1e03549ec64dc.zip FreeBSD-src-9e26c8685091c80a47fa747bd0b1e03549ec64dc.tar.gz |
MFp4: A bunch of patches from myself and Tisco to improve the
robustness of IIC transactions when parts aren't present. This also
removes a bunch of debug. This also moves this driver to 7-1
addressing rather than 6-0 addressing, which is more inline with all
the other iic drivers in the tree. I've tested this for about a
million years on the systems at work.
-rw-r--r-- | sys/arm/at91/at91_twi.c | 63 | ||||
-rw-r--r-- | sys/arm/at91/at91_twireg.h | 2 |
2 files changed, 40 insertions, 25 deletions
diff --git a/sys/arm/at91/at91_twi.c b/sys/arm/at91/at91_twi.c index 0319e12..565df58 100644 --- a/sys/arm/at91/at91_twi.c +++ b/sys/arm/at91/at91_twi.c @@ -45,6 +45,10 @@ __FBSDID("$FreeBSD$"); #include <dev/iicbus/iicbus.h> #include "iicbus_if.h" +#define TWI_SLOW_CLOCK 1500 +#define TWI_FAST_CLOCK 45000 +#define TWI_FASTEST_CLOCK 90000 + struct at91_twi_softc { device_t dev; /* Myself */ @@ -123,7 +127,7 @@ at91_twi_attach(device_t dev) AT91_TWI_LOCK_DESTROY(sc); goto out; } - sc->cwgr = TWI_CWGR_CKDIV(8 * AT91C_MASTER_CLOCK / 90000) | + sc->cwgr = TWI_CWGR_CKDIV(8 * AT91C_MASTER_CLOCK / TWI_FASTEST_CLOCK) | 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); @@ -226,14 +230,13 @@ at91_twi_wait(struct at91_twi_softc *sc, uint32_t bit) int counter = 100000; uint32_t sr; - while (!((sr = RD4(sc, TWI_SR)) & bit) && counter-- > 0) + while (!((sr = RD4(sc, TWI_SR)) & bit) && counter-- > 0 && + !(sr & TWI_SR_NACK)) continue; if (counter <= 0) err = EBUSY; else if (sr & TWI_SR_NACK) - err = EADDRNOTAVAIL; - if (sr & ~bit) - printf("status is %x\n", sr); + err = ENXIO; // iic nack convention return (err); } @@ -253,17 +256,17 @@ at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr) */ switch (speed) { case IIC_SLOW: - clk = 1500; + clk = TWI_SLOW_CLOCK; break; case IIC_FAST: - clk = 45000; + clk = TWI_FAST_CLOCK; break; case IIC_UNKNOWN: case IIC_FASTEST: default: - clk = 90000; + clk = TWI_FASTEST_CLOCK; break; } sc->cwgr = TWI_CWGR_CKDIV(1) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(clk)) | @@ -302,6 +305,7 @@ at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) int i, len, err; uint32_t rdwr; uint8_t *buf; + uint32_t sr; sc = device_get_softc(dev); err = 0; @@ -320,26 +324,36 @@ at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) 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); + /* zero byte transfers aren't allowed */ + if (len == 0 || buf == NULL) { + err = EINVAL; + goto out; + } + if (len == 1) + WR4(sc, TWI_CR, TWI_CR_START | TWI_CR_STOP); + else + 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 ((err = at91_twi_wait(sc, TWI_SR_RXRDY))) - goto out; - *buf++ = RD4(sc, TWI_RHR) & 0xff; + sr = RD4(sc, TWI_SR); + while (!(sr & TWI_SR_TXCOMP)) { + if ((sr = RD4(sc, TWI_SR)) & TWI_SR_RXRDY) { + len--; + *buf++ = RD4(sc, TWI_RHR) & 0xff; + if (len == 0 && msgs[i].len != 1) + WR4(sc, TWI_CR, TWI_CR_STOP); + } + } + if (len > 0 || (sr & TWI_SR_NACK)) { + err = ENXIO; // iic nack convention + goto out; } } else { while (len--) { - WR4(sc, TWI_THR, *buf++); - if (len == 0) + if (len == 0 && msgs[i].len != 1) WR4(sc, TWI_CR, TWI_CR_STOP); - if ((err = at91_twi_wait(sc, TWI_SR_TXRDY))) { - printf("Len %d\n", len); + if ((err = at91_twi_wait(sc, TWI_SR_TXRDY))) goto out; - } + WR4(sc, TWI_THR, *buf++); } } if ((err = at91_twi_wait(sc, TWI_SR_TXCOMP))) @@ -347,8 +361,9 @@ at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) } out:; if (err) { - WR4(sc, TWI_CR, TWI_CR_STOP); - printf("Err is %d\n", err); + WR4(sc, TWI_CR, TWI_CR_SWRST); + WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); + WR4(sc, TWI_CWGR, sc->cwgr); } AT91_TWI_UNLOCK(sc); return (err); diff --git a/sys/arm/at91/at91_twireg.h b/sys/arm/at91/at91_twireg.h index 7afe0bd..5030262 100644 --- a/sys/arm/at91/at91_twireg.h +++ b/sys/arm/at91/at91_twireg.h @@ -56,7 +56,7 @@ #define TWI_MMR_IADRSZ(n) ((n) << 8) /* Set size of transfer */ #define TWI_MMR_MWRITE 0U /* Master Read Direction */ #define TWI_MMR_MREAD (1U << 12) /* Master Read Direction */ -#define TWI_MMR_DADR(n) ((n) << 16) /* Device Address */ +#define TWI_MMR_DADR(n) ((n) << 15) /* Device Address */ /* TWI_CWGR */ #define TWI_CWGR_CKDIV(x) ((x) << 16) /* Clock Divider */ |