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:
parent
fcf50d482e
commit
e8c8e11c08
@ -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[] = {
|
||||||
|
Loading…
Reference in New Issue
Block a user