Current-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

PXA2x0 I2C slave mode



Hi! all,


My pxa255 machine requires support for I2C slave mode.
This patch supports interrupt handler and polling mode.

I will commit this patch next weekend.

Thanks
--
kiyohara

Index: arch/arm/xscale/pxa2x0_i2c.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/xscale/pxa2x0_i2c.c,v
retrieving revision 1.7
diff -u -r1.7 pxa2x0_i2c.c
--- arch/arm/xscale/pxa2x0_i2c.c        23 Jun 2011 11:26:22 -0000      1.7
+++ arch/arm/xscale/pxa2x0_i2c.c        31 Jul 2011 02:41:43 -0000
@@ -21,9 +21,10 @@
 __KERNEL_RCSID(0, "$NetBSD: pxa2x0_i2c.c,v 1.7 2011/06/23 11:26:22 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: arch/arm/xscale/pxa2x0_i2c.h
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/xscale/pxa2x0_i2c.h,v
retrieving revision 1.4
diff -u -r1.4 pxa2x0_i2c.h
--- arch/arm/xscale/pxa2x0_i2c.h        22 Jun 2011 16:18:55 -0000      1.4
+++ arch/arm/xscale/pxa2x0_i2c.h        31 Jul 2011 02:41:43 -0000
@@ -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_ */


Home | Main Index | Thread Index | Old Index