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