tegra_usbphy.c revision 1.4 1 /* $NetBSD: tegra_usbphy.c,v 1.4 2015/12/16 19:46:55 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.4 2015/12/16 19:46:55 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 uint32_t sc_hssync_start_delay;
73 uint32_t sc_idle_wait_delay;
74 uint32_t sc_elastic_limit;
75 uint32_t sc_term_range_adj;
76 uint32_t sc_xcvr_setup;
77 uint32_t sc_xcvr_lsfslew;
78 uint32_t sc_xcvr_lsrslew;
79 uint32_t sc_hssquelch_level;
80 uint32_t sc_hsdiscon_level;
81 uint32_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 #define PROPGET(k, v) \
151 if (of_getprop_uint32(sc->sc_phandle, (k), (v))) { \
152 aprint_error_dev(sc->sc_dev, \
153 "missing property '%s'\n", (k)); \
154 return EIO; \
155 }
156
157 PROPGET("nvidia,hssync-start-delay", &sc->sc_hssync_start_delay);
158 PROPGET("nvidia,idle-wait-delay", &sc->sc_idle_wait_delay);
159 PROPGET("nvidia,elastic-limit", &sc->sc_elastic_limit);
160 PROPGET("nvidia,term-range-adj", &sc->sc_term_range_adj);
161 PROPGET("nvidia,xcvr-setup", &sc->sc_xcvr_setup);
162 PROPGET("nvidia,xcvr-lsfslew", &sc->sc_xcvr_lsfslew);
163 PROPGET("nvidia,xcvr-lsrslew", &sc->sc_xcvr_lsrslew);
164 PROPGET("nvidia,hssquelch-level", &sc->sc_hssquelch_level);
165 PROPGET("nvidia,hsdiscon-level", &sc->sc_hsdiscon_level);
166 PROPGET("nvidia,xcvr-hsslew", &sc->sc_xcvr_hsslew);
167
168 return 0;
169 #undef PROPGET
170 }
171
172 static void
173 tegra_usbphy_utmip_init(struct tegra_usbphy_softc *sc)
174 {
175 bus_space_tag_t bst = sc->sc_bst;
176 bus_space_handle_t bsh = sc->sc_bsh;
177 int retry;
178
179 /* Put UTMIP PHY into reset before programming UTMIP config registers */
180 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
181 TEGRA_EHCI_SUSP_CTRL_UTMIP_RESET, 0);
182
183 /* Enable UTMIP PHY mode */
184 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
185 TEGRA_EHCI_SUSP_CTRL_UTMIP_PHY_ENB, 0);
186
187 /* Stop crystal clock */
188 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG1_REG,
189 0, TEGRA_EHCI_UTMIP_MISC_CFG1_PHY_XTAL_CLOCKEN);
190 delay(1);
191
192 /* Clear session status */
193 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_PHY_VBUS_SENSORS_REG,
194 0,
195 TEGRA_EHCI_PHY_VBUS_SENSORS_B_VLD_SW_VALUE |
196 TEGRA_EHCI_PHY_VBUS_SENSORS_B_VLD_SW_EN);
197
198 /* PLL configuration */
199 tegra_car_utmip_init();
200
201 /* Transceiver configuration */
202 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
203 __SHIFTIN(4, TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP) |
204 __SHIFTIN(3, TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP_MSB) |
205 __SHIFTIN(sc->sc_xcvr_hsslew,
206 TEGRA_EHCI_UTMIP_XCVR_CFG0_HSSLEW_MSB),
207 TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP |
208 TEGRA_EHCI_UTMIP_XCVR_CFG0_SETUP_MSB |
209 TEGRA_EHCI_UTMIP_XCVR_CFG0_HSSLEW_MSB);
210 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG1_REG,
211 __SHIFTIN(sc->sc_term_range_adj,
212 TEGRA_EHCI_UTMIP_XCVR_CFG1_TERM_RANGE_ADJ),
213 TEGRA_EHCI_UTMIP_XCVR_CFG1_TERM_RANGE_ADJ);
214
215 if (sc->sc_port == 0) {
216 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG0_REG,
217 TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL_MSB |
218 __SHIFTIN(sc->sc_hsdiscon_level,
219 TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL),
220 TEGRA_EHCI_UTMIP_BIAS_CFG0_HSDISCON_LEVEL);
221 }
222
223 /* Misc config */
224 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG0_REG,
225 0,
226 TEGRA_EHCI_UTMIP_MISC_CFG0_SUSPEND_EXIT_ON_EDGE);
227
228 /* BIAS cell power down lag */
229 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG1_REG,
230 __SHIFTIN(5, TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_COUNT),
231 TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_COUNT);
232
233 /* Debounce config */
234 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_REG,
235 __SHIFTIN(0x7530, TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_A),
236 TEGRA_EHCI_UTMIP_DEBOUNCE_CFG0_A);
237
238 /* Transmit signal preamble config */
239 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_TX_CFG0_REG,
240 TEGRA_EHCI_UTMIP_TX_CFG0_FS_PREAMBLE_J, 0);
241
242 /* Power-down battery charger circuit */
243 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BAT_CHRG_CFG0_REG,
244 TEGRA_EHCI_UTMIP_BAT_CHRG_CFG0_PD_CHRG, 0);
245
246 /* Select low speed bias method */
247 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
248 0, TEGRA_EHCI_UTMIP_XCVR_CFG0_LSBIAS_SEL);
249
250 /* High speed receive config */
251 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_HSRX_CFG0_REG,
252 __SHIFTIN(sc->sc_idle_wait_delay,
253 TEGRA_EHCI_UTMIP_HSRX_CFG0_IDLE_WAIT) |
254 __SHIFTIN(sc->sc_elastic_limit,
255 TEGRA_EHCI_UTMIP_HSRX_CFG0_ELASTIC_LIMIT),
256 TEGRA_EHCI_UTMIP_HSRX_CFG0_IDLE_WAIT |
257 TEGRA_EHCI_UTMIP_HSRX_CFG0_ELASTIC_LIMIT);
258 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_HSRX_CFG1_REG,
259 __SHIFTIN(sc->sc_hssync_start_delay,
260 TEGRA_EHCI_UTMIP_HSRX_CFG1_SYNC_START_DLY),
261 TEGRA_EHCI_UTMIP_HSRX_CFG1_SYNC_START_DLY);
262
263 /* Start crystal clock */
264 delay(1);
265 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_MISC_CFG1_REG,
266 TEGRA_EHCI_UTMIP_MISC_CFG1_PHY_XTAL_CLOCKEN, 0);
267
268 /* Clear port PLL powerdown status */
269 tegra_car_utmip_enable(sc->sc_port);
270
271 /* Bring UTMIP PHY out of reset */
272 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_SUSP_CTRL_REG,
273 0, TEGRA_EHCI_SUSP_CTRL_UTMIP_RESET);
274 for (retry = 100000; retry > 0; retry--) {
275 const uint32_t susp = bus_space_read_4(bst, bsh,
276 TEGRA_EHCI_SUSP_CTRL_REG);
277 if (susp & TEGRA_EHCI_SUSP_CTRL_PHY_CLK_VALID)
278 break;
279 delay(1);
280 }
281 if (retry == 0) {
282 aprint_error_dev(sc->sc_dev, "PHY clock is not valid\n");
283 return;
284 }
285
286 /* Disable ICUSB transceiver */
287 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_ICUSB_CTRL_REG,
288 0,
289 TEGRA_EHCI_ICUSB_CTRL_ENB1);
290
291 /* Power up UTMPI transceiver */
292 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG0_REG,
293 0,
294 TEGRA_EHCI_UTMIP_XCVR_CFG0_PD_POWERDOWN |
295 TEGRA_EHCI_UTMIP_XCVR_CFG0_PD2_POWERDOWN |
296 TEGRA_EHCI_UTMIP_XCVR_CFG0_PDZI_POWERDOWN);
297 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_XCVR_CFG1_REG,
298 0,
299 TEGRA_EHCI_UTMIP_XCVR_CFG1_PDDISC_POWERDOWN |
300 TEGRA_EHCI_UTMIP_XCVR_CFG1_PDCHRP_POWERDOWN |
301 TEGRA_EHCI_UTMIP_XCVR_CFG1_PDDR_POWERDOWN);
302
303 if (sc->sc_port == 0) {
304 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG0_REG,
305 0, TEGRA_EHCI_UTMIP_BIAS_CFG0_BIASPD);
306 delay(25);
307 tegra_reg_set_clear(bst, bsh, TEGRA_EHCI_UTMIP_BIAS_CFG1_REG,
308 0, TEGRA_EHCI_UTMIP_BIAS_CFG1_PDTRK_POWERDOWN);
309 }
310 }
311