diff options
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 182 |
1 files changed, 138 insertions, 44 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index c7178bcde67a..e7d3d8def282 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -106,12 +106,75 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) | |||
106 | ehci->owned_ports = 0; | 106 | ehci->owned_ports = 0; |
107 | } | 107 | } |
108 | 108 | ||
109 | static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, | ||
110 | bool suspending) | ||
111 | { | ||
112 | int port; | ||
113 | u32 temp; | ||
114 | |||
115 | /* If remote wakeup is enabled for the root hub but disabled | ||
116 | * for the controller, we must adjust all the port wakeup flags | ||
117 | * when the controller is suspended or resumed. In all other | ||
118 | * cases they don't need to be changed. | ||
119 | */ | ||
120 | if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || | ||
121 | device_may_wakeup(ehci_to_hcd(ehci)->self.controller)) | ||
122 | return; | ||
123 | |||
124 | /* clear phy low-power mode before changing wakeup flags */ | ||
125 | if (ehci->has_hostpc) { | ||
126 | port = HCS_N_PORTS(ehci->hcs_params); | ||
127 | while (port--) { | ||
128 | u32 __iomem *hostpc_reg; | ||
129 | |||
130 | hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs | ||
131 | + HOSTPC0 + 4 * port); | ||
132 | temp = ehci_readl(ehci, hostpc_reg); | ||
133 | ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg); | ||
134 | } | ||
135 | msleep(5); | ||
136 | } | ||
137 | |||
138 | port = HCS_N_PORTS(ehci->hcs_params); | ||
139 | while (port--) { | ||
140 | u32 __iomem *reg = &ehci->regs->port_status[port]; | ||
141 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | ||
142 | u32 t2 = t1 & ~PORT_WAKE_BITS; | ||
143 | |||
144 | /* If we are suspending the controller, clear the flags. | ||
145 | * If we are resuming the controller, set the wakeup flags. | ||
146 | */ | ||
147 | if (!suspending) { | ||
148 | if (t1 & PORT_CONNECT) | ||
149 | t2 |= PORT_WKOC_E | PORT_WKDISC_E; | ||
150 | else | ||
151 | t2 |= PORT_WKOC_E | PORT_WKCONN_E; | ||
152 | } | ||
153 | ehci_vdbg(ehci, "port %d, %08x -> %08x\n", | ||
154 | port + 1, t1, t2); | ||
155 | ehci_writel(ehci, t2, reg); | ||
156 | } | ||
157 | |||
158 | /* enter phy low-power mode again */ | ||
159 | if (ehci->has_hostpc) { | ||
160 | port = HCS_N_PORTS(ehci->hcs_params); | ||
161 | while (port--) { | ||
162 | u32 __iomem *hostpc_reg; | ||
163 | |||
164 | hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs | ||
165 | + HOSTPC0 + 4 * port); | ||
166 | temp = ehci_readl(ehci, hostpc_reg); | ||
167 | ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg); | ||
168 | } | ||
169 | } | ||
170 | } | ||
171 | |||
109 | static int ehci_bus_suspend (struct usb_hcd *hcd) | 172 | static int ehci_bus_suspend (struct usb_hcd *hcd) |
110 | { | 173 | { |
111 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 174 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
112 | int port; | 175 | int port; |
113 | int mask; | 176 | int mask; |
114 | u32 __iomem *hostpc_reg = NULL; | 177 | int changed; |
115 | 178 | ||
116 | ehci_dbg(ehci, "suspend root hub\n"); | 179 | ehci_dbg(ehci, "suspend root hub\n"); |
117 | 180 | ||
@@ -155,15 +218,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
155 | */ | 218 | */ |
156 | ehci->bus_suspended = 0; | 219 | ehci->bus_suspended = 0; |
157 | ehci->owned_ports = 0; | 220 | ehci->owned_ports = 0; |
221 | changed = 0; | ||
158 | port = HCS_N_PORTS(ehci->hcs_params); | 222 | port = HCS_N_PORTS(ehci->hcs_params); |
159 | while (port--) { | 223 | while (port--) { |
160 | u32 __iomem *reg = &ehci->regs->port_status [port]; | 224 | u32 __iomem *reg = &ehci->regs->port_status [port]; |
161 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | 225 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; |
162 | u32 t2 = t1; | 226 | u32 t2 = t1 & ~PORT_WAKE_BITS; |
163 | 227 | ||
164 | if (ehci->has_hostpc) | ||
165 | hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs | ||
166 | + HOSTPC0 + 4 * (port & 0xff)); | ||
167 | /* keep track of which ports we suspend */ | 228 | /* keep track of which ports we suspend */ |
168 | if (t1 & PORT_OWNER) | 229 | if (t1 & PORT_OWNER) |
169 | set_bit(port, &ehci->owned_ports); | 230 | set_bit(port, &ehci->owned_ports); |
@@ -172,40 +233,45 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
172 | set_bit(port, &ehci->bus_suspended); | 233 | set_bit(port, &ehci->bus_suspended); |
173 | } | 234 | } |
174 | 235 | ||
175 | /* enable remote wakeup on all ports */ | 236 | /* enable remote wakeup on all ports, if told to do so */ |
176 | if (hcd->self.root_hub->do_remote_wakeup) { | 237 | if (hcd->self.root_hub->do_remote_wakeup) { |
177 | /* only enable appropriate wake bits, otherwise the | 238 | /* only enable appropriate wake bits, otherwise the |
178 | * hardware can not go phy low power mode. If a race | 239 | * hardware can not go phy low power mode. If a race |
179 | * condition happens here(connection change during bits | 240 | * condition happens here(connection change during bits |
180 | * set), the port change detection will finally fix it. | 241 | * set), the port change detection will finally fix it. |
181 | */ | 242 | */ |
182 | if (t1 & PORT_CONNECT) { | 243 | if (t1 & PORT_CONNECT) |
183 | t2 |= PORT_WKOC_E | PORT_WKDISC_E; | 244 | t2 |= PORT_WKOC_E | PORT_WKDISC_E; |
184 | t2 &= ~PORT_WKCONN_E; | 245 | else |
185 | } else { | ||
186 | t2 |= PORT_WKOC_E | PORT_WKCONN_E; | 246 | t2 |= PORT_WKOC_E | PORT_WKCONN_E; |
187 | t2 &= ~PORT_WKDISC_E; | 247 | } |
188 | } | ||
189 | } else | ||
190 | t2 &= ~PORT_WAKE_BITS; | ||
191 | 248 | ||
192 | if (t1 != t2) { | 249 | if (t1 != t2) { |
193 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", | 250 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", |
194 | port + 1, t1, t2); | 251 | port + 1, t1, t2); |
195 | ehci_writel(ehci, t2, reg); | 252 | ehci_writel(ehci, t2, reg); |
196 | if (hostpc_reg) { | 253 | changed = 1; |
197 | u32 t3; | 254 | } |
255 | } | ||
198 | 256 | ||
199 | spin_unlock_irq(&ehci->lock); | 257 | if (changed && ehci->has_hostpc) { |
200 | msleep(5);/* 5ms for HCD enter low pwr mode */ | 258 | spin_unlock_irq(&ehci->lock); |
201 | spin_lock_irq(&ehci->lock); | 259 | msleep(5); /* 5 ms for HCD to enter low-power mode */ |
202 | t3 = ehci_readl(ehci, hostpc_reg); | 260 | spin_lock_irq(&ehci->lock); |
203 | ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); | 261 | |
204 | t3 = ehci_readl(ehci, hostpc_reg); | 262 | port = HCS_N_PORTS(ehci->hcs_params); |
205 | ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", | 263 | while (port--) { |
264 | u32 __iomem *hostpc_reg; | ||
265 | u32 t3; | ||
266 | |||
267 | hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs | ||
268 | + HOSTPC0 + 4 * port); | ||
269 | t3 = ehci_readl(ehci, hostpc_reg); | ||
270 | ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); | ||
271 | t3 = ehci_readl(ehci, hostpc_reg); | ||
272 | ehci_dbg(ehci, "Port %d phy low-power mode %s\n", | ||
206 | port, (t3 & HOSTPC_PHCD) ? | 273 | port, (t3 & HOSTPC_PHCD) ? |
207 | "succeeded" : "failed"); | 274 | "succeeded" : "failed"); |
208 | } | ||
209 | } | 275 | } |
210 | } | 276 | } |
211 | 277 | ||
@@ -291,6 +357,25 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
291 | msleep(8); | 357 | msleep(8); |
292 | spin_lock_irq(&ehci->lock); | 358 | spin_lock_irq(&ehci->lock); |
293 | 359 | ||
360 | /* clear phy low-power mode before resume */ | ||
361 | if (ehci->bus_suspended && ehci->has_hostpc) { | ||
362 | i = HCS_N_PORTS(ehci->hcs_params); | ||
363 | while (i--) { | ||
364 | if (test_bit(i, &ehci->bus_suspended)) { | ||
365 | u32 __iomem *hostpc_reg; | ||
366 | |||
367 | hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs | ||
368 | + HOSTPC0 + 4 * i); | ||
369 | temp = ehci_readl(ehci, hostpc_reg); | ||
370 | ehci_writel(ehci, temp & ~HOSTPC_PHCD, | ||
371 | hostpc_reg); | ||
372 | } | ||
373 | } | ||
374 | spin_unlock_irq(&ehci->lock); | ||
375 | msleep(5); | ||
376 | spin_lock_irq(&ehci->lock); | ||
377 | } | ||
378 | |||
294 | /* manually resume the ports we suspended during bus_suspend() */ | 379 | /* manually resume the ports we suspended during bus_suspend() */ |
295 | i = HCS_N_PORTS (ehci->hcs_params); | 380 | i = HCS_N_PORTS (ehci->hcs_params); |
296 | while (i--) { | 381 | while (i--) { |
@@ -659,7 +744,7 @@ static int ehci_hub_control ( | |||
659 | * Even if OWNER is set, so the port is owned by the | 744 | * Even if OWNER is set, so the port is owned by the |
660 | * companion controller, khubd needs to be able to clear | 745 | * companion controller, khubd needs to be able to clear |
661 | * the port-change status bits (especially | 746 | * the port-change status bits (especially |
662 | * USB_PORT_FEAT_C_CONNECTION). | 747 | * USB_PORT_STAT_C_CONNECTION). |
663 | */ | 748 | */ |
664 | 749 | ||
665 | switch (wValue) { | 750 | switch (wValue) { |
@@ -675,16 +760,25 @@ static int ehci_hub_control ( | |||
675 | goto error; | 760 | goto error; |
676 | if (ehci->no_selective_suspend) | 761 | if (ehci->no_selective_suspend) |
677 | break; | 762 | break; |
678 | if (temp & PORT_SUSPEND) { | 763 | if (!(temp & PORT_SUSPEND)) |
679 | if ((temp & PORT_PE) == 0) | 764 | break; |
680 | goto error; | 765 | if ((temp & PORT_PE) == 0) |
681 | /* resume signaling for 20 msec */ | 766 | goto error; |
682 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); | 767 | |
683 | ehci_writel(ehci, temp | PORT_RESUME, | 768 | /* clear phy low-power mode before resume */ |
684 | status_reg); | 769 | if (hostpc_reg) { |
685 | ehci->reset_done [wIndex] = jiffies | 770 | temp1 = ehci_readl(ehci, hostpc_reg); |
686 | + msecs_to_jiffies (20); | 771 | ehci_writel(ehci, temp1 & ~HOSTPC_PHCD, |
772 | hostpc_reg); | ||
773 | spin_unlock_irqrestore(&ehci->lock, flags); | ||
774 | msleep(5);/* wait to leave low-power mode */ | ||
775 | spin_lock_irqsave(&ehci->lock, flags); | ||
687 | } | 776 | } |
777 | /* resume signaling for 20 msec */ | ||
778 | temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); | ||
779 | ehci_writel(ehci, temp | PORT_RESUME, status_reg); | ||
780 | ehci->reset_done[wIndex] = jiffies | ||
781 | + msecs_to_jiffies(20); | ||
688 | break; | 782 | break; |
689 | case USB_PORT_FEAT_C_SUSPEND: | 783 | case USB_PORT_FEAT_C_SUSPEND: |
690 | clear_bit(wIndex, &ehci->port_c_suspend); | 784 | clear_bit(wIndex, &ehci->port_c_suspend); |
@@ -729,12 +823,12 @@ static int ehci_hub_control ( | |||
729 | 823 | ||
730 | // wPortChange bits | 824 | // wPortChange bits |
731 | if (temp & PORT_CSC) | 825 | if (temp & PORT_CSC) |
732 | status |= 1 << USB_PORT_FEAT_C_CONNECTION; | 826 | status |= USB_PORT_STAT_C_CONNECTION << 16; |
733 | if (temp & PORT_PEC) | 827 | if (temp & PORT_PEC) |
734 | status |= 1 << USB_PORT_FEAT_C_ENABLE; | 828 | status |= USB_PORT_STAT_C_ENABLE << 16; |
735 | 829 | ||
736 | if ((temp & PORT_OCC) && !ignore_oc){ | 830 | if ((temp & PORT_OCC) && !ignore_oc){ |
737 | status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; | 831 | status |= USB_PORT_STAT_C_OVERCURRENT << 16; |
738 | 832 | ||
739 | /* | 833 | /* |
740 | * Hubs should disable port power on over-current. | 834 | * Hubs should disable port power on over-current. |
@@ -791,7 +885,7 @@ static int ehci_hub_control ( | |||
791 | if ((temp & PORT_RESET) | 885 | if ((temp & PORT_RESET) |
792 | && time_after_eq(jiffies, | 886 | && time_after_eq(jiffies, |
793 | ehci->reset_done[wIndex])) { | 887 | ehci->reset_done[wIndex])) { |
794 | status |= 1 << USB_PORT_FEAT_C_RESET; | 888 | status |= USB_PORT_STAT_C_RESET << 16; |
795 | ehci->reset_done [wIndex] = 0; | 889 | ehci->reset_done [wIndex] = 0; |
796 | 890 | ||
797 | /* force reset to complete */ | 891 | /* force reset to complete */ |
@@ -833,7 +927,7 @@ static int ehci_hub_control ( | |||
833 | */ | 927 | */ |
834 | 928 | ||
835 | if (temp & PORT_CONNECT) { | 929 | if (temp & PORT_CONNECT) { |
836 | status |= 1 << USB_PORT_FEAT_CONNECTION; | 930 | status |= USB_PORT_STAT_CONNECTION; |
837 | // status may be from integrated TT | 931 | // status may be from integrated TT |
838 | if (ehci->has_hostpc) { | 932 | if (ehci->has_hostpc) { |
839 | temp1 = ehci_readl(ehci, hostpc_reg); | 933 | temp1 = ehci_readl(ehci, hostpc_reg); |
@@ -842,11 +936,11 @@ static int ehci_hub_control ( | |||
842 | status |= ehci_port_speed(ehci, temp); | 936 | status |= ehci_port_speed(ehci, temp); |
843 | } | 937 | } |
844 | if (temp & PORT_PE) | 938 | if (temp & PORT_PE) |
845 | status |= 1 << USB_PORT_FEAT_ENABLE; | 939 | status |= USB_PORT_STAT_ENABLE; |
846 | 940 | ||
847 | /* maybe the port was unsuspended without our knowledge */ | 941 | /* maybe the port was unsuspended without our knowledge */ |
848 | if (temp & (PORT_SUSPEND|PORT_RESUME)) { | 942 | if (temp & (PORT_SUSPEND|PORT_RESUME)) { |
849 | status |= 1 << USB_PORT_FEAT_SUSPEND; | 943 | status |= USB_PORT_STAT_SUSPEND; |
850 | } else if (test_bit(wIndex, &ehci->suspended_ports)) { | 944 | } else if (test_bit(wIndex, &ehci->suspended_ports)) { |
851 | clear_bit(wIndex, &ehci->suspended_ports); | 945 | clear_bit(wIndex, &ehci->suspended_ports); |
852 | ehci->reset_done[wIndex] = 0; | 946 | ehci->reset_done[wIndex] = 0; |
@@ -855,13 +949,13 @@ static int ehci_hub_control ( | |||
855 | } | 949 | } |
856 | 950 | ||
857 | if (temp & PORT_OC) | 951 | if (temp & PORT_OC) |
858 | status |= 1 << USB_PORT_FEAT_OVER_CURRENT; | 952 | status |= USB_PORT_STAT_OVERCURRENT; |
859 | if (temp & PORT_RESET) | 953 | if (temp & PORT_RESET) |
860 | status |= 1 << USB_PORT_FEAT_RESET; | 954 | status |= USB_PORT_STAT_RESET; |
861 | if (temp & PORT_POWER) | 955 | if (temp & PORT_POWER) |
862 | status |= 1 << USB_PORT_FEAT_POWER; | 956 | status |= USB_PORT_STAT_POWER; |
863 | if (test_bit(wIndex, &ehci->port_c_suspend)) | 957 | if (test_bit(wIndex, &ehci->port_c_suspend)) |
864 | status |= 1 << USB_PORT_FEAT_C_SUSPEND; | 958 | status |= USB_PORT_STAT_C_SUSPEND << 16; |
865 | 959 | ||
866 | #ifndef VERBOSE_DEBUG | 960 | #ifndef VERBOSE_DEBUG |
867 | if (status & ~0xffff) /* only if wPortChange is interesting */ | 961 | if (status & ~0xffff) /* only if wPortChange is interesting */ |