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