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