1/* $NetBSD: ingenic_ehci.c,v 1.8 2021/08/07 16:18:59 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2015 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: ingenic_ehci.c,v 1.8 2021/08/07 16:18:59 thorpej Exp $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/device.h> 35#include <sys/mutex.h> 36#include <sys/bus.h> 37#include <sys/workqueue.h> 38 39#include <mips/ingenic/ingenic_var.h> 40#include <mips/ingenic/ingenic_regs.h> 41 42#include <dev/usb/usb.h> 43#include <dev/usb/usbdi.h> 44#include <dev/usb/usbdivar.h> 45#include <dev/usb/usb_mem.h> 46 47#include <dev/usb/ehcireg.h> 48#include <dev/usb/ehcivar.h> 49 50#include <dev/usb/usbdevs.h> 51 52#include "opt_ingenic.h" 53#include "ohci.h" 54 55static int ingenic_ehci_match(device_t, struct cfdata *, void *); 56static void ingenic_ehci_attach(device_t, device_t, void *); 57 58CFATTACH_DECL_NEW(ingenic_ehci, sizeof(struct ehci_softc), 59 ingenic_ehci_match, ingenic_ehci_attach, NULL, NULL); 60 61#if NOHCI > 0 62extern device_t ingenic_ohci; 63#endif 64 65/* ARGSUSED */ 66static int 67ingenic_ehci_match(device_t parent, struct cfdata *match, void *aux) 68{ 69 struct apbus_attach_args *aa = aux; 70 71 if (strcmp(aa->aa_name, "ehci") != 0) 72 return 0; 73 74 return 1; 75} 76 77static int 78ingenic_ehci_enable(struct ehci_softc *sc) 79{ 80 uint32_t reg; 81 82 /* Togle VBUS pin */ 83 gpio_set(5, 15, 0); 84 delay(250000); 85 gpio_set(5, 15, 1); 86 delay(250000); 87 88 /* Enable OTG, should not be necessary since we use PLL clock */ 89 reg = readreg(JZ_USBPCR); 90 reg &= ~(PCR_OTG_DISABLE); 91 writereg(JZ_USBPCR, reg); 92 93 /* Select CORE as PLL reference */ 94 reg = readreg(JZ_USBPCR1); 95 reg |= PCR_REFCLK_CORE; 96 writereg(JZ_USBPCR1, reg); 97 98 /* Configure OTG PHY clock frequency */ 99 reg = readreg(JZ_USBPCR1); 100 reg &= ~PCR_CLK_M; 101 reg |= PCR_CLK_48; 102 writereg(JZ_USBPCR1, reg); 103 104 /* Do not force port1 to suspend mode */ 105 reg = readreg(JZ_OPCR); 106 reg |= OPCR_SPENDN1; 107 writereg(JZ_OPCR, reg); 108 109 /* D- pulldown */ 110 reg = readreg(JZ_USBPCR1); 111 reg |= PCR_DMPD1; 112 writereg(JZ_USBPCR1, reg); 113 114 /* D+ pulldown */ 115 reg = readreg(JZ_USBPCR1); 116 reg |= PCR_DPPD1; 117 writereg(JZ_USBPCR1, reg); 118 119 /* 16 bit bus witdth for port 1 (and 0) */ 120 reg = readreg(JZ_USBPCR1); 121 reg |= PCR_WORD_I_F1 | PCR_WORD_I_F0; 122 writereg(JZ_USBPCR1, reg); 123 124 /* Reset USB */ 125 reg = readreg(JZ_USBPCR); 126 reg |= PCR_POR; 127 writereg(JZ_USBPCR, reg); 128 delay(1); 129 reg = readreg(JZ_USBPCR); 130 reg &= ~(PCR_POR); 131 writereg(JZ_USBPCR, reg); 132 133 /* Soft-reset USB */ 134 reg = readreg(JZ_SRBC); 135 reg |= (1 << 14); 136 writereg(JZ_SRBC, reg); 137 /* 300ms */ 138 delay(300000); 139 140 reg = readreg(JZ_SRBC); 141 reg &= ~(1 << 14); 142 writereg(JZ_SRBC, reg); 143 144 /* 300ms */ 145 delay(300000); 146 147 return (0); 148} 149 150/* ARGSUSED */ 151static void 152ingenic_ehci_attach(device_t parent, device_t self, void *aux) 153{ 154 struct ehci_softc *sc = device_private(self); 155 struct apbus_attach_args *aa = aux; 156 void *ih; 157 int error; 158 uint32_t reg; 159 160 sc->sc_dev = self; 161 162 sc->iot = aa->aa_bst; 163 sc->sc_bus.ub_dmatag = aa->aa_dmat; 164 sc->sc_bus.ub_hcpriv = sc; 165 sc->sc_size = 0x1000; 166 sc->sc_bus.ub_revision = USBREV_2_0; 167 168 if (aa->aa_addr == 0) 169 aa->aa_addr = JZ_EHCI_BASE; 170 171 error = bus_space_map(aa->aa_bst, aa->aa_addr, 0x1000, 0, &sc->ioh); 172 if (error) { 173 aprint_error_dev(self, 174 "can't map registers for %s: %d\n", aa->aa_name, error); 175 return; 176 } 177 178 aprint_naive(": EHCI USB controller\n"); 179 aprint_normal(": EHCI USB controller\n"); 180 181 ingenic_ehci_enable(sc); 182 183 /* Disable EHCI interrupts */ 184 bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0); 185 186 ih = evbmips_intr_establish(aa->aa_irq, ehci_intr, sc); 187 188 if (ih == NULL) { 189 aprint_error_dev(self, "failed to establish interrupt %d\n", 190 aa->aa_irq); 191 goto fail; 192 } 193 194#if NOHCI > 0 195 if (ingenic_ohci != NULL) { 196 sc->sc_ncomp = 1; 197 sc->sc_comps[0] = ingenic_ohci; 198 } else 199 sc->sc_ncomp = 0; 200#else 201 sc->sc_ncomp = 0; 202 sc->sc_npcomp = 0; 203#endif 204 205 error = ehci_init(sc); 206 if (error) { 207 aprint_error_dev(self, "init failed, error=%d\n", error); 208 goto fail; 209 } 210 211 /* 212 * voodoo from the linux driver: 213 * select utmi data bus width of controller to 16bit 214 */ 215 reg = bus_space_read_4(sc->iot, sc->ioh, 0xb0); 216 reg |= 1 << 6; 217 bus_space_write_4(sc->iot, sc->ioh, 0xb0, reg); 218 219 /* Attach USB device */ 220 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint, CFARGS_NONE); 221 222 return; 223 224fail: 225 if (ih) { 226 evbmips_intr_disestablish(ih); 227 } 228 bus_space_unmap(sc->iot, sc->ioh, 0x1000); 229} 230