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