octusb_octeon.c revision 210312
1210312Sjmallett#include <sys/cdefs.h>
2210312Sjmallett__FBSDID("$FreeBSD: head/sys/mips/cavium/usb/octusb_octeon.c 210312 2010-07-20 19:32:25Z jmallett $");
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/linker_set.h>
38210312Sjmallett#include <sys/module.h>
39210312Sjmallett#include <sys/lock.h>
40210312Sjmallett#include <sys/mutex.h>
41210312Sjmallett#include <sys/condvar.h>
42210312Sjmallett#include <sys/sysctl.h>
43210312Sjmallett#include <sys/sx.h>
44210312Sjmallett#include <sys/unistd.h>
45210312Sjmallett#include <sys/callout.h>
46210312Sjmallett#include <sys/malloc.h>
47210312Sjmallett#include <sys/priv.h>
48210312Sjmallett#include <sys/rman.h>
49210312Sjmallett
50210312Sjmallett#include <dev/usb/usb.h>
51210312Sjmallett#include <dev/usb/usbdi.h>
52210312Sjmallett
53210312Sjmallett#include <dev/usb/usb_core.h>
54210312Sjmallett#include <dev/usb/usb_busdma.h>
55210312Sjmallett#include <dev/usb/usb_process.h>
56210312Sjmallett#include <dev/usb/usb_util.h>
57210312Sjmallett
58210312Sjmallett#include <dev/usb/usb_controller.h>
59210312Sjmallett#include <dev/usb/usb_bus.h>
60210312Sjmallett
61210312Sjmallett#include <contrib/octeon-sdk/cvmx.h>
62210312Sjmallett#include <contrib/octeon-sdk/cvmx-interrupt.h>
63210312Sjmallett#include <contrib/octeon-sdk/cvmx-usb.h>
64210312Sjmallett
65210312Sjmallett#include <mips/cavium/usb/octusb.h>
66210312Sjmallett
67210312Sjmallett#define	MEM_RID	0
68210312Sjmallett
69210312Sjmallettstatic device_identify_t octusb_octeon_identify;
70210312Sjmallettstatic device_probe_t octusb_octeon_probe;
71210312Sjmallettstatic device_attach_t octusb_octeon_attach;
72210312Sjmallettstatic device_detach_t octusb_octeon_detach;
73210312Sjmallettstatic device_shutdown_t octusb_octeon_shutdown;
74210312Sjmallett
75210312Sjmallettstruct octusb_octeon_softc {
76210312Sjmallett	struct octusb_softc sc_dci;	/* must be first */
77210312Sjmallett};
78210312Sjmallett
79210312Sjmallettstatic void
80210312Sjmallettoctusb_octeon_identify(driver_t *drv, device_t parent)
81210312Sjmallett{
82210312Sjmallett	if (octeon_has_feature(OCTEON_FEATURE_USB))
83210312Sjmallett		BUS_ADD_CHILD(parent, 0, "octusb", 0);
84210312Sjmallett}
85210312Sjmallett
86210312Sjmallettstatic int
87210312Sjmallettoctusb_octeon_probe(device_t dev)
88210312Sjmallett{
89210312Sjmallett	device_set_desc(dev, "Cavium Octeon USB controller");
90210312Sjmallett	return (0);
91210312Sjmallett}
92210312Sjmallett
93210312Sjmallettstatic int
94210312Sjmallettoctusb_octeon_attach(device_t dev)
95210312Sjmallett{
96210312Sjmallett	struct octusb_octeon_softc *sc = device_get_softc(dev);
97210312Sjmallett	int err;
98210312Sjmallett	int rid;
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	}
112210312Sjmallett	rid = 0;
113210312Sjmallett	sc->sc_dci.sc_irq_res =
114210312Sjmallett	    bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
115210312Sjmallett			       CVMX_IRQ_USB, CVMX_IRQ_USB, 1, RF_ACTIVE);
116210312Sjmallett	if (!(sc->sc_dci.sc_irq_res)) {
117210312Sjmallett		goto error;
118210312Sjmallett	}
119210312Sjmallett
120210312Sjmallett	sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
121210312Sjmallett	if (!(sc->sc_dci.sc_bus.bdev)) {
122210312Sjmallett		goto error;
123210312Sjmallett	}
124210312Sjmallett	device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus);
125210312Sjmallett
126210312Sjmallett#if (__FreeBSD_version >= 700031)
127210312Sjmallett	err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
128210312Sjmallett	    NULL, (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
129210312Sjmallett#else
130210312Sjmallett	err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
131210312Sjmallett	    (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
132210312Sjmallett#endif
133210312Sjmallett	if (err) {
134210312Sjmallett		sc->sc_dci.sc_intr_hdl = NULL;
135210312Sjmallett		goto error;
136210312Sjmallett	}
137210312Sjmallett	err = octusb_init(&sc->sc_dci);
138210312Sjmallett	if (!err) {
139210312Sjmallett		err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev);
140210312Sjmallett	}
141210312Sjmallett	if (err) {
142210312Sjmallett		goto error;
143210312Sjmallett	}
144210312Sjmallett	return (0);
145210312Sjmallett
146210312Sjmalletterror:
147210312Sjmallett	octusb_octeon_detach(dev);
148210312Sjmallett	return (ENXIO);
149210312Sjmallett}
150210312Sjmallett
151210312Sjmallettstatic int
152210312Sjmallettoctusb_octeon_detach(device_t dev)
153210312Sjmallett{
154210312Sjmallett	struct octusb_octeon_softc *sc = device_get_softc(dev);
155210312Sjmallett	device_t bdev;
156210312Sjmallett	int err;
157210312Sjmallett
158210312Sjmallett	if (sc->sc_dci.sc_bus.bdev) {
159210312Sjmallett		bdev = sc->sc_dci.sc_bus.bdev;
160210312Sjmallett		device_detach(bdev);
161210312Sjmallett		device_delete_child(dev, bdev);
162210312Sjmallett	}
163210312Sjmallett	/* during module unload there are lots of children leftover */
164210312Sjmallett	device_delete_all_children(dev);
165210312Sjmallett
166210312Sjmallett	if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) {
167210312Sjmallett		/*
168210312Sjmallett		 * only call octusb_octeon_uninit() after octusb_octeon_init()
169210312Sjmallett		 */
170210312Sjmallett		octusb_uninit(&sc->sc_dci);
171210312Sjmallett
172210312Sjmallett		err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res,
173210312Sjmallett		    sc->sc_dci.sc_intr_hdl);
174210312Sjmallett		sc->sc_dci.sc_intr_hdl = NULL;
175210312Sjmallett	}
176210312Sjmallett	if (sc->sc_dci.sc_irq_res) {
177210312Sjmallett		bus_release_resource(dev, SYS_RES_IRQ, 0,
178210312Sjmallett		    sc->sc_dci.sc_irq_res);
179210312Sjmallett		sc->sc_dci.sc_irq_res = NULL;
180210312Sjmallett	}
181210312Sjmallett	usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL);
182210312Sjmallett
183210312Sjmallett	return (0);
184210312Sjmallett}
185210312Sjmallett
186210312Sjmallettstatic int
187210312Sjmallettoctusb_octeon_shutdown(device_t dev)
188210312Sjmallett{
189210312Sjmallett	struct octusb_octeon_softc *sc = device_get_softc(dev);
190210312Sjmallett	int err;
191210312Sjmallett
192210312Sjmallett	err = bus_generic_shutdown(dev);
193210312Sjmallett	if (err)
194210312Sjmallett		return (err);
195210312Sjmallett
196210312Sjmallett	octusb_uninit(&sc->sc_dci);
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),
207210312Sjmallett	DEVMETHOD(device_shutdown, octusb_octeon_shutdown),
208210312Sjmallett
209210312Sjmallett	/* Bus interface */
210210312Sjmallett	DEVMETHOD(bus_print_child, bus_generic_print_child),
211210312Sjmallett
212210312Sjmallett	{0, 0}
213210312Sjmallett};
214210312Sjmallett
215210312Sjmallettstatic driver_t octusb_octeon_driver = {
216210312Sjmallett	"octusb",
217210312Sjmallett	octusb_octeon_methods,
218210312Sjmallett	sizeof(struct octusb_octeon_softc),
219210312Sjmallett};
220210312Sjmallett
221210312Sjmallettstatic devclass_t octusb_octeon_devclass;
222210312Sjmallett
223210312SjmallettDRIVER_MODULE(octusb, ciu, octusb_octeon_driver, octusb_octeon_devclass, 0, 0);
224