1 /* $OpenBSD: ohci_fdt.c,v 1.3 2021/10/24 17:52:26 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2019 David Gwynne <dlg@openbsd.org> 5 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 #include <machine/intr.h> 26 #include <machine/bus.h> 27 #include <machine/fdt.h> 28 29 #include <dev/ofw/openfirm.h> 30 #include <dev/ofw/ofw_clock.h> 31 #include <dev/ofw/ofw_pinctrl.h> 32 #include <dev/ofw/ofw_regulator.h> 33 #include <dev/ofw/fdt.h> 34 35 #include <dev/usb/usb.h> 36 #include <dev/usb/usbdi.h> 37 #include <dev/usb/usbdivar.h> 38 #include <dev/usb/usb_mem.h> 39 40 #include <dev/usb/ohcireg.h> 41 #include <dev/usb/ohcivar.h> 42 43 struct ohci_fdt_softc { 44 struct ohci_softc sc; 45 int sc_node; 46 void *sc_ih; 47 }; 48 49 int ohci_fdt_match(struct device *, void *, void *); 50 void ohci_fdt_attach(struct device *, struct device *, void *); 51 int ohci_fdt_detach(struct device *, int); 52 53 const struct cfattach ohci_fdt_ca = { 54 sizeof(struct ohci_fdt_softc), 55 ohci_fdt_match, 56 ohci_fdt_attach, 57 ohci_fdt_detach, 58 ohci_activate 59 }; 60 61 void ohci_fdt_attach_deferred(struct device *); 62 63 int 64 ohci_fdt_match(struct device *parent, void *match, void *aux) 65 { 66 struct fdt_attach_args *faa = aux; 67 68 return OF_is_compatible(faa->fa_node, "generic-ohci"); 69 } 70 71 void 72 ohci_fdt_attach(struct device *parent, struct device *self, void *aux) 73 { 74 struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self; 75 struct fdt_attach_args *faa = aux; 76 char *devname = sc->sc.sc_bus.bdev.dv_xname; 77 78 if (faa->fa_nreg < 1) { 79 printf(": no registers\n"); 80 return; 81 } 82 83 sc->sc_node = faa->fa_node; 84 sc->sc.iot = faa->fa_iot; 85 sc->sc.sc_bus.dmatag = faa->fa_dmat; 86 sc->sc.sc_size = faa->fa_reg[0].size; 87 88 if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr, 89 faa->fa_reg[0].size, 0, &sc->sc.ioh)) { 90 printf(": can't map registers\n"); 91 goto out; 92 } 93 94 pinctrl_byname(sc->sc_node, "default"); 95 96 clock_enable_all(sc->sc_node); 97 reset_deassert_all(sc->sc_node); 98 99 /* Record what interrupts were enabled by SMM/BIOS. */ 100 sc->sc.sc_intre = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 101 OHCI_INTERRUPT_ENABLE); 102 103 /* Disable interrupts, so we don't get any spurious ones. */ 104 bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE, 105 OHCI_MIE); 106 107 bus_space_barrier(sc->sc.iot, sc->sc.ioh, 0, sc->sc.sc_size, 108 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE); 109 bus_space_write_4(sc->sc.iot, sc->sc.ioh, 110 OHCI_INTERRUPT_DISABLE, OHCI_MIE); 111 112 /* Map and establish the interrupt. */ 113 splassert(IPL_USB); 114 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_USB, 115 ohci_intr, &sc->sc, devname); 116 if (sc->sc_ih == NULL) { 117 printf(": can't establish interrupt\n"); 118 goto disable_clocks; 119 } 120 printf(": "); 121 122 strlcpy(sc->sc.sc_vendor, "Generic", sizeof(sc->sc.sc_vendor)); 123 124 /* Display revision. Legacy handover isn't needed as there's no smm */ 125 if (ohci_checkrev(&sc->sc) != USBD_NORMAL_COMPLETION) { 126 goto disestablish_intr; 127 } 128 129 /* Ignore interrupts for now */ 130 sc->sc.sc_bus.dying = 1; 131 132 config_defer(self, ohci_fdt_attach_deferred); 133 134 return; 135 136 disestablish_intr: 137 fdt_intr_disestablish(sc->sc_ih); 138 sc->sc_ih = NULL; 139 disable_clocks: 140 clock_disable_all(sc->sc_node); 141 142 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 143 sc->sc.sc_size = 0; 144 out: 145 return; 146 } 147 148 void 149 ohci_fdt_attach_deferred(struct device *self) 150 { 151 struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self; 152 usbd_status r; 153 int s; 154 155 s = splusb(); 156 157 sc->sc.sc_bus.dying = 0; 158 159 r = ohci_init(&sc->sc); 160 161 splx(s); 162 if (r != USBD_NORMAL_COMPLETION) { 163 printf("%s: init failed, error=%d\n", 164 sc->sc.sc_bus.bdev.dv_xname, r); 165 fdt_intr_disestablish(sc->sc_ih); 166 sc->sc_ih = NULL; 167 clock_disable_all(sc->sc_node); 168 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 169 sc->sc.sc_size = 0; 170 return; 171 } 172 173 /* Attach usb device. */ 174 config_found(self, &sc->sc.sc_bus, usbctlprint); 175 } 176 177 int 178 ohci_fdt_detach(struct device *self, int flags) 179 { 180 struct ohci_fdt_softc *sc = (struct ohci_fdt_softc *)self; 181 int rv; 182 183 rv = ohci_detach(self, flags); 184 if (rv) 185 return rv; 186 187 if (sc->sc_ih != NULL) { 188 fdt_intr_disestablish(sc->sc_ih); 189 sc->sc_ih = NULL; 190 } 191 192 if (sc->sc.sc_size) { 193 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); 194 sc->sc.sc_size = 0; 195 } 196 197 clock_disable_all(sc->sc_node); 198 return 0; 199 } 200