1249997Swkoszek/*-
2250015Swkoszek * Copyright (c) 2012-2013 Thomas Skibo
3249997Swkoszek * All rights reserved.
4249997Swkoszek *
5249997Swkoszek * Redistribution and use in source and binary forms, with or without
6250015Swkoszek * modification, are permitted provided that the following conditions
7250015Swkoszek * are met:
8250015Swkoszek * 1. Redistributions of source code must retain the above copyright
9250015Swkoszek *    notice, this list of conditions and the following disclaimer.
10250015Swkoszek * 2. Redistributions in binary form must reproduce the above copyright
11250015Swkoszek *    notice, this list of conditions and the following disclaimer in the
12250015Swkoszek *    documentation and/or other materials provided with the distribution.
13250015Swkoszek *
14250015Swkoszek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15250015Swkoszek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16249997Swkoszek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17250015Swkoszek * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18250015Swkoszek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19250015Swkoszek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20250015Swkoszek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21250015Swkoszek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22250015Swkoszek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23250015Swkoszek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24250015Swkoszek * SUCH DAMAGE.
25249997Swkoszek *
26250015Swkoszek * $FreeBSD: stable/10/sys/arm/xilinx/zy7_ehci.c 308402 2016-11-07 09:19:04Z hselasky $
27249997Swkoszek */
28249997Swkoszek
29250015Swkoszek/*
30250015Swkoszek * A host-controller driver for Zynq-7000's USB OTG controller.
31249997Swkoszek *
32249997Swkoszek * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
33249997Swkoszek * (v1.4) November 16, 2012.  Xilinx doc UG585.  Ch. 15 covers the USB
34249997Swkoszek * controller and register definitions are in appendix B.34.
35249997Swkoszek */
36249997Swkoszek
37249997Swkoszek
38249997Swkoszek#include <sys/cdefs.h>
39249997Swkoszek__FBSDID("$FreeBSD: stable/10/sys/arm/xilinx/zy7_ehci.c 308402 2016-11-07 09:19:04Z hselasky $");
40249997Swkoszek
41249997Swkoszek#include <sys/param.h>
42249997Swkoszek#include <sys/systm.h>
43249997Swkoszek#include <sys/bus.h>
44249997Swkoszek#include <sys/conf.h>
45249997Swkoszek#include <sys/kernel.h>
46249997Swkoszek#include <sys/lock.h>
47249997Swkoszek#include <sys/module.h>
48249997Swkoszek#include <sys/mutex.h>
49249997Swkoszek#include <sys/condvar.h>
50249997Swkoszek#include <sys/resource.h>
51249997Swkoszek#include <sys/rman.h>
52249997Swkoszek
53249997Swkoszek#include <machine/bus.h>
54249997Swkoszek#include <machine/resource.h>
55249997Swkoszek#include <machine/stdarg.h>
56249997Swkoszek
57249997Swkoszek#include <dev/fdt/fdt_common.h>
58249997Swkoszek#include <dev/ofw/ofw_bus.h>
59249997Swkoszek#include <dev/ofw/ofw_bus_subr.h>
60249997Swkoszek
61249997Swkoszek#include <dev/usb/usb.h>
62249997Swkoszek#include <dev/usb/usbdi.h>
63249997Swkoszek
64249997Swkoszek#include <dev/usb/usb_core.h>
65249997Swkoszek#include <dev/usb/usb_busdma.h>
66249997Swkoszek#include <dev/usb/usb_process.h>
67249997Swkoszek#include <dev/usb/usb_util.h>
68249997Swkoszek
69249997Swkoszek#include <dev/usb/usb_controller.h>
70249997Swkoszek#include <dev/usb/usb_bus.h>
71249997Swkoszek#include <dev/usb/controller/ehci.h>
72249997Swkoszek#include <dev/usb/controller/ehcireg.h>
73249997Swkoszek
74249997Swkoszek
75249997Swkoszek/* Register definitions. */
76249997Swkoszek#define ZY7_USB_ID				0x0000
77249997Swkoszek#define ZY7_USB_HWGENERAL			0x0004
78249997Swkoszek#define ZY7_USB_HWHOST				0x0008
79249997Swkoszek#define ZY7_USB_HWDEVICE			0x000c
80249997Swkoszek#define ZY7_USB_HWTXBUF				0x0010
81249997Swkoszek#define ZY7_USB_HWRXBUF				0x0014
82249997Swkoszek#define ZY7_USB_GPTIMER0LD			0x0080
83249997Swkoszek#define ZY7_USB_GPTIMER0CTRL			0x0084
84249997Swkoszek#define ZY7_USB_GPTIMER1LD			0x0088
85249997Swkoszek#define ZY7_USB_GPTIMER1CTRL			0x008c
86249997Swkoszek#define ZY7_USB_SBUSCFG				0x0090
87249997Swkoszek#define ZY7_USB_CAPLENGTH_HCIVERSION		0x0100
88249997Swkoszek#define ZY7_USB_HCSPARAMS			0x0104
89249997Swkoszek#define ZY7_USB_HCCPARAMS			0x0108
90249997Swkoszek#define ZY7_USB_DCIVERSION			0x0120
91249997Swkoszek#define ZY7_USB_DCCPARAMS			0x0124
92249997Swkoszek#define ZY7_USB_USBCMD				0x0140
93249997Swkoszek#define ZY7_USB_USBSTS				0x0144
94249997Swkoszek#define ZY7_USB_USBINTR				0x0148
95249997Swkoszek#define ZY7_USB_FRINDEX				0x014c
96249997Swkoszek#define ZY7_USB_PERIODICLISTBASE_DEICEADDR 	0x0154
97249997Swkoszek#define ZY7_USB_ASYNCLISTADDR_ENDPOINTLISTADDR 	0x0158
98249997Swkoszek#define ZY7_USB_TTCTRL				0x015c
99249997Swkoszek#define ZY7_USB_BURSTSIZE			0x0160
100249997Swkoszek#define ZY7_USB_TXFILLTUNING			0x0164
101249997Swkoszek#define   ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT		16
102249997Swkoszek#define   ZY7_USB_TXFILLTUNING_TXFIFOTHRES_MASK		(0x3f<<16)
103249997Swkoszek#define ZY7_USB_TXTFILLTUNING			0x0168
104249997Swkoszek#define ZY7_USB_IC_USB				0x016c
105249997Swkoszek#define ZY7_USB_ULPI_VIEWPORT			0x0170
106249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_WU			(1<<31)
107249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_RUN			(1<<30)
108249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_RW			(1<<29)
109249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_SS			(1<<27)
110249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_PORT_MASK		(7<<24)
111249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT		24
112249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_ADDR_MASK		(0xff<<16)
113249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT		16
114249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_DATARD_MASK		(0xff<<8)
115249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_DATARD_SHIFT		8
116249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_DATAWR_MASK		(0xff<<0)
117249997Swkoszek#define   ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT		0
118249997Swkoszek#define ZY7_USB_ENDPTNAK			0x0178
119249997Swkoszek#define ZY7_USB_ENDPTNAKEN			0x017c
120249997Swkoszek#define ZY7_USB_CONFIGFLAG			0x0180
121249997Swkoszek#define ZY7_USB_PORTSC(n)			(0x0180+4*(n))
122249997Swkoszek#define   ZY7_USB_PORTSC_PTS_MASK			(3<<30)
123249997Swkoszek#define   ZY7_USB_PORTSC_PTS_SHIFT			30
124249997Swkoszek#define   ZY7_USB_PORTSC_PTS_UTMI			(0<<30)
125249997Swkoszek#define   ZY7_USB_PORTSC_PTS_ULPI			(2<<30)
126249997Swkoszek#define   ZY7_USB_PORTSC_PTS_SERIAL			(3<<30)
127249997Swkoszek#define   ZY7_USB_PORTSC_PTW				(1<<28)
128249997Swkoszek#define   ZY7_USB_PORTSC_PTS2				(1<<25)
129249997Swkoszek#define ZY7_USB_OTGSC				0x01a4
130249997Swkoszek#define ZY7_USB_USBMODE				0x01a8
131249997Swkoszek#define ZY7_USB_ENDPTSETUPSTAT			0x01ac
132249997Swkoszek#define ZY7_USB_ENDPTPRIME			0x01b0
133249997Swkoszek#define ZY7_USB_ENDPTFLUSH			0x01b4
134249997Swkoszek#define ZY7_USB_ENDPTSTAT			0x01b8
135249997Swkoszek#define ZY7_USB_ENDPTCOMPLETE			0x01bc
136249997Swkoszek#define ZY7_USB_ENDPTCTRL(n)			(0x01c0+4*(n))
137249997Swkoszek
138249997Swkoszek#define EHCI_REG_OFFSET	ZY7_USB_CAPLENGTH_HCIVERSION
139249997Swkoszek#define EHCI_REG_SIZE	0x100
140249997Swkoszek
141249997Swkoszekstatic int
142249997Swkoszekzy7_phy_config(device_t dev, bus_space_tag_t io_tag, bus_space_handle_t bsh)
143249997Swkoszek{
144249997Swkoszek	phandle_t node;
145249997Swkoszek	char buf[64];
146249997Swkoszek	uint32_t portsc;
147249997Swkoszek	int tries;
148249997Swkoszek
149249997Swkoszek	node = ofw_bus_get_node(dev);
150249997Swkoszek
151249997Swkoszek	if (OF_getprop(node, "phy_type", buf, sizeof(buf)) > 0) {
152249997Swkoszek		portsc = bus_space_read_4(io_tag, bsh, ZY7_USB_PORTSC(1));
153249997Swkoszek		portsc &= ~(ZY7_USB_PORTSC_PTS_MASK | ZY7_USB_PORTSC_PTW |
154249997Swkoszek			    ZY7_USB_PORTSC_PTS2);
155249997Swkoszek
156249997Swkoszek		if (strcmp(buf,"ulpi") == 0)
157249997Swkoszek			portsc |= ZY7_USB_PORTSC_PTS_ULPI;
158249997Swkoszek		else if (strcmp(buf,"utmi") == 0)
159249997Swkoszek			portsc |= ZY7_USB_PORTSC_PTS_UTMI;
160249997Swkoszek		else if (strcmp(buf,"utmi-wide") == 0)
161249997Swkoszek			portsc |= (ZY7_USB_PORTSC_PTS_UTMI |
162249997Swkoszek				   ZY7_USB_PORTSC_PTW);
163249997Swkoszek		else if (strcmp(buf, "serial") == 0)
164249997Swkoszek			portsc |= ZY7_USB_PORTSC_PTS_SERIAL;
165249997Swkoszek
166249997Swkoszek		bus_space_write_4(io_tag, bsh, ZY7_USB_PORTSC(1), portsc);
167249997Swkoszek	}
168249997Swkoszek
169249997Swkoszek	if (OF_getprop(node, "phy_vbus_ext", buf, sizeof(buf)) >= 0) {
170249997Swkoszek
171249997Swkoszek		/* Tell PHY that VBUS is supplied externally. */
172249997Swkoszek		bus_space_write_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT,
173249997Swkoszek				  ZY7_USB_ULPI_VIEWPORT_RUN |
174249997Swkoszek				  ZY7_USB_ULPI_VIEWPORT_RW |
175249997Swkoszek				  (0 << ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT) |
176249997Swkoszek				  (0x0b << ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT) |
177249997Swkoszek				  (0x60 << ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT)
178249997Swkoszek			);
179249997Swkoszek
180249997Swkoszek		tries = 100;
181249997Swkoszek		while ((bus_space_read_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT) &
182249997Swkoszek			ZY7_USB_ULPI_VIEWPORT_RUN) != 0) {
183249997Swkoszek			if (--tries < 0)
184249997Swkoszek				return (-1);
185249997Swkoszek			DELAY(1);
186249997Swkoszek		}
187249997Swkoszek	}
188249997Swkoszek
189249997Swkoszek	return (0);
190249997Swkoszek}
191249997Swkoszek
192249997Swkoszekstatic int
193249997Swkoszekzy7_ehci_probe(device_t dev)
194249997Swkoszek{
195249997Swkoszek
196266152Sian	if (!ofw_bus_status_okay(dev))
197266152Sian		return (ENXIO);
198266152Sian
199249997Swkoszek	if (!ofw_bus_is_compatible(dev, "xlnx,zy7_ehci"))
200249997Swkoszek		return (ENXIO);
201249997Swkoszek
202249997Swkoszek	device_set_desc(dev, "Zynq-7000 EHCI USB 2.0 controller");
203249997Swkoszek	return (0);
204249997Swkoszek}
205249997Swkoszek
206249997Swkoszekstatic int zy7_ehci_detach(device_t dev);
207249997Swkoszek
208249997Swkoszekstatic int
209249997Swkoszekzy7_ehci_attach(device_t dev)
210249997Swkoszek{
211249997Swkoszek	ehci_softc_t *sc = device_get_softc(dev);
212249997Swkoszek	bus_space_handle_t bsh;
213249997Swkoszek	int err, rid;
214249997Swkoszek
215249997Swkoszek	/* initialize some bus fields */
216249997Swkoszek	sc->sc_bus.parent = dev;
217249997Swkoszek	sc->sc_bus.devices = sc->sc_devices;
218249997Swkoszek	sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
219278278Shselasky	sc->sc_bus.dma_bits = 32;
220249997Swkoszek
221249997Swkoszek	/* get all DMA memory */
222249997Swkoszek	if (usb_bus_mem_alloc_all(&sc->sc_bus,
223249997Swkoszek	    USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc))
224249997Swkoszek		return (ENOMEM);
225249997Swkoszek
226249997Swkoszek	/* Allocate memory. */
227249997Swkoszek	rid = 0;
228249997Swkoszek	sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
229249997Swkoszek					       &rid, RF_ACTIVE);
230249997Swkoszek	if (sc->sc_io_res == NULL) {
231249997Swkoszek		device_printf(dev, "Can't allocate memory");
232249997Swkoszek		zy7_ehci_detach(dev);
233249997Swkoszek		return (ENOMEM);
234249997Swkoszek	}
235249997Swkoszek
236249997Swkoszek	sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
237249997Swkoszek	bsh = rman_get_bushandle(sc->sc_io_res);
238249997Swkoszek	sc->sc_io_size = EHCI_REG_SIZE;
239249997Swkoszek
240249997Swkoszek	if (bus_space_subregion(sc->sc_io_tag, bsh, EHCI_REG_OFFSET,
241249997Swkoszek				sc->sc_io_size, &sc->sc_io_hdl) != 0)
242249997Swkoszek		panic("%s: unable to subregion USB host registers",
243249997Swkoszek		      device_get_name(dev));
244249997Swkoszek
245249997Swkoszek	/* Allocate IRQ. */
246249997Swkoszek	rid = 0;
247249997Swkoszek	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
248249997Swkoszek						RF_ACTIVE);
249249997Swkoszek	if (sc->sc_irq_res == NULL) {
250249997Swkoszek		device_printf(dev, "Can't allocate IRQ\n");
251249997Swkoszek		zy7_ehci_detach(dev);
252249997Swkoszek		return (ENOMEM);
253249997Swkoszek	}
254249997Swkoszek
255249997Swkoszek	/* Add USB device */
256249997Swkoszek	sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
257249997Swkoszek	if (!sc->sc_bus.bdev) {
258249997Swkoszek		device_printf(dev, "Could not add USB device\n");
259249997Swkoszek		zy7_ehci_detach(dev);
260249997Swkoszek		return (ENXIO);
261249997Swkoszek	}
262249997Swkoszek	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
263249997Swkoszek	device_set_desc(sc->sc_bus.bdev, "Zynq-7000 ehci USB 2.0 controller");
264249997Swkoszek
265249997Swkoszek	strcpy(sc->sc_vendor, "Xilinx"); /* or IP vendor? */
266249997Swkoszek
267249997Swkoszek	/* Activate the interrupt */
268249997Swkoszek	err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
269249997Swkoszek			     NULL, (driver_intr_t *)ehci_interrupt, sc,
270249997Swkoszek			     &sc->sc_intr_hdl);
271249997Swkoszek	if (err) {
272249997Swkoszek		device_printf(dev, "Cannot setup IRQ\n");
273249997Swkoszek		zy7_ehci_detach(dev);
274249997Swkoszek		return (err);
275249997Swkoszek	}
276249997Swkoszek
277249997Swkoszek	/* Customization. */
278249997Swkoszek	sc->sc_flags |= EHCI_SCFLG_SETMODE | EHCI_SCFLG_TT |
279249997Swkoszek		EHCI_SCFLG_NORESTERM;
280249997Swkoszek
281249997Swkoszek	/* Modify FIFO burst threshold from 2 to 8. */
282249997Swkoszek	bus_space_write_4(sc->sc_io_tag, bsh,
283249997Swkoszek			  ZY7_USB_TXFILLTUNING,
284249997Swkoszek			  8 << ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT);
285249997Swkoszek
286249997Swkoszek	/* Handle PHY options. */
287249997Swkoszek	if (zy7_phy_config(dev, sc->sc_io_tag, bsh) < 0) {
288249997Swkoszek		device_printf(dev, "Cannot config phy!\n");
289249997Swkoszek		zy7_ehci_detach(dev);
290249997Swkoszek		return (EIO);
291249997Swkoszek	}
292249997Swkoszek
293249997Swkoszek	/* Init ehci. */
294249997Swkoszek	err = ehci_init(sc);
295249997Swkoszek	if (!err) {
296249997Swkoszek		sc->sc_flags |= EHCI_SCFLG_DONEINIT;
297249997Swkoszek		err = device_probe_and_attach(sc->sc_bus.bdev);
298249997Swkoszek	}
299249997Swkoszek	if (err) {
300249997Swkoszek		device_printf(dev, "USB init failed err=%d\n", err);
301249997Swkoszek		zy7_ehci_detach(dev);
302249997Swkoszek		return (err);
303249997Swkoszek	}
304249997Swkoszek
305249997Swkoszek	return (0);
306249997Swkoszek}
307249997Swkoszek
308249997Swkoszekstatic int
309249997Swkoszekzy7_ehci_detach(device_t dev)
310249997Swkoszek{
311249997Swkoszek	ehci_softc_t *sc = device_get_softc(dev);
312249997Swkoszek
313308402Shselasky	/* during module unload there are lots of children leftover */
314308402Shselasky	device_delete_children(dev);
315308402Shselasky
316249997Swkoszek	sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
317249997Swkoszek
318249997Swkoszek	if (sc->sc_irq_res && sc->sc_intr_hdl)
319249997Swkoszek		/* call ehci_detach() after ehci_init() called after
320249997Swkoszek		 * successful bus_setup_intr().
321249997Swkoszek		 */
322249997Swkoszek		ehci_detach(sc);
323308402Shselasky
324249997Swkoszek	if (sc->sc_irq_res) {
325249997Swkoszek		if (sc->sc_intr_hdl != NULL)
326249997Swkoszek			bus_teardown_intr(dev, sc->sc_irq_res,
327249997Swkoszek					  sc->sc_intr_hdl);
328249997Swkoszek		bus_release_resource(dev, SYS_RES_IRQ,
329249997Swkoszek			     rman_get_rid(sc->sc_irq_res), sc->sc_irq_res);
330249997Swkoszek	}
331249997Swkoszek
332249997Swkoszek	if (sc->sc_io_res)
333249997Swkoszek		bus_release_resource(dev, SYS_RES_MEMORY,
334249997Swkoszek			     rman_get_rid(sc->sc_io_res), sc->sc_io_res);
335249997Swkoszek	usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
336249997Swkoszek
337249997Swkoszek	return (0);
338249997Swkoszek}
339249997Swkoszek
340249997Swkoszekstatic device_method_t ehci_methods[] = {
341249997Swkoszek	/* Device interface */
342249997Swkoszek	DEVMETHOD(device_probe,		zy7_ehci_probe),
343249997Swkoszek	DEVMETHOD(device_attach, 	zy7_ehci_attach),
344249997Swkoszek	DEVMETHOD(device_detach, 	zy7_ehci_detach),
345249997Swkoszek	DEVMETHOD(device_suspend, 	bus_generic_suspend),
346249997Swkoszek	DEVMETHOD(device_resume, 	bus_generic_resume),
347249997Swkoszek	DEVMETHOD(device_shutdown, 	bus_generic_shutdown),
348249997Swkoszek
349249997Swkoszek	/* Bus interface */
350249997Swkoszek	DEVMETHOD(bus_print_child, bus_generic_print_child),
351249997Swkoszek
352249997Swkoszek	DEVMETHOD_END
353249997Swkoszek};
354249997Swkoszek
355249997Swkoszekstatic driver_t ehci_driver = {
356249997Swkoszek	"ehci",
357249997Swkoszek	ehci_methods,
358249997Swkoszek	sizeof(struct ehci_softc),
359249997Swkoszek};
360249997Swkoszekstatic devclass_t ehci_devclass;
361249997Swkoszek
362249997SwkoszekDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL);
363249997SwkoszekMODULE_DEPEND(ehci, usb, 1, 1, 1);
364