1f9f848faSopenharmony_ci/*-
2f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause
3f9f848faSopenharmony_ci *
4f9f848faSopenharmony_ci * Copyright (c) 1998 The NetBSD Foundation, Inc.
5f9f848faSopenharmony_ci * All rights reserved.
6f9f848faSopenharmony_ci *
7f9f848faSopenharmony_ci * This code is derived from software contributed to The NetBSD Foundation
8f9f848faSopenharmony_ci * by Lennart Augustsson (augustss@carlstedt.se) at
9f9f848faSopenharmony_ci * Carlstedt Research & Technology.
10f9f848faSopenharmony_ci *
11f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without
12f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions
13f9f848faSopenharmony_ci * are met:
14f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright
15f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer.
16f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
17f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
18f9f848faSopenharmony_ci *    documentation and/or other materials provided with the distribution.
19f9f848faSopenharmony_ci *
20f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21f9f848faSopenharmony_ci * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22f9f848faSopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23f9f848faSopenharmony_ci * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24f9f848faSopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25f9f848faSopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26f9f848faSopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27f9f848faSopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28f9f848faSopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29f9f848faSopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30f9f848faSopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
31f9f848faSopenharmony_ci */
32f9f848faSopenharmony_ci
33f9f848faSopenharmony_ci#include <sys/cdefs.h>
34f9f848faSopenharmony_ci/*
35f9f848faSopenharmony_ci * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
36f9f848faSopenharmony_ci *
37f9f848faSopenharmony_ci * The EHCI 1.0 spec can be found at
38f9f848faSopenharmony_ci * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
39f9f848faSopenharmony_ci * and the USB 2.0 spec at
40f9f848faSopenharmony_ci * http://www.usb.org/developers/docs/usb_20.zip
41f9f848faSopenharmony_ci */
42f9f848faSopenharmony_ci
43f9f848faSopenharmony_ci/* The low level controller code for EHCI has been split into
44f9f848faSopenharmony_ci * PCI probes and EHCI specific code. This was done to facilitate the
45f9f848faSopenharmony_ci * sharing of code between *BSD's
46f9f848faSopenharmony_ci */
47f9f848faSopenharmony_ci
48f9f848faSopenharmony_ci#include "implementation/global_implementation.h"
49f9f848faSopenharmony_ci#include "controller/ehci.h"
50f9f848faSopenharmony_ci#include "controller/ehcireg.h"
51f9f848faSopenharmony_ci
52f9f848faSopenharmony_cistatic device_probe_t ehci_pci_probe;
53f9f848faSopenharmony_cistatic device_attach_t ehci_pci_attach;
54f9f848faSopenharmony_cistatic device_detach_t ehci_pci_detach;
55f9f848faSopenharmony_cistatic usb_take_controller_t ehci_pci_take_controller;
56f9f848faSopenharmony_ci
57f9f848faSopenharmony_cistatic void
58f9f848faSopenharmony_cihiehci_post_reset(struct ehci_softc *sc)
59f9f848faSopenharmony_ci{
60f9f848faSopenharmony_ci	uint32_t usb_mode;
61f9f848faSopenharmony_ci
62f9f848faSopenharmony_ci	/* Force HOST mode */
63f9f848faSopenharmony_ci	usb_mode = EOREAD4(sc, EHCI_USBMODE_NOLPM);
64f9f848faSopenharmony_ci	usb_mode &= ~EHCI_UM_CM;
65f9f848faSopenharmony_ci	usb_mode |= EHCI_UM_CM_HOST;
66f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBMODE_NOLPM, usb_mode);
67f9f848faSopenharmony_ci}
68f9f848faSopenharmony_ci
69f9f848faSopenharmony_cistatic const char *
70f9f848faSopenharmony_ciehci_pci_match(device_t self)
71f9f848faSopenharmony_ci{
72f9f848faSopenharmony_ci	return ("EHCI (generic) USB 2.0 controller");
73f9f848faSopenharmony_ci}
74f9f848faSopenharmony_ci
75f9f848faSopenharmony_cistatic int
76f9f848faSopenharmony_ciehci_pci_probe(device_t self)
77f9f848faSopenharmony_ci{
78f9f848faSopenharmony_ci	const char *desc = ehci_pci_match(self);
79f9f848faSopenharmony_ci
80f9f848faSopenharmony_ci	if (desc) {
81f9f848faSopenharmony_ci		device_set_desc(self, desc);
82f9f848faSopenharmony_ci		return (BUS_PROBE_DEFAULT);
83f9f848faSopenharmony_ci	} else {
84f9f848faSopenharmony_ci		return (ENXIO);
85f9f848faSopenharmony_ci	}
86f9f848faSopenharmony_ci}
87f9f848faSopenharmony_ci
88f9f848faSopenharmony_cistatic int
89f9f848faSopenharmony_ciehci_pci_attach(device_t self)
90f9f848faSopenharmony_ci{
91f9f848faSopenharmony_ci	struct resource *res;
92f9f848faSopenharmony_ci	ehci_softc_t *sc = device_get_softc(self);
93f9f848faSopenharmony_ci	int err = ENXIO;
94f9f848faSopenharmony_ci
95f9f848faSopenharmony_ci	/* initialise some bus fields */
96f9f848faSopenharmony_ci	sc->sc_bus.parent = self;
97f9f848faSopenharmony_ci	sc->sc_bus.devices = sc->sc_devices;
98f9f848faSopenharmony_ci	sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
99f9f848faSopenharmony_ci	sc->sc_bus.dma_bits = 32;
100f9f848faSopenharmony_ci	sc->sc_bus.dma_parent_tag[0].dma_bits = 32;
101f9f848faSopenharmony_ci
102f9f848faSopenharmony_ci	/* get all DMA memory */
103f9f848faSopenharmony_ci	if (usb_bus_mem_alloc_all(&sc->sc_bus,
104f9f848faSopenharmony_ci	    USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) {
105f9f848faSopenharmony_ci		err = ENOMEM;
106f9f848faSopenharmony_ci		goto error0;
107f9f848faSopenharmony_ci	}
108f9f848faSopenharmony_ci
109f9f848faSopenharmony_ci	res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &unit, 0);
110f9f848faSopenharmony_ci	if (res == NULL) {
111f9f848faSopenharmony_ci		goto error;
112f9f848faSopenharmony_ci	}
113f9f848faSopenharmony_ci
114f9f848faSopenharmony_ci	sc->sc_io_res = ioremap(res->start, res->count);
115f9f848faSopenharmony_ci	if (!sc->sc_io_res) {
116f9f848faSopenharmony_ci		goto error1;
117f9f848faSopenharmony_ci	}
118f9f848faSopenharmony_ci
119f9f848faSopenharmony_ci	sc->sc_io_tag = (void *)sc->sc_io_res;
120f9f848faSopenharmony_ci	sc->sc_io_hdl = (bus_space_handle_t)sc->sc_io_res;
121f9f848faSopenharmony_ci	sc->sc_io_size = res->count;
122f9f848faSopenharmony_ci
123f9f848faSopenharmony_ci	sc->sc_flags |= EHCI_SCFLG_DONTRESET;
124f9f848faSopenharmony_ci
125f9f848faSopenharmony_ci	/* Setup callbacks. */
126f9f848faSopenharmony_ci	sc->sc_vendor_post_reset = hiehci_post_reset;
127f9f848faSopenharmony_ci	sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc;
128f9f848faSopenharmony_ci
129f9f848faSopenharmony_ci	sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
130f9f848faSopenharmony_ci	if (!sc->sc_bus.bdev) {
131f9f848faSopenharmony_ci		device_printf(self, "Could not add USB device\n");
132f9f848faSopenharmony_ci		goto error1;
133f9f848faSopenharmony_ci	}
134f9f848faSopenharmony_ci	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
135f9f848faSopenharmony_ci
136f9f848faSopenharmony_ci	/*
137f9f848faSopenharmony_ci	 * ehci_pci_match will never return NULL if ehci_pci_probe
138f9f848faSopenharmony_ci	 * succeeded
139f9f848faSopenharmony_ci	 */
140f9f848faSopenharmony_ci	device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self));
141f9f848faSopenharmony_ci
142f9f848faSopenharmony_ci	res = bus_alloc_resource_any(self, SYS_RES_IRQ, &unit, 0);
143f9f848faSopenharmony_ci	if (res == NULL) {
144f9f848faSopenharmony_ci		goto error;
145f9f848faSopenharmony_ci	}
146f9f848faSopenharmony_ci	sc->sc_irq_res = res;
147f9f848faSopenharmony_ci	err = bus_setup_intr(res->start, 0, ehci_interrupt, sc);
148f9f848faSopenharmony_ci	if (err) {
149f9f848faSopenharmony_ci		goto error2;
150f9f848faSopenharmony_ci	}
151f9f848faSopenharmony_ci
152f9f848faSopenharmony_ci	ehci_pci_take_controller(self);
153f9f848faSopenharmony_ci
154f9f848faSopenharmony_ci	hiusb_start_hcd();
155f9f848faSopenharmony_ci	hiusb_device2host();
156f9f848faSopenharmony_ci
157f9f848faSopenharmony_ci	err = ehci_init(sc);
158f9f848faSopenharmony_ci	if (!err) {
159f9f848faSopenharmony_ci		err = device_probe_and_attach(sc->sc_bus.bdev);
160f9f848faSopenharmony_ci	}
161f9f848faSopenharmony_ci	if (err) {
162f9f848faSopenharmony_ci		device_printf(self, "ehci init failed err=%d\n", err);
163f9f848faSopenharmony_ci		goto error;
164f9f848faSopenharmony_ci	}
165f9f848faSopenharmony_ci
166f9f848faSopenharmony_ci	return (0);
167f9f848faSopenharmony_ci
168f9f848faSopenharmony_cierror:
169f9f848faSopenharmony_ci	device_printf(self, "ehci attach failed err=%d\n", err);
170f9f848faSopenharmony_ci	(void)ehci_pci_detach(self);
171f9f848faSopenharmony_ci	return (err);
172f9f848faSopenharmony_cierror2:
173f9f848faSopenharmony_ci	if (sc->sc_io_res != NULL) {
174f9f848faSopenharmony_ci		iounmap((void *)sc->sc_io_res);
175f9f848faSopenharmony_ci		sc->sc_io_res = NULL;
176f9f848faSopenharmony_ci	}
177f9f848faSopenharmony_cierror1:
178f9f848faSopenharmony_ci	(void)bus_teardown_intr(NUM_HAL_INTERRUPT_USB_EHCI, sc);
179f9f848faSopenharmony_cierror0:
180f9f848faSopenharmony_ci	usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
181f9f848faSopenharmony_ci	device_printf(self, "ehci attach failed err=%d\n", err);
182f9f848faSopenharmony_ci	return (err);
183f9f848faSopenharmony_ci}
184f9f848faSopenharmony_ci
185f9f848faSopenharmony_cistatic int
186f9f848faSopenharmony_ciehci_pci_detach(device_t self)
187f9f848faSopenharmony_ci{
188f9f848faSopenharmony_ci	ehci_softc_t *sc = device_get_softc(self);
189f9f848faSopenharmony_ci
190f9f848faSopenharmony_ci	/* during module unload there are lots of children leftover */
191f9f848faSopenharmony_ci	(void)device_delete_children(dev);
192f9f848faSopenharmony_ci
193f9f848faSopenharmony_ci	ehci_detach(sc);
194f9f848faSopenharmony_ci	hiusb_stop_hcd();
195f9f848faSopenharmony_ci
196f9f848faSopenharmony_ci	if (sc->sc_irq_res != NULL) {
197f9f848faSopenharmony_ci		(void)bus_teardown_intr(sc->sc_irq_res->start, sc);
198f9f848faSopenharmony_ci		sc->sc_irq_res = NULL;
199f9f848faSopenharmony_ci	}
200f9f848faSopenharmony_ci	if (sc->sc_io_res != NULL) {
201f9f848faSopenharmony_ci		iounmap((void *)sc->sc_io_res);
202f9f848faSopenharmony_ci		sc->sc_io_res = NULL;
203f9f848faSopenharmony_ci		sc->sc_io_tag = NULL;
204f9f848faSopenharmony_ci		sc->sc_io_hdl = (uintptr_t)NULL;
205f9f848faSopenharmony_ci		sc->sc_io_size = 0;
206f9f848faSopenharmony_ci	}
207f9f848faSopenharmony_ci	usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
208f9f848faSopenharmony_ci
209f9f848faSopenharmony_ci	return (0);
210f9f848faSopenharmony_ci}
211f9f848faSopenharmony_ci
212f9f848faSopenharmony_cistatic int
213f9f848faSopenharmony_ciehci_pci_take_controller(device_t self)
214f9f848faSopenharmony_ci{
215f9f848faSopenharmony_ci	return (0);
216f9f848faSopenharmony_ci}
217f9f848faSopenharmony_ci
218f9f848faSopenharmony_cistatic device_method_t ehci_pci_methods[] = {
219f9f848faSopenharmony_ci	/* Device interface */
220f9f848faSopenharmony_ci	DEVMETHOD(device_probe, ehci_pci_probe),
221f9f848faSopenharmony_ci	DEVMETHOD(device_attach, ehci_pci_attach),
222f9f848faSopenharmony_ci	DEVMETHOD(device_detach, ehci_pci_detach),
223f9f848faSopenharmony_ci	DEVMETHOD(device_suspend, bus_generic_suspend),
224f9f848faSopenharmony_ci	DEVMETHOD(device_resume, bus_generic_resume),
225f9f848faSopenharmony_ci	DEVMETHOD(device_shutdown, bus_generic_shutdown),
226f9f848faSopenharmony_ci	DEVMETHOD(usb_take_controller, ehci_pci_take_controller),
227f9f848faSopenharmony_ci
228f9f848faSopenharmony_ci	DEVMETHOD_END
229f9f848faSopenharmony_ci};
230f9f848faSopenharmony_ci
231f9f848faSopenharmony_cistatic driver_t ehci_driver = {
232f9f848faSopenharmony_ci	.name = "ehci",
233f9f848faSopenharmony_ci	.methods = ehci_pci_methods,
234f9f848faSopenharmony_ci	.size = sizeof(struct ehci_softc),
235f9f848faSopenharmony_ci};
236f9f848faSopenharmony_ci
237f9f848faSopenharmony_cistatic devclass_t ehci_devclass;
238f9f848faSopenharmony_ci
239f9f848faSopenharmony_ciDRIVER_MODULE(ehci, nexus, ehci_driver, ehci_devclass, 0, 0);
240f9f848faSopenharmony_ci
241f9f848faSopenharmony_ciint
242f9f848faSopenharmony_cihiehci_init(void)
243f9f848faSopenharmony_ci{
244f9f848faSopenharmony_ci	DPRINTF("hiehci_init");
245f9f848faSopenharmony_ci	return driver_module_handler(NULL, MOD_LOAD, &ehci_nexus_driver_mod);
246f9f848faSopenharmony_ci}
247f9f848faSopenharmony_ci
248f9f848faSopenharmony_civoid
249f9f848faSopenharmony_cihiehci_exit(void)
250f9f848faSopenharmony_ci{
251f9f848faSopenharmony_ci	DPRINTF("hiehci_exit");
252f9f848faSopenharmony_ci	(void)driver_module_handler(NULL, MOD_UNLOAD, &ehci_nexus_driver_mod);
253f9f848faSopenharmony_ci}
254