mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 05:41:55 +00:00
310018d52e
Commit 62285963
(usb: musb: drop a gigantic amount of ifdeferry)
got rid of a bunch of ifdefs in the MUSB code. Looks like the
platform init code is still using these dropped defines though,
which in many cases results the board defaulting always to host
mode.
Currently the situation is that USB_MUSB_HDRC is the main
Kconfig option with additional USB_GADGET_MUSB_HDRC so only
these two should be used to select between host and OTG mode.
Fix the situation for omaps. The following users should fix the
platform init code in a similar way:
Dropped Kconfig option Current users
USB_MUSB_OTG blackfin, davinci, not in Kconfigs
USB_MUSB_PERIPHERAL davinci, not in Kconfigs
USB_MUSB_HOST davinci, not in Kconfigs
USB_MUSB_HDRC_HCD blackfin, not in Kconfigs
USB_MUSB_OTG blackfin, not in Kconfigs
Cc: Mike Frysinger <vapier@gentoo.org>
Cc: Sekhar Nori <nsekhar@ti.com>
Cc: linux-usb@vger.kernel.org
Cc: Felipe Balbi <balbi@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
292 lines
6.9 KiB
C
292 lines
6.9 KiB
C
/*
|
|
* This file configures the internal USB PHY in OMAP4430. Used
|
|
* with TWL6030 transceiver and MUSB on OMAP4430.
|
|
*
|
|
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Author: Hema HK <hemahk@ti.com>
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/io.h>
|
|
#include <linux/err.h>
|
|
#include <linux/usb.h>
|
|
|
|
#include <plat/usb.h>
|
|
#include "control.h"
|
|
|
|
/* OMAP control module register for UTMI PHY */
|
|
#define CONTROL_DEV_CONF 0x300
|
|
#define PHY_PD 0x1
|
|
|
|
#define USBOTGHS_CONTROL 0x33c
|
|
#define AVALID BIT(0)
|
|
#define BVALID BIT(1)
|
|
#define VBUSVALID BIT(2)
|
|
#define SESSEND BIT(3)
|
|
#define IDDIG BIT(4)
|
|
|
|
static struct clk *phyclk, *clk48m, *clk32k;
|
|
static void __iomem *ctrl_base;
|
|
static int usbotghs_control;
|
|
|
|
int omap4430_phy_init(struct device *dev)
|
|
{
|
|
ctrl_base = ioremap(OMAP443X_SCM_BASE, SZ_1K);
|
|
if (!ctrl_base) {
|
|
pr_err("control module ioremap failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
/* Power down the phy */
|
|
__raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
|
|
|
|
if (!dev) {
|
|
iounmap(ctrl_base);
|
|
return 0;
|
|
}
|
|
|
|
phyclk = clk_get(dev, "ocp2scp_usb_phy_ick");
|
|
if (IS_ERR(phyclk)) {
|
|
dev_err(dev, "cannot clk_get ocp2scp_usb_phy_ick\n");
|
|
iounmap(ctrl_base);
|
|
return PTR_ERR(phyclk);
|
|
}
|
|
|
|
clk48m = clk_get(dev, "ocp2scp_usb_phy_phy_48m");
|
|
if (IS_ERR(clk48m)) {
|
|
dev_err(dev, "cannot clk_get ocp2scp_usb_phy_phy_48m\n");
|
|
clk_put(phyclk);
|
|
iounmap(ctrl_base);
|
|
return PTR_ERR(clk48m);
|
|
}
|
|
|
|
clk32k = clk_get(dev, "usb_phy_cm_clk32k");
|
|
if (IS_ERR(clk32k)) {
|
|
dev_err(dev, "cannot clk_get usb_phy_cm_clk32k\n");
|
|
clk_put(phyclk);
|
|
clk_put(clk48m);
|
|
iounmap(ctrl_base);
|
|
return PTR_ERR(clk32k);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int omap4430_phy_set_clk(struct device *dev, int on)
|
|
{
|
|
static int state;
|
|
|
|
if (on && !state) {
|
|
/* Enable the phy clocks */
|
|
clk_enable(phyclk);
|
|
clk_enable(clk48m);
|
|
clk_enable(clk32k);
|
|
state = 1;
|
|
} else if (state) {
|
|
/* Disable the phy clocks */
|
|
clk_disable(phyclk);
|
|
clk_disable(clk48m);
|
|
clk_disable(clk32k);
|
|
state = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int omap4430_phy_power(struct device *dev, int ID, int on)
|
|
{
|
|
if (on) {
|
|
if (ID)
|
|
/* enable VBUS valid, IDDIG groung */
|
|
__raw_writel(AVALID | VBUSVALID, ctrl_base +
|
|
USBOTGHS_CONTROL);
|
|
else
|
|
/*
|
|
* Enable VBUS Valid, AValid and IDDIG
|
|
* high impedance
|
|
*/
|
|
__raw_writel(IDDIG | AVALID | VBUSVALID,
|
|
ctrl_base + USBOTGHS_CONTROL);
|
|
} else {
|
|
/* Enable session END and IDIG to high impedance. */
|
|
__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;
|
|
}
|
|
|
|
int omap4430_phy_exit(struct device *dev)
|
|
{
|
|
if (ctrl_base)
|
|
iounmap(ctrl_base);
|
|
if (phyclk)
|
|
clk_put(phyclk);
|
|
if (clk48m)
|
|
clk_put(clk48m);
|
|
if (clk32k)
|
|
clk_put(clk32k);
|
|
|
|
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_set_mode(u8 musb_mode)
|
|
{
|
|
u32 devconf2 = omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2);
|
|
|
|
devconf2 &= ~CONF2_OTGMODE;
|
|
switch (musb_mode) {
|
|
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
|
|
devconf2 |= CONF2_FORCE_HOST;
|
|
break;
|
|
case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */
|
|
devconf2 |= CONF2_FORCE_DEVICE;
|
|
break;
|
|
case MUSB_OTG: /* Don't override the VBUS/ID comparators */
|
|
devconf2 |= CONF2_NO_OVERRIDE;
|
|
break;
|
|
default:
|
|
pr_info(KERN_INFO "Unsupported mode %u\n", musb_mode);
|
|
}
|
|
|
|
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
|
|
}
|
|
|
|
void ti81xx_musb_phy_power(u8 on)
|
|
{
|
|
void __iomem *scm_base = NULL;
|
|
u32 usbphycfg;
|
|
|
|
scm_base = ioremap(TI81XX_SCM_BASE, SZ_2K);
|
|
if (!scm_base) {
|
|
pr_err("system control module ioremap failed\n");
|
|
return;
|
|
}
|
|
|
|
usbphycfg = __raw_readl(scm_base + USBCTRL0);
|
|
|
|
if (on) {
|
|
if (cpu_is_ti816x()) {
|
|
usbphycfg |= TI816X_USBPHY0_NORMAL_MODE;
|
|
usbphycfg &= ~TI816X_USBPHY_REFCLK_OSC;
|
|
} else if (cpu_is_ti814x()) {
|
|
usbphycfg &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN
|
|
| USBPHY_DPINPUT | USBPHY_DMINPUT);
|
|
usbphycfg |= (USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN
|
|
| USBPHY_DPOPBUFCTL | USBPHY_DMOPBUFCTL);
|
|
}
|
|
} else {
|
|
if (cpu_is_ti816x())
|
|
usbphycfg &= ~TI816X_USBPHY0_NORMAL_MODE;
|
|
else if (cpu_is_ti814x())
|
|
usbphycfg |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN;
|
|
|
|
}
|
|
__raw_writel(usbphycfg, scm_base + USBCTRL0);
|
|
|
|
iounmap(scm_base);
|
|
}
|