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