EHCI: Make core reset and port speed reading more generic.

Use driver settable callbacks for handling of:
- core post reset
- reading actual port speed

Typically, OTG enabled EHCI cores wants setting of USBMODE register,
but this register is not defined in EHCI specification and different
cores can have it on different offset.

Also, for cores with TT extension, actual port speed must be determinable.
But again, EHCI specification not covers this so this patch provides
function for two most common variant of speed bits layout.

Reviewed by: hselasky
Differential Revision: https://reviews.freebsd.org/D5088
This commit is contained in:
Michal Meloun 2016-01-28 14:11:59 +00:00
parent 60ba692c78
commit cdf4ec6873
9 changed files with 170 additions and 46 deletions

View File

@ -169,6 +169,18 @@ static devclass_t ehci_devclass;
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 @@ vybrid_ehci_attach(device_t dev)
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) {

View File

@ -138,6 +138,18 @@ __FBSDID("$FreeBSD$");
#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 @@ zy7_ehci_attach(device_t dev)
}
/* 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,

View File

@ -189,24 +189,8 @@ ehci_reset(ehci_softc_t *sc)
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 @@ struct usb_hub_descriptor ehci_hubd =
.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 @@ ehci_roothub_exec(struct usb_device *udev,
}
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;
}

View File

@ -337,11 +337,8 @@ typedef struct ehci_softc {
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 @@ typedef struct ehci_softc {
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_reset(ehci_softc_t *sc);
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_ */

View File

@ -86,6 +86,19 @@ static void ehci_bs_w_2(bus_space_tag_t tag, bus_space_handle_t, bus_size_t, uin
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 @@ ehci_ixp_attach(device_t self)
}
/*
* 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);

View File

@ -105,6 +105,18 @@ static struct ofw_compat_data compat_data[] = {
{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 @@ mv_ehci_attach(device_t self)
* 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);

View File

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

View File

@ -61,6 +61,10 @@ __FBSDID("$FreeBSD$");
#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 @@ static device_detach_t ar71xx_ehci_detach;
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 @@ ar71xx_ehci_attach(device_t self)
* 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 @@ ar71xx_ehci_attach(device_t self)
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 */

View File

@ -69,6 +69,17 @@ struct ps3_ehci_softc {
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 @@ ehci_ps3_attach(device_t dev)
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);