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.6 2014/06/24 05:07:31 skrll 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 return; 129 } 130 131 /* Attach usb device. */ 132 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint); 133 } 134 135 #ifdef EHCI_DEBUG 136 #define EHCI_DPRINTF(x) if (ehcidebug) printf x 137 extern int ehcidebug; 138 #else 139 #define EHCI_DPRINTF(x) 140 #endif 141 142 static int ehci_bcmusb_match(device_t, cfdata_t, void *); 143 static void ehci_bcmusb_attach(device_t, device_t, void *); 144 145 CFATTACH_DECL_NEW(ehci_bcmusb, sizeof(struct ehci_softc), 146 ehci_bcmusb_match, ehci_bcmusb_attach, NULL, NULL); 147 148 static int 149 ehci_bcmusb_match(device_t parent, cfdata_t cf, void *aux) 150 { 151 struct bcmusb_attach_args * const usbaa = aux; 152 153 if (strcmp(cf->cf_name, usbaa->usbaa_name)) 154 return 0; 155 156 return 1; 157 } 158 159 static void 160 ehci_bcmusb_attach(device_t parent, device_t self, void *aux) 161 { 162 struct bcmusb_softc * const usbsc = device_private(parent); 163 struct ehci_softc * const sc = device_private(self); 164 struct bcmusb_attach_args * const usbaa = aux; 165 166 sc->sc_dev = self; 167 168 sc->iot = usbaa->usbaa_bst; 169 sc->ioh = usbaa->usbaa_bsh; 170 sc->sc_size = usbaa->usbaa_size; 171 sc->sc_bus.dmatag = usbaa->usbaa_dmat; 172 sc->sc_bus.hci_private = sc; 173 sc->sc_bus.usbrev = USBREV_2_0; 174 sc->sc_ncomp = 0; 175 if (usbsc->usbsc_ohci_dev != NULL) { 176 sc->sc_comps[sc->sc_ncomp++] = usbsc->usbsc_ohci_dev; 177 } 178 179 sc->sc_id_vendor = PCI_VENDOR_BROADCOM; 180 strlcpy(sc->sc_vendor, "Broadcom", sizeof(sc->sc_vendor)); 181 182 aprint_naive(": EHCI USB controller\n"); 183 aprint_normal(": ECHI USB controller\n"); 184 185 int error = ehci_init(sc); 186 if (error != USBD_NORMAL_COMPLETION) { 187 aprint_error_dev(self, "init failed, error=%d\n", error); 188 return; 189 } 190 /* Attach usb device. */ 191 sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint); 192 } 193 194 /* 195 * There's only IRQ shared between both OCHI and EHCI devices. 196 */ 197 static int 198 bcmusb_intr(void *arg) 199 { 200 struct bcmusb_softc * const usbsc = arg; 201 int rv0 = 0, rv1 = 0; 202 203 if (usbsc->usbsc_ohci_sc) 204 rv0 = ohci_intr(usbsc->usbsc_ohci_sc); 205 206 if (usbsc->usbsc_ehci_sc) 207 rv1 = ehci_intr(usbsc->usbsc_ehci_sc); 208 209 return rv0 ? rv0 : rv1; 210 } 211 212 static int bcmusb_ccb_match(device_t, cfdata_t, void *); 213 static void bcmusb_ccb_attach(device_t, device_t, void *); 214 215 CFATTACH_DECL_NEW(bcmusb_ccb, sizeof(struct bcmusb_softc), 216 bcmusb_ccb_match, bcmusb_ccb_attach, NULL, NULL); 217 218 int 219 bcmusb_ccb_match(device_t parent, cfdata_t cf, void *aux) 220 { 221 struct bcmccb_attach_args * const ccbaa = aux; 222 const struct bcm_locators * const loc = &ccbaa->ccbaa_loc; 223 224 if (strcmp(cf->cf_name, loc->loc_name) != 0) 225 return 0; 226 227 KASSERT(cf->cf_loc[BCMCCBCF_PORT] == BCMCCBCF_PORT_DEFAULT); 228 229 return 1; 230 } 231 232 #define OHCI_OFFSET (OHCI_BASE - EHCI_BASE) 233 234 void 235 bcmusb_ccb_attach(device_t parent, device_t self, void *aux) 236 { 237 struct bcmusb_softc * const usbsc = device_private(self); 238 const struct bcmccb_attach_args * const ccbaa = aux; 239 const struct bcm_locators * const loc = &ccbaa->ccbaa_loc; 240 241 usbsc->usbsc_bst = ccbaa->ccbaa_ccb_bst; 242 usbsc->usbsc_dmat = ccbaa->ccbaa_dmat; 243 244 bus_space_subregion(usbsc->usbsc_bst, ccbaa->ccbaa_ccb_bsh, 245 loc->loc_offset, 0x1000, &usbsc->usbsc_ehci_bsh); 246 bus_space_subregion(usbsc->usbsc_bst, ccbaa->ccbaa_ccb_bsh, 247 loc->loc_offset + OHCI_OFFSET, 0x1000, &usbsc->usbsc_ohci_bsh); 248 249 /* 250 * Bring the PHYs out of reset. 251 */ 252 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 253 USBH_PHY_CTRL_P0, USBH_PHY_CTRL_INIT); 254 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 255 USBH_PHY_CTRL_P1, USBH_PHY_CTRL_INIT); 256 257 /* 258 * Disable interrupts 259 */ 260 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ohci_bsh, 261 OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS); 262 bus_size_t caplength = bus_space_read_1(usbsc->usbsc_bst, 263 usbsc->usbsc_ehci_bsh, EHCI_CAPLENGTH); 264 bus_space_write_4(usbsc->usbsc_bst, usbsc->usbsc_ehci_bsh, 265 caplength + EHCI_USBINTR, 0); 266 267 aprint_naive("\n"); 268 aprint_normal("\n"); 269 270 struct bcmusb_attach_args usbaa_ohci = { 271 .usbaa_name = "ohci", 272 .usbaa_dmat = usbsc->usbsc_dmat, 273 .usbaa_bst = usbsc->usbsc_bst, 274 .usbaa_bsh = usbsc->usbsc_ohci_bsh, 275 .usbaa_size = 0x100, 276 }; 277 278 usbsc->usbsc_ohci_dev = config_found(self, &usbaa_ohci, NULL); 279 if (usbsc->usbsc_ohci_dev != NULL) 280 usbsc->usbsc_ohci_sc = device_private(usbsc->usbsc_ohci_dev); 281 282 struct bcmusb_attach_args usbaa_ehci = { 283 .usbaa_name = "ehci", 284 .usbaa_dmat = usbsc->usbsc_dmat, 285 .usbaa_bst = usbsc->usbsc_bst, 286 .usbaa_bsh = usbsc->usbsc_ehci_bsh, 287 .usbaa_size = 0x100, 288 }; 289 290 usbsc->usbsc_ehci_dev = config_found(self, &usbaa_ehci, NULL); 291 if (usbsc->usbsc_ehci_dev != NULL) 292 usbsc->usbsc_ehci_sc = device_private(usbsc->usbsc_ehci_dev); 293 294 usbsc->usbsc_ih = intr_establish(loc->loc_intrs[0], IPL_USB, IST_LEVEL, 295 bcmusb_intr, usbsc); 296 if (usbsc->usbsc_ih == NULL) { 297 aprint_error_dev(self, "failed to establish interrupt %d\n", 298 loc->loc_intrs[0]); 299 return; 300 } 301 aprint_normal_dev(self, "interrupting on irq %d\n", loc->loc_intrs[0]); 302 } 303