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