Make this work a lot better:

Remove a lot of older cruft not needed.
	Improve ISR support, but it is still unused since polling is faster
	Properly initalize the speed register to get 90kb/s, not 400b/s.
	Try to catch NACK
	Allow 0 length read transfers to generate start/top pairs.
This commit is contained in:
Warner Losh 2006-11-29 08:15:59 +00:00
parent fcf50d482e
commit e8c8e11c08

View File

@ -52,9 +52,7 @@ struct at91_twi_softc
struct resource *irq_res; /* IRQ resource */ struct resource *irq_res; /* IRQ resource */
struct resource *mem_res; /* Memory resource */ struct resource *mem_res; /* Memory resource */
struct mtx sc_mtx; /* basically a perimeter lock */ struct mtx sc_mtx; /* basically a perimeter lock */
volatile int flags; volatile uint32_t flags;
#define RXRDY 4
#define TXRDY 0x10
uint32_t cwgr; uint32_t cwgr;
int sc_started; int sc_started;
int twi_addr; int twi_addr;
@ -131,8 +129,6 @@ at91_twi_attach(device_t dev)
WR4(sc, TWI_CR, TWI_CR_SWRST); WR4(sc, TWI_CR, TWI_CR_SWRST);
WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
WR4(sc, TWI_CWGR, sc->cwgr); 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) if ((sc->iicbus = device_add_child(dev, "iicbus", -1)) == NULL)
device_printf(dev, "could not allocate iicbus instance\n"); device_printf(dev, "could not allocate iicbus instance\n");
@ -208,17 +204,17 @@ at91_twi_intr(void *xsc)
struct at91_twi_softc *sc = xsc; struct at91_twi_softc *sc = xsc;
uint32_t status; uint32_t status;
/* Reading the status also clears the interrupt */
status = RD4(sc, TWI_SR); status = RD4(sc, TWI_SR);
printf("status %x\n", status);
if (status == 0) if (status == 0)
return; return;
AT91_TWI_LOCK(sc); sc->flags |= status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK);
if (status & TWI_SR_RXRDY) if (status & TWI_SR_RXRDY)
sc->flags |= RXRDY; sc->flags |= TWI_SR_RXRDY;
if (status & TWI_SR_TXRDY) if (status & TWI_SR_TXRDY)
sc->flags |= TXRDY; sc->flags |= TWI_SR_TXRDY;
AT91_TWI_UNLOCK(sc); if (status & TWI_SR_TXCOMP)
sc->flags |= TWI_SR_TXCOMP;
WR4(sc, TWI_IDR, status);
wakeup(sc); wakeup(sc);
return; return;
} }
@ -228,11 +224,16 @@ at91_twi_wait(struct at91_twi_softc *sc, uint32_t bit)
{ {
int err = 0; int err = 0;
int counter = 100000; int counter = 100000;
uint32_t sr;
while (!(RD4(sc, TWI_SR) & bit) && counter-- >= 0) while (!((sr = RD4(sc, TWI_SR)) & bit) && counter-- > 0)
continue; continue;
if (counter <= 0) if (counter <= 0)
err = EIO; err = EBUSY;
else if (sr & TWI_SR_NACK)
err = EADDRNOTAVAIL;
if (sr & ~bit)
printf("status is %x\n", sr);
return (err); return (err);
} }
@ -240,42 +241,37 @@ static int
at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr) at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
{ {
struct at91_twi_softc *sc; struct at91_twi_softc *sc;
int ckdiv, rate; int clk;
sc = device_get_softc(dev); sc = device_get_softc(dev);
if (oldaddr) if (oldaddr)
*oldaddr = sc->twi_addr; *oldaddr = sc->twi_addr;
if (addr != 0) sc->twi_addr = addr;
sc->twi_addr = 0;
else
sc->twi_addr = addr;
rate = 1;
/* /*
* speeds are for 1.5kb/s, 45kb/s and 90kb/s. * speeds are for 1.5kb/s, 45kb/s and 90kb/s.
*/ */
switch (speed) { switch (speed) {
case IIC_SLOW: case IIC_SLOW:
ckdiv = AT91C_MASTER_CLOCK / (1500 * 4) - 2; clk = 1500;
break; break;
case IIC_FAST: case IIC_FAST:
ckdiv = AT91C_MASTER_CLOCK / (45000 * 4) - 2; clk = 45000;
break; break;
case IIC_UNKNOWN: case IIC_UNKNOWN:
case IIC_FASTEST: case IIC_FASTEST:
default: default:
ckdiv = AT91C_MASTER_CLOCK / (90000 * 4) - 2; clk = 90000;
break; break;
} }
sc->cwgr = TWI_CWGR_CKDIV(1) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(clk)) |
sc->cwgr = TWI_CWGR_CKDIV(ckdiv) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(rate)) | TWI_CWGR_CLDIV(TWI_CWGR_DIV(clk));
TWI_CWGR_CLDIV(TWI_CWGR_DIV(rate));
WR4(sc, TWI_CR, TWI_CR_SWRST); WR4(sc, TWI_CR, TWI_CR_SWRST);
WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS);
WR4(sc, TWI_CWGR, sc->cwgr); WR4(sc, TWI_CWGR, sc->cwgr);
printf("setting cwgr to %#x\n", sc->cwgr);
return 0; return 0;
} }
@ -303,32 +299,36 @@ static int
at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
{ {
struct at91_twi_softc *sc; struct at91_twi_softc *sc;
int i, len; int i, len, err;
uint32_t rdwr; uint32_t rdwr;
uint8_t *buf; uint8_t *buf;
sc = device_get_softc(dev); sc = device_get_softc(dev);
err = 0;
AT91_TWI_LOCK(sc);
for (i = 0; i < nmsgs; i++) { for (i = 0; i < nmsgs; i++) {
/* /*
* The linux atmel driver doesn't use the internal device * The linux atmel driver doesn't use the internal device
* address feature of twi. A separate i2c message needs to * address feature of twi. A separate i2c message needs to
* be written to use this. * be written to use this.
* See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html * See http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2004-September/024411.html
* for details. * for details. Upon reflection, we could use this as an
* optimization, but it is unclear the code bloat will
* result in faster/better operations.
*/ */
rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0; rdwr = (msgs[i].flags & IIC_M_RD) ? TWI_MMR_MREAD : 0;
WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr); WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr);
len = msgs[i].len; len = msgs[i].len;
buf = msgs[i].buf; buf = msgs[i].buf;
if (len == 0 || buf == NULL) if (len != 0 && buf == NULL)
return (EINVAL); return (EINVAL);
WR4(sc, TWI_CR, TWI_CR_START); WR4(sc, TWI_CR, TWI_CR_START);
if (msgs[i].flags & IIC_M_RD) { if (msgs[i].flags & IIC_M_RD) {
while (len--) { while (len--) {
if (len == 0) if (len == 0)
WR4(sc, TWI_CR, TWI_CR_STOP); WR4(sc, TWI_CR, TWI_CR_STOP);
if (at91_twi_wait(sc, TWI_SR_RXRDY)) if ((err = at91_twi_wait(sc, TWI_SR_RXRDY)))
return (EIO); goto out;
*buf++ = RD4(sc, TWI_RHR) & 0xff; *buf++ = RD4(sc, TWI_RHR) & 0xff;
} }
} else { } else {
@ -336,14 +336,22 @@ at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
WR4(sc, TWI_THR, *buf++); WR4(sc, TWI_THR, *buf++);
if (len == 0) if (len == 0)
WR4(sc, TWI_CR, TWI_CR_STOP); WR4(sc, TWI_CR, TWI_CR_STOP);
if (at91_twi_wait(sc, TWI_SR_TXRDY)) if ((err = at91_twi_wait(sc, TWI_SR_TXRDY))) {
return (EIO); printf("Len %d\n", len);
goto out;
}
} }
} }
if (at91_twi_wait(sc, TWI_SR_TXCOMP)) if ((err = at91_twi_wait(sc, TWI_SR_TXCOMP)))
return (EIO); break;
} }
return (0); out:;
if (err) {
WR4(sc, TWI_CR, TWI_CR_STOP);
printf("Err is %d\n", err);
}
AT91_TWI_UNLOCK(sc);
return (err);
} }
static device_method_t at91_twi_methods[] = { static device_method_t at91_twi_methods[] = {