Deleted Added
full compact
1/*-
2 * Copyright (c) 2008 Sam Leffler. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25/*
26 * IXP435 attachment driver for the USB Enhanced Host Controller.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/dev/usb/controller/ehci_ixp4xx.c 196219 2009-08-14 20:03:53Z jhb $");
30__FBSDID("$FreeBSD: head/sys/dev/usb/controller/ehci_ixp4xx.c 198151 2009-10-15 20:07:08Z thompsa $");
31
32#include "opt_bus.h"
33
34#include <sys/stdint.h>
35#include <sys/stddef.h>
36#include <sys/param.h>
37#include <sys/queue.h>
38#include <sys/types.h>
39#include <sys/systm.h>
40#include <sys/kernel.h>
41#include <sys/bus.h>
42#include <sys/linker_set.h>
43#include <sys/module.h>
44#include <sys/lock.h>
45#include <sys/mutex.h>
46#include <sys/condvar.h>
47#include <sys/sysctl.h>
48#include <sys/sx.h>
49#include <sys/unistd.h>
50#include <sys/callout.h>
51#include <sys/malloc.h>
52#include <sys/priv.h>
53
54#include <dev/usb/usb.h>
55#include <dev/usb/usbdi.h>
56
57#include <dev/usb/usb_core.h>
58#include <dev/usb/usb_busdma.h>
59#include <dev/usb/usb_process.h>
60#include <dev/usb/usb_util.h>
61
62#include <dev/usb/usb_controller.h>
63#include <dev/usb/usb_bus.h>
64#include <dev/usb/controller/ehci.h>
65#include <dev/usb/controller/ehcireg.h>
66
67#include <arm/xscale/ixp425/ixp425reg.h>
68#include <arm/xscale/ixp425/ixp425var.h>
69
70#define EHCI_VENDORID_IXP4XX 0x42fa05
71#define EHCI_HC_DEVSTR "IXP4XX Integrated USB 2.0 controller"
72
73struct ixp_ehci_softc {
74 ehci_softc_t base; /* storage for EHCI code */
75 bus_space_tag_t iot;
76 bus_space_handle_t ioh;
77 struct bus_space tag; /* tag for private bus space ops */
78};
79
80static device_attach_t ehci_ixp_attach;
81static device_detach_t ehci_ixp_detach;
82static device_shutdown_t ehci_ixp_shutdown;
83static device_suspend_t ehci_ixp_suspend;
84static device_resume_t ehci_ixp_resume;
85
86static uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t);
87static void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t);
88static uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t);
89static void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t);
90static uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t);
91static void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t);
92
93static int
94ehci_ixp_suspend(device_t self)
95{
96 ehci_softc_t *sc = device_get_softc(self);
97 int err;
98
99 err = bus_generic_suspend(self);
100 if (err)
101 return (err);
102 ehci_suspend(sc);
103 return (0);
104}
105
106static int
107ehci_ixp_resume(device_t self)
108{
109 ehci_softc_t *sc = device_get_softc(self);
110
111 ehci_resume(sc);
112
113 bus_generic_resume(self);
114
115 return (0);
116}
117
118static int
119ehci_ixp_shutdown(device_t self)
120{
121 ehci_softc_t *sc = device_get_softc(self);
122 int err;
123
124 err = bus_generic_shutdown(self);
125 if (err)
126 return (err);
127 ehci_shutdown(sc);
128
129 return (0);
130}
131
132static int
133ehci_ixp_probe(device_t self)
134{
135
136 device_set_desc(self, EHCI_HC_DEVSTR);
137
138 return (BUS_PROBE_DEFAULT);
139}
140
141static int
142ehci_ixp_attach(device_t self)
143{
144 struct ixp_ehci_softc *isc = device_get_softc(self);
145 ehci_softc_t *sc = &isc->base;
146 int err;
147 int rid;
148
149 /* initialise some bus fields */
150 sc->sc_bus.parent = self;
151 sc->sc_bus.devices = sc->sc_devices;
152 sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
153
154 /* get all DMA memory */
155 if (usb_bus_mem_alloc_all(&sc->sc_bus,
156 USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
157 return (ENOMEM);
158 }
159
160 sc->sc_bus.usbrev = USB_REV_2_0;
161
162 /* NB: hints fix the memory location and irq */
163
164 rid = 0;
165 sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
166 if (!sc->sc_io_res) {
167 device_printf(self, "Could not map memory\n");
168 goto error;
169 }
170
171 /*
172 * Craft special resource for bus space ops that handle
173 * byte-alignment of non-word addresses. Also, since
174 * we're already intercepting bus space ops we handle
175 * the register window offset that could otherwise be
176 * done with bus_space_subregion.
177 */
178 isc->iot = rman_get_bustag(sc->sc_io_res);
179 isc->tag.bs_cookie = isc->iot;
180 /* read single */
181 isc->tag.bs_r_1 = ehci_bs_r_1,
182 isc->tag.bs_r_2 = ehci_bs_r_2,
183 isc->tag.bs_r_4 = ehci_bs_r_4,
184 /* write (single) */
185 isc->tag.bs_w_1 = ehci_bs_w_1,
186 isc->tag.bs_w_2 = ehci_bs_w_2,
187 isc->tag.bs_w_4 = ehci_bs_w_4,
188
189 sc->sc_io_tag = &isc->tag;
190 sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
191 sc->sc_io_size = IXP435_USB1_SIZE - 0x100;
192
193 rid = 0;
194 sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
195 RF_ACTIVE);
196 if (sc->sc_irq_res == NULL) {
197 device_printf(self, "Could not allocate irq\n");
198 goto error;
199 }
200 sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
201 if (!sc->sc_bus.bdev) {
202 device_printf(self, "Could not add USB device\n");
203 goto error;
204 }
205 device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
206 device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
207
208 sprintf(sc->sc_vendor, "Intel");
209
210
211 err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
212 NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
213 if (err) {
214 device_printf(self, "Could not setup irq, %d\n", err);
215 sc->sc_intr_hdl = NULL;
216 goto error;
217 }
218
219 /*
220 * Arrange to force Host mode, select big-endian byte alignment,
221 * and arrange to not terminate reset operations (the adapter
222 * will ignore it if we do but might as well save a reg write).
223 * Also, the controller has an embedded Transaction Translator
224 * which means port speed must be read from the Port Status
225 * register following a port enable.
226 */
227 sc->sc_flags |= EHCI_SCFLG_TT
228 | EHCI_SCFLG_SETMODE
229 | EHCI_SCFLG_BIGEDESC
230 | EHCI_SCFLG_BIGEMMIO
231 | EHCI_SCFLG_NORESTERM
232 ;
233 (void) ehci_reset(sc);
234
235 err = ehci_init(sc);
236 if (!err) {
237 err = device_probe_and_attach(sc->sc_bus.bdev);
238 }
239 if (err) {
240 device_printf(self, "USB init failed err=%d\n", err);
241 goto error;
242 }
243 return (0);
244
245error:
246 ehci_ixp_detach(self);
247 return (ENXIO);
248}
249
250static int
251ehci_ixp_detach(device_t self)
252{
253 struct ixp_ehci_softc *isc = device_get_softc(self);
254 ehci_softc_t *sc = &isc->base;
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 }
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
288 if (sc->sc_irq_res) {
289 bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
290 sc->sc_irq_res = NULL;
291 }
292 if (sc->sc_io_res) {
293 bus_release_resource(self, SYS_RES_MEMORY, 0,
294 sc->sc_io_res);
295 sc->sc_io_res = NULL;
296 }
297 usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
298
299 return (0);
300}
301
302/*
303 * Bus space accessors for PIO operations.
304 */
305
306static uint8_t
307ehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o)
308{
309 return bus_space_read_1((bus_space_tag_t) t, h,
310 0x100 + (o &~ 3) + (3 - (o & 3)));
311}
312
313static void
314ehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v)
315{
316 panic("%s", __func__);
317}
318
319static uint16_t
320ehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o)
321{
322 return bus_space_read_2((bus_space_tag_t) t, h,
323 0x100 + (o &~ 3) + (2 - (o & 3)));
324}
325
326static void
327ehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v)
328{
329 panic("%s", __func__);
330}
331
332static uint32_t
333ehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o)
334{
335 return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o);
336}
337
338static void
339ehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v)
340{
341 bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v);
342}
343
344static device_method_t ehci_methods[] = {
345 /* Device interface */
346 DEVMETHOD(device_probe, ehci_ixp_probe),
347 DEVMETHOD(device_attach, ehci_ixp_attach),
348 DEVMETHOD(device_detach, ehci_ixp_detach),
349 DEVMETHOD(device_suspend, ehci_ixp_suspend),
350 DEVMETHOD(device_resume, ehci_ixp_resume),
351 DEVMETHOD(device_shutdown, ehci_ixp_shutdown),
352
353 /* Bus interface */
354 DEVMETHOD(bus_print_child, bus_generic_print_child),
355
356 {0, 0}
357};
358
359static driver_t ehci_driver = {
360 "ehci",
361 ehci_methods,
362 sizeof(struct ixp_ehci_softc),
363};
364
365static devclass_t ehci_devclass;
366
367DRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0);
368MODULE_DEPEND(ehci, usb, 1, 1, 1);