xref: /openbsd-src/sys/arch/armv7/exynos/exehci.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
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 
80 int	exehci_match(struct device *, void *, void *);
81 void	exehci_attach(struct device *, struct device *, void *);
82 int	exehci_detach(struct device *, int);
83 
84 struct exehci_softc {
85 	struct ehci_softc	sc;
86 	void			*sc_ih;
87 	int			sc_phy;
88 	bus_space_handle_t	ph_ioh;
89 };
90 
91 const struct cfattach exehci_ca = {
92 	sizeof (struct exehci_softc), exehci_match, exehci_attach,
93 	exehci_detach
94 };
95 
96 struct cfdriver exehci_cd = {
97 	NULL, "exehci", DV_DULL
98 };
99 
100 void	exehci_setup(struct exehci_softc *);
101 
102 int
exehci_match(struct device * parent,void * match,void * aux)103 exehci_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 
110 void
exehci_attach(struct device * parent,struct device * self,void * aux)111 exehci_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 
181 intr:
182 	arm_intr_disestablish(sc->sc_ih);
183 	sc->sc_ih = NULL;
184 mem1:
185 	bus_space_unmap(sc->sc.iot, sc->ph_ioh, phy_reg[1]);
186 mem0:
187 	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
188 	sc->sc.sc_size = 0;
189 out:
190 	return;
191 }
192 
193 int
exehci_detach(struct device * self,int flags)194 exehci_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 
216 void
exehci_setup(struct exehci_softc * sc)217 exehci_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