xHCI: BESL calculation based on USB2.0 LPM errata
authorAndiry Xu <andiry.xu@amd.com>
Mon, 12 Dec 2011 08:45:28 +0000 (16:45 +0800)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Mon, 12 Mar 2012 16:31:24 +0000 (09:31 -0700)
The latest released errata for USB2.0 ECN LPM adds new fields to USB2.0
extension descriptor, defines two BESL values for device: baseline BESL
and deep BESL. Baseline BESL value communicates a nominal power savings
design point and the deep BESL value communicates a significant power
savings design point.

If device indicates BESL value, driver will use a value count in both
host BESL and device BESL. Use baseline BESL value as default.

Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Tested-by: Jason Fan <jcfan@qca.qualcomm.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
drivers/usb/host/xhci.c
include/linux/usb/ch9.h

index a629ad8..262400c 100644 (file)
@@ -3614,26 +3614,38 @@ static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000,
        3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000};
 
 /* Calculate HIRD/BESL for USB2 PORTPMSC*/
-static int xhci_calculate_hird_besl(int u2del, bool use_besl)
+static int xhci_calculate_hird_besl(struct xhci_hcd *xhci,
+                                       struct usb_device *udev)
 {
-       int hird;
+       int u2del, besl, besl_host;
+       int besl_device = 0;
+       u32 field;
+
+       u2del = HCS_U2_LATENCY(xhci->hcs_params3);
+       field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
 
-       if (use_besl) {
-               for (hird = 0; hird < 16; hird++) {
-                       if (xhci_besl_encoding[hird] >= u2del)
+       if (field & USB_BESL_SUPPORT) {
+               for (besl_host = 0; besl_host < 16; besl_host++) {
+                       if (xhci_besl_encoding[besl_host] >= u2del)
                                break;
                }
+               /* Use baseline BESL value as default */
+               if (field & USB_BESL_BASELINE_VALID)
+                       besl_device = USB_GET_BESL_BASELINE(field);
+               else if (field & USB_BESL_DEEP_VALID)
+                       besl_device = USB_GET_BESL_DEEP(field);
        } else {
                if (u2del <= 50)
-                       hird = 0;
+                       besl_host = 0;
                else
-                       hird = (u2del - 51) / 75 + 1;
-
-               if (hird > 15)
-                       hird = 15;
+                       besl_host = (u2del - 51) / 75 + 1;
        }
 
-       return hird;
+       besl = besl_host + besl_device;
+       if (besl > 15)
+               besl = 15;
+
+       return besl;
 }
 
 static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
@@ -3646,7 +3658,7 @@ static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
        u32             temp, dev_id;
        unsigned int    port_num;
        unsigned long   flags;
-       int             u2del, hird;
+       int             hird;
        int             ret;
 
        if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
@@ -3692,12 +3704,7 @@ static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
         * HIRD or BESL shoule be used. See USB2.0 LPM errata.
         */
        pm_addr = port_array[port_num] + 1;
-       u2del = HCS_U2_LATENCY(xhci->hcs_params3);
-       if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2))
-               hird = xhci_calculate_hird_besl(u2del, 1);
-       else
-               hird = xhci_calculate_hird_besl(u2del, 0);
-
+       hird = xhci_calculate_hird_besl(xhci, udev);
        temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
        xhci_writel(xhci, temp, pm_addr);
 
@@ -3776,7 +3783,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
        u32             temp;
        unsigned int    port_num;
        unsigned long   flags;
-       int             u2del, hird;
+       int             hird;
 
        if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support ||
                        !udev->lpm_capable)
@@ -3799,11 +3806,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
        xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
                        enable ? "enable" : "disable", port_num);
 
-       u2del = HCS_U2_LATENCY(xhci->hcs_params3);
-       if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2))
-               hird = xhci_calculate_hird_besl(u2del, 1);
-       else
-               hird = xhci_calculate_hird_besl(u2del, 0);
+       hird = xhci_calculate_hird_besl(xhci, udev);
 
        if (enable) {
                temp &= ~PORT_HIRD_MASK;
index 3b6f628..af21f31 100644 (file)
@@ -789,6 +789,11 @@ struct usb_ext_cap_descriptor {            /* Link Power Management */
        __u8  bDevCapabilityType;
        __le32 bmAttributes;
 #define USB_LPM_SUPPORT                        (1 << 1)        /* supports LPM */
+#define USB_BESL_SUPPORT               (1 << 2)        /* supports BESL */
+#define USB_BESL_BASELINE_VALID                (1 << 3)        /* Baseline BESL valid*/
+#define USB_BESL_DEEP_VALID            (1 << 4)        /* Deep BESL valid */
+#define USB_GET_BESL_BASELINE(p)       (((p) & (0xf << 8)) >> 8)
+#define USB_GET_BESL_DEEP(p)           (((p) & (0xf << 12)) >> 12)
 } __attribute__((packed));
 
 #define USB_DT_USB_EXT_CAP_SIZE        7