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