| Commit | Line | Data |
|---|---|---|
| 12bd3c8b SW |
1 | /*- |
| 2 | * Copyright (c) 2006 M. Warner Losh. All rights reserved. | |
| 3 | * Copyright (c) 2009 Andrew Turner. All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
| 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
| 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
| 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
| 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 24 | */ | |
| 25 | ||
| 26 | #include <sys/cdefs.h> | |
| 27 | __FBSDID("$FreeBSD$"); | |
| 28 | ||
| 29 | #include <sys/stdint.h> | |
| 30 | #include <sys/stddef.h> | |
| 31 | #include <sys/param.h> | |
| 32 | #include <sys/queue.h> | |
| 33 | #include <sys/types.h> | |
| 34 | #include <sys/systm.h> | |
| 35 | #include <sys/kernel.h> | |
| 36 | #include <sys/bus.h> | |
| 37 | #include <sys/module.h> | |
| 38 | #include <sys/lock.h> | |
| 39 | #include <sys/mutex.h> | |
| 40 | #include <sys/condvar.h> | |
| 41 | #include <sys/sysctl.h> | |
| 42 | #include <sys/sx.h> | |
| 43 | #include <sys/unistd.h> | |
| 44 | #include <sys/callout.h> | |
| 45 | #include <sys/malloc.h> | |
| 46 | #include <sys/priv.h> | |
| 47 | ||
| 48 | #include <dev/usb/usb.h> | |
| 49 | #include <dev/usb/usbdi.h> | |
| 50 | ||
| 51 | #include <dev/usb/usb_core.h> | |
| 52 | #include <dev/usb/usb_busdma.h> | |
| 53 | #include <dev/usb/usb_process.h> | |
| 54 | #include <dev/usb/usb_util.h> | |
| 55 | ||
| 56 | #include <dev/usb/usb_controller.h> | |
| 57 | #include <dev/usb/usb_bus.h> | |
| 58 | #include <dev/usb/controller/ohci.h> | |
| 59 | #include <dev/usb/controller/ohcireg.h> | |
| 60 | ||
| 61 | #include <sys/rman.h> | |
| 62 | ||
| 63 | #include <arm/s3c2xx0/s3c24x0reg.h> | |
| 64 | ||
| 65 | static device_probe_t ohci_s3c24x0_probe; | |
| 66 | static device_attach_t ohci_s3c24x0_attach; | |
| 67 | static device_detach_t ohci_s3c24x0_detach; | |
| 68 | ||
| 69 | static int | |
| 70 | ohci_s3c24x0_probe(device_t dev) | |
| 71 | { | |
| 72 | device_set_desc(dev, "S3C24x0 integrated OHCI controller"); | |
| 73 | return (BUS_PROBE_DEFAULT); | |
| 74 | } | |
| 75 | ||
| 76 | static int | |
| 77 | ohci_s3c24x0_attach(device_t dev) | |
| 78 | { | |
| 79 | struct ohci_softc *sc = device_get_softc(dev); | |
| 80 | int err; | |
| 81 | int rid; | |
| 82 | ||
| 83 | /* initialise some bus fields */ | |
| 84 | sc->sc_bus.parent = dev; | |
| 85 | sc->sc_bus.devices = sc->sc_devices; | |
| 86 | sc->sc_bus.devices_max = OHCI_MAX_DEVICES; | |
| 87 | ||
| 88 | /* get all DMA memory */ | |
| 89 | if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), | |
| 90 | &ohci_iterate_hw_softc)) { | |
| 91 | return (ENOMEM); | |
| 92 | } | |
| 93 | ||
| 94 | sc->sc_dev = dev; | |
| 95 | ||
| 96 | rid = 0; | |
| 97 | sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, | |
| 98 | &rid, RF_ACTIVE); | |
| 99 | ||
| 100 | if (!(sc->sc_io_res)) { | |
| 101 | err = ENOMEM; | |
| 102 | goto error; | |
| 103 | } | |
| 104 | sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); | |
| 105 | sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); | |
| 106 | sc->sc_io_size = rman_get_size(sc->sc_io_res); | |
| 107 | ||
| 108 | rid = 0; | |
| 109 | sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, | |
| 110 | RF_ACTIVE); | |
| 111 | if (!(sc->sc_irq_res)) { | |
| 112 | goto error; | |
| 113 | } | |
| 114 | sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); | |
| 115 | if (!(sc->sc_bus.bdev)) { | |
| 116 | goto error; | |
| 117 | } | |
| 118 | device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); | |
| 119 | ||
| 120 | strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); | |
| 121 | ||
| 122 | err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, | |
| 123 | NULL, (void *)ohci_interrupt, sc, &sc->sc_intr_hdl); | |
| 124 | if (err) { | |
| 125 | sc->sc_intr_hdl = NULL; | |
| 126 | goto error; | |
| 127 | } | |
| 128 | ||
| 129 | bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, | |
| 130 | OHCI_CONTROL, 0); | |
| 131 | ||
| 132 | err = ohci_init(sc); | |
| 133 | if (!err) { | |
| 134 | err = device_probe_and_attach(sc->sc_bus.bdev); | |
| 135 | } | |
| 136 | if (err) { | |
| 137 | goto error; | |
| 138 | } | |
| 139 | return (0); | |
| 140 | ||
| 141 | error: | |
| 142 | ohci_s3c24x0_detach(dev); | |
| 143 | return (ENXIO); | |
| 144 | } | |
| 145 | ||
| 146 | static int | |
| 147 | ohci_s3c24x0_detach(device_t dev) | |
| 148 | { | |
| 149 | struct ohci_softc *sc = device_get_softc(dev); | |
| 150 | device_t bdev; | |
| 151 | int err; | |
| 152 | ||
| 153 | if (sc->sc_bus.bdev) { | |
| 154 | bdev = sc->sc_bus.bdev; | |
| 155 | device_detach(bdev); | |
| 156 | device_delete_child(dev, bdev); | |
| 157 | } | |
| 158 | /* during module unload there are lots of children leftover */ | |
| 159 | device_delete_children(dev); | |
| 160 | ||
| 161 | /* | |
| 162 | * Put the controller into reset, then disable clocks and do | |
| 163 | * the MI tear down. We have to disable the clocks/hardware | |
| 164 | * after we do the rest of the teardown. We also disable the | |
| 165 | * clocks in the opposite order we acquire them, but that | |
| 166 | * doesn't seem to be absolutely necessary. We free up the | |
| 167 | * clocks after we disable them, so the system could, in | |
| 168 | * theory, reuse them. | |
| 169 | */ | |
| 170 | bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, | |
| 171 | OHCI_CONTROL, 0); | |
| 172 | ||
| 173 | if (sc->sc_irq_res && sc->sc_intr_hdl) { | |
| 174 | /* | |
| 175 | * only call ohci_detach() after ohci_init() | |
| 176 | */ | |
| 177 | ohci_detach(sc); | |
| 178 | ||
| 179 | err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl); | |
| 180 | sc->sc_intr_hdl = NULL; | |
| 181 | } | |
| 182 | if (sc->sc_irq_res) { | |
| 183 | bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); | |
| 184 | sc->sc_irq_res = NULL; | |
| 185 | } | |
| 186 | if (sc->sc_io_res) { | |
| 187 | bus_release_resource(dev, SYS_RES_MEMORY, 0, | |
| 188 | sc->sc_io_res); | |
| 189 | sc->sc_io_res = NULL; | |
| 190 | } | |
| 191 | usb_bus_mem_free_all(&sc->sc_bus, &ohci_iterate_hw_softc); | |
| 192 | ||
| 193 | return (0); | |
| 194 | } | |
| 195 | ||
| 196 | static device_method_t ohci_methods[] = { | |
| 197 | /* Device interface */ | |
| 198 | DEVMETHOD(device_probe, ohci_s3c24x0_probe), | |
| 199 | DEVMETHOD(device_attach, ohci_s3c24x0_attach), | |
| 200 | DEVMETHOD(device_detach, ohci_s3c24x0_detach), | |
| 201 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
| 202 | DEVMETHOD(device_resume, bus_generic_resume), | |
| 203 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
| 204 | ||
| 205 | DEVMETHOD_END | |
| 206 | }; | |
| 207 | ||
| 208 | static driver_t ohci_driver = { | |
| 209 | .name = "ohci", | |
| 210 | .methods = ohci_methods, | |
| 211 | .size = sizeof(struct ohci_softc), | |
| 212 | }; | |
| 213 | ||
| 214 | static devclass_t ohci_devclass; | |
| 215 | ||
| 216 | DRIVER_MODULE(ohci, s3c24x0, ohci_driver, ohci_devclass, 0, 0); | |
| 217 | MODULE_DEPEND(ohci, usb, 1, 1, 1); |