blob: 3c458a7fe74b7c98b8d5efd8aea56bd44fd34362 [file] [log] [blame]
/*
* drivers/usb/phy/tegra11x_usb_phy.c
*
* Copyright (c) 2012-2013 NVIDIA Corporation. All rights reserved.
*
*
* 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/interrupt.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/usb/ulpi.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_data/tegra_usb.h>
#include <linux/clk/tegra.h>
#include <mach/pinmux.h>
#include <mach/tegra_usb_pmc.h>
#include <mach/tegra_usb_pad_ctrl.h>
#include <mach/hardware.h>
#ifdef CONFIG_ARCH_TEGRA_14x_SOC
#include <mach/pinmux-t14.h>
#else
#include <mach/pinmux-t11.h>
#endif
#include "tegra_usb_phy.h"
#include "../../../arch/arm/mach-tegra/gpio-names.h"
#include "../../../arch/arm/mach-tegra/fuse.h"
/* HACK! This needs to come from DT */
#include "../../../arch/arm/mach-tegra/iomap.h"
#define USB_USBCMD 0x130
#define USB_USBCMD_RS (1 << 0)
#define USB_CMD_RESET (1<<1)
#define USB_USBSTS 0x134
#define USB_USBSTS_PCI (1 << 2)
#define USB_USBSTS_SRI (1 << 7)
#define USB_USBSTS_HCH (1 << 12)
#define USB_TXFILLTUNING 0x154
#define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16)
#define USB_FIFO_TXFILL_MASK 0x1f0000
#define ULPI_VIEWPORT 0x170
#define ULPI_WAKEUP (1 << 31)
#define ULPI_RUN (1 << 30)
#define ULPI_RD_WR (1 << 29)
#define USB_ASYNCLISTADDR 0x148
#define ICUSB_CTRL 0x15c
#define USB_USBMODE 0x1f8
#define USB_USBMODE_MASK (3 << 0)
#define USB_USBMODE_HOST (3 << 0)
#define USB_USBMODE_DEVICE (2 << 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_CLKEN (1 << 6)
#define USB_PHY_CLK_VALID (1 << 7)
#define USB_PHY_CLK_VALID_INT_ENB (1 << 9)
#define USB_PHY_CLK_VALID_INT_STS (1 << 8)
#define UTMIP_RESET (1 << 11)
#define UTMIP_PHY_ENABLE (1 << 12)
#define ULPI_PHY_ENABLE (1 << 13)
#define UHSIC_RESET (1 << 14)
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
#define UHSIC_PHY_ENABLE (1 << 19)
#define ULPIS2S_SLV0_RESET (1 << 20)
#define ULPIS2S_SLV1_RESET (1 << 21)
#define ULPIS2S_LINE_RESET (1 << 22)
#define ULPI_PADS_RESET (1 << 23)
#define ULPI_PADS_CLKEN_RESET (1 << 24)
#define USB_PHY_VBUS_WAKEUP_ID 0x408
#define VDCD_DET_STS (1 << 26)
#define VDCD_DET_CHG_DET (1 << 25)
#define VDAT_DET_INT_EN (1 << 16)
#define VDAT_DET_CHG_DET (1 << 17)
#define VDAT_DET_STS (1 << 18)
#define USB_ID_STATUS (1 << 2)
#define ULPIS2S_CTRL 0x418
#define ULPIS2S_ENA (1 << 0)
#define ULPIS2S_SUPPORT_DISCONNECT (1 << 2)
#define ULPIS2S_PLLU_MASTER_BLASTER60 (1 << 3)
#define ULPIS2S_SPARE(x) (((x) & 0xF) << 8)
#define ULPIS2S_FORCE_ULPI_CLK_OUT (1 << 12)
#define ULPIS2S_DISCON_DONT_CHECK_SE0 (1 << 13)
#define ULPIS2S_SUPPORT_HS_KEEP_ALIVE (1 << 14)
#define ULPIS2S_DISABLE_STP_PU (1 << 15)
#define ULPIS2S_SLV0_CLAMP_XMIT (1 << 16)
#define ULPI_TIMING_CTRL_0 0x424
#define ULPI_CLOCK_OUT_DELAY(x) ((x) & 0x1F)
#define ULPI_OUTPUT_PINMUX_BYP (1 << 10)
#define ULPI_CLKOUT_PINMUX_BYP (1 << 11)
#define ULPI_SHADOW_CLK_LOOPBACK_EN (1 << 12)
#define ULPI_SHADOW_CLK_SEL (1 << 13)
#define ULPI_CORE_CLK_SEL (1 << 14)
#define ULPI_SHADOW_CLK_DELAY(x) (((x) & 0x1F) << 16)
#define ULPI_LBK_PAD_EN (1 << 26)
#define ULPI_LBK_PAD_E_INPUT_OR (1 << 27)
#define ULPI_CLK_OUT_ENA (1 << 28)
#define ULPI_CLK_PADOUT_ENA (1 << 29)
#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 USB_IF_SPARE 0x498
#define USB_HS_RSM_EOP_EN (1 << 4)
#define USB_PORT_SUSPEND_EN (1 << 5)
#define USB_NEW_CONTROL 0x4c0
#define USB_COHRENCY_EN (1 << 0)
#define USB_MEM_ALLIGNMENT_MUX_EN (1 << 1)
#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_LSBIAS_SEL (1 << 21)
#define UTMIP_XCVR_SETUP_MSB(x) (((x) & 0x7) << 22)
#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25)
#define UTMIP_XCVR_HSSLEW_LSB(x) (((x) & 0x3) << 4)
#define UTMIP_XCVR_MAX_OFFSET 2
#define UTMIP_XCVR_SETUP_MAX_VALUE 0x7f
#define UTMIP_XCVR_SETUP_MIN_VALUE 0
#define XCVR_SETUP_MSB_CALIB(x) ((x) >> 4)
#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_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_OP_SINK_EN (1 << 1)
#define UTMIP_ON_SINK_EN (1 << 2)
#define UTMIP_OP_SRC_EN (1 << 3)
#define UTMIP_ON_SRC_EN (1 << 4)
#define UTMIP_OP_I_SRC_EN (1 << 5)
#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)
#define UTMIP_BIAS_PDTRK_POWERDOWN (1 << 0)
#define UTMIP_BIAS_PDTRK_POWERUP (1 << 1)
#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 DISABLE_PULLUP_DP (1 << 15)
#define DISABLE_PULLUP_DM (1 << 14)
#define DISABLE_PULLDN_DP (1 << 13)
#define DISABLE_PULLDN_DM (1 << 12)
#define FORCE_PULLUP_DP (1 << 11)
#define FORCE_PULLUP_DM (1 << 10)
#define FORCE_PULLDN_DP (1 << 9)
#define FORCE_PULLDN_DM (1 << 8)
#define COMB_TERMS (1 << 0)
#define ALWAYS_FREE_RUNNING_TERMS (1 << 1)
#define MASK_ALL_PULLUP_PULLDOWN (0xff << 8)
#define UTMIP_SPARE_CFG0 0x834
#define FUSE_SETUP_SEL (1 << 3)
#define FUSE_ATERM_SEL (1 << 4)
#define UTMIP_BIAS_STS0 0x840
#define UTMIP_RCTRL_VAL(x) (((x) & 0xffff) << 0)
#define UTMIP_TCTRL_VAL(x) (((x) & (0xffff << 16)) >> 16)
#define UHSIC_PLL_CFG1 0xc04
#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14)
#define UHSIC_HSRX_CFG0 0xc08
#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2)
#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8)
#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13)
#define UHSIC_HSRX_CFG1 0xc0c
#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
#define UHSIC_TX_CFG0 0xc10
#define UHSIC_HS_READY_WAIT_FOR_VALID (1 << 9)
#define UHSIC_MISC_CFG0 0xc14
#define UHSIC_SUSPEND_EXIT_ON_EDGE (1 << 7)
#define UHSIC_DETECT_SHORT_CONNECT (1 << 8)
#define UHSIC_FORCE_XCVR_MODE (1 << 15)
#define UHSIC_DISABLE_BUSRESET (1 << 20)
#define UHSIC_MISC_CFG1 0xc18
#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2)
#define UHSIC_PADS_CFG0 0xc1c
#define UHSIC_TX_RTUNEN 0xf000
#define UHSIC_TX_RTUNEP 0xf00
#define UHSIC_TX_RTUNE_P(x) (((x) & 0xf) << 8)
#define UHSIC_TX_SLEWP (0xf << 16)
#define UHSIC_TX_SLEWN (0xf << 20)
#define UHSIC_PADS_CFG1 0xc20
#define UHSIC_AUTO_RTERM_EN (1 << 0)
#define UHSIC_PD_BG (1 << 2)
#define UHSIC_PD_TX (1 << 3)
#define UHSIC_PD_TRK (1 << 4)
#define UHSIC_PD_RX (1 << 5)
#define UHSIC_PD_ZI (1 << 6)
#define UHSIC_RX_SEL (1 << 7)
#define UHSIC_RPD_DATA (1 << 9)
#define UHSIC_RPD_STROBE (1 << 10)
#define UHSIC_RPU_DATA (1 << 11)
#define UHSIC_RPU_STROBE (1 << 12)
#define UHSIC_SPARE_CFG0 0xc2c
#define FORCE_BK_ON (1 << 12)
#define UHSIC_STAT_CFG0 0xc28
#define UHSIC_CONNECT_DETECT (1 << 0)
#define UHSIC_STATUS(inst) UHSIC_INST(inst, 0x214, 0x290)
#define UHSIC_WAKE_ALARM(inst) (1 << UHSIC_INST(inst, 19, 4))
#define UHSIC_WALK_PTR_VAL(inst) (0x3 << UHSIC_INST(inst, 6, 0))
#define UHSIC_DATA_VAL(inst) (1 << UHSIC_INST(inst, 15, 3))
#define UHSIC_STROBE_VAL(inst) (1 << UHSIC_INST(inst, 14, 2))
#define UHSIC_CMD_CFG0 0xc24
#define UHSIC_PRETEND_CONNECT_DETECT (1 << 5)
#define USB_USBINTR 0x138
#define UTMIP_STATUS 0x214
#define UTMIP_WALK_PTR_VAL(inst) (0x3 << ((inst)*2))
#define UTMIP_USBOP_VAL(inst) (1 << ((2*(inst)) + 8))
#define UTMIP_USBOP_VAL_P2 (1 << 12)
#define UTMIP_USBOP_VAL_P1 (1 << 10)
#define UTMIP_USBOP_VAL_P0 (1 << 8)
#define UTMIP_USBON_VAL(inst) (1 << ((2*(inst)) + 9))
#define UTMIP_USBON_VAL_P2 (1 << 13)
#define UTMIP_USBON_VAL_P1 (1 << 11)
#define UTMIP_USBON_VAL_P0 (1 << 9)
#define UTMIP_WAKE_ALARM(inst) (1 << ((inst) + 16))
#define UTMIP_WAKE_ALARM_P2 (1 << 18)
#define UTMIP_WAKE_ALARM_P1 (1 << 17)
#define UTMIP_WAKE_ALARM_P0 (1 << 16)
#define UTMIP_WALK_PTR(inst) (1 << ((inst)*2))
#define UTMIP_WALK_PTR_P2 (1 << 4)
#define UTMIP_WALK_PTR_P1 (1 << 2)
#define UTMIP_WALK_PTR_P0 (1 << 0)
#define USB1_PREFETCH_ID 6
#define USB2_PREFETCH_ID 18
#define USB3_PREFETCH_ID 17
#define FUSE_USB_CALIB_0 0x1F0
#define XCVR_SETUP(x) (((x) & 0x7F) << 0)
#define XCVR_SETUP_LSB_MASK 0xF
#define XCVR_SETUP_MSB_MASK 0x70
#define XCVR_SETUP_LSB_MAX_VAL 0xF
#define APB_MISC_GP_OBSCTRL_0 0x818
#define APB_MISC_GP_OBSDATA_0 0x81c
#define PADCTL_SNPS_OC_MAP 0xC
#define CONTROLLER_OC(inst, x) (((x) & 0x7) << (3 * (inst)))
#define CONTROLLER_OC_P0(x) (((x) & 0x7) << 0)
#define CONTROLLER_OC_P1(x) (((x) & 0x7) << 3)
#define CONTROLLER_OC_P2(x) (((x) & 0x7) << 6)
#define PADCTL_OC_DET 0x18
#define ENABLE0_OC_MAP(x) (((x) & 0x7) << 10)
#define ENABLE1_OC_MAP(x) (((x) & 0x7) << 13)
/* ULPI GPIO */
#define ULPI_STP TEGRA_GPIO_PY3
#define ULPI_DIR TEGRA_GPIO_PY1
#define ULPI_D0 TEGRA_GPIO_PO1
#define ULPI_D1 TEGRA_GPIO_PO2
#define TEGRA_STREAM_DISABLE 0x1f8
#define TEGRA_STREAM_DISABLE_OFFSET (1 << 4)
/* These values (in milli second) are taken from the battery charging spec */
#define TDP_SRC_ON_MS 100
#define TDPSRC_CON_MS 40
/* Force port resume wait time in micro second on remote resume */
#define FPR_WAIT_TIME_US 25000
/* define HSIC phy params */
#define HSIC_SYNC_START_DELAY 9
#define HSIC_IDLE_WAIT_DELAY 17
#define HSIC_ELASTIC_UNDERRUN_LIMIT 16
#define HSIC_ELASTIC_OVERRUN_LIMIT 16
struct tegra_usb_pmc_data pmc_data[3];
static struct tegra_xtal_freq utmip_freq_table[] = {
{
.freq = 12000000,
.enable_delay = 0x02,
.stable_count = 0x2F,
.active_delay = 0x04,
.xtal_freq_count = 0x76,
.debounce = 0x7530,
.pdtrk_count = 5,
},
{
.freq = 13000000,
.enable_delay = 0x02,
.stable_count = 0x33,
.active_delay = 0x05,
.xtal_freq_count = 0x7F,
.debounce = 0x7EF4,
.pdtrk_count = 5,
},
{
.freq = 19200000,
.enable_delay = 0x03,
.stable_count = 0x4B,
.active_delay = 0x06,
.xtal_freq_count = 0xBB,
.debounce = 0xBB80,
.pdtrk_count = 7,
},
{
.freq = 26000000,
.enable_delay = 0x04,
.stable_count = 0x66,
.active_delay = 0x09,
.xtal_freq_count = 0xFE,
.debounce = 0xFDE8,
.pdtrk_count = 9,
},
};
static struct tegra_xtal_freq uhsic_freq_table[] = {
{
.freq = 12000000,
.enable_delay = 0x02,
.stable_count = 0x2F,
.active_delay = 0x0,
.xtal_freq_count = 0x1CA,
},
{
.freq = 13000000,
.enable_delay = 0x02,
.stable_count = 0x33,
.active_delay = 0x0,
.xtal_freq_count = 0x1F0,
},
{
.freq = 19200000,
.enable_delay = 0x03,
.stable_count = 0x4B,
.active_delay = 0x0,
.xtal_freq_count = 0x2DD,
},
{
.freq = 26000000,
.enable_delay = 0x04,
.stable_count = 0x66,
.active_delay = 0x0,
.xtal_freq_count = 0x3E0,
},
};
static void pmc_init(struct tegra_usb_phy *phy)
{
pmc_data[phy->inst].instance = phy->inst;
pmc_data[phy->inst].phy_type = phy->pdata->phy_intf;
pmc_data[phy->inst].controller_type = TEGRA_USB_2_0;
pmc_data[phy->inst].usb_base = phy->regs;
tegra_usb_pmc_init(&pmc_data[phy->inst]);
}
static int _usb_phy_init(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
val = readl(base + HOSTPC1_DEVLC);
val &= ~HOSTPC1_DEVLC_STS;
writel(val, base + HOSTPC1_DEVLC);
val = readl(base + USB_IF_SPARE);
val |= USB_HS_RSM_EOP_EN;
val |= USB_PORT_SUSPEND_EN;
writel(val, base + USB_IF_SPARE);
if (phy->pdata->unaligned_dma_buf_supported == true) {
val = readl(base + USB_NEW_CONTROL);
val |= USB_COHRENCY_EN;
val |= USB_MEM_ALLIGNMENT_MUX_EN;
writel(val, base + USB_NEW_CONTROL);
}
val = readl(base + TEGRA_STREAM_DISABLE);
if (!tegra_platform_is_silicon())
val |= TEGRA_STREAM_DISABLE_OFFSET;
else
val &= ~TEGRA_STREAM_DISABLE_OFFSET;
writel(val , base + TEGRA_STREAM_DISABLE);
return 0;
}
static void usb_phy_fence_read(struct tegra_usb_phy *phy)
{
/* Fence read for coherency of AHB master intiated writes */
if (phy->inst == 0)
readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID));
else if (phy->inst == 1)
readb(IO_ADDRESS(IO_PPCS_PHYS + USB2_PREFETCH_ID));
else if (phy->inst == 2)
readb(IO_ADDRESS(IO_PPCS_PHYS + USB3_PREFETCH_ID));
return;
}
static int usb_phy_reset(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (!tegra_platform_is_silicon()) {
val = readl(base + TEGRA_STREAM_DISABLE);
val |= TEGRA_STREAM_DISABLE_OFFSET;
writel(val , base + TEGRA_STREAM_DISABLE);
}
val = readl(base + USB_TXFILLTUNING);
if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) {
val = USB_FIFO_TXFILL_THRES(0x10);
writel(val, base + USB_TXFILLTUNING);
}
return 0;
}
static bool utmi_phy_remotewake_detected(struct tegra_usb_phy *phy)
{
void __iomem *base = phy->regs;
unsigned int inst = phy->inst;
u32 val;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
val = readl(base + UTMIP_PMC_WAKEUP0);
if (val & EVENT_INT_ENB) {
val = tegra_usb_pmc_reg_read(UTMIP_STATUS);
if (UTMIP_WAKE_ALARM(inst) & val) {
tegra_usb_pmc_reg_update(PMC_SLEEP_CFG,
UTMIP_WAKE_VAL(inst, 0xF),
UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE));
tegra_usb_pmc_reg_update(PMC_TRIGGERS, 0,
UTMIP_CLR_WAKE_ALARM(inst));
val = readl(base + UTMIP_PMC_WAKEUP0);
val &= ~EVENT_INT_ENB;
writel(val, base + UTMIP_PMC_WAKEUP0);
val = tegra_usb_pmc_reg_read(UTMIP_STATUS);
if (phy->port_speed < USB_PHY_PORT_SPEED_UNKNOWN) {
pr_info("%s: utmip remote wake detected\n",
__func__);
phy->pmc_remote_wakeup = true;
} else {
phy->pmc_hotplug_wakeup = true;
}
return true;
}
}
return false;
}
static void utmi_phy_enable_trking_data(struct tegra_usb_phy *phy)
{
void __iomem *base = IO_ADDRESS(TEGRA_USB_BASE);
static bool init_done = false;
u32 val;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
/* Should be done only once after system boot */
if (init_done)
return;
clk_enable(phy->utmi_pad_clk);
/* Bias pad MASTER_ENABLE=1 */
tegra_usb_pmc_reg_update(PMC_UTMIP_BIAS_MASTER_CNTRL, 0,
BIAS_MASTER_PROG_VAL);
/* Setting the tracking length time */
val = readl(base + UTMIP_BIAS_CFG1);
val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
val |= UTMIP_BIAS_PDTRK_COUNT(5);
writel(val, base + UTMIP_BIAS_CFG1);
/* Bias PDTRK is Shared and MUST be done from USB1 ONLY, PD_TRK=0 */
val = readl(base + UTMIP_BIAS_CFG1);
val &= ~UTMIP_BIAS_PDTRK_POWERDOWN;
writel(val, base + UTMIP_BIAS_CFG1);
val = readl(base + UTMIP_BIAS_CFG1);
val |= UTMIP_BIAS_PDTRK_POWERUP;
writel(val, base + UTMIP_BIAS_CFG1);
/* Wait for 25usec */
udelay(25);
/* Bias pad MASTER_ENABLE=0 */
tegra_usb_pmc_reg_update(PMC_UTMIP_BIAS_MASTER_CNTRL,
BIAS_MASTER_PROG_VAL, 0);
/* Wait for 1usec */
udelay(1);
/* Bias pad MASTER_ENABLE=1 */
tegra_usb_pmc_reg_update(PMC_UTMIP_BIAS_MASTER_CNTRL, 0,
BIAS_MASTER_PROG_VAL);
/* Read RCTRL and TCTRL from UTMIP space */
val = readl(base + UTMIP_BIAS_STS0);
pmc_data[phy->inst].utmip_rctrl_val = 0xf + ffz(UTMIP_RCTRL_VAL(val));
pmc_data[phy->inst].utmip_tctrl_val = 0xf + ffz(UTMIP_TCTRL_VAL(val));
/* PD_TRK=1 */
val = readl(base + UTMIP_BIAS_CFG1);
val |= UTMIP_BIAS_PDTRK_POWERDOWN;
writel(val, base + UTMIP_BIAS_CFG1);
/* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */
val = PMC_TCTRL_VAL(pmc_data[phy->inst].utmip_tctrl_val) |
PMC_RCTRL_VAL(pmc_data[phy->inst].utmip_rctrl_val);
tegra_usb_pmc_reg_update(PMC_UTMIP_TERM_PAD_CFG, 0xffffffff, val);
clk_disable(phy->utmi_pad_clk);
init_done = true;
}
static int usb_phy_bringup_host_controller(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
PHY_DBG("[%d] USB_USBSTS[0x%x] USB_PORTSC[0x%x] port_speed[%d]\n",
__LINE__, readl(base + USB_USBSTS), readl(base + USB_PORTSC),
phy->port_speed);
/* Device is plugged in when system is in LP0 */
/* Bring up the controller from LP0*/
val = readl(base + USB_USBCMD);
val |= USB_CMD_RESET;
writel(val, base + USB_USBCMD);
if (usb_phy_reg_status_wait(base + USB_USBCMD,
USB_CMD_RESET, 0, 2500) < 0) {
pr_err("%s: timeout waiting for reset\n", __func__);
}
val = readl(base + USB_USBMODE);
val &= ~USB_USBMODE_MASK;
val |= USB_USBMODE_HOST;
writel(val, base + USB_USBMODE);
val = readl(base + HOSTPC1_DEVLC);
val &= ~HOSTPC1_DEVLC_PTS(~0);
if (phy->pdata->phy_intf == TEGRA_USB_PHY_INTF_HSIC)
val |= HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_HSIC);
else
val |= HOSTPC1_DEVLC_STS;
writel(val, base + HOSTPC1_DEVLC);
/* Enable Port Power */
val = readl(base + USB_PORTSC);
val |= USB_PORTSC_PP;
writel(val, base + USB_PORTSC);
udelay(10);
/* Check if the phy resume from LP0. When the phy resume from LP0
* USB register will be reset.to zero */
if (!readl(base + USB_ASYNCLISTADDR)) {
/* Program the field PTC based on the saved speed mode */
val = readl(base + USB_PORTSC);
val &= ~USB_PORTSC_PTC(~0);
if ((phy->port_speed == USB_PHY_PORT_SPEED_HIGH) ||
(phy->pdata->phy_intf == TEGRA_USB_PHY_INTF_HSIC))
val |= USB_PORTSC_PTC(5);
else if (phy->port_speed == USB_PHY_PORT_SPEED_FULL)
val |= USB_PORTSC_PTC(6);
else if (phy->port_speed == USB_PHY_PORT_SPEED_LOW)
val |= USB_PORTSC_PTC(7);
writel(val, base + USB_PORTSC);
udelay(10);
/* Disable test mode by setting PTC field to NORMAL_OP */
val = readl(base + USB_PORTSC);
val &= ~USB_PORTSC_PTC(~0);
writel(val, base + USB_PORTSC);
udelay(10);
}
/* Poll until CCS is enabled */
if (usb_phy_reg_status_wait(base + USB_PORTSC, USB_PORTSC_CCS,
USB_PORTSC_CCS, 2000)) {
pr_err("%s: timeout waiting for USB_PORTSC_CCS\n", __func__);
}
/* Poll until PE is enabled */
if (usb_phy_reg_status_wait(base + USB_PORTSC, USB_PORTSC_PE,
USB_PORTSC_PE, 2000)) {
pr_err("%s: timeout waiting for USB_PORTSC_PE\n", __func__);
}
/* Clear the PCI status, to avoid an interrupt taken upon resume */
val = readl(base + USB_USBSTS);
val |= USB_USBSTS_PCI;
writel(val, base + USB_USBSTS);
phy->ctrlr_suspended = false;
if (!phy->pmc_remote_wakeup) {
/* Put controller in suspend mode by writing 1
* to SUSP bit of PORTSC */
val = readl(base + USB_PORTSC);
if ((val & USB_PORTSC_PP) && (val & USB_PORTSC_PE)) {
val |= USB_PORTSC_SUSP;
writel(val, base + USB_PORTSC);
phy->ctrlr_suspended = true;
/* Wait until port suspend completes */
if (usb_phy_reg_status_wait(base + USB_PORTSC,
USB_PORTSC_SUSP, USB_PORTSC_SUSP, 4000)) {
pr_err("%s: timeout waiting for PORT_SUSPEND\n",
__func__);
}
}
}
PHY_DBG("[%d] USB_USBSTS[0x%x] USB_PORTSC[0x%x] port_speed[%d]\n",
__LINE__, readl(base + USB_USBSTS), readl(base + USB_PORTSC),
phy->port_speed);
DBG("USB_USBSTS[0x%x] USB_PORTSC[0x%x]\n",
readl(base + USB_USBSTS), readl(base + USB_PORTSC));
return 0;
}
static unsigned int utmi_phy_xcvr_setup_value(struct tegra_usb_phy *phy)
{
struct tegra_utmi_config *cfg = &phy->pdata->u_cfg.utmi;
signed long val;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (cfg->xcvr_use_fuses) {
val = XCVR_SETUP(tegra_fuse_readl(FUSE_USB_CALIB_0));
if (cfg->xcvr_use_lsb) {
val = min((unsigned int) ((val & XCVR_SETUP_LSB_MASK)
+ cfg->xcvr_setup_offset),
(unsigned int) XCVR_SETUP_LSB_MAX_VAL);
val |= (cfg->xcvr_setup & XCVR_SETUP_MSB_MASK);
} else {
if (cfg->xcvr_setup_offset <= UTMIP_XCVR_MAX_OFFSET)
val = val + cfg->xcvr_setup_offset;
if (val > UTMIP_XCVR_SETUP_MAX_VALUE) {
val = UTMIP_XCVR_SETUP_MAX_VALUE;
pr_info("%s: reset XCVR_SETUP to max value\n",
__func__);
} else if (val < UTMIP_XCVR_SETUP_MIN_VALUE) {
val = UTMIP_XCVR_SETUP_MIN_VALUE;
pr_info("%s: reset XCVR_SETUP to min value\n",
__func__);
}
}
} else {
val = cfg->xcvr_setup;
}
return (unsigned int) val;
}
static int utmi_phy_open(struct tegra_usb_phy *phy)
{
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
unsigned long parent_rate;
int i;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
phy->utmi_pad_clk = clk_get_sys("utmip-pad", NULL);
if (IS_ERR(phy->utmi_pad_clk)) {
pr_err("%s: can't get utmip pad clock\n", __func__);
return PTR_ERR(phy->utmi_pad_clk);
}
pmc_init(phy);
phy->utmi_xcvr_setup = utmi_phy_xcvr_setup_value(phy);
if (tegra_platform_is_silicon()) {
parent_rate = clk_get_rate(clk_get_parent(phy->pllu_clk));
for (i = 0; i < ARRAY_SIZE(utmip_freq_table); i++) {
if (utmip_freq_table[i].freq == parent_rate) {
phy->freq = &utmip_freq_table[i];
break;
}
}
if (!phy->freq) {
pr_err("invalid pll_u parent rate %ld\n", parent_rate);
return -EINVAL;
}
}
/* Power-up the VBUS, ID detector for UTMIP PHY */
if (phy->pdata->id_det_type == TEGRA_USB_ID)
tegra_usb_pmc_reg_update(PMC_USB_AO,
PMC_USB_AO_VBUS_WAKEUP_PD_P0 | PMC_USB_AO_ID_PD_P0, 0);
else
tegra_usb_pmc_reg_update(PMC_USB_AO,
PMC_USB_AO_VBUS_WAKEUP_PD_P0, PMC_USB_AO_ID_PD_P0);
pmc->pmc_ops->powerup_pmc_wake_detect(pmc);
return 0;
}
static void utmi_phy_close(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
DBG("%s inst:[%d]\n", __func__, phy->inst);
/* Disable PHY clock valid interrupts while going into suspend*/
if (phy->hot_plug) {
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_PHY_CLK_VALID_INT_ENB;
writel(val, base + USB_SUSP_CTRL);
}
val = tegra_usb_pmc_reg_read(PMC_SLEEP_CFG);
if (val & UTMIP_MASTER_ENABLE(phy->inst)) {
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
phy->pmc_remote_wakeup = false;
phy->pmc_hotplug_wakeup = false;
PHY_DBG("%s DISABLE_PMC inst = %d\n", __func__, phy->inst);
}
clk_put(phy->utmi_pad_clk);
}
static int utmi_phy_irq(struct tegra_usb_phy *phy)
{
void __iomem *base = phy->regs;
unsigned long val = 0;
bool remote_wakeup = false;
int irq_status = IRQ_HANDLED;
if (phy->phy_clk_on) {
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
DBG("USB_USBSTS[0x%x] USB_PORTSC[0x%x]\n",
readl(base + USB_USBSTS), readl(base + USB_PORTSC));
DBG("USB_USBMODE[0x%x] USB_USBCMD[0x%x]\n",
readl(base + USB_USBMODE), readl(base + USB_USBCMD));
}
if (!phy->pdata->unaligned_dma_buf_supported)
usb_phy_fence_read(phy);
/* check if it is pmc wake event */
if (utmi_phy_remotewake_detected(phy))
remote_wakeup = phy->pmc_remote_wakeup;
if (phy->hot_plug) {
val = readl(base + USB_SUSP_CTRL);
if ((val & USB_PHY_CLK_VALID_INT_STS) &&
(val & USB_PHY_CLK_VALID_INT_ENB)) {
val &= ~USB_PHY_CLK_VALID_INT_ENB |
USB_PHY_CLK_VALID_INT_STS;
writel(val , (base + USB_SUSP_CTRL));
/* In case of remote wakeup PHY clock will not up
* immediately, so should not access any controller
* register but normal plug-in/plug-out should be
* executed
*/
if (!remote_wakeup) {
val = readl(base + USB_USBSTS);
if (!(val & USB_USBSTS_PCI)) {
irq_status = IRQ_NONE;
goto exit;
}
val = readl(base + USB_PORTSC);
if (val & USB_PORTSC_CCS)
val &= ~USB_PORTSC_WKCN;
else
val &= ~USB_PORTSC_WKDS;
val &= ~USB_PORTSC_RWC_BITS;
writel(val , (base + USB_PORTSC));
}
} else if (!phy->phy_clk_on) {
if (remote_wakeup)
irq_status = IRQ_HANDLED;
else
irq_status = IRQ_NONE;
goto exit;
}
}
exit:
return irq_status;
}
static int utmi_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
{
unsigned long val;
unsigned int inst = phy->inst;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
val = tegra_usb_pmc_reg_read(PMC_SLEEP_CFG);
if (val & UTMIP_MASTER_ENABLE(inst)) {
if (!remote_wakeup) {
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
phy->pmc_remote_wakeup = false;
phy->pmc_hotplug_wakeup = false;
PHY_DBG("%s DISABLE_PMC inst = %d\n", __func__, inst);
}
}
return 0;
}
static int utmi_phy_power_off(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
PHY_DBG("%s(%d) inst:[%d] BEGIN\n", __func__, __LINE__, phy->inst);
if (!phy->phy_clk_on) {
PHY_DBG("%s(%d) inst:[%d] phy clk is already off\n",
__func__, __LINE__, phy->inst);
return 0;
}
if (phy->pdata->op_mode == TEGRA_USB_OPMODE_DEVICE) {
pmc_data[phy->inst].is_xhci = phy->pdata->u_data.dev.is_xhci;
pmc->pmc_ops->powerdown_pmc_wake_detect(pmc);
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 + UTMIP_BAT_CHRG_CFG0);
val |= UTMIP_PD_CHRG;
writel(val, base + UTMIP_BAT_CHRG_CFG0);
} else {
phy->port_speed = (readl(base + HOSTPC1_DEVLC) >> 25) &
HOSTPC1_DEVLC_PSPD_MASK;
/* Disable interrupts */
writel(0, base + USB_USBINTR);
/* Clear the run bit to stop SOFs when USB is suspended */
val = readl(base + USB_USBCMD);
val &= ~USB_USBCMD_RS;
writel(val, base + USB_USBCMD);
if (usb_phy_reg_status_wait(base + USB_USBSTS, USB_USBSTS_HCH,
USB_USBSTS_HCH, 2000)) {
pr_err("%s: timeout waiting for USB_USBSTS_HCH\n"
, __func__);
}
pmc_data[phy->inst].port_speed = (readl(base +
HOSTPC1_DEVLC) >> 25) &
HOSTPC1_DEVLC_PSPD_MASK;
if (pmc_data[phy->inst].port_speed <
USB_PMC_PORT_SPEED_UNKNOWN)
phy->pmc_remote_wakeup = false;
else
phy->pmc_hotplug_wakeup = false;
pmc->pmc_ops->setup_pmc_wake_detect(pmc);
val = readl(base + UTMIP_PMC_WAKEUP0);
val |= EVENT_INT_ENB;
writel(val, base + UTMIP_PMC_WAKEUP0);
PHY_DBG("%s ENABLE_PMC inst = %d\n", __func__, phy->inst);
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_WAKE_ON_CNNT_EN_DEV;
writel(val, base + USB_SUSP_CTRL);
}
if (!phy->hot_plug) {
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);
val = readl(base + UTMIP_BIAS_CFG1);
val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
writel(val, base + UTMIP_BIAS_CFG1);
if (phy->hot_plug) {
bool enable_hotplug = true;
/* if it is OTG port then make sure to enable hot-plug feature
only if host adaptor is connected, i.e id is low */
if (phy->pdata->port_otg) {
val = readl(base + USB_PHY_VBUS_WAKEUP_ID);
enable_hotplug = (val & USB_ID_STATUS) ? false : true;
}
if (enable_hotplug) {
/* Enable wakeup event of device plug-in/plug-out */
val = readl(base + USB_PORTSC);
if (val & USB_PORTSC_CCS)
val |= USB_PORTSC_WKDS;
else
val |= USB_PORTSC_WKCN;
writel(val, base + USB_PORTSC);
if (val & USB_PORTSC_CCS) {
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_PHY_CLK_VALID_INT_ENB;
} else {
val = readl(base + USB_SUSP_CTRL);
val |= USB_PHY_CLK_VALID_INT_ENB;
}
writel(val, base + USB_SUSP_CTRL);
} else {
/* Disable PHY clock valid interrupts while going into suspend*/
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_PHY_CLK_VALID_INT_ENB;
writel(val, base + USB_SUSP_CTRL);
}
}
/* Disable PHY clock */
val = readl(base + HOSTPC1_DEVLC);
val |= HOSTPC1_DEVLC_PHCD;
writel(val, base + HOSTPC1_DEVLC);
if (!phy->hot_plug) {
val = readl(base + USB_SUSP_CTRL);
val |= UTMIP_RESET;
writel(val, base + USB_SUSP_CTRL);
}
utmi_phy_pad_disable();
utmi_phy_iddq_override(true);
phy->phy_clk_on = false;
phy->hw_accessible = false;
PHY_DBG("%s(%d) inst:[%d] END\n", __func__, __LINE__, phy->inst);
return 0;
}
static int utmi_phy_power_on(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
#ifdef CONFIG_ARCH_TEGRA_11x_SOC
void __iomem *padctl_base = IO_ADDRESS(TEGRA_XUSB_PADCTL_BASE);
#endif
struct tegra_utmi_config *config = &phy->pdata->u_cfg.utmi;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
PHY_DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (phy->phy_clk_on) {
PHY_DBG("%s(%d) inst:[%d] phy clk is already On\n",
__func__, __LINE__, phy->inst);
return 0;
}
val = readl(base + USB_SUSP_CTRL);
val |= UTMIP_RESET;
writel(val, base + USB_SUSP_CTRL);
val = readl(base + UTMIP_TX_CFG0);
val |= UTMIP_FS_PREABMLE_J;
writel(val, base + UTMIP_TX_CFG0);
val = readl(base + USB_USBMODE);
val &= ~USB_USBMODE_MASK;
if (phy->pdata->op_mode == TEGRA_USB_OPMODE_HOST)
val |= USB_USBMODE_HOST;
else
val |= USB_USBMODE_DEVICE;
writel(val, base + USB_USBMODE);
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);
if (tegra_platform_is_silicon()) {
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);
if (phy->pdata->op_mode == TEGRA_USB_OPMODE_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);
val = readl(base + UTMIP_BAT_CHRG_CFG0);
val &= ~UTMIP_PD_CHRG;
writel(val, base + UTMIP_BAT_CHRG_CFG0);
} else {
val = readl(base + UTMIP_BAT_CHRG_CFG0);
val |= UTMIP_PD_CHRG;
writel(val, base + UTMIP_BAT_CHRG_CFG0);
}
utmi_phy_pad_enable();
val = readl(base + UTMIP_XCVR_CFG0);
val &= ~(UTMIP_XCVR_LSBIAS_SEL | 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(phy->utmi_xcvr_setup);
val |= UTMIP_XCVR_SETUP_MSB(XCVR_SETUP_MSB_CALIB(phy->utmi_xcvr_setup));
val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
if (!config->xcvr_use_lsb)
val |= UTMIP_XCVR_HSSLEW_MSB(0x3);
if (config->xcvr_hsslew_lsb)
val |= UTMIP_XCVR_HSSLEW_LSB(config->xcvr_hsslew_lsb);
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);
if (tegra_platform_is_silicon()) {
val = readl(base + UTMIP_BIAS_CFG1);
val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
val |= UTMIP_BIAS_PDTRK_COUNT(phy->freq->pdtrk_count);
writel(val, base + UTMIP_BIAS_CFG1);
}
val = readl(base + UTMIP_SPARE_CFG0);
val &= ~FUSE_SETUP_SEL;
val |= FUSE_ATERM_SEL;
writel(val, base + UTMIP_SPARE_CFG0);
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);
/* Bring UTMIPLL out of IDDQ mode while exiting from reset/suspend */
utmi_phy_iddq_override(false);
if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL,
USB_PHY_CLK_VALID, USB_PHY_CLK_VALID, 2500))
pr_warn("%s: timeout waiting for phy to stabilize\n", __func__);
val = readl(base + HOSTPC1_DEVLC);
val &= ~HOSTPC1_DEVLC_PHCD;
writel(val, base + HOSTPC1_DEVLC);
utmi_phy_enable_trking_data(phy);
if (phy->inst == 2)
writel(0, base + ICUSB_CTRL);
val = readl(base + USB_USBMODE);
val &= ~USB_USBMODE_MASK;
if (phy->pdata->op_mode == TEGRA_USB_OPMODE_HOST)
val |= USB_USBMODE_HOST;
else
val |= USB_USBMODE_DEVICE;
writel(val, base + USB_USBMODE);
val = readl(base + HOSTPC1_DEVLC);
val &= ~HOSTPC1_DEVLC_PTS(~0);
val |= HOSTPC1_DEVLC_STS;
writel(val, base + HOSTPC1_DEVLC);
if (phy->pdata->op_mode == TEGRA_USB_OPMODE_DEVICE) {
pmc_data[phy->inst].is_xhci = phy->pdata->u_data.dev.is_xhci;
pmc->pmc_ops->powerup_pmc_wake_detect(pmc);
}
phy->phy_clk_on = true;
phy->hw_accessible = true;
#ifdef CONFIG_ARCH_TEGRA_11x_SOC
val = readl(padctl_base + PADCTL_SNPS_OC_MAP);
val |= CONTROLLER_OC(phy->inst, 0x4);
writel(val, padctl_base + PADCTL_SNPS_OC_MAP);
val = readl(padctl_base + PADCTL_OC_DET);
if (phy->inst == 0)
val |= ENABLE0_OC_MAP(config->vbus_oc_map);
if (phy->inst == 2)
val |= ENABLE1_OC_MAP(config->vbus_oc_map);
writel(val, padctl_base + PADCTL_OC_DET);
#endif
PHY_DBG("%s(%d) End inst:[%d]\n", __func__, __LINE__, phy->inst);
return 0;
}
static void utmi_phy_restore_start(struct tegra_usb_phy *phy)
{
unsigned long val;
int inst = phy->inst;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
val = tegra_usb_pmc_reg_read(UTMIP_STATUS);
/* Check whether we wake up from the remote resume.
For lp1 case, pmc is not responsible for waking the
system, it's the flow controller and hence
UTMIP_WALK_PTR_VAL(inst) will return 0.
Also, for lp1 case phy->pmc_remote_wakeup will already be set
to true by utmi_phy_irq() when the remote wakeup happens.
Hence change the logic in the else part to enter only
if phy->pmc_remote_wakeup is not set to true by the
utmi_phy_irq(). */
if (UTMIP_WALK_PTR_VAL(inst) & val) {
phy->pmc_remote_wakeup = true;
}
}
static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
{
unsigned long val, flags = 0;
void __iomem *base = phy->regs;
int wait_time_us = 25000; /* FPR should be set by this time */
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
/* check whether we wake up from the remote resume */
if (phy->pmc_remote_wakeup) {
/* wait until SUSPEND and RESUME bit
* is cleared on remote resume */
do {
val = readl(base + USB_PORTSC);
udelay(1);
if (wait_time_us == 0) {
PHY_DBG("%s PMC FPR" \
"timeout val = 0x%x instance = %d\n", \
__func__, (u32)val, phy->inst);
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
phy->pmc_remote_wakeup = false;
phy->pmc_hotplug_wakeup = false;
return;
}
wait_time_us--;
} while (val & (USB_PORTSC_RESUME | USB_PORTSC_SUSP));
/* Add delay sothat resume will be driven for more than 20 ms */
mdelay(10);
local_irq_save(flags);
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 1);
phy->pmc_remote_wakeup = false;
phy->pmc_hotplug_wakeup = false;
PHY_DBG("%s DISABLE_PMC inst = %d\n", __func__, phy->inst);
local_irq_restore(flags);
if (usb_phy_reg_status_wait(base + USB_USBCMD, USB_USBCMD_RS,
USB_USBCMD_RS, 2000)) {
pr_err("%s: timeout waiting for USB_USBCMD_RS\n",\
__func__);
}
/* Clear PCI and SRI bits to avoid an interrupt upon resume */
val = readl(base + USB_USBSTS);
writel(val, base + USB_USBSTS);
/* wait to avoid SOF if there is any */
if (usb_phy_reg_status_wait(base + USB_USBSTS,
USB_USBSTS_SRI, USB_USBSTS_SRI, 2500) < 0) {
pr_err("%s: timeout waiting for SOF\n", __func__);
}
} else {
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
phy->pmc_remote_wakeup = false;
phy->pmc_hotplug_wakeup = false;
PHY_DBG("%s DISABLE_PMC inst = %d\n", __func__, phy->inst);
}
}
static int utmi_phy_resume(struct tegra_usb_phy *phy)
{
int status = 0;
unsigned long val;
int port_connected = 0;
int is_lp0;
void __iomem *base = phy->regs;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (phy->pdata->op_mode == TEGRA_USB_OPMODE_HOST) {
if (readl(base + USB_ASYNCLISTADDR) &&
!phy->pdata->u_data.host.power_off_on_suspend)
return 0;
val = readl(base + USB_PORTSC);
port_connected = val & USB_PORTSC_CCS;
is_lp0 = !(readl(base + USB_ASYNCLISTADDR));
if ((phy->port_speed < USB_PHY_PORT_SPEED_UNKNOWN) &&
(port_connected ^ is_lp0)) {
utmi_phy_restore_start(phy);
usb_phy_bringup_host_controller(phy);
utmi_phy_restore_end(phy);
} else {
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
phy->pmc_remote_wakeup = false;
phy->pmc_hotplug_wakeup = false;
/* bring up the controller from suspend*/
val = readl(base + USB_USBCMD);
val |= USB_CMD_RESET;
writel(val, base + USB_USBCMD);
if (usb_phy_reg_status_wait(base + USB_USBCMD,
USB_CMD_RESET, 0, 2500) < 0) {
pr_err("%s: timeout waiting for reset\n", __func__);
}
val = readl(base + USB_USBMODE);
val &= ~USB_USBMODE_MASK;
val |= USB_USBMODE_HOST;
writel(val, base + USB_USBMODE);
val = readl(base + HOSTPC1_DEVLC);
val &= ~HOSTPC1_DEVLC_PTS(~0);
val |= HOSTPC1_DEVLC_STS;
writel(val, base + HOSTPC1_DEVLC);
writel(USB_USBCMD_RS, base + USB_USBCMD);
if (usb_phy_reg_status_wait(base + USB_USBCMD,
USB_USBCMD_RS, USB_USBCMD_RS, 2500) < 0) {
pr_err("%s: timeout waiting for run bit\n", __func__);
}
/* Enable Port Power */
val = readl(base + USB_PORTSC);
val |= USB_PORTSC_PP;
writel(val, base + USB_PORTSC);
udelay(10);
DBG("USB_USBSTS[0x%x] USB_PORTSC[0x%x]\n",
readl(base + USB_USBSTS), readl(base + USB_PORTSC));
}
}
return status;
}
static unsigned long utmi_phy_set_dp_dm_pull_up_down(struct tegra_usb_phy *phy,
unsigned long pull_up_down_flags)
{
unsigned long val;
unsigned long org;
void __iomem *base = phy->regs;
org = readl(base + UTMIP_MISC_CFG0);
val = org & ~MASK_ALL_PULLUP_PULLDOWN;
val |= pull_up_down_flags;
writel(val, base + UTMIP_MISC_CFG0);
usleep_range(500, 2000);
return org;
}
unsigned long utmi_phy_get_dp_dm_status(struct tegra_usb_phy *phy,
unsigned long pull_up_down_flags)
{
void __iomem *base = phy->regs;
unsigned long org_flags;
unsigned long ret;
org_flags = utmi_phy_set_dp_dm_pull_up_down(phy, pull_up_down_flags);
ret = USB_PORTSC_LINE_STATE(readl(base + USB_PORTSC));
utmi_phy_set_dp_dm_pull_up_down(phy, org_flags);
return ret;
}
/*
* Per Battery Charging Specification 1.2 section 3.2.3:
* We check Data Contact Detect (DCD) before we check the USB cable type.
*/
static bool utmi_phy_dcd_detect(struct tegra_usb_phy *phy)
{
void __iomem *base = phy->regs;
unsigned long val;
unsigned long org_flags;
bool ret;
val = readl(base + UTMIP_BAT_CHRG_CFG0);
/* inject IDP_SRC */
val |= UTMIP_OP_I_SRC_EN;
writel(val, base + UTMIP_BAT_CHRG_CFG0);
/* enable pull down resistor RDM_DWN on D- */
org_flags = utmi_phy_set_dp_dm_pull_up_down(phy,
DISABLE_PULLUP_DP | DISABLE_PULLUP_DM |
DISABLE_PULLDN_DP | FORCE_PULLDN_DM);
usleep_range(20000, 30000);
ret = false;
if (0 == USB_PORTSC_LINE_STATE(readl(base + USB_PORTSC))) {
/* minimum debounce time is 10mS per TDCD_DBNC */
usleep_range(10000, 12000);
if (0 == USB_PORTSC_LINE_STATE(readl(base + USB_PORTSC)))
ret = true;
}
val &= ~UTMIP_OP_I_SRC_EN;
writel(val, base + UTMIP_BAT_CHRG_CFG0);
utmi_phy_set_dp_dm_pull_up_down(phy, org_flags);
return ret;
}
static bool utmi_phy_charger_detect(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
bool status;
int dcd_timeout_ms;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (phy->pdata->op_mode != TEGRA_USB_OPMODE_DEVICE) {
/* Charger detection is not there for ULPI
* return Charger not available */
return false;
}
/* DCD timeout max value is 900mS */
dcd_timeout_ms = 0;
while (dcd_timeout_ms < 900) {
/* standard DCD detect for SDP/DCP/CDP */
if (utmi_phy_dcd_detect(phy))
break;
/* for NV-charger, we wait D+/D- both set */
if ((USB_PORTSC_LINE_DP_SET | USB_PORTSC_LINE_DM_SET) ==
utmi_phy_get_dp_dm_status(phy,
DISABLE_PULLUP_DP | DISABLE_PULLUP_DM |
DISABLE_PULLDN_DP | DISABLE_PULLDN_DM))
break;
usleep_range(20000, 22000);
dcd_timeout_ms += 22;
}
/* Enable charger detection logic */
val = readl(base + UTMIP_BAT_CHRG_CFG0);
val |= UTMIP_OP_SRC_EN | UTMIP_ON_SINK_EN;
writel(val, base + UTMIP_BAT_CHRG_CFG0);
/* Source should be on for 100 ms as per USB charging spec */
msleep(TDP_SRC_ON_MS);
val = readl(base + USB_PHY_VBUS_WAKEUP_ID);
/* If charger is not connected disable the interrupt */
val &= ~VDAT_DET_INT_EN;
val |= VDAT_DET_CHG_DET;
writel(val, base + USB_PHY_VBUS_WAKEUP_ID);
val = readl(base + USB_PHY_VBUS_WAKEUP_ID);
if (val & VDAT_DET_STS)
status = true;
else
status = false;
/* Disable charger detection logic */
val = readl(base + UTMIP_BAT_CHRG_CFG0);
val &= ~(UTMIP_OP_SRC_EN | UTMIP_ON_SINK_EN);
writel(val, base + UTMIP_BAT_CHRG_CFG0);
/* Delay of 40 ms before we pull the D+ as per battery charger spec */
msleep(TDPSRC_CON_MS);
return status;
}
static bool utmi_phy_is_non_std_charger(struct tegra_usb_phy *phy)
{
/*
* non std charger has D+/D- line float, we can apply pull up/down on
* each line and verify if line status change.
*/
/* pull up DP only */
if (USB_PORTSC_LINE_DP_SET != utmi_phy_get_dp_dm_status(phy,
FORCE_PULLUP_DP | DISABLE_PULLUP_DM |
DISABLE_PULLDN_DP | DISABLE_PULLDN_DM))
goto NOT_NON_STD_CHARGER;
/* pull down DP only */
if (0x0 != utmi_phy_get_dp_dm_status(phy,
DISABLE_PULLUP_DP | DISABLE_PULLUP_DM |
FORCE_PULLDN_DP | DISABLE_PULLDN_DM))
goto NOT_NON_STD_CHARGER;
/* pull up DM only */
if (USB_PORTSC_LINE_DM_SET != utmi_phy_get_dp_dm_status(phy,
DISABLE_PULLUP_DP | FORCE_PULLUP_DM |
DISABLE_PULLDN_DP | DISABLE_PULLDN_DM))
goto NOT_NON_STD_CHARGER;
/* pull down DM only */
if (0x0 != utmi_phy_get_dp_dm_status(phy,
DISABLE_PULLUP_DP | DISABLE_PULLUP_DM |
DISABLE_PULLDN_DP | FORCE_PULLDN_DM))
goto NOT_NON_STD_CHARGER;
utmi_phy_set_dp_dm_pull_up_down(phy, 0);
return true;
NOT_NON_STD_CHARGER:
utmi_phy_set_dp_dm_pull_up_down(phy, 0);
return false;
}
static void utmi_phy_pmc_disable(struct tegra_usb_phy *phy)
{
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
if (phy->pdata->u_data.host.turn_off_vbus_on_lp0 &&
phy->pdata->port_otg) {
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
pmc->pmc_ops->powerdown_pmc_wake_detect(pmc);
}
}
static bool utmi_phy_nv_charger_detect(struct tegra_usb_phy *phy)
{
int status1;
int status2;
int status3;
bool ret;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (utmi_phy_is_non_std_charger(phy))
return false;
ret = false;
/* Turn off all terminations except DP pulldown */
status1 = utmi_phy_get_dp_dm_status(phy,
DISABLE_PULLUP_DP | DISABLE_PULLUP_DM |
FORCE_PULLDN_DP | DISABLE_PULLDN_DM);
/* Turn off all terminations except for DP pullup */
status2 = utmi_phy_get_dp_dm_status(phy,
FORCE_PULLUP_DP | DISABLE_PULLUP_DM |
DISABLE_PULLDN_DP | DISABLE_PULLDN_DM);
/* Check for NV charger DISABLE all terminations */
status3 = utmi_phy_get_dp_dm_status(phy,
DISABLE_PULLUP_DP | DISABLE_PULLUP_DM |
DISABLE_PULLDN_DP | DISABLE_PULLDN_DM);
if ((status1 == (USB_PORTSC_LINE_DP_SET | USB_PORTSC_LINE_DM_SET)) &&
(status2 == (USB_PORTSC_LINE_DP_SET | USB_PORTSC_LINE_DM_SET)) &&
(status3 == (USB_PORTSC_LINE_DP_SET | USB_PORTSC_LINE_DM_SET)))
ret = true;
/* Restore standard termination by hardware. */
utmi_phy_set_dp_dm_pull_up_down(phy, 0);
return ret;
}
static bool uhsic_phy_remotewake_detected(struct tegra_usb_phy *phy)
{
void __iomem *base = phy->regs;
u32 val;
unsigned int inst = phy->inst;
val = readl(base + UHSIC_PMC_WAKEUP0);
if (!(val & EVENT_INT_ENB))
return false;
val = tegra_usb_pmc_reg_read(UHSIC_STATUS(inst));
if (!(UHSIC_WAKE_ALARM(inst) & val))
return false;
tegra_usb_pmc_reg_update(PMC_UHSIC_SLEEP_CFG(inst),
UHSIC_WAKE_VAL(inst, WAKE_VAL_ANY),
UHSIC_WAKE_VAL(inst, WAKE_VAL_NONE));
tegra_usb_pmc_reg_update(PMC_UHSIC_TRIGGERS(inst),
0, UHSIC_CLR_WAKE_ALARM(inst));
val = readl(base + UHSIC_PMC_WAKEUP0);
val &= ~EVENT_INT_ENB;
writel(val, base + UHSIC_PMC_WAKEUP0);
phy->pmc_remote_wakeup = true;
DBG("%s:PMC remote wakeup detected for HSIC\n", __func__);
return true;
}
static int uhsic_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
{
unsigned long val;
void __iomem *base = phy->regs;
if (remote_wakeup) {
/* Set RUN bit */
val = readl(base + USB_USBCMD);
val |= USB_USBCMD_RS;
writel(val, base + USB_USBCMD);
}
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
return 0;
}
static void uhsic_phy_restore_start(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
unsigned int inst = phy->inst;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
val = tegra_usb_pmc_reg_read(UHSIC_STATUS(inst));
/* check whether we wake up from the remote resume */
if (UHSIC_WALK_PTR_VAL(inst) & val) {
phy->pmc_remote_wakeup = true;
DBG("%s: uhsic remote wakeup detected\n", __func__);
} else {
if (!((UHSIC_STROBE_VAL(inst) | UHSIC_DATA_VAL(inst)) & val)) {
/*
* If pmc wakeup is detected after putting controller
* in suspend in usb_phy_bringup_host_cotroller,
* restart bringing up host controller as
* in case of only pmc wakeup.
*/
if (phy->pmc_remote_wakeup && phy->ctrlr_suspended) {
usb_phy_bringup_host_controller(phy);
if (usb_phy_reg_status_wait(base + USB_PORTSC,
(USB_PORTSC_RESUME | USB_PORTSC_SUSP),
0, FPR_WAIT_TIME_US) < 0)
pr_err("%s: timeout waiting" \
"for SUSPEND to clear\n",
__func__);
phy->ctrlr_suspended = false;
}
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
phy->pmc_remote_wakeup = false;
} else {
DBG("%s(%d): setting pretend connect\n", __func__, __LINE__);
val = readl(base + UHSIC_CMD_CFG0);
val |= UHSIC_PRETEND_CONNECT_DETECT;
writel(val, base + UHSIC_CMD_CFG0);
}
}
}
static void uhsic_phy_restore_end(struct tegra_usb_phy *phy)
{
unsigned long val, flags = 0;
void __iomem *base = phy->regs;
int wait_time_us = FPR_WAIT_TIME_US; /* FPR should be set by this time */
bool irq_disabled = false;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
DBG("%s(%d)\n", __func__, __LINE__);
/*
* check whether we wake up from the remote wake detected before putting
* controller in suspend in usb_phy_bringup_host_controller.
*/
if (!phy->ctrlr_suspended) {
/* wait until FPR bit is set automatically on remote resume */
do {
val = readl(base + USB_PORTSC);
udelay(1);
if (wait_time_us == 0) {
/*
* If pmc wakeup is detected after putting controller
* in suspend in usb_phy_bringup_host_cotroller,
* restart bringing up host controller as
* in case of only pmc wakeup.
*/
if (phy->pmc_remote_wakeup && phy->ctrlr_suspended) {
usb_phy_bringup_host_controller(phy);
if (usb_phy_reg_status_wait(base + USB_PORTSC,
(USB_PORTSC_RESUME | USB_PORTSC_SUSP),
0, FPR_WAIT_TIME_US) < 0)
pr_err("%s: timeout waiting" \
" for SUSPEND to clear\n",
__func__);
phy->ctrlr_suspended = false;
}
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
phy->pmc_remote_wakeup = false;
return;
}
wait_time_us--;
} while (val & (USB_PORTSC_RESUME | USB_PORTSC_SUSP));
/* In case of remote wakeup, disable local irq to prevent
* context switch b/t disable PMC and set RUN bit ops */
local_irq_save(flags);
irq_disabled = true;
}
/*
* If pmc wakeup is detected after putting controller in suspend
* in usb_phy_bringup_host_cotroller, restart bringing up host
* controller as in case of only pmc wakeup.
*/
if (phy->pmc_remote_wakeup && phy->ctrlr_suspended) {
usb_phy_bringup_host_controller(phy);
if (usb_phy_reg_status_wait(base + USB_PORTSC,
(USB_PORTSC_RESUME | USB_PORTSC_SUSP), 0,
FPR_WAIT_TIME_US) < 0)
pr_err("%s: timeout waiting for SUSPEND to clear\n",
__func__);
phy->ctrlr_suspended = false;
}
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 1);
phy->pmc_remote_wakeup = false;
/* Restore local irq if disabled before */
if (irq_disabled)
local_irq_restore(flags);
if (usb_phy_reg_status_wait(base + USB_USBCMD, USB_USBCMD_RS,
USB_USBCMD_RS, 2000)) {
pr_err("%s: timeout waiting for USB_USBCMD_RS\n", __func__);
return;
}
}
static int uhsic_rail_enable(struct tegra_usb_phy *phy)
{
int ret;
if (phy->hsic_reg == NULL) {
phy->hsic_reg = regulator_get(&phy->pdev->dev, "vddio_hsic");
if (IS_ERR_OR_NULL(phy->hsic_reg)) {
pr_err("UHSIC: Could not get regulator vddio_hsic\n");
ret = PTR_ERR(phy->hsic_reg);
phy->hsic_reg = NULL;
return ret;
}
}
ret = regulator_enable(phy->hsic_reg);
if (ret < 0) {
pr_err("%s vddio_hsic could not be enabled\n", __func__);
return ret;
}
return 0;
}
static int uhsic_rail_disable(struct tegra_usb_phy *phy)
{
int ret;
if (phy->hsic_reg == NULL) {
pr_warn("%s: unbalanced disable\n", __func__);
return -EIO;
}
ret = regulator_disable(phy->hsic_reg);
if (ret < 0) {
pr_err("HSIC regulator vddio_hsic cannot be disabled\n");
return ret;
}
regulator_put(phy->hsic_reg);
phy->hsic_reg = NULL;
return 0;
}
static int uhsic_phy_open(struct tegra_usb_phy *phy)
{
unsigned long parent_rate;
int i;
int ret;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
phy->hsic_reg = NULL;
ret = uhsic_rail_enable(phy);
if (ret < 0) {
pr_err("%s vddio_hsic could not be enabled\n", __func__);
return ret;
}
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
parent_rate = clk_get_rate(clk_get_parent(phy->pllu_clk));
for (i = 0; i < ARRAY_SIZE(uhsic_freq_table); i++) {
if (uhsic_freq_table[i].freq == parent_rate) {
phy->freq = &uhsic_freq_table[i];
break;
}
}
if (!phy->freq) {
pr_err("invalid pll_u parent rate %ld\n", parent_rate);
return -EINVAL;
}
/* reset controller for reenumerating hsic device */
tegra_periph_reset_assert(phy->ctrlr_clk);
udelay(2);
tegra_periph_reset_deassert(phy->ctrlr_clk);
udelay(2);
pmc_init(phy);
pmc->pmc_ops->powerup_pmc_wake_detect(pmc);
return 0;
}
static void uhsic_phy_close(struct tegra_usb_phy *phy)
{
int ret;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
pmc->pmc_ops->powerdown_pmc_wake_detect(pmc);
ret = uhsic_rail_disable(phy);
if (ret < 0)
pr_err("%s vddio_hsic could not be disabled\n", __func__);
}
static int uhsic_phy_irq(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
int irq_status = IRQ_HANDLED;
/* check if there is any remote wake event */
if (!phy->pdata->unaligned_dma_buf_supported)
usb_phy_fence_read(phy);
if (uhsic_phy_remotewake_detected(phy))
DBG("%s: uhsic remote wake detected\n", __func__);
val = readl(base + USB_SUSP_CTRL);
if ((val & USB_PHY_CLK_VALID_INT_STS) &&
(val & USB_PHY_CLK_VALID_INT_ENB)) {
val &= ~USB_PHY_CLK_VALID_INT_ENB |
USB_PHY_CLK_VALID_INT_STS;
writel(val , (base + USB_SUSP_CTRL));
val = readl(base + USB_USBSTS);
if (!(val & USB_USBSTS_PCI)) {
irq_status = IRQ_NONE;
goto exit;
}
val = readl(base + USB_PORTSC);
if (val & USB_PORTSC_CCS)
val &= ~USB_PORTSC_WKCN;
val &= ~USB_PORTSC_RWC_BITS;
writel(val , (base + USB_PORTSC));
irq_status = IRQ_HANDLED;
}
exit:
return irq_status;
}
static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
#ifdef CONFIG_ARCH_TEGRA_11x_SOC
void __iomem *padctl_base = IO_ADDRESS(TEGRA_XUSB_PADCTL_BASE);
#endif
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (phy->phy_clk_on) {
DBG("%s(%d) inst:[%d] phy clk is already On\n",
__func__, __LINE__, phy->inst);
return 0;
}
#ifdef CONFIG_ARCH_TEGRA_11x_SOC
val = readl(padctl_base + PADCTL_SNPS_OC_MAP);
val |= CONTROLLER_OC(phy->inst, 0x7);
writel(val, padctl_base + PADCTL_SNPS_OC_MAP);
#endif
val = readl(base + UHSIC_PADS_CFG1);
val &= ~(UHSIC_PD_BG | UHSIC_PD_RX |
UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
writel(val, base + UHSIC_PADS_CFG1);
val |= (UHSIC_RX_SEL | UHSIC_PD_TX);
val |= UHSIC_AUTO_RTERM_EN;
writel(val, base + UHSIC_PADS_CFG1);
val = readl(base + USB_SUSP_CTRL);
val |= UHSIC_RESET;
writel(val, base + USB_SUSP_CTRL);
udelay(1);
val = readl(base + USB_SUSP_CTRL);
val |= UHSIC_PHY_ENABLE;
writel(val, base + USB_SUSP_CTRL);
val = readl(base + UHSIC_HSRX_CFG0);
val |= UHSIC_IDLE_WAIT(HSIC_IDLE_WAIT_DELAY);
val |= UHSIC_ELASTIC_UNDERRUN_LIMIT(HSIC_ELASTIC_UNDERRUN_LIMIT);
val |= UHSIC_ELASTIC_OVERRUN_LIMIT(HSIC_ELASTIC_OVERRUN_LIMIT);
writel(val, base + UHSIC_HSRX_CFG0);
val = readl(base + UHSIC_HSRX_CFG1);
val |= UHSIC_HS_SYNC_START_DLY(HSIC_SYNC_START_DELAY);
writel(val, base + UHSIC_HSRX_CFG1);
/* WAR HSIC TX */
val = readl(base + UHSIC_TX_CFG0);
val &= ~UHSIC_HS_READY_WAIT_FOR_VALID;
writel(val, base + UHSIC_TX_CFG0);
val = readl(base + UHSIC_MISC_CFG0);
val |= UHSIC_SUSPEND_EXIT_ON_EDGE;
writel(val, base + UHSIC_MISC_CFG0);
val = readl(base + UHSIC_MISC_CFG1);
val |= UHSIC_PLLU_STABLE_COUNT(phy->freq->stable_count);
writel(val, base + UHSIC_MISC_CFG1);
val = readl(base + UHSIC_PLL_CFG1);
val |= UHSIC_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
val |= UHSIC_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count);
writel(val, base + UHSIC_PLL_CFG1);
val = readl(base + USB_SUSP_CTRL);
val &= ~(UHSIC_RESET);
writel(val, base + USB_SUSP_CTRL);
udelay(1);
val = readl(base + UHSIC_PADS_CFG1);
val &= ~(UHSIC_PD_TX);
writel(val, base + UHSIC_PADS_CFG1);
/* HSIC pad tracking circuit power down sequence */
val = readl(base + UHSIC_PADS_CFG1);
val &= ~(UHSIC_PD_TRK);
writel(val, base + UHSIC_PADS_CFG1);
/* Wait for 25usec */
udelay(25);
val |= UHSIC_PD_TRK;
writel(val, base + UHSIC_PADS_CFG1);
/* Enable bus keepers always */
val = readl(base + UHSIC_SPARE_CFG0);
val |= FORCE_BK_ON;
writel(val, base + UHSIC_SPARE_CFG0);
/*SUSP_CTRL has to be toggled to enable host PHY clock */
val = readl(base + USB_SUSP_CTRL);
val |= USB_SUSP_CLR;
writel(val, base + USB_SUSP_CTRL);
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_CLR;
writel(val, base + USB_SUSP_CTRL);
val = readl(base + USB_USBMODE);
val |= USB_USBMODE_HOST;
writel(val, base + USB_USBMODE);
/* Change the USB controller PHY type to HSIC */
val = readl(base + HOSTPC1_DEVLC);
val &= ~HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_MASK);
val |= HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_HSIC);
val &= ~HOSTPC1_DEVLC_PSPD(HOSTPC1_DEVLC_PSPD_MASK);
val |= HOSTPC1_DEVLC_PSPD(HOSTPC1_DEVLC_PSPD_HIGH_SPEED);
val &= ~HOSTPC1_DEVLC_STS;
writel(val, base + HOSTPC1_DEVLC);
val = readl(base + USB_PORTSC);
val &= ~(USB_PORTSC_WKOC | USB_PORTSC_WKDS);
writel(val, base + USB_PORTSC);
val = readl(base + UHSIC_PADS_CFG0);
/* Clear RTUNEP, SLEWP & SLEWN bit fields */
val &= ~(UHSIC_TX_RTUNEP | UHSIC_TX_SLEWP | UHSIC_TX_SLEWN);
/* set Rtune impedance to 50 ohm */
#ifdef CONFIG_ARCH_TEGRA_14x_SOC
val |= UHSIC_TX_RTUNE_P(0xD);
#else
val |= UHSIC_TX_RTUNE_P(0xC);
#endif
writel(val, base + UHSIC_PADS_CFG0);
if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL,
USB_PHY_CLK_VALID, USB_PHY_CLK_VALID, 2500)) {
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
return -ETIMEDOUT;
}
phy->phy_clk_on = true;
phy->hw_accessible = true;
if (phy->pmc_sleepwalk) {
DBG("%s(%d) inst:[%d] restore phy\n", __func__, __LINE__,
phy->inst);
uhsic_phy_restore_start(phy);
usb_phy_bringup_host_controller(phy);
uhsic_phy_restore_end(phy);
phy->pmc_sleepwalk = false;
}
val = readl(base + USB_TXFILLTUNING);
if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) {
val = USB_FIFO_TXFILL_THRES(0x10);
writel(val, base + USB_TXFILLTUNING);
}
return 0;
}
static int uhsic_phy_power_off(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
bool port_connected;
struct tegra_usb_pmc_data *pmc = &pmc_data[phy->inst];
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (!phy->phy_clk_on) {
DBG("%s(%d) inst:[%d] phy clk is already off\n",
__func__, __LINE__, phy->inst);
return 0;
}
/* Disable interrupts */
writel(0, base + USB_USBINTR);
/* check for port connect status */
val = readl(base + USB_PORTSC);
port_connected = val & USB_PORTSC_CCS;
if (phy->pmc_sleepwalk == false && port_connected) {
pmc->pmc_ops->setup_pmc_wake_detect(pmc);
phy->pmc_remote_wakeup = false;
phy->pmc_sleepwalk = true;
val = readl(base + UHSIC_PMC_WAKEUP0);
val |= EVENT_INT_ENB;
writel(val, base + UHSIC_PMC_WAKEUP0);
} else {
phy->pmc_sleepwalk = true;
}
val = readl(base + HOSTPC1_DEVLC);
val |= HOSTPC1_DEVLC_PHCD;
writel(val, base + HOSTPC1_DEVLC);
/* Remove power downs for HSIC from PADS CFG1 register */
val = readl(base + UHSIC_PADS_CFG1);
val |= (UHSIC_PD_BG | UHSIC_PD_TRK |
UHSIC_PD_ZI | UHSIC_PD_TX);
writel(val, base + UHSIC_PADS_CFG1);
if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL,
USB_PHY_CLK_VALID, 0, 2500))
pr_warn("%s: timeout waiting for phy to disable\n", __func__);
DBG("%s(%d) inst:[%d] End\n", __func__, __LINE__, phy->inst);
phy->phy_clk_on = false;
phy->hw_accessible = false;
return 0;
}
static int uhsic_phy_bus_port_power(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
val = readl(base + USB_USBMODE);
val |= USB_USBMODE_HOST;
writel(val, base + USB_USBMODE);
/* Change the USB controller PHY type to HSIC */
val = readl(base + HOSTPC1_DEVLC);
val &= ~(HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_MASK));
val |= HOSTPC1_DEVLC_PTS(HOSTPC1_DEVLC_PTS_HSIC);
val &= ~(HOSTPC1_DEVLC_PSPD(HOSTPC1_DEVLC_PSPD_MASK));
val |= HOSTPC1_DEVLC_PSPD(HOSTPC1_DEVLC_PSPD_HIGH_SPEED);
writel(val, base + HOSTPC1_DEVLC);
val = readl(base + UHSIC_MISC_CFG0);
val |= UHSIC_DETECT_SHORT_CONNECT;
writel(val, base + UHSIC_MISC_CFG0);
udelay(1);
if (phy->pdata->ops && phy->pdata->ops->port_power)
phy->pdata->ops->port_power();
return 0;
}
static void ulpi_set_trimmer(struct tegra_usb_phy *phy)
{
struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
void __iomem *base = phy->regs;
unsigned long val;
val = ULPI_DATA_TRIMMER_SEL(config->data_trimmer);
val |= ULPI_STPDIRNXT_TRIMMER_SEL(config->stpdirnxt_trimmer);
val |= ULPI_DIR_TRIMMER_SEL(config->dir_trimmer);
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);
}
static int ulpi_link_phy_open(struct tegra_usb_phy *phy)
{
#if defined(CONFIG_TEGRA_SILICON_PLATFORM)
struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
#endif
int err = 0;
phy->ulpi_clk = NULL;
DBG("%s inst:[%d]\n", __func__, phy->inst);
#if defined(CONFIG_TEGRA_SILICON_PLATFORM)
if (config->clk) {
phy->ulpi_clk = clk_get_sys(NULL, config->clk);
if (IS_ERR(phy->ulpi_clk)) {
pr_err("%s: can't get ulpi clock\n", __func__);
err = -ENXIO;
}
}
#endif
phy->ulpi_vp = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
phy->ulpi_vp->io_priv = phy->regs + ULPI_VIEWPORT;
return err;
}
static void ulpi_link_phy_close(struct tegra_usb_phy *phy)
{
DBG("%s inst:[%d]\n", __func__, phy->inst);
if (phy->ulpi_clk)
clk_put(phy->ulpi_clk);
}
static int ulpi_link_phy_irq(struct tegra_usb_phy *phy)
{
DBG("%s inst:[%d]\n", __func__, phy->inst);
return IRQ_HANDLED;
}
static int ulpi_link_phy_power_off(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
int ret;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (!phy->phy_clk_on) {
DBG("%s(%d) inst:[%d] phy clk is already off\n", __func__,
__LINE__, phy->inst);
return 0;
}
/* Disable VbusValid, SessEnd comparators */
ret = usb_phy_io_write(phy->ulpi_vp, 0x00, 0x0D);
if (ret)
pr_err("%s: ulpi write 0x0D failed\n", __func__);
ret = usb_phy_io_write(phy->ulpi_vp, 0x00, 0x10);
if (ret)
pr_err("%s: ulpi write 0x10 failed\n", __func__);
/* Disable IdFloat comparator */
ret = usb_phy_io_write(phy->ulpi_vp, 0x00, 0x19);
if (ret)
pr_err("%s: ulpi write 0x19 failed\n", __func__);
ret = usb_phy_io_write(phy->ulpi_vp, 0x00, 0x1D);
if (ret)
pr_err("%s: ulpi write 0x1D failed\n", __func__);
phy->port_speed = (readl(base + USB_PORTSC) >> 26) &
USB_PORTSC_PSPD_MASK;
/* 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_PORTSC);
val &= ~(USB_PORTSC_WKOC | USB_PORTSC_WKDS | USB_PORTSC_WKCN);
writel(val, base + USB_PORTSC);
/* Put the PHY in the low power mode */
val = readl(base + USB_PORTSC);
val |= USB_PORTSC_PHCD;
writel(val, base + USB_PORTSC);
if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
0, 2500)) {
pr_err("%s: timeout waiting for phy to stop\n", __func__);
}
if (phy->ulpi_clk)
clk_disable(phy->ulpi_clk);
phy->phy_clk_on = false;
phy->hw_accessible = false;
return 0;
}
static int ulpi_link_phy_power_on(struct tegra_usb_phy *phy)
{
int ret;
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (phy->phy_clk_on) {
DBG("%s(%d) inst:[%d] phy clk is already On\n", __func__,
__LINE__, phy->inst);
return 0;
}
if (phy->ulpi_clk) {
clk_enable(phy->ulpi_clk);
mdelay(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 = readl(base + USB_SUSP_CTRL);
val |= USB_SUSP_CLR;
writel(val, base + USB_SUSP_CTRL);
if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
USB_PHY_CLK_VALID, 2500))
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, USB_CLKEN,
USB_CLKEN, 2500))
pr_err("%s: timeout waiting for AHB clock\n", __func__);
val = readl(base + USB_SUSP_CTRL);
val &= ~USB_SUSP_CLR;
writel(val, base + USB_SUSP_CTRL);
val = 0;
writel(val, base + ULPI_TIMING_CTRL_1);
ulpi_set_trimmer(phy);
/* Fix VbusInvalid due to floating VBUS */
ret = usb_phy_io_write(phy->ulpi_vp, 0x40, 0x08);
if (ret) {
pr_err("%s: ulpi write failed\n", __func__);
return ret;
}
ret = usb_phy_io_write(phy->ulpi_vp, 0x80, 0x0B);
if (ret) {
pr_err("%s: ulpi write failed\n", __func__);
return ret;
}
val = readl(base + USB_PORTSC);
val |= USB_PORTSC_WKOC | USB_PORTSC_WKDS | USB_PORTSC_WKCN;
writel(val, base + USB_PORTSC);
phy->phy_clk_on = true;
phy->hw_accessible = true;
return 0;
}
static inline void ulpi_link_phy_set_tristate(bool enable)
{
}
static void ulpi_link_phy_restore_start(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
/*Tristate ulpi interface before USB controller resume*/
ulpi_link_phy_set_tristate(true);
val = readl(base + ULPI_TIMING_CTRL_0);
val &= ~ULPI_OUTPUT_PINMUX_BYP;
writel(val, base + ULPI_TIMING_CTRL_0);
}
static void ulpi_link_phy_restore_end(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
val = readl(base + ULPI_TIMING_CTRL_0);
val |= ULPI_OUTPUT_PINMUX_BYP;
writel(val, base + ULPI_TIMING_CTRL_0);
ulpi_link_phy_set_tristate(false);
}
static int ulpi_link_phy_resume(struct tegra_usb_phy *phy)
{
int status = 0;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (phy->pdata->u_data.host.power_off_on_suspend) {
status = ulpi_link_phy_power_on(phy);
if (phy->port_speed < USB_PHY_PORT_SPEED_UNKNOWN) {
ulpi_link_phy_restore_start(phy);
usb_phy_bringup_host_controller(phy);
ulpi_link_phy_restore_end(phy);
}
}
return status;
}
static void reset_utmip_uhsic(void __iomem *base)
{
unsigned long val;
val = readl(base + USB_SUSP_CTRL);
val |= UHSIC_RESET;
writel(val, base + USB_SUSP_CTRL);
val = readl(base + USB_SUSP_CTRL);
val |= UTMIP_RESET;
writel(val, base + USB_SUSP_CTRL);
}
static void ulpi_set_host(void __iomem *base)
{
unsigned long val;
val = readl(base + USB_USBMODE);
val &= ~USB_USBMODE_MASK;
val |= USB_USBMODE_HOST;
writel(val, base + USB_USBMODE);
val = readl(base + HOSTPC1_DEVLC);
val |= HOSTPC1_DEVLC_PTS(2);
writel(val, base + HOSTPC1_DEVLC);
}
static inline void ulpi_pinmux_bypass(struct tegra_usb_phy *phy, bool enable)
{
unsigned long val;
void __iomem *base = phy->regs;
val = readl(base + ULPI_TIMING_CTRL_0);
if (enable)
val |= ULPI_OUTPUT_PINMUX_BYP;
else
val &= ~ULPI_OUTPUT_PINMUX_BYP;
writel(val, base + ULPI_TIMING_CTRL_0);
}
static inline void ulpi_null_phy_set_tristate(bool enable)
{
#ifdef CONFIG_ARCH_TEGRA_11x_SOC
int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL;
DBG("%s(%d) inst:[%s] FIXME enable pin group +++\n", __func__,
__LINE__, enable ? "TRISTATE" : "NORMAL");
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA0, tristate);
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA1, tristate);
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA2, tristate);
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA3, tristate);
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA4, tristate);
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA5, tristate);
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA6, tristate);
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA7, tristate);
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_NXT, tristate);
if (enable)
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, tristate);
#endif
}
static void ulpi_null_phy_obs_read(void)
{
static void __iomem *apb_misc;
unsigned slv0_obs, s2s_obs;
if (!apb_misc)
apb_misc = ioremap(TEGRA_APB_MISC_BASE, TEGRA_APB_MISC_SIZE);
writel(0x80d1003c, apb_misc + APB_MISC_GP_OBSCTRL_0);
slv0_obs = readl(apb_misc + APB_MISC_GP_OBSDATA_0);
writel(0x80d10040, apb_misc + APB_MISC_GP_OBSCTRL_0);
s2s_obs = readl(apb_misc + APB_MISC_GP_OBSDATA_0);
pr_debug("slv0 obs: %08x\ns2s obs: %08x\n", slv0_obs, s2s_obs);
}
static const struct gpio ulpi_gpios[] = {
{ULPI_STP, GPIOF_IN, "ULPI_STP"},
{ULPI_DIR, GPIOF_OUT_INIT_LOW, "ULPI_DIR"},
{ULPI_D0, GPIOF_OUT_INIT_LOW, "ULPI_D0"},
{ULPI_D1, GPIOF_OUT_INIT_LOW, "ULPI_D1"},
};
static int ulpi_null_phy_open(struct tegra_usb_phy *phy)
{
struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
int ret;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
ret = gpio_request_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
if (ret)
return ret;
if (gpio_is_valid(config->phy_restore_gpio)) {
ret = gpio_request(config->phy_restore_gpio, "phy_restore");
if (ret)
goto err_gpio_free;
gpio_direction_input(config->phy_restore_gpio);
}
tegra_periph_reset_assert(phy->ctrlr_clk);
udelay(10);
tegra_periph_reset_deassert(phy->ctrlr_clk);
return 0;
err_gpio_free:
gpio_free_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
return ret;
}
static void ulpi_null_phy_close(struct tegra_usb_phy *phy)
{
struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (gpio_is_valid(config->phy_restore_gpio))
gpio_free(config->phy_restore_gpio);
gpio_free_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
}
static int ulpi_null_phy_power_off(struct tegra_usb_phy *phy)
{
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (!phy->phy_clk_on) {
DBG("%s(%d) inst:[%d] phy clk is already off\n", __func__,
__LINE__, phy->inst);
return 0;
}
phy->phy_clk_on = false;
phy->hw_accessible = false;
ulpi_null_phy_set_tristate(true);
return 0;
}
/* NOTE: this function must be called before ehci reset */
static int ulpi_null_phy_init(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
_usb_phy_init(phy);
val = readl(base + ULPIS2S_CTRL);
val |= ULPIS2S_SLV0_CLAMP_XMIT;
writel(val, base + ULPIS2S_CTRL);
val = readl(base + USB_SUSP_CTRL);
val |= ULPIS2S_SLV0_RESET;
writel(val, base + USB_SUSP_CTRL);
udelay(10);
return 0;
}
static int ulpi_null_phy_irq(struct tegra_usb_phy *phy)
{
return IRQ_HANDLED;
}
/* NOTE: this function must be called after ehci reset */
static int ulpi_null_phy_cmd_reset(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
ulpi_set_host(base);
/* remove slave0 reset */
val = readl(base + USB_SUSP_CTRL);
val &= ~ULPIS2S_SLV0_RESET;
writel(val, base + USB_SUSP_CTRL);
val = readl(base + ULPIS2S_CTRL);
val &= ~ULPIS2S_SLV0_CLAMP_XMIT;
writel(val, base + ULPIS2S_CTRL);
udelay(10);
return 0;
}
static int ulpi_null_phy_restore(struct tegra_usb_phy *phy)
{
struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
unsigned long timeout;
int ulpi_stp = ULPI_STP;
if (gpio_is_valid(config->phy_restore_gpio))
ulpi_stp = config->phy_restore_gpio;
/* disable ULPI pinmux bypass */
ulpi_pinmux_bypass(phy, false);
/* driving linstate by GPIO */
gpio_set_value(ULPI_D0, 0);
gpio_set_value(ULPI_D1, 0);
/* driving DIR high */
gpio_set_value(ULPI_DIR, 1);
/* remove ULPI tristate */
ulpi_null_phy_set_tristate(false);
/* wait for STP high */
timeout = jiffies + msecs_to_jiffies(25);
while (!gpio_get_value(ulpi_stp)) {
if (time_after(jiffies, timeout)) {
pr_warn("phy restore timeout\n");
return 1;
}
}
return 0;
}
static int ulpi_null_phy_lp0_resume(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
ulpi_null_phy_init(phy);
val = readl(base + USB_USBCMD);
val |= USB_CMD_RESET;
writel(val, base + USB_USBCMD);
if (usb_phy_reg_status_wait(base + USB_USBCMD,
USB_CMD_RESET, 0, 2500) < 0) {
pr_err("%s: timeout waiting for reset\n", __func__);
}
ulpi_null_phy_cmd_reset(phy);
val = readl(base + USB_USBCMD);
val |= USB_USBCMD_RS;
writel(val, base + USB_USBCMD);
if (usb_phy_reg_status_wait(base + USB_USBCMD, USB_USBCMD_RS,
USB_USBCMD_RS, 2000)) {
pr_err("%s: timeout waiting for USB_USBCMD_RS\n", __func__);
return -ETIMEDOUT;
}
/* Enable Port Power */
val = readl(base + USB_PORTSC);
val |= USB_PORTSC_PP;
writel(val, base + USB_PORTSC);
udelay(10);
ulpi_null_phy_restore(phy);
return 0;
}
static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (phy->phy_clk_on) {
DBG("%s(%d) inst:[%d] phy clk is already On\n", __func__,
__LINE__, phy->inst);
return 0;
}
reset_utmip_uhsic(base);
/* remove ULPI PADS CLKEN reset */
val = readl(base + USB_SUSP_CTRL);
val &= ~ULPI_PADS_CLKEN_RESET;
writel(val, base + USB_SUSP_CTRL);
udelay(10);
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);
udelay(10);
/* set timming parameters */
val = readl(base + ULPI_TIMING_CTRL_0);
val |= ULPI_SHADOW_CLK_LOOPBACK_EN;
val &= ~ULPI_SHADOW_CLK_SEL;
val &= ~ULPI_LBK_PAD_EN;
val |= ULPI_SHADOW_CLK_DELAY(config->shadow_clk_delay);
val |= ULPI_CLOCK_OUT_DELAY(config->clock_out_delay);
val |= ULPI_LBK_PAD_E_INPUT_OR;
writel(val, base + ULPI_TIMING_CTRL_0);
writel(0, base + ULPI_TIMING_CTRL_1);
udelay(10);
/* start internal 60MHz clock */
val = readl(base + ULPIS2S_CTRL);
val |= ULPIS2S_ENA;
val |= ULPIS2S_SUPPORT_DISCONNECT;
val |= ULPIS2S_SPARE((phy->pdata->op_mode == TEGRA_USB_OPMODE_HOST) ? 3 : 1);
val |= ULPIS2S_PLLU_MASTER_BLASTER60;
writel(val, base + ULPIS2S_CTRL);
/* select ULPI_CORE_CLK_SEL to SHADOW_CLK */
val = readl(base + ULPI_TIMING_CTRL_0);
val |= ULPI_CORE_CLK_SEL;
writel(val, base + ULPI_TIMING_CTRL_0);
udelay(10);
/* enable ULPI null phy clock - can't set the trimmers before this */
val = readl(base + ULPI_TIMING_CTRL_0);
val |= ULPI_CLK_OUT_ENA;
writel(val, base + ULPI_TIMING_CTRL_0);
udelay(10);
if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
USB_PHY_CLK_VALID, 2500)) {
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
return -ETIMEDOUT;
}
/* set ULPI trimmers */
ulpi_set_trimmer(phy);
ulpi_set_host(base);
/* remove slave0 reset */
val = readl(base + USB_SUSP_CTRL);
val &= ~ULPIS2S_SLV0_RESET;
writel(val, base + USB_SUSP_CTRL);
/* remove slave1 and line reset */
val = readl(base + USB_SUSP_CTRL);
val &= ~ULPIS2S_SLV1_RESET;
val &= ~ULPIS2S_LINE_RESET;
/* remove ULPI PADS reset */
val &= ~ULPI_PADS_RESET;
writel(val, base + USB_SUSP_CTRL);
if (!phy->ulpi_clk_padout_ena) {
val = readl(base + ULPI_TIMING_CTRL_0);
val |= ULPI_CLK_PADOUT_ENA;
writel(val, base + ULPI_TIMING_CTRL_0);
phy->ulpi_clk_padout_ena = true;
} else {
if (!readl(base + USB_ASYNCLISTADDR))
ulpi_null_phy_lp0_resume(phy);
}
udelay(10);
phy->phy_clk_on = true;
phy->hw_accessible = true;
return 0;
}
static int ulpi_null_phy_pre_resume(struct tegra_usb_phy *phy,
bool remote_wakeup)
{
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
ulpi_null_phy_obs_read();
return 0;
}
static int ulpi_null_phy_post_resume(struct tegra_usb_phy *phy)
{
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
ulpi_null_phy_obs_read();
return 0;
}
static int ulpi_null_phy_resume(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
if (!readl(base + USB_ASYNCLISTADDR)) {
/* enable ULPI CLK output pad */
val = readl(base + ULPI_TIMING_CTRL_0);
val |= ULPI_CLK_PADOUT_ENA;
writel(val, base + ULPI_TIMING_CTRL_0);
/* enable ULPI pinmux bypass */
ulpi_pinmux_bypass(phy, true);
udelay(5);
#ifdef CONFIG_ARCH_TEGRA_11x_SOC
/* remove DIR tristate */
tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR,
TEGRA_TRI_NORMAL);
#endif
}
return 0;
}
static struct tegra_usb_phy_ops utmi_phy_ops = {
.init = _usb_phy_init,
.reset = usb_phy_reset,
.open = utmi_phy_open,
.close = utmi_phy_close,
.irq = utmi_phy_irq,
.power_on = utmi_phy_power_on,
.power_off = utmi_phy_power_off,
.pre_resume = utmi_phy_pre_resume,
.resume = utmi_phy_resume,
.charger_detect = utmi_phy_charger_detect,
.nv_charger_detect = utmi_phy_nv_charger_detect,
.pmc_disable = utmi_phy_pmc_disable,
};
static struct tegra_usb_phy_ops uhsic_phy_ops = {
.init = _usb_phy_init,
.open = uhsic_phy_open,
.close = uhsic_phy_close,
.irq = uhsic_phy_irq,
.power_on = uhsic_phy_power_on,
.power_off = uhsic_phy_power_off,
.pre_resume = uhsic_phy_pre_resume,
.port_power = uhsic_phy_bus_port_power,
};
static struct tegra_usb_phy_ops ulpi_link_phy_ops = {
.init = _usb_phy_init,
.reset = usb_phy_reset,
.open = ulpi_link_phy_open,
.close = ulpi_link_phy_close,
.irq = ulpi_link_phy_irq,
.power_on = ulpi_link_phy_power_on,
.power_off = ulpi_link_phy_power_off,
.resume = ulpi_link_phy_resume,
};
static struct tegra_usb_phy_ops ulpi_null_phy_ops = {
.open = ulpi_null_phy_open,
.close = ulpi_null_phy_close,
.init = ulpi_null_phy_init,
.irq = ulpi_null_phy_irq,
.power_on = ulpi_null_phy_power_on,
.power_off = ulpi_null_phy_power_off,
.pre_resume = ulpi_null_phy_pre_resume,
.resume = ulpi_null_phy_resume,
.post_resume = ulpi_null_phy_post_resume,
.reset = ulpi_null_phy_cmd_reset,
};
static struct tegra_usb_phy_ops icusb_phy_ops;
static struct tegra_usb_phy_ops *phy_ops[] = {
[TEGRA_USB_PHY_INTF_UTMI] = &utmi_phy_ops,
[TEGRA_USB_PHY_INTF_ULPI_LINK] = &ulpi_link_phy_ops,
[TEGRA_USB_PHY_INTF_ULPI_NULL] = &ulpi_null_phy_ops,
[TEGRA_USB_PHY_INTF_HSIC] = &uhsic_phy_ops,
[TEGRA_USB_PHY_INTF_ICUSB] = &icusb_phy_ops,
};
int tegra11x_usb_phy_init_ops(struct tegra_usb_phy *phy)
{
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
phy->ops = phy_ops[phy->pdata->phy_intf];
/* FIXME: uncommenting below line to make USB host mode fail*/
/* pmc->pmc_ops->power_down_pmc(pmc); */
return 0;
}