xref: /freebsd-src/sys/arm/xilinx/zy7_ehci.c (revision 3ddaf8200bc90b1410755ebac7b5c979ea90a2f6)
1a9caca6aSWojciech A. Koszek /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3af3dc4a7SPedro F. Giffuni  *
440713190SWojciech A. Koszek  * Copyright (c) 2012-2013 Thomas Skibo
5a9caca6aSWojciech A. Koszek  * All rights reserved.
6a9caca6aSWojciech A. Koszek  *
7a9caca6aSWojciech A. Koszek  * Redistribution and use in source and binary forms, with or without
840713190SWojciech A. Koszek  * modification, are permitted provided that the following conditions
940713190SWojciech A. Koszek  * are met:
1040713190SWojciech A. Koszek  * 1. Redistributions of source code must retain the above copyright
11a9caca6aSWojciech A. Koszek  *    notice, this list of conditions and the following disclaimer.
1240713190SWojciech A. Koszek  * 2. Redistributions in binary form must reproduce the above copyright
13a9caca6aSWojciech A. Koszek  *    notice, this list of conditions and the following disclaimer in the
14a9caca6aSWojciech A. Koszek  *    documentation and/or other materials provided with the distribution.
15a9caca6aSWojciech A. Koszek  *
1640713190SWojciech A. Koszek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1740713190SWojciech A. Koszek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18a9caca6aSWojciech A. Koszek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1940713190SWojciech A. Koszek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2040713190SWojciech A. Koszek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2140713190SWojciech A. Koszek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2240713190SWojciech A. Koszek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2340713190SWojciech A. Koszek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2440713190SWojciech A. Koszek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2540713190SWojciech A. Koszek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2640713190SWojciech A. Koszek  * SUCH DAMAGE.
27a9caca6aSWojciech A. Koszek  */
28a9caca6aSWojciech A. Koszek 
2940713190SWojciech A. Koszek /*
3040713190SWojciech A. Koszek  * A host-controller driver for Zynq-7000's USB OTG controller.
31a9caca6aSWojciech A. Koszek  *
32a9caca6aSWojciech A. Koszek  * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
33a9caca6aSWojciech A. Koszek  * (v1.4) November 16, 2012.  Xilinx doc UG585.  Ch. 15 covers the USB
34a9caca6aSWojciech A. Koszek  * controller and register definitions are in appendix B.34.
35a9caca6aSWojciech A. Koszek  */
36a9caca6aSWojciech A. Koszek 
37a9caca6aSWojciech A. Koszek #include <sys/param.h>
38a9caca6aSWojciech A. Koszek #include <sys/systm.h>
39a9caca6aSWojciech A. Koszek #include <sys/bus.h>
40a9caca6aSWojciech A. Koszek #include <sys/conf.h>
41a9caca6aSWojciech A. Koszek #include <sys/kernel.h>
42a9caca6aSWojciech A. Koszek #include <sys/lock.h>
43a9caca6aSWojciech A. Koszek #include <sys/module.h>
44a9caca6aSWojciech A. Koszek #include <sys/mutex.h>
45a9caca6aSWojciech A. Koszek #include <sys/condvar.h>
46a9caca6aSWojciech A. Koszek #include <sys/resource.h>
47a9caca6aSWojciech A. Koszek #include <sys/rman.h>
48a9caca6aSWojciech A. Koszek 
49a9caca6aSWojciech A. Koszek #include <machine/bus.h>
50a9caca6aSWojciech A. Koszek #include <machine/resource.h>
51a9caca6aSWojciech A. Koszek #include <machine/stdarg.h>
52a9caca6aSWojciech A. Koszek 
53a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus.h>
54a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus_subr.h>
55a9caca6aSWojciech A. Koszek 
56a9caca6aSWojciech A. Koszek #include <dev/usb/usb.h>
57a9caca6aSWojciech A. Koszek #include <dev/usb/usbdi.h>
58a9caca6aSWojciech A. Koszek 
59a9caca6aSWojciech A. Koszek #include <dev/usb/usb_core.h>
60a9caca6aSWojciech A. Koszek #include <dev/usb/usb_busdma.h>
61a9caca6aSWojciech A. Koszek #include <dev/usb/usb_process.h>
62a9caca6aSWojciech A. Koszek #include <dev/usb/usb_util.h>
63a9caca6aSWojciech A. Koszek 
64a9caca6aSWojciech A. Koszek #include <dev/usb/usb_controller.h>
65a9caca6aSWojciech A. Koszek #include <dev/usb/usb_bus.h>
66a9caca6aSWojciech A. Koszek #include <dev/usb/controller/ehci.h>
67a9caca6aSWojciech A. Koszek #include <dev/usb/controller/ehcireg.h>
68a9caca6aSWojciech A. Koszek 
69a9caca6aSWojciech A. Koszek /* Register definitions. */
70a9caca6aSWojciech A. Koszek #define ZY7_USB_ID				0x0000
71a9caca6aSWojciech A. Koszek #define ZY7_USB_HWGENERAL			0x0004
72a9caca6aSWojciech A. Koszek #define ZY7_USB_HWHOST				0x0008
73a9caca6aSWojciech A. Koszek #define ZY7_USB_HWDEVICE			0x000c
74a9caca6aSWojciech A. Koszek #define ZY7_USB_HWTXBUF				0x0010
75a9caca6aSWojciech A. Koszek #define ZY7_USB_HWRXBUF				0x0014
76a9caca6aSWojciech A. Koszek #define ZY7_USB_GPTIMER0LD			0x0080
77a9caca6aSWojciech A. Koszek #define ZY7_USB_GPTIMER0CTRL			0x0084
78a9caca6aSWojciech A. Koszek #define ZY7_USB_GPTIMER1LD			0x0088
79a9caca6aSWojciech A. Koszek #define ZY7_USB_GPTIMER1CTRL			0x008c
80a9caca6aSWojciech A. Koszek #define ZY7_USB_SBUSCFG				0x0090
81a9caca6aSWojciech A. Koszek #define ZY7_USB_CAPLENGTH_HCIVERSION		0x0100
82a9caca6aSWojciech A. Koszek #define ZY7_USB_HCSPARAMS			0x0104
83a9caca6aSWojciech A. Koszek #define ZY7_USB_HCCPARAMS			0x0108
84a9caca6aSWojciech A. Koszek #define ZY7_USB_DCIVERSION			0x0120
85a9caca6aSWojciech A. Koszek #define ZY7_USB_DCCPARAMS			0x0124
86a9caca6aSWojciech A. Koszek #define ZY7_USB_USBCMD				0x0140
87a9caca6aSWojciech A. Koszek #define ZY7_USB_USBSTS				0x0144
88a9caca6aSWojciech A. Koszek #define ZY7_USB_USBINTR				0x0148
89a9caca6aSWojciech A. Koszek #define ZY7_USB_FRINDEX				0x014c
90a9caca6aSWojciech A. Koszek #define ZY7_USB_PERIODICLISTBASE_DEICEADDR 	0x0154
91a9caca6aSWojciech A. Koszek #define ZY7_USB_ASYNCLISTADDR_ENDPOINTLISTADDR 	0x0158
92a9caca6aSWojciech A. Koszek #define ZY7_USB_TTCTRL				0x015c
93a9caca6aSWojciech A. Koszek #define ZY7_USB_BURSTSIZE			0x0160
94a9caca6aSWojciech A. Koszek #define ZY7_USB_TXFILLTUNING			0x0164
95a9caca6aSWojciech A. Koszek #define   ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT		16
96a9caca6aSWojciech A. Koszek #define   ZY7_USB_TXFILLTUNING_TXFIFOTHRES_MASK		(0x3f<<16)
97a9caca6aSWojciech A. Koszek #define ZY7_USB_TXTFILLTUNING			0x0168
98a9caca6aSWojciech A. Koszek #define ZY7_USB_IC_USB				0x016c
99a9caca6aSWojciech A. Koszek #define ZY7_USB_ULPI_VIEWPORT			0x0170
100a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_WU			(1<<31)
101a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_RUN			(1<<30)
102a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_RW			(1<<29)
103a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_SS			(1<<27)
104a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_PORT_MASK		(7<<24)
105a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT		24
106a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_ADDR_MASK		(0xff<<16)
107a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT		16
108a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_DATARD_MASK		(0xff<<8)
109a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_DATARD_SHIFT		8
110a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_DATAWR_MASK		(0xff<<0)
111a9caca6aSWojciech A. Koszek #define   ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT		0
112a9caca6aSWojciech A. Koszek #define ZY7_USB_ENDPTNAK			0x0178
113a9caca6aSWojciech A. Koszek #define ZY7_USB_ENDPTNAKEN			0x017c
114a9caca6aSWojciech A. Koszek #define ZY7_USB_CONFIGFLAG			0x0180
115a9caca6aSWojciech A. Koszek #define ZY7_USB_PORTSC(n)			(0x0180+4*(n))
116a9caca6aSWojciech A. Koszek #define   ZY7_USB_PORTSC_PTS_MASK			(3<<30)
117a9caca6aSWojciech A. Koszek #define   ZY7_USB_PORTSC_PTS_SHIFT			30
118a9caca6aSWojciech A. Koszek #define   ZY7_USB_PORTSC_PTS_UTMI			(0<<30)
119a9caca6aSWojciech A. Koszek #define   ZY7_USB_PORTSC_PTS_ULPI			(2<<30)
120a9caca6aSWojciech A. Koszek #define   ZY7_USB_PORTSC_PTS_SERIAL			(3<<30)
121a9caca6aSWojciech A. Koszek #define   ZY7_USB_PORTSC_PTW				(1<<28)
122a9caca6aSWojciech A. Koszek #define   ZY7_USB_PORTSC_PTS2				(1<<25)
123a9caca6aSWojciech A. Koszek #define ZY7_USB_OTGSC				0x01a4
124a9caca6aSWojciech A. Koszek #define ZY7_USB_USBMODE				0x01a8
125a9caca6aSWojciech A. Koszek #define ZY7_USB_ENDPTSETUPSTAT			0x01ac
126a9caca6aSWojciech A. Koszek #define ZY7_USB_ENDPTPRIME			0x01b0
127a9caca6aSWojciech A. Koszek #define ZY7_USB_ENDPTFLUSH			0x01b4
128a9caca6aSWojciech A. Koszek #define ZY7_USB_ENDPTSTAT			0x01b8
129a9caca6aSWojciech A. Koszek #define ZY7_USB_ENDPTCOMPLETE			0x01bc
130a9caca6aSWojciech A. Koszek #define ZY7_USB_ENDPTCTRL(n)			(0x01c0+4*(n))
131a9caca6aSWojciech A. Koszek 
132a9caca6aSWojciech A. Koszek #define EHCI_REG_OFFSET	ZY7_USB_CAPLENGTH_HCIVERSION
133a9caca6aSWojciech A. Koszek #define EHCI_REG_SIZE	0x100
134a9caca6aSWojciech A. Koszek 
135cdf4ec68SMichal Meloun static void
136cdf4ec68SMichal Meloun zy7_ehci_post_reset(struct ehci_softc *ehci_softc)
137cdf4ec68SMichal Meloun {
138cdf4ec68SMichal Meloun 	uint32_t usbmode;
139cdf4ec68SMichal Meloun 
140cdf4ec68SMichal Meloun 	/* Force HOST mode */
141cdf4ec68SMichal Meloun 	usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM);
142cdf4ec68SMichal Meloun 	usbmode &= ~EHCI_UM_CM;
143cdf4ec68SMichal Meloun 	usbmode |= EHCI_UM_CM_HOST;
144cdf4ec68SMichal Meloun 	EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode);
145cdf4ec68SMichal Meloun }
146cdf4ec68SMichal Meloun 
147a9caca6aSWojciech A. Koszek static int
148a9caca6aSWojciech A. Koszek zy7_phy_config(device_t dev, bus_space_tag_t io_tag, bus_space_handle_t bsh)
149a9caca6aSWojciech A. Koszek {
150a9caca6aSWojciech A. Koszek 	phandle_t node;
151a9caca6aSWojciech A. Koszek 	char buf[64];
152a9caca6aSWojciech A. Koszek 	uint32_t portsc;
153a9caca6aSWojciech A. Koszek 	int tries;
154a9caca6aSWojciech A. Koszek 
155a9caca6aSWojciech A. Koszek 	node = ofw_bus_get_node(dev);
156a9caca6aSWojciech A. Koszek 
157a9caca6aSWojciech A. Koszek 	if (OF_getprop(node, "phy_type", buf, sizeof(buf)) > 0) {
158a9caca6aSWojciech A. Koszek 		portsc = bus_space_read_4(io_tag, bsh, ZY7_USB_PORTSC(1));
159a9caca6aSWojciech A. Koszek 		portsc &= ~(ZY7_USB_PORTSC_PTS_MASK | ZY7_USB_PORTSC_PTW |
160a9caca6aSWojciech A. Koszek 			    ZY7_USB_PORTSC_PTS2);
161a9caca6aSWojciech A. Koszek 
162a9caca6aSWojciech A. Koszek 		if (strcmp(buf,"ulpi") == 0)
163a9caca6aSWojciech A. Koszek 			portsc |= ZY7_USB_PORTSC_PTS_ULPI;
164a9caca6aSWojciech A. Koszek 		else if (strcmp(buf,"utmi") == 0)
165a9caca6aSWojciech A. Koszek 			portsc |= ZY7_USB_PORTSC_PTS_UTMI;
166a9caca6aSWojciech A. Koszek 		else if (strcmp(buf,"utmi-wide") == 0)
167a9caca6aSWojciech A. Koszek 			portsc |= (ZY7_USB_PORTSC_PTS_UTMI |
168a9caca6aSWojciech A. Koszek 				   ZY7_USB_PORTSC_PTW);
169a9caca6aSWojciech A. Koszek 		else if (strcmp(buf, "serial") == 0)
170a9caca6aSWojciech A. Koszek 			portsc |= ZY7_USB_PORTSC_PTS_SERIAL;
171a9caca6aSWojciech A. Koszek 
172a9caca6aSWojciech A. Koszek 		bus_space_write_4(io_tag, bsh, ZY7_USB_PORTSC(1), portsc);
173a9caca6aSWojciech A. Koszek 	}
174a9caca6aSWojciech A. Koszek 
175a9caca6aSWojciech A. Koszek 	if (OF_getprop(node, "phy_vbus_ext", buf, sizeof(buf)) >= 0) {
176a9caca6aSWojciech A. Koszek 		/* Tell PHY that VBUS is supplied externally. */
177a9caca6aSWojciech A. Koszek 		bus_space_write_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT,
178a9caca6aSWojciech A. Koszek 				  ZY7_USB_ULPI_VIEWPORT_RUN |
179a9caca6aSWojciech A. Koszek 				  ZY7_USB_ULPI_VIEWPORT_RW |
180a9caca6aSWojciech A. Koszek 				  (0 << ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT) |
181a9caca6aSWojciech A. Koszek 				  (0x0b << ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT) |
182a9caca6aSWojciech A. Koszek 				  (0x60 << ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT)
183a9caca6aSWojciech A. Koszek 			);
184a9caca6aSWojciech A. Koszek 
185a9caca6aSWojciech A. Koszek 		tries = 100;
186a9caca6aSWojciech A. Koszek 		while ((bus_space_read_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT) &
187a9caca6aSWojciech A. Koszek 			ZY7_USB_ULPI_VIEWPORT_RUN) != 0) {
188a9caca6aSWojciech A. Koszek 			if (--tries < 0)
189a9caca6aSWojciech A. Koszek 				return (-1);
190a9caca6aSWojciech A. Koszek 			DELAY(1);
191a9caca6aSWojciech A. Koszek 		}
192a9caca6aSWojciech A. Koszek 	}
193a9caca6aSWojciech A. Koszek 
194a9caca6aSWojciech A. Koszek 	return (0);
195a9caca6aSWojciech A. Koszek }
196a9caca6aSWojciech A. Koszek 
197a9caca6aSWojciech A. Koszek static int
198a9caca6aSWojciech A. Koszek zy7_ehci_probe(device_t dev)
199a9caca6aSWojciech A. Koszek {
200a9caca6aSWojciech A. Koszek 
201add35ed5SIan Lepore 	if (!ofw_bus_status_okay(dev))
202add35ed5SIan Lepore 		return (ENXIO);
203add35ed5SIan Lepore 
204a9caca6aSWojciech A. Koszek 	if (!ofw_bus_is_compatible(dev, "xlnx,zy7_ehci"))
205a9caca6aSWojciech A. Koszek 		return (ENXIO);
206a9caca6aSWojciech A. Koszek 
207a9caca6aSWojciech A. Koszek 	device_set_desc(dev, "Zynq-7000 EHCI USB 2.0 controller");
208a9caca6aSWojciech A. Koszek 	return (0);
209a9caca6aSWojciech A. Koszek }
210a9caca6aSWojciech A. Koszek 
211a9caca6aSWojciech A. Koszek static int zy7_ehci_detach(device_t dev);
212a9caca6aSWojciech A. Koszek 
213a9caca6aSWojciech A. Koszek static int
214a9caca6aSWojciech A. Koszek zy7_ehci_attach(device_t dev)
215a9caca6aSWojciech A. Koszek {
216a9caca6aSWojciech A. Koszek 	ehci_softc_t *sc = device_get_softc(dev);
217a9caca6aSWojciech A. Koszek 	bus_space_handle_t bsh;
218a9caca6aSWojciech A. Koszek 	int err, rid;
219a9caca6aSWojciech A. Koszek 
220a9caca6aSWojciech A. Koszek 	/* initialize some bus fields */
221a9caca6aSWojciech A. Koszek 	sc->sc_bus.parent = dev;
222a9caca6aSWojciech A. Koszek 	sc->sc_bus.devices = sc->sc_devices;
223a9caca6aSWojciech A. Koszek 	sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
224b217d184SHans Petter Selasky 	sc->sc_bus.dma_bits = 32;
225a9caca6aSWojciech A. Koszek 
226a9caca6aSWojciech A. Koszek 	/* get all DMA memory */
227a9caca6aSWojciech A. Koszek 	if (usb_bus_mem_alloc_all(&sc->sc_bus,
228a9caca6aSWojciech A. Koszek 	    USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc))
229a9caca6aSWojciech A. Koszek 		return (ENOMEM);
230a9caca6aSWojciech A. Koszek 
231a9caca6aSWojciech A. Koszek 	/* Allocate memory. */
232a9caca6aSWojciech A. Koszek 	rid = 0;
233a9caca6aSWojciech A. Koszek 	sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
234a9caca6aSWojciech A. Koszek 					       &rid, RF_ACTIVE);
235a9caca6aSWojciech A. Koszek 	if (sc->sc_io_res == NULL) {
236a9caca6aSWojciech A. Koszek 		device_printf(dev, "Can't allocate memory");
237a9caca6aSWojciech A. Koszek 		zy7_ehci_detach(dev);
238a9caca6aSWojciech A. Koszek 		return (ENOMEM);
239a9caca6aSWojciech A. Koszek 	}
240a9caca6aSWojciech A. Koszek 
241a9caca6aSWojciech A. Koszek 	sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
242a9caca6aSWojciech A. Koszek 	bsh = rman_get_bushandle(sc->sc_io_res);
243a9caca6aSWojciech A. Koszek 	sc->sc_io_size = EHCI_REG_SIZE;
244a9caca6aSWojciech A. Koszek 
245a9caca6aSWojciech A. Koszek 	if (bus_space_subregion(sc->sc_io_tag, bsh, EHCI_REG_OFFSET,
246a9caca6aSWojciech A. Koszek 				sc->sc_io_size, &sc->sc_io_hdl) != 0)
247a9caca6aSWojciech A. Koszek 		panic("%s: unable to subregion USB host registers",
248a9caca6aSWojciech A. Koszek 		      device_get_name(dev));
249a9caca6aSWojciech A. Koszek 
250a9caca6aSWojciech A. Koszek 	/* Allocate IRQ. */
251a9caca6aSWojciech A. Koszek 	rid = 0;
252a9caca6aSWojciech A. Koszek 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
253a9caca6aSWojciech A. Koszek 						RF_ACTIVE);
254a9caca6aSWojciech A. Koszek 	if (sc->sc_irq_res == NULL) {
255a9caca6aSWojciech A. Koszek 		device_printf(dev, "Can't allocate IRQ\n");
256a9caca6aSWojciech A. Koszek 		zy7_ehci_detach(dev);
257a9caca6aSWojciech A. Koszek 		return (ENOMEM);
258a9caca6aSWojciech A. Koszek 	}
259a9caca6aSWojciech A. Koszek 
260a9caca6aSWojciech A. Koszek 	/* Add USB device */
2615b56413dSWarner Losh 	sc->sc_bus.bdev = device_add_child(dev, "usbus", DEVICE_UNIT_ANY);
262a9caca6aSWojciech A. Koszek 	if (!sc->sc_bus.bdev) {
263a9caca6aSWojciech A. Koszek 		device_printf(dev, "Could not add USB device\n");
264a9caca6aSWojciech A. Koszek 		zy7_ehci_detach(dev);
265a9caca6aSWojciech A. Koszek 		return (ENXIO);
266a9caca6aSWojciech A. Koszek 	}
267a9caca6aSWojciech A. Koszek 	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
268a9caca6aSWojciech A. Koszek 	device_set_desc(sc->sc_bus.bdev, "Zynq-7000 ehci USB 2.0 controller");
269a9caca6aSWojciech A. Koszek 
270a9caca6aSWojciech A. Koszek 	strcpy(sc->sc_vendor, "Xilinx"); /* or IP vendor? */
271a9caca6aSWojciech A. Koszek 
272a9caca6aSWojciech A. Koszek 	/* Activate the interrupt */
273a9caca6aSWojciech A. Koszek 	err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
274a9caca6aSWojciech A. Koszek 			     NULL, (driver_intr_t *)ehci_interrupt, sc,
275a9caca6aSWojciech A. Koszek 			     &sc->sc_intr_hdl);
276a9caca6aSWojciech A. Koszek 	if (err) {
277a9caca6aSWojciech A. Koszek 		device_printf(dev, "Cannot setup IRQ\n");
278a9caca6aSWojciech A. Koszek 		zy7_ehci_detach(dev);
279a9caca6aSWojciech A. Koszek 		return (err);
280a9caca6aSWojciech A. Koszek 	}
281a9caca6aSWojciech A. Koszek 
282a9caca6aSWojciech A. Koszek 	/* Customization. */
283cdf4ec68SMichal Meloun 	sc->sc_flags |= EHCI_SCFLG_TT |	EHCI_SCFLG_NORESTERM;
284cdf4ec68SMichal Meloun 	sc->sc_vendor_post_reset = zy7_ehci_post_reset;
285cdf4ec68SMichal Meloun 	sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc;
286a9caca6aSWojciech A. Koszek 
287a9caca6aSWojciech A. Koszek 	/* Modify FIFO burst threshold from 2 to 8. */
288a9caca6aSWojciech A. Koszek 	bus_space_write_4(sc->sc_io_tag, bsh,
289a9caca6aSWojciech A. Koszek 			  ZY7_USB_TXFILLTUNING,
290a9caca6aSWojciech A. Koszek 			  8 << ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT);
291a9caca6aSWojciech A. Koszek 
292a9caca6aSWojciech A. Koszek 	/* Handle PHY options. */
293a9caca6aSWojciech A. Koszek 	if (zy7_phy_config(dev, sc->sc_io_tag, bsh) < 0) {
294a9caca6aSWojciech A. Koszek 		device_printf(dev, "Cannot config phy!\n");
295a9caca6aSWojciech A. Koszek 		zy7_ehci_detach(dev);
296a9caca6aSWojciech A. Koszek 		return (EIO);
297a9caca6aSWojciech A. Koszek 	}
298a9caca6aSWojciech A. Koszek 
299a9caca6aSWojciech A. Koszek 	/* Init ehci. */
300a9caca6aSWojciech A. Koszek 	err = ehci_init(sc);
301a9caca6aSWojciech A. Koszek 	if (!err) {
302a9caca6aSWojciech A. Koszek 		sc->sc_flags |= EHCI_SCFLG_DONEINIT;
303a9caca6aSWojciech A. Koszek 		err = device_probe_and_attach(sc->sc_bus.bdev);
304a9caca6aSWojciech A. Koszek 	}
305a9caca6aSWojciech A. Koszek 	if (err) {
306a9caca6aSWojciech A. Koszek 		device_printf(dev, "USB init failed err=%d\n", err);
307a9caca6aSWojciech A. Koszek 		zy7_ehci_detach(dev);
308a9caca6aSWojciech A. Koszek 		return (err);
309a9caca6aSWojciech A. Koszek 	}
310a9caca6aSWojciech A. Koszek 
311a9caca6aSWojciech A. Koszek 	return (0);
312a9caca6aSWojciech A. Koszek }
313a9caca6aSWojciech A. Koszek 
314a9caca6aSWojciech A. Koszek static int
315a9caca6aSWojciech A. Koszek zy7_ehci_detach(device_t dev)
316a9caca6aSWojciech A. Koszek {
317a9caca6aSWojciech A. Koszek 	ehci_softc_t *sc = device_get_softc(dev);
318*3ddaf820SJohn Baldwin 	int error;
319a9caca6aSWojciech A. Koszek 
320d3bf5efcSHans Petter Selasky 	/* during module unload there are lots of children leftover */
321*3ddaf820SJohn Baldwin 	error = bus_generic_detach(dev);
322*3ddaf820SJohn Baldwin 	if (error != 0)
323*3ddaf820SJohn Baldwin 		return (error);
324a9caca6aSWojciech A. Koszek 
325024fe4e7SOleksandr Tymoshenko 	if ((sc->sc_flags & EHCI_SCFLG_DONEINIT) != 0) {
326a9caca6aSWojciech A. Koszek 		ehci_detach(sc);
327024fe4e7SOleksandr Tymoshenko 		sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
328024fe4e7SOleksandr Tymoshenko 	}
329d3bf5efcSHans Petter Selasky 
330a9caca6aSWojciech A. Koszek 	if (sc->sc_irq_res) {
331a9caca6aSWojciech A. Koszek 		if (sc->sc_intr_hdl != NULL)
332a9caca6aSWojciech A. Koszek 			bus_teardown_intr(dev, sc->sc_irq_res,
333a9caca6aSWojciech A. Koszek 					  sc->sc_intr_hdl);
334a9caca6aSWojciech A. Koszek 		bus_release_resource(dev, SYS_RES_IRQ,
335a9caca6aSWojciech A. Koszek 			     rman_get_rid(sc->sc_irq_res), sc->sc_irq_res);
336a9caca6aSWojciech A. Koszek 	}
337a9caca6aSWojciech A. Koszek 
338a9caca6aSWojciech A. Koszek 	if (sc->sc_io_res)
339a9caca6aSWojciech A. Koszek 		bus_release_resource(dev, SYS_RES_MEMORY,
340a9caca6aSWojciech A. Koszek 			     rman_get_rid(sc->sc_io_res), sc->sc_io_res);
341a9caca6aSWojciech A. Koszek 	usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
342a9caca6aSWojciech A. Koszek 
343a9caca6aSWojciech A. Koszek 	return (0);
344a9caca6aSWojciech A. Koszek }
345a9caca6aSWojciech A. Koszek 
346a9caca6aSWojciech A. Koszek static device_method_t ehci_methods[] = {
347a9caca6aSWojciech A. Koszek 	/* Device interface */
348a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_probe,		zy7_ehci_probe),
349a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_attach, 	zy7_ehci_attach),
350a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_detach, 	zy7_ehci_detach),
351a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_suspend, 	bus_generic_suspend),
352a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_resume, 	bus_generic_resume),
353a9caca6aSWojciech A. Koszek 	DEVMETHOD(device_shutdown, 	bus_generic_shutdown),
354a9caca6aSWojciech A. Koszek 
355a9caca6aSWojciech A. Koszek 	/* Bus interface */
356a9caca6aSWojciech A. Koszek 	DEVMETHOD(bus_print_child, bus_generic_print_child),
357a9caca6aSWojciech A. Koszek 
358a9caca6aSWojciech A. Koszek 	DEVMETHOD_END
359a9caca6aSWojciech A. Koszek };
360a9caca6aSWojciech A. Koszek 
361a9caca6aSWojciech A. Koszek static driver_t ehci_driver = {
362a9caca6aSWojciech A. Koszek 	"ehci",
363a9caca6aSWojciech A. Koszek 	ehci_methods,
364a9caca6aSWojciech A. Koszek 	sizeof(struct ehci_softc),
365a9caca6aSWojciech A. Koszek };
366a9caca6aSWojciech A. Koszek 
367680ccae3SJohn Baldwin DRIVER_MODULE(zy7_ehci, simplebus, ehci_driver, NULL, NULL);
3687f7d3ba7SEmmanuel Vadot MODULE_DEPEND(zy7_ehci, usb, 1, 1, 1);
369