summaryrefslogtreecommitdiffstats
path: root/sys/arm/nvidia/tegra_usbphy.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/nvidia/tegra_usbphy.c')
-rw-r--r--sys/arm/nvidia/tegra_usbphy.c839
1 files changed, 839 insertions, 0 deletions
diff --git a/sys/arm/nvidia/tegra_usbphy.c b/sys/arm/nvidia/tegra_usbphy.c
new file mode 100644
index 0000000..40c0714
--- /dev/null
+++ b/sys/arm/nvidia/tegra_usbphy.c
@@ -0,0 +1,839 @@
+/*-
+ * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+/*
+ * USB phy driver for Tegra SoCs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
+#include <dev/extres/regulator/regulator.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/fdt/fdt_pinctrl.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "phy_if.h"
+
+#define CTRL_ICUSB_CTRL 0x15c
+#define ICUSB_CTR_IC_ENB1 (1 << 3)
+
+#define CTRL_USB_USBMODE 0x1f8
+#define USB_USBMODE_MASK (3 << 0)
+#define USB_USBMODE_HOST (3 << 0)
+#define USB_USBMODE_DEVICE (2 << 0)
+
+#define CTRL_USB_HOSTPC1_DEVLC 0x1b4
+#define USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
+#define USB_HOSTPC1_DEVLC_STS (1 << 28)
+#define USB_HOSTPC1_DEVLC_PHCD (1 << 22)
+
+
+#define IF_USB_SUSP_CTRL 0x400
+#define FAST_WAKEUP_RESP (1 << 26)
+#define UTMIP_SUSPL1_SET (1 << 25)
+#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+#define USB_SUSP_SET (1 << 14)
+#define UTMIP_PHY_ENB (1 << 12)
+#define UTMIP_RESET (1 << 11)
+#define USB_SUSP_POL (1 << 10)
+#define USB_PHY_CLK_VALID_INT_ENB (1 << 9)
+#define USB_PHY_CLK_VALID_INT_STS (1 << 8)
+#define USB_PHY_CLK_VALID (1 << 7)
+#define USB_CLKEN (1 << 6)
+#define USB_SUSP_CLR (1 << 5)
+#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
+#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
+#define USB_WAKE_ON_RESUME_EN (1 << 2)
+#define USB_WAKEUP_INT_ENB (1 << 1)
+#define USB_WAKEUP_INT_STS (1 << 0)
+
+#define IF_USB_PHY_VBUS_SENSORS 0x404
+#define B_SESS_END_SW_VALUE (1 << 4)
+#define B_SESS_END_SW_EN (1 << 3)
+
+
+#define UTMIP_XCVR_CFG0 0x808
+#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25)
+#define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22)
+#define UTMIP_XCVR_LSBIAS_SEL (1 << 21)
+#define UTMIP_XCVR_DISCON_METHOD (1 << 20)
+#define UTMIP_FORCE_PDZI_POWERUP (1 << 19)
+#define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18)
+#define UTMIP_FORCE_PD2_POWERUP (1 << 17)
+#define UTMIP_FORCE_PD2_POWERDOWN (1 << 16)
+#define UTMIP_FORCE_PD_POWERUP (1 << 15)
+#define UTMIP_FORCE_PD_POWERDOWN (1 << 14)
+#define UTMIP_XCVR_TERMEN (1 << 13)
+#define UTMIP_XCVR_HSLOOPBACK (1 << 12)
+#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10)
+#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8)
+#define UTMIP_XCVR_FSSLEW(x) (((x) & 0x3) << 6)
+#define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4)
+#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0)
+
+#define UTMIP_BIAS_CFG0 0x80C
+#define UTMIP_IDDIG_C_VAL (1 << 30)
+#define UTMIP_IDDIG_C_SEL (1 << 29)
+#define UTMIP_IDDIG_B_VAL (1 << 28)
+#define UTMIP_IDDIG_B_SEL (1 << 27)
+#define UTMIP_IDDIG_A_VAL (1 << 26)
+#define UTMIP_IDDIG_A_SEL (1 << 25)
+#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24)
+#define UTMIP_IDPD_VAL (1 << 23)
+#define UTMIP_IDPD_SEL (1 << 22)
+#define UTMIP_IDDIG_VAL (1 << 21)
+#define UTMIP_IDDIG_SEL (1 << 20)
+#define UTMIP_GPI_VAL (1 << 19)
+#define UTMIP_GPI_SEL (1 << 18)
+#define UTMIP_ACTIVE_TERM_OFFSET(x) (((x) & 0x7) << 15)
+#define UTMIP_ACTIVE_PULLUP_OFFSET(x) (((x) & 0x7) << 12)
+#define UTMIP_OTGPD (1 << 11)
+#define UTMIP_BIASPD (1 << 10)
+#define UTMIP_VBUS_LEVEL_LEVEL(x) (((x) & 0x3) << 8)
+#define UTMIP_SESS_LEVEL_LEVEL(x) (((x) & 0x3) << 6)
+#define UTMIP_HSCHIRP_LEVEL(x) (((x) & 0x3) << 4)
+#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2)
+#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0)
+
+
+#define UTMIP_HSRX_CFG0 0x810
+#define UTMIP_KEEP_PATT_ON_ACTIVE(x) (((x) & 0x3) << 30)
+#define UTMIP_ALLOW_CONSEC_UPDN (1 << 29)
+#define UTMIP_REALIGN_ON_NEW_PKT (1 << 28)
+#define UTMIP_PCOUNT_UPDN_DIV(x) (((x) & 0xf) << 24)
+#define UTMIP_SQUELCH_EOP_DLY(x) (((x) & 0x7) << 21)
+#define UTMIP_NO_STRIPPING (1 << 20)
+#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15)
+#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
+#define UTMIP_ELASTIC_OVERRUN_DISABLE (1 << 9)
+#define UTMIP_ELASTIC_UNDERRUN_DISABLE (1 << 8)
+#define UTMIP_PASS_CHIRP (1 << 7)
+#define UTMIP_PASS_FEEDBACK (1 << 6)
+#define UTMIP_PCOUNT_INERTIA(x) (((x) & 0x3) << 4)
+#define UTMIP_PHASE_ADJUST(x) (((x) & 0x3) << 2)
+#define UTMIP_THREE_SYNCBITS (1 << 1)
+#define UTMIP_USE4SYNC_TRAN (1 << 0)
+
+#define UTMIP_HSRX_CFG1 0x814
+#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1F) << 1)
+#define UTMIP_HS_ALLOW_KEEP_ALIVE (1 << 0)
+
+#define UTMIP_TX_CFG0 0x820
+#define UTMIP_FS_PREAMBLE_J (1 << 19)
+#define UTMIP_FS_POSTAMBLE_OUTPUT_ENABLE (1 << 18)
+#define UTMIP_FS_PREAMBLE_OUTPUT_ENABLE (1 << 17)
+#define UTMIP_FSLS_ALLOW_SOP_TX_STUFF_ERR (1 << 16)
+#define UTMIP_HS_READY_WAIT_FOR_VALID (1 << 15)
+#define UTMIP_HS_TX_IPG_DLY(x) (((x) & 0x1f) << 10)
+#define UTMIP_HS_DISCON_EOP_ONLY (1 << 9)
+#define UTMIP_HS_DISCON_DISABLE (1 << 8)
+#define UTMIP_HS_POSTAMBLE_OUTPUT_ENABLE (1 << 7)
+#define UTMIP_HS_PREAMBLE_OUTPUT_ENABLE (1 << 6)
+#define UTMIP_SIE_RESUME_ON_LINESTATE (1 << 5)
+#define UTMIP_SOF_ON_NO_STUFF (1 << 4)
+#define UTMIP_SOF_ON_NO_ENCODE (1 << 3)
+#define UTMIP_NO_STUFFING (1 << 2)
+#define UTMIP_NO_ENCODING (1 << 1)
+#define UTMIP_NO_SYNC_NO_EOP (1 << 0)
+
+#define UTMIP_MISC_CFG0 0x824
+#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
+#define UTMIP_DPDM_OBSERVE (1 << 26)
+#define UTMIP_KEEP_XCVR_PD_ON_SOFT_DISCON (1 << 25)
+#define UTMIP_ALLOW_LS_ON_SOFT_DISCON (1 << 24)
+#define UTMIP_FORCE_FS_DISABLE_ON_DEV_CHIRP (1 << 23)
+#define UTMIP_SUSPEND_EXIT_ON_EDGE (1 << 22)
+#define UTMIP_LS_TO_FS_SKIP_4MS (1 << 21)
+#define UTMIP_INJECT_ERROR_TYPE(x) (((x) & 0x3) << 19)
+#define UTMIP_FORCE_HS_CLOCK_ON (1 << 18)
+#define UTMIP_DISABLE_HS_TERM (1 << 17)
+#define UTMIP_FORCE_HS_TERM (1 << 16)
+#define UTMIP_DISABLE_PULLUP_DP (1 << 15)
+#define UTMIP_DISABLE_PULLUP_DM (1 << 14)
+#define UTMIP_DISABLE_PULLDN_DP (1 << 13)
+#define UTMIP_DISABLE_PULLDN_DM (1 << 12)
+#define UTMIP_FORCE_PULLUP_DP (1 << 11)
+#define UTMIP_FORCE_PULLUP_DM (1 << 10)
+#define UTMIP_FORCE_PULLDN_DP (1 << 9)
+#define UTMIP_FORCE_PULLDN_DM (1 << 8)
+#define UTMIP_STABLE_COUNT(x) (((x) & 0x7) << 5)
+#define UTMIP_STABLE_ALL (1 << 4)
+#define UTMIP_NO_FREE_ON_SUSPEND (1 << 3)
+#define UTMIP_NEVER_FREE_RUNNING_TERMS (1 << 2)
+#define UTMIP_ALWAYS_FREE_RUNNING_TERMS (1 << 1)
+#define UTMIP_COMB_TERMS (1 << 0)
+
+#define UTMIP_MISC_CFG1 0x828
+#define UTMIP_PHY_XTAL_CLOCKEN (1 << 30)
+
+#define UTMIP_DEBOUNCE_CFG0 0x82C
+#define UTMIP_BIAS_DEBOUNCE_B(x) (((x) & 0xffff) << 16)
+#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0)
+
+#define UTMIP_BAT_CHRG_CFG0 0x830
+#define UTMIP_CHRG_DEBOUNCE_TIMESCALE(x) (((x) & 0x1f) << 8)
+#define UTMIP_OP_I_SRC_ENG (1 << 5)
+#define UTMIP_ON_SRC_ENG (1 << 4)
+#define UTMIP_OP_SRC_ENG (1 << 3)
+#define UTMIP_ON_SINK_ENG (1 << 2)
+#define UTMIP_OP_SINK_ENG (1 << 1)
+#define UTMIP_PD_CHRG (1 << 0)
+
+#define UTMIP_SPARE_CFG0 0x834
+#define FUSE_HS_IREF_CAP_CFG (1 << 7)
+#define FUSE_HS_SQUELCH_LEVEL (1 << 6)
+#define FUSE_SPARE (1 << 5)
+#define FUSE_TERM_RANGE_ADJ_SEL (1 << 4)
+#define FUSE_SETUP_SEL (1 << 3)
+#define HS_RX_LATE_SQUELCH (1 << 2)
+#define HS_RX_FLUSH_ALAP (1 << 1)
+#define HS_RX_IPG_ERROR_ENABLE (1 << 0)
+
+#define UTMIP_XCVR_CFG1 0x838
+#define UTMIP_XCVR_RPU_RANGE_ADJ(x) (((x) & 0x3) << 26)
+#define UTMIP_XCVR_HS_IREF_CAP(x) (((x) & 0x3) << 24)
+#define UTMIP_XCVR_SPARE(x) (((x) & 0x3) << 22)
+#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
+#define UTMIP_RCTRL_SW_SET (1 << 17)
+#define UTMIP_RCTRL_SW_VAL(x) (((x) & 0x1f) << 12)
+#define UTMIP_TCTRL_SW_SET (1 << 11)
+#define UTMIP_TCTRL_SW_VAL(x) (((x) & 0x1f) << 6)
+#define UTMIP_FORCE_PDDR_POWERUP (1 << 5)
+#define UTMIP_FORCE_PDDR_POWERDOWN (1 << 4)
+#define UTMIP_FORCE_PDCHRP_POWERUP (1 << 3)
+#define UTMIP_FORCE_PDCHRP_POWERDOWN (1 << 2)
+#define UTMIP_FORCE_PDDISC_POWERUP (1 << 1)
+#define UTMIP_FORCE_PDDISC_POWERDOWN (1 << 0)
+
+#define UTMIP_BIAS_CFG1 0x83c
+#define UTMIP_BIAS_DEBOUNCE_TIMESCALE(x) (((x) & 0x3f) << 8)
+#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
+#define UTMIP_VBUS_WAKEUP_POWERDOWN (1 << 2)
+#define UTMIP_FORCE_PDTRK_POWERUP (1 << 1)
+#define UTMIP_FORCE_PDTRK_POWERDOWN (1 << 0)
+
+static int usbpby_enable_cnt;
+
+enum usb_ifc_type {
+ USB_IFC_TYPE_UNKNOWN = 0,
+ USB_IFC_TYPE_UTMI,
+ USB_IFC_TYPE_ULPI
+};
+
+enum usb_dr_mode {
+ USB_DR_MODE_UNKNOWN = 0,
+ USB_DR_MODE_DEVICE,
+ USB_DR_MODE_HOST,
+ USB_DR_MODE_OTG
+};
+
+struct usbphy_softc {
+ device_t dev;
+ struct resource *mem_res;
+ struct resource *pads_res;
+ clk_t clk_reg;
+ clk_t clk_pads;
+ clk_t clk_pllu;
+ regulator_t supply_vbus;
+ hwreset_t reset_usb;
+ hwreset_t reset_pads;
+ enum usb_ifc_type ifc_type;
+ enum usb_dr_mode dr_mode;
+ bool have_utmi_regs;
+
+ /* UTMI params */
+ int hssync_start_delay;
+ int elastic_limit;
+ int idle_wait_delay;
+ int term_range_adj;
+ int xcvr_lsfslew;
+ int xcvr_lsrslew;
+ int xcvr_hsslew;
+ int hssquelch_level;
+ int hsdiscon_level;
+ int xcvr_setup;
+ int xcvr_setup_use_fuses;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"nvidia,tegra30-usb-phy", 1},
+ {NULL, 0},
+};
+
+#define RD4(sc, offs) \
+ bus_read_4(sc->mem_res, offs)
+
+#define WR4(sc, offs, val) \
+ bus_write_4(sc->mem_res, offs, val)
+
+static int
+reg_wait(struct usbphy_softc *sc, uint32_t reg, uint32_t mask, uint32_t val)
+{
+ int i;
+
+ for (i = 0; i < 1000; i++) {
+ if ((RD4(sc, reg) & mask) == val)
+ return (0);
+ DELAY(10);
+ }
+ return (ETIMEDOUT);
+}
+
+static int
+usbphy_utmi_phy_clk(struct usbphy_softc *sc, bool enable)
+{
+ uint32_t val;
+ int rv;
+
+ val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC);
+ if (enable)
+ val &= ~USB_HOSTPC1_DEVLC_PHCD;
+ else
+ val |= USB_HOSTPC1_DEVLC_PHCD;
+ WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val);
+
+ rv = reg_wait(sc, IF_USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ enable ? USB_PHY_CLK_VALID: 0);
+ if (rv != 0) {
+ device_printf(sc->dev, "USB phy clock timeout.\n");
+ return (ETIMEDOUT);
+ }
+ return (0);
+}
+
+static int
+usbphy_utmi_enable(struct usbphy_softc *sc)
+{
+ int rv;
+ uint32_t val;
+
+ /* Reset phy */
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val |= UTMIP_RESET;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+
+ val = RD4(sc, UTMIP_TX_CFG0);
+ val |= UTMIP_FS_PREAMBLE_J;
+ WR4(sc, UTMIP_TX_CFG0, val);
+
+ val = RD4(sc, UTMIP_HSRX_CFG0);
+ val &= ~UTMIP_IDLE_WAIT(~0);
+ val &= ~UTMIP_ELASTIC_LIMIT(~0);
+ val |= UTMIP_IDLE_WAIT(sc->idle_wait_delay);
+ val |= UTMIP_ELASTIC_LIMIT(sc->elastic_limit);
+ WR4(sc, UTMIP_HSRX_CFG0, val);
+
+ val = RD4(sc, UTMIP_HSRX_CFG1);
+ val &= ~UTMIP_HS_SYNC_START_DLY(~0);
+ val |= UTMIP_HS_SYNC_START_DLY(sc->hssync_start_delay);
+ WR4(sc, UTMIP_HSRX_CFG1, val);
+
+ val = RD4(sc, UTMIP_DEBOUNCE_CFG0);
+ val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
+ val |= UTMIP_BIAS_DEBOUNCE_A(0x7530); /* For 12MHz */
+ WR4(sc, UTMIP_DEBOUNCE_CFG0, val);
+
+ val = RD4(sc, UTMIP_MISC_CFG0);
+ val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
+ WR4(sc, UTMIP_MISC_CFG0, val);
+
+ if (sc->dr_mode == USB_DR_MODE_DEVICE) {
+ val = RD4(sc,IF_USB_SUSP_CTRL);
+ val &= ~USB_WAKE_ON_CNNT_EN_DEV;
+ val &= ~USB_WAKE_ON_DISCON_EN_DEV;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+ val &= ~UTMIP_PD_CHRG;
+ WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+ } else {
+ val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+ val |= UTMIP_PD_CHRG;
+ WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+ }
+
+ usbpby_enable_cnt++;
+ if (usbpby_enable_cnt == 1) {
+ rv = hwreset_deassert(sc->reset_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot unreset 'utmi-pads' reset\n");
+ return (rv);
+ }
+ rv = clk_enable(sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable 'utmi-pads' clock\n");
+ return (rv);
+ }
+
+ val = bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0);
+ val &= ~UTMIP_OTGPD;
+ val &= ~UTMIP_BIASPD;
+ val &= ~UTMIP_HSSQUELCH_LEVEL(~0);
+ val &= ~UTMIP_HSDISCON_LEVEL(~0);
+ val &= ~UTMIP_HSDISCON_LEVEL_MSB(~0);
+ val |= UTMIP_HSSQUELCH_LEVEL(sc->hssquelch_level);
+ val |= UTMIP_HSDISCON_LEVEL(sc->hsdiscon_level);
+ val |= UTMIP_HSDISCON_LEVEL_MSB(sc->hsdiscon_level);
+ bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val);
+
+ rv = clk_disable(sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot disable 'utmi-pads' clock\n");
+ return (rv);
+ }
+ }
+
+ val = RD4(sc, UTMIP_XCVR_CFG0);
+ val &= ~UTMIP_FORCE_PD_POWERDOWN;
+ val &= ~UTMIP_FORCE_PD2_POWERDOWN ;
+ val &= ~UTMIP_FORCE_PDZI_POWERDOWN;
+ val &= ~UTMIP_XCVR_LSBIAS_SEL;
+ val &= ~UTMIP_XCVR_LSFSLEW(~0);
+ val &= ~UTMIP_XCVR_LSRSLEW(~0);
+ val &= ~UTMIP_XCVR_HSSLEW(~0);
+ val &= ~UTMIP_XCVR_HSSLEW_MSB(~0);
+ val |= UTMIP_XCVR_LSFSLEW(sc->xcvr_lsfslew);
+ val |= UTMIP_XCVR_LSRSLEW(sc->xcvr_lsrslew);
+ val |= UTMIP_XCVR_HSSLEW(sc->xcvr_hsslew);
+ val |= UTMIP_XCVR_HSSLEW_MSB(sc->xcvr_hsslew);
+ if (!sc->xcvr_setup_use_fuses) {
+ val &= ~UTMIP_XCVR_SETUP(~0);
+ val &= ~UTMIP_XCVR_SETUP_MSB(~0);
+ val |= UTMIP_XCVR_SETUP(sc->xcvr_setup);
+ val |= UTMIP_XCVR_SETUP_MSB(sc->xcvr_setup);
+ }
+ WR4(sc, UTMIP_XCVR_CFG0, val);
+
+ val = RD4(sc, UTMIP_XCVR_CFG1);
+ val &= ~UTMIP_FORCE_PDDISC_POWERDOWN;
+ val &= ~UTMIP_FORCE_PDCHRP_POWERDOWN;
+ val &= ~UTMIP_FORCE_PDDR_POWERDOWN;
+ val &= ~UTMIP_XCVR_TERM_RANGE_ADJ(~0);
+ val |= UTMIP_XCVR_TERM_RANGE_ADJ(sc->term_range_adj);
+ WR4(sc, UTMIP_XCVR_CFG1, val);
+
+
+ val = RD4(sc, UTMIP_BIAS_CFG1);
+ val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
+ val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
+ WR4(sc, UTMIP_BIAS_CFG1, val);
+
+ val = RD4(sc, UTMIP_SPARE_CFG0);
+ if (sc->xcvr_setup_use_fuses)
+ val |= FUSE_SETUP_SEL;
+ else
+ val &= ~FUSE_SETUP_SEL;
+ WR4(sc, UTMIP_SPARE_CFG0, val);
+
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val |= UTMIP_PHY_ENB;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val &= ~UTMIP_RESET;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ usbphy_utmi_phy_clk(sc, true);
+
+ val = RD4(sc, CTRL_USB_USBMODE);
+ val &= ~USB_USBMODE_MASK;
+ if (sc->dr_mode == USB_DR_MODE_HOST)
+ val |= USB_USBMODE_HOST;
+ else
+ val |= USB_USBMODE_DEVICE;
+ WR4(sc, CTRL_USB_USBMODE, val);
+
+ val = RD4(sc, CTRL_USB_HOSTPC1_DEVLC);
+ val &= ~USB_HOSTPC1_DEVLC_PTS(~0);
+ val |= USB_HOSTPC1_DEVLC_PTS(0);
+ WR4(sc, CTRL_USB_HOSTPC1_DEVLC, val);
+
+ return (0);
+}
+
+static int
+usbphy_utmi_disable(struct usbphy_softc *sc)
+{
+ int rv;
+ uint32_t val;
+
+ usbphy_utmi_phy_clk(sc, false);
+
+ if (sc->dr_mode == USB_DR_MODE_DEVICE) {
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
+ val |= USB_WAKE_ON_CNNT_EN_DEV;
+ val |= USB_WAKEUP_DEBOUNCE_COUNT(5);
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+ }
+
+ val = RD4(sc, IF_USB_SUSP_CTRL);
+ val |= UTMIP_RESET;
+ WR4(sc, IF_USB_SUSP_CTRL, val);
+
+ val = RD4(sc, UTMIP_BAT_CHRG_CFG0);
+ val |= UTMIP_PD_CHRG;
+ WR4(sc, UTMIP_BAT_CHRG_CFG0, val);
+
+ val = RD4(sc, UTMIP_XCVR_CFG0);
+ val |= UTMIP_FORCE_PD_POWERDOWN;
+ val |= UTMIP_FORCE_PD2_POWERDOWN;
+ val |= UTMIP_FORCE_PDZI_POWERDOWN;
+ WR4(sc, UTMIP_XCVR_CFG0, val);
+
+ val = RD4(sc, UTMIP_XCVR_CFG1);
+ val |= UTMIP_FORCE_PDDISC_POWERDOWN;
+ val |= UTMIP_FORCE_PDCHRP_POWERDOWN;
+ val |= UTMIP_FORCE_PDDR_POWERDOWN;
+ WR4(sc, UTMIP_XCVR_CFG1, val);
+
+ usbpby_enable_cnt--;
+ if (usbpby_enable_cnt <= 0) {
+ rv = clk_enable(sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable 'utmi-pads' clock\n");
+ return (rv);
+ }
+ val =bus_read_4(sc->pads_res, UTMIP_BIAS_CFG0);
+ val |= UTMIP_OTGPD;
+ val |= UTMIP_BIASPD;
+ bus_write_4(sc->pads_res, UTMIP_BIAS_CFG0, val);
+
+ rv = clk_disable(sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot disable 'utmi-pads' clock\n");
+ return (rv);
+ }
+ }
+ return (0);
+}
+
+static int
+usbphy_phy_enable(device_t dev, int id, bool enable)
+{
+ struct usbphy_softc *sc;
+ int rv = 0;
+
+ sc = device_get_softc(dev);
+
+ if (sc->ifc_type != USB_IFC_TYPE_UTMI) {
+ device_printf(sc->dev,
+ "Only UTMI interface is supported.\n");
+ return (ENXIO);
+ }
+ if (enable)
+ rv = usbphy_utmi_enable(sc);
+ else
+ rv = usbphy_utmi_disable(sc);
+
+ return (rv);
+}
+
+static enum usb_ifc_type
+usb_get_ifc_mode(device_t dev, phandle_t node, char *name)
+{
+ char *tmpstr;
+ int rv;
+ enum usb_ifc_type ret;
+
+ rv = OF_getprop_alloc(node, name, 1, (void **)&tmpstr);
+ if (rv <= 0)
+ return (USB_IFC_TYPE_UNKNOWN);
+
+ ret = USB_IFC_TYPE_UNKNOWN;
+ if (strcmp(tmpstr, "utmi") == 0)
+ ret = USB_IFC_TYPE_UTMI;
+ else if (strcmp(tmpstr, "ulpi") == 0)
+ ret = USB_IFC_TYPE_ULPI;
+ else
+ device_printf(dev, "Unsupported phy type: %s\n", tmpstr);
+ free(tmpstr, M_OFWPROP);
+ return (ret);
+}
+
+static enum usb_dr_mode
+usb_get_dr_mode(device_t dev, phandle_t node, char *name)
+{
+ char *tmpstr;
+ int rv;
+ enum usb_dr_mode ret;
+
+ rv = OF_getprop_alloc(node, name, 1, (void **)&tmpstr);
+ if (rv <= 0)
+ return (USB_DR_MODE_UNKNOWN);
+
+ ret = USB_DR_MODE_UNKNOWN;
+ if (strcmp(tmpstr, "device") == 0)
+ ret = USB_DR_MODE_DEVICE;
+ else if (strcmp(tmpstr, "host") == 0)
+ ret = USB_DR_MODE_HOST;
+ else if (strcmp(tmpstr, "otg") == 0)
+ ret = USB_DR_MODE_OTG;
+ else
+ device_printf(dev, "Unknown dr mode: %s\n", tmpstr);
+ free(tmpstr, M_OFWPROP);
+ return (ret);
+}
+
+static int
+usbphy_utmi_read_params(struct usbphy_softc *sc, phandle_t node)
+{
+ int rv;
+
+ rv = OF_getencprop(node, "nvidia,hssync-start-delay",
+ &sc->hssync_start_delay, sizeof (sc->hssync_start_delay));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,elastic-limit",
+ &sc->elastic_limit, sizeof (sc->elastic_limit));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,idle-wait-delay",
+ &sc->idle_wait_delay, sizeof (sc->idle_wait_delay));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,term-range-adj",
+ &sc->term_range_adj, sizeof (sc->term_range_adj));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,xcvr-lsfslew",
+ &sc->xcvr_lsfslew, sizeof (sc->xcvr_lsfslew));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,xcvr-lsrslew",
+ &sc->xcvr_lsrslew, sizeof (sc->xcvr_lsrslew));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,xcvr-hsslew",
+ &sc->xcvr_hsslew, sizeof (sc->xcvr_hsslew));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,hssquelch-level",
+ &sc->hssquelch_level, sizeof (sc->hssquelch_level));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getencprop(node, "nvidia,hsdiscon-level",
+ &sc->hsdiscon_level, sizeof (sc->hsdiscon_level));
+ if (rv <= 0)
+ return (ENXIO);
+
+ rv = OF_getproplen(node, "nvidia,xcvr-setup-use-fuses");
+ if (rv >= 1) {
+ sc->xcvr_setup_use_fuses = 1;
+ } else {
+ rv = OF_getencprop(node, "nvidia,xcvr-setup",
+ &sc->xcvr_setup, sizeof (sc->xcvr_setup));
+ if (rv <= 0)
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+usbphy_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Tegra USB phy");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+usbphy_attach(device_t dev)
+{
+ struct usbphy_softc * sc;
+ int rid, rv;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ rid = 1;
+ sc->pads_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "Cannot allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ node = ofw_bus_get_node(dev);
+
+ rv = hwreset_get_by_ofw_name(sc->dev, "usb", &sc->reset_usb);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'usb' reset\n");
+ return (ENXIO);
+ }
+ rv = hwreset_get_by_ofw_name(sc->dev, "utmi-pads", &sc->reset_pads);
+ if (rv != 0) {
+ device_printf(dev, "Cannot get 'utmi-pads' reset\n");
+ return (ENXIO);
+ }
+
+ rv = clk_get_by_ofw_name(sc->dev, "reg", &sc->clk_reg);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'reg' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, "pll_u", &sc->clk_pllu);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'pll_u' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_get_by_ofw_name(sc->dev, "utmi-pads", &sc->clk_pads);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot get 'utmi-pads' clock\n");
+ return (ENXIO);
+ }
+
+ rv = hwreset_deassert(sc->reset_usb);
+ if (rv != 0) {
+ device_printf(dev, "Cannot unreset 'usb' reset\n");
+ return (ENXIO);
+ }
+
+ rv = clk_enable(sc->clk_pllu);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'pllu' clock\n");
+ return (ENXIO);
+ }
+ rv = clk_enable(sc->clk_reg);
+ if (rv != 0) {
+ device_printf(sc->dev, "Cannot enable 'reg' clock\n");
+ return (ENXIO);
+ }
+ if (OF_hasprop(node, "nvidia,has-utmi-pad-registers"))
+ sc->have_utmi_regs = true;
+
+ sc->dr_mode = usb_get_dr_mode(dev, node, "dr_mode");
+ if (sc->dr_mode == USB_DR_MODE_UNKNOWN)
+ sc->dr_mode = USB_DR_MODE_HOST;
+
+ sc->ifc_type = usb_get_ifc_mode(dev, node, "phy_type");
+
+ /* We supports only utmi phy mode for now .... */
+ if (sc->ifc_type != USB_IFC_TYPE_UTMI) {
+ device_printf(dev, "Unsupported phy type\n");
+ return (ENXIO);
+ }
+ rv = usbphy_utmi_read_params(sc, node);
+ if (rv < 0)
+ return rv;
+
+ if (OF_hasprop(node, "vbus-supply")) {
+ rv = regulator_get_by_ofw_property(sc->dev, "vbus-supply",
+ &sc->supply_vbus);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot get \"vbus\" regulator\n");
+ return (ENXIO);
+ }
+ rv = regulator_enable(sc->supply_vbus);
+ if (rv != 0) {
+ device_printf(sc->dev,
+ "Cannot enable \"vbus\" regulator\n");
+ return (rv);
+ }
+ }
+
+ phy_register_provider(dev);
+ return (0);
+}
+
+static int
+usbphy_detach(device_t dev)
+{
+
+ /* This device is always present. */
+ return (EBUSY);
+}
+
+static device_method_t tegra_usbphy_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, usbphy_probe),
+ DEVMETHOD(device_attach, usbphy_attach),
+ DEVMETHOD(device_detach, usbphy_detach),
+
+ /* phy interface */
+ DEVMETHOD(phy_enable, usbphy_phy_enable),
+
+ DEVMETHOD_END
+};
+
+static driver_t tegra_usbphy_driver = {
+ "tegra_usbphy",
+ tegra_usbphy_methods,
+ sizeof(struct usbphy_softc),
+};
+
+static devclass_t tegra_usbphy_devclass;
+
+EARLY_DRIVER_MODULE(tegra_usbphy, simplebus, tegra_usbphy_driver,
+ tegra_usbphy_devclass, 0, 0, 79);
OpenPOWER on IntegriCloud