xref: /openbsd/sys/dev/fdt/imxehci.c (revision 09467b48)
1 /*	$OpenBSD: imxehci.c,v 1.4 2020/04/27 20:15:41 patrick 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 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
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
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, "fsl,usbphy",
134 	    phy, sizeof(phy)) != sizeof(phy))
135 		return;
136 
137 	if (OF_getpropintarray(faa->fa_node, "fsl,usbmisc",
138 	    misc, sizeof(misc)) != sizeof(misc))
139 		return;
140 
141 	misc_node = OF_getnodebyphandle(misc[0]);
142 	if (misc_node == 0)
143 		return;
144 
145 	if (OF_getpropintarray(misc_node, "reg", misc_reg,
146 	    sizeof(misc_reg)) != sizeof(misc_reg))
147 		return;
148 
149 	sc->sc.iot = faa->fa_iot;
150 	sc->sc.sc_bus.dmatag = faa->fa_dmat;
151 	sc->sc.sc_size = faa->fa_reg[0].size - USB_EHCI_OFFSET;
152 	sc->sc.sc_flags = EHCIF_USBMODE;
153 	sc->sc_unit = misc[1];
154 
155 	/* Map I/O space */
156 	if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
157 	    faa->fa_reg[0].size, 0, &sc->uh_ioh)) {
158 		printf(": cannot map mem space\n");
159 		goto out;
160 	}
161 	if (bus_space_subregion(sc->sc.iot, sc->uh_ioh, USB_EHCI_OFFSET,
162 	    sc->sc.sc_size, &sc->sc.ioh)) {
163 		printf(": cannot map mem space\n");
164 		goto mem0;
165 	}
166 
167 	if (bus_space_map(sc->sc.iot, misc_reg[0],
168 	    misc_reg[1], 0, &sc->nc_ioh)) {
169 		printf(": cannot map mem space\n");
170 		goto mem1;
171 	}
172 
173 	printf("\n");
174 
175 	pinctrl_byname(faa->fa_node, "default");
176 	power_domain_enable(faa->fa_node);
177 	clock_set_assigned(faa->fa_node);
178 	clock_enable(faa->fa_node, NULL);
179 	delay(1000);
180 
181 	/* over current and polarity setting */
182 	switch (misc[1]) {
183 	case 0:
184 		off = USBNC_USB_OTG_CTRL;
185 		break;
186 	case 1:
187 		off = USBNC_USB_UH1_CTRL;
188 		break;
189 	default:
190 		printf("%s: invalid usbmisc property\n", devname);
191 		return;
192 	}
193 
194 	reg = bus_space_read_4(sc->sc.iot, sc->nc_ioh, off);
195 	reg &= ~USBNC_USB_CTRL_OVER_CUR_DIS;
196 	if (OF_getproplen(faa->fa_node, "disable-over-current") == 0)
197 		reg |= USBNC_USB_CTRL_OVER_CUR_DIS;
198 	if (OF_getproplen(faa->fa_node, "over-current-active-low") == 0)
199 		reg |= USBNC_USB_CTRL_OVER_CUR_POL;
200 	else if (OF_getproplen(faa->fa_node, "over-current-active-high") == 0)
201 		reg &= ~USBNC_USB_CTRL_OVER_CUR_POL;
202 	if (OF_getproplen(faa->fa_node, "power-active-high") == 0)
203 		reg |= USBNC_USB_CTRL_PWR_POL;
204 	reg |= USBNC_USB_CTRL_NON_BURST;
205 	bus_space_write_4(sc->sc.iot, sc->nc_ioh, off, reg);
206 
207 	/* enable usb bus power */
208 	vbus = OF_getpropint(faa->fa_node, "vbus-supply", 0);
209 	if (vbus)
210 		regulator_enable(vbus);
211 
212 	/* Disable interrupts, so we don't get any spurious ones. */
213 	sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
214 	EOWRITE2(&sc->sc, EHCI_USBINTR, 0);
215 
216 	/* Stop then Reset */
217 	uint32_t val = EOREAD4(&sc->sc, EHCI_USBCMD);
218 	val &= ~EHCI_CMD_RS;
219 	EOWRITE4(&sc->sc, EHCI_USBCMD, val);
220 
221 	while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_RS)
222 		;
223 
224 	val = EOREAD4(&sc->sc, EHCI_USBCMD);
225 	val |= EHCI_CMD_HCRESET;
226 	EOWRITE4(&sc->sc, EHCI_USBCMD, val);
227 
228 	while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_HCRESET)
229 		;
230 
231 	/* power up PHY */
232 	imxehci_init_phy(sc, phy);
233 
234 	/* set host mode */
235 	EOWRITE4(&sc->sc, EHCI_USBMODE,
236 	    EOREAD4(&sc->sc, EHCI_USBMODE) | EHCI_USBMODE_CM_HOST);
237 
238 	/* set to UTMI mode */
239 	EOWRITE4(&sc->sc, EHCI_PORTSC(1),
240 	    EOREAD4(&sc->sc, EHCI_PORTSC(1)) & ~EHCI_PS_PTS_UTMI_MASK);
241 
242 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB,
243 	    ehci_intr, &sc->sc, devname);
244 	if (sc->sc_ih == NULL) {
245 		printf(": unable to establish interrupt\n");
246 		goto mem2;
247 	}
248 
249 	strlcpy(sc->sc.sc_vendor, "i.MX", sizeof(sc->sc.sc_vendor));
250 	r = ehci_init(&sc->sc);
251 	if (r != USBD_NORMAL_COMPLETION) {
252 		printf("%s: init failed, error=%d\n", devname, r);
253 		goto intr;
254 	}
255 
256 	config_found(self, &sc->sc.sc_bus, usbctlprint);
257 
258 	goto out;
259 
260 intr:
261 	fdt_intr_disestablish(sc->sc_ih);
262 	sc->sc_ih = NULL;
263 mem2:
264 	bus_space_unmap(sc->sc.iot, sc->nc_ioh, misc_reg[1]);
265 mem1:
266 mem0:
267 	bus_space_unmap(sc->sc.iot, sc->sc.ioh, faa->fa_reg[0].size);
268 	sc->sc.sc_size = 0;
269 out:
270 	return;
271 }
272 
273 int
274 imxehci_detach(struct device *self, int flags)
275 {
276 	struct imxehci_softc		*sc = (struct imxehci_softc *)self;
277 	int				rv;
278 
279 	rv = ehci_detach(self, flags);
280 	if (rv)
281 		return (rv);
282 
283 	if (sc->sc_ih != NULL) {
284 		fdt_intr_disestablish(sc->sc_ih);
285 		sc->sc_ih = NULL;
286 	}
287 
288 	if (sc->sc.sc_size) {
289 		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
290 		sc->sc.sc_size = 0;
291 	}
292 
293 	return (0);
294 }
295 
296 /*
297  * PHY initialization.
298  */
299 
300 struct imxehci_phy {
301 	const char *compat;
302 	void (*init)(struct imxehci_softc *, uint32_t *);
303 };
304 
305 void imx23_usb_init(struct imxehci_softc *, uint32_t *);
306 static void nop_xceiv_init(struct imxehci_softc *, uint32_t *);
307 
308 struct imxehci_phy imxehci_phys[] = {
309 	{ "fsl,imx23-usbphy", imx23_usb_init },
310 	{ "usb-nop-xceiv", nop_xceiv_init },
311 };
312 
313 void
314 imxehci_init_phy(struct imxehci_softc *sc, uint32_t *cells)
315 {
316 	int node;
317 	int i;
318 
319 	node = OF_getnodebyphandle(cells[0]);
320 	if (node == 0)
321 		return;
322 
323 	for (i = 0; i < nitems(imxehci_phys); i++) {
324 		if (OF_is_compatible(node, imxehci_phys[i].compat)) {
325 			imxehci_phys[i].init(sc, cells);
326 			return;
327 		}
328 	}
329 }
330 
331 /*
332  * i.MX5/6 PHYs.
333  */
334 
335 void
336 imx23_usb_init(struct imxehci_softc *sc, uint32_t *cells)
337 {
338 	struct regmap *rm = NULL;
339 	uint32_t phy_reg[2];
340 	uint32_t anatop[1];
341 	int node;
342 
343 	node = OF_getnodebyphandle(cells[0]);
344 	KASSERT(node != 0);
345 
346 	if (OF_getpropintarray(node, "reg", phy_reg,
347 	    sizeof(phy_reg)) != sizeof(phy_reg))
348 		return;
349 
350 	if (bus_space_map(sc->sc.iot, phy_reg[0],
351 	    phy_reg[1], 0, &sc->ph_ioh)) {
352 		printf("%s: can't map PHY registers\n",
353 		    sc->sc.sc_bus.bdev.dv_xname);
354 		return;
355 	}
356 
357 	if (OF_getpropintarray(node, "fsl,anatop",
358 	    anatop, sizeof(anatop)) == sizeof(anatop))
359 		rm = regmap_byphandle(anatop[0]);
360 
361 	/* Disable the carger detection, else signal on DP will be poor */
362 	switch (sc->sc_unit) {
363 	case 0:
364 		if (rm != NULL)
365 			regmap_write_4(rm, ANALOG_USB1_CHRG_DETECT_SET,
366 			    ANALOG_USB1_CHRG_DETECT_CHK_CHRG_B |
367 			    ANALOG_USB1_CHRG_DETECT_EN_B);
368 		break;
369 	case 1:
370 		if (rm != NULL)
371 			regmap_write_4(rm, ANALOG_USB2_CHRG_DETECT_SET,
372 			    ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B |
373 			    ANALOG_USB2_CHRG_DETECT_EN_B);
374 		break;
375 	}
376 
377 	clock_enable(node, NULL);
378 
379 	/* Reset USBPHY module */
380 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET,
381 	    USBPHY_CTRL_SFTRST);
382 
383 	delay(10);
384 
385 	/* Remove CLKGATE and SFTRST */
386 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_CLR,
387 	    USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST);
388 
389 	delay(10);
390 
391 	/* Power up the PHY */
392 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_PWD, 0);
393 
394 	/* enable FS/LS device */
395 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET,
396 	    USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3);
397 }
398 
399 /*
400  * i.MX7 PHYs.
401  */
402 
403 static void
404 nop_xceiv_init(struct imxehci_softc *sc, uint32_t *cells)
405 {
406 	int node;
407 
408 	node = OF_getnodebyphandle(cells[0]);
409 	KASSERT(node != 0);
410 
411 	clock_set_assigned(node);
412 	clock_enable(node, NULL);
413 }
414