xref: /netbsd-src/sys/arch/arm/broadcom/bcm53xx_usb.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
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