* Andiry Xu | 2011-08-17 18:01:42 [+0800]: >This patch tests USB2 software LPM for a USB2 LPM-capable device. > >When a lpm-capable device is addressed, if the host also supports software >LPM, apply a test by putting the device into L1 state and resume it to see >if the device can do L1 suspend/resume successfully. > >If the device fails to enter L1 or resume from L1 state, it may not >function normally and usbcore may disconnect and re-enumerate it. In this >case, store the device's Vid and Pid information, make sure the host will >not test LPM for it twice. > >The test result is per device/host. Some devices claim to be lpm-capable, >but fail to enter L1 or resume. The behavior is also different on different >IP-vendor hosts: one device passes the test on a host but fails to resume >on another with different IP vendor. The one IPcore that fails on one device, does it also fail on other devices or does it pass on other devices? So pattern is something like: IP Core A B C device A ok ok fail device B fail fail ok device C fail ok fail i.e. totally random but reproducible mapping between IP core and device? Do you see any difference with a USB sniffer? >diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c >index 3a0f695..fce413a 100644 >--- a/drivers/usb/host/xhci.c >+++ b/drivers/usb/host/xhci.c >@@ -37,6 +37,10 @@ static int link_quirk; > module_param(link_quirk, int, S_IRUGO | S_IWUSR); > MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB"); > >+/* BESL to HIRD Encoding array for USB2 LPM */ >+static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000, >+ 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000}; >+ > /* TODO: copied from ehci-hcd.c - can this be refactored? */ > /* > * handshake - spin reading hc until handshake completes or fails >@@ -2957,6 +2961,166 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) > return 0; > } > >+/* Calculate HIRD/BESL for USB2 PORTPMSC*/ >+static int xhci_calculate_hird_besl(int u2del, bool use_besl) >+{ >+ int hird; >+ >+ if (use_besl) { >+ for (hird = 0; hird < 16; hird++) { ARRAY_SIZE_OF >+ if (xhci_besl_encoding[hird] >= u2del) >+ break; >+ } >+ } else { >+ if (u2del <= 50) >+ hird = 0; >+ else >+ hird = (u2del - 51) / 75 + 1; >+ >+ if (hird > 15) >+ hird = 15; >+ } >+ >+ return hird; >+} >+ >+static int xhci_usb2_software_lpm_test(struct xhci_hcd *xhci, >+ struct usb_device *udev, unsigned long flags) >+{ >+ struct usb_hcd *hcd = xhci_to_hcd(xhci); >+ struct dev_info *dev_info; >+ __le32 __iomem **port_array; >+ __le32 __iomem *addr, *pm_addr; >+ u32 temp, dev_id; >+ unsigned int port_num; >+ int u2del, hird; >+ int ret; >+ >+ if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || >+ !udev->lpm_capable) >+ return -EINVAL; >+ >+ /* we only support lpm for non-hub device connected to root hub yet */ >+ if (!udev->parent || udev->parent->parent || >+ udev->descriptor.bDeviceClass == USB_CLASS_HUB) >+ return -EINVAL; >+ >+ /* Look for devices in lpm_failed_devs list */ >+ dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 | >+ le16_to_cpu(udev->descriptor.idProduct); >+ list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) { >+ if (dev_info->dev_id == dev_id) >+ return -EINVAL; >+ } I'm still not really convinced about the list for each device. If the device passes the test you re-test it on the next plug-in. So why not re-test it in the fail case if your static array is full? Can you quote some numbers about the time a successfull and a failed test needs? According to the code would say about 20ms in both cases (unless it is already in the list). Sebastian -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html