diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 15:33:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 15:33:02 -0400 |
commit | c44dead70a841d90ddc01968012f323c33217c9e (patch) | |
tree | 85489ebe9b9a3413cd8ee197ffb40c8aa8d97e63 /drivers/usb/core/hub.c | |
parent | 99dff5856220a02b8711f2e8746413ea6e53ccf6 (diff) | |
parent | d5f6db9e1aff6ccf1876224f152c0268b0c8a992 (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.c | 131 |
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 */ | ||
2155 | static 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 */ | ||
2191 | static 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) | 2209 | static 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); |