exynos5_xhci.c revision 279542
1236769Sobrien/*-
2236769Sobrien * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3236769Sobrien * All rights reserved.
4236769Sobrien *
5236769Sobrien * Redistribution and use in source and binary forms, with or without
6236769Sobrien * modification, are permitted provided that the following conditions
7236769Sobrien * are met:
8236769Sobrien * 1. Redistributions of source code must retain the above copyright
9236769Sobrien *    notice, this list of conditions and the following disclaimer.
10236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright
11236769Sobrien *    notice, this list of conditions and the following disclaimer in the
12236769Sobrien *    documentation and/or other materials provided with the distribution.
13236769Sobrien *
14236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17236769Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24236769Sobrien * SUCH DAMAGE.
25236769Sobrien */
26236769Sobrien
27236769Sobrien#include <sys/cdefs.h>
28236769Sobrien__FBSDID("$FreeBSD: head/sys/arm/samsung/exynos/exynos5_xhci.c 279542 2015-03-02 20:38:17Z hselasky $");
29236769Sobrien
30236769Sobrien#include "opt_bus.h"
31236769Sobrien
32236769Sobrien#include <sys/param.h>
33236769Sobrien#include <sys/systm.h>
34236769Sobrien#include <sys/kernel.h>
35236769Sobrien#include <sys/module.h>
36236769Sobrien#include <sys/bus.h>
37236769Sobrien#include <sys/condvar.h>
38236769Sobrien#include <sys/rman.h>
39236769Sobrien
40236769Sobrien#include <dev/ofw/ofw_bus.h>
41236769Sobrien#include <dev/ofw/ofw_bus_subr.h>
42236769Sobrien
43236769Sobrien#include <dev/usb/usb.h>
44236769Sobrien#include <dev/usb/usbdi.h>
45236769Sobrien#include <dev/usb/usb_busdma.h>
46236769Sobrien#include <dev/usb/usb_process.h>
47236769Sobrien#include <dev/usb/usb_controller.h>
48236769Sobrien#include <dev/usb/usb_bus.h>
49236769Sobrien#include <dev/usb/controller/xhci.h>
50236769Sobrien#include <dev/usb/controller/xhcireg.h>
51236769Sobrien
52236769Sobrien#include <machine/bus.h>
53236769Sobrien#include <machine/resource.h>
54236769Sobrien
55236769Sobrien#include <arm/samsung/exynos/exynos5_common.h>
56236769Sobrien
57236769Sobrien#include "opt_platform.h"
58236769Sobrien
59236769Sobrien#define	GSNPSID				0x20
60236769Sobrien#define	 GSNPSID_MASK			0xffff0000
61236769Sobrien#define	 REVISION_MASK			0xffff
62236769Sobrien#define	GCTL				0x10
63236769Sobrien#define	 GCTL_PWRDNSCALE(n)		((n) << 19)
64236769Sobrien#define	 GCTL_U2RSTECN			(1 << 16)
65236769Sobrien#define	 GCTL_CLK_BUS			(0)
66236769Sobrien#define	 GCTL_CLK_PIPE			(1)
67236769Sobrien#define	 GCTL_CLK_PIPEHALF		(2)
68236769Sobrien#define	 GCTL_CLK_M			(3)
69236769Sobrien#define	 GCTL_CLK_S			(6)
70236769Sobrien#define	 GCTL_PRTCAP(n)			(((n) & (3 << 12)) >> 12)
71236769Sobrien#define	 GCTL_PRTCAPDIR(n)		((n) << 12)
72236769Sobrien#define	 GCTL_PRTCAP_HOST		1
73236769Sobrien#define	 GCTL_PRTCAP_DEVICE		2
74236769Sobrien#define	 GCTL_PRTCAP_OTG		3
75236769Sobrien#define	 GCTL_CORESOFTRESET		(1 << 11)
76236769Sobrien#define	 GCTL_SCALEDOWN_MASK		3
77236769Sobrien#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)) {
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	device_t bdev;
292	int err;
293
294	if (esc->base.sc_bus.bdev != NULL) {
295		bdev = esc->base.sc_bus.bdev;
296		device_detach(bdev);
297		device_delete_child(dev, bdev);
298	}
299	/* During module unload there are lots of children leftover */
300	device_delete_children(dev);
301
302	xhci_halt_controller(&esc->base);
303
304	if (esc->res[2] && esc->base.sc_intr_hdl) {
305		err = bus_teardown_intr(dev, esc->res[2],
306		    esc->base.sc_intr_hdl);
307		if (err) {
308			device_printf(dev, "Could not tear down IRQ,"
309			    " %d\n", err);
310			return (err);
311		}
312	}
313
314	bus_release_resources(dev, exynos_xhci_spec, esc->res);
315
316	xhci_uninit(&esc->base);
317
318	return (0);
319}
320