exynos5_xhci.c revision 269703
1/*-
2 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/arm/samsung/exynos/exynos5_xhci.c 269703 2014-08-08 06:30:17Z nwhitehorn $");
29
30#include "opt_bus.h"
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/module.h>
36#include <sys/bus.h>
37#include <sys/condvar.h>
38#include <sys/rman.h>
39
40#include <dev/ofw/ofw_bus.h>
41#include <dev/ofw/ofw_bus_subr.h>
42
43#include <dev/usb/usb.h>
44#include <dev/usb/usbdi.h>
45#include <dev/usb/usb_busdma.h>
46#include <dev/usb/usb_process.h>
47#include <dev/usb/usb_controller.h>
48#include <dev/usb/usb_bus.h>
49#include <dev/usb/controller/xhci.h>
50#include <dev/usb/controller/xhcireg.h>
51
52#include <machine/bus.h>
53#include <machine/resource.h>
54
55#include <arm/samsung/exynos/exynos5_common.h>
56
57#include "opt_platform.h"
58
59#define	GSNPSID				0x20
60#define	 GSNPSID_MASK			0xffff0000
61#define	 REVISION_MASK			0xffff
62#define	GCTL				0x10
63#define	 GCTL_PWRDNSCALE(n)		((n) << 19)
64#define	 GCTL_U2RSTECN			(1 << 16)
65#define	 GCTL_CLK_BUS			(0)
66#define	 GCTL_CLK_PIPE			(1)
67#define	 GCTL_CLK_PIPEHALF		(2)
68#define	 GCTL_CLK_M			(3)
69#define	 GCTL_CLK_S			(6)
70#define	 GCTL_PRTCAP(n)			(((n) & (3 << 12)) >> 12)
71#define	 GCTL_PRTCAPDIR(n)		((n) << 12)
72#define	 GCTL_PRTCAP_HOST		1
73#define	 GCTL_PRTCAP_DEVICE		2
74#define	 GCTL_PRTCAP_OTG		3
75#define	 GCTL_CORESOFTRESET		(1 << 11)
76#define	 GCTL_SCALEDOWN_MASK		3
77#define	 GCTL_SCALEDOWN_SHIFT		4
78#define	 GCTL_DISSCRAMBLE		(1 << 3)
79#define	 GCTL_DSBLCLKGTNG		(1 << 0)
80#define	GHWPARAMS1			0x3c
81#define	 GHWPARAMS1_EN_PWROPT(n)	(((n) & (3 << 24)) >> 24)
82#define	 GHWPARAMS1_EN_PWROPT_NO	0
83#define	 GHWPARAMS1_EN_PWROPT_CLK	1
84#define	GUSB2PHYCFG(n)			(0x100 + (n * 0x04))
85#define	 GUSB2PHYCFG_PHYSOFTRST		(1 << 31)
86#define	 GUSB2PHYCFG_SUSPHY		(1 << 6)
87#define	GUSB3PIPECTL(n)			(0x1c0 + (n * 0x04))
88#define	 GUSB3PIPECTL_PHYSOFTRST	(1 << 31)
89#define	 GUSB3PIPECTL_SUSPHY		(1 << 17)
90
91/* Forward declarations */
92static int	exynos_xhci_attach(device_t dev);
93static int	exynos_xhci_detach(device_t dev);
94static int	exynos_xhci_probe(device_t dev);
95
96struct exynos_xhci_softc {
97	device_t		dev;
98	struct xhci_softc	base;
99	struct resource		*res[3];
100	bus_space_tag_t		bst;
101	bus_space_handle_t	bsh;
102};
103
104static struct resource_spec exynos_xhci_spec[] = {
105	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
106	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
107	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
108	{ -1, 0 }
109};
110
111static device_method_t xhci_methods[] = {
112	/* Device interface */
113	DEVMETHOD(device_probe, exynos_xhci_probe),
114	DEVMETHOD(device_attach, exynos_xhci_attach),
115	DEVMETHOD(device_detach, exynos_xhci_detach),
116	DEVMETHOD(device_suspend, bus_generic_suspend),
117	DEVMETHOD(device_resume, bus_generic_resume),
118	DEVMETHOD(device_shutdown, bus_generic_shutdown),
119
120	DEVMETHOD_END
121};
122
123/* kobj_class definition */
124static driver_t xhci_driver = {
125	"xhci",
126	xhci_methods,
127	sizeof(struct xhci_softc)
128};
129
130static devclass_t xhci_devclass;
131
132DRIVER_MODULE(xhci, simplebus, xhci_driver, xhci_devclass, 0, 0);
133MODULE_DEPEND(xhci, usb, 1, 1, 1);
134
135/*
136 * Public methods
137 */
138static int
139exynos_xhci_probe(device_t dev)
140{
141
142	if (!ofw_bus_status_okay(dev))
143		return (ENXIO);
144
145	if (ofw_bus_is_compatible(dev, "samsung,exynos5250-dwusb3") == 0)
146		return (ENXIO);
147
148	device_set_desc(dev, "Exynos USB 3.0 controller");
149	return (BUS_PROBE_DEFAULT);
150}
151
152static int
153dwc3_init(struct exynos_xhci_softc *esc)
154{
155	struct xhci_softc *sc;
156	int hwparams1;
157	int rev;
158	int reg;
159
160	sc = &esc->base;
161
162	rev = READ4(esc, GSNPSID);
163	if ((rev & GSNPSID_MASK) != 0x55330000) {
164		printf("It is not DWC3 controller\n");
165		return (-1);
166	}
167
168	/* Reset controller */
169	WRITE4(esc, GCTL, GCTL_CORESOFTRESET);
170	WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST);
171	WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST);
172
173	DELAY(100000);
174
175	reg = READ4(esc, GUSB3PIPECTL(0));
176	reg &= ~(GUSB3PIPECTL_PHYSOFTRST);
177	WRITE4(esc, GUSB3PIPECTL(0), reg);
178
179	reg = READ4(esc, GUSB2PHYCFG(0));
180	reg &= ~(GUSB2PHYCFG_PHYSOFTRST);
181	WRITE4(esc, GUSB2PHYCFG(0), reg);
182
183	reg = READ4(esc, GCTL);
184	reg &= ~GCTL_CORESOFTRESET;
185	WRITE4(esc, GCTL, reg);
186
187	hwparams1 = READ4(esc, GHWPARAMS1);
188
189	reg = READ4(esc, GCTL);
190	reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT);
191	reg &= ~(GCTL_DISSCRAMBLE);
192
193	if (GHWPARAMS1_EN_PWROPT(hwparams1) == \
194	    GHWPARAMS1_EN_PWROPT_CLK)
195		reg &= ~(GCTL_DSBLCLKGTNG);
196
197	if ((rev & REVISION_MASK) < 0x190a)
198		reg |= (GCTL_U2RSTECN);
199	WRITE4(esc, GCTL, reg);
200
201	/* Set host mode */
202	reg = READ4(esc, GCTL);
203	reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG));
204	reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST);
205	WRITE4(esc, GCTL, reg);
206
207	return (0);
208}
209
210static int
211exynos_xhci_attach(device_t dev)
212{
213	struct exynos_xhci_softc *esc;
214	struct xhci_softc *sc;
215	bus_space_handle_t bsh;
216	int err;
217
218	esc = device_get_softc(dev);
219	esc->dev = dev;
220
221	sc = &esc->base;
222
223	if (xhci_init(sc, dev)) {
224		device_printf(dev, "Could not initialize softc\n");
225		return (ENXIO);
226	}
227
228	if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) {
229		device_printf(dev, "could not allocate resources\n");
230		return (ENXIO);
231	}
232
233	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0);
234
235	/* XHCI registers */
236	sc->sc_io_tag = rman_get_bustag(esc->res[0]);
237	bsh = rman_get_bushandle(esc->res[0]);
238	sc->sc_io_size = rman_get_size(esc->res[0]);
239
240	/* DWC3 ctrl registers */
241	esc->bst = rman_get_bustag(esc->res[1]);
242	esc->bsh = rman_get_bushandle(esc->res[1]);
243
244	/*
245	 * Set handle to USB related registers subregion used by
246	 * generic XHCI driver.
247	 */
248	err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0,
249	    sc->sc_io_size, &sc->sc_io_hdl);
250	if (err != 0)
251		return (ENXIO);
252
253	/* Setup interrupt handler */
254	err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE,
255	    NULL, (driver_intr_t *)xhci_interrupt, sc,
256	    &sc->sc_intr_hdl);
257	if (err) {
258		device_printf(dev, "Could not setup irq, %d\n", err);
259		return (1);
260	}
261
262	/* Add USB device */
263	sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
264	if (!sc->sc_bus.bdev) {
265		device_printf(dev, "Could not add USB device\n");
266		err = bus_teardown_intr(dev, esc->res[2],
267		    sc->sc_intr_hdl);
268		if (err)
269			device_printf(dev, "Could not tear down irq,"
270			    " %d\n", err);
271		return (1);
272	}
273	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
274	strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor));
275
276	dwc3_init(esc);
277
278	err = xhci_halt_controller(sc);
279	if (err == 0) {
280		device_printf(dev, "Starting controller\n");
281		err = xhci_start_controller(sc);
282	}
283
284	if (err == 0) {
285		device_printf(dev, "Controller started\n");
286		err = device_probe_and_attach(sc->sc_bus.bdev);
287	}
288
289	if (err == 0) {
290		device_printf(dev, "Attached success\n");
291	} else {
292		device_printf(dev, "USB 3.0 init failed err=%d\n", err);
293
294		device_delete_child(dev, sc->sc_bus.bdev);
295		sc->sc_bus.bdev = NULL;
296
297		err = bus_teardown_intr(dev, esc->res[2],
298		    sc->sc_intr_hdl);
299		if (err)
300			device_printf(dev, "Could not tear down irq,"
301			    " %d\n", err);
302		return (1);
303	}
304	return (0);
305}
306
307static int
308exynos_xhci_detach(device_t dev)
309{
310	struct exynos_xhci_softc *esc;
311	struct xhci_softc *sc;
312	int err;
313
314	esc = device_get_softc(dev);
315	sc = &esc->base;
316
317	if (esc->res[2] && sc->sc_intr_hdl) {
318		err = bus_teardown_intr(dev, esc->res[2],
319		    sc->sc_intr_hdl);
320		if (err) {
321			device_printf(dev, "Could not tear down irq,"
322			    " %d\n", err);
323			return (err);
324		}
325		sc->sc_intr_hdl = NULL;
326	}
327
328	if (sc->sc_bus.bdev) {
329		device_delete_child(dev, sc->sc_bus.bdev);
330		sc->sc_bus.bdev = NULL;
331	}
332
333	/* During module unload there are lots of children leftover */
334	device_delete_children(dev);
335
336	bus_release_resources(dev, exynos_xhci_spec, esc->res);
337
338	return (0);
339}
340