xref: /netbsd-src/sys/arch/arm/nvidia/tegra_usbphy.c (revision a6f3f22f245acb8ee3bbf6871d7dce989204fa97)
1 /* $NetBSD: tegra_usbphy.c,v 1.1 2015/10/21 20:02:12 jmcneill Exp $ */
2 
3 /*-
4  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "locators.h"
30 
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: tegra_usbphy.c,v 1.1 2015/10/21 20:02:12 jmcneill Exp $");
33 
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/device.h>
37 #include <sys/intr.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 
41 #include <arm/nvidia/tegra_var.h>
42 #include <arm/nvidia/tegra_usbreg.h>
43 
44 static int	tegra_usbphy_match(device_t, cfdata_t, void *);
45 static void	tegra_usbphy_attach(device_t, device_t, void *);
46 
47 struct tegra_usbphy_softc {
48 	device_t		sc_dev;
49 	bus_space_tag_t		sc_bst;
50 	bus_space_handle_t	sc_bsh;
51 	u_int			sc_port;
52 
53 	struct tegra_gpio_pin	*sc_pin_vbus;
54 	uint8_t			sc_hssync_start_delay;
55 	uint8_t			sc_idle_wait_delay;
56 	uint8_t			sc_elastic_limit;
57 	uint8_t			sc_term_range_adj;
58 	uint8_t			sc_xcvr_setup;
59 	uint8_t			sc_xcvr_lsfslew;
60 	uint8_t			sc_xcvr_lsrslew;
61 	uint8_t			sc_hssquelch_level;
62 	uint8_t			sc_hsdiscon_level;
63 	uint8_t			sc_xcvr_hsslew;
64 };
65 
66 static int	tegra_usbphy_parse_properties(struct tegra_usbphy_softc *);
67 static void	tegra_usbphy_utmip_init(struct tegra_usbphy_softc *);
68 
69 CFATTACH_DECL_NEW(tegra_usbphy, sizeof(struct tegra_usbphy_softc),
70 	tegra_usbphy_match, tegra_usbphy_attach, NULL, NULL);
71 
72 static int
73 tegra_usbphy_match(device_t parent, cfdata_t cf, void *aux)
74 {
75 	return 1;
76 }
77 
78 static void
79 tegra_usbphy_attach(device_t parent, device_t self, void *aux)
80 {
81 	struct tegra_usbphy_softc * const sc = device_private(self);
82 	struct tegraio_attach_args * const tio = aux;
83 	const struct tegra_locators * const loc = &tio->tio_loc;
84 	prop_dictionary_t prop = device_properties(self);
85 	const char *pin;
86 
87 	sc->sc_dev = self;
88 	sc->sc_bst = tio->tio_bst;
89 	bus_space_subregion(tio->tio_bst, tio->tio_bsh,
90 	    loc->loc_offset, loc->loc_size, &sc->sc_bsh);
91 	sc->sc_port = loc->loc_port;
92 
93 	aprint_naive("\n");
94 	aprint_normal(": USB PHY%d\n", loc->loc_port + 1);
95 
96 	if (tegra_usbphy_parse_properties(sc) != 0)
97 		return;
98 
99 	tegra_car_periph_usb_enable(sc->sc_port);
100 	delay(2);
101 
102 	tegra_usbphy_utmip_init(sc);
103 
104 	if (prop_dictionary_get_cstring_nocopy(prop, "vbus-gpio", &pin)) {
105 		const uint32_t v = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
106 		    TEGRA_EHCI_PHY_VBUS_SENSORS_REG);
107 		if ((v & TEGRA_EHCI_PHY_VBUS_SENSORS_A_VBUS_VLD_STS) == 0) {
108 			sc->sc_pin_vbus = tegra_gpio_acquire(pin,
109 			    GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN);
110 			if (sc->sc_pin_vbus)
111 				tegra_gpio_write(sc->sc_pin_vbus, 1);
112 		} else {
113 			aprint_normal_dev(self, "VBUS input active\n");
114 		}
115         }
116 }
117 
118 static int
119 tegra_usbphy_parse_properties(struct tegra_usbphy_softc *sc)
120 {
121 #define PROPGET(k, v)	\
122 	if (prop_dictionary_get_uint8(prop, (k), (v)) == false) {	\
123 		aprint_error_dev(sc->sc_dev,				\
124 		    "missing property '%s'\n", (k));			\
125 		return EIO;						\
126 	}
127 
128 	prop_dictionary_t prop = device_properties(sc->sc_dev);
129 
130 	PROPGET("nvidia,hssync-start-delay", &sc->sc_hssync_start_delay);
131 	PROPGET("nvidia,idle-wait-delay", &sc->sc_idle_wait_delay);
132 	PROPGET("nvidia,elastic-limit", &sc->sc_elastic_limit);
133 	PROPGET("nvidia,term-range-adj", &sc->sc_term_range_adj);
134 	PROPGET("nvidia,xcvr-setup", &sc->sc_xcvr_setup);
135 	PROPGET("nvidia,xcvr-lsfslew", &sc->sc_xcvr_lsfslew);
136 	PROPGET("nvidia,xcvr-lsrslew", &sc->sc_xcvr_lsrslew);
137 	PROPGET("nvidia,hssquelch-level", &sc->sc_hssquelch_level);
138 	PROPGET("nvidia,hsdiscon-level", &sc->sc_hsdiscon_level);
139 	PROPGET("nvidia,xcvr-hsslew", &sc->sc_xcvr_hsslew);
140 
141 	return 0;
142 #undef PROPGET
143 }
144 
145 static void
146 tegra_usbphy_utmip_init(struct tegra_usbphy_softc *sc)
147 {
148 	bus_space_tag_t bst = sc->sc_bst;
149 	bus_space_handle_t bsh = sc->sc_bsh;
150 	int retry;
151 
152 	/* Put UTMIP PHY into reset before programming UTMIP config registers */
153 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
154 	    TEGRA_EHCI_SUSP_CTRL_UTMIP_RESET, 0);
155 
156 	/* Enable UTMIP PHY mode */
157 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
158 	    TEGRA_EHCI_SUSP_CTRL_UTMIP_PHY_ENB, 0);
159 
160 	/* Stop crystal clock */
161 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG1_REG,
162 	    0, TEGRA_EHCI_UTMIP_MISC_CFG1_PHY_XTAL_CLOCKEN);
163 	delay(1);
164 
165 	/* Clear session status */
166 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_PHY_VBUS_SENSORS_REG,
167 	    0,
168 	    TEGRA_EHCI_PHY_VBUS_SENSORS_B_VLD_SW_VALUE |
169 	    TEGRA_EHCI_PHY_VBUS_SENSORS_B_VLD_SW_EN);
170 
171 	/* PLL configuration */
172 	tegra_car_utmip_init();
173 
174 	/* Transceiver configuration */
175 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
176 	    __SHIFTIN(4, TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP) |
177 	    __SHIFTIN(3, TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP_MSB) |
178 	    __SHIFTIN(sc->sc_xcvr_hsslew,
179 		      TEGRA_EHCI_UTMIP_XCVR_CFG0_HSSLEW_MSB),
180 	    TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP |
181 	    TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP_MSB |
182 	    TEGRA_EHCI_UTMIP_XCVR_CFG0_HSSLEW_MSB);
183 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG1_REG,
184 	    __SHIFTIN(sc->sc_term_range_adj,
185 		      TEGRA_EHCI_UTMIP_XCVR_CFG1_TERM_RANGE_ADJ),
186 	    TEGRA_EHCI_UTMIP_XCVR_CFG1_TERM_RANGE_ADJ);
187 
188 	if (sc->sc_port == 0) {
189 		tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG0_REG,
190 		    TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL_MSB |
191 		    __SHIFTIN(sc->sc_hsdiscon_level,
192 			      TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL),
193 		    TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL);
194 	}
195 
196 	/* Misc config */
197 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG0_REG,
198 	    0,
199 	    TEGRA_EHCI_UTMIP_MISC_CFG0_SUSPEND_EXIT_ON_EDGE);
200 
201 	/* BIAS cell power down lag */
202 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG1_REG,
203 	    __SHIFTIN(5, TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_COUNT),
204 	    TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_COUNT);
205 
206 	/* Debounce config */
207 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_REG,
208 	    __SHIFTIN(0x7530, TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_A),
209 	    TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_A);
210 
211 	/* Transmit signal preamble config */
212 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_TX_CFG0_REG,
213 	    TEGRA_EHCI_UTMIP_TX_CFG0_FS_PREAMBLE_J, 0);
214 
215 	/* Power-down battery charger circuit */
216 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BAT_CHRG_CFG0_REG,
217 	    TEGRA_EHCI_UTMIP_BAT_CHRG_CFG0_PD_CHRG, 0);
218 
219 	/* Select low speed bias method */
220 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
221 	    0, TEGRA_EHCI_UTMIP_XCVR_CFG0_LSBIAS_SEL);
222 
223 	/* High speed receive config */
224 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_HSRX_CFG0_REG,
225 	    __SHIFTIN(sc->sc_idle_wait_delay,
226 		      TEGRA_EHCI_UTMIP_HSRX_CFG0_IDLE_WAIT) |
227 	    __SHIFTIN(sc->sc_elastic_limit,
228 		      TEGRA_EHCI_UTMIP_HSRX_CFG0_ELASTIC_LIMIT),
229 	    TEGRA_EHCI_UTMIP_HSRX_CFG0_IDLE_WAIT |
230 	    TEGRA_EHCI_UTMIP_HSRX_CFG0_ELASTIC_LIMIT);
231 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_HSRX_CFG1_REG,
232 	    __SHIFTIN(sc->sc_hssync_start_delay,
233 		      TEGRA_EHCI_UTMIP_HSRX_CFG1_SYNC_START_DLY),
234 	    TEGRA_EHCI_UTMIP_HSRX_CFG1_SYNC_START_DLY);
235 
236 	/* Start crystal clock */
237 	delay(1);
238 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG1_REG,
239 	    TEGRA_EHCI_UTMIP_MISC_CFG1_PHY_XTAL_CLOCKEN, 0);
240 
241 	/* Clear port PLL powerdown status */
242 	tegra_car_utmip_enable(sc->sc_port);
243 
244 	/* Bring UTMIP PHY out of reset */
245 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
246 	    0, TEGRA_EHCI_SUSP_CTRL_UTMIP_RESET);
247 	for (retry = 100000; retry > 0; retry--) {
248 		const uint32_t susp = bus_space_read_4(bst, bsh,
249 		    TEGRA_EHCI_SUSP_CTRL_REG);
250 		if (susp & TEGRA_EHCI_SUSP_CTRL_PHY_CLK_VALID)
251 			break;
252 		delay(1);
253 	}
254 	if (retry == 0) {
255 		aprint_error_dev(sc->sc_dev, "PHY clock is not valid\n");
256 		return;
257 	}
258 
259 	/* Disable ICUSB transceiver */
260 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_ICUSB_CTRL_REG,
261 	    0,
262 	    TEGRA_EHCI_ICUSB_CTRL_ENB1);
263 
264 	/* Power up UTMPI transceiver */
265 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
266 	    0,
267 	    TEGRA_EHCI_UTMIP_XCVR_CFG0_PD_POWERDOWN |
268 	    TEGRA_EHCI_UTMIP_XCVR_CFG0_PD2_POWERDOWN |
269 	    TEGRA_EHCI_UTMIP_XCVR_CFG0_PDZI_POWERDOWN);
270 	tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG1_REG,
271 	    0,
272 	    TEGRA_EHCI_UTMIP_XCVR_CFG1_PDDISC_POWERDOWN |
273 	    TEGRA_EHCI_UTMIP_XCVR_CFG1_PDCHRP_POWERDOWN |
274 	    TEGRA_EHCI_UTMIP_XCVR_CFG1_PDDR_POWERDOWN);
275 
276 	if (sc->sc_port == 0) {
277 		tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG0_REG,
278 		    0, TEGRA_EHCI_UTMIP_BIAS_CFG0_BIASPD);
279 		delay(25);
280 		tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG1_REG,
281 		    0, TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_POWERDOWN);
282 	}
283 }
284