[PATCH] USB UHCI: improved reset handling

This patch improves the strategy uhci-hcd uses for performing controller
resets and checking whether they are needed.

	The HCRESET command doesn't affect the Suspend, Resume,
	or Reset bits in the port status & control registers, so
	the driver must clear them by itself.  This means the
	code to figure out how many ports there are has to be moved
	to an earlier spot in the driver.

	The R/WC bits in the USBLEGSUP register can be set by the
	hardware even in the absence of BIOS meddling with legacy
	support features.  Hence it's not a good idea to check them
	while trying to determine whether the BIOS has altered the
	controller's state.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Alan Stern 2005-04-22 14:39:12 -04:00 committed by Greg Kroah-Hartman
parent 6c1b445c22
commit c074b416b9
1 changed files with 40 additions and 29 deletions

View File

@ -112,6 +112,8 @@ static inline void restart_timer(struct uhci_hcd *uhci)
*/
static void reset_hc(struct uhci_hcd *uhci)
{
int port;
/* Turn off PIRQ enable and SMI enable. (This also turns off the
* BIOS's USB Legacy Support.) Turn off all the R/WC bits too.
*/
@ -135,6 +137,13 @@ static void reset_hc(struct uhci_hcd *uhci)
outw(0, uhci->io_addr + USBINTR);
outw(0, uhci->io_addr + USBCMD);
/* HCRESET doesn't affect the Suspend, Reset, and Resume Detect
* bits in the port status and control registers.
* We have to clear them by hand.
*/
for (port = 0; port < uhci->rh_numports; ++port)
outw(0, uhci->io_addr + USBPORTSC1 + (port * 2));
uhci->port_c_suspend = uhci->suspended_ports =
uhci->resuming_ports = 0;
uhci->rh_state = UHCI_RH_RESET;
@ -166,14 +175,14 @@ static void check_and_reset_hc(struct uhci_hcd *uhci)
* When restarting a suspended controller, we expect all the
* settings to be the same as we left them:
*
* PIRQ and SMI disabled, no R/WC bits set in USBLEGSUP;
* PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
* Controller is stopped and configured with EGSM set;
* No interrupts enabled except possibly Resume Detect.
*
* If any of these conditions are violated we do a complete reset.
*/
pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup);
if (legsup & ~USBLEGSUP_RO) {
if (legsup & ~(USBLEGSUP_RO | USBLEGSUP_RWC)) {
dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n",
__FUNCTION__, legsup);
goto reset_needed;
@ -478,9 +487,37 @@ static void release_uhci(struct uhci_hcd *uhci)
static int uhci_reset(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned io_size = (unsigned) hcd->rsrc_len;
int port;
uhci->io_addr = (unsigned long) hcd->rsrc_start;
/* The UHCI spec says devices must have 2 ports, and goes on to say
* they may have more but gives no way to determine how many there
* are. However, according to the UHCI spec, Bit 7 of the port
* status and control register is always set to 1. So we try to
* use this to our advantage.
*/
for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) {
unsigned int portstatus;
portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2));
if (!(portstatus & 0x0080))
break;
}
if (debug)
dev_info(uhci_dev(uhci), "detected %d ports\n", port);
/* Anything less than 2 or greater than 7 is weird,
* so we'll ignore it.
*/
if (port < 2 || port > UHCI_RH_MAXCHILD) {
dev_info(uhci_dev(uhci), "port count misdetected? "
"forcing to 2 ports\n");
port = 2;
}
uhci->rh_numports = port;
/* Kick BIOS off this hardware and reset if the controller
* isn't already safely quiescent.
*/
@ -508,13 +545,11 @@ static int uhci_start(struct usb_hcd *hcd)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i, port;
unsigned io_size;
int i;
dma_addr_t dma_handle;
struct usb_device *udev;
struct dentry *dentry;
io_size = (unsigned) hcd->rsrc_len;
hcd->uses_new_polling = 1;
if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM))
hcd->can_wakeup = 1; /* Assume it supports PME# */
@ -578,30 +613,6 @@ static int uhci_start(struct usb_hcd *hcd)
/* Initialize the root hub */
/* UHCI specs says devices must have 2 ports, but goes on to say */
/* they may have more but give no way to determine how many they */
/* have. However, according to the UHCI spec, Bit 7 is always set */
/* to 1. So we try to use this to our advantage */
for (port = 0; port < (io_size - 0x10) / 2; port++) {
unsigned int portstatus;
portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
if (!(portstatus & 0x0080))
break;
}
if (debug)
dev_info(uhci_dev(uhci), "detected %d ports\n", port);
/* This is experimental so anything less than 2 or greater than 8 is */
/* something weird and we'll ignore it */
if (port < 2 || port > UHCI_RH_MAXCHILD) {
dev_info(uhci_dev(uhci), "port count misdetected? "
"forcing to 2 ports\n");
port = 2;
}
uhci->rh_numports = port;
udev = usb_alloc_dev(NULL, &hcd->self, 0);
if (!udev) {
dev_err(uhci_dev(uhci), "unable to allocate root hub\n");