exynos5_xhci.c revision 269369
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 269369 2014-08-01 06:20:25Z br $");
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 <dev/fdt/fdt_common.h>
53
54#include <machine/bus.h>
55#include <machine/resource.h>
56
57#include <arm/samsung/exynos/exynos5_common.h>
58
59#include "opt_platform.h"
60
61#define	GSNPSID				0x20
62#define	 GSNPSID_MASK			0xffff0000
63#define	 REVISION_MASK			0xffff
64#define	GCTL				0x10
65#define	 GCTL_PWRDNSCALE(n)		((n) << 19)
66#define	 GCTL_U2RSTECN			(1 << 16)
67#define	 GCTL_CLK_BUS			(0)
68#define	 GCTL_CLK_PIPE			(1)
69#define	 GCTL_CLK_PIPEHALF		(2)
70#define	 GCTL_CLK_M			(3)
71#define	 GCTL_CLK_S			(6)
72#define	 GCTL_PRTCAP(n)			(((n) & (3 << 12)) >> 12)
73#define	 GCTL_PRTCAPDIR(n)		((n) << 12)
74#define	 GCTL_PRTCAP_HOST		1
75#define	 GCTL_PRTCAP_DEVICE		2
76#define	 GCTL_PRTCAP_OTG		3
77#define	 GCTL_CORESOFTRESET		(1 << 11)
78#define	 GCTL_SCALEDOWN_MASK		3
79#define	 GCTL_SCALEDOWN_SHIFT		4
80#define	 GCTL_DISSCRAMBLE		(1 << 3)
81#define	 GCTL_DSBLCLKGTNG		(1 << 0)
82#define	GHWPARAMS1			0x3c
83#define	 GHWPARAMS1_EN_PWROPT(n)	(((n) & (3 << 24)) >> 24)
84#define	 GHWPARAMS1_EN_PWROPT_NO	0
85#define	 GHWPARAMS1_EN_PWROPT_CLK	1
86#define	GUSB2PHYCFG(n)			(0x100 + (n * 0x04))
87#define	 GUSB2PHYCFG_PHYSOFTRST		(1 << 31)
88#define	 GUSB2PHYCFG_SUSPHY		(1 << 6)
89#define	GUSB3PIPECTL(n)			(0x1c0 + (n * 0x04))
90#define	 GUSB3PIPECTL_PHYSOFTRST	(1 << 31)
91#define	 GUSB3PIPECTL_SUSPHY		(1 << 17)
92
93/* Forward declarations */
94static int	exynos_xhci_attach(device_t dev);
95static int	exynos_xhci_detach(device_t dev);
96static int	exynos_xhci_probe(device_t dev);
97
98struct exynos_xhci_softc {
99	device_t		dev;
100	struct xhci_softc	base;
101	struct resource		*res[3];
102	bus_space_tag_t		bst;
103	bus_space_handle_t	bsh;
104};
105
106static struct resource_spec exynos_xhci_spec[] = {
107	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
108	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
109	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
110	{ -1, 0 }
111};
112
113static device_method_t xhci_methods[] = {
114	/* Device interface */
115	DEVMETHOD(device_probe, exynos_xhci_probe),
116	DEVMETHOD(device_attach, exynos_xhci_attach),
117	DEVMETHOD(device_detach, exynos_xhci_detach),
118	DEVMETHOD(device_suspend, bus_generic_suspend),
119	DEVMETHOD(device_resume, bus_generic_resume),
120	DEVMETHOD(device_shutdown, bus_generic_shutdown),
121
122	DEVMETHOD_END
123};
124
125/* kobj_class definition */
126static driver_t xhci_driver = {
127	"xhci",
128	xhci_methods,
129	sizeof(struct xhci_softc)
130};
131
132static devclass_t xhci_devclass;
133
134DRIVER_MODULE(xhci, simplebus, xhci_driver, xhci_devclass, 0, 0);
135MODULE_DEPEND(xhci, usb, 1, 1, 1);
136
137/*
138 * Public methods
139 */
140static int
141exynos_xhci_probe(device_t dev)
142{
143
144	if (!ofw_bus_status_okay(dev))
145		return (ENXIO);
146
147	if (ofw_bus_is_compatible(dev, "samsung,exynos5250-dwusb3") == 0)
148		return (ENXIO);
149
150	device_set_desc(dev, "Exynos USB 3.0 controller");
151	return (BUS_PROBE_DEFAULT);
152}
153
154static int
155dwc3_init(struct exynos_xhci_softc *esc)
156{
157	struct xhci_softc *sc;
158	int hwparams1;
159	int rev;
160	int reg;
161
162	sc = &esc->base;
163
164	rev = READ4(esc, GSNPSID);
165	if ((rev & GSNPSID_MASK) != 0x55330000) {
166		printf("It is not DWC3 controller\n");
167		return (-1);
168	}
169
170	/* Reset controller */
171	WRITE4(esc, GCTL, GCTL_CORESOFTRESET);
172	WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST);
173	WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST);
174
175	DELAY(100000);
176
177	reg = READ4(esc, GUSB3PIPECTL(0));
178	reg &= ~(GUSB3PIPECTL_PHYSOFTRST);
179	WRITE4(esc, GUSB3PIPECTL(0), reg);
180
181	reg = READ4(esc, GUSB2PHYCFG(0));
182	reg &= ~(GUSB2PHYCFG_PHYSOFTRST);
183	WRITE4(esc, GUSB2PHYCFG(0), reg);
184
185	reg = READ4(esc, GCTL);
186	reg &= ~GCTL_CORESOFTRESET;
187	WRITE4(esc, GCTL, reg);
188
189	hwparams1 = READ4(esc, GHWPARAMS1);
190
191	reg = READ4(esc, GCTL);
192	reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT);
193	reg &= ~(GCTL_DISSCRAMBLE);
194
195	if (GHWPARAMS1_EN_PWROPT(hwparams1) == \
196	    GHWPARAMS1_EN_PWROPT_CLK)
197		reg &= ~(GCTL_DSBLCLKGTNG);
198
199	if ((rev & REVISION_MASK) < 0x190a)
200		reg |= (GCTL_U2RSTECN);
201	WRITE4(esc, GCTL, reg);
202
203	/* Set host mode */
204	reg = READ4(esc, GCTL);
205	reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG));
206	reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST);
207	WRITE4(esc, GCTL, reg);
208
209	return (0);
210}
211
212static int
213exynos_xhci_attach(device_t dev)
214{
215	struct exynos_xhci_softc *esc;
216	struct xhci_softc *sc;
217	bus_space_handle_t bsh;
218	int err;
219
220	esc = device_get_softc(dev);
221	esc->dev = dev;
222
223	sc = &esc->base;
224
225	if (xhci_init(sc, dev)) {
226		device_printf(dev, "Could not initialize softc\n");
227		return (ENXIO);
228	}
229
230	if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) {
231		device_printf(dev, "could not allocate resources\n");
232		return (ENXIO);
233	}
234
235	usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0);
236
237	/* XHCI registers */
238	sc->sc_io_tag = rman_get_bustag(esc->res[0]);
239	bsh = rman_get_bushandle(esc->res[0]);
240	sc->sc_io_size = rman_get_size(esc->res[0]);
241
242	/* DWC3 ctrl registers */
243	esc->bst = rman_get_bustag(esc->res[1]);
244	esc->bsh = rman_get_bushandle(esc->res[1]);
245
246	/*
247	 * Set handle to USB related registers subregion used by
248	 * generic XHCI driver.
249	 */
250	err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0,
251	    sc->sc_io_size, &sc->sc_io_hdl);
252	if (err != 0)
253		return (ENXIO);
254
255	/* Setup interrupt handler */
256	err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE,
257	    NULL, (driver_intr_t *)xhci_interrupt, sc,
258	    &sc->sc_intr_hdl);
259	if (err) {
260		device_printf(dev, "Could not setup irq, %d\n", err);
261		return (1);
262	}
263
264	/* Add USB device */
265	sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
266	if (!sc->sc_bus.bdev) {
267		device_printf(dev, "Could not add USB device\n");
268		err = bus_teardown_intr(dev, esc->res[2],
269		    sc->sc_intr_hdl);
270		if (err)
271			device_printf(dev, "Could not tear down irq,"
272			    " %d\n", err);
273		return (1);
274	}
275	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
276	strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor));
277
278	dwc3_init(esc);
279
280	err = xhci_halt_controller(sc);
281	if (err == 0) {
282		device_printf(dev, "Starting controller\n");
283		err = xhci_start_controller(sc);
284	}
285
286	if (err == 0) {
287		device_printf(dev, "Controller started\n");
288		err = device_probe_and_attach(sc->sc_bus.bdev);
289	}
290
291	if (err == 0) {
292		device_printf(dev, "Attached success\n");
293	} else {
294		device_printf(dev, "USB 3.0 init failed err=%d\n", err);
295
296		device_delete_child(dev, sc->sc_bus.bdev);
297		sc->sc_bus.bdev = NULL;
298
299		err = bus_teardown_intr(dev, esc->res[2],
300		    sc->sc_intr_hdl);
301		if (err)
302			device_printf(dev, "Could not tear down irq,"
303			    " %d\n", err);
304		return (1);
305	}
306	return (0);
307}
308
309static int
310exynos_xhci_detach(device_t dev)
311{
312	struct exynos_xhci_softc *esc;
313	struct xhci_softc *sc;
314	int err;
315
316	esc = device_get_softc(dev);
317	sc = &esc->base;
318
319	if (esc->res[2] && sc->sc_intr_hdl) {
320		err = bus_teardown_intr(dev, esc->res[2],
321		    sc->sc_intr_hdl);
322		if (err) {
323			device_printf(dev, "Could not tear down irq,"
324			    " %d\n", err);
325			return (err);
326		}
327		sc->sc_intr_hdl = NULL;
328	}
329
330	if (sc->sc_bus.bdev) {
331		device_delete_child(dev, sc->sc_bus.bdev);
332		sc->sc_bus.bdev = NULL;
333	}
334
335	/* During module unload there are lots of children leftover */
336	device_delete_children(dev);
337
338	bus_release_resources(dev, exynos_xhci_spec, esc->res);
339
340	return (0);
341}
342