ar71xx_ohci.c revision 199233
185587Sobrien/*-
285587Sobrien * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org>
385587Sobrien * All rights reserved.
485587Sobrien *
585587Sobrien * Redistribution and use in source and binary forms, with or without
685587Sobrien * modification, are permitted provided that the following conditions
785587Sobrien * are met:
885587Sobrien * 1. Redistributions of source code must retain the above copyright
985587Sobrien *    notice unmodified, this list of conditions, and the following
1085587Sobrien *    disclaimer.
1185587Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1285587Sobrien *    notice, this list of conditions and the following disclaimer in the
1385587Sobrien *    documentation and/or other materials provided with the distribution.
1485587Sobrien *
1585587Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1685587Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1785587Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1885587Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1985587Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2085587Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2185587Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2285587Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2385587Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2485587Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25170331Srafan * SUCH DAMAGE.
2685587Sobrien */
27201989Sru
28201989Sru#include <sys/cdefs.h>
29201989Sru__FBSDID("$FreeBSD$");
3085587Sobrien
3185587Sobrien#include <sys/param.h>
3285587Sobrien#include <sys/systm.h>
3385587Sobrien#include <sys/bus.h>
3485587Sobrien#include <sys/rman.h>
3585587Sobrien#include <sys/condvar.h>
3685587Sobrien#include <sys/kernel.h>
3785587Sobrien#include <sys/module.h>
3885587Sobrien
39118194Sru#include <dev/usb/usb.h>
4085587Sobrien#include <dev/usb/usbdi.h>
4185587Sobrien
4285587Sobrien#include <dev/usb/usb_core.h>
4385587Sobrien#include <dev/usb/usb_busdma.h>
4485587Sobrien#include <dev/usb/usb_process.h>
4585587Sobrien#include <dev/usb/usb_util.h>
4685587Sobrien
4785587Sobrien#include <dev/usb/usb_controller.h>
4885587Sobrien#include <dev/usb/usb_bus.h>
4985587Sobrien#include <dev/usb/controller/ohci.h>
50170331Srafan#include <dev/usb/controller/ohcireg.h>
5185587Sobrien
5285587Sobrien#include <sys/rman.h>
5385587Sobrien
54170331Srafanstatic int ar71xx_ohci_attach(device_t dev);
5585587Sobrienstatic int ar71xx_ohci_detach(device_t dev);
5685587Sobrienstatic int ar71xx_ohci_probe(device_t dev);
5785587Sobrien
5885587Sobrienstruct ar71xx_ohci_softc
5985587Sobrien{
6085587Sobrien	struct ohci_softc sc_ohci;
6185587Sobrien};
6285587Sobrien
6385587Sobrienstatic int
6485587Sobrienar71xx_ohci_probe(device_t dev)
6585587Sobrien{
6685587Sobrien	device_set_desc(dev, "AR71XX integrated OHCI controller");
6785587Sobrien	return (BUS_PROBE_DEFAULT);
6885587Sobrien}
6985587Sobrien
7085587Sobrienstatic int
7185587Sobrienar71xx_ohci_attach(device_t dev)
7285587Sobrien{
7385587Sobrien	struct ar71xx_ohci_softc *sc = device_get_softc(dev);
7485587Sobrien	int err;
7585587Sobrien	int rid;
7685587Sobrien
7785587Sobrien	/* initialise some bus fields */
7885587Sobrien	sc->sc_ohci.sc_bus.parent = dev;
7985587Sobrien	sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices;
8085587Sobrien	sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES;
8185587Sobrien
82107806Sobrien	/* get all DMA memory */
8385587Sobrien	if (usb_bus_mem_alloc_all(&sc->sc_ohci.sc_bus,
8485587Sobrien	    USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) {
8585587Sobrien		return (ENOMEM);
8685587Sobrien	}
8785587Sobrien
8885587Sobrien	sc->sc_ohci.sc_dev = dev;
8985587Sobrien
9085587Sobrien	rid = 0;
9185587Sobrien	sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
9285587Sobrien	    RF_ACTIVE);
9385587Sobrien	if (sc->sc_ohci.sc_io_res == NULL) {
9485587Sobrien		err = ENOMEM;
9585587Sobrien		goto error;
9685587Sobrien	}
9785587Sobrien	sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res);
9885587Sobrien	sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res);
9985587Sobrien	sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res);
10090902Sdes
10185587Sobrien	rid = 0;
10285587Sobrien	sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
10385587Sobrien	    RF_ACTIVE);
10485587Sobrien	if (sc->sc_ohci.sc_irq_res == NULL) {
10585587Sobrien		err = ENOMEM;
10685587Sobrien		goto error;
10785587Sobrien	}
10885587Sobrien	sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
10985587Sobrien	if (sc->sc_ohci.sc_bus.bdev == NULL) {
11085587Sobrien		err = ENOMEM;
11185587Sobrien		goto error;
11285587Sobrien	}
11385587Sobrien	device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus);
11485587Sobrien
11585587Sobrien	err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res,
11685587Sobrien	    INTR_TYPE_BIO | INTR_MPSAFE, NULL,
11785587Sobrien	    (driver_intr_t *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl);
11885587Sobrien	if (err) {
11985587Sobrien		err = ENXIO;
12085587Sobrien		goto error;
12185587Sobrien	}
12285587Sobrien
12385587Sobrien	strlcpy(sc->sc_ohci.sc_vendor, "Atheros", sizeof(sc->sc_ohci.sc_vendor));
124107806Sobrien
12585587Sobrien	bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl, OHCI_CONTROL, 0);
12685587Sobrien
12785587Sobrien	err = ohci_init(&sc->sc_ohci);
12885587Sobrien	if (!err)
12985587Sobrien		err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev);
13085587Sobrien
13185587Sobrien	if (err)
13285587Sobrien		goto error;
13385587Sobrien	return (0);
13485587Sobrien
13585587Sobrienerror:
13685587Sobrien	if (err) {
13785587Sobrien		ar71xx_ohci_detach(dev);
13885587Sobrien		return (err);
13985587Sobrien	}
14085587Sobrien	return (err);
14185587Sobrien}
14285587Sobrien
14385587Sobrienstatic int
14485587Sobrienar71xx_ohci_detach(device_t dev)
14585587Sobrien{
14685587Sobrien	struct ar71xx_ohci_softc *sc = device_get_softc(dev);
14785587Sobrien	device_t bdev;
14885587Sobrien
14985587Sobrien	if (sc->sc_ohci.sc_bus.bdev) {
15085587Sobrien		bdev = sc->sc_ohci.sc_bus.bdev;
15185587Sobrien		device_detach(bdev);
15285587Sobrien		device_delete_child(dev, bdev);
15385587Sobrien	}
15485587Sobrien	/* during module unload there are lots of children leftover */
15585587Sobrien	device_delete_all_children(dev);
15685587Sobrien
15785587Sobrien	/*
15885587Sobrien	 * Put the controller into reset, then disable clocks and do
15985587Sobrien	 * the MI tear down.  We have to disable the clocks/hardware
16085587Sobrien	 * after we do the rest of the teardown.  We also disable the
16185587Sobrien	 * clocks in the opposite order we acquire them, but that
16285587Sobrien	 * doesn't seem to be absolutely necessary.  We free up the
16385587Sobrien	 * clocks after we disable them, so the system could, in
16485587Sobrien	 * theory, reuse them.
16585587Sobrien	 */
16685587Sobrien	bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl,
16785587Sobrien	    OHCI_CONTROL, 0);
16885587Sobrien
16985587Sobrien	if (sc->sc_ohci.sc_intr_hdl) {
17085587Sobrien		bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl);
17185587Sobrien		sc->sc_ohci.sc_intr_hdl = NULL;
17285587Sobrien	}
17385587Sobrien
17485587Sobrien	if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) {
17585587Sobrien		/*
17685587Sobrien		 * only call ohci_detach() after ohci_init()
17785587Sobrien		 */
17885587Sobrien		ohci_detach(&sc->sc_ohci);
17985587Sobrien
18085587Sobrien		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res);
18185587Sobrien		sc->sc_ohci.sc_irq_res = NULL;
18285587Sobrien	}
18385587Sobrien	if (sc->sc_ohci.sc_io_res) {
18485587Sobrien		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_ohci.sc_io_res);
18585587Sobrien		sc->sc_ohci.sc_io_res = NULL;
18685587Sobrien		sc->sc_ohci.sc_io_tag = 0;
18785587Sobrien		sc->sc_ohci.sc_io_hdl = 0;
18885587Sobrien	}
189170331Srafan	usb_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc);
19085587Sobrien
19185587Sobrien	return (0);
19285587Sobrien}
19385587Sobrien
19485587Sobrienstatic device_method_t ohci_methods[] = {
19585587Sobrien	/* Device interface */
19685587Sobrien	DEVMETHOD(device_probe, ar71xx_ohci_probe),
19785587Sobrien	DEVMETHOD(device_attach, ar71xx_ohci_attach),
19885587Sobrien	DEVMETHOD(device_detach, ar71xx_ohci_detach),
19985587Sobrien	DEVMETHOD(device_shutdown, bus_generic_shutdown),
20085587Sobrien
20185587Sobrien	/* Bus interface */
20285587Sobrien	DEVMETHOD(bus_print_child, bus_generic_print_child),
20385587Sobrien
20485587Sobrien	{0, 0}
20585587Sobrien};
20685587Sobrien
20785587Sobrienstatic driver_t ohci_driver = {
20885587Sobrien	"ohci",
20985587Sobrien	ohci_methods,
21085587Sobrien	sizeof(struct ar71xx_ohci_softc),
21185587Sobrien};
21285587Sobrien
21385587Sobrienstatic devclass_t ohci_devclass;
214170331Srafan
21585587SobrienDRIVER_MODULE(ohci, apb, ohci_driver, ohci_devclass, 0, 0);
21685587Sobrien