/* $NetBSD: tegra210_xusbpad.c,v 1.16 2021/08/07 16:18:44 thorpej Exp $ */ /*- * Copyright (c) 2017 Jared McNeill * 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 __KERNEL_RCSID(0, "$NetBSD: tegra210_xusbpad.c,v 1.16 2021/08/07 16:18:44 thorpej Exp $"); #include #include #include #include #include #include #include #include #include #include #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);