1/*	$OpenBSD: exehci.c,v 1.11 2021/10/24 17:52:27 mpi Exp $ */
2/*
3 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21
22#include <machine/intr.h>
23#include <machine/bus.h>
24#include <machine/fdt.h>
25
26#include <dev/usb/usb.h>
27#include <dev/usb/usbdi.h>
28#include <dev/usb/usbdivar.h>
29
30#include <dev/ofw/openfirm.h>
31#include <dev/ofw/ofw_clock.h>
32#include <dev/ofw/ofw_gpio.h>
33#include <dev/ofw/ofw_misc.h>
34#include <dev/ofw/fdt.h>
35
36#include <dev/usb/ehcireg.h>
37#include <dev/usb/ehcivar.h>
38
39/* registers */
40#define USBPHY_CTRL0			0x00
41#define USBPHY_TUNE0			0x04
42#define HSICPHY_CTRL1			0x10
43#define HSICPHY_TUNE1			0x14
44#define HSICPHY_CTRL2			0x20
45#define HSICPHY_TUNE2			0x24
46#define EHCI_CTRL			0x30
47#define OHCI_CTRL			0x34
48#define USBOTG_SYS			0x38
49#define USBOTG_TUNE			0x40
50
51/* bits and bytes */
52#define CLK_24MHZ			5
53
54#define HOST_CTRL0_PHYSWRSTALL		(1U << 31)
55#define HOST_CTRL0_COMMONON_N		(1 << 9)
56#define HOST_CTRL0_SIDDQ		(1 << 6)
57#define HOST_CTRL0_FORCESLEEP		(1 << 5)
58#define HOST_CTRL0_FORCESUSPEND		(1 << 4)
59#define HOST_CTRL0_WORDINTERFACE	(1 << 3)
60#define HOST_CTRL0_UTMISWRST		(1 << 2)
61#define HOST_CTRL0_LINKSWRST		(1 << 1)
62#define HOST_CTRL0_PHYSWRST		(1 << 0)
63
64#define HOST_CTRL0_FSEL_MASK		(7 << 16)
65
66#define EHCI_CTRL_ENAINCRXALIGN		(1 << 29)
67#define EHCI_CTRL_ENAINCR4		(1 << 28)
68#define EHCI_CTRL_ENAINCR8		(1 << 27)
69#define EHCI_CTRL_ENAINCR16		(1 << 26)
70
71/* SYSREG registers */
72#define USB20PHY_CFG			0x230
73#define  USB20PHY_CFG_HOST_LINK_EN	(1 << 0)
74
75/* PMU registers */
76#define USB_HOST_POWER_5250	0x708
77#define USB_HOST_POWER_54XX	0x70c
78#define USB_HOST_POWER_EN	(1 << 0)
79
80int	exehci_match(struct device *, void *, void *);
81void	exehci_attach(struct device *, struct device *, void *);
82int	exehci_detach(struct device *, int);
83
84struct exehci_softc {
85	struct ehci_softc	sc;
86	void			*sc_ih;
87	int			sc_phy;
88	bus_space_handle_t	ph_ioh;
89};
90
91const struct cfattach exehci_ca = {
92	sizeof (struct exehci_softc), exehci_match, exehci_attach,
93	exehci_detach
94};
95
96struct cfdriver exehci_cd = {
97	NULL, "exehci", DV_DULL
98};
99
100void	exehci_setup(struct exehci_softc *);
101
102int
103exehci_match(struct device *parent, void *match, void *aux)
104{
105	struct fdt_attach_args *faa = aux;
106
107	return OF_is_compatible(faa->fa_node, "samsung,exynos4210-ehci");
108}
109
110void
111exehci_attach(struct device *parent, struct device *self, void *aux)
112{
113	struct exehci_softc *sc = (struct exehci_softc *)self;
114	struct fdt_attach_args *faa = aux;
115	usbd_status r;
116	char *devname = sc->sc.sc_bus.bdev.dv_xname;
117	uint32_t phys[2];
118	uint32_t phy_reg[2];
119	int node;
120
121	if (faa->fa_nreg < 1)
122		return;
123
124	node = OF_child(faa->fa_node);
125	if (node == 0)
126		node = faa->fa_node;
127
128	if (OF_getpropintarray(node, "phys", phys,
129	    sizeof(phys)) != sizeof(phys))
130		return;
131
132	sc->sc_phy = OF_getnodebyphandle(phys[0]);
133	if (sc->sc_phy == 0)
134		return;
135
136	if (OF_getpropintarray(sc->sc_phy, "reg", phy_reg,
137	    sizeof(phy_reg)) != sizeof(phy_reg))
138		return;
139
140	sc->sc.iot = faa->fa_iot;
141	sc->sc.sc_bus.dmatag = faa->fa_dmat;
142	sc->sc.sc_size = faa->fa_reg[0].size;
143
144	/* Map I/O space */
145	if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
146	    faa->fa_reg[0].size, 0, &sc->sc.ioh)) {
147		printf(": cannot map mem space\n");
148		goto out;
149	}
150
151	if (bus_space_map(sc->sc.iot, phy_reg[0],
152	    phy_reg[1], 0, &sc->ph_ioh)) {
153		printf(": cannot map mem space\n");
154		goto mem0;
155	}
156
157	printf("\n");
158
159	clock_enable_all(faa->fa_node);
160
161	exehci_setup(sc);
162
163	sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_USB,
164	    ehci_intr, &sc->sc, devname);
165	if (sc->sc_ih == NULL) {
166		printf(": unable to establish interrupt\n");
167		goto mem1;
168	}
169
170	strlcpy(sc->sc.sc_vendor, "Exynos 5", sizeof(sc->sc.sc_vendor));
171	r = ehci_init(&sc->sc);
172	if (r != USBD_NORMAL_COMPLETION) {
173		printf("%s: init failed, error=%d\n", devname, r);
174		goto intr;
175	}
176
177	config_found(self, &sc->sc.sc_bus, usbctlprint);
178
179	goto out;
180
181intr:
182	arm_intr_disestablish(sc->sc_ih);
183	sc->sc_ih = NULL;
184mem1:
185	bus_space_unmap(sc->sc.iot, sc->ph_ioh, phy_reg[1]);
186mem0:
187	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
188	sc->sc.sc_size = 0;
189out:
190	return;
191}
192
193int
194exehci_detach(struct device *self, int flags)
195{
196	struct exehci_softc *sc = (struct exehci_softc *)self;
197	int rv;
198
199	rv = ehci_detach(self, flags);
200	if (rv)
201		return rv;
202
203	if (sc->sc_ih != NULL) {
204		arm_intr_disestablish(sc->sc_ih);
205		sc->sc_ih = NULL;
206	}
207
208	if (sc->sc.sc_size) {
209		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
210		sc->sc.sc_size = 0;
211	}
212
213	return 0;
214}
215
216void
217exehci_setup(struct exehci_softc *sc)
218{
219	struct regmap *pmurm, *sysrm;
220	uint32_t pmureg, sysreg;
221	bus_size_t offset;
222	uint32_t val;
223	int node;
224
225#if 0
226	/* VBUS, GPIO_X11, only on SMDK5250 and Chromebooks */
227	exgpio_set_dir(0xa9, EXGPIO_DIR_OUT);
228	exgpio_set_bit(0xa9);
229	delay(3000);
230#endif
231
232	/* Enable host mode. */
233	sysreg = OF_getpropint(sc->sc_phy, "samsung,sysreg-phandle", 0);
234	sysrm = regmap_byphandle(sysreg);
235	if (sysrm) {
236		val = regmap_read_4(sysrm, USB20PHY_CFG);
237		val |= USB20PHY_CFG_HOST_LINK_EN;
238		regmap_write_4(sysrm, USB20PHY_CFG, val);
239	}
240
241	/* Power up the PHY block. */
242	pmureg = OF_getpropint(sc->sc_phy, "samsung,pmureg-phandle", 0);
243	pmurm = regmap_byphandle(pmureg);
244	if (pmurm) {
245		node = OF_getnodebyphandle(pmureg);
246		if (OF_is_compatible(node, "samsung,exynos5250-pmu"))
247			offset = USB_HOST_POWER_5250;
248		else
249			offset = USB_HOST_POWER_54XX;
250
251		val = regmap_read_4(pmurm, offset);
252		val |= USB_HOST_POWER_EN;
253		regmap_write_4(pmurm, offset, val);
254	}
255
256	delay(10000);
257
258	/* Setting up host and device simultaneously */
259	val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0);
260	val &= ~(HOST_CTRL0_FSEL_MASK |
261		 /* HOST Phy setting */
262		 HOST_CTRL0_PHYSWRST |
263		 HOST_CTRL0_PHYSWRSTALL |
264		 HOST_CTRL0_SIDDQ |
265		 HOST_CTRL0_FORCESUSPEND |
266		 HOST_CTRL0_FORCESLEEP);
267	val |= (/* Setting up the ref freq */
268		 CLK_24MHZ << 16 |
269		 HOST_CTRL0_COMMONON_N |
270		 /* HOST Phy setting */
271		 HOST_CTRL0_LINKSWRST |
272		 HOST_CTRL0_UTMISWRST);
273	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0, val);
274	delay(10000);
275	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0,
276	    bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0) &
277		~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST));
278	delay(20000);
279
280	/* EHCI Ctrl setting */
281	bus_space_write_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL,
282	    bus_space_read_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL) |
283		EHCI_CTRL_ENAINCRXALIGN |
284		EHCI_CTRL_ENAINCR4 |
285		EHCI_CTRL_ENAINCR8 |
286		EHCI_CTRL_ENAINCR16);
287
288#if 0
289	/* HSIC USB Hub initialization. */
290	if (1) {
291		exgpio_set_dir(0xc8, EXGPIO_DIR_OUT);
292		exgpio_clear_bit(0xc8);
293		delay(1000);
294		exgpio_set_bit(0xc8);
295		delay(5000);
296
297		val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1);
298		val &= ~(HOST_CTRL0_SIDDQ |
299			 HOST_CTRL0_FORCESLEEP |
300			 HOST_CTRL0_FORCESUSPEND);
301		bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
302		val |= HOST_CTRL0_PHYSWRST;
303		bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
304		delay(1000);
305		val &= ~HOST_CTRL0_PHYSWRST;
306		bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
307	}
308#endif
309
310	/* PHY clock and power setup time */
311	delay(50000);
312}
313