1269369Sbr/*-
2269369Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3269369Sbr * All rights reserved.
4269369Sbr *
5269369Sbr * Redistribution and use in source and binary forms, with or without
6269369Sbr * modification, are permitted provided that the following conditions
7269369Sbr * are met:
8269369Sbr * 1. Redistributions of source code must retain the above copyright
9269369Sbr *    notice, this list of conditions and the following disclaimer.
10269369Sbr * 2. Redistributions in binary form must reproduce the above copyright
11269369Sbr *    notice, this list of conditions and the following disclaimer in the
12269369Sbr *    documentation and/or other materials provided with the distribution.
13269369Sbr *
14269369Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15269369Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16269369Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17269369Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18269369Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19269369Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20269369Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21269369Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22269369Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23269369Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24269369Sbr * SUCH DAMAGE.
25269369Sbr */
26269369Sbr
27269369Sbr#include <sys/cdefs.h>
28269369Sbr__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_xhci.c 308401 2016-11-07 08:36:06Z hselasky $");
29269369Sbr
30269369Sbr#include "opt_bus.h"
31269369Sbr
32269369Sbr#include <sys/param.h>
33269369Sbr#include <sys/systm.h>
34269369Sbr#include <sys/kernel.h>
35269369Sbr#include <sys/module.h>
36269369Sbr#include <sys/bus.h>
37269369Sbr#include <sys/condvar.h>
38269369Sbr#include <sys/rman.h>
39269369Sbr
40269369Sbr#include <dev/ofw/ofw_bus.h>
41269369Sbr#include <dev/ofw/ofw_bus_subr.h>
42269369Sbr
43269369Sbr#include <dev/usb/usb.h>
44269369Sbr#include <dev/usb/usbdi.h>
45269369Sbr#include <dev/usb/usb_busdma.h>
46269369Sbr#include <dev/usb/usb_process.h>
47269369Sbr#include <dev/usb/usb_controller.h>
48269369Sbr#include <dev/usb/usb_bus.h>
49269369Sbr#include <dev/usb/controller/xhci.h>
50269369Sbr#include <dev/usb/controller/xhcireg.h>
51269369Sbr
52269369Sbr#include <machine/bus.h>
53269369Sbr#include <machine/resource.h>
54269369Sbr
55269369Sbr#include <arm/samsung/exynos/exynos5_common.h>
56269369Sbr
57269369Sbr#include "opt_platform.h"
58269369Sbr
59269369Sbr#define	GSNPSID				0x20
60269369Sbr#define	 GSNPSID_MASK			0xffff0000
61269369Sbr#define	 REVISION_MASK			0xffff
62269369Sbr#define	GCTL				0x10
63269369Sbr#define	 GCTL_PWRDNSCALE(n)		((n) << 19)
64269369Sbr#define	 GCTL_U2RSTECN			(1 << 16)
65269369Sbr#define	 GCTL_CLK_BUS			(0)
66269369Sbr#define	 GCTL_CLK_PIPE			(1)
67269369Sbr#define	 GCTL_CLK_PIPEHALF		(2)
68269369Sbr#define	 GCTL_CLK_M			(3)
69269369Sbr#define	 GCTL_CLK_S			(6)
70269369Sbr#define	 GCTL_PRTCAP(n)			(((n) & (3 << 12)) >> 12)
71269369Sbr#define	 GCTL_PRTCAPDIR(n)		((n) << 12)
72269369Sbr#define	 GCTL_PRTCAP_HOST		1
73269369Sbr#define	 GCTL_PRTCAP_DEVICE		2
74269369Sbr#define	 GCTL_PRTCAP_OTG		3
75269369Sbr#define	 GCTL_CORESOFTRESET		(1 << 11)
76269369Sbr#define	 GCTL_SCALEDOWN_MASK		3
77269369Sbr#define	 GCTL_SCALEDOWN_SHIFT		4
78269369Sbr#define	 GCTL_DISSCRAMBLE		(1 << 3)
79269369Sbr#define	 GCTL_DSBLCLKGTNG		(1 << 0)
80269369Sbr#define	GHWPARAMS1			0x3c
81269369Sbr#define	 GHWPARAMS1_EN_PWROPT(n)	(((n) & (3 << 24)) >> 24)
82269369Sbr#define	 GHWPARAMS1_EN_PWROPT_NO	0
83269369Sbr#define	 GHWPARAMS1_EN_PWROPT_CLK	1
84269369Sbr#define	GUSB2PHYCFG(n)			(0x100 + (n * 0x04))
85269369Sbr#define	 GUSB2PHYCFG_PHYSOFTRST		(1 << 31)
86269369Sbr#define	 GUSB2PHYCFG_SUSPHY		(1 << 6)
87269369Sbr#define	GUSB3PIPECTL(n)			(0x1c0 + (n * 0x04))
88269369Sbr#define	 GUSB3PIPECTL_PHYSOFTRST	(1 << 31)
89269369Sbr#define	 GUSB3PIPECTL_SUSPHY		(1 << 17)
90269369Sbr
91269369Sbr/* Forward declarations */
92279542Shselaskystatic device_attach_t exynos_xhci_attach;
93279542Shselaskystatic device_detach_t exynos_xhci_detach;
94279542Shselaskystatic device_probe_t exynos_xhci_probe;
95269369Sbr
96269369Sbrstruct exynos_xhci_softc {
97269369Sbr	device_t		dev;
98269369Sbr	struct xhci_softc	base;
99269369Sbr	struct resource		*res[3];
100269369Sbr	bus_space_tag_t		bst;
101269369Sbr	bus_space_handle_t	bsh;
102269369Sbr};
103269369Sbr
104269369Sbrstatic struct resource_spec exynos_xhci_spec[] = {
105269369Sbr	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
106269369Sbr	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
107269369Sbr	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
108269369Sbr	{ -1, 0 }
109269369Sbr};
110269369Sbr
111269369Sbrstatic device_method_t xhci_methods[] = {
112269369Sbr	/* Device interface */
113269369Sbr	DEVMETHOD(device_probe, exynos_xhci_probe),
114269369Sbr	DEVMETHOD(device_attach, exynos_xhci_attach),
115269369Sbr	DEVMETHOD(device_detach, exynos_xhci_detach),
116269369Sbr	DEVMETHOD(device_suspend, bus_generic_suspend),
117269369Sbr	DEVMETHOD(device_resume, bus_generic_resume),
118269369Sbr	DEVMETHOD(device_shutdown, bus_generic_shutdown),
119269369Sbr
120269369Sbr	DEVMETHOD_END
121269369Sbr};
122269369Sbr
123269369Sbr/* kobj_class definition */
124269369Sbrstatic driver_t xhci_driver = {
125269369Sbr	"xhci",
126269369Sbr	xhci_methods,
127269369Sbr	sizeof(struct xhci_softc)
128269369Sbr};
129269369Sbr
130269369Sbrstatic devclass_t xhci_devclass;
131269369Sbr
132269369SbrDRIVER_MODULE(xhci, simplebus, xhci_driver, xhci_devclass, 0, 0);
133269369SbrMODULE_DEPEND(xhci, usb, 1, 1, 1);
134269369Sbr
135269369Sbr/*
136269369Sbr * Public methods
137269369Sbr */
138269369Sbrstatic int
139269369Sbrexynos_xhci_probe(device_t dev)
140269369Sbr{
141269369Sbr
142269369Sbr	if (!ofw_bus_status_okay(dev))
143269369Sbr		return (ENXIO);
144269369Sbr
145269369Sbr	if (ofw_bus_is_compatible(dev, "samsung,exynos5250-dwusb3") == 0)
146269369Sbr		return (ENXIO);
147269369Sbr
148269369Sbr	device_set_desc(dev, "Exynos USB 3.0 controller");
149269369Sbr	return (BUS_PROBE_DEFAULT);
150269369Sbr}
151269369Sbr
152269369Sbrstatic int
153269369Sbrdwc3_init(struct exynos_xhci_softc *esc)
154269369Sbr{
155269369Sbr	int hwparams1;
156269369Sbr	int rev;
157269369Sbr	int reg;
158269369Sbr
159269369Sbr	rev = READ4(esc, GSNPSID);
160269369Sbr	if ((rev & GSNPSID_MASK) != 0x55330000) {
161269369Sbr		printf("It is not DWC3 controller\n");
162269369Sbr		return (-1);
163269369Sbr	}
164269369Sbr
165269369Sbr	/* Reset controller */
166269369Sbr	WRITE4(esc, GCTL, GCTL_CORESOFTRESET);
167269369Sbr	WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST);
168269369Sbr	WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST);
169269369Sbr
170269369Sbr	DELAY(100000);
171269369Sbr
172269369Sbr	reg = READ4(esc, GUSB3PIPECTL(0));
173269369Sbr	reg &= ~(GUSB3PIPECTL_PHYSOFTRST);
174269369Sbr	WRITE4(esc, GUSB3PIPECTL(0), reg);
175269369Sbr
176269369Sbr	reg = READ4(esc, GUSB2PHYCFG(0));
177269369Sbr	reg &= ~(GUSB2PHYCFG_PHYSOFTRST);
178269369Sbr	WRITE4(esc, GUSB2PHYCFG(0), reg);
179269369Sbr
180269369Sbr	reg = READ4(esc, GCTL);
181269369Sbr	reg &= ~GCTL_CORESOFTRESET;
182269369Sbr	WRITE4(esc, GCTL, reg);
183269369Sbr
184269369Sbr	hwparams1 = READ4(esc, GHWPARAMS1);
185269369Sbr
186269369Sbr	reg = READ4(esc, GCTL);
187269369Sbr	reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT);
188269369Sbr	reg &= ~(GCTL_DISSCRAMBLE);
189269369Sbr
190269369Sbr	if (GHWPARAMS1_EN_PWROPT(hwparams1) == \
191269369Sbr	    GHWPARAMS1_EN_PWROPT_CLK)
192269369Sbr		reg &= ~(GCTL_DSBLCLKGTNG);
193269369Sbr
194269369Sbr	if ((rev & REVISION_MASK) < 0x190a)
195269369Sbr		reg |= (GCTL_U2RSTECN);
196269369Sbr	WRITE4(esc, GCTL, reg);
197269369Sbr
198269369Sbr	/* Set host mode */
199269369Sbr	reg = READ4(esc, GCTL);
200269369Sbr	reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG));
201269369Sbr	reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST);
202269369Sbr	WRITE4(esc, GCTL, reg);
203269369Sbr
204269369Sbr	return (0);
205269369Sbr}
206269369Sbr
207269369Sbrstatic int
208269369Sbrexynos_xhci_attach(device_t dev)
209269369Sbr{
210279542Shselasky	struct exynos_xhci_softc *esc = device_get_softc(dev);
211269369Sbr	bus_space_handle_t bsh;
212269369Sbr	int err;
213269369Sbr
214269369Sbr	esc->dev = dev;
215269369Sbr
216269369Sbr	if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) {
217269369Sbr		device_printf(dev, "could not allocate resources\n");
218269369Sbr		return (ENXIO);
219269369Sbr	}
220269369Sbr
221269369Sbr	/* XHCI registers */
222279542Shselasky	esc->base.sc_io_tag = rman_get_bustag(esc->res[0]);
223269369Sbr	bsh = rman_get_bushandle(esc->res[0]);
224279542Shselasky	esc->base.sc_io_size = rman_get_size(esc->res[0]);
225269369Sbr
226269369Sbr	/* DWC3 ctrl registers */
227269369Sbr	esc->bst = rman_get_bustag(esc->res[1]);
228269369Sbr	esc->bsh = rman_get_bushandle(esc->res[1]);
229269369Sbr
230269369Sbr	/*
231269369Sbr	 * Set handle to USB related registers subregion used by
232269369Sbr	 * generic XHCI driver.
233269369Sbr	 */
234279542Shselasky	err = bus_space_subregion(esc->base.sc_io_tag, bsh, 0x0,
235279542Shselasky	    esc->base.sc_io_size, &esc->base.sc_io_hdl);
236279542Shselasky	if (err != 0) {
237279542Shselasky		device_printf(dev, "Subregion failed\n");
238279542Shselasky		bus_release_resources(dev, exynos_xhci_spec, esc->res);
239269369Sbr		return (ENXIO);
240279542Shselasky	}
241269369Sbr
242279544Shselasky	if (xhci_init(&esc->base, dev, 0)) {
243279542Shselasky		device_printf(dev, "Could not initialize softc\n");
244279542Shselasky		bus_release_resources(dev, exynos_xhci_spec, esc->res);
245279542Shselasky		return (ENXIO);
246279542Shselasky	}
247279542Shselasky
248269369Sbr	/* Setup interrupt handler */
249269369Sbr	err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE,
250279542Shselasky	    NULL, (driver_intr_t *)xhci_interrupt, &esc->base,
251279542Shselasky	    &esc->base.sc_intr_hdl);
252269369Sbr	if (err) {
253269369Sbr		device_printf(dev, "Could not setup irq, %d\n", err);
254279542Shselasky		esc->base.sc_intr_hdl = NULL;
255279542Shselasky		goto error;
256269369Sbr	}
257269369Sbr
258269369Sbr	/* Add USB device */
259279542Shselasky	esc->base.sc_bus.bdev = device_add_child(dev, "usbus", -1);
260279542Shselasky	if (esc->base.sc_bus.bdev == NULL) {
261269369Sbr		device_printf(dev, "Could not add USB device\n");
262279542Shselasky		goto error;
263269369Sbr	}
264279542Shselasky	device_set_ivars(esc->base.sc_bus.bdev, &esc->base.sc_bus);
265279542Shselasky	strlcpy(esc->base.sc_vendor, "Samsung", sizeof(esc->base.sc_vendor));
266269369Sbr
267269369Sbr	dwc3_init(esc);
268269369Sbr
269279542Shselasky	err = xhci_halt_controller(&esc->base);
270269369Sbr	if (err == 0) {
271269369Sbr		device_printf(dev, "Starting controller\n");
272279542Shselasky		err = xhci_start_controller(&esc->base);
273269369Sbr	}
274269369Sbr	if (err == 0) {
275269369Sbr		device_printf(dev, "Controller started\n");
276279542Shselasky		err = device_probe_and_attach(esc->base.sc_bus.bdev);
277269369Sbr	}
278279542Shselasky	if (err != 0)
279279542Shselasky		goto error;
280279542Shselasky	return (0);
281269369Sbr
282279542Shselaskyerror:
283279542Shselasky	exynos_xhci_detach(dev);
284279542Shselasky	return (ENXIO);
285269369Sbr}
286269369Sbr
287269369Sbrstatic int
288269369Sbrexynos_xhci_detach(device_t dev)
289269369Sbr{
290279542Shselasky	struct exynos_xhci_softc *esc = device_get_softc(dev);
291269369Sbr	int err;
292269369Sbr
293279542Shselasky	/* During module unload there are lots of children leftover */
294279542Shselasky	device_delete_children(dev);
295269369Sbr
296279542Shselasky	xhci_halt_controller(&esc->base);
297279542Shselasky
298279542Shselasky	if (esc->res[2] && esc->base.sc_intr_hdl) {
299269369Sbr		err = bus_teardown_intr(dev, esc->res[2],
300279542Shselasky		    esc->base.sc_intr_hdl);
301269369Sbr		if (err) {
302279542Shselasky			device_printf(dev, "Could not tear down IRQ,"
303269369Sbr			    " %d\n", err);
304269369Sbr			return (err);
305269369Sbr		}
306269369Sbr	}
307269369Sbr
308269369Sbr	bus_release_resources(dev, exynos_xhci_spec, esc->res);
309269369Sbr
310279542Shselasky	xhci_uninit(&esc->base);
311279542Shselasky
312269369Sbr	return (0);
313269369Sbr}
314