aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-01-16 11:59:45 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-07 18:44:37 -0500
commit57e06c11372eccf5acebdd4664eb025fee76c561 (patch)
treea792559056735005ce7037196509fb151d3c3bc5 /drivers/usb/host
parent625b5c9a0069ef1b61feb3ce599b39f1b04b5666 (diff)
EHCI: force high-speed devices to run at full speed
This patch (as710) adds a sysfs class-device attribute file named "companion" for EHCI controllers. The file contains a list of port numbers that are dedicated to the companion controller; by writing a port number to the file the user can force a high-speed device attached directly to the computer to run at full speed. (As far as I know it is not possible to do this for a device attached to an external hub.) A port is removed from the file by writing the negative of its port number. Several users have asked for this facility and it seems like a useful thing to have. Every now and then one runs across a device which behaves much better at full speed than at high speed. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
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 */