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