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
27ef2ee5d0SMichal Meloun #include <sys/param.h>
28ef2ee5d0SMichal Meloun #include <sys/systm.h>
29ef2ee5d0SMichal Meloun #include <sys/bus.h>
30ef2ee5d0SMichal Meloun #include <sys/kernel.h>
31ef2ee5d0SMichal Meloun #include <sys/module.h>
32ef2ee5d0SMichal Meloun #include <sys/malloc.h>
33ef2ee5d0SMichal Meloun #include <sys/rman.h>
34ef2ee5d0SMichal Meloun
35ef2ee5d0SMichal Meloun #include <machine/bus.h>
36ef2ee5d0SMichal Meloun #include <machine/fdt.h>
37ef2ee5d0SMichal Meloun
381f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
39*950a6087SEmmanuel Vadot #include <dev/phy/phy.h>
40b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h>
41ef2ee5d0SMichal Meloun #include <dev/fdt/fdt_common.h>
42ef2ee5d0SMichal Meloun #include <dev/fdt/fdt_pinctrl.h>
43ef2ee5d0SMichal Meloun #include <dev/ofw/openfirm.h>
44ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h>
45ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
46ef2ee5d0SMichal Meloun
4759b591b1SMichal Meloun #include <arm/nvidia/tegra_efuse.h>
4859b591b1SMichal Meloun
498a7a4683SEmmanuel Vadot #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
50ef2ee5d0SMichal Meloun
51f8759facSMichal Meloun #include "phydev_if.h"
52ef2ee5d0SMichal Meloun
5359b591b1SMichal Meloun /* FUSE calibration data. */
5459b591b1SMichal Meloun #define FUSE_XUSB_CALIB 0x0F0
5559b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(x) (((x) >> 15) & 0x3F);
5659b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_IREF_CAP(x) (((x) >> 13) & 0x03);
5759b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(x) (((x) >> 11) & 0x03);
5859b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(x) (((x) >> 7) & 0x0F);
5959b591b1SMichal Meloun #define FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(x) (((x) >> 0) & 0x3F);
6059b591b1SMichal Meloun
6159b591b1SMichal Meloun /* Registers. */
62ef2ee5d0SMichal Meloun #define XUSB_PADCTL_USB2_PAD_MUX 0x004
63ef2ee5d0SMichal Meloun
6459b591b1SMichal Meloun #define XUSB_PADCTL_USB2_PORT_CAP 0x008
6559b591b1SMichal Meloun #define USB2_PORT_CAP_ULPI_PORT_INTERNAL (1 << 25)
6659b591b1SMichal Meloun #define USB2_PORT_CAP_ULPI_PORT_CAP (1 << 24)
6759b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_REVERSE_ID(p) (1 << (3 + (p) * 4))
6859b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_INTERNAL(p) (1 << (2 + (p) * 4))
6959b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP(p, x) (((x) & 3) << ((p) * 4))
7059b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP_OTG 0x3
7159b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP_DEVICE 0x2
7259b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP_HOST 0x1
7359b591b1SMichal Meloun #define USB2_PORT_CAP_PORT_CAP_DISABLED 0x0
7459b591b1SMichal Meloun
7559b591b1SMichal Meloun #define XUSB_PADCTL_SS_PORT_MAP 0x014
7659b591b1SMichal Meloun #define SS_PORT_MAP_PORT_INTERNAL(p) (1 << (3 + (p) * 4))
7759b591b1SMichal Meloun #define SS_PORT_MAP_PORT_MAP(p, x) (((x) & 7) << ((p) * 4))
7859b591b1SMichal Meloun
79ef2ee5d0SMichal Meloun #define XUSB_PADCTL_ELPG_PROGRAM 0x01C
80ef2ee5d0SMichal Meloun #define ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
81ef2ee5d0SMichal Meloun #define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
82ef2ee5d0SMichal Meloun #define ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
8359b591b1SMichal Meloun #define ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
8459b591b1SMichal Meloun #define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(x) (1 << (17 + (x) * 4))
8559b591b1SMichal Meloun #define ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))
86ef2ee5d0SMichal Meloun
87ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
88ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
8959b591b1SMichal Meloun #define IOPHY_PLL_P0_CTL1_REFCLK_SEL(x) (((x) & 0xF) << 12)
90ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1)
91ef2ee5d0SMichal Meloun
92ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044
93ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6)
94ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
95ef2ee5d0SMichal Meloun #define IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
96ef2ee5d0SMichal Meloun
9759b591b1SMichal Meloun #define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(x) (0x058 + (x) * 4)
9859b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_CDR_CNTL(x) (((x) & 0x00FF) << 4)
9959b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_RX_EQ(x) (((x) & 0xFFFF) << 8)
10059b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_RX_WANDER(x) (((x) & 0x000F) << 4)
10159b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_RX_TERM_CNTL(x) (((x) & 0x0003) << 2)
10259b591b1SMichal Meloun #define IOPHY_USB3_PAD_CTL2_TX_TERM_CNTL(x) (((x) & 0x0003) << 0)
10359b591b1SMichal Meloun
10459b591b1SMichal Meloun #define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(x) (0x068 + (x) * 4)
10559b591b1SMichal Meloun
10659b591b1SMichal Meloun #define XUSB_PADCTL_USB2_OTG_PAD_CTL0(x) (0x0A0 + (x) * 4)
10759b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_LSBIAS_SEL (1 << 23)
10859b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_DISCON_DETECT_METHOD (1 << 22)
10959b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
11059b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_PD2 (1 << 20)
11159b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_PD (1 << 19)
11259b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_TERM_EN (1 << 18)
11359b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_LS_LS_FSLEW(x) (((x) & 0x03) << 16)
11459b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_LS_RSLEW(x) (((x) & 0x03) << 14)
11559b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_FS_SLEW(x) (((x) & 0x03) << 12)
11659b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_HS_SLEW(x) (((x) & 0x3F) << 6)
11759b591b1SMichal Meloun #define USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(x) (((x) & 0x3F) << 0)
11859b591b1SMichal Meloun
11959b591b1SMichal Meloun #define XUSB_PADCTL_USB2_OTG_PAD_CTL1(x) (0x0AC + (x) * 4)
12059b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_RPU_RANGE_ADJ(x) (((x) & 0x3) << 11)
12159b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_HS_IREF_CAP(x) (((x) & 0x3) << 9)
12259b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_SPARE(x) (((x) & 0x3) << 7)
12359b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(x) (((x) & 0xF) << 3)
12459b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
12559b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
12659b591b1SMichal Meloun #define USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
12759b591b1SMichal Meloun
12859b591b1SMichal Meloun #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0B8
12959b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_ADJRPU(x) (((x) & 0x7) << 14)
13059b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_PD_TRK (1 << 13)
13159b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_PD (1 << 12)
13259b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_TERM_OFFSETL(x) (((x) & 0x3) << 9)
13359b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_VBUS_LEVEL(x) (((x) & 0x3) << 7)
13459b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_HS_CHIRP_LEVEL(x) (((x) & 0x3) << 5)
13559b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(x) (((x) & 0x7) << 2)
13659b591b1SMichal Meloun #define USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(x) (((x) & 0x3) << 0)
13759b591b1SMichal Meloun
13859b591b1SMichal Meloun #define XUSB_PADCTL_HSIC_PAD0_CTL0 0x0C8
13959b591b1SMichal Meloun #define HSIC_PAD0_CTL0_HSIC_OPT(x) (((x) & 0xF) << 16)
14059b591b1SMichal Meloun #define HSIC_PAD0_CTL0_TX_SLEWN(x) (((x) & 0xF) << 12)
14159b591b1SMichal Meloun #define HSIC_PAD0_CTL0_TX_SLEWP(x) (((x) & 0xF) << 8)
14259b591b1SMichal Meloun #define HSIC_PAD0_CTL0_TX_RTUNEN(x) (((x) & 0xF) << 4)
14359b591b1SMichal Meloun #define HSIC_PAD0_CTL0_TX_RTUNEP(x) (((x) & 0xF) << 0)
144ef2ee5d0SMichal Meloun
145ef2ee5d0SMichal Meloun #define XUSB_PADCTL_USB3_PAD_MUX 0x134
14659b591b1SMichal Meloun #define USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x)))
14759b591b1SMichal Meloun #define USB3_PAD_MUX_SATA_IDDQ_DISABLE (1 << 6)
14859b591b1SMichal Meloun
149ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
150ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
151ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
152ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
153ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL_RST_L (1 << 1)
154ef2ee5d0SMichal Meloun #define IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
155ef2ee5d0SMichal Meloun
156ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13C
157ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
158ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_PLL_S0_CTL4 0x144
159ef2ee5d0SMichal Meloun
160ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
161ef2ee5d0SMichal Meloun #define IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
162ef2ee5d0SMichal Meloun #define IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
163ef2ee5d0SMichal Meloun
164ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14C
165ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL3 0x150
166ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL4 0x154
167ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
168ef2ee5d0SMichal Meloun #define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15C
169ef2ee5d0SMichal Meloun
17059b591b1SMichal Meloun #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
17159b591b1SMichal Meloun #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
17259b591b1SMichal Meloun
17359b591b1SMichal Meloun struct padctl_softc {
174ef2ee5d0SMichal Meloun device_t dev;
175ef2ee5d0SMichal Meloun struct resource *mem_res;
176ef2ee5d0SMichal Meloun hwreset_t rst;
177ef2ee5d0SMichal Meloun int phy_ena_cnt;
17859b591b1SMichal Meloun
17959b591b1SMichal Meloun /* Fuses calibration data */
18059b591b1SMichal Meloun uint32_t hs_curr_level_0;
18159b591b1SMichal Meloun uint32_t hs_curr_level_123;
18259b591b1SMichal Meloun uint32_t hs_iref_cap;
18359b591b1SMichal Meloun uint32_t hs_term_range_adj;
18459b591b1SMichal Meloun uint32_t hs_squelch_level;
18559b591b1SMichal Meloun
18659b591b1SMichal Meloun uint32_t hs_curr_level_offset;
187ef2ee5d0SMichal Meloun };
188ef2ee5d0SMichal Meloun
189ef2ee5d0SMichal Meloun static struct ofw_compat_data compat_data[] = {
190ef2ee5d0SMichal Meloun {"nvidia,tegra124-xusb-padctl", 1},
191ef2ee5d0SMichal Meloun {NULL, 0},
192ef2ee5d0SMichal Meloun };
193ef2ee5d0SMichal Meloun
19459b591b1SMichal Meloun /* Ports. */
19559b591b1SMichal Meloun enum padctl_port_type {
19659b591b1SMichal Meloun PADCTL_PORT_USB2,
19759b591b1SMichal Meloun PADCTL_PORT_ULPI,
19859b591b1SMichal Meloun PADCTL_PORT_HSIC,
19959b591b1SMichal Meloun PADCTL_PORT_USB3,
20059b591b1SMichal Meloun };
20159b591b1SMichal Meloun
20259b591b1SMichal Meloun struct padctl_lane;
20359b591b1SMichal Meloun struct padctl_port {
20459b591b1SMichal Meloun enum padctl_port_type type;
20559b591b1SMichal Meloun const char *name;
20659b591b1SMichal Meloun const char *base_name;
20759b591b1SMichal Meloun int idx;
20859b591b1SMichal Meloun int (*init)(struct padctl_softc *sc,
20959b591b1SMichal Meloun struct padctl_port *port);
21059b591b1SMichal Meloun
21159b591b1SMichal Meloun /* Runtime data. */
21259b591b1SMichal Meloun bool enabled;
21359b591b1SMichal Meloun regulator_t supply_vbus; /* USB2, USB3 */
21459b591b1SMichal Meloun bool internal; /* ULPI, USB2, USB3 */
21559b591b1SMichal Meloun uint32_t companion; /* USB3 */
21659b591b1SMichal Meloun struct padctl_lane *lane;
21759b591b1SMichal Meloun };
21859b591b1SMichal Meloun
21959b591b1SMichal Meloun static int usb3_port_init(struct padctl_softc *sc, struct padctl_port *port);
22059b591b1SMichal Meloun
22159b591b1SMichal Meloun #define PORT(t, n, p, i) { \
22259b591b1SMichal Meloun .type = t, \
22359b591b1SMichal Meloun .name = n "-" #p, \
22459b591b1SMichal Meloun .base_name = n, \
22559b591b1SMichal Meloun .idx = p, \
22659b591b1SMichal Meloun .init = i, \
22759b591b1SMichal Meloun }
22859b591b1SMichal Meloun static struct padctl_port ports_tbl[] = {
22959b591b1SMichal Meloun PORT(PADCTL_PORT_USB2, "usb2", 0, NULL),
23059b591b1SMichal Meloun PORT(PADCTL_PORT_USB2, "usb2", 1, NULL),
23159b591b1SMichal Meloun PORT(PADCTL_PORT_USB2, "usb2", 2, NULL),
23259b591b1SMichal Meloun PORT(PADCTL_PORT_ULPI, "ulpi", 0, NULL),
23359b591b1SMichal Meloun PORT(PADCTL_PORT_HSIC, "hsic", 0, NULL),
23459b591b1SMichal Meloun PORT(PADCTL_PORT_HSIC, "hsic", 1, NULL),
23559b591b1SMichal Meloun PORT(PADCTL_PORT_USB3, "usb3", 0, usb3_port_init),
23659b591b1SMichal Meloun PORT(PADCTL_PORT_USB3, "usb3", 1, usb3_port_init),
23759b591b1SMichal Meloun };
23859b591b1SMichal Meloun
23959b591b1SMichal Meloun /* Pads - a group of lannes. */
24059b591b1SMichal Meloun enum padctl_pad_type {
24159b591b1SMichal Meloun PADCTL_PAD_USB2,
24259b591b1SMichal Meloun PADCTL_PAD_ULPI,
24359b591b1SMichal Meloun PADCTL_PAD_HSIC,
24459b591b1SMichal Meloun PADCTL_PAD_PCIE,
24559b591b1SMichal Meloun PADCTL_PAD_SATA,
24659b591b1SMichal Meloun };
24759b591b1SMichal Meloun
24859b591b1SMichal Meloun struct padctl_lane;
24959b591b1SMichal Meloun struct padctl_pad {
25059b591b1SMichal Meloun const char *name;
25159b591b1SMichal Meloun enum padctl_pad_type type;
25259b591b1SMichal Meloun int (*powerup)(struct padctl_softc *sc,
25359b591b1SMichal Meloun struct padctl_lane *lane);
25459b591b1SMichal Meloun int (*powerdown)(struct padctl_softc *sc,
25559b591b1SMichal Meloun struct padctl_lane *lane);
25659b591b1SMichal Meloun /* Runtime data. */
25759b591b1SMichal Meloun bool enabled;
25859b591b1SMichal Meloun struct padctl_lane *lanes[8]; /* Safe maximum value. */
25959b591b1SMichal Meloun int nlanes;
26059b591b1SMichal Meloun };
26159b591b1SMichal Meloun
26259b591b1SMichal Meloun static int usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
26359b591b1SMichal Meloun static int usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
26459b591b1SMichal Meloun static int pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
26559b591b1SMichal Meloun static int pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
26659b591b1SMichal Meloun static int sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane);
26759b591b1SMichal Meloun static int sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane);
26859b591b1SMichal Meloun
26959b591b1SMichal Meloun #define PAD(n, t, u, d) { \
27059b591b1SMichal Meloun .name = n, \
27159b591b1SMichal Meloun .type = t, \
27259b591b1SMichal Meloun .powerup = u, \
27359b591b1SMichal Meloun .powerdown = d, \
27459b591b1SMichal Meloun }
27559b591b1SMichal Meloun static struct padctl_pad pads_tbl[] = {
27659b591b1SMichal Meloun PAD("usb2", PADCTL_PAD_USB2, usb2_powerup, usb2_powerdown),
27759b591b1SMichal Meloun PAD("ulpi", PADCTL_PAD_ULPI, NULL, NULL),
27859b591b1SMichal Meloun PAD("hsic", PADCTL_PAD_HSIC, NULL, NULL),
27959b591b1SMichal Meloun PAD("pcie", PADCTL_PAD_PCIE, pcie_powerup, pcie_powerdown),
28059b591b1SMichal Meloun PAD("sata", PADCTL_PAD_SATA, sata_powerup, sata_powerdown),
28159b591b1SMichal Meloun };
28259b591b1SMichal Meloun
28359b591b1SMichal Meloun /* Lanes. */
28459b591b1SMichal Meloun static char *otg_mux[] = {"snps", "xusb", "uart", "rsvd"};
28559b591b1SMichal Meloun static char *usb_mux[] = {"snps", "xusb"};
28659b591b1SMichal Meloun static char *pci_mux[] = {"pcie", "usb3-ss", "sata", "rsvd"};
28759b591b1SMichal Meloun
288ef2ee5d0SMichal Meloun struct padctl_lane {
289ef2ee5d0SMichal Meloun const char *name;
29059b591b1SMichal Meloun int idx;
291ef2ee5d0SMichal Meloun bus_size_t reg;
292ef2ee5d0SMichal Meloun uint32_t shift;
293ef2ee5d0SMichal Meloun uint32_t mask;
294ef2ee5d0SMichal Meloun char **mux;
295ef2ee5d0SMichal Meloun int nmux;
29659b591b1SMichal Meloun /* Runtime data. */
29759b591b1SMichal Meloun bool enabled;
29859b591b1SMichal Meloun struct padctl_pad *pad;
29959b591b1SMichal Meloun struct padctl_port *port;
30059b591b1SMichal Meloun int mux_idx;
30159b591b1SMichal Meloun
302ef2ee5d0SMichal Meloun };
303ef2ee5d0SMichal Meloun
30459b591b1SMichal Meloun #define LANE(n, p, r, s, m, mx) { \
30559b591b1SMichal Meloun .name = n "-" #p, \
30659b591b1SMichal Meloun .idx = p, \
307ef2ee5d0SMichal Meloun .reg = r, \
308ef2ee5d0SMichal Meloun .shift = s, \
309ef2ee5d0SMichal Meloun .mask = m, \
310ef2ee5d0SMichal Meloun .mux = mx, \
311ef2ee5d0SMichal Meloun .nmux = nitems(mx), \
312ef2ee5d0SMichal Meloun }
31359b591b1SMichal Meloun static struct padctl_lane lanes_tbl[] = {
31459b591b1SMichal Meloun LANE("usb2", 0, XUSB_PADCTL_USB2_PAD_MUX, 0, 0x3, otg_mux),
31559b591b1SMichal Meloun LANE("usb2", 1, XUSB_PADCTL_USB2_PAD_MUX, 2, 0x3, otg_mux),
31659b591b1SMichal Meloun LANE("usb2", 2, XUSB_PADCTL_USB2_PAD_MUX, 4, 0x3, otg_mux),
31759b591b1SMichal Meloun LANE("ulpi", 0, XUSB_PADCTL_USB2_PAD_MUX, 12, 0x1, usb_mux),
31859b591b1SMichal Meloun LANE("hsic", 0, XUSB_PADCTL_USB2_PAD_MUX, 14, 0x1, usb_mux),
31959b591b1SMichal Meloun LANE("hsic", 1, XUSB_PADCTL_USB2_PAD_MUX, 15, 0x1, usb_mux),
32059b591b1SMichal Meloun LANE("pcie", 0, XUSB_PADCTL_USB3_PAD_MUX, 16, 0x3, pci_mux),
32159b591b1SMichal Meloun LANE("pcie", 1, XUSB_PADCTL_USB3_PAD_MUX, 18, 0x3, pci_mux),
32259b591b1SMichal Meloun LANE("pcie", 2, XUSB_PADCTL_USB3_PAD_MUX, 20, 0x3, pci_mux),
32359b591b1SMichal Meloun LANE("pcie", 3, XUSB_PADCTL_USB3_PAD_MUX, 22, 0x3, pci_mux),
32459b591b1SMichal Meloun LANE("pcie", 4, XUSB_PADCTL_USB3_PAD_MUX, 24, 0x3, pci_mux),
32559b591b1SMichal Meloun LANE("sata", 0, XUSB_PADCTL_USB3_PAD_MUX, 26, 0x3, pci_mux),
326ef2ee5d0SMichal Meloun };
327ef2ee5d0SMichal Meloun
32859b591b1SMichal Meloun /* Define all possible mappings for USB3 port lanes */
32959b591b1SMichal Meloun struct padctl_lane_map {
33059b591b1SMichal Meloun int port_idx;
33159b591b1SMichal Meloun enum padctl_pad_type pad_type;
33259b591b1SMichal Meloun int lane_idx;
33359b591b1SMichal Meloun };
334ef2ee5d0SMichal Meloun
33559b591b1SMichal Meloun #define LANE_MAP(pi, pt, li) { \
33659b591b1SMichal Meloun .port_idx = pi, \
33759b591b1SMichal Meloun .pad_type = pt, \
33859b591b1SMichal Meloun .lane_idx = li, \
339ef2ee5d0SMichal Meloun }
34059b591b1SMichal Meloun static struct padctl_lane_map lane_map_tbl[] = {
34159b591b1SMichal Meloun LANE_MAP(0, PADCTL_PAD_PCIE, 0), /* port USB3-0 -> lane PCIE-0 */
34259b591b1SMichal Meloun LANE_MAP(1, PADCTL_PAD_PCIE, 1), /* port USB3-1 -> lane PCIE-1 */
34359b591b1SMichal Meloun /* -- or -- */
34459b591b1SMichal Meloun LANE_MAP(1, PADCTL_PAD_SATA, 0), /* port USB3-1 -> lane SATA-0 */
34559b591b1SMichal Meloun };
346ef2ee5d0SMichal Meloun
347f8759facSMichal Meloun /* Phy class and methods. */
348f8759facSMichal Meloun static int xusbpadctl_phy_enable(struct phynode *phy, bool enable);
349f8759facSMichal Meloun static phynode_method_t xusbpadctl_phynode_methods[] = {
350f8759facSMichal Meloun PHYNODEMETHOD(phynode_enable, xusbpadctl_phy_enable),
351f8759facSMichal Meloun PHYNODEMETHOD_END
352f8759facSMichal Meloun
353f8759facSMichal Meloun };
354f8759facSMichal Meloun DEFINE_CLASS_1(xusbpadctl_phynode, xusbpadctl_phynode_class,
355f8759facSMichal Meloun xusbpadctl_phynode_methods, 0, phynode_class);
356f8759facSMichal Meloun
35759b591b1SMichal Meloun static struct padctl_port *search_lane_port(struct padctl_softc *sc,
35859b591b1SMichal Meloun struct padctl_lane *lane);
35959b591b1SMichal Meloun /* -------------------------------------------------------------------------
36059b591b1SMichal Meloun *
36159b591b1SMichal Meloun * PHY functions
36259b591b1SMichal Meloun */
363ef2ee5d0SMichal Meloun static int
usb3_port_init(struct padctl_softc * sc,struct padctl_port * port)36459b591b1SMichal Meloun usb3_port_init(struct padctl_softc *sc, struct padctl_port *port)
365ef2ee5d0SMichal Meloun {
366ef2ee5d0SMichal Meloun uint32_t reg;
367ef2ee5d0SMichal Meloun
36859b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_SS_PORT_MAP);
36959b591b1SMichal Meloun if (port->internal)
37059b591b1SMichal Meloun reg &= ~SS_PORT_MAP_PORT_INTERNAL(port->idx);
371ef2ee5d0SMichal Meloun else
37259b591b1SMichal Meloun reg |= SS_PORT_MAP_PORT_INTERNAL(port->idx);
37359b591b1SMichal Meloun reg &= ~SS_PORT_MAP_PORT_MAP(port->idx, ~0);
37459b591b1SMichal Meloun reg |= SS_PORT_MAP_PORT_MAP(port->idx, port->companion);
37559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_SS_PORT_MAP, reg);
376ef2ee5d0SMichal Meloun
37759b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx));
37859b591b1SMichal Meloun reg &= ~IOPHY_USB3_PAD_CTL2_CDR_CNTL(~0);
37959b591b1SMichal Meloun reg &= ~IOPHY_USB3_PAD_CTL2_RX_EQ(~0);
38059b591b1SMichal Meloun reg &= ~IOPHY_USB3_PAD_CTL2_RX_WANDER(~0);
38159b591b1SMichal Meloun reg |= IOPHY_USB3_PAD_CTL2_CDR_CNTL(0x24);
38259b591b1SMichal Meloun reg |= IOPHY_USB3_PAD_CTL2_RX_EQ(0xF070);
38359b591b1SMichal Meloun reg |= IOPHY_USB3_PAD_CTL2_RX_WANDER(0xF);
38459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL2(port->idx), reg);
385ef2ee5d0SMichal Meloun
38659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_USB3_PAD_CTL4(port->idx),
38759b591b1SMichal Meloun 0x002008EE);
388ef2ee5d0SMichal Meloun
38959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
39059b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_SSP_ELPG_VCORE_DOWN(port->idx);
39159b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
392ef2ee5d0SMichal Meloun DELAY(100);
393ef2ee5d0SMichal Meloun
39459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
39559b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN_EARLY(port->idx);
39659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
39759b591b1SMichal Meloun DELAY(100);
39859b591b1SMichal Meloun
39959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
40059b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_SSP_ELPG_CLAMP_EN(port->idx);
40159b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
40259b591b1SMichal Meloun DELAY(100);
40359b591b1SMichal Meloun
40459b591b1SMichal Meloun return (0);
40559b591b1SMichal Meloun }
40659b591b1SMichal Meloun
40759b591b1SMichal Meloun static int
pcie_powerup(struct padctl_softc * sc,struct padctl_lane * lane)40859b591b1SMichal Meloun pcie_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
40959b591b1SMichal Meloun {
41059b591b1SMichal Meloun uint32_t reg;
41159b591b1SMichal Meloun int i;
41259b591b1SMichal Meloun
41359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
41459b591b1SMichal Meloun reg &= ~IOPHY_PLL_P0_CTL1_REFCLK_SEL(~0);
41559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
41659b591b1SMichal Meloun DELAY(100);
41759b591b1SMichal Meloun
41859b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
419ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_P0_CTL2_REFCLKBUF_EN;
420ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_EN;
421ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_P0_CTL2_TXCLKREF_SEL;
42259b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL2, reg);
423ef2ee5d0SMichal Meloun DELAY(100);
424ef2ee5d0SMichal Meloun
42559b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
426ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_P0_CTL1_PLL_RST;
42759b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
428ef2ee5d0SMichal Meloun DELAY(100);
429ef2ee5d0SMichal Meloun
43059b591b1SMichal Meloun for (i = 100; i > 0; i--) {
43159b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
432ef2ee5d0SMichal Meloun if (reg & IOPHY_PLL_P0_CTL1_PLL0_LOCKDET)
43359b591b1SMichal Meloun break;
434ef2ee5d0SMichal Meloun DELAY(10);
435ef2ee5d0SMichal Meloun }
43659b591b1SMichal Meloun if (i <= 0) {
43759b591b1SMichal Meloun device_printf(sc->dev, "Failed to power up PCIe phy\n");
438ef2ee5d0SMichal Meloun return (ETIMEDOUT);
439ef2ee5d0SMichal Meloun }
44059b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
44159b591b1SMichal Meloun reg |= USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx);
44259b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
443ef2ee5d0SMichal Meloun
44459b591b1SMichal Meloun return (0);
44559b591b1SMichal Meloun }
446ef2ee5d0SMichal Meloun
447ef2ee5d0SMichal Meloun static int
pcie_powerdown(struct padctl_softc * sc,struct padctl_lane * lane)44859b591b1SMichal Meloun pcie_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
449ef2ee5d0SMichal Meloun {
450ef2ee5d0SMichal Meloun uint32_t reg;
451ef2ee5d0SMichal Meloun
45259b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
45359b591b1SMichal Meloun reg &= ~USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->idx);
45459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
45559b591b1SMichal Meloun
45659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
457ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_P0_CTL1_PLL_RST;
45859b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_P0_CTL1, reg);
459ef2ee5d0SMichal Meloun DELAY(100);
46059b591b1SMichal Meloun
461ef2ee5d0SMichal Meloun return (0);
462ef2ee5d0SMichal Meloun
463ef2ee5d0SMichal Meloun }
464ef2ee5d0SMichal Meloun
465ef2ee5d0SMichal Meloun static int
sata_powerup(struct padctl_softc * sc,struct padctl_lane * lane)46659b591b1SMichal Meloun sata_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
467ef2ee5d0SMichal Meloun {
468ef2ee5d0SMichal Meloun uint32_t reg;
469ef2ee5d0SMichal Meloun int i;
470ef2ee5d0SMichal Meloun
47159b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
472ef2ee5d0SMichal Meloun reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
473ef2ee5d0SMichal Meloun reg &= ~IOPHY_MISC_PAD_S0_CTL1_IDDQ;
47459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
475ef2ee5d0SMichal Meloun
47659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
477ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
478ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_S0_CTL1_PLL_IDDQ;
47959b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
480ef2ee5d0SMichal Meloun
48159b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
482ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL1_MODE;
48359b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
484ef2ee5d0SMichal Meloun
48559b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
486ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
48759b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
488ef2ee5d0SMichal Meloun
489ef2ee5d0SMichal Meloun for (i = 100; i >= 0; i--) {
49059b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
491ef2ee5d0SMichal Meloun if (reg & IOPHY_PLL_S0_CTL1_PLL1_LOCKDET)
492ef2ee5d0SMichal Meloun break;
493ef2ee5d0SMichal Meloun DELAY(100);
494ef2ee5d0SMichal Meloun }
495ef2ee5d0SMichal Meloun if (i <= 0) {
496ef2ee5d0SMichal Meloun device_printf(sc->dev, "Failed to power up SATA phy\n");
497ef2ee5d0SMichal Meloun return (ETIMEDOUT);
498ef2ee5d0SMichal Meloun }
49959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
50059b591b1SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL_RST_L;
50159b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
50259b591b1SMichal Meloun
50359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
50459b591b1SMichal Meloun reg |= USB3_PAD_MUX_SATA_IDDQ_DISABLE;
50559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
506ef2ee5d0SMichal Meloun
507ef2ee5d0SMichal Meloun return (0);
508ef2ee5d0SMichal Meloun }
509ef2ee5d0SMichal Meloun
510ef2ee5d0SMichal Meloun static int
sata_powerdown(struct padctl_softc * sc,struct padctl_lane * lane)51159b591b1SMichal Meloun sata_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
512ef2ee5d0SMichal Meloun {
513ef2ee5d0SMichal Meloun uint32_t reg;
514ef2ee5d0SMichal Meloun
51559b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB3_PAD_MUX);
51659b591b1SMichal Meloun reg &= ~USB3_PAD_MUX_SATA_IDDQ_DISABLE;
51759b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB3_PAD_MUX, reg);
51859b591b1SMichal Meloun
51959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
520ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_S0_CTL1_PLL_RST_L;
52159b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
522ef2ee5d0SMichal Meloun DELAY(100);
523ef2ee5d0SMichal Meloun
52459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
525ef2ee5d0SMichal Meloun reg &= ~IOPHY_PLL_S0_CTL1_PLL1_MODE;
52659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
527ef2ee5d0SMichal Meloun DELAY(100);
528ef2ee5d0SMichal Meloun
52959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
530ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
531ef2ee5d0SMichal Meloun reg |= IOPHY_PLL_S0_CTL1_PLL_IDDQ;
53259b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_PLL_S0_CTL1, reg);
533ef2ee5d0SMichal Meloun DELAY(100);
534ef2ee5d0SMichal Meloun
53559b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
536ef2ee5d0SMichal Meloun reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
537ef2ee5d0SMichal Meloun reg |= IOPHY_MISC_PAD_S0_CTL1_IDDQ;
53859b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1, reg);
539ef2ee5d0SMichal Meloun DELAY(100);
540ef2ee5d0SMichal Meloun
541ef2ee5d0SMichal Meloun return (0);
542ef2ee5d0SMichal Meloun }
543ef2ee5d0SMichal Meloun
544ef2ee5d0SMichal Meloun static int
usb2_powerup(struct padctl_softc * sc,struct padctl_lane * lane)54559b591b1SMichal Meloun usb2_powerup(struct padctl_softc *sc, struct padctl_lane *lane)
54659b591b1SMichal Meloun {
54759b591b1SMichal Meloun uint32_t reg;
54859b591b1SMichal Meloun struct padctl_port *port;
54959b591b1SMichal Meloun int rv;
55059b591b1SMichal Meloun
55159b591b1SMichal Meloun port = search_lane_port(sc, lane);
55259b591b1SMichal Meloun if (port == NULL) {
55359b591b1SMichal Meloun device_printf(sc->dev, "Cannot find port for lane: %s\n",
55459b591b1SMichal Meloun lane->name);
55559b591b1SMichal Meloun }
55659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
55759b591b1SMichal Meloun reg &= ~USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(~0);
55859b591b1SMichal Meloun reg &= ~USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(~0);
55959b591b1SMichal Meloun reg |= USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL(sc->hs_squelch_level);
56059b591b1SMichal Meloun reg |= USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL(5);
56159b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
56259b591b1SMichal Meloun
56359b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_PORT_CAP);
56459b591b1SMichal Meloun reg &= ~USB2_PORT_CAP_PORT_CAP(lane->idx, ~0);
56559b591b1SMichal Meloun reg |= USB2_PORT_CAP_PORT_CAP(lane->idx, USB2_PORT_CAP_PORT_CAP_HOST);
56659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_PORT_CAP, reg);
56759b591b1SMichal Meloun
56859b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx));
56959b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(~0);
57059b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_HS_SLEW(~0);
57159b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_LS_RSLEW(~0);
57259b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_PD;
57359b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_PD2;
57459b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL0_PD_ZI;
57559b591b1SMichal Meloun
57659b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_HS_SLEW(14);
57759b591b1SMichal Meloun if (lane->idx == 0) {
57859b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_0);
57959b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(3);
58059b591b1SMichal Meloun } else {
58159b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_HS_CURR_LEVEL(sc->hs_curr_level_123);
58259b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL0_LS_RSLEW(0);
58359b591b1SMichal Meloun }
58459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL0(lane->idx), reg);
58559b591b1SMichal Meloun
58659b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx));
58759b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(~0);
58859b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_HS_IREF_CAP(~0);
58959b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_PD_DR;
59059b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP;
59159b591b1SMichal Meloun reg &= ~USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP;
59259b591b1SMichal Meloun
59359b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ(sc->hs_term_range_adj);
59459b591b1SMichal Meloun reg |= USB2_OTG_PAD_CTL1_HS_IREF_CAP(sc->hs_iref_cap);
59559b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_OTG_PAD_CTL1(lane->idx), reg);
59659b591b1SMichal Meloun
59759b591b1SMichal Meloun if (port != NULL && port->supply_vbus != NULL) {
59859b591b1SMichal Meloun rv = regulator_enable(port->supply_vbus);
59959b591b1SMichal Meloun if (rv != 0) {
60059b591b1SMichal Meloun device_printf(sc->dev,
60159b591b1SMichal Meloun "Cannot enable vbus regulator\n");
60259b591b1SMichal Meloun return (rv);
60359b591b1SMichal Meloun }
60459b591b1SMichal Meloun }
60559b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
60659b591b1SMichal Meloun reg &= ~USB2_BIAS_PAD_CTL0_PD;
60759b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
60859b591b1SMichal Meloun
60959b591b1SMichal Meloun return (0);
61059b591b1SMichal Meloun }
61159b591b1SMichal Meloun
61259b591b1SMichal Meloun static int
usb2_powerdown(struct padctl_softc * sc,struct padctl_lane * lane)61359b591b1SMichal Meloun usb2_powerdown(struct padctl_softc *sc, struct padctl_lane *lane)
61459b591b1SMichal Meloun {
61559b591b1SMichal Meloun uint32_t reg;
61659b591b1SMichal Meloun struct padctl_port *port;
61759b591b1SMichal Meloun int rv;
61859b591b1SMichal Meloun
61959b591b1SMichal Meloun port = search_lane_port(sc, lane);
62059b591b1SMichal Meloun if (port == NULL) {
62159b591b1SMichal Meloun device_printf(sc->dev, "Cannot find port for lane: %s\n",
62259b591b1SMichal Meloun lane->name);
62359b591b1SMichal Meloun }
62459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
62559b591b1SMichal Meloun reg |= USB2_BIAS_PAD_CTL0_PD;
62659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_USB2_BIAS_PAD_CTL0, reg);
62759b591b1SMichal Meloun
62859b591b1SMichal Meloun if (port != NULL && port->supply_vbus != NULL) {
62959b591b1SMichal Meloun rv = regulator_enable(port->supply_vbus);
63059b591b1SMichal Meloun if (rv != 0) {
63159b591b1SMichal Meloun device_printf(sc->dev,
63259b591b1SMichal Meloun "Cannot disable vbus regulator\n");
63359b591b1SMichal Meloun return (rv);
63459b591b1SMichal Meloun }
63559b591b1SMichal Meloun }
63659b591b1SMichal Meloun return (0);
63759b591b1SMichal Meloun }
63859b591b1SMichal Meloun
63959b591b1SMichal Meloun static int
phy_powerup(struct padctl_softc * sc)64059b591b1SMichal Meloun phy_powerup(struct padctl_softc *sc)
641ef2ee5d0SMichal Meloun {
642ef2ee5d0SMichal Meloun uint32_t reg;
643ef2ee5d0SMichal Meloun
64459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
645ef2ee5d0SMichal Meloun reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
64659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
64759b591b1SMichal Meloun DELAY(100);
64859b591b1SMichal Meloun
64959b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
65059b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
65159b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
65259b591b1SMichal Meloun DELAY(100);
65359b591b1SMichal Meloun
65459b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
65559b591b1SMichal Meloun reg &= ~ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
65659b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
657ef2ee5d0SMichal Meloun DELAY(100);
658ef2ee5d0SMichal Meloun
659ef2ee5d0SMichal Meloun return (0);
660ef2ee5d0SMichal Meloun }
661ef2ee5d0SMichal Meloun
662ef2ee5d0SMichal Meloun static int
phy_powerdown(struct padctl_softc * sc)66359b591b1SMichal Meloun phy_powerdown(struct padctl_softc *sc)
664ef2ee5d0SMichal Meloun {
665ef2ee5d0SMichal Meloun uint32_t reg;
666ef2ee5d0SMichal Meloun
66759b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
668ef2ee5d0SMichal Meloun reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
66959b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
670ef2ee5d0SMichal Meloun DELAY(100);
671ef2ee5d0SMichal Meloun
67259b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
673ef2ee5d0SMichal Meloun reg |= ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
67459b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
67559b591b1SMichal Meloun DELAY(100);
67659b591b1SMichal Meloun
67759b591b1SMichal Meloun reg = RD4(sc, XUSB_PADCTL_ELPG_PROGRAM);
67859b591b1SMichal Meloun reg |= ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
67959b591b1SMichal Meloun WR4(sc, XUSB_PADCTL_ELPG_PROGRAM, reg);
680ef2ee5d0SMichal Meloun DELAY(100);
681ef2ee5d0SMichal Meloun
682ef2ee5d0SMichal Meloun return (0);
683ef2ee5d0SMichal Meloun }
684ef2ee5d0SMichal Meloun
685ef2ee5d0SMichal Meloun static int
xusbpadctl_phy_enable(struct phynode * phy,bool enable)686f8759facSMichal Meloun xusbpadctl_phy_enable(struct phynode *phy, bool enable)
687ef2ee5d0SMichal Meloun {
688f8759facSMichal Meloun device_t dev;
689f8759facSMichal Meloun intptr_t id;
69059b591b1SMichal Meloun struct padctl_softc *sc;
69159b591b1SMichal Meloun struct padctl_lane *lane;
69259b591b1SMichal Meloun struct padctl_pad *pad;
693ef2ee5d0SMichal Meloun int rv;
694ef2ee5d0SMichal Meloun
695f8759facSMichal Meloun dev = phynode_get_device(phy);
696f8759facSMichal Meloun id = phynode_get_id(phy);
697ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
698ef2ee5d0SMichal Meloun
69959b591b1SMichal Meloun if (id < 0 || id >= nitems(lanes_tbl)) {
700ef2ee5d0SMichal Meloun device_printf(dev, "Unknown phy: %d\n", id);
701ef2ee5d0SMichal Meloun return (ENXIO);
702ef2ee5d0SMichal Meloun }
70359b591b1SMichal Meloun lane = lanes_tbl + id;
70459b591b1SMichal Meloun if (!lane->enabled) {
70559b591b1SMichal Meloun device_printf(dev, "Lane is not enabled/configured: %s\n",
70659b591b1SMichal Meloun lane->name);
70759b591b1SMichal Meloun return (ENXIO);
70859b591b1SMichal Meloun }
70959b591b1SMichal Meloun pad = lane->pad;
710ef2ee5d0SMichal Meloun if (enable) {
711ef2ee5d0SMichal Meloun if (sc->phy_ena_cnt == 0) {
71259b591b1SMichal Meloun rv = phy_powerup(sc);
713ef2ee5d0SMichal Meloun if (rv != 0)
714ef2ee5d0SMichal Meloun return (rv);
715ef2ee5d0SMichal Meloun }
716ef2ee5d0SMichal Meloun sc->phy_ena_cnt++;
717ef2ee5d0SMichal Meloun }
718ef2ee5d0SMichal Meloun
719ef2ee5d0SMichal Meloun if (enable)
72059b591b1SMichal Meloun rv = pad->powerup(sc, lane);
721ef2ee5d0SMichal Meloun else
72259b591b1SMichal Meloun rv = pad->powerdown(sc, lane);
723ef2ee5d0SMichal Meloun if (rv != 0)
724ef2ee5d0SMichal Meloun return (rv);
72559b591b1SMichal Meloun
726ef2ee5d0SMichal Meloun if (!enable) {
727ef2ee5d0SMichal Meloun if (sc->phy_ena_cnt == 1) {
72859b591b1SMichal Meloun rv = phy_powerdown(sc);
729ef2ee5d0SMichal Meloun if (rv != 0)
730ef2ee5d0SMichal Meloun return (rv);
731ef2ee5d0SMichal Meloun }
732ef2ee5d0SMichal Meloun sc->phy_ena_cnt--;
733ef2ee5d0SMichal Meloun }
734ef2ee5d0SMichal Meloun
735ef2ee5d0SMichal Meloun return (0);
736ef2ee5d0SMichal Meloun }
737ef2ee5d0SMichal Meloun
73859b591b1SMichal Meloun /* -------------------------------------------------------------------------
73959b591b1SMichal Meloun *
74059b591b1SMichal Meloun * FDT processing
74159b591b1SMichal Meloun */
74259b591b1SMichal Meloun static struct padctl_port *
search_port(struct padctl_softc * sc,char * port_name)74359b591b1SMichal Meloun search_port(struct padctl_softc *sc, char *port_name)
74459b591b1SMichal Meloun {
74559b591b1SMichal Meloun int i;
74659b591b1SMichal Meloun
74759b591b1SMichal Meloun for (i = 0; i < nitems(ports_tbl); i++) {
74859b591b1SMichal Meloun if (strcmp(port_name, ports_tbl[i].name) == 0)
74959b591b1SMichal Meloun return (&ports_tbl[i]);
75059b591b1SMichal Meloun }
75159b591b1SMichal Meloun return (NULL);
75259b591b1SMichal Meloun }
75359b591b1SMichal Meloun
75459b591b1SMichal Meloun static struct padctl_port *
search_lane_port(struct padctl_softc * sc,struct padctl_lane * lane)75559b591b1SMichal Meloun search_lane_port(struct padctl_softc *sc, struct padctl_lane *lane)
75659b591b1SMichal Meloun {
75759b591b1SMichal Meloun int i;
75859b591b1SMichal Meloun
75959b591b1SMichal Meloun for (i = 0; i < nitems(ports_tbl); i++) {
76059b591b1SMichal Meloun if (!ports_tbl[i].enabled)
76159b591b1SMichal Meloun continue;
76259b591b1SMichal Meloun if (ports_tbl[i].lane == lane)
76359b591b1SMichal Meloun return (ports_tbl + i);
76459b591b1SMichal Meloun }
76559b591b1SMichal Meloun return (NULL);
76659b591b1SMichal Meloun }
76759b591b1SMichal Meloun
76859b591b1SMichal Meloun static struct padctl_lane *
search_lane(struct padctl_softc * sc,char * lane_name)76959b591b1SMichal Meloun search_lane(struct padctl_softc *sc, char *lane_name)
77059b591b1SMichal Meloun {
77159b591b1SMichal Meloun int i;
77259b591b1SMichal Meloun
77359b591b1SMichal Meloun for (i = 0; i < nitems(lanes_tbl); i++) {
77459b591b1SMichal Meloun if (strcmp(lane_name, lanes_tbl[i].name) == 0)
77559b591b1SMichal Meloun return (lanes_tbl + i);
77659b591b1SMichal Meloun }
77759b591b1SMichal Meloun return (NULL);
77859b591b1SMichal Meloun }
77959b591b1SMichal Meloun
78059b591b1SMichal Meloun static struct padctl_lane *
search_pad_lane(struct padctl_softc * sc,enum padctl_pad_type type,int idx)78159b591b1SMichal Meloun search_pad_lane(struct padctl_softc *sc, enum padctl_pad_type type, int idx)
78259b591b1SMichal Meloun {
78359b591b1SMichal Meloun int i;
78459b591b1SMichal Meloun
78559b591b1SMichal Meloun for (i = 0; i < nitems(lanes_tbl); i++) {
78659b591b1SMichal Meloun if (!lanes_tbl[i].enabled)
78759b591b1SMichal Meloun continue;
78859b591b1SMichal Meloun if (type == lanes_tbl[i].pad->type && idx == lanes_tbl[i].idx)
78959b591b1SMichal Meloun return (lanes_tbl + i);
79059b591b1SMichal Meloun }
79159b591b1SMichal Meloun return (NULL);
79259b591b1SMichal Meloun }
79359b591b1SMichal Meloun
79459b591b1SMichal Meloun static struct padctl_lane *
search_usb3_pad_lane(struct padctl_softc * sc,int idx)79559b591b1SMichal Meloun search_usb3_pad_lane(struct padctl_softc *sc, int idx)
79659b591b1SMichal Meloun {
79759b591b1SMichal Meloun int i;
79859b591b1SMichal Meloun struct padctl_lane *lane, *tmp;
79959b591b1SMichal Meloun
80059b591b1SMichal Meloun lane = NULL;
80159b591b1SMichal Meloun for (i = 0; i < nitems(lane_map_tbl); i++) {
80259b591b1SMichal Meloun if (idx != lane_map_tbl[i].port_idx)
80359b591b1SMichal Meloun continue;
80459b591b1SMichal Meloun tmp = search_pad_lane(sc, lane_map_tbl[i].pad_type,
80559b591b1SMichal Meloun lane_map_tbl[i].lane_idx);
80659b591b1SMichal Meloun if (tmp == NULL)
80759b591b1SMichal Meloun continue;
80859b591b1SMichal Meloun if (strcmp(tmp->mux[tmp->mux_idx], "usb3-ss") != 0)
80959b591b1SMichal Meloun continue;
81059b591b1SMichal Meloun if (lane != NULL) {
81159b591b1SMichal Meloun device_printf(sc->dev, "Duplicated mappings found for"
81259b591b1SMichal Meloun " lanes: %s and %s\n", lane->name, tmp->name);
81359b591b1SMichal Meloun return (NULL);
81459b591b1SMichal Meloun }
81559b591b1SMichal Meloun lane = tmp;
81659b591b1SMichal Meloun }
81759b591b1SMichal Meloun return (lane);
81859b591b1SMichal Meloun }
81959b591b1SMichal Meloun
82059b591b1SMichal Meloun static struct padctl_pad *
search_pad(struct padctl_softc * sc,char * pad_name)82159b591b1SMichal Meloun search_pad(struct padctl_softc *sc, char *pad_name)
82259b591b1SMichal Meloun {
82359b591b1SMichal Meloun int i;
82459b591b1SMichal Meloun
82559b591b1SMichal Meloun for (i = 0; i < nitems(pads_tbl); i++) {
82659b591b1SMichal Meloun if (strcmp(pad_name, pads_tbl[i].name) == 0)
82759b591b1SMichal Meloun return (pads_tbl + i);
82859b591b1SMichal Meloun }
82959b591b1SMichal Meloun return (NULL);
83059b591b1SMichal Meloun }
83159b591b1SMichal Meloun
83259b591b1SMichal Meloun static int
search_mux(struct padctl_softc * sc,struct padctl_lane * lane,char * fnc_name)83359b591b1SMichal Meloun search_mux(struct padctl_softc *sc, struct padctl_lane *lane, char *fnc_name)
83459b591b1SMichal Meloun {
83559b591b1SMichal Meloun int i;
83659b591b1SMichal Meloun
83759b591b1SMichal Meloun for (i = 0; i < lane->nmux; i++) {
83859b591b1SMichal Meloun if (strcmp(fnc_name, lane->mux[i]) == 0)
83959b591b1SMichal Meloun return (i);
84059b591b1SMichal Meloun }
84159b591b1SMichal Meloun return (-1);
84259b591b1SMichal Meloun }
84359b591b1SMichal Meloun
84459b591b1SMichal Meloun static int
config_lane(struct padctl_softc * sc,struct padctl_lane * lane)84559b591b1SMichal Meloun config_lane(struct padctl_softc *sc, struct padctl_lane *lane)
84659b591b1SMichal Meloun {
84759b591b1SMichal Meloun uint32_t reg;
84859b591b1SMichal Meloun
84959b591b1SMichal Meloun reg = RD4(sc, lane->reg);
85059b591b1SMichal Meloun reg &= ~(lane->mask << lane->shift);
85159b591b1SMichal Meloun reg |= (lane->mux_idx & lane->mask) << lane->shift;
85259b591b1SMichal Meloun WR4(sc, lane->reg, reg);
85359b591b1SMichal Meloun return (0);
85459b591b1SMichal Meloun }
85559b591b1SMichal Meloun
85659b591b1SMichal Meloun static int
process_lane(struct padctl_softc * sc,phandle_t node,struct padctl_pad * pad)85759b591b1SMichal Meloun process_lane(struct padctl_softc *sc, phandle_t node, struct padctl_pad *pad)
85859b591b1SMichal Meloun {
85959b591b1SMichal Meloun struct padctl_lane *lane;
860f8759facSMichal Meloun struct phynode *phynode;
861f8759facSMichal Meloun struct phynode_init_def phy_init;
86259b591b1SMichal Meloun char *name;
86359b591b1SMichal Meloun char *function;
86459b591b1SMichal Meloun int rv;
86559b591b1SMichal Meloun
86659b591b1SMichal Meloun name = NULL;
86759b591b1SMichal Meloun function = NULL;
868217d17bcSOleksandr Tymoshenko rv = OF_getprop_alloc(node, "name", (void **)&name);
86959b591b1SMichal Meloun if (rv <= 0) {
87059b591b1SMichal Meloun device_printf(sc->dev, "Cannot read lane name.\n");
87159b591b1SMichal Meloun return (ENXIO);
87259b591b1SMichal Meloun }
87359b591b1SMichal Meloun
87459b591b1SMichal Meloun lane = search_lane(sc, name);
87559b591b1SMichal Meloun if (lane == NULL) {
87659b591b1SMichal Meloun device_printf(sc->dev, "Unknown lane: %s\n", name);
87759b591b1SMichal Meloun rv = ENXIO;
87859b591b1SMichal Meloun goto end;
87959b591b1SMichal Meloun }
88059b591b1SMichal Meloun
88159b591b1SMichal Meloun /* Read function (mux) settings. */
882217d17bcSOleksandr Tymoshenko rv = OF_getprop_alloc(node, "nvidia,function", (void **)&function);
88359b591b1SMichal Meloun if (rv <= 0) {
88459b591b1SMichal Meloun device_printf(sc->dev, "Cannot read lane function.\n");
88559b591b1SMichal Meloun rv = ENXIO;
88659b591b1SMichal Meloun goto end;
88759b591b1SMichal Meloun }
88859b591b1SMichal Meloun
88959b591b1SMichal Meloun lane->mux_idx = search_mux(sc, lane, function);
89059b591b1SMichal Meloun if (lane->mux_idx == ~0) {
89159b591b1SMichal Meloun device_printf(sc->dev, "Unknown function %s for lane %s\n",
89259b591b1SMichal Meloun function, name);
89359b591b1SMichal Meloun rv = ENXIO;
89459b591b1SMichal Meloun goto end;
89559b591b1SMichal Meloun }
89659b591b1SMichal Meloun
89759b591b1SMichal Meloun rv = config_lane(sc, lane);
89859b591b1SMichal Meloun if (rv != 0) {
89959b591b1SMichal Meloun device_printf(sc->dev, "Cannot configure lane: %s: %d\n",
90059b591b1SMichal Meloun name, rv);
90159b591b1SMichal Meloun rv = ENXIO;
90259b591b1SMichal Meloun goto end;
90359b591b1SMichal Meloun }
90459b591b1SMichal Meloun lane->pad = pad;
90559b591b1SMichal Meloun lane->enabled = true;
90659b591b1SMichal Meloun pad->lanes[pad->nlanes++] = lane;
907f8759facSMichal Meloun
908f8759facSMichal Meloun /* Create and register phy. */
909f8759facSMichal Meloun bzero(&phy_init, sizeof(phy_init));
910f8759facSMichal Meloun phy_init.id = lane - lanes_tbl;
911f8759facSMichal Meloun phy_init.ofw_node = node;
912f8759facSMichal Meloun phynode = phynode_create(sc->dev, &xusbpadctl_phynode_class, &phy_init);
913f8759facSMichal Meloun if (phynode == NULL) {
914f8759facSMichal Meloun device_printf(sc->dev, "Cannot create phy\n");
915f8759facSMichal Meloun rv = ENXIO;
916f8759facSMichal Meloun goto end;
917f8759facSMichal Meloun }
918f8759facSMichal Meloun if (phynode_register(phynode) == NULL) {
919f8759facSMichal Meloun device_printf(sc->dev, "Cannot create phy\n");
920f8759facSMichal Meloun return (ENXIO);
921f8759facSMichal Meloun }
922f8759facSMichal Meloun
92359b591b1SMichal Meloun rv = 0;
92459b591b1SMichal Meloun
92559b591b1SMichal Meloun end:
92659b591b1SMichal Meloun if (name != NULL)
92759b591b1SMichal Meloun OF_prop_free(name);
92859b591b1SMichal Meloun if (function != NULL)
92959b591b1SMichal Meloun OF_prop_free(function);
93059b591b1SMichal Meloun return (rv);
93159b591b1SMichal Meloun }
93259b591b1SMichal Meloun
93359b591b1SMichal Meloun static int
process_pad(struct padctl_softc * sc,phandle_t node)93459b591b1SMichal Meloun process_pad(struct padctl_softc *sc, phandle_t node)
93559b591b1SMichal Meloun {
93659b591b1SMichal Meloun struct padctl_pad *pad;
93759b591b1SMichal Meloun char *name;
93859b591b1SMichal Meloun int rv;
93959b591b1SMichal Meloun
94059b591b1SMichal Meloun name = NULL;
941217d17bcSOleksandr Tymoshenko rv = OF_getprop_alloc(node, "name", (void **)&name);
94259b591b1SMichal Meloun if (rv <= 0) {
94359b591b1SMichal Meloun device_printf(sc->dev, "Cannot read pad name.\n");
94459b591b1SMichal Meloun return (ENXIO);
94559b591b1SMichal Meloun }
94659b591b1SMichal Meloun pad = search_pad(sc, name);
94759b591b1SMichal Meloun if (pad == NULL) {
94859b591b1SMichal Meloun device_printf(sc->dev, "Unknown pad: %s\n", name);
94959b591b1SMichal Meloun rv = ENXIO;
95059b591b1SMichal Meloun goto end;
95159b591b1SMichal Meloun }
95259b591b1SMichal Meloun
95359b591b1SMichal Meloun /* Read and process associated lanes. */
95459b591b1SMichal Meloun node = ofw_bus_find_child(node, "lanes");
95559b591b1SMichal Meloun if (node <= 0) {
95659b591b1SMichal Meloun device_printf(sc->dev, "Cannot find regulators subnode\n");
95759b591b1SMichal Meloun rv = ENXIO;
95859b591b1SMichal Meloun goto end;
95959b591b1SMichal Meloun }
96059b591b1SMichal Meloun
96159b591b1SMichal Meloun for (node = OF_child(node); node != 0; node = OF_peer(node)) {
9627bc28467SAndrew Turner if (!ofw_bus_node_status_okay(node))
96359b591b1SMichal Meloun continue;
96459b591b1SMichal Meloun
96559b591b1SMichal Meloun rv = process_lane(sc, node, pad);
96659b591b1SMichal Meloun if (rv != 0)
96759b591b1SMichal Meloun goto end;
96859b591b1SMichal Meloun }
96959b591b1SMichal Meloun pad->enabled = true;
97059b591b1SMichal Meloun rv = 0;
97159b591b1SMichal Meloun end:
97259b591b1SMichal Meloun if (name != NULL)
97359b591b1SMichal Meloun OF_prop_free(name);
97459b591b1SMichal Meloun return (rv);
97559b591b1SMichal Meloun }
97659b591b1SMichal Meloun
97759b591b1SMichal Meloun static int
process_port(struct padctl_softc * sc,phandle_t node)97859b591b1SMichal Meloun process_port(struct padctl_softc *sc, phandle_t node)
97959b591b1SMichal Meloun {
98059b591b1SMichal Meloun
98159b591b1SMichal Meloun struct padctl_port *port;
98259b591b1SMichal Meloun char *name;
98359b591b1SMichal Meloun int rv;
98459b591b1SMichal Meloun
98559b591b1SMichal Meloun name = NULL;
986217d17bcSOleksandr Tymoshenko rv = OF_getprop_alloc(node, "name", (void **)&name);
98759b591b1SMichal Meloun if (rv <= 0) {
98859b591b1SMichal Meloun device_printf(sc->dev, "Cannot read port name.\n");
98959b591b1SMichal Meloun return (ENXIO);
99059b591b1SMichal Meloun }
99159b591b1SMichal Meloun
99259b591b1SMichal Meloun port = search_port(sc, name);
99359b591b1SMichal Meloun if (port == NULL) {
99459b591b1SMichal Meloun device_printf(sc->dev, "Unknown port: %s\n", name);
99559b591b1SMichal Meloun rv = ENXIO;
99659b591b1SMichal Meloun goto end;
99759b591b1SMichal Meloun }
99859b591b1SMichal Meloun
99959b591b1SMichal Meloun if (port->type == PADCTL_PORT_USB3) {
100059b591b1SMichal Meloun rv = OF_getencprop(node, "nvidia,usb2-companion",
100159b591b1SMichal Meloun &(port->companion), sizeof(port->companion));
100259b591b1SMichal Meloun if (rv <= 0) {
100359b591b1SMichal Meloun device_printf(sc->dev,
100459b591b1SMichal Meloun "Missing 'nvidia,usb2-companion' property "
100559b591b1SMichal Meloun "for port: %s\n", name);
100659b591b1SMichal Meloun rv = ENXIO;
100759b591b1SMichal Meloun goto end;
100859b591b1SMichal Meloun }
100959b591b1SMichal Meloun }
101059b591b1SMichal Meloun
101159b591b1SMichal Meloun if (OF_hasprop(node, "vbus-supply")) {
101259b591b1SMichal Meloun rv = regulator_get_by_ofw_property(sc->dev, 0,
101359b591b1SMichal Meloun "vbus-supply", &port->supply_vbus);
101459b591b1SMichal Meloun if (rv <= 0) {
101559b591b1SMichal Meloun device_printf(sc->dev,
101659b591b1SMichal Meloun "Cannot get 'vbus-supply' regulator "
101759b591b1SMichal Meloun "for port: %s\n", name);
101859b591b1SMichal Meloun rv = ENXIO;
101959b591b1SMichal Meloun goto end;
102059b591b1SMichal Meloun }
102159b591b1SMichal Meloun }
102259b591b1SMichal Meloun
102359b591b1SMichal Meloun if (OF_hasprop(node, "nvidia,internal"))
102459b591b1SMichal Meloun port->internal = true;
102559b591b1SMichal Meloun /* Find assigned lane */
102659b591b1SMichal Meloun if (port->lane == NULL) {
102759b591b1SMichal Meloun switch(port->type) {
102859b591b1SMichal Meloun /* Routing is fixed for USB2, ULPI AND HSIC. */
102959b591b1SMichal Meloun case PADCTL_PORT_USB2:
103059b591b1SMichal Meloun port->lane = search_pad_lane(sc, PADCTL_PAD_USB2,
103159b591b1SMichal Meloun port->idx);
103259b591b1SMichal Meloun break;
103359b591b1SMichal Meloun case PADCTL_PORT_ULPI:
103459b591b1SMichal Meloun port->lane = search_pad_lane(sc, PADCTL_PAD_ULPI,
103559b591b1SMichal Meloun port->idx);
103659b591b1SMichal Meloun break;
103759b591b1SMichal Meloun case PADCTL_PORT_HSIC:
103859b591b1SMichal Meloun port->lane = search_pad_lane(sc, PADCTL_PAD_HSIC,
103959b591b1SMichal Meloun port->idx);
104059b591b1SMichal Meloun break;
104159b591b1SMichal Meloun case PADCTL_PORT_USB3:
104259b591b1SMichal Meloun port->lane = search_usb3_pad_lane(sc, port->idx);
104359b591b1SMichal Meloun break;
104459b591b1SMichal Meloun }
104559b591b1SMichal Meloun }
104659b591b1SMichal Meloun if (port->lane == NULL) {
104759b591b1SMichal Meloun device_printf(sc->dev, "Cannot find lane for port: %s\n", name);
104859b591b1SMichal Meloun rv = ENXIO;
104959b591b1SMichal Meloun goto end;
105059b591b1SMichal Meloun }
105159b591b1SMichal Meloun port->enabled = true;
105259b591b1SMichal Meloun rv = 0;
105359b591b1SMichal Meloun end:
105459b591b1SMichal Meloun if (name != NULL)
105559b591b1SMichal Meloun OF_prop_free(name);
105659b591b1SMichal Meloun return (rv);
105759b591b1SMichal Meloun }
105859b591b1SMichal Meloun
105959b591b1SMichal Meloun static int
parse_fdt(struct padctl_softc * sc,phandle_t base_node)106059b591b1SMichal Meloun parse_fdt(struct padctl_softc *sc, phandle_t base_node)
106159b591b1SMichal Meloun {
106259b591b1SMichal Meloun phandle_t node;
106359b591b1SMichal Meloun int rv;
106459b591b1SMichal Meloun
106559b591b1SMichal Meloun rv = 0;
106659b591b1SMichal Meloun node = ofw_bus_find_child(base_node, "pads");
106759b591b1SMichal Meloun
106859b591b1SMichal Meloun if (node <= 0) {
106959b591b1SMichal Meloun device_printf(sc->dev, "Cannot find pads subnode.\n");
107059b591b1SMichal Meloun return (ENXIO);
107159b591b1SMichal Meloun }
107259b591b1SMichal Meloun for (node = OF_child(node); node != 0; node = OF_peer(node)) {
10737bc28467SAndrew Turner if (!ofw_bus_node_status_okay(node))
107459b591b1SMichal Meloun continue;
107559b591b1SMichal Meloun rv = process_pad(sc, node);
107659b591b1SMichal Meloun if (rv != 0)
107759b591b1SMichal Meloun return (rv);
107859b591b1SMichal Meloun }
107959b591b1SMichal Meloun
108059b591b1SMichal Meloun node = ofw_bus_find_child(base_node, "ports");
108159b591b1SMichal Meloun if (node <= 0) {
108259b591b1SMichal Meloun device_printf(sc->dev, "Cannot find ports subnode.\n");
108359b591b1SMichal Meloun return (ENXIO);
108459b591b1SMichal Meloun }
108559b591b1SMichal Meloun for (node = OF_child(node); node != 0; node = OF_peer(node)) {
10867bc28467SAndrew Turner if (!ofw_bus_node_status_okay(node))
108759b591b1SMichal Meloun continue;
108859b591b1SMichal Meloun rv = process_port(sc, node);
108959b591b1SMichal Meloun if (rv != 0)
109059b591b1SMichal Meloun return (rv);
109159b591b1SMichal Meloun }
109259b591b1SMichal Meloun
109359b591b1SMichal Meloun return (0);
109459b591b1SMichal Meloun }
109559b591b1SMichal Meloun
109659b591b1SMichal Meloun static void
load_calibration(struct padctl_softc * sc)109759b591b1SMichal Meloun load_calibration(struct padctl_softc *sc)
109859b591b1SMichal Meloun {
109959b591b1SMichal Meloun uint32_t reg;
110059b591b1SMichal Meloun
110159b591b1SMichal Meloun /* All XUSB pad calibrations are packed into single dword.*/
110259b591b1SMichal Meloun reg = tegra_fuse_read_4(FUSE_XUSB_CALIB);
110359b591b1SMichal Meloun sc->hs_curr_level_0 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_0(reg);
110459b591b1SMichal Meloun sc->hs_curr_level_123 = FUSE_XUSB_CALIB_HS_CURR_LEVEL_123(reg);
110559b591b1SMichal Meloun sc->hs_iref_cap = FUSE_XUSB_CALIB_HS_IREF_CAP(reg);
110659b591b1SMichal Meloun sc->hs_squelch_level = FUSE_XUSB_CALIB_HS_SQUELCH_LEVEL(reg);
110759b591b1SMichal Meloun sc->hs_term_range_adj = FUSE_XUSB_CALIB_HS_TERM_RANGE_ADJ(reg);
110859b591b1SMichal Meloun }
110959b591b1SMichal Meloun
111059b591b1SMichal Meloun /* -------------------------------------------------------------------------
111159b591b1SMichal Meloun *
111259b591b1SMichal Meloun * BUS functions
111359b591b1SMichal Meloun */
111459b591b1SMichal Meloun static int
xusbpadctl_probe(device_t dev)1115ef2ee5d0SMichal Meloun xusbpadctl_probe(device_t dev)
1116ef2ee5d0SMichal Meloun {
1117ef2ee5d0SMichal Meloun
1118ef2ee5d0SMichal Meloun if (!ofw_bus_status_okay(dev))
1119ef2ee5d0SMichal Meloun return (ENXIO);
1120ef2ee5d0SMichal Meloun
1121ef2ee5d0SMichal Meloun if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
1122ef2ee5d0SMichal Meloun return (ENXIO);
1123ef2ee5d0SMichal Meloun
1124ef2ee5d0SMichal Meloun device_set_desc(dev, "Tegra XUSB phy");
1125ef2ee5d0SMichal Meloun return (BUS_PROBE_DEFAULT);
1126ef2ee5d0SMichal Meloun }
1127ef2ee5d0SMichal Meloun
1128ef2ee5d0SMichal Meloun static int
xusbpadctl_detach(device_t dev)1129ef2ee5d0SMichal Meloun xusbpadctl_detach(device_t dev)
1130ef2ee5d0SMichal Meloun {
1131ef2ee5d0SMichal Meloun
1132ef2ee5d0SMichal Meloun /* This device is always present. */
1133ef2ee5d0SMichal Meloun return (EBUSY);
1134ef2ee5d0SMichal Meloun }
1135ef2ee5d0SMichal Meloun
1136ef2ee5d0SMichal Meloun static int
xusbpadctl_attach(device_t dev)1137ef2ee5d0SMichal Meloun xusbpadctl_attach(device_t dev)
1138ef2ee5d0SMichal Meloun {
113959b591b1SMichal Meloun struct padctl_softc * sc;
114059b591b1SMichal Meloun int i, rid, rv;
114159b591b1SMichal Meloun struct padctl_port *port;
1142ef2ee5d0SMichal Meloun phandle_t node;
1143ef2ee5d0SMichal Meloun
1144ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
1145ef2ee5d0SMichal Meloun sc->dev = dev;
114659b591b1SMichal Meloun node = ofw_bus_get_node(dev);
1147ef2ee5d0SMichal Meloun
1148ef2ee5d0SMichal Meloun rid = 0;
1149ef2ee5d0SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
1150ef2ee5d0SMichal Meloun RF_ACTIVE);
1151ef2ee5d0SMichal Meloun if (sc->mem_res == NULL) {
1152ef2ee5d0SMichal Meloun device_printf(dev, "Cannot allocate memory resources\n");
1153ef2ee5d0SMichal Meloun return (ENXIO);
1154ef2ee5d0SMichal Meloun }
1155ef2ee5d0SMichal Meloun
1156dac93553SMichal Meloun rv = hwreset_get_by_ofw_name(dev, 0, "padctl", &sc->rst);
1157ef2ee5d0SMichal Meloun if (rv != 0) {
1158ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get 'padctl' reset: %d\n", rv);
1159ef2ee5d0SMichal Meloun return (rv);
1160ef2ee5d0SMichal Meloun }
1161ef2ee5d0SMichal Meloun rv = hwreset_deassert(sc->rst);
1162ef2ee5d0SMichal Meloun if (rv != 0) {
1163ef2ee5d0SMichal Meloun device_printf(dev, "Cannot unreset 'padctl' reset: %d\n", rv);
1164ef2ee5d0SMichal Meloun return (rv);
1165ef2ee5d0SMichal Meloun }
1166ef2ee5d0SMichal Meloun
116759b591b1SMichal Meloun load_calibration(sc);
1168ef2ee5d0SMichal Meloun
116959b591b1SMichal Meloun rv = parse_fdt(sc, node);
117059b591b1SMichal Meloun if (rv != 0) {
117159b591b1SMichal Meloun device_printf(dev, "Cannot parse fdt configuration: %d\n", rv);
117259b591b1SMichal Meloun return (rv);
117359b591b1SMichal Meloun }
117459b591b1SMichal Meloun for (i = 0; i < nitems(ports_tbl); i++) {
117559b591b1SMichal Meloun port = ports_tbl + i;
117659b591b1SMichal Meloun if (!port->enabled)
117759b591b1SMichal Meloun continue;
117859b591b1SMichal Meloun if (port->init == NULL)
117959b591b1SMichal Meloun continue;
118059b591b1SMichal Meloun rv = port->init(sc, port);
118159b591b1SMichal Meloun if (rv != 0) {
118259b591b1SMichal Meloun device_printf(dev, "Cannot init port '%s'\n",
118359b591b1SMichal Meloun port->name);
118459b591b1SMichal Meloun return (rv);
118559b591b1SMichal Meloun }
118659b591b1SMichal Meloun }
1187ef2ee5d0SMichal Meloun return (0);
1188ef2ee5d0SMichal Meloun }
1189ef2ee5d0SMichal Meloun
1190ef2ee5d0SMichal Meloun static device_method_t tegra_xusbpadctl_methods[] = {
1191ef2ee5d0SMichal Meloun /* Device interface */
1192ef2ee5d0SMichal Meloun DEVMETHOD(device_probe, xusbpadctl_probe),
1193ef2ee5d0SMichal Meloun DEVMETHOD(device_attach, xusbpadctl_attach),
1194ef2ee5d0SMichal Meloun DEVMETHOD(device_detach, xusbpadctl_detach),
1195ef2ee5d0SMichal Meloun
1196ef2ee5d0SMichal Meloun DEVMETHOD_END
1197ef2ee5d0SMichal Meloun };
1198ef2ee5d0SMichal Meloun
11994bda238aSMichal Meloun static DEFINE_CLASS_0(xusbpadctl, tegra_xusbpadctl_driver,
120059b591b1SMichal Meloun tegra_xusbpadctl_methods, sizeof(struct padctl_softc));
1201ef2ee5d0SMichal Meloun EARLY_DRIVER_MODULE(tegra_xusbpadctl, simplebus, tegra_xusbpadctl_driver,
1202289f133bSJohn Baldwin NULL, NULL, 73);
1203