linux/arch/arm/mach-omap2/omap_phy_internal.c
<<
>>
Prefs
   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
  45static struct clk *phyclk, *clk48m, *clk32k;
  46static void __iomem *ctrl_base;
  47static int usbotghs_control;
  48
  49int 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
  90int 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
 110int 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
 132int 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
 158int 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
 172void 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
 188void 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
 226void 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
 236void 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#ifdef  CONFIG_USB_MUSB_HDRC_HCD
 243        case MUSB_HOST:         /* Force VBUS valid, ID = 0 */
 244                devconf2 |= CONF2_FORCE_HOST;
 245                break;
 246#endif
 247#ifdef  CONFIG_USB_GADGET_MUSB_HDRC
 248        case MUSB_PERIPHERAL:   /* Force VBUS valid, ID = 1 */
 249                devconf2 |= CONF2_FORCE_DEVICE;
 250                break;
 251#endif
 252#ifdef  CONFIG_USB_MUSB_OTG
 253        case MUSB_OTG:          /* Don't override the VBUS/ID comparators */
 254                devconf2 |= CONF2_NO_OVERRIDE;
 255                break;
 256#endif
 257        default:
 258                pr_info(KERN_INFO "Unsupported mode %u\n", musb_mode);
 259        }
 260
 261        omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
 262}
 263