musb_otg_atmelarm.c revision 194677
1218885Sdim/* $FreeBSD: head/sys/dev/usb/controller/musb_otg_atmelarm.c 194677 2009-06-23 02:19:59Z thompsa $ */
2218885Sdim/*-
3218885Sdim * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4218885Sdim *
5218885Sdim * Redistribution and use in source and binary forms, with or without
6218885Sdim * modification, are permitted provided that the following conditions
7218885Sdim * are met:
8218885Sdim * 1. Redistributions of source code must retain the above copyright
9218885Sdim *    notice, this list of conditions and the following disclaimer.
10218885Sdim * 2. Redistributions in binary form must reproduce the above copyright
11218885Sdim *    notice, this list of conditions and the following disclaimer in the
12218885Sdim *    documentation and/or other materials provided with the distribution.
13218885Sdim *
14218885Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15218885Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16218885Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17218885Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18218885Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19218885Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20218885Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21218885Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22218885Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23218885Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24218885Sdim * SUCH DAMAGE.
25218885Sdim */
26218885Sdim
27218885Sdim#include <sys/stdint.h>
28218885Sdim#include <sys/stddef.h>
29218885Sdim#include <sys/param.h>
30218885Sdim#include <sys/queue.h>
31218885Sdim#include <sys/types.h>
32218885Sdim#include <sys/systm.h>
33218885Sdim#include <sys/kernel.h>
34218885Sdim#include <sys/bus.h>
35218885Sdim#include <sys/linker_set.h>
36218885Sdim#include <sys/module.h>
37218885Sdim#include <sys/lock.h>
38218885Sdim#include <sys/mutex.h>
39218885Sdim#include <sys/condvar.h>
40218885Sdim#include <sys/sysctl.h>
41218885Sdim#include <sys/sx.h>
42218885Sdim#include <sys/unistd.h>
43218885Sdim#include <sys/callout.h>
44218885Sdim#include <sys/malloc.h>
45218885Sdim#include <sys/priv.h>
46218885Sdim
47218885Sdim#include <dev/usb/usb.h>
48218885Sdim#include <dev/usb/usbdi.h>
49218885Sdim
50218885Sdim#include <dev/usb/usb_core.h>
51218885Sdim#include <dev/usb/usb_busdma.h>
52218885Sdim#include <dev/usb/usb_process.h>
53218885Sdim#include <dev/usb/usb_util.h>
54218885Sdim
55218885Sdim#include <dev/usb/usb_controller.h>
56218885Sdim#include <dev/usb/usb_bus.h>
57218885Sdim#include <dev/usb/controller/musb_otg.h>
58218885Sdim
59218885Sdim#include <sys/rman.h>
60218885Sdim
61218885Sdimstatic device_probe_t musbotg_probe;
62218885Sdimstatic device_attach_t musbotg_attach;
63218885Sdimstatic device_detach_t musbotg_detach;
64218885Sdimstatic device_shutdown_t musbotg_shutdown;
65218885Sdim
66218885Sdimstruct musbotg_super_softc {
67218885Sdim	struct musbotg_softc sc_otg;	/* must be first */
68218885Sdim};
69218885Sdim
70218885Sdimstatic void
71218885Sdimmusbotg_vbus_poll(struct musbotg_super_softc *sc)
72218885Sdim{
73218885Sdim	uint8_t vbus_val = 1;		/* fake VBUS on - TODO */
74218885Sdim
75218885Sdim	/* just forward it */
76218885Sdim	musbotg_vbus_interrupt(&sc->sc_otg, vbus_val);
77218885Sdim}
78218885Sdim
79218885Sdimstatic void
80218885Sdimmusbotg_clocks_on(void *arg)
81218885Sdim{
82218885Sdim#if 0
83218885Sdim	struct musbotg_super_softc *sc = arg;
84218885Sdim
85218885Sdim#endif
86218885Sdim}
87218885Sdim
88218885Sdimstatic void
89218885Sdimmusbotg_clocks_off(void *arg)
90218885Sdim{
91218885Sdim#if 0
92218885Sdim	struct musbotg_super_softc *sc = arg;
93218885Sdim
94218885Sdim#endif
95218885Sdim}
96218885Sdim
97218885Sdimstatic int
98218885Sdimmusbotg_probe(device_t dev)
99218885Sdim{
100218885Sdim	device_set_desc(dev, "MUSB OTG integrated USB controller");
101218885Sdim	return (0);
102218885Sdim}
103218885Sdim
104218885Sdimstatic int
105218885Sdimmusbotg_attach(device_t dev)
106218885Sdim{
107218885Sdim	struct musbotg_super_softc *sc = device_get_softc(dev);
108218885Sdim	int err;
109221345Sdim	int rid;
110218885Sdim
111218885Sdim	/* setup MUSB OTG USB controller interface softc */
112221345Sdim	sc->sc_otg.sc_clocks_on = &musbotg_clocks_on;
113218885Sdim	sc->sc_otg.sc_clocks_off = &musbotg_clocks_off;
114218885Sdim	sc->sc_otg.sc_clocks_arg = sc;
115221345Sdim
116218885Sdim	/* initialise some bus fields */
117218885Sdim	sc->sc_otg.sc_bus.parent = dev;
118218885Sdim	sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices;
119218885Sdim	sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES;
120218885Sdim
121218885Sdim	/* get all DMA memory */
122218885Sdim	if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus,
123218885Sdim	    USB_GET_DMA_TAG(dev), NULL)) {
124218885Sdim		return (ENOMEM);
125218885Sdim	}
126218885Sdim	rid = 0;
127218885Sdim	sc->sc_otg.sc_io_res =
128218885Sdim	    bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
129218885Sdim
130218885Sdim	if (!(sc->sc_otg.sc_io_res)) {
131218885Sdim		err = ENOMEM;
132218885Sdim		goto error;
133218885Sdim	}
134218885Sdim	sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res);
135218885Sdim	sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res);
136218885Sdim	sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res);
137218885Sdim
138218885Sdim	rid = 0;
139218885Sdim	sc->sc_otg.sc_irq_res =
140218885Sdim	    bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
141218885Sdim	if (!(sc->sc_otg.sc_irq_res)) {
142218885Sdim		goto error;
143221345Sdim	}
144221345Sdim	sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1);
145218885Sdim	if (!(sc->sc_otg.sc_bus.bdev)) {
146218885Sdim		goto error;
147218885Sdim	}
148221345Sdim	device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus);
149221345Sdim
150218885Sdim#if (__FreeBSD_version >= 700031)
151218885Sdim	err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
152218885Sdim	    NULL, (driver_intr_t *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
153218885Sdim#else
154218885Sdim	err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
155218885Sdim	    (driver_intr_t *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
156218885Sdim#endif
157218885Sdim	if (err) {
158218885Sdim		sc->sc_otg.sc_intr_hdl = NULL;
159218885Sdim		goto error;
160218885Sdim	}
161218885Sdim	err = musbotg_init(&sc->sc_otg);
162218885Sdim	if (!err) {
163218885Sdim		err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev);
164218885Sdim	}
165218885Sdim	if (err) {
166218885Sdim		goto error;
167218885Sdim	} else {
168218885Sdim		/* poll VBUS one time */
169218885Sdim		musbotg_vbus_poll(sc);
170218885Sdim	}
171218885Sdim	return (0);
172218885Sdim
173218885Sdimerror:
174218885Sdim	musbotg_detach(dev);
175218885Sdim	return (ENXIO);
176218885Sdim}
177218885Sdim
178218885Sdimstatic int
179218885Sdimmusbotg_detach(device_t dev)
180218885Sdim{
181218885Sdim	struct musbotg_super_softc *sc = device_get_softc(dev);
182218885Sdim	device_t bdev;
183218885Sdim	int err;
184218885Sdim
185218885Sdim	if (sc->sc_otg.sc_bus.bdev) {
186218885Sdim		bdev = sc->sc_otg.sc_bus.bdev;
187218885Sdim		device_detach(bdev);
188218885Sdim		device_delete_child(dev, bdev);
189218885Sdim	}
190218885Sdim	/* during module unload there are lots of children leftover */
191218885Sdim	device_delete_all_children(dev);
192218885Sdim
193218885Sdim	if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) {
194218885Sdim		/*
195218885Sdim		 * only call musbotg_uninit() after musbotg_init()
196218885Sdim		 */
197218885Sdim		musbotg_uninit(&sc->sc_otg);
198218885Sdim
199218885Sdim		err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res,
200221345Sdim		    sc->sc_otg.sc_intr_hdl);
201221345Sdim		sc->sc_otg.sc_intr_hdl = NULL;
202218885Sdim	}
203218885Sdim	/* free IRQ channel, if any */
204218885Sdim	if (sc->sc_otg.sc_irq_res) {
205218885Sdim		bus_release_resource(dev, SYS_RES_IRQ, 0,
206218885Sdim		    sc->sc_otg.sc_irq_res);
207218885Sdim		sc->sc_otg.sc_irq_res = NULL;
208218885Sdim	}
209218885Sdim	/* free memory resource, if any */
210218885Sdim	if (sc->sc_otg.sc_io_res) {
211218885Sdim		bus_release_resource(dev, SYS_RES_MEMORY, 0,
212218885Sdim		    sc->sc_otg.sc_io_res);
213218885Sdim		sc->sc_otg.sc_io_res = NULL;
214218885Sdim	}
215218885Sdim	usb_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL);
216218885Sdim
217218885Sdim	return (0);
218218885Sdim}
219218885Sdim
220219077Sdimstatic int
221219077Sdimmusbotg_shutdown(device_t dev)
222219077Sdim{
223219077Sdim	struct musbotg_super_softc *sc = device_get_softc(dev);
224219077Sdim	int err;
225219077Sdim
226219077Sdim	err = bus_generic_shutdown(dev);
227218885Sdim	if (err)
228218885Sdim		return (err);
229218885Sdim
230218885Sdim	musbotg_uninit(&sc->sc_otg);
231221345Sdim
232221345Sdim	return (0);
233218885Sdim}
234218885Sdim
235218885Sdimstatic device_method_t musbotg_methods[] = {
236218885Sdim	/* Device interface */
237218885Sdim	DEVMETHOD(device_probe, musbotg_probe),
238218885Sdim	DEVMETHOD(device_attach, musbotg_attach),
239218885Sdim	DEVMETHOD(device_detach, musbotg_detach),
240218885Sdim	DEVMETHOD(device_shutdown, musbotg_shutdown),
241218885Sdim
242218885Sdim	/* Bus interface */
243218885Sdim	DEVMETHOD(bus_print_child, bus_generic_print_child),
244218885Sdim
245218885Sdim	{0, 0}
246218885Sdim};
247218885Sdim
248218885Sdimstatic driver_t musbotg_driver = {
249218885Sdim	"musbotg",
250218885Sdim	musbotg_methods,
251218885Sdim	sizeof(struct musbotg_super_softc),
252218885Sdim};
253218885Sdim
254218885Sdimstatic devclass_t musbotg_devclass;
255218885Sdim
256218885SdimDRIVER_MODULE(musbotg, atmelarm, musbotg_driver, musbotg_devclass, 0, 0);
257218885SdimMODULE_DEPEND(musbotg, usb, 1, 1, 1);
258218885Sdim