diff options
Diffstat (limited to 'arch/arm/mach-tegra/usb_phy.c')
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 2365 |
1 files changed, 2218 insertions, 147 deletions
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index 88081bb3ec52..532bf73c10d1 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -2,6 +2,7 @@ * arch/arm/mach-tegra/usb_phy.c * * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 - 2011 NVIDIA Corporation * * Author: * Erik Gilling <konkers@google.com> @@ -20,6 +21,7 @@ #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> @@ -30,8 +32,30 @@ #include <asm/mach-types.h> #include <mach/usb_phy.h> #include <mach/iomap.h> +#include <mach/pinmux.h> +#include "gpio-names.h" +#include "fuse.h" + +/* Modem hibernate test parameters */ +#define TRIGGER_BY_MDM2AP_ACK 1 +/* ------------------------------- */ + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +#define USB_USBCMD 0x140 +#define USB_USBCMD_RS (1 << 0) + +#define USB_USBSTS 0x144 +#define USB_USBSTS_PCI (1 << 2) +#define USB_USBSTS_HCH (1 << 12) + +#define USB_TXFILLTUNING 0x164 +#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_PORTSC1 0x184 #define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) @@ -42,6 +66,7 @@ #define USB_PORTSC1_WKCN (1 << 20) #define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) #define USB_PORTSC1_PP (1 << 12) +#define USB_PORTSC1_LS(x) (((x) & 0x3) << 10) #define USB_PORTSC1_SUSP (1 << 7) #define USB_PORTSC1_PE (1 << 2) #define USB_PORTSC1_CCS (1 << 0) @@ -50,14 +75,22 @@ #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 UTMIP_RESET (1 << 11) -#define UHSIC_RESET (1 << 11) -#define UTMIP_PHY_ENABLE (1 << 12) +#define USB_PHY_CLK_VALID_INT_ENB (1 << 9) +#define UTMIP_RESET (1 << 11) +#define UHSIC_RESET (1 << 11) +#define UTMIP_PHY_ENABLE (1 << 12) +#define UHSIC_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 USB_PHY_VBUS_WAKEUP_ID 0x408 +#define VDAT_DET_INT_EN (1 << 16) +#define VDAT_DET_CHG_DET (1 << 17) +#define VDAT_DET_STS (1 << 18) + #define USB1_LEGACY_CTRL 0x410 #define USB1_NO_LEGACY_MODE (1 << 0) #define USB1_VBUS_SENSE_CTL_MASK (3 << 1) @@ -67,17 +100,6 @@ #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) @@ -90,8 +112,14 @@ #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_MAX_OFFSET 2 +#define UTMIP_XCVR_SETUP_MAX_VALUE 0x7f +#define XCVR_SETUP_MSB_CALIB(x) ((x) >> 4) + #define UTMIP_BIAS_CFG0 0x80c #define UTMIP_OTGPD (1 << 11) #define UTMIP_BIASPD (1 << 10) @@ -107,15 +135,6 @@ #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) @@ -125,9 +144,164 @@ #define UTMIP_BAT_CHRG_CFG0 0x830 #define UTMIP_PD_CHRG (1 << 0) +#define UTMIP_ON_SINK_EN (1 << 2) +#define UTMIP_OP_SRC_EN (1 << 3) -#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) + +#define UHSIC_PLL_CFG1 0x804 +#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14) + +#define UHSIC_HSRX_CFG0 0x808 +#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 0x80c +#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) + +#define UHSIC_MISC_CFG0 0x814 +#define UHSIC_SUSPEND_EXIT_ON_EDGE (1 << 7) +#define UHSIC_DETECT_SHORT_CONNECT (1 << 8) +#define UHSIC_FORCE_XCVR_MODE (1 << 15) + +#define UHSIC_MISC_CFG1 0X818 +#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2) + +#define UHSIC_PADS_CFG0 0x81c +#define UHSIC_TX_RTUNEN 0xf000 +#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12) + +#define UHSIC_PADS_CFG1 0x820 +#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_STAT_CFG0 0x828 +#define UHSIC_CONNECT_DETECT (1 << 0) + + +#else + +#define USB_USBCMD 0x130 +#define USB_USBCMD_RS (1 << 0) + +#define USB_USBSTS 0x134 +#define USB_USBSTS_PCI (1 << 2) +#define USB_USBSTS_SRI (1 << 7) +#define USB_USBSTS_HCH (1 << 12) + +#define ULPI_VIEWPORT 0x160 + +#define USB_PORTSC1 0x174 +#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_RESUME (1 << 6) +#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 USB_PHY_CLK_VALID_INT_ENB (1 << 9) + + +#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 VDAT_DET_INT_EN (1 << 16) +#define VDAT_DET_CHG_DET (1 << 17) +#define VDAT_DET_STS (1 << 18) + +#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 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_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_MAX_OFFSET 5 +#define UTMIP_XCVR_SETUP_MAX_VALUE 0x7f +#define XCVR_SETUP_MSB_CALIB(x) ((x) >> 4) + +#define UTMIP_BIAS_CFG0 0x80c +#define UTMIP_OTGPD (1 << 11) +#define UTMIP_BIASPD (1 << 10) +#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0) +#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2) +#define UTMIP_HSDISCON_LEVEL_MSB (1 << 24) + +#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_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_ON_SINK_EN (1 << 2) +#define UTMIP_OP_SRC_EN (1 << 3) #define UTMIP_XCVR_CFG1 0x838 #define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0) @@ -137,6 +311,270 @@ #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 HOSTPC1_DEVLC 0x1b4 +#define HOSTPC1_DEVLC_PHCD (1 << 22) +#define HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) +#define HOSTPC1_DEVLC_PTS_MASK 7 +#define HOSTPC1_DEVLC_PTS_HSIC 4 +#define HOSTPC1_DEVLC_STS (1 << 28) +#define HOSTPC1_DEVLC_PSPD(x) (((x) & 0x3) << 25) +#define HOSTPC1_DEVLC_PSPD_MASK 3 +#define HOSTPC1_DEVLC_PSPD_HIGH_SPEED 2 + +#define TEGRA_USB_USBMODE_REG_OFFSET 0x1f8 +#define TEGRA_USB_USBMODE_HOST (3 << 0) + +#define TEGRA_PMC_USB_AO 0xf0 +#define TEGRA_PMC_USB_AO_VBUS_WAKEUP_PD_P0 (1 << 2) +#define TEGRA_PMC_USB_AO_ID_PD_P0 (1 << 3) +#define TEGRA_PMC_USB_AO_PD_P2 (0xf << 8) + +#define ICUSB_CTRL 0x15c + +#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_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_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_RTUNE(x) (((x) & 0xf) << 12) + +#define UHSIC_PADS_CFG1 0xc20 +#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_STAT_CFG0 0xc28 +#define UHSIC_CONNECT_DETECT (1 << 0) + +#define PMC_UTMIP_MASTER_CONFIG 0x310 +#define UTMIP_PWR(inst) (1 << (inst)) + +#define PMC_USB_DEBOUNCE 0xec +#define UTMIP_LINE_DEB_CNT(x) (((x) & 0xf) << 16) + +#define PMC_UTMIP_UHSIC_FAKE 0x218 +#define USBON_VAL(inst) (1 << ((4*(inst))+1)) +#define USBON_VAL_P2 (1 << 9) +#define USBON_VAL_P1 (1 << 5) +#define USBON_VAL_P0 (1 << 1) +#define USBOP_VAL(inst) (1 << (4*(inst))) +#define USBOP_VAL_P2 (1 << 8) +#define USBOP_VAL_P1 (1 << 4) +#define USBOP_VAL_P0 (1 << 0) + +#define PMC_SLEEPWALK_CFG 0x200 +#define UTMIP_LINEVAL_WALK_EN(inst) (1 << ((8*(inst))+7)) +#define UTMIP_LINEVAL_WALK_EN_P2 (1 << 23) +#define UTMIP_LINEVAL_WALK_EN_P1 (1 << 15) +#define UTMIP_LINEVAL_WALK_EN_P0 (1 << 7) +#define UTMIP_WAKE_VAL(inst, x) (((x) & 0xf) << ((8*(inst))+4)) +#define UTMIP_WAKE_VAL_P2(x) (((x) & 0xf) << 20) +#define UTMIP_WAKE_VAL_P1(x) (((x) & 0xf) << 12) +#define UTMIP_WAKE_VAL_P0(x) (((x) & 0xf) << 4) +#define WAKE_VAL_NONE 0xc +#define WAKE_VAL_FSJ 0x2 +#define WAKE_VAL_FSK 0x1 +#define WAKE_VAL_SE0 0x0 +#define WAKE_VAL_ANY 0xf + +#define PMC_SLEEP_CFG 0x1fc +#define UTMIP_TCTRL_USE_PMC(inst) (1 << ((8*(inst))+3)) +#define UTMIP_TCTRL_USE_PMC_P2 (1 << 19) +#define UTMIP_TCTRL_USE_PMC_P1 (1 << 11) +#define UTMIP_TCTRL_USE_PMC_P0 (1 << 3) +#define UTMIP_RCTRL_USE_PMC(inst) (1 << ((8*(inst))+2)) +#define UTMIP_RCTRL_USE_PMC_P2 (1 << 18) +#define UTMIP_RCTRL_USE_PMC_P1 (1 << 10) +#define UTMIP_RCTRL_USE_PMC_P0 (1 << 2) +#define UTMIP_FSLS_USE_PMC(inst) (1 << ((8*(inst))+1)) +#define UTMIP_FSLS_USE_PMC_P2 (1 << 17) +#define UTMIP_FSLS_USE_PMC_P1 (1 << 9) +#define UTMIP_FSLS_USE_PMC_P0 (1 << 1) +#define UTMIP_MASTER_ENABLE(inst) (1 << (8*(inst))) +#define UTMIP_MASTER_ENABLE_P2 (1 << 16) +#define UTMIP_MASTER_ENABLE_P1 (1 << 8) +#define UTMIP_MASTER_ENABLE_P0 (1 << 0) + +#define PMC_USB_AO 0xf0 +#define USBON_VAL_PD(inst) (1 << ((4*(inst))+1)) +#define USBON_VAL_PD_P2 (1 << 9) +#define USBON_VAL_PD_P1 (1 << 5) +#define USBON_VAL_PD_P0 (1 << 1) +#define USBOP_VAL_PD(inst) (1 << (4*(inst))) +#define USBOP_VAL_PD_P2 (1 << 8) +#define USBOP_VAL_PD_P1 (1 << 4) +#define USBOP_VAL_PD_P0 (1 << 0) + +#define PMC_TRIGGERS 0x1ec +#define UTMIP_CLR_WALK_PTR(inst) (1 << (inst)) +#define UTMIP_CLR_WALK_PTR_P2 (1 << 2) +#define UTMIP_CLR_WALK_PTR_P1 (1 << 1) +#define UTMIP_CLR_WALK_PTR_P0 (1 << 0) +#define UTMIP_CAP_CFG(inst) (1 << ((inst)+4)) +#define UTMIP_CAP_CFG_P2 (1 << 6) +#define UTMIP_CAP_CFG_P1 (1 << 5) +#define UTMIP_CAP_CFG_P0 (1 << 4) +#define UTMIP_CLR_WAKE_ALARM(inst) (1 << ((inst)+12)) +#define UTMIP_CLR_WAKE_ALARM_P2 (1 << 14) + +#define PMC_PAD_CFG (0x1f4) + +#define PMC_UTMIP_BIAS_MASTER_CNTRL 0x30c +#define BIAS_MASTER_PROG_VAL (1 << 1) + +#define PMC_SLEEPWALK_REG(inst) (0x204 + (4*(inst))) +#define PMC_SLEEPWALK_P0 0x204 +#define PMC_SLEEPWALK_P1 0x208 +#define PMC_SLEEPWALK_P2 0x20c +#define UTMIP_USBOP_RPD_A (1 << 0) +#define UTMIP_USBON_RPD_A (1 << 1) +#define UTMIP_AP_A (1 << 4) +#define UTMIP_AN_A (1 << 5) +#define UTMIP_HIGHZ_A (1 << 6) +#define UTMIP_USBOP_RPD_B (1 << 8) +#define UTMIP_USBON_RPD_B (1 << 9) +#define UTMIP_AP_B (1 << 12) +#define UTMIP_AN_B (1 << 13) +#define UTMIP_HIGHZ_B (1 << 14) +#define UTMIP_USBOP_RPD_C (1 << 16) +#define UTMIP_USBON_RPD_C (1 << 17) +#define UTMIP_AP_C (1 << 20) +#define UTMIP_AN_C (1 << 21) +#define UTMIP_HIGHZ_C (1 << 22) +#define UTMIP_USBOP_RPD_D (1 << 24) +#define UTMIP_USBON_RPD_D (1 << 25) +#define UTMIP_AP_D (1 << 28) +#define UTMIP_AN_D (1 << 29) +#define UTMIP_HIGHZ_D (1 << 30) + +#define UTMIP_PMC_WAKEUP0 0x84c +#define EVENT_INT_ENB (1 << 0) + +#define UTMIP_UHSIC_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 UTMIP_BIAS_STS0 0x840 +#define UTMIP_RCTRL_VAL(x) (((x) & 0xffff) << 0) +#define UTMIP_TCTRL_VAL(x) (((x) & (0xffff << 16)) >> 16) + +#define PMC_UTMIP_TERM_PAD_CFG 0x1f8 +#define PMC_TCTRL_VAL(x) (((x) & 0x1f) << 5) +#define PMC_RCTRL_VAL(x) (((x) & 0x1f) << 0) + +static u32 utmip_rctrl_val, utmip_tctrl_val; + +#endif + +/* Common registers */ +#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 FORCE_PULLDN_DM (1 << 8) +#define FORCE_PULLDN_DP (1 << 9) +#define COMB_TERMS (1 << 0) +#define ALWAYS_FREE_RUNNING_TERMS (1 << 1) + +#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 UTMIP_SPARE_CFG0 0x834 +#define FUSE_SETUP_SEL (1 << 3) +#define FUSE_ATERM_SEL (1 << 4) + +#define FUSE_USB_CALIB_0 0x1F0 +#define FUSE_USB_CALIB_XCVR_SETUP(x) (((x) & 0x7F) << 0) + +#define UHSIC_PLL_CFG0 0x800 + +#define UHSIC_TX_CFG0 0x810 +#define UHSIC_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 6) + +#define UHSIC_CMD_CFG0 0x824 +#define UHSIC_PRETEND_CONNECT_DETECT (1 << 5) + +#define UHSIC_SPARE_CFG0 0x82c + +/* These values (in milli second) are taken from the battery charging spec */ +#define TDP_SRC_ON_MS 100 +#define TDPSRC_CON_MS 40 static DEFINE_SPINLOCK(utmip_pad_lock); static int utmip_pad_count; @@ -146,8 +584,9 @@ struct tegra_xtal_freq { u8 enable_delay; u8 stable_count; u8 active_delay; - u8 xtal_freq_count; + u16 xtal_freq_count; u16 debounce; + u8 pdtrk_count; }; static const struct tegra_xtal_freq tegra_freq_table[] = { @@ -158,6 +597,7 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .active_delay = 0x04, .xtal_freq_count = 0x76, .debounce = 0x7530, + .pdtrk_count = 5, }, { .freq = 13000000, @@ -166,6 +606,7 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .active_delay = 0x05, .xtal_freq_count = 0x7F, .debounce = 0x7EF4, + .pdtrk_count = 5, }, { .freq = 19200000, @@ -174,6 +615,7 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .active_delay = 0x06, .xtal_freq_count = 0xBB, .debounce = 0xBB80, + .pdtrk_count = 7, }, { .freq = 26000000, @@ -182,6 +624,38 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .active_delay = 0x09, .xtal_freq_count = 0xFE, .debounce = 0xFDE8, + .pdtrk_count = 9, + }, +}; + +static const struct tegra_xtal_freq tegra_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, }, }; @@ -192,24 +666,29 @@ static struct tegra_utmip_config utmip_default[] = { .elastic_limit = 16, .term_range_adj = 6, .xcvr_setup = 9, - .xcvr_lsfslew = 1, - .xcvr_lsrslew = 1, + .xcvr_setup_offset = 0, + .xcvr_use_fuses = 1, + .xcvr_lsfslew = 2, + .xcvr_lsrslew = 2, }, [2] = { .hssync_start_delay = 9, .idle_wait_delay = 17, .elastic_limit = 16, .term_range_adj = 6, + .xcvr_setup_offset = 0, + .xcvr_use_fuses = 1, .xcvr_setup = 9, .xcvr_lsfslew = 2, .xcvr_lsrslew = 2, }, }; -static inline bool phy_is_ulpi(struct tegra_usb_phy *phy) -{ - return (phy->instance == 1); -} +struct usb_phy_plat_data usb_phy_data[] = { + { 0, 0, -1, NULL}, + { 0, 0, -1, NULL}, + { 0, 0, -1, NULL}, +}; static int utmip_pad_open(struct tegra_usb_phy *phy) { @@ -239,7 +718,7 @@ static void utmip_pad_close(struct tegra_usb_phy *phy) clk_put(phy->pad_clk); } -static void utmip_pad_power_on(struct tegra_usb_phy *phy) +static int utmip_pad_power_on(struct tegra_usb_phy *phy) { unsigned long val, flags; void __iomem *base = phy->pad_regs; @@ -248,18 +727,23 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy) 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); - } + utmip_pad_count++; + val = readl(base + UTMIP_BIAS_CFG0); + val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + val |= UTMIP_HSSQUELCH_LEVEL(0x2) | UTMIP_HSDISCON_LEVEL(0x1) | + UTMIP_HSDISCON_LEVEL_MSB; +#endif + writel(val, base + UTMIP_BIAS_CFG0); spin_unlock_irqrestore(&utmip_pad_lock, flags); clk_disable(phy->pad_clk); + + return 0; } -static int utmip_pad_power_off(struct tegra_usb_phy *phy) +static int utmip_pad_power_off(struct tegra_usb_phy *phy, bool is_dpd) { unsigned long val, flags; void __iomem *base = phy->pad_regs; @@ -273,9 +757,13 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) spin_lock_irqsave(&utmip_pad_lock, flags); - if (--utmip_pad_count == 0) { + if (--utmip_pad_count == 0 && is_dpd) { val = readl(base + UTMIP_BIAS_CFG0); val |= UTMIP_OTGPD | UTMIP_BIASPD; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + val &= ~(UTMIP_HSSQUELCH_LEVEL(~0) | UTMIP_HSDISCON_LEVEL(~0) | + UTMIP_HSDISCON_LEVEL_MSB); +#endif writel(val, base + UTMIP_BIAS_CFG0); } @@ -288,7 +776,7 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) { - unsigned long timeout = 2000; + unsigned long timeout = 2500; do { if ((readl(reg) & mask) == result) return 0; @@ -302,7 +790,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) { unsigned long val; void __iomem *base = phy->regs; - +#ifdef CONFIG_ARCH_TEGRA_2x_SOC if (phy->instance == 0) { val = readl(base + USB_SUSP_CTRL); val |= USB_SUSP_SET; @@ -320,6 +808,11 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) val |= USB_PORTSC1_PHCD; writel(val, base + USB_PORTSC1); } +#else + val = readl(base + HOSTPC1_DEVLC); + val |= HOSTPC1_DEVLC_PHCD; + writel(val, base + HOSTPC1_DEVLC); +#endif 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__); @@ -342,35 +835,175 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) writel(val, base + USB_SUSP_CTRL); } +#ifdef CONFIG_ARCH_TEGRA_2x_SOC if (phy->instance == 2) { val = readl(base + USB_PORTSC1); val &= ~USB_PORTSC1_PHCD; writel(val, base + USB_PORTSC1); } +#endif if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, - USB_PHY_CLK_VALID)) + USB_PHY_CLK_VALID) < 0) pr_err("%s: timeout waiting for phy to stabilize\n", __func__); } -static int utmi_phy_power_on(struct tegra_usb_phy *phy) +static void vbus_enable(struct tegra_usb_phy *phy) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + int gpio_status; + int gpio = usb_phy_data[phy->instance].vbus_gpio; + + if (gpio == -1) + return; + + gpio_status = gpio_request(gpio,"VBUS_USB"); + if (gpio_status < 0) { + printk("VBUS_USB request GPIO FAILED\n"); + WARN_ON(1); + return; + } + if (gpio < TEGRA_NR_GPIOS) tegra_gpio_enable(gpio); + gpio_status = gpio_direction_output(gpio, 1); + if (gpio_status < 0) { + printk("VBUS_USB request GPIO DIRECTION FAILED \n"); + WARN_ON(1); + return; + } + gpio_set_value_cansleep(gpio, 1); +#else + if (phy->reg_vbus) + regulator_enable(phy->reg_vbus); +#endif +} + +static void vbus_disable(struct tegra_usb_phy *phy) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + int gpio = usb_phy_data[phy->instance].vbus_gpio; + + if (gpio == -1) + return; + + gpio_set_value_cansleep(gpio, 0); + gpio_free(gpio); +#else + if (phy->reg_vbus) + regulator_disable(phy->reg_vbus); +#endif +} + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC +static void utmip_phy_enable_trking_data(struct tegra_usb_phy *phy) +{ + void __iomem *base = phy->pad_regs; + void __iomem *pmc_base = IO_ADDRESS(TEGRA_USB_BASE); + static bool init_done = false; + u32 val; + + /* Should be done only once after system boot */ + if (init_done) + return; + + clk_enable(phy->pad_clk); + /* Bias pad MASTER_ENABLE=1 */ + val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL); + val |= BIAS_MASTER_PROG_VAL; + writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL); + + /* 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 */ + val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL); + val &= ~BIAS_MASTER_PROG_VAL; + writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL); + + /* Wait for 1usec */ + udelay(1); + + /* Bias pad MASTER_ENABLE=1 */ + val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL); + val |= BIAS_MASTER_PROG_VAL; + writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL); + + /* Read RCTRL and TCTRL from UTMIP space */ + val = readl(base + UTMIP_BIAS_STS0); + utmip_rctrl_val = ffz(UTMIP_RCTRL_VAL(val)); + utmip_tctrl_val = 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 = readl(pmc_base + PMC_UTMIP_TERM_PAD_CFG); + val = PMC_TCTRL_VAL(utmip_tctrl_val) | PMC_RCTRL_VAL(utmip_rctrl_val); + writel(val, pmc_base + PMC_UTMIP_TERM_PAD_CFG); + clk_disable(phy->pad_clk); + init_done = true; +} +#endif + +static unsigned int tegra_phy_xcvr_setup_value(struct tegra_utmip_config *cfg) +{ + unsigned long val; + + if (cfg->xcvr_use_fuses) { + val = FUSE_USB_CALIB_XCVR_SETUP( + tegra_fuse_readl(FUSE_USB_CALIB_0)); + 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 { + val = cfg->xcvr_setup; + } + + return val; +} + +static int utmi_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) { unsigned long val; void __iomem *base = phy->regs; + unsigned int xcvr_setup_value; struct tegra_utmip_config *config = phy->config; val = readl(base + USB_SUSP_CTRL); val |= UTMIP_RESET; writel(val, base + USB_SUSP_CTRL); +#ifdef CONFIG_ARCH_TEGRA_2x_SOC if (phy->instance == 0) { val = readl(base + USB1_LEGACY_CTRL); val |= USB1_NO_LEGACY_MODE; writel(val, base + USB1_LEGACY_CTRL); } +#endif val = readl(base + UTMIP_TX_CFG0); - val &= ~UTMIP_FS_PREABMLE_J; + val |= UTMIP_FS_PREABMLE_J; writel(val, base + UTMIP_TX_CFG0); val = readl(base + UTMIP_HSRX_CFG0); @@ -393,6 +1026,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; writel(val, base + UTMIP_MISC_CFG0); +#ifdef CONFIG_ARCH_TEGRA_2x_SOC 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) | @@ -404,6 +1038,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) 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); +#endif if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { val = readl(base + USB_SUSP_CTRL); @@ -413,14 +1048,20 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) utmip_pad_power_on(phy); + xcvr_setup_value = phy->xcvr_setup_value; + 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_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(xcvr_setup_value); + val |= UTMIP_XCVR_SETUP_MSB(XCVR_SETUP_MSB_CALIB(xcvr_setup_value)); val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + val |= UTMIP_XCVR_HSSLEW_MSB(0x8); +#endif writel(val, base + UTMIP_XCVR_CFG0); val = readl(base + UTMIP_XCVR_CFG1); @@ -430,28 +1071,41 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) writel(val, base + UTMIP_XCVR_CFG1); val = readl(base + UTMIP_BAT_CHRG_CFG0); - val &= ~UTMIP_PD_CHRG; + if (phy->mode == TEGRA_USB_PHY_MODE_HOST) + val |= UTMIP_PD_CHRG; + else + 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); + val |= UTMIP_BIAS_PDTRK_COUNT(phy->freq->pdtrk_count); writel(val, base + UTMIP_BIAS_CFG1); - if (phy->instance == 0) { +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + val = readl(base + UTMIP_SPARE_CFG0); + val &= ~FUSE_SETUP_SEL; + writel(val, base + UTMIP_SPARE_CFG0); + + if (phy->instance == 2) { val = readl(base + UTMIP_SPARE_CFG0); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) - val &= ~FUSE_SETUP_SEL; - else - val |= FUSE_SETUP_SEL; + 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); } +#else + 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); +#endif val = readl(base + USB_SUSP_CTRL); val &= ~UTMIP_RESET; @@ -463,29 +1117,202 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD; writel(val, base + USB1_LEGACY_CTRL); +#ifdef CONFIG_ARCH_TEGRA_2x_SOC val = readl(base + USB_SUSP_CTRL); val &= ~USB_SUSP_SET; writel(val, base + USB_SUSP_CTRL); +#endif } utmi_phy_clk_enable(phy); - +#ifdef CONFIG_ARCH_TEGRA_2x_SOC if (phy->instance == 2) { val = readl(base + USB_PORTSC1); val &= ~USB_PORTSC1_PTS(~0); writel(val, base + USB_PORTSC1); } +#else + if (phy->instance == 0) + utmip_phy_enable_trking_data(phy); + + if(phy->instance == 2) { + writel(0, base + ICUSB_CTRL); + } + + if (phy->mode == TEGRA_USB_PHY_MODE_HOST) { + val = readl(base + TEGRA_USB_USBMODE_REG_OFFSET); + writel((val | TEGRA_USB_USBMODE_HOST), + (base + TEGRA_USB_USBMODE_REG_OFFSET)); + } + val = readl(base + HOSTPC1_DEVLC); + val &= ~HOSTPC1_DEVLC_PTS(~0); + val |= HOSTPC1_DEVLC_STS; + writel(val, base + HOSTPC1_DEVLC); +#endif return 0; } -static void utmi_phy_power_off(struct tegra_usb_phy *phy) +#ifndef CONFIG_ARCH_TEGRA_2x_SOC +static void utmip_setup_pmc_wake_detect(struct tegra_usb_phy *phy) { - unsigned long val; + unsigned long val, pmc_pad_cfg_val; + void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); + unsigned int inst = phy->instance; void __iomem *base = phy->regs; + bool port_connected; + enum tegra_usb_phy_port_speed port_speed; - utmi_phy_clk_disable(phy); + /* check for port connect status */ + val = readl(base + USB_PORTSC1); + port_connected = val & USB_PORTSC1_CCS; + + if (!port_connected) + return; + + port_speed = (readl(base + HOSTPC1_DEVLC) >> 25) & + HOSTPC1_DEVLC_PSPD_MASK; + /*Set PMC MASTER bits to do the following + * a. Take over the UTMI drivers + * b. set up such that it will take over resume + * if remote wakeup is detected + * Prepare PMC to take over suspend-wake detect-drive resume until USB + * controller ready + */ + + /* disable master enable in PMC */ + val = readl(pmc_base + PMC_SLEEP_CFG); + val &= ~UTMIP_MASTER_ENABLE(inst); + writel(val, pmc_base + PMC_SLEEP_CFG); + + /* UTMIP_PWR_PX=1 for power savings mode */ + val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG); + val |= UTMIP_PWR(inst); + writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG); + + /* config debouncer */ + val = readl(pmc_base + PMC_USB_DEBOUNCE); + val &= ~UTMIP_LINE_DEB_CNT(~0); + val |= UTMIP_LINE_DEB_CNT(1); + writel(val, pmc_base + PMC_USB_DEBOUNCE); + + /* Make sure nothing is happening on the line with respect to PMC */ + val = readl(pmc_base + PMC_UTMIP_UHSIC_FAKE); + val &= ~USBOP_VAL(inst); + val &= ~USBON_VAL(inst); + writel(val, pmc_base + PMC_UTMIP_UHSIC_FAKE); + + /* Make sure wake value for line is none */ + val = readl(pmc_base + PMC_SLEEPWALK_CFG); + val &= ~UTMIP_LINEVAL_WALK_EN(inst); + writel(val, pmc_base + PMC_SLEEPWALK_CFG); + val = readl(pmc_base + PMC_SLEEP_CFG); + val &= ~UTMIP_WAKE_VAL(inst, ~0); + val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE); + writel(val, pmc_base + PMC_SLEEP_CFG); + + /* turn off pad detectors */ + val = readl(pmc_base + PMC_USB_AO); + val |= (USBOP_VAL_PD(inst) | USBON_VAL_PD(inst)); + writel(val, pmc_base + PMC_USB_AO); + + /* Remove fake values and make synchronizers work a bit */ + val = readl(pmc_base + PMC_UTMIP_UHSIC_FAKE); + val &= ~USBOP_VAL(inst); + val &= ~USBON_VAL(inst); + writel(val, pmc_base + PMC_UTMIP_UHSIC_FAKE); + + /* Enable which type of event can trigger a walk, + in this case usb_line_wake */ + val = readl(pmc_base + PMC_SLEEPWALK_CFG); + val |= UTMIP_LINEVAL_WALK_EN(inst); + writel(val, pmc_base + PMC_SLEEPWALK_CFG); + + /* Enable which type of event can trigger a walk, + * in this case usb_line_wake */ + val = readl(pmc_base + PMC_SLEEPWALK_CFG); + val |= UTMIP_LINEVAL_WALK_EN(inst); + writel(val, pmc_base + PMC_SLEEPWALK_CFG); + + /* Clear the walk pointers and wake alarm */ + val = readl(pmc_base + PMC_TRIGGERS); + val |= UTMIP_CLR_WAKE_ALARM(inst) | UTMIP_CLR_WALK_PTR(inst); + writel(val, pmc_base + PMC_TRIGGERS); + + + /* Capture FS/LS pad configurations */ + pmc_pad_cfg_val = readl(pmc_base + PMC_PAD_CFG); + val = readl(pmc_base + PMC_TRIGGERS); + val |= UTMIP_CAP_CFG(inst); + writel(val, pmc_base + PMC_TRIGGERS); + udelay(1); + pmc_pad_cfg_val = readl(pmc_base + PMC_PAD_CFG); + + /* BIAS MASTER_ENABLE=0 */ + val = readl(pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL); + val &= ~BIAS_MASTER_PROG_VAL; + writel(val, pmc_base + PMC_UTMIP_BIAS_MASTER_CNTRL); + + /* program walk sequence, maintain a J, followed by a driven K + * to signal a resume once an wake event is detected */ + val = readl(pmc_base + PMC_SLEEPWALK_REG(inst)); + val &= ~UTMIP_AP_A; + val |= UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A| UTMIP_AN_A | UTMIP_HIGHZ_A | + UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_AP_B | + UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_AP_C | + UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_AP_D; + writel(val, pmc_base + PMC_SLEEPWALK_REG(inst)); + + if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) { + val = readl(pmc_base + PMC_SLEEPWALK_REG(inst)); + val &= ~(UTMIP_AN_B | UTMIP_HIGHZ_B | UTMIP_AN_C | + UTMIP_HIGHZ_C | UTMIP_AN_D | UTMIP_HIGHZ_D); + writel(val, pmc_base + PMC_SLEEPWALK_REG(inst)); + } else { + val = readl(pmc_base + PMC_SLEEPWALK_REG(inst)); + val &= ~(UTMIP_AP_B | UTMIP_HIGHZ_B | UTMIP_AP_C | + UTMIP_HIGHZ_C | UTMIP_AP_D | UTMIP_HIGHZ_D); + writel(val, pmc_base + PMC_SLEEPWALK_REG(inst)); + } + + /* turn on pad detectors */ + val = readl(pmc_base + PMC_USB_AO); + val &= ~(USBOP_VAL_PD(inst) | USBON_VAL_PD(inst)); + writel(val, pmc_base + PMC_USB_AO); + + /* Add small delay before usb detectors provide stable line values */ + udelay(1); + /* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */ + val = readl(pmc_base + PMC_UTMIP_TERM_PAD_CFG); + val = PMC_TCTRL_VAL(utmip_tctrl_val) | PMC_RCTRL_VAL(utmip_rctrl_val); + writel(val, pmc_base + PMC_UTMIP_TERM_PAD_CFG); + + phy->remote_wakeup = false; + + /* Turn over pad configuration to PMC for line wake events*/ + val = readl(pmc_base + PMC_SLEEP_CFG); + val &= ~UTMIP_WAKE_VAL(inst, ~0); + val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_ANY); + val |= UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst); + val |= UTMIP_MASTER_ENABLE(inst) | UTMIP_FSLS_USE_PMC(inst); + writel(val, pmc_base + PMC_SLEEP_CFG); + + val = readl(base + UTMIP_PMC_WAKEUP0); + val |= EVENT_INT_ENB; + writel(val, base + UTMIP_PMC_WAKEUP0); +} +#endif + +static int utmi_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd) +{ + unsigned long val; + void __iomem *base = phy->regs; + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + if (phy->mode == TEGRA_USB_PHY_MODE_HOST) + utmip_setup_pmc_wake_detect(phy); +#endif if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { val = readl(base + USB_SUSP_CTRL); val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); @@ -493,50 +1320,187 @@ static void utmi_phy_power_off(struct tegra_usb_phy *phy) 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); + if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { + val = readl(base + UTMIP_BAT_CHRG_CFG0); + val |= UTMIP_PD_CHRG; + writel(val, base + UTMIP_BAT_CHRG_CFG0); + } + if (phy->instance != 2) { + 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); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + val = readl(base + UTMIP_BIAS_CFG1); + val |= UTMIP_BIAS_PDTRK_COUNT(0x5); + writel(val, base + UTMIP_BIAS_CFG1); +#endif + + if (phy->hotplug) { + val = readl(base + USB_PORTSC1); + val |= USB_PORTSC1_WKCN; + writel(val, base + USB_PORTSC1); + } + if (phy->instance != 0) { + val = readl(base + UTMIP_BIAS_CFG0); + val |= UTMIP_OTGPD; + writel(val, base + UTMIP_BIAS_CFG0); + } + + utmi_phy_clk_disable(phy); + + if (phy->hotplug) { + val = readl(base + USB_SUSP_CTRL); + val |= USB_PHY_CLK_VALID_INT_ENB; + writel(val, base + USB_SUSP_CTRL); + } else { + val = readl(base + USB_SUSP_CTRL); + val |= UTMIP_RESET; + writel(val, base + USB_SUSP_CTRL); + } + utmip_pad_power_off(phy, true); + return 0; } -static void utmi_phy_preresume(struct tegra_usb_phy *phy) +static void utmip_phy_disable_pmc_bus_ctrl(struct tegra_usb_phy *phy) { +#ifndef CONFIG_ARCH_TEGRA_2x_SOC unsigned long val; + void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); + unsigned int inst = phy->instance; void __iomem *base = phy->regs; + val = readl(pmc_base + PMC_SLEEP_CFG); + val &= ~UTMIP_WAKE_VAL(inst, 0x0); + val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE); + writel(val, pmc_base + PMC_SLEEP_CFG); + + val = readl(pmc_base + PMC_TRIGGERS); + val |= UTMIP_CLR_WAKE_ALARM(inst) | UTMIP_CLR_WALK_PTR(inst); + writel(val, pmc_base + PMC_TRIGGERS); + + val = readl(base + UTMIP_PMC_WAKEUP0); + val &= ~EVENT_INT_ENB; + writel(val, base + UTMIP_PMC_WAKEUP0); + + /* Disable PMC master mode by clearing MASTER_EN */ + val = readl(pmc_base + PMC_SLEEP_CFG); + val &= ~(UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst) | + UTMIP_FSLS_USE_PMC(inst) | UTMIP_MASTER_ENABLE(inst)); + writel(val, pmc_base + PMC_SLEEP_CFG); + + val = readl(pmc_base + PMC_TRIGGERS); + val &= ~UTMIP_CAP_CFG(inst); + writel(val, pmc_base + PMC_TRIGGERS); + + /* turn off pad detectors */ + val = readl(pmc_base + PMC_USB_AO); + val |= (USBOP_VAL_PD(inst) | USBON_VAL_PD(inst)); + writel(val, pmc_base + PMC_USB_AO); + + phy->remote_wakeup = false; +#endif +} + +static int utmi_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + 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); +#else + utmip_phy_disable_pmc_bus_ctrl(phy); +#endif + + return 0; } -static void utmi_phy_postresume(struct tegra_usb_phy *phy) +static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd) { unsigned long val; void __iomem *base = phy->regs; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* check if OBS bus is already enabled */ + val = readl(base + UTMIP_MISC_CFG0); + if (val & UTMIP_DPDM_OBSERVE) { + /* Change the UTMIP OBS bus to drive SE0 */ + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); + val |= UTMIP_DPDM_OBSERVE_SEL_FS_SE0; + writel(val, base + UTMIP_MISC_CFG0); + + /* Wait for 3us(2 LS bit times) */ + udelay (3); + + /* Release UTMIP OBS bus */ + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE; + writel(val, base + UTMIP_MISC_CFG0); + + /* Release DP/DM pulldown for Host mode */ + val = readl(base + UTMIP_MISC_CFG0); + val &= ~(FORCE_PULLDN_DM | FORCE_PULLDN_DP | + COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS); + writel(val, base + UTMIP_MISC_CFG0); + } +#else val = readl(base + UTMIP_TX_CFG0); val &= ~UTMIP_HS_DISCON_DISABLE; writel(val, base + UTMIP_TX_CFG0); +#endif + return 0; +} + +static int uhsic_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd) +{ + struct tegra_uhsic_config *uhsic_config = phy->config; + + if (uhsic_config->postsuspend) + uhsic_config->postsuspend(); + + return 0; +} + +static int uhsic_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd) +{ + struct tegra_uhsic_config *uhsic_config = phy->config; + + if (uhsic_config->preresume) + uhsic_config->preresume(); + + return 0; +} + +static int uhsic_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + unsigned long val; + void __iomem *base = phy->regs; + + 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); + } +#endif + + return 0; } static void utmi_phy_restore_start(struct tegra_usb_phy *phy, enum tegra_usb_phy_port_speed port_speed) { +#ifdef CONFIG_ARCH_TEGRA_2x_SOC unsigned long val; void __iomem *base = phy->regs; @@ -553,10 +1517,56 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy, val |= UTMIP_DPDM_OBSERVE; writel(val, base + UTMIP_MISC_CFG0); udelay(10); +#else + unsigned long val; + void __iomem *base = phy->regs; + void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); + int inst = phy->instance; + + val = readl(pmc_base + UTMIP_UHSIC_STATUS); + /* check whether we wake up from the remote resume */ + if (UTMIP_WALK_PTR_VAL(inst) & val) { + phy->remote_wakeup = true; + } else { + if (!((UTMIP_USBON_VAL(phy->instance) | + UTMIP_USBOP_VAL(phy->instance)) & val)) { + utmip_phy_disable_pmc_bus_ctrl(phy); + } + } + + /* (2LS WAR)is not required for LS and FS devices and is only for HS */ + if ((port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) || + (port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL)) { + /* do not enable the OBS bus */ + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); + writel(val, base + UTMIP_MISC_CFG0); + return; + } + /* Force DP/DM pulldown active for Host mode */ + val = readl(base + UTMIP_MISC_CFG0); + val |= FORCE_PULLDN_DM | FORCE_PULLDN_DP | + COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS; + writel(val, base + UTMIP_MISC_CFG0); + 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_J; + else + val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; + 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); +#endif } static void utmi_phy_restore_end(struct tegra_usb_phy *phy) { +#ifdef CONFIG_ARCH_TEGRA_2x_SOC unsigned long val; void __iomem *base = phy->regs; @@ -564,25 +1574,169 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy) val &= ~UTMIP_DPDM_OBSERVE; writel(val, base + UTMIP_MISC_CFG0); udelay(10); +#else + unsigned long val; + void __iomem *base = phy->regs; + int wait_time_us = 3000; /* FPR should be set by this time */ + + /* check whether we wake up from the remote resume */ + if (phy->remote_wakeup) { + /* wait until FPR bit is set automatically on remote resume */ + do { + val = readl(base + USB_PORTSC1); + udelay(1); + if (wait_time_us == 0) { + utmip_phy_disable_pmc_bus_ctrl(phy); + tegra_usb_phy_postresume(phy, false); + return; + } + wait_time_us--; + } while (!(val & USB_PORTSC1_RESUME)); + /* disable PMC master control */ + utmip_phy_disable_pmc_bus_ctrl(phy); + /* wait for 25 ms to port resume complete */ + msleep(25); + + /* 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 (utmi_wait_register(base + USB_USBSTS, + USB_USBSTS_SRI, USB_USBSTS_SRI) < 0) { + pr_err("%s: timeout waiting for SOF\n", __func__); + } + tegra_usb_phy_postresume(phy, false); + } +#endif +} + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +static void ulpi_set_tristate(bool enable) +{ + int tristate = (enable)? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL; + + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, tristate); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate); +} +#endif + +static void ulpi_phy_reset(void __iomem *base) +{ + unsigned long val; + + val = readl(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel(val, base + USB_SUSP_CTRL); + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + val = readl(base + USB_SUSP_CTRL); + val |= UTMIP_RESET; + writel(val, base + USB_SUSP_CTRL); +#endif +} + +static void ulpi_set_host(void __iomem *base) +{ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + unsigned long val; + + val = readl(base + TEGRA_USB_USBMODE_REG_OFFSET); + val |= TEGRA_USB_USBMODE_HOST; + writel(val, base + TEGRA_USB_USBMODE_REG_OFFSET); + + val = readl(base + HOSTPC1_DEVLC); + val |= HOSTPC1_DEVLC_PTS(2); + writel(val, base + HOSTPC1_DEVLC); +#endif +} + +static void ulpi_set_trimmer(void __iomem *base, u8 data, u8 sdn, u8 dir) +{ + unsigned long val; + + val = ULPI_DATA_TRIMMER_SEL(data); + val |= ULPI_STPDIRNXT_TRIMMER_SEL(sdn); + val |= ULPI_DIR_TRIMMER_SEL(dir); + 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 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 int ulpi_phy_power_on(struct tegra_usb_phy *phy) +static void ulpi_phy_restore_start(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed port_speed) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + unsigned long val; + void __iomem *base = phy->regs; + + /*Tristate ulpi interface before USB controller resume*/ + ulpi_set_tristate(true); + + val = readl(base + ULPI_TIMING_CTRL_0); + val &= ~ULPI_OUTPUT_PINMUX_BYP; + writel(val, base + ULPI_TIMING_CTRL_0); +#endif +} + +static void ulpi_phy_restore_end(struct tegra_usb_phy *phy) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + unsigned long val; + void __iomem *base = phy->regs; + + val = readl(base + ULPI_TIMING_CTRL_0); + val |= ULPI_OUTPUT_PINMUX_BYP; + writel(val, base + ULPI_TIMING_CTRL_0); + + ulpi_set_tristate(false); +#endif +} + +static int ulpi_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) { int ret; unsigned long val; void __iomem *base = phy->regs; +#ifdef CONFIG_ARCH_TEGRA_2x_SOC struct tegra_ulpi_config *config = phy->config; +#endif - gpio_direction_output(config->reset_gpio, 0); - msleep(5); - gpio_direction_output(config->reset_gpio, 1); + if (phy->clk) + clk_enable(phy->clk); - clk_enable(phy->clk); msleep(1); - val = readl(base + USB_SUSP_CTRL); - val |= UHSIC_RESET; - writel(val, base + USB_SUSP_CTRL); + if (!phy->initialized) { + phy->initialized = 1; +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + gpio_direction_output(config->reset_gpio, 0); + msleep(5); + gpio_direction_output(config->reset_gpio, 1); +#endif + } + + ulpi_phy_reset(base); + ulpi_set_host(base); val = readl(base + ULPI_TIMING_CTRL_0); val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; @@ -592,20 +1746,30 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) val |= ULPI_PHY_ENABLE; writel(val, base + USB_SUSP_CTRL); - val = 0; - writel(val, base + ULPI_TIMING_CTRL_1); + val = readl(base + USB_SUSP_CTRL); + val |= USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); - 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); +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, + USB_PHY_CLK_VALID) < 0) + pr_err("%s: timeout waiting for phy to stabilize\n", __func__); - val |= ULPI_DATA_TRIMMER_LOAD; - val |= ULPI_STPDIRNXT_TRIMMER_LOAD; - val |= ULPI_DIR_TRIMMER_LOAD; + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_CLKEN, USB_CLKEN) < 0) + pr_err("%s: timeout waiting for AHB clock\n", __func__); +#else + udelay(100); +#endif + + 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(base, 4, 4, 4); + /* Fix VbusInvalid due to floating VBUS */ ret = otg_io_write(phy->ulpi, 0x40, 0x08); if (ret) { @@ -623,45 +1787,463 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN; writel(val, base + USB_PORTSC1); + return 0; +} + +static int ulpi_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd) +{ + unsigned long val; + void __iomem *base = phy->regs; +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + int ret; + + /* Disable VbusValid, SessEnd comparators */ + ret = otg_io_write(phy->ulpi, 0x00, 0x0D); + if (ret) + pr_err("%s: ulpi write 0x0D failed\n", __func__); + + ret = otg_io_write(phy->ulpi, 0x00, 0x10); + if (ret) + pr_err("%s: ulpi write 0x10 failed\n", __func__); + + /* Disable IdFloat comparator */ + ret = otg_io_write(phy->ulpi, 0x00, 0x19); + if (ret) + pr_err("%s: ulpi write 0x19 failed\n", __func__); + + ret = otg_io_write(phy->ulpi, 0x00, 0x1D); + if (ret) + pr_err("%s: ulpi write 0x1D failed\n", __func__); + + /* 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); + + /* Put the PHY in the low power mode */ + 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 stop\n", __func__); +#else + val = readl(base + HOSTPC1_DEVLC); + val &= ~(HOSTPC1_DEVLC_PHCD); + writel(val, base + HOSTPC1_DEVLC); +#endif + + if(phy->clk) + clk_disable(phy->clk); + + return 0; +} + +static inline void null_phy_set_tristate(bool enable) +{ + int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL; + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, tristate); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate); +#else + 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 null_phy_restore_start(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed port_speed) +{ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + struct tegra_ulpi_config *config = phy->config; + + if (config->phy_restore_start) + config->phy_restore_start(); +#endif +} + +static void null_phy_restore_end(struct tegra_usb_phy *phy) +{ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + int retry = 20000; + + /* disable ULPI pinmux bypass */ + ulpi_pinmux_bypass(phy, false); + + /* driving linestate using GPIO */ + gpio_set_value(TEGRA_GPIO_PO1, 0); + gpio_set_value(TEGRA_GPIO_PO2, 0); + + /* remove ULPI tristate except DIR */ + null_phy_set_tristate(false); + + if (config->phy_restore_end) + config->phy_restore_end(); + + while (retry) { +#if TRIGGER_BY_MDM2AP_ACK + if (gpio_get_value(TEGRA_GPIO_PU5)) /* poll MDM2AP_ACK high */ + break; +#else + if (gpio_get_value(TEGRA_GPIO_PY3)) /* poll STP high */ + break; +#endif + retry--; + } + + if (retry == 0) + pr_info("MDM2AP_ACK timeout\n"); + + /* 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); + + /* remove DIR tristate */ + tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, TEGRA_TRI_NORMAL); +#endif +} + +static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) +{ + const struct tegra_ulpi_trimmer default_trimmer = {0, 0, 4, 4}; + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + static bool cold_boot = true; + + if (!config->trimmer) + config->trimmer = &default_trimmer; + + ulpi_phy_reset(base); + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* remove ULPI PADS CLKEN reset */ val = readl(base + USB_SUSP_CTRL); - val |= USB_SUSP_CLR; + val &= ~ULPI_PADS_CLKEN_RESET; writel(val, base + USB_SUSP_CTRL); - udelay(100); + udelay(10); +#endif + val = readl(base + ULPI_TIMING_CTRL_0); + val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; + writel(val, base + ULPI_TIMING_CTRL_0); + + if (config->pre_phy_on && config->pre_phy_on()) + return -EAGAIN; val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_CLR; + 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->trimmer->shadow_clk_delay); + val |= ULPI_CLOCK_OUT_DELAY(config->trimmer->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->mode == TEGRA_USB_PHY_MODE_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 (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__); + return -ETIMEDOUT; + } + + /* set ULPI trimmers */ + ulpi_set_trimmer(base, config->trimmer->data_trimmer, + config->trimmer->stpdirnxt_trimmer, 1); + + ulpi_set_host(base); + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* 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); +#endif + if (cold_boot) { + val = readl(base + ULPI_TIMING_CTRL_0); + val |= ULPI_CLK_PADOUT_ENA; + writel(val, base + ULPI_TIMING_CTRL_0); + cold_boot = false; + } + + udelay(10); + + if (config->post_phy_on && config->post_phy_on()) + return -EAGAIN; + return 0; } -static void ulpi_phy_power_off(struct tegra_usb_phy *phy) +static int null_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd) { + struct tegra_ulpi_config *config = phy->config; + + if (config->pre_phy_off && config->pre_phy_off()) + return -EAGAIN; + + null_phy_set_tristate(true); + + if (config->post_phy_off && config->post_phy_off()) + return -EAGAIN; + + return 0; +} + +static int null_phy_pre_usbcmd_reset(struct tegra_usb_phy *phy, bool is_dpd) +{ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC 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 + 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); +#endif + return 0; +} + +static int null_phy_post_usbcmd_reset(struct tegra_usb_phy *phy, bool is_dpd) +{ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + unsigned long val; + void __iomem *base = phy->regs; + + 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); +#endif + return 0; +} + +static int uhsic_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_uhsic_config *uhsic_config = phy->config; + + if (uhsic_config->enable_gpio != -1) { + gpio_set_value_cansleep(uhsic_config->enable_gpio, 1); + /* keep hsic reset asserted for 1 ms */ + udelay(1000); + } + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX | + UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE); + val |= UHSIC_RX_SEL; + writel(val, base + UHSIC_PADS_CFG1); + udelay(2); + + val = readl(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel(val, base + USB_SUSP_CTRL); + udelay(30); + + 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(uhsic_config->idle_wait_delay); + val |= UHSIC_ELASTIC_UNDERRUN_LIMIT(uhsic_config->elastic_underrun_limit); + val |= UHSIC_ELASTIC_OVERRUN_LIMIT(uhsic_config->elastic_overrun_limit); + writel(val, base + UHSIC_HSRX_CFG0); + + val = readl(base + UHSIC_HSRX_CFG1); + val |= UHSIC_HS_SYNC_START_DLY(uhsic_config->sync_start_delay); + writel(val, base + UHSIC_HSRX_CFG1); + + 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(2); + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + val = readl(base + USB_PORTSC1); + val &= ~USB_PORTSC1_PTS(~0); + writel(val, base + USB_PORTSC1); + + 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); + } +#endif + 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); + val = readl(base + UHSIC_PADS_CFG0); + val &= ~(UHSIC_TX_RTUNEN); + /* set Rtune impedance to 40 ohm */ + val |= UHSIC_TX_RTUNE(0); + writel(val, base + UHSIC_PADS_CFG0); + + 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__); + return -ETIMEDOUT; + } + + return 0; +} + +static int uhsic_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_uhsic_config *uhsic_config = phy->config; + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPU_STROBE; + val |= UHSIC_RPD_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + + val = readl(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel(val, base + USB_SUSP_CTRL); + udelay(30); + + val = readl(base + USB_SUSP_CTRL); + val &= ~UHSIC_PHY_ENABLE; + writel(val, base + USB_SUSP_CTRL); + + if (uhsic_config->enable_gpio != -1) { + gpio_set_value_cansleep(uhsic_config->enable_gpio, 0); + /* keep hsic reset de-asserted for 1 ms */ + udelay(1000); + } + if (uhsic_config->post_phy_off && uhsic_config->post_phy_off()) + return -EAGAIN; + + return 0; +} + +#ifdef CONFIG_USB_TEGRA_OTG +extern void tegra_otg_check_vbus_detection(void); +#endif + +static irqreturn_t usb_phy_vbus_irq_thr(int irq, void *pdata) +{ + struct tegra_usb_phy *phy = pdata; + + if (!phy->regulator_on) { + regulator_enable(phy->reg_vdd); + phy->regulator_on = 1; + /* + * Optimal time to get the regulator turned on + * before detecting vbus interrupt. + */ + mdelay(15); + } + +#ifdef CONFIG_USB_TEGRA_OTG + tegra_otg_check_vbus_detection(); +#endif + + return IRQ_HANDLED; } struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, - void *config, enum tegra_usb_phy_mode phy_mode) + void *config, enum tegra_usb_phy_mode phy_mode, + enum tegra_usb_phy_type usb_phy_type) { struct tegra_usb_phy *phy; struct tegra_ulpi_config *ulpi_config; unsigned long parent_rate; int i; int err; +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + struct tegra_ulpi_config *uhsic_config; + int reset_gpio, enable_gpio; +#endif - phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); + phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); if (!phy) return ERR_PTR(-ENOMEM); @@ -669,9 +2251,17 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, phy->regs = regs; phy->config = config; phy->mode = phy_mode; + phy->usb_phy_type = usb_phy_type; + phy->initialized = 0; + phy->regulator_on = 0; + phy->power_on = 0; + phy->remote_wakeup = false; + phy->hotplug = 0; + phy->xcvr_setup_value = 0; if (!phy->config) { - if (phy_is_ulpi(phy)) { + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_LINK_ULPI || + phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) { pr_err("%s: ulpi phy configuration missing", __func__); err = -EINVAL; goto err0; @@ -689,10 +2279,19 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, 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->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) { + for (i = 0; i < ARRAY_SIZE(tegra_uhsic_freq_table); i++) { + if (tegra_uhsic_freq_table[i].freq == parent_rate) { + phy->freq = &tegra_uhsic_freq_table[i]; + break; + } + } + } else { + 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) { @@ -701,25 +2300,105 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, 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; + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { + err = utmip_pad_open(phy); + phy->xcvr_setup_value = tegra_phy_xcvr_setup_value(phy->config); + if (err < 0) goto err1; + } else if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_LINK_ULPI) { + ulpi_config = config; + + if (ulpi_config->clk) { + 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; + } + } else { + /* Some USB ULPI chips are not driven by Tegra clocks or PLL */ + phy->clk = NULL; } 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) + } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + else if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) { + uhsic_config = config; + enable_gpio = gpio_request(uhsic_config->enable_gpio, + "uhsic_enable"); + reset_gpio = gpio_request(uhsic_config->reset_gpio, + "uhsic_reset"); + /* hsic enable signal deasserted, hsic reset asserted */ + if (!enable_gpio) + gpio_direction_output(uhsic_config->enable_gpio, + 0 /* deasserted */); + if (!reset_gpio) + gpio_direction_output(uhsic_config->reset_gpio, + 0 /* asserted */); + if (!enable_gpio) + tegra_gpio_enable(uhsic_config->enable_gpio); + if (!reset_gpio) + tegra_gpio_enable(uhsic_config->reset_gpio); + /* keep hsic reset asserted for 1 ms */ + udelay(1000); + /* enable (power on) hsic */ + if (!enable_gpio) + gpio_set_value_cansleep(uhsic_config->enable_gpio, 1); + udelay(1000); + /* deassert reset */ + if (!reset_gpio) + gpio_set_value_cansleep(uhsic_config->reset_gpio, 1); + } +#endif + + phy->reg_vdd = regulator_get(NULL, "avdd_usb"); + if (WARN_ON(IS_ERR_OR_NULL(phy->reg_vdd))) { + pr_err("couldn't get regulator avdd_usb: %ld \n", + PTR_ERR(phy->reg_vdd)); + err = PTR_ERR(phy->reg_vdd); + goto err1; + } + + if (instance == 0 && usb_phy_data[0].vbus_irq) { + err = request_threaded_irq(usb_phy_data[0].vbus_irq, NULL, usb_phy_vbus_irq_thr, IRQF_SHARED, + "usb_phy_vbus", phy); + if (err) { + pr_err("Failed to register IRQ\n"); goto err1; + } } +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* Power-up the VBUS detector for UTMIP PHY */ + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { + writel(readl((IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO)) & + ~(TEGRA_PMC_USB_AO_VBUS_WAKEUP_PD_P0 | TEGRA_PMC_USB_AO_ID_PD_P0), + (IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO)); + + if (usb_phy_data[phy->instance].vbus_reg_supply) { + phy->reg_vbus = regulator_get(NULL, usb_phy_data[phy->instance].vbus_reg_supply); + if (WARN_ON(IS_ERR_OR_NULL(phy->reg_vbus))) { + pr_err("couldn't get regulator vdd_vbus_usb: %ld, instance : %d\n", + PTR_ERR(phy->reg_vbus), phy->instance); + err = PTR_ERR(phy->reg_vbus); + goto err1; + } + } + } + if (instance == 2) { + writel(readl((IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO)) & + (TEGRA_PMC_USB_AO_PD_P2), + (IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO)); + } +#endif + if (((instance == 2) || (instance == 0)) && + (phy->mode == TEGRA_USB_PHY_MODE_HOST)) { + vbus_enable(phy); + } return phy; err1: @@ -730,66 +2409,458 @@ err0: return ERR_PTR(err); } -int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) +int tegra_usb_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) { - if (phy_is_ulpi(phy)) - return ulpi_phy_power_on(phy); - else - return utmi_phy_power_on(phy); + int ret = 0; + + const tegra_phy_fp power_on[] = { + utmi_phy_power_on, + ulpi_phy_power_on, + null_phy_power_on, + uhsic_phy_power_on, + }; + + if (phy->power_on) + return ret; + + if (phy->reg_vdd && !phy->regulator_on) { + regulator_enable(phy->reg_vdd); + phy->regulator_on = 1; + } + + if (power_on[phy->usb_phy_type]) + ret = power_on[phy->usb_phy_type](phy, is_dpd); + + phy->power_on = true; + return ret; } -void tegra_usb_phy_power_off(struct tegra_usb_phy *phy) +void tegra_usb_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd) { - if (phy_is_ulpi(phy)) - ulpi_phy_power_off(phy); - else - utmi_phy_power_off(phy); + const tegra_phy_fp power_off[] = { + utmi_phy_power_off, + ulpi_phy_power_off, + null_phy_power_off, + uhsic_phy_power_off, + }; + + if (!phy->power_on) + return; + + if (power_off[phy->usb_phy_type]) + power_off[phy->usb_phy_type](phy, is_dpd); + + if (phy->reg_vdd && phy->regulator_on && is_dpd) { +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + if (tegra_get_revision() >= TEGRA_REVISION_A03) +#endif + regulator_disable(phy->reg_vdd); + phy->regulator_on = 0; + } + phy->power_on = false; +} + +void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd) +{ + const tegra_phy_fp preresume[] = { + utmi_phy_preresume, + NULL, + NULL, + uhsic_phy_preresume, + }; + + if (preresume[phy->usb_phy_type]) + preresume[phy->usb_phy_type](phy, is_dpd); +} + +void tegra_usb_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd) + +{ + const tegra_phy_fp postsuspend[] = { + NULL, + NULL, + NULL, + uhsic_phy_postsuspend, + }; + + if (postsuspend[phy->usb_phy_type]) + postsuspend[phy->usb_phy_type](phy, is_dpd); } -void tegra_usb_phy_preresume(struct tegra_usb_phy *phy) +void tegra_usb_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd) { - if (!phy_is_ulpi(phy)) - utmi_phy_preresume(phy); + const tegra_phy_fp postresume[] = { + utmi_phy_postresume, + NULL, + NULL, + uhsic_phy_postresume, + }; + + if (postresume[phy->usb_phy_type]) + postresume[phy->usb_phy_type](phy, is_dpd); } -void tegra_usb_phy_postresume(struct tegra_usb_phy *phy) +void tegra_ehci_pre_reset(struct tegra_usb_phy *phy, bool is_dpd) { - if (!phy_is_ulpi(phy)) - utmi_phy_postresume(phy); + const tegra_phy_fp pre_reset[] = { + NULL, + NULL, + null_phy_pre_usbcmd_reset, + NULL, + }; + + if (pre_reset[phy->usb_phy_type]) + pre_reset[phy->usb_phy_type](phy, is_dpd); +} + +void tegra_ehci_post_reset(struct tegra_usb_phy *phy, bool is_dpd) +{ + const tegra_phy_fp post_reset[] = { + NULL, + NULL, + null_phy_post_usbcmd_reset, + NULL, + }; + + if (post_reset[phy->usb_phy_type]) + post_reset[phy->usb_phy_type](phy, is_dpd); } 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); + const tegra_phy_restore_start_fp phy_restore_start[] = { + utmi_phy_restore_start, + ulpi_phy_restore_start, + null_phy_restore_start, + NULL, + }; + + if (phy_restore_start[phy->usb_phy_type]) + phy_restore_start[phy->usb_phy_type](phy, port_speed); } void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) { - if (!phy_is_ulpi(phy)) - utmi_phy_restore_end(phy); + const tegra_phy_restore_end_fp phy_restore_end[] = { + utmi_phy_restore_end, + ulpi_phy_restore_end, + null_phy_restore_end, + NULL, + }; + + if (phy_restore_end[phy->usb_phy_type]) + phy_restore_end[phy->usb_phy_type](phy); } void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) { - if (!phy_is_ulpi(phy)) + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) utmi_phy_clk_disable(phy); } void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy) { - if (!phy_is_ulpi(phy)) + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) utmi_phy_clk_enable(phy); } void tegra_usb_phy_close(struct tegra_usb_phy *phy) { - if (phy_is_ulpi(phy)) - clk_put(phy->clk); - else + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { utmip_pad_close(phy); + utmip_phy_disable_pmc_bus_ctrl(phy); + } + else if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_LINK_ULPI && phy->clk) + clk_put(phy->clk); + if (phy->mode == TEGRA_USB_PHY_MODE_HOST) { + vbus_disable(phy); + } clk_disable(phy->pll_u); clk_put(phy->pll_u); + if (phy->reg_vbus) + regulator_put(phy->reg_vbus); + if (phy->reg_vdd) + regulator_put(phy->reg_vdd); + if (phy->instance == 0 && usb_phy_data[0].vbus_irq) + free_irq(usb_phy_data[0].vbus_irq, phy); kfree(phy); } + +int tegra_usb_phy_bus_connect(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_uhsic_config *uhsic_config = phy->config; + + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) { +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* 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); +#endif + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_DETECT_SHORT_CONNECT; + writel(val, base + UHSIC_MISC_CFG0); + udelay(1); + + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_FORCE_XCVR_MODE; + writel(val, base + UHSIC_MISC_CFG0); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPD_STROBE; +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + val |= UHSIC_RPU_STROBE; +#endif + writel(val, base + UHSIC_PADS_CFG1); + + if (uhsic_config->usb_phy_ready && + uhsic_config->usb_phy_ready()) + return -EAGAIN; + + if (utmi_wait_register(base + UHSIC_STAT_CFG0, UHSIC_CONNECT_DETECT, UHSIC_CONNECT_DETECT) < 0) { + pr_err("%s: timeout waiting for hsic connect detect\n", __func__); + return -ETIMEDOUT; + } + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(2), USB_PORTSC1_LS(2)) < 0) { + pr_err("%s: timeout waiting for dplus state\n", __func__); + return -ETIMEDOUT; + } +#endif + } + + return 0; +} + +int tegra_usb_phy_bus_reset(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) { +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + val = readl(base + USB_PORTSC1); + val |= USB_PORTSC1_PTC(5); + writel(val, base + USB_PORTSC1); + udelay(2); + + val = readl(base + USB_PORTSC1); + val &= ~USB_PORTSC1_PTC(~0); + writel(val, base + USB_PORTSC1); + udelay(2); +#endif + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(0), 0) < 0) { + pr_err("%s: timeout waiting for SE0\n", __func__); + return -ETIMEDOUT; + } +#endif + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_CCS, USB_PORTSC1_CCS) < 0) { + pr_err("%s: timeout waiting for connection status\n", __func__); + return -ETIMEDOUT; + } + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_PSPD(2), USB_PORTSC1_PSPD(2)) < 0) { + pr_err("%s: timeout waiting hsic high speed configuration\n", __func__); + return -ETIMEDOUT; + } +#endif + val = readl(base + USB_USBCMD); + val &= ~USB_USBCMD_RS; + writel(val, base + USB_USBCMD); + + if (utmi_wait_register(base + USB_USBSTS, USB_USBSTS_HCH, USB_USBSTS_HCH) < 0) { + pr_err("%s: timeout waiting for stopping the controller\n", __func__); + return -ETIMEDOUT; + } + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPU_STROBE; + val |= UHSIC_RPD_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + + mdelay(50); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPD_STROBE; + val |= UHSIC_RPU_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + + val = readl(base + USB_USBCMD); + val |= USB_USBCMD_RS; + writel(val, base + USB_USBCMD); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPU_STROBE; + writel(val, base + UHSIC_PADS_CFG1); + + if (utmi_wait_register(base + USB_USBCMD, USB_USBCMD_RS, USB_USBCMD_RS) < 0) { + pr_err("%s: timeout waiting for starting the controller\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +int tegra_usb_phy_bus_idle(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_uhsic_config *uhsic_config = phy->config; + + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) { +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + /* 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); +#endif + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_DETECT_SHORT_CONNECT; + writel(val, base + UHSIC_MISC_CFG0); + udelay(1); + + val = readl(base + UHSIC_MISC_CFG0); + val |= UHSIC_FORCE_XCVR_MODE; + writel(val, base + UHSIC_MISC_CFG0); + + val = readl(base + UHSIC_PADS_CFG1); + val &= ~UHSIC_RPD_STROBE; +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + val |= UHSIC_RPU_STROBE; +#endif + writel(val, base + UHSIC_PADS_CFG1); + + if (uhsic_config->usb_phy_ready && + uhsic_config->usb_phy_ready()) + return -EAGAIN; + } + return 0; +} + +bool tegra_usb_phy_is_device_connected(struct tegra_usb_phy *phy) +{ + void __iomem *base = phy->regs; + + if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) { + if (!((readl(base + UHSIC_STAT_CFG0) & UHSIC_CONNECT_DETECT) == UHSIC_CONNECT_DETECT)) { + pr_err("%s: hsic no device connection\n", __func__); + return false; + } +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(2), USB_PORTSC1_LS(2)) < 0) { + pr_err("%s: timeout waiting for dplus state\n", __func__); + return false; + } +#endif + } + return true; +} + +bool tegra_usb_phy_charger_detect(struct tegra_usb_phy *phy) +{ + unsigned long val; + void __iomem *base = phy->regs; + bool status; + + if (phy->usb_phy_type != TEGRA_USB_PHY_TYPE_UTMIP) + { + /* Charger detection is not there for ULPI + * return Charger not available */ + return false; + } + + /* 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; +} + +int __init tegra_usb_phy_init(struct usb_phy_plat_data *pdata, int size) +{ + if (pdata) { + int i; + + for (i = 0; i < size; i++, pdata++) { + usb_phy_data[pdata->instance].instance = pdata->instance; + usb_phy_data[pdata->instance].vbus_irq = pdata->vbus_irq; + usb_phy_data[pdata->instance].vbus_gpio = pdata->vbus_gpio; + usb_phy_data[pdata->instance].vbus_reg_supply = pdata->vbus_reg_supply; + } + } + + return 0; +} + +/* disable walk and wake events after resume from LP0 */ +bool tegra_usb_phy_is_remotewake_detected(struct tegra_usb_phy *phy) +{ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); + void __iomem *base = phy->regs; + unsigned int inst = phy->instance; + u32 val; + + val = readl(base + UTMIP_PMC_WAKEUP0); + if (val & EVENT_INT_ENB) { + val = readl(pmc_base + UTMIP_UHSIC_STATUS); + if (UTMIP_WAKE_ALARM(inst) & val) { + val = readl(pmc_base + PMC_SLEEP_CFG); + val &= ~UTMIP_WAKE_VAL(inst, 0x0); + val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE); + writel(val, pmc_base + PMC_SLEEP_CFG); + + val = readl(pmc_base + PMC_TRIGGERS); + val |= UTMIP_CLR_WAKE_ALARM(inst) | + UTMIP_CLR_WALK_PTR(inst); + writel(val, pmc_base + PMC_TRIGGERS); + + val = readl(base + UTMIP_PMC_WAKEUP0); + val &= ~EVENT_INT_ENB; + writel(val, base + UTMIP_PMC_WAKEUP0); + phy->remote_wakeup = true; + return true; + } + } +#endif + return false; +} + |