1ef2ee5d0SMichal Meloun /*- 2ef2ee5d0SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3ef2ee5d0SMichal Meloun * All rights reserved. 4ef2ee5d0SMichal Meloun * 5ef2ee5d0SMichal Meloun * Redistribution and use in source and binary forms, with or without 6ef2ee5d0SMichal Meloun * modification, are permitted provided that the following conditions 7ef2ee5d0SMichal Meloun * are met: 8ef2ee5d0SMichal Meloun * 1. Redistributions of source code must retain the above copyright 9ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer. 10ef2ee5d0SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 11ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer in the 12ef2ee5d0SMichal Meloun * documentation and/or other materials provided with the distribution. 13ef2ee5d0SMichal Meloun * 14ef2ee5d0SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ef2ee5d0SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ef2ee5d0SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ef2ee5d0SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ef2ee5d0SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ef2ee5d0SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ef2ee5d0SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ef2ee5d0SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ef2ee5d0SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ef2ee5d0SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ef2ee5d0SMichal Meloun * SUCH DAMAGE. 25ef2ee5d0SMichal Meloun */ 26ef2ee5d0SMichal Meloun 27f8759facSMichal Meloun #include <sys/cdefs.h> 28f8759facSMichal Meloun __FBSDID("$FreeBSD$"); 29f8759facSMichal Meloun 30ef2ee5d0SMichal Meloun #include <sys/param.h> 31ef2ee5d0SMichal Meloun #include <sys/systm.h> 32ef2ee5d0SMichal Meloun #include <sys/bus.h> 33ef2ee5d0SMichal Meloun #include <sys/kernel.h> 34ef2ee5d0SMichal Meloun #include <sys/module.h> 35ef2ee5d0SMichal Meloun #include <sys/malloc.h> 36ef2ee5d0SMichal Meloun #include <sys/rman.h> 37ef2ee5d0SMichal Meloun 38ef2ee5d0SMichal Meloun #include <machine/bus.h> 39ef2ee5d0SMichal Meloun #include <machine/fdt.h> 40ef2ee5d0SMichal Meloun 41ef2ee5d0SMichal Meloun #include <dev/extres/hwreset/hwreset.h> 42ef2ee5d0SMichal Meloun #include <dev/extres/phy/phy.h> 4359b591b1SMichal Meloun #include <dev/extres/regulator/regulator.h> 44ef2ee5d0SMichal Meloun #include <dev/fdt/fdt_common.h> 45ef2ee5d0SMichal Meloun #include <dev/fdt/fdt_pinctrl.h> 46ef2ee5d0SMichal Meloun #include <dev/ofw/openfirm.h> 47ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h> 48ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 49ef2ee5d0SMichal Meloun 5059b591b1SMichal Meloun #include <arm/nvidia/tegra_efuse.h> 5159b591b1SMichal Meloun 52ef2ee5d0SMichal Meloun #include <gnu/dts/include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h> 53ef2ee5d0SMichal Meloun 54f8759facSMichal Meloun #include "phydev_if.h" 55ef2ee5d0SMichal Meloun 5659b591b1SMichal Meloun /* FUSE calibration data. */ 5759b591b1SMichal Meloun #define FUSE_XUSB_CALIB 0x0F0 5859b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(x) (((x) >> 15) & 0x3F); 5959b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_IREF_CAP(x) (((x) >> 13) & 0x03); 6059b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(x) (((x) >> 11) & 0x03); 6159b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(x) (((x) >> 7) & 0x0F); 6259b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(x) (((x) >> 0) & 0x3F); 6359b591b1SMichal Meloun 6459b591b1SMichal Meloun 6559b591b1SMichal Meloun /* Registers. */ 66ef2ee5d0SMichal Meloun #define XUSB_PADCTL_USB2_PAD_MUX 0x004 67ef2ee5d0SMichal Meloun 6859b591b1SMichal Meloun #define XUSB_PADCTL_USB2_PORT_CAP 0x008 6959b591b1SMichal Meloun #define USB2_PORT_CAP_ULPI_PORT_INTERNAL (1 << 25) 7059b591b1SMichal Meloun #define USB2_PORT_CAP_ULPI_PORT_CAP (1 << 24) 7159b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_REVERSE_ID(p) (1 << (3 + (p) * 4)) 7259b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_INTERNAL(p) (1 << (2 + (p) * 4)) 7359b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP(p, x) (((x) & 3) << ((p) * 4)) 7459b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP_OTG 0x3 7559b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP_DEVICE 0x2 7659b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP_HOST 0x1 7759b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP_DISABLED 0x0 7859b591b1SMichal Meloun 7959b591b1SMichal Meloun #define XUSB_PADCTL_SS_PORT_MAP 0x014 8059b591b1SMichal Meloun #define SS_PORT_MAP_PORT_INTERNAL(p) (1 << (3 + (p) * 4)) 8159b591b1SMichal Meloun #define SS_PORT_MAP_PORT_MAP(p, x) (((x) & 7) << ((p) * 4)) 8259b591b1SMichal Meloun 83ef2ee5d0SMichal Meloun #define XUSB_PADCTL_ELPG_PROGRAM 0x01C 84ef2ee5d0SMichal Meloun #define ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26) 85ef2ee5d0SMichal Meloun #define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25) 86ef2ee5d0SMichal Meloun #define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24) 8759b591b1SMichal Meloun #define ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4)) 8859b591b1SMichal Meloun #define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(x) (1 << (17 + (x) * 4)) 8959b591b1SMichal Meloun #define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4)) 90ef2ee5d0SMichal Meloun 91ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040 92ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19) 9359b591b1SMichal Meloun #define IOPHY_PLL_P0_CTL1_REFCLK_SEL(x) (((x) & 0xF) << 12) 94ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1) 95ef2ee5d0SMichal Meloun 96ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044 97ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6) 98ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5) 99ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4) 100ef2ee5d0SMichal Meloun 10159b591b1SMichal Meloun #define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(x) (0x058 + (x) * 4) 10259b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_CDR_CNTL(x) (((x) & 0x00FF) << 4) 10359b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_RX_EQ(x) (((x) & 0xFFFF) << 8) 10459b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_RX_WANDER(x) (((x) & 0x000F) << 4) 10559b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_RX_TERM_CNTL(x) (((x) & 0x0003) << 2) 10659b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_TX_TERM_CNTL(x) (((x) & 0x0003) << 0) 10759b591b1SMichal Meloun 10859b591b1SMichal Meloun 10959b591b1SMichal Meloun #define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(x) (0x068 + (x) * 4) 11059b591b1SMichal Meloun 11159b591b1SMichal Meloun #define XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) (0x0A0 + (x) * 4) 11259b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_LSBIAS_SEL (1 << 23) 11359b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_DISCON_DETECT_METHOD (1 << 22) 11459b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_PD_ZI (1 << 21) 11559b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_PD2 (1 << 20) 11659b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_PD (1 << 19) 11759b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_TERM_EN (1 << 18) 11859b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_LS_LS_FSLEW(x) (((x) & 0x03) << 16) 11959b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_LS_RSLEW(x) (((x) & 0x03) << 14) 12059b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_FS_SLEW(x) (((x) & 0x03) << 12) 12159b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_HS_SLEW(x) (((x) & 0x3F) << 6) 12259b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x) (((x) & 0x3F) << 0) 12359b591b1SMichal Meloun 12459b591b1SMichal Meloun #define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) (0x0AC + (x) * 4) 12559b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x) (((x) & 0x3) << 11) 12659b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_HS_IREF_CAP(x) (((x) & 0x3) << 9) 12759b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_SPARE(x) (((x) & 0x3) << 7) 12859b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x) (((x) & 0xF) << 3) 12959b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_PD_DR (1 << 2) 13059b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1) 13159b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0) 13259b591b1SMichal Meloun 13359b591b1SMichal Meloun #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0B8 13459b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_ADJRPU(x) (((x) & 0x7) << 14) 13559b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_PD_TRK (1 << 13) 13659b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_PD (1 << 12) 13759b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x) (((x) & 0x3) << 9) 13859b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_VBUS_LEVEL(x) (((x) & 0x3) << 7) 13959b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x) (((x) & 0x3) << 5) 14059b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x) (((x) & 0x7) << 2) 14159b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x) (((x) & 0x3) << 0) 14259b591b1SMichal Meloun 14359b591b1SMichal Meloun #define XUSB_PADCTL_HSIC_PAD0_CTL0 0x0C8 14459b591b1SMichal Meloun #define HSIC_PAD0_CTL0_HSIC_OPT(x) (((x) & 0xF) << 16) 14559b591b1SMichal Meloun #define HSIC_PAD0_CTL0_TX_SLEWN(x) (((x) & 0xF) << 12) 14659b591b1SMichal Meloun #define HSIC_PAD0_CTL0_TX_SLEWP(x) (((x) & 0xF) << 8) 14759b591b1SMichal Meloun #define HSIC_PAD0_CTL0_TX_RTUNEN(x) (((x) & 0xF) << 4) 14859b591b1SMichal Meloun #define HSIC_PAD0_CTL0_TX_RTUNEP(x) (((x) & 0xF) << 0) 149ef2ee5d0SMichal Meloun 150ef2ee5d0SMichal Meloun #define XUSB_PADCTL_USB3_PAD_MUX 0x134 15159b591b1SMichal Meloun #define USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x))) 15259b591b1SMichal Meloun #define USB3_PAD_MUX_SATA_IDDQ_DISABLE (1 << 6) 15359b591b1SMichal Meloun 154ef2ee5d0SMichal Meloun 155ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138 156ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27) 157ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24) 158ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3) 159ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL_RST_L (1 << 1) 160ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0) 161ef2ee5d0SMichal Meloun 162ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13C 163ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140 164ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_S0_CTL4 0x144 165ef2ee5d0SMichal Meloun 166ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148 167ef2ee5d0SMichal Meloun #define IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1) 168ef2ee5d0SMichal Meloun #define IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0) 169ef2ee5d0SMichal Meloun 170ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14C 171ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL3 0x150 172ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL4 0x154 173ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158 174ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15C 175ef2ee5d0SMichal Meloun 176ef2ee5d0SMichal Meloun 17759b591b1SMichal Meloun #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) 17859b591b1SMichal Meloun #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) 17959b591b1SMichal Meloun 18059b591b1SMichal Meloun 18159b591b1SMichal Meloun struct padctl_softc { 182ef2ee5d0SMichal Meloun device_t dev; 183ef2ee5d0SMichal Meloun struct resource *mem_res; 184ef2ee5d0SMichal Meloun hwreset_t rst; 185ef2ee5d0SMichal Meloun int phy_ena_cnt; 18659b591b1SMichal Meloun 18759b591b1SMichal Meloun /* Fuses calibration data */ 18859b591b1SMichal Meloun uint32_t hs_curr_level_0; 18959b591b1SMichal Meloun uint32_t hs_curr_level_123; 19059b591b1SMichal Meloun uint32_t hs_iref_cap; 19159b591b1SMichal Meloun uint32_t hs_term_range_adj; 19259b591b1SMichal Meloun uint32_t hs_squelch_level; 19359b591b1SMichal Meloun 19459b591b1SMichal Meloun uint32_t hs_curr_level_offset; 195ef2ee5d0SMichal Meloun }; 196ef2ee5d0SMichal Meloun 197ef2ee5d0SMichal Meloun static struct ofw_compat_data compat_data[] = { 198ef2ee5d0SMichal Meloun {"nvidia,tegra124-xusb-padctl", 1}, 199ef2ee5d0SMichal Meloun {NULL, 0}, 200ef2ee5d0SMichal Meloun }; 201ef2ee5d0SMichal Meloun 20259b591b1SMichal Meloun /* Ports. */ 20359b591b1SMichal Meloun enum padctl_port_type { 20459b591b1SMichal Meloun PADCTL_PORT_USB2, 20559b591b1SMichal Meloun PADCTL_PORT_ULPI, 20659b591b1SMichal Meloun PADCTL_PORT_HSIC, 20759b591b1SMichal Meloun PADCTL_PORT_USB3, 20859b591b1SMichal Meloun }; 20959b591b1SMichal Meloun 21059b591b1SMichal Meloun struct padctl_lane; 21159b591b1SMichal Meloun struct padctl_port { 21259b591b1SMichal Meloun enum padctl_port_type type; 21359b591b1SMichal Meloun const char *name; 21459b591b1SMichal Meloun const char *base_name; 21559b591b1SMichal Meloun int idx; 21659b591b1SMichal Meloun int (*init)(struct padctl_softc *sc, 21759b591b1SMichal Meloun struct padctl_port *port); 21859b591b1SMichal Meloun 21959b591b1SMichal Meloun /* Runtime data. */ 22059b591b1SMichal Meloun bool enabled; 22159b591b1SMichal Meloun regulator_t supply_vbus; /* USB2, USB3 */ 22259b591b1SMichal Meloun bool internal; /* ULPI, USB2, USB3 */ 22359b591b1SMichal Meloun uint32_t companion; /* USB3 */ 22459b591b1SMichal Meloun struct padctl_lane *lane; 22559b591b1SMichal Meloun }; 22659b591b1SMichal Meloun 22759b591b1SMichal Meloun static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port); 22859b591b1SMichal Meloun 22959b591b1SMichal Meloun #define PORT(t, n, p, i) { \ 23059b591b1SMichal Meloun .type = t, \ 23159b591b1SMichal Meloun .name = n "-" #p, \ 23259b591b1SMichal Meloun .base_name = n, \ 23359b591b1SMichal Meloun .idx = p, \ 23459b591b1SMichal Meloun .init = i, \ 23559b591b1SMichal Meloun } 23659b591b1SMichal Meloun static struct padctl_port ports_tbl[] = { 23759b591b1SMichal Meloun PORT(PADCTL_PORT_USB2, "usb2", 0, NULL), 23859b591b1SMichal Meloun PORT(PADCTL_PORT_USB2, "usb2", 1, NULL), 23959b591b1SMichal Meloun PORT(PADCTL_PORT_USB2, "usb2", 2, NULL), 24059b591b1SMichal Meloun PORT(PADCTL_PORT_ULPI, "ulpi", 0, NULL), 24159b591b1SMichal Meloun PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL), 24259b591b1SMichal Meloun PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL), 24359b591b1SMichal Meloun PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init), 24459b591b1SMichal Meloun PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init), 24559b591b1SMichal Meloun }; 24659b591b1SMichal Meloun 24759b591b1SMichal Meloun /* Pads - a group of lannes. */ 24859b591b1SMichal Meloun enum padctl_pad_type { 24959b591b1SMichal Meloun PADCTL_PAD_USB2, 25059b591b1SMichal Meloun PADCTL_PAD_ULPI, 25159b591b1SMichal Meloun PADCTL_PAD_HSIC, 25259b591b1SMichal Meloun PADCTL_PAD_PCIE, 25359b591b1SMichal Meloun PADCTL_PAD_SATA, 25459b591b1SMichal Meloun }; 25559b591b1SMichal Meloun 25659b591b1SMichal Meloun struct padctl_lane; 25759b591b1SMichal Meloun struct padctl_pad { 25859b591b1SMichal Meloun const char *name; 25959b591b1SMichal Meloun enum padctl_pad_type type; 26059b591b1SMichal Meloun int (*powerup)(struct padctl_softc *sc, 26159b591b1SMichal Meloun struct padctl_lane *lane); 26259b591b1SMichal Meloun int (*powerdown)(struct padctl_softc *sc, 26359b591b1SMichal Meloun struct padctl_lane *lane); 26459b591b1SMichal Meloun /* Runtime data. */ 26559b591b1SMichal Meloun bool enabled; 26659b591b1SMichal Meloun struct padctl_lane *lanes[8]; /* Safe maximum value. */ 26759b591b1SMichal Meloun int nlanes; 26859b591b1SMichal Meloun }; 26959b591b1SMichal Meloun 27059b591b1SMichal Meloun static int usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane); 27159b591b1SMichal Meloun static int usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); 27259b591b1SMichal Meloun static int pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane); 27359b591b1SMichal Meloun static int pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); 27459b591b1SMichal Meloun static int sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane); 27559b591b1SMichal Meloun static int sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane); 27659b591b1SMichal Meloun 27759b591b1SMichal Meloun #define PAD(n, t, u, d) { \ 27859b591b1SMichal Meloun .name = n, \ 27959b591b1SMichal Meloun .type = t, \ 28059b591b1SMichal Meloun .powerup = u, \ 28159b591b1SMichal Meloun .powerdown = d, \ 28259b591b1SMichal Meloun } 28359b591b1SMichal Meloun static struct padctl_pad pads_tbl[] = { 28459b591b1SMichal Meloun PAD("usb2", PADCTL_PAD_USB2, usb2_powerup, usb2_powerdown), 28559b591b1SMichal Meloun PAD("ulpi", PADCTL_PAD_ULPI, NULL, NULL), 28659b591b1SMichal Meloun PAD("hsic", PADCTL_PAD_HSIC, NULL, NULL), 28759b591b1SMichal Meloun PAD("pcie", PADCTL_PAD_PCIE, pcie_powerup, pcie_powerdown), 28859b591b1SMichal Meloun PAD("sata", PADCTL_PAD_SATA, sata_powerup, sata_powerdown), 28959b591b1SMichal Meloun }; 29059b591b1SMichal Meloun 29159b591b1SMichal Meloun /* Lanes. */ 29259b591b1SMichal Meloun static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"}; 29359b591b1SMichal Meloun static char *usb_mux[] = {"snps", "xusb"}; 29459b591b1SMichal Meloun static char *pci_mux[] = {"pcie", "usb3-ss", "sata", "rsvd"}; 29559b591b1SMichal Meloun 296ef2ee5d0SMichal Meloun struct padctl_lane { 297ef2ee5d0SMichal Meloun const char *name; 29859b591b1SMichal Meloun int idx; 299ef2ee5d0SMichal Meloun bus_size_t reg; 300ef2ee5d0SMichal Meloun uint32_t shift; 301ef2ee5d0SMichal Meloun uint32_t mask; 302ef2ee5d0SMichal Meloun char **mux; 303ef2ee5d0SMichal Meloun int nmux; 30459b591b1SMichal Meloun /* Runtime data. */ 30559b591b1SMichal Meloun bool enabled; 30659b591b1SMichal Meloun struct padctl_pad *pad; 30759b591b1SMichal Meloun struct padctl_port *port; 30859b591b1SMichal Meloun int mux_idx; 30959b591b1SMichal Meloun 310ef2ee5d0SMichal Meloun }; 311ef2ee5d0SMichal Meloun 31259b591b1SMichal Meloun #define LANE(n, p, r, s, m, mx) { \ 31359b591b1SMichal Meloun .name = n "-" #p, \ 31459b591b1SMichal Meloun .idx = p, \ 315ef2ee5d0SMichal Meloun .reg = r, \ 316ef2ee5d0SMichal Meloun .shift = s, \ 317ef2ee5d0SMichal Meloun .mask = m, \ 318ef2ee5d0SMichal Meloun .mux = mx, \ 319ef2ee5d0SMichal Meloun .nmux = nitems(mx), \ 320ef2ee5d0SMichal Meloun } 32159b591b1SMichal Meloun static struct padctl_lane lanes_tbl[] = { 32259b591b1SMichal Meloun LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, otg_mux), 32359b591b1SMichal Meloun LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, otg_mux), 32459b591b1SMichal Meloun LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, otg_mux), 32559b591b1SMichal Meloun LANE("ulpi", 0, XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, usb_mux), 32659b591b1SMichal Meloun LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, usb_mux), 32759b591b1SMichal Meloun LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, usb_mux), 32859b591b1SMichal Meloun LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux), 32959b591b1SMichal Meloun LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux), 33059b591b1SMichal Meloun LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux), 33159b591b1SMichal Meloun LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux), 33259b591b1SMichal Meloun LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux), 33359b591b1SMichal Meloun LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, pci_mux), 334ef2ee5d0SMichal Meloun }; 335ef2ee5d0SMichal Meloun 33659b591b1SMichal Meloun /* Define all possible mappings for USB3 port lanes */ 33759b591b1SMichal Meloun struct padctl_lane_map { 33859b591b1SMichal Meloun int port_idx; 33959b591b1SMichal Meloun enum padctl_pad_type pad_type; 34059b591b1SMichal Meloun int lane_idx; 34159b591b1SMichal Meloun }; 342ef2ee5d0SMichal Meloun 34359b591b1SMichal Meloun #define LANE_MAP(pi, pt, li) { \ 34459b591b1SMichal Meloun .port_idx = pi, \ 34559b591b1SMichal Meloun .pad_type = pt, \ 34659b591b1SMichal Meloun .lane_idx = li, \ 347ef2ee5d0SMichal Meloun } 34859b591b1SMichal Meloun static struct padctl_lane_map lane_map_tbl[] = { 34959b591b1SMichal Meloun LANE_MAP(0, PADCTL_PAD_PCIE, 0), /* port USB3-0 -> lane PCIE-0 */ 35059b591b1SMichal Meloun LANE_MAP(1, PADCTL_PAD_PCIE, 1), /* port USB3-1 -> lane PCIE-1 */ 35159b591b1SMichal Meloun /* -- or -- */ 35259b591b1SMichal Meloun LANE_MAP(1, PADCTL_PAD_SATA, 0), /* port USB3-1 -> lane SATA-0 */ 35359b591b1SMichal Meloun }; 354ef2ee5d0SMichal Meloun 355f8759facSMichal Meloun /* Phy class and methods. */ 356f8759facSMichal Meloun static int xusbpadctl_phy_enable(struct phynode *phy, bool enable); 357f8759facSMichal Meloun static phynode_method_t xusbpadctl_phynode_methods[] = { 358f8759facSMichal Meloun PHYNODEMETHOD(phynode_enable, xusbpadctl_phy_enable), 359f8759facSMichal Meloun PHYNODEMETHOD_END 360f8759facSMichal Meloun 361f8759facSMichal Meloun }; 362f8759facSMichal Meloun DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class, 363f8759facSMichal Meloun xusbpadctl_phynode_methods, 0, phynode_class); 364f8759facSMichal Meloun 36559b591b1SMichal Meloun static struct padctl_port *search_lane_port(struct padctl_softc *sc, 36659b591b1SMichal Meloun struct padctl_lane *lane); 36759b591b1SMichal Meloun /* ------------------------------------------------------------------------- 36859b591b1SMichal Meloun * 36959b591b1SMichal Meloun * PHY functions 37059b591b1SMichal Meloun */ 371ef2ee5d0SMichal Meloun static int 37259b591b1SMichal Meloun usb3_port_init(struct padctl_softc *sc, struct padctl_port *port) 373ef2ee5d0SMichal Meloun { 374ef2ee5d0SMichal Meloun uint32_t reg; 375ef2ee5d0SMichal Meloun 37659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP); 37759b591b1SMichal Meloun if (port->internal) 37859b591b1SMichal Meloun reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx); 379ef2ee5d0SMichal Meloun else 38059b591b1SMichal Meloun reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx); 38159b591b1SMichal Meloun reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0); 38259b591b1SMichal Meloun reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion); 38359b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg); 384ef2ee5d0SMichal Meloun 38559b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx)); 38659b591b1SMichal Meloun reg &= ~IOPHY_USB3_PAD_CTL2_CDR_CNTL(~0); 38759b591b1SMichal Meloun reg &= ~IOPHY_USB3_PAD_CTL2_RX_EQ(~0); 38859b591b1SMichal Meloun reg &= ~IOPHY_USB3_PAD_CTL2_RX_WANDER(~0); 38959b591b1SMichal Meloun reg |= IOPHY_USB3_PAD_CTL2_CDR_CNTL(0x24); 39059b591b1SMichal Meloun reg |= IOPHY_USB3_PAD_CTL2_RX_EQ(0xF070); 39159b591b1SMichal Meloun reg |= IOPHY_USB3_PAD_CTL2_RX_WANDER(0xF); 39259b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx), reg); 393ef2ee5d0SMichal Meloun 39459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(port->idx), 39559b591b1SMichal Meloun 0x002008EE); 396ef2ee5d0SMichal Meloun 39759b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 39859b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(port->idx); 39959b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 400ef2ee5d0SMichal Meloun DELAY(100); 401ef2ee5d0SMichal Meloun 40259b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 40359b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(port->idx); 40459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 40559b591b1SMichal Meloun DELAY(100); 40659b591b1SMichal Meloun 40759b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 40859b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(port->idx); 40959b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 41059b591b1SMichal Meloun DELAY(100); 41159b591b1SMichal Meloun 41259b591b1SMichal Meloun return (0); 41359b591b1SMichal Meloun } 41459b591b1SMichal Meloun 41559b591b1SMichal Meloun static int 41659b591b1SMichal Meloun pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane) 41759b591b1SMichal Meloun { 41859b591b1SMichal Meloun uint32_t reg; 41959b591b1SMichal Meloun int i; 42059b591b1SMichal Meloun 42159b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); 42259b591b1SMichal Meloun reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL(~0); 42359b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); 42459b591b1SMichal Meloun DELAY(100); 42559b591b1SMichal Meloun 42659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2); 427ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_P0_CTL2_REFCLKBUF_EN; 428ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_EN; 429ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_SEL; 43059b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg); 431ef2ee5d0SMichal Meloun DELAY(100); 432ef2ee5d0SMichal Meloun 43359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); 434ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_P0_CTL1_PLL_RST; 43559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); 436ef2ee5d0SMichal Meloun DELAY(100); 437ef2ee5d0SMichal Meloun 43859b591b1SMichal Meloun for (i = 100; i > 0; i--) { 43959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); 440ef2ee5d0SMichal Meloun if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) 44159b591b1SMichal Meloun break; 442ef2ee5d0SMichal Meloun DELAY(10); 443ef2ee5d0SMichal Meloun } 44459b591b1SMichal Meloun if (i <= 0) { 44559b591b1SMichal Meloun device_printf(sc->dev, "Failed to power up PCIe phy\n"); 446ef2ee5d0SMichal Meloun return (ETIMEDOUT); 447ef2ee5d0SMichal Meloun } 44859b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); 44959b591b1SMichal Meloun reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); 45059b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); 451ef2ee5d0SMichal Meloun 45259b591b1SMichal Meloun return (0); 45359b591b1SMichal Meloun } 454ef2ee5d0SMichal Meloun 455ef2ee5d0SMichal Meloun static int 45659b591b1SMichal Meloun pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) 457ef2ee5d0SMichal Meloun { 458ef2ee5d0SMichal Meloun uint32_t reg; 459ef2ee5d0SMichal Meloun 46059b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); 46159b591b1SMichal Meloun reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx); 46259b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); 46359b591b1SMichal Meloun 46459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1); 465ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST; 46659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg); 467ef2ee5d0SMichal Meloun DELAY(100); 46859b591b1SMichal Meloun 469ef2ee5d0SMichal Meloun return (0); 470ef2ee5d0SMichal Meloun 471ef2ee5d0SMichal Meloun } 472ef2ee5d0SMichal Meloun 473ef2ee5d0SMichal Meloun static int 47459b591b1SMichal Meloun sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane) 475ef2ee5d0SMichal Meloun { 476ef2ee5d0SMichal Meloun uint32_t reg; 477ef2ee5d0SMichal Meloun int i; 478ef2ee5d0SMichal Meloun 47959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); 480ef2ee5d0SMichal Meloun reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; 481ef2ee5d0SMichal Meloun reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ; 48259b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); 483ef2ee5d0SMichal Meloun 48459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); 485ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; 486ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ; 48759b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); 488ef2ee5d0SMichal Meloun 48959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); 490ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE; 49159b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); 492ef2ee5d0SMichal Meloun 49359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); 494ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; 49559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); 496ef2ee5d0SMichal Meloun 497ef2ee5d0SMichal Meloun for (i = 100; i >= 0; i--) { 49859b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); 499ef2ee5d0SMichal Meloun if (reg & IOPHY_PLL_S0_CTL1_PLL1_LOCKDET) 500ef2ee5d0SMichal Meloun break; 501ef2ee5d0SMichal Meloun DELAY(100); 502ef2ee5d0SMichal Meloun } 503ef2ee5d0SMichal Meloun if (i <= 0) { 504ef2ee5d0SMichal Meloun device_printf(sc->dev, "Failed to power up SATA phy\n"); 505ef2ee5d0SMichal Meloun return (ETIMEDOUT); 506ef2ee5d0SMichal Meloun } 50759b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); 50859b591b1SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L; 50959b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); 51059b591b1SMichal Meloun 51159b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); 51259b591b1SMichal Meloun reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE; 51359b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); 514ef2ee5d0SMichal Meloun 515ef2ee5d0SMichal Meloun return (0); 516ef2ee5d0SMichal Meloun } 517ef2ee5d0SMichal Meloun 518ef2ee5d0SMichal Meloun static int 51959b591b1SMichal Meloun sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) 520ef2ee5d0SMichal Meloun { 521ef2ee5d0SMichal Meloun uint32_t reg; 522ef2ee5d0SMichal Meloun 52359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX); 52459b591b1SMichal Meloun reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE; 52559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg); 52659b591b1SMichal Meloun 52759b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); 528ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L; 52959b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); 530ef2ee5d0SMichal Meloun DELAY(100); 531ef2ee5d0SMichal Meloun 53259b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); 533ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE; 53459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); 535ef2ee5d0SMichal Meloun DELAY(100); 536ef2ee5d0SMichal Meloun 53759b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1); 538ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD; 539ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ; 54059b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg); 541ef2ee5d0SMichal Meloun DELAY(100); 542ef2ee5d0SMichal Meloun 54359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1); 544ef2ee5d0SMichal Meloun reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD; 545ef2ee5d0SMichal Meloun reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ; 54659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg); 547ef2ee5d0SMichal Meloun DELAY(100); 548ef2ee5d0SMichal Meloun 549ef2ee5d0SMichal Meloun return (0); 550ef2ee5d0SMichal Meloun } 551ef2ee5d0SMichal Meloun 552ef2ee5d0SMichal Meloun static int 55359b591b1SMichal Meloun usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane) 55459b591b1SMichal Meloun { 55559b591b1SMichal Meloun uint32_t reg; 55659b591b1SMichal Meloun struct padctl_port *port; 55759b591b1SMichal Meloun int rv; 55859b591b1SMichal Meloun 55959b591b1SMichal Meloun port = search_lane_port(sc, lane); 56059b591b1SMichal Meloun if (port == NULL) { 56159b591b1SMichal Meloun device_printf(sc->dev, "Cannot find port for lane: %s\n", 56259b591b1SMichal Meloun lane->name); 56359b591b1SMichal Meloun } 56459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); 56559b591b1SMichal Meloun reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0); 56659b591b1SMichal Meloun reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0); 56759b591b1SMichal Meloun reg |= USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(sc->hs_squelch_level); 56859b591b1SMichal Meloun reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(5); 56959b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); 57059b591b1SMichal Meloun 57159b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP); 57259b591b1SMichal Meloun reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0); 57359b591b1SMichal Meloun reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST); 57459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg); 57559b591b1SMichal Meloun 57659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx)); 57759b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0); 57859b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0); 57959b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_LS_RSLEW(~0); 58059b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_PD; 58159b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_PD2; 58259b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_PD_ZI; 58359b591b1SMichal Meloun 58459b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14); 58559b591b1SMichal Meloun if (lane->idx == 0) { 58659b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_0); 58759b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(3); 58859b591b1SMichal Meloun } else { 58959b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_123); 59059b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(0); 59159b591b1SMichal Meloun } 59259b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg); 59359b591b1SMichal Meloun 59459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx)); 59559b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0); 59659b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_HS_IREF_CAP(~0); 59759b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_PD_DR; 59859b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP; 59959b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP; 60059b591b1SMichal Meloun 60159b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj); 60259b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL1_HS_IREF_CAP(sc->hs_iref_cap); 60359b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg); 60459b591b1SMichal Meloun 60559b591b1SMichal Meloun if (port != NULL && port->supply_vbus != NULL) { 60659b591b1SMichal Meloun rv = regulator_enable(port->supply_vbus); 60759b591b1SMichal Meloun if (rv != 0) { 60859b591b1SMichal Meloun device_printf(sc->dev, 60959b591b1SMichal Meloun "Cannot enable vbus regulator\n"); 61059b591b1SMichal Meloun return (rv); 61159b591b1SMichal Meloun } 61259b591b1SMichal Meloun } 61359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); 61459b591b1SMichal Meloun reg &= ~USB2_BIAS_PAD_CTL0_PD; 61559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); 61659b591b1SMichal Meloun 61759b591b1SMichal Meloun return (0); 61859b591b1SMichal Meloun } 61959b591b1SMichal Meloun 62059b591b1SMichal Meloun static int 62159b591b1SMichal Meloun usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane) 62259b591b1SMichal Meloun { 62359b591b1SMichal Meloun uint32_t reg; 62459b591b1SMichal Meloun struct padctl_port *port; 62559b591b1SMichal Meloun int rv; 62659b591b1SMichal Meloun 62759b591b1SMichal Meloun port = search_lane_port(sc, lane); 62859b591b1SMichal Meloun if (port == NULL) { 62959b591b1SMichal Meloun device_printf(sc->dev, "Cannot find port for lane: %s\n", 63059b591b1SMichal Meloun lane->name); 63159b591b1SMichal Meloun } 63259b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); 63359b591b1SMichal Meloun reg |= USB2_BIAS_PAD_CTL0_PD; 63459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg); 63559b591b1SMichal Meloun 63659b591b1SMichal Meloun if (port != NULL && port->supply_vbus != NULL) { 63759b591b1SMichal Meloun rv = regulator_enable(port->supply_vbus); 63859b591b1SMichal Meloun if (rv != 0) { 63959b591b1SMichal Meloun device_printf(sc->dev, 64059b591b1SMichal Meloun "Cannot disable vbus regulator\n"); 64159b591b1SMichal Meloun return (rv); 64259b591b1SMichal Meloun } 64359b591b1SMichal Meloun } 64459b591b1SMichal Meloun return (0); 64559b591b1SMichal Meloun } 64659b591b1SMichal Meloun 64759b591b1SMichal Meloun 64859b591b1SMichal Meloun static int 64959b591b1SMichal Meloun phy_powerup(struct padctl_softc *sc) 650ef2ee5d0SMichal Meloun { 651ef2ee5d0SMichal Meloun uint32_t reg; 652ef2ee5d0SMichal Meloun 65359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 654ef2ee5d0SMichal Meloun reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; 65559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 65659b591b1SMichal Meloun DELAY(100); 65759b591b1SMichal Meloun 65859b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 65959b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; 66059b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 66159b591b1SMichal Meloun DELAY(100); 66259b591b1SMichal Meloun 66359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 66459b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; 66559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 666ef2ee5d0SMichal Meloun DELAY(100); 667ef2ee5d0SMichal Meloun 668ef2ee5d0SMichal Meloun return (0); 669ef2ee5d0SMichal Meloun } 670ef2ee5d0SMichal Meloun 671ef2ee5d0SMichal Meloun static int 67259b591b1SMichal Meloun phy_powerdown(struct padctl_softc *sc) 673ef2ee5d0SMichal Meloun { 674ef2ee5d0SMichal Meloun uint32_t reg; 675ef2ee5d0SMichal Meloun 67659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 677ef2ee5d0SMichal Meloun reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY; 67859b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 679ef2ee5d0SMichal Meloun DELAY(100); 680ef2ee5d0SMichal Meloun 68159b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 682ef2ee5d0SMichal Meloun reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN; 68359b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 68459b591b1SMichal Meloun DELAY(100); 68559b591b1SMichal Meloun 68659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM); 68759b591b1SMichal Meloun reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN; 68859b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg); 689ef2ee5d0SMichal Meloun DELAY(100); 690ef2ee5d0SMichal Meloun 691ef2ee5d0SMichal Meloun return (0); 692ef2ee5d0SMichal Meloun } 693ef2ee5d0SMichal Meloun 694ef2ee5d0SMichal Meloun static int 695f8759facSMichal Meloun xusbpadctl_phy_enable(struct phynode *phy, bool enable) 696ef2ee5d0SMichal Meloun { 697f8759facSMichal Meloun device_t dev; 698f8759facSMichal Meloun intptr_t id; 69959b591b1SMichal Meloun struct padctl_softc *sc; 70059b591b1SMichal Meloun struct padctl_lane *lane; 70159b591b1SMichal Meloun struct padctl_pad *pad; 702ef2ee5d0SMichal Meloun int rv; 703ef2ee5d0SMichal Meloun 704f8759facSMichal Meloun dev = phynode_get_device(phy); 705f8759facSMichal Meloun id = phynode_get_id(phy); 706ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 707ef2ee5d0SMichal Meloun 70859b591b1SMichal Meloun if (id < 0 || id >= nitems(lanes_tbl)) { 709ef2ee5d0SMichal Meloun device_printf(dev, "Unknown phy: %d\n", id); 710ef2ee5d0SMichal Meloun return (ENXIO); 711ef2ee5d0SMichal Meloun } 71259b591b1SMichal Meloun lane = lanes_tbl + id; 71359b591b1SMichal Meloun if (!lane->enabled) { 71459b591b1SMichal Meloun device_printf(dev, "Lane is not enabled/configured: %s\n", 71559b591b1SMichal Meloun lane->name); 71659b591b1SMichal Meloun return (ENXIO); 71759b591b1SMichal Meloun } 71859b591b1SMichal Meloun pad = lane->pad; 719ef2ee5d0SMichal Meloun if (enable) { 720ef2ee5d0SMichal Meloun if (sc->phy_ena_cnt == 0) { 72159b591b1SMichal Meloun rv = phy_powerup(sc); 722ef2ee5d0SMichal Meloun if (rv != 0) 723ef2ee5d0SMichal Meloun return (rv); 724ef2ee5d0SMichal Meloun } 725ef2ee5d0SMichal Meloun sc->phy_ena_cnt++; 726ef2ee5d0SMichal Meloun } 727ef2ee5d0SMichal Meloun 728ef2ee5d0SMichal Meloun if (enable) 72959b591b1SMichal Meloun rv = pad->powerup(sc, lane); 730ef2ee5d0SMichal Meloun else 73159b591b1SMichal Meloun rv = pad->powerdown(sc, lane); 732ef2ee5d0SMichal Meloun if (rv != 0) 733ef2ee5d0SMichal Meloun return (rv); 73459b591b1SMichal Meloun 735ef2ee5d0SMichal Meloun if (!enable) { 736ef2ee5d0SMichal Meloun if (sc->phy_ena_cnt == 1) { 73759b591b1SMichal Meloun rv = phy_powerdown(sc); 738ef2ee5d0SMichal Meloun if (rv != 0) 739ef2ee5d0SMichal Meloun return (rv); 740ef2ee5d0SMichal Meloun } 741ef2ee5d0SMichal Meloun sc->phy_ena_cnt--; 742ef2ee5d0SMichal Meloun } 743ef2ee5d0SMichal Meloun 744ef2ee5d0SMichal Meloun return (0); 745ef2ee5d0SMichal Meloun } 746ef2ee5d0SMichal Meloun 74759b591b1SMichal Meloun /* ------------------------------------------------------------------------- 74859b591b1SMichal Meloun * 74959b591b1SMichal Meloun * FDT processing 75059b591b1SMichal Meloun */ 75159b591b1SMichal Meloun static struct padctl_port * 75259b591b1SMichal Meloun search_port(struct padctl_softc *sc, char *port_name) 75359b591b1SMichal Meloun { 75459b591b1SMichal Meloun int i; 75559b591b1SMichal Meloun 75659b591b1SMichal Meloun for (i = 0; i < nitems(ports_tbl); i++) { 75759b591b1SMichal Meloun if (strcmp(port_name, ports_tbl[i].name) == 0) 75859b591b1SMichal Meloun return (&ports_tbl[i]); 75959b591b1SMichal Meloun } 76059b591b1SMichal Meloun return (NULL); 76159b591b1SMichal Meloun } 76259b591b1SMichal Meloun 76359b591b1SMichal Meloun static struct padctl_port * 76459b591b1SMichal Meloun search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane) 76559b591b1SMichal Meloun { 76659b591b1SMichal Meloun int i; 76759b591b1SMichal Meloun 76859b591b1SMichal Meloun for (i = 0; i < nitems(ports_tbl); i++) { 76959b591b1SMichal Meloun if (!ports_tbl[i].enabled) 77059b591b1SMichal Meloun continue; 77159b591b1SMichal Meloun if (ports_tbl[i].lane == lane) 77259b591b1SMichal Meloun return (ports_tbl + i); 77359b591b1SMichal Meloun } 77459b591b1SMichal Meloun return (NULL); 77559b591b1SMichal Meloun } 77659b591b1SMichal Meloun 77759b591b1SMichal Meloun static struct padctl_lane * 77859b591b1SMichal Meloun search_lane(struct padctl_softc *sc, char *lane_name) 77959b591b1SMichal Meloun { 78059b591b1SMichal Meloun int i; 78159b591b1SMichal Meloun 78259b591b1SMichal Meloun for (i = 0; i < nitems(lanes_tbl); i++) { 78359b591b1SMichal Meloun if (strcmp(lane_name, lanes_tbl[i].name) == 0) 78459b591b1SMichal Meloun return (lanes_tbl + i); 78559b591b1SMichal Meloun } 78659b591b1SMichal Meloun return (NULL); 78759b591b1SMichal Meloun } 78859b591b1SMichal Meloun 78959b591b1SMichal Meloun static struct padctl_lane * 79059b591b1SMichal Meloun search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx) 79159b591b1SMichal Meloun { 79259b591b1SMichal Meloun int i; 79359b591b1SMichal Meloun 79459b591b1SMichal Meloun for (i = 0; i < nitems(lanes_tbl); i++) { 79559b591b1SMichal Meloun if (!lanes_tbl[i].enabled) 79659b591b1SMichal Meloun continue; 79759b591b1SMichal Meloun if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx) 79859b591b1SMichal Meloun return (lanes_tbl + i); 79959b591b1SMichal Meloun } 80059b591b1SMichal Meloun return (NULL); 80159b591b1SMichal Meloun } 80259b591b1SMichal Meloun 80359b591b1SMichal Meloun static struct padctl_lane * 80459b591b1SMichal Meloun search_usb3_pad_lane(struct padctl_softc *sc, int idx) 80559b591b1SMichal Meloun { 80659b591b1SMichal Meloun int i; 80759b591b1SMichal Meloun struct padctl_lane *lane, *tmp; 80859b591b1SMichal Meloun 80959b591b1SMichal Meloun lane = NULL; 81059b591b1SMichal Meloun for (i = 0; i < nitems(lane_map_tbl); i++) { 81159b591b1SMichal Meloun if (idx != lane_map_tbl[i].port_idx) 81259b591b1SMichal Meloun continue; 81359b591b1SMichal Meloun tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type, 81459b591b1SMichal Meloun lane_map_tbl[i].lane_idx); 81559b591b1SMichal Meloun if (tmp == NULL) 81659b591b1SMichal Meloun continue; 81759b591b1SMichal Meloun if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0) 81859b591b1SMichal Meloun continue; 81959b591b1SMichal Meloun if (lane != NULL) { 82059b591b1SMichal Meloun device_printf(sc->dev, "Duplicated mappings found for" 82159b591b1SMichal Meloun " lanes: %s and %s\n", lane->name, tmp->name); 82259b591b1SMichal Meloun return (NULL); 82359b591b1SMichal Meloun } 82459b591b1SMichal Meloun lane = tmp; 82559b591b1SMichal Meloun } 82659b591b1SMichal Meloun return (lane); 82759b591b1SMichal Meloun } 82859b591b1SMichal Meloun 82959b591b1SMichal Meloun static struct padctl_pad * 83059b591b1SMichal Meloun search_pad(struct padctl_softc *sc, char *pad_name) 83159b591b1SMichal Meloun { 83259b591b1SMichal Meloun int i; 83359b591b1SMichal Meloun 83459b591b1SMichal Meloun for (i = 0; i < nitems(pads_tbl); i++) { 83559b591b1SMichal Meloun if (strcmp(pad_name, pads_tbl[i].name) == 0) 83659b591b1SMichal Meloun return (pads_tbl + i); 83759b591b1SMichal Meloun } 83859b591b1SMichal Meloun return (NULL); 83959b591b1SMichal Meloun } 84059b591b1SMichal Meloun 84159b591b1SMichal Meloun static int 84259b591b1SMichal Meloun search_mux(struct padctl_softc *sc, struct padctl_lane *lane, char *fnc_name) 84359b591b1SMichal Meloun { 84459b591b1SMichal Meloun int i; 84559b591b1SMichal Meloun 84659b591b1SMichal Meloun for (i = 0; i < lane->nmux; i++) { 84759b591b1SMichal Meloun if (strcmp(fnc_name, lane->mux[i]) == 0) 84859b591b1SMichal Meloun return (i); 84959b591b1SMichal Meloun } 85059b591b1SMichal Meloun return (-1); 85159b591b1SMichal Meloun } 85259b591b1SMichal Meloun 85359b591b1SMichal Meloun static int 85459b591b1SMichal Meloun config_lane(struct padctl_softc *sc, struct padctl_lane *lane) 85559b591b1SMichal Meloun { 85659b591b1SMichal Meloun uint32_t reg; 85759b591b1SMichal Meloun 85859b591b1SMichal Meloun reg = RD4(sc, lane->reg); 85959b591b1SMichal Meloun reg &= ~(lane->mask << lane->shift); 86059b591b1SMichal Meloun reg |= (lane->mux_idx & lane->mask) << lane->shift; 86159b591b1SMichal Meloun WR4(sc, lane->reg, reg); 86259b591b1SMichal Meloun return (0); 86359b591b1SMichal Meloun } 86459b591b1SMichal Meloun 86559b591b1SMichal Meloun static int 86659b591b1SMichal Meloun process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad) 86759b591b1SMichal Meloun { 86859b591b1SMichal Meloun struct padctl_lane *lane; 869f8759facSMichal Meloun struct phynode *phynode; 870f8759facSMichal Meloun struct phynode_init_def phy_init; 87159b591b1SMichal Meloun char *name; 87259b591b1SMichal Meloun char *function; 87359b591b1SMichal Meloun int rv; 87459b591b1SMichal Meloun 87559b591b1SMichal Meloun name = NULL; 87659b591b1SMichal Meloun function = NULL; 87759b591b1SMichal Meloun rv = OF_getprop_alloc(node, "name", 1, (void **)&name); 87859b591b1SMichal Meloun if (rv <= 0) { 87959b591b1SMichal Meloun device_printf(sc->dev, "Cannot read lane name.\n"); 88059b591b1SMichal Meloun return (ENXIO); 88159b591b1SMichal Meloun } 88259b591b1SMichal Meloun 88359b591b1SMichal Meloun lane = search_lane(sc, name); 88459b591b1SMichal Meloun if (lane == NULL) { 88559b591b1SMichal Meloun device_printf(sc->dev, "Unknown lane: %s\n", name); 88659b591b1SMichal Meloun rv = ENXIO; 88759b591b1SMichal Meloun goto end; 88859b591b1SMichal Meloun } 88959b591b1SMichal Meloun 89059b591b1SMichal Meloun /* Read function (mux) settings. */ 89159b591b1SMichal Meloun rv = OF_getprop_alloc(node, "nvidia,function", 1, (void **)&function); 89259b591b1SMichal Meloun if (rv <= 0) { 89359b591b1SMichal Meloun device_printf(sc->dev, "Cannot read lane function.\n"); 89459b591b1SMichal Meloun rv = ENXIO; 89559b591b1SMichal Meloun goto end; 89659b591b1SMichal Meloun } 89759b591b1SMichal Meloun 89859b591b1SMichal Meloun lane->mux_idx = search_mux(sc, lane, function); 89959b591b1SMichal Meloun if (lane->mux_idx == ~0) { 90059b591b1SMichal Meloun device_printf(sc->dev, "Unknown function %s for lane %s\n", 90159b591b1SMichal Meloun function, name); 90259b591b1SMichal Meloun rv = ENXIO; 90359b591b1SMichal Meloun goto end; 90459b591b1SMichal Meloun } 90559b591b1SMichal Meloun 90659b591b1SMichal Meloun rv = config_lane(sc, lane); 90759b591b1SMichal Meloun if (rv != 0) { 90859b591b1SMichal Meloun device_printf(sc->dev, "Cannot configure lane: %s: %d\n", 90959b591b1SMichal Meloun name, rv); 91059b591b1SMichal Meloun rv = ENXIO; 91159b591b1SMichal Meloun goto end; 91259b591b1SMichal Meloun } 91359b591b1SMichal Meloun lane->pad = pad; 91459b591b1SMichal Meloun lane->enabled = true; 91559b591b1SMichal Meloun pad->lanes[pad->nlanes++] = lane; 916f8759facSMichal Meloun 917f8759facSMichal Meloun /* Create and register phy. */ 918f8759facSMichal Meloun bzero(&phy_init, sizeof(phy_init)); 919f8759facSMichal Meloun phy_init.id = lane - lanes_tbl; 920f8759facSMichal Meloun phy_init.ofw_node = node; 921f8759facSMichal Meloun phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init); 922f8759facSMichal Meloun if (phynode == NULL) { 923f8759facSMichal Meloun device_printf(sc->dev, "Cannot create phy\n"); 924f8759facSMichal Meloun rv = ENXIO; 925f8759facSMichal Meloun goto end; 926f8759facSMichal Meloun } 927f8759facSMichal Meloun if (phynode_register(phynode) == NULL) { 928f8759facSMichal Meloun device_printf(sc->dev, "Cannot create phy\n"); 929f8759facSMichal Meloun return (ENXIO); 930f8759facSMichal Meloun } 931f8759facSMichal Meloun 93259b591b1SMichal Meloun rv = 0; 93359b591b1SMichal Meloun 93459b591b1SMichal Meloun end: 93559b591b1SMichal Meloun if (name != NULL) 93659b591b1SMichal Meloun OF_prop_free(name); 93759b591b1SMichal Meloun if (function != NULL) 93859b591b1SMichal Meloun OF_prop_free(function); 93959b591b1SMichal Meloun return (rv); 94059b591b1SMichal Meloun } 94159b591b1SMichal Meloun 94259b591b1SMichal Meloun static int 94359b591b1SMichal Meloun process_pad(struct padctl_softc *sc, phandle_t node) 94459b591b1SMichal Meloun { 94559b591b1SMichal Meloun struct padctl_pad *pad; 94659b591b1SMichal Meloun char *name; 94759b591b1SMichal Meloun int rv; 94859b591b1SMichal Meloun 94959b591b1SMichal Meloun name = NULL; 95059b591b1SMichal Meloun rv = OF_getprop_alloc(node, "name", 1, (void **)&name); 95159b591b1SMichal Meloun if (rv <= 0) { 95259b591b1SMichal Meloun device_printf(sc->dev, "Cannot read pad name.\n"); 95359b591b1SMichal Meloun return (ENXIO); 95459b591b1SMichal Meloun } 95559b591b1SMichal Meloun pad = search_pad(sc, name); 95659b591b1SMichal Meloun if (pad == NULL) { 95759b591b1SMichal Meloun device_printf(sc->dev, "Unknown pad: %s\n", name); 95859b591b1SMichal Meloun rv = ENXIO; 95959b591b1SMichal Meloun goto end; 96059b591b1SMichal Meloun } 96159b591b1SMichal Meloun 96259b591b1SMichal Meloun /* Read and process associated lanes. */ 96359b591b1SMichal Meloun node = ofw_bus_find_child(node, "lanes"); 96459b591b1SMichal Meloun if (node <= 0) { 96559b591b1SMichal Meloun device_printf(sc->dev, "Cannot find regulators subnode\n"); 96659b591b1SMichal Meloun rv = ENXIO; 96759b591b1SMichal Meloun goto end; 96859b591b1SMichal Meloun } 96959b591b1SMichal Meloun 97059b591b1SMichal Meloun for (node = OF_child(node); node != 0; node = OF_peer(node)) { 97159b591b1SMichal Meloun if (!fdt_is_enabled(node)) 97259b591b1SMichal Meloun continue; 97359b591b1SMichal Meloun 97459b591b1SMichal Meloun rv = process_lane(sc, node, pad); 97559b591b1SMichal Meloun if (rv != 0) 97659b591b1SMichal Meloun goto end; 97759b591b1SMichal Meloun } 97859b591b1SMichal Meloun pad->enabled = true; 97959b591b1SMichal Meloun rv = 0; 98059b591b1SMichal Meloun end: 98159b591b1SMichal Meloun if (name != NULL) 98259b591b1SMichal Meloun OF_prop_free(name); 98359b591b1SMichal Meloun return (rv); 98459b591b1SMichal Meloun } 98559b591b1SMichal Meloun 98659b591b1SMichal Meloun static int 98759b591b1SMichal Meloun process_port(struct padctl_softc *sc, phandle_t node) 98859b591b1SMichal Meloun { 98959b591b1SMichal Meloun 99059b591b1SMichal Meloun struct padctl_port *port; 99159b591b1SMichal Meloun char *name; 99259b591b1SMichal Meloun int rv; 99359b591b1SMichal Meloun 99459b591b1SMichal Meloun name = NULL; 99559b591b1SMichal Meloun rv = OF_getprop_alloc(node, "name", 1, (void **)&name); 99659b591b1SMichal Meloun if (rv <= 0) { 99759b591b1SMichal Meloun device_printf(sc->dev, "Cannot read port name.\n"); 99859b591b1SMichal Meloun return (ENXIO); 99959b591b1SMichal Meloun } 100059b591b1SMichal Meloun 100159b591b1SMichal Meloun port = search_port(sc, name); 100259b591b1SMichal Meloun if (port == NULL) { 100359b591b1SMichal Meloun device_printf(sc->dev, "Unknown port: %s\n", name); 100459b591b1SMichal Meloun rv = ENXIO; 100559b591b1SMichal Meloun goto end; 100659b591b1SMichal Meloun } 100759b591b1SMichal Meloun 100859b591b1SMichal Meloun if (port->type == PADCTL_PORT_USB3) { 100959b591b1SMichal Meloun rv = OF_getencprop(node, "nvidia,usb2-companion", 101059b591b1SMichal Meloun &(port->companion), sizeof(port->companion)); 101159b591b1SMichal Meloun if (rv <= 0) { 101259b591b1SMichal Meloun device_printf(sc->dev, 101359b591b1SMichal Meloun "Missing 'nvidia,usb2-companion' property " 101459b591b1SMichal Meloun "for port: %s\n", name); 101559b591b1SMichal Meloun rv = ENXIO; 101659b591b1SMichal Meloun goto end; 101759b591b1SMichal Meloun } 101859b591b1SMichal Meloun } 101959b591b1SMichal Meloun 102059b591b1SMichal Meloun if (OF_hasprop(node, "vbus-supply")) { 102159b591b1SMichal Meloun rv = regulator_get_by_ofw_property(sc->dev, 0, 102259b591b1SMichal Meloun "vbus-supply", &port->supply_vbus); 102359b591b1SMichal Meloun if (rv <= 0) { 102459b591b1SMichal Meloun device_printf(sc->dev, 102559b591b1SMichal Meloun "Cannot get 'vbus-supply' regulator " 102659b591b1SMichal Meloun "for port: %s\n", name); 102759b591b1SMichal Meloun rv = ENXIO; 102859b591b1SMichal Meloun goto end; 102959b591b1SMichal Meloun } 103059b591b1SMichal Meloun } 103159b591b1SMichal Meloun 103259b591b1SMichal Meloun if (OF_hasprop(node, "nvidia,internal")) 103359b591b1SMichal Meloun port->internal = true; 103459b591b1SMichal Meloun /* Find assigned lane */ 103559b591b1SMichal Meloun if (port->lane == NULL) { 103659b591b1SMichal Meloun switch(port->type) { 103759b591b1SMichal Meloun /* Routing is fixed for USB2, ULPI AND HSIC. */ 103859b591b1SMichal Meloun case PADCTL_PORT_USB2: 103959b591b1SMichal Meloun port->lane = search_pad_lane(sc, PADCTL_PAD_USB2, 104059b591b1SMichal Meloun port->idx); 104159b591b1SMichal Meloun break; 104259b591b1SMichal Meloun case PADCTL_PORT_ULPI: 104359b591b1SMichal Meloun port->lane = search_pad_lane(sc, PADCTL_PAD_ULPI, 104459b591b1SMichal Meloun port->idx); 104559b591b1SMichal Meloun break; 104659b591b1SMichal Meloun case PADCTL_PORT_HSIC: 104759b591b1SMichal Meloun port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC, 104859b591b1SMichal Meloun port->idx); 104959b591b1SMichal Meloun break; 105059b591b1SMichal Meloun case PADCTL_PORT_USB3: 105159b591b1SMichal Meloun port->lane = search_usb3_pad_lane(sc, port->idx); 105259b591b1SMichal Meloun break; 105359b591b1SMichal Meloun } 105459b591b1SMichal Meloun } 105559b591b1SMichal Meloun if (port->lane == NULL) { 105659b591b1SMichal Meloun device_printf(sc->dev, "Cannot find lane for port: %s\n", name); 105759b591b1SMichal Meloun rv = ENXIO; 105859b591b1SMichal Meloun goto end; 105959b591b1SMichal Meloun } 106059b591b1SMichal Meloun port->enabled = true; 106159b591b1SMichal Meloun rv = 0; 106259b591b1SMichal Meloun end: 106359b591b1SMichal Meloun if (name != NULL) 106459b591b1SMichal Meloun OF_prop_free(name); 106559b591b1SMichal Meloun return (rv); 106659b591b1SMichal Meloun } 106759b591b1SMichal Meloun 106859b591b1SMichal Meloun static int 106959b591b1SMichal Meloun parse_fdt(struct padctl_softc *sc, phandle_t base_node) 107059b591b1SMichal Meloun { 107159b591b1SMichal Meloun phandle_t node; 107259b591b1SMichal Meloun int rv; 107359b591b1SMichal Meloun 107459b591b1SMichal Meloun rv = 0; 107559b591b1SMichal Meloun node = ofw_bus_find_child(base_node, "pads"); 107659b591b1SMichal Meloun 107759b591b1SMichal Meloun if (node <= 0) { 107859b591b1SMichal Meloun device_printf(sc->dev, "Cannot find pads subnode.\n"); 107959b591b1SMichal Meloun return (ENXIO); 108059b591b1SMichal Meloun } 108159b591b1SMichal Meloun for (node = OF_child(node); node != 0; node = OF_peer(node)) { 108259b591b1SMichal Meloun if (!fdt_is_enabled(node)) 108359b591b1SMichal Meloun continue; 108459b591b1SMichal Meloun rv = process_pad(sc, node); 108559b591b1SMichal Meloun if (rv != 0) 108659b591b1SMichal Meloun return (rv); 108759b591b1SMichal Meloun } 108859b591b1SMichal Meloun 108959b591b1SMichal Meloun node = ofw_bus_find_child(base_node, "ports"); 109059b591b1SMichal Meloun if (node <= 0) { 109159b591b1SMichal Meloun device_printf(sc->dev, "Cannot find ports subnode.\n"); 109259b591b1SMichal Meloun return (ENXIO); 109359b591b1SMichal Meloun } 109459b591b1SMichal Meloun for (node = OF_child(node); node != 0; node = OF_peer(node)) { 109559b591b1SMichal Meloun if (!fdt_is_enabled(node)) 109659b591b1SMichal Meloun continue; 109759b591b1SMichal Meloun rv = process_port(sc, node); 109859b591b1SMichal Meloun if (rv != 0) 109959b591b1SMichal Meloun return (rv); 110059b591b1SMichal Meloun } 110159b591b1SMichal Meloun 110259b591b1SMichal Meloun return (0); 110359b591b1SMichal Meloun } 110459b591b1SMichal Meloun 110559b591b1SMichal Meloun static void 110659b591b1SMichal Meloun load_calibration(struct padctl_softc *sc) 110759b591b1SMichal Meloun { 110859b591b1SMichal Meloun uint32_t reg; 110959b591b1SMichal Meloun 111059b591b1SMichal Meloun /* All XUSB pad calibrations are packed into single dword.*/ 111159b591b1SMichal Meloun reg = tegra_fuse_read_4(FUSE_XUSB_CALIB); 111259b591b1SMichal Meloun sc->hs_curr_level_0 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(reg); 111359b591b1SMichal Meloun sc->hs_curr_level_123 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(reg); 111459b591b1SMichal Meloun sc->hs_iref_cap = FUSE_XUSB_CALIB_HS_IREF_CAP(reg); 111559b591b1SMichal Meloun sc->hs_squelch_level = FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(reg); 111659b591b1SMichal Meloun sc->hs_term_range_adj = FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(reg); 111759b591b1SMichal Meloun } 111859b591b1SMichal Meloun 111959b591b1SMichal Meloun /* ------------------------------------------------------------------------- 112059b591b1SMichal Meloun * 112159b591b1SMichal Meloun * BUS functions 112259b591b1SMichal Meloun */ 112359b591b1SMichal Meloun static int 1124ef2ee5d0SMichal Meloun xusbpadctl_probe(device_t dev) 1125ef2ee5d0SMichal Meloun { 1126ef2ee5d0SMichal Meloun 1127ef2ee5d0SMichal Meloun if (!ofw_bus_status_okay(dev)) 1128ef2ee5d0SMichal Meloun return (ENXIO); 1129ef2ee5d0SMichal Meloun 1130ef2ee5d0SMichal Meloun if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 1131ef2ee5d0SMichal Meloun return (ENXIO); 1132ef2ee5d0SMichal Meloun 1133ef2ee5d0SMichal Meloun device_set_desc(dev, "Tegra XUSB phy"); 1134ef2ee5d0SMichal Meloun return (BUS_PROBE_DEFAULT); 1135ef2ee5d0SMichal Meloun } 1136ef2ee5d0SMichal Meloun 1137ef2ee5d0SMichal Meloun static int 1138ef2ee5d0SMichal Meloun xusbpadctl_detach(device_t dev) 1139ef2ee5d0SMichal Meloun { 1140ef2ee5d0SMichal Meloun 1141ef2ee5d0SMichal Meloun /* This device is always present. */ 1142ef2ee5d0SMichal Meloun return (EBUSY); 1143ef2ee5d0SMichal Meloun } 1144ef2ee5d0SMichal Meloun 1145ef2ee5d0SMichal Meloun static int 1146ef2ee5d0SMichal Meloun xusbpadctl_attach(device_t dev) 1147ef2ee5d0SMichal Meloun { 114859b591b1SMichal Meloun struct padctl_softc * sc; 114959b591b1SMichal Meloun int i, rid, rv; 115059b591b1SMichal Meloun struct padctl_port *port; 1151ef2ee5d0SMichal Meloun phandle_t node; 1152ef2ee5d0SMichal Meloun 1153ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 1154ef2ee5d0SMichal Meloun sc->dev = dev; 115559b591b1SMichal Meloun node = ofw_bus_get_node(dev); 1156ef2ee5d0SMichal Meloun 1157ef2ee5d0SMichal Meloun rid = 0; 1158ef2ee5d0SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1159ef2ee5d0SMichal Meloun RF_ACTIVE); 1160ef2ee5d0SMichal Meloun if (sc->mem_res == NULL) { 1161ef2ee5d0SMichal Meloun device_printf(dev, "Cannot allocate memory resources\n"); 1162ef2ee5d0SMichal Meloun return (ENXIO); 1163ef2ee5d0SMichal Meloun } 1164ef2ee5d0SMichal Meloun 1165dac93553SMichal Meloun rv = hwreset_get_by_ofw_name(dev, 0, "padctl", &sc->rst); 1166ef2ee5d0SMichal Meloun if (rv != 0) { 1167ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv); 1168ef2ee5d0SMichal Meloun return (rv); 1169ef2ee5d0SMichal Meloun } 1170ef2ee5d0SMichal Meloun rv = hwreset_deassert(sc->rst); 1171ef2ee5d0SMichal Meloun if (rv != 0) { 1172ef2ee5d0SMichal Meloun device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv); 1173ef2ee5d0SMichal Meloun return (rv); 1174ef2ee5d0SMichal Meloun } 1175ef2ee5d0SMichal Meloun 117659b591b1SMichal Meloun load_calibration(sc); 1177ef2ee5d0SMichal Meloun 117859b591b1SMichal Meloun rv = parse_fdt(sc, node); 117959b591b1SMichal Meloun if (rv != 0) { 118059b591b1SMichal Meloun device_printf(dev, "Cannot parse fdt configuration: %d\n", rv); 118159b591b1SMichal Meloun return (rv); 118259b591b1SMichal Meloun } 118359b591b1SMichal Meloun for (i = 0; i < nitems(ports_tbl); i++) { 118459b591b1SMichal Meloun port = ports_tbl + i; 118559b591b1SMichal Meloun if (!port->enabled) 118659b591b1SMichal Meloun continue; 118759b591b1SMichal Meloun if (port->init == NULL) 118859b591b1SMichal Meloun continue; 118959b591b1SMichal Meloun rv = port->init(sc, port); 119059b591b1SMichal Meloun if (rv != 0) { 119159b591b1SMichal Meloun device_printf(dev, "Cannot init port '%s'\n", 119259b591b1SMichal Meloun port->name); 119359b591b1SMichal Meloun return (rv); 119459b591b1SMichal Meloun } 119559b591b1SMichal Meloun } 1196ef2ee5d0SMichal Meloun return (0); 1197ef2ee5d0SMichal Meloun } 1198ef2ee5d0SMichal Meloun 1199ef2ee5d0SMichal Meloun static device_method_t tegra_xusbpadctl_methods[] = { 1200ef2ee5d0SMichal Meloun /* Device interface */ 1201ef2ee5d0SMichal Meloun DEVMETHOD(device_probe, xusbpadctl_probe), 1202ef2ee5d0SMichal Meloun DEVMETHOD(device_attach, xusbpadctl_attach), 1203ef2ee5d0SMichal Meloun DEVMETHOD(device_detach, xusbpadctl_detach), 1204ef2ee5d0SMichal Meloun 1205ef2ee5d0SMichal Meloun DEVMETHOD_END 1206ef2ee5d0SMichal Meloun }; 1207ef2ee5d0SMichal Meloun 1208ef2ee5d0SMichal Meloun static devclass_t tegra_xusbpadctl_devclass; 12094bda238aSMichal Meloun static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver, 121059b591b1SMichal Meloun tegra_xusbpadctl_methods, sizeof(struct padctl_softc)); 1211ef2ee5d0SMichal Meloun EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver, 12124bda238aSMichal Meloun tegra_xusbpadctl_devclass, NULL, NULL, 73); 1213