octusb_octeon.c revision 227849
1184610Salfred#include <sys/cdefs.h>
2184610Salfred__FBSDID("$FreeBSD: head/sys/mips/cavium/usb/octusb_octeon.c 227849 2011-11-22 21:56:55Z hselasky $");
3184610Salfred
4184610Salfred/*-
5184610Salfred * Copyright (c) 2007-2008 Hans Petter Selasky. All rights reserved.
6184610Salfred *
7184610Salfred * Redistribution and use in source and binary forms, with or without
8184610Salfred * modification, are permitted provided that the following conditions
9184610Salfred * are met:
10184610Salfred * 1. Redistributions of source code must retain the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer.
12184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
13184610Salfred *    notice, this list of conditions and the following disclaimer in the
14184610Salfred *    documentation and/or other materials provided with the distribution.
15184610Salfred *
16184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26188746Sthompsa * SUCH DAMAGE.
27188942Sthompsa */
28188942Sthompsa
29188942Sthompsa#include <sys/stdint.h>
30188942Sthompsa#include <sys/stddef.h>
31184610Salfred#include <sys/param.h>
32184610Salfred#include <sys/queue.h>
33184610Salfred#include <sys/types.h>
34188942Sthompsa#include <sys/systm.h>
35188942Sthompsa#include <sys/kernel.h>
36188942Sthompsa#include <sys/bus.h>
37188942Sthompsa#include <sys/module.h>
38188942Sthompsa#include <sys/lock.h>
39188942Sthompsa#include <sys/mutex.h>
40184610Salfred#include <sys/condvar.h>
41188942Sthompsa#include <sys/sysctl.h>
42184610Salfred#include <sys/sx.h>
43184610Salfred#include <sys/unistd.h>
44184610Salfred#include <sys/callout.h>
45184610Salfred#include <sys/malloc.h>
46184610Salfred#include <sys/priv.h>
47184610Salfred#include <sys/rman.h>
48184610Salfred
49184610Salfred#include <dev/usb/usb.h>
50184610Salfred#include <dev/usb/usbdi.h>
51184610Salfred
52184610Salfred#include <dev/usb/usb_core.h>
53184610Salfred#include <dev/usb/usb_busdma.h>
54184610Salfred#include <dev/usb/usb_process.h>
55184610Salfred#include <dev/usb/usb_util.h>
56184610Salfred
57184610Salfred#include <dev/usb/usb_controller.h>
58184610Salfred#include <dev/usb/usb_bus.h>
59184610Salfred
60184610Salfred#include <contrib/octeon-sdk/cvmx.h>
61184610Salfred#include <contrib/octeon-sdk/cvmx-interrupt.h>
62184610Salfred#include <contrib/octeon-sdk/cvmx-usb.h>
63184610Salfred
64187259Sthompsa#include <mips/cavium/usb/octusb.h>
65187259Sthompsa
66187259Sthompsa#define	MEM_RID	0
67188413Sthompsa
68187259Sthompsastatic device_identify_t octusb_octeon_identify;
69187259Sthompsastatic device_probe_t octusb_octeon_probe;
70184610Salfredstatic device_attach_t octusb_octeon_attach;
71192984Sthompsastatic device_detach_t octusb_octeon_detach;
72192984Sthompsastatic device_shutdown_t octusb_octeon_shutdown;
73184610Salfred
74192984Sthompsastruct octusb_octeon_softc {
75192984Sthompsa	struct octusb_softc sc_dci;	/* must be first */
76189265Sthompsa};
77184610Salfred
78184610Salfredstatic void
79184610Salfredoctusb_octeon_identify(driver_t *drv, device_t parent)
80184610Salfred{
81184610Salfred	if (octeon_has_feature(OCTEON_FEATURE_USB))
82184610Salfred		BUS_ADD_CHILD(parent, 0, "octusb", 0);
83184610Salfred}
84184610Salfred
85184610Salfredstatic int
86184610Salfredoctusb_octeon_probe(device_t dev)
87184610Salfred{
88184610Salfred	device_set_desc(dev, "Cavium Octeon USB controller");
89184610Salfred	return (0);
90184610Salfred}
91192984Sthompsa
92192984Sthompsastatic int
93192984Sthompsaoctusb_octeon_attach(device_t dev)
94192984Sthompsa{
95192984Sthompsa	struct octusb_octeon_softc *sc = device_get_softc(dev);
96192984Sthompsa	int err;
97192984Sthompsa	int rid;
98185948Sthompsa
99192984Sthompsa	/* setup controller interface softc */
100185948Sthompsa
101184610Salfred	/* initialise some bus fields */
102192984Sthompsa	sc->sc_dci.sc_bus.parent = dev;
103184610Salfred	sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices;
104184610Salfred	sc->sc_dci.sc_bus.devices_max = OCTUSB_MAX_DEVICES;
105187259Sthompsa
106184610Salfred	/* get all DMA memory */
107184610Salfred	if (usb_bus_mem_alloc_all(&sc->sc_dci.sc_bus,
108184610Salfred	    USB_GET_DMA_TAG(dev), NULL)) {
109190734Sthompsa		return (ENOMEM);
110190734Sthompsa	}
111190734Sthompsa	rid = 0;
112184610Salfred	sc->sc_dci.sc_irq_res =
113184610Salfred	    bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
114187259Sthompsa			       CVMX_IRQ_USB, CVMX_IRQ_USB, 1, RF_ACTIVE);
115184610Salfred	if (!(sc->sc_dci.sc_irq_res)) {
116184610Salfred		goto error;
117184610Salfred	}
118190734Sthompsa
119190734Sthompsa	sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
120190734Sthompsa	if (!(sc->sc_dci.sc_bus.bdev)) {
121184610Salfred		goto error;
122184610Salfred	}
123184610Salfred	device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus);
124192984Sthompsa
125184610Salfred#if (__FreeBSD_version >= 700031)
126184610Salfred	err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
127184610Salfred	    NULL, (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
128184610Salfred#else
129184610Salfred	err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
130184610Salfred	    (driver_intr_t *)octusb_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
131184610Salfred#endif
132184610Salfred	if (err) {
133184610Salfred		sc->sc_dci.sc_intr_hdl = NULL;
134184610Salfred		goto error;
135184610Salfred	}
136184610Salfred	err = octusb_init(&sc->sc_dci);
137184610Salfred	if (!err) {
138184610Salfred		err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev);
139184610Salfred	}
140184610Salfred	if (err) {
141184610Salfred		goto error;
142184610Salfred	}
143184610Salfred	return (0);
144184610Salfred
145184610Salfrederror:
146184610Salfred	octusb_octeon_detach(dev);
147184610Salfred	return (ENXIO);
148184610Salfred}
149184610Salfred
150184610Salfredstatic int
151189275Sthompsaoctusb_octeon_detach(device_t dev)
152188942Sthompsa{
153188942Sthompsa	struct octusb_octeon_softc *sc = device_get_softc(dev);
154184610Salfred	device_t bdev;
155192984Sthompsa	int err;
156184610Salfred
157184610Salfred	if (sc->sc_dci.sc_bus.bdev) {
158184610Salfred		bdev = sc->sc_dci.sc_bus.bdev;
159184610Salfred		device_detach(bdev);
160184610Salfred		device_delete_child(dev, bdev);
161184610Salfred	}
162192984Sthompsa	/* during module unload there are lots of children leftover */
163184610Salfred	device_delete_children(dev);
164192499Sthompsa
165184610Salfred	if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) {
166184610Salfred		/*
167184610Salfred		 * only call octusb_octeon_uninit() after octusb_octeon_init()
168184610Salfred		 */
169184610Salfred		octusb_uninit(&sc->sc_dci);
170184610Salfred
171184610Salfred		err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res,
172184610Salfred		    sc->sc_dci.sc_intr_hdl);
173184610Salfred		sc->sc_dci.sc_intr_hdl = NULL;
174184610Salfred	}
175184610Salfred	if (sc->sc_dci.sc_irq_res) {
176184610Salfred		bus_release_resource(dev, SYS_RES_IRQ, 0,
177184610Salfred		    sc->sc_dci.sc_irq_res);
178184610Salfred		sc->sc_dci.sc_irq_res = NULL;
179192984Sthompsa	}
180184610Salfred	usb_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL);
181184610Salfred
182184610Salfred	return (0);
183184610Salfred}
184184610Salfred
185189265Sthompsastatic int
186184610Salfredoctusb_octeon_shutdown(device_t dev)
187184610Salfred{
188184610Salfred	struct octusb_octeon_softc *sc = device_get_softc(dev);
189184610Salfred	int err;
190184610Salfred
191184610Salfred	err = bus_generic_shutdown(dev);
192189265Sthompsa	if (err)
193184610Salfred		return (err);
194184610Salfred
195184610Salfred	octusb_uninit(&sc->sc_dci);
196184610Salfred
197184610Salfred	return (0);
198184610Salfred}
199184610Salfred
200189265Sthompsastatic device_method_t octusb_octeon_methods[] = {
201188413Sthompsa	/* Device interface */
202188413Sthompsa	DEVMETHOD(device_identify, octusb_octeon_identify),
203189265Sthompsa	DEVMETHOD(device_probe, octusb_octeon_probe),
204184610Salfred	DEVMETHOD(device_attach, octusb_octeon_attach),
205184610Salfred	DEVMETHOD(device_detach, octusb_octeon_detach),
206189265Sthompsa	DEVMETHOD(device_shutdown, octusb_octeon_shutdown),
207184610Salfred
208184610Salfred	DEVMETHOD_END
209184610Salfred};
210184610Salfred
211184610Salfredstatic driver_t octusb_octeon_driver = {
212184610Salfred	"octusb",
213184610Salfred	octusb_octeon_methods,
214184610Salfred	sizeof(struct octusb_octeon_softc),
215184610Salfred};
216184610Salfred
217184610Salfredstatic devclass_t octusb_octeon_devclass;
218184610Salfred
219184610SalfredDRIVER_MODULE(octusb, ciu, octusb_octeon_driver, octusb_octeon_devclass, 0, 0);
220184610Salfred