Commit 56c1e26d authored by David Brownell's avatar David Brownell Committed by Greg KH

[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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent e2e66446
...@@ -346,6 +346,22 @@ ehci_reboot (struct notifier_block *self, unsigned long code, void *null) ...@@ -346,6 +346,22 @@ ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
return 0; 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 */ /* called by khubd or root hub init threads */
...@@ -362,8 +378,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd) ...@@ -362,8 +378,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
dbg_hcs_params (ehci, "reset"); dbg_hcs_params (ehci, "reset");
dbg_hcc_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 #ifdef CONFIG_PCI
/* EHCI 0.96 and later may have "extended capabilities" */
if (hcd->self.controller->bus == &pci_bus_type) { if (hcd->self.controller->bus == &pci_bus_type) {
struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
...@@ -383,9 +401,30 @@ static int ehci_hc_reset (struct usb_hcd *hcd) ...@@ -383,9 +401,30 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
break; 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)); temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
} else } else
temp = 0; temp = 0;
/* EHCI 0.96 and later may have "extended capabilities" */
while (temp && count--) { while (temp && count--) {
u32 cap; u32 cap;
...@@ -414,8 +453,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd) ...@@ -414,8 +453,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
ehci_reset (ehci); ehci_reset (ehci);
#endif #endif
/* cache this readonly data; minimize PCI reads */ ehci_port_power (ehci, 0);
ehci->hcs_params = readl (&ehci->caps->hcs_params);
/* at least the Genesys GL880S needs fixup here */ /* at least the Genesys GL880S needs fixup here */
temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
...@@ -657,16 +695,11 @@ done2: ...@@ -657,16 +695,11 @@ done2:
static void ehci_stop (struct usb_hcd *hcd) static void ehci_stop (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u8 rh_ports, port;
ehci_dbg (ehci, "stop\n"); ehci_dbg (ehci, "stop\n");
/* Turn off port power on all root hub ports. */ /* Turn off port power on all root hub ports. */
rh_ports = HCS_N_PORTS (ehci->hcs_params); ehci_port_power (ehci, 0);
for (port = 1; port <= rh_ports; port++)
(void) ehci_hub_control(hcd,
ClearPortFeature, USB_PORT_FEAT_POWER,
port, NULL, 0);
/* no more interrupts ... */ /* no more interrupts ... */
del_timer_sync (&ehci->watchdog); del_timer_sync (&ehci->watchdog);
...@@ -748,7 +781,6 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -748,7 +781,6 @@ static int ehci_resume (struct usb_hcd *hcd)
unsigned port; unsigned port;
struct usb_device *root = hcd->self.root_hub; struct usb_device *root = hcd->self.root_hub;
int retval = -EINVAL; int retval = -EINVAL;
int powerup = 0;
// maybe restore (PCI) FLADJ // maybe restore (PCI) FLADJ
...@@ -766,8 +798,6 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -766,8 +798,6 @@ static int ehci_resume (struct usb_hcd *hcd)
up (&hcd->self.root_hub->serialize); up (&hcd->self.root_hub->serialize);
break; break;
} }
if ((status & PORT_POWER) == 0)
powerup = 1;
if (!root->children [port]) if (!root->children [port])
continue; continue;
dbg_port (ehci, __FUNCTION__, port + 1, status); dbg_port (ehci, __FUNCTION__, port + 1, status);
...@@ -794,16 +824,9 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -794,16 +824,9 @@ static int ehci_resume (struct usb_hcd *hcd)
retval = ehci_start (hcd); retval = ehci_start (hcd);
/* here we "know" root ports should always stay powered; /* 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_port_power (ehci, 1);
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);
}
} }
return retval; return retval;
......
...@@ -281,6 +281,8 @@ ehci_hub_descriptor ( ...@@ -281,6 +281,8 @@ ehci_hub_descriptor (
temp = 0x0008; /* per-port overcurrent reporting */ temp = 0x0008; /* per-port overcurrent reporting */
if (HCS_PPC (ehci->hcs_params)) if (HCS_PPC (ehci->hcs_params))
temp |= 0x0001; /* per-port power control */ temp |= 0x0001; /* per-port power control */
else
temp |= 0x0002; /* no power switching */
#if 0 #if 0
// re-enable when we support USB_PORT_FEAT_INDICATOR below. // re-enable when we support USB_PORT_FEAT_INDICATOR below.
if (HCS_INDICATOR (ehci->hcs_params)) if (HCS_INDICATOR (ehci->hcs_params))
......
...@@ -47,6 +47,12 @@ struct ehci_stats { ...@@ -47,6 +47,12 @@ struct ehci_stats {
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ #define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
struct ehci_hcd { /* one per controller */ 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; spinlock_t lock;
/* async schedule support */ /* async schedule support */
...@@ -84,11 +90,6 @@ struct ehci_hcd { /* one per controller */ ...@@ -84,11 +90,6 @@ struct ehci_hcd { /* one per controller */
unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */ 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 */ /* irq statistics */
#ifdef EHCI_STATS #ifdef EHCI_STATS
struct ehci_stats stats; struct ehci_stats stats;
...@@ -165,7 +166,7 @@ struct ehci_caps { ...@@ -165,7 +166,7 @@ struct ehci_caps {
/* these fields are specified as 8 and 16 bit registers, /* these fields are specified as 8 and 16 bit registers,
* but some hosts can't perform 8 or 16 bit PCI accesses. * but some hosts can't perform 8 or 16 bit PCI accesses.
*/ */
u32 hc_capbase; u32 hc_capbase;
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ #define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ #define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
u32 hcs_params; /* HCSPARAMS - offset 0x4 */ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
...@@ -273,7 +274,7 @@ struct ehci_dbg_port { ...@@ -273,7 +274,7 @@ struct ehci_dbg_port {
#define DBGP_ENABLED (1<<28) #define DBGP_ENABLED (1<<28)
#define DBGP_DONE (1<<16) #define DBGP_DONE (1<<16)
#define DBGP_INUSE (1<<10) #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_BAD 1
# define DBGP_ERR_SIGNAL 2 # define DBGP_ERR_SIGNAL 2
#define DBGP_ERROR (1<<6) #define DBGP_ERROR (1<<6)
...@@ -282,11 +283,11 @@ struct ehci_dbg_port { ...@@ -282,11 +283,11 @@ struct ehci_dbg_port {
#define DBGP_LEN(x) (((x)>>0)&0x0f) #define DBGP_LEN(x) (((x)>>0)&0x0f)
u32 pids; u32 pids;
#define DBGP_PID_GET(x) (((x)>>16)&0xff) #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 data03;
u32 data47; u32 data47;
u32 address; u32 address;
#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep)); #define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep))
} __attribute__ ((packed)); } __attribute__ ((packed));
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment