static int at91_twi_rst_card(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct at91_twi_softc *sc; int clk; sc = device_get_softc(dev); AT91_TWI_LOCK(sc); if (oldaddr) *oldaddr = sc->twi_addr; sc->twi_addr = addr; /* * speeds are for 1.5kb/s, 45kb/s and 90kb/s. */ switch (speed) { case IIC_SLOW: clk = TWI_SLOW_CLOCK; break; case IIC_FAST: clk = TWI_FAST_CLOCK; break; case IIC_UNKNOWN: case IIC_FASTEST: default: clk = TWI_FASTEST_CLOCK; break; } sc->cwgr = TWI_CWGR_CKDIV(1) | TWI_CWGR_CHDIV(TWI_CWGR_DIV(clk)) | TWI_CWGR_CLDIV(TWI_CWGR_DIV(clk)); WR4(sc, TWI_CR, TWI_CR_SWRST); WR4(sc, TWI_CR, TWI_CR_MSEN | TWI_CR_SVDIS); WR4(sc, TWI_CWGR, sc->cwgr); printf("setting cwgr to %#x\n", sc->cwgr); AT91_TWI_UNLOCK(sc); return 0; }
static void at91_twi_intr(void *xsc) { struct at91_twi_softc *sc = xsc; uint32_t status; status = RD4(sc, TWI_SR); if (status == 0) return; AT91_TWI_LOCK(sc); sc->flags |= status & (TWI_SR_OVRE | TWI_SR_UNRE | TWI_SR_NACK); if (status & TWI_SR_RXRDY) sc->flags |= TWI_SR_RXRDY; if (status & TWI_SR_TXRDY) sc->flags |= TWI_SR_TXRDY; if (status & TWI_SR_TXCOMP) sc->flags |= TWI_SR_TXCOMP; WR4(sc, TWI_IDR, status); wakeup(sc); AT91_TWI_UNLOCK(sc); return; }
static int at91_twi_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct at91_twi_softc *sc; int i, len, err; uint32_t rdwr; uint8_t *buf; uint32_t sr; sc = device_get_softc(dev); err = 0; AT91_TWI_LOCK(sc); 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. 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; WR4(sc, TWI_MMR, TWI_MMR_DADR(msgs[i].slave) | rdwr); len = msgs[i].len; buf = msgs[i].buf; /* zero byte transfers aren't allowed */ if (len == 0 || buf == NULL) { err = EINVAL; goto out; } if (len == 1 && msgs[i].flags & IIC_M_RD) 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) { 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 == 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--) { if ((err = at91_twi_wait(sc, TWI_SR_TXRDY))) goto out; WR4(sc, TWI_THR, *buf++); } WR4(sc, TWI_CR, TWI_CR_STOP); } if ((err = at91_twi_wait(sc, TWI_SR_TXCOMP))) break; } out: if (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); }