xref: /freebsd-13-stable/sys/dev/bhnd/cores/usb/bhnd_usb.c (revision 3bc80996974a61a4223eae4c1ccd47b6ee32a48a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2010, Aleksandr Rybalko <ray@ddteam.net>
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 unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 /*
32  * Ported version of BroadCom USB core driver from ZRouter project
33  */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/errno.h>
39 #include <sys/bus.h>
40 #include <sys/rman.h>
41 #include <sys/malloc.h>
42 
43 #include <machine/bus.h>
44 #include <machine/resource.h>
45 
46 #include <dev/bhnd/bhnd.h>
47 
48 #include <dev/bhnd/cores/pmu/bhnd_pmureg.h>
49 
50 #include "bhnd_usbvar.h"
51 
52 /****************************** Variables ************************************/
53 static const struct bhnd_device bhnd_usb_devs[] = {
54 	BHND_DEVICE(BCM,	USB20H,	"USB2.0 Host core",		NULL),
55 	BHND_DEVICE_END
56 };
57 
58 /****************************** Prototypes ***********************************/
59 
60 static int	bhnd_usb_attach(device_t);
61 static int	bhnd_usb_probe(device_t);
62 static device_t	bhnd_usb_add_child(device_t dev, u_int order, const char *name,
63 		    int unit);
64 static int	bhnd_usb_print_all_resources(device_t dev);
65 static int	bhnd_usb_print_child(device_t bus, device_t child);
66 
67 static struct resource *	bhnd_usb_alloc_resource(device_t bus,
68 				    device_t child, int type, int *rid,
69 				    rman_res_t start, rman_res_t end,
70 				    rman_res_t count, u_int flags);
71 static int			bhnd_usb_release_resource(device_t dev,
72 				    device_t child, int type, int rid,
73 				    struct resource *r);
74 
75 static struct resource_list *	bhnd_usb_get_reslist(device_t dev,
76 				    device_t child);
77 
78 static int
bhnd_usb_probe(device_t dev)79 bhnd_usb_probe(device_t dev)
80 {
81 	const struct bhnd_device	*id;
82 
83 	id = bhnd_device_lookup(dev, bhnd_usb_devs, sizeof(bhnd_usb_devs[0]));
84 	if (id == NULL)
85 		return (ENXIO);
86 
87 	device_set_desc(dev, id->desc);
88 	return (BUS_PROBE_DEFAULT);
89 }
90 
91 static int
bhnd_usb_attach(device_t dev)92 bhnd_usb_attach(device_t dev)
93 {
94 	struct bhnd_usb_softc	*sc;
95 	int			 rid;
96 	uint32_t		 tmp;
97 	int			 tries, err;
98 
99 	sc = device_get_softc(dev);
100 
101 	bhnd_reset_hw(dev, 0, 0);
102 
103 	/*
104 	 * Allocate the resources which the parent bus has already
105 	 * determined for us.
106 	 * XXX: There are few windows (usually 2), RID should be chip-specific
107 	 */
108 	rid = 0;
109 	sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
110 	if (sc->sc_mem == NULL) {
111 		BHND_ERROR_DEV(dev, "unable to allocate memory");
112 		return (ENXIO);
113 	}
114 
115 	sc->sc_bt = rman_get_bustag(sc->sc_mem);
116 	sc->sc_bh = rman_get_bushandle(sc->sc_mem);
117 	sc->sc_maddr = rman_get_start(sc->sc_mem);
118 	sc->sc_msize = rman_get_size(sc->sc_mem);
119 
120 	rid = 0;
121 	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
122 		RF_SHAREABLE | RF_ACTIVE);
123 	if (sc->sc_irq == NULL) {
124 		BHND_ERROR_DEV(dev, "unable to allocate IRQ");
125 		return (ENXIO);
126 	}
127 
128 	sc->sc_irqn = rman_get_start(sc->sc_irq);
129 
130 	sc->mem_rman.rm_start = sc->sc_maddr;
131 	sc->mem_rman.rm_end = sc->sc_maddr + sc->sc_msize - 1;
132 	sc->mem_rman.rm_type = RMAN_ARRAY;
133 	sc->mem_rman.rm_descr = "BHND USB core I/O memory addresses";
134 	if (rman_init(&sc->mem_rman) != 0 ||
135 	    rman_manage_region(&sc->mem_rman, sc->mem_rman.rm_start,
136 	    sc->mem_rman.rm_end) != 0) {
137 		panic("%s: sc->mem_rman", __func__);
138 	}
139 
140 	/* TODO: macros for registers */
141 	bus_write_4(sc->sc_mem, 0x200, 0x7ff);
142 	DELAY(100);
143 
144 #define	OHCI_CONTROL		0x04
145 	bus_write_4(sc->sc_mem, OHCI_CONTROL, 0);
146 
147 	if ( bhnd_get_device(dev) == BHND_COREID_USB20H) {
148 		uint32_t rev = bhnd_get_hwrev(dev);
149 		BHND_INFO_DEV(dev, "USB HOST 2.0 setup for rev %d", rev);
150 		if (rev == 1/* ? == 2 */) {
151 			/* SiBa code */
152 
153 			/* Change Flush control reg */
154 			tmp = bus_read_4(sc->sc_mem, 0x400) & ~0x8;
155 			bus_write_4(sc->sc_mem, 0x400, tmp);
156 			tmp = bus_read_4(sc->sc_mem, 0x400);
157 			BHND_DEBUG_DEV(dev, "USB20H fcr: 0x%x", tmp);
158 
159 			/* Change Shim control reg */
160 			tmp = bus_read_4(sc->sc_mem, 0x304) & ~0x100;
161 			bus_write_4(sc->sc_mem, 0x304, tmp);
162 			tmp = bus_read_4(sc->sc_mem, 0x304);
163 			BHND_DEBUG_DEV(dev, "USB20H shim: 0x%x", tmp);
164 		} else if (rev >= 5) {
165 			/* BCMA code */
166 			err = bhnd_alloc_pmu(dev);
167 			if(err) {
168 				BHND_ERROR_DEV(dev, "can't alloc pmu: %d", err);
169 				return (err);
170 			}
171 
172 			err = bhnd_request_ext_rsrc(dev, 1);
173 			if(err) {
174 				BHND_ERROR_DEV(dev, "can't req ext: %d", err);
175 				return (err);
176 			}
177 			/* Take out of resets */
178 			bus_write_4(sc->sc_mem, 0x200, 0x4ff);
179 			DELAY(25);
180 			bus_write_4(sc->sc_mem, 0x200, 0x6ff);
181 			DELAY(25);
182 
183 			/* Make sure digital and AFE are locked in USB PHY */
184 			bus_write_4(sc->sc_mem, 0x524, 0x6b);
185 			DELAY(50);
186 			bus_read_4(sc->sc_mem, 0x524);
187 			DELAY(50);
188 			bus_write_4(sc->sc_mem, 0x524, 0xab);
189 			DELAY(50);
190 			bus_read_4(sc->sc_mem, 0x524);
191 			DELAY(50);
192 			bus_write_4(sc->sc_mem, 0x524, 0x2b);
193 			DELAY(50);
194 			bus_read_4(sc->sc_mem, 0x524);
195 			DELAY(50);
196 			bus_write_4(sc->sc_mem, 0x524, 0x10ab);
197 			DELAY(50);
198 			bus_read_4(sc->sc_mem, 0x524);
199 
200 			tries = 10000;
201 			for (;;) {
202 				DELAY(10);
203 				tmp = bus_read_4(sc->sc_mem, 0x528);
204 				if (tmp & 0xc000)
205 					break;
206 				if (--tries != 0)
207 					continue;
208 
209 				tmp = bus_read_4(sc->sc_mem, 0x528);
210 				BHND_ERROR_DEV(dev, "USB20H mdio_rddata 0x%08x", tmp);
211 			}
212 
213 			/* XXX: Puzzle code */
214 			bus_write_4(sc->sc_mem, 0x528, 0x80000000);
215 			bus_read_4(sc->sc_mem, 0x314);
216 			DELAY(265);
217 			bus_write_4(sc->sc_mem, 0x200, 0x7ff);
218 			DELAY(10);
219 
220 			/* Take USB and HSIC out of non-driving modes */
221 			bus_write_4(sc->sc_mem, 0x510, 0);
222 		}
223 	}
224 
225 	bus_generic_probe(dev);
226 
227 	if (bhnd_get_device(dev) == BHND_COREID_USB20H &&
228 	    ( bhnd_get_hwrev(dev) > 0))
229 		bhnd_usb_add_child(dev, 0, "ehci", -1);
230 	bhnd_usb_add_child(dev, 1, "ohci", -1);
231 
232 	bus_generic_attach(dev);
233 
234 	return (0);
235 }
236 
237 static struct resource *
bhnd_usb_alloc_resource(device_t bus,device_t child,int type,int * rid,rman_res_t start,rman_res_t end,rman_res_t count,u_int flags)238 bhnd_usb_alloc_resource(device_t bus, device_t child, int type, int *rid,
239     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
240 {
241 	struct resource			*rv;
242 	struct resource_list		*rl;
243 	struct resource_list_entry	*rle;
244 	int				 passthrough, isdefault, needactivate;
245 	struct bhnd_usb_softc		*sc = device_get_softc(bus);
246 
247 	isdefault = RMAN_IS_DEFAULT_RANGE(start,end);
248 	passthrough = (device_get_parent(child) != bus);
249 	needactivate = flags & RF_ACTIVE;
250 	rle = NULL;
251 
252 	if (!passthrough && isdefault) {
253 		BHND_INFO_DEV(bus, "trying allocate def %d - %d for %s", type,
254 		    *rid, device_get_nameunit(child) );
255 
256 		rl = BUS_GET_RESOURCE_LIST(bus, child);
257 		rle = resource_list_find(rl, type, *rid);
258 		if (rle == NULL)
259 			return (NULL);
260 		if (rle->res != NULL)
261 			panic("%s: resource entry is busy", __func__);
262 		start = rle->start;
263 		end = rle->end;
264 		count = rle->count;
265 	} else {
266 		BHND_INFO_DEV(bus, "trying allocate %d - %d (%jx-%jx) for %s", type,
267 		   *rid, start, end, device_get_nameunit(child) );
268 	}
269 
270 	/*
271 	 * If the request is for a resource which we manage,
272 	 * attempt to satisfy the allocation ourselves.
273 	 */
274 	if (type == SYS_RES_MEMORY) {
275 		rv = rman_reserve_resource(&sc->mem_rman, start, end, count,
276 		    flags, child);
277 		if (rv == NULL) {
278 			BHND_ERROR_DEV(bus, "could not reserve resource");
279 			return (0);
280 		}
281 
282 		rman_set_rid(rv, *rid);
283 
284 		if (needactivate &&
285 		    bus_activate_resource(child, type, *rid, rv)) {
286 			BHND_ERROR_DEV(bus, "could not activate resource");
287 			rman_release_resource(rv);
288 			return (0);
289 		}
290 
291 		return (rv);
292 	}
293 
294 	/*
295 	 * Pass the request to the parent.
296 	 */
297 	return (bus_generic_rl_alloc_resource(bus, child, type, rid, start, end,
298 	    count, flags));
299 }
300 
301 static struct resource_list *
bhnd_usb_get_reslist(device_t dev,device_t child)302 bhnd_usb_get_reslist(device_t dev, device_t child)
303 {
304 	struct bhnd_usb_devinfo	*sdi;
305 
306 	sdi = device_get_ivars(child);
307 
308 	return (&sdi->sdi_rl);
309 }
310 
311 static int
bhnd_usb_release_resource(device_t dev,device_t child,int type,int rid,struct resource * r)312 bhnd_usb_release_resource(device_t dev, device_t child, int type,
313     int rid, struct resource *r)
314 {
315 	struct bhnd_usb_softc		*sc;
316 	struct resource_list_entry	*rle;
317 	bool				 passthrough;
318 	int				 error;
319 
320 	sc = device_get_softc(dev);
321 	passthrough = (device_get_parent(child) != dev);
322 
323 	/* Delegate to our parent device's bus if the requested resource type
324 	 * isn't handled locally. */
325 	if (type != SYS_RES_MEMORY) {
326 		return (bus_generic_rl_release_resource(dev, child, type, rid,
327 		    r));
328 	}
329 
330 	/* Deactivate resources */
331 	if (rman_get_flags(r) & RF_ACTIVE) {
332 		error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r);
333 		if (error)
334 			return (error);
335 	}
336 
337 	if ((error = rman_release_resource(r)))
338 		return (error);
339 
340 	if (!passthrough) {
341 		/* Clean resource list entry */
342 		rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
343 		    type, rid);
344 		if (rle != NULL)
345 			rle->res = NULL;
346 	}
347 
348 	return (0);
349 }
350 
351 static int
bhnd_usb_print_all_resources(device_t dev)352 bhnd_usb_print_all_resources(device_t dev)
353 {
354 	struct bhnd_usb_devinfo	*sdi;
355 	struct resource_list	*rl;
356 	int			 retval;
357 
358 	retval = 0;
359 	sdi = device_get_ivars(dev);
360 	rl = &sdi->sdi_rl;
361 
362 	if (STAILQ_FIRST(rl))
363 		retval += printf(" at");
364 
365 	retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%jx");
366 	retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
367 
368 	return (retval);
369 }
370 
371 static int
bhnd_usb_print_child(device_t bus,device_t child)372 bhnd_usb_print_child(device_t bus, device_t child)
373 {
374 	int retval = 0;
375 
376 	retval += bus_print_child_header(bus, child);
377 	retval += bhnd_usb_print_all_resources(child);
378 	if (device_get_flags(child))
379 		retval += printf(" flags %#x", device_get_flags(child));
380 	retval += printf(" on %s\n", device_get_nameunit(bus));
381 
382 	return (retval);
383 }
384 
385 static device_t
bhnd_usb_add_child(device_t dev,u_int order,const char * name,int unit)386 bhnd_usb_add_child(device_t dev, u_int order, const char *name, int unit)
387 {
388 	struct bhnd_usb_softc		*sc;
389 	struct bhnd_usb_devinfo 	*sdi;
390 	device_t 			 child;
391 	int				 error;
392 
393 	sc = device_get_softc(dev);
394 
395 	sdi = malloc(sizeof(struct bhnd_usb_devinfo), M_DEVBUF, M_NOWAIT|M_ZERO);
396 	if (sdi == NULL)
397 		return (NULL);
398 
399 	resource_list_init(&sdi->sdi_rl);
400 	sdi->sdi_irq_mapped = false;
401 
402 	if (strncmp(name, "ohci", 4) == 0)
403 	{
404 		sdi->sdi_maddr = sc->sc_maddr + 0x000;
405 		sdi->sdi_msize = 0x200;
406 	}
407 	else if (strncmp(name, "ehci", 4) == 0)
408 	{
409 		sdi->sdi_maddr = sc->sc_maddr + 0x000;
410 		sdi->sdi_msize = 0x1000;
411 	}
412 	else
413 	{
414 		panic("Unknown subdevice");
415 	}
416 
417 	/* Map the child's IRQ */
418 	if ((error = bhnd_map_intr(dev, 0, &sdi->sdi_irq))) {
419 		BHND_ERROR_DEV(dev, "could not map %s interrupt: %d", name,
420 		    error);
421 		goto failed;
422 	}
423 	sdi->sdi_irq_mapped = true;
424 
425 	BHND_INFO_DEV(dev, "%s: irq=%ju maddr=0x%jx", name, sdi->sdi_irq,
426 	    sdi->sdi_maddr);
427 
428 	/*
429 	 * Add memory window and irq to child's resource list.
430 	 */
431 	resource_list_add(&sdi->sdi_rl, SYS_RES_MEMORY, 0, sdi->sdi_maddr,
432 	    sdi->sdi_maddr + sdi->sdi_msize - 1, sdi->sdi_msize);
433 
434 	resource_list_add(&sdi->sdi_rl, SYS_RES_IRQ, 0, sdi->sdi_irq,
435 	    sdi->sdi_irq, 1);
436 
437 	child = device_add_child_ordered(dev, order, name, unit);
438 	if (child == NULL) {
439 		BHND_ERROR_DEV(dev, "could not add %s", name);
440 		goto failed;
441 	}
442 
443 	device_set_ivars(child, sdi);
444 	return (child);
445 
446 failed:
447 	if (sdi->sdi_irq_mapped)
448 		bhnd_unmap_intr(dev, sdi->sdi_irq);
449 
450 	resource_list_free(&sdi->sdi_rl);
451 
452 	free(sdi, M_DEVBUF);
453 	return (NULL);
454 }
455 
456 static void
bhnd_usb_child_deleted(device_t dev,device_t child)457 bhnd_usb_child_deleted(device_t dev, device_t child)
458 {
459 	struct bhnd_usb_devinfo	*dinfo;
460 
461 	if ((dinfo = device_get_ivars(child)) == NULL)
462 		return;
463 
464 	if (dinfo->sdi_irq_mapped)
465 		bhnd_unmap_intr(dev, dinfo->sdi_irq);
466 
467 	resource_list_free(&dinfo->sdi_rl);
468 	free(dinfo, M_DEVBUF);
469 }
470 
471 static device_method_t bhnd_usb_methods[] = {
472 	/* Device interface */
473 	DEVMETHOD(device_attach,		bhnd_usb_attach),
474 	DEVMETHOD(device_probe,			bhnd_usb_probe),
475 
476 	/* Bus interface */
477 	DEVMETHOD(bus_add_child,		bhnd_usb_add_child),
478 	DEVMETHOD(bus_child_deleted,		bhnd_usb_child_deleted),
479 	DEVMETHOD(bus_alloc_resource,		bhnd_usb_alloc_resource),
480 	DEVMETHOD(bus_get_resource_list,	bhnd_usb_get_reslist),
481 	DEVMETHOD(bus_print_child,		bhnd_usb_print_child),
482 	DEVMETHOD(bus_release_resource,		bhnd_usb_release_resource),
483 	/* Bus interface: generic part */
484 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
485 	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
486 	DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
487 	DEVMETHOD(bus_teardown_intr,		bus_generic_teardown_intr),
488 
489 	DEVMETHOD_END
490 };
491 
492 static devclass_t bhnd_usb_devclass;
493 
494 DEFINE_CLASS_0(bhnd_usb, bhnd_usb_driver, bhnd_usb_methods,
495     sizeof(struct bhnd_usb_softc));
496 DRIVER_MODULE(bhnd_usb, bhnd, bhnd_usb_driver, bhnd_usb_devclass, 0, 0);
497 
498 MODULE_VERSION(bhnd_usb, 1);
499