diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5a909ba6fb67..31a492a4a881 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -751,16 +751,20 @@ int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub, int port1, bool set) { int ret; - struct usb_port *port_dev = hub->ports[port1 - 1]; if (set) ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); else ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER); - if (!ret) - port_dev->power_is_on = set; - return ret; + if (ret) + return ret; + + if (set) + set_bit(port1, hub->power_bits); + else + clear_bit(port1, hub->power_bits); + return 0; } /** @@ -839,7 +843,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) dev_dbg(hub->intfdev, "trying to enable port power on " "non-switchable hub\n"); for (port1 = 1; port1 <= hub->hdev->maxchild; port1++) - if (hub->ports[port1 - 1]->power_is_on) + if (test_bit(port1, hub->power_bits)) set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); else usb_clear_port_feature(hub->hdev, port1, @@ -1180,15 +1184,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) set_bit(port1, hub->change_bits); } else if (udev->persist_enabled) { - struct usb_port *port_dev = hub->ports[port1 - 1]; - #ifdef CONFIG_PM udev->reset_resume = 1; #endif /* Don't set the change_bits when the device * was powered off. */ - if (port_dev->power_is_on) + if (test_bit(port1, hub->power_bits)) set_bit(port1, hub->change_bits); } else { @@ -2096,16 +2098,15 @@ void usb_disconnect(struct usb_device **pdev) usb_hcd_synchronize_unlinks(udev); if (udev->parent) { + int port1 = udev->portnum; struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); - struct usb_port *port_dev = hub->ports[udev->portnum - 1]; + struct usb_port *port_dev = hub->ports[port1 - 1]; sysfs_remove_link(&udev->dev.kobj, "port"); sysfs_remove_link(&port_dev->dev.kobj, "device"); - if (!port_dev->did_runtime_put) + if (test_and_clear_bit(port1, hub->child_usage_bits)) pm_runtime_put(&port_dev->dev); - else - port_dev->did_runtime_put = false; } usb_remove_ep_devs(&udev->ep0); @@ -2416,7 +2417,8 @@ int usb_new_device(struct usb_device *udev) /* Create link files between child device and usb port device. */ if (udev->parent) { struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); - struct usb_port *port_dev = hub->ports[udev->portnum - 1]; + int port1 = udev->portnum; + struct usb_port *port_dev = hub->ports[port1 - 1]; err = sysfs_create_link(&udev->dev.kobj, &port_dev->dev.kobj, "port"); @@ -2430,7 +2432,8 @@ int usb_new_device(struct usb_device *udev) goto fail; } - pm_runtime_get_sync(&port_dev->dev); + if (!test_and_set_bit(port1, hub->child_usage_bits)) + pm_runtime_get_sync(&port_dev->dev); } (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev); @@ -3100,10 +3103,9 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_set_device_state(udev, USB_STATE_SUSPENDED); } - if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) { + if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled + && test_and_clear_bit(port1, hub->child_usage_bits)) pm_runtime_put_sync(&port_dev->dev); - port_dev->did_runtime_put = true; - } usb_mark_last_busy(hub->hdev); return status; @@ -3245,9 +3247,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) int status; u16 portchange, portstatus; - if (port_dev->did_runtime_put) { + if (!test_and_set_bit(port1, hub->child_usage_bits)) { status = pm_runtime_get_sync(&port_dev->dev); - port_dev->did_runtime_put = false; if (status < 0) { dev_dbg(&udev->dev, "can't resume usb port, status %d\n", status); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 048c797f394c..3ef1c2e435cc 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -51,6 +51,9 @@ struct usb_hub { device present */ unsigned long wakeup_bits[1]; /* ports that have signaled remote wakeup */ + unsigned long power_bits[1]; /* ports that are powered */ + unsigned long child_usage_bits[1]; /* ports powered on for + children */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif @@ -86,8 +89,6 @@ struct usb_hub { * @connect_type: port's connect type * @location: opaque representation of platform connector location * @portnum: port index num based one - * @power_is_on: port's power state - * @did_runtime_put: port has done pm_runtime_put(). */ struct usb_port { struct usb_device *child; @@ -97,8 +98,6 @@ struct usb_port { enum usb_port_connect_type connect_type; usb_port_location_t location; u8 portnum; - unsigned power_is_on:1; - unsigned did_runtime_put:1; }; #define to_usb_port(_dev) \ diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 40c3ac173e9e..795778c71e31 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -82,7 +82,7 @@ static int usb_port_runtime_resume(struct device *dev) if (!hub) return -EINVAL; if (hub->in_reset) { - port_dev->power_is_on = 1; + set_bit(port1, hub->power_bits); return 0; } @@ -320,7 +320,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) hub->ports[port1 - 1] = port_dev; port_dev->portnum = port1; - port_dev->power_is_on = true; + set_bit(port1, hub->power_bits); port_dev->dev.parent = hub->intfdev; port_dev->dev.groups = port_dev_group; port_dev->dev.type = &usb_port_device_type;