1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-02-04 17:15:50 +00:00

Replace three copies of the host controller reset sequence that

differ in their details with calls to a new function, ehci_hcreset(),
that performs the reset.

The original sequences either had no delay or a 1ms delay between
telling the controller to stop and asserting the controller reset
bit.  One instance of the original reset sequence waited for the
controller to indicate that its reset was complete before continuing,
but the other two immediately let the subsequent code execute.  The
latter is a problem on some hardware, because a read of the HCCPARAMS
register returns an incorrect value while the reset is in progress,
which triggers an infinite loop in ehci_pci_givecontroller(), which
hangs the system on shutdown.

The reset sequence in ehci_hcreset() starts with the most complete
instance from the original code, which contains a loop to wait for
the controller to indicate that its reset is complete.   This appears
to be the correct thing to do according to "Enhanced Host Controller
Interface Specification for Universal Serial Bus" revision 1.0,
section 2.3.1.  Add another loop to wait for the controller to
indicate that it has stopped before setting the HCRESET bit.  This
is required by the section 2.3.1 in the specification, which says
that setting HCRESET before the controller has halted "will result
in undefined behaviour".

Reviewed by:	imp (previous patch version without the extra wait loop)
Tested by:	se  (previous patch version without the extra wait loop)
Approved by:	re (bmah)
MFC after:	1 week
This commit is contained in:
Don Lewis 2007-08-12 18:45:24 +00:00
parent 9136384dc2
commit 4d54b88811
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=171812

View File

@ -311,6 +311,39 @@ static struct usbd_pipe_methods ehci_device_isoc_methods = {
ehci_device_isoc_done,
};
static usbd_status
ehci_hcreset(ehci_softc_t *sc)
{
u_int32_t hcr;
u_int i;
EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */
for (i = 0; i < 100; i++) {
usb_delay_ms(&sc->sc_bus, 1);
hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
if (hcr)
break;
}
if (!hcr)
/*
* Fall through and try reset anyway even though
* Table 2-9 in the EHCI spec says this will result
* in undefined behavior.
*/
printf("%s: stop timeout\n",
device_get_nameunit(sc->sc_bus.bdev));
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
for (i = 0; i < 100; i++) {
usb_delay_ms(&sc->sc_bus, 1);
hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET;
if (!hcr)
return (USBD_NORMAL_COMPLETION);
}
printf("%s: reset timeout\n", device_get_nameunit(sc->sc_bus.bdev));
return (USBD_IOERROR);
}
usbd_status
ehci_init(ehci_softc_t *sc)
{
@ -365,20 +398,9 @@ ehci_init(ehci_softc_t *sc)
/* Reset the controller */
DPRINTF(("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev)));
EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */
usb_delay_ms(&sc->sc_bus, 1);
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
for (i = 0; i < 100; i++) {
usb_delay_ms(&sc->sc_bus, 1);
hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET;
if (!hcr)
break;
}
if (hcr) {
printf("%s: reset timeout\n",
device_get_nameunit(sc->sc_bus.bdev));
return (USBD_IOERROR);
}
err = ehci_hcreset(sc);
if (err != USBD_NORMAL_COMPLETION)
return (err);
/* frame list size at default, read back what we got and use that */
switch (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD))) {
@ -927,8 +949,7 @@ ehci_detach(struct ehci_softc *sc, int flags)
sc->sc_dying = 1;
EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
EOWRITE4(sc, EHCI_USBCMD, 0);
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
(void) ehci_hcreset(sc);
callout_stop(&sc->sc_tmo_intrlist);
callout_stop(&sc->sc_tmo_pcd);
@ -1090,8 +1111,7 @@ ehci_shutdown(void *v)
ehci_softc_t *sc = v;
DPRINTF(("ehci_shutdown: stopping the HC\n"));
EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */
EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
(void) ehci_hcreset(sc);
}
usbd_status