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