diff options
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 122 |
1 files changed, 116 insertions, 6 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index f4d301bc83b9..0dcb4164dc83 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -28,6 +28,89 @@ | |||
28 | 28 | ||
29 | /*-------------------------------------------------------------------------*/ | 29 | /*-------------------------------------------------------------------------*/ |
30 | 30 | ||
31 | #ifdef CONFIG_USB_PERSIST | ||
32 | |||
33 | static int ehci_hub_control( | ||
34 | struct usb_hcd *hcd, | ||
35 | u16 typeReq, | ||
36 | u16 wValue, | ||
37 | u16 wIndex, | ||
38 | char *buf, | ||
39 | u16 wLength | ||
40 | ); | ||
41 | |||
42 | /* After a power loss, ports that were owned by the companion must be | ||
43 | * reset so that the companion can still own them. | ||
44 | */ | ||
45 | static void ehci_handover_companion_ports(struct ehci_hcd *ehci) | ||
46 | { | ||
47 | u32 __iomem *reg; | ||
48 | u32 status; | ||
49 | int port; | ||
50 | __le32 buf; | ||
51 | struct usb_hcd *hcd = ehci_to_hcd(ehci); | ||
52 | |||
53 | if (!ehci->owned_ports) | ||
54 | return; | ||
55 | |||
56 | /* Give the connections some time to appear */ | ||
57 | msleep(20); | ||
58 | |||
59 | port = HCS_N_PORTS(ehci->hcs_params); | ||
60 | while (port--) { | ||
61 | if (test_bit(port, &ehci->owned_ports)) { | ||
62 | reg = &ehci->regs->port_status[port]; | ||
63 | status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | ||
64 | |||
65 | /* Port already owned by companion? */ | ||
66 | if (status & PORT_OWNER) | ||
67 | clear_bit(port, &ehci->owned_ports); | ||
68 | else if (test_bit(port, &ehci->companion_ports)) | ||
69 | ehci_writel(ehci, status & ~PORT_PE, reg); | ||
70 | else | ||
71 | ehci_hub_control(hcd, SetPortFeature, | ||
72 | USB_PORT_FEAT_RESET, port + 1, | ||
73 | NULL, 0); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if (!ehci->owned_ports) | ||
78 | return; | ||
79 | msleep(90); /* Wait for resets to complete */ | ||
80 | |||
81 | port = HCS_N_PORTS(ehci->hcs_params); | ||
82 | while (port--) { | ||
83 | if (test_bit(port, &ehci->owned_ports)) { | ||
84 | ehci_hub_control(hcd, GetPortStatus, | ||
85 | 0, port + 1, | ||
86 | (char *) &buf, sizeof(buf)); | ||
87 | |||
88 | /* The companion should now own the port, | ||
89 | * but if something went wrong the port must not | ||
90 | * remain enabled. | ||
91 | */ | ||
92 | reg = &ehci->regs->port_status[port]; | ||
93 | status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | ||
94 | if (status & PORT_OWNER) | ||
95 | ehci_writel(ehci, status | PORT_CSC, reg); | ||
96 | else { | ||
97 | ehci_dbg(ehci, "failed handover port %d: %x\n", | ||
98 | port + 1, status); | ||
99 | ehci_writel(ehci, status & ~PORT_PE, reg); | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | ehci->owned_ports = 0; | ||
105 | } | ||
106 | |||
107 | #else /* CONFIG_USB_PERSIST */ | ||
108 | |||
109 | static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci) | ||
110 | { } | ||
111 | |||
112 | #endif | ||
113 | |||
31 | #ifdef CONFIG_PM | 114 | #ifdef CONFIG_PM |
32 | 115 | ||
33 | static int ehci_bus_suspend (struct usb_hcd *hcd) | 116 | static int ehci_bus_suspend (struct usb_hcd *hcd) |
@@ -60,14 +143,16 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
60 | * then manually resume them in the bus_resume() routine. | 143 | * then manually resume them in the bus_resume() routine. |
61 | */ | 144 | */ |
62 | ehci->bus_suspended = 0; | 145 | ehci->bus_suspended = 0; |
146 | ehci->owned_ports = 0; | ||
63 | while (port--) { | 147 | while (port--) { |
64 | u32 __iomem *reg = &ehci->regs->port_status [port]; | 148 | u32 __iomem *reg = &ehci->regs->port_status [port]; |
65 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | 149 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; |
66 | u32 t2 = t1; | 150 | u32 t2 = t1; |
67 | 151 | ||
68 | /* keep track of which ports we suspend */ | 152 | /* keep track of which ports we suspend */ |
69 | if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && | 153 | if (t1 & PORT_OWNER) |
70 | !(t1 & PORT_SUSPEND)) { | 154 | set_bit(port, &ehci->owned_ports); |
155 | else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) { | ||
71 | t2 |= PORT_SUSPEND; | 156 | t2 |= PORT_SUSPEND; |
72 | set_bit(port, &ehci->bus_suspended); | 157 | set_bit(port, &ehci->bus_suspended); |
73 | } | 158 | } |
@@ -108,11 +193,16 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
108 | { | 193 | { |
109 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 194 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
110 | u32 temp; | 195 | u32 temp; |
196 | u32 power_okay; | ||
111 | int i; | 197 | int i; |
112 | 198 | ||
113 | if (time_before (jiffies, ehci->next_statechange)) | 199 | if (time_before (jiffies, ehci->next_statechange)) |
114 | msleep(5); | 200 | msleep(5); |
115 | spin_lock_irq (&ehci->lock); | 201 | spin_lock_irq (&ehci->lock); |
202 | if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { | ||
203 | spin_unlock_irq(&ehci->lock); | ||
204 | return -ESHUTDOWN; | ||
205 | } | ||
116 | 206 | ||
117 | /* Ideally and we've got a real resume here, and no port's power | 207 | /* Ideally and we've got a real resume here, and no port's power |
118 | * was lost. (For PCI, that means Vaux was maintained.) But we | 208 | * was lost. (For PCI, that means Vaux was maintained.) But we |
@@ -120,8 +210,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
120 | * the last user of the controller, not reset/pm hardware keeping | 210 | * the last user of the controller, not reset/pm hardware keeping |
121 | * state we gave to it. | 211 | * state we gave to it. |
122 | */ | 212 | */ |
123 | temp = ehci_readl(ehci, &ehci->regs->intr_enable); | 213 | power_okay = ehci_readl(ehci, &ehci->regs->intr_enable); |
124 | ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); | 214 | ehci_dbg(ehci, "resume root hub%s\n", |
215 | power_okay ? "" : " after power loss"); | ||
125 | 216 | ||
126 | /* at least some APM implementations will try to deliver | 217 | /* at least some APM implementations will try to deliver |
127 | * IRQs right away, so delay them until we're ready. | 218 | * IRQs right away, so delay them until we're ready. |
@@ -184,6 +275,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
184 | ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); | 275 | ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); |
185 | 276 | ||
186 | spin_unlock_irq (&ehci->lock); | 277 | spin_unlock_irq (&ehci->lock); |
278 | |||
279 | if (!power_okay) | ||
280 | ehci_handover_companion_ports(ehci); | ||
187 | return 0; | 281 | return 0; |
188 | } | 282 | } |
189 | 283 | ||
@@ -448,7 +542,8 @@ static int ehci_hub_control ( | |||
448 | ) { | 542 | ) { |
449 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 543 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
450 | int ports = HCS_N_PORTS (ehci->hcs_params); | 544 | int ports = HCS_N_PORTS (ehci->hcs_params); |
451 | u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1]; | 545 | u32 __iomem *status_reg = &ehci->regs->port_status[ |
546 | (wIndex & 0xff) - 1]; | ||
452 | u32 temp, status; | 547 | u32 temp, status; |
453 | unsigned long flags; | 548 | unsigned long flags; |
454 | int retval = 0; | 549 | int retval = 0; |
@@ -556,9 +651,24 @@ static int ehci_hub_control ( | |||
556 | status |= 1 << USB_PORT_FEAT_C_CONNECTION; | 651 | status |= 1 << USB_PORT_FEAT_C_CONNECTION; |
557 | if (temp & PORT_PEC) | 652 | if (temp & PORT_PEC) |
558 | status |= 1 << USB_PORT_FEAT_C_ENABLE; | 653 | status |= 1 << USB_PORT_FEAT_C_ENABLE; |
559 | if ((temp & PORT_OCC) && !ignore_oc) | 654 | |
655 | if ((temp & PORT_OCC) && !ignore_oc){ | ||
560 | status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; | 656 | status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; |
561 | 657 | ||
658 | /* | ||
659 | * Hubs should disable port power on over-current. | ||
660 | * However, not all EHCI implementations do this | ||
661 | * automatically, even if they _do_ support per-port | ||
662 | * power switching; they're allowed to just limit the | ||
663 | * current. khubd will turn the power back on. | ||
664 | */ | ||
665 | if (HCS_PPC (ehci->hcs_params)){ | ||
666 | ehci_writel(ehci, | ||
667 | temp & ~(PORT_RWC_BITS | PORT_POWER), | ||
668 | status_reg); | ||
669 | } | ||
670 | } | ||
671 | |||
562 | /* whoever resumes must GetPortStatus to complete it!! */ | 672 | /* whoever resumes must GetPortStatus to complete it!! */ |
563 | if (temp & PORT_RESUME) { | 673 | if (temp & PORT_RESUME) { |
564 | 674 | ||