1252391Sray/*-
2252391Sray * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
3252391Sray * All rights reserved.
4252391Sray *
5252391Sray * Redistribution and use in source and binary forms, with or without
6252391Sray * modification, are permitted provided that the following conditions
7252391Sray * are met:
8252391Sray * 1. Redistributions of source code must retain the above copyright
9252391Sray *    notice, this list of conditions and the following disclaimer.
10252391Sray * 2. Redistributions in binary form must reproduce the above copyright
11252391Sray *    notice, this list of conditions and the following disclaimer in the
12252391Sray *    documentation and/or other materials provided with the distribution.
13252391Sray *
14252391Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15252391Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16252391Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17252391Sray * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18252391Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19252391Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20252391Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21252391Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22252391Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23252391Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24252391Sray * SUCH DAMAGE.
25252391Sray */
26252391Sray
27252391Sray#include <sys/cdefs.h>
28252391Sray__FBSDID("$FreeBSD: releng/10.2/sys/arm/samsung/exynos/exynos5_ehci.c 278278 2015-02-05 20:03:02Z hselasky $");
29252391Sray
30252391Sray#include "opt_bus.h"
31252391Sray
32252391Sray#include <sys/param.h>
33252391Sray#include <sys/systm.h>
34252391Sray#include <sys/kernel.h>
35252391Sray#include <sys/module.h>
36252391Sray#include <sys/bus.h>
37252391Sray#include <sys/condvar.h>
38252391Sray#include <sys/rman.h>
39266341Sian#include <sys/gpio.h>
40252391Sray
41252391Sray#include <dev/ofw/ofw_bus.h>
42252391Sray#include <dev/ofw/ofw_bus_subr.h>
43252391Sray
44252391Sray#include <dev/usb/usb.h>
45252391Sray#include <dev/usb/usbdi.h>
46252391Sray#include <dev/usb/usb_busdma.h>
47252391Sray#include <dev/usb/usb_process.h>
48252391Sray#include <dev/usb/usb_controller.h>
49252391Sray#include <dev/usb/usb_bus.h>
50252391Sray#include <dev/usb/controller/ehci.h>
51252391Sray#include <dev/usb/controller/ehcireg.h>
52252391Sray
53252391Sray#include <dev/fdt/fdt_common.h>
54252391Sray
55252391Sray#include <machine/bus.h>
56252391Sray#include <machine/resource.h>
57252391Sray
58266341Sian#include "gpio_if.h"
59266341Sian
60252391Sray#include "opt_platform.h"
61252391Sray
62252391Sray/* GPIO control */
63252391Sray#define	GPIO_OUTPUT	1
64252391Sray#define	GPIO_INPUT	0
65266341Sian#define	PIN_USB		161
66252391Sray
67252391Sray/* PWR control */
68252391Sray#define	EXYNOS5_PWR_USBHOST_PHY		0x708
69252391Sray#define	PHY_POWER_ON			1
70252391Sray#define	PHY_POWER_OFF			0
71252391Sray
72252391Sray/* SYSREG */
73263425Sbr#define	EXYNOS5_SYSREG_USB2_PHY	0x0
74252391Sray#define	USB2_MODE_HOST		0x1
75252391Sray
76252391Sray/* USB HOST */
77252391Sray#define	HOST_CTRL_CLK_24MHZ	(5 << 16)
78252391Sray#define	HOST_CTRL_CLK_MASK	(7 << 16)
79252391Sray#define	HOST_CTRL_SIDDQ		(1 << 6)
80252391Sray#define	HOST_CTRL_SLEEP		(1 << 5)
81252391Sray#define	HOST_CTRL_SUSPEND	(1 << 4)
82252391Sray#define	HOST_CTRL_RESET_LINK	(1 << 1)
83252391Sray#define	HOST_CTRL_RESET_PHY	(1 << 0)
84258780Seadler#define	HOST_CTRL_RESET_PHY_ALL	(1U << 31)
85252391Sray
86252391Sray/* Forward declarations */
87252391Sraystatic int	exynos_ehci_attach(device_t dev);
88252391Sraystatic int	exynos_ehci_detach(device_t dev);
89252391Sraystatic int	exynos_ehci_probe(device_t dev);
90252391Sray
91252391Sraystruct exynos_ehci_softc {
92266341Sian	device_t		dev;
93252391Sray	ehci_softc_t		base;
94266341Sian	struct resource		*res[5];
95252391Sray	bus_space_tag_t		host_bst;
96252391Sray	bus_space_tag_t		pwr_bst;
97252391Sray	bus_space_tag_t		sysreg_bst;
98252391Sray	bus_space_handle_t	host_bsh;
99252391Sray	bus_space_handle_t	pwr_bsh;
100252391Sray	bus_space_handle_t	sysreg_bsh;
101252391Sray
102252391Sray};
103252391Sray
104252391Sraystatic struct resource_spec exynos_ehci_spec[] = {
105252391Sray	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
106252391Sray	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
107252391Sray	{ SYS_RES_MEMORY,	2,	RF_ACTIVE },
108252391Sray	{ SYS_RES_MEMORY,	3,	RF_ACTIVE },
109252391Sray	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
110252391Sray	{ -1, 0 }
111252391Sray};
112252391Sray
113252391Sraystatic device_method_t ehci_methods[] = {
114252391Sray	/* Device interface */
115252391Sray	DEVMETHOD(device_probe, exynos_ehci_probe),
116252391Sray	DEVMETHOD(device_attach, exynos_ehci_attach),
117252391Sray	DEVMETHOD(device_detach, exynos_ehci_detach),
118252391Sray	DEVMETHOD(device_suspend, bus_generic_suspend),
119252391Sray	DEVMETHOD(device_resume, bus_generic_resume),
120252391Sray	DEVMETHOD(device_shutdown, bus_generic_shutdown),
121252391Sray
122252391Sray	/* Bus interface */
123252391Sray	DEVMETHOD(bus_print_child, bus_generic_print_child),
124252391Sray
125252391Sray	{ 0, 0 }
126252391Sray};
127252391Sray
128252391Sray/* kobj_class definition */
129252391Sraystatic driver_t ehci_driver = {
130252391Sray	"ehci",
131252391Sray	ehci_methods,
132252391Sray	sizeof(ehci_softc_t)
133252391Sray};
134252391Sray
135252391Sraystatic devclass_t ehci_devclass;
136252391Sray
137252391SrayDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
138252391SrayMODULE_DEPEND(ehci, usb, 1, 1, 1);
139252391Sray
140252391Sray/*
141252391Sray * Public methods
142252391Sray */
143252391Sraystatic int
144252391Srayexynos_ehci_probe(device_t dev)
145252391Sray{
146252391Sray
147261410Sian	if (!ofw_bus_status_okay(dev))
148261410Sian		return (ENXIO);
149261410Sian
150252391Sray	if (ofw_bus_is_compatible(dev, "exynos,usb-ehci") == 0)
151252391Sray		return (ENXIO);
152252391Sray
153252391Sray	device_set_desc(dev, "Exynos integrated USB controller");
154252391Sray	return (BUS_PROBE_DEFAULT);
155252391Sray}
156252391Sray
157252391Sraystatic int
158252391Sraygpio_ctrl(struct exynos_ehci_softc *esc, int dir, int power)
159252391Sray{
160266341Sian	device_t gpio_dev;
161252391Sray
162266341Sian	/* Get the GPIO device, we need this to give power to USB */
163266341Sian	gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
164266341Sian	if (gpio_dev == NULL) {
165266341Sian		device_printf(esc->dev, "cant find gpio_dev\n");
166266341Sian		return (1);
167266341Sian	}
168252391Sray
169266341Sian	if (power)
170266341Sian		GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_HIGH);
171266341Sian	else
172266341Sian		GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_LOW);
173252391Sray
174266341Sian	if (dir)
175266341Sian		GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_OUTPUT);
176266341Sian	else
177266341Sian		GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_INPUT);
178266341Sian
179252391Sray	return (0);
180252391Sray}
181252391Sray
182252391Sraystatic int
183252391Srayphy_init(struct exynos_ehci_softc *esc)
184252391Sray{
185252391Sray	int reg;
186252391Sray
187252391Sray	gpio_ctrl(esc, GPIO_INPUT, 1);
188252391Sray
189252391Sray	/* set USB HOST mode */
190252391Sray	bus_space_write_4(esc->sysreg_bst, esc->sysreg_bsh,
191252391Sray	    EXYNOS5_SYSREG_USB2_PHY, USB2_MODE_HOST);
192252391Sray
193252391Sray	/* Power ON phy */
194252391Sray	bus_space_write_4(esc->pwr_bst, esc->pwr_bsh,
195252391Sray	    EXYNOS5_PWR_USBHOST_PHY, PHY_POWER_ON);
196252391Sray
197252391Sray	reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0);
198252391Sray	reg &= ~(HOST_CTRL_CLK_MASK |
199252391Sray	    HOST_CTRL_RESET_PHY |
200252391Sray	    HOST_CTRL_RESET_PHY_ALL |
201252391Sray	    HOST_CTRL_SIDDQ |
202252391Sray	    HOST_CTRL_SUSPEND |
203252391Sray	    HOST_CTRL_SLEEP);
204252391Sray
205252391Sray	reg |= (HOST_CTRL_CLK_24MHZ |
206252391Sray	    HOST_CTRL_RESET_LINK);
207252391Sray	bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg);
208252391Sray
209252391Sray	DELAY(10);
210252391Sray
211252391Sray	reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0);
212252391Sray	reg &= ~(HOST_CTRL_RESET_LINK);
213252391Sray	bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg);
214252391Sray
215252391Sray	gpio_ctrl(esc, GPIO_OUTPUT, 1);
216252391Sray
217252391Sray	return (0);
218252391Sray}
219252391Sray
220252391Sraystatic int
221252391Srayexynos_ehci_attach(device_t dev)
222252391Sray{
223252391Sray	struct exynos_ehci_softc *esc;
224252391Sray	ehci_softc_t *sc;
225252391Sray	bus_space_handle_t bsh;
226252391Sray	int err;
227252391Sray
228252391Sray	esc = device_get_softc(dev);
229266341Sian	esc->dev = dev;
230252391Sray	sc = &esc->base;
231252391Sray	sc->sc_bus.parent = dev;
232252391Sray	sc->sc_bus.devices = sc->sc_devices;
233252391Sray	sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
234278278Shselasky	sc->sc_bus.dma_bits = 32;
235252391Sray
236252391Sray	if (bus_alloc_resources(dev, exynos_ehci_spec, esc->res)) {
237252391Sray		device_printf(dev, "could not allocate resources\n");
238252391Sray		return (ENXIO);
239252391Sray	}
240252391Sray
241252391Sray	/* EHCI registers */
242252391Sray	sc->sc_io_tag = rman_get_bustag(esc->res[0]);
243252391Sray	bsh = rman_get_bushandle(esc->res[0]);
244252391Sray	sc->sc_io_size = rman_get_size(esc->res[0]);
245252391Sray
246252391Sray	/* EHCI HOST ctrl registers */
247252391Sray	esc->host_bst = rman_get_bustag(esc->res[1]);
248252391Sray	esc->host_bsh = rman_get_bushandle(esc->res[1]);
249252391Sray
250252391Sray	/* PWR registers */
251252391Sray	esc->pwr_bst = rman_get_bustag(esc->res[2]);
252252391Sray	esc->pwr_bsh = rman_get_bushandle(esc->res[2]);
253252391Sray
254252391Sray	/* SYSREG */
255252391Sray	esc->sysreg_bst = rman_get_bustag(esc->res[3]);
256252391Sray	esc->sysreg_bsh = rman_get_bushandle(esc->res[3]);
257252391Sray
258252391Sray	/* get all DMA memory */
259252391Sray	if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev),
260252391Sray		&ehci_iterate_hw_softc))
261252391Sray		return (ENXIO);
262252391Sray
263252391Sray	/*
264252391Sray	 * Set handle to USB related registers subregion used by
265252391Sray	 * generic EHCI driver.
266252391Sray	 */
267252391Sray	err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0,
268252391Sray	    sc->sc_io_size, &sc->sc_io_hdl);
269252391Sray	if (err != 0)
270252391Sray		return (ENXIO);
271252391Sray
272252391Sray	phy_init(esc);
273252391Sray
274252391Sray	/* Setup interrupt handler */
275266341Sian	err = bus_setup_intr(dev, esc->res[4], INTR_TYPE_BIO | INTR_MPSAFE,
276252391Sray	    NULL, (driver_intr_t *)ehci_interrupt, sc,
277252391Sray	    &sc->sc_intr_hdl);
278252391Sray	if (err) {
279252391Sray		device_printf(dev, "Could not setup irq, "
280252391Sray		    "%d\n", err);
281252391Sray		return (1);
282252391Sray	}
283252391Sray
284252391Sray	/* Add USB device */
285252391Sray	sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
286252391Sray	if (!sc->sc_bus.bdev) {
287252391Sray		device_printf(dev, "Could not add USB device\n");
288266341Sian		err = bus_teardown_intr(dev, esc->res[4],
289252391Sray		    sc->sc_intr_hdl);
290252391Sray		if (err)
291252391Sray			device_printf(dev, "Could not tear down irq,"
292252391Sray			    " %d\n", err);
293252391Sray		return (1);
294252391Sray	}
295252391Sray	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
296252391Sray
297252391Sray	strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor));
298252391Sray
299252391Sray	err = ehci_init(sc);
300252391Sray	if (!err) {
301252391Sray		sc->sc_flags |= EHCI_SCFLG_DONEINIT;
302252391Sray		err = device_probe_and_attach(sc->sc_bus.bdev);
303252391Sray	} else {
304252391Sray		device_printf(dev, "USB init failed err=%d\n", err);
305252391Sray
306252391Sray		device_delete_child(dev, sc->sc_bus.bdev);
307252391Sray		sc->sc_bus.bdev = NULL;
308252391Sray
309266341Sian		err = bus_teardown_intr(dev, esc->res[4],
310252391Sray		    sc->sc_intr_hdl);
311252391Sray		if (err)
312252391Sray			device_printf(dev, "Could not tear down irq,"
313252391Sray			    " %d\n", err);
314252391Sray		return (1);
315252391Sray	}
316252391Sray	return (0);
317252391Sray}
318252391Sray
319252391Sraystatic int
320252391Srayexynos_ehci_detach(device_t dev)
321252391Sray{
322252391Sray	struct exynos_ehci_softc *esc;
323252391Sray	ehci_softc_t *sc;
324252391Sray	int err;
325252391Sray
326252391Sray	esc = device_get_softc(dev);
327252391Sray	sc = &esc->base;
328252391Sray
329252391Sray	if (sc->sc_flags & EHCI_SCFLG_DONEINIT)
330252391Sray		return (0);
331252391Sray
332252391Sray	/*
333252391Sray	 * only call ehci_detach() after ehci_init()
334252391Sray	 */
335252391Sray	if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
336252391Sray		ehci_detach(sc);
337252391Sray		sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
338252391Sray	}
339252391Sray
340252391Sray	/*
341252391Sray	 * Disable interrupts that might have been switched on in
342252391Sray	 * ehci_init.
343252391Sray	 */
344252391Sray	if (sc->sc_io_tag && sc->sc_io_hdl)
345252391Sray		bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl,
346252391Sray		    EHCI_USBINTR, 0);
347252391Sray
348266341Sian	if (esc->res[4] && sc->sc_intr_hdl) {
349266341Sian		err = bus_teardown_intr(dev, esc->res[4],
350252391Sray		    sc->sc_intr_hdl);
351252391Sray		if (err) {
352252391Sray			device_printf(dev, "Could not tear down irq,"
353252391Sray			    " %d\n", err);
354252391Sray			return (err);
355252391Sray		}
356252391Sray		sc->sc_intr_hdl = NULL;
357252391Sray	}
358252391Sray
359252391Sray	if (sc->sc_bus.bdev) {
360252391Sray		device_delete_child(dev, sc->sc_bus.bdev);
361252391Sray		sc->sc_bus.bdev = NULL;
362252391Sray	}
363252391Sray
364252391Sray	/* During module unload there are lots of children leftover */
365252391Sray	device_delete_children(dev);
366252391Sray
367252391Sray	bus_release_resources(dev, exynos_ehci_spec, esc->res);
368252391Sray
369252391Sray	return (0);
370252391Sray}
371