1 /* $OpenBSD: imxehci.c,v 1.7 2024/09/01 03:08:56 jsg Exp $ */
2 /*
3 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/kernel.h>
22 #include <sys/rwlock.h>
23 #include <sys/timeout.h>
24
25 #include <machine/intr.h>
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28
29 #include <dev/usb/usb.h>
30 #include <dev/usb/usbdi.h>
31 #include <dev/usb/usbdivar.h>
32 #include <dev/usb/usb_mem.h>
33
34 #include <dev/ofw/openfirm.h>
35 #include <dev/ofw/ofw_clock.h>
36 #include <dev/ofw/ofw_gpio.h>
37 #include <dev/ofw/ofw_misc.h>
38 #include <dev/ofw/ofw_pinctrl.h>
39 #include <dev/ofw/ofw_power.h>
40 #include <dev/ofw/ofw_regulator.h>
41 #include <dev/ofw/fdt.h>
42
43 #include <dev/usb/ehcireg.h>
44 #include <dev/usb/ehcivar.h>
45
46 /* usb phy */
47 #define USBPHY_PWD 0x00
48 #define USBPHY_CTRL 0x30
49 #define USBPHY_CTRL_SET 0x34
50 #define USBPHY_CTRL_CLR 0x38
51 #define USBPHY_CTRL_TOG 0x3c
52
53 #define USBPHY_CTRL_ENUTMILEVEL2 (1 << 14)
54 #define USBPHY_CTRL_ENUTMILEVEL3 (1 << 15)
55 #define USBPHY_CTRL_CLKGATE (1 << 30)
56 #define USBPHY_CTRL_SFTRST (1U << 31)
57
58 /* ehci */
59 #define USB_EHCI_OFFSET 0x100
60
61 #define EHCI_PS_PTS_UTMI_MASK ((1 << 25) | (3 << 30))
62
63 /* usb non-core */
64 #define USBNC_USB_OTG_CTRL 0x00
65 #define USBNC_USB_UH1_CTRL 0x04
66
67 #define USBNC_USB_CTRL_PWR_POL (1 << 9)
68 #define USBNC_USB_CTRL_OVER_CUR_POL (1 << 8)
69 #define USBNC_USB_CTRL_OVER_CUR_DIS (1 << 7)
70 #define USBNC_USB_CTRL_NON_BURST (1 << 1)
71
72 /* anatop */
73 #define ANALOG_USB1_CHRG_DETECT 0x1b0
74 #define ANALOG_USB1_CHRG_DETECT_SET 0x1b4
75 #define ANALOG_USB1_CHRG_DETECT_CLR 0x1b8
76 #define ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B (1 << 19)
77 #define ANALOG_USB1_CHRG_DETECT_EN_B (1 << 20)
78 #define ANALOG_USB2_CHRG_DETECT 0x210
79 #define ANALOG_USB2_CHRG_DETECT_SET 0x214
80 #define ANALOG_USB2_CHRG_DETECT_CLR 0x218
81 #define ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B (1 << 19)
82 #define ANALOG_USB2_CHRG_DETECT_EN_B (1 << 20)
83
84 int imxehci_match(struct device *, void *, void *);
85 void imxehci_attach(struct device *, struct device *, void *);
86 int imxehci_detach(struct device *, int);
87
88 struct imxehci_softc {
89 struct ehci_softc sc;
90 void *sc_ih;
91 bus_space_handle_t uh_ioh;
92 bus_space_handle_t ph_ioh;
93 bus_space_handle_t nc_ioh;
94 uint32_t sc_unit;
95 };
96
97 const struct cfattach imxehci_ca = {
98 sizeof (struct imxehci_softc), imxehci_match, imxehci_attach,
99 imxehci_detach
100 };
101
102 struct cfdriver imxehci_cd = {
103 NULL, "imxehci", DV_DULL
104 };
105
106 void imxehci_init_phy(struct imxehci_softc *, uint32_t *);
107
108 int
imxehci_match(struct device * parent,void * match,void * aux)109 imxehci_match(struct device *parent, void *match, void *aux)
110 {
111 struct fdt_attach_args *faa = aux;
112
113 return (OF_is_compatible(faa->fa_node, "fsl,imx27-usb") ||
114 OF_is_compatible(faa->fa_node, "fsl,imx7d-usb"));
115 }
116
117 void
imxehci_attach(struct device * parent,struct device * self,void * aux)118 imxehci_attach(struct device *parent, struct device *self, void *aux)
119 {
120 struct imxehci_softc *sc = (struct imxehci_softc *)self;
121 struct fdt_attach_args *faa = aux;
122 usbd_status r;
123 char *devname = sc->sc.sc_bus.bdev.dv_xname;
124 uint32_t phy[1], misc[2];
125 uint32_t misc_reg[2];
126 uint32_t off, reg;
127 uint32_t vbus;
128 int misc_node;
129
130 if (faa->fa_nreg < 1)
131 return;
132
133 if (OF_getpropintarray(faa->fa_node, "phys",
134 phy, sizeof(phy)) != sizeof(phy)) {
135 if (OF_getpropintarray(faa->fa_node, "fsl,usbphy",
136 phy, sizeof(phy)) != sizeof(phy))
137 return;
138 }
139
140 if (OF_getpropintarray(faa->fa_node, "fsl,usbmisc",
141 misc, sizeof(misc)) != sizeof(misc))
142 return;
143
144 misc_node = OF_getnodebyphandle(misc[0]);
145 if (misc_node == 0)
146 return;
147
148 if (OF_getpropintarray(misc_node, "reg", misc_reg,
149 sizeof(misc_reg)) != sizeof(misc_reg))
150 return;
151
152 sc->sc.iot = faa->fa_iot;
153 sc->sc.sc_bus.dmatag = faa->fa_dmat;
154 sc->sc.sc_size = faa->fa_reg[0].size - USB_EHCI_OFFSET;
155 sc->sc.sc_flags = EHCIF_USBMODE;
156 sc->sc_unit = misc[1];
157
158 /* Map I/O space */
159 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
160 faa->fa_reg[0].size, 0, &sc->uh_ioh)) {
161 printf(": cannot map mem space\n");
162 goto out;
163 }
164 if (bus_space_subregion(sc->sc.iot, sc->uh_ioh, USB_EHCI_OFFSET,
165 sc->sc.sc_size, &sc->sc.ioh)) {
166 printf(": cannot map mem space\n");
167 goto mem0;
168 }
169
170 if (bus_space_map(sc->sc.iot, misc_reg[0],
171 misc_reg[1], 0, &sc->nc_ioh)) {
172 printf(": cannot map mem space\n");
173 goto mem1;
174 }
175
176 printf("\n");
177
178 pinctrl_byname(faa->fa_node, "default");
179 power_domain_enable(faa->fa_node);
180 clock_set_assigned(faa->fa_node);
181 clock_enable(faa->fa_node, NULL);
182 delay(1000);
183
184 /* over current and polarity setting */
185 switch (misc[1]) {
186 case 0:
187 off = USBNC_USB_OTG_CTRL;
188 break;
189 case 1:
190 off = USBNC_USB_UH1_CTRL;
191 break;
192 default:
193 printf("%s: invalid usbmisc property\n", devname);
194 return;
195 }
196
197 reg = bus_space_read_4(sc->sc.iot, sc->nc_ioh, off);
198 reg &= ~USBNC_USB_CTRL_OVER_CUR_DIS;
199 if (OF_getproplen(faa->fa_node, "disable-over-current") == 0)
200 reg |= USBNC_USB_CTRL_OVER_CUR_DIS;
201 if (OF_getproplen(faa->fa_node, "over-current-active-low") == 0)
202 reg |= USBNC_USB_CTRL_OVER_CUR_POL;
203 else if (OF_getproplen(faa->fa_node, "over-current-active-high") == 0)
204 reg &= ~USBNC_USB_CTRL_OVER_CUR_POL;
205 if (OF_getproplen(faa->fa_node, "power-active-high") == 0)
206 reg |= USBNC_USB_CTRL_PWR_POL;
207 reg |= USBNC_USB_CTRL_NON_BURST;
208 bus_space_write_4(sc->sc.iot, sc->nc_ioh, off, reg);
209
210 /* enable usb bus power */
211 vbus = OF_getpropint(faa->fa_node, "vbus-supply", 0);
212 if (vbus)
213 regulator_enable(vbus);
214
215 /* Disable interrupts, so we don't get any spurious ones. */
216 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
217 EOWRITE2(&sc->sc, EHCI_USBINTR, 0);
218
219 /* Stop then Reset */
220 uint32_t val = EOREAD4(&sc->sc, EHCI_USBCMD);
221 val &= ~EHCI_CMD_RS;
222 EOWRITE4(&sc->sc, EHCI_USBCMD, val);
223
224 while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_RS)
225 ;
226
227 val = EOREAD4(&sc->sc, EHCI_USBCMD);
228 val |= EHCI_CMD_HCRESET;
229 EOWRITE4(&sc->sc, EHCI_USBCMD, val);
230
231 while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_HCRESET)
232 ;
233
234 /* power up PHY */
235 imxehci_init_phy(sc, phy);
236
237 /* set host mode */
238 EOWRITE4(&sc->sc, EHCI_USBMODE,
239 EOREAD4(&sc->sc, EHCI_USBMODE) | EHCI_USBMODE_CM_HOST);
240
241 /* set to UTMI mode */
242 EOWRITE4(&sc->sc, EHCI_PORTSC(1),
243 EOREAD4(&sc->sc, EHCI_PORTSC(1)) & ~EHCI_PS_PTS_UTMI_MASK);
244
245 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB,
246 ehci_intr, &sc->sc, devname);
247 if (sc->sc_ih == NULL) {
248 printf(": unable to establish interrupt\n");
249 goto mem2;
250 }
251
252 strlcpy(sc->sc.sc_vendor, "i.MX", sizeof(sc->sc.sc_vendor));
253 r = ehci_init(&sc->sc);
254 if (r != USBD_NORMAL_COMPLETION) {
255 printf("%s: init failed, error=%d\n", devname, r);
256 goto intr;
257 }
258
259 config_found(self, &sc->sc.sc_bus, usbctlprint);
260
261 goto out;
262
263 intr:
264 fdt_intr_disestablish(sc->sc_ih);
265 sc->sc_ih = NULL;
266 mem2:
267 bus_space_unmap(sc->sc.iot, sc->nc_ioh, misc_reg[1]);
268 mem1:
269 mem0:
270 bus_space_unmap(sc->sc.iot, sc->sc.ioh, faa->fa_reg[0].size);
271 sc->sc.sc_size = 0;
272 out:
273 return;
274 }
275
276 int
imxehci_detach(struct device * self,int flags)277 imxehci_detach(struct device *self, int flags)
278 {
279 struct imxehci_softc *sc = (struct imxehci_softc *)self;
280 int rv;
281
282 rv = ehci_detach(self, flags);
283 if (rv)
284 return (rv);
285
286 if (sc->sc_ih != NULL) {
287 fdt_intr_disestablish(sc->sc_ih);
288 sc->sc_ih = NULL;
289 }
290
291 if (sc->sc.sc_size) {
292 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
293 sc->sc.sc_size = 0;
294 }
295
296 return (0);
297 }
298
299 /*
300 * PHY initialization.
301 */
302
303 struct imxehci_phy {
304 const char *compat;
305 void (*init)(struct imxehci_softc *, uint32_t *);
306 };
307
308 void imx23_usb_init(struct imxehci_softc *, uint32_t *);
309 static void nop_xceiv_init(struct imxehci_softc *, uint32_t *);
310
311 struct imxehci_phy imxehci_phys[] = {
312 { "fsl,imx23-usbphy", imx23_usb_init },
313 { "usb-nop-xceiv", nop_xceiv_init },
314 };
315
316 void
imxehci_init_phy(struct imxehci_softc * sc,uint32_t * cells)317 imxehci_init_phy(struct imxehci_softc *sc, uint32_t *cells)
318 {
319 int node;
320 int i;
321
322 node = OF_getnodebyphandle(cells[0]);
323 if (node == 0)
324 return;
325
326 for (i = 0; i < nitems(imxehci_phys); i++) {
327 if (OF_is_compatible(node, imxehci_phys[i].compat)) {
328 imxehci_phys[i].init(sc, cells);
329 return;
330 }
331 }
332 }
333
334 /*
335 * i.MX5/6 PHYs.
336 */
337
338 void
imx23_usb_init(struct imxehci_softc * sc,uint32_t * cells)339 imx23_usb_init(struct imxehci_softc *sc, uint32_t *cells)
340 {
341 struct regmap *rm = NULL;
342 uint32_t phy_reg[2];
343 uint32_t anatop[1];
344 int node;
345
346 node = OF_getnodebyphandle(cells[0]);
347 KASSERT(node != 0);
348
349 if (OF_getpropintarray(node, "reg", phy_reg,
350 sizeof(phy_reg)) != sizeof(phy_reg))
351 return;
352
353 if (bus_space_map(sc->sc.iot, phy_reg[0],
354 phy_reg[1], 0, &sc->ph_ioh)) {
355 printf("%s: can't map PHY registers\n",
356 sc->sc.sc_bus.bdev.dv_xname);
357 return;
358 }
359
360 if (OF_getpropintarray(node, "fsl,anatop",
361 anatop, sizeof(anatop)) == sizeof(anatop))
362 rm = regmap_byphandle(anatop[0]);
363
364 /* Disable the charger detection, else signal on DP will be poor */
365 switch (sc->sc_unit) {
366 case 0:
367 if (rm != NULL)
368 regmap_write_4(rm, ANALOG_USB1_CHRG_DETECT_SET,
369 ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B |
370 ANALOG_USB1_CHRG_DETECT_EN_B);
371 break;
372 case 1:
373 if (rm != NULL)
374 regmap_write_4(rm, ANALOG_USB2_CHRG_DETECT_SET,
375 ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B |
376 ANALOG_USB2_CHRG_DETECT_EN_B);
377 break;
378 }
379
380 clock_enable(node, NULL);
381
382 /* Reset USBPHY module */
383 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET,
384 USBPHY_CTRL_SFTRST);
385
386 delay(10);
387
388 /* Remove CLKGATE and SFTRST */
389 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_CLR,
390 USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST);
391
392 delay(10);
393
394 /* Power up the PHY */
395 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_PWD, 0);
396
397 /* enable FS/LS device */
398 bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET,
399 USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3);
400 }
401
402 /*
403 * i.MX7 PHYs.
404 */
405
406 static void
nop_xceiv_init(struct imxehci_softc * sc,uint32_t * cells)407 nop_xceiv_init(struct imxehci_softc *sc, uint32_t *cells)
408 {
409 int node;
410
411 node = OF_getnodebyphandle(cells[0]);
412 KASSERT(node != 0);
413
414 clock_set_assigned(node);
415 clock_enable(node, NULL);
416 }
417