USB: EHCI: Add Intel Moorestown EHCI controller HOSTPCx extensions and support phy...
authorAlek Du <alek.du@intel.com>
Mon, 13 Jul 2009 04:41:20 +0000 (12:41 +0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 23 Sep 2009 13:46:29 +0000 (06:46 -0700)
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>
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci.h
include/linux/usb/ehci_def.h

index 7bee1638dfd75180b2002b564ed6ad05bbbe867c..5fbc67c5a9136986930101eb2add9e225bc1394f 100644 (file)
@@ -249,6 +249,12 @@ static int ehci_reset (struct ehci_hcd *ehci)
        retval = handshake (ehci, &ehci->regs->command,
                            CMD_RESET, 0, 250 * 1000);
 
+       if (ehci->has_hostpc) {
+               ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS,
+                       (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX));
+               ehci_writel(ehci, TXFIFO_DEFAULT,
+                       (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING));
+       }
        if (retval)
                return retval;
 
index f46ad27c9a90e2cda2e8bbb8b9b45c861af765c5..6fef1ee7d1010e9cc223290e73054cc0f60ccdc0 100644 (file)
@@ -111,6 +111,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
        int                     port;
        int                     mask;
+       u32 __iomem             *hostpc_reg = NULL;
 
        ehci_dbg(ehci, "suspend root hub\n");
 
@@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
                u32             t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
                u32             t2 = t1;
 
+               if (ehci->has_hostpc)
+                       hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
+                               + HOSTPC0 + 4 * (port & 0xff));
                /* keep track of which ports we suspend */
                if (t1 & PORT_OWNER)
                        set_bit(port, &ehci->owned_ports);
@@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
                }
 
                /* enable remote wakeup on all ports */
-               if (hcd->self.root_hub->do_remote_wakeup)
-                       t2 |= PORT_WAKE_BITS;
-               else
+               if (hcd->self.root_hub->do_remote_wakeup) {
+                       /* only enable appropriate wake bits, otherwise the
+                        * hardware can not go phy low power mode. If a race
+                        * condition happens here(connection change during bits
+                        * set), the port change detection will finally fix it.
+                        */
+                       if (t1 & PORT_CONNECT) {
+                               t2 |= PORT_WKOC_E | PORT_WKDISC_E;
+                               t2 &= ~PORT_WKCONN_E;
+                       } else {
+                               t2 |= PORT_WKOC_E | PORT_WKCONN_E;
+                               t2 &= ~PORT_WKDISC_E;
+                       }
+               } else
                        t2 &= ~PORT_WAKE_BITS;
 
                if (t1 != t2) {
                        ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
                                port + 1, t1, t2);
                        ehci_writel(ehci, t2, reg);
+                       if (hostpc_reg) {
+                               u32     t3;
+
+                               msleep(5);/* 5ms for HCD enter low pwr mode */
+                               t3 = ehci_readl(ehci, hostpc_reg);
+                               ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
+                               t3 = ehci_readl(ehci, hostpc_reg);
+                               ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
+                                       port, (t3 & HOSTPC_PHCD) ?
+                                       "succeeded" : "failed");
+                       }
                }
        }
 
@@ -563,7 +589,8 @@ static int ehci_hub_control (
        int             ports = HCS_N_PORTS (ehci->hcs_params);
        u32 __iomem     *status_reg = &ehci->regs->port_status[
                                (wIndex & 0xff) - 1];
-       u32             temp, status;
+       u32 __iomem     *hostpc_reg = NULL;
+       u32             temp, temp1, status;
        unsigned long   flags;
        int             retval = 0;
        unsigned        selector;
@@ -575,6 +602,9 @@ static int ehci_hub_control (
         * power, "this is the one", etc.  EHCI spec supports this.
         */
 
+       if (ehci->has_hostpc)
+               hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
+                               + HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
        spin_lock_irqsave (&ehci->lock, flags);
        switch (typeReq) {
        case ClearHubFeature:
@@ -773,7 +803,11 @@ static int ehci_hub_control (
                if (temp & PORT_CONNECT) {
                        status |= 1 << USB_PORT_FEAT_CONNECTION;
                        // status may be from integrated TT
-                       status |= ehci_port_speed(ehci, temp);
+                       if (ehci->has_hostpc) {
+                               temp1 = ehci_readl(ehci, hostpc_reg);
+                               status |= ehci_port_speed(ehci, temp1);
+                       } else
+                               status |= ehci_port_speed(ehci, temp);
                }
                if (temp & PORT_PE)
                        status |= 1 << USB_PORT_FEAT_ENABLE;
@@ -832,6 +866,24 @@ static int ehci_hub_control (
                                        || (temp & PORT_RESET) != 0)
                                goto error;
                        ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+                       /* After above check the port must be connected.
+                        * Set appropriate bit thus could put phy into low power
+                        * mode if we have hostpc feature
+                        */
+                       if (hostpc_reg) {
+                               temp &= ~PORT_WKCONN_E;
+                               temp |= (PORT_WKDISC_E | PORT_WKOC_E);
+                               ehci_writel(ehci, temp | PORT_SUSPEND,
+                                                       status_reg);
+                               msleep(5);/* 5ms for HCD enter low pwr mode */
+                               temp1 = ehci_readl(ehci, hostpc_reg);
+                               ehci_writel(ehci, temp1 | HOSTPC_PHCD,
+                                       hostpc_reg);
+                               temp1 = ehci_readl(ehci, hostpc_reg);
+                               ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
+                                       wIndex, (temp1 & HOSTPC_PHCD) ?
+                                       "succeeded" : "failed");
+                       }
                        set_bit(wIndex, &ehci->suspended_ports);
                        break;
                case USB_PORT_FEAT_POWER:
index fa12f20fbfe2c00866ce7f102f3021d3826731c6..ec3dba6b8e48ae41518c48eb11cfd525550d185d 100644 (file)
@@ -136,6 +136,7 @@ struct ehci_hcd {                   /* one per controller */
        #define OHCI_HCCTRL_OFFSET      0x4
        #define OHCI_HCCTRL_LEN         0x4
        __hc32                  *ohci_hcctrl_reg;
+       unsigned                has_hostpc:1;
 
        u8                      sbrn;           /* packed release number */
 
@@ -548,7 +549,7 @@ static inline unsigned int
 ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
 {
        if (ehci_is_TDI(ehci)) {
-               switch ((portsc>>26)&3) {
+               switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) {
                case 0:
                        return 0;
                case 1:
index 5b88e36c91033e376a88be4ccf570622247d81a9..4c4b701ff265a03ec1f3be27a9cfd65aa7736737 100644 (file)
@@ -132,6 +132,19 @@ struct ehci_regs {
 #define USBMODE_CM_HC  (3<<0)          /* host controller mode */
 #define USBMODE_CM_IDLE        (0<<0)          /* idle state */
 
+/* Moorestown has some non-standard registers, partially due to the fact that
+ * its EHCI controller has both TT and LPM support. HOSTPCx are extentions to
+ * PORTSCx
+ */
+#define HOSTPC0                0x84            /* HOSTPC extension */
+#define HOSTPC_PHCD    (1<<22)         /* Phy clock disable */
+#define HOSTPC_PSPD    (3<<25)         /* Port speed detection */
+#define USBMODE_EX     0xc8            /* USB Device mode extension */
+#define USBMODE_EX_VBPS        (1<<5)          /* VBus Power Select On */
+#define USBMODE_EX_HC  (3<<0)          /* host controller mode */
+#define TXFILLTUNING   0x24            /* TX FIFO Tuning register */
+#define TXFIFO_DEFAULT (8<<16)         /* FIFO burst threshold 8 */
+
 /* Appendix C, Debug port ... intended for use with special "debug devices"
  * that can help if there's no serial console.  (nonstandard enumeration.)
  */