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