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