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 */