ehci_ixp4xx.c revision 188410
1188410Sthompsa/*-
2188410Sthompsa * Copyright (c) 2008 Sam Leffler.  All rights reserved.
3188410Sthompsa *
4188410Sthompsa * Redistribution and use in source and binary forms, with or without
5188410Sthompsa * modification, are permitted provided that the following conditions
6188410Sthompsa * are met:
7188410Sthompsa * 1. Redistributions of source code must retain the above copyright
8188410Sthompsa *    notice, this list of conditions and the following disclaimer.
9188410Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
10188410Sthompsa *    notice, this list of conditions and the following disclaimer in the
11188410Sthompsa *    documentation and/or other materials provided with the distribution.
12188410Sthompsa *
13188410Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14188410Sthompsa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15188410Sthompsa * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16188410Sthompsa * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17188410Sthompsa * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18188410Sthompsa * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19188410Sthompsa * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20188410Sthompsa * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21188410Sthompsa * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22188410Sthompsa * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23188410Sthompsa */
24188410Sthompsa
25188410Sthompsa/*
26188410Sthompsa * IXP435 attachment driver for the USB Enhanced Host Controller.
27188410Sthompsa */
28188410Sthompsa
29188410Sthompsa#include <sys/cdefs.h>
30188410Sthompsa__FBSDID("$FreeBSD: head/sys/dev/usb2/controller/ehci2_ixp4xx.c 188410 2009-02-09 21:50:04Z thompsa $");
31188410Sthompsa
32188410Sthompsa#include "opt_bus.h"
33188410Sthompsa
34188410Sthompsa#include <dev/usb2/include/usb2_mfunc.h>
35188410Sthompsa#include <dev/usb2/include/usb2_defs.h>
36188410Sthompsa#include <dev/usb2/include/usb2_standard.h>
37188410Sthompsa
38188410Sthompsa#include <dev/usb2/core/usb2_core.h>
39188410Sthompsa#include <dev/usb2/core/usb2_busdma.h>
40188410Sthompsa#include <dev/usb2/core/usb2_process.h>
41188410Sthompsa#include <dev/usb2/core/usb2_sw_transfer.h>
42188410Sthompsa#include <dev/usb2/core/usb2_util.h>
43188410Sthompsa
44188410Sthompsa#include <dev/usb2/controller/usb2_controller.h>
45188410Sthompsa#include <dev/usb2/controller/usb2_bus.h>
46188410Sthompsa#include <dev/usb2/controller/ehci2.h>
47188410Sthompsa
48188410Sthompsa#include <arm/xscale/ixp425/ixp425reg.h>
49188410Sthompsa#include <arm/xscale/ixp425/ixp425var.h>
50188410Sthompsa
51188410Sthompsa#define EHCI_VENDORID_IXP4XX	0x42fa05
52188410Sthompsa#define EHCI_HC_DEVSTR		"IXP4XX Integrated USB 2.0 controller"
53188410Sthompsa
54188410Sthompsastruct ixp_ehci_softc {
55188410Sthompsa	ehci_softc_t		base;	/* storage for EHCI code */
56188410Sthompsa	bus_space_tag_t		iot;
57188410Sthompsa	bus_space_handle_t	ioh;
58188410Sthompsa	struct bus_space	tag;	/* tag for private bus space ops */
59188410Sthompsa};
60188410Sthompsa
61188410Sthompsastatic device_attach_t ehci_ixp_attach;
62188410Sthompsastatic device_detach_t ehci_ixp_detach;
63188410Sthompsastatic device_shutdown_t ehci_ixp_shutdown;
64188410Sthompsastatic device_suspend_t ehci_ixp_suspend;
65188410Sthompsastatic device_resume_t ehci_ixp_resume;
66188410Sthompsa
67188410Sthompsastatic uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t);
68188410Sthompsastatic void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t);
69188410Sthompsastatic uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t);
70188410Sthompsastatic void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t);
71188410Sthompsastatic uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t);
72188410Sthompsastatic void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t);
73188410Sthompsa
74188410Sthompsastatic int
75188410Sthompsaehci_ixp_suspend(device_t self)
76188410Sthompsa{
77188410Sthompsa	ehci_softc_t *sc = device_get_softc(self);
78188410Sthompsa	int err;
79188410Sthompsa
80188410Sthompsa	err = bus_generic_suspend(self);
81188410Sthompsa	if (err)
82188410Sthompsa		return (err);
83188410Sthompsa	ehci_suspend(sc);
84188410Sthompsa	return (0);
85188410Sthompsa}
86188410Sthompsa
87188410Sthompsastatic int
88188410Sthompsaehci_ixp_resume(device_t self)
89188410Sthompsa{
90188410Sthompsa	ehci_softc_t *sc = device_get_softc(self);
91188410Sthompsa
92188410Sthompsa	ehci_resume(sc);
93188410Sthompsa
94188410Sthompsa	bus_generic_resume(self);
95188410Sthompsa
96188410Sthompsa	return (0);
97188410Sthompsa}
98188410Sthompsa
99188410Sthompsastatic int
100188410Sthompsaehci_ixp_shutdown(device_t self)
101188410Sthompsa{
102188410Sthompsa	ehci_softc_t *sc = device_get_softc(self);
103188410Sthompsa	int err;
104188410Sthompsa
105188410Sthompsa	err = bus_generic_shutdown(self);
106188410Sthompsa	if (err)
107188410Sthompsa		return (err);
108188410Sthompsa	ehci_shutdown(sc);
109188410Sthompsa
110188410Sthompsa	return (0);
111188410Sthompsa}
112188410Sthompsa
113188410Sthompsastatic int
114188410Sthompsaehci_ixp_probe(device_t self)
115188410Sthompsa{
116188410Sthompsa
117188410Sthompsa	device_set_desc(self, EHCI_HC_DEVSTR);
118188410Sthompsa
119188410Sthompsa	return (BUS_PROBE_DEFAULT);
120188410Sthompsa}
121188410Sthompsa
122188410Sthompsastatic int
123188410Sthompsaehci_ixp_attach(device_t self)
124188410Sthompsa{
125188410Sthompsa	struct ixp_ehci_softc *isc = device_get_softc(self);
126188410Sthompsa	ehci_softc_t *sc = &isc->base;
127188410Sthompsa	int err;
128188410Sthompsa	int rid;
129188410Sthompsa
130188410Sthompsa	/* initialise some bus fields */
131188410Sthompsa	sc->sc_bus.parent = self;
132188410Sthompsa	sc->sc_bus.devices = sc->sc_devices;
133188410Sthompsa	sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
134188410Sthompsa
135188410Sthompsa	/* get all DMA memory */
136188410Sthompsa	if (usb2_bus_mem_alloc_all(&sc->sc_bus,
137188410Sthompsa	    USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
138188410Sthompsa		return (ENOMEM);
139188410Sthompsa	}
140188410Sthompsa
141188410Sthompsa	sc->sc_bus.usbrev = USB_REV_2_0;
142188410Sthompsa
143188410Sthompsa	/* NB: hints fix the memory location and irq */
144188410Sthompsa
145188410Sthompsa	rid = 0;
146188410Sthompsa	sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
147188410Sthompsa	if (!sc->sc_io_res) {
148188410Sthompsa		device_printf(self, "Could not map memory\n");
149188410Sthompsa		goto error;
150188410Sthompsa	}
151188410Sthompsa
152188410Sthompsa	/*
153188410Sthompsa	 * Craft special resource for bus space ops that handle
154188410Sthompsa	 * byte-alignment of non-word addresses.  Also, since
155188410Sthompsa	 * we're already intercepting bus space ops we handle
156188410Sthompsa	 * the register window offset that could otherwise be
157188410Sthompsa	 * done with bus_space_subregion.
158188410Sthompsa	 */
159188410Sthompsa	isc->iot = rman_get_bustag(sc->sc_io_res);
160188410Sthompsa	isc->tag.bs_cookie = isc->iot;
161188410Sthompsa	/* read single */
162188410Sthompsa	isc->tag.bs_r_1	= ehci_bs_r_1,
163188410Sthompsa	isc->tag.bs_r_2	= ehci_bs_r_2,
164188410Sthompsa	isc->tag.bs_r_4	= ehci_bs_r_4,
165188410Sthompsa	/* write (single) */
166188410Sthompsa	isc->tag.bs_w_1	= ehci_bs_w_1,
167188410Sthompsa	isc->tag.bs_w_2	= ehci_bs_w_2,
168188410Sthompsa	isc->tag.bs_w_4	= ehci_bs_w_4,
169188410Sthompsa
170188410Sthompsa	sc->sc_io_tag = &isc->tag;
171188410Sthompsa	sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
172188410Sthompsa	sc->sc_io_size = IXP435_USB1_SIZE - 0x100;
173188410Sthompsa
174188410Sthompsa	rid = 0;
175188410Sthompsa	sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
176188410Sthompsa	    RF_ACTIVE);
177188410Sthompsa	if (sc->sc_irq_res == NULL) {
178188410Sthompsa		device_printf(self, "Could not allocate irq\n");
179188410Sthompsa		goto error;
180188410Sthompsa	}
181188410Sthompsa	sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
182188410Sthompsa	if (!sc->sc_bus.bdev) {
183188410Sthompsa		device_printf(self, "Could not add USB device\n");
184188410Sthompsa		goto error;
185188410Sthompsa	}
186188410Sthompsa	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
187188410Sthompsa	device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
188188410Sthompsa
189188410Sthompsa	sprintf(sc->sc_vendor, "Intel");
190188410Sthompsa
191188410Sthompsa
192188410Sthompsa	err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
193188410Sthompsa	    NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl);
194188410Sthompsa	if (err) {
195188410Sthompsa		device_printf(self, "Could not setup irq, %d\n", err);
196188410Sthompsa		sc->sc_intr_hdl = NULL;
197188410Sthompsa		goto error;
198188410Sthompsa	}
199188410Sthompsa
200188410Sthompsa	/*
201188410Sthompsa	 * Arrange to force Host mode, select big-endian byte alignment,
202188410Sthompsa	 * and arrange to not terminate reset operations (the adapter
203188410Sthompsa	 * will ignore it if we do but might as well save a reg write).
204188410Sthompsa	 * Also, the controller has an embedded Transaction Translator
205188410Sthompsa	 * which means port speed must be read from the Port Status
206188410Sthompsa	 * register following a port enable.
207188410Sthompsa	 */
208188410Sthompsa	sc->sc_flags |= EHCI_SCFLG_TT
209188410Sthompsa		     | EHCI_SCFLG_SETMODE
210188410Sthompsa		     | EHCI_SCFLG_BIGEDESC
211188410Sthompsa		     | EHCI_SCFLG_BIGEMMIO
212188410Sthompsa		     | EHCI_SCFLG_NORESTERM
213188410Sthompsa		     ;
214188410Sthompsa
215188410Sthompsa	err = ehci_init(sc);
216188410Sthompsa	if (!err) {
217188410Sthompsa		err = device_probe_and_attach(sc->sc_bus.bdev);
218188410Sthompsa	}
219188410Sthompsa	if (err) {
220188410Sthompsa		device_printf(self, "USB init failed err=%d\n", err);
221188410Sthompsa		goto error;
222188410Sthompsa	}
223188410Sthompsa	return (0);
224188410Sthompsa
225188410Sthompsaerror:
226188410Sthompsa	ehci_ixp_detach(self);
227188410Sthompsa	return (ENXIO);
228188410Sthompsa}
229188410Sthompsa
230188410Sthompsastatic int
231188410Sthompsaehci_ixp_detach(device_t self)
232188410Sthompsa{
233188410Sthompsa	struct ixp_ehci_softc *isc = device_get_softc(self);
234188410Sthompsa	ehci_softc_t *sc = &isc->base;
235188410Sthompsa	device_t bdev;
236188410Sthompsa	int err;
237188410Sthompsa
238188410Sthompsa 	if (sc->sc_bus.bdev) {
239188410Sthompsa		bdev = sc->sc_bus.bdev;
240188410Sthompsa		device_detach(bdev);
241188410Sthompsa		device_delete_child(self, bdev);
242188410Sthompsa	}
243188410Sthompsa	/* during module unload there are lots of children leftover */
244188410Sthompsa	device_delete_all_children(self);
245188410Sthompsa
246188410Sthompsa	/*
247188410Sthompsa	 * disable interrupts that might have been switched on in ehci_init
248188410Sthompsa	 */
249188410Sthompsa	if (sc->sc_io_res) {
250188410Sthompsa		EWRITE4(sc, EHCI_USBINTR, 0);
251188410Sthompsa	}
252188410Sthompsa
253188410Sthompsa 	if (sc->sc_irq_res && sc->sc_intr_hdl) {
254188410Sthompsa		/*
255188410Sthompsa		 * only call ehci_detach() after ehci_init()
256188410Sthompsa		 */
257188410Sthompsa		ehci_detach(sc);
258188410Sthompsa
259188410Sthompsa		err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
260188410Sthompsa
261188410Sthompsa		if (err)
262188410Sthompsa			/* XXX or should we panic? */
263188410Sthompsa			device_printf(self, "Could not tear down irq, %d\n",
264188410Sthompsa			    err);
265188410Sthompsa		sc->sc_intr_hdl = NULL;
266188410Sthompsa	}
267188410Sthompsa
268188410Sthompsa 	if (sc->sc_irq_res) {
269188410Sthompsa		bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
270188410Sthompsa		sc->sc_irq_res = NULL;
271188410Sthompsa	}
272188410Sthompsa	if (sc->sc_io_res) {
273188410Sthompsa		bus_release_resource(self, SYS_RES_MEMORY, 0,
274188410Sthompsa		    sc->sc_io_res);
275188410Sthompsa		sc->sc_io_res = NULL;
276188410Sthompsa	}
277188410Sthompsa	usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
278188410Sthompsa
279188410Sthompsa	return (0);
280188410Sthompsa}
281188410Sthompsa
282188410Sthompsa/*
283188410Sthompsa * Bus space accessors for PIO operations.
284188410Sthompsa */
285188410Sthompsa
286188410Sthompsastatic uint8_t
287188410Sthompsaehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o)
288188410Sthompsa{
289188410Sthompsa	return bus_space_read_1((bus_space_tag_t) t, h,
290188410Sthompsa	    0x100 + (o &~ 3) + (3 - (o & 3)));
291188410Sthompsa}
292188410Sthompsa
293188410Sthompsastatic void
294188410Sthompsaehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v)
295188410Sthompsa{
296188410Sthompsa	panic("%s", __func__);
297188410Sthompsa}
298188410Sthompsa
299188410Sthompsastatic uint16_t
300188410Sthompsaehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o)
301188410Sthompsa{
302188410Sthompsa	return bus_space_read_2((bus_space_tag_t) t, h,
303188410Sthompsa	    0x100 + (o &~ 3) + (2 - (o & 3)));
304188410Sthompsa}
305188410Sthompsa
306188410Sthompsastatic void
307188410Sthompsaehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v)
308188410Sthompsa{
309188410Sthompsa	panic("%s", __func__);
310188410Sthompsa}
311188410Sthompsa
312188410Sthompsastatic uint32_t
313188410Sthompsaehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o)
314188410Sthompsa{
315188410Sthompsa	return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o);
316188410Sthompsa}
317188410Sthompsa
318188410Sthompsastatic void
319188410Sthompsaehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v)
320188410Sthompsa{
321188410Sthompsa	bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v);
322188410Sthompsa}
323188410Sthompsa
324188410Sthompsastatic device_method_t ehci_methods[] = {
325188410Sthompsa	/* Device interface */
326188410Sthompsa	DEVMETHOD(device_probe, ehci_ixp_probe),
327188410Sthompsa	DEVMETHOD(device_attach, ehci_ixp_attach),
328188410Sthompsa	DEVMETHOD(device_detach, ehci_ixp_detach),
329188410Sthompsa	DEVMETHOD(device_suspend, ehci_ixp_suspend),
330188410Sthompsa	DEVMETHOD(device_resume, ehci_ixp_resume),
331188410Sthompsa	DEVMETHOD(device_shutdown, ehci_ixp_shutdown),
332188410Sthompsa
333188410Sthompsa	/* Bus interface */
334188410Sthompsa	DEVMETHOD(bus_print_child, bus_generic_print_child),
335188410Sthompsa
336188410Sthompsa	{0, 0}
337188410Sthompsa};
338188410Sthompsa
339188410Sthompsastatic driver_t ehci_driver = {
340188410Sthompsa	"ehci",
341188410Sthompsa	ehci_methods,
342188410Sthompsa	sizeof(struct ixp_ehci_softc),
343188410Sthompsa};
344188410Sthompsa
345188410Sthompsastatic devclass_t ehci_devclass;
346188410Sthompsa
347188410SthompsaDRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0);
348188410SthompsaMODULE_DEPEND(ehci, usb2_controller, 1, 1, 1);
349188410SthompsaMODULE_DEPEND(ehci, usb2_core, 1, 1, 1);
350