|
@@ -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;
|