mirror of https://gitee.com/openkylin/linux.git
xhci: Link PM and bug fixes for 3.5.
Hi Greg, Here's the final Link Power Management patches, along with a couple of bug fixes that have been sitting in my queue. I've fixed all the comments that Alan and Andiry had on the Link PM patches, so I think they're ready to go. Sarah Sharp -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJPttDmAAoJEBMGWMLi1Gc5IJgP/jPNhCjHWr+YqsmcvJdlHxkE 1TrMCScAhAk7OkAaZ+4xpFsD4G3bN5+SS4U6x3hEdhqgJIWbtDazOBctfVKezJQ1 uywnJNW08aW6GWYCTtoDVHY/ofLjpRnSRC21RXW4zKW8U4dHp1UcoCfkLRpqbb1C /GYuLb03Ipl25+wDDDQAmFIs5IlEGHfE7a1r27I43MHIHvg6iw7rzgJPI5HKEIH2 3Usw4+DL1aMAlU1YKRwiwVoOVnmujzlb4a0ONhuBUdHKCGbfi6/Tce6lH0ElXavH mJgv5S9Ro2H/rMtYFq1ZoR9NxeizqtKT2Dv0KOsWiFwZBOfhjvmDel62N+7w1akt EWgahjASunwS56LBMg5ni0eK0VOt1kHQl3yeF6HmSdRftljN5Fke2r4BDA83sfl/ VggQPPoLNdHou9LRO+XLOKe3sW5+nIH/b+SJUvWYktCSN9wMPCH5wbXNp8WIh/7p MvD2Kj7iqgF2PPzqpYGIwTHNwh5YdmnyYeCkr/IaoCBmUV0qD8HConsRN6USVIRI aBCZR4APWlaTzhu/eP5inuuGwNBGTVi3aJKe/WgN2aG2TsYnAzKhmvbELpYaheZj /Fe2itLuejVSjVsQI6KfhneCZyM45M7Ip8FTcEE1/z4R3cTEc5vE1adjFGWIzHRo bHPQ0JRfpamuJkxaSTOY =ixwg -----END PGP SIGNATURE----- Merge tag 'for-usb-next-2012-05-18' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-next xhci: Link PM and bug fixes for 3.5. Hi Greg, Here's the final Link Power Management patches, along with a couple of bug fixes that have been sitting in my queue. I've fixed all the comments that Alan and Andiry had on the Link PM patches, so I think they're ready to go. Sarah Sharp
This commit is contained in:
commit
f06b9f3ced
|
@ -439,6 +439,7 @@ static struct usb_driver ath3k_driver = {
|
|||
.probe = ath3k_probe,
|
||||
.disconnect = ath3k_disconnect,
|
||||
.id_table = ath3k_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(ath3k_driver);
|
||||
|
|
|
@ -279,6 +279,7 @@ static struct usb_driver bcm203x_driver = {
|
|||
.probe = bcm203x_probe,
|
||||
.disconnect = bcm203x_disconnect,
|
||||
.id_table = bcm203x_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(bcm203x_driver);
|
||||
|
|
|
@ -749,6 +749,7 @@ static struct usb_driver bfusb_driver = {
|
|||
.probe = bfusb_probe,
|
||||
.disconnect = bfusb_disconnect,
|
||||
.id_table = bfusb_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(bfusb_driver);
|
||||
|
|
|
@ -508,6 +508,7 @@ static struct usb_driver bpa10x_driver = {
|
|||
.probe = bpa10x_probe,
|
||||
.disconnect = bpa10x_disconnect,
|
||||
.id_table = bpa10x_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(bpa10x_driver);
|
||||
|
|
|
@ -1218,6 +1218,7 @@ static struct usb_driver btusb_driver = {
|
|||
#endif
|
||||
.id_table = btusb_table,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(btusb_driver);
|
||||
|
|
|
@ -148,6 +148,7 @@ static struct usb_driver gigaset_usb_driver = {
|
|||
.reset_resume = gigaset_post_reset,
|
||||
.pre_reset = gigaset_pre_reset,
|
||||
.post_reset = gigaset_post_reset,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
/* get message text for usb_submit_urb return code
|
||||
|
|
|
@ -124,6 +124,7 @@ static struct usb_driver gigaset_usb_driver = {
|
|||
.reset_resume = gigaset_resume,
|
||||
.pre_reset = gigaset_pre_reset,
|
||||
.post_reset = gigaset_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
struct usb_cardstate {
|
||||
|
|
|
@ -2151,6 +2151,7 @@ static struct usb_driver hfcsusb_drv = {
|
|||
.id_table = hfcsusb_idtab,
|
||||
.probe = hfcsusb_probe,
|
||||
.disconnect = hfcsusb_disconnect,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(hfcsusb_drv);
|
||||
|
|
|
@ -1568,6 +1568,7 @@ static struct usb_driver hfc_drv = {
|
|||
.id_table = hfcusb_idtab,
|
||||
.probe = hfc_usb_probe,
|
||||
.disconnect = hfc_usb_disconnect,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
static void __exit
|
||||
|
|
|
@ -182,6 +182,7 @@ static struct usb_driver st5481_usb_driver = {
|
|||
.probe = probe_st5481,
|
||||
.disconnect = disconnect_st5481,
|
||||
.id_table = st5481_ids,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
static int __init st5481_usb_init(void)
|
||||
|
|
|
@ -1647,6 +1647,7 @@ static struct usb_driver asix_driver = {
|
|||
.resume = usbnet_resume,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(asix_driver);
|
||||
|
|
|
@ -952,6 +952,7 @@ static struct usb_driver catc_driver = {
|
|||
.probe = catc_probe,
|
||||
.disconnect = catc_disconnect,
|
||||
.id_table = catc_id_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(catc_driver);
|
||||
|
|
|
@ -457,6 +457,7 @@ static struct usb_driver usbpn_driver = {
|
|||
.probe = usbpn_probe,
|
||||
.disconnect = usbpn_disconnect,
|
||||
.id_table = usbpn_ids,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(usbpn_driver);
|
||||
|
|
|
@ -368,6 +368,7 @@ static struct usb_driver eem_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(eem_driver);
|
||||
|
|
|
@ -633,6 +633,7 @@ static struct usb_driver cdc_driver = {
|
|||
.resume = usbnet_resume,
|
||||
.reset_resume = usbnet_resume,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(cdc_driver);
|
||||
|
|
|
@ -1212,6 +1212,7 @@ static struct usb_driver cdc_ncm_driver = {
|
|||
.resume = usbnet_resume,
|
||||
.reset_resume = usbnet_resume,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
static const struct ethtool_ops cdc_ncm_ethtool_ops = {
|
||||
|
|
|
@ -336,6 +336,7 @@ static struct usb_driver cdc_subset_driver = {
|
|||
.resume = usbnet_resume,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.id_table = products,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(cdc_subset_driver);
|
||||
|
|
|
@ -327,6 +327,7 @@ static struct usb_driver cx82310_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(cx82310_driver);
|
||||
|
|
|
@ -670,6 +670,7 @@ static struct usb_driver dm9601_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(dm9601_driver);
|
||||
|
|
|
@ -225,6 +225,7 @@ static struct usb_driver gl620a_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(gl620a_driver);
|
||||
|
|
|
@ -3291,6 +3291,7 @@ static struct usb_driver hso_driver = {
|
|||
.resume = hso_resume,
|
||||
.reset_resume = hso_resume,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
static int __init hso_init(void)
|
||||
|
|
|
@ -236,6 +236,7 @@ static struct usb_driver int51x1_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(int51x1_driver);
|
||||
|
|
|
@ -554,6 +554,7 @@ static struct usb_driver ipheth_driver = {
|
|||
.probe = ipheth_probe,
|
||||
.disconnect = ipheth_disconnect,
|
||||
.id_table = ipheth_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(ipheth_driver);
|
||||
|
|
|
@ -372,7 +372,8 @@ static struct usb_driver kalmia_driver = {
|
|||
.probe = usbnet_probe,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(kalmia_driver);
|
||||
|
|
|
@ -179,6 +179,7 @@ static struct usb_driver kaweth_driver = {
|
|||
.resume = kaweth_resume,
|
||||
.id_table = usb_klsi_table,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
typedef __u8 eth_addr_t[6];
|
||||
|
|
|
@ -344,6 +344,7 @@ static struct usb_driver lg_vl600_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(lg_vl600_driver);
|
||||
|
|
|
@ -690,6 +690,7 @@ static struct usb_driver mcs7830_driver = {
|
|||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.reset_resume = mcs7830_reset_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(mcs7830_driver);
|
||||
|
|
|
@ -587,6 +587,7 @@ static struct usb_driver net1080_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(net1080_driver);
|
||||
|
|
|
@ -1489,6 +1489,7 @@ static struct usb_driver pegasus_driver = {
|
|||
.id_table = pegasus_ids,
|
||||
.suspend = pegasus_suspend,
|
||||
.resume = pegasus_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
static void __init parse_id(char *id)
|
||||
|
|
|
@ -152,6 +152,7 @@ static struct usb_driver plusb_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(plusb_driver);
|
||||
|
|
|
@ -525,6 +525,7 @@ static struct usb_driver qmi_wwan_driver = {
|
|||
.resume = qmi_wwan_resume,
|
||||
.reset_resume = qmi_wwan_resume,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
static int __init qmi_wwan_init(void)
|
||||
|
|
|
@ -633,6 +633,7 @@ static struct usb_driver rndis_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(rndis_driver);
|
||||
|
|
|
@ -948,7 +948,8 @@ static struct usb_driver rtl8150_driver = {
|
|||
.disconnect = rtl8150_disconnect,
|
||||
.id_table = rtl8150_table,
|
||||
.suspend = rtl8150_suspend,
|
||||
.resume = rtl8150_resume
|
||||
.resume = rtl8150_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(rtl8150_driver);
|
||||
|
|
|
@ -982,6 +982,7 @@ static struct usb_driver sierra_net_driver = {
|
|||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.no_dynamic_id = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(sierra_net_driver);
|
||||
|
|
|
@ -1250,6 +1250,7 @@ static struct usb_driver smsc75xx_driver = {
|
|||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(smsc75xx_driver);
|
||||
|
|
|
@ -1297,6 +1297,7 @@ static struct usb_driver smsc95xx_driver = {
|
|||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disconnect = usbnet_disconnect,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(smsc95xx_driver);
|
||||
|
|
|
@ -377,6 +377,7 @@ static struct usb_driver zaurus_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(zaurus_driver);
|
||||
|
|
|
@ -2486,6 +2486,7 @@ static struct usb_driver at76_driver = {
|
|||
.probe = at76_probe,
|
||||
.disconnect = at76_disconnect,
|
||||
.id_table = dev_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
static int __init at76_mod_init(void)
|
||||
|
|
|
@ -405,6 +405,7 @@ static struct usb_driver ath6kl_usb_driver = {
|
|||
.probe = ath6kl_usb_probe,
|
||||
.disconnect = ath6kl_usb_remove,
|
||||
.id_table = ath6kl_usb_ids,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
static int ath6kl_usb_init(void)
|
||||
|
|
|
@ -1356,6 +1356,7 @@ static struct usb_driver ath9k_hif_usb_driver = {
|
|||
#endif
|
||||
.id_table = ath9k_hif_usb_ids,
|
||||
.soft_unbind = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
int ath9k_hif_usb_init(void)
|
||||
|
|
|
@ -1159,6 +1159,7 @@ static struct usb_driver carl9170_driver = {
|
|||
.resume = carl9170_usb_resume,
|
||||
.reset_resume = carl9170_usb_resume,
|
||||
#endif /* CONFIG_PM */
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(carl9170_driver);
|
||||
|
|
|
@ -1605,6 +1605,7 @@ static struct usb_driver brcmf_usbdrvr = {
|
|||
.suspend = brcmf_usb_suspend,
|
||||
.resume = brcmf_usb_resume,
|
||||
.supports_autosuspend = 1
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
void brcmf_usb_exit(void)
|
||||
|
|
|
@ -1180,6 +1180,7 @@ static struct usb_driver if_usb_driver = {
|
|||
.suspend = if_usb_suspend,
|
||||
.resume = if_usb_resume,
|
||||
.reset_resume = if_usb_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(if_usb_driver);
|
||||
|
|
|
@ -920,6 +920,7 @@ static struct usb_driver if_usb_driver = {
|
|||
.id_table = if_usb_table,
|
||||
.suspend = if_usb_suspend,
|
||||
.resume = if_usb_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(if_usb_driver);
|
||||
|
|
|
@ -1752,6 +1752,7 @@ static struct usb_driver orinoco_driver = {
|
|||
.probe = ezusb_probe,
|
||||
.disconnect = ezusb_disconnect,
|
||||
.id_table = ezusb_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(orinoco_driver);
|
||||
|
|
|
@ -1081,6 +1081,7 @@ static struct usb_driver p54u_driver = {
|
|||
.reset_resume = p54u_resume,
|
||||
#endif /* CONFIG_PM */
|
||||
.soft_unbind = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(p54u_driver);
|
||||
|
|
|
@ -3776,6 +3776,7 @@ static struct usb_driver rndis_wlan_driver = {
|
|||
.disconnect = usbnet_disconnect,
|
||||
.suspend = usbnet_suspend,
|
||||
.resume = usbnet_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(rndis_wlan_driver);
|
||||
|
|
|
@ -1980,6 +1980,7 @@ static struct usb_driver rt2500usb_driver = {
|
|||
.disconnect = rt2x00usb_disconnect,
|
||||
.suspend = rt2x00usb_suspend,
|
||||
.resume = rt2x00usb_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(rt2500usb_driver);
|
||||
|
|
|
@ -1293,6 +1293,7 @@ static struct usb_driver rt2800usb_driver = {
|
|||
.disconnect = rt2x00usb_disconnect,
|
||||
.suspend = rt2x00usb_suspend,
|
||||
.resume = rt2x00usb_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(rt2800usb_driver);
|
||||
|
|
|
@ -2526,6 +2526,7 @@ static struct usb_driver rt73usb_driver = {
|
|||
.disconnect = rt2x00usb_disconnect,
|
||||
.suspend = rt2x00usb_suspend,
|
||||
.resume = rt2x00usb_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(rt73usb_driver);
|
||||
|
|
|
@ -1662,6 +1662,7 @@ static struct usb_driver rtl8187_driver = {
|
|||
.id_table = rtl8187_table,
|
||||
.probe = rtl8187_probe,
|
||||
.disconnect = __devexit_p(rtl8187_disconnect),
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(rtl8187_driver);
|
||||
|
|
|
@ -372,6 +372,7 @@ static struct usb_driver rtl8192cu_driver = {
|
|||
#ifdef CONFIG_AUTOSUSPEND
|
||||
.supports_autosuspend = 1,
|
||||
#endif
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(rtl8192cu_driver);
|
||||
|
|
|
@ -1905,6 +1905,7 @@ static struct usb_driver zd1201_usb = {
|
|||
.id_table = zd1201_table,
|
||||
.suspend = zd1201_suspend,
|
||||
.resume = zd1201_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(zd1201_usb);
|
||||
|
|
|
@ -1542,6 +1542,7 @@ static struct usb_driver driver = {
|
|||
.disconnect = disconnect,
|
||||
.pre_reset = pre_reset,
|
||||
.post_reset = post_reset,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
struct workqueue_struct *zd_workqueue;
|
||||
|
|
|
@ -1664,6 +1664,7 @@ static struct usb_driver acm_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.supports_autosuspend = 1,
|
||||
#endif
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -1034,6 +1034,7 @@ static struct usb_driver wdm_driver = {
|
|||
.post_reset = wdm_post_reset,
|
||||
.id_table = wdm_ids,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(wdm_driver);
|
||||
|
|
|
@ -288,6 +288,7 @@ static int usb_probe_interface(struct device *dev)
|
|||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
const struct usb_device_id *id;
|
||||
int error = -ENODEV;
|
||||
int lpm_disable_error;
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
|
@ -324,6 +325,25 @@ static int usb_probe_interface(struct device *dev)
|
|||
if (driver->supports_autosuspend)
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/* If the new driver doesn't allow hub-initiated LPM, and we can't
|
||||
* disable hub-initiated LPM, then fail the probe.
|
||||
*
|
||||
* Otherwise, leaving LPM enabled should be harmless, because the
|
||||
* endpoint intervals should remain the same, and the U1/U2 timeouts
|
||||
* should remain the same.
|
||||
*
|
||||
* If we need to install alt setting 0 before probe, or another alt
|
||||
* setting during probe, that should also be fine. usb_set_interface()
|
||||
* will attempt to disable LPM, and fail if it can't disable it.
|
||||
*/
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
|
||||
dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
|
||||
__func__, driver->name);
|
||||
error = lpm_disable_error;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Carry out a deferred switch to altsetting 0 */
|
||||
if (intf->needs_altsetting0) {
|
||||
error = usb_set_interface(udev, intf->altsetting[0].
|
||||
|
@ -338,6 +358,11 @@ static int usb_probe_interface(struct device *dev)
|
|||
goto err;
|
||||
|
||||
intf->condition = USB_INTERFACE_BOUND;
|
||||
|
||||
/* If the LPM disable succeeded, balance the ref counts. */
|
||||
if (!lpm_disable_error)
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
|
||||
usb_autosuspend_device(udev);
|
||||
return error;
|
||||
|
||||
|
@ -361,7 +386,7 @@ static int usb_unbind_interface(struct device *dev)
|
|||
struct usb_driver *driver = to_usb_driver(dev->driver);
|
||||
struct usb_interface *intf = to_usb_interface(dev);
|
||||
struct usb_device *udev;
|
||||
int error, r;
|
||||
int error, r, lpm_disable_error;
|
||||
|
||||
intf->condition = USB_INTERFACE_UNBINDING;
|
||||
|
||||
|
@ -369,6 +394,13 @@ static int usb_unbind_interface(struct device *dev)
|
|||
udev = interface_to_usbdev(intf);
|
||||
error = usb_autoresume_device(udev);
|
||||
|
||||
/* Hub-initiated LPM policy may change, so attempt to disable LPM until
|
||||
* the driver is unbound. If LPM isn't disabled, that's fine because it
|
||||
* wouldn't be enabled unless all the bound interfaces supported
|
||||
* hub-initiated LPM.
|
||||
*/
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
|
||||
/* Terminate all URBs for this interface unless the driver
|
||||
* supports "soft" unbinding.
|
||||
*/
|
||||
|
@ -402,6 +434,10 @@ static int usb_unbind_interface(struct device *dev)
|
|||
intf->condition = USB_INTERFACE_UNBOUND;
|
||||
intf->needs_remote_wakeup = 0;
|
||||
|
||||
/* Attempt to re-enable USB3 LPM, if the disable succeeded. */
|
||||
if (!lpm_disable_error)
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
|
||||
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
|
||||
if (driver->supports_autosuspend)
|
||||
pm_runtime_disable(dev);
|
||||
|
@ -442,17 +478,29 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
struct usb_interface *iface, void *priv)
|
||||
{
|
||||
struct device *dev = &iface->dev;
|
||||
struct usb_device *udev;
|
||||
int retval = 0;
|
||||
int lpm_disable_error;
|
||||
|
||||
if (dev->driver)
|
||||
return -EBUSY;
|
||||
|
||||
udev = interface_to_usbdev(iface);
|
||||
|
||||
dev->driver = &driver->drvwrap.driver;
|
||||
usb_set_intfdata(iface, priv);
|
||||
iface->needs_binding = 0;
|
||||
|
||||
iface->condition = USB_INTERFACE_BOUND;
|
||||
|
||||
/* Disable LPM until this driver is bound. */
|
||||
lpm_disable_error = usb_unlocked_disable_lpm(udev);
|
||||
if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
|
||||
__func__, driver->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Claimed interfaces are initially inactive (suspended) and
|
||||
* runtime-PM-enabled, but only if the driver has autosuspend
|
||||
* support. Otherwise they are marked active, to prevent the
|
||||
|
@ -471,6 +519,10 @@ int usb_driver_claim_interface(struct usb_driver *driver,
|
|||
if (device_is_registered(dev))
|
||||
retval = device_bind_driver(dev);
|
||||
|
||||
/* Attempt to re-enable USB3 LPM, if the disable was successful. */
|
||||
if (!lpm_disable_error)
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_driver_claim_interface);
|
||||
|
|
|
@ -997,6 +997,15 @@ static int register_root_hub(struct usb_hcd *hcd)
|
|||
dev_name(&usb_dev->dev), retval);
|
||||
return (retval < 0) ? retval : -EMSGSIZE;
|
||||
}
|
||||
if (usb_dev->speed == USB_SPEED_SUPER) {
|
||||
retval = usb_get_bos_descriptor(usb_dev);
|
||||
if (retval < 0) {
|
||||
mutex_unlock(&usb_bus_list_lock);
|
||||
dev_dbg(parent_dev, "can't read %s bos descriptor %d\n",
|
||||
dev_name(&usb_dev->dev), retval);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
retval = usb_new_device (usb_dev);
|
||||
if (retval) {
|
||||
|
|
|
@ -177,6 +177,228 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
|
|||
return usb_get_intfdata(hdev->actconfig->interface[0]);
|
||||
}
|
||||
|
||||
static int usb_device_supports_lpm(struct usb_device *udev)
|
||||
{
|
||||
/* USB 2.1 (and greater) devices indicate LPM support through
|
||||
* their USB 2.0 Extended Capabilities BOS descriptor.
|
||||
*/
|
||||
if (udev->speed == USB_SPEED_HIGH) {
|
||||
if (udev->bos->ext_cap &&
|
||||
(USB_LPM_SUPPORT &
|
||||
le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* All USB 3.0 must support LPM, but we need their max exit latency
|
||||
* information from the SuperSpeed Extended Capabilities BOS descriptor.
|
||||
*/
|
||||
if (!udev->bos->ss_cap) {
|
||||
dev_warn(&udev->dev, "No LPM exit latency info found. "
|
||||
"Power management will be impacted.\n");
|
||||
return 0;
|
||||
}
|
||||
if (udev->parent->lpm_capable)
|
||||
return 1;
|
||||
|
||||
dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. "
|
||||
"Power management will be impacted.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the Maximum Exit Latency (MEL) for the host to initiate a transition from
|
||||
* either U1 or U2.
|
||||
*/
|
||||
static void usb_set_lpm_mel(struct usb_device *udev,
|
||||
struct usb3_lpm_parameters *udev_lpm_params,
|
||||
unsigned int udev_exit_latency,
|
||||
struct usb_hub *hub,
|
||||
struct usb3_lpm_parameters *hub_lpm_params,
|
||||
unsigned int hub_exit_latency)
|
||||
{
|
||||
unsigned int total_mel;
|
||||
unsigned int device_mel;
|
||||
unsigned int hub_mel;
|
||||
|
||||
/*
|
||||
* Calculate the time it takes to transition all links from the roothub
|
||||
* to the parent hub into U0. The parent hub must then decode the
|
||||
* packet (hub header decode latency) to figure out which port it was
|
||||
* bound for.
|
||||
*
|
||||
* The Hub Header decode latency is expressed in 0.1us intervals (0x1
|
||||
* means 0.1us). Multiply that by 100 to get nanoseconds.
|
||||
*/
|
||||
total_mel = hub_lpm_params->mel +
|
||||
(hub->descriptor->u.ss.bHubHdrDecLat * 100);
|
||||
|
||||
/*
|
||||
* How long will it take to transition the downstream hub's port into
|
||||
* U0? The greater of either the hub exit latency or the device exit
|
||||
* latency.
|
||||
*
|
||||
* The BOS U1/U2 exit latencies are expressed in 1us intervals.
|
||||
* Multiply that by 1000 to get nanoseconds.
|
||||
*/
|
||||
device_mel = udev_exit_latency * 1000;
|
||||
hub_mel = hub_exit_latency * 1000;
|
||||
if (device_mel > hub_mel)
|
||||
total_mel += device_mel;
|
||||
else
|
||||
total_mel += hub_mel;
|
||||
|
||||
udev_lpm_params->mel = total_mel;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the maximum Device to Host Exit Latency (PEL) for the device to initiate
|
||||
* a transition from either U1 or U2.
|
||||
*/
|
||||
static void usb_set_lpm_pel(struct usb_device *udev,
|
||||
struct usb3_lpm_parameters *udev_lpm_params,
|
||||
unsigned int udev_exit_latency,
|
||||
struct usb_hub *hub,
|
||||
struct usb3_lpm_parameters *hub_lpm_params,
|
||||
unsigned int hub_exit_latency,
|
||||
unsigned int port_to_port_exit_latency)
|
||||
{
|
||||
unsigned int first_link_pel;
|
||||
unsigned int hub_pel;
|
||||
|
||||
/*
|
||||
* First, the device sends an LFPS to transition the link between the
|
||||
* device and the parent hub into U0. The exit latency is the bigger of
|
||||
* the device exit latency or the hub exit latency.
|
||||
*/
|
||||
if (udev_exit_latency > hub_exit_latency)
|
||||
first_link_pel = udev_exit_latency * 1000;
|
||||
else
|
||||
first_link_pel = hub_exit_latency * 1000;
|
||||
|
||||
/*
|
||||
* When the hub starts to receive the LFPS, there is a slight delay for
|
||||
* it to figure out that one of the ports is sending an LFPS. Then it
|
||||
* will forward the LFPS to its upstream link. The exit latency is the
|
||||
* delay, plus the PEL that we calculated for this hub.
|
||||
*/
|
||||
hub_pel = port_to_port_exit_latency * 1000 + hub_lpm_params->pel;
|
||||
|
||||
/*
|
||||
* According to figure C-7 in the USB 3.0 spec, the PEL for this device
|
||||
* is the greater of the two exit latencies.
|
||||
*/
|
||||
if (first_link_pel > hub_pel)
|
||||
udev_lpm_params->pel = first_link_pel;
|
||||
else
|
||||
udev_lpm_params->pel = hub_pel;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the System Exit Latency (SEL) to indicate the total worst-case time from
|
||||
* when a device initiates a transition to U0, until when it will receive the
|
||||
* first packet from the host controller.
|
||||
*
|
||||
* Section C.1.5.1 describes the four components to this:
|
||||
* - t1: device PEL
|
||||
* - t2: time for the ERDY to make it from the device to the host.
|
||||
* - t3: a host-specific delay to process the ERDY.
|
||||
* - t4: time for the packet to make it from the host to the device.
|
||||
*
|
||||
* t3 is specific to both the xHCI host and the platform the host is integrated
|
||||
* into. The Intel HW folks have said it's negligible, FIXME if a different
|
||||
* vendor says otherwise.
|
||||
*/
|
||||
static void usb_set_lpm_sel(struct usb_device *udev,
|
||||
struct usb3_lpm_parameters *udev_lpm_params)
|
||||
{
|
||||
struct usb_device *parent;
|
||||
unsigned int num_hubs;
|
||||
unsigned int total_sel;
|
||||
|
||||
/* t1 = device PEL */
|
||||
total_sel = udev_lpm_params->pel;
|
||||
/* How many external hubs are in between the device & the root port. */
|
||||
for (parent = udev->parent, num_hubs = 0; parent->parent;
|
||||
parent = parent->parent)
|
||||
num_hubs++;
|
||||
/* t2 = 2.1us + 250ns * (num_hubs - 1) */
|
||||
if (num_hubs > 0)
|
||||
total_sel += 2100 + 250 * (num_hubs - 1);
|
||||
|
||||
/* t4 = 250ns * num_hubs */
|
||||
total_sel += 250 * num_hubs;
|
||||
|
||||
udev_lpm_params->sel = total_sel;
|
||||
}
|
||||
|
||||
static void usb_set_lpm_parameters(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hub *hub;
|
||||
unsigned int port_to_port_delay;
|
||||
unsigned int udev_u1_del;
|
||||
unsigned int udev_u2_del;
|
||||
unsigned int hub_u1_del;
|
||||
unsigned int hub_u2_del;
|
||||
|
||||
if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER)
|
||||
return;
|
||||
|
||||
hub = hdev_to_hub(udev->parent);
|
||||
/* It doesn't take time to transition the roothub into U0, since it
|
||||
* doesn't have an upstream link.
|
||||
*/
|
||||
if (!hub)
|
||||
return;
|
||||
|
||||
udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
|
||||
udev_u2_del = udev->bos->ss_cap->bU2DevExitLat;
|
||||
hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
|
||||
hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat;
|
||||
|
||||
usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
|
||||
hub, &udev->parent->u1_params, hub_u1_del);
|
||||
|
||||
usb_set_lpm_mel(udev, &udev->u2_params, udev_u2_del,
|
||||
hub, &udev->parent->u2_params, hub_u2_del);
|
||||
|
||||
/*
|
||||
* Appendix C, section C.2.2.2, says that there is a slight delay from
|
||||
* when the parent hub notices the downstream port is trying to
|
||||
* transition to U0 to when the hub initiates a U0 transition on its
|
||||
* upstream port. The section says the delays are tPort2PortU1EL and
|
||||
* tPort2PortU2EL, but it doesn't define what they are.
|
||||
*
|
||||
* The hub chapter, sections 10.4.2.4 and 10.4.2.5 seem to be talking
|
||||
* about the same delays. Use the maximum delay calculations from those
|
||||
* sections. For U1, it's tHubPort2PortExitLat, which is 1us max. For
|
||||
* U2, it's tHubPort2PortExitLat + U2DevExitLat - U1DevExitLat. I
|
||||
* assume the device exit latencies they are talking about are the hub
|
||||
* exit latencies.
|
||||
*
|
||||
* What do we do if the U2 exit latency is less than the U1 exit
|
||||
* latency? It's possible, although not likely...
|
||||
*/
|
||||
port_to_port_delay = 1;
|
||||
|
||||
usb_set_lpm_pel(udev, &udev->u1_params, udev_u1_del,
|
||||
hub, &udev->parent->u1_params, hub_u1_del,
|
||||
port_to_port_delay);
|
||||
|
||||
if (hub_u2_del > hub_u1_del)
|
||||
port_to_port_delay = 1 + hub_u2_del - hub_u1_del;
|
||||
else
|
||||
port_to_port_delay = 1 + hub_u1_del;
|
||||
|
||||
usb_set_lpm_pel(udev, &udev->u2_params, udev_u2_del,
|
||||
hub, &udev->parent->u2_params, hub_u2_del,
|
||||
port_to_port_delay);
|
||||
|
||||
/* Now that we've got PEL, calculate SEL. */
|
||||
usb_set_lpm_sel(udev, &udev->u1_params);
|
||||
usb_set_lpm_sel(udev, &udev->u2_params);
|
||||
}
|
||||
|
||||
/* USB 2.0 spec Section 11.24.4.5 */
|
||||
static int get_hub_descriptor(struct usb_device *hdev, void *data)
|
||||
{
|
||||
|
@ -2480,6 +2702,12 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
if (udev->usb2_hw_lpm_enabled == 1)
|
||||
usb_set_usb2_hardware_lpm(udev, 0);
|
||||
|
||||
if (usb_unlocked_disable_lpm(udev)) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* see 7.1.7.6 */
|
||||
if (hub_is_superspeed(hub->hdev))
|
||||
status = set_port_feature(hub->hdev,
|
||||
|
@ -2499,6 +2727,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
|
|||
NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
/* Try to enable USB2 hardware LPM again */
|
||||
if (udev->usb2_hw_lpm_capable == 1)
|
||||
usb_set_usb2_hardware_lpm(udev, 1);
|
||||
|
||||
/* Try to enable USB3 LPM again */
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
|
||||
/* System sleep transitions should never fail */
|
||||
if (!PMSG_IS_AUTO(msg))
|
||||
status = 0;
|
||||
|
@ -2696,6 +2931,9 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
|||
/* Try to enable USB2 hardware LPM */
|
||||
if (udev->usb2_hw_lpm_capable == 1)
|
||||
usb_set_usb2_hardware_lpm(udev, 1);
|
||||
|
||||
/* Try to enable USB3 LPM */
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -2824,11 +3062,425 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
|
||||
|
||||
static const char * const usb3_lpm_names[] = {
|
||||
"U0",
|
||||
"U1",
|
||||
"U2",
|
||||
"U3",
|
||||
};
|
||||
|
||||
/*
|
||||
* Send a Set SEL control transfer to the device, prior to enabling
|
||||
* device-initiated U1 or U2. This lets the device know the exit latencies from
|
||||
* the time the device initiates a U1 or U2 exit, to the time it will receive a
|
||||
* packet from the host.
|
||||
*
|
||||
* This function will fail if the SEL or PEL values for udev are greater than
|
||||
* the maximum allowed values for the link state to be enabled.
|
||||
*/
|
||||
static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
|
||||
{
|
||||
struct usb_set_sel_req *sel_values;
|
||||
unsigned long long u1_sel;
|
||||
unsigned long long u1_pel;
|
||||
unsigned long long u2_sel;
|
||||
unsigned long long u2_pel;
|
||||
int ret;
|
||||
|
||||
/* Convert SEL and PEL stored in ns to us */
|
||||
u1_sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
|
||||
u1_pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
|
||||
u2_sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
|
||||
u2_pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
|
||||
|
||||
/*
|
||||
* Make sure that the calculated SEL and PEL values for the link
|
||||
* state we're enabling aren't bigger than the max SEL/PEL
|
||||
* value that will fit in the SET SEL control transfer.
|
||||
* Otherwise the device would get an incorrect idea of the exit
|
||||
* latency for the link state, and could start a device-initiated
|
||||
* U1/U2 when the exit latencies are too high.
|
||||
*/
|
||||
if ((state == USB3_LPM_U1 &&
|
||||
(u1_sel > USB3_LPM_MAX_U1_SEL_PEL ||
|
||||
u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) ||
|
||||
(state == USB3_LPM_U2 &&
|
||||
(u2_sel > USB3_LPM_MAX_U2_SEL_PEL ||
|
||||
u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) {
|
||||
dev_dbg(&udev->dev, "Device-initiated %s disabled due "
|
||||
"to long SEL %llu ms or PEL %llu ms\n",
|
||||
usb3_lpm_names[state], u1_sel, u1_pel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're enabling device-initiated LPM for one link state,
|
||||
* but the other link state has a too high SEL or PEL value,
|
||||
* just set those values to the max in the Set SEL request.
|
||||
*/
|
||||
if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL)
|
||||
u1_sel = USB3_LPM_MAX_U1_SEL_PEL;
|
||||
|
||||
if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL)
|
||||
u1_pel = USB3_LPM_MAX_U1_SEL_PEL;
|
||||
|
||||
if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL)
|
||||
u2_sel = USB3_LPM_MAX_U2_SEL_PEL;
|
||||
|
||||
if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL)
|
||||
u2_pel = USB3_LPM_MAX_U2_SEL_PEL;
|
||||
|
||||
/*
|
||||
* usb_enable_lpm() can be called as part of a failed device reset,
|
||||
* which may be initiated by an error path of a mass storage driver.
|
||||
* Therefore, use GFP_NOIO.
|
||||
*/
|
||||
sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO);
|
||||
if (!sel_values)
|
||||
return -ENOMEM;
|
||||
|
||||
sel_values->u1_sel = u1_sel;
|
||||
sel_values->u1_pel = u1_pel;
|
||||
sel_values->u2_sel = cpu_to_le16(u2_sel);
|
||||
sel_values->u2_pel = cpu_to_le16(u2_pel);
|
||||
|
||||
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_SEL,
|
||||
USB_RECIP_DEVICE,
|
||||
0, 0,
|
||||
sel_values, sizeof *(sel_values),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
kfree(sel_values);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable device-initiated U1 or U2 transitions.
|
||||
*/
|
||||
static int usb_set_device_initiated_lpm(struct usb_device *udev,
|
||||
enum usb3_link_state state, bool enable)
|
||||
{
|
||||
int ret;
|
||||
int feature;
|
||||
|
||||
switch (state) {
|
||||
case USB3_LPM_U1:
|
||||
feature = USB_DEVICE_U1_ENABLE;
|
||||
break;
|
||||
case USB3_LPM_U2:
|
||||
feature = USB_DEVICE_U2_ENABLE;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n",
|
||||
__func__, enable ? "enable" : "disable");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (udev->state != USB_STATE_CONFIGURED) {
|
||||
dev_dbg(&udev->dev, "%s: Can't %s %s state "
|
||||
"for unconfigured device.\n",
|
||||
__func__, enable ? "enable" : "disable",
|
||||
usb3_lpm_names[state]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
/*
|
||||
* First, let the device know about the exit latencies
|
||||
* associated with the link state we're about to enable.
|
||||
*/
|
||||
ret = usb_req_set_sel(udev, state);
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev, "Set SEL for device-initiated "
|
||||
"%s failed.\n", usb3_lpm_names[state]);
|
||||
return -EBUSY;
|
||||
}
|
||||
/*
|
||||
* Now send the control transfer to enable device-initiated LPM
|
||||
* for either U1 or U2.
|
||||
*/
|
||||
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_SET_FEATURE,
|
||||
USB_RECIP_DEVICE,
|
||||
feature,
|
||||
0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
} else {
|
||||
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
|
||||
USB_REQ_CLEAR_FEATURE,
|
||||
USB_RECIP_DEVICE,
|
||||
feature,
|
||||
0, NULL, 0,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev, "%s of device-initiated %s failed.\n",
|
||||
enable ? "Enable" : "Disable",
|
||||
usb3_lpm_names[state]);
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_set_lpm_timeout(struct usb_device *udev,
|
||||
enum usb3_link_state state, int timeout)
|
||||
{
|
||||
int ret;
|
||||
int feature;
|
||||
|
||||
switch (state) {
|
||||
case USB3_LPM_U1:
|
||||
feature = USB_PORT_FEAT_U1_TIMEOUT;
|
||||
break;
|
||||
case USB3_LPM_U2:
|
||||
feature = USB_PORT_FEAT_U2_TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&udev->dev, "%s: Can't set timeout for non-U1 or U2 state.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state == USB3_LPM_U1 && timeout > USB3_LPM_U1_MAX_TIMEOUT &&
|
||||
timeout != USB3_LPM_DEVICE_INITIATED) {
|
||||
dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x, "
|
||||
"which is a reserved value.\n",
|
||||
usb3_lpm_names[state], timeout);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = set_port_feature(udev->parent,
|
||||
USB_PORT_LPM_TIMEOUT(timeout) | udev->portnum,
|
||||
feature);
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x,"
|
||||
"error code %i\n", usb3_lpm_names[state],
|
||||
timeout, ret);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (state == USB3_LPM_U1)
|
||||
udev->u1_params.timeout = timeout;
|
||||
else
|
||||
udev->u2_params.timeout = timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the hub-initiated U1/U2 idle timeouts, and enable device-initiated
|
||||
* U1/U2 entry.
|
||||
*
|
||||
* We will attempt to enable U1 or U2, but there are no guarantees that the
|
||||
* control transfers to set the hub timeout or enable device-initiated U1/U2
|
||||
* will be successful.
|
||||
*
|
||||
* If we cannot set the parent hub U1/U2 timeout, we attempt to let the xHCI
|
||||
* driver know about it. If that call fails, it should be harmless, and just
|
||||
* take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
|
||||
*/
|
||||
static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
int timeout;
|
||||
|
||||
/* We allow the host controller to set the U1/U2 timeout internally
|
||||
* first, so that it can change its schedule to account for the
|
||||
* additional latency to send data to a device in a lower power
|
||||
* link state.
|
||||
*/
|
||||
timeout = hcd->driver->enable_usb3_lpm_timeout(hcd, udev, state);
|
||||
|
||||
/* xHCI host controller doesn't want to enable this LPM state. */
|
||||
if (timeout == 0)
|
||||
return;
|
||||
|
||||
if (timeout < 0) {
|
||||
dev_warn(&udev->dev, "Could not enable %s link state, "
|
||||
"xHCI error %i.\n", usb3_lpm_names[state],
|
||||
timeout);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usb_set_lpm_timeout(udev, state, timeout))
|
||||
/* If we can't set the parent hub U1/U2 timeout,
|
||||
* device-initiated LPM won't be allowed either, so let the xHCI
|
||||
* host know that this link state won't be enabled.
|
||||
*/
|
||||
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
|
||||
|
||||
/* Only a configured device will accept the Set Feature U1/U2_ENABLE */
|
||||
else if (udev->actconfig)
|
||||
usb_set_device_initiated_lpm(udev, state, true);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
|
||||
* U1/U2 entry.
|
||||
*
|
||||
* If this function returns -EBUSY, the parent hub will still allow U1/U2 entry.
|
||||
* If zero is returned, the parent will not allow the link to go into U1/U2.
|
||||
*
|
||||
* If zero is returned, device-initiated U1/U2 entry may still be enabled, but
|
||||
* it won't have an effect on the bus link state because the parent hub will
|
||||
* still disallow device-initiated U1/U2 entry.
|
||||
*
|
||||
* If zero is returned, the xHCI host controller may still think U1/U2 entry is
|
||||
* possible. The result will be slightly more bus bandwidth will be taken up
|
||||
* (to account for U1/U2 exit latency), but it should be harmless.
|
||||
*/
|
||||
static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
int feature;
|
||||
|
||||
switch (state) {
|
||||
case USB3_LPM_U1:
|
||||
feature = USB_PORT_FEAT_U1_TIMEOUT;
|
||||
break;
|
||||
case USB3_LPM_U2:
|
||||
feature = USB_PORT_FEAT_U2_TIMEOUT;
|
||||
break;
|
||||
default:
|
||||
dev_warn(&udev->dev, "%s: Can't disable non-U1 or U2 state.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (usb_set_lpm_timeout(udev, state, 0))
|
||||
return -EBUSY;
|
||||
|
||||
usb_set_device_initiated_lpm(udev, state, false);
|
||||
|
||||
if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
|
||||
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
|
||||
"bus schedule bandwidth may be impacted.\n",
|
||||
usb3_lpm_names[state]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable hub-initiated and device-initiated U1 and U2 entry.
|
||||
* Caller must own the bandwidth_mutex.
|
||||
*
|
||||
* This will call usb_enable_lpm() on failure, which will decrement
|
||||
* lpm_disable_count, and will re-enable LPM if lpm_disable_count reaches zero.
|
||||
*/
|
||||
int usb_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable)
|
||||
return 0;
|
||||
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
if (!hcd || !hcd->driver->disable_usb3_lpm_timeout)
|
||||
return 0;
|
||||
|
||||
udev->lpm_disable_count++;
|
||||
if ((udev->u1_params.timeout == 0 && udev->u1_params.timeout == 0))
|
||||
return 0;
|
||||
|
||||
/* If LPM is enabled, attempt to disable it. */
|
||||
if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
|
||||
goto enable_lpm;
|
||||
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
|
||||
goto enable_lpm;
|
||||
|
||||
return 0;
|
||||
|
||||
enable_lpm:
|
||||
usb_enable_lpm(udev);
|
||||
return -EBUSY;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disable_lpm);
|
||||
|
||||
/* Grab the bandwidth_mutex before calling usb_disable_lpm() */
|
||||
int usb_unlocked_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
int ret;
|
||||
|
||||
if (!hcd)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
ret = usb_disable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
|
||||
|
||||
/*
|
||||
* Attempt to enable device-initiated and hub-initiated U1 and U2 entry. The
|
||||
* xHCI host policy may prevent U1 or U2 from being enabled.
|
||||
*
|
||||
* Other callers may have disabled link PM, so U1 and U2 entry will be disabled
|
||||
* until the lpm_disable_count drops to zero. Caller must own the
|
||||
* bandwidth_mutex.
|
||||
*/
|
||||
void usb_enable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
if (!udev || !udev->parent ||
|
||||
udev->speed != USB_SPEED_SUPER ||
|
||||
!udev->lpm_capable)
|
||||
return;
|
||||
|
||||
udev->lpm_disable_count--;
|
||||
hcd = bus_to_hcd(udev->bus);
|
||||
/* Double check that we can both enable and disable LPM.
|
||||
* Device must be configured to accept set feature U1/U2 timeout.
|
||||
*/
|
||||
if (!hcd || !hcd->driver->enable_usb3_lpm_timeout ||
|
||||
!hcd->driver->disable_usb3_lpm_timeout)
|
||||
return;
|
||||
|
||||
if (udev->lpm_disable_count > 0)
|
||||
return;
|
||||
|
||||
usb_enable_link_state(hcd, udev, USB3_LPM_U1);
|
||||
usb_enable_link_state(hcd, udev, USB3_LPM_U2);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_enable_lpm);
|
||||
|
||||
/* Grab the bandwidth_mutex before calling usb_enable_lpm() */
|
||||
void usb_unlocked_enable_lpm(struct usb_device *udev)
|
||||
{
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
if (!hcd)
|
||||
return;
|
||||
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
usb_enable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
|
||||
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
|
||||
#define hub_suspend NULL
|
||||
#define hub_resume NULL
|
||||
#define hub_reset_resume NULL
|
||||
|
||||
int usb_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_enable_lpm(struct usb_device *udev) { }
|
||||
|
||||
int usb_unlocked_disable_lpm(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_unlocked_enable_lpm(struct usb_device *udev) { }
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -3208,9 +3860,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
|||
if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
|
||||
retval = usb_get_bos_descriptor(udev);
|
||||
if (!retval) {
|
||||
if (udev->bos->ext_cap && (USB_LPM_SUPPORT &
|
||||
le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
|
||||
udev->lpm_capable = 1;
|
||||
udev->lpm_capable = usb_device_supports_lpm(udev);
|
||||
usb_set_lpm_parameters(udev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4042,11 +4693,22 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
goto done;
|
||||
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM while we reset the device and reinstall the alt settings.
|
||||
* Device-initiated LPM settings, and system exit latency settings are
|
||||
* cleared when the device is reset, so we have to set them up again.
|
||||
*/
|
||||
ret = usb_disable_lpm(udev);
|
||||
if (ret) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto done;
|
||||
}
|
||||
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev,
|
||||
"Busted HC? Not enough HCD resources for "
|
||||
"old configuration.\n");
|
||||
usb_enable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
@ -4058,6 +4720,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
dev_err(&udev->dev,
|
||||
"can't restore configuration #%d (error=%d)\n",
|
||||
udev->actconfig->desc.bConfigurationValue, ret);
|
||||
usb_enable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
@ -4096,10 +4759,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
desc->bInterfaceNumber,
|
||||
desc->bAlternateSetting,
|
||||
ret);
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
goto re_enumerate;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that the alt settings are re-installed, enable LPM. */
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
done:
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -1308,10 +1308,19 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
* Remove the current alt setting and add the new alt setting.
|
||||
*/
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM, and re-enable it once the new alt setting is installed,
|
||||
* so that the xHCI driver can recalculate the U1/U2 timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
|
||||
if (ret < 0) {
|
||||
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
|
||||
alternate);
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1334,6 +1343,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
} else if (ret < 0) {
|
||||
/* Re-instate the old alt setting */
|
||||
usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1354,6 +1364,9 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
|
|||
|
||||
iface->cur_altsetting = alt;
|
||||
|
||||
/* Now that the interface is installed, re-enable LPM. */
|
||||
usb_unlocked_enable_lpm(dev);
|
||||
|
||||
/* If the interface only has one altsetting and the device didn't
|
||||
* accept the request, we attempt to carry out the equivalent action
|
||||
* by manually clearing the HALT feature for each endpoint in the
|
||||
|
@ -1437,6 +1450,14 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
config = dev->actconfig;
|
||||
retval = 0;
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM, and re-enable it once the configuration is reset, so
|
||||
* that the xHCI driver can recalculate the U1/U2 timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Make sure we have enough bandwidth for each alternate setting 0 */
|
||||
for (i = 0; i < config->desc.bNumInterfaces; i++) {
|
||||
struct usb_interface *intf = config->interface[i];
|
||||
|
@ -1465,6 +1486,7 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
usb_hcd_alloc_bandwidth(dev, NULL,
|
||||
alt, intf->cur_altsetting);
|
||||
}
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
@ -1502,6 +1524,8 @@ int usb_reset_configuration(struct usb_device *dev)
|
|||
create_intf_ep_devs(intf);
|
||||
}
|
||||
}
|
||||
/* Now that the interfaces are installed, re-enable LPM. */
|
||||
usb_unlocked_enable_lpm(dev);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_reset_configuration);
|
||||
|
@ -1763,8 +1787,18 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
|
|||
* this call fails, the device state is unchanged.
|
||||
*/
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM, and re-enable it once the new configuration is
|
||||
* installed, so that the xHCI driver can recalculate the U1/U2
|
||||
* timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
usb_autosuspend_device(dev);
|
||||
goto free_interfaces;
|
||||
|
@ -1784,6 +1818,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
|
|||
if (!cp) {
|
||||
usb_set_device_state(dev, USB_STATE_ADDRESS);
|
||||
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
usb_autosuspend_device(dev);
|
||||
goto free_interfaces;
|
||||
|
@ -1838,6 +1873,9 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
|
|||
!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
|
||||
cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
|
||||
|
||||
/* Now that the interfaces are installed, re-enable LPM. */
|
||||
usb_unlocked_enable_lpm(dev);
|
||||
|
||||
/* Now that all the interfaces are set up, register them
|
||||
* to trigger binding of drivers to interfaces. probe()
|
||||
* routines may install different altsettings and may
|
||||
|
|
|
@ -475,6 +475,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
struct xhci_bus_state *bus_state;
|
||||
u16 link_state = 0;
|
||||
u16 wake_mask = 0;
|
||||
u16 timeout = 0;
|
||||
|
||||
max_ports = xhci_get_ports(hcd, &port_array);
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
|
@ -623,6 +624,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
link_state = (wIndex & 0xff00) >> 3;
|
||||
if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
|
||||
wake_mask = wIndex & 0xff00;
|
||||
/* The MSB of wIndex is the U1/U2 timeout */
|
||||
timeout = (wIndex & 0xff00) >> 8;
|
||||
wIndex &= 0xff;
|
||||
if (!wIndex || wIndex > max_ports)
|
||||
goto error;
|
||||
|
@ -747,6 +750,22 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
|
||||
temp = xhci_readl(xhci, port_array[wIndex]);
|
||||
break;
|
||||
case USB_PORT_FEAT_U1_TIMEOUT:
|
||||
if (hcd->speed != HCD_USB3)
|
||||
goto error;
|
||||
temp = xhci_readl(xhci, port_array[wIndex] + 1);
|
||||
temp &= ~PORT_U1_TIMEOUT_MASK;
|
||||
temp |= PORT_U1_TIMEOUT(timeout);
|
||||
xhci_writel(xhci, temp, port_array[wIndex] + 1);
|
||||
break;
|
||||
case USB_PORT_FEAT_U2_TIMEOUT:
|
||||
if (hcd->speed != HCD_USB3)
|
||||
goto error;
|
||||
temp = xhci_readl(xhci, port_array[wIndex] + 1);
|
||||
temp &= ~PORT_U2_TIMEOUT_MASK;
|
||||
temp |= PORT_U2_TIMEOUT(timeout);
|
||||
xhci_writel(xhci, temp, port_array[wIndex] + 1);
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
|
|
@ -1791,6 +1791,14 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
|
||||
struct dev_info *dev_info, *next;
|
||||
struct list_head *tt_list_head;
|
||||
struct list_head *tt;
|
||||
struct list_head *endpoints;
|
||||
struct list_head *ep, *q;
|
||||
struct xhci_tt_bw_info *tt_info;
|
||||
struct xhci_interval_bw_table *bwt;
|
||||
struct xhci_virt_ep *virt_ep;
|
||||
|
||||
unsigned long flags;
|
||||
int size;
|
||||
int i;
|
||||
|
@ -1807,6 +1815,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
xhci->event_ring = NULL;
|
||||
xhci_dbg(xhci, "Freed event ring\n");
|
||||
|
||||
if (xhci->lpm_command)
|
||||
xhci_free_command(xhci, xhci->lpm_command);
|
||||
xhci->cmd_ring_reserved_trbs = 0;
|
||||
if (xhci->cmd_ring)
|
||||
xhci_ring_free(xhci, xhci->cmd_ring);
|
||||
xhci->cmd_ring = NULL;
|
||||
|
@ -1849,8 +1860,26 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
}
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
bwt = &xhci->rh_bw->bw_table;
|
||||
for (i = 0; i < XHCI_MAX_INTERVAL; i++) {
|
||||
endpoints = &bwt->interval_bw[i].endpoints;
|
||||
list_for_each_safe(ep, q, endpoints) {
|
||||
virt_ep = list_entry(ep, struct xhci_virt_ep, bw_endpoint_list);
|
||||
list_del(&virt_ep->bw_endpoint_list);
|
||||
kfree(virt_ep);
|
||||
}
|
||||
}
|
||||
|
||||
tt_list_head = &xhci->rh_bw->tts;
|
||||
list_for_each_safe(tt, q, tt_list_head) {
|
||||
tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list);
|
||||
list_del(tt);
|
||||
kfree(tt_info);
|
||||
}
|
||||
|
||||
xhci->num_usb2_ports = 0;
|
||||
xhci->num_usb3_ports = 0;
|
||||
xhci->num_active_eps = 0;
|
||||
kfree(xhci->usb2_ports);
|
||||
kfree(xhci->usb3_ports);
|
||||
kfree(xhci->port_array);
|
||||
|
@ -2350,6 +2379,16 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
|
||||
xhci_dbg_cmd_ptrs(xhci);
|
||||
|
||||
xhci->lpm_command = xhci_alloc_command(xhci, true, true, flags);
|
||||
if (!xhci->lpm_command)
|
||||
goto fail;
|
||||
|
||||
/* Reserve one command ring TRB for disabling LPM.
|
||||
* Since the USB core grabs the shared usb_bus bandwidth mutex before
|
||||
* disabling LPM, we only need to reserve one TRB for all devices.
|
||||
*/
|
||||
xhci->cmd_ring_reserved_trbs++;
|
||||
|
||||
val = xhci_readl(xhci, &xhci->cap_regs->db_off);
|
||||
val &= DBOFF_MASK;
|
||||
xhci_dbg(xhci, "// Doorbell array is located at offset 0x%x"
|
||||
|
|
|
@ -72,6 +72,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||
xhci_dbg(xhci, "QUIRK: Fresco Logic revision %u "
|
||||
"has broken MSI implementation\n",
|
||||
pdev->revision);
|
||||
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_NEC)
|
||||
|
@ -83,6 +84,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||
/* AMD PLL quirk */
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
|
||||
xhci->quirks |= XHCI_AMD_PLL_FIX;
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
|
||||
xhci->quirks |= XHCI_LPM_SUPPORT;
|
||||
xhci->quirks |= XHCI_INTEL_HOST;
|
||||
}
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) {
|
||||
xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
|
||||
|
@ -169,6 +174,13 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
if (retval)
|
||||
goto put_usb3_hcd;
|
||||
/* Roothub already marked as USB 3.0 speed */
|
||||
|
||||
/* We know the LPM timeout algorithms for this host, let the USB core
|
||||
* enable and disable LPM for devices under the USB 3.0 roothub.
|
||||
*/
|
||||
if (xhci->quirks & XHCI_LPM_SUPPORT)
|
||||
hcd_to_bus(xhci->shared_hcd)->root_hub->lpm_capable = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
put_usb3_hcd:
|
||||
|
@ -292,6 +304,8 @@ static const struct hc_driver xhci_pci_hc_driver = {
|
|||
*/
|
||||
.update_device = xhci_update_device,
|
||||
.set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm,
|
||||
.enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,
|
||||
.disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
|
@ -1787,8 +1787,12 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||
/* handle completion code */
|
||||
switch (trb_comp_code) {
|
||||
case COMP_SUCCESS:
|
||||
frame->status = 0;
|
||||
break;
|
||||
if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {
|
||||
frame->status = 0;
|
||||
break;
|
||||
}
|
||||
if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
|
||||
trb_comp_code = COMP_SHORT_TX;
|
||||
case COMP_SHORT_TX:
|
||||
frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
|
||||
-EREMOTEIO : 0;
|
||||
|
@ -1885,13 +1889,16 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||
switch (trb_comp_code) {
|
||||
case COMP_SUCCESS:
|
||||
/* Double check that the HW transferred everything. */
|
||||
if (event_trb != td->last_trb) {
|
||||
if (event_trb != td->last_trb ||
|
||||
TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
|
||||
xhci_warn(xhci, "WARN Successful completion "
|
||||
"on short TX\n");
|
||||
if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
|
||||
*status = -EREMOTEIO;
|
||||
else
|
||||
*status = 0;
|
||||
if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
|
||||
trb_comp_code = COMP_SHORT_TX;
|
||||
} else {
|
||||
*status = 0;
|
||||
}
|
||||
|
@ -2050,6 +2057,13 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||
* transfer type
|
||||
*/
|
||||
case COMP_SUCCESS:
|
||||
if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
|
||||
break;
|
||||
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
|
||||
trb_comp_code = COMP_SHORT_TX;
|
||||
else
|
||||
xhci_warn(xhci, "WARN Successful completion on short TX: "
|
||||
"needs XHCI_TRUST_TX_LENGTH quirk?\n");
|
||||
case COMP_SHORT_TX:
|
||||
break;
|
||||
case COMP_STOP:
|
||||
|
@ -3595,12 +3609,12 @@ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
|||
|
||||
/* Queue an evaluate context command TRB */
|
||||
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||
u32 slot_id)
|
||||
u32 slot_id, bool command_must_succeed)
|
||||
{
|
||||
return queue_command(xhci, lower_32_bits(in_ctx_ptr),
|
||||
upper_32_bits(in_ctx_ptr), 0,
|
||||
TRB_TYPE(TRB_EVAL_CONTEXT) | SLOT_ID_FOR_TRB(slot_id),
|
||||
false);
|
||||
command_must_succeed);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -2446,7 +2446,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
|
|||
udev->slot_id, must_succeed);
|
||||
else
|
||||
ret = xhci_queue_evaluate_context(xhci, in_ctx->dma,
|
||||
udev->slot_id);
|
||||
udev->slot_id, must_succeed);
|
||||
if (ret < 0) {
|
||||
if (command)
|
||||
list_del(&command->cmd_list);
|
||||
|
@ -3837,6 +3837,458 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*---------------------- USB 3.0 Link PM functions ------------------------*/
|
||||
|
||||
/* Service interval in nanoseconds = 2^(bInterval - 1) * 125us * 1000ns / 1us */
|
||||
static unsigned long long xhci_service_interval_to_ns(
|
||||
struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
return (1 << (desc->bInterval - 1)) * 125 * 1000;
|
||||
}
|
||||
|
||||
static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
unsigned long long sel;
|
||||
unsigned long long pel;
|
||||
unsigned int max_sel_pel;
|
||||
char *state_name;
|
||||
|
||||
switch (state) {
|
||||
case USB3_LPM_U1:
|
||||
/* Convert SEL and PEL stored in nanoseconds to microseconds */
|
||||
sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
|
||||
pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
|
||||
max_sel_pel = USB3_LPM_MAX_U1_SEL_PEL;
|
||||
state_name = "U1";
|
||||
break;
|
||||
case USB3_LPM_U2:
|
||||
sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
|
||||
pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
|
||||
max_sel_pel = USB3_LPM_MAX_U2_SEL_PEL;
|
||||
state_name = "U2";
|
||||
break;
|
||||
default:
|
||||
dev_warn(&udev->dev, "%s: Can't get timeout for non-U1 or U2 state.\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sel <= max_sel_pel && pel <= max_sel_pel)
|
||||
return USB3_LPM_DEVICE_INITIATED;
|
||||
|
||||
if (sel > max_sel_pel)
|
||||
dev_dbg(&udev->dev, "Device-initiated %s disabled "
|
||||
"due to long SEL %llu ms\n",
|
||||
state_name, sel);
|
||||
else
|
||||
dev_dbg(&udev->dev, "Device-initiated %s disabled "
|
||||
"due to long PEL %llu\n ms",
|
||||
state_name, pel);
|
||||
return USB3_LPM_DISABLED;
|
||||
}
|
||||
|
||||
/* Returns the hub-encoded U1 timeout value.
|
||||
* The U1 timeout should be the maximum of the following values:
|
||||
* - For control endpoints, U1 system exit latency (SEL) * 3
|
||||
* - For bulk endpoints, U1 SEL * 5
|
||||
* - For interrupt endpoints:
|
||||
* - Notification EPs, U1 SEL * 3
|
||||
* - Periodic EPs, max(105% of bInterval, U1 SEL * 2)
|
||||
* - For isochronous endpoints, max(105% of bInterval, U1 SEL * 2)
|
||||
*/
|
||||
static u16 xhci_calculate_intel_u1_timeout(struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
unsigned long long timeout_ns;
|
||||
int ep_type;
|
||||
int intr_type;
|
||||
|
||||
ep_type = usb_endpoint_type(desc);
|
||||
switch (ep_type) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
timeout_ns = udev->u1_params.sel * 3;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
timeout_ns = udev->u1_params.sel * 5;
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_INT:
|
||||
intr_type = usb_endpoint_interrupt_type(desc);
|
||||
if (intr_type == USB_ENDPOINT_INTR_NOTIFICATION) {
|
||||
timeout_ns = udev->u1_params.sel * 3;
|
||||
break;
|
||||
}
|
||||
/* Otherwise the calculation is the same as isoc eps */
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
timeout_ns = xhci_service_interval_to_ns(desc);
|
||||
timeout_ns = DIV_ROUND_UP(timeout_ns * 105, 100);
|
||||
if (timeout_ns < udev->u1_params.sel * 2)
|
||||
timeout_ns = udev->u1_params.sel * 2;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The U1 timeout is encoded in 1us intervals. */
|
||||
timeout_ns = DIV_ROUND_UP(timeout_ns, 1000);
|
||||
/* Don't return a timeout of zero, because that's USB3_LPM_DISABLED. */
|
||||
if (timeout_ns == USB3_LPM_DISABLED)
|
||||
timeout_ns++;
|
||||
|
||||
/* If the necessary timeout value is bigger than what we can set in the
|
||||
* USB 3.0 hub, we have to disable hub-initiated U1.
|
||||
*/
|
||||
if (timeout_ns <= USB3_LPM_U1_MAX_TIMEOUT)
|
||||
return timeout_ns;
|
||||
dev_dbg(&udev->dev, "Hub-initiated U1 disabled "
|
||||
"due to long timeout %llu ms\n", timeout_ns);
|
||||
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1);
|
||||
}
|
||||
|
||||
/* Returns the hub-encoded U2 timeout value.
|
||||
* The U2 timeout should be the maximum of:
|
||||
* - 10 ms (to avoid the bandwidth impact on the scheduler)
|
||||
* - largest bInterval of any active periodic endpoint (to avoid going
|
||||
* into lower power link states between intervals).
|
||||
* - the U2 Exit Latency of the device
|
||||
*/
|
||||
static u16 xhci_calculate_intel_u2_timeout(struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
unsigned long long timeout_ns;
|
||||
unsigned long long u2_del_ns;
|
||||
|
||||
timeout_ns = 10 * 1000 * 1000;
|
||||
|
||||
if ((usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) &&
|
||||
(xhci_service_interval_to_ns(desc) > timeout_ns))
|
||||
timeout_ns = xhci_service_interval_to_ns(desc);
|
||||
|
||||
u2_del_ns = udev->bos->ss_cap->bU2DevExitLat * 1000;
|
||||
if (u2_del_ns > timeout_ns)
|
||||
timeout_ns = u2_del_ns;
|
||||
|
||||
/* The U2 timeout is encoded in 256us intervals */
|
||||
timeout_ns = DIV_ROUND_UP(timeout_ns, 256 * 1000);
|
||||
/* If the necessary timeout value is bigger than what we can set in the
|
||||
* USB 3.0 hub, we have to disable hub-initiated U2.
|
||||
*/
|
||||
if (timeout_ns <= USB3_LPM_U2_MAX_TIMEOUT)
|
||||
return timeout_ns;
|
||||
dev_dbg(&udev->dev, "Hub-initiated U2 disabled "
|
||||
"due to long timeout %llu ms\n", timeout_ns);
|
||||
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U2);
|
||||
}
|
||||
|
||||
static u16 xhci_call_host_update_timeout_for_endpoint(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
enum usb3_link_state state,
|
||||
u16 *timeout)
|
||||
{
|
||||
if (state == USB3_LPM_U1) {
|
||||
if (xhci->quirks & XHCI_INTEL_HOST)
|
||||
return xhci_calculate_intel_u1_timeout(udev, desc);
|
||||
} else {
|
||||
if (xhci->quirks & XHCI_INTEL_HOST)
|
||||
return xhci_calculate_intel_u2_timeout(udev, desc);
|
||||
}
|
||||
|
||||
return USB3_LPM_DISABLED;
|
||||
}
|
||||
|
||||
static int xhci_update_timeout_for_endpoint(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
enum usb3_link_state state,
|
||||
u16 *timeout)
|
||||
{
|
||||
u16 alt_timeout;
|
||||
|
||||
alt_timeout = xhci_call_host_update_timeout_for_endpoint(xhci, udev,
|
||||
desc, state, timeout);
|
||||
|
||||
/* If we found we can't enable hub-initiated LPM, or
|
||||
* the U1 or U2 exit latency was too high to allow
|
||||
* device-initiated LPM as well, just stop searching.
|
||||
*/
|
||||
if (alt_timeout == USB3_LPM_DISABLED ||
|
||||
alt_timeout == USB3_LPM_DEVICE_INITIATED) {
|
||||
*timeout = alt_timeout;
|
||||
return -E2BIG;
|
||||
}
|
||||
if (alt_timeout > *timeout)
|
||||
*timeout = alt_timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xhci_update_timeout_for_interface(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev,
|
||||
struct usb_host_interface *alt,
|
||||
enum usb3_link_state state,
|
||||
u16 *timeout)
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < alt->desc.bNumEndpoints; j++) {
|
||||
if (xhci_update_timeout_for_endpoint(xhci, udev,
|
||||
&alt->endpoint[j].desc, state, timeout))
|
||||
return -E2BIG;
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xhci_check_intel_tier_policy(struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
struct usb_device *parent;
|
||||
unsigned int num_hubs;
|
||||
|
||||
if (state == USB3_LPM_U2)
|
||||
return 0;
|
||||
|
||||
/* Don't enable U1 if the device is on a 2nd tier hub or lower. */
|
||||
for (parent = udev->parent, num_hubs = 0; parent->parent;
|
||||
parent = parent->parent)
|
||||
num_hubs++;
|
||||
|
||||
if (num_hubs < 2)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&udev->dev, "Disabling U1 link state for device"
|
||||
" below second-tier hub.\n");
|
||||
dev_dbg(&udev->dev, "Plug device into first-tier hub "
|
||||
"to decrease power consumption.\n");
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
static int xhci_check_tier_policy(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev,
|
||||
enum usb3_link_state state)
|
||||
{
|
||||
if (xhci->quirks & XHCI_INTEL_HOST)
|
||||
return xhci_check_intel_tier_policy(udev, state);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Returns the U1 or U2 timeout that should be enabled.
|
||||
* If the tier check or timeout setting functions return with a non-zero exit
|
||||
* code, that means the timeout value has been finalized and we shouldn't look
|
||||
* at any more endpoints.
|
||||
*/
|
||||
static u16 xhci_calculate_lpm_timeout(struct usb_hcd *hcd,
|
||||
struct usb_device *udev, enum usb3_link_state state)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct usb_host_config *config;
|
||||
char *state_name;
|
||||
int i;
|
||||
u16 timeout = USB3_LPM_DISABLED;
|
||||
|
||||
if (state == USB3_LPM_U1)
|
||||
state_name = "U1";
|
||||
else if (state == USB3_LPM_U2)
|
||||
state_name = "U2";
|
||||
else {
|
||||
dev_warn(&udev->dev, "Can't enable unknown link state %i\n",
|
||||
state);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
if (xhci_check_tier_policy(xhci, udev, state) < 0)
|
||||
return timeout;
|
||||
|
||||
/* Gather some information about the currently installed configuration
|
||||
* and alternate interface settings.
|
||||
*/
|
||||
if (xhci_update_timeout_for_endpoint(xhci, udev, &udev->ep0.desc,
|
||||
state, &timeout))
|
||||
return timeout;
|
||||
|
||||
config = udev->actconfig;
|
||||
if (!config)
|
||||
return timeout;
|
||||
|
||||
for (i = 0; i < USB_MAXINTERFACES; i++) {
|
||||
struct usb_driver *driver;
|
||||
struct usb_interface *intf = config->interface[i];
|
||||
|
||||
if (!intf)
|
||||
continue;
|
||||
|
||||
/* Check if any currently bound drivers want hub-initiated LPM
|
||||
* disabled.
|
||||
*/
|
||||
if (intf->dev.driver) {
|
||||
driver = to_usb_driver(intf->dev.driver);
|
||||
if (driver && driver->disable_hub_initiated_lpm) {
|
||||
dev_dbg(&udev->dev, "Hub-initiated %s disabled "
|
||||
"at request of driver %s\n",
|
||||
state_name, driver->name);
|
||||
return xhci_get_timeout_no_hub_lpm(udev, state);
|
||||
}
|
||||
}
|
||||
|
||||
/* Not sure how this could happen... */
|
||||
if (!intf->cur_altsetting)
|
||||
continue;
|
||||
|
||||
if (xhci_update_timeout_for_interface(xhci, udev,
|
||||
intf->cur_altsetting,
|
||||
state, &timeout))
|
||||
return timeout;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue an Evaluate Context command to change the Maximum Exit Latency in the
|
||||
* slot context. If that succeeds, store the new MEL in the xhci_virt_device.
|
||||
*/
|
||||
static int xhci_change_max_exit_latency(struct xhci_hcd *xhci,
|
||||
struct usb_device *udev, u16 max_exit_latency)
|
||||
{
|
||||
struct xhci_virt_device *virt_dev;
|
||||
struct xhci_command *command;
|
||||
struct xhci_input_control_ctx *ctrl_ctx;
|
||||
struct xhci_slot_ctx *slot_ctx;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
if (max_exit_latency == xhci->devs[udev->slot_id]->current_mel) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Attempt to issue an Evaluate Context command to change the MEL. */
|
||||
virt_dev = xhci->devs[udev->slot_id];
|
||||
command = xhci->lpm_command;
|
||||
xhci_slot_copy(xhci, command->in_ctx, virt_dev->out_ctx);
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
|
||||
ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
|
||||
ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
|
||||
slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx);
|
||||
slot_ctx->dev_info2 &= cpu_to_le32(~((u32) MAX_EXIT));
|
||||
slot_ctx->dev_info2 |= cpu_to_le32(max_exit_latency);
|
||||
|
||||
xhci_dbg(xhci, "Set up evaluate context for LPM MEL change.\n");
|
||||
xhci_dbg(xhci, "Slot %u Input Context:\n", udev->slot_id);
|
||||
xhci_dbg_ctx(xhci, command->in_ctx, 0);
|
||||
|
||||
/* Issue and wait for the evaluate context command. */
|
||||
ret = xhci_configure_endpoint(xhci, udev, command,
|
||||
true, true);
|
||||
xhci_dbg(xhci, "Slot %u Output Context:\n", udev->slot_id);
|
||||
xhci_dbg_ctx(xhci, virt_dev->out_ctx, 0);
|
||||
|
||||
if (!ret) {
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
virt_dev->current_mel = max_exit_latency;
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int calculate_max_exit_latency(struct usb_device *udev,
|
||||
enum usb3_link_state state_changed,
|
||||
u16 hub_encoded_timeout)
|
||||
{
|
||||
unsigned long long u1_mel_us = 0;
|
||||
unsigned long long u2_mel_us = 0;
|
||||
unsigned long long mel_us = 0;
|
||||
bool disabling_u1;
|
||||
bool disabling_u2;
|
||||
bool enabling_u1;
|
||||
bool enabling_u2;
|
||||
|
||||
disabling_u1 = (state_changed == USB3_LPM_U1 &&
|
||||
hub_encoded_timeout == USB3_LPM_DISABLED);
|
||||
disabling_u2 = (state_changed == USB3_LPM_U2 &&
|
||||
hub_encoded_timeout == USB3_LPM_DISABLED);
|
||||
|
||||
enabling_u1 = (state_changed == USB3_LPM_U1 &&
|
||||
hub_encoded_timeout != USB3_LPM_DISABLED);
|
||||
enabling_u2 = (state_changed == USB3_LPM_U2 &&
|
||||
hub_encoded_timeout != USB3_LPM_DISABLED);
|
||||
|
||||
/* If U1 was already enabled and we're not disabling it,
|
||||
* or we're going to enable U1, account for the U1 max exit latency.
|
||||
*/
|
||||
if ((udev->u1_params.timeout != USB3_LPM_DISABLED && !disabling_u1) ||
|
||||
enabling_u1)
|
||||
u1_mel_us = DIV_ROUND_UP(udev->u1_params.mel, 1000);
|
||||
if ((udev->u2_params.timeout != USB3_LPM_DISABLED && !disabling_u2) ||
|
||||
enabling_u2)
|
||||
u2_mel_us = DIV_ROUND_UP(udev->u2_params.mel, 1000);
|
||||
|
||||
if (u1_mel_us > u2_mel_us)
|
||||
mel_us = u1_mel_us;
|
||||
else
|
||||
mel_us = u2_mel_us;
|
||||
/* xHCI host controller max exit latency field is only 16 bits wide. */
|
||||
if (mel_us > MAX_EXIT) {
|
||||
dev_warn(&udev->dev, "Link PM max exit latency of %lluus "
|
||||
"is too big.\n", mel_us);
|
||||
return -E2BIG;
|
||||
}
|
||||
return mel_us;
|
||||
}
|
||||
|
||||
/* Returns the USB3 hub-encoded value for the U1/U2 timeout. */
|
||||
int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
|
||||
struct usb_device *udev, enum usb3_link_state state)
|
||||
{
|
||||
struct xhci_hcd *xhci;
|
||||
u16 hub_encoded_timeout;
|
||||
int mel;
|
||||
int ret;
|
||||
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
/* The LPM timeout values are pretty host-controller specific, so don't
|
||||
* enable hub-initiated timeouts unless the vendor has provided
|
||||
* information about their timeout algorithm.
|
||||
*/
|
||||
if (!xhci || !(xhci->quirks & XHCI_LPM_SUPPORT) ||
|
||||
!xhci->devs[udev->slot_id])
|
||||
return USB3_LPM_DISABLED;
|
||||
|
||||
hub_encoded_timeout = xhci_calculate_lpm_timeout(hcd, udev, state);
|
||||
mel = calculate_max_exit_latency(udev, state, hub_encoded_timeout);
|
||||
if (mel < 0) {
|
||||
/* Max Exit Latency is too big, disable LPM. */
|
||||
hub_encoded_timeout = USB3_LPM_DISABLED;
|
||||
mel = 0;
|
||||
}
|
||||
|
||||
ret = xhci_change_max_exit_latency(xhci, udev, mel);
|
||||
if (ret)
|
||||
return ret;
|
||||
return hub_encoded_timeout;
|
||||
}
|
||||
|
||||
int xhci_disable_usb3_lpm_timeout(struct usb_hcd *hcd,
|
||||
struct usb_device *udev, enum usb3_link_state state)
|
||||
{
|
||||
struct xhci_hcd *xhci;
|
||||
u16 mel;
|
||||
int ret;
|
||||
|
||||
xhci = hcd_to_xhci(hcd);
|
||||
if (!xhci || !(xhci->quirks & XHCI_LPM_SUPPORT) ||
|
||||
!xhci->devs[udev->slot_id])
|
||||
return 0;
|
||||
|
||||
mel = calculate_max_exit_latency(udev, state, USB3_LPM_DISABLED);
|
||||
ret = xhci_change_max_exit_latency(xhci, udev, mel);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
|
|
@ -362,8 +362,10 @@ struct xhci_op_regs {
|
|||
* Timeout can be up to 127us. 0xFF means an infinite timeout.
|
||||
*/
|
||||
#define PORT_U1_TIMEOUT(p) ((p) & 0xff)
|
||||
#define PORT_U1_TIMEOUT_MASK 0xff
|
||||
/* Inactivity timer value for transitions into U2 */
|
||||
#define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8)
|
||||
#define PORT_U2_TIMEOUT_MASK (0xff << 8)
|
||||
/* Bits 24:31 for port testing */
|
||||
|
||||
/* USB2 Protocol PORTSPMSC */
|
||||
|
@ -914,6 +916,8 @@ struct xhci_virt_device {
|
|||
u8 real_port;
|
||||
struct xhci_interval_bw_table *bw_table;
|
||||
struct xhci_tt_bw_info *tt_info;
|
||||
/* The current max exit latency for the enabled USB3 link states. */
|
||||
u16 current_mel;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1424,6 +1428,8 @@ struct xhci_hcd {
|
|||
/* slot enabling and address device helpers */
|
||||
struct completion addr_dev;
|
||||
int slot_id;
|
||||
/* For USB 3.0 LPM enable/disable. */
|
||||
struct xhci_command *lpm_command;
|
||||
/* Internal mirror of the HW's dcbaa */
|
||||
struct xhci_virt_device *devs[MAX_HC_SLOTS];
|
||||
/* For keeping track of bandwidth domains per roothub. */
|
||||
|
@ -1481,6 +1487,9 @@ struct xhci_hcd {
|
|||
#define XHCI_RESET_ON_RESUME (1 << 7)
|
||||
#define XHCI_SW_BW_CHECKING (1 << 8)
|
||||
#define XHCI_AMD_0x96_HOST (1 << 9)
|
||||
#define XHCI_TRUST_TX_LENGTH (1 << 10)
|
||||
#define XHCI_LPM_SUPPORT (1 << 11)
|
||||
#define XHCI_INTEL_HOST (1 << 12)
|
||||
unsigned int num_active_eps;
|
||||
unsigned int limit_active_eps;
|
||||
/* There are two roothubs to keep track of bus suspend info for */
|
||||
|
@ -1754,7 +1763,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||
u32 slot_id, bool command_must_succeed);
|
||||
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
|
||||
u32 slot_id);
|
||||
u32 slot_id, bool command_must_succeed);
|
||||
int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id,
|
||||
unsigned int ep_index);
|
||||
int xhci_queue_reset_device(struct xhci_hcd *xhci, u32 slot_id);
|
||||
|
@ -1778,6 +1787,10 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
|
|||
/* xHCI roothub code */
|
||||
void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
||||
int port_id, u32 link_state);
|
||||
int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
|
||||
struct usb_device *udev, enum usb3_link_state state);
|
||||
int xhci_disable_usb3_lpm_timeout(struct usb_hcd *hcd,
|
||||
struct usb_device *udev, enum usb3_link_state state);
|
||||
void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
|
||||
int port_id, u32 port_bit);
|
||||
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
|
||||
|
|
|
@ -378,6 +378,45 @@ enum usb_device_removable {
|
|||
USB_DEVICE_FIXED,
|
||||
};
|
||||
|
||||
/*
|
||||
* USB 3.0 Link Power Management (LPM) parameters.
|
||||
*
|
||||
* PEL and SEL are USB 3.0 Link PM latencies for device-initiated LPM exit.
|
||||
* MEL is the USB 3.0 Link PM latency for host-initiated LPM exit.
|
||||
* All three are stored in nanoseconds.
|
||||
*/
|
||||
struct usb3_lpm_parameters {
|
||||
/*
|
||||
* Maximum exit latency (MEL) for the host to send a packet to the
|
||||
* device (either a Ping for isoc endpoints, or a data packet for
|
||||
* interrupt endpoints), the hubs to decode the packet, and for all hubs
|
||||
* in the path to transition the links to U0.
|
||||
*/
|
||||
unsigned int mel;
|
||||
/*
|
||||
* Maximum exit latency for a device-initiated LPM transition to bring
|
||||
* all links into U0. Abbreviated as "PEL" in section 9.4.12 of the USB
|
||||
* 3.0 spec, with no explanation of what "P" stands for. "Path"?
|
||||
*/
|
||||
unsigned int pel;
|
||||
|
||||
/*
|
||||
* The System Exit Latency (SEL) includes PEL, and three other
|
||||
* latencies. After a device initiates a U0 transition, it will take
|
||||
* some time from when the device sends the ERDY to when it will finally
|
||||
* receive the data packet. Basically, SEL should be the worse-case
|
||||
* latency from when a device starts initiating a U0 transition to when
|
||||
* it will get data.
|
||||
*/
|
||||
unsigned int sel;
|
||||
/*
|
||||
* The idle timeout value that is currently programmed into the parent
|
||||
* hub for this device. When the timer counts to zero, the parent hub
|
||||
* will initiate an LPM transition to either U1 or U2.
|
||||
*/
|
||||
int timeout;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct usb_device - kernel's representation of a USB device
|
||||
* @devnum: device number; address on a USB bus
|
||||
|
@ -435,6 +474,12 @@ enum usb_device_removable {
|
|||
* specific data for the device.
|
||||
* @slot_id: Slot ID assigned by xHCI
|
||||
* @removable: Device can be physically removed from this port
|
||||
* @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout.
|
||||
* @u2_params: exit latencies for USB3 U2 LPM state, and hub-initiated timeout.
|
||||
* @lpm_disable_count: Ref count used by usb_disable_lpm() and usb_enable_lpm()
|
||||
* to keep track of the number of functions that require USB 3.0 Link Power
|
||||
* Management to be disabled for this usb_device. This count should only
|
||||
* be manipulated by those functions, with the bandwidth_mutex is held.
|
||||
*
|
||||
* Notes:
|
||||
* Usbcore drivers should not set usbdev->state directly. Instead use
|
||||
|
@ -481,6 +526,7 @@ struct usb_device {
|
|||
unsigned lpm_capable:1;
|
||||
unsigned usb2_hw_lpm_capable:1;
|
||||
unsigned usb2_hw_lpm_enabled:1;
|
||||
unsigned usb3_lpm_enabled:1;
|
||||
int string_langid;
|
||||
|
||||
/* static strings from the device */
|
||||
|
@ -507,6 +553,10 @@ struct usb_device {
|
|||
struct wusb_dev *wusb_dev;
|
||||
int slot_id;
|
||||
enum usb_device_removable removable;
|
||||
struct usb3_lpm_parameters u1_params;
|
||||
struct usb3_lpm_parameters u2_params;
|
||||
unsigned lpm_disable_count;
|
||||
unsigned hub_initiated_lpm_disable_count;
|
||||
};
|
||||
#define to_usb_device(d) container_of(d, struct usb_device, dev)
|
||||
|
||||
|
@ -542,6 +592,12 @@ extern void usb_autopm_put_interface_async(struct usb_interface *intf);
|
|||
extern void usb_autopm_get_interface_no_resume(struct usb_interface *intf);
|
||||
extern void usb_autopm_put_interface_no_suspend(struct usb_interface *intf);
|
||||
|
||||
extern int usb_disable_lpm(struct usb_device *udev);
|
||||
extern void usb_enable_lpm(struct usb_device *udev);
|
||||
/* Same as above, but these functions lock/unlock the bandwidth_mutex. */
|
||||
extern int usb_unlocked_disable_lpm(struct usb_device *udev);
|
||||
extern void usb_unlocked_enable_lpm(struct usb_device *udev);
|
||||
|
||||
static inline void usb_mark_last_busy(struct usb_device *udev)
|
||||
{
|
||||
pm_runtime_mark_last_busy(&udev->dev);
|
||||
|
@ -842,6 +898,9 @@ struct usbdrv_wrap {
|
|||
* for interfaces bound to this driver.
|
||||
* @soft_unbind: if set to 1, the USB core will not kill URBs and disable
|
||||
* endpoints before calling the driver's disconnect method.
|
||||
* @disable_hub_initiated_lpm: if set to 0, the USB core will not allow hubs
|
||||
* to initiate lower power link state transitions when an idle timeout
|
||||
* occurs. Device-initiated USB 3.0 link PM will still be allowed.
|
||||
*
|
||||
* USB interface drivers must provide a name, probe() and disconnect()
|
||||
* methods, and an id_table. Other driver fields are optional.
|
||||
|
@ -882,6 +941,7 @@ struct usb_driver {
|
|||
struct usbdrv_wrap drvwrap;
|
||||
unsigned int no_dynamic_id:1;
|
||||
unsigned int supports_autosuspend:1;
|
||||
unsigned int disable_hub_initiated_lpm:1;
|
||||
unsigned int soft_unbind:1;
|
||||
};
|
||||
#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
|
||||
|
|
|
@ -76,6 +76,8 @@
|
|||
#define USB_PORT_FEAT_C_BH_PORT_RESET 29
|
||||
#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30
|
||||
|
||||
#define USB_PORT_LPM_TIMEOUT(p) (((p) & 0xff) << 8)
|
||||
|
||||
/* USB 3.0 hub remote wake mask bits, see table 10-14 */
|
||||
#define USB_PORT_FEAT_REMOTE_WAKE_CONNECT (1 << 8)
|
||||
#define USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT (1 << 9)
|
||||
|
|
|
@ -392,6 +392,11 @@ struct usb_endpoint_descriptor {
|
|||
#define USB_ENDPOINT_XFER_INT 3
|
||||
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
|
||||
|
||||
/* The USB 3.0 spec redefines bits 5:4 of bmAttributes as interrupt ep type. */
|
||||
#define USB_ENDPOINT_INTRTYPE 0x30
|
||||
#define USB_ENDPOINT_INTR_PERIODIC (0 << 4)
|
||||
#define USB_ENDPOINT_INTR_NOTIFICATION (1 << 4)
|
||||
|
||||
#define USB_ENDPOINT_SYNCTYPE 0x0c
|
||||
#define USB_ENDPOINT_SYNC_NONE (0 << 2)
|
||||
#define USB_ENDPOINT_SYNC_ASYNC (1 << 2)
|
||||
|
@ -594,6 +599,12 @@ static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd)
|
|||
return __le16_to_cpu(epd->wMaxPacketSize);
|
||||
}
|
||||
|
||||
static inline int usb_endpoint_interrupt_type(
|
||||
const struct usb_endpoint_descriptor *epd)
|
||||
{
|
||||
return epd->bmAttributes & USB_ENDPOINT_INTRTYPE;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */
|
||||
|
@ -935,6 +946,51 @@ enum usb_device_state {
|
|||
*/
|
||||
};
|
||||
|
||||
enum usb3_link_state {
|
||||
USB3_LPM_U0 = 0,
|
||||
USB3_LPM_U1,
|
||||
USB3_LPM_U2,
|
||||
USB3_LPM_U3
|
||||
};
|
||||
|
||||
/*
|
||||
* A U1 timeout of 0x0 means the parent hub will reject any transitions to U1.
|
||||
* 0xff means the parent hub will accept transitions to U1, but will not
|
||||
* initiate a transition.
|
||||
*
|
||||
* A U1 timeout of 0x1 to 0x7F also causes the hub to initiate a transition to
|
||||
* U1 after that many microseconds. Timeouts of 0x80 to 0xFE are reserved
|
||||
* values.
|
||||
*
|
||||
* A U2 timeout of 0x0 means the parent hub will reject any transitions to U2.
|
||||
* 0xff means the parent hub will accept transitions to U2, but will not
|
||||
* initiate a transition.
|
||||
*
|
||||
* A U2 timeout of 0x1 to 0xFE also causes the hub to initiate a transition to
|
||||
* U2 after N*256 microseconds. Therefore a U2 timeout value of 0x1 means a U2
|
||||
* idle timer of 256 microseconds, 0x2 means 512 microseconds, 0xFE means
|
||||
* 65.024ms.
|
||||
*/
|
||||
#define USB3_LPM_DISABLED 0x0
|
||||
#define USB3_LPM_U1_MAX_TIMEOUT 0x7F
|
||||
#define USB3_LPM_U2_MAX_TIMEOUT 0xFE
|
||||
#define USB3_LPM_DEVICE_INITIATED 0xFF
|
||||
|
||||
struct usb_set_sel_req {
|
||||
__u8 u1_sel;
|
||||
__u8 u1_pel;
|
||||
__le16 u2_sel;
|
||||
__le16 u2_pel;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* The Set System Exit Latency control transfer provides one byte each for
|
||||
* U1 SEL and U1 PEL, so the max exit latency is 0xFF. U2 SEL and U2 PEL each
|
||||
* are two bytes long.
|
||||
*/
|
||||
#define USB3_LPM_MAX_U1_SEL_PEL 0xFF
|
||||
#define USB3_LPM_MAX_U2_SEL_PEL 0xFFFF
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
|
|
|
@ -344,6 +344,15 @@ struct hc_driver {
|
|||
*/
|
||||
int (*update_device)(struct usb_hcd *, struct usb_device *);
|
||||
int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int);
|
||||
/* USB 3.0 Link Power Management */
|
||||
/* Returns the USB3 hub-encoded value for the U1/U2 timeout. */
|
||||
int (*enable_usb3_lpm_timeout)(struct usb_hcd *,
|
||||
struct usb_device *, enum usb3_link_state state);
|
||||
/* The xHCI host controller can still fail the command to
|
||||
* disable the LPM timeouts, so this can return an error code.
|
||||
*/
|
||||
int (*disable_usb3_lpm_timeout)(struct usb_hcd *,
|
||||
struct usb_device *, enum usb3_link_state state);
|
||||
};
|
||||
|
||||
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||
|
|
Loading…
Reference in New Issue