Module Name: src Committed By: kiyohara Date: Sat Aug 6 03:42:11 UTC 2011
Modified Files: src/sys/arch/arm/xscale: pxa2x0_i2c.c pxa2x0_i2c.h Log Message: Support slave mode for PXA2x0 I2C. To generate a diff of this commit: cvs rdiff -u -r1.7 -r1.8 src/sys/arch/arm/xscale/pxa2x0_i2c.c cvs rdiff -u -r1.4 -r1.5 src/sys/arch/arm/xscale/pxa2x0_i2c.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/xscale/pxa2x0_i2c.c diff -u src/sys/arch/arm/xscale/pxa2x0_i2c.c:1.7 src/sys/arch/arm/xscale/pxa2x0_i2c.c:1.8 --- src/sys/arch/arm/xscale/pxa2x0_i2c.c:1.7 Thu Jun 23 11:26:22 2011 +++ src/sys/arch/arm/xscale/pxa2x0_i2c.c Sat Aug 6 03:42:11 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: pxa2x0_i2c.c,v 1.7 2011/06/23 11:26:22 kiyohara Exp $ */ +/* $NetBSD: pxa2x0_i2c.c,v 1.8 2011/08/06 03:42:11 kiyohara Exp $ */ /* $OpenBSD: pxa2x0_i2c.c,v 1.2 2005/05/26 03:52:07 pascoe Exp $ */ /* @@ -18,12 +18,13 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pxa2x0_i2c.c,v 1.7 2011/06/23 11:26:22 kiyohara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pxa2x0_i2c.c,v 1.8 2011/08/06 03:42:11 kiyohara Exp $"); #include <sys/param.h> -#include <sys/systm.h> -#include <sys/device.h> #include <sys/bus.h> +#include <sys/device.h> +#include <sys/errno.h> +#include <sys/systm.h> #include <dev/i2c/i2cvar.h> @@ -118,7 +119,7 @@ retry: bus_space_write_4(iot, ioh, I2C_ICR, ICR_UR); - bus_space_write_4(iot, ioh, I2C_ISAR, 0x00); + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE | ISR_IRF); delay(1); bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SCLE); @@ -162,8 +163,7 @@ rv = bus_space_read_4(iot, ioh, I2C_IDBR); *valuep = (u_char)rv; - rv = bus_space_read_4(iot, ioh, I2C_ICR); - bus_space_write_4(iot, ioh, I2C_ICR, rv & ~(ICR_STOP | ICR_ACKNAK)); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); return 0; @@ -172,9 +172,9 @@ goto retry; bus_space_write_4(iot, ioh, I2C_ICR, ICR_UR); - bus_space_write_4(iot, ioh, I2C_ISAR, 0x00); + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE | ISR_IRF); - bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SCLE); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); return EIO; } @@ -190,7 +190,7 @@ retry: bus_space_write_4(iot, ioh, I2C_ICR, ICR_UR); - bus_space_write_4(iot, ioh, I2C_ISAR, 0x00); + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); delay(1); bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SCLE); @@ -235,8 +235,7 @@ bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); - rv = bus_space_read_4(iot, ioh, I2C_ICR); - bus_space_write_4(iot, ioh, I2C_ICR, rv & ~ICR_STOP); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); return 0; @@ -245,9 +244,9 @@ goto retry; bus_space_write_4(iot, ioh, I2C_ICR, ICR_UR); - bus_space_write_4(iot, ioh, I2C_ISAR, 0x00); + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); - bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SCLE); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); return EIO; } @@ -266,7 +265,7 @@ retry: bus_space_write_4(iot, ioh, I2C_ICR, ICR_UR); - bus_space_write_4(iot, ioh, I2C_ISAR, 0x00); + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); delay(1); bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SCLE); @@ -290,8 +289,7 @@ bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); - rv = bus_space_read_4(iot, ioh, I2C_ICR); - bus_space_write_4(iot, ioh, I2C_ICR, rv & ~ICR_STOP); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); return 0; @@ -300,9 +298,9 @@ goto retry; bus_space_write_4(iot, ioh, I2C_ICR, ICR_UR); - bus_space_write_4(iot, ioh, I2C_ISAR, 0x00); + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); - bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SCLE); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); return EIO; } @@ -318,7 +316,7 @@ retry: bus_space_write_4(iot, ioh, I2C_ICR, ICR_UR); - bus_space_write_4(iot, ioh, I2C_ISAR, 0x00); + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); delay(1); bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SCLE); @@ -386,6 +384,8 @@ rv = bus_space_read_4(iot, ioh, I2C_ICR); bus_space_write_4(iot, ioh, I2C_ICR, rv & ~ICR_STOP); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); + return 0; err: @@ -393,9 +393,9 @@ goto retry; bus_space_write_4(iot, ioh, I2C_ICR, ICR_UR); - bus_space_write_4(iot, ioh, I2C_ISAR, 0x00); + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); - bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SCLE); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); return EIO; } @@ -529,3 +529,190 @@ } return 0; } + + +int +pxa2x0_i2c_poll(struct pxa2x0_i2c_softc *sc, int len, char *data, int op) +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + uint32_t rv; + int timeout, tries, n = 0; + + KASSERT(len > 0); + + if (sc->sc_stat == PI2C_STAT_SEND) { + if (op != I2C_F_WRITE) + return 0; + goto send; + } else if (sc->sc_stat == PI2C_STAT_RECEIVE) { + if (op != I2C_F_READ) + return 0; + goto receive; + } + + bus_space_write_4(iot, ioh, I2C_ISAR, sc->sc_isar); + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_SADIE); + + /* Poll Slave Address Detected */ + tries = I2C_RETRY_COUNT; + while (1 /*CONSTCOND*/) { + rv = bus_space_read_4(iot, ioh, I2C_ISR); + if ((rv & (ISR_SAD | ISR_UB)) == (ISR_SAD | ISR_UB)) + break; + if (--tries <= 0) + return 0; + delay(1000); /* XXXX */ + } + bus_space_write_4(iot, ioh, I2C_ISR, ISR_SAD); /* Clear SAD */ + + rv = bus_space_read_4(iot, ioh, I2C_ISR); + if (rv & ISR_RWM) { + if (op != I2C_F_WRITE) + return 0; + if (rv & ISR_ACKNAK) + return 0; + +send: + bus_space_write_4(iot, ioh, I2C_IDBR, data[n]); + + /* Initiate the transfer */ + rv = bus_space_read_4(iot, ioh, I2C_ICR); + bus_space_write_4(iot, ioh, I2C_ICR, rv | ICR_TB | ICR_ITEIE); + + while (n < len - 1) { + timeout = 10000; + while (--timeout > 0) { + rv = bus_space_read_4(iot, ioh, I2C_ISR); + rv &= (ISR_ITE | ISR_ACKNAK | ISR_RWM); + if (rv == ISR_ITE) + break; + delay(1); + } + if (timeout == 0) + goto err; + bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); + + n++; + if (n < len) + bus_space_write_4(iot, ioh, I2C_IDBR, data[n]); + + rv = bus_space_read_4(iot, ioh, I2C_ICR); + bus_space_write_4(iot, ioh, I2C_ICR, + rv | ICR_TB | ICR_ITEIE); + } + + timeout = 10000; + while (--timeout > 0) { + rv = bus_space_read_4(iot, ioh, I2C_ISR); + rv &= (ISR_ITE | ISR_ACKNAK | ISR_RWM); + if (rv == (ISR_ITE | ISR_ACKNAK)) + break; + delay(1); + } + if (timeout == 0) + goto err; + bus_space_write_4(iot, ioh, I2C_ISR, ISR_ITE); + + n++; + } else { + if (op != I2C_F_READ) + return 0; + + while (n < len) { + rv = bus_space_read_4(iot, ioh, I2C_ICR); + bus_space_write_4(iot, ioh, I2C_ICR, + rv | ICR_TB | ICR_IRFIE); + +receive: + timeout = 10000; + while (--timeout > 0) { + rv = bus_space_read_4(iot, ioh, I2C_ISR); + rv &= (ISR_IRF | ISR_ACKNAK | ISR_RWM); + if (rv == ISR_IRF) + break; + delay(1); + } + if (timeout == 0) + goto err; + + data[n++] = bus_space_read_4(iot, ioh, I2C_IDBR); + + bus_space_write_4(iot, ioh, I2C_ISR, ISR_IRF); + } + + /* Release I2C bus and allow next transfer. */ + rv = bus_space_read_4(iot, ioh, I2C_ICR); + bus_space_write_4(iot, ioh, I2C_ICR, rv | ICR_TB); + } + + timeout = 10000; + while (--timeout > 0) { + rv = bus_space_read_4(iot, ioh, I2C_ISR); + rv &= (ISR_UB | ISR_SSD); + if (rv == ISR_SSD) + break; + delay(1); + } + if (timeout == 0) + goto err; + bus_space_write_4(iot, ioh, I2C_ISR, ISR_SSD); + +err: + bus_space_write_4(iot, ioh, I2C_ICR, ICR_IUE | ICR_BEIE | ICR_SADIE); + + sc->sc_stat = PI2C_STAT_STOP; + return n; +} + +int +pxa2x0_i2c_intr_sub(struct pxa2x0_i2c_softc *sc, int *len, uint8_t *buf) +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int rv; + uint16_t isr; + + isr = bus_space_read_4(iot, ioh, I2C_ISR); + bus_space_write_4(iot, ioh, I2C_ISR, + isr & (ISR_BED|ISR_SAD|ISR_IRF|ISR_ITE|ISR_ALD|ISR_SSD)); + + DPRINTF(("%s: Interrupt Status 0x%x\n", __func__, isr)); + + *len = 0; + if (isr & ISR_SAD) { /* Slave Address Detected */ + if (isr & ISR_RWM) + sc->sc_stat = PI2C_STAT_SEND; + else { + rv = bus_space_read_4(iot, ioh, I2C_ICR); + bus_space_write_4(iot, ioh, I2C_ICR, + rv | ICR_TB | ICR_IRFIE); + sc->sc_stat = PI2C_STAT_RECEIVE; + } + return 1; /* handled */ + } else if (isr & ISR_IRF) { /* IDBR Receive Full */ + *buf = bus_space_read_4(iot, ioh, I2C_IDBR); + *len = 1; + + /* Next transfer start */ + rv = bus_space_read_4(iot, ioh, I2C_ICR); + bus_space_write_4(iot, ioh, I2C_ICR, + rv | ICR_TB | ICR_IRFIE | ICR_SSDIE); + return 1; /* handled */ + } else if (isr & ISR_SSD) { /* Slave STOP Detected */ + sc->sc_stat = PI2C_STAT_STOP; + + bus_space_write_4(iot, ioh, I2C_ICR, + ICR_IUE | ICR_BEIE | ICR_SADIE); + return 1; /* handled */ + } else if (isr & ISR_BED) { /* Bus Error Detected */ + aprint_error_dev(sc->sc_dev, + "%s: Bus Error Detected\n", __func__); + sc->sc_stat = PI2C_STAT_ERROR; + return 0; /* not handled */ + } + + sc->sc_stat = PI2C_STAT_UNKNOWN; + aprint_error_dev(sc->sc_dev, "Interrupt not handled 0x%x\n", isr); + return 0; /* not handled */ +} Index: src/sys/arch/arm/xscale/pxa2x0_i2c.h diff -u src/sys/arch/arm/xscale/pxa2x0_i2c.h:1.4 src/sys/arch/arm/xscale/pxa2x0_i2c.h:1.5 --- src/sys/arch/arm/xscale/pxa2x0_i2c.h:1.4 Wed Jun 22 16:18:55 2011 +++ src/sys/arch/arm/xscale/pxa2x0_i2c.h Sat Aug 6 03:42:11 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: pxa2x0_i2c.h,v 1.4 2011/06/22 16:18:55 kiyohara Exp $ */ +/* $NetBSD: pxa2x0_i2c.h,v 1.5 2011/08/06 03:42:11 kiyohara Exp $ */ /* $OpenBSD: pxa2x0_i2c.h,v 1.2 2005/05/26 03:52:07 pascoe Exp $ */ /* @@ -31,6 +31,16 @@ bus_size_t sc_size; uint32_t sc_icr; + uint32_t sc_isar; /* I2C Slave Address */ + + enum { + PI2C_STAT_UNKNOWN = -2, + PI2C_STAT_ERROR = -1, + PI2C_STAT_INIT = 0, + PI2C_STAT_SEND, + PI2C_STAT_RECEIVE, + PI2C_STAT_STOP, + } sc_stat; uint32_t sc_flags; #define PI2CF_FAST_MODE (1U << 0) @@ -41,10 +51,10 @@ void pxa2x0_i2c_init(struct pxa2x0_i2c_softc *); void pxa2x0_i2c_open(struct pxa2x0_i2c_softc *); void pxa2x0_i2c_close(struct pxa2x0_i2c_softc *); -int pxa2x0_i2c_read(struct pxa2x0_i2c_softc *sc, u_char, u_char *); +int pxa2x0_i2c_read(struct pxa2x0_i2c_softc *, u_char, u_char *); int pxa2x0_i2c_write(struct pxa2x0_i2c_softc *, u_char, u_char); int pxa2x0_i2c_write_2(struct pxa2x0_i2c_softc *, u_char, u_short); -int pxa2x0_i2c_quick(struct pxa2x0_i2c_softc *sc, u_char, u_char); +int pxa2x0_i2c_quick(struct pxa2x0_i2c_softc *, u_char, u_char); int pxa2x0_i2c_send_start(struct pxa2x0_i2c_softc *, int flags); int pxa2x0_i2c_send_stop(struct pxa2x0_i2c_softc *, int flags); @@ -55,4 +65,7 @@ void pxa2x0_i2c_reset(struct pxa2x0_i2c_softc *); int pxa2x0_i2c_wait(struct pxa2x0_i2c_softc *, int, int); +int pxa2x0_i2c_poll(struct pxa2x0_i2c_softc *, int, char *, int); +int pxa2x0_i2c_intr_sub(struct pxa2x0_i2c_softc *, int *, uint8_t *); + #endif /* _PXA2X0_I2C_H_ */