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>
This commit is contained in:
parent
625b5c9a00
commit
57e06c1137
|
@ -388,6 +388,7 @@ static void ehci_stop (struct usb_hcd *hcd)
|
|||
/* let companion controllers work when we aren't */
|
||||
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
|
||||
|
||||
remove_companion_file(ehci);
|
||||
remove_debug_files (ehci);
|
||||
|
||||
/* root hub is shut down separately (first, when possible) */
|
||||
|
@ -563,6 +564,7 @@ static int ehci_run (struct usb_hcd *hcd)
|
|||
* since the class device isn't created that early.
|
||||
*/
|
||||
create_debug_files(ehci);
|
||||
create_companion_file(ehci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -188,6 +188,103 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
|||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Display the ports dedicated to the companion controller */
|
||||
static ssize_t show_companion(struct class_device *class_dev, char *buf)
|
||||
{
|
||||
struct ehci_hcd *ehci;
|
||||
int nports, index, n;
|
||||
int count = PAGE_SIZE;
|
||||
char *ptr = buf;
|
||||
|
||||
ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev)));
|
||||
nports = HCS_N_PORTS(ehci->hcs_params);
|
||||
|
||||
for (index = 0; index < nports; ++index) {
|
||||
if (test_bit(index, &ehci->companion_ports)) {
|
||||
n = scnprintf(ptr, count, "%d\n", index + 1);
|
||||
ptr += n;
|
||||
count -= n;
|
||||
}
|
||||
}
|
||||
return ptr - buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dedicate or undedicate a port to the companion controller.
|
||||
* Syntax is "[-]portnum", where a leading '-' sign means
|
||||
* return control of the port to the EHCI controller.
|
||||
*/
|
||||
static ssize_t store_companion(struct class_device *class_dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ehci_hcd *ehci;
|
||||
int portnum, new_owner, try;
|
||||
u32 __iomem *status_reg;
|
||||
u32 port_status;
|
||||
|
||||
ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev)));
|
||||
new_owner = PORT_OWNER; /* Owned by companion */
|
||||
if (sscanf(buf, "%d", &portnum) != 1)
|
||||
return -EINVAL;
|
||||
if (portnum < 0) {
|
||||
portnum = - portnum;
|
||||
new_owner = 0; /* Owned by EHCI */
|
||||
}
|
||||
if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
|
||||
return -ENOENT;
|
||||
status_reg = &ehci->regs->port_status[--portnum];
|
||||
if (new_owner)
|
||||
set_bit(portnum, &ehci->companion_ports);
|
||||
else
|
||||
clear_bit(portnum, &ehci->companion_ports);
|
||||
|
||||
/*
|
||||
* The controller won't set the OWNER bit if the port is
|
||||
* enabled, so this loop will sometimes require at least two
|
||||
* iterations: one to disable the port and one to set OWNER.
|
||||
*/
|
||||
|
||||
for (try = 4; try > 0; --try) {
|
||||
spin_lock_irq(&ehci->lock);
|
||||
port_status = ehci_readl(ehci, status_reg);
|
||||
if ((port_status & PORT_OWNER) == new_owner
|
||||
|| (port_status & (PORT_OWNER | PORT_CONNECT))
|
||||
== 0)
|
||||
try = 0;
|
||||
else {
|
||||
port_status ^= PORT_OWNER;
|
||||
port_status &= ~(PORT_PE | PORT_RWC_BITS);
|
||||
ehci_writel(ehci, port_status, status_reg);
|
||||
}
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
if (try > 1)
|
||||
msleep(5);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
static CLASS_DEVICE_ATTR(companion, 0644, show_companion, store_companion);
|
||||
|
||||
static inline void create_companion_file(struct ehci_hcd *ehci)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* with integrated TT there is no companion! */
|
||||
if (!ehci_is_TDI(ehci))
|
||||
i = class_device_create_file(ehci_to_hcd(ehci)->self.class_dev,
|
||||
&class_device_attr_companion);
|
||||
}
|
||||
|
||||
static inline void remove_companion_file(struct ehci_hcd *ehci)
|
||||
{
|
||||
/* with integrated TT there is no companion! */
|
||||
if (!ehci_is_TDI(ehci))
|
||||
class_device_remove_file(ehci_to_hcd(ehci)->self.class_dev,
|
||||
&class_device_attr_companion);
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int check_reset_complete (
|
||||
|
@ -504,6 +601,16 @@ static int ehci_hub_control (
|
|||
ehci_readl(ehci, status_reg));
|
||||
}
|
||||
|
||||
/* transfer dedicated ports to the companion hc */
|
||||
if ((temp & PORT_CONNECT) &&
|
||||
test_bit(wIndex, &ehci->companion_ports)) {
|
||||
temp &= ~PORT_RWC_BITS;
|
||||
temp |= PORT_OWNER;
|
||||
ehci_writel(ehci, temp, status_reg);
|
||||
ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1);
|
||||
temp = ehci_readl(ehci, status_reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Even if OWNER is set, there's no harm letting khubd
|
||||
* see the wPortStatus values (they should all be 0 except
|
||||
|
|
|
@ -74,7 +74,11 @@ struct ehci_hcd { /* one per controller */
|
|||
|
||||
/* per root hub port */
|
||||
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
|
||||
unsigned long bus_suspended;
|
||||
/* bit vectors (one bit per port) */
|
||||
unsigned long bus_suspended; /* which ports were
|
||||
already suspended at the start of a bus suspend */
|
||||
unsigned long companion_ports; /* which ports are
|
||||
dedicated to the companion controller */
|
||||
|
||||
/* per-HC memory pools (could be per-bus, but ...) */
|
||||
struct dma_pool *qh_pool; /* qh per active urb */
|
||||
|
|
Loading…
Reference in New Issue