| /* |
| * arch/arm/mach-tegra/usb_phy.c |
| * |
| * Copyright (C) 2010 Google, Inc. |
| * Copyright (C) 2010 - 2011 NVIDIA Corporation |
| * |
| * Author: |
| * Erik Gilling <konkers@google.com> |
| * Benoit Goby <benoit@android.com> |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #include <linux/resource.h> |
| #include <linux/delay.h> |
| #include <linux/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/otg.h> |
| #include <linux/usb/ulpi.h> |
| #include <linux/mfd/tps6591x.h> |
| #include <asm/mach-types.h> |
| #include <mach/board-grouper-misc.h> |
| #include <mach/usb_phy.h> |
| #include <mach/iomap.h> |
| #include <mach/pinmux.h> |
| #include "fuse.h" |
| #include "board-grouper.h" |
| #include "baseband-xmm-power.h" |
| |
| #define TPS6591X_GPIO_BASE TEGRA_NR_GPIOS |
| #define AC_PRESENT_GPIO (TPS6591X_GPIO_BASE + TPS6591X_GPIO_GP4) |
| |
| #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) |
| #define USB_PORTSC1_PSPD(x) (((x) & 0x3) << 26) |
| #define USB_PORTSC1_PHCD (1 << 23) |
| #define USB_PORTSC1_WKOC (1 << 22) |
| #define USB_PORTSC1_WKDS (1 << 21) |
| #define USB_PORTSC1_WKCN (1 << 20) |
| #define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) |
| #define USB_PORTSC1_PP (1 << 12) |
| #define USB_PORTSC1_LS(x) (((x) & 0x3) << 10) |
| #define USB_PORTSC1_SUSP (1 << 7) |
| #define USB_PORTSC1_PE (1 << 2) |
| #define USB_PORTSC1_CCS (1 << 0) |
| |
| #define USB_SUSP_CTRL 0x400 |
| #define USB_WAKE_ON_CNNT_EN_DEV (1 << 3) |
| #define USB_WAKE_ON_DISCON_EN_DEV (1 << 4) |
| #define USB_SUSP_CLR (1 << 5) |
| #define USB_CLKEN (1 << 6) |
| #define USB_PHY_CLK_VALID (1 << 7) |
| #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) |
| #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 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_BIAS_CFG0 0x80c |
| #define UTMIP_OTGPD (1 << 11) |
| #define UTMIP_BIASPD (1 << 10) |
| |
| #define UTMIP_HSRX_CFG0 0x810 |
| #define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) |
| #define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15) |
| |
| #define UTMIP_HSRX_CFG1 0x814 |
| #define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) |
| |
| #define UTMIP_TX_CFG0 0x820 |
| #define UTMIP_FS_PREABMLE_J (1 << 19) |
| #define UTMIP_HS_DISCON_DISABLE (1 << 8) |
| |
| #define UTMIP_MISC_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) |
| #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_TX_CFG0 0x810 |
| #define UHSIC_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 6) |
| |
| #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 USB_TXFILLTUNING 0x154 |
| #define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16) |
| #define USB_FIFO_TXFILL_MASK 0x1f0000 |
| |
| #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_LS(x) (((x) & 0x3) << 10) |
| #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 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_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) |
| #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 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_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_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 UHSIC_MASTER_ENABLE_P0 (1 << 24) |
| #define UHSIC_WAKE_VAL_P0(x) (((x) & 0xf) << 28) |
| |
| #define PMC_USB_AO 0xf0 |
| #define PMC_POWER_DOWN_MASK 0xffff |
| #define HSIC_RESERVED_P0 (3 << 14) |
| #define HSIC_STOBE_VAL_PD_P0 (1 << 13) |
| #define HSIC_DATA_VAL_PD_P0 (1 << 12) |
| #define USB_ID_PD(inst) (1 << ((4*(inst))+3)) |
| #define VBUS_WAKEUP_PD(inst) (1 << ((4*(inst))+2)) |
| #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) |
| |
| #define UHSIC_SLEEPWALK_REG 0x210 |
| #define UHSIC_DATA_RPD_D (1 << 25) |
| #define UHSIC_STRB_RPD_D (1 << 24) |
| #define UHSIC_DATA_RPD_C (1 << 17) |
| #define UHSIC_STRB_RPD_C (1 << 16) |
| #define UHSIC_DATA_RPD_B (1 << 9) |
| #define UHSIC_STRB_RPD_B (1 << 8) |
| #define UHSIC_DATA_RPD_A (1 << 1) |
| #define UHSIC_STRB_RPD_A (1 << 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_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 |
| |
| #define CONNECT_DETECT_TIMEOUT 25000 |
| |
| #define AHB_MEM_PREFETCH_CFG3 0xe0 |
| #define AHB_MEM_PREFETCH_CFG4 0xe4 |
| #define AHB_MEM_PREFETCH_CFG1 0xec |
| #define AHB_MEM_PREFETCH_CFG2 0xf0 |
| #define PREFETCH_ENB (1 << 31) |
| |
| static DEFINE_SPINLOCK(utmip_pad_lock); |
| static int utmip_pad_count; |
| |
| struct tegra_xtal_freq { |
| int freq; |
| u8 enable_delay; |
| u8 stable_count; |
| u8 active_delay; |
| u16 xtal_freq_count; |
| u16 debounce; |
| u8 pdtrk_count; |
| }; |
| |
| static const struct tegra_xtal_freq tegra_freq_table[] = { |
| { |
| .freq = 12000000, |
| .enable_delay = 0x02, |
| .stable_count = 0x2F, |
| .active_delay = 0x04, |
| .xtal_freq_count = 0x76, |
| .debounce = 0x7530, |
| .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 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, |
| }, |
| }; |
| |
| static struct tegra_utmip_config utmip_default[] = { |
| [0] = { |
| .hssync_start_delay = 9, |
| .idle_wait_delay = 17, |
| .elastic_limit = 16, |
| .term_range_adj = 6, |
| .xcvr_setup = 9, |
| .xcvr_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, |
| }, |
| }; |
| |
| 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) |
| { |
| phy->pad_clk = clk_get_sys("utmip-pad", NULL); |
| if (IS_ERR(phy->pad_clk)) { |
| pr_err("%s: can't get utmip pad clock\n", __func__); |
| return PTR_ERR(phy->pad_clk); |
| } |
| |
| if (phy->instance == 0) { |
| phy->pad_regs = phy->regs; |
| } else { |
| phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE); |
| if (!phy->pad_regs) { |
| pr_err("%s: can't remap usb registers\n", __func__); |
| clk_put(phy->pad_clk); |
| return -ENOMEM; |
| } |
| } |
| return 0; |
| } |
| |
| static void utmip_pad_close(struct tegra_usb_phy *phy) |
| { |
| if (phy->instance != 0) |
| iounmap(phy->pad_regs); |
| clk_put(phy->pad_clk); |
| } |
| |
| static int utmip_pad_power_on(struct tegra_usb_phy *phy) |
| { |
| unsigned long val, flags; |
| void __iomem *base = phy->pad_regs; |
| |
| clk_enable(phy->pad_clk); |
| |
| spin_lock_irqsave(&utmip_pad_lock, flags); |
| |
| 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, bool is_dpd) |
| { |
| unsigned long val, flags; |
| void __iomem *base = phy->pad_regs; |
| |
| if (!utmip_pad_count) { |
| pr_err("%s: utmip pad already powered off\n", __func__); |
| return -EINVAL; |
| } |
| |
| clk_enable(phy->pad_clk); |
| |
| spin_lock_irqsave(&utmip_pad_lock, flags); |
| |
| if (--utmip_pad_count == 0 && 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); |
| } |
| |
| spin_unlock_irqrestore(&utmip_pad_lock, flags); |
| |
| clk_disable(phy->pad_clk); |
| |
| return 0; |
| } |
| |
| static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) |
| { |
| unsigned long timeout = 2500; |
| do { |
| if ((readl(reg) & mask) == result) |
| return 0; |
| udelay(1); |
| timeout--; |
| } while (timeout); |
| return -1; |
| } |
| |
| static int utmi_wait_register_timeout(void __iomem *reg, u32 mask, u32 result, |
| unsigned long timeout) |
| { |
| do { |
| if ((readl(reg) & mask) == result) |
| return 0; |
| udelay(1); |
| timeout--; |
| } while (timeout); |
| return -1; |
| } |
| |
| static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) |
| { |
| unsigned long val; |
| void __iomem *base = phy->regs; |
| |
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC |
| if (phy->instance == 0) { |
| val = readl(base + USB_SUSP_CTRL); |
| val |= USB_SUSP_SET; |
| writel(val, base + USB_SUSP_CTRL); |
| |
| udelay(10); |
| |
| val = readl(base + USB_SUSP_CTRL); |
| val &= ~USB_SUSP_SET; |
| writel(val, base + USB_SUSP_CTRL); |
| } |
| |
| if (phy->instance == 2) { |
| val = readl(base + USB_PORTSC1); |
| val |= USB_PORTSC1_PHCD; |
| writel(val, base + USB_PORTSC1); |
| } |
| #else |
| val = readl(base + HOSTPC1_DEVLC); |
| val |= HOSTPC1_DEVLC_PHCD; |
| writel(val, base + HOSTPC1_DEVLC); |
| #endif |
| 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); |
| } |
| |
| if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0) |
| pr_err("%s: timeout waiting for phy to stabilize\n", __func__); |
| } |
| |
| static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) |
| { |
| unsigned long val; |
| void __iomem *base = phy->regs; |
| |
| if (phy->instance == 0) { |
| val = readl(base + USB_SUSP_CTRL); |
| val |= USB_SUSP_CLR; |
| writel(val, base + USB_SUSP_CTRL); |
| |
| udelay(10); |
| |
| val = readl(base + USB_SUSP_CTRL); |
| val &= ~USB_SUSP_CLR; |
| writel(val, base + USB_SUSP_CTRL); |
| } |
| |
| #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) < 0) |
| pr_err("%s: timeout waiting for phy to stabilize\n", __func__); |
| } |
| |
| 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_PMC_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; |
| } |
| |
| static void utmip_powerdown_pmc_wake_detect(struct tegra_usb_phy *phy) |
| { |
| unsigned long val; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| unsigned int inst = phy->instance; |
| |
| /* power down UTMIP interfaces */ |
| val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG); |
| val |= UTMIP_PWR(inst); |
| writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG); |
| |
| /* setup sleep walk usb controller */ |
| val = UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A | UTMIP_HIGHZ_A | |
| UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_HIGHZ_B | |
| UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_HIGHZ_C | |
| UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_HIGHZ_D; |
| writel(val, pmc_base + PMC_SLEEPWALK_REG(inst)); |
| |
| /* 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); |
| |
| /* Turn over pad configuration to PMC */ |
| val = readl(pmc_base + PMC_SLEEP_CFG); |
| val &= ~UTMIP_WAKE_VAL(inst, ~0); |
| val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE) | |
| 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); |
| } |
| |
| static void utmip_powerup_pmc_wake_detect(struct tegra_usb_phy *phy) |
| { |
| unsigned long val; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| unsigned int inst = phy->instance; |
| |
| /* 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); |
| mdelay(1); |
| } |
| |
| static void uhsic_powerdown_pmc_wake_detect(struct tegra_usb_phy *phy) |
| { |
| unsigned long val; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| |
| /* turn on pad detectors for HSIC*/ |
| val = readl(pmc_base + PMC_USB_AO); |
| val |= (HSIC_RESERVED_P0 | HSIC_STOBE_VAL_PD_P0 | HSIC_DATA_VAL_PD_P0); |
| writel(val, pmc_base + PMC_USB_AO); |
| |
| /* enable pull downs on HSIC PMC */ |
| val = UHSIC_STRB_RPD_A | UHSIC_DATA_RPD_A | UHSIC_STRB_RPD_B | |
| UHSIC_DATA_RPD_B | UHSIC_STRB_RPD_C | UHSIC_DATA_RPD_C | |
| UHSIC_STRB_RPD_D | UHSIC_DATA_RPD_D; |
| writel(val, pmc_base + UHSIC_SLEEPWALK_REG); |
| |
| /* Turn over pad configuration to PMC */ |
| val = readl(pmc_base + PMC_SLEEP_CFG); |
| val &= ~UHSIC_WAKE_VAL_P0(~0); |
| val |= UHSIC_WAKE_VAL_P0(WAKE_VAL_NONE) | UHSIC_MASTER_ENABLE_P0; |
| writel(val, pmc_base + PMC_SLEEP_CFG); |
| } |
| |
| static void uhsic_powerup_pmc_wake_detect(struct tegra_usb_phy *phy) |
| { |
| unsigned long val; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| |
| /* turn on pad detectors for HSIC*/ |
| val = readl(pmc_base + PMC_USB_AO); |
| val &= ~(HSIC_RESERVED_P0 | HSIC_STOBE_VAL_PD_P0 | HSIC_DATA_VAL_PD_P0); |
| writel(val, pmc_base + PMC_USB_AO); |
| |
| /* Disable PMC master mode by clearing MASTER_EN */ |
| val = readl(pmc_base + PMC_SLEEP_CFG); |
| val &= ~(UHSIC_MASTER_ENABLE_P0); |
| writel(val, pmc_base + PMC_SLEEP_CFG); |
| mdelay(1); |
| } |
| #endif |
| |
| static unsigned int tegra_phy_xcvr_setup_value(struct tegra_utmip_config *cfg) |
| { |
| signed 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 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_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; |
| writel(val, base + UTMIP_TX_CFG0); |
| |
| val = readl(base + UTMIP_HSRX_CFG0); |
| val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0)); |
| val |= UTMIP_IDLE_WAIT(config->idle_wait_delay); |
| val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit); |
| writel(val, base + UTMIP_HSRX_CFG0); |
| |
| val = readl(base + UTMIP_HSRX_CFG1); |
| val &= ~UTMIP_HS_SYNC_START_DLY(~0); |
| val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay); |
| writel(val, base + UTMIP_HSRX_CFG1); |
| |
| val = readl(base + UTMIP_DEBOUNCE_CFG0); |
| val &= ~UTMIP_BIAS_DEBOUNCE_A(~0); |
| val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce); |
| writel(val, base + UTMIP_DEBOUNCE_CFG0); |
| |
| val = readl(base + UTMIP_MISC_CFG0); |
| val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; |
| writel(val, base + UTMIP_MISC_CFG0); |
| |
| #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) | |
| UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); |
| writel(val, base + UTMIP_MISC_CFG1); |
| |
| val = readl(base + UTMIP_PLL_CFG1); |
| val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); |
| val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | |
| UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); |
| writel(val, base + UTMIP_PLL_CFG1); |
| #endif |
| |
| if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { |
| val = readl(base + USB_SUSP_CTRL); |
| val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV); |
| writel(val, base + USB_SUSP_CTRL); |
| } |
| |
| utmip_pad_power_on(phy); |
| |
| xcvr_setup_value = phy->xcvr_setup_value; |
| |
| 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(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(0x0); |
| #endif |
| writel(val, base + UTMIP_XCVR_CFG0); |
| |
| val = readl(base + UTMIP_XCVR_CFG1); |
| val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN | |
| UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0)); |
| val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj); |
| writel(val, base + UTMIP_XCVR_CFG1); |
| |
| val = readl(base + UTMIP_BAT_CHRG_CFG0); |
| 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(phy->freq->pdtrk_count); |
| writel(val, base + UTMIP_BIAS_CFG1); |
| |
| #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); |
| val |= FUSE_SETUP_SEL; |
| writel(val, base + UTMIP_SPARE_CFG0); |
| |
| 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; |
| writel(val, base + USB_SUSP_CTRL); |
| |
| if (phy->instance == 0) { |
| val = readl(base + USB1_LEGACY_CTRL); |
| val &= ~USB1_VBUS_SENSE_CTL_MASK; |
| val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD; |
| writel(val, base + USB1_LEGACY_CTRL); |
| |
| #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); |
| |
| if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) |
| utmip_powerup_pmc_wake_detect(phy); |
| #endif |
| |
| return 0; |
| } |
| |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| static void utmip_setup_pmc_wake_detect(struct tegra_usb_phy *phy) |
| { |
| 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; |
| |
| /* 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_AN_B | |
| UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_AP_C | UTMIP_AN_C | |
| UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_AP_D | UTMIP_AN_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 */ |
| mdelay(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); |
| else |
| utmip_powerdown_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); |
| val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5); |
| writel(val, base + USB_SUSP_CTRL); |
| } |
| |
| 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->hotplug) { |
| 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); |
| |
| #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); |
| |
| utmip_pad_power_off(phy, is_dpd); |
| return 0; |
| } |
| |
| 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 void utmi_phy_enable_obs_bus(struct tegra_usb_phy *phy, |
| enum tegra_usb_phy_port_speed port_speed) |
| { |
| unsigned long val; |
| void __iomem *base = phy->regs; |
| |
| /* (2LS WAR)is not required for LS and FS devices and is only for HS */ |
| if (port_speed != TEGRA_USB_PHY_PORT_SPEED_HIGH) { |
| /* 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; |
| } |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| /* 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); |
| #endif |
| 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); |
| } |
| |
| static void utmi_phy_disable_obs_bus(struct tegra_usb_phy *phy) |
| { |
| unsigned long val; |
| void __iomem *base = phy->regs; |
| |
| /* 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); |
| |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| /* 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); |
| #endif |
| } |
| } |
| |
| |
| static int utmi_phy_preresume(struct tegra_usb_phy *phy, bool remote_wakeup) |
| { |
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC |
| unsigned long val; |
| void __iomem *base = phy->regs; |
| enum tegra_usb_phy_port_speed port_speed; |
| |
| val = readl(base + UTMIP_TX_CFG0); |
| val |= UTMIP_HS_DISCON_DISABLE; |
| writel(val, base + UTMIP_TX_CFG0); |
| |
| port_speed = (readl(base + USB_PORTSC1) >> 26) & 0x3; |
| if (remote_wakeup && (port_speed == TEGRA_USB_PHY_PORT_SPEED_HIGH)) { |
| 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 controller\n", __func__); |
| return -ETIMEDOUT; |
| } |
| } |
| utmi_phy_enable_obs_bus(phy, port_speed); |
| |
| #else |
| unsigned long val; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| unsigned int inst = phy->instance; |
| void __iomem *base = phy->regs; |
| enum tegra_usb_phy_port_speed port_speed; |
| |
| val = readl(pmc_base + PMC_SLEEP_CFG); |
| if (val & UTMIP_MASTER_ENABLE(inst)) { |
| if (!remote_wakeup) |
| utmip_phy_disable_pmc_bus_ctrl(phy); |
| } else { |
| port_speed = (readl(base + HOSTPC1_DEVLC) >> 25) & |
| HOSTPC1_DEVLC_PSPD_MASK; |
| utmi_phy_enable_obs_bus(phy, port_speed); |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd) |
| { |
| unsigned long val; |
| void __iomem *base = phy->regs; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| unsigned int inst = phy->instance; |
| |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| val = readl(pmc_base + PMC_SLEEP_CFG); |
| /* if PMC is not disabled by now then disable it */ |
| if (val & UTMIP_MASTER_ENABLE(inst)) { |
| utmip_phy_disable_pmc_bus_ctrl(phy); |
| } |
| utmi_phy_disable_obs_bus(phy); |
| #else |
| val = readl(base + USB_USBCMD); |
| if (val & USB_USBCMD_RS) { |
| 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 controller\n", __func__); |
| return -ETIMEDOUT; |
| } |
| } |
| writel(val, base + USB_USBCMD); |
| utmi_phy_disable_obs_bus(phy); |
| val = readl(base + USB_USBCMD); |
| val |= USB_USBCMD_RS; |
| writel(val, base + USB_USBCMD); |
| 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 remote_wakeup) |
| { |
| 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) |
| { |
| 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); |
| } |
| |
| 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; |
| |
| val = readl(base + UTMIP_MISC_CFG0); |
| val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); |
| if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) |
| val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; |
| else |
| val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; |
| writel(val, base + UTMIP_MISC_CFG0); |
| udelay(1); |
| |
| val = readl(base + UTMIP_MISC_CFG0); |
| val |= UTMIP_DPDM_OBSERVE; |
| writel(val, base + UTMIP_MISC_CFG0); |
| udelay(10); |
| #else |
| unsigned long val; |
| 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); |
| } |
| } |
| utmi_phy_enable_obs_bus(phy, port_speed); |
| #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; |
| |
| val = readl(base + UTMIP_MISC_CFG0); |
| 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)); |
| /* wait for 25 ms to port resume complete */ |
| msleep(25); |
| /* disable PMC master control */ |
| utmip_phy_disable_pmc_bus_ctrl(phy); |
| |
| /* 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 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 |
| |
| if (phy->clk) |
| clk_enable(phy->clk); |
| |
| msleep(1); |
| |
| 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; |
| 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); |
| |
| #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__); |
| |
| 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) { |
| pr_err("%s: ulpi write failed\n", __func__); |
| return ret; |
| } |
| |
| ret = otg_io_write(phy->ulpi, 0x80, 0x0B); |
| if (ret) { |
| pr_err("%s: ulpi write failed\n", __func__); |
| return ret; |
| } |
| |
| val = readl(base + USB_PORTSC1); |
| val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN; |
| writel(val, base + USB_PORTSC1); |
| |
| 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_UDA, tristate); |
| tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate); |
| tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, 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) |
| { |
| struct tegra_ulpi_config *config = phy->config; |
| |
| if (config->phy_restore_start) |
| config->phy_restore_start(); |
| } |
| |
| static void null_phy_restore_end(struct tegra_usb_phy *phy) |
| { |
| unsigned long val; |
| void __iomem *base = phy->regs; |
| struct tegra_ulpi_config *config = phy->config; |
| |
| /* disable ULPI pinmux bypass */ |
| ulpi_pinmux_bypass(phy, false); |
| |
| /* driving linestate using GPIO */ |
| gpio_set_value(config->ulpi_d0_gpio, 0); |
| gpio_set_value(config->ulpi_d1_gpio, 0); |
| |
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC |
| /* driving DIR high */ |
| gpio_set_value(config->ulpi_dir_gpio, 1); |
| #endif |
| |
| /* remove ULPI tristate */ |
| null_phy_set_tristate(false); |
| |
| if (config->phy_restore_end) |
| config->phy_restore_end(); |
| |
| if (gpio_is_valid(config->phy_restore_gpio)) { |
| int phy_restore_gpio = config->phy_restore_gpio; |
| int retry = 20000; |
| |
| while (retry) { |
| /* poll phy_restore_gpio high */ |
| if (gpio_get_value(phy_restore_gpio)) |
| break; |
| retry--; |
| } |
| |
| if (retry == 0) |
| pr_info("phy_restore_gpio 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); |
| |
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC |
| udelay(5); /* wait for CLK stabilize */ |
| |
| /* enable ULPI pinmux bypass */ |
| ulpi_pinmux_bypass(phy, true); |
| #else |
| /* 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 &= ~ULPI_PADS_CLKEN_RESET; |
| writel(val, base + USB_SUSP_CTRL); |
| 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 |= 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; |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| val &= ~ULPI_SHADOW_CLK_SEL; |
| val &= ~ULPI_LBK_PAD_EN; |
| #else |
| val |= ULPI_SHADOW_CLK_SEL; |
| val |= ULPI_LBK_PAD_EN; |
| #endif |
| 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 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; |
| |
| 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; |
| |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| uhsic_powerup_pmc_wake_detect(phy); |
| #endif |
| |
| if (uhsic_config->enable_gpio != -1) { |
| baseband_xmm_enable_hsic_power(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); |
| |
| #ifdef CONFIG_ARCH_TEGRA_3x_SOC |
| /* WAR HSIC TX */ |
| val = readl(base + UHSIC_TX_CFG0); |
| val &= ~UHSIC_HS_READY_WAIT_FOR_VALID; |
| writel(val, base + UHSIC_TX_CFG0); |
| #endif |
| |
| val = readl(base + UHSIC_MISC_CFG0); |
| val |= UHSIC_SUSPEND_EXIT_ON_EDGE; |
| #ifdef CONFIG_ARCH_TEGRA_3x_SOC |
| /* Disable generic bus reset, to allow AP30 specific bus reset*/ |
| val |= UHSIC_DISABLE_BUSRESET; |
| #endif |
| 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); |
| |
| #endif |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| val = readl(base + TEGRA_USB_USBMODE_REG_OFFSET); |
| val |= TEGRA_USB_USBMODE_HOST; |
| writel(val, base + TEGRA_USB_USBMODE_REG_OFFSET); |
| |
| /* 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 + USB_TXFILLTUNING); |
| if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) { |
| val = USB_FIFO_TXFILL_THRES(0x10); |
| writel(val, base + USB_TXFILLTUNING); |
| } |
| |
| val = readl(base + USB_PORTSC1); |
| val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN); |
| writel(val, base + USB_PORTSC1); |
| |
| val = readl(base + UHSIC_PADS_CFG0); |
| val &= ~(UHSIC_TX_RTUNEN); |
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC |
| /* set Rtune impedance to 40 ohm */ |
| val |= UHSIC_TX_RTUNE(0); |
| #else |
| /* set Rtune impedance to 50 ohm */ |
| val |= UHSIC_TX_RTUNE(8); |
| #endif |
| 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); |
| baseband_xmm_enable_hsic_power(0); |
| } |
| if (uhsic_config->post_phy_off && uhsic_config->post_phy_off()) |
| return -EAGAIN; |
| |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| uhsic_powerdown_pmc_wake_detect(phy); |
| #endif |
| |
| 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->reg_vdd && !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; |
| } |
| |
| static irqreturn_t usb_cable_remove_irq_thr(int irq, void *pdate) |
| { |
| // Do nothing |
| 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, |
| 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 |
| unsigned int pcb_id_version = grouper_query_pcba_revision(); |
| unsigned int project_id = grouper_get_project_id(); |
| int pmu_hw = grouper_query_pmic_id(); |
| |
| phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL); |
| if (!phy) |
| return ERR_PTR(-ENOMEM); |
| |
| phy->instance = instance; |
| phy->regs = regs; |
| phy->config = config; |
| phy->mode = phy_mode; |
| 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->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; |
| } else { |
| phy->config = &utmip_default[instance]; |
| } |
| } |
| |
| phy->pll_u = clk_get_sys(NULL, "pll_u"); |
| if (IS_ERR(phy->pll_u)) { |
| pr_err("Can't get pll_u clock\n"); |
| err = PTR_ERR(phy->pll_u); |
| goto err0; |
| } |
| clk_enable(phy->pll_u); |
| |
| parent_rate = clk_get_rate(clk_get_parent(phy->pll_u)); |
| 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) { |
| pr_err("invalid pll_u parent rate %ld\n", parent_rate); |
| err = -EINVAL; |
| goto err1; |
| } |
| |
| 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 (phy->instance == 0) { |
| if (project_id == GROUPER_PROJECT_NAKASI) { |
| if (pcb_id_version > 0x2) |
| phy->xcvr_setup_value = phy->xcvr_setup_value + 4; |
| else |
| phy->xcvr_setup_value = phy->xcvr_setup_value + 2; |
| } |
| else if (project_id == GROUPER_PROJECT_BACH) { |
| phy->xcvr_setup_value = phy->xcvr_setup_value + 4; |
| } |
| |
| if (phy->xcvr_setup_value > 63) |
| phy->xcvr_setup_value = 63; |
| |
| pr_info("phy->instance = %d, phy->xcvr_setup_value = %d\n", phy->instance, phy->xcvr_setup_value); |
| } |
| 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; |
| } |
| #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 (IS_ERR_OR_NULL(phy->reg_vdd)) { |
| pr_err("couldn't get regulator avdd_usb: %ld \n", |
| PTR_ERR(phy->reg_vdd)); |
| phy->reg_vdd = NULL; |
| } |
| |
| if (instance == 0 && pmu_hw == GROUPER_PMIC_MAXIM) { |
| usb_phy_data[0].vbus_irq = MAX77663_IRQ_BASE + MAX77663_IRQ_ACOK_RISING; |
| printk(KERN_INFO "%s instance %d MAX77663_IRQ_ACOK_RISING\n", __func__, instance); |
| } else if (instance == 0 && pmu_hw == GROUPER_PMIC_TI) { |
| tegra_gpio_enable(AC_PRESENT_GPIO); |
| err = gpio_request(AC_PRESENT_GPIO, "PMU_ACOK"); |
| if (err < 0) |
| printk(KERN_ERR "Failed to request the GPIO%d: %d\n", AC_PRESENT_GPIO, err); |
| |
| err = gpio_direction_input(AC_PRESENT_GPIO); |
| if (err) |
| printk(KERN_ERR "gpio_direction_input failed for input %d\n", AC_PRESENT_GPIO); |
| |
| usb_phy_data[0].vbus_irq = gpio_to_irq(AC_PRESENT_GPIO); |
| printk(KERN_INFO "%s instance %d TI AC_PRESENT_GPIO = %d \n", __func__, instance, AC_PRESENT_GPIO); |
| } |
| |
| if (instance == 0 && usb_phy_data[0].vbus_irq) { |
| if (pmu_hw == GROUPER_PMIC_MAXIM) { |
| 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; |
| } |
| |
| err = request_threaded_irq(MAX77663_IRQ_BASE + MAX77663_IRQ_ACOK_FALLING , NULL, |
| usb_cable_remove_irq_thr, IRQF_SHARED, "usb_cable_remove", phy); |
| |
| if (err) { |
| pr_err("Failed to register IRQ for removing the USB cable.\n"); |
| goto err1; |
| } |
| } else if (pmu_hw == GROUPER_PMIC_TI) { |
| err = request_threaded_irq(usb_phy_data[0].vbus_irq, NULL, usb_phy_vbus_irq_thr, |
| IRQF_SHARED |IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING , "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) { |
| unsigned long val; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| |
| /* turn on pad detectors for HSIC*/ |
| val = readl(pmc_base + PMC_USB_AO); |
| val &= ~(TEGRA_PMC_USB_AO_VBUS_WAKEUP_PD_P0 | TEGRA_PMC_USB_AO_ID_PD_P0); |
| writel(val, pmc_base + PMC_USB_AO); |
| |
| utmip_powerup_pmc_wake_detect(phy); |
| |
| 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 (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) { |
| uhsic_powerup_pmc_wake_detect(phy); |
| } |
| |
| #endif |
| if (((instance == 2) || (instance == 0)) && |
| (phy->mode == TEGRA_USB_PHY_MODE_HOST)) { |
| vbus_enable(phy); |
| } |
| return phy; |
| |
| err1: |
| clk_disable(phy->pll_u); |
| clk_put(phy->pll_u); |
| err0: |
| kfree(phy); |
| return ERR_PTR(err); |
| } |
| |
| int tegra_usb_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) |
| { |
| 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->instance == 0) && usb_phy_data[0].vbus_irq && |
| (phy->mode == TEGRA_USB_PHY_MODE_DEVICE)) |
| is_dpd = true; |
| |
| 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, bool is_dpd) |
| { |
| 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 ((phy->instance == 0) && usb_phy_data[0].vbus_irq && |
| (phy->mode == TEGRA_USB_PHY_MODE_DEVICE)) |
| is_dpd = true; |
| |
| 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 remote_wakeup) |
| { |
| 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, remote_wakeup); |
| } |
| |
| 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_postresume(struct tegra_usb_phy *phy, bool is_dpd) |
| { |
| 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_ehci_pre_reset(struct tegra_usb_phy *phy, bool is_dpd) |
| { |
| 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) |
| { |
| 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) |
| { |
| 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->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->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->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 |
| val = readl(base + TEGRA_USB_USBMODE_REG_OFFSET); |
| val |= TEGRA_USB_USBMODE_HOST; |
| writel(val, base + TEGRA_USB_USBMODE_REG_OFFSET); |
| |
| /* 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; |
| |
| /* connect detect on T30 requires extra wait */ |
| if (utmi_wait_register_timeout(base + UHSIC_STAT_CFG0, |
| UHSIC_CONNECT_DETECT, UHSIC_CONNECT_DETECT, |
| CONNECT_DETECT_TIMEOUT) < 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) { |
| #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); |
| /* wait here, otherwise HOSTPC1_DEVLC_PSPD will timeout */ |
| mdelay(5); |
| #endif |
| 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); |
| |
| if (utmi_wait_register(base + USB_PORTSC1, USB_PORTSC1_LS(0), 0) < 0) { |
| pr_err("%s: timeout waiting for SE0\n", __func__); |
| return -ETIMEDOUT; |
| } |
| |
| 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; |
| } |
| |
| #if defined(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; |
| } |
| #elif defined(CONFIG_ARCH_TEGRA_3x_SOC) |
| if (utmi_wait_register(base + HOSTPC1_DEVLC, |
| HOSTPC1_DEVLC_PSPD(2), |
| HOSTPC1_DEVLC_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 |
| val = readl(base + TEGRA_USB_USBMODE_REG_OFFSET); |
| val |= TEGRA_USB_USBMODE_HOST; |
| writel(val, base + TEGRA_USB_USBMODE_REG_OFFSET); |
| |
| /* 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; |
| /* safe to enable RPU on STROBE at all times during idle */ |
| val |= UHSIC_RPU_STROBE; |
| writel(val, base + UHSIC_PADS_CFG1); |
| |
| val = readl(base + USB_USBCMD); |
| val &= ~USB_USBCMD_RS; |
| writel(val, base + USB_USBCMD); |
| |
| if (uhsic_config->usb_phy_ready && |
| uhsic_config->usb_phy_ready()) |
| return -EAGAIN; |
| |
| /* connect detect on T30 requires extra wait */ |
| if (utmi_wait_register_timeout(base + UHSIC_STAT_CFG0, |
| UHSIC_CONNECT_DETECT, UHSIC_CONNECT_DETECT, |
| CONNECT_DETECT_TIMEOUT) < 0) { |
| pr_err("%s: timeout waiting for hsic connect detect\n", |
| __func__); |
| return -ETIMEDOUT; |
| } |
| } |
| 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 (utmi_wait_register(base + UHSIC_STAT_CFG0, |
| UHSIC_CONNECT_DETECT, UHSIC_CONNECT_DETECT) < 0) { |
| pr_err("%s: no hsic 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; |
| } |
| |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| void tegra_usb_phy_power_down_pmc(void) |
| { |
| unsigned long val; |
| void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); |
| |
| /* power down all 3 UTMIP interfaces */ |
| val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG); |
| val |= UTMIP_PWR(0) | UTMIP_PWR(1) | UTMIP_PWR(2); |
| writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG); |
| |
| /* turn on pad detectors */ |
| writel(PMC_POWER_DOWN_MASK, pmc_base + PMC_USB_AO); |
| |
| /* setup sleep walk fl all 3 usb controllers */ |
| val = UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A | UTMIP_HIGHZ_A | |
| UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_HIGHZ_B | |
| UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_HIGHZ_C | |
| UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_HIGHZ_D; |
| writel(val, pmc_base + PMC_SLEEPWALK_REG(0)); |
| writel(val, pmc_base + PMC_SLEEPWALK_REG(1)); |
| writel(val, pmc_base + PMC_SLEEPWALK_REG(2)); |
| |
| /* enable pull downs on HSIC PMC */ |
| val = UHSIC_STRB_RPD_A | UHSIC_DATA_RPD_A | UHSIC_STRB_RPD_B | |
| UHSIC_DATA_RPD_B | UHSIC_STRB_RPD_C | UHSIC_DATA_RPD_C | |
| UHSIC_STRB_RPD_D | UHSIC_DATA_RPD_D; |
| writel(val, pmc_base + UHSIC_SLEEPWALK_REG); |
| |
| /* Turn over pad configuration to PMC */ |
| val = readl(pmc_base + PMC_SLEEP_CFG); |
| val &= ~UTMIP_WAKE_VAL(0, ~0); |
| val &= ~UTMIP_WAKE_VAL(1, ~0); |
| val &= ~UTMIP_WAKE_VAL(2, ~0); |
| val &= ~UHSIC_WAKE_VAL_P0(~0); |
| val |= UTMIP_WAKE_VAL(0, WAKE_VAL_NONE) | UHSIC_WAKE_VAL_P0(WAKE_VAL_NONE) | |
| UTMIP_WAKE_VAL(1, WAKE_VAL_NONE) | UTMIP_WAKE_VAL(2, WAKE_VAL_NONE) | |
| UTMIP_RCTRL_USE_PMC(0) | UTMIP_RCTRL_USE_PMC(1) | UTMIP_RCTRL_USE_PMC(2) | |
| UTMIP_TCTRL_USE_PMC(0) | UTMIP_TCTRL_USE_PMC(1) | UTMIP_TCTRL_USE_PMC(2) | |
| UTMIP_FSLS_USE_PMC(0) | UTMIP_FSLS_USE_PMC(1) | UTMIP_FSLS_USE_PMC(2) | |
| UTMIP_MASTER_ENABLE(0) | UTMIP_MASTER_ENABLE(1) | UTMIP_MASTER_ENABLE(2) | |
| UHSIC_MASTER_ENABLE_P0; |
| writel(val, pmc_base + PMC_SLEEP_CFG); |
| } |
| #endif |
| |
| 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; |
| } |
| } |
| |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| tegra_usb_phy_power_down_pmc(); |
| #endif |
| |
| return 0; |
| } |
| |
| void tegra_usb_phy_memory_prefetch_on(struct tegra_usb_phy *phy) |
| { |
| void __iomem *ahb_gizmo = IO_ADDRESS(TEGRA_AHB_GIZMO_BASE); |
| unsigned long val; |
| |
| if (phy->instance == 0 && phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { |
| val = readl(ahb_gizmo + AHB_MEM_PREFETCH_CFG1); |
| val |= PREFETCH_ENB; |
| writel(val, ahb_gizmo + AHB_MEM_PREFETCH_CFG1); |
| val = readl(ahb_gizmo + AHB_MEM_PREFETCH_CFG2); |
| val |= PREFETCH_ENB; |
| writel(val, ahb_gizmo + AHB_MEM_PREFETCH_CFG2); |
| } |
| } |
| |
| void tegra_usb_phy_memory_prefetch_off(struct tegra_usb_phy *phy) |
| { |
| void __iomem *ahb_gizmo = IO_ADDRESS(TEGRA_AHB_GIZMO_BASE); |
| unsigned long val; |
| |
| if (phy->instance == 0 && phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { |
| val = readl(ahb_gizmo + AHB_MEM_PREFETCH_CFG1); |
| val &= ~(PREFETCH_ENB); |
| writel(val, ahb_gizmo + AHB_MEM_PREFETCH_CFG1); |
| val = readl(ahb_gizmo + AHB_MEM_PREFETCH_CFG2); |
| val &= ~(PREFETCH_ENB); |
| writel(val, ahb_gizmo + AHB_MEM_PREFETCH_CFG2); |
| } |
| } |
| |
| /* 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; |
| } |
| |