diff options
Diffstat (limited to 'drivers/usb/host/xhci.c')
-rw-r--r-- | drivers/usb/host/xhci.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 54cb762d15c8..b0649a4bd315 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c | |||
@@ -3539,6 +3539,187 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) | |||
3539 | return 0; | 3539 | return 0; |
3540 | } | 3540 | } |
3541 | 3541 | ||
3542 | #ifdef CONFIG_USB_SUSPEND | ||
3543 | |||
3544 | /* BESL to HIRD Encoding array for USB2 LPM */ | ||
3545 | static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000, | ||
3546 | 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000}; | ||
3547 | |||
3548 | /* Calculate HIRD/BESL for USB2 PORTPMSC*/ | ||
3549 | static int xhci_calculate_hird_besl(int u2del, bool use_besl) | ||
3550 | { | ||
3551 | int hird; | ||
3552 | |||
3553 | if (use_besl) { | ||
3554 | for (hird = 0; hird < 16; hird++) { | ||
3555 | if (xhci_besl_encoding[hird] >= u2del) | ||
3556 | break; | ||
3557 | } | ||
3558 | } else { | ||
3559 | if (u2del <= 50) | ||
3560 | hird = 0; | ||
3561 | else | ||
3562 | hird = (u2del - 51) / 75 + 1; | ||
3563 | |||
3564 | if (hird > 15) | ||
3565 | hird = 15; | ||
3566 | } | ||
3567 | |||
3568 | return hird; | ||
3569 | } | ||
3570 | |||
3571 | static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, | ||
3572 | struct usb_device *udev) | ||
3573 | { | ||
3574 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | ||
3575 | struct dev_info *dev_info; | ||
3576 | __le32 __iomem **port_array; | ||
3577 | __le32 __iomem *addr, *pm_addr; | ||
3578 | u32 temp, dev_id; | ||
3579 | unsigned int port_num; | ||
3580 | unsigned long flags; | ||
3581 | int u2del, hird; | ||
3582 | int ret; | ||
3583 | |||
3584 | if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || | ||
3585 | !udev->lpm_capable) | ||
3586 | return -EINVAL; | ||
3587 | |||
3588 | /* we only support lpm for non-hub device connected to root hub yet */ | ||
3589 | if (!udev->parent || udev->parent->parent || | ||
3590 | udev->descriptor.bDeviceClass == USB_CLASS_HUB) | ||
3591 | return -EINVAL; | ||
3592 | |||
3593 | spin_lock_irqsave(&xhci->lock, flags); | ||
3594 | |||
3595 | /* Look for devices in lpm_failed_devs list */ | ||
3596 | dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 | | ||
3597 | le16_to_cpu(udev->descriptor.idProduct); | ||
3598 | list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) { | ||
3599 | if (dev_info->dev_id == dev_id) { | ||
3600 | ret = -EINVAL; | ||
3601 | goto finish; | ||
3602 | } | ||
3603 | } | ||
3604 | |||
3605 | port_array = xhci->usb2_ports; | ||
3606 | port_num = udev->portnum - 1; | ||
3607 | |||
3608 | if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) { | ||
3609 | xhci_dbg(xhci, "invalid port number %d\n", udev->portnum); | ||
3610 | ret = -EINVAL; | ||
3611 | goto finish; | ||
3612 | } | ||
3613 | |||
3614 | /* | ||
3615 | * Test USB 2.0 software LPM. | ||
3616 | * FIXME: some xHCI 1.0 hosts may implement a new register to set up | ||
3617 | * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1 | ||
3618 | * in the June 2011 errata release. | ||
3619 | */ | ||
3620 | xhci_dbg(xhci, "test port %d software LPM\n", port_num); | ||
3621 | /* | ||
3622 | * Set L1 Device Slot and HIRD/BESL. | ||
3623 | * Check device's USB 2.0 extension descriptor to determine whether | ||
3624 | * HIRD or BESL shoule be used. See USB2.0 LPM errata. | ||
3625 | */ | ||
3626 | pm_addr = port_array[port_num] + 1; | ||
3627 | u2del = HCS_U2_LATENCY(xhci->hcs_params3); | ||
3628 | if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) | ||
3629 | hird = xhci_calculate_hird_besl(u2del, 1); | ||
3630 | else | ||
3631 | hird = xhci_calculate_hird_besl(u2del, 0); | ||
3632 | |||
3633 | temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird); | ||
3634 | xhci_writel(xhci, temp, pm_addr); | ||
3635 | |||
3636 | /* Set port link state to U2(L1) */ | ||
3637 | addr = port_array[port_num]; | ||
3638 | xhci_set_link_state(xhci, port_array, port_num, XDEV_U2); | ||
3639 | |||
3640 | /* wait for ACK */ | ||
3641 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
3642 | msleep(10); | ||
3643 | spin_lock_irqsave(&xhci->lock, flags); | ||
3644 | |||
3645 | /* Check L1 Status */ | ||
3646 | ret = handshake(xhci, pm_addr, PORT_L1S_MASK, PORT_L1S_SUCCESS, 125); | ||
3647 | if (ret != -ETIMEDOUT) { | ||
3648 | /* enter L1 successfully */ | ||
3649 | temp = xhci_readl(xhci, addr); | ||
3650 | xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n", | ||
3651 | port_num, temp); | ||
3652 | ret = 0; | ||
3653 | } else { | ||
3654 | temp = xhci_readl(xhci, pm_addr); | ||
3655 | xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n", | ||
3656 | port_num, temp & PORT_L1S_MASK); | ||
3657 | ret = -EINVAL; | ||
3658 | } | ||
3659 | |||
3660 | /* Resume the port */ | ||
3661 | xhci_set_link_state(xhci, port_array, port_num, XDEV_U0); | ||
3662 | |||
3663 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
3664 | msleep(10); | ||
3665 | spin_lock_irqsave(&xhci->lock, flags); | ||
3666 | |||
3667 | /* Clear PLC */ | ||
3668 | xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC); | ||
3669 | |||
3670 | /* Check PORTSC to make sure the device is in the right state */ | ||
3671 | if (!ret) { | ||
3672 | temp = xhci_readl(xhci, addr); | ||
3673 | xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp); | ||
3674 | if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) || | ||
3675 | (temp & PORT_PLS_MASK) != XDEV_U0) { | ||
3676 | xhci_dbg(xhci, "port L1 resume fail\n"); | ||
3677 | ret = -EINVAL; | ||
3678 | } | ||
3679 | } | ||
3680 | |||
3681 | if (ret) { | ||
3682 | /* Insert dev to lpm_failed_devs list */ | ||
3683 | xhci_warn(xhci, "device LPM test failed, may disconnect and " | ||
3684 | "re-enumerate\n"); | ||
3685 | dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC); | ||
3686 | if (!dev_info) { | ||
3687 | ret = -ENOMEM; | ||
3688 | goto finish; | ||
3689 | } | ||
3690 | dev_info->dev_id = dev_id; | ||
3691 | INIT_LIST_HEAD(&dev_info->list); | ||
3692 | list_add(&dev_info->list, &xhci->lpm_failed_devs); | ||
3693 | } else { | ||
3694 | xhci_ring_device(xhci, udev->slot_id); | ||
3695 | } | ||
3696 | |||
3697 | finish: | ||
3698 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
3699 | return ret; | ||
3700 | } | ||
3701 | |||
3702 | int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) | ||
3703 | { | ||
3704 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | ||
3705 | int ret; | ||
3706 | |||
3707 | ret = xhci_usb2_software_lpm_test(hcd, udev); | ||
3708 | if (!ret) | ||
3709 | xhci_dbg(xhci, "software LPM test succeed\n"); | ||
3710 | |||
3711 | return 0; | ||
3712 | } | ||
3713 | |||
3714 | #else | ||
3715 | |||
3716 | int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) | ||
3717 | { | ||
3718 | return 0; | ||
3719 | } | ||
3720 | |||
3721 | #endif /* CONFIG_USB_SUSPEND */ | ||
3722 | |||
3542 | /* Once a hub descriptor is fetched for a device, we need to update the xHC's | 3723 | /* Once a hub descriptor is fetched for a device, we need to update the xHC's |
3543 | * internal data structures for the device. | 3724 | * internal data structures for the device. |
3544 | */ | 3725 | */ |