Preparation for making reset_resume a port event. End goal is to trigger port recovery actions like warm reset when recovering a port power session. A usb_device may not be attached to the port, so the flag for requesting reset service can not be contained within the usb_device. Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx> --- drivers/usb/core/driver.c | 40 +++++++++++++++++++++++++++++++++++----- drivers/usb/core/hub.c | 29 ++++++++++++++++------------- drivers/usb/core/hub.h | 2 ++ include/linux/usb.h | 1 - include/linux/usb/hcd.h | 1 + 5 files changed, 54 insertions(+), 19 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8d989b1d3dc5..6447bfc3e4c1 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -29,7 +29,7 @@ #include <linux/usb/quirks.h> #include <linux/usb/hcd.h> -#include "usb.h" +#include "hub.h" /* @@ -1088,6 +1088,36 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) return status; } +#define clear_reset_resume(udev) op_reset_resume(udev, 0) +#define set_reset_resume(udev) op_reset_resume(udev, 1) +#define test_reset_resume(udev) op_reset_resume(udev, 2) +static int op_reset_resume(struct usb_device *udev, int op) +{ + unsigned long *addr; + int nr, rc = 0; + + if (udev->parent) { + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); + + addr = hub->resume_bits; + nr = udev->portnum; + } else { + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + addr = &hcd->flags; + nr = HCD_FLAG_RESET_RESUME; + } + + if (op == 0) + clear_bit(nr, addr); + else if (op == 1) + set_bit(nr, addr); + else + rc = test_bit(nr, addr); + + return rc; +} + static int usb_resume_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -1110,7 +1140,7 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) &udev->bus->hs_companion->root_hub->dev); if (udev->quirks & USB_QUIRK_RESET_RESUME) - udev->reset_resume = 1; + set_reset_resume(udev); udriver = to_usb_device_driver(udev->dev.driver); status = udriver->resume(udev, msg); @@ -1318,7 +1348,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) udev->can_submit = 1; /* Resume the device */ - if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) + if (udev->state == USB_STATE_SUSPENDED || test_reset_resume(udev)) status = usb_resume_device(udev, msg); /* Resume the interfaces */ @@ -1326,7 +1356,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; usb_resume_interface(udev, intf, msg, - udev->reset_resume); + test_reset_resume(udev)); } } usb_mark_last_busy(udev); @@ -1334,7 +1364,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); if (!status) - udev->reset_resume = 0; + clear_reset_resume(udev); return status; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 162e94dbed53..48d5c8fff77f 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1176,9 +1176,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) } else if (udev->persist_enabled) { struct usb_port *port_dev = hub->ports[port1 - 1]; -#ifdef CONFIG_PM - udev->reset_resume = 1; -#endif + set_bit(port1, hub->resume_bits); /* Don't set the change_bits when the device * was powered off. */ @@ -2784,9 +2782,10 @@ static int check_port_resume_type(struct usb_device *udev, /* Can't do a normal resume if the port isn't enabled, * so try a reset-resume instead. */ - else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) { + else if (!(portstatus & USB_PORT_STAT_ENABLE) + && !test_bit(port1, hub->resume_bits)) { if (udev->persist_enabled) - udev->reset_resume = 1; + set_bit(port1, hub->resume_bits); else status = -ENODEV; } @@ -2795,7 +2794,7 @@ static int check_port_resume_type(struct usb_device *udev, dev_dbg(hub->intfdev, "port %d status %04x.%04x after resume, %d\n", port1, portchange, portstatus, status); - } else if (udev->reset_resume) { + } else if (test_bit(port1, hub->resume_bits)) { /* Late port handoff can set status-change bits */ if (portchange & USB_PORT_STAT_C_CONNECTION) @@ -3081,10 +3080,12 @@ static int finish_port_resume(struct usb_device *udev) { int status = 0; u16 devstatus = 0; + int port1 = udev->portnum; + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); /* caller owns the udev device lock */ - dev_dbg(&udev->dev, "%s\n", - udev->reset_resume ? "finish reset-resume" : "finish resume"); + dev_dbg(&udev->dev, "%s\n", test_bit(port1, hub->resume_bits) + ? "finish reset-resume" : "finish resume"); /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the @@ -3100,7 +3101,7 @@ static int finish_port_resume(struct usb_device *udev) * operation is carried out here, after the port has been * resumed. */ - if (udev->reset_resume) + if (test_bit(port1, hub->resume_bits)) retry_reset_resume: status = usb_reset_and_verify_device(udev); @@ -3113,9 +3114,9 @@ static int finish_port_resume(struct usb_device *udev) status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); /* If a normal resume failed, try doing a reset-resume */ - if (status && !udev->reset_resume && udev->persist_enabled) { + if (status && udev->persist_enabled + && !test_and_set_bit(port1, hub->resume_bits)) { dev_dbg(&udev->dev, "retry with reset-resume\n"); - udev->reset_resume = 1; goto retry_reset_resume; } } @@ -3129,7 +3130,7 @@ static int finish_port_resume(struct usb_device *udev) * which crash if the feature is cleared, hence check for * udev->reset_resume */ - } else if (udev->actconfig && !udev->reset_resume) { + } else if (udev->actconfig && !test_bit(port1, hub->resume_bits)) { if (udev->speed < USB_SPEED_SUPER) { if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) status = usb_disable_remote_wakeup(udev); @@ -3392,8 +3393,10 @@ static int hub_reset_resume(struct usb_interface *intf) */ void usb_root_hub_lost_power(struct usb_device *rhdev) { + struct usb_hcd *hcd = bus_to_hcd(rhdev->bus); + dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); - rhdev->reset_resume = 1; + set_bit(HCD_FLAG_RESET_RESUME, &hcd->flags); } EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 4e4790dea343..419e7ac51c12 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -51,6 +51,8 @@ struct usb_hub { device present */ unsigned long wakeup_bits[1]; /* ports that have signaled remote wakeup */ + unsigned long resume_bits[1]; /* ports that have seen a + resume event */ #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ #error event_bits[] is too short! #endif diff --git a/include/linux/usb.h b/include/linux/usb.h index 512ab162832c..851917571a9e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -571,7 +571,6 @@ struct usb_device { unsigned long connect_time; unsigned do_remote_wakeup:1; - unsigned reset_resume:1; unsigned port_is_suspended:1; #endif struct wusb_dev *wusb_dev; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index efe8d8a7c7ad..b6781f4f0d9c 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -119,6 +119,7 @@ struct usb_hcd { #define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */ #define HCD_FLAG_RH_RUNNING 5 /* root hub is running? */ #define HCD_FLAG_DEAD 6 /* controller has died? */ +#define HCD_FLAG_RESET_RESUME 7 /* reset resume root hub */ /* The flags can be tested using these macros; they are likely to * be slightly faster than test_bit(). -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html