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.5 2014/02/19 22:21:16 matt 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 57 #include <dev/pci/pcidevs.h> 58 59 struct bcmusb_softc { 60 device_t usbsc_dev; 61 bus_dma_tag_t usbsc_dmat; 62 bus_space_tag_t usbsc_bst; 63 bus_space_handle_t usbsc_ehci_bsh; 64 bus_space_handle_t usbsc_ohci_bsh; 65 66 device_t usbsc_ohci_dev; 67 device_t usbsc_ehci_dev; 68 void *usbsc_ohci_sc; 69 void *usbsc_ehci_sc; 70 void *usbsc_ih; 71 }; 72 73 struct bcmusb_attach_args { 74 const char *usbaa_name; 75 bus_dma_tag_t usbaa_dmat; 76 bus_space_tag_t usbaa_bst; 77 bus_space_handle_t usbaa_bsh; 78 bus_size_t usbaa_size; 79 }; 80 81 #ifdef OHCI_DEBUG 82 #define OHCI_DPRINTF(x) if (ohcidebug) printf x 83 extern int ohcidebug; 84 #else 85 #define OHCI_DPRINTF(x) 86 #endif 87 88 static int ohci_bcmusb_match(device_t, cfdata_t, void *); 89 static void ohci_bcmusb_attach(device_t, device_t, void *); 90 91 CFATTACH_DECL_NEW(ohci_bcmusb, sizeof(struct ohci_softc), 92 ohci_bcmusb_match, ohci_bcmusb_attach, NULL, NULL); 93 94 static int 95 ohci_bcmusb_match(device_t parent, cfdata_t cf, void *aux) 96 { 97 struct bcmusb_attach_args * const usbaa = aux; 98 99 if (strcmp(cf->cf_name, usbaa->usbaa_name)) 100 return 0; 101 102 return 1; 103 } 104 105 static void 106 ohci_bcmusb_attach(device_t parent, device_t self, void *aux) 107 { 108 struct ohci_softc * const sc = device_private(self); 109 struct bcmusb_attach_args * const usbaa = aux; 110 111 sc->sc_dev = self; 112 113 sc->iot = usbaa->usbaa_bst; 114 sc->ioh = usbaa->usbaa_bsh; 115 sc->sc_size = usbaa->usbaa_size; 116 sc->sc_bus.dmatag = usbaa->usbaa_dmat; 117 sc->sc_bus.hci_private = sc; 118 119 sc->sc_id_vendor = PCI_VENDOR_BROADCOM; 120 strlcpy(sc->sc_vendor, "Broadcom", sizeof(sc->sc_vendor)); 121 122 aprint_naive(": OHCI USB controller\n"); 123 aprint_normal(": OHCI USB controller\n"); 124 125 int error = ohci_init(sc); 126 if (error != USBD_NORMAL_COMPLETION) { 127 aprint_error_dev(self, "init failed, error=%d\n", error); 128 } else { 129 /* Attach usb device. */ 130 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint); 131 } 132 } 133 134 #ifdef EHCI_DEBUG 135 #define EHCI_DPRINTF(x) if (ehcidebug) printf x 136 extern int ehcidebug; 137 #else 138 #define EHCI_DPRINTF(x) 139 #endif 140 141 static int ehci_bcmusb_match(device_t, cfdata_t, void *); 142 static void ehci_bcmusb_attach(device_t, device_t, void *); 143 144 CFATTACH_DECL_NEW(ehci_bcmusb, sizeof(struct ehci_softc), 145 ehci_bcmusb_match, ehci_bcmusb_attach, NULL, NULL); 146 147 static int 148 ehci_bcmusb_match(device_t parent, cfdata_t cf, void *aux) 149 { 150 struct bcmusb_attach_args * const usbaa = aux; 151 152 if (strcmp(cf->cf_name, usbaa->usbaa_name)) 153 return 0; 154 155 return 1; 156 } 157 158 static void 159 ehci_bcmusb_attach(device_t parent, device_t self, void *aux) 160 { 161 struct bcmusb_softc * const usbsc = device_private(parent); 162 struct ehci_softc * const sc = device_private(self); 163 struct bcmusb_attach_args * const usbaa = aux; 164 165 sc->sc_dev = self; 166 167 sc->iot = usbaa->usbaa_bst; 168 sc->ioh = usbaa->usbaa_bsh; 169 sc->sc_size = usbaa->usbaa_size; 170 sc->sc_bus.dmatag = usbaa->usbaa_dmat; 171 sc->sc_bus.hci_private = sc; 172 sc->sc_bus.usbrev = USBREV_2_0; 173 sc->sc_ncomp = 0; 174 if (usbsc->usbsc_ohci_dev != NULL) { 175 sc->sc_comps[sc->sc_ncomp++] = usbsc->usbsc_ohci_dev; 176 } 177 178 sc->sc_id_vendor = PCI_VENDOR_BROADCOM; 179 strlcpy(sc->sc_vendor, "Broadcom", sizeof(sc->sc_vendor)); 180 181 aprint_naive(": EHCI USB controller\n"); 182 aprint_normal(": ECHI USB controller\n"); 183 184 int error = ehci_init(sc); 185 if (error != USBD_NORMAL_COMPLETION) { 186 aprint_error_dev(self, "init failed, error=%d\n", error); 187 } else { 188 /* Attach usb device. */ 189 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint); 190 } 191 } 192 193 /* 194 * There's only IRQ shared between both OCHI and EHCI devices. 195 */ 196 static int 197 bcmusb_intr(void *arg) 198 { 199 struct bcmusb_softc * const usbsc = arg; 200 int rv0 = 0, rv1 = 0; 201 202 if (usbsc->usbsc_ohci_sc) 203 rv0 = ohci_intr(usbsc->usbsc_ohci_sc); 204 205 if (usbsc->usbsc_ehci_sc) 206 rv1 = ehci_intr(usbsc->usbsc_ehci_sc); 207 208 return rv0 ? rv0 : rv1; 209 } 210 211 static int bcmusb_ccb_match(device_t, cfdata_t, void *); 212 static void bcmusb_ccb_attach(device_t, device_t, void *); 213 214 CFATTACH_DECL_NEW(bcmusb_ccb, sizeof(struct bcmusb_softc), 215 bcmusb_ccb_match, bcmusb_ccb_attach, NULL, NULL); 216 217 int 218 bcmusb_ccb_match(device_t parent, cfdata_t cf, void *aux) 219 { 220 struct bcmccb_attach_args * const ccbaa = aux; 221 const struct bcm_locators * const loc = &ccbaa->ccbaa_loc; 222 223 if (strcmp(cf->cf_name, loc->loc_name) != 0) 224 return 0; 225 226 KASSERT(cf->cf_loc[BCMCCBCF_PORT] == BCMCCBCF_PORT_DEFAULT); 227 228 return 1; 229 } 230 231 #define OHCI_OFFSET (OHCI_BASE - EHCI_BASE) 232 233 void 234 bcmusb_ccb_attach(device_t parent, device_t self, void *aux) 235 { 236 struct bcmusb_softc * const usbsc = device_private(self); 237 const struct bcmccb_attach_args * const ccbaa = aux; 238 const struct bcm_locators * const loc = &ccbaa->ccbaa_loc; 239 240 usbsc->usbsc_bst = ccbaa->ccbaa_ccb_bst; 241 usbsc->usbsc_dmat = ccbaa->ccbaa_dmat; 242 243 bus_space_subregion(usbsc->usbsc_bst, ccbaa->ccbaa_ccb_bsh, 244 loc->loc_offset, 0x1000, &usbsc->usbsc_ehci_bsh); 245 bus_space_subregion(usbsc->usbsc_bst, ccbaa->ccbaa_ccb_bsh, 246 loc->loc_offset + OHCI_OFFSET, 0x1000, &usbsc->usbsc_ohci_bsh); 247 248 /* 249 * Bring the PHYs out of reset. 250 */ 251 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 252 USBH_PHY_CTRL_P0, USBH_PHY_CTRL_INIT); 253 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 254 USBH_PHY_CTRL_P1, USBH_PHY_CTRL_INIT); 255 256 /* 257 * Disable interrupts 258 */ 259 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ohci_bsh, 260 OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); 261 bus_size_t caplength = bus_space_read_1(usbsc->usbsc_bst, 262 usbsc->usbsc_ehci_bsh, EHCI_CAPLENGTH); 263 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 264 caplength + EHCI_USBINTR, 0); 265 266 aprint_naive("\n"); 267 aprint_normal("\n"); 268 269 struct bcmusb_attach_args usbaa_ohci = { 270 .usbaa_name = "ohci", 271 .usbaa_dmat = usbsc->usbsc_dmat, 272 .usbaa_bst = usbsc->usbsc_bst, 273 .usbaa_bsh = usbsc->usbsc_ohci_bsh, 274 .usbaa_size = 0x100, 275 }; 276 277 usbsc->usbsc_ohci_dev = config_found(self, &usbaa_ohci, NULL); 278 if (usbsc->usbsc_ohci_dev != NULL) 279 usbsc->usbsc_ohci_sc = device_private(usbsc->usbsc_ohci_dev); 280 281 struct bcmusb_attach_args usbaa_ehci = { 282 .usbaa_name = "ehci", 283 .usbaa_dmat = usbsc->usbsc_dmat, 284 .usbaa_bst = usbsc->usbsc_bst, 285 .usbaa_bsh = usbsc->usbsc_ehci_bsh, 286 .usbaa_size = 0x100, 287 }; 288 289 usbsc->usbsc_ehci_dev = config_found(self, &usbaa_ehci, NULL); 290 if (usbsc->usbsc_ehci_dev != NULL) 291 usbsc->usbsc_ehci_sc = device_private(usbsc->usbsc_ehci_dev); 292 293 usbsc->usbsc_ih = intr_establish(loc->loc_intrs[0], IPL_USB, IST_LEVEL, 294 bcmusb_intr, usbsc); 295 if (usbsc->usbsc_ih == NULL) { 296 aprint_error_dev(self, "failed to establish interrupt %d\n", 297 loc->loc_intrs[0]); 298 return; 299 } 300 aprint_normal_dev(self, "interrupting on irq %d\n", loc->loc_intrs[0]); 301 } 302