The USB controller on the Freescale i.MX application processors has a
dual role port that can act as device (OTG) or as host.  Since we
don't have any device mode support in our kernel, we try to switch the
port into host mode.  Unfortunately that never worked.  Here's why:

The USBMODE register that controls the mode, gets reset whenever we
reset the controller, i.e. when ehci_reset() gets called.  Since
ehci_init() calls ehci_reset() we lose the host mode setting almost
immediately, and nothing works.

The diff below adds code to save and restore the USBMODE register if
the EHCIF_USBMODE flag is set, and sets this flag in the imxehci(4)
driver.  I also moved the defines for this register to ehcireg.h.
While this isn't a standard EHCI register, it seems that it is present
on many dual-role USB 2.0 controller.  Allegedly it is part of a
design that ended up being licensed to many other companies.

Note that the register offset changed from 0xa8 to 0x68.  This is not
a bug.  Imade the offset relative to the offset given by the
EHCI_CAPLENGTH register.  It is now accessed using EOREAD4/EOWRITE4
instead of EREAD4/EWRITE4.

ok?


Index: arch/armv7/imx/imxehci.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/imx/imxehci.c,v
retrieving revision 1.17
diff -u -p -r1.17 imxehci.c
--- arch/armv7/imx/imxehci.c    13 Aug 2016 11:08:58 -0000      1.17
+++ arch/armv7/imx/imxehci.c    1 Oct 2016 13:26:02 -0000
@@ -58,9 +58,6 @@
 /* ehci */
 #define USB_EHCI_OFFSET                        0x100
 
-#define EHCI_USBMODE                   0xa8
-
-#define EHCI_USBMODE_HOST              (3 << 0)
 #define EHCI_PS_PTS_UTMI_MASK  ((1 << 25) | (3 << 30))
 
 /* usb non-core */
@@ -144,6 +141,7 @@ imxehci_attach(struct device *parent, st
        sc->sc.iot = faa->fa_iot;
        sc->sc.sc_bus.dmatag = faa->fa_dmat;
        sc->sc.sc_size = faa->fa_reg[0].size - USB_EHCI_OFFSET;
+       sc->sc.sc_flags = EHCIF_USBMODE;
 
        /* Map I/O space */
        if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
@@ -247,8 +245,8 @@ imxehci_attach(struct device *parent, st
            USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3);
 
        /* set host mode */
-       EWRITE4(&sc->sc, EHCI_USBMODE,
-           EREAD4(&sc->sc, EHCI_USBMODE) | EHCI_USBMODE_HOST);
+       EOWRITE4(&sc->sc, EHCI_USBMODE,
+           EOREAD4(&sc->sc, EHCI_USBMODE) | EHCI_USBMODE_CM_HOST);
 
        /* set to UTMI mode */
        EOWRITE4(&sc->sc, EHCI_PORTSC(1),
Index: dev/usb/ehci.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/ehci.c,v
retrieving revision 1.193
diff -u -p -r1.193 ehci.c
--- dev/usb/ehci.c      15 Sep 2016 02:00:17 -0000      1.193
+++ dev/usb/ehci.c      1 Oct 2016 13:26:02 -0000
@@ -1114,7 +1114,7 @@ ehci_activate(struct device *self, int a
 usbd_status
 ehci_reset(struct ehci_softc *sc)
 {
-       u_int32_t hcr;
+       u_int32_t hcr, usbmode;
        int i;
 
        EOWRITE4(sc, EHCI_USBCMD, 0);   /* Halt controller */
@@ -1128,6 +1128,9 @@ ehci_reset(struct ehci_softc *sc)
        if (!hcr)
                printf("%s: halt timeout\n", sc->sc_bus.bdev.dv_xname);
 
+       if (sc->sc_flags & EHCIF_USBMODE)
+               usbmode = EOREAD4(sc, EHCI_USBMODE);
+
        EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
        for (i = 0; i < 100; i++) {
                usb_delay_ms(&sc->sc_bus, 1);
@@ -1140,6 +1143,9 @@ ehci_reset(struct ehci_softc *sc)
                printf("%s: reset timeout\n", sc->sc_bus.bdev.dv_xname);
                return (USBD_IOERROR);
        }
+
+       if (sc->sc_flags & EHCIF_USBMODE)
+               EOWRITE4(sc, EHCI_USBMODE, usbmode);
 
        return (USBD_NORMAL_COMPLETION);
 }
Index: dev/usb/ehcireg.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/ehcireg.h,v
retrieving revision 1.20
diff -u -p -r1.20 ehcireg.h
--- dev/usb/ehcireg.h   10 Apr 2015 13:56:42 -0000      1.20
+++ dev/usb/ehcireg.h   1 Oct 2016 13:26:02 -0000
@@ -162,6 +162,13 @@
 
 #define EHCI_PORT_RESET_COMPLETE 2 /* ms */
 
+/* Nonstandard register to set controller mode. */
+#define EHCI_USBMODE           0x68
+#define  EHCI_USBMODE_CM_M     0x00000003
+#define  EHCI_USBMODE_CM_IDLE  0x00000000
+#define  EHCI_USBMODE_CM_DEVICE        0x00000002
+#define  EHCI_USBMODE_CM_HOST  0x00000003      
+
 #define EHCI_FLALIGN_ALIGN     0x1000
 
 /* No data structure may cross a page boundary. */
Index: dev/usb/ehcivar.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/ehcivar.h,v
retrieving revision 1.36
diff -u -p -r1.36 ehcivar.h
--- dev/usb/ehcivar.h   2 Nov 2015 14:55:41 -0000       1.36
+++ dev/usb/ehcivar.h   1 Oct 2016 13:26:02 -0000
@@ -130,6 +130,7 @@ struct ehci_softc {
        int sc_flags;                   /* misc flags */
 #define EHCIF_DROPPED_INTR_WORKAROUND  0x01
 #define EHCIF_PCB_INTR                 0x02
+#define EHCIF_USBMODE                  0x04
 
        char sc_vendor[16];             /* vendor string for root hub */
        int sc_id_vendor;               /* vendor ID for root hub */

Reply via email to