提交 c074b416 编写于 作者: A Alan Stern 提交者: Greg Kroah-Hartman

[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: NAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 6c1b445c
...@@ -112,6 +112,8 @@ static inline void restart_timer(struct uhci_hcd *uhci) ...@@ -112,6 +112,8 @@ static inline void restart_timer(struct uhci_hcd *uhci)
*/ */
static void reset_hc(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 /* 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. * 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) ...@@ -135,6 +137,13 @@ static void reset_hc(struct uhci_hcd *uhci)
outw(0, uhci->io_addr + USBINTR); outw(0, uhci->io_addr + USBINTR);
outw(0, uhci->io_addr + USBCMD); 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->port_c_suspend = uhci->suspended_ports =
uhci->resuming_ports = 0; uhci->resuming_ports = 0;
uhci->rh_state = UHCI_RH_RESET; uhci->rh_state = UHCI_RH_RESET;
...@@ -166,14 +175,14 @@ static void check_and_reset_hc(struct uhci_hcd *uhci) ...@@ -166,14 +175,14 @@ static void check_and_reset_hc(struct uhci_hcd *uhci)
* When restarting a suspended controller, we expect all the * When restarting a suspended controller, we expect all the
* settings to be the same as we left them: * 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; * Controller is stopped and configured with EGSM set;
* No interrupts enabled except possibly Resume Detect. * No interrupts enabled except possibly Resume Detect.
* *
* If any of these conditions are violated we do a complete reset. * If any of these conditions are violated we do a complete reset.
*/ */
pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup); 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", dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n",
__FUNCTION__, legsup); __FUNCTION__, legsup);
goto reset_needed; goto reset_needed;
...@@ -478,9 +487,37 @@ static void release_uhci(struct uhci_hcd *uhci) ...@@ -478,9 +487,37 @@ static void release_uhci(struct uhci_hcd *uhci)
static int uhci_reset(struct usb_hcd *hcd) static int uhci_reset(struct usb_hcd *hcd)
{ {
struct uhci_hcd *uhci = hcd_to_uhci(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; 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 /* Kick BIOS off this hardware and reset if the controller
* isn't already safely quiescent. * isn't already safely quiescent.
*/ */
...@@ -508,13 +545,11 @@ static int uhci_start(struct usb_hcd *hcd) ...@@ -508,13 +545,11 @@ static int uhci_start(struct usb_hcd *hcd)
{ {
struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY; int retval = -EBUSY;
int i, port; int i;
unsigned io_size;
dma_addr_t dma_handle; dma_addr_t dma_handle;
struct usb_device *udev; struct usb_device *udev;
struct dentry *dentry; struct dentry *dentry;
io_size = (unsigned) hcd->rsrc_len;
hcd->uses_new_polling = 1; hcd->uses_new_polling = 1;
if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM)) if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM))
hcd->can_wakeup = 1; /* Assume it supports PME# */ hcd->can_wakeup = 1; /* Assume it supports PME# */
...@@ -578,30 +613,6 @@ static int uhci_start(struct usb_hcd *hcd) ...@@ -578,30 +613,6 @@ static int uhci_start(struct usb_hcd *hcd)
/* Initialize the root hub */ /* 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); udev = usb_alloc_dev(NULL, &hcd->self, 0);
if (!udev) { if (!udev) {
dev_err(uhci_dev(uhci), "unable to allocate root hub\n"); dev_err(uhci_dev(uhci), "unable to allocate root hub\n");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册