1210312Sjmallett#include <sys/cdefs.h>
2210312Sjmallett__FBSDID("$FreeBSD$");
3210312Sjmallett
4210312Sjmallett/*-
5210312Sjmallett * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved.
6210312Sjmallett *
7210312Sjmallett * Redistribution and use in source and binary forms, with or without
8210312Sjmallett * modification, are permitted provided that the following conditions
9210312Sjmallett * are met:
10210312Sjmallett * 1. Redistributions of source code must retain the above copyright
11210312Sjmallett *    notice, this list of conditions and the following disclaimer.
12210312Sjmallett * 2. Redistributions in binary form must reproduce the above copyright
13210312Sjmallett *    notice, this list of conditions and the following disclaimer in the
14210312Sjmallett *    documentation and/or other materials provided with the distribution.
15210312Sjmallett *
16210312Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17210312Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18210312Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19210312Sjmallett * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20210312Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21210312Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22210312Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23210312Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24210312Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25210312Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26210312Sjmallett * SUCH DAMAGE.
27210312Sjmallett */
28210312Sjmallett
29210312Sjmallett#include <sys/stdint.h>
30210312Sjmallett#include <sys/stddef.h>
31210312Sjmallett#include <sys/param.h>
32210312Sjmallett#include <sys/queue.h>
33210312Sjmallett#include <sys/types.h>
34210312Sjmallett#include <sys/systm.h>
35210312Sjmallett#include <sys/kernel.h>
36210312Sjmallett#include <sys/bus.h>
37210312Sjmallett#include <sys/module.h>
38210312Sjmallett#include <sys/lock.h>
39210312Sjmallett#include <sys/mutex.h>
40210312Sjmallett#include <sys/condvar.h>
41210312Sjmallett#include <sys/sysctl.h>
42210312Sjmallett#include <sys/sx.h>
43210312Sjmallett#include <sys/unistd.h>
44210312Sjmallett#include <sys/callout.h>
45210312Sjmallett#include <sys/malloc.h>
46210312Sjmallett#include <sys/priv.h>
47210312Sjmallett#include <sys/rman.h>
48210312Sjmallett
49210312Sjmallett#include <dev/usb/usb.h>
50210312Sjmallett#include <dev/usb/usbdi.h>
51210312Sjmallett
52210312Sjmallett#include <dev/usb/usb_core.h>
53210312Sjmallett#include <dev/usb/usb_busdma.h>
54210312Sjmallett#include <dev/usb/usb_process.h>
55210312Sjmallett#include <dev/usb/usb_util.h>
56210312Sjmallett
57210312Sjmallett#include <dev/usb/usb_controller.h>
58210312Sjmallett#include <dev/usb/usb_bus.h>
59210312Sjmallett
60210312Sjmallett#include <contrib/octeon-sdk/cvmx.h>
61210312Sjmallett#include <contrib/octeon-sdk/cvmx-interrupt.h>
62210312Sjmallett#include <contrib/octeon-sdk/cvmx-usb.h>
63210312Sjmallett
64210312Sjmallett#include <mips/cavium/usb/octusb.h>
65210312Sjmallett
66210312Sjmallett#define	MEM_RID	0
67210312Sjmallett
68210312Sjmallettstatic device_identify_t octusb_octeon_identify;
69210312Sjmallettstatic device_probe_t octusb_octeon_probe;
70210312Sjmallettstatic device_attach_t octusb_octeon_attach;
71210312Sjmallettstatic device_detach_t octusb_octeon_detach;
72210312Sjmallett
73210312Sjmallettstruct octusb_octeon_softc {
74210312Sjmallett	struct octusb_softc sc_dci;	/* must be first */
75210312Sjmallett};
76210312Sjmallett
77210312Sjmallettstatic void
78210312Sjmallettoctusb_octeon_identify(driver_t *drv, device_t parent)
79210312Sjmallett{
80210312Sjmallett	if (octeon_has_feature(OCTEON_FEATURE_USB))
81210312Sjmallett		BUS_ADD_CHILD(parent, 0, "octusb", 0);
82210312Sjmallett}
83210312Sjmallett
84210312Sjmallettstatic int
85210312Sjmallettoctusb_octeon_probe(device_t dev)
86210312Sjmallett{
87210312Sjmallett	device_set_desc(dev, "Cavium Octeon USB controller");
88210312Sjmallett	return (0);
89210312Sjmallett}
90210312Sjmallett
91210312Sjmallettstatic int
92210312Sjmallettoctusb_octeon_attach(device_t dev)
93210312Sjmallett{
94210312Sjmallett	struct octusb_octeon_softc *sc = device_get_softc(dev);
95210312Sjmallett	int err;
96210312Sjmallett	int rid;
97210312Sjmallett
98210312Sjmallett	/* setup controller interface softc */
99210312Sjmallett
100210312Sjmallett	/* initialise some bus fields */
101210312Sjmallett	sc->sc_dci.sc_bus.parent = dev;
102210312Sjmallett	sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices;
103210312Sjmallett	sc->sc_dci.sc_bus.devices_max = OCTUSB_MAX_DEVICES;
104210312Sjmallett
105210312Sjmallett	/* get all DMA memory */
106210312Sjmallett	if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus,
107210312Sjmallett	    USB_GET_DMA_TAG(dev), NULL)) {
108210312Sjmallett		return (ENOMEM);
109210312Sjmallett	}
110210312Sjmallett	rid = 0;
111210312Sjmallett	sc->sc_dci.sc_irq_res =
112210312Sjmallett	    bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
113210312Sjmallett			       CVMX_IRQ_USB, CVMX_IRQ_USB, 1, RF_ACTIVE);
114210312Sjmallett	if (!(sc->sc_dci.sc_irq_res)) {
115210312Sjmallett		goto error;
116210312Sjmallett	}
117210312Sjmallett
118210312Sjmallett	sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
119210312Sjmallett	if (!(sc->sc_dci.sc_bus.bdev)) {
120210312Sjmallett		goto error;
121210312Sjmallett	}
122210312Sjmallett	device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus);
123210312Sjmallett
124210312Sjmallett#if (__FreeBSD_version >= 700031)
125210312Sjmallett	err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
126210312Sjmallett	    NULL, (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
127210312Sjmallett#else
128210312Sjmallett	err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
129210312Sjmallett	    (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
130210312Sjmallett#endif
131210312Sjmallett	if (err) {
132210312Sjmallett		sc->sc_dci.sc_intr_hdl = NULL;
133210312Sjmallett		goto error;
134210312Sjmallett	}
135210312Sjmallett	err = octusb_init(&sc->sc_dci);
136210312Sjmallett	if (!err) {
137210312Sjmallett		err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev);
138210312Sjmallett	}
139210312Sjmallett	if (err) {
140210312Sjmallett		goto error;
141210312Sjmallett	}
142210312Sjmallett	return (0);
143210312Sjmallett
144210312Sjmalletterror:
145210312Sjmallett	octusb_octeon_detach(dev);
146210312Sjmallett	return (ENXIO);
147210312Sjmallett}
148210312Sjmallett
149210312Sjmallettstatic int
150210312Sjmallettoctusb_octeon_detach(device_t dev)
151210312Sjmallett{
152210312Sjmallett	struct octusb_octeon_softc *sc = device_get_softc(dev);
153210312Sjmallett	device_t bdev;
154210312Sjmallett	int err;
155210312Sjmallett
156210312Sjmallett	if (sc->sc_dci.sc_bus.bdev) {
157210312Sjmallett		bdev = sc->sc_dci.sc_bus.bdev;
158210312Sjmallett		device_detach(bdev);
159210312Sjmallett		device_delete_child(dev, bdev);
160210312Sjmallett	}
161210312Sjmallett	/* during module unload there are lots of children leftover */
162229118Shselasky	device_delete_children(dev);
163210312Sjmallett
164210312Sjmallett	if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) {
165210312Sjmallett		/*
166210312Sjmallett		 * only call octusb_octeon_uninit() after octusb_octeon_init()
167210312Sjmallett		 */
168210312Sjmallett		octusb_uninit(&sc->sc_dci);
169210312Sjmallett
170210312Sjmallett		err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res,
171210312Sjmallett		    sc->sc_dci.sc_intr_hdl);
172210312Sjmallett		sc->sc_dci.sc_intr_hdl = NULL;
173210312Sjmallett	}
174210312Sjmallett	if (sc->sc_dci.sc_irq_res) {
175210312Sjmallett		bus_release_resource(dev, SYS_RES_IRQ, 0,
176210312Sjmallett		    sc->sc_dci.sc_irq_res);
177210312Sjmallett		sc->sc_dci.sc_irq_res = NULL;
178210312Sjmallett	}
179210312Sjmallett	usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL);
180210312Sjmallett
181210312Sjmallett	return (0);
182210312Sjmallett}
183210312Sjmallett
184210312Sjmallettstatic device_method_t octusb_octeon_methods[] = {
185210312Sjmallett	/* Device interface */
186210312Sjmallett	DEVMETHOD(device_identify, octusb_octeon_identify),
187210312Sjmallett	DEVMETHOD(device_probe, octusb_octeon_probe),
188210312Sjmallett	DEVMETHOD(device_attach, octusb_octeon_attach),
189210312Sjmallett	DEVMETHOD(device_detach, octusb_octeon_detach),
190229096Shselasky	DEVMETHOD(device_resume, bus_generic_resume),
191229096Shselasky	DEVMETHOD(device_suspend, bus_generic_suspend),
192229096Shselasky	DEVMETHOD(device_shutdown, bus_generic_shutdown),
193210312Sjmallett
194229093Shselasky	DEVMETHOD_END
195210312Sjmallett};
196210312Sjmallett
197210312Sjmallettstatic driver_t octusb_octeon_driver = {
198229096Shselasky	.name = "octusb",
199229096Shselasky	.methods = octusb_octeon_methods,
200229096Shselasky	.size = sizeof(struct octusb_octeon_softc),
201210312Sjmallett};
202210312Sjmallett
203210312Sjmallettstatic devclass_t octusb_octeon_devclass;
204210312Sjmallett
205210312SjmallettDRIVER_MODULE(octusb, ciu, octusb_octeon_driver, octusb_octeon_devclass, 0, 0);
206