xhci_pci.c revision 213379
1/*-
2 * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/sys/dev/usb/controller/xhci_pci.c 213379 2010-10-03 08:12:17Z hselasky $");
28
29#include <sys/stdint.h>
30#include <sys/stddef.h>
31#include <sys/param.h>
32#include <sys/queue.h>
33#include <sys/types.h>
34#include <sys/systm.h>
35#include <sys/kernel.h>
36#include <sys/bus.h>
37#include <sys/linker_set.h>
38#include <sys/module.h>
39#include <sys/lock.h>
40#include <sys/mutex.h>
41#include <sys/condvar.h>
42#include <sys/sysctl.h>
43#include <sys/sx.h>
44#include <sys/unistd.h>
45#include <sys/callout.h>
46#include <sys/malloc.h>
47#include <sys/priv.h>
48
49#include <dev/usb/usb.h>
50#include <dev/usb/usbdi.h>
51
52#include <dev/usb/usb_core.h>
53#include <dev/usb/usb_busdma.h>
54#include <dev/usb/usb_process.h>
55#include <dev/usb/usb_util.h>
56
57#include <dev/usb/usb_controller.h>
58#include <dev/usb/usb_bus.h>
59#include <dev/usb/usb_pci.h>
60#include <dev/usb/controller/xhci.h>
61#include <dev/usb/controller/xhcireg.h>
62
63static device_probe_t xhci_pci_probe;
64static device_attach_t xhci_pci_attach;
65static device_detach_t xhci_pci_detach;
66static device_suspend_t xhci_pci_suspend;
67static device_resume_t xhci_pci_resume;
68static device_shutdown_t xhci_pci_shutdown;
69static void xhci_pci_takecontroller(device_t);
70
71static device_method_t xhci_device_methods[] = {
72	/* device interface */
73	DEVMETHOD(device_probe, xhci_pci_probe),
74	DEVMETHOD(device_attach, xhci_pci_attach),
75	DEVMETHOD(device_detach, xhci_pci_detach),
76	DEVMETHOD(device_suspend, xhci_pci_suspend),
77	DEVMETHOD(device_resume, xhci_pci_resume),
78	DEVMETHOD(device_shutdown, xhci_pci_shutdown),
79	/* bus interface */
80	DEVMETHOD(bus_print_child, bus_generic_print_child),
81
82	{0, 0}
83};
84
85static driver_t xhci_driver = {
86	.name = "xhci",
87	.methods = xhci_device_methods,
88	.size = sizeof(struct xhci_softc),
89};
90
91static devclass_t xhci_devclass;
92
93DRIVER_MODULE(xhci, pci, xhci_driver, xhci_devclass, 0, 0);
94MODULE_DEPEND(xhci, usb, 1, 1, 1);
95
96static int
97xhci_pci_suspend(device_t self)
98{
99	struct xhci_softc *sc = device_get_softc(self);
100	int err;
101
102	err = bus_generic_suspend(self);
103	if (err)
104		return (err);
105	xhci_suspend(sc);
106	return (0);
107}
108
109static int
110xhci_pci_resume(device_t self)
111{
112	struct xhci_softc *sc = device_get_softc(self);
113
114	xhci_pci_takecontroller(self);
115	xhci_resume(sc);
116
117	bus_generic_resume(self);
118
119	return (0);
120}
121
122static int
123xhci_pci_shutdown(device_t self)
124{
125	struct xhci_softc *sc = device_get_softc(self);
126	int err;
127
128	err = bus_generic_shutdown(self);
129	if (err)
130		return (err);
131	xhci_shutdown(sc);
132
133	return (0);
134}
135
136static const char *
137xhci_pci_match(device_t self)
138{
139	if ((pci_get_class(self) == PCIC_SERIALBUS)
140	    && (pci_get_subclass(self) == PCIS_SERIALBUS_USB)
141	    && (pci_get_progif(self) == PCI_INTERFACE_XHCI)) {
142		return ("XHCI (generic) USB 3.0 controller");
143	}
144	return (NULL);			/* dunno */
145}
146
147static int
148xhci_pci_probe(device_t self)
149{
150	const char *desc = xhci_pci_match(self);
151
152	if (desc) {
153		device_set_desc(self, desc);
154		return (0);
155	} else {
156		return (ENXIO);
157	}
158}
159
160static int
161xhci_pci_attach(device_t self)
162{
163	struct xhci_softc *sc = device_get_softc(self);
164	int err;
165	int rid;
166
167	/* XXX check for 64-bit capability */
168
169	if (xhci_init(sc, self)) {
170		device_printf(self, "Could not initialize softc\n");
171		goto error;
172	}
173
174	pci_enable_busmaster(self);
175
176	rid = PCI_XHCI_CBMEM;
177	sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
178	    RF_ACTIVE);
179	if (!sc->sc_io_res) {
180		device_printf(self, "Could not map memory\n");
181		goto error;
182	}
183	sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
184	sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
185	sc->sc_io_size = rman_get_size(sc->sc_io_res);
186
187	rid = 0;
188	sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
189	    RF_SHAREABLE | RF_ACTIVE);
190	if (sc->sc_irq_res == NULL) {
191		device_printf(self, "Could not allocate IRQ\n");
192		goto error;
193	}
194	sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
195	if (sc->sc_bus.bdev == NULL) {
196		device_printf(self, "Could not add USB device\n");
197		goto error;
198	}
199	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
200
201	sprintf(sc->sc_vendor, "0x%04x", pci_get_vendor(self));
202
203#if (__FreeBSD_version >= 700031)
204	err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
205	    NULL, (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl);
206#else
207	err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
208	    (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl);
209#endif
210	if (err) {
211		device_printf(self, "Could not setup IRQ, err=%d\n", err);
212		sc->sc_intr_hdl = NULL;
213		goto error;
214	}
215	xhci_pci_takecontroller(self);
216
217	err = xhci_halt_controller(sc);
218
219	if (err == 0)
220		err = xhci_start_controller(sc);
221
222	if (err == 0)
223		err = device_probe_and_attach(sc->sc_bus.bdev);
224
225	if (err) {
226		device_printf(self, "XHCI halt/start/probe failed err=%d\n", err);
227		goto error;
228	}
229	return (0);
230
231error:
232	xhci_pci_detach(self);
233	return (ENXIO);
234}
235
236static int
237xhci_pci_detach(device_t self)
238{
239	struct xhci_softc *sc = device_get_softc(self);
240	device_t bdev;
241
242	if (sc->sc_bus.bdev != NULL) {
243		bdev = sc->sc_bus.bdev;
244		device_detach(bdev);
245		device_delete_child(self, bdev);
246	}
247	/* during module unload there are lots of children leftover */
248	device_delete_all_children(self);
249
250	pci_disable_busmaster(self);
251
252	if (sc->sc_irq_res && sc->sc_intr_hdl) {
253
254		xhci_halt_controller(sc);
255
256		bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
257		sc->sc_intr_hdl = NULL;
258	}
259	if (sc->sc_irq_res) {
260		bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
261		sc->sc_irq_res = NULL;
262	}
263	if (sc->sc_io_res) {
264		bus_release_resource(self, SYS_RES_MEMORY, PCI_XHCI_CBMEM,
265		    sc->sc_io_res);
266		sc->sc_io_res = NULL;
267	}
268
269	xhci_uninit(sc);
270
271	return (0);
272}
273
274static void
275xhci_pci_takecontroller(device_t self)
276{
277	struct xhci_softc *sc = device_get_softc(self);
278	uint32_t cparams;
279	uint32_t eecp;
280	uint32_t eec;
281	uint16_t to;
282	uint8_t bios_sem;
283
284	cparams = XREAD4(sc, capa, XHCI_HCSPARAMS0);
285
286	eec = -1;
287
288	/* Synchronise with the BIOS if it owns the controller. */
289	for (eecp = XHCI_HCS0_XECP(cparams) << 2; eecp != 0 && XHCI_XECP_NEXT(eec);
290	    eecp += XHCI_XECP_NEXT(eec) << 2) {
291		eec = XREAD4(sc, capa, eecp);
292
293		if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY)
294			continue;
295		bios_sem = XREAD1(sc, capa, eecp +
296		    XHCI_XECP_BIOS_SEM);
297		if (bios_sem == 0)
298			continue;
299		device_printf(sc->sc_bus.bdev, "waiting for BIOS "
300		    "to give up control\n");
301		XWRITE1(sc, capa, eecp +
302		    XHCI_XECP_OS_SEM, 1);
303		to = 500;
304		while (1) {
305			bios_sem = XREAD1(sc, capa, eecp +
306			    XHCI_XECP_BIOS_SEM);
307			if (bios_sem == 0)
308				break;
309
310			if (--to == 0) {
311				device_printf(sc->sc_bus.bdev,
312				    "timed out waiting for BIOS\n");
313				break;
314			}
315			usb_pause_mtx(NULL, hz / 100);	/* wait 10ms */
316		}
317	}
318}
319