aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathias Nyman <mathias.nyman@linux.intel.com>2013-05-23 10:14:28 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2013-06-05 19:46:19 -0400
commitb630d4b9d05ba2e66878ca4614946d0f950d4111 (patch)
treebd4e9e106016f4dd3d0bd7400d6d21b2d49a38ef
parent851ec164b14aa9dacafd6edb099e76a4cc9d06ec (diff)
usb: xhci: check usb2 port capabilities before adding hw link PM support
Hardware link powermanagement in usb2 is a per-port capability. Previously support for hw lpm was enabled for all ports if any usb2 port supported it. Now instead cache the capability values and check them for each port individually Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
-rw-r--r--drivers/usb/host/xhci-mem.c33
-rw-r--r--drivers/usb/host/xhci.c27
-rw-r--r--drivers/usb/host/xhci.h3
3 files changed, 58 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 2cfc465925bd..832f05ede427 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1852,6 +1852,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
1852 kfree(xhci->usb3_ports); 1852 kfree(xhci->usb3_ports);
1853 kfree(xhci->port_array); 1853 kfree(xhci->port_array);
1854 kfree(xhci->rh_bw); 1854 kfree(xhci->rh_bw);
1855 kfree(xhci->ext_caps);
1855 1856
1856 xhci->page_size = 0; 1857 xhci->page_size = 0;
1857 xhci->page_shift = 0; 1858 xhci->page_shift = 0;
@@ -2039,7 +2040,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
2039} 2040}
2040 2041
2041static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, 2042static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
2042 __le32 __iomem *addr, u8 major_revision) 2043 __le32 __iomem *addr, u8 major_revision, int max_caps)
2043{ 2044{
2044 u32 temp, port_offset, port_count; 2045 u32 temp, port_offset, port_count;
2045 int i; 2046 int i;
@@ -2064,6 +2065,10 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
2064 /* WTF? "Valid values are ‘1’ to MaxPorts" */ 2065 /* WTF? "Valid values are ‘1’ to MaxPorts" */
2065 return; 2066 return;
2066 2067
2068 /* cache usb2 port capabilities */
2069 if (major_revision < 0x03 && xhci->num_ext_caps < max_caps)
2070 xhci->ext_caps[xhci->num_ext_caps++] = temp;
2071
2067 /* Check the host's USB2 LPM capability */ 2072 /* Check the host's USB2 LPM capability */
2068 if ((xhci->hci_version == 0x96) && (major_revision != 0x03) && 2073 if ((xhci->hci_version == 0x96) && (major_revision != 0x03) &&
2069 (temp & XHCI_L1C)) { 2074 (temp & XHCI_L1C)) {
@@ -2121,10 +2126,11 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
2121 */ 2126 */
2122static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) 2127static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
2123{ 2128{
2124 __le32 __iomem *addr; 2129 __le32 __iomem *addr, *tmp_addr;
2125 u32 offset; 2130 u32 offset, tmp_offset;
2126 unsigned int num_ports; 2131 unsigned int num_ports;
2127 int i, j, port_index; 2132 int i, j, port_index;
2133 int cap_count = 0;
2128 2134
2129 addr = &xhci->cap_regs->hcc_params; 2135 addr = &xhci->cap_regs->hcc_params;
2130 offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr)); 2136 offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr));
@@ -2157,13 +2163,32 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
2157 * See section 5.3.6 for offset calculation. 2163 * See section 5.3.6 for offset calculation.
2158 */ 2164 */
2159 addr = &xhci->cap_regs->hc_capbase + offset; 2165 addr = &xhci->cap_regs->hc_capbase + offset;
2166
2167 tmp_addr = addr;
2168 tmp_offset = offset;
2169
2170 /* count extended protocol capability entries for later caching */
2171 do {
2172 u32 cap_id;
2173 cap_id = xhci_readl(xhci, tmp_addr);
2174 if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
2175 cap_count++;
2176 tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id);
2177 tmp_addr += tmp_offset;
2178 } while (tmp_offset);
2179
2180 xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
2181 if (!xhci->ext_caps)
2182 return -ENOMEM;
2183
2160 while (1) { 2184 while (1) {
2161 u32 cap_id; 2185 u32 cap_id;
2162 2186
2163 cap_id = xhci_readl(xhci, addr); 2187 cap_id = xhci_readl(xhci, addr);
2164 if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) 2188 if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
2165 xhci_add_in_port(xhci, num_ports, addr, 2189 xhci_add_in_port(xhci, num_ports, addr,
2166 (u8) XHCI_EXT_PORT_MAJOR(cap_id)); 2190 (u8) XHCI_EXT_PORT_MAJOR(cap_id),
2191 cap_count);
2167 offset = XHCI_EXT_CAPS_NEXT(cap_id); 2192 offset = XHCI_EXT_CAPS_NEXT(cap_id);
2168 if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports) 2193 if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports)
2169 == num_ports) 2194 == num_ports)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 4a05555bf6e3..df5e881e3bc5 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4033,15 +4033,40 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
4033 return 0; 4033 return 0;
4034} 4034}
4035 4035
4036/* check if a usb2 port supports a given extened capability protocol
4037 * only USB2 ports extended protocol capability values are cached.
4038 * Return 1 if capability is supported
4039 */
4040static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port,
4041 unsigned capability)
4042{
4043 u32 port_offset, port_count;
4044 int i;
4045
4046 for (i = 0; i < xhci->num_ext_caps; i++) {
4047 if (xhci->ext_caps[i] & capability) {
4048 /* port offsets starts at 1 */
4049 port_offset = XHCI_EXT_PORT_OFF(xhci->ext_caps[i]) - 1;
4050 port_count = XHCI_EXT_PORT_COUNT(xhci->ext_caps[i]);
4051 if (port >= port_offset &&
4052 port < port_offset + port_count)
4053 return 1;
4054 }
4055 }
4056 return 0;
4057}
4058
4036int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) 4059int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
4037{ 4060{
4038 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 4061 struct xhci_hcd *xhci = hcd_to_xhci(hcd);
4039 int ret; 4062 int ret;
4063 int portnum = udev->portnum - 1;
4040 4064
4041 ret = xhci_usb2_software_lpm_test(hcd, udev); 4065 ret = xhci_usb2_software_lpm_test(hcd, udev);
4042 if (!ret) { 4066 if (!ret) {
4043 xhci_dbg(xhci, "software LPM test succeed\n"); 4067 xhci_dbg(xhci, "software LPM test succeed\n");
4044 if (xhci->hw_lpm_support == 1) { 4068 if (xhci->hw_lpm_support == 1 &&
4069 xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) {
4045 udev->usb2_hw_lpm_capable = 1; 4070 udev->usb2_hw_lpm_capable = 1;
4046 ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); 4071 ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1);
4047 if (!ret) 4072 if (!ret)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index d62ebca1e91c..66b048a64ee0 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1532,6 +1532,9 @@ struct xhci_hcd {
1532 unsigned sw_lpm_support:1; 1532 unsigned sw_lpm_support:1;
1533 /* support xHCI 1.0 spec USB2 hardware LPM */ 1533 /* support xHCI 1.0 spec USB2 hardware LPM */
1534 unsigned hw_lpm_support:1; 1534 unsigned hw_lpm_support:1;
1535 /* cached usb2 extened protocol capabilites */
1536 u32 *ext_caps;
1537 unsigned int num_ext_caps;
1535 /* Compliance Mode Recovery Data */ 1538 /* Compliance Mode Recovery Data */
1536 struct timer_list comp_mode_recovery_timer; 1539 struct timer_list comp_mode_recovery_timer;
1537 u32 port_status_u0; 1540 u32 port_status_u0;