diff options
| -rw-r--r-- | drivers/usb/host/xhci-hub.c | 188 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-mem.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-pci.c | 2 | ||||
| -rw-r--r-- | drivers/usb/host/xhci.h | 9 |
4 files changed, 200 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 8163f17e7043..7f2f63cb6c53 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
| @@ -24,6 +24,10 @@ | |||
| 24 | 24 | ||
| 25 | #include "xhci.h" | 25 | #include "xhci.h" |
| 26 | 26 | ||
| 27 | #define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E) | ||
| 28 | #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ | ||
| 29 | PORT_RC | PORT_PLC | PORT_PE) | ||
| 30 | |||
| 27 | static void xhci_hub_descriptor(struct xhci_hcd *xhci, | 31 | static void xhci_hub_descriptor(struct xhci_hcd *xhci, |
| 28 | struct usb_hub_descriptor *desc) | 32 | struct usb_hub_descriptor *desc) |
| 29 | { | 33 | { |
| @@ -560,3 +564,187 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
| 560 | spin_unlock_irqrestore(&xhci->lock, flags); | 564 | spin_unlock_irqrestore(&xhci->lock, flags); |
| 561 | return status ? retval : 0; | 565 | return status ? retval : 0; |
| 562 | } | 566 | } |
| 567 | |||
| 568 | #ifdef CONFIG_PM | ||
| 569 | |||
| 570 | int xhci_bus_suspend(struct usb_hcd *hcd) | ||
| 571 | { | ||
| 572 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | ||
| 573 | int port; | ||
| 574 | unsigned long flags; | ||
| 575 | |||
| 576 | xhci_dbg(xhci, "suspend root hub\n"); | ||
| 577 | |||
| 578 | spin_lock_irqsave(&xhci->lock, flags); | ||
| 579 | |||
| 580 | if (hcd->self.root_hub->do_remote_wakeup) { | ||
| 581 | port = HCS_MAX_PORTS(xhci->hcs_params1); | ||
| 582 | while (port--) { | ||
| 583 | if (xhci->resume_done[port] != 0) { | ||
| 584 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
| 585 | xhci_dbg(xhci, "suspend failed because " | ||
| 586 | "port %d is resuming\n", | ||
| 587 | port + 1); | ||
| 588 | return -EBUSY; | ||
| 589 | } | ||
| 590 | } | ||
| 591 | } | ||
| 592 | |||
| 593 | port = HCS_MAX_PORTS(xhci->hcs_params1); | ||
| 594 | xhci->bus_suspended = 0; | ||
| 595 | while (port--) { | ||
| 596 | /* suspend the port if the port is not suspended */ | ||
| 597 | u32 __iomem *addr; | ||
| 598 | u32 t1, t2; | ||
| 599 | int slot_id; | ||
| 600 | |||
| 601 | addr = &xhci->op_regs->port_status_base + | ||
| 602 | NUM_PORT_REGS * (port & 0xff); | ||
| 603 | t1 = xhci_readl(xhci, addr); | ||
| 604 | t2 = xhci_port_state_to_neutral(t1); | ||
| 605 | |||
| 606 | if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { | ||
| 607 | xhci_dbg(xhci, "port %d not suspended\n", port); | ||
| 608 | slot_id = xhci_find_slot_id_by_port(xhci, port + 1); | ||
| 609 | if (slot_id) { | ||
| 610 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
| 611 | xhci_stop_device(xhci, slot_id, 1); | ||
| 612 | spin_lock_irqsave(&xhci->lock, flags); | ||
| 613 | } | ||
| 614 | t2 &= ~PORT_PLS_MASK; | ||
| 615 | t2 |= PORT_LINK_STROBE | XDEV_U3; | ||
| 616 | set_bit(port, &xhci->bus_suspended); | ||
| 617 | } | ||
| 618 | if (hcd->self.root_hub->do_remote_wakeup) { | ||
| 619 | if (t1 & PORT_CONNECT) { | ||
| 620 | t2 |= PORT_WKOC_E | PORT_WKDISC_E; | ||
| 621 | t2 &= ~PORT_WKCONN_E; | ||
| 622 | } else { | ||
| 623 | t2 |= PORT_WKOC_E | PORT_WKCONN_E; | ||
| 624 | t2 &= ~PORT_WKDISC_E; | ||
| 625 | } | ||
| 626 | } else | ||
| 627 | t2 &= ~PORT_WAKE_BITS; | ||
| 628 | |||
| 629 | t1 = xhci_port_state_to_neutral(t1); | ||
| 630 | if (t1 != t2) | ||
| 631 | xhci_writel(xhci, t2, addr); | ||
| 632 | |||
| 633 | if (DEV_HIGHSPEED(t1)) { | ||
| 634 | /* enable remote wake up for USB 2.0 */ | ||
| 635 | u32 __iomem *addr; | ||
| 636 | u32 tmp; | ||
| 637 | |||
| 638 | addr = &xhci->op_regs->port_power_base + | ||
| 639 | NUM_PORT_REGS * (port & 0xff); | ||
| 640 | tmp = xhci_readl(xhci, addr); | ||
| 641 | tmp |= PORT_RWE; | ||
| 642 | xhci_writel(xhci, tmp, addr); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | hcd->state = HC_STATE_SUSPENDED; | ||
| 646 | xhci->next_statechange = jiffies + msecs_to_jiffies(10); | ||
| 647 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
| 648 | return 0; | ||
| 649 | } | ||
| 650 | |||
| 651 | int xhci_bus_resume(struct usb_hcd *hcd) | ||
| 652 | { | ||
| 653 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | ||
| 654 | int port; | ||
| 655 | u32 temp; | ||
| 656 | unsigned long flags; | ||
| 657 | |||
| 658 | xhci_dbg(xhci, "resume root hub\n"); | ||
| 659 | |||
| 660 | if (time_before(jiffies, xhci->next_statechange)) | ||
| 661 | msleep(5); | ||
| 662 | |||
| 663 | spin_lock_irqsave(&xhci->lock, flags); | ||
| 664 | if (!HCD_HW_ACCESSIBLE(hcd)) { | ||
| 665 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
| 666 | return -ESHUTDOWN; | ||
| 667 | } | ||
| 668 | |||
| 669 | /* delay the irqs */ | ||
| 670 | temp = xhci_readl(xhci, &xhci->op_regs->command); | ||
| 671 | temp &= ~CMD_EIE; | ||
| 672 | xhci_writel(xhci, temp, &xhci->op_regs->command); | ||
| 673 | |||
| 674 | port = HCS_MAX_PORTS(xhci->hcs_params1); | ||
| 675 | while (port--) { | ||
| 676 | /* Check whether need resume ports. If needed | ||
| 677 | resume port and disable remote wakeup */ | ||
| 678 | u32 __iomem *addr; | ||
| 679 | u32 temp; | ||
| 680 | int slot_id; | ||
| 681 | |||
| 682 | addr = &xhci->op_regs->port_status_base + | ||
| 683 | NUM_PORT_REGS * (port & 0xff); | ||
| 684 | temp = xhci_readl(xhci, addr); | ||
| 685 | if (DEV_SUPERSPEED(temp)) | ||
| 686 | temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); | ||
| 687 | else | ||
| 688 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); | ||
| 689 | if (test_bit(port, &xhci->bus_suspended) && | ||
| 690 | (temp & PORT_PLS_MASK)) { | ||
| 691 | if (DEV_SUPERSPEED(temp)) { | ||
| 692 | temp = xhci_port_state_to_neutral(temp); | ||
| 693 | temp &= ~PORT_PLS_MASK; | ||
| 694 | temp |= PORT_LINK_STROBE | XDEV_U0; | ||
| 695 | xhci_writel(xhci, temp, addr); | ||
| 696 | } else { | ||
| 697 | temp = xhci_port_state_to_neutral(temp); | ||
| 698 | temp &= ~PORT_PLS_MASK; | ||
| 699 | temp |= PORT_LINK_STROBE | XDEV_RESUME; | ||
| 700 | xhci_writel(xhci, temp, addr); | ||
| 701 | |||
| 702 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
| 703 | msleep(20); | ||
| 704 | spin_lock_irqsave(&xhci->lock, flags); | ||
| 705 | |||
| 706 | temp = xhci_readl(xhci, addr); | ||
| 707 | temp = xhci_port_state_to_neutral(temp); | ||
| 708 | temp &= ~PORT_PLS_MASK; | ||
| 709 | temp |= PORT_LINK_STROBE | XDEV_U0; | ||
| 710 | xhci_writel(xhci, temp, addr); | ||
| 711 | } | ||
| 712 | slot_id = xhci_find_slot_id_by_port(xhci, port + 1); | ||
| 713 | if (slot_id) | ||
| 714 | xhci_ring_device(xhci, slot_id); | ||
| 715 | } else | ||
| 716 | xhci_writel(xhci, temp, addr); | ||
| 717 | |||
| 718 | if (DEV_HIGHSPEED(temp)) { | ||
| 719 | /* disable remote wake up for USB 2.0 */ | ||
| 720 | u32 __iomem *addr; | ||
| 721 | u32 tmp; | ||
| 722 | |||
| 723 | addr = &xhci->op_regs->port_power_base + | ||
| 724 | NUM_PORT_REGS * (port & 0xff); | ||
| 725 | tmp = xhci_readl(xhci, addr); | ||
| 726 | tmp &= ~PORT_RWE; | ||
| 727 | xhci_writel(xhci, tmp, addr); | ||
| 728 | } | ||
| 729 | } | ||
| 730 | |||
| 731 | (void) xhci_readl(xhci, &xhci->op_regs->command); | ||
| 732 | |||
| 733 | xhci->next_statechange = jiffies + msecs_to_jiffies(5); | ||
| 734 | hcd->state = HC_STATE_RUNNING; | ||
| 735 | /* re-enable irqs */ | ||
| 736 | temp = xhci_readl(xhci, &xhci->op_regs->command); | ||
| 737 | temp |= CMD_EIE; | ||
| 738 | xhci_writel(xhci, temp, &xhci->op_regs->command); | ||
| 739 | temp = xhci_readl(xhci, &xhci->op_regs->command); | ||
| 740 | |||
| 741 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
| 742 | return 0; | ||
| 743 | } | ||
| 744 | |||
| 745 | #else | ||
| 746 | |||
| 747 | #define xhci_bus_suspend NULL | ||
| 748 | #define xhci_bus_resume NULL | ||
| 749 | |||
| 750 | #endif | ||
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index fd888bc0422b..202770676da3 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c | |||
| @@ -1445,6 +1445,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) | |||
| 1445 | scratchpad_free(xhci); | 1445 | scratchpad_free(xhci); |
| 1446 | xhci->page_size = 0; | 1446 | xhci->page_size = 0; |
| 1447 | xhci->page_shift = 0; | 1447 | xhci->page_shift = 0; |
| 1448 | xhci->bus_suspended = 0; | ||
| 1448 | } | 1449 | } |
| 1449 | 1450 | ||
| 1450 | static int xhci_test_trb_in_td(struct xhci_hcd *xhci, | 1451 | static int xhci_test_trb_in_td(struct xhci_hcd *xhci, |
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index aefc3496376a..3865f8c6f647 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c | |||
| @@ -162,6 +162,8 @@ static const struct hc_driver xhci_pci_hc_driver = { | |||
| 162 | /* Root hub support */ | 162 | /* Root hub support */ |
| 163 | .hub_control = xhci_hub_control, | 163 | .hub_control = xhci_hub_control, |
| 164 | .hub_status_data = xhci_hub_status_data, | 164 | .hub_status_data = xhci_hub_status_data, |
| 165 | .bus_suspend = xhci_bus_suspend, | ||
| 166 | .bus_resume = xhci_bus_resume, | ||
| 165 | }; | 167 | }; |
| 166 | 168 | ||
| 167 | /*-------------------------------------------------------------------------*/ | 169 | /*-------------------------------------------------------------------------*/ |
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ca4a923dc810..196e21fb36ff 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h | |||
| @@ -357,6 +357,8 @@ struct xhci_op_regs { | |||
| 357 | #define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8) | 357 | #define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8) |
| 358 | /* Bits 24:31 for port testing */ | 358 | /* Bits 24:31 for port testing */ |
| 359 | 359 | ||
| 360 | /* USB2 Protocol PORTSPMSC */ | ||
| 361 | #define PORT_RWE (1 << 0x3) | ||
| 360 | 362 | ||
| 361 | /** | 363 | /** |
| 362 | * struct xhci_intr_reg - Interrupt Register Set | 364 | * struct xhci_intr_reg - Interrupt Register Set |
| @@ -1191,6 +1193,11 @@ struct xhci_hcd { | |||
| 1191 | #endif | 1193 | #endif |
| 1192 | /* Host controller watchdog timer structures */ | 1194 | /* Host controller watchdog timer structures */ |
| 1193 | unsigned int xhc_state; | 1195 | unsigned int xhc_state; |
| 1196 | |||
| 1197 | unsigned long bus_suspended; | ||
| 1198 | unsigned long next_statechange; | ||
| 1199 | |||
| 1200 | u32 command; | ||
| 1194 | /* Host controller is dying - not responding to commands. "I'm not dead yet!" | 1201 | /* Host controller is dying - not responding to commands. "I'm not dead yet!" |
| 1195 | * | 1202 | * |
| 1196 | * xHC interrupts have been disabled and a watchdog timer will (or has already) | 1203 | * xHC interrupts have been disabled and a watchdog timer will (or has already) |
| @@ -1460,6 +1467,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, | |||
| 1460 | int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, | 1467 | int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, |
| 1461 | char *buf, u16 wLength); | 1468 | char *buf, u16 wLength); |
| 1462 | int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); | 1469 | int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); |
| 1470 | int xhci_bus_suspend(struct usb_hcd *hcd); | ||
| 1471 | int xhci_bus_resume(struct usb_hcd *hcd); | ||
| 1463 | u32 xhci_port_state_to_neutral(u32 state); | 1472 | u32 xhci_port_state_to_neutral(u32 state); |
| 1464 | int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port); | 1473 | int xhci_find_slot_id_by_port(struct xhci_hcd *xhci, u16 port); |
| 1465 | void xhci_ring_device(struct xhci_hcd *xhci, int slot_id); | 1474 | void xhci_ring_device(struct xhci_hcd *xhci, int slot_id); |
