1 /* 2 * This file configures the internal USB PHY in OMAP4430. Used 3 * with TWL6030 transceiver and MUSB on OMAP4430. 4 * 5 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * Author: Hema HK <hemahk@ti.com> 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 * 22 */ 23 24 #include <linux/types.h> 25 #include <linux/delay.h> 26 #include <linux/clk.h> 27 #include <linux/io.h> 28 #include <linux/err.h> 29 #include <linux/usb.h> 30 31 #include <plat/usb.h> 32 #include "control.h" 33 34 /* OMAP control module register for UTMI PHY */ 35 #define CONTROL_DEV_CONF 0x300 36 #define PHY_PD 0x1 37 38 #define USBOTGHS_CONTROL 0x33c 39 #define AVALID BIT(0) 40 #define BVALID BIT(1) 41 #define VBUSVALID BIT(2) 42 #define SESSEND BIT(3) 43 #define IDDIG BIT(4) 44 45 static struct clk *phyclk, *clk48m, *clk32k; 46 static void __iomem *ctrl_base; 47 static int usbotghs_control; 48 49 int omap4430_phy_init(struct device *dev) 50 { 51 ctrl_base = ioremap(OMAP443X_SCM_BASE, SZ_1K); 52 if (!ctrl_base) { 53 pr_err("control module ioremap failed\n"); 54 return -ENOMEM; 55 } 56 /* Power down the phy */ 57 __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF); 58 59 if (!dev) { 60 iounmap(ctrl_base); 61 return 0; 62 } 63 64 phyclk = clk_get(dev, "ocp2scp_usb_phy_ick"); 65 if (IS_ERR(phyclk)) { 66 dev_err(dev, "cannot clk_get ocp2scp_usb_phy_ick\n"); 67 iounmap(ctrl_base); 68 return PTR_ERR(phyclk); 69 } 70 71 clk48m = clk_get(dev, "ocp2scp_usb_phy_phy_48m"); 72 if (IS_ERR(clk48m)) { 73 dev_err(dev, "cannot clk_get ocp2scp_usb_phy_phy_48m\n"); 74 clk_put(phyclk); 75 iounmap(ctrl_base); 76 return PTR_ERR(clk48m); 77 } 78 79 clk32k = clk_get(dev, "usb_phy_cm_clk32k"); 80 if (IS_ERR(clk32k)) { 81 dev_err(dev, "cannot clk_get usb_phy_cm_clk32k\n"); 82 clk_put(phyclk); 83 clk_put(clk48m); 84 iounmap(ctrl_base); 85 return PTR_ERR(clk32k); 86 } 87 return 0; 88 } 89 90 int omap4430_phy_set_clk(struct device *dev, int on) 91 { 92 static int state; 93 94 if (on && !state) { 95 /* Enable the phy clocks */ 96 clk_enable(phyclk); 97 clk_enable(clk48m); 98 clk_enable(clk32k); 99 state = 1; 100 } else if (state) { 101 /* Disable the phy clocks */ 102 clk_disable(phyclk); 103 clk_disable(clk48m); 104 clk_disable(clk32k); 105 state = 0; 106 } 107 return 0; 108 } 109 110 int omap4430_phy_power(struct device *dev, int ID, int on) 111 { 112 if (on) { 113 if (ID) 114 /* enable VBUS valid, IDDIG groung */ 115 __raw_writel(AVALID | VBUSVALID, ctrl_base + 116 USBOTGHS_CONTROL); 117 else 118 /* 119 * Enable VBUS Valid, AValid and IDDIG 120 * high impedance 121 */ 122 __raw_writel(IDDIG | AVALID | VBUSVALID, 123 ctrl_base + USBOTGHS_CONTROL); 124 } else { 125 /* Enable session END and IDIG to high impedance. */ 126 __raw_writel(SESSEND | IDDIG, ctrl_base + 127 USBOTGHS_CONTROL); 128 } 129 return 0; 130 } 131 132 int omap4430_phy_suspend(struct device *dev, int suspend) 133 { 134 if (suspend) { 135 /* Disable the clocks */ 136 omap4430_phy_set_clk(dev, 0); 137 /* Power down the phy */ 138 __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF); 139 140 /* save the context */ 141 usbotghs_control = __raw_readl(ctrl_base + USBOTGHS_CONTROL); 142 } else { 143 /* Enable the internel phy clcoks */ 144 omap4430_phy_set_clk(dev, 1); 145 /* power on the phy */ 146 if (__raw_readl(ctrl_base + CONTROL_DEV_CONF) & PHY_PD) { 147 __raw_writel(~PHY_PD, ctrl_base + CONTROL_DEV_CONF); 148 mdelay(200); 149 } 150 151 /* restore the context */ 152 __raw_writel(usbotghs_control, ctrl_base + USBOTGHS_CONTROL); 153 } 154 155 return 0; 156 } 157 158 int omap4430_phy_exit(struct device *dev) 159 { 160 if (ctrl_base) 161 iounmap(ctrl_base); 162 if (phyclk) 163 clk_put(phyclk); 164 if (clk48m) 165 clk_put(clk48m); 166 if (clk32k) 167 clk_put(clk32k); 168 169 return 0; 170 } 171 172 void am35x_musb_reset(void) 173 { 174 u32 regval; 175 176 /* Reset the musb interface */ 177 regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); 178 179 regval |= AM35XX_USBOTGSS_SW_RST; 180 omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET); 181 182 regval &= ~AM35XX_USBOTGSS_SW_RST; 183 omap_ctrl_writel(regval, AM35XX_CONTROL_IP_SW_RESET); 184 185 regval = omap_ctrl_readl(AM35XX_CONTROL_IP_SW_RESET); 186 } 187 188 void am35x_musb_phy_power(u8 on) 189 { 190 unsigned long timeout = jiffies + msecs_to_jiffies(100); 191 u32 devconf2; 192 193 if (on) { 194 /* 195 * Start the on-chip PHY and its PLL. 196 */ 197 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); 198 199 devconf2 &= ~(CONF2_RESET | CONF2_PHYPWRDN | CONF2_OTGPWRDN); 200 devconf2 |= CONF2_PHY_PLLON; 201 202 omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); 203 204 pr_info(KERN_INFO "Waiting for PHY clock good...\n"); 205 while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2) 206 & CONF2_PHYCLKGD)) { 207 cpu_relax(); 208 209 if (time_after(jiffies, timeout)) { 210 pr_err(KERN_ERR "musb PHY clock good timed out\n"); 211 break; 212 } 213 } 214 } else { 215 /* 216 * Power down the on-chip PHY. 217 */ 218 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); 219 220 devconf2 &= ~CONF2_PHY_PLLON; 221 devconf2 |= CONF2_PHYPWRDN | CONF2_OTGPWRDN; 222 omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); 223 } 224 } 225 226 void am35x_musb_clear_irq(void) 227 { 228 u32 regval; 229 230 regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); 231 regval |= AM35XX_USBOTGSS_INT_CLR; 232 omap_ctrl_writel(regval, AM35XX_CONTROL_LVL_INTR_CLEAR); 233 regval = omap_ctrl_readl(AM35XX_CONTROL_LVL_INTR_CLEAR); 234 } 235 236 void am35x_set_mode(u8 musb_mode) 237 { 238 u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2); 239 240 devconf2 &= ~CONF2_OTGMODE; 241 switch (musb_mode) { 242 case MUSB_HOST: /* Force VBUS valid, ID = 0 */ 243 devconf2 |= CONF2_FORCE_HOST; 244 break; 245 case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */ 246 devconf2 |= CONF2_FORCE_DEVICE; 247 break; 248 case MUSB_OTG: /* Don't override the VBUS/ID comparators */ 249 devconf2 |= CONF2_NO_OVERRIDE; 250 break; 251 default: 252 pr_info(KERN_INFO "Unsupported mode %u\n", musb_mode); 253 } 254 255 omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2); 256 } 257 258 void ti81xx_musb_phy_power(u8 on) 259 { 260 void __iomem *scm_base = NULL; 261 u32 usbphycfg; 262 263 scm_base = ioremap(TI81XX_SCM_BASE, SZ_2K); 264 if (!scm_base) { 265 pr_err("system control module ioremap failed\n"); 266 return; 267 } 268 269 usbphycfg = __raw_readl(scm_base + USBCTRL0); 270 271 if (on) { 272 if (cpu_is_ti816x()) { 273 usbphycfg |= TI816X_USBPHY0_NORMAL_MODE; 274 usbphycfg &= ~TI816X_USBPHY_REFCLK_OSC; 275 } else if (cpu_is_ti814x()) { 276 usbphycfg &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN 277 | USBPHY_DPINPUT | USBPHY_DMINPUT); 278 usbphycfg |= (USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN 279 | USBPHY_DPOPBUFCTL | USBPHY_DMOPBUFCTL); 280 } 281 } else { 282 if (cpu_is_ti816x()) 283 usbphycfg &= ~TI816X_USBPHY0_NORMAL_MODE; 284 else if (cpu_is_ti814x()) 285 usbphycfg |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN; 286 287 } 288 __raw_writel(usbphycfg, scm_base + USBCTRL0); 289 290 iounmap(scm_base); 291 } 292