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: releng/11.0/sys/dev/usb/controller/ehci_ixp4xx.c 294989 2016-01-28 14:11:59Z mmel $");
31188410Sthompsa
32188410Sthompsa#include "opt_bus.h"
33188410Sthompsa
34194677Sthompsa#include <sys/stdint.h>
35194677Sthompsa#include <sys/stddef.h>
36194677Sthompsa#include <sys/param.h>
37194677Sthompsa#include <sys/queue.h>
38194677Sthompsa#include <sys/types.h>
39194677Sthompsa#include <sys/systm.h>
40194677Sthompsa#include <sys/kernel.h>
41194677Sthompsa#include <sys/bus.h>
42194677Sthompsa#include <sys/module.h>
43194677Sthompsa#include <sys/lock.h>
44194677Sthompsa#include <sys/mutex.h>
45194677Sthompsa#include <sys/condvar.h>
46194677Sthompsa#include <sys/sysctl.h>
47194677Sthompsa#include <sys/sx.h>
48194677Sthompsa#include <sys/unistd.h>
49194677Sthompsa#include <sys/callout.h>
50194677Sthompsa#include <sys/malloc.h>
51194677Sthompsa#include <sys/priv.h>
52194677Sthompsa
53188942Sthompsa#include <dev/usb/usb.h>
54194677Sthompsa#include <dev/usb/usbdi.h>
55188410Sthompsa
56188942Sthompsa#include <dev/usb/usb_core.h>
57188942Sthompsa#include <dev/usb/usb_busdma.h>
58188942Sthompsa#include <dev/usb/usb_process.h>
59188942Sthompsa#include <dev/usb/usb_util.h>
60188410Sthompsa
61188942Sthompsa#include <dev/usb/usb_controller.h>
62188942Sthompsa#include <dev/usb/usb_bus.h>
63188942Sthompsa#include <dev/usb/controller/ehci.h>
64198151Sthompsa#include <dev/usb/controller/ehcireg.h>
65188410Sthompsa
66188410Sthompsa#include <arm/xscale/ixp425/ixp425reg.h>
67188410Sthompsa#include <arm/xscale/ixp425/ixp425var.h>
68188410Sthompsa
69188410Sthompsa#define EHCI_VENDORID_IXP4XX	0x42fa05
70188410Sthompsa#define EHCI_HC_DEVSTR		"IXP4XX Integrated USB 2.0 controller"
71188410Sthompsa
72188410Sthompsastruct ixp_ehci_softc {
73188410Sthompsa	ehci_softc_t		base;	/* storage for EHCI code */
74188410Sthompsa	bus_space_tag_t		iot;
75188410Sthompsa	bus_space_handle_t	ioh;
76188410Sthompsa	struct bus_space	tag;	/* tag for private bus space ops */
77188410Sthompsa};
78188410Sthompsa
79188410Sthompsastatic device_attach_t ehci_ixp_attach;
80188410Sthompsastatic device_detach_t ehci_ixp_detach;
81188410Sthompsa
82277460Sianstatic uint8_t ehci_bs_r_1(bus_space_tag_t tag, bus_space_handle_t, bus_size_t);
83277460Sianstatic void ehci_bs_w_1(bus_space_tag_t tag, bus_space_handle_t, bus_size_t, u_int8_t);
84277460Sianstatic uint16_t ehci_bs_r_2(bus_space_tag_t tag, bus_space_handle_t, bus_size_t);
85277460Sianstatic void ehci_bs_w_2(bus_space_tag_t tag, bus_space_handle_t, bus_size_t, uint16_t);
86277460Sianstatic uint32_t ehci_bs_r_4(bus_space_tag_t tag, bus_space_handle_t, bus_size_t);
87277460Sianstatic void ehci_bs_w_4(bus_space_tag_t tag, bus_space_handle_t, bus_size_t, uint32_t);
88188410Sthompsa
89294989Smmelstatic void
90294989Smmelehci_ixp_post_reset(struct ehci_softc *ehci_softc)
91294989Smmel{
92294989Smmel	uint32_t usbmode;
93294989Smmel
94294989Smmel	/* Force HOST mode, select big-endian mode */
95294989Smmel	usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM);
96294989Smmel	usbmode &= ~EHCI_UM_CM;
97294989Smmel	usbmode |= EHCI_UM_CM_HOST;
98294989Smmel	usbmode |= EHCI_UM_ES_BE;
99294989Smmel	EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode);
100294989Smmel}
101294989Smmel
102188410Sthompsastatic int
103188410Sthompsaehci_ixp_probe(device_t self)
104188410Sthompsa{
105188410Sthompsa
106188410Sthompsa	device_set_desc(self, EHCI_HC_DEVSTR);
107188410Sthompsa
108188410Sthompsa	return (BUS_PROBE_DEFAULT);
109188410Sthompsa}
110188410Sthompsa
111188410Sthompsastatic int
112188410Sthompsaehci_ixp_attach(device_t self)
113188410Sthompsa{
114188410Sthompsa	struct ixp_ehci_softc *isc = device_get_softc(self);
115188410Sthompsa	ehci_softc_t *sc = &isc->base;
116188410Sthompsa	int err;
117188410Sthompsa	int rid;
118188410Sthompsa
119188410Sthompsa	/* initialise some bus fields */
120188410Sthompsa	sc->sc_bus.parent = self;
121188410Sthompsa	sc->sc_bus.devices = sc->sc_devices;
122188410Sthompsa	sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
123276717Shselasky	sc->sc_bus.dma_bits = 32;
124188410Sthompsa
125188410Sthompsa	/* get all DMA memory */
126194228Sthompsa	if (usb_bus_mem_alloc_all(&sc->sc_bus,
127188410Sthompsa	    USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
128188410Sthompsa		return (ENOMEM);
129188410Sthompsa	}
130188410Sthompsa
131188410Sthompsa	/* NB: hints fix the memory location and irq */
132188410Sthompsa
133188410Sthompsa	rid = 0;
134188410Sthompsa	sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
135188410Sthompsa	if (!sc->sc_io_res) {
136188410Sthompsa		device_printf(self, "Could not map memory\n");
137188410Sthompsa		goto error;
138188410Sthompsa	}
139188410Sthompsa
140188410Sthompsa	/*
141188410Sthompsa	 * Craft special resource for bus space ops that handle
142188410Sthompsa	 * byte-alignment of non-word addresses.  Also, since
143188410Sthompsa	 * we're already intercepting bus space ops we handle
144188410Sthompsa	 * the register window offset that could otherwise be
145188410Sthompsa	 * done with bus_space_subregion.
146188410Sthompsa	 */
147188410Sthompsa	isc->iot = rman_get_bustag(sc->sc_io_res);
148277460Sian	isc->tag.bs_privdata = isc->iot;
149188410Sthompsa	/* read single */
150188410Sthompsa	isc->tag.bs_r_1	= ehci_bs_r_1,
151188410Sthompsa	isc->tag.bs_r_2	= ehci_bs_r_2,
152188410Sthompsa	isc->tag.bs_r_4	= ehci_bs_r_4,
153188410Sthompsa	/* write (single) */
154188410Sthompsa	isc->tag.bs_w_1	= ehci_bs_w_1,
155188410Sthompsa	isc->tag.bs_w_2	= ehci_bs_w_2,
156188410Sthompsa	isc->tag.bs_w_4	= ehci_bs_w_4,
157188410Sthompsa
158188410Sthompsa	sc->sc_io_tag = &isc->tag;
159188410Sthompsa	sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
160188410Sthompsa	sc->sc_io_size = IXP435_USB1_SIZE - 0x100;
161188410Sthompsa
162188410Sthompsa	rid = 0;
163188410Sthompsa	sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
164188410Sthompsa	    RF_ACTIVE);
165188410Sthompsa	if (sc->sc_irq_res == NULL) {
166188410Sthompsa		device_printf(self, "Could not allocate irq\n");
167188410Sthompsa		goto error;
168188410Sthompsa	}
169188410Sthompsa	sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
170188410Sthompsa	if (!sc->sc_bus.bdev) {
171188410Sthompsa		device_printf(self, "Could not add USB device\n");
172188410Sthompsa		goto error;
173188410Sthompsa	}
174188410Sthompsa	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
175188410Sthompsa	device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
176188410Sthompsa
177188410Sthompsa	sprintf(sc->sc_vendor, "Intel");
178188410Sthompsa
179188410Sthompsa
180188410Sthompsa	err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
181190183Sthompsa	    NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
182188410Sthompsa	if (err) {
183188410Sthompsa		device_printf(self, "Could not setup irq, %d\n", err);
184188410Sthompsa		sc->sc_intr_hdl = NULL;
185188410Sthompsa		goto error;
186188410Sthompsa	}
187188410Sthompsa
188188410Sthompsa	/*
189294989Smmel	 * Select big-endian byte alignment and arrange to not terminate
190294989Smmel	 * reset operations (the adapter will ignore it if we do but might
191294989Smmel	 * as well save a reg write). Also, the controller has an embedded
192294989Smmel	 * Transaction Translator which means port speed must be read from
193294989Smmel	 * the Port Status register following a port enable.
194188410Sthompsa	 */
195188410Sthompsa	sc->sc_flags |= EHCI_SCFLG_TT
196188410Sthompsa		     | EHCI_SCFLG_BIGEDESC
197188410Sthompsa		     | EHCI_SCFLG_NORESTERM
198188410Sthompsa		     ;
199188410Sthompsa
200294989Smmel	/* Setup callbacks. */
201294989Smmel	sc->sc_vendor_post_reset = ehci_ixp_post_reset;
202294989Smmel	sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc;
203294989Smmel
204188410Sthompsa	err = ehci_init(sc);
205188410Sthompsa	if (!err) {
206188410Sthompsa		err = device_probe_and_attach(sc->sc_bus.bdev);
207188410Sthompsa	}
208188410Sthompsa	if (err) {
209188410Sthompsa		device_printf(self, "USB init failed err=%d\n", err);
210188410Sthompsa		goto error;
211188410Sthompsa	}
212188410Sthompsa	return (0);
213188410Sthompsa
214188410Sthompsaerror:
215188410Sthompsa	ehci_ixp_detach(self);
216188410Sthompsa	return (ENXIO);
217188410Sthompsa}
218188410Sthompsa
219188410Sthompsastatic int
220188410Sthompsaehci_ixp_detach(device_t self)
221188410Sthompsa{
222188410Sthompsa	struct ixp_ehci_softc *isc = device_get_softc(self);
223188410Sthompsa	ehci_softc_t *sc = &isc->base;
224188410Sthompsa	device_t bdev;
225188410Sthompsa	int err;
226188410Sthompsa
227188410Sthompsa 	if (sc->sc_bus.bdev) {
228188410Sthompsa		bdev = sc->sc_bus.bdev;
229188410Sthompsa		device_detach(bdev);
230188410Sthompsa		device_delete_child(self, bdev);
231188410Sthompsa	}
232188410Sthompsa	/* during module unload there are lots of children leftover */
233227849Shselasky	device_delete_children(self);
234188410Sthompsa
235188410Sthompsa 	if (sc->sc_irq_res && sc->sc_intr_hdl) {
236188410Sthompsa		/*
237188410Sthompsa		 * only call ehci_detach() after ehci_init()
238188410Sthompsa		 */
239188410Sthompsa		ehci_detach(sc);
240188410Sthompsa
241188410Sthompsa		err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
242188410Sthompsa
243188410Sthompsa		if (err)
244188410Sthompsa			/* XXX or should we panic? */
245188410Sthompsa			device_printf(self, "Could not tear down irq, %d\n",
246188410Sthompsa			    err);
247188410Sthompsa		sc->sc_intr_hdl = NULL;
248188410Sthompsa	}
249188410Sthompsa
250188410Sthompsa 	if (sc->sc_irq_res) {
251188410Sthompsa		bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
252188410Sthompsa		sc->sc_irq_res = NULL;
253188410Sthompsa	}
254188410Sthompsa	if (sc->sc_io_res) {
255188410Sthompsa		bus_release_resource(self, SYS_RES_MEMORY, 0,
256188410Sthompsa		    sc->sc_io_res);
257188410Sthompsa		sc->sc_io_res = NULL;
258188410Sthompsa	}
259194228Sthompsa	usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
260188410Sthompsa
261188410Sthompsa	return (0);
262188410Sthompsa}
263188410Sthompsa
264188410Sthompsa/*
265188410Sthompsa * Bus space accessors for PIO operations.
266188410Sthompsa */
267188410Sthompsa
268188410Sthompsastatic uint8_t
269277460Sianehci_bs_r_1(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o)
270188410Sthompsa{
271277460Sian	return bus_space_read_1((bus_space_tag_t)tag->bs_privdata, h,
272188410Sthompsa	    0x100 + (o &~ 3) + (3 - (o & 3)));
273188410Sthompsa}
274188410Sthompsa
275188410Sthompsastatic void
276277460Sianehci_bs_w_1(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o, u_int8_t v)
277188410Sthompsa{
278188410Sthompsa	panic("%s", __func__);
279188410Sthompsa}
280188410Sthompsa
281188410Sthompsastatic uint16_t
282277460Sianehci_bs_r_2(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o)
283188410Sthompsa{
284277460Sian	return bus_space_read_2((bus_space_tag_t)tag->bs_privdata, h,
285188410Sthompsa	    0x100 + (o &~ 3) + (2 - (o & 3)));
286188410Sthompsa}
287188410Sthompsa
288188410Sthompsastatic void
289277460Sianehci_bs_w_2(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o, uint16_t v)
290188410Sthompsa{
291188410Sthompsa	panic("%s", __func__);
292188410Sthompsa}
293188410Sthompsa
294188410Sthompsastatic uint32_t
295277460Sianehci_bs_r_4(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o)
296188410Sthompsa{
297277460Sian	return bus_space_read_4((bus_space_tag_t) tag->bs_privdata, h, 0x100 + o);
298188410Sthompsa}
299188410Sthompsa
300188410Sthompsastatic void
301277460Sianehci_bs_w_4(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o, uint32_t v)
302188410Sthompsa{
303277460Sian	bus_space_write_4((bus_space_tag_t) tag->bs_privdata, h, 0x100 + o, v);
304188410Sthompsa}
305188410Sthompsa
306188410Sthompsastatic device_method_t ehci_methods[] = {
307188410Sthompsa	/* Device interface */
308188410Sthompsa	DEVMETHOD(device_probe, ehci_ixp_probe),
309188410Sthompsa	DEVMETHOD(device_attach, ehci_ixp_attach),
310188410Sthompsa	DEVMETHOD(device_detach, ehci_ixp_detach),
311228483Shselasky	DEVMETHOD(device_suspend, bus_generic_suspend),
312228483Shselasky	DEVMETHOD(device_resume, bus_generic_resume),
313228483Shselasky	DEVMETHOD(device_shutdown, bus_generic_shutdown),
314188410Sthompsa
315227843Smarius	DEVMETHOD_END
316188410Sthompsa};
317188410Sthompsa
318188410Sthompsastatic driver_t ehci_driver = {
319188410Sthompsa	"ehci",
320188410Sthompsa	ehci_methods,
321188410Sthompsa	sizeof(struct ixp_ehci_softc),
322188410Sthompsa};
323188410Sthompsa
324188410Sthompsastatic devclass_t ehci_devclass;
325188410Sthompsa
326188410SthompsaDRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0);
327188942SthompsaMODULE_DEPEND(ehci, usb, 1, 1, 1);
328