mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-18 09:44:18 +08:00
d61b7a572b
Quite a bit of code gets removed, and some stuff moved around, mostly the old samsung s3c24xx stuff. There should be no functional changes in this series otherwise. Some cleanups have dependencies on other arm-soc branches and will be sent in the second round. Signed-off-by: Arnd Bergmann <arnd@arndb.de> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIVAwUAT2pCjGCrR//JCVInAQLd8RAAqCxhzSc4ewTUP/974gVhujj3TrpiEQcS FKvYWF76yP38Lbf3CJZBZaONRtrQNOhYpVQ0jb3WCV4F8mEH9PCes2q9RObeBYiY TNX8VdcuVjX2U9HaH0+RQtBUdujNLHpEOqtO57un7T5UDNssR5JOive1tNAooRv1 pL0Hgx3AVqUbNOPpqQqHzy/MDdd67S6dX80yysANjFGMX87Nvp/ztYAdNnIdta+Z pDJt+DPlmK8LvjoSL3SEUN0p3Thk75621cCuauGq88PLIB2w62tzF0NFFbvIAgJT 3aMlHM2flOiTJAWkUvA8zJiUzwv/0vYvH3xPoTo84abve3lVfZcY+fHNcfxE/Gge ri2MmkHyimVP3rNeyM0GbN1RTej1TN1MezeQW3nq2wP6nvS2k0/t32ObLLtWU7XA 6iA0hKVMSnhqj4ln6jPAmyaDkaWHyYz97urhgetHqGadvLTiGPXCSBPalSiFmyMo 11tvuqwUNz9tw4nsvGboFQwS2ZoVquC5inoHp5seqZETkGCB67JyeRGxtAM4gbP/ wIRa3OBLY99yo1on6QovWNnSOMC6X4cOvBI/qHIjSEY/T9JVkslY87gRg3LkxCBR XpXfZ6iuLHoSRUGcIjE8D6KHjMgWIDPRnLkIliK4H+3Jn08g0R1MxCplevFCRtis egswZ8C24Xw= =o5Xl -----END PGP SIGNATURE----- Merge tag 'cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Pull "ARM: global cleanups" from Arnd Bergmann: "Quite a bit of code gets removed, and some stuff moved around, mostly the old samsung s3c24xx stuff. There should be no functional changes in this series otherwise. Some cleanups have dependencies on other arm-soc branches and will be sent in the second round. Signed-off-by: Arnd Bergmann <arnd@arndb.de>" Fixed up trivial conflicts mainly due to #include's being changes on both sides. * tag 'cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (121 commits) ep93xx: Remove unnecessary includes of ep93xx-regs.h ep93xx: Move EP93XX_SYSCON defines to SoC private header ep93xx: Move crunch code to mach-ep93xx directory ep93xx: Make syscon access functions private to SoC ep93xx: Configure GPIO ports in core code ep93xx: Move peripheral defines to local SoC header ep93xx: Convert the watchdog driver into a platform device. ep93xx: Use ioremap for backlight driver ep93xx: Move GPIO defines to gpio-ep93xx.h ep93xx: Don't use system controller defines in audio drivers ep93xx: Move PHYS_BASE defines to local SoC header file ARM: EXYNOS: Add clock register addresses for EXYNOS4X12 bus devfreq driver ARM: EXYNOS: add clock registers for exynos4x12-cpufreq PM / devfreq: update the name of EXYNOS clock registers that were omitted PM / devfreq: update the name of EXYNOS clock register ARM: EXYNOS: change the prefix S5P_ to EXYNOS4_ for clock ARM: EXYNOS: use static declaration on regarding clock ARM: EXYNOS: replace clock.c for other new EXYNOS SoCs ARM: OMAP2+: Fix build error after merge ARM: S3C24XX: remove call to s3c24xx_setup_clocks ...
808 lines
20 KiB
C
808 lines
20 KiB
C
/*
|
|
* arch/arm/mach-tegra/usb_phy.c
|
|
*
|
|
* Copyright (C) 2010 Google, Inc.
|
|
*
|
|
* Author:
|
|
* Erik Gilling <konkers@google.com>
|
|
* Benoit Goby <benoit@android.com>
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/resource.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/err.h>
|
|
#include <linux/export.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/io.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/usb/otg.h>
|
|
#include <linux/usb/ulpi.h>
|
|
#include <asm/mach-types.h>
|
|
#include <mach/gpio-tegra.h>
|
|
#include <mach/usb_phy.h>
|
|
#include <mach/iomap.h>
|
|
|
|
#define ULPI_VIEWPORT 0x170
|
|
|
|
#define USB_PORTSC1 0x184
|
|
#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
|
|
#define USB_PORTSC1_PSPD(x) (((x) & 0x3) << 26)
|
|
#define USB_PORTSC1_PHCD (1 << 23)
|
|
#define USB_PORTSC1_WKOC (1 << 22)
|
|
#define USB_PORTSC1_WKDS (1 << 21)
|
|
#define USB_PORTSC1_WKCN (1 << 20)
|
|
#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16)
|
|
#define USB_PORTSC1_PP (1 << 12)
|
|
#define USB_PORTSC1_SUSP (1 << 7)
|
|
#define USB_PORTSC1_PE (1 << 2)
|
|
#define USB_PORTSC1_CCS (1 << 0)
|
|
|
|
#define USB_SUSP_CTRL 0x400
|
|
#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
|
|
#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
|
|
#define USB_SUSP_CLR (1 << 5)
|
|
#define USB_PHY_CLK_VALID (1 << 7)
|
|
#define UTMIP_RESET (1 << 11)
|
|
#define UHSIC_RESET (1 << 11)
|
|
#define UTMIP_PHY_ENABLE (1 << 12)
|
|
#define ULPI_PHY_ENABLE (1 << 13)
|
|
#define USB_SUSP_SET (1 << 14)
|
|
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
|
|
|
|
#define USB1_LEGACY_CTRL 0x410
|
|
#define USB1_NO_LEGACY_MODE (1 << 0)
|
|
#define USB1_VBUS_SENSE_CTL_MASK (3 << 1)
|
|
#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1)
|
|
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \
|
|
(1 << 1)
|
|
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1)
|
|
#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1)
|
|
|
|
#define ULPI_TIMING_CTRL_0 0x424
|
|
#define ULPI_OUTPUT_PINMUX_BYP (1 << 10)
|
|
#define ULPI_CLKOUT_PINMUX_BYP (1 << 11)
|
|
|
|
#define ULPI_TIMING_CTRL_1 0x428
|
|
#define ULPI_DATA_TRIMMER_LOAD (1 << 0)
|
|
#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1)
|
|
#define ULPI_STPDIRNXT_TRIMMER_LOAD (1 << 16)
|
|
#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17)
|
|
#define ULPI_DIR_TRIMMER_LOAD (1 << 24)
|
|
#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
|
|
|
|
#define UTMIP_PLL_CFG1 0x804
|
|
#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
|
|
#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
|
|
|
|
#define UTMIP_XCVR_CFG0 0x808
|
|
#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0)
|
|
#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8)
|
|
#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10)
|
|
#define UTMIP_FORCE_PD_POWERDOWN (1 << 14)
|
|
#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16)
|
|
#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18)
|
|
#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25)
|
|
|
|
#define UTMIP_BIAS_CFG0 0x80c
|
|
#define UTMIP_OTGPD (1 << 11)
|
|
#define UTMIP_BIASPD (1 << 10)
|
|
|
|
#define UTMIP_HSRX_CFG0 0x810
|
|
#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
|
|
#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15)
|
|
|
|
#define UTMIP_HSRX_CFG1 0x814
|
|
#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
|
|
|
|
#define UTMIP_TX_CFG0 0x820
|
|
#define UTMIP_FS_PREABMLE_J (1 << 19)
|
|
#define UTMIP_HS_DISCON_DISABLE (1 << 8)
|
|
|
|
#define UTMIP_MISC_CFG0 0x824
|
|
#define UTMIP_DPDM_OBSERVE (1 << 26)
|
|
#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
|
|
#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf)
|
|
#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe)
|
|
#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
|
|
#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
|
|
#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22)
|
|
|
|
#define UTMIP_MISC_CFG1 0x828
|
|
#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18)
|
|
#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6)
|
|
|
|
#define UTMIP_DEBOUNCE_CFG0 0x82c
|
|
#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0)
|
|
|
|
#define UTMIP_BAT_CHRG_CFG0 0x830
|
|
#define UTMIP_PD_CHRG (1 << 0)
|
|
|
|
#define UTMIP_SPARE_CFG0 0x834
|
|
#define FUSE_SETUP_SEL (1 << 3)
|
|
|
|
#define UTMIP_XCVR_CFG1 0x838
|
|
#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0)
|
|
#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2)
|
|
#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4)
|
|
#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
|
|
|
|
#define UTMIP_BIAS_CFG1 0x83c
|
|
#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
|
|
|
|
static DEFINE_SPINLOCK(utmip_pad_lock);
|
|
static int utmip_pad_count;
|
|
|
|
struct tegra_xtal_freq {
|
|
int freq;
|
|
u8 enable_delay;
|
|
u8 stable_count;
|
|
u8 active_delay;
|
|
u8 xtal_freq_count;
|
|
u16 debounce;
|
|
};
|
|
|
|
static const struct tegra_xtal_freq tegra_freq_table[] = {
|
|
{
|
|
.freq = 12000000,
|
|
.enable_delay = 0x02,
|
|
.stable_count = 0x2F,
|
|
.active_delay = 0x04,
|
|
.xtal_freq_count = 0x76,
|
|
.debounce = 0x7530,
|
|
},
|
|
{
|
|
.freq = 13000000,
|
|
.enable_delay = 0x02,
|
|
.stable_count = 0x33,
|
|
.active_delay = 0x05,
|
|
.xtal_freq_count = 0x7F,
|
|
.debounce = 0x7EF4,
|
|
},
|
|
{
|
|
.freq = 19200000,
|
|
.enable_delay = 0x03,
|
|
.stable_count = 0x4B,
|
|
.active_delay = 0x06,
|
|
.xtal_freq_count = 0xBB,
|
|
.debounce = 0xBB80,
|
|
},
|
|
{
|
|
.freq = 26000000,
|
|
.enable_delay = 0x04,
|
|
.stable_count = 0x66,
|
|
.active_delay = 0x09,
|
|
.xtal_freq_count = 0xFE,
|
|
.debounce = 0xFDE8,
|
|
},
|
|
};
|
|
|
|
static struct tegra_utmip_config utmip_default[] = {
|
|
[0] = {
|
|
.hssync_start_delay = 9,
|
|
.idle_wait_delay = 17,
|
|
.elastic_limit = 16,
|
|
.term_range_adj = 6,
|
|
.xcvr_setup = 9,
|
|
.xcvr_lsfslew = 1,
|
|
.xcvr_lsrslew = 1,
|
|
},
|
|
[2] = {
|
|
.hssync_start_delay = 9,
|
|
.idle_wait_delay = 17,
|
|
.elastic_limit = 16,
|
|
.term_range_adj = 6,
|
|
.xcvr_setup = 9,
|
|
.xcvr_lsfslew = 2,
|
|
.xcvr_lsrslew = 2,
|
|
},
|
|
};
|
|
|
|
static inline bool phy_is_ulpi(struct tegra_usb_phy *phy)
|
|
{
|
|
return (phy->instance == 1);
|
|
}
|
|
|
|
static int utmip_pad_open(struct tegra_usb_phy *phy)
|
|
{
|
|
phy->pad_clk = clk_get_sys("utmip-pad", NULL);
|
|
if (IS_ERR(phy->pad_clk)) {
|
|
pr_err("%s: can't get utmip pad clock\n", __func__);
|
|
return PTR_ERR(phy->pad_clk);
|
|
}
|
|
|
|
if (phy->instance == 0) {
|
|
phy->pad_regs = phy->regs;
|
|
} else {
|
|
phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE);
|
|
if (!phy->pad_regs) {
|
|
pr_err("%s: can't remap usb registers\n", __func__);
|
|
clk_put(phy->pad_clk);
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void utmip_pad_close(struct tegra_usb_phy *phy)
|
|
{
|
|
if (phy->instance != 0)
|
|
iounmap(phy->pad_regs);
|
|
clk_put(phy->pad_clk);
|
|
}
|
|
|
|
static void utmip_pad_power_on(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val, flags;
|
|
void __iomem *base = phy->pad_regs;
|
|
|
|
clk_enable(phy->pad_clk);
|
|
|
|
spin_lock_irqsave(&utmip_pad_lock, flags);
|
|
|
|
if (utmip_pad_count++ == 0) {
|
|
val = readl(base + UTMIP_BIAS_CFG0);
|
|
val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
|
|
writel(val, base + UTMIP_BIAS_CFG0);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&utmip_pad_lock, flags);
|
|
|
|
clk_disable(phy->pad_clk);
|
|
}
|
|
|
|
static int utmip_pad_power_off(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val, flags;
|
|
void __iomem *base = phy->pad_regs;
|
|
|
|
if (!utmip_pad_count) {
|
|
pr_err("%s: utmip pad already powered off\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
clk_enable(phy->pad_clk);
|
|
|
|
spin_lock_irqsave(&utmip_pad_lock, flags);
|
|
|
|
if (--utmip_pad_count == 0) {
|
|
val = readl(base + UTMIP_BIAS_CFG0);
|
|
val |= UTMIP_OTGPD | UTMIP_BIASPD;
|
|
writel(val, base + UTMIP_BIAS_CFG0);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&utmip_pad_lock, flags);
|
|
|
|
clk_disable(phy->pad_clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
|
|
{
|
|
unsigned long timeout = 2000;
|
|
do {
|
|
if ((readl(reg) & mask) == result)
|
|
return 0;
|
|
udelay(1);
|
|
timeout--;
|
|
} while (timeout);
|
|
return -1;
|
|
}
|
|
|
|
static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
|
|
if (phy->instance == 0) {
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val |= USB_SUSP_SET;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
|
|
udelay(10);
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val &= ~USB_SUSP_SET;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
}
|
|
|
|
if (phy->instance == 2) {
|
|
val = readl(base + USB_PORTSC1);
|
|
val |= USB_PORTSC1_PHCD;
|
|
writel(val, base + USB_PORTSC1);
|
|
}
|
|
|
|
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
|
|
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
|
|
}
|
|
|
|
static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
|
|
if (phy->instance == 0) {
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val |= USB_SUSP_CLR;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
|
|
udelay(10);
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val &= ~USB_SUSP_CLR;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
}
|
|
|
|
if (phy->instance == 2) {
|
|
val = readl(base + USB_PORTSC1);
|
|
val &= ~USB_PORTSC1_PHCD;
|
|
writel(val, base + USB_PORTSC1);
|
|
}
|
|
|
|
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
|
|
USB_PHY_CLK_VALID))
|
|
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
|
|
}
|
|
|
|
static int utmi_phy_power_on(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
struct tegra_utmip_config *config = phy->config;
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val |= UTMIP_RESET;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
|
|
if (phy->instance == 0) {
|
|
val = readl(base + USB1_LEGACY_CTRL);
|
|
val |= USB1_NO_LEGACY_MODE;
|
|
writel(val, base + USB1_LEGACY_CTRL);
|
|
}
|
|
|
|
val = readl(base + UTMIP_TX_CFG0);
|
|
val &= ~UTMIP_FS_PREABMLE_J;
|
|
writel(val, base + UTMIP_TX_CFG0);
|
|
|
|
val = readl(base + UTMIP_HSRX_CFG0);
|
|
val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0));
|
|
val |= UTMIP_IDLE_WAIT(config->idle_wait_delay);
|
|
val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit);
|
|
writel(val, base + UTMIP_HSRX_CFG0);
|
|
|
|
val = readl(base + UTMIP_HSRX_CFG1);
|
|
val &= ~UTMIP_HS_SYNC_START_DLY(~0);
|
|
val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay);
|
|
writel(val, base + UTMIP_HSRX_CFG1);
|
|
|
|
val = readl(base + UTMIP_DEBOUNCE_CFG0);
|
|
val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
|
|
val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce);
|
|
writel(val, base + UTMIP_DEBOUNCE_CFG0);
|
|
|
|
val = readl(base + UTMIP_MISC_CFG0);
|
|
val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
|
|
writel(val, base + UTMIP_MISC_CFG0);
|
|
|
|
val = readl(base + UTMIP_MISC_CFG1);
|
|
val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0));
|
|
val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) |
|
|
UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count);
|
|
writel(val, base + UTMIP_MISC_CFG1);
|
|
|
|
val = readl(base + UTMIP_PLL_CFG1);
|
|
val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
|
|
val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
|
|
UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
|
|
writel(val, base + UTMIP_PLL_CFG1);
|
|
|
|
if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV);
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
}
|
|
|
|
utmip_pad_power_on(phy);
|
|
|
|
val = readl(base + UTMIP_XCVR_CFG0);
|
|
val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
|
|
UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_SETUP(~0) |
|
|
UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) |
|
|
UTMIP_XCVR_HSSLEW_MSB(~0));
|
|
val |= UTMIP_XCVR_SETUP(config->xcvr_setup);
|
|
val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
|
|
val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
|
|
writel(val, base + UTMIP_XCVR_CFG0);
|
|
|
|
val = readl(base + UTMIP_XCVR_CFG1);
|
|
val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
|
|
UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0));
|
|
val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj);
|
|
writel(val, base + UTMIP_XCVR_CFG1);
|
|
|
|
val = readl(base + UTMIP_BAT_CHRG_CFG0);
|
|
val &= ~UTMIP_PD_CHRG;
|
|
writel(val, base + UTMIP_BAT_CHRG_CFG0);
|
|
|
|
val = readl(base + UTMIP_BIAS_CFG1);
|
|
val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
|
|
val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
|
|
writel(val, base + UTMIP_BIAS_CFG1);
|
|
|
|
if (phy->instance == 0) {
|
|
val = readl(base + UTMIP_SPARE_CFG0);
|
|
if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE)
|
|
val &= ~FUSE_SETUP_SEL;
|
|
else
|
|
val |= FUSE_SETUP_SEL;
|
|
writel(val, base + UTMIP_SPARE_CFG0);
|
|
}
|
|
|
|
if (phy->instance == 2) {
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val |= UTMIP_PHY_ENABLE;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
}
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val &= ~UTMIP_RESET;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
|
|
if (phy->instance == 0) {
|
|
val = readl(base + USB1_LEGACY_CTRL);
|
|
val &= ~USB1_VBUS_SENSE_CTL_MASK;
|
|
val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD;
|
|
writel(val, base + USB1_LEGACY_CTRL);
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val &= ~USB_SUSP_SET;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
}
|
|
|
|
utmi_phy_clk_enable(phy);
|
|
|
|
if (phy->instance == 2) {
|
|
val = readl(base + USB_PORTSC1);
|
|
val &= ~USB_PORTSC1_PTS(~0);
|
|
writel(val, base + USB_PORTSC1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void utmi_phy_power_off(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
|
|
utmi_phy_clk_disable(phy);
|
|
|
|
if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
|
|
val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5);
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
}
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val |= UTMIP_RESET;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
|
|
val = readl(base + UTMIP_BAT_CHRG_CFG0);
|
|
val |= UTMIP_PD_CHRG;
|
|
writel(val, base + UTMIP_BAT_CHRG_CFG0);
|
|
|
|
val = readl(base + UTMIP_XCVR_CFG0);
|
|
val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
|
|
UTMIP_FORCE_PDZI_POWERDOWN;
|
|
writel(val, base + UTMIP_XCVR_CFG0);
|
|
|
|
val = readl(base + UTMIP_XCVR_CFG1);
|
|
val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
|
|
UTMIP_FORCE_PDDR_POWERDOWN;
|
|
writel(val, base + UTMIP_XCVR_CFG1);
|
|
|
|
utmip_pad_power_off(phy);
|
|
}
|
|
|
|
static void utmi_phy_preresume(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
|
|
val = readl(base + UTMIP_TX_CFG0);
|
|
val |= UTMIP_HS_DISCON_DISABLE;
|
|
writel(val, base + UTMIP_TX_CFG0);
|
|
}
|
|
|
|
static void utmi_phy_postresume(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
|
|
val = readl(base + UTMIP_TX_CFG0);
|
|
val &= ~UTMIP_HS_DISCON_DISABLE;
|
|
writel(val, base + UTMIP_TX_CFG0);
|
|
}
|
|
|
|
static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
|
|
enum tegra_usb_phy_port_speed port_speed)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
|
|
val = readl(base + UTMIP_MISC_CFG0);
|
|
val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
|
|
if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
|
|
val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
|
|
else
|
|
val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
|
|
writel(val, base + UTMIP_MISC_CFG0);
|
|
udelay(1);
|
|
|
|
val = readl(base + UTMIP_MISC_CFG0);
|
|
val |= UTMIP_DPDM_OBSERVE;
|
|
writel(val, base + UTMIP_MISC_CFG0);
|
|
udelay(10);
|
|
}
|
|
|
|
static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
|
|
val = readl(base + UTMIP_MISC_CFG0);
|
|
val &= ~UTMIP_DPDM_OBSERVE;
|
|
writel(val, base + UTMIP_MISC_CFG0);
|
|
udelay(10);
|
|
}
|
|
|
|
static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
|
|
{
|
|
int ret;
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
struct tegra_ulpi_config *config = phy->config;
|
|
|
|
gpio_direction_output(config->reset_gpio, 0);
|
|
msleep(5);
|
|
gpio_direction_output(config->reset_gpio, 1);
|
|
|
|
clk_enable(phy->clk);
|
|
msleep(1);
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val |= UHSIC_RESET;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
|
|
val = readl(base + ULPI_TIMING_CTRL_0);
|
|
val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
|
|
writel(val, base + ULPI_TIMING_CTRL_0);
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val |= ULPI_PHY_ENABLE;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
|
|
val = 0;
|
|
writel(val, base + ULPI_TIMING_CTRL_1);
|
|
|
|
val |= ULPI_DATA_TRIMMER_SEL(4);
|
|
val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
|
|
val |= ULPI_DIR_TRIMMER_SEL(4);
|
|
writel(val, base + ULPI_TIMING_CTRL_1);
|
|
udelay(10);
|
|
|
|
val |= ULPI_DATA_TRIMMER_LOAD;
|
|
val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
|
|
val |= ULPI_DIR_TRIMMER_LOAD;
|
|
writel(val, base + ULPI_TIMING_CTRL_1);
|
|
|
|
/* Fix VbusInvalid due to floating VBUS */
|
|
ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
|
|
if (ret) {
|
|
pr_err("%s: ulpi write failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
|
|
if (ret) {
|
|
pr_err("%s: ulpi write failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
val = readl(base + USB_PORTSC1);
|
|
val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
|
|
writel(val, base + USB_PORTSC1);
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val |= USB_SUSP_CLR;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
udelay(100);
|
|
|
|
val = readl(base + USB_SUSP_CTRL);
|
|
val &= ~USB_SUSP_CLR;
|
|
writel(val, base + USB_SUSP_CTRL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ulpi_phy_power_off(struct tegra_usb_phy *phy)
|
|
{
|
|
unsigned long val;
|
|
void __iomem *base = phy->regs;
|
|
struct tegra_ulpi_config *config = phy->config;
|
|
|
|
/* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB
|
|
* Controller to immediately bring the ULPI PHY out of low power
|
|
*/
|
|
val = readl(base + USB_PORTSC1);
|
|
val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN);
|
|
writel(val, base + USB_PORTSC1);
|
|
|
|
gpio_direction_output(config->reset_gpio, 0);
|
|
clk_disable(phy->clk);
|
|
}
|
|
|
|
struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
|
|
void *config, enum tegra_usb_phy_mode phy_mode)
|
|
{
|
|
struct tegra_usb_phy *phy;
|
|
struct tegra_ulpi_config *ulpi_config;
|
|
unsigned long parent_rate;
|
|
int i;
|
|
int err;
|
|
|
|
phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
|
|
if (!phy)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
phy->instance = instance;
|
|
phy->regs = regs;
|
|
phy->config = config;
|
|
phy->mode = phy_mode;
|
|
|
|
if (!phy->config) {
|
|
if (phy_is_ulpi(phy)) {
|
|
pr_err("%s: ulpi phy configuration missing", __func__);
|
|
err = -EINVAL;
|
|
goto err0;
|
|
} else {
|
|
phy->config = &utmip_default[instance];
|
|
}
|
|
}
|
|
|
|
phy->pll_u = clk_get_sys(NULL, "pll_u");
|
|
if (IS_ERR(phy->pll_u)) {
|
|
pr_err("Can't get pll_u clock\n");
|
|
err = PTR_ERR(phy->pll_u);
|
|
goto err0;
|
|
}
|
|
clk_enable(phy->pll_u);
|
|
|
|
parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
|
|
for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) {
|
|
if (tegra_freq_table[i].freq == parent_rate) {
|
|
phy->freq = &tegra_freq_table[i];
|
|
break;
|
|
}
|
|
}
|
|
if (!phy->freq) {
|
|
pr_err("invalid pll_u parent rate %ld\n", parent_rate);
|
|
err = -EINVAL;
|
|
goto err1;
|
|
}
|
|
|
|
if (phy_is_ulpi(phy)) {
|
|
ulpi_config = config;
|
|
phy->clk = clk_get_sys(NULL, ulpi_config->clk);
|
|
if (IS_ERR(phy->clk)) {
|
|
pr_err("%s: can't get ulpi clock\n", __func__);
|
|
err = -ENXIO;
|
|
goto err1;
|
|
}
|
|
tegra_gpio_enable(ulpi_config->reset_gpio);
|
|
gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
|
|
gpio_direction_output(ulpi_config->reset_gpio, 0);
|
|
phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
|
|
phy->ulpi->io_priv = regs + ULPI_VIEWPORT;
|
|
} else {
|
|
err = utmip_pad_open(phy);
|
|
if (err < 0)
|
|
goto err1;
|
|
}
|
|
|
|
return phy;
|
|
|
|
err1:
|
|
clk_disable(phy->pll_u);
|
|
clk_put(phy->pll_u);
|
|
err0:
|
|
kfree(phy);
|
|
return ERR_PTR(err);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_open);
|
|
|
|
int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
|
|
{
|
|
if (phy_is_ulpi(phy))
|
|
return ulpi_phy_power_on(phy);
|
|
else
|
|
return utmi_phy_power_on(phy);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_power_on);
|
|
|
|
void tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
|
|
{
|
|
if (phy_is_ulpi(phy))
|
|
ulpi_phy_power_off(phy);
|
|
else
|
|
utmi_phy_power_off(phy);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_power_off);
|
|
|
|
void tegra_usb_phy_preresume(struct tegra_usb_phy *phy)
|
|
{
|
|
if (!phy_is_ulpi(phy))
|
|
utmi_phy_preresume(phy);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume);
|
|
|
|
void tegra_usb_phy_postresume(struct tegra_usb_phy *phy)
|
|
{
|
|
if (!phy_is_ulpi(phy))
|
|
utmi_phy_postresume(phy);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume);
|
|
|
|
void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
|
|
enum tegra_usb_phy_port_speed port_speed)
|
|
{
|
|
if (!phy_is_ulpi(phy))
|
|
utmi_phy_restore_start(phy, port_speed);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start);
|
|
|
|
void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy)
|
|
{
|
|
if (!phy_is_ulpi(phy))
|
|
utmi_phy_restore_end(phy);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end);
|
|
|
|
void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy)
|
|
{
|
|
if (!phy_is_ulpi(phy))
|
|
utmi_phy_clk_disable(phy);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_disable);
|
|
|
|
void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
|
|
{
|
|
if (!phy_is_ulpi(phy))
|
|
utmi_phy_clk_enable(phy);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_enable);
|
|
|
|
void tegra_usb_phy_close(struct tegra_usb_phy *phy)
|
|
{
|
|
if (phy_is_ulpi(phy))
|
|
clk_put(phy->clk);
|
|
else
|
|
utmip_pad_close(phy);
|
|
clk_disable(phy->pll_u);
|
|
clk_put(phy->pll_u);
|
|
kfree(phy);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_close);
|