From 56c1e26d75008d39f1067f453719857a81109d9f Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 9 Apr 2005 09:00:29 -0700 Subject: [PATCH] [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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 65 +++++++++++++++++++++++++------------ drivers/usb/host/ehci-hub.c | 2 ++ drivers/usb/host/ehci.h | 19 ++++++----- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 84d2b93aca37..bc69bd7acebe 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -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; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 2373537fabed..02fefab3501e 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -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)) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e763a8399a75..4df498231752 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -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; @@ -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 */ @@ -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)); /*-------------------------------------------------------------------------*/ -- 2.30.2