zy7_ehci.c revision 249997
154359Sroberto/*-
2182007Sroberto * Copyright (C) 2012-2013, Thomas Skibo.
354359Sroberto * All rights reserved.
4182007Sroberto *
5182007Sroberto * Redistribution and use in source and binary forms, with or without
654359Sroberto * modification, are permitted provided that the following conditions are met:
754359Sroberto * * Redistributions of source code must retain the above copyright
8182007Sroberto *   notice, this list of conditions and the following disclaimer.
9182007Sroberto * * Redistributions in binary form must reproduce the above copyright
10182007Sroberto *   notice, this list of conditions and the following disclaimer in the
11182007Sroberto *   documentation and/or other materials provided with the distribution.
12182007Sroberto * * The names of contributors may not be used to endorse or promote products
13182007Sroberto *   derived from this software without specific prior written permission.
14182007Sroberto *
15182007Sroberto * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16182007Sroberto * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17182007Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18182007Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL AUTHORS OR CONTRIBUTORS BE LIABLE FOR
19182007Sroberto * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20182007Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21182007Sroberto * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22182007Sroberto * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23182007Sroberto * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24182007Sroberto * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25182007Sroberto *
26182007Sroberto */
27182007Sroberto
28182007Sroberto/* A host-controller driver for Zynq-7000's USB OTG controller.
29182007Sroberto *
30182007Sroberto * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
31182007Sroberto * (v1.4) November 16, 2012.  Xilinx doc UG585.  Ch. 15 covers the USB
32182007Sroberto * controller and register definitions are in appendix B.34.
33182007Sroberto */
3454359Sroberto
3554359Sroberto
36285612Sdelphij#include <sys/cdefs.h>
3754359Sroberto__FBSDID("$FreeBSD: head/sys/arm/xilinx/zy7_ehci.c 249997 2013-04-27 22:38:29Z wkoszek $");
3854359Sroberto
3954359Sroberto#include <sys/param.h>
4054359Sroberto#include <sys/systm.h>
4154359Sroberto#include <sys/bus.h>
4254359Sroberto#include <sys/conf.h>
4354359Sroberto#include <sys/kernel.h>
4454359Sroberto#include <sys/lock.h>
4554359Sroberto#include <sys/module.h>
46285612Sdelphij#include <sys/mutex.h>
4754359Sroberto#include <sys/condvar.h>
4854359Sroberto#include <sys/resource.h>
4954359Sroberto#include <sys/rman.h>
5054359Sroberto
51285612Sdelphij#include <machine/bus.h>
52285612Sdelphij#include <machine/resource.h>
5354359Sroberto#include <machine/stdarg.h>
5454359Sroberto
5554359Sroberto#include <dev/fdt/fdt_common.h>
5654359Sroberto#include <dev/ofw/ofw_bus.h>
5754359Sroberto#include <dev/ofw/ofw_bus_subr.h>
5854359Sroberto
5954359Sroberto#include <dev/usb/usb.h>
6054359Sroberto#include <dev/usb/usbdi.h>
6154359Sroberto
6254359Sroberto#include <dev/usb/usb_core.h>
6354359Sroberto#include <dev/usb/usb_busdma.h>
6454359Sroberto#include <dev/usb/usb_process.h>
6554359Sroberto#include <dev/usb/usb_util.h>
6654359Sroberto
67285612Sdelphij#include <dev/usb/usb_controller.h>
68285612Sdelphij#include <dev/usb/usb_bus.h>
69285612Sdelphij#include <dev/usb/controller/ehci.h>
70285612Sdelphij#include <dev/usb/controller/ehcireg.h>
7154359Sroberto
7254359Sroberto
7354359Sroberto/* Register definitions. */
7454359Sroberto#define ZY7_USB_ID				0x0000
7554359Sroberto#define ZY7_USB_HWGENERAL			0x0004
7654359Sroberto#define ZY7_USB_HWHOST				0x0008
7754359Sroberto#define ZY7_USB_HWDEVICE			0x000c
7854359Sroberto#define ZY7_USB_HWTXBUF				0x0010
79285612Sdelphij#define ZY7_USB_HWRXBUF				0x0014
8054359Sroberto#define ZY7_USB_GPTIMER0LD			0x0080
8154359Sroberto#define ZY7_USB_GPTIMER0CTRL			0x0084
8254359Sroberto#define ZY7_USB_GPTIMER1LD			0x0088
8354359Sroberto#define ZY7_USB_GPTIMER1CTRL			0x008c
8454359Sroberto#define ZY7_USB_SBUSCFG				0x0090
8554359Sroberto#define ZY7_USB_CAPLENGTH_HCIVERSION		0x0100
8654359Sroberto#define ZY7_USB_HCSPARAMS			0x0104
8754359Sroberto#define ZY7_USB_HCCPARAMS			0x0108
8854359Sroberto#define ZY7_USB_DCIVERSION			0x0120
8954359Sroberto#define ZY7_USB_DCCPARAMS			0x0124
90285612Sdelphij#define ZY7_USB_USBCMD				0x0140
91285612Sdelphij#define ZY7_USB_USBSTS				0x0144
9254359Sroberto#define ZY7_USB_USBINTR				0x0148
9354359Sroberto#define ZY7_USB_FRINDEX				0x014c
9454359Sroberto#define ZY7_USB_PERIODICLISTBASE_DEICEADDR 	0x0154
9554359Sroberto#define ZY7_USB_ASYNCLISTADDR_ENDPOINTLISTADDR 	0x0158
9654359Sroberto#define ZY7_USB_TTCTRL				0x015c
9754359Sroberto#define ZY7_USB_BURSTSIZE			0x0160
9854359Sroberto#define ZY7_USB_TXFILLTUNING			0x0164
9954359Sroberto#define   ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT		16
10054359Sroberto#define   ZY7_USB_TXFILLTUNING_TXFIFOTHRES_MASK		(0x3f<<16)
10154359Sroberto#define ZY7_USB_TXTFILLTUNING			0x0168
102285612Sdelphij#define ZY7_USB_IC_USB				0x016c
10354359Sroberto#define ZY7_USB_ULPI_VIEWPORT			0x0170
10454359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_WU			(1<<31)
10554359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_RUN			(1<<30)
10654359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_RW			(1<<29)
10754359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_SS			(1<<27)
10854359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_PORT_MASK		(7<<24)
10954359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT		24
11054359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_ADDR_MASK		(0xff<<16)
111285612Sdelphij#define   ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT		16
11254359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_DATARD_MASK		(0xff<<8)
11354359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_DATARD_SHIFT		8
11454359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_DATAWR_MASK		(0xff<<0)
11554359Sroberto#define   ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT		0
11654359Sroberto#define ZY7_USB_ENDPTNAK			0x0178
11754359Sroberto#define ZY7_USB_ENDPTNAKEN			0x017c
11854359Sroberto#define ZY7_USB_CONFIGFLAG			0x0180
11954359Sroberto#define ZY7_USB_PORTSC(n)			(0x0180+4*(n))
120285612Sdelphij#define   ZY7_USB_PORTSC_PTS_MASK			(3<<30)
12154359Sroberto#define   ZY7_USB_PORTSC_PTS_SHIFT			30
12254359Sroberto#define   ZY7_USB_PORTSC_PTS_UTMI			(0<<30)
12354359Sroberto#define   ZY7_USB_PORTSC_PTS_ULPI			(2<<30)
12454359Sroberto#define   ZY7_USB_PORTSC_PTS_SERIAL			(3<<30)
12554359Sroberto#define   ZY7_USB_PORTSC_PTW				(1<<28)
12654359Sroberto#define   ZY7_USB_PORTSC_PTS2				(1<<25)
12754359Sroberto#define ZY7_USB_OTGSC				0x01a4
12854359Sroberto#define ZY7_USB_USBMODE				0x01a8
129285612Sdelphij#define ZY7_USB_ENDPTSETUPSTAT			0x01ac
13054359Sroberto#define ZY7_USB_ENDPTPRIME			0x01b0
13154359Sroberto#define ZY7_USB_ENDPTFLUSH			0x01b4
13254359Sroberto#define ZY7_USB_ENDPTSTAT			0x01b8
13354359Sroberto#define ZY7_USB_ENDPTCOMPLETE			0x01bc
13454359Sroberto#define ZY7_USB_ENDPTCTRL(n)			(0x01c0+4*(n))
13554359Sroberto
13654359Sroberto#define EHCI_REG_OFFSET	ZY7_USB_CAPLENGTH_HCIVERSION
137285612Sdelphij#define EHCI_REG_SIZE	0x100
13854359Sroberto
13954359Srobertostatic int
140285612Sdelphijzy7_phy_config(device_t dev, bus_space_tag_t io_tag, bus_space_handle_t bsh)
14154359Sroberto{
142285612Sdelphij	phandle_t node;
143285612Sdelphij	char buf[64];
144285612Sdelphij	uint32_t portsc;
145285612Sdelphij	int tries;
146285612Sdelphij
147285612Sdelphij	node = ofw_bus_get_node(dev);
148285612Sdelphij
14954359Sroberto	if (OF_getprop(node, "phy_type", buf, sizeof(buf)) > 0) {
150285612Sdelphij		portsc = bus_space_read_4(io_tag, bsh, ZY7_USB_PORTSC(1));
15154359Sroberto		portsc &= ~(ZY7_USB_PORTSC_PTS_MASK | ZY7_USB_PORTSC_PTW |
15254359Sroberto			    ZY7_USB_PORTSC_PTS2);
15354359Sroberto
15454359Sroberto		if (strcmp(buf,"ulpi") == 0)
15554359Sroberto			portsc |= ZY7_USB_PORTSC_PTS_ULPI;
15654359Sroberto		else if (strcmp(buf,"utmi") == 0)
15754359Sroberto			portsc |= ZY7_USB_PORTSC_PTS_UTMI;
15854359Sroberto		else if (strcmp(buf,"utmi-wide") == 0)
159285612Sdelphij			portsc |= (ZY7_USB_PORTSC_PTS_UTMI |
16054359Sroberto				   ZY7_USB_PORTSC_PTW);
16154359Sroberto		else if (strcmp(buf, "serial") == 0)
16254359Sroberto			portsc |= ZY7_USB_PORTSC_PTS_SERIAL;
16354359Sroberto
16454359Sroberto		bus_space_write_4(io_tag, bsh, ZY7_USB_PORTSC(1), portsc);
16554359Sroberto	}
16654359Sroberto
16754359Sroberto	if (OF_getprop(node, "phy_vbus_ext", buf, sizeof(buf)) >= 0) {
16854359Sroberto
16954359Sroberto		/* Tell PHY that VBUS is supplied externally. */
170285612Sdelphij		bus_space_write_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT,
171285612Sdelphij				  ZY7_USB_ULPI_VIEWPORT_RUN |
172285612Sdelphij				  ZY7_USB_ULPI_VIEWPORT_RW |
17354359Sroberto				  (0 << ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT) |
17454359Sroberto				  (0x0b << ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT) |
17554359Sroberto				  (0x60 << ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT)
17654359Sroberto			);
17754359Sroberto
17854359Sroberto		tries = 100;
17954359Sroberto		while ((bus_space_read_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT) &
18054359Sroberto			ZY7_USB_ULPI_VIEWPORT_RUN) != 0) {
181285612Sdelphij			if (--tries < 0)
18254359Sroberto				return (-1);
18354359Sroberto			DELAY(1);
18454359Sroberto		}
18554359Sroberto	}
18654359Sroberto
18754359Sroberto	return (0);
18854359Sroberto}
18954359Sroberto
19054359Srobertostatic int
19154359Srobertozy7_ehci_probe(device_t dev)
19254359Sroberto{
19354359Sroberto
19454359Sroberto	if (!ofw_bus_is_compatible(dev, "xlnx,zy7_ehci"))
19554359Sroberto		return (ENXIO);
19654359Sroberto
19754359Sroberto	device_set_desc(dev, "Zynq-7000 EHCI USB 2.0 controller");
19854359Sroberto	return (0);
19954359Sroberto}
20054359Sroberto
20154359Srobertostatic int zy7_ehci_detach(device_t dev);
20254359Sroberto
20354359Srobertostatic int
20454359Srobertozy7_ehci_attach(device_t dev)
205285612Sdelphij{
20654359Sroberto	ehci_softc_t *sc = device_get_softc(dev);
20754359Sroberto	bus_space_handle_t bsh;
20854359Sroberto	int err, rid;
20954359Sroberto
21054359Sroberto	/* initialize some bus fields */
21154359Sroberto	sc->sc_bus.parent = dev;
21254359Sroberto	sc->sc_bus.devices = sc->sc_devices;
213182007Sroberto	sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
214182007Sroberto
215182007Sroberto	/* get all DMA memory */
21654359Sroberto	if (usb_bus_mem_alloc_all(&sc->sc_bus,
21754359Sroberto	    USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc))
218182007Sroberto		return (ENOMEM);
219182007Sroberto
220285612Sdelphij	/* Allocate memory. */
221285612Sdelphij	rid = 0;
222182007Sroberto	sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
223182007Sroberto					       &rid, RF_ACTIVE);
224182007Sroberto	if (sc->sc_io_res == NULL) {
225182007Sroberto		device_printf(dev, "Can't allocate memory");
226182007Sroberto		zy7_ehci_detach(dev);
227182007Sroberto		return (ENOMEM);
228182007Sroberto	}
229182007Sroberto
230182007Sroberto	sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
231182007Sroberto	bsh = rman_get_bushandle(sc->sc_io_res);
23254359Sroberto	sc->sc_io_size = EHCI_REG_SIZE;
233182007Sroberto
234182007Sroberto	if (bus_space_subregion(sc->sc_io_tag, bsh, EHCI_REG_OFFSET,
235182007Sroberto				sc->sc_io_size, &sc->sc_io_hdl) != 0)
236182007Sroberto		panic("%s: unable to subregion USB host registers",
237285612Sdelphij		      device_get_name(dev));
238182007Sroberto
23954359Sroberto	/* Allocate IRQ. */
240182007Sroberto	rid = 0;
24154359Sroberto	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
242182007Sroberto						RF_ACTIVE);
243182007Sroberto	if (sc->sc_irq_res == NULL) {
244182007Sroberto		device_printf(dev, "Can't allocate IRQ\n");
245182007Sroberto		zy7_ehci_detach(dev);
246285612Sdelphij		return (ENOMEM);
247182007Sroberto	}
248182007Sroberto
249285612Sdelphij	/* Add USB device */
250182007Sroberto	sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
251182007Sroberto	if (!sc->sc_bus.bdev) {
25254359Sroberto		device_printf(dev, "Could not add USB device\n");
253182007Sroberto		zy7_ehci_detach(dev);
25454359Sroberto		return (ENXIO);
25554359Sroberto	}
256285612Sdelphij	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
25754359Sroberto	device_set_desc(sc->sc_bus.bdev, "Zynq-7000 ehci USB 2.0 controller");
25854359Sroberto
259182007Sroberto	strcpy(sc->sc_vendor, "Xilinx"); /* or IP vendor? */
260285612Sdelphij
261285612Sdelphij	/* Activate the interrupt */
262285612Sdelphij	err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
26354359Sroberto			     NULL, (driver_intr_t *)ehci_interrupt, sc,
26454359Sroberto			     &sc->sc_intr_hdl);
265182007Sroberto	if (err) {
266182007Sroberto		device_printf(dev, "Cannot setup IRQ\n");
267182007Sroberto		zy7_ehci_detach(dev);
268182007Sroberto		return (err);
269285612Sdelphij	}
270182007Sroberto
271285612Sdelphij	/* Customization. */
272285612Sdelphij	sc->sc_flags |= EHCI_SCFLG_SETMODE | EHCI_SCFLG_TT |
273182007Sroberto		EHCI_SCFLG_NORESTERM;
274182007Sroberto
275285612Sdelphij	/* Modify FIFO burst threshold from 2 to 8. */
276285612Sdelphij	bus_space_write_4(sc->sc_io_tag, bsh,
27754359Sroberto			  ZY7_USB_TXFILLTUNING,
27854359Sroberto			  8 << ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT);
27954359Sroberto
28054359Sroberto	/* Handle PHY options. */
281182007Sroberto	if (zy7_phy_config(dev, sc->sc_io_tag, bsh) < 0) {
282182007Sroberto		device_printf(dev, "Cannot config phy!\n");
283182007Sroberto		zy7_ehci_detach(dev);
28454359Sroberto		return (EIO);
28554359Sroberto	}
286182007Sroberto
287285612Sdelphij	/* Init ehci. */
288285612Sdelphij	err = ehci_init(sc);
289182007Sroberto	if (!err) {
29054359Sroberto		sc->sc_flags |= EHCI_SCFLG_DONEINIT;
29154359Sroberto		err = device_probe_and_attach(sc->sc_bus.bdev);
29254359Sroberto	}
29354359Sroberto	if (err) {
29454359Sroberto		device_printf(dev, "USB init failed err=%d\n", err);
29554359Sroberto		zy7_ehci_detach(dev);
29654359Sroberto		return (err);
29754359Sroberto	}
29854359Sroberto
299285612Sdelphij	return (0);
300285612Sdelphij}
301285612Sdelphij
30254359Srobertostatic int
30354359Srobertozy7_ehci_detach(device_t dev)
30454359Sroberto{
30554359Sroberto	ehci_softc_t *sc = device_get_softc(dev);
306285612Sdelphij
30754359Sroberto	sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
30854359Sroberto
30954359Sroberto	if (device_is_attached(dev))
310285612Sdelphij		bus_generic_detach(dev);
311285612Sdelphij
31254359Sroberto	if (sc->sc_irq_res && sc->sc_intr_hdl)
31354359Sroberto		/* call ehci_detach() after ehci_init() called after
31454359Sroberto		 * successful bus_setup_intr().
31554359Sroberto		 */
31654359Sroberto		ehci_detach(sc);
31754359Sroberto	if (sc->sc_bus.bdev) {
31854359Sroberto		device_detach(sc->sc_bus.bdev);
31954359Sroberto		device_delete_child(dev, sc->sc_bus.bdev);
32054359Sroberto	}
32154359Sroberto	if (sc->sc_irq_res) {
32254359Sroberto		if (sc->sc_intr_hdl != NULL)
323285612Sdelphij			bus_teardown_intr(dev, sc->sc_irq_res,
324285612Sdelphij					  sc->sc_intr_hdl);
32554359Sroberto		bus_release_resource(dev, SYS_RES_IRQ,
32654359Sroberto			     rman_get_rid(sc->sc_irq_res), sc->sc_irq_res);
327285612Sdelphij	}
32854359Sroberto
32954359Sroberto	if (sc->sc_io_res)
33054359Sroberto		bus_release_resource(dev, SYS_RES_MEMORY,
33154359Sroberto			     rman_get_rid(sc->sc_io_res), sc->sc_io_res);
332285612Sdelphij	usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
33354359Sroberto
33454359Sroberto	return (0);
33554359Sroberto}
33654359Sroberto
33754359Srobertostatic device_method_t ehci_methods[] = {
338285612Sdelphij	/* Device interface */
339285612Sdelphij	DEVMETHOD(device_probe,		zy7_ehci_probe),
34054359Sroberto	DEVMETHOD(device_attach, 	zy7_ehci_attach),
34154359Sroberto	DEVMETHOD(device_detach, 	zy7_ehci_detach),
34254359Sroberto	DEVMETHOD(device_suspend, 	bus_generic_suspend),
34354359Sroberto	DEVMETHOD(device_resume, 	bus_generic_resume),
34454359Sroberto	DEVMETHOD(device_shutdown, 	bus_generic_shutdown),
34554359Sroberto
34654359Sroberto	/* Bus interface */
34754359Sroberto	DEVMETHOD(bus_print_child, bus_generic_print_child),
34854359Sroberto
34954359Sroberto	DEVMETHOD_END
35054359Sroberto};
351285612Sdelphij
35254359Srobertostatic driver_t ehci_driver = {
35354359Sroberto	"ehci",
35454359Sroberto	ehci_methods,
35554359Sroberto	sizeof(struct ehci_softc),
35654359Sroberto};
35754359Srobertostatic devclass_t ehci_devclass;
35854359Sroberto
35954359SrobertoDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL);
36054359SrobertoMODULE_DEPEND(ehci, usb, 1, 1, 1);
36154359Sroberto