diff options
author | gonzo <gonzo@FreeBSD.org> | 2009-04-19 22:58:36 +0000 |
---|---|---|
committer | gonzo <gonzo@FreeBSD.org> | 2009-04-19 22:58:36 +0000 |
commit | 9cf27d53b334e18629875b784110ad20906b4254 (patch) | |
tree | d8795cb1676b47674d75f72ebd7882f733d7a0e6 /sys/mips/atheros/ar71xx_ehci.c | |
parent | 75d47e36330a7994de3418ec9e1d0aa253346dc3 (diff) | |
download | FreeBSD-src-9cf27d53b334e18629875b784110ad20906b4254.zip FreeBSD-src-9cf27d53b334e18629875b784110ad20906b4254.tar.gz |
- Add EHCI controller driver for AR71XX-based boards.
Diffstat (limited to 'sys/mips/atheros/ar71xx_ehci.c')
-rw-r--r-- | sys/mips/atheros/ar71xx_ehci.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/sys/mips/atheros/ar71xx_ehci.c b/sys/mips/atheros/ar71xx_ehci.c new file mode 100644 index 0000000..272a52f --- /dev/null +++ b/sys/mips/atheros/ar71xx_ehci.c @@ -0,0 +1,291 @@ +/*- + * Copyright (c) 2008 Sam Leffler. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * AR71XX attachment driver for the USB Enhanced Host Controller. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> + +#include <machine/bus.h> + +#include <dev/usb/usb_mfunc.h> +#include <dev/usb/usb.h> + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/usb_controller.h> +#include <dev/usb/usb_bus.h> +#include <dev/usb/controller/ehci.h> + +#include <mips/atheros/ar71xx_bus_space_reversed.h> + +#define EHCI_HC_DEVSTR "AR71XX Integrated USB 2.0 controller" + +struct ar71xx_ehci_softc { + ehci_softc_t base; /* storage for EHCI code */ +}; + +static device_attach_t ar71xx_ehci_attach; +static device_detach_t ar71xx_ehci_detach; +static device_shutdown_t ar71xx_ehci_shutdown; +static device_suspend_t ar71xx_ehci_suspend; +static device_resume_t ar71xx_ehci_resume; + +bs_r_1_proto(reversed); +bs_w_1_proto(reversed); + +static int +ar71xx_ehci_suspend(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + int err; + + err = bus_generic_suspend(self); + if (err) + return (err); + ehci_suspend(sc); + return (0); +} + +static int +ar71xx_ehci_resume(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + + ehci_resume(sc); + + bus_generic_resume(self); + + return (0); +} + +static int +ar71xx_ehci_shutdown(device_t self) +{ + ehci_softc_t *sc = device_get_softc(self); + int err; + + err = bus_generic_shutdown(self); + if (err) + return (err); + ehci_shutdown(sc); + + return (0); +} + +static int +ar71xx_ehci_probe(device_t self) +{ + + device_set_desc(self, EHCI_HC_DEVSTR); + printf("EHCI probed\n"); + + return (BUS_PROBE_DEFAULT); +} + +static int +ar71xx_ehci_attach(device_t self) +{ + struct ar71xx_ehci_softc *isc = device_get_softc(self); + ehci_softc_t *sc = &isc->base; + int err; + int rid; + + printf("EHCI attach\n"); + /* initialise some bus fields */ + sc->sc_bus.parent = self; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + + /* get all DMA memory */ + if (usb2_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) { + return (ENOMEM); + } + + sc->sc_bus.usbrev = USB_REV_2_0; + + /* NB: hints fix the memory location and irq */ + + rid = 0; + sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (!sc->sc_io_res) { + device_printf(self, "Could not map memory\n"); + goto error; + } + + /* + * Craft special resource for bus space ops that handle + * byte-alignment of non-word addresses. + */ + sc->sc_io_tag = &ar71xx_bus_space_reversed; + sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = rman_get_size(sc->sc_io_res); + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(self, "Could not allocate irq\n"); + goto error; + } + sc->sc_bus.bdev = device_add_child(self, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(self, "Could not add USB device\n"); + goto error; + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR); + + sprintf(sc->sc_vendor, "Atheros"); + + + err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); + if (err) { + device_printf(self, "Could not setup irq, %d\n", err); + sc->sc_intr_hdl = NULL; + goto error; + } + + /* + * Arrange to force Host mode, select big-endian byte alignment, + * and arrange to not terminate reset operations (the adapter + * will ignore it if we do but might as well save a reg write). + * Also, the controller has an embedded Transaction Translator + * which means port speed must be read from the Port Status + * register following a port enable. + */ + sc->sc_flags |= EHCI_SCFLG_TT + | EHCI_SCFLG_SETMODE + | EHCI_SCFLG_BIGEDESC + | EHCI_SCFLG_BIGEMMIO + | EHCI_SCFLG_NORESTERM + ; + (void) ehci_reset(sc); + + err = ehci_init(sc); + if (!err) { + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(self, "USB init failed err=%d\n", err); + goto error; + } + return (0); + +error: + ar71xx_ehci_detach(self); + return (ENXIO); +} + +static int +ar71xx_ehci_detach(device_t self) +{ + struct ar71xx_ehci_softc *isc = device_get_softc(self); + ehci_softc_t *sc = &isc->base; + device_t bdev; + int err; + + if (sc->sc_bus.bdev) { + bdev = sc->sc_bus.bdev; + device_detach(bdev); + device_delete_child(self, bdev); + } + /* during module unload there are lots of children leftover */ + device_delete_all_children(self); + + /* + * disable interrupts that might have been switched on in ehci_init + */ + if (sc->sc_io_res) { + EWRITE4(sc, EHCI_USBINTR, 0); + } + + if (sc->sc_irq_res && sc->sc_intr_hdl) { + /* + * only call ehci_detach() after ehci_init() + */ + ehci_detach(sc); + + err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl); + + if (err) + /* XXX or should we panic? */ + device_printf(self, "Could not tear down irq, %d\n", + err); + sc->sc_intr_hdl = NULL; + } + + if (sc->sc_irq_res) { + bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); + sc->sc_irq_res = NULL; + } + if (sc->sc_io_res) { + bus_release_resource(self, SYS_RES_MEMORY, 0, + sc->sc_io_res); + sc->sc_io_res = NULL; + } + usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + return (0); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ar71xx_ehci_probe), + DEVMETHOD(device_attach, ar71xx_ehci_attach), + DEVMETHOD(device_detach, ar71xx_ehci_detach), + DEVMETHOD(device_suspend, ar71xx_ehci_suspend), + DEVMETHOD(device_resume, ar71xx_ehci_resume), + DEVMETHOD(device_shutdown, ar71xx_ehci_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + {0, 0} +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(struct ar71xx_ehci_softc), +}; + +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, nexus, ehci_driver, ehci_devclass, 0, 0); +MODULE_DEPEND(ehci, usb, 1, 1, 1); |