1 /*- 2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3 * Copyright (c) 2018 Andrew Turner <andrew@FreeBSD.org> 4 * All rights reserved. 5 * 6 * This software was developed by SRI International and the University of 7 * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 8 * ("CTSRD"), as part of the DARPA CRASH research programme. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 34 /* 35 * Allwinner USB Dual-Role Device (DRD) controller 36 */ 37 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/bus.h> 44 #include <sys/rman.h> 45 #include <sys/kernel.h> 46 #include <sys/condvar.h> 47 #include <sys/module.h> 48 49 #include <machine/bus.h> 50 51 #include <dev/ofw/ofw_bus.h> 52 #include <dev/ofw/ofw_bus_subr.h> 53 54 #include <dev/usb/usb.h> 55 #include <dev/usb/usbdi.h> 56 57 #include <dev/usb/usb_core.h> 58 #include <dev/usb/usb_busdma.h> 59 #include <dev/usb/usb_process.h> 60 #include <dev/usb/usb_util.h> 61 62 #include <dev/usb/usb_controller.h> 63 #include <dev/usb/usb_bus.h> 64 #include <dev/usb/controller/musb_otg.h> 65 66 #include <dev/extres/clk/clk.h> 67 #include <dev/extres/hwreset/hwreset.h> 68 #include <dev/extres/phy/phy.h> 69 #include <dev/extres/phy/phy_usb.h> 70 71 #ifdef __arm__ 72 #include <arm/allwinner/aw_machdep.h> 73 #include <arm/allwinner/a10_sramc.h> 74 #endif 75 76 #define DRD_EP_MAX 5 77 #define DRD_EP_MAX_H3 4 78 79 #define MUSB2_REG_AWIN_VEND0 0x0043 80 #define VEND0_PIO_MODE 0 81 82 #if defined(__arm__) 83 #define bs_parent_space(bs) ((bs)->bs_parent) 84 typedef bus_space_tag_t awusb_bs_tag; 85 #elif defined(__aarch64__) 86 #define bs_parent_space(bs) (bs) 87 typedef void * awusb_bs_tag; 88 #endif 89 90 #define AWUSB_OKAY 0x01 91 #define AWUSB_NO_CONFDATA 0x02 92 static struct ofw_compat_data compat_data[] = { 93 { "allwinner,sun4i-a10-musb", AWUSB_OKAY }, 94 { "allwinner,sun6i-a31-musb", AWUSB_OKAY }, 95 { "allwinner,sun8i-a33-musb", AWUSB_OKAY | AWUSB_NO_CONFDATA }, 96 { "allwinner,sun8i-h3-musb", AWUSB_OKAY | AWUSB_NO_CONFDATA }, 97 { NULL, 0 } 98 }; 99 100 static const struct musb_otg_ep_cfg musbotg_ep_allwinner[] = { 101 { 102 .ep_end = DRD_EP_MAX, 103 .ep_fifosz_shift = 9, 104 .ep_fifosz_reg = MUSB2_VAL_FIFOSZ_512, 105 }, 106 { 107 .ep_end = -1, 108 }, 109 }; 110 111 static const struct musb_otg_ep_cfg musbotg_ep_allwinner_h3[] = { 112 { 113 .ep_end = DRD_EP_MAX_H3, 114 .ep_fifosz_shift = 9, 115 .ep_fifosz_reg = MUSB2_VAL_FIFOSZ_512, 116 }, 117 { 118 .ep_end = -1, 119 }, 120 }; 121 122 struct awusbdrd_softc { 123 struct musbotg_softc sc; 124 struct resource *res[2]; 125 clk_t clk; 126 hwreset_t reset; 127 phy_t phy; 128 struct bus_space bs; 129 int flags; 130 }; 131 132 static struct resource_spec awusbdrd_spec[] = { 133 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 134 { SYS_RES_IRQ, 0, RF_ACTIVE }, 135 { -1, 0 } 136 }; 137 138 #define REMAPFLAG 0x8000 139 #define REGDECL(a, b) [(a)] = ((b) | REMAPFLAG) 140 141 /* Allwinner USB DRD register mappings */ 142 static const uint16_t awusbdrd_regmap[] = { 143 REGDECL(MUSB2_REG_EPFIFO(0), 0x0000), 144 REGDECL(MUSB2_REG_EPFIFO(1), 0x0004), 145 REGDECL(MUSB2_REG_EPFIFO(2), 0x0008), 146 REGDECL(MUSB2_REG_EPFIFO(3), 0x000c), 147 REGDECL(MUSB2_REG_EPFIFO(4), 0x0010), 148 REGDECL(MUSB2_REG_EPFIFO(5), 0x0014), 149 REGDECL(MUSB2_REG_POWER, 0x0040), 150 REGDECL(MUSB2_REG_DEVCTL, 0x0041), 151 REGDECL(MUSB2_REG_EPINDEX, 0x0042), 152 REGDECL(MUSB2_REG_INTTX, 0x0044), 153 REGDECL(MUSB2_REG_INTRX, 0x0046), 154 REGDECL(MUSB2_REG_INTTXE, 0x0048), 155 REGDECL(MUSB2_REG_INTRXE, 0x004a), 156 REGDECL(MUSB2_REG_INTUSB, 0x004c), 157 REGDECL(MUSB2_REG_INTUSBE, 0x0050), 158 REGDECL(MUSB2_REG_FRAME, 0x0054), 159 REGDECL(MUSB2_REG_TESTMODE, 0x007c), 160 REGDECL(MUSB2_REG_TXMAXP, 0x0080), 161 REGDECL(MUSB2_REG_TXCSRL, 0x0082), 162 REGDECL(MUSB2_REG_TXCSRH, 0x0083), 163 REGDECL(MUSB2_REG_RXMAXP, 0x0084), 164 REGDECL(MUSB2_REG_RXCSRL, 0x0086), 165 REGDECL(MUSB2_REG_RXCSRH, 0x0087), 166 REGDECL(MUSB2_REG_RXCOUNT, 0x0088), 167 REGDECL(MUSB2_REG_TXTI, 0x008c), 168 REGDECL(MUSB2_REG_TXNAKLIMIT, 0x008d), 169 REGDECL(MUSB2_REG_RXNAKLIMIT, 0x008f), 170 REGDECL(MUSB2_REG_RXTI, 0x008e), 171 REGDECL(MUSB2_REG_TXFIFOSZ, 0x0090), 172 REGDECL(MUSB2_REG_TXFIFOADD, 0x0092), 173 REGDECL(MUSB2_REG_RXFIFOSZ, 0x0094), 174 REGDECL(MUSB2_REG_RXFIFOADD, 0x0096), 175 REGDECL(MUSB2_REG_FADDR, 0x0098), 176 REGDECL(MUSB2_REG_TXFADDR(0), 0x0098), 177 REGDECL(MUSB2_REG_TXHADDR(0), 0x009a), 178 REGDECL(MUSB2_REG_TXHUBPORT(0), 0x009b), 179 REGDECL(MUSB2_REG_RXFADDR(0), 0x009c), 180 REGDECL(MUSB2_REG_RXHADDR(0), 0x009e), 181 REGDECL(MUSB2_REG_RXHUBPORT(0), 0x009f), 182 REGDECL(MUSB2_REG_TXFADDR(1), 0x0098), 183 REGDECL(MUSB2_REG_TXHADDR(1), 0x009a), 184 REGDECL(MUSB2_REG_TXHUBPORT(1), 0x009b), 185 REGDECL(MUSB2_REG_RXFADDR(1), 0x009c), 186 REGDECL(MUSB2_REG_RXHADDR(1), 0x009e), 187 REGDECL(MUSB2_REG_RXHUBPORT(1), 0x009f), 188 REGDECL(MUSB2_REG_TXFADDR(2), 0x0098), 189 REGDECL(MUSB2_REG_TXHADDR(2), 0x009a), 190 REGDECL(MUSB2_REG_TXHUBPORT(2), 0x009b), 191 REGDECL(MUSB2_REG_RXFADDR(2), 0x009c), 192 REGDECL(MUSB2_REG_RXHADDR(2), 0x009e), 193 REGDECL(MUSB2_REG_RXHUBPORT(2), 0x009f), 194 REGDECL(MUSB2_REG_TXFADDR(3), 0x0098), 195 REGDECL(MUSB2_REG_TXHADDR(3), 0x009a), 196 REGDECL(MUSB2_REG_TXHUBPORT(3), 0x009b), 197 REGDECL(MUSB2_REG_RXFADDR(3), 0x009c), 198 REGDECL(MUSB2_REG_RXHADDR(3), 0x009e), 199 REGDECL(MUSB2_REG_RXHUBPORT(3), 0x009f), 200 REGDECL(MUSB2_REG_TXFADDR(4), 0x0098), 201 REGDECL(MUSB2_REG_TXHADDR(4), 0x009a), 202 REGDECL(MUSB2_REG_TXHUBPORT(4), 0x009b), 203 REGDECL(MUSB2_REG_RXFADDR(4), 0x009c), 204 REGDECL(MUSB2_REG_RXHADDR(4), 0x009e), 205 REGDECL(MUSB2_REG_RXHUBPORT(4), 0x009f), 206 REGDECL(MUSB2_REG_TXFADDR(5), 0x0098), 207 REGDECL(MUSB2_REG_TXHADDR(5), 0x009a), 208 REGDECL(MUSB2_REG_TXHUBPORT(5), 0x009b), 209 REGDECL(MUSB2_REG_RXFADDR(5), 0x009c), 210 REGDECL(MUSB2_REG_RXHADDR(5), 0x009e), 211 REGDECL(MUSB2_REG_RXHUBPORT(5), 0x009f), 212 REGDECL(MUSB2_REG_CONFDATA, 0x00c0), 213 }; 214 215 static bus_size_t 216 awusbdrd_reg(bus_size_t o) 217 { 218 bus_size_t v; 219 220 KASSERT(o < nitems(awusbdrd_regmap), 221 ("%s: Invalid register %#lx", __func__, o)); 222 if (o >= nitems(awusbdrd_regmap)) 223 return (o); 224 225 v = awusbdrd_regmap[o]; 226 227 KASSERT((v & REMAPFLAG) != 0, ("%s: reg %#lx not in regmap", 228 __func__, o)); 229 230 return (v & ~REMAPFLAG); 231 } 232 233 static int 234 awusbdrd_filt(bus_size_t o) 235 { 236 switch (o) { 237 case MUSB2_REG_MISC: 238 case MUSB2_REG_RXDBDIS: 239 case MUSB2_REG_TXDBDIS: 240 return (1); 241 default: 242 return (0); 243 } 244 } 245 246 static uint8_t 247 awusbdrd_bs_r_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) 248 { 249 struct bus_space *bs = t; 250 251 switch (o) { 252 case MUSB2_REG_HWVERS: 253 return (0); /* no known equivalent */ 254 } 255 256 return (bus_space_read_1(bs_parent_space(bs), h, awusbdrd_reg(o))); 257 } 258 259 static uint8_t 260 awusbdrd_bs_r_1_noconf(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) 261 { 262 263 /* 264 * There is no confdata register on some SoCs, return the same 265 * magic value as Linux. 266 */ 267 if (o == MUSB2_REG_CONFDATA) 268 return (0xde); 269 270 return (awusbdrd_bs_r_1(t, h, o)); 271 } 272 273 274 static uint16_t 275 awusbdrd_bs_r_2(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) 276 { 277 struct bus_space *bs = t; 278 279 if (awusbdrd_filt(o) != 0) 280 return (0); 281 return bus_space_read_2(bs_parent_space(bs), h, awusbdrd_reg(o)); 282 } 283 284 static void 285 awusbdrd_bs_w_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, 286 uint8_t v) 287 { 288 struct bus_space *bs = t; 289 290 if (awusbdrd_filt(o) != 0) 291 return; 292 293 bus_space_write_1(bs_parent_space(bs), h, awusbdrd_reg(o), v); 294 } 295 296 static void 297 awusbdrd_bs_w_2(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, 298 uint16_t v) 299 { 300 struct bus_space *bs = t; 301 302 if (awusbdrd_filt(o) != 0) 303 return; 304 305 bus_space_write_2(bs_parent_space(bs), h, awusbdrd_reg(o), v); 306 } 307 308 static void 309 awusbdrd_bs_rm_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, 310 uint8_t *d, bus_size_t c) 311 { 312 struct bus_space *bs = t; 313 314 bus_space_read_multi_1(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); 315 } 316 317 static void 318 awusbdrd_bs_rm_4(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, 319 uint32_t *d, bus_size_t c) 320 { 321 struct bus_space *bs = t; 322 323 bus_space_read_multi_4(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); 324 } 325 326 static void 327 awusbdrd_bs_wm_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, 328 const uint8_t *d, bus_size_t c) 329 { 330 struct bus_space *bs = t; 331 332 if (awusbdrd_filt(o) != 0) 333 return; 334 335 bus_space_write_multi_1(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); 336 } 337 338 static void 339 awusbdrd_bs_wm_4(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, 340 const uint32_t *d, bus_size_t c) 341 { 342 struct bus_space *bs = t; 343 344 if (awusbdrd_filt(o) != 0) 345 return; 346 347 bus_space_write_multi_4(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); 348 } 349 350 static void 351 awusbdrd_intr(void *arg) 352 { 353 struct awusbdrd_softc *sc = arg; 354 uint8_t intusb; 355 uint16_t inttx, intrx; 356 357 intusb = MUSB2_READ_1(&sc->sc, MUSB2_REG_INTUSB); 358 inttx = MUSB2_READ_2(&sc->sc, MUSB2_REG_INTTX); 359 intrx = MUSB2_READ_2(&sc->sc, MUSB2_REG_INTRX); 360 if (intusb == 0 && inttx == 0 && intrx == 0) 361 return; 362 363 if (intusb) 364 MUSB2_WRITE_1(&sc->sc, MUSB2_REG_INTUSB, intusb); 365 if (inttx) 366 MUSB2_WRITE_2(&sc->sc, MUSB2_REG_INTTX, inttx); 367 if (intrx) 368 MUSB2_WRITE_2(&sc->sc, MUSB2_REG_INTRX, intrx); 369 370 musbotg_interrupt(arg, intrx, inttx, intusb); 371 } 372 373 static int 374 awusbdrd_probe(device_t dev) 375 { 376 if (!ofw_bus_status_okay(dev)) 377 return (ENXIO); 378 379 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 380 return (ENXIO); 381 382 device_set_desc(dev, "Allwinner USB DRD"); 383 return (BUS_PROBE_DEFAULT); 384 } 385 386 static int 387 awusbdrd_attach(device_t dev) 388 { 389 char usb_mode[24]; 390 struct awusbdrd_softc *sc; 391 uint8_t musb_mode; 392 int phy_mode; 393 int error; 394 395 sc = device_get_softc(dev); 396 sc->flags = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 397 398 error = bus_alloc_resources(dev, awusbdrd_spec, sc->res); 399 if (error != 0) 400 return (error); 401 402 musb_mode = MUSB2_HOST_MODE; /* default */ 403 phy_mode = PHY_USB_MODE_HOST; 404 if (OF_getprop(ofw_bus_get_node(dev), "dr_mode", 405 &usb_mode, sizeof(usb_mode)) > 0) { 406 usb_mode[sizeof(usb_mode) - 1] = 0; 407 if (strcasecmp(usb_mode, "host") == 0) { 408 musb_mode = MUSB2_HOST_MODE; 409 phy_mode = PHY_USB_MODE_HOST; 410 } else if (strcasecmp(usb_mode, "peripheral") == 0) { 411 musb_mode = MUSB2_DEVICE_MODE; 412 phy_mode = PHY_USB_MODE_DEVICE; 413 } else if (strcasecmp(usb_mode, "otg") == 0) { 414 /* 415 * XXX phy has PHY_USB_MODE_OTG, but MUSB does not have 416 * it. It's not clear how to propagate mode changes 417 * from phy layer (that detects them) to MUSB. 418 */ 419 musb_mode = MUSB2_DEVICE_MODE; 420 phy_mode = PHY_USB_MODE_DEVICE; 421 } else { 422 device_printf(dev, "Invalid FDT dr_mode: %s\n", 423 usb_mode); 424 } 425 } 426 427 /* AHB gate clock is required */ 428 error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); 429 if (error != 0) 430 goto fail; 431 432 /* AHB reset is only present on some SoCs */ 433 (void)hwreset_get_by_ofw_idx(dev, 0, 0, &sc->reset); 434 435 /* Enable clocks */ 436 error = clk_enable(sc->clk); 437 if (error != 0) { 438 device_printf(dev, "failed to enable clock: %d\n", error); 439 goto fail; 440 } 441 if (sc->reset != NULL) { 442 error = hwreset_deassert(sc->reset); 443 if (error != 0) { 444 device_printf(dev, "failed to de-assert reset: %d\n", 445 error); 446 goto fail; 447 } 448 } 449 450 /* XXX not sure if this is universally needed. */ 451 (void)phy_get_by_ofw_name(dev, 0, "usb", &sc->phy); 452 if (sc->phy != NULL) { 453 device_printf(dev, "setting phy mode %d\n", phy_mode); 454 if (musb_mode == MUSB2_HOST_MODE) { 455 error = phy_enable(sc->phy); 456 if (error != 0) { 457 device_printf(dev, "Could not enable phy\n"); 458 goto fail; 459 } 460 } 461 error = phy_usb_set_mode(sc->phy, phy_mode); 462 if (error != 0) { 463 device_printf(dev, "Could not set phy mode\n"); 464 goto fail; 465 } 466 } 467 468 sc->sc.sc_bus.parent = dev; 469 sc->sc.sc_bus.devices = sc->sc.sc_devices; 470 sc->sc.sc_bus.devices_max = MUSB2_MAX_DEVICES; 471 sc->sc.sc_bus.dma_bits = 32; 472 473 error = usb_bus_mem_alloc_all(&sc->sc.sc_bus, USB_GET_DMA_TAG(dev), 474 NULL); 475 if (error != 0) { 476 error = ENOMEM; 477 goto fail; 478 } 479 480 #if defined(__arm__) 481 sc->bs.bs_parent = rman_get_bustag(sc->res[0]); 482 #elif defined(__aarch64__) 483 sc->bs.bs_cookie = rman_get_bustag(sc->res[0]); 484 #endif 485 486 if ((sc->flags & AWUSB_NO_CONFDATA) == AWUSB_NO_CONFDATA) 487 sc->bs.bs_r_1 = awusbdrd_bs_r_1_noconf; 488 else 489 sc->bs.bs_r_1 = awusbdrd_bs_r_1; 490 sc->bs.bs_r_2 = awusbdrd_bs_r_2; 491 sc->bs.bs_w_1 = awusbdrd_bs_w_1; 492 sc->bs.bs_w_2 = awusbdrd_bs_w_2; 493 sc->bs.bs_rm_1 = awusbdrd_bs_rm_1; 494 sc->bs.bs_rm_4 = awusbdrd_bs_rm_4; 495 sc->bs.bs_wm_1 = awusbdrd_bs_wm_1; 496 sc->bs.bs_wm_4 = awusbdrd_bs_wm_4; 497 498 sc->sc.sc_io_tag = &sc->bs; 499 sc->sc.sc_io_hdl = rman_get_bushandle(sc->res[0]); 500 sc->sc.sc_io_size = rman_get_size(sc->res[0]); 501 502 sc->sc.sc_bus.bdev = device_add_child(dev, "usbus", -1); 503 if (sc->sc.sc_bus.bdev == NULL) { 504 error = ENXIO; 505 goto fail; 506 } 507 device_set_ivars(sc->sc.sc_bus.bdev, &sc->sc.sc_bus); 508 sc->sc.sc_id = 0; 509 sc->sc.sc_platform_data = sc; 510 sc->sc.sc_mode = musb_mode; 511 if (ofw_bus_is_compatible(dev, "allwinner,sun8i-h3-musb")) { 512 sc->sc.sc_ep_cfg = musbotg_ep_allwinner_h3; 513 sc->sc.sc_ep_max = DRD_EP_MAX_H3; 514 } else { 515 sc->sc.sc_ep_cfg = musbotg_ep_allwinner; 516 sc->sc.sc_ep_max = DRD_EP_MAX; 517 } 518 519 error = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_BIO, 520 NULL, awusbdrd_intr, sc, &sc->sc.sc_intr_hdl); 521 if (error != 0) 522 goto fail; 523 524 /* Enable PIO mode */ 525 bus_write_1(sc->res[0], MUSB2_REG_AWIN_VEND0, VEND0_PIO_MODE); 526 527 #ifdef __arm__ 528 /* Map SRAMD area to USB0 (sun4i/sun7i only) */ 529 switch (allwinner_soc_family()) { 530 case ALLWINNERSOC_SUN4I: 531 case ALLWINNERSOC_SUN7I: 532 a10_map_to_otg(); 533 break; 534 } 535 #endif 536 537 error = musbotg_init(&sc->sc); 538 if (error != 0) 539 goto fail; 540 541 error = device_probe_and_attach(sc->sc.sc_bus.bdev); 542 if (error != 0) 543 goto fail; 544 545 musbotg_vbus_interrupt(&sc->sc, 1); /* XXX VBUS */ 546 547 return (0); 548 549 fail: 550 if (sc->phy != NULL) { 551 if (musb_mode == MUSB2_HOST_MODE) 552 (void)phy_disable(sc->phy); 553 phy_release(sc->phy); 554 } 555 if (sc->reset != NULL) { 556 hwreset_assert(sc->reset); 557 hwreset_release(sc->reset); 558 } 559 if (sc->clk != NULL) 560 clk_release(sc->clk); 561 bus_release_resources(dev, awusbdrd_spec, sc->res); 562 return (error); 563 } 564 565 static int 566 awusbdrd_detach(device_t dev) 567 { 568 struct awusbdrd_softc *sc; 569 device_t bdev; 570 int error; 571 572 sc = device_get_softc(dev); 573 574 if (sc->sc.sc_bus.bdev != NULL) { 575 bdev = sc->sc.sc_bus.bdev; 576 device_detach(bdev); 577 device_delete_child(dev, bdev); 578 } 579 580 musbotg_uninit(&sc->sc); 581 error = bus_teardown_intr(dev, sc->res[1], sc->sc.sc_intr_hdl); 582 if (error != 0) 583 return (error); 584 585 usb_bus_mem_free_all(&sc->sc.sc_bus, NULL); 586 587 if (sc->phy != NULL) { 588 if (sc->sc.sc_mode == MUSB2_HOST_MODE) 589 phy_disable(sc->phy); 590 phy_release(sc->phy); 591 } 592 if (sc->reset != NULL) { 593 if (hwreset_assert(sc->reset) != 0) 594 device_printf(dev, "failed to assert reset\n"); 595 hwreset_release(sc->reset); 596 } 597 if (sc->clk != NULL) 598 clk_release(sc->clk); 599 600 bus_release_resources(dev, awusbdrd_spec, sc->res); 601 602 device_delete_children(dev); 603 604 return (0); 605 } 606 607 static device_method_t awusbdrd_methods[] = { 608 /* Device interface */ 609 DEVMETHOD(device_probe, awusbdrd_probe), 610 DEVMETHOD(device_attach, awusbdrd_attach), 611 DEVMETHOD(device_detach, awusbdrd_detach), 612 DEVMETHOD(device_suspend, bus_generic_suspend), 613 DEVMETHOD(device_resume, bus_generic_resume), 614 DEVMETHOD(device_shutdown, bus_generic_shutdown), 615 616 DEVMETHOD_END 617 }; 618 619 static driver_t awusbdrd_driver = { 620 .name = "musbotg", 621 .methods = awusbdrd_methods, 622 .size = sizeof(struct awusbdrd_softc), 623 }; 624 625 DRIVER_MODULE(musbotg, simplebus, awusbdrd_driver, 0, 0); 626 MODULE_DEPEND(musbotg, usb, 1, 1, 1); 627