diff options
author | Alek Du <alek.du@intel.com> | 2009-07-13 00:41:20 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 09:46:29 -0400 |
commit | 331ac6b288d9f3689514ced1878041fb0df7e13c (patch) | |
tree | c2a9d0187fea9224b81a0460b3fbe7f2e4ed0c21 /drivers/usb | |
parent | 3807e26d69b9ad3864fe03224ebebc9610d5802e (diff) |
USB: EHCI: Add Intel Moorestown EHCI controller HOSTPCx extensions and support phy low power mode
The Intel Moorestown EHCI controller supports non-standard HOSTPCx register
extension. This register controls the LPM behaviour and controls the behaviour
of each USB port.
Signed-off-by: Jacob Pan <jacob.jun.pan@intel.com>
Signed-off-by: Alek Du <alek.du@intel.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 62 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 3 |
3 files changed, 65 insertions, 6 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7bee1638dfd7..5fbc67c5a913 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -249,6 +249,12 @@ static int ehci_reset (struct ehci_hcd *ehci) | |||
249 | retval = handshake (ehci, &ehci->regs->command, | 249 | retval = handshake (ehci, &ehci->regs->command, |
250 | CMD_RESET, 0, 250 * 1000); | 250 | CMD_RESET, 0, 250 * 1000); |
251 | 251 | ||
252 | if (ehci->has_hostpc) { | ||
253 | ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS, | ||
254 | (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX)); | ||
255 | ehci_writel(ehci, TXFIFO_DEFAULT, | ||
256 | (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING)); | ||
257 | } | ||
252 | if (retval) | 258 | if (retval) |
253 | return retval; | 259 | return retval; |
254 | 260 | ||
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index f46ad27c9a90..6fef1ee7d101 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -111,6 +111,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
111 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 111 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
112 | int port; | 112 | int port; |
113 | int mask; | 113 | int mask; |
114 | u32 __iomem *hostpc_reg = NULL; | ||
114 | 115 | ||
115 | ehci_dbg(ehci, "suspend root hub\n"); | 116 | ehci_dbg(ehci, "suspend root hub\n"); |
116 | 117 | ||
@@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
142 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; | 143 | u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; |
143 | u32 t2 = t1; | 144 | u32 t2 = t1; |
144 | 145 | ||
146 | if (ehci->has_hostpc) | ||
147 | hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs | ||
148 | + HOSTPC0 + 4 * (port & 0xff)); | ||
145 | /* keep track of which ports we suspend */ | 149 | /* keep track of which ports we suspend */ |
146 | if (t1 & PORT_OWNER) | 150 | if (t1 & PORT_OWNER) |
147 | set_bit(port, &ehci->owned_ports); | 151 | set_bit(port, &ehci->owned_ports); |
@@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
151 | } | 155 | } |
152 | 156 | ||
153 | /* enable remote wakeup on all ports */ | 157 | /* enable remote wakeup on all ports */ |
154 | if (hcd->self.root_hub->do_remote_wakeup) | 158 | if (hcd->self.root_hub->do_remote_wakeup) { |
155 | t2 |= PORT_WAKE_BITS; | 159 | /* only enable appropriate wake bits, otherwise the |
156 | else | 160 | * hardware can not go phy low power mode. If a race |
161 | * condition happens here(connection change during bits | ||
162 | * set), the port change detection will finally fix it. | ||
163 | */ | ||
164 | if (t1 & PORT_CONNECT) { | ||
165 | t2 |= PORT_WKOC_E | PORT_WKDISC_E; | ||
166 | t2 &= ~PORT_WKCONN_E; | ||
167 | } else { | ||
168 | t2 |= PORT_WKOC_E | PORT_WKCONN_E; | ||
169 | t2 &= ~PORT_WKDISC_E; | ||
170 | } | ||
171 | } else | ||
157 | t2 &= ~PORT_WAKE_BITS; | 172 | t2 &= ~PORT_WAKE_BITS; |
158 | 173 | ||
159 | if (t1 != t2) { | 174 | if (t1 != t2) { |
160 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", | 175 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", |
161 | port + 1, t1, t2); | 176 | port + 1, t1, t2); |
162 | ehci_writel(ehci, t2, reg); | 177 | ehci_writel(ehci, t2, reg); |
178 | if (hostpc_reg) { | ||
179 | u32 t3; | ||
180 | |||
181 | msleep(5);/* 5ms for HCD enter low pwr mode */ | ||
182 | t3 = ehci_readl(ehci, hostpc_reg); | ||
183 | ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); | ||
184 | t3 = ehci_readl(ehci, hostpc_reg); | ||
185 | ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", | ||
186 | port, (t3 & HOSTPC_PHCD) ? | ||
187 | "succeeded" : "failed"); | ||
188 | } | ||
163 | } | 189 | } |
164 | } | 190 | } |
165 | 191 | ||
@@ -563,7 +589,8 @@ static int ehci_hub_control ( | |||
563 | int ports = HCS_N_PORTS (ehci->hcs_params); | 589 | int ports = HCS_N_PORTS (ehci->hcs_params); |
564 | u32 __iomem *status_reg = &ehci->regs->port_status[ | 590 | u32 __iomem *status_reg = &ehci->regs->port_status[ |
565 | (wIndex & 0xff) - 1]; | 591 | (wIndex & 0xff) - 1]; |
566 | u32 temp, status; | 592 | u32 __iomem *hostpc_reg = NULL; |
593 | u32 temp, temp1, status; | ||
567 | unsigned long flags; | 594 | unsigned long flags; |
568 | int retval = 0; | 595 | int retval = 0; |
569 | unsigned selector; | 596 | unsigned selector; |
@@ -575,6 +602,9 @@ static int ehci_hub_control ( | |||
575 | * power, "this is the one", etc. EHCI spec supports this. | 602 | * power, "this is the one", etc. EHCI spec supports this. |
576 | */ | 603 | */ |
577 | 604 | ||
605 | if (ehci->has_hostpc) | ||
606 | hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs | ||
607 | + HOSTPC0 + 4 * ((wIndex & 0xff) - 1)); | ||
578 | spin_lock_irqsave (&ehci->lock, flags); | 608 | spin_lock_irqsave (&ehci->lock, flags); |
579 | switch (typeReq) { | 609 | switch (typeReq) { |
580 | case ClearHubFeature: | 610 | case ClearHubFeature: |
@@ -773,7 +803,11 @@ static int ehci_hub_control ( | |||
773 | if (temp & PORT_CONNECT) { | 803 | if (temp & PORT_CONNECT) { |
774 | status |= 1 << USB_PORT_FEAT_CONNECTION; | 804 | status |= 1 << USB_PORT_FEAT_CONNECTION; |
775 | // status may be from integrated TT | 805 | // status may be from integrated TT |
776 | status |= ehci_port_speed(ehci, temp); | 806 | if (ehci->has_hostpc) { |
807 | temp1 = ehci_readl(ehci, hostpc_reg); | ||
808 | status |= ehci_port_speed(ehci, temp1); | ||
809 | } else | ||
810 | status |= ehci_port_speed(ehci, temp); | ||
777 | } | 811 | } |
778 | if (temp & PORT_PE) | 812 | if (temp & PORT_PE) |
779 | status |= 1 << USB_PORT_FEAT_ENABLE; | 813 | status |= 1 << USB_PORT_FEAT_ENABLE; |
@@ -832,6 +866,24 @@ static int ehci_hub_control ( | |||
832 | || (temp & PORT_RESET) != 0) | 866 | || (temp & PORT_RESET) != 0) |
833 | goto error; | 867 | goto error; |
834 | ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); | 868 | ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); |
869 | /* After above check the port must be connected. | ||
870 | * Set appropriate bit thus could put phy into low power | ||
871 | * mode if we have hostpc feature | ||
872 | */ | ||
873 | if (hostpc_reg) { | ||
874 | temp &= ~PORT_WKCONN_E; | ||
875 | temp |= (PORT_WKDISC_E | PORT_WKOC_E); | ||
876 | ehci_writel(ehci, temp | PORT_SUSPEND, | ||
877 | status_reg); | ||
878 | msleep(5);/* 5ms for HCD enter low pwr mode */ | ||
879 | temp1 = ehci_readl(ehci, hostpc_reg); | ||
880 | ehci_writel(ehci, temp1 | HOSTPC_PHCD, | ||
881 | hostpc_reg); | ||
882 | temp1 = ehci_readl(ehci, hostpc_reg); | ||
883 | ehci_dbg(ehci, "Port%d phy low pwr mode %s\n", | ||
884 | wIndex, (temp1 & HOSTPC_PHCD) ? | ||
885 | "succeeded" : "failed"); | ||
886 | } | ||
835 | set_bit(wIndex, &ehci->suspended_ports); | 887 | set_bit(wIndex, &ehci->suspended_ports); |
836 | break; | 888 | break; |
837 | case USB_PORT_FEAT_POWER: | 889 | case USB_PORT_FEAT_POWER: |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index fa12f20fbfe2..ec3dba6b8e48 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -136,6 +136,7 @@ struct ehci_hcd { /* one per controller */ | |||
136 | #define OHCI_HCCTRL_OFFSET 0x4 | 136 | #define OHCI_HCCTRL_OFFSET 0x4 |
137 | #define OHCI_HCCTRL_LEN 0x4 | 137 | #define OHCI_HCCTRL_LEN 0x4 |
138 | __hc32 *ohci_hcctrl_reg; | 138 | __hc32 *ohci_hcctrl_reg; |
139 | unsigned has_hostpc:1; | ||
139 | 140 | ||
140 | u8 sbrn; /* packed release number */ | 141 | u8 sbrn; /* packed release number */ |
141 | 142 | ||
@@ -548,7 +549,7 @@ static inline unsigned int | |||
548 | ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) | 549 | ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) |
549 | { | 550 | { |
550 | if (ehci_is_TDI(ehci)) { | 551 | if (ehci_is_TDI(ehci)) { |
551 | switch ((portsc>>26)&3) { | 552 | switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) { |
552 | case 0: | 553 | case 0: |
553 | return 0; | 554 | return 0; |
554 | case 1: | 555 | case 1: |