xref: /openbsd-src/sys/arch/armv7/exynos/exehci.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: exehci.c,v 1.4 2016/07/26 22:10:10 patrick 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 #include <sys/kernel.h>
22 #include <sys/rwlock.h>
23 #include <sys/timeout.h>
24 
25 #include <machine/intr.h>
26 #include <machine/bus.h>
27 #if NFDT > 0
28 #include <machine/fdt.h>
29 #endif
30 
31 #include <dev/usb/usb.h>
32 #include <dev/usb/usbdi.h>
33 #include <dev/usb/usbdivar.h>
34 #include <dev/usb/usb_mem.h>
35 
36 #include <armv7/armv7/armv7var.h>
37 #include <armv7/exynos/exsysregvar.h>
38 #include <armv7/exynos/expowervar.h>
39 #include <armv7/exynos/exgpiovar.h>
40 
41 #include <dev/usb/ehcireg.h>
42 #include <dev/usb/ehcivar.h>
43 
44 /* registers */
45 #define USBPHY_CTRL0			0x00
46 #define USBPHY_TUNE0			0x04
47 #define HSICPHY_CTRL1			0x10
48 #define HSICPHY_TUNE1			0x14
49 #define HSICPHY_CTRL2			0x20
50 #define HSICPHY_TUNE2			0x24
51 #define EHCI_CTRL			0x30
52 #define OHCI_CTRL			0x34
53 #define USBOTG_SYS			0x38
54 #define USBOTG_TUNE			0x40
55 
56 /* bits and bytes */
57 #define CLK_24MHZ			5
58 
59 #define HOST_CTRL0_PHYSWRSTALL		(1U << 31)
60 #define HOST_CTRL0_COMMONON_N		(1 << 9)
61 #define HOST_CTRL0_SIDDQ		(1 << 6)
62 #define HOST_CTRL0_FORCESLEEP		(1 << 5)
63 #define HOST_CTRL0_FORCESUSPEND		(1 << 4)
64 #define HOST_CTRL0_WORDINTERFACE	(1 << 3)
65 #define HOST_CTRL0_UTMISWRST		(1 << 2)
66 #define HOST_CTRL0_LINKSWRST		(1 << 1)
67 #define HOST_CTRL0_PHYSWRST		(1 << 0)
68 
69 #define HOST_CTRL0_FSEL_MASK		(7 << 16)
70 
71 #define EHCI_CTRL_ENAINCRXALIGN		(1 << 29)
72 #define EHCI_CTRL_ENAINCR4		(1 << 28)
73 #define EHCI_CTRL_ENAINCR8		(1 << 27)
74 #define EHCI_CTRL_ENAINCR16		(1 << 26)
75 
76 int	exehci_match(struct device *, void *, void *);
77 void	exehci_attach(struct device *, struct device *, void *);
78 int	exehci_detach(struct device *, int);
79 
80 struct exehci_softc {
81 	struct device		sc_dev;
82 	struct ehci_softc	*sc_ehci;
83 	void			*sc_ih;
84 	bus_dma_tag_t		sc_dmat;
85 	bus_space_tag_t		sc_iot;
86 	bus_space_handle_t	sc_ioh;
87 	bus_size_t		sc_size;
88 	bus_space_handle_t	ph_ioh;
89 };
90 
91 struct cfdriver exehci_cd = {
92 	NULL, "exehci", DV_DULL
93 };
94 
95 struct cfattach exehci_ca = {
96 	sizeof (struct exehci_softc), NULL, exehci_attach,
97 	exehci_detach, NULL
98 };
99 struct cfattach exehci_fdt_ca = {
100 	sizeof (struct exehci_softc), exehci_match, exehci_attach,
101 	exehci_detach, NULL
102 };
103 
104 void	exehci_setup(struct exehci_softc *);
105 
106 int
107 exehci_match(struct device *parent, void *v, void *aux)
108 {
109 #if NFDT > 0
110 	struct armv7_attach_args *aa = aux;
111 
112 	if (fdt_node_compatible("samsung,exynos4210-ehci", aa->aa_node))
113 		return 1;
114 #endif
115 
116 	return 0;
117 }
118 
119 void
120 exehci_attach(struct device *parent, struct device *self, void *aux)
121 {
122 	struct exehci_softc		*sc = (struct exehci_softc *)self;
123 	struct ehci_softc		*esc;
124 	struct armv7_attach_args	*aa = aux;
125 	struct armv7mem			 hmem, pmem;
126 	int				 irq;
127 	usbd_status			 r;
128 
129 	sc->sc_iot = aa->aa_iot;
130 	sc->sc_dmat = aa->aa_dmat;
131 
132 #if NFDT > 0
133 	if (aa->aa_node) {
134 		struct fdt_reg hreg, preg;
135 		uint32_t ints[3];
136 
137 		if (fdt_get_reg(aa->aa_node, 0, &hreg))
138 			panic("%s: could not extract memory data from FDT",
139 			    __func__);
140 
141 		/* XXX: In a different way, please. */
142 		void *node = fdt_find_compatible("samsung,exynos5250-usb2-phy");
143 		if (node == NULL || fdt_get_reg(node, 0, &preg))
144 			panic("%s: could not extract phy data from FDT",
145 			    __func__);
146 
147 		/* TODO: Add interrupt FDT API. */
148 		if (fdt_node_property_ints(aa->aa_node, "interrupts",
149 		    ints, 3) != 3)
150 			panic("%s: could not extract interrupt data from FDT",
151 			    __func__);
152 
153 		hmem.addr = hreg.addr;
154 		hmem.size = hreg.size;
155 		pmem.addr = preg.addr;
156 		pmem.size = preg.size;
157 
158 		irq = ints[1];
159 	} else
160 #endif
161 	{
162 		hmem.addr = aa->aa_dev->mem[0].addr;
163 		hmem.size = aa->aa_dev->mem[0].size;
164 		pmem.addr = aa->aa_dev->mem[1].addr;
165 		pmem.size = aa->aa_dev->mem[1].size;
166 		irq = aa->aa_dev->irq[0];
167 	}
168 
169 	/* Map I/O space */
170 	sc->sc_size = hmem.size;
171 	if (bus_space_map(sc->sc_iot, hmem.addr, hmem.size, 0, &sc->sc_ioh)) {
172 		printf(": cannot map mem space\n");
173 		goto out;
174 	}
175 
176 	if (bus_space_map(sc->sc_iot, pmem.addr, pmem.size, 0, &sc->ph_ioh)) {
177 		printf(": cannot map mem space\n");
178 		goto pmem;
179 	}
180 
181 	printf("\n");
182 
183 	exehci_setup(sc);
184 
185 	if ((esc = (struct ehci_softc *)config_found(self, NULL, NULL)) == NULL)
186 		goto hmem;
187 
188 	sc->sc_ehci = esc;
189 	esc->iot = sc->sc_iot;
190 	esc->ioh = sc->sc_ioh;
191 	esc->sc_bus.dmatag = aa->aa_dmat;
192 
193 	sc->sc_ih = arm_intr_establish(irq, IPL_USB,
194 	    ehci_intr, esc, esc->sc_bus.bdev.dv_xname);
195 	if (sc->sc_ih == NULL) {
196 		printf(": unable to establish interrupt\n");
197 		goto hmem;
198 	}
199 
200 	strlcpy(esc->sc_vendor, "Exynos 5", sizeof(esc->sc_vendor));
201 	r = ehci_init(esc);
202 	if (r != USBD_NORMAL_COMPLETION) {
203 		printf("%s: init failed, error=%d\n",
204 		    esc->sc_bus.bdev.dv_xname, r);
205 		goto intr;
206 	}
207 
208 	printf("\n");
209 
210 	config_found((struct device *)esc, &esc->sc_bus, usbctlprint);
211 
212 	goto out;
213 
214 intr:
215 	arm_intr_disestablish(sc->sc_ih);
216 	sc->sc_ih = NULL;
217 hmem:
218 	bus_space_unmap(sc->sc_iot, sc->ph_ioh, hmem.size);
219 pmem:
220 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
221 	sc->sc_size = 0;
222 out:
223 	return;
224 }
225 
226 int
227 exehci_detach(struct device *self, int flags)
228 {
229 	struct exehci_softc		*sc = (struct exehci_softc *)self;
230 	int				 rv = 0;
231 
232 	rv = ehci_detach(self, flags);
233 	if (rv)
234 		return (rv);
235 
236 	if (sc->sc_ih != NULL) {
237 		arm_intr_disestablish(sc->sc_ih);
238 		sc->sc_ih = NULL;
239 	}
240 
241 	if (sc->sc_size) {
242 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
243 		sc->sc_size = 0;
244 	}
245 
246 	return 0;
247 }
248 
249 void
250 exehci_setup(struct exehci_softc *sc)
251 {
252 	uint32_t val;
253 
254 	/* VBUS, GPIO_X11, only on SMDK5250 and Chromebooks */
255 	exgpio_set_dir(0xa9, EXGPIO_DIR_OUT);
256 	exgpio_set_bit(0xa9);
257 	delay(3000);
258 
259 	exsysreg_usbhost_mode(1);
260 	expower_usbhost_phy_ctrl(1);
261 
262 	delay(10000);
263 
264 	/* Setting up host and device simultaneously */
265 	val = bus_space_read_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0);
266 	val &= ~(HOST_CTRL0_FSEL_MASK |
267 		 HOST_CTRL0_COMMONON_N |
268 		 /* HOST Phy setting */
269 		 HOST_CTRL0_PHYSWRST |
270 		 HOST_CTRL0_PHYSWRSTALL |
271 		 HOST_CTRL0_SIDDQ |
272 		 HOST_CTRL0_FORCESUSPEND |
273 		 HOST_CTRL0_FORCESLEEP);
274 	val |= (/* Setting up the ref freq */
275 		 CLK_24MHZ << 16 |
276 		 /* HOST Phy setting */
277 		 HOST_CTRL0_LINKSWRST |
278 		 HOST_CTRL0_UTMISWRST);
279 	bus_space_write_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0, val);
280 	delay(10000);
281 	bus_space_write_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0,
282 	    bus_space_read_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0) &
283 		~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST));
284 	delay(20000);
285 
286 	/* EHCI Ctrl setting */
287 	bus_space_write_4(sc->sc_iot, sc->ph_ioh, EHCI_CTRL,
288 	    bus_space_read_4(sc->sc_iot, sc->ph_ioh, EHCI_CTRL) |
289 		EHCI_CTRL_ENAINCRXALIGN |
290 		EHCI_CTRL_ENAINCR4 |
291 		EHCI_CTRL_ENAINCR8 |
292 		EHCI_CTRL_ENAINCR16);
293 
294 	/* HSIC USB Hub initialization. */
295 	if (1) {
296 		exgpio_set_dir(0xc8, EXGPIO_DIR_OUT);
297 		exgpio_clear_bit(0xc8);
298 		delay(1000);
299 		exgpio_set_bit(0xc8);
300 		delay(5000);
301 
302 		val = bus_space_read_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1);
303 		val &= ~(HOST_CTRL0_SIDDQ |
304 			 HOST_CTRL0_FORCESLEEP |
305 			 HOST_CTRL0_FORCESUSPEND);
306 		bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val);
307 		val |= HOST_CTRL0_PHYSWRST;
308 		bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val);
309 		delay(1000);
310 		val &= ~HOST_CTRL0_PHYSWRST;
311 		bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val);
312 	}
313 
314 	/* PHY clock and power setup time */
315 	delay(50000);
316 }
317 
318 int	ehci_ex_match(struct device *, void *, void *);
319 void	ehci_ex_attach(struct device *, struct device *, void *);
320 
321 struct cfattach ehci_ex_ca = {
322 	sizeof (struct ehci_softc), ehci_ex_match, ehci_ex_attach
323 };
324 
325 int
326 ehci_ex_match(struct device *parent, void *v, void *aux)
327 {
328 	return 1;
329 }
330 
331 void
332 ehci_ex_attach(struct device *parent, struct device *self, void *aux)
333 {
334 }
335