Hi,
ehci_hub_resume() reads the hub port registers during resume. However, on S3
or swsusp the root hub loses power, making read data invalid and forcing the
user to replug all devices. Saving the data in memory during suspend helps -
at least for my i855gm based notebook.
There are still issues. When uhci resumes before ehci, it reconnects all
devices. Also, I need a rather bad hack that ignores pci_set_power_state()
return values in usb_hcd_pci_suspend(), but that is all not directly related
to my patch.
Signed-off-by: Stefan Rompf ([EMAIL PROTECTED])
Stefan
Files linux/drivers/usb/host.old/ehci-hcd.ko and linux/drivers/usb/host/ehci-hcd.ko differ
Files linux/drivers/usb/host.old/ehci-hcd.o and linux/drivers/usb/host/ehci-hcd.o differ
diff -purN linux/drivers/usb/host.old/ehci-hub.c linux/drivers/usb/host/ehci-hub.c
--- linux/drivers/usb/host.old/ehci-hub.c 2004-10-06 20:45:12.000000000 +0200
+++ linux/drivers/usb/host/ehci-hub.c 2004-10-07 22:43:15.562972960 +0200
@@ -43,6 +43,11 @@ static int ehci_hub_suspend (struct usb_
return -EAGAIN;
port = HCS_N_PORTS (ehci->hcs_params);
+
+ ehci->regs_suspend = kmalloc(sizeof(u32) * port, GFP_ATOMIC);
+ if (unlikely(ehci->regs_suspend == NULL))
+ return -EIO;
+
spin_lock_irq (&ehci->lock);
/* suspend any active/unsuspended ports, maybe allow wakeup */
@@ -62,6 +67,8 @@ static int ehci_hub_suspend (struct usb_
port + 1, t1, t2);
writel (t2, &ehci->regs->port_status [port]);
}
+
+ ehci->regs_suspend[port] = t1;
}
/* stop schedules, then turn off HC and clean any completed work */
@@ -109,7 +116,7 @@ static int ehci_hub_resume (struct usb_h
/* take ports out of suspend */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
- temp = readl (&ehci->regs->port_status [i]);
+ temp = ehci->regs_suspend[i];
temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
if (temp & PORT_SUSPEND) {
ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
@@ -129,6 +136,9 @@ static int ehci_hub_resume (struct usb_h
}
(void) readl (&ehci->regs->command);
+ kfree(ehci->regs_suspend);
+ ehci->regs_suspend = NULL;
+
/* maybe re-activate the schedule(s) */
temp = 0;
if (ehci->async->qh_next.qh)
diff -purN linux/drivers/usb/host.old/ehci.h linux/drivers/usb/host/ehci.h
--- linux/drivers/usb/host.old/ehci.h 2004-10-06 20:45:12.000000000 +0200
+++ linux/drivers/usb/host/ehci.h 2004-10-07 22:25:27.047412016 +0200
@@ -73,6 +73,9 @@ struct ehci_hcd { /* one per controlle
struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs;
u32 hcs_params; /* cached register copy */
+#ifdef CONFIG_PM
+ u32 *regs_suspend;
+#endif
/* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */