]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/controller/ehci_mbus.c
MFp4 //depot/projects/usb @159430
[FreeBSD/FreeBSD.git] / sys / dev / usb / controller / ehci_mbus.c
1 /*-
2  * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
3  * All rights reserved.
4  *
5  * Developed by Semihalf.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of MARVELL nor the names of contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 /*
33  * MBus attachment driver for the USB Enhanced Host Controller.
34  */
35
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38
39 #include "opt_bus.h"
40
41 #include <dev/usb/usb_mfunc.h>
42 #include <dev/usb/usb.h>
43
44 #include <dev/usb/usb_core.h>
45 #include <dev/usb/usb_busdma.h>
46 #include <dev/usb/usb_process.h>
47 #include <dev/usb/usb_sw_transfer.h>
48 #include <dev/usb/usb_util.h>
49
50 #include <dev/usb/usb_controller.h>
51 #include <dev/usb/usb_bus.h>
52 #include <dev/usb/controller/ehci.h>
53
54 #include <arm/mv/mvreg.h>
55 #include <arm/mv/mvvar.h>
56
57 #define EHCI_VENDORID_MRVL      0x1286
58 #define EHCI_HC_DEVSTR          "Marvell Integrated USB 2.0 controller"
59
60 static device_attach_t ehci_mbus_attach;
61 static device_detach_t ehci_mbus_detach;
62 static device_shutdown_t ehci_mbus_shutdown;
63 static device_suspend_t ehci_mbus_suspend;
64 static device_resume_t ehci_mbus_resume;
65
66 static int err_intr(void *arg);
67
68 static struct resource *irq_err;
69 static void *ih_err;
70
71 #define USB_BRIDGE_INTR_CAUSE  0x210
72 #define USB_BRIDGE_INTR_MASK   0x214
73
74 #define MV_USB_ADDR_DECODE_ERR (1 << 0)
75 #define MV_USB_HOST_UNDERFLOW  (1 << 1)
76 #define MV_USB_HOST_OVERFLOW   (1 << 2)
77 #define MV_USB_DEVICE_UNDERFLOW (1 << 3)
78
79 static int
80 ehci_mbus_suspend(device_t self)
81 {
82         ehci_softc_t *sc = device_get_softc(self);
83         int err;
84
85         err = bus_generic_suspend(self);
86         if (err)
87                 return (err);
88         ehci_suspend(sc);
89         return (0);
90 }
91
92 static int
93 ehci_mbus_resume(device_t self)
94 {
95         ehci_softc_t *sc = device_get_softc(self);
96
97         ehci_resume(sc);
98
99         bus_generic_resume(self);
100
101         return (0);
102 }
103
104 static int
105 ehci_mbus_shutdown(device_t self)
106 {
107         ehci_softc_t *sc = device_get_softc(self);
108         int err;
109
110         err = bus_generic_shutdown(self);
111         if (err)
112                 return (err);
113         ehci_shutdown(sc);
114
115         return (0);
116 }
117
118 static int
119 ehci_mbus_probe(device_t self)
120 {
121
122         device_set_desc(self, EHCI_HC_DEVSTR);
123
124         return (BUS_PROBE_DEFAULT);
125 }
126
127 static int
128 ehci_mbus_attach(device_t self)
129 {
130         ehci_softc_t *sc = device_get_softc(self);
131         bus_space_handle_t bsh;
132         int err;
133         int rid;
134
135         /* initialise some bus fields */
136         sc->sc_bus.parent = self;
137         sc->sc_bus.devices = sc->sc_devices;
138         sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
139
140         /* get all DMA memory */
141         if (usb2_bus_mem_alloc_all(&sc->sc_bus,
142             USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
143                 return (ENOMEM);
144         }
145
146         sc->sc_bus.usbrev = USB_REV_2_0;
147
148         rid = 0;
149         sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
150         if (!sc->sc_io_res) {
151                 device_printf(self, "Could not map memory\n");
152                 goto error;
153         }
154         sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
155         bsh = rman_get_bushandle(sc->sc_io_res);
156         sc->sc_io_size = MV_USB_SIZE - MV_USB_HOST_OFST;
157
158         /*
159          * Marvell EHCI host controller registers start at certain offset within
160          * the whole USB registers range, so create a subregion for the host
161          * mode configuration purposes.
162          */
163         if (bus_space_subregion(sc->sc_io_tag, bsh, MV_USB_HOST_OFST,
164             sc->sc_io_size, &sc->sc_io_hdl) != 0)
165                 panic("%s: unable to subregion USB host registers",
166                     device_get_name(self));
167
168         rid = 0;
169         irq_err = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
170             RF_SHAREABLE | RF_ACTIVE);
171         if (irq_err == NULL) {
172                 device_printf(self, "Could not allocate error irq\n");
173                 ehci_mbus_detach(self);
174                 return (ENXIO);
175         }
176
177         /*
178          * Notice: Marvell EHCI controller has TWO interrupt lines, so make sure to
179          * use the correct rid for the main one (controller interrupt) --
180          * refer to obio_devices[] for the right resource number to use here.
181          */
182         rid = 1;
183         sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
184             RF_SHAREABLE | RF_ACTIVE);
185         if (sc->sc_irq_res == NULL) {
186                 device_printf(self, "Could not allocate irq\n");
187                 goto error;
188         }
189
190         sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
191         if (!sc->sc_bus.bdev) {
192                 device_printf(self, "Could not add USB device\n");
193                 goto error;
194         }
195         device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
196         device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
197
198         sprintf(sc->sc_vendor, "Marvell");
199
200         err = bus_setup_intr(self, irq_err, INTR_FAST | INTR_TYPE_BIO,
201             err_intr, NULL, sc, &ih_err);
202         if (err) {
203                 device_printf(self, "Could not setup error irq, %d\n", err);
204                 ih_err = NULL;
205                 goto error;
206         }
207
208         EWRITE4(sc, USB_BRIDGE_INTR_MASK, MV_USB_ADDR_DECODE_ERR |
209             MV_USB_HOST_UNDERFLOW | MV_USB_HOST_OVERFLOW |
210             MV_USB_DEVICE_UNDERFLOW);
211
212         err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
213             NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl);
214         if (err) {
215                 device_printf(self, "Could not setup irq, %d\n", err);
216                 sc->sc_intr_hdl = NULL;
217                 goto error;
218         }
219
220         /*
221          * Workaround for Marvell integrated EHCI controller: reset of
222          * the EHCI core clears the USBMODE register, which sets the core in
223          * an undefined state (neither host nor agent), so it needs to be set
224          * again for proper operation.
225          *
226          * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for
227          * details.
228          */
229         sc->sc_flags |= EHCI_SCFLG_SETMODE;
230         if (bootverbose)
231                 device_printf(self, "5.24 GL USB-2 workaround enabled\n");
232
233         /* XXX all MV chips need it? */
234         sc->sc_flags |= EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_NORESTERM;
235
236         err = ehci_init(sc);
237         if (!err) {
238                 err = device_probe_and_attach(sc->sc_bus.bdev);
239         }
240         if (err) {
241                 device_printf(self, "USB init failed err=%d\n", err);
242                 goto error;
243         }
244         return (0);
245
246 error:
247         ehci_mbus_detach(self);
248         return (ENXIO);
249 }
250
251 static int
252 ehci_mbus_detach(device_t self)
253 {
254         ehci_softc_t *sc = device_get_softc(self);
255         device_t bdev;
256         int err;
257
258         if (sc->sc_bus.bdev) {
259                 bdev = sc->sc_bus.bdev;
260                 device_detach(bdev);
261                 device_delete_child(self, bdev);
262         }
263         /* during module unload there are lots of children leftover */
264         device_delete_all_children(self);
265
266         /*
267          * disable interrupts that might have been switched on in ehci_init
268          */
269         if (sc->sc_io_res) {
270                 EWRITE4(sc, EHCI_USBINTR, 0);
271                 EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0);
272         }
273         if (sc->sc_irq_res && sc->sc_intr_hdl) {
274                 /*
275                  * only call ehci_detach() after ehci_init()
276                  */
277                 ehci_detach(sc);
278
279                 err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
280
281                 if (err)
282                         /* XXX or should we panic? */
283                         device_printf(self, "Could not tear down irq, %d\n",
284                             err);
285                 sc->sc_intr_hdl = NULL;
286         }
287         if (irq_err && ih_err) {
288                 err = bus_teardown_intr(self, irq_err, ih_err);
289
290                 if (err)
291                         device_printf(self, "Could not tear down irq, %d\n",
292                             err);
293                 ih_err = NULL;
294         }
295         if (irq_err) {
296                 bus_release_resource(self, SYS_RES_IRQ, 0, irq_err);
297                 irq_err = NULL;
298         }
299         if (sc->sc_irq_res) {
300                 bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res);
301                 sc->sc_irq_res = NULL;
302         }
303         if (sc->sc_io_res) {
304                 bus_release_resource(self, SYS_RES_MEMORY, 0,
305                     sc->sc_io_res);
306                 sc->sc_io_res = NULL;
307         }
308         usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
309
310         return (0);
311 }
312
313 static int
314 err_intr(void *arg)
315 {
316         ehci_softc_t *sc = arg;
317         unsigned int cause;
318
319         cause = EREAD4(sc, USB_BRIDGE_INTR_CAUSE);
320         if (cause) {
321                 printf("IRQ ERR: cause: 0x%08x\n", cause);
322                 if (cause & MV_USB_ADDR_DECODE_ERR)
323                         printf("IRQ ERR: Address decoding error\n");
324                 if (cause & MV_USB_HOST_UNDERFLOW)
325                         printf("IRQ ERR: USB Host Underflow\n");
326                 if (cause & MV_USB_HOST_OVERFLOW)
327                         printf("IRQ ERR: USB Host Overflow\n");
328                 if (cause & MV_USB_DEVICE_UNDERFLOW)
329                         printf("IRQ ERR: USB Device Underflow\n");
330                 if (cause & ~(MV_USB_ADDR_DECODE_ERR | MV_USB_HOST_UNDERFLOW |
331                     MV_USB_HOST_OVERFLOW | MV_USB_DEVICE_UNDERFLOW))
332                         printf("IRQ ERR: Unknown error\n");
333
334                 EWRITE4(sc, USB_BRIDGE_INTR_CAUSE, 0);
335         }
336         return (FILTER_HANDLED);
337 }
338
339 static device_method_t ehci_methods[] = {
340         /* Device interface */
341         DEVMETHOD(device_probe, ehci_mbus_probe),
342         DEVMETHOD(device_attach, ehci_mbus_attach),
343         DEVMETHOD(device_detach, ehci_mbus_detach),
344         DEVMETHOD(device_suspend, ehci_mbus_suspend),
345         DEVMETHOD(device_resume, ehci_mbus_resume),
346         DEVMETHOD(device_shutdown, ehci_mbus_shutdown),
347
348         /* Bus interface */
349         DEVMETHOD(bus_print_child, bus_generic_print_child),
350
351         {0, 0}
352 };
353
354 static driver_t ehci_driver = {
355         "ehci",
356         ehci_methods,
357         sizeof(ehci_softc_t),
358 };
359
360 static devclass_t ehci_devclass;
361
362 DRIVER_MODULE(ehci, mbus, ehci_driver, ehci_devclass, 0, 0);
363 MODULE_DEPEND(ehci, usb, 1, 1, 1);