diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
| -rw-r--r-- | drivers/usb/host/xhci-hub.c | 44 |
1 files changed, 39 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 14b48b261e06..8163f17e7043 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
| @@ -123,7 +123,7 @@ static unsigned int xhci_port_speed(unsigned int port_status) | |||
| 123 | * writing a 0 clears the bit and writing a 1 sets the bit (RWS). | 123 | * writing a 0 clears the bit and writing a 1 sets the bit (RWS). |
| 124 | * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect. | 124 | * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect. |
| 125 | */ | 125 | */ |
| 126 | static u32 xhci_port_state_to_neutral(u32 state) | 126 | u32 xhci_port_state_to_neutral(u32 state) |
| 127 | { | 127 | { |
| 128 | /* Save read-only status and port state */ | 128 | /* Save read-only status and port state */ |
| 129 | return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); | 129 | return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); |
| @@ -132,7 +132,7 @@ static u32 xhci_port_state_to_neutral(u32 state) | |||
| 132 | /* | 132 | /* |
| 133 | * find slot id based on port number. | 133 | * find slot id based on port number. |
| 134 | */ | 134 | */ |
| 135 | static int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port) | 135 | int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port) |
| 136 | { | 136 | { |
| 137 | int slot_id; | 137 | int slot_id; |
| 138 | int i; | 138 | int i; |
| @@ -210,7 +210,7 @@ command_cleanup: | |||
| 210 | /* | 210 | /* |
| 211 | * Ring device, it rings the all doorbells unconditionally. | 211 | * Ring device, it rings the all doorbells unconditionally. |
| 212 | */ | 212 | */ |
| 213 | static void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) | 213 | void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) |
| 214 | { | 214 | { |
| 215 | int i; | 215 | int i; |
| 216 | 216 | ||
| @@ -276,7 +276,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
| 276 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 276 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
| 277 | int ports; | 277 | int ports; |
| 278 | unsigned long flags; | 278 | unsigned long flags; |
| 279 | u32 temp, status; | 279 | u32 temp, temp1, status; |
| 280 | int retval = 0; | 280 | int retval = 0; |
| 281 | u32 __iomem *addr; | 281 | u32 __iomem *addr; |
| 282 | int slot_id; | 282 | int slot_id; |
| @@ -315,6 +315,34 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
| 315 | if ((temp & PORT_PLS_MASK) == XDEV_U3 | 315 | if ((temp & PORT_PLS_MASK) == XDEV_U3 |
| 316 | && (temp & PORT_POWER)) | 316 | && (temp & PORT_POWER)) |
| 317 | status |= 1 << USB_PORT_FEAT_SUSPEND; | 317 | status |= 1 << USB_PORT_FEAT_SUSPEND; |
| 318 | if ((temp & PORT_PLS_MASK) == XDEV_RESUME) { | ||
| 319 | if ((temp & PORT_RESET) || !(temp & PORT_PE)) | ||
| 320 | goto error; | ||
| 321 | if (!DEV_SUPERSPEED(temp) && time_after_eq(jiffies, | ||
| 322 | xhci->resume_done[wIndex])) { | ||
| 323 | xhci_dbg(xhci, "Resume USB2 port %d\n", | ||
| 324 | wIndex + 1); | ||
| 325 | xhci->resume_done[wIndex] = 0; | ||
| 326 | temp1 = xhci_port_state_to_neutral(temp); | ||
| 327 | temp1 &= ~PORT_PLS_MASK; | ||
| 328 | temp1 |= PORT_LINK_STROBE | XDEV_U0; | ||
| 329 | xhci_writel(xhci, temp1, addr); | ||
| 330 | |||
| 331 | xhci_dbg(xhci, "set port %d resume\n", | ||
| 332 | wIndex + 1); | ||
| 333 | slot_id = xhci_find_slot_id_by_port(xhci, | ||
| 334 | wIndex + 1); | ||
| 335 | if (!slot_id) { | ||
| 336 | xhci_dbg(xhci, "slot_id is zero\n"); | ||
| 337 | goto error; | ||
| 338 | } | ||
| 339 | xhci_ring_device(xhci, slot_id); | ||
| 340 | xhci->port_c_suspend[wIndex >> 5] |= | ||
| 341 | 1 << (wIndex & 31); | ||
| 342 | xhci->suspended_ports[wIndex >> 5] &= | ||
| 343 | ~(1 << (wIndex & 31)); | ||
| 344 | } | ||
| 345 | } | ||
| 318 | if ((temp & PORT_PLS_MASK) == XDEV_U0 | 346 | if ((temp & PORT_PLS_MASK) == XDEV_U0 |
| 319 | && (temp & PORT_POWER) | 347 | && (temp & PORT_POWER) |
| 320 | && (xhci->suspended_ports[wIndex >> 5] & | 348 | && (xhci->suspended_ports[wIndex >> 5] & |
| @@ -500,6 +528,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
| 500 | { | 528 | { |
| 501 | unsigned long flags; | 529 | unsigned long flags; |
| 502 | u32 temp, status; | 530 | u32 temp, status; |
| 531 | u32 mask; | ||
| 503 | int i, retval; | 532 | int i, retval; |
| 504 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 533 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
| 505 | int ports; | 534 | int ports; |
| @@ -512,13 +541,18 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
| 512 | memset(buf, 0, retval); | 541 | memset(buf, 0, retval); |
| 513 | status = 0; | 542 | status = 0; |
| 514 | 543 | ||
| 544 | mask = PORT_CSC | PORT_PEC | PORT_OCC; | ||
| 545 | |||
| 515 | spin_lock_irqsave(&xhci->lock, flags); | 546 | spin_lock_irqsave(&xhci->lock, flags); |
| 516 | /* For each port, did anything change? If so, set that bit in buf. */ | 547 | /* For each port, did anything change? If so, set that bit in buf. */ |
| 517 | for (i = 0; i < ports; i++) { | 548 | for (i = 0; i < ports; i++) { |
| 518 | addr = &xhci->op_regs->port_status_base + | 549 | addr = &xhci->op_regs->port_status_base + |
| 519 | NUM_PORT_REGS*i; | 550 | NUM_PORT_REGS*i; |
| 520 | temp = xhci_readl(xhci, addr); | 551 | temp = xhci_readl(xhci, addr); |
| 521 | if (temp & (PORT_CSC | PORT_PEC | PORT_OCC)) { | 552 | if ((temp & mask) != 0 || |
| 553 | (xhci->port_c_suspend[i >> 5] & 1 << (i & 31)) || | ||
| 554 | (xhci->resume_done[i] && time_after_eq( | ||
| 555 | jiffies, xhci->resume_done[i]))) { | ||
| 522 | buf[(i + 1) / 8] |= 1 << (i + 1) % 8; | 556 | buf[(i + 1) / 8] |= 1 << (i + 1) % 8; |
| 523 | status = 1; | 557 | status = 1; |
| 524 | } | 558 | } |
