On Sat, Oct 01, 2016 at 03:48:35PM +0200, Mark Kettenis wrote:
> 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?
The top usb port on the cubox now works with this. ok jsg@
>
>
> 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 */
>