Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
[pandora-kernel.git] / arch / arm / mach-omap2 / omap_phy_internal.c
index 745252c..e2e605f 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/usb.h>
 
 #include <plat/usb.h>
+#include "control.h"
 
 /* OMAP control module register for UTMI PHY */
 #define CONTROL_DEV_CONF               0x300
@@ -43,6 +44,7 @@
 
 static struct clk *phyclk, *clk48m, *clk32k;
 static void __iomem *ctrl_base;
+static int usbotghs_control;
 
 int omap4430_phy_init(struct device *dev)
 {
@@ -103,13 +105,6 @@ int omap4430_phy_set_clk(struct device *dev, int on)
 int omap4430_phy_power(struct device *dev, int ID, int on)
 {
        if (on) {
-               /* enabled the clocks */
-               omap4430_phy_set_clk(dev, 1);
-               /* power on the phy */
-               if (__raw_readl(ctrl_base + CONTROL_DEV_CONF) & PHY_PD) {
-                       __raw_writel(~PHY_PD, ctrl_base + CONTROL_DEV_CONF);
-                       mdelay(200);
-               }
                if (ID)
                        /* enable VBUS valid, IDDIG groung */
                        __raw_writel(AVALID | VBUSVALID, ctrl_base +
@@ -125,10 +120,31 @@ int omap4430_phy_power(struct device *dev, int ID, int on)
                /* Enable session END and IDIG to high impedence. */
                __raw_writel(SESSEND | IDDIG, ctrl_base +
                                        USBOTGHS_CONTROL);
+       }
+       return 0;
+}
+
+int omap4430_phy_suspend(struct device *dev, int suspend)
+{
+       if (suspend) {
                /* Disable the clocks */
                omap4430_phy_set_clk(dev, 0);
                /* Power down the phy */
                __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
+
+               /* save the context */
+               usbotghs_control = __raw_readl(ctrl_base + USBOTGHS_CONTROL);
+       } else {
+               /* Enable the internel phy clcoks */
+               omap4430_phy_set_clk(dev, 1);
+               /* power on the phy */
+               if (__raw_readl(ctrl_base + CONTROL_DEV_CONF) & PHY_PD) {
+                       __raw_writel(~PHY_PD, ctrl_base + CONTROL_DEV_CONF);
+                       mdelay(200);
+               }
+
+               /* restore the context */
+               __raw_writel(usbotghs_control, ctrl_base + USBOTGHS_CONTROL);
        }
 
        return 0;
@@ -147,3 +163,95 @@ int omap4430_phy_exit(struct device *dev)
 
        return 0;
 }
+
+void am35x_musb_reset(void)
+{
+       u32     regval;
+
+       /* Reset the musb interface */
+       regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET);
+
+       regval |= AM35XX_USBOTGSS_SW_RST;
+       omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET);
+
+       regval &= ~AM35XX_USBOTGSS_SW_RST;
+       omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET);
+
+       regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET);
+}
+
+void am35x_musb_phy_power(u8 on)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(100);
+       u32 devconf2;
+
+       if (on) {
+               /*
+                * Start the on-chip PHY and its PLL.
+                */
+               devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
+
+               devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN);
+               devconf2 |= CONF2_PHY_PLLON;
+
+               omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
+
+               pr_info(KERN_INFO "Waiting for PHY clock good...\n");
+               while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2)
+                               & CONF2_PHYCLKGD)) {
+                       cpu_relax();
+
+                       if (time_after(jiffies, timeout)) {
+                               pr_err(KERN_ERR "musb PHY clock good timed out\n");
+                               break;
+                       }
+               }
+       } else {
+               /*
+                * Power down the on-chip PHY.
+                */
+               devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
+
+               devconf2 &= ~CONF2_PHY_PLLON;
+               devconf2 |=  CONF2_PHYPWRDN | CONF2_OTGPWRDN;
+               omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
+       }
+}
+
+void am35x_musb_clear_irq(void)
+{
+       u32 regval;
+
+       regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
+       regval |= AM35XX_USBOTGSS_INT_CLR;
+       omap_ctrl_writel(regval, AM35XX_CONTROL_LVL_INTR_CLEAR);
+       regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR);
+}
+
+void am35x_musb_set_mode(u8 musb_mode)
+{
+       u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
+
+       devconf2 &= ~CONF2_OTGMODE;
+       switch (musb_mode) {
+#ifdef CONFIG_USB_MUSB_HDRC_HCD
+       case MUSB_HOST:         /* Force VBUS valid, ID = 0 */
+               devconf2 |= CONF2_FORCE_HOST;
+               break;
+#endif
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+       case MUSB_PERIPHERAL:   /* Force VBUS valid, ID = 1 */
+               devconf2 |= CONF2_FORCE_DEVICE;
+               break;
+#endif
+#ifdef CONFIG_USB_MUSB_OTG
+       case MUSB_OTG:          /* Don't override the VBUS/ID comparators */
+               devconf2 |= CONF2_NO_OVERRIDE;
+               break;
+#endif
+       default:
+               pr_info(KERN_INFO "Unsupported mode %u\n", musb_mode);
+       }
+
+       omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
+}