1213379Shselasky/*-
2213379Shselasky * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3213379Shselasky *
4213379Shselasky * Redistribution and use in source and binary forms, with or without
5213379Shselasky * modification, are permitted provided that the following conditions
6213379Shselasky * are met:
7213379Shselasky * 1. Redistributions of source code must retain the above copyright
8213379Shselasky *    notice, this list of conditions and the following disclaimer.
9213379Shselasky * 2. Redistributions in binary form must reproduce the above copyright
10213379Shselasky *    notice, this list of conditions and the following disclaimer in the
11213379Shselasky *    documentation and/or other materials provided with the distribution.
12213379Shselasky *
13213379Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14213379Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15213379Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16213379Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17213379Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18213379Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19213379Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20213379Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21213379Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22213379Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23213379Shselasky * SUCH DAMAGE.
24213379Shselasky */
25213379Shselasky
26213379Shselasky#include <sys/cdefs.h>
27213379Shselasky__FBSDID("$FreeBSD$");
28213379Shselasky
29213379Shselasky#include <sys/stdint.h>
30213379Shselasky#include <sys/stddef.h>
31213379Shselasky#include <sys/param.h>
32213379Shselasky#include <sys/queue.h>
33213379Shselasky#include <sys/types.h>
34213379Shselasky#include <sys/systm.h>
35213379Shselasky#include <sys/kernel.h>
36213379Shselasky#include <sys/bus.h>
37213379Shselasky#include <sys/module.h>
38213379Shselasky#include <sys/lock.h>
39213379Shselasky#include <sys/mutex.h>
40213379Shselasky#include <sys/condvar.h>
41213379Shselasky#include <sys/sysctl.h>
42213379Shselasky#include <sys/sx.h>
43213379Shselasky#include <sys/unistd.h>
44213379Shselasky#include <sys/callout.h>
45213379Shselasky#include <sys/malloc.h>
46213379Shselasky#include <sys/priv.h>
47213379Shselasky
48213379Shselasky#include <dev/usb/usb.h>
49213379Shselasky#include <dev/usb/usbdi.h>
50213379Shselasky
51213379Shselasky#include <dev/usb/usb_core.h>
52213379Shselasky#include <dev/usb/usb_busdma.h>
53213379Shselasky#include <dev/usb/usb_process.h>
54213379Shselasky#include <dev/usb/usb_util.h>
55213379Shselasky
56213379Shselasky#include <dev/usb/usb_controller.h>
57213379Shselasky#include <dev/usb/usb_bus.h>
58213379Shselasky#include <dev/usb/usb_pci.h>
59213379Shselasky#include <dev/usb/controller/xhci.h>
60213379Shselasky#include <dev/usb/controller/xhcireg.h>
61228483Shselasky#include "usb_if.h"
62213379Shselasky
63213379Shselaskystatic device_probe_t xhci_pci_probe;
64213379Shselaskystatic device_attach_t xhci_pci_attach;
65213379Shselaskystatic device_detach_t xhci_pci_detach;
66228483Shselaskystatic usb_take_controller_t xhci_pci_take_controller;
67213379Shselasky
68213379Shselaskystatic device_method_t xhci_device_methods[] = {
69213379Shselasky	/* device interface */
70213379Shselasky	DEVMETHOD(device_probe, xhci_pci_probe),
71213379Shselasky	DEVMETHOD(device_attach, xhci_pci_attach),
72213379Shselasky	DEVMETHOD(device_detach, xhci_pci_detach),
73228483Shselasky	DEVMETHOD(device_suspend, bus_generic_suspend),
74228483Shselasky	DEVMETHOD(device_resume, bus_generic_resume),
75228483Shselasky	DEVMETHOD(device_shutdown, bus_generic_shutdown),
76228483Shselasky	DEVMETHOD(usb_take_controller, xhci_pci_take_controller),
77213379Shselasky
78227843Smarius	DEVMETHOD_END
79213379Shselasky};
80213379Shselasky
81213379Shselaskystatic driver_t xhci_driver = {
82213379Shselasky	.name = "xhci",
83213379Shselasky	.methods = xhci_device_methods,
84213379Shselasky	.size = sizeof(struct xhci_softc),
85213379Shselasky};
86213379Shselasky
87213379Shselaskystatic devclass_t xhci_devclass;
88213379Shselasky
89213379ShselaskyDRIVER_MODULE(xhci, pci, xhci_driver, xhci_devclass, 0, 0);
90213379ShselaskyMODULE_DEPEND(xhci, usb, 1, 1, 1);
91213379Shselasky
92213379Shselasky
93213379Shselaskystatic const char *
94213379Shselaskyxhci_pci_match(device_t self)
95213379Shselasky{
96238015Smav	uint32_t device_id = pci_get_devid(self);
97238015Smav
98238015Smav	switch (device_id) {
99238015Smav	case 0x01941033:
100238015Smav		return ("NEC uPD720200 USB 3.0 controller");
101238015Smav
102249336Smav	case 0x10421b21:
103249336Smav		return ("ASMedia ASM1042 USB 3.0 controller");
104249336Smav
105238015Smav	case 0x1e318086:
106238015Smav		return ("Intel Panther Point USB 3.0 controller");
107238551Smav	case 0x8c318086:
108238551Smav		return ("Intel Lynx Point USB 3.0 controller");
109238015Smav
110238015Smav	default:
111238015Smav		break;
112238015Smav	}
113238015Smav
114213379Shselasky	if ((pci_get_class(self) == PCIC_SERIALBUS)
115213379Shselasky	    && (pci_get_subclass(self) == PCIS_SERIALBUS_USB)
116222018Sru	    && (pci_get_progif(self) == PCIP_SERIALBUS_USB_XHCI)) {
117213379Shselasky		return ("XHCI (generic) USB 3.0 controller");
118213379Shselasky	}
119213379Shselasky	return (NULL);			/* dunno */
120213379Shselasky}
121213379Shselasky
122213379Shselaskystatic int
123213379Shselaskyxhci_pci_probe(device_t self)
124213379Shselasky{
125213379Shselasky	const char *desc = xhci_pci_match(self);
126213379Shselasky
127213379Shselasky	if (desc) {
128213379Shselasky		device_set_desc(self, desc);
129213379Shselasky		return (0);
130213379Shselasky	} else {
131213379Shselasky		return (ENXIO);
132213379Shselasky	}
133213379Shselasky}
134213379Shselasky
135253398Skibstatic int xhci_use_msi = 1;
136253398SkibTUNABLE_INT("hw.usb.xhci.msi", &xhci_use_msi);
137253398Skib
138251499Shselaskystatic void
139251499Shselaskyxhci_interrupt_poll(void *_sc)
140251499Shselasky{
141251499Shselasky	struct xhci_softc *sc = _sc;
142251499Shselasky	USB_BUS_UNLOCK(&sc->sc_bus);
143251499Shselasky	xhci_interrupt(sc);
144251499Shselasky	USB_BUS_LOCK(&sc->sc_bus);
145251499Shselasky	usb_callout_reset(&sc->sc_callout, 1, (void *)&xhci_interrupt_poll, sc);
146251499Shselasky}
147251499Shselasky
148213379Shselaskystatic int
149255768Shselaskyxhci_pci_port_route(device_t self, uint32_t set, uint32_t clear)
150255768Shselasky{
151255768Shselasky	uint32_t temp;
152255768Shselasky
153255768Shselasky	temp = pci_read_config(self, PCI_XHCI_INTEL_USB3_PSSEN, 4) |
154255768Shselasky	    pci_read_config(self, PCI_XHCI_INTEL_XUSB2PR, 4);
155255768Shselasky
156255768Shselasky	temp |= set;
157255768Shselasky	temp &= ~clear;
158255768Shselasky
159255768Shselasky	pci_write_config(self, PCI_XHCI_INTEL_USB3_PSSEN, temp, 4);
160255768Shselasky	pci_write_config(self, PCI_XHCI_INTEL_XUSB2PR, temp, 4);
161255768Shselasky
162255768Shselasky	device_printf(self, "Port routing mask set to 0x%08x\n", temp);
163255768Shselasky
164255768Shselasky	return (0);
165255768Shselasky}
166255768Shselasky
167255768Shselaskystatic int
168213379Shselaskyxhci_pci_attach(device_t self)
169213379Shselasky{
170213379Shselasky	struct xhci_softc *sc = device_get_softc(self);
171253094Skib	int count, err, rid;
172213379Shselasky
173213379Shselasky	/* XXX check for 64-bit capability */
174213379Shselasky
175213379Shselasky	if (xhci_init(sc, self)) {
176213379Shselasky		device_printf(self, "Could not initialize softc\n");
177213379Shselasky		goto error;
178213379Shselasky	}
179213379Shselasky
180213379Shselasky	pci_enable_busmaster(self);
181213379Shselasky
182213379Shselasky	rid = PCI_XHCI_CBMEM;
183213379Shselasky	sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
184213379Shselasky	    RF_ACTIVE);
185213379Shselasky	if (!sc->sc_io_res) {
186213379Shselasky		device_printf(self, "Could not map memory\n");
187213379Shselasky		goto error;
188213379Shselasky	}
189213379Shselasky	sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
190213379Shselasky	sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
191213379Shselasky	sc->sc_io_size = rman_get_size(sc->sc_io_res);
192213379Shselasky
193251499Shselasky	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0);
194251499Shselasky
195253094Skib	sc->sc_irq_rid = 0;
196253398Skib	if (xhci_use_msi) {
197253398Skib		count = pci_msi_count(self);
198253398Skib		if (count >= 1) {
199253398Skib			count = 1;
200253398Skib			if (pci_alloc_msi(self, &count) == 0) {
201253398Skib				if (bootverbose)
202253398Skib					device_printf(self, "MSI enabled\n");
203253398Skib				sc->sc_irq_rid = 1;
204253398Skib			}
205253094Skib		}
206253094Skib	}
207253094Skib	sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ,
208253094Skib	    &sc->sc_irq_rid, RF_SHAREABLE | RF_ACTIVE);
209213379Shselasky	if (sc->sc_irq_res == NULL) {
210213379Shselasky		device_printf(self, "Could not allocate IRQ\n");
211255768Shselasky		/* goto error; FALLTHROUGH - use polling */
212213379Shselasky	}
213213379Shselasky	sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
214213379Shselasky	if (sc->sc_bus.bdev == NULL) {
215213379Shselasky		device_printf(self, "Could not add USB device\n");
216213379Shselasky		goto error;
217213379Shselasky	}
218213379Shselasky	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
219213379Shselasky
220213379Shselasky	sprintf(sc->sc_vendor, "0x%04x", pci_get_vendor(self));
221213379Shselasky
222251499Shselasky	if (sc->sc_irq_res != NULL) {
223251499Shselasky		err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
224251499Shselasky		    NULL, (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl);
225251499Shselasky		if (err != 0) {
226251499Shselasky			device_printf(self, "Could not setup IRQ, err=%d\n", err);
227251499Shselasky			sc->sc_intr_hdl = NULL;
228251499Shselasky		}
229213379Shselasky	}
230251499Shselasky	if (sc->sc_irq_res == NULL || sc->sc_intr_hdl == NULL ||
231251499Shselasky	    xhci_use_polling() != 0) {
232251499Shselasky		device_printf(self, "Interrupt polling at %dHz\n", hz);
233251499Shselasky		USB_BUS_LOCK(&sc->sc_bus);
234251499Shselasky		xhci_interrupt_poll(sc);
235251499Shselasky		USB_BUS_UNLOCK(&sc->sc_bus);
236251499Shselasky	}
237251499Shselasky
238255768Shselasky	/* On Intel chipsets reroute ports from EHCI to XHCI controller. */
239255768Shselasky	switch (pci_get_devid(self)) {
240255768Shselasky	case 0x1e318086:	/* Panther Point */
241255768Shselasky	case 0x8c318086:	/* Lynx Point */
242255768Shselasky		sc->sc_port_route = &xhci_pci_port_route;
243255768Shselasky		break;
244255768Shselasky	default:
245255768Shselasky		break;
246255768Shselasky	}
247255768Shselasky
248228483Shselasky	xhci_pci_take_controller(self);
249213379Shselasky
250213379Shselasky	err = xhci_halt_controller(sc);
251213379Shselasky
252213379Shselasky	if (err == 0)
253213379Shselasky		err = xhci_start_controller(sc);
254213379Shselasky
255213379Shselasky	if (err == 0)
256213379Shselasky		err = device_probe_and_attach(sc->sc_bus.bdev);
257213379Shselasky
258213379Shselasky	if (err) {
259213379Shselasky		device_printf(self, "XHCI halt/start/probe failed err=%d\n", err);
260213379Shselasky		goto error;
261213379Shselasky	}
262213379Shselasky	return (0);
263213379Shselasky
264213379Shselaskyerror:
265213379Shselasky	xhci_pci_detach(self);
266213379Shselasky	return (ENXIO);
267213379Shselasky}
268213379Shselasky
269213379Shselaskystatic int
270213379Shselaskyxhci_pci_detach(device_t self)
271213379Shselasky{
272213379Shselasky	struct xhci_softc *sc = device_get_softc(self);
273213379Shselasky	device_t bdev;
274213379Shselasky
275213379Shselasky	if (sc->sc_bus.bdev != NULL) {
276213379Shselasky		bdev = sc->sc_bus.bdev;
277213379Shselasky		device_detach(bdev);
278213379Shselasky		device_delete_child(self, bdev);
279213379Shselasky	}
280213379Shselasky	/* during module unload there are lots of children leftover */
281227849Shselasky	device_delete_children(self);
282213379Shselasky
283251499Shselasky	if (sc->sc_io_res) {
284251499Shselasky		usb_callout_drain(&sc->sc_callout);
285251499Shselasky		xhci_halt_controller(sc);
286251499Shselasky	}
287251499Shselasky
288213379Shselasky	pci_disable_busmaster(self);
289213379Shselasky
290213379Shselasky	if (sc->sc_irq_res && sc->sc_intr_hdl) {
291213379Shselasky		bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
292213379Shselasky		sc->sc_intr_hdl = NULL;
293213379Shselasky	}
294213379Shselasky	if (sc->sc_irq_res) {
295253094Skib		if (sc->sc_irq_rid == 1)
296253094Skib			pci_release_msi(self);
297253094Skib		bus_release_resource(self, SYS_RES_IRQ, sc->sc_irq_rid,
298253094Skib		    sc->sc_irq_res);
299213379Shselasky		sc->sc_irq_res = NULL;
300213379Shselasky	}
301213379Shselasky	if (sc->sc_io_res) {
302213379Shselasky		bus_release_resource(self, SYS_RES_MEMORY, PCI_XHCI_CBMEM,
303213379Shselasky		    sc->sc_io_res);
304213379Shselasky		sc->sc_io_res = NULL;
305213379Shselasky	}
306213379Shselasky
307213379Shselasky	xhci_uninit(sc);
308213379Shselasky
309213379Shselasky	return (0);
310213379Shselasky}
311213379Shselasky
312228483Shselaskystatic int
313228483Shselaskyxhci_pci_take_controller(device_t self)
314213379Shselasky{
315213379Shselasky	struct xhci_softc *sc = device_get_softc(self);
316213379Shselasky	uint32_t cparams;
317213379Shselasky	uint32_t eecp;
318213379Shselasky	uint32_t eec;
319213379Shselasky	uint16_t to;
320213379Shselasky	uint8_t bios_sem;
321213379Shselasky
322213379Shselasky	cparams = XREAD4(sc, capa, XHCI_HCSPARAMS0);
323213379Shselasky
324213379Shselasky	eec = -1;
325213379Shselasky
326213379Shselasky	/* Synchronise with the BIOS if it owns the controller. */
327213379Shselasky	for (eecp = XHCI_HCS0_XECP(cparams) << 2; eecp != 0 && XHCI_XECP_NEXT(eec);
328213379Shselasky	    eecp += XHCI_XECP_NEXT(eec) << 2) {
329213379Shselasky		eec = XREAD4(sc, capa, eecp);
330213379Shselasky
331213379Shselasky		if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY)
332213379Shselasky			continue;
333213379Shselasky		bios_sem = XREAD1(sc, capa, eecp +
334213379Shselasky		    XHCI_XECP_BIOS_SEM);
335213379Shselasky		if (bios_sem == 0)
336213379Shselasky			continue;
337213379Shselasky		device_printf(sc->sc_bus.bdev, "waiting for BIOS "
338213379Shselasky		    "to give up control\n");
339213379Shselasky		XWRITE1(sc, capa, eecp +
340213379Shselasky		    XHCI_XECP_OS_SEM, 1);
341213379Shselasky		to = 500;
342213379Shselasky		while (1) {
343213379Shselasky			bios_sem = XREAD1(sc, capa, eecp +
344213379Shselasky			    XHCI_XECP_BIOS_SEM);
345213379Shselasky			if (bios_sem == 0)
346213379Shselasky				break;
347213379Shselasky
348213379Shselasky			if (--to == 0) {
349213379Shselasky				device_printf(sc->sc_bus.bdev,
350213379Shselasky				    "timed out waiting for BIOS\n");
351213379Shselasky				break;
352213379Shselasky			}
353213379Shselasky			usb_pause_mtx(NULL, hz / 100);	/* wait 10ms */
354213379Shselasky		}
355213379Shselasky	}
356228483Shselasky	return (0);
357213379Shselasky}
358