mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-02 08:34:20 +08:00
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 */
|
/* let companion controllers work when we aren't */
|
||||||
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
|
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
|
||||||
|
|
||||||
|
remove_companion_file(ehci);
|
||||||
remove_debug_files (ehci);
|
remove_debug_files (ehci);
|
||||||
|
|
||||||
/* root hub is shut down separately (first, when possible) */
|
/* 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.
|
* since the class device isn't created that early.
|
||||||
*/
|
*/
|
||||||
create_debug_files(ehci);
|
create_debug_files(ehci);
|
||||||
|
create_companion_file(ehci);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -188,6 +188,103 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
|
|||||||
|
|
||||||
#endif /* CONFIG_PM */
|
#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 (
|
static int check_reset_complete (
|
||||||
@ -504,6 +601,16 @@ static int ehci_hub_control (
|
|||||||
ehci_readl(ehci, status_reg));
|
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
|
* Even if OWNER is set, there's no harm letting khubd
|
||||||
* see the wPortStatus values (they should all be 0 except
|
* see the wPortStatus values (they should all be 0 except
|
||||||
|
@ -74,7 +74,11 @@ struct ehci_hcd { /* one per controller */
|
|||||||
|
|
||||||
/* per root hub port */
|
/* per root hub port */
|
||||||
unsigned long reset_done [EHCI_MAX_ROOT_PORTS];
|
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 ...) */
|
/* per-HC memory pools (could be per-bus, but ...) */
|
||||||
struct dma_pool *qh_pool; /* qh per active urb */
|
struct dma_pool *qh_pool; /* qh per active urb */
|
||||||
|
Loading…
Reference in New Issue
Block a user