aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/ehci-hub.c107
-rw-r--r--drivers/usb/host/ehci.h6
3 files changed, 114 insertions, 1 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 9ec896218feb..92c62911f574 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -388,6 +388,7 @@ static void ehci_stop (struct usb_hcd *hcd)
388 /* let companion controllers work when we aren't */ 388 /* let companion controllers work when we aren't */
389 ehci_writel(ehci, 0, &ehci->regs->configured_flag); 389 ehci_writel(ehci, 0, &ehci->regs->configured_flag);
390 390
391 remove_companion_file(ehci);
391 remove_debug_files (ehci); 392 remove_debug_files (ehci);
392 393
393 /* root hub is shut down separately (first, when possible) */ 394 /* root hub is shut down separately (first, when possible) */
@@ -563,6 +564,7 @@ static int ehci_run (struct usb_hcd *hcd)
563 * since the class device isn't created that early. 564 * since the class device isn't created that early.
564 */ 565 */
565 create_debug_files(ehci); 566 create_debug_files(ehci);
567 create_companion_file(ehci);
566 568
567 return 0; 569 return 0;
568} 570}
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 076474d95dbf..3cfba69e0767 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -190,6 +190,103 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
190 190
191/*-------------------------------------------------------------------------*/ 191/*-------------------------------------------------------------------------*/
192 192
193/* Display the ports dedicated to the companion controller */
194static ssize_t show_companion(struct class_device *class_dev, char *buf)
195{
196 struct ehci_hcd *ehci;
197 int nports, index, n;
198 int count = PAGE_SIZE;
199 char *ptr = buf;
200
201 ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev)));
202 nports = HCS_N_PORTS(ehci->hcs_params);
203
204 for (index = 0; index < nports; ++index) {
205 if (test_bit(index, &ehci->companion_ports)) {
206 n = scnprintf(ptr, count, "%d\n", index + 1);
207 ptr += n;
208 count -= n;
209 }
210 }
211 return ptr - buf;
212}
213
214/*
215 * Dedicate or undedicate a port to the companion controller.
216 * Syntax is "[-]portnum", where a leading '-' sign means
217 * return control of the port to the EHCI controller.
218 */
219static ssize_t store_companion(struct class_device *class_dev,
220 const char *buf, size_t count)
221{
222 struct ehci_hcd *ehci;
223 int portnum, new_owner, try;
224 u32 __iomem *status_reg;
225 u32 port_status;
226
227 ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev)));
228 new_owner = PORT_OWNER; /* Owned by companion */
229 if (sscanf(buf, "%d", &portnum) != 1)
230 return -EINVAL;
231 if (portnum < 0) {
232 portnum = - portnum;
233 new_owner = 0; /* Owned by EHCI */
234 }
235 if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
236 return -ENOENT;
237 status_reg = &ehci->regs->port_status[--portnum];
238 if (new_owner)
239 set_bit(portnum, &ehci->companion_ports);
240 else
241 clear_bit(portnum, &ehci->companion_ports);
242
243 /*
244 * The controller won't set the OWNER bit if the port is
245 * enabled, so this loop will sometimes require at least two
246 * iterations: one to disable the port and one to set OWNER.
247 */
248
249 for (try = 4; try > 0; --try) {
250 spin_lock_irq(&ehci->lock);
251 port_status = ehci_readl(ehci, status_reg);
252 if ((port_status & PORT_OWNER) == new_owner
253 || (port_status & (PORT_OWNER | PORT_CONNECT))
254 == 0)
255 try = 0;
256 else {
257 port_status ^= PORT_OWNER;
258 port_status &= ~(PORT_PE | PORT_RWC_BITS);
259 ehci_writel(ehci, port_status, status_reg);
260 }
261 spin_unlock_irq(&ehci->lock);
262 if (try > 1)
263 msleep(5);
264 }
265 return count;
266}
267static CLASS_DEVICE_ATTR(companion, 0644, show_companion, store_companion);
268
269static inline void create_companion_file(struct ehci_hcd *ehci)
270{
271 int i;
272
273 /* with integrated TT there is no companion! */
274 if (!ehci_is_TDI(ehci))
275 i = class_device_create_file(ehci_to_hcd(ehci)->self.class_dev,
276 &class_device_attr_companion);
277}
278
279static inline void remove_companion_file(struct ehci_hcd *ehci)
280{
281 /* with integrated TT there is no companion! */
282 if (!ehci_is_TDI(ehci))
283 class_device_remove_file(ehci_to_hcd(ehci)->self.class_dev,
284 &class_device_attr_companion);
285}
286
287
288/*-------------------------------------------------------------------------*/
289
193static int check_reset_complete ( 290static int check_reset_complete (
194 struct ehci_hcd *ehci, 291 struct ehci_hcd *ehci,
195 int index, 292 int index,
@@ -504,6 +601,16 @@ static int ehci_hub_control (
504 ehci_readl(ehci, status_reg)); 601 ehci_readl(ehci, status_reg));
505 } 602 }
506 603
604 /* transfer dedicated ports to the companion hc */
605 if ((temp & PORT_CONNECT) &&
606 test_bit(wIndex, &ehci->companion_ports)) {
607 temp &= ~PORT_RWC_BITS;
608 temp |= PORT_OWNER;
609 ehci_writel(ehci, temp, status_reg);
610 ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1);
611 temp = ehci_readl(ehci, status_reg);
612 }
613
507 /* 614 /*
508 * Even if OWNER is set, there's no harm letting khubd 615 * Even if OWNER is set, there's no harm letting khubd
509 * see the wPortStatus values (they should all be 0 except 616 * see the wPortStatus values (they should all be 0 except
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 3ce7249085d5..ec0da0343be4 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -74,7 +74,11 @@ struct ehci_hcd { /* one per controller */
74 74
75 /* per root hub port */ 75 /* per root hub port */
76 unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; 76 unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
77 unsigned long bus_suspended; 77 /* bit vectors (one bit per port) */
78 unsigned long bus_suspended; /* which ports were
79 already suspended at the start of a bus suspend */
80 unsigned long companion_ports; /* which ports are
81 dedicated to the companion controller */
78 82
79 /* per-HC memory pools (could be per-bus, but ...) */ 83 /* per-HC memory pools (could be per-bus, but ...) */
80 struct dma_pool *qh_pool; /* qh per active urb */ 84 struct dma_pool *qh_pool; /* qh per active urb */