1f9f848faSopenharmony_ci/*-
2f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause
3f9f848faSopenharmony_ci *
4f9f848faSopenharmony_ci * Copyright (c) 2010-2022 Hans Petter Selasky
5f9f848faSopenharmony_ci *
6f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without
7f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions
8f9f848faSopenharmony_ci * are met:
9f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright
10f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer.
11f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
12f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
13f9f848faSopenharmony_ci *    documentation and/or other materials provided with the distribution.
14f9f848faSopenharmony_ci *
15f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16f9f848faSopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17f9f848faSopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18f9f848faSopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19f9f848faSopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20f9f848faSopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21f9f848faSopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22f9f848faSopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23f9f848faSopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24f9f848faSopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25f9f848faSopenharmony_ci * SUCH DAMAGE.
26f9f848faSopenharmony_ci */
27f9f848faSopenharmony_ci
28f9f848faSopenharmony_ci#include "implementation/global_implementation.h"
29f9f848faSopenharmony_ci#include "controller/xhci.h"
30f9f848faSopenharmony_ci#include "controller/xhcireg.h"
31f9f848faSopenharmony_ci
32f9f848faSopenharmony_ci#include <linux/kernel.h>
33f9f848faSopenharmony_ci
34f9f848faSopenharmony_ci#define	PCI_XHCI_VENDORID_AMD		0x1022
35f9f848faSopenharmony_ci#define	PCI_XHCI_VENDORID_INTEL		0x8086
36f9f848faSopenharmony_ci
37f9f848faSopenharmony_cistatic device_probe_t xhci_pci_probe;
38f9f848faSopenharmony_cistatic device_attach_t xhci_pci_attach;
39f9f848faSopenharmony_cistatic device_detach_t xhci_pci_detach;
40f9f848faSopenharmony_cistatic usb_take_controller_t xhci_pci_take_controller;
41f9f848faSopenharmony_ci
42f9f848faSopenharmony_cistatic device_method_t xhci_device_methods[] = {
43f9f848faSopenharmony_ci        /* device interface */
44f9f848faSopenharmony_ci        DEVMETHOD(device_probe, xhci_pci_probe),
45f9f848faSopenharmony_ci        DEVMETHOD(device_attach, xhci_pci_attach),
46f9f848faSopenharmony_ci        DEVMETHOD(device_detach, xhci_pci_detach),
47f9f848faSopenharmony_ci        DEVMETHOD(device_suspend, bus_generic_suspend),
48f9f848faSopenharmony_ci        DEVMETHOD(device_resume, bus_generic_resume),
49f9f848faSopenharmony_ci        DEVMETHOD(device_shutdown, bus_generic_shutdown),
50f9f848faSopenharmony_ci        DEVMETHOD(usb_take_controller, xhci_pci_take_controller),
51f9f848faSopenharmony_ci
52f9f848faSopenharmony_ci        DEVMETHOD_END
53f9f848faSopenharmony_ci};
54f9f848faSopenharmony_ci
55f9f848faSopenharmony_cistatic driver_t xhci_driver = {
56f9f848faSopenharmony_ci        .name = "xhci",
57f9f848faSopenharmony_ci        .methods = xhci_device_methods,
58f9f848faSopenharmony_ci        .size = sizeof(struct xhci_softc),
59f9f848faSopenharmony_ci};
60f9f848faSopenharmony_ci
61f9f848faSopenharmony_cistatic devclass_t xhci_devclass;
62f9f848faSopenharmony_ci
63f9f848faSopenharmony_ciDRIVER_MODULE(xhci, nexus, xhci_driver, xhci_devclass, NULL, NULL);
64f9f848faSopenharmony_ci
65f9f848faSopenharmony_cistatic const char *
66f9f848faSopenharmony_cixhci_pci_match(device_t self)
67f9f848faSopenharmony_ci{
68f9f848faSopenharmony_ci	(void)self;
69f9f848faSopenharmony_ci	return ("XHCI (generic) USB 3.0 controller");
70f9f848faSopenharmony_ci}
71f9f848faSopenharmony_ci
72f9f848faSopenharmony_cistatic int
73f9f848faSopenharmony_cixhci_pci_probe(device_t self)
74f9f848faSopenharmony_ci{
75f9f848faSopenharmony_ci	const char *desc = xhci_pci_match(self);
76f9f848faSopenharmony_ci
77f9f848faSopenharmony_ci	if (desc) {
78f9f848faSopenharmony_ci		device_set_desc(self, desc);
79f9f848faSopenharmony_ci		return (BUS_PROBE_DEFAULT);
80f9f848faSopenharmony_ci	} else {
81f9f848faSopenharmony_ci		return (ENXIO);
82f9f848faSopenharmony_ci	}
83f9f848faSopenharmony_ci}
84f9f848faSopenharmony_ci
85f9f848faSopenharmony_cistatic int
86f9f848faSopenharmony_cixhci_pci_attach(device_t self)
87f9f848faSopenharmony_ci{
88f9f848faSopenharmony_ci	struct resource *res;
89f9f848faSopenharmony_ci	struct xhci_softc *sc = device_get_softc(self);;
90f9f848faSopenharmony_ci	int err = ENXIO;
91f9f848faSopenharmony_ci	uint8_t usedma32 = 0;
92f9f848faSopenharmony_ci	int unit = device_get_unit(self);
93f9f848faSopenharmony_ci
94f9f848faSopenharmony_ci	usb_debug("+\n");
95f9f848faSopenharmony_ci
96f9f848faSopenharmony_ci	res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &unit, 0);
97f9f848faSopenharmony_ci	if (res == NULL) {
98f9f848faSopenharmony_ci		goto error;
99f9f848faSopenharmony_ci	}
100f9f848faSopenharmony_ci
101f9f848faSopenharmony_ci	sc->sc_io_res = ioremap(res->start, res->count);
102f9f848faSopenharmony_ci	if (!sc->sc_io_res) {
103f9f848faSopenharmony_ci		goto error;
104f9f848faSopenharmony_ci	}
105f9f848faSopenharmony_ci	sc->sc_io_tag = (void *)sc->sc_io_res;
106f9f848faSopenharmony_ci	sc->sc_io_hdl = (uintptr_t)sc->sc_io_res;
107f9f848faSopenharmony_ci	sc->sc_io_size = res->count;
108f9f848faSopenharmony_ci	usb_debug("bus_setup_intr\n");
109f9f848faSopenharmony_ci
110f9f848faSopenharmony_ci	res = bus_alloc_resource_any(self, SYS_RES_IRQ, &unit, 0);
111f9f848faSopenharmony_ci	if (res == NULL) {
112f9f848faSopenharmony_ci		goto error;
113f9f848faSopenharmony_ci	}
114f9f848faSopenharmony_ci
115f9f848faSopenharmony_ci	sc->sc_irq_res = res;
116f9f848faSopenharmony_ci	err = bus_setup_intr(res->start, 0, (driver_intr_t *)xhci_interrupt, sc);
117f9f848faSopenharmony_ci	if (err) {
118f9f848faSopenharmony_ci		goto error;
119f9f848faSopenharmony_ci	}
120f9f848faSopenharmony_ci
121f9f848faSopenharmony_ci	usb_debug("xhci_init\n");
122f9f848faSopenharmony_ci	HiUsb3StartHcd();
123f9f848faSopenharmony_ci	if (xhci_init(sc, self, usedma32)) {
124f9f848faSopenharmony_ci		device_printf(self, "Could not initialize softc\n");
125f9f848faSopenharmony_ci		goto error;
126f9f848faSopenharmony_ci	}
127f9f848faSopenharmony_ci	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0);
128f9f848faSopenharmony_ci
129f9f848faSopenharmony_ci	usb_debug("add child to usbus\n");
130f9f848faSopenharmony_ci	sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
131f9f848faSopenharmony_ci	if (sc->sc_bus.bdev == NULL) {
132f9f848faSopenharmony_ci		device_printf(self, "Could not add USB device\n");
133f9f848faSopenharmony_ci		goto error;
134f9f848faSopenharmony_ci	}
135f9f848faSopenharmony_ci	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
136f9f848faSopenharmony_ci
137f9f848faSopenharmony_ci	usb_debug("xhci halt and start controller\n");
138f9f848faSopenharmony_ci	err = xhci_halt_controller(sc);
139f9f848faSopenharmony_ci
140f9f848faSopenharmony_ci	if (err == 0)
141f9f848faSopenharmony_ci		err = xhci_start_controller(sc);
142f9f848faSopenharmony_ci
143f9f848faSopenharmony_ci	usb_debug("device_probe_and_attach bus_dev\n");
144f9f848faSopenharmony_ci	if (err == 0)
145f9f848faSopenharmony_ci		err = device_probe_and_attach(sc->sc_bus.bdev);
146f9f848faSopenharmony_ci
147f9f848faSopenharmony_ci	if (err) {
148f9f848faSopenharmony_ci		device_printf(self, "XHCI halt/start/probe failed err=%d\n", err);
149f9f848faSopenharmony_ci		goto error;
150f9f848faSopenharmony_ci	}
151f9f848faSopenharmony_ci
152f9f848faSopenharmony_ci	usb_debug("-\n");
153f9f848faSopenharmony_ci
154f9f848faSopenharmony_ci	return (0);
155f9f848faSopenharmony_cierror:
156f9f848faSopenharmony_ci	device_printf(self, "XHCI halt/start/probe failed err=%d\n", err);
157f9f848faSopenharmony_ci	if (sc) {
158f9f848faSopenharmony_ci		iounmap((void *)sc->sc_io_res);
159f9f848faSopenharmony_ci	}
160f9f848faSopenharmony_ci	(void)xhci_pci_detach(self);
161f9f848faSopenharmony_ci	return (err);
162f9f848faSopenharmony_ci}
163f9f848faSopenharmony_ci
164f9f848faSopenharmony_cistatic int
165f9f848faSopenharmony_cixhci_pci_detach(device_t self)
166f9f848faSopenharmony_ci{
167f9f848faSopenharmony_ci
168f9f848faSopenharmony_ci	struct xhci_softc *sc = device_get_softc(self);
169f9f848faSopenharmony_ci
170f9f848faSopenharmony_ci	/* during module unload there are lots of children leftover */
171f9f848faSopenharmony_ci	(void)device_delete_children(self);
172f9f848faSopenharmony_ci
173f9f848faSopenharmony_ci	usb_callout_drain(&sc->sc_callout);
174f9f848faSopenharmony_ci	(void)xhci_halt_controller(sc);
175f9f848faSopenharmony_ci	(void)xhci_reset_controller(sc);
176f9f848faSopenharmony_ci
177f9f848faSopenharmony_ci	// release resouce
178f9f848faSopenharmony_ci	if (sc->sc_irq_res) {
179f9f848faSopenharmony_ci		(void)bus_teardown_intr(sc->sc_irq_res->start, sc);
180f9f848faSopenharmony_ci		sc->sc_irq_res = NULL;
181f9f848faSopenharmony_ci	}
182f9f848faSopenharmony_ci	if (sc->sc_io_res) {
183f9f848faSopenharmony_ci		iounmap((void *)sc->sc_io_res);
184f9f848faSopenharmony_ci		sc->sc_io_res = NULL;
185f9f848faSopenharmony_ci		sc->sc_io_tag = NULL;
186f9f848faSopenharmony_ci		sc->sc_io_hdl = (uintptr_t)NULL;
187f9f848faSopenharmony_ci		sc->sc_io_size = 0;
188f9f848faSopenharmony_ci	}
189f9f848faSopenharmony_ci
190f9f848faSopenharmony_ci	xhci_uninit(sc);
191f9f848faSopenharmony_ci
192f9f848faSopenharmony_ci	return (0);
193f9f848faSopenharmony_ci}
194f9f848faSopenharmony_ci
195f9f848faSopenharmony_cistatic int
196f9f848faSopenharmony_cixhci_pci_take_controller(device_t self)
197f9f848faSopenharmony_ci{
198f9f848faSopenharmony_ci	struct xhci_softc *sc = device_get_softc(self);
199f9f848faSopenharmony_ci	uint32_t cparams;
200f9f848faSopenharmony_ci	uint32_t eecp;
201f9f848faSopenharmony_ci	uint32_t eec;
202f9f848faSopenharmony_ci	uint16_t to;
203f9f848faSopenharmony_ci	uint8_t bios_sem;
204f9f848faSopenharmony_ci
205f9f848faSopenharmony_ci	cparams = XREAD4(sc, capa, XHCI_HCSPARAMS0);
206f9f848faSopenharmony_ci
207f9f848faSopenharmony_ci	eec = (uint32_t)(-1);
208f9f848faSopenharmony_ci
209f9f848faSopenharmony_ci	/* Synchronise with the BIOS if it owns the controller. */
210f9f848faSopenharmony_ci	for (eecp = XHCI_HCS0_XECP(cparams) << 2; (eecp != 0) && XHCI_XECP_NEXT(eec);
211f9f848faSopenharmony_ci	    eecp += (XHCI_XECP_NEXT(eec) << 2)) {
212f9f848faSopenharmony_ci		eec = XREAD4(sc, capa, eecp);
213f9f848faSopenharmony_ci
214f9f848faSopenharmony_ci		if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY)
215f9f848faSopenharmony_ci			continue;
216f9f848faSopenharmony_ci		bios_sem = XREAD1(sc, capa, eecp +
217f9f848faSopenharmony_ci		    XHCI_XECP_BIOS_SEM);
218f9f848faSopenharmony_ci		if (bios_sem == 0)
219f9f848faSopenharmony_ci			continue;
220f9f848faSopenharmony_ci		device_printf(sc->sc_bus.bdev, "waiting for BIOS "
221f9f848faSopenharmony_ci		    "to give up control\n");
222f9f848faSopenharmony_ci		XWRITE1(sc, capa, eecp +
223f9f848faSopenharmony_ci		    XHCI_XECP_OS_SEM, 1);
224f9f848faSopenharmony_ci		to = 500;
225f9f848faSopenharmony_ci		while (1) {
226f9f848faSopenharmony_ci			bios_sem = XREAD1(sc, capa, eecp +
227f9f848faSopenharmony_ci			    XHCI_XECP_BIOS_SEM);
228f9f848faSopenharmony_ci			if (bios_sem == 0)
229f9f848faSopenharmony_ci				break;
230f9f848faSopenharmony_ci
231f9f848faSopenharmony_ci			if (--to == 0) {
232f9f848faSopenharmony_ci				device_printf(sc->sc_bus.bdev,
233f9f848faSopenharmony_ci				    "timed out waiting for BIOS\n");
234f9f848faSopenharmony_ci				break;
235f9f848faSopenharmony_ci			}
236f9f848faSopenharmony_ci			usb_pause_mtx(NULL, hz / 100);	/* wait 10ms */
237f9f848faSopenharmony_ci		}
238f9f848faSopenharmony_ci	}
239f9f848faSopenharmony_ci	return (0);
240f9f848faSopenharmony_ci}
241f9f848faSopenharmony_ci
242f9f848faSopenharmony_ciint
243f9f848faSopenharmony_cihixhci_init(void)
244f9f848faSopenharmony_ci{
245f9f848faSopenharmony_ci	DPRINTF("hixhci_init");
246f9f848faSopenharmony_ci	return driver_module_handler(NULL, MOD_LOAD, &xhci_nexus_driver_mod);
247f9f848faSopenharmony_ci}
248f9f848faSopenharmony_ci
249f9f848faSopenharmony_civoid
250f9f848faSopenharmony_cihixhci_exit(void)
251f9f848faSopenharmony_ci{
252f9f848faSopenharmony_ci	DPRINTF("hixhci_exit");
253f9f848faSopenharmony_ci	(void)driver_module_handler(NULL, MOD_UNLOAD, &xhci_nexus_driver_mod);
254f9f848faSopenharmony_ci}
255