tegra_usbphy.c revision 1.3 1 /* $NetBSD: tegra_usbphy.c,v 1.3 2015/12/13 17:39:19 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) 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 <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: tegra_usbphy.c,v 1.3 2015/12/13 17:39:19 jmcneill Exp $");
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/device.h>
35 #include <sys/intr.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38
39 #include <arm/nvidia/tegra_reg.h>
40 #include <arm/nvidia/tegra_var.h>
41 #include <arm/nvidia/tegra_usbreg.h>
42
43 #include <dev/fdt/fdtvar.h>
44
45 /* XXX */
46 static int
47 tegra_usbphy_addr2port(bus_addr_t addr)
48 {
49 switch (addr) {
50 case TEGRA_AHB_A2_BASE + TEGRA_USB1_OFFSET:
51 return 0;
52 case TEGRA_AHB_A2_BASE + TEGRA_USB2_OFFSET:
53 return 1;
54 case TEGRA_AHB_A2_BASE + TEGRA_USB3_OFFSET:
55 return 2;
56 default:
57 return -1;
58 }
59 }
60
61 static int tegra_usbphy_match(device_t, cfdata_t, void *);
62 static void tegra_usbphy_attach(device_t, device_t, void *);
63
64 struct tegra_usbphy_softc {
65 device_t sc_dev;
66 bus_space_tag_t sc_bst;
67 bus_space_handle_t sc_bsh;
68 int sc_phandle;
69 u_int sc_port;
70
71 struct tegra_gpio_pin *sc_pin_vbus;
72 uint8_t sc_hssync_start_delay;
73 uint8_t sc_idle_wait_delay;
74 uint8_t sc_elastic_limit;
75 uint8_t sc_term_range_adj;
76 uint8_t sc_xcvr_setup;
77 uint8_t sc_xcvr_lsfslew;
78 uint8_t sc_xcvr_lsrslew;
79 uint8_t sc_hssquelch_level;
80 uint8_t sc_hsdiscon_level;
81 uint8_t sc_xcvr_hsslew;
82 };
83
84 static int tegra_usbphy_parse_properties(struct tegra_usbphy_softc *);
85 static void tegra_usbphy_utmip_init(struct tegra_usbphy_softc *);
86
87 CFATTACH_DECL_NEW(tegra_usbphy, sizeof(struct tegra_usbphy_softc),
88 tegra_usbphy_match, tegra_usbphy_attach, NULL, NULL);
89
90 static int
91 tegra_usbphy_match(device_t parent, cfdata_t cf, void *aux)
92 {
93 const char * const compatible[] = { "nvidia,tegra124-usb-phy", NULL };
94 struct fdt_attach_args * const faa = aux;
95
96 return of_match_compatible(faa->faa_phandle, compatible);
97 }
98
99 static void
100 tegra_usbphy_attach(device_t parent, device_t self, void *aux)
101 {
102 struct tegra_usbphy_softc * const sc = device_private(self);
103 struct fdt_attach_args * const faa = aux;
104 struct fdtbus_regulator *reg;
105 bus_addr_t addr;
106 bus_size_t size;
107 int error;
108
109 if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
110 aprint_error(": couldn't get registers\n");
111 return;
112 }
113
114 sc->sc_dev = self;
115 sc->sc_phandle = faa->faa_phandle;
116 sc->sc_bst = faa->faa_bst;
117 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
118 if (error) {
119 aprint_error(": couldn't map %#llx: %d", (uint64_t)addr, error);
120 return;
121 }
122 sc->sc_port = tegra_usbphy_addr2port(addr);
123
124 aprint_naive("\n");
125 aprint_normal(": USB PHY%d\n", sc->sc_port + 1);
126
127 if (tegra_usbphy_parse_properties(sc) != 0)
128 return;
129
130 tegra_car_periph_usb_enable(sc->sc_port);
131 delay(2);
132
133 tegra_usbphy_utmip_init(sc);
134
135 reg = fdtbus_regulator_acquire(faa->faa_phandle, "vbus-supply");
136 if (reg) {
137 const uint32_t v = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
138 TEGRA_EHCI_PHY_VBUS_SENSORS_REG);
139 if ((v & TEGRA_EHCI_PHY_VBUS_SENSORS_A_VBUS_VLD_STS) == 0) {
140 fdtbus_regulator_enable(reg);
141 } else {
142 aprint_normal_dev(self, "VBUS input active\n");
143 }
144 }
145 }
146
147 static int
148 tegra_usbphy_parse_properties(struct tegra_usbphy_softc *sc)
149 {
150 const int phandle = sc->sc_phandle;
151 const int plen = sizeof(u_int);
152 u_int val;
153
154 #define PROPGET(k, v) \
155 do { \
156 if (OF_getprop(phandle, (k), &val, plen) != plen) { \
157 aprint_error_dev(sc->sc_dev, \
158 "missing property '%s'\n", (k)); \
159 return EIO; \
160 } \
161 *(v) = be32toh(val); \
162 } while (0)
163
164 PROPGET("nvidia,hssync-start-delay", &sc->sc_hssync_start_delay);
165 PROPGET("nvidia,idle-wait-delay", &sc->sc_idle_wait_delay);
166 PROPGET("nvidia,elastic-limit", &sc->sc_elastic_limit);
167 PROPGET("nvidia,term-range-adj", &sc->sc_term_range_adj);
168 PROPGET("nvidia,xcvr-setup", &sc->sc_xcvr_setup);
169 PROPGET("nvidia,xcvr-lsfslew", &sc->sc_xcvr_lsfslew);
170 PROPGET("nvidia,xcvr-lsrslew", &sc->sc_xcvr_lsrslew);
171 PROPGET("nvidia,hssquelch-level", &sc->sc_hssquelch_level);
172 PROPGET("nvidia,hsdiscon-level", &sc->sc_hsdiscon_level);
173 PROPGET("nvidia,xcvr-hsslew", &sc->sc_xcvr_hsslew);
174
175 return 0;
176 #undef PROPGET
177 }
178
179 static void
180 tegra_usbphy_utmip_init(struct tegra_usbphy_softc *sc)
181 {
182 bus_space_tag_t bst = sc->sc_bst;
183 bus_space_handle_t bsh = sc->sc_bsh;
184 int retry;
185
186 /* Put UTMIP PHY into reset before programming UTMIP config registers */
187 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
188 TEGRA_EHCI_SUSP_CTRL_UTMIP_RESET, 0);
189
190 /* Enable UTMIP PHY mode */
191 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
192 TEGRA_EHCI_SUSP_CTRL_UTMIP_PHY_ENB, 0);
193
194 /* Stop crystal clock */
195 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG1_REG,
196 0, TEGRA_EHCI_UTMIP_MISC_CFG1_PHY_XTAL_CLOCKEN);
197 delay(1);
198
199 /* Clear session status */
200 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_PHY_VBUS_SENSORS_REG,
201 0,
202 TEGRA_EHCI_PHY_VBUS_SENSORS_B_VLD_SW_VALUE |
203 TEGRA_EHCI_PHY_VBUS_SENSORS_B_VLD_SW_EN);
204
205 /* PLL configuration */
206 tegra_car_utmip_init();
207
208 /* Transceiver configuration */
209 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
210 __SHIFTIN(4, TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP) |
211 __SHIFTIN(3, TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP_MSB) |
212 __SHIFTIN(sc->sc_xcvr_hsslew,
213 TEGRA_EHCI_UTMIP_XCVR_CFG0_HSSLEW_MSB),
214 TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP |
215 TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP_MSB |
216 TEGRA_EHCI_UTMIP_XCVR_CFG0_HSSLEW_MSB);
217 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG1_REG,
218 __SHIFTIN(sc->sc_term_range_adj,
219 TEGRA_EHCI_UTMIP_XCVR_CFG1_TERM_RANGE_ADJ),
220 TEGRA_EHCI_UTMIP_XCVR_CFG1_TERM_RANGE_ADJ);
221
222 if (sc->sc_port == 0) {
223 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG0_REG,
224 TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL_MSB |
225 __SHIFTIN(sc->sc_hsdiscon_level,
226 TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL),
227 TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL);
228 }
229
230 /* Misc config */
231 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG0_REG,
232 0,
233 TEGRA_EHCI_UTMIP_MISC_CFG0_SUSPEND_EXIT_ON_EDGE);
234
235 /* BIAS cell power down lag */
236 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG1_REG,
237 __SHIFTIN(5, TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_COUNT),
238 TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_COUNT);
239
240 /* Debounce config */
241 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_REG,
242 __SHIFTIN(0x7530, TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_A),
243 TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_A);
244
245 /* Transmit signal preamble config */
246 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_TX_CFG0_REG,
247 TEGRA_EHCI_UTMIP_TX_CFG0_FS_PREAMBLE_J, 0);
248
249 /* Power-down battery charger circuit */
250 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BAT_CHRG_CFG0_REG,
251 TEGRA_EHCI_UTMIP_BAT_CHRG_CFG0_PD_CHRG, 0);
252
253 /* Select low speed bias method */
254 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
255 0, TEGRA_EHCI_UTMIP_XCVR_CFG0_LSBIAS_SEL);
256
257 /* High speed receive config */
258 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_HSRX_CFG0_REG,
259 __SHIFTIN(sc->sc_idle_wait_delay,
260 TEGRA_EHCI_UTMIP_HSRX_CFG0_IDLE_WAIT) |
261 __SHIFTIN(sc->sc_elastic_limit,
262 TEGRA_EHCI_UTMIP_HSRX_CFG0_ELASTIC_LIMIT),
263 TEGRA_EHCI_UTMIP_HSRX_CFG0_IDLE_WAIT |
264 TEGRA_EHCI_UTMIP_HSRX_CFG0_ELASTIC_LIMIT);
265 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_HSRX_CFG1_REG,
266 __SHIFTIN(sc->sc_hssync_start_delay,
267 TEGRA_EHCI_UTMIP_HSRX_CFG1_SYNC_START_DLY),
268 TEGRA_EHCI_UTMIP_HSRX_CFG1_SYNC_START_DLY);
269
270 /* Start crystal clock */
271 delay(1);
272 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG1_REG,
273 TEGRA_EHCI_UTMIP_MISC_CFG1_PHY_XTAL_CLOCKEN, 0);
274
275 /* Clear port PLL powerdown status */
276 tegra_car_utmip_enable(sc->sc_port);
277
278 /* Bring UTMIP PHY out of reset */
279 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
280 0, TEGRA_EHCI_SUSP_CTRL_UTMIP_RESET);
281 for (retry = 100000; retry > 0; retry--) {
282 const uint32_t susp = bus_space_read_4(bst, bsh,
283 TEGRA_EHCI_SUSP_CTRL_REG);
284 if (susp & TEGRA_EHCI_SUSP_CTRL_PHY_CLK_VALID)
285 break;
286 delay(1);
287 }
288 if (retry == 0) {
289 aprint_error_dev(sc->sc_dev, "PHY clock is not valid\n");
290 return;
291 }
292
293 /* Disable ICUSB transceiver */
294 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_ICUSB_CTRL_REG,
295 0,
296 TEGRA_EHCI_ICUSB_CTRL_ENB1);
297
298 /* Power up UTMPI transceiver */
299 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
300 0,
301 TEGRA_EHCI_UTMIP_XCVR_CFG0_PD_POWERDOWN |
302 TEGRA_EHCI_UTMIP_XCVR_CFG0_PD2_POWERDOWN |
303 TEGRA_EHCI_UTMIP_XCVR_CFG0_PDZI_POWERDOWN);
304 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG1_REG,
305 0,
306 TEGRA_EHCI_UTMIP_XCVR_CFG1_PDDISC_POWERDOWN |
307 TEGRA_EHCI_UTMIP_XCVR_CFG1_PDCHRP_POWERDOWN |
308 TEGRA_EHCI_UTMIP_XCVR_CFG1_PDDR_POWERDOWN);
309
310 if (sc->sc_port == 0) {
311 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG0_REG,
312 0, TEGRA_EHCI_UTMIP_BIAS_CFG0_BIASPD);
313 delay(25);
314 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG1_REG,
315 0, TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_POWERDOWN);
316 }
317 }
318