mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 01:22:07 +00:00
[PATCH] USB: ehci power fixes
Miscellaneous updates for EHCI. - Mostly updates the power switching on EHCI controllers. One routine centralizes the "power on/off all ports" logic, and the capability to do that is reported more correctly. - Courtesy Colin Leroy, a patch to always power up ports after resumes which didn't keep a USB device suspended. The reset-everything logic powers down those ports (on some hardware) so something needs to turn them back on. - Minor tweaks/bugfixes for the debug port support. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
e2e66446e0
commit
56c1e26d75
@ -346,6 +346,22 @@ ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
|
||||
{
|
||||
unsigned port;
|
||||
|
||||
if (!HCS_PPC (ehci->hcs_params))
|
||||
return;
|
||||
|
||||
ehci_dbg (ehci, "...power%s ports...\n", is_on ? "up" : "down");
|
||||
for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )
|
||||
(void) ehci_hub_control(ehci_to_hcd(ehci),
|
||||
is_on ? SetPortFeature : ClearPortFeature,
|
||||
USB_PORT_FEAT_POWER,
|
||||
port--, NULL, 0);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
|
||||
/* called by khubd or root hub init threads */
|
||||
|
||||
@ -362,8 +378,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
|
||||
dbg_hcs_params (ehci, "reset");
|
||||
dbg_hcc_params (ehci, "reset");
|
||||
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = readl (&ehci->caps->hcs_params);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/* EHCI 0.96 and later may have "extended capabilities" */
|
||||
if (hcd->self.controller->bus == &pci_bus_type) {
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
|
||||
@ -383,9 +401,30 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
|
||||
break;
|
||||
}
|
||||
|
||||
/* optional debug port, normally in the first BAR */
|
||||
temp = pci_find_capability (pdev, 0x0a);
|
||||
if (temp) {
|
||||
pci_read_config_dword(pdev, temp, &temp);
|
||||
temp >>= 16;
|
||||
if ((temp & (3 << 13)) == (1 << 13)) {
|
||||
temp &= 0x1fff;
|
||||
ehci->debug = hcd->regs + temp;
|
||||
temp = readl (&ehci->debug->control);
|
||||
ehci_info (ehci, "debug port %d%s\n",
|
||||
HCS_DEBUG_PORT(ehci->hcs_params),
|
||||
(temp & DBGP_ENABLED)
|
||||
? " IN USE"
|
||||
: "");
|
||||
if (!(temp & DBGP_ENABLED))
|
||||
ehci->debug = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
|
||||
} else
|
||||
temp = 0;
|
||||
|
||||
/* EHCI 0.96 and later may have "extended capabilities" */
|
||||
while (temp && count--) {
|
||||
u32 cap;
|
||||
|
||||
@ -414,8 +453,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
|
||||
ehci_reset (ehci);
|
||||
#endif
|
||||
|
||||
/* cache this readonly data; minimize PCI reads */
|
||||
ehci->hcs_params = readl (&ehci->caps->hcs_params);
|
||||
ehci_port_power (ehci, 0);
|
||||
|
||||
/* at least the Genesys GL880S needs fixup here */
|
||||
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
|
||||
@ -657,16 +695,11 @@ done2:
|
||||
static void ehci_stop (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
|
||||
u8 rh_ports, port;
|
||||
|
||||
ehci_dbg (ehci, "stop\n");
|
||||
|
||||
/* Turn off port power on all root hub ports. */
|
||||
rh_ports = HCS_N_PORTS (ehci->hcs_params);
|
||||
for (port = 1; port <= rh_ports; port++)
|
||||
(void) ehci_hub_control(hcd,
|
||||
ClearPortFeature, USB_PORT_FEAT_POWER,
|
||||
port, NULL, 0);
|
||||
ehci_port_power (ehci, 0);
|
||||
|
||||
/* no more interrupts ... */
|
||||
del_timer_sync (&ehci->watchdog);
|
||||
@ -748,7 +781,6 @@ static int ehci_resume (struct usb_hcd *hcd)
|
||||
unsigned port;
|
||||
struct usb_device *root = hcd->self.root_hub;
|
||||
int retval = -EINVAL;
|
||||
int powerup = 0;
|
||||
|
||||
// maybe restore (PCI) FLADJ
|
||||
|
||||
@ -766,8 +798,6 @@ static int ehci_resume (struct usb_hcd *hcd)
|
||||
up (&hcd->self.root_hub->serialize);
|
||||
break;
|
||||
}
|
||||
if ((status & PORT_POWER) == 0)
|
||||
powerup = 1;
|
||||
if (!root->children [port])
|
||||
continue;
|
||||
dbg_port (ehci, __FUNCTION__, port + 1, status);
|
||||
@ -794,16 +824,9 @@ static int ehci_resume (struct usb_hcd *hcd)
|
||||
retval = ehci_start (hcd);
|
||||
|
||||
/* here we "know" root ports should always stay powered;
|
||||
* but some controllers may lost all power.
|
||||
* but some controllers may lose all power.
|
||||
*/
|
||||
if (powerup) {
|
||||
ehci_dbg (ehci, "...powerup ports...\n");
|
||||
for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )
|
||||
(void) ehci_hub_control(hcd,
|
||||
SetPortFeature, USB_PORT_FEAT_POWER,
|
||||
port--, NULL, 0);
|
||||
msleep(20);
|
||||
}
|
||||
ehci_port_power (ehci, 1);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
@ -281,6 +281,8 @@ ehci_hub_descriptor (
|
||||
temp = 0x0008; /* per-port overcurrent reporting */
|
||||
if (HCS_PPC (ehci->hcs_params))
|
||||
temp |= 0x0001; /* per-port power control */
|
||||
else
|
||||
temp |= 0x0002; /* no power switching */
|
||||
#if 0
|
||||
// re-enable when we support USB_PORT_FEAT_INDICATOR below.
|
||||
if (HCS_INDICATOR (ehci->hcs_params))
|
||||
|
@ -47,6 +47,12 @@ struct ehci_stats {
|
||||
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
|
||||
|
||||
struct ehci_hcd { /* one per controller */
|
||||
/* glue to PCI and HCD framework */
|
||||
struct ehci_caps __iomem *caps;
|
||||
struct ehci_regs __iomem *regs;
|
||||
struct ehci_dbg_port __iomem *debug;
|
||||
|
||||
__u32 hcs_params; /* cached register copy */
|
||||
spinlock_t lock;
|
||||
|
||||
/* async schedule support */
|
||||
@ -84,11 +90,6 @@ struct ehci_hcd { /* one per controller */
|
||||
|
||||
unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */
|
||||
|
||||
/* glue to PCI and HCD framework */
|
||||
struct ehci_caps __iomem *caps;
|
||||
struct ehci_regs __iomem *regs;
|
||||
__u32 hcs_params; /* cached register copy */
|
||||
|
||||
/* irq statistics */
|
||||
#ifdef EHCI_STATS
|
||||
struct ehci_stats stats;
|
||||
@ -273,7 +274,7 @@ struct ehci_dbg_port {
|
||||
#define DBGP_ENABLED (1<<28)
|
||||
#define DBGP_DONE (1<<16)
|
||||
#define DBGP_INUSE (1<<10)
|
||||
#define DBGP_ERRCODE(x) (((x)>>7)&0x0f)
|
||||
#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
|
||||
# define DBGP_ERR_BAD 1
|
||||
# define DBGP_ERR_SIGNAL 2
|
||||
#define DBGP_ERROR (1<<6)
|
||||
@ -282,11 +283,11 @@ struct ehci_dbg_port {
|
||||
#define DBGP_LEN(x) (((x)>>0)&0x0f)
|
||||
u32 pids;
|
||||
#define DBGP_PID_GET(x) (((x)>>16)&0xff)
|
||||
#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok));
|
||||
#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok))
|
||||
u32 data03;
|
||||
u32 data47;
|
||||
u32 address;
|
||||
#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep));
|
||||
#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep))
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
Loading…
Reference in New Issue
Block a user