[PATCH] USB: EHCI port tweaks

One change may improve some S1 or S3 resume cases, and the other
seems mostly to explain some strange state "lsusb" would show.
Two fixes:

  - On resume, don't think about resuming any unpowered port, or
    resetting any port with OWNER set to the OHCI/UHCI companion.
    This will make some S1 and S3 resume scenarios work better.

  - PORT_CSC was not being cleared correctly in ehci_hub_status_data.
    This was visible at least through current versions of "lsusb",
    and might have caused some other hub related strangeness.

    The fix addresses all three write-to-clear bits, using the same
    approach that UHCI happens to use:  a mask of bits that are
    cleared in most writes to that port status register.

Original patch seems to have been from from William.Morrow@amd.com
and this version (from David) finishes the write-to-clear changes.

Signed-off-by: Jordan Crouse <jordan.crouse@amd.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 36cc1f2..18d3f22 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -54,7 +54,7 @@
 	/* suspend any active/unsuspended ports, maybe allow wakeup */
 	while (port--) {
 		u32 __iomem	*reg = &ehci->regs->port_status [port];
-		u32		t1 = readl (reg);
+		u32		t1 = readl (reg) & ~PORT_RWC_BITS;
 		u32		t2 = t1;
 
 		if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
@@ -115,7 +115,8 @@
 	i = HCS_N_PORTS (ehci->hcs_params);
 	while (i--) {
 		temp = readl (&ehci->regs->port_status [i]);
-		temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
+		temp &= ~(PORT_RWC_BITS
+			| PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E);
 		if (temp & PORT_SUSPEND) {
 			ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
 			temp |= PORT_RESUME;
@@ -128,7 +129,7 @@
 		temp = readl (&ehci->regs->port_status [i]);
 		if ((temp & PORT_SUSPEND) == 0)
 			continue;
-		temp &= ~PORT_RESUME;
+		temp &= ~(PORT_RWC_BITS | PORT_RESUME);
 		writel (temp, &ehci->regs->port_status [i]);
 		ehci_vdbg (ehci, "resumed port %d\n", i + 1);
 	}
@@ -191,6 +192,7 @@
 
 		// what happens if HCS_N_CC(params) == 0 ?
 		port_status |= PORT_OWNER;
+		port_status &= ~PORT_RWC_BITS;
 		writel (port_status, &ehci->regs->port_status [index]);
 
 	} else
@@ -233,7 +235,8 @@
 		if (temp & PORT_OWNER) {
 			/* don't report this in GetPortStatus */
 			if (temp & PORT_CSC) {
-				temp &= ~PORT_CSC;
+				temp &= ~PORT_RWC_BITS;
+				temp |= PORT_CSC;
 				writel (temp, &ehci->regs->port_status [i]);
 			}
 			continue;
@@ -343,7 +346,7 @@
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_C_ENABLE:
-			writel (temp | PORT_PEC,
+			writel((temp & ~PORT_RWC_BITS) | PORT_PEC,
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_SUSPEND:
@@ -353,7 +356,8 @@
 				if ((temp & PORT_PE) == 0)
 					goto error;
 				/* resume signaling for 20 msec */
-				writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME,
+				temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
+				writel (temp | PORT_RESUME,
 					&ehci->regs->port_status [wIndex]);
 				ehci->reset_done [wIndex] = jiffies
 						+ msecs_to_jiffies (20);
@@ -364,15 +368,15 @@
 			break;
 		case USB_PORT_FEAT_POWER:
 			if (HCS_PPC (ehci->hcs_params))
-				writel (temp & ~PORT_POWER,
+				writel (temp & ~(PORT_RWC_BITS | PORT_POWER),
 					&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_C_CONNECTION:
-			writel (temp | PORT_CSC,
+			writel((temp & ~PORT_RWC_BITS) | PORT_CSC,
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_C_OVER_CURRENT:
-			writel (temp | PORT_OCC,
+			writel((temp & ~PORT_RWC_BITS) | PORT_OCC,
 				&ehci->regs->port_status [wIndex]);
 			break;
 		case USB_PORT_FEAT_C_RESET:
@@ -416,7 +420,7 @@
 
 			/* stop resume signaling */
 			temp = readl (&ehci->regs->port_status [wIndex]);
-			writel (temp & ~PORT_RESUME,
+			writel (temp & ~(PORT_RWC_BITS | PORT_RESUME),
 				&ehci->regs->port_status [wIndex]);
 			retval = handshake (
 					&ehci->regs->port_status [wIndex],
@@ -437,7 +441,7 @@
 			ehci->reset_done [wIndex] = 0;
 
 			/* force reset to complete */
-			writel (temp & ~PORT_RESET,
+			writel (temp & ~(PORT_RWC_BITS | PORT_RESET),
 					&ehci->regs->port_status [wIndex]);
 			/* REVISIT:  some hardware needs 550+ usec to clear
 			 * this bit; seems too long to spin routinely...
@@ -500,6 +504,7 @@
 		if (temp & PORT_OWNER)
 			break;
 
+		temp &= ~PORT_RWC_BITS;
 		switch (wValue) {
 		case USB_PORT_FEAT_SUSPEND:
 			if ((temp & PORT_PE) == 0