1 /* $NetBSD: mpc5200_ohci.c,v 1.1 2026/06/27 13:28:35 rkujawa Exp $ */ 2 3 /*- 4 * Copyright (c) 2008, 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Robert Swindells. 9 * This code is derived from software contributed to The NetBSD Foundation 10 * by Radoslaw Kujawa. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * USB OHCI host controller on the Freescale MPC5200B SoC. 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: mpc5200_ohci.c,v 1.1 2026/06/27 13:28:35 rkujawa Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/bus.h> 43 #include <sys/device.h> 44 #include <sys/intr.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 48 #include <dev/ofw/openfirm.h> 49 50 #include <dev/usb/usb.h> 51 #include <dev/usb/usbdi.h> 52 #include <dev/usb/usbdivar.h> 53 #include <dev/usb/usb_mem.h> 54 55 #include <dev/usb/ohcireg.h> 56 #include <dev/usb/ohcivar.h> 57 58 #include <powerpc/mpc5200/obiovar.h> 59 #include <powerpc/mpc5200/mpc5200reg.h> 60 #include <powerpc/mpc5200/mpc5200_ohcivar.h> 61 62 static int mpc5200_ohci_match(device_t, cfdata_t, void *); 63 static void mpc5200_ohci_attach(device_t, device_t, void *); 64 65 CFATTACH_DECL2_NEW(ohci_obio, sizeof(struct mpc5200_ohci_softc), 66 mpc5200_ohci_match, mpc5200_ohci_attach, NULL, ohci_activate, 67 NULL, ohci_childdet); 68 69 static int 70 mpc5200_ohci_match(device_t parent, cfdata_t cf, void *aux) 71 { 72 struct obio_attach_args *oba = aux; 73 char compat[40]; 74 int len; 75 76 if (strcmp(oba->obio_name, "usb") == 0) 77 return 1; 78 79 len = OF_getprop(oba->obio_node, "compatible", compat, sizeof(compat)); 80 if (len > 0 && 81 (strcmp(compat, "mpc5200-ohci") == 0 || 82 strcmp(compat, "mpc5200b-ohci") == 0 || 83 strcmp(compat, "mpc5200-usb-ohci") == 0 || 84 strcmp(compat, "mpc5200b-usb-ohci") == 0)) 85 return 1; 86 87 return 0; 88 } 89 90 static void 91 mpc5200_ohci_attach(device_t parent, device_t self, void *aux) 92 { 93 struct mpc5200_ohci_softc *msc = device_private(self); 94 ohci_softc_t *sc = &msc->sc; 95 struct obio_attach_args *oba = aux; 96 bus_addr_t addr; 97 bus_size_t size; 98 int ist, error; 99 100 sc->sc_dev = self; 101 sc->sc_bus.ub_hcpriv = sc; 102 sc->sc_bus.ub_dmatag = oba->obio_dmat; 103 sc->iot = oba->obio_bst; 104 sc->sc_endian = OHCI_HOST_ENDIAN; 105 106 /* 107 * Honour the firmware, but fall back to SoC MBAR offset. 108 */ 109 addr = oba->obio_addr != 0 ? oba->obio_addr : 110 MPC5200_MBAR_DEFAULT + MPC5200_REG_USB; 111 size = oba->obio_size != 0 ? oba->obio_size : MPC5200_USB_SIZE; 112 sc->sc_size = size; 113 114 aprint_naive("\n"); 115 aprint_normal(": MPC5200 USB OHCI\n"); 116 117 if (bus_space_map(sc->iot, addr, size, 0, &sc->ioh) != 0) { 118 aprint_error_dev(self, "can't map registers\n"); 119 return; 120 } 121 122 /* Mask all controller interrupts until the MI driver is ready. */ 123 bus_space_write_4(sc->iot, sc->ioh, OHCI_INTERRUPT_DISABLE, 124 OHCI_ALL_INTRS); 125 126 if (!obio_decode_interrupt(oba->obio_node, 0, &msc->sc_irq, &ist)) { 127 aprint_error_dev(self, "can't decode interrupt\n"); 128 goto fail_unmap; 129 } 130 msc->sc_ih = intr_establish(msc->sc_irq, ist, IPL_USB, ohci_intr, sc); 131 if (msc->sc_ih == NULL) { 132 aprint_error_dev(self, "can't establish interrupt\n"); 133 goto fail_unmap; 134 } 135 136 error = ohci_init(sc); 137 if (error != 0) { 138 aprint_error_dev(self, "init failed, error=%d\n", error); 139 goto fail_intr; 140 } 141 142 if (!pmf_device_register1(self, NULL, NULL, ohci_shutdown)) 143 aprint_error_dev(self, "can't establish power handler\n"); 144 145 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint, 146 CFARGS_NONE); 147 return; 148 149 fail_intr: 150 intr_disestablish(msc->sc_ih); 151 msc->sc_ih = NULL; 152 fail_unmap: 153 bus_space_unmap(sc->iot, sc->ioh, size); 154 sc->sc_size = 0; 155 } 156