aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-05-23 15:33:02 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-23 15:33:02 -0400
commitc44dead70a841d90ddc01968012f323c33217c9e (patch)
tree85489ebe9b9a3413cd8ee197ffb40c8aa8d97e63 /drivers/usb/core/hub.c
parent99dff5856220a02b8711f2e8746413ea6e53ccf6 (diff)
parentd5f6db9e1aff6ccf1876224f152c0268b0c8a992 (diff)
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (205 commits) USB: EHCI: Remove SPARC_LEON {read,write}_be definitions from ehci.h USB: UHCI: Support big endian GRUSBHC HC sparc: add {read,write}*_be routines USB: UHCI: Add support for big endian descriptors USB: UHCI: Use ACCESS_ONCE rather than using a full compiler barrier USB: UHCI: Add support for big endian mmio usb-storage: Correct adjust_quirks to include latest flags usb/isp1760: Fix possible unlink problems usb/isp1760: Move function isp1760_endpoint_disable() within file. USB: remove remaining usages of hcd->state from usbcore and fix regression usb: musb: ux500: add configuration and build options for ux500 dma usb: musb: ux500: add dma glue layer for ux500 usb: musb: ux500: add dma name for ux500 usb: musb: ux500: add ux500 specific code for gadget side usb: musb: fix compile error usb-storage: fix up the unusual_realtek device list USB: gadget: f_audio: Fix invalid dereference of initdata EHCI: don't rescan interrupt QHs needlessly OHCI: fix regression caused by nVidia shutdown workaround USB: OTG: msm: Free VCCCX regulator even if we can't set the voltage ...
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c131
1 files changed, 103 insertions, 28 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 93720bdc9ef..79a58c3a2e2 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -379,15 +379,6 @@ static int hub_port_status(struct usb_hub *hub, int port1,
379 *status = le16_to_cpu(hub->status->port.wPortStatus); 379 *status = le16_to_cpu(hub->status->port.wPortStatus);
380 *change = le16_to_cpu(hub->status->port.wPortChange); 380 *change = le16_to_cpu(hub->status->port.wPortChange);
381 381
382 if ((hub->hdev->parent != NULL) &&
383 hub_is_superspeed(hub->hdev)) {
384 /* Translate the USB 3 port status */
385 u16 tmp = *status & USB_SS_PORT_STAT_MASK;
386 if (*status & USB_SS_PORT_STAT_POWER)
387 tmp |= USB_PORT_STAT_POWER;
388 *status = tmp;
389 }
390
391 ret = 0; 382 ret = 0;
392 } 383 }
393 mutex_unlock(&hub->status_mutex); 384 mutex_unlock(&hub->status_mutex);
@@ -2160,11 +2151,76 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
2160 return status; 2151 return status;
2161} 2152}
2162 2153
2154/* Warm reset a USB3 protocol port */
2155static int hub_port_warm_reset(struct usb_hub *hub, int port)
2156{
2157 int ret;
2158 u16 portstatus, portchange;
2159
2160 if (!hub_is_superspeed(hub->hdev)) {
2161 dev_err(hub->intfdev, "only USB3 hub support warm reset\n");
2162 return -EINVAL;
2163 }
2164
2165 /* Warm reset the port */
2166 ret = set_port_feature(hub->hdev,
2167 port, USB_PORT_FEAT_BH_PORT_RESET);
2168 if (ret) {
2169 dev_err(hub->intfdev, "cannot warm reset port %d\n", port);
2170 return ret;
2171 }
2172
2173 msleep(20);
2174 ret = hub_port_status(hub, port, &portstatus, &portchange);
2175
2176 if (portchange & USB_PORT_STAT_C_RESET)
2177 clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET);
2178
2179 if (portchange & USB_PORT_STAT_C_BH_RESET)
2180 clear_port_feature(hub->hdev, port,
2181 USB_PORT_FEAT_C_BH_PORT_RESET);
2182
2183 if (portchange & USB_PORT_STAT_C_LINK_STATE)
2184 clear_port_feature(hub->hdev, port,
2185 USB_PORT_FEAT_C_PORT_LINK_STATE);
2186
2187 return ret;
2188}
2189
2190/* Check if a port is power on */
2191static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
2192{
2193 int ret = 0;
2194
2195 if (hub_is_superspeed(hub->hdev)) {
2196 if (portstatus & USB_SS_PORT_STAT_POWER)
2197 ret = 1;
2198 } else {
2199 if (portstatus & USB_PORT_STAT_POWER)
2200 ret = 1;
2201 }
2202
2203 return ret;
2204}
2205
2163#ifdef CONFIG_PM 2206#ifdef CONFIG_PM
2164 2207
2165#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \ 2208/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
2166 USB_PORT_STAT_SUSPEND) 2209static int port_is_suspended(struct usb_hub *hub, unsigned portstatus)
2167#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION) 2210{
2211 int ret = 0;
2212
2213 if (hub_is_superspeed(hub->hdev)) {
2214 if ((portstatus & USB_PORT_STAT_LINK_STATE)
2215 == USB_SS_PORT_LS_U3)
2216 ret = 1;
2217 } else {
2218 if (portstatus & USB_PORT_STAT_SUSPEND)
2219 ret = 1;
2220 }
2221
2222 return ret;
2223}
2168 2224
2169/* Determine whether the device on a port is ready for a normal resume, 2225/* Determine whether the device on a port is ready for a normal resume,
2170 * is ready for a reset-resume, or should be disconnected. 2226 * is ready for a reset-resume, or should be disconnected.
@@ -2174,7 +2230,9 @@ static int check_port_resume_type(struct usb_device *udev,
2174 int status, unsigned portchange, unsigned portstatus) 2230 int status, unsigned portchange, unsigned portstatus)
2175{ 2231{
2176 /* Is the device still present? */ 2232 /* Is the device still present? */
2177 if (status || (portstatus & MASK_BITS) != WANT_BITS) { 2233 if (status || port_is_suspended(hub, portstatus) ||
2234 !port_is_power_on(hub, portstatus) ||
2235 !(portstatus & USB_PORT_STAT_CONNECTION)) {
2178 if (status >= 0) 2236 if (status >= 0)
2179 status = -ENODEV; 2237 status = -ENODEV;
2180 } 2238 }
@@ -2285,14 +2343,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
2285 } 2343 }
2286 2344
2287 /* see 7.1.7.6 */ 2345 /* see 7.1.7.6 */
2288 /* Clear PORT_POWER if it's a USB3.0 device connected to USB 3.0 2346 if (hub_is_superspeed(hub->hdev))
2289 * external hub. 2347 status = set_port_feature(hub->hdev,
2290 * FIXME: this is a temporary workaround to make the system able 2348 port1 | (USB_SS_PORT_LS_U3 << 3),
2291 * to suspend/resume. 2349 USB_PORT_FEAT_LINK_STATE);
2292 */
2293 if ((hub->hdev->parent != NULL) && hub_is_superspeed(hub->hdev))
2294 status = clear_port_feature(hub->hdev, port1,
2295 USB_PORT_FEAT_POWER);
2296 else 2350 else
2297 status = set_port_feature(hub->hdev, port1, 2351 status = set_port_feature(hub->hdev, port1,
2298 USB_PORT_FEAT_SUSPEND); 2352 USB_PORT_FEAT_SUSPEND);
@@ -2439,7 +2493,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
2439 2493
2440 /* Skip the initial Clear-Suspend step for a remote wakeup */ 2494 /* Skip the initial Clear-Suspend step for a remote wakeup */
2441 status = hub_port_status(hub, port1, &portstatus, &portchange); 2495 status = hub_port_status(hub, port1, &portstatus, &portchange);
2442 if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND)) 2496 if (status == 0 && !port_is_suspended(hub, portstatus))
2443 goto SuspendCleared; 2497 goto SuspendCleared;
2444 2498
2445 // dev_dbg(hub->intfdev, "resume port %d\n", port1); 2499 // dev_dbg(hub->intfdev, "resume port %d\n", port1);
@@ -2447,8 +2501,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
2447 set_bit(port1, hub->busy_bits); 2501 set_bit(port1, hub->busy_bits);
2448 2502
2449 /* see 7.1.7.7; affects power usage, but not budgeting */ 2503 /* see 7.1.7.7; affects power usage, but not budgeting */
2450 status = clear_port_feature(hub->hdev, 2504 if (hub_is_superspeed(hub->hdev))
2451 port1, USB_PORT_FEAT_SUSPEND); 2505 status = set_port_feature(hub->hdev,
2506 port1 | (USB_SS_PORT_LS_U0 << 3),
2507 USB_PORT_FEAT_LINK_STATE);
2508 else
2509 status = clear_port_feature(hub->hdev,
2510 port1, USB_PORT_FEAT_SUSPEND);
2452 if (status) { 2511 if (status) {
2453 dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", 2512 dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
2454 port1, status); 2513 port1, status);
@@ -2470,9 +2529,15 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
2470 2529
2471 SuspendCleared: 2530 SuspendCleared:
2472 if (status == 0) { 2531 if (status == 0) {
2473 if (portchange & USB_PORT_STAT_C_SUSPEND) 2532 if (hub_is_superspeed(hub->hdev)) {
2474 clear_port_feature(hub->hdev, port1, 2533 if (portchange & USB_PORT_STAT_C_LINK_STATE)
2475 USB_PORT_FEAT_C_SUSPEND); 2534 clear_port_feature(hub->hdev, port1,
2535 USB_PORT_FEAT_C_PORT_LINK_STATE);
2536 } else {
2537 if (portchange & USB_PORT_STAT_C_SUSPEND)
2538 clear_port_feature(hub->hdev, port1,
2539 USB_PORT_FEAT_C_SUSPEND);
2540 }
2476 } 2541 }
2477 2542
2478 clear_bit(port1, hub->busy_bits); 2543 clear_bit(port1, hub->busy_bits);
@@ -3147,7 +3212,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
3147 3212
3148 /* maybe switch power back on (e.g. root hub was reset) */ 3213 /* maybe switch power back on (e.g. root hub was reset) */
3149 if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 3214 if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
3150 && !(portstatus & USB_PORT_STAT_POWER)) 3215 && !port_is_power_on(hub, portstatus))
3151 set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); 3216 set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
3152 3217
3153 if (portstatus & USB_PORT_STAT_ENABLE) 3218 if (portstatus & USB_PORT_STAT_ENABLE)
@@ -3490,6 +3555,16 @@ static void hub_events(void)
3490 USB_PORT_FEAT_C_PORT_CONFIG_ERROR); 3555 USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
3491 } 3556 }
3492 3557
3558 /* Warm reset a USB3 protocol port if it's in
3559 * SS.Inactive state.
3560 */
3561 if (hub_is_superspeed(hub->hdev) &&
3562 (portstatus & USB_PORT_STAT_LINK_STATE)
3563 == USB_SS_PORT_LS_SS_INACTIVE) {
3564 dev_dbg(hub_dev, "warm reset port %d\n", i);
3565 hub_port_warm_reset(hub, i);
3566 }
3567
3493 if (connect_change) 3568 if (connect_change)
3494 hub_port_connect_change(hub, i, 3569 hub_port_connect_change(hub, i,
3495 portstatus, portchange); 3570 portstatus, portchange);