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>
61232812Sjmallett#include <mips/cavium/octeon_irq.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;
97229168Sgonzo	int nports;
98229168Sgonzo	int i;
99210312Sjmallett
100210312Sjmallett	/* setup controller interface softc */
101210312Sjmallett
102210312Sjmallett	/* initialise some bus fields */
103210312Sjmallett	sc->sc_dci.sc_bus.parent = dev;
104210312Sjmallett	sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices;
105210312Sjmallett	sc->sc_dci.sc_bus.devices_max = OCTUSB_MAX_DEVICES;
106210312Sjmallett
107210312Sjmallett	/* get all DMA memory */
108210312Sjmallett	if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus,
109210312Sjmallett	    USB_GET_DMA_TAG(dev), NULL)) {
110210312Sjmallett		return (ENOMEM);
111210312Sjmallett	}
112229168Sgonzo	nports = cvmx_usb_get_num_ports();
113229168Sgonzo	if (nports > OCTUSB_MAX_PORTS)
114229168Sgonzo		panic("octusb: too many USB ports %d", nports);
115229168Sgonzo	for (i = 0; i < nports; i++) {
116229168Sgonzo		rid = 0;
117229168Sgonzo		sc->sc_dci.sc_irq_res[i] =
118229168Sgonzo		    bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
119232812Sjmallett			       OCTEON_IRQ_USB0 + i, OCTEON_IRQ_USB0 + i, 1, RF_ACTIVE);
120229168Sgonzo		if (!(sc->sc_dci.sc_irq_res[i])) {
121229168Sgonzo			goto error;
122229168Sgonzo		}
123229168Sgonzo
124229168Sgonzo#if (__FreeBSD_version >= 700031)
125229168Sgonzo		err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res[i], INTR_TYPE_BIO | INTR_MPSAFE,
126229168Sgonzo		    NULL, (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl[i]);
127229168Sgonzo#else
128229168Sgonzo		err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res[i], INTR_TYPE_BIO | INTR_MPSAFE,
129229168Sgonzo		    (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl[i]);
130229168Sgonzo#endif
131229168Sgonzo		if (err) {
132229168Sgonzo			sc->sc_dci.sc_intr_hdl[i] = NULL;
133229168Sgonzo			goto error;
134229168Sgonzo		}
135210312Sjmallett	}
136210312Sjmallett
137210312Sjmallett	sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
138210312Sjmallett	if (!(sc->sc_dci.sc_bus.bdev)) {
139210312Sjmallett		goto error;
140210312Sjmallett	}
141210312Sjmallett	device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus);
142210312Sjmallett
143229168Sgonzo
144210312Sjmallett	err = octusb_init(&sc->sc_dci);
145210312Sjmallett	if (!err) {
146210312Sjmallett		err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev);
147210312Sjmallett	}
148210312Sjmallett	if (err) {
149210312Sjmallett		goto error;
150210312Sjmallett	}
151210312Sjmallett	return (0);
152210312Sjmallett
153210312Sjmalletterror:
154210312Sjmallett	octusb_octeon_detach(dev);
155210312Sjmallett	return (ENXIO);
156210312Sjmallett}
157210312Sjmallett
158210312Sjmallettstatic int
159210312Sjmallettoctusb_octeon_detach(device_t dev)
160210312Sjmallett{
161210312Sjmallett	struct octusb_octeon_softc *sc = device_get_softc(dev);
162210312Sjmallett	device_t bdev;
163210312Sjmallett	int err;
164229168Sgonzo	int nports;
165229168Sgonzo	int i;
166210312Sjmallett
167210312Sjmallett	if (sc->sc_dci.sc_bus.bdev) {
168210312Sjmallett		bdev = sc->sc_dci.sc_bus.bdev;
169210312Sjmallett		device_detach(bdev);
170210312Sjmallett		device_delete_child(dev, bdev);
171210312Sjmallett	}
172210312Sjmallett	/* during module unload there are lots of children leftover */
173227849Shselasky	device_delete_children(dev);
174210312Sjmallett
175229168Sgonzo	if (sc->sc_dci.sc_irq_res[0] && sc->sc_dci.sc_intr_hdl[0])
176210312Sjmallett		/*
177229168Sgonzo	 	 * only call octusb_octeon_uninit() after octusb_octeon_init()
178229168Sgonzo	 	 */
179210312Sjmallett		octusb_uninit(&sc->sc_dci);
180210312Sjmallett
181229168Sgonzo	nports = cvmx_usb_get_num_ports();
182229168Sgonzo	if (nports > OCTUSB_MAX_PORTS)
183229168Sgonzo		panic("octusb: too many USB ports %d", nports);
184229168Sgonzo	for (i = 0; i < nports; i++) {
185229168Sgonzo		if (sc->sc_dci.sc_irq_res[0] && sc->sc_dci.sc_intr_hdl[0]) {
186229168Sgonzo			err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res[i],
187229168Sgonzo			    sc->sc_dci.sc_intr_hdl[i]);
188229168Sgonzo			sc->sc_dci.sc_intr_hdl[i] = NULL;
189229168Sgonzo		}
190229168Sgonzo		if (sc->sc_dci.sc_irq_res) {
191229168Sgonzo			bus_release_resource(dev, SYS_RES_IRQ, 0,
192229168Sgonzo			    sc->sc_dci.sc_irq_res[i]);
193229168Sgonzo			sc->sc_dci.sc_irq_res[i] = NULL;
194229168Sgonzo		}
195210312Sjmallett	}
196210312Sjmallett	usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL);
197210312Sjmallett
198210312Sjmallett	return (0);
199210312Sjmallett}
200210312Sjmallett
201210312Sjmallettstatic device_method_t octusb_octeon_methods[] = {
202210312Sjmallett	/* Device interface */
203210312Sjmallett	DEVMETHOD(device_identify, octusb_octeon_identify),
204210312Sjmallett	DEVMETHOD(device_probe, octusb_octeon_probe),
205210312Sjmallett	DEVMETHOD(device_attach, octusb_octeon_attach),
206210312Sjmallett	DEVMETHOD(device_detach, octusb_octeon_detach),
207228483Shselasky	DEVMETHOD(device_resume, bus_generic_resume),
208228483Shselasky	DEVMETHOD(device_suspend, bus_generic_suspend),
209228483Shselasky	DEVMETHOD(device_shutdown, bus_generic_shutdown),
210210312Sjmallett
211227843Smarius	DEVMETHOD_END
212210312Sjmallett};
213210312Sjmallett
214210312Sjmallettstatic driver_t octusb_octeon_driver = {
215228483Shselasky	.name = "octusb",
216228483Shselasky	.methods = octusb_octeon_methods,
217228483Shselasky	.size = sizeof(struct octusb_octeon_softc),
218210312Sjmallett};
219210312Sjmallett
220210312Sjmallettstatic devclass_t octusb_octeon_devclass;
221210312Sjmallett
222210312SjmallettDRIVER_MODULE(octusb, ciu, octusb_octeon_driver, octusb_octeon_devclass, 0, 0);
223