1/*- 2 * Copyright (c) 2012 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Matt Thomas of 3am Software Foundry. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29#define USBH_PRIVATE 30 31#include "locators.h" 32 33#include <sys/cdefs.h> 34 35__KERNEL_RCSID(1, "$NetBSD: bcm53xx_usb.c,v 1.10 2021/08/07 16:18:43 thorpej Exp $"); 36 37#include <sys/param.h> 38#include <sys/bus.h> 39#include <sys/device.h> 40#include <sys/intr.h> 41#include <sys/systm.h> 42 43#include <arm/broadcom/bcm53xx_reg.h> 44#include <arm/broadcom/bcm53xx_var.h> 45 46#include <dev/usb/usb.h> 47#include <dev/usb/usbdi.h> 48#include <dev/usb/usbdivar.h> 49#include <dev/usb/usb_mem.h> 50 51#include <dev/usb/ohcireg.h> 52#include <dev/usb/ohcivar.h> 53 54#include <dev/usb/ehcireg.h> 55#include <dev/usb/ehcivar.h> 56 57struct bcmusb_softc { 58 device_t usbsc_dev; 59 bus_dma_tag_t usbsc_dmat; 60 bus_space_tag_t usbsc_bst; 61 bus_space_handle_t usbsc_ehci_bsh; 62 bus_space_handle_t usbsc_ohci_bsh; 63 64 device_t usbsc_ohci_dev; 65 device_t usbsc_ehci_dev; 66 void *usbsc_ohci_sc; 67 void *usbsc_ehci_sc; 68 void *usbsc_ih; 69}; 70 71struct bcmusb_attach_args { 72 const char *usbaa_name; 73 bus_dma_tag_t usbaa_dmat; 74 bus_space_tag_t usbaa_bst; 75 bus_space_handle_t usbaa_bsh; 76 bus_size_t usbaa_size; 77}; 78 79#ifdef OHCI_DEBUG 80#define OHCI_DPRINTF(x) if (ohcidebug) printf x 81extern int ohcidebug; 82#else 83#define OHCI_DPRINTF(x) 84#endif 85 86static int ohci_bcmusb_match(device_t, cfdata_t, void *); 87static void ohci_bcmusb_attach(device_t, device_t, void *); 88 89CFATTACH_DECL_NEW(ohci_bcmusb, sizeof(struct ohci_softc), 90 ohci_bcmusb_match, ohci_bcmusb_attach, NULL, NULL); 91 92static int 93ohci_bcmusb_match(device_t parent, cfdata_t cf, void *aux) 94{ 95 struct bcmusb_attach_args * const usbaa = aux; 96 97 if (strcmp(cf->cf_name, usbaa->usbaa_name)) 98 return 0; 99 100 return 1; 101} 102 103static void 104ohci_bcmusb_attach(device_t parent, device_t self, void *aux) 105{ 106 struct ohci_softc * const sc = device_private(self); 107 struct bcmusb_attach_args * const usbaa = aux; 108 109 sc->sc_dev = self; 110 111 sc->iot = usbaa->usbaa_bst; 112 sc->ioh = usbaa->usbaa_bsh; 113 sc->sc_size = usbaa->usbaa_size; 114 sc->sc_bus.ub_dmatag = usbaa->usbaa_dmat; 115 sc->sc_bus.ub_hcpriv = sc; 116 117 aprint_naive(": OHCI USB controller\n"); 118 aprint_normal(": OHCI USB controller\n"); 119 120 int error = ohci_init(sc); 121 if (error) { 122 aprint_error_dev(self, "init failed, error=%d\n", error); 123 return; 124 } 125 126 /* Attach usb device. */ 127 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint, CFARGS_NONE); 128} 129 130#ifdef EHCI_DEBUG 131#define EHCI_DPRINTF(x) if (ehcidebug) printf x 132extern int ehcidebug; 133#else 134#define EHCI_DPRINTF(x) 135#endif 136 137static int ehci_bcmusb_match(device_t, cfdata_t, void *); 138static void ehci_bcmusb_attach(device_t, device_t, void *); 139 140CFATTACH_DECL_NEW(ehci_bcmusb, sizeof(struct ehci_softc), 141 ehci_bcmusb_match, ehci_bcmusb_attach, NULL, NULL); 142 143static int 144ehci_bcmusb_match(device_t parent, cfdata_t cf, void *aux) 145{ 146 struct bcmusb_attach_args * const usbaa = aux; 147 148 if (strcmp(cf->cf_name, usbaa->usbaa_name)) 149 return 0; 150 151 return 1; 152} 153 154static void 155ehci_bcmusb_attach(device_t parent, device_t self, void *aux) 156{ 157 struct bcmusb_softc * const usbsc = device_private(parent); 158 struct ehci_softc * const sc = device_private(self); 159 struct bcmusb_attach_args * const usbaa = aux; 160 161 sc->sc_dev = self; 162 163 sc->iot = usbaa->usbaa_bst; 164 sc->ioh = usbaa->usbaa_bsh; 165 sc->sc_size = usbaa->usbaa_size; 166 sc->sc_bus.ub_dmatag = usbaa->usbaa_dmat; 167 sc->sc_bus.ub_hcpriv = sc; 168 sc->sc_bus.ub_revision = USBREV_2_0; 169 sc->sc_ncomp = 0; 170 if (usbsc->usbsc_ohci_dev != NULL) { 171 sc->sc_comps[sc->sc_ncomp++] = usbsc->usbsc_ohci_dev; 172 } 173 174 aprint_naive(": EHCI USB controller\n"); 175 aprint_normal(": ECHI USB controller\n"); 176 177 int error = ehci_init(sc); 178 if (error) { 179 aprint_error_dev(self, "init failed, error=%d\n", error); 180 return; 181 } 182 /* Attach usb device. */ 183 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint, CFARGS_NONE); 184} 185 186/* 187 * There's only IRQ shared between both OCHI and EHCI devices. 188 */ 189static int 190bcmusb_intr(void *arg) 191{ 192 struct bcmusb_softc * const usbsc = arg; 193 int rv0 = 0, rv1 = 0; 194 195 if (usbsc->usbsc_ohci_sc) 196 rv0 = ohci_intr(usbsc->usbsc_ohci_sc); 197 198 if (usbsc->usbsc_ehci_sc) 199 rv1 = ehci_intr(usbsc->usbsc_ehci_sc); 200 201 return rv0 ? rv0 : rv1; 202} 203 204static int bcmusb_ccb_match(device_t, cfdata_t, void *); 205static void bcmusb_ccb_attach(device_t, device_t, void *); 206 207CFATTACH_DECL_NEW(bcmusb_ccb, sizeof(struct bcmusb_softc), 208 bcmusb_ccb_match, bcmusb_ccb_attach, NULL, NULL); 209 210int 211bcmusb_ccb_match(device_t parent, cfdata_t cf, void *aux) 212{ 213 struct bcmccb_attach_args * const ccbaa = aux; 214 const struct bcm_locators * const loc = &ccbaa->ccbaa_loc; 215 216 if (strcmp(cf->cf_name, loc->loc_name) != 0) 217 return 0; 218 219 KASSERT(cf->cf_loc[BCMCCBCF_PORT] == BCMCCBCF_PORT_DEFAULT); 220 221 return 1; 222} 223 224#define OHCI_OFFSET (OHCI_BASE - EHCI_BASE) 225 226void 227bcmusb_ccb_attach(device_t parent, device_t self, void *aux) 228{ 229 struct bcmusb_softc * const usbsc = device_private(self); 230 const struct bcmccb_attach_args * const ccbaa = aux; 231 const struct bcm_locators * const loc = &ccbaa->ccbaa_loc; 232 233 usbsc->usbsc_bst = ccbaa->ccbaa_ccb_bst; 234 usbsc->usbsc_dmat = ccbaa->ccbaa_dmat; 235 236 bus_space_subregion(usbsc->usbsc_bst, ccbaa->ccbaa_ccb_bsh, 237 loc->loc_offset, 0x1000, &usbsc->usbsc_ehci_bsh); 238 bus_space_subregion(usbsc->usbsc_bst, ccbaa->ccbaa_ccb_bsh, 239 loc->loc_offset + OHCI_OFFSET, 0x1000, &usbsc->usbsc_ohci_bsh); 240 241 /* 242 * Bring the PHYs out of reset. 243 */ 244 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 245 USBH_PHY_CTRL_P0, USBH_PHY_CTRL_INIT); 246 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 247 USBH_PHY_CTRL_P1, USBH_PHY_CTRL_INIT); 248 249 /* 250 * Disable interrupts 251 */ 252 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ohci_bsh, 253 OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); 254 bus_size_t caplength = bus_space_read_1(usbsc->usbsc_bst, 255 usbsc->usbsc_ehci_bsh, EHCI_CAPLENGTH); 256 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 257 caplength + EHCI_USBINTR, 0); 258 259 aprint_naive("\n"); 260 aprint_normal("\n"); 261 262 struct bcmusb_attach_args usbaa_ohci = { 263 .usbaa_name = "ohci", 264 .usbaa_dmat = usbsc->usbsc_dmat, 265 .usbaa_bst = usbsc->usbsc_bst, 266 .usbaa_bsh = usbsc->usbsc_ohci_bsh, 267 .usbaa_size = 0x100, 268 }; 269 270 usbsc->usbsc_ohci_dev = config_found(self, &usbaa_ohci, NULL, 271 CFARGS_NONE); 272 if (usbsc->usbsc_ohci_dev != NULL) 273 usbsc->usbsc_ohci_sc = device_private(usbsc->usbsc_ohci_dev); 274 275 struct bcmusb_attach_args usbaa_ehci = { 276 .usbaa_name = "ehci", 277 .usbaa_dmat = usbsc->usbsc_dmat, 278 .usbaa_bst = usbsc->usbsc_bst, 279 .usbaa_bsh = usbsc->usbsc_ehci_bsh, 280 .usbaa_size = 0x100, 281 }; 282 283 usbsc->usbsc_ehci_dev = config_found(self, &usbaa_ehci, NULL, 284 CFARGS_NONE); 285 if (usbsc->usbsc_ehci_dev != NULL) 286 usbsc->usbsc_ehci_sc = device_private(usbsc->usbsc_ehci_dev); 287 288 usbsc->usbsc_ih = intr_establish(loc->loc_intrs[0], IPL_USB, IST_LEVEL, 289 bcmusb_intr, usbsc); 290 if (usbsc->usbsc_ih == NULL) { 291 aprint_error_dev(self, "failed to establish interrupt %d\n", 292 loc->loc_intrs[0]); 293 return; 294 } 295 aprint_normal_dev(self, "interrupting on irq %d\n", loc->loc_intrs[0]); 296} 297