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