exynos5_xhci.c revision 308401
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: stable/11/sys/arm/samsung/exynos/exynos5_xhci.c 308401 2016-11-07 08:36:06Z hselasky $");
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 device_attach_t exynos_xhci_attach;
93static device_detach_t exynos_xhci_detach;
94static device_probe_t exynos_xhci_probe;
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	int hwparams1;
156	int rev;
157	int reg;
158
159	rev = READ4(esc, GSNPSID);
160	if ((rev & GSNPSID_MASK) != 0x55330000) {
161		printf("It is not DWC3 controller\n");
162		return (-1);
163	}
164
165	/* Reset controller */
166	WRITE4(esc, GCTL, GCTL_CORESOFTRESET);
167	WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST);
168	WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST);
169
170	DELAY(100000);
171
172	reg = READ4(esc, GUSB3PIPECTL(0));
173	reg &= ~(GUSB3PIPECTL_PHYSOFTRST);
174	WRITE4(esc, GUSB3PIPECTL(0), reg);
175
176	reg = READ4(esc, GUSB2PHYCFG(0));
177	reg &= ~(GUSB2PHYCFG_PHYSOFTRST);
178	WRITE4(esc, GUSB2PHYCFG(0), reg);
179
180	reg = READ4(esc, GCTL);
181	reg &= ~GCTL_CORESOFTRESET;
182	WRITE4(esc, GCTL, reg);
183
184	hwparams1 = READ4(esc, GHWPARAMS1);
185
186	reg = READ4(esc, GCTL);
187	reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT);
188	reg &= ~(GCTL_DISSCRAMBLE);
189
190	if (GHWPARAMS1_EN_PWROPT(hwparams1) == \
191	    GHWPARAMS1_EN_PWROPT_CLK)
192		reg &= ~(GCTL_DSBLCLKGTNG);
193
194	if ((rev & REVISION_MASK) < 0x190a)
195		reg |= (GCTL_U2RSTECN);
196	WRITE4(esc, GCTL, reg);
197
198	/* Set host mode */
199	reg = READ4(esc, GCTL);
200	reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG));
201	reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST);
202	WRITE4(esc, GCTL, reg);
203
204	return (0);
205}
206
207static int
208exynos_xhci_attach(device_t dev)
209{
210	struct exynos_xhci_softc *esc = device_get_softc(dev);
211	bus_space_handle_t bsh;
212	int err;
213
214	esc->dev = dev;
215
216	if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) {
217		device_printf(dev, "could not allocate resources\n");
218		return (ENXIO);
219	}
220
221	/* XHCI registers */
222	esc->base.sc_io_tag = rman_get_bustag(esc->res[0]);
223	bsh = rman_get_bushandle(esc->res[0]);
224	esc->base.sc_io_size = rman_get_size(esc->res[0]);
225
226	/* DWC3 ctrl registers */
227	esc->bst = rman_get_bustag(esc->res[1]);
228	esc->bsh = rman_get_bushandle(esc->res[1]);
229
230	/*
231	 * Set handle to USB related registers subregion used by
232	 * generic XHCI driver.
233	 */
234	err = bus_space_subregion(esc->base.sc_io_tag, bsh, 0x0,
235	    esc->base.sc_io_size, &esc->base.sc_io_hdl);
236	if (err != 0) {
237		device_printf(dev, "Subregion failed\n");
238		bus_release_resources(dev, exynos_xhci_spec, esc->res);
239		return (ENXIO);
240	}
241
242	if (xhci_init(&esc->base, dev, 0)) {
243		device_printf(dev, "Could not initialize softc\n");
244		bus_release_resources(dev, exynos_xhci_spec, esc->res);
245		return (ENXIO);
246	}
247
248	/* Setup interrupt handler */
249	err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE,
250	    NULL, (driver_intr_t *)xhci_interrupt, &esc->base,
251	    &esc->base.sc_intr_hdl);
252	if (err) {
253		device_printf(dev, "Could not setup irq, %d\n", err);
254		esc->base.sc_intr_hdl = NULL;
255		goto error;
256	}
257
258	/* Add USB device */
259	esc->base.sc_bus.bdev = device_add_child(dev, "usbus", -1);
260	if (esc->base.sc_bus.bdev == NULL) {
261		device_printf(dev, "Could not add USB device\n");
262		goto error;
263	}
264	device_set_ivars(esc->base.sc_bus.bdev, &esc->base.sc_bus);
265	strlcpy(esc->base.sc_vendor, "Samsung", sizeof(esc->base.sc_vendor));
266
267	dwc3_init(esc);
268
269	err = xhci_halt_controller(&esc->base);
270	if (err == 0) {
271		device_printf(dev, "Starting controller\n");
272		err = xhci_start_controller(&esc->base);
273	}
274	if (err == 0) {
275		device_printf(dev, "Controller started\n");
276		err = device_probe_and_attach(esc->base.sc_bus.bdev);
277	}
278	if (err != 0)
279		goto error;
280	return (0);
281
282error:
283	exynos_xhci_detach(dev);
284	return (ENXIO);
285}
286
287static int
288exynos_xhci_detach(device_t dev)
289{
290	struct exynos_xhci_softc *esc = device_get_softc(dev);
291	int err;
292
293	/* During module unload there are lots of children leftover */
294	device_delete_children(dev);
295
296	xhci_halt_controller(&esc->base);
297
298	if (esc->res[2] && esc->base.sc_intr_hdl) {
299		err = bus_teardown_intr(dev, esc->res[2],
300		    esc->base.sc_intr_hdl);
301		if (err) {
302			device_printf(dev, "Could not tear down IRQ,"
303			    " %d\n", err);
304			return (err);
305		}
306	}
307
308	bus_release_resources(dev, exynos_xhci_spec, esc->res);
309
310	xhci_uninit(&esc->base);
311
312	return (0);
313}
314