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:
parent
60ba692c78
commit
cdf4ec6873
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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_ */
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user