diff options
Diffstat (limited to 'drivers/usb/core/port.c')
-rw-r--r-- | drivers/usb/core/port.c | 89 |
1 files changed, 65 insertions, 24 deletions
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 62036faf56c0..fe1b6d0967e3 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c | |||
@@ -21,6 +21,8 @@ | |||
21 | 21 | ||
22 | #include "hub.h" | 22 | #include "hub.h" |
23 | 23 | ||
24 | static int usb_port_block_power_off; | ||
25 | |||
24 | static const struct attribute_group *port_dev_group[]; | 26 | static const struct attribute_group *port_dev_group[]; |
25 | 27 | ||
26 | static ssize_t connect_type_show(struct device *dev, | 28 | static ssize_t connect_type_show(struct device *dev, |
@@ -66,6 +68,7 @@ static void usb_port_device_release(struct device *dev) | |||
66 | { | 68 | { |
67 | struct usb_port *port_dev = to_usb_port(dev); | 69 | struct usb_port *port_dev = to_usb_port(dev); |
68 | 70 | ||
71 | kfree(port_dev->req); | ||
69 | kfree(port_dev); | 72 | kfree(port_dev); |
70 | } | 73 | } |
71 | 74 | ||
@@ -142,6 +145,9 @@ static int usb_port_runtime_suspend(struct device *dev) | |||
142 | == PM_QOS_FLAGS_ALL) | 145 | == PM_QOS_FLAGS_ALL) |
143 | return -EAGAIN; | 146 | return -EAGAIN; |
144 | 147 | ||
148 | if (usb_port_block_power_off) | ||
149 | return -EBUSY; | ||
150 | |||
145 | usb_autopm_get_interface(intf); | 151 | usb_autopm_get_interface(intf); |
146 | retval = usb_hub_set_port_power(hdev, hub, port1, false); | 152 | retval = usb_hub_set_port_power(hdev, hub, port1, false); |
147 | usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); | 153 | usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); |
@@ -190,11 +196,19 @@ static int link_peers(struct usb_port *left, struct usb_port *right) | |||
190 | if (left->peer || right->peer) { | 196 | if (left->peer || right->peer) { |
191 | struct usb_port *lpeer = left->peer; | 197 | struct usb_port *lpeer = left->peer; |
192 | struct usb_port *rpeer = right->peer; | 198 | struct usb_port *rpeer = right->peer; |
193 | 199 | char *method; | |
194 | WARN(1, "failed to peer %s and %s (%s -> %p) (%s -> %p)\n", | 200 | |
195 | dev_name(&left->dev), dev_name(&right->dev), | 201 | if (left->location && left->location == right->location) |
196 | dev_name(&left->dev), lpeer, | 202 | method = "location"; |
197 | dev_name(&right->dev), rpeer); | 203 | else |
204 | method = "default"; | ||
205 | |||
206 | pr_warn("usb: failed to peer %s and %s by %s (%s:%s) (%s:%s)\n", | ||
207 | dev_name(&left->dev), dev_name(&right->dev), method, | ||
208 | dev_name(&left->dev), | ||
209 | lpeer ? dev_name(&lpeer->dev) : "none", | ||
210 | dev_name(&right->dev), | ||
211 | rpeer ? dev_name(&rpeer->dev) : "none"); | ||
198 | return -EBUSY; | 212 | return -EBUSY; |
199 | } | 213 | } |
200 | 214 | ||
@@ -251,6 +265,7 @@ static void link_peers_report(struct usb_port *left, struct usb_port *right) | |||
251 | dev_warn(&left->dev, "failed to peer to %s (%d)\n", | 265 | dev_warn(&left->dev, "failed to peer to %s (%d)\n", |
252 | dev_name(&right->dev), rc); | 266 | dev_name(&right->dev), rc); |
253 | pr_warn_once("usb: port power management may be unreliable\n"); | 267 | pr_warn_once("usb: port power management may be unreliable\n"); |
268 | usb_port_block_power_off = 1; | ||
254 | } | 269 | } |
255 | } | 270 | } |
256 | 271 | ||
@@ -386,9 +401,13 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) | |||
386 | int retval; | 401 | int retval; |
387 | 402 | ||
388 | port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); | 403 | port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); |
389 | if (!port_dev) { | 404 | if (!port_dev) |
390 | retval = -ENOMEM; | 405 | return -ENOMEM; |
391 | goto exit; | 406 | |
407 | port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL); | ||
408 | if (!port_dev->req) { | ||
409 | kfree(port_dev); | ||
410 | return -ENOMEM; | ||
392 | } | 411 | } |
393 | 412 | ||
394 | hub->ports[port1 - 1] = port_dev; | 413 | hub->ports[port1 - 1] = port_dev; |
@@ -404,31 +423,53 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) | |||
404 | port1); | 423 | port1); |
405 | mutex_init(&port_dev->status_lock); | 424 | mutex_init(&port_dev->status_lock); |
406 | retval = device_register(&port_dev->dev); | 425 | retval = device_register(&port_dev->dev); |
407 | if (retval) | 426 | if (retval) { |
408 | goto error_register; | 427 | put_device(&port_dev->dev); |
428 | return retval; | ||
429 | } | ||
430 | |||
431 | /* Set default policy of port-poweroff disabled. */ | ||
432 | retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req, | ||
433 | DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF); | ||
434 | if (retval < 0) { | ||
435 | device_unregister(&port_dev->dev); | ||
436 | return retval; | ||
437 | } | ||
409 | 438 | ||
410 | find_and_link_peer(hub, port1); | 439 | find_and_link_peer(hub, port1); |
411 | 440 | ||
441 | /* | ||
442 | * Enable runtime pm and hold a refernce that hub_configure() | ||
443 | * will drop once the PM_QOS_NO_POWER_OFF flag state has been set | ||
444 | * and the hub has been fully registered (hdev->maxchild set). | ||
445 | */ | ||
412 | pm_runtime_set_active(&port_dev->dev); | 446 | pm_runtime_set_active(&port_dev->dev); |
447 | pm_runtime_get_noresume(&port_dev->dev); | ||
448 | pm_runtime_enable(&port_dev->dev); | ||
449 | device_enable_async_suspend(&port_dev->dev); | ||
413 | 450 | ||
414 | /* | 451 | /* |
415 | * Do not enable port runtime pm if the hub does not support | 452 | * Keep hidden the ability to enable port-poweroff if the hub |
416 | * power switching. Also, userspace must have final say of | 453 | * does not support power switching. |
417 | * whether a port is permitted to power-off. Do not enable | ||
418 | * runtime pm if we fail to expose pm_qos_no_power_off. | ||
419 | */ | 454 | */ |
420 | if (hub_is_port_power_switchable(hub) | 455 | if (!hub_is_port_power_switchable(hub)) |
421 | && dev_pm_qos_expose_flags(&port_dev->dev, | 456 | return 0; |
422 | PM_QOS_FLAG_NO_POWER_OFF) == 0) | ||
423 | pm_runtime_enable(&port_dev->dev); | ||
424 | 457 | ||
425 | device_enable_async_suspend(&port_dev->dev); | 458 | /* Attempt to let userspace take over the policy. */ |
426 | return 0; | 459 | retval = dev_pm_qos_expose_flags(&port_dev->dev, |
460 | PM_QOS_FLAG_NO_POWER_OFF); | ||
461 | if (retval < 0) { | ||
462 | dev_warn(&port_dev->dev, "failed to expose pm_qos_no_poweroff\n"); | ||
463 | return 0; | ||
464 | } | ||
427 | 465 | ||
428 | error_register: | 466 | /* Userspace owns the policy, drop the kernel 'no_poweroff' request. */ |
429 | put_device(&port_dev->dev); | 467 | retval = dev_pm_qos_remove_request(port_dev->req); |
430 | exit: | 468 | if (retval >= 0) { |
431 | return retval; | 469 | kfree(port_dev->req); |
470 | port_dev->req = NULL; | ||
471 | } | ||
472 | return 0; | ||
432 | } | 473 | } |
433 | 474 | ||
434 | void usb_hub_remove_port_device(struct usb_hub *hub, int port1) | 475 | void usb_hub_remove_port_device(struct usb_hub *hub, int port1) |