Index: head/sys/arm/freescale/vybrid/vf_ehci.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_ehci.c +++ head/sys/arm/freescale/vybrid/vf_ehci.c @@ -169,6 +169,18 @@ DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); MODULE_DEPEND(ehci, usb, 1, 1, 1); +static void +vybrid_ehci_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Force HOST mode */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); + usbmode &= ~EHCI_UM_CM; + usbmode |= EHCI_UM_CM_HOST; + EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); +} + /* * Public methods */ @@ -343,8 +355,10 @@ reg |= 0x3; bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, 0xA8, reg); - /* Set flags */ - sc->sc_flags |= EHCI_SCFLG_SETMODE | EHCI_SCFLG_NORESTERM; + /* Set flags and callbacks*/ + sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; + sc->sc_vendor_post_reset = vybrid_ehci_post_reset; + sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc; err = ehci_init(sc); if (!err) { Index: head/sys/arm/xilinx/zy7_ehci.c =================================================================== --- head/sys/arm/xilinx/zy7_ehci.c +++ head/sys/arm/xilinx/zy7_ehci.c @@ -138,6 +138,18 @@ #define EHCI_REG_OFFSET ZY7_USB_CAPLENGTH_HCIVERSION #define EHCI_REG_SIZE 0x100 +static void +zy7_ehci_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Force HOST mode */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); + usbmode &= ~EHCI_UM_CM; + usbmode |= EHCI_UM_CM_HOST; + EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); +} + static int zy7_phy_config(device_t dev, bus_space_tag_t io_tag, bus_space_handle_t bsh) { @@ -275,8 +287,9 @@ } /* Customization. */ - sc->sc_flags |= EHCI_SCFLG_SETMODE | EHCI_SCFLG_TT | - EHCI_SCFLG_NORESTERM; + sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; + sc->sc_vendor_post_reset = zy7_ehci_post_reset; + sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc; /* Modify FIFO burst threshold from 2 to 8. */ bus_space_write_4(sc->sc_io_tag, bsh, Index: head/sys/dev/usb/controller/ehci.h =================================================================== --- head/sys/dev/usb/controller/ehci.h +++ head/sys/dev/usb/controller/ehci.h @@ -337,11 +337,8 @@ uint16_t sc_intr_stat[EHCI_VIRTUAL_FRAMELIST_COUNT]; uint16_t sc_id_vendor; /* vendor ID for root hub */ uint16_t sc_flags; /* chip specific flags */ -#define EHCI_SCFLG_SETMODE 0x0001 /* set bridge mode again after init */ -#define EHCI_SCFLG_FORCESPEED 0x0002 /* force speed */ #define EHCI_SCFLG_NORESTERM 0x0004 /* don't terminate reset sequence */ #define EHCI_SCFLG_BIGEDESC 0x0008 /* big-endian byte order descriptors */ -#define EHCI_SCFLG_BIGEMMIO 0x0010 /* big-endian byte order MMIO */ #define EHCI_SCFLG_TT 0x0020 /* transaction translator present */ #define EHCI_SCFLG_LOSTINTRBUG 0x0040 /* workaround for VIA / ATI chipsets */ #define EHCI_SCFLG_IAADBUG 0x0080 /* workaround for nVidia chipsets */ @@ -358,6 +355,10 @@ char sc_vendor[16]; /* vendor string for root hub */ + void (*sc_vendor_post_reset)(struct ehci_softc *sc); + uint16_t (*sc_vendor_get_port_speed)(struct ehci_softc *sc, + uint16_t index); + } ehci_softc_t; #define EREAD1(sc, a) bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (a)) @@ -446,5 +447,7 @@ usb_error_t ehci_init(ehci_softc_t *sc); void ehci_detach(struct ehci_softc *sc); void ehci_interrupt(ehci_softc_t *sc); +uint16_t ehci_get_port_speed_portsc(struct ehci_softc *sc, uint16_t index); +uint16_t ehci_get_port_speed_hostc(struct ehci_softc *sc, uint16_t index); #endif /* _EHCI_H_ */ Index: head/sys/dev/usb/controller/ehci.c =================================================================== --- head/sys/dev/usb/controller/ehci.c +++ head/sys/dev/usb/controller/ehci.c @@ -189,24 +189,8 @@ usb_pause_mtx(NULL, hz / 128); hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET; if (!hcr) { - if (sc->sc_flags & (EHCI_SCFLG_SETMODE | EHCI_SCFLG_BIGEMMIO)) { - /* - * Force USBMODE as requested. Controllers - * may have multiple operating modes. - */ - uint32_t usbmode = EOREAD4(sc, EHCI_USBMODE); - if (sc->sc_flags & EHCI_SCFLG_SETMODE) { - usbmode = (usbmode &~ EHCI_UM_CM) | EHCI_UM_CM_HOST; - device_printf(sc->sc_bus.bdev, - "set host controller mode\n"); - } - if (sc->sc_flags & EHCI_SCFLG_BIGEMMIO) { - usbmode = (usbmode &~ EHCI_UM_ES) | EHCI_UM_ES_BE; - device_printf(sc->sc_bus.bdev, - "set big-endian mode\n"); - } - EOWRITE4(sc, EHCI_USBMODE, usbmode); - } + if (sc->sc_vendor_post_reset != NULL) + sc->sc_vendor_post_reset(sc); return (0); } } @@ -3066,6 +3050,36 @@ .bDescriptorType = UDESC_HUB, }; +uint16_t +ehci_get_port_speed_portsc(struct ehci_softc *sc, uint16_t index) +{ + uint32_t v; + + v = EOREAD4(sc, EHCI_PORTSC(index)); + v = (v >> EHCI_PORTSC_PSPD_SHIFT) & EHCI_PORTSC_PSPD_MASK; + + if (v == EHCI_PORT_SPEED_HIGH) + return (UPS_HIGH_SPEED); + if (v == EHCI_PORT_SPEED_LOW) + return (UPS_LOW_SPEED); + return (0); +} + +uint16_t +ehci_get_port_speed_hostc(struct ehci_softc *sc, uint16_t index) +{ + uint32_t v; + + v = EOREAD4(sc, EHCI_HOSTC(index)); + v = (v >> EHCI_HOSTC_PSPD_SHIFT) & EHCI_HOSTC_PSPD_MASK; + + if (v == EHCI_PORT_SPEED_HIGH) + return (UPS_HIGH_SPEED); + if (v == EHCI_PORT_SPEED_LOW) + return (UPS_LOW_SPEED); + return (0); +} + static void ehci_disown(ehci_softc_t *sc, uint16_t index, uint8_t lowspeed) { @@ -3330,13 +3344,15 @@ } v = EOREAD4(sc, EHCI_PORTSC(index)); DPRINTFN(9, "port status=0x%04x\n", v); - if (sc->sc_flags & (EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_TT)) { - if ((v & 0xc000000) == 0x8000000) + if (sc->sc_flags & EHCI_SCFLG_TT) { + if (sc->sc_vendor_get_port_speed != NULL) { + i = sc->sc_vendor_get_port_speed(sc, index); + } else { + device_printf(sc->sc_bus.bdev, + "EHCI_SCFLG_TT quirk is set but " + "sc_vendor_get_hub_speed() is NULL\n"); i = UPS_HIGH_SPEED; - else if ((v & 0xc000000) == 0x4000000) - i = UPS_LOW_SPEED; - else - i = 0; + } } else { i = UPS_HIGH_SPEED; } Index: head/sys/dev/usb/controller/ehci_ixp4xx.c =================================================================== --- head/sys/dev/usb/controller/ehci_ixp4xx.c +++ head/sys/dev/usb/controller/ehci_ixp4xx.c @@ -86,6 +86,19 @@ static uint32_t ehci_bs_r_4(bus_space_tag_t tag, bus_space_handle_t, bus_size_t); static void ehci_bs_w_4(bus_space_tag_t tag, bus_space_handle_t, bus_size_t, uint32_t); +static void +ehci_ixp_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Force HOST mode, select big-endian mode */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); + usbmode &= ~EHCI_UM_CM; + usbmode |= EHCI_UM_CM_HOST; + usbmode |= EHCI_UM_ES_BE; + EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); +} + static int ehci_ixp_probe(device_t self) { @@ -173,20 +186,21 @@ } /* - * Arrange to force Host mode, select big-endian byte alignment, - * and arrange to not terminate reset operations (the adapter - * will ignore it if we do but might as well save a reg write). - * Also, the controller has an embedded Transaction Translator - * which means port speed must be read from the Port Status - * register following a port enable. + * Select big-endian byte alignment and arrange to not terminate + * reset operations (the adapter will ignore it if we do but might + * as well save a reg write). Also, the controller has an embedded + * Transaction Translator which means port speed must be read from + * the Port Status register following a port enable. */ sc->sc_flags |= EHCI_SCFLG_TT - | EHCI_SCFLG_SETMODE | EHCI_SCFLG_BIGEDESC - | EHCI_SCFLG_BIGEMMIO | EHCI_SCFLG_NORESTERM ; + /* Setup callbacks. */ + sc->sc_vendor_post_reset = ehci_ixp_post_reset; + sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc; + err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); Index: head/sys/dev/usb/controller/ehci_mv.c =================================================================== --- head/sys/dev/usb/controller/ehci_mv.c +++ head/sys/dev/usb/controller/ehci_mv.c @@ -105,6 +105,18 @@ {NULL, false} }; +static void +mv_ehci_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Force HOST mode */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); + usbmode &= ~EHCI_UM_CM; + usbmode |= EHCI_UM_CM_HOST; + EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); +} + static int mv_ehci_probe(device_t self) { @@ -226,13 +238,13 @@ * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for * details. */ - sc->sc_flags |= EHCI_SCFLG_SETMODE; + sc->sc_vendor_post_reset = mv_ehci_post_reset; if (bootverbose) device_printf(self, "5.24 GL USB-2 workaround enabled\n"); /* XXX all MV chips need it? */ - sc->sc_flags |= EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_NORESTERM; - + sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; + sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc; err = ehci_init(sc); if (!err) { err = device_probe_and_attach(sc->sc_bus.bdev); Index: head/sys/dev/usb/controller/ehcireg.h =================================================================== --- head/sys/dev/usb/controller/ehcireg.h +++ head/sys/dev/usb/controller/ehcireg.h @@ -157,7 +157,17 @@ #define EHCI_PS_CS 0x00000001 /* RO connect status */ #define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) -#define EHCI_USBMODE 0x68 /* RW USB Device mode register */ +#define EHCI_PORT_RESET_COMPLETE 2 /* ms */ + +/* + * Registers not covered by EHCI specification + * + * + * EHCI_USBMODE register offset is different for cores with LPM support, + * bits are equal + */ +#define EHCI_USBMODE_NOLPM 0x68 /* RW USB Device mode reg (no LPM) */ +#define EHCI_USBMODE_LPM 0xA8 /* RW USB Device mode reg (LPM) */ #define EHCI_UM_CM 0x00000003 /* R/WO Controller Mode */ #define EHCI_UM_CM_IDLE 0x0 /* Idle */ #define EHCI_UM_CM_HOST 0x3 /* Host Controller */ @@ -166,6 +176,18 @@ #define EHCI_UM_ES_BE 0x4 /* Big-endian byte alignment */ #define EHCI_UM_SDIS 0x00000010 /* R/WO Stream Disable Mode */ -#define EHCI_PORT_RESET_COMPLETE 2 /* ms */ - +/* + * Actual port speed bits depends on EHCI_HOSTC(n) registers presence, + * speed encoding is equal + */ +#define EHCI_HOSTC(n) (0x80+(4*(n))) /* RO, RW Host mode control reg */ +#define EHCI_HOSTC_PSPD_SHIFT 25 +#define EHCI_HOSTC_PSPD_MASK 0x3 + +#define EHCI_PORTSC_PSPD_SHIFT 26 +#define EHCI_PORTSC_PSPD_MASK 0x3 + +#define EHCI_PORT_SPEED_FULL 0 +#define EHCI_PORT_SPEED_LOW 1 +#define EHCI_PORT_SPEED_HIGH 2 #endif /* _EHCIREG_H_ */ Index: head/sys/mips/atheros/ar71xx_ehci.c =================================================================== --- head/sys/mips/atheros/ar71xx_ehci.c +++ head/sys/mips/atheros/ar71xx_ehci.c @@ -61,6 +61,10 @@ #define EHCI_HC_DEVSTR "AR71XX Integrated USB 2.0 controller" +#define EHCI_USBMODE 0x68 /* USB Device mode register */ +#define EHCI_UM_CM 0x00000003 /* R/WO Controller Mode */ +#define EHCI_UM_CM_HOST 0x3 /* Host Controller */ + struct ar71xx_ehci_softc { ehci_softc_t base; /* storage for EHCI code */ }; @@ -71,6 +75,18 @@ bs_r_1_proto(reversed); bs_w_1_proto(reversed); +static void +ar71xx_ehci_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Force HOST mode */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); + usbmode &= ~EHCI_UM_CM; + usbmode |= EHCI_UM_CM_HOST; + EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); +} + static int ar71xx_ehci_probe(device_t self) { @@ -161,7 +177,8 @@ * which means port speed must be read from the Port Status * register following a port enable. */ - sc->sc_flags = EHCI_SCFLG_SETMODE; + sc->sc_flags = 0; + sc->sc_vendor_post_reset = ar71xx_ehci_post_reset; switch (ar71xx_soc) { case AR71XX_SOC_AR7241: @@ -178,6 +195,8 @@ case AR71XX_SOC_QCA9556: case AR71XX_SOC_QCA9558: sc->sc_flags |= EHCI_SCFLG_TT | EHCI_SCFLG_NORESTERM; + sc->sc_vendor_get_port_speed = + ehci_get_port_speed_portsc; break; default: /* fallthrough */ Index: head/sys/powerpc/ps3/ehci_ps3.c =================================================================== --- head/sys/powerpc/ps3/ehci_ps3.c +++ head/sys/powerpc/ps3/ehci_ps3.c @@ -69,6 +69,17 @@ struct bus_space tag; }; +static void +ehci_ps3_post_reset(struct ehci_softc *ehci_softc) +{ + uint32_t usbmode; + + /* Select big-endian mode */ + usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM); + usbmode |= EHCI_UM_ES_BE; + EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode); +} + static int ehci_ps3_probe(device_t dev) { @@ -135,7 +146,7 @@ goto error; } - sc->sc_flags |= EHCI_SCFLG_BIGEMMIO; + sc->sc_vendor_post_reset = ehci_ps3_post_reset; err = ehci_init(sc); if (err) { device_printf(dev, "USB init failed err=%d\n", err);