USB: ehci-fsl: add MPC5121E specific suspend and resume
authorAnatolij Gustschin <agust@denx.de>
Mon, 18 Apr 2011 20:01:55 +0000 (22:01 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 2 May 2011 23:59:36 +0000 (16:59 -0700)
Signed-off-by: Anatolij Gustschin <agust@denx.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-fsl.h
include/linux/fsl_devices.h

index 5c761df7fa83cfb212943009bc7a10beb4a619fe..caf3d4ac42bddda44e6803a84acb0fdc43c77bda 100644 (file)
@@ -328,6 +328,149 @@ struct ehci_fsl {
 
 #ifdef CONFIG_PM
 
+#ifdef CONFIG_PPC_MPC512x
+static int ehci_fsl_mpc512x_drv_suspend(struct device *dev)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       struct fsl_usb2_platform_data *pdata = dev->platform_data;
+       u32 tmp;
+
+#ifdef DEBUG
+       u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE);
+       mode &= USBMODE_CM_MASK;
+       tmp = ehci_readl(ehci, hcd->regs + 0x140);      /* usbcmd */
+
+       dev_dbg(dev, "suspend=%d already_suspended=%d "
+               "mode=%d  usbcmd %08x\n", pdata->suspended,
+               pdata->already_suspended, mode, tmp);
+#endif
+
+       /*
+        * If the controller is already suspended, then this must be a
+        * PM suspend.  Remember this fact, so that we will leave the
+        * controller suspended at PM resume time.
+        */
+       if (pdata->suspended) {
+               dev_dbg(dev, "already suspended, leaving early\n");
+               pdata->already_suspended = 1;
+               return 0;
+       }
+
+       dev_dbg(dev, "suspending...\n");
+
+       hcd->state = HC_STATE_SUSPENDED;
+       dev->power.power_state = PMSG_SUSPEND;
+
+       /* ignore non-host interrupts */
+       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
+       /* stop the controller */
+       tmp = ehci_readl(ehci, &ehci->regs->command);
+       tmp &= ~CMD_RUN;
+       ehci_writel(ehci, tmp, &ehci->regs->command);
+
+       /* save EHCI registers */
+       pdata->pm_command = ehci_readl(ehci, &ehci->regs->command);
+       pdata->pm_command &= ~CMD_RUN;
+       pdata->pm_status  = ehci_readl(ehci, &ehci->regs->status);
+       pdata->pm_intr_enable  = ehci_readl(ehci, &ehci->regs->intr_enable);
+       pdata->pm_frame_index  = ehci_readl(ehci, &ehci->regs->frame_index);
+       pdata->pm_segment  = ehci_readl(ehci, &ehci->regs->segment);
+       pdata->pm_frame_list  = ehci_readl(ehci, &ehci->regs->frame_list);
+       pdata->pm_async_next  = ehci_readl(ehci, &ehci->regs->async_next);
+       pdata->pm_configured_flag  =
+               ehci_readl(ehci, &ehci->regs->configured_flag);
+       pdata->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]);
+       pdata->pm_usbgenctrl = ehci_readl(ehci,
+                                         hcd->regs + FSL_SOC_USB_USBGENCTRL);
+
+       /* clear the W1C bits */
+       pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS);
+
+       pdata->suspended = 1;
+
+       /* clear PP to cut power to the port */
+       tmp = ehci_readl(ehci, &ehci->regs->port_status[0]);
+       tmp &= ~PORT_POWER;
+       ehci_writel(ehci, tmp, &ehci->regs->port_status[0]);
+
+       return 0;
+}
+
+static int ehci_fsl_mpc512x_drv_resume(struct device *dev)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       struct fsl_usb2_platform_data *pdata = dev->platform_data;
+       u32 tmp;
+
+       dev_dbg(dev, "suspend=%d already_suspended=%d\n",
+               pdata->suspended, pdata->already_suspended);
+
+       /*
+        * If the controller was already suspended at suspend time,
+        * then don't resume it now.
+        */
+       if (pdata->already_suspended) {
+               dev_dbg(dev, "already suspended, leaving early\n");
+               pdata->already_suspended = 0;
+               return 0;
+       }
+
+       if (!pdata->suspended) {
+               dev_dbg(dev, "not suspended, leaving early\n");
+               return 0;
+       }
+
+       pdata->suspended = 0;
+
+       dev_dbg(dev, "resuming...\n");
+
+       /* set host mode */
+       tmp = USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0);
+       ehci_writel(ehci, tmp, hcd->regs + FSL_SOC_USB_USBMODE);
+
+       ehci_writel(ehci, pdata->pm_usbgenctrl,
+                   hcd->regs + FSL_SOC_USB_USBGENCTRL);
+       ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE,
+                   hcd->regs + FSL_SOC_USB_ISIPHYCTRL);
+
+       /* restore EHCI registers */
+       ehci_writel(ehci, pdata->pm_command, &ehci->regs->command);
+       ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable);
+       ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index);
+       ehci_writel(ehci, pdata->pm_segment, &ehci->regs->segment);
+       ehci_writel(ehci, pdata->pm_frame_list, &ehci->regs->frame_list);
+       ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next);
+       ehci_writel(ehci, pdata->pm_configured_flag,
+                   &ehci->regs->configured_flag);
+       ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]);
+
+       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+       hcd->state = HC_STATE_RUNNING;
+       dev->power.power_state = PMSG_ON;
+
+       tmp = ehci_readl(ehci, &ehci->regs->command);
+       tmp |= CMD_RUN;
+       ehci_writel(ehci, tmp, &ehci->regs->command);
+
+       usb_hcd_resume_root_hub(hcd);
+
+       return 0;
+}
+#else
+static inline int ehci_fsl_mpc512x_drv_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev)
+{
+       return 0;
+}
+#endif /* CONFIG_PPC_MPC512x */
+
 static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd)
 {
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -341,6 +484,11 @@ static int ehci_fsl_drv_suspend(struct device *dev)
        struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
        void __iomem *non_ehci = hcd->regs;
 
+       if (of_device_is_compatible(dev->parent->of_node,
+                                   "fsl,mpc5121-usb2-dr")) {
+               return ehci_fsl_mpc512x_drv_suspend(dev);
+       }
+
        ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
                        device_may_wakeup(dev));
        if (!fsl_deep_sleep())
@@ -357,6 +505,11 @@ static int ehci_fsl_drv_resume(struct device *dev)
        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
        void __iomem *non_ehci = hcd->regs;
 
+       if (of_device_is_compatible(dev->parent->of_node,
+                                   "fsl,mpc5121-usb2-dr")) {
+               return ehci_fsl_mpc512x_drv_resume(dev);
+       }
+
        ehci_prepare_ports_for_controller_resume(ehci);
        if (!fsl_deep_sleep())
                return 0;
index 3fabed33d940d9f2f6bbb094de137b82f5282bbc..4918062211656c276f5904cc50d45605076ed026 100644 (file)
 #define        PORT_PTS_SERIAL         (3<<30)
 #define PORT_PTS_PTW           (1<<28)
 #define FSL_SOC_USB_PORTSC2    0x188
+#define FSL_SOC_USB_USBMODE    0x1a8
+#define USBMODE_CM_MASK                (3 << 0)        /* controller mode mask */
+#define USBMODE_CM_HOST                (3 << 0)        /* controller mode: host */
+#define USBMODE_ES             (1 << 2)        /* (Big) Endian Select */
 
 #define FSL_SOC_USB_USBGENCTRL 0x200
 #define USBGENCTRL_PPP         (1 << 3)
index 4eb56ed75fbceec07dce3a92cbd5a81b9c150a78..3773c5dab8f5423e6a92149cc50324d99ea7eb3c 100644 (file)
@@ -79,6 +79,21 @@ struct fsl_usb2_platform_data {
        unsigned        have_sysif_regs:1;
        unsigned        invert_drvvbus:1;
        unsigned        invert_pwr_fault:1;
+
+       unsigned        suspended:1;
+       unsigned        already_suspended:1;
+
+       /* register save area for suspend/resume */
+       u32             pm_command;
+       u32             pm_status;
+       u32             pm_intr_enable;
+       u32             pm_frame_index;
+       u32             pm_segment;
+       u32             pm_frame_list;
+       u32             pm_async_next;
+       u32             pm_configured_flag;
+       u32             pm_portsc;
+       u32             pm_usbgenctrl;
 };
 
 /* Flags in fsl_usb2_mph_platform_data */