xref: /openbsd/sys/dev/fdt/imxehci.c (revision 54fbbda3)
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