/* $NetBSD: tegra210_xusbpad.c,v 1.16 2021/08/07 16:18:44 thorpej Exp $ */
/*-
* Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
* 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 ``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 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>
__KERNEL_RCSID(0, "$NetBSD: tegra210_xusbpad.c,v 1.16 2021/08/07 16:18:44 thorpej Exp $");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/intr.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <arm/nvidia/tegra_reg.h>
#include <arm/nvidia/tegra_var.h>
#include <arm/nvidia/tegra_xusbpad.h>
#include <dev/fdt/fdtvar.h>
#define XUSB_PADCTL_USB2_PAD_MUX_REG 0x04
#define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD __BITS(19,18)
#define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB 1
#define XUSB_PADCTL_VBUS_OC_MAP_REG 0x18
#define XUSB_PADCTL_VBUS_OC_MAP_VBUS_ENABLE(n) __BIT((n) * 5)
#define XUSB_PADCTL_OC_DET_REG 0x1c
#define XUSB_PADCTL_OC_DET_OC_DETECTED_VBUS_PAD(n) __BIT(12 + (n))
#define XUSB_PADCTL_OC_DET_OC_DETECTED(n) __BIT(8 + (n))
#define XUSB_PADCTL_OC_DET_SET_OC_DETECTED(n) __BIT(0 + (n))
#define XUSB_PADCTL_ELPG_PROGRAM_1_REG 0x24
#define XUSB_PADCTL_ELPG_PROGRAM_1_AUX_MUX_LP0_VCORE_DOWN __BIT(31)
#define XUSB_PADCTL_ELPG_PROGRAM_1_AUX_MUX_LP0_CLAMP_EN_EARLY __BIT(30)
#define XUSB_PADCTL_ELPG_PROGRAM_1_AUX_MUX_LP0_CLAMP_EN __BIT(29)
#define XUSB_PADCTL_ELPG_PROGRAM_1_SSPn_ELPG_VCORE_DOWN(n) __BIT((n) * 3 + 2)
#define XUSB_PADCTL_ELPG_PROGRAM_1_SSPn_ELPG_CLAMP_EN_EARLY(n) __BIT((n) * 3 + 1)
#define XUSB_PADCTL_ELPG_PROGRAM_1_SSPn_ELPG_CLAMP_EN(n) __BIT((n) * 3 + 0)
#define XUSB_PADCTL_USB3_PAD_MUX_REG 0x28
#define XUSB_PADCTL_USB3_PAD_MUX_FORCE_SATA_PAD_IDDQ_DISABLE(n) __BIT(8 + (n))
#define XUSB_PADCTL_USB3_PAD_MUX_FORCE_PCIE_PAD_IDDQ_DISABLE(n) __BIT(1 + (n))
#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADn_CTL_1_REG(n) (0x84 + (n) * 0x40)
#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADn_CTL_1_VREG_LEV __BITS(8,7)
#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADn_CTL_1_VREG_FIX18 __BIT(6)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_0_REG(n) (0x88 + (n) * 0x40)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_0_PD_ZI __BIT(29)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_0_PD2 __BIT(27)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_0_PD __BIT(26)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_0_HS_CURR_LEVEL __BITS(5,0)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_1_REG(n) (0x8c + (n) * 0x40)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_1_RPD_CTRL __BITS(30,26)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_1_TERM_RANGE_ADJ __BITS(6,3)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_1_PD_DR __BIT(2)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_1_PD_DISC_OVRD __BIT(1)
#define XUSB_PADCTL_USB2_OTG_PADn_CTL_1_PD_CHRP_OVRD __BIT(0)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_REG 0x284
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_PD __BIT(11)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_HS_DISCON_LEVEL __BITS(5,3)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_HS_SQUELCH_LEVEL __BITS(2,0)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_REG 0x288
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_PD_TRK __BIT(26)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_TRK_DONE_RESET_TIMER __BITS(25,19)
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_TRK_START_TIMER __BITS(18,12)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG 0x360
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_PSDIV __BITS(29,28)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_NDIV __BITS(27,20)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_MDIV __BITS(17,16)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_LOCKDET_STATUS __BIT(15)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_MODE __BITS(9,8)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_BYPASS_ENABLE __BIT(7)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREERUN_ENABLE __BIT(6)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_PWR_OVRD __BIT(4)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_ENABLE __BIT(3)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_SLEEP __BITS(2,1)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_1_IDDQ __BIT(0)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_2_REG 0x364
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_CTRL __BITS(27,4)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_RESET __BIT(3)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_OVRD __BIT(2)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_DONE __BIT(1)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_EN __BIT(0)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_3_REG 0x368
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_3_LOCKDET_CTRL __BITS(27,4)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_3_LOCKDET_RESET __BIT(0)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REG 0x36c
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_4_TCLKOUT_EN __BIT(28)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_4_CLKDIST_CTRL __BITS(23,20)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_4_TXCLKREF_EN __BIT(15)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_4_TXCLKREF_SEL __BITS(13,12)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REFCLKBUF_EN __BIT(8)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REFCLK_SEL __BITS(7,4)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_5_REG 0x370
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_5_DCO_CTRL __BITS(23,16)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_6_REG 0x374
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_7_REG 0x378
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG 0x37c
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_DONE __BIT(31)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_OVRD __BIT(15)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_CLK_EN __BIT(13)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_EN __BIT(12)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_9_REG 0x380
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_10_REG 0x384
#define XUSB_PADCTL_UPHY_PLL_P0_CTL_11_REG 0x388
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG 0x860
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_PSDIV __BITS(29,28)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_NDIV __BITS(27,20)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_MDIV __BITS(17,16)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_LOCKDET_STATUS __BIT(15)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_MODE __BITS(9,8)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_BYPASS_ENABLE __BIT(7)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREERUN_ENABLE __BIT(6)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_PWR_OVRD __BIT(4)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_ENABLE __BIT(3)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_SLEEP __BITS(2,1)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_1_IDDQ __BIT(0)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_2_REG 0x864
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_CTRL __BITS(27,4)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_RESET __BIT(3)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_OVRD __BIT(2)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_DONE __BIT(1)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_EN __BIT(0)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_3_REG 0x868
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_3_LOCKDET_CTRL __BITS(27,4)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_3_LOCKDET_RESET __BIT(0)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REG 0x86c
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_4_TCLKOUT_EN __BIT(28)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_4_CLKDIST_CTRL __BITS(23,20)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_4_TXCLKREF_EN __BIT(15)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_4_TXCLKREF_SEL __BITS(13,12)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REFCLKBUF_EN __BIT(8)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REFCLK_SEL __BITS(7,4)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_5_REG 0x870
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_5_DCO_CTRL __BITS(23,16)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_6_REG 0x874
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_7_REG 0x878
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG 0x87c
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_DONE __BIT(31)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_OVRD __BIT(15)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_CLK_EN __BIT(13)
#define XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_EN __BIT(12)
#define XUSB_PADCTL_UPHY_USB3_PADn_ECTL_1_REG(n) (0xa60 + (n) * 0x40)
#define XUSB_PADCTL_UPHY_USB3_PADn_ECTL_2_TX_TERM_CTRL __BITS(19,18)
#define XUSB_PADCTL_UPHY_USB3_PADn_ECTL_2_REG(n) (0xa64 + (n) * 0x40)
#define XUSB_PADCTL_UPHY_USB3_PADn_ECTL_2_RX_CTLE __BITS(15,0)
#define XUSB_PADCTL_UPHY_USB3_PADn_ECTL_3_REG(n) (0xa68 + (n) * 0x40)
#define XUSB_PADCTL_UPHY_USB3_PADn_ECTL_4_REG(n) (0xa6c + (n) * 0x40)
#define XUSB_PADCTL_UPHY_USB3_PADn_ECTL_4_RX_CDR_CTRL __BITS(31,16)
#define XUSB_PADCTL_UPHY_USB3_PADn_ECTL_6_REG(n) (0xa74 + (n) * 0x40)
#define FUSE_SKUCALIB_REG 0xf0
#define FUSE_SKUCALIB_HS_CURR_LEVEL(n) \
((n) == 0 ? __BITS(6,0) : __BITS(((n) - 1) * 6 + 17, ((n) - 1) * 6 + 11))
#define FUSE_SKUCALIB_HS_TERM_RANGE_ADJ __BITS(10,7)
#define FUSE_USBCALIB_REG 0x250
#define FUSE_USBCALIB_EXT_RPD_CTRL __BITS(4,0)
struct tegra210_xusbpad_softc {
device_t sc_dev;
int sc_phandle;
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
struct fdtbus_reset *sc_rst;
bool sc_enabled;
};
struct tegra210_xusbpad_phy_softc {
device_t sc_dev;
int sc_phandle;
struct tegra210_xusbpad_softc *sc_xusbpad;
};
struct tegra210_xusbpad_phy_attach_args {
struct tegra210_xusbpad_softc *paa_xusbpad;
int paa_phandle;
};
#define RD4(sc, reg) \
bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
#define WR4(sc, reg, val) \
bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
#define SETCLR4(sc, reg, set, clr) \
tegra_reg_set_clear((sc)->sc_bst, (sc)->sc_bsh, (reg), (set), (clr))
static const char * tegra210_xusbpad_usb2_func[] = { "snps", "xusb", "uart" };
static const char * tegra210_xusbpad_hsic_func[] = { "snps", "xusb" };
static const char * tegra210_xusbpad_pcie_func[] = { "pcie-x1", "usb3-ss", "sata", "pcie-x4" };
static void
tegra210_xusbpad_uphy_enable_pcie(struct tegra210_xusbpad_softc *sc)
{
uint32_t val;
int retry;
/* UPHY PLLs */
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_REG,
__SHIFTIN(0x136, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_CTRL),
XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_CTRL);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_5_REG,
__SHIFTIN(0x2a, XUSB_PADCTL_UPHY_PLL_P0_CTL_5_DCO_CTRL),
XUSB_PADCTL_UPHY_PLL_P0_CTL_5_DCO_CTRL);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_1_PWR_OVRD, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_OVRD, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_OVRD, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REG,
__SHIFTIN(0, XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REFCLK_SEL),
XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REFCLK_SEL);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REG,
__SHIFTIN(2, XUSB_PADCTL_UPHY_PLL_P0_CTL_4_TXCLKREF_SEL),
XUSB_PADCTL_UPHY_PLL_P0_CTL_4_TXCLKREF_SEL);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_4_TXCLKREF_EN, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG,
__SHIFTIN(0, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_MDIV),
XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_MDIV);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG,
__SHIFTIN(0x19, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_NDIV),
XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_NDIV);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG,
__SHIFTIN(0, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_PSDIV),
XUSB_PADCTL_UPHY_PLL_P0_CTL_1_FREQ_PSDIV);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG,
0, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_IDDQ);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG,
0, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_SLEEP);
delay(20);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_4_REFCLKBUF_EN, 0);
/* Calibration */
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_EN, 0);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_DONE) != 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout calibrating UPHY PLL (1)\n");
return;
}
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_REG,
0, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_EN);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_DONE) == 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout calibrating UPHY PLL (2)\n");
return;
}
/* Enable the PLL */
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_1_ENABLE, 0);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_P0_CTL_1_LOCKDET_STATUS) != 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout enabling UPHY PLL\n");
return;
}
/* RCAL */
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_EN, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG,
XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_CLK_EN, 0);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_DONE) != 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout calibrating UPHY PLL (3)\n");
return;
}
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG,
0, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_EN);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_DONE) == 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout calibrating UPHY PLL (4)\n");
return;
}
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG,
0, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_CLK_EN);
tegra210_car_xusbio_enable_hw_control();
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_REG,
0, XUSB_PADCTL_UPHY_PLL_P0_CTL_1_PWR_OVRD);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_REG,
0, XUSB_PADCTL_UPHY_PLL_P0_CTL_2_CAL_OVRD);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_REG,
0, XUSB_PADCTL_UPHY_PLL_P0_CTL_8_RCAL_OVRD);
delay(1);
tegra210_car_xusbio_enable_hw_seq();
}
static void
tegra210_xusbpad_lane_enable_pcie(struct tegra210_xusbpad_softc *sc, int index)
{
tegra210_xusbpad_uphy_enable_pcie(sc);
SETCLR4(sc, XUSB_PADCTL_USB3_PAD_MUX_REG,
XUSB_PADCTL_USB3_PAD_MUX_FORCE_PCIE_PAD_IDDQ_DISABLE(index), 0);
}
static void
tegra210_xusbpad_lane_enable_usb2(struct tegra210_xusbpad_softc *sc, int index)
{
uint32_t skucalib, usbcalib;
skucalib = tegra_fuse_read(FUSE_SKUCALIB_REG);
const u_int hs_curr_level = __SHIFTOUT(skucalib, FUSE_SKUCALIB_HS_CURR_LEVEL((u_int)index));
const u_int hs_term_range_adj = __SHIFTOUT(skucalib, FUSE_SKUCALIB_HS_TERM_RANGE_ADJ);
usbcalib = tegra_fuse_read(FUSE_USBCALIB_REG);
const u_int ext_rpd_ctrl = __SHIFTOUT(usbcalib, FUSE_USBCALIB_EXT_RPD_CTRL);
SETCLR4(sc, XUSB_PADCTL_USB2_PAD_MUX_REG,
__SHIFTIN(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB,
XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD),
XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD);
SETCLR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_REG,
__SHIFTIN(0x7, XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_HS_DISCON_LEVEL) |
__SHIFTIN(0x0, XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_HS_SQUELCH_LEVEL),
XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_HS_DISCON_LEVEL |
XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_HS_SQUELCH_LEVEL);
SETCLR4(sc, XUSB_PADCTL_USB2_OTG_PADn_CTL_0_REG(index),
__SHIFTIN(hs_curr_level, XUSB_PADCTL_USB2_OTG_PADn_CTL_0_HS_CURR_LEVEL),
XUSB_PADCTL_USB2_OTG_PADn_CTL_0_HS_CURR_LEVEL |
XUSB_PADCTL_USB2_OTG_PADn_CTL_0_PD |
XUSB_PADCTL_USB2_OTG_PADn_CTL_0_PD2 |
XUSB_PADCTL_USB2_OTG_PADn_CTL_0_PD_ZI);
SETCLR4(sc, XUSB_PADCTL_USB2_OTG_PADn_CTL_1_REG(index),
__SHIFTIN(hs_term_range_adj, XUSB_PADCTL_USB2_OTG_PADn_CTL_1_TERM_RANGE_ADJ) |
__SHIFTIN(ext_rpd_ctrl, XUSB_PADCTL_USB2_OTG_PADn_CTL_1_RPD_CTRL),
XUSB_PADCTL_USB2_OTG_PADn_CTL_1_TERM_RANGE_ADJ |
XUSB_PADCTL_USB2_OTG_PADn_CTL_1_RPD_CTRL |
XUSB_PADCTL_USB2_OTG_PADn_CTL_1_PD_DR |
XUSB_PADCTL_USB2_OTG_PADn_CTL_1_PD_CHRP_OVRD |
XUSB_PADCTL_USB2_OTG_PADn_CTL_1_PD_DISC_OVRD);
SETCLR4(sc, XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADn_CTL_1_REG(index),
XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADn_CTL_1_VREG_FIX18,
XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADn_CTL_1_VREG_LEV);
SETCLR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_REG,
__SHIFTIN(0x1e, XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_TRK_START_TIMER) |
__SHIFTIN(0xa, XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_TRK_DONE_RESET_TIMER),
XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_TRK_START_TIMER |
XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_TRK_DONE_RESET_TIMER);
SETCLR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_REG,
0, XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_PD);
delay(1);
SETCLR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_REG,
0, XUSB_PADCTL_USB2_BIAS_PAD_CTL_1_PD_TRK);
delay(50);
}
static void
tegra210_xusbpad_uphy_enable_sata(struct tegra210_xusbpad_softc *sc)
{
uint32_t val;
int retry;
/* UPHY PLLs */
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_REG,
__SHIFTIN(0x136, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_CTRL),
XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_CTRL);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_5_REG,
__SHIFTIN(0x2a, XUSB_PADCTL_UPHY_PLL_S0_CTL_5_DCO_CTRL),
XUSB_PADCTL_UPHY_PLL_S0_CTL_5_DCO_CTRL);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_1_PWR_OVRD, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_OVRD, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_OVRD, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REG,
__SHIFTIN(0, XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REFCLK_SEL),
XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REFCLK_SEL);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REG,
__SHIFTIN(0, XUSB_PADCTL_UPHY_PLL_S0_CTL_4_TXCLKREF_SEL),
XUSB_PADCTL_UPHY_PLL_S0_CTL_4_TXCLKREF_SEL);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_4_TXCLKREF_EN, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG,
__SHIFTIN(0, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_MDIV),
XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_MDIV);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG,
__SHIFTIN(0x1e, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_NDIV),
XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_NDIV);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG,
__SHIFTIN(0, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_PSDIV),
XUSB_PADCTL_UPHY_PLL_S0_CTL_1_FREQ_PSDIV);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG,
0, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_IDDQ);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG,
0, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_SLEEP);
delay(20);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_4_REFCLKBUF_EN, 0);
/* Calibration */
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_EN, 0);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_DONE) != 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout calibrating UPHY PLL (1)\n");
return;
}
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_REG,
0, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_EN);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_DONE) == 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout calibrating UPHY PLL (2)\n");
return;
}
/* Enable the PLL */
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_1_ENABLE, 0);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_S0_CTL_1_LOCKDET_STATUS) != 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout enabling UPHY PLL\n");
return;
}
/* RCAL */
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_EN, 0);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG,
XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_CLK_EN, 0);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_DONE) != 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout calibrating UPHY PLL (3)\n");
return;
}
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG,
0, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_EN);
for (retry = 10000; retry > 0; retry--) {
delay(2);
val = RD4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG);
if ((val & XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_DONE) == 0)
break;
}
if (retry == 0) {
aprint_error_dev(sc->sc_dev, "timeout calibrating UPHY PLL (4)\n");
return;
}
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG,
0, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_CLK_EN);
tegra210_car_sata_enable_hw_control();
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_REG,
0, XUSB_PADCTL_UPHY_PLL_S0_CTL_1_PWR_OVRD);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_REG,
0, XUSB_PADCTL_UPHY_PLL_S0_CTL_2_CAL_OVRD);
SETCLR4(sc, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_REG,
0, XUSB_PADCTL_UPHY_PLL_S0_CTL_8_RCAL_OVRD);
delay(1);
tegra210_car_sata_enable_hw_seq();
}
static void
tegra210_xusbpad_lane_enable_sata(struct tegra210_xusbpad_softc *sc, int index)
{
tegra210_xusbpad_uphy_enable_sata(sc);
KASSERT(index == 0);
SETCLR4(sc, XUSB_PADCTL_USB3_PAD_MUX_REG,
XUSB_PADCTL_USB3_PAD_MUX_FORCE_SATA_PAD_IDDQ_DISABLE(index), 0);
}
#define XUSBPAD_LANE(n, i, r, m, f, ef) \
{ \
.name = (n), \
.index = (i), \
.reg = (r), \
.mask = (m), \
.funcs = (f), \
.nfuncs = __arraycount(f), \
.enable = (ef) \
}
static const struct tegra210_xusbpad_lane {
const char *name;
int index;
bus_size_t reg;
uint32_t mask;
const char **funcs;
int nfuncs;
void (*enable)(struct tegra210_xusbpad_softc *, int);
} tegra210_xusbpad_lanes[] = {
XUSBPAD_LANE("usb2-0", 0, 0x04, __BITS(1,0), tegra210_xusbpad_usb2_func,
tegra210_xusbpad_lane_enable_usb2),
XUSBPAD_LANE("usb2-1", 1, 0x04, __BITS(3,2), tegra210_xusbpad_usb2_func,
tegra210_xusbpad_lane_enable_usb2),
XUSBPAD_LANE("usb2-2", 2, 0x04, __BITS(5,4), tegra210_xusbpad_usb2_func,
tegra210_xusbpad_lane_enable_usb2),
XUSBPAD_LANE("usb2-3", 3, 0x04, __BITS(7,6), tegra210_xusbpad_usb2_func,
tegra210_xusbpad_lane_enable_usb2),
XUSBPAD_LANE("hsic-0", 0, 0x04, __BIT(14), tegra210_xusbpad_hsic_func,
NULL),
XUSBPAD_LANE("hsic-1", 1, 0x04, __BIT(15), tegra210_xusbpad_hsic_func,
NULL),
XUSBPAD_LANE("pcie-0", 0, 0x28, __BITS(13,12), tegra210_xusbpad_pcie_func,
tegra210_xusbpad_lane_enable_pcie),
XUSBPAD_LANE("pcie-1", 1, 0x28, __BITS(15,14), tegra210_xusbpad_pcie_func,
tegra210_xusbpad_lane_enable_pcie),
XUSBPAD_LANE("pcie-2", 2, 0x28, __BITS(17,16), tegra210_xusbpad_pcie_func,
tegra210_xusbpad_lane_enable_pcie),
XUSBPAD_LANE("pcie-3", 3, 0x28, __BITS(19,18), tegra210_xusbpad_pcie_func,
tegra210_xusbpad_lane_enable_pcie),
XUSBPAD_LANE("pcie-4", 4, 0x28, __BITS(21,20), tegra210_xusbpad_pcie_func,
tegra210_xusbpad_lane_enable_pcie),
XUSBPAD_LANE("pcie-5", 5, 0x28, __BITS(23,22), tegra210_xusbpad_pcie_func,
tegra210_xusbpad_lane_enable_pcie),
XUSBPAD_LANE("pcie-6", 6, 0x28, __BITS(25,24), tegra210_xusbpad_pcie_func,
tegra210_xusbpad_lane_enable_pcie),
XUSBPAD_LANE("sata-0", 0, XUSB_PADCTL_USB3_PAD_MUX_REG, __BITS(31,30),
tegra210_xusbpad_pcie_func, tegra210_xusbpad_lane_enable_sata),
};
#define XUSBPAD_PORT(n, i, r, m, im) \
{ \
.name = (n), \
.index = (i), \
.reg = (r), \
.mask = (m), \
.internal_mask = (im) \
}
struct tegra210_xusbpad_port {
const char *name;
int index;
bus_size_t reg;
uint32_t mask;
uint32_t internal_mask;
};
static const struct tegra210_xusbpad_port tegra210_xusbpad_usb2_ports[] = {
XUSBPAD_PORT("usb2-0", 0, 0x08, __BITS(1,0), __BIT(2)),
XUSBPAD_PORT("usb2-1", 1, 0x08, __BITS(5,4), __BIT(6)),
XUSBPAD_PORT("usb2-2", 2, 0x08, __BITS(9,8), __BIT(10)),
XUSBPAD_PORT("usb2-3", 3, 0x08, __BITS(13,12), __BIT(14)),
};
static const struct tegra210_xusbpad_port tegra210_xusbpad_usb3_ports[] = {
XUSBPAD_PORT("usb3-0", 0, 0x14, __BITS(3,0), __BIT(4)),
XUSBPAD_PORT("usb3-1", 1, 0x14, __BITS(8,5), __BIT(9)),
XUSBPAD_PORT("usb3-2", 2, 0x14, __BITS(13,10), __BIT(14)),
XUSBPAD_PORT("usb3-3", 3, 0x14, __BITS(18,15), __BIT(19)),
};
static const struct tegra210_xusbpad_port tegra210_xusbpad_hsic_ports[] = {
XUSBPAD_PORT("hsic-0", 0, 0, 0, 0),
XUSBPAD_PORT("hsic-1", 1, 0, 0, 0),
};
static int
tegra210_xusbpad_find_func(const struct tegra210_xusbpad_lane *lane,
const char *func)
{
for (int n = 0; n < lane->nfuncs; n++)
if (strcmp(lane->funcs[n], func) == 0)
return n;
return -1;
}
static const struct tegra210_xusbpad_lane *
tegra210_xusbpad_find_lane(const char *name)
{
for (int n = 0; n < __arraycount(tegra210_xusbpad_lanes); n++)
if (strcmp(tegra210_xusbpad_lanes[n].name, name) == 0)
return &tegra210_xusbpad_lanes[n];
return NULL;
}
static void
tegra210_xusbpad_configure_lane(struct tegra210_xusbpad_softc *sc,
int phandle)
{
const struct tegra210_xusbpad_lane *lane;
const char *name, *function;
int func;
name = fdtbus_get_string(phandle, "name");
if (name == NULL) {
aprint_error_dev(sc->sc_dev, "no 'name' property\n");
return;
}
function = fdtbus_get_string(phandle, "nvidia,function");
if (function == NULL) {
aprint_error_dev(sc->sc_dev, "no 'nvidia,function' property\n");
return;
}
lane = tegra210_xusbpad_find_lane(name);
if (lane == NULL) {
aprint_error_dev(sc->sc_dev, "unsupported lane '%s'\n", name);
return;
}
func = tegra210_xusbpad_find_func(lane, function);
if (func == -1) {
aprint_error_dev(sc->sc_dev, "unsupported function '%s'\n", function);
return;
}
aprint_normal_dev(sc->sc_dev, "lane %s: set func %s\n", name, function);
SETCLR4(sc, lane->reg, __SHIFTIN(func, lane->mask), lane->mask);
if (lane->enable)
lane->enable(sc, lane->index);
}
static void
tegra210_xusbpad_configure_pads(struct tegra210_xusbpad_softc *sc,
const char *name)
{
struct fdtbus_reset *rst;
struct clk *clk;
int phandle, child;
/* Search for the pad's node */
phandle = of_find_firstchild_byname(sc->sc_phandle, "pads");
if (phandle == -1) {
aprint_error_dev(sc->sc_dev, "no 'pads' node\n");
return;
}
phandle = of_find_firstchild_byname(phandle, name);
if (phandle == -1) {
aprint_error_dev(sc->sc_dev, "no 'pads/%s' node\n", name);
return;
}
if (!fdtbus_status_okay(phandle))
return; /* pad is disabled */
/* Enable the pad's resources */
if (of_hasprop(phandle, "clocks")) {
clk = fdtbus_clock_get_index(phandle, 0);
if (clk == NULL || clk_enable(clk) != 0) {
aprint_error_dev(sc->sc_dev, "couldn't enable %s's clock\n", name);
return;
}
}
if (of_hasprop(phandle, "resets")) {
rst = fdtbus_reset_get_index(phandle, 0);
if (rst == NULL || fdtbus_reset_deassert(rst) != 0) {
aprint_error_dev(sc->sc_dev, "couldn't de-assert %s's reset\n", name);
return;
}
}
/* Attach PHYs */
phandle = of_find_firstchild_byname(phandle, "lanes");
if (phandle == -1) {
aprint_error_dev(sc->sc_dev, "no 'pads/%s/lanes' node\n", name);
return;
}
for (child = OF_child(phandle); child; child = OF_peer(child)) {
struct tegra210_xusbpad_phy_attach_args paa = {
.paa_xusbpad = sc,
.paa_phandle = child
};
config_found(sc->sc_dev, &paa, NULL, CFARGS_NONE);
}
}
static const struct tegra210_xusbpad_port *
tegra210_xusbpad_find_port(const char *name, const struct tegra210_xusbpad_port *ports,
int nports)
{
for (int n = 0; n < nports; n++)
if (strcmp(name, ports[n].name) == 0)
return &ports[n];
return NULL;
}
static const struct tegra210_xusbpad_port *
tegra210_xusbpad_find_usb2_port(const char *name)
{
return tegra210_xusbpad_find_port(name, tegra210_xusbpad_usb2_ports,
__arraycount(tegra210_xusbpad_usb2_ports));
}
static const struct tegra210_xusbpad_port *
tegra210_xusbpad_find_usb3_port(const char *name)
{
return tegra210_xusbpad_find_port(name, tegra210_xusbpad_usb3_ports,
__arraycount(tegra210_xusbpad_usb3_ports));
}
static const struct tegra210_xusbpad_port *
tegra210_xusbpad_find_hsic_port(const char *name)
{
return tegra210_xusbpad_find_port(name, tegra210_xusbpad_hsic_ports,
__arraycount(tegra210_xusbpad_hsic_ports));
}
static void
tegra210_xusbpad_enable_vbus(struct tegra210_xusbpad_softc *sc,
const struct tegra210_xusbpad_port *port, int phandle)
{
struct fdtbus_regulator *vbus_reg;
if (!of_hasprop(phandle, "vbus-supply"))
return;
vbus_reg = fdtbus_regulator_acquire(phandle, "vbus-supply");
if (vbus_reg == NULL || fdtbus_regulator_enable(vbus_reg) != 0) {
aprint_error_dev(sc->sc_dev,
"couldn't enable vbus regulator for port %s\n",
port->name);
}
}
static void
tegra210_xusbpad_configure_usb2_port(struct tegra210_xusbpad_softc *sc,
int phandle, const struct tegra210_xusbpad_port *port)
{
u_int modeval, internal;
const char *mode;
mode = fdtbus_get_string(phandle, "mode");
if (mode == NULL) {
aprint_error_dev(sc->sc_dev, "no 'mode' property on port %s\n", port->name);
return;
}
if (strcmp(mode, "host") == 0)
modeval = 1;
else if (strcmp(mode, "device") == 0)
modeval = 2;
else if (strcmp(mode, "otg") == 0)
modeval = 3;
else {
aprint_error_dev(sc->sc_dev, "unsupported mode '%s' on port %s\n", mode, port->name);
return;
}
internal = of_hasprop(phandle, "nvidia,internal");
tegra210_xusbpad_enable_vbus(sc, port, phandle);
aprint_normal_dev(sc->sc_dev, "port %s: set mode %s, %s\n", port->name, mode,
internal ? "internal" : "external");
SETCLR4(sc, port->reg, __SHIFTIN(internal, port->internal_mask), port->internal_mask);
SETCLR4(sc, port->reg, __SHIFTIN(modeval, port->mask), port->mask);
}
static void
tegra210_xusbpad_configure_usb3_port(struct tegra210_xusbpad_softc *sc,
int phandle, const struct tegra210_xusbpad_port *port)
{
u_int companion, internal;
if (of_getprop_uint32(phandle, "nvidia,usb2-companion", &companion)) {
aprint_error_dev(sc->sc_dev, "no 'nvidia,usb2-companion' property on port %s\n", port->name);
return;
}
internal = of_hasprop(phandle, "nvidia,internal");
tegra210_xusbpad_enable_vbus(sc, port, phandle);
aprint_normal_dev(sc->sc_dev, "port %s: set companion usb2-%d, %s\n", port->name,
companion, internal ? "internal" : "external");
SETCLR4(sc, port->reg, __SHIFTIN(internal, port->internal_mask), port->internal_mask);
SETCLR4(sc, port->reg, __SHIFTIN(companion, port->mask), port->mask);
SETCLR4(sc, XUSB_PADCTL_UPHY_USB3_PADn_ECTL_1_REG(port->index),
__SHIFTIN(2, XUSB_PADCTL_UPHY_USB3_PADn_ECTL_2_TX_TERM_CTRL),
XUSB_PADCTL_UPHY_USB3_PADn_ECTL_2_TX_TERM_CTRL);
SETCLR4(sc, XUSB_PADCTL_UPHY_USB3_PADn_ECTL_2_REG(port->index),
__SHIFTIN(0xfc, XUSB_PADCTL_UPHY_USB3_PADn_ECTL_2_RX_CTLE),
XUSB_PADCTL_UPHY_USB3_PADn_ECTL_2_RX_CTLE);
WR4(sc, XUSB_PADCTL_UPHY_USB3_PADn_ECTL_3_REG(port->index), 0xc0077f1f);
SETCLR4(sc, XUSB_PADCTL_UPHY_USB3_PADn_ECTL_4_REG(port->index),
__SHIFTIN(0x01c7, XUSB_PADCTL_UPHY_USB3_PADn_ECTL_4_RX_CDR_CTRL),
XUSB_PADCTL_UPHY_USB3_PADn_ECTL_4_RX_CDR_CTRL);
WR4(sc, XUSB_PADCTL_UPHY_USB3_PADn_ECTL_6_REG(port->index), 0xfcf01368);
SETCLR4(sc, XUSB_PADCTL_ELPG_PROGRAM_1_REG,
0, XUSB_PADCTL_ELPG_PROGRAM_1_SSPn_ELPG_CLAMP_EN(port->index));
delay(200);
SETCLR4(sc, XUSB_PADCTL_ELPG_PROGRAM_1_REG,
0, XUSB_PADCTL_ELPG_PROGRAM_1_SSPn_ELPG_CLAMP_EN_EARLY(port->index));
delay(200);
SETCLR4(sc, XUSB_PADCTL_ELPG_PROGRAM_1_REG,
0, XUSB_PADCTL_ELPG_PROGRAM_1_SSPn_ELPG_VCORE_DOWN(port->index));
SETCLR4(sc, XUSB_PADCTL_VBUS_OC_MAP_REG,
XUSB_PADCTL_VBUS_OC_MAP_VBUS_ENABLE(port->index), 0);
}
static void
tegra210_xusbpad_configure_hsic_port(struct tegra210_xusbpad_softc *sc,
int phandle, const struct tegra210_xusbpad_port *port)
{
tegra210_xusbpad_enable_vbus(sc, port, phandle);
}
static void
tegra210_xusbpad_configure_ports(struct tegra210_xusbpad_softc *sc)
{
const struct tegra210_xusbpad_port *port;
const char *port_name;
int phandle, child;
/* Search for the ports node */
phandle = of_find_firstchild_byname(sc->sc_phandle, "ports");
/* Configure ports */
for (child = OF_child(phandle); child; child = OF_peer(child)) {
if (!fdtbus_status_okay(child))
continue;
port_name = fdtbus_get_string(child, "name");
if ((port = tegra210_xusbpad_find_usb2_port(port_name)) != NULL)
tegra210_xusbpad_configure_usb2_port(sc, child, port);
else if ((port = tegra210_xusbpad_find_usb3_port(port_name)) != NULL)
tegra210_xusbpad_configure_usb3_port(sc, child, port);
else if ((port = tegra210_xusbpad_find_hsic_port(port_name)) != NULL)
tegra210_xusbpad_configure_hsic_port(sc, child, port);
else
aprint_error_dev(sc->sc_dev, "unsupported port '%s'\n", port_name);
}
}
static void
tegra210_xusbpad_enable(struct tegra210_xusbpad_softc *sc)
{
if (sc->sc_enabled)
return;
SETCLR4(sc, XUSB_PADCTL_ELPG_PROGRAM_1_REG, 0, XUSB_PADCTL_ELPG_PROGRAM_1_AUX_MUX_LP0_CLAMP_EN);
delay(200);
SETCLR4(sc, XUSB_PADCTL_ELPG_PROGRAM_1_REG, 0, XUSB_PADCTL_ELPG_PROGRAM_1_AUX_MUX_LP0_CLAMP_EN_EARLY);
delay(200);
SETCLR4(sc, XUSB_PADCTL_ELPG_PROGRAM_1_REG, 0, XUSB_PADCTL_ELPG_PROGRAM_1_AUX_MUX_LP0_VCORE_DOWN);
sc->sc_enabled = true;
}
static void
tegra210_xusbpad_sata_enable(device_t dev)
{
struct tegra210_xusbpad_softc * const sc = device_private(dev);
tegra210_xusbpad_enable(sc);
}
static void
tegra210_xusbpad_xhci_enable(device_t dev)
{
struct tegra210_xusbpad_softc * const sc = device_private(dev);
tegra210_xusbpad_enable(sc);
}
static const struct tegra_xusbpad_ops tegra210_xusbpad_ops = {
.sata_enable = tegra210_xusbpad_sata_enable,
.xhci_enable = tegra210_xusbpad_xhci_enable,
};
static const struct device_compatible_entry compat_data[] = {
{ .compat = "nvidia,tegra210-xusb-padctl" },
DEVICE_COMPAT_EOL
};
static int
tegra210_xusbpad_match(device_t parent, cfdata_t cf, void *aux)
{
struct fdt_attach_args * const faa = aux;
return of_compatible_match(faa->faa_phandle, compat_data);
}
static void
tegra210_xusbpad_attach(device_t parent, device_t self, void *aux)
{
struct tegra210_xusbpad_softc * const sc = device_private(self);
struct fdt_attach_args * const faa = aux;
bus_addr_t addr;
bus_size_t size;
int error;
if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
aprint_error(": couldn't get registers\n");
return;
}
sc->sc_rst = fdtbus_reset_get(faa->faa_phandle, "padctl");
if (sc->sc_rst == NULL) {
aprint_error(": couldn't get reset padctl\n");
return;
}
sc->sc_dev = self;
sc->sc_phandle = faa->faa_phandle;
sc->sc_bst = faa->faa_bst;
error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
if (error) {
aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, error);
return;
}
aprint_naive("\n");
aprint_normal(": XUSB PADCTL\n");
fdtbus_reset_deassert(sc->sc_rst);
tegra_xusbpad_register(self, &tegra210_xusbpad_ops);
tegra210_xusbpad_configure_pads(sc, "usb2");
tegra210_xusbpad_configure_pads(sc, "hsic");
tegra210_xusbpad_configure_pads(sc, "pcie");
tegra210_xusbpad_configure_pads(sc, "sata");
tegra210_xusbpad_configure_ports(sc);
}
static void *
tegra210_xusbpad_phy_acquire(device_t dev, const void *data, size_t len)
{
struct tegra210_xusbpad_phy_softc * const sc = device_private(dev);
if (len != 0)
return NULL;
return sc;
}
static void
tegra210_xusbpad_phy_release(device_t dev, void *priv)
{
};
static int
tegra210_xusbpad_phy_enable(device_t dev, void *priv, bool enable)
{
struct tegra210_xusbpad_phy_softc * const sc = device_private(dev);
if (enable == false)
return ENXIO; /* not implemented */
tegra210_xusbpad_configure_lane(sc->sc_xusbpad, sc->sc_phandle);
return 0;
}
static const struct fdtbus_phy_controller_func tegra210_xusbpad_phy_funcs = {
.acquire = tegra210_xusbpad_phy_acquire,
.release = tegra210_xusbpad_phy_release,
.enable = tegra210_xusbpad_phy_enable,
};
CFATTACH_DECL_NEW(tegra210_xusbpad, sizeof(struct tegra210_xusbpad_softc),
tegra210_xusbpad_match, tegra210_xusbpad_attach, NULL, NULL);
static int
tegra210_xusbpad_phy_match(device_t parent, cfdata_t cf, void *aux)
{
struct tegra210_xusbpad_phy_attach_args * const paa = aux;
if (!fdtbus_status_okay(paa->paa_phandle))
return 0;
return 1;
}
static void
tegra210_xusbpad_phy_attach(device_t parent, device_t self, void *aux)
{
struct tegra210_xusbpad_phy_softc * const sc = device_private(self);
struct tegra210_xusbpad_phy_attach_args * const paa = aux;
sc->sc_dev = self;
sc->sc_phandle = paa->paa_phandle;
sc->sc_xusbpad = paa->paa_xusbpad;
aprint_naive("\n");
aprint_normal(": %s\n", fdtbus_get_string(sc->sc_phandle, "name"));
fdtbus_register_phy_controller(self, sc->sc_phandle, &tegra210_xusbpad_phy_funcs);
}
CFATTACH_DECL_NEW(tegra210xphy, sizeof(struct tegra210_xusbpad_phy_softc),
tegra210_xusbpad_phy_match, tegra210_xusbpad_phy_attach, NULL, NULL);