Skip to content

Commit

Permalink
[PATCH] USB: ehci power fixes
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Brownell authored and Greg KH committed May 4, 2005
1 parent e2e6644 commit 56c1e26
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 30 deletions.
65 changes: 44 additions & 21 deletions drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand All @@ -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);

Expand All @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -657,16 +695,11 @@ static int ehci_start (struct usb_hcd *hcd)
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);
Expand Down Expand Up @@ -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

Expand All @@ -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);
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/ehci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
19 changes: 10 additions & 9 deletions drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -165,7 +166,7 @@ struct ehci_caps {
/* these fields are specified as 8 and 16 bit registers,
* 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_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
Expand Down Expand Up @@ -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)
Expand All @@ -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));

/*-------------------------------------------------------------------------*/
Expand Down

0 comments on commit 56c1e26

Please sign in to comment.