summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlek Du <alek.du@intel.com>2010-06-04 09:47:56 +0200
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-10 23:35:35 +0200
commit5a9cdf332eae724b11906cb1712e3a662eba32b2 (patch)
tree67aad1a4e795b2b26e4e320305accba16c4d201e
parentUSB: EHCI: EHCI 1.1 addendum: Basic LPM feature support (diff)
downloadlinux-5a9cdf332eae724b11906cb1712e3a662eba32b2.tar.xz
linux-5a9cdf332eae724b11906cb1712e3a662eba32b2.zip
USB: EHCI: EHCI 1.1 addendum: Enable Per-port change detect bits
This patch will enable Per-port event feature defined in EHCI 1.1 addendum. This feature addresses an issue where HCD is currently required to read and parse PORTSC for all enabled root hub ports. With this patch, the overhead will be reduced. Signed-off-by: Alek Du <alek.du@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/host/ehci-hcd.c19
-rw-r--r--drivers/usb/host/ehci-hub.c9
-rw-r--r--drivers/usb/host/ehci.h1
3 files changed, 27 insertions, 2 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index baf9b648bb1f..8697ad19f313 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -584,6 +584,11 @@ static int ehci_init(struct usb_hcd *hcd)
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
log2_irq_thresh = 0;
temp = 1 << (16 + log2_irq_thresh);
+ if (HCC_PER_PORT_CHANGE_EVENT(hcc_params)) {
+ ehci->has_ppcd = 1;
+ ehci_dbg(ehci, "enable per-port change event\n");
+ temp |= CMD_PPCEE;
+ }
if (HCC_CANPARK(hcc_params)) {
/* HW default park == 3, on hardware that supports it (like
* NVidia and ALI silicon), maximizes throughput on the async
@@ -782,6 +787,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* remote wakeup [4.3.1] */
if (status & STS_PCD) {
unsigned i = HCS_N_PORTS (ehci->hcs_params);
+ u32 ppcd = 0;
/* kick root hub later */
pcd_status = status;
@@ -790,9 +796,18 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
if (!(cmd & CMD_RUN))
usb_hcd_resume_root_hub(hcd);
+ /* get per-port change detect bits */
+ if (ehci->has_ppcd)
+ ppcd = status >> 16;
+
while (i--) {
- int pstatus = ehci_readl(ehci,
- &ehci->regs->port_status [i]);
+ int pstatus;
+
+ /* leverage per-port change bits feature */
+ if (ehci->has_ppcd && !(ppcd & (1 << i)))
+ continue;
+ pstatus = ehci_readl(ehci,
+ &ehci->regs->port_status[i]);
if (pstatus & PORT_OWNER)
continue;
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 8a28dae8a375..84e792d71c22 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -603,6 +603,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
u32 mask;
int ports, i, retval = 1;
unsigned long flags;
+ u32 ppcd = 0;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (!HC_IS_RUNNING(hcd->state))
@@ -632,7 +633,15 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
/* port N changes (bit N)? */
spin_lock_irqsave (&ehci->lock, flags);
+
+ /* get per-port change detect bits */
+ if (ehci->has_ppcd)
+ ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16;
+
for (i = 0; i < ports; i++) {
+ /* leverage per-port change bits feature */
+ if (ehci->has_ppcd && !(ppcd & (1 << i)))
+ continue;
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
/*
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 21f30a0c3d2f..e6c57cc416f6 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -141,6 +141,7 @@ struct ehci_hcd { /* one per controller */
__hc32 *ohci_hcctrl_reg;
unsigned has_hostpc:1;
unsigned has_lpm:1; /* support link power management */
+ unsigned has_ppcd:1; /* support per-port change bits */
u8 sbrn; /* packed release number */
/* irq statistics */