initial commit, pull in sys/arch/armv7/omap
[bbb-pru.git] / omehci.c
1 /* $OpenBSD: omehci.c,v 1.3 2014/05/19 13:11:31 mpi Exp $ */
2
3 /*
4 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*-
20 * Copyright (c) 2011
21 * Ben Gray <ben.r.gray@gmail.com>.
22 * All rights reserved.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
26 * are met:
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 *
33 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 */
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/device.h>
49 #include <sys/kernel.h>
50 #include <sys/rwlock.h>
51 #include <sys/timeout.h>
52
53 #include <machine/intr.h>
54 #include <machine/bus.h>
55
56 #include <dev/usb/usb.h>
57 #include <dev/usb/usbdi.h>
58 #include <dev/usb/usbdivar.h>
59 #include <dev/usb/usb_mem.h>
60
61 #include <armv7/armv7/armv7var.h>
62 #include <armv7/omap/prcmvar.h>
63 #include <armv7/omap/omgpiovar.h>
64 #include <armv7/omap/omehcivar.h>
65
66 #include <dev/usb/ehcireg.h>
67 #include <dev/usb/ehcivar.h>
68
69 void omehci_attach(struct device *, struct device *, void *);
70 int omehci_detach(struct device *, int);
71 int omehci_activate(struct device *, int);
72
73 struct omehci_softc {
74 struct ehci_softc sc;
75 void *sc_ih;
76 bus_space_handle_t uhh_ioh;
77 bus_space_handle_t tll_ioh;
78
79 uint32_t ehci_rev;
80 uint32_t tll_avail;
81
82 uint32_t port_mode[OMAP_HS_USB_PORTS];
83 uint32_t phy_reset[OMAP_HS_USB_PORTS];
84 uint32_t reset_gpio_pin[OMAP_HS_USB_PORTS];
85
86 void (*early_init)(void);
87 };
88
89 int omehci_init(struct omehci_softc *);
90 void omehci_soft_phy_reset(struct omehci_softc *sc, unsigned int port);
91 void omehci_enable(struct omehci_softc *);
92 void omehci_disable(struct omehci_softc *);
93 void omehci_utmi_init(struct omehci_softc *sc, unsigned int en_mask);
94 void misc_setup(struct omehci_softc *sc);
95 void omehci_phy_reset(uint32_t on, uint32_t _delay);
96 void omehci_uhh_init(struct omehci_softc *sc);
97 void omehci_v4_early_init(void);
98
99 struct cfattach omehci_ca = {
100 sizeof (struct omehci_softc), NULL, omehci_attach,
101 omehci_detach, omehci_activate
102 };
103
104 void
105 omehci_attach(struct device *parent, struct device *self, void *aux)
106 {
107 struct omehci_softc *sc = (struct omehci_softc *)self;
108 struct armv7_attach_args *aa = aux;
109 usbd_status r;
110 char *devname = sc->sc.sc_bus.bdev.dv_xname;
111 uint32_t i;
112
113 sc->sc.iot = aa->aa_iot;
114 sc->sc.sc_bus.dmatag = aa->aa_dmat;
115 sc->sc.sc_size = aa->aa_dev->mem[0].size;
116
117 /* set defaults */
118 for (i = 0; i < 3; i++) {
119 sc->phy_reset[i] = 0;
120 sc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN;
121 sc->reset_gpio_pin[i] = -1;
122 }
123
124 switch (board_id)
125 {
126 case BOARD_ID_OMAP4_PANDA:
127 sc->tll_avail = 0;
128 sc->port_mode[0] = EHCI_HCD_OMAP_MODE_PHY;
129 sc->early_init = omehci_v4_early_init;
130 break;
131 default:
132 break;
133 }
134
135 /* Map I/O space */
136 if (bus_space_map(sc->sc.iot, aa->aa_dev->mem[0].addr,
137 aa->aa_dev->mem[0].size, 0, &sc->sc.ioh)) {
138 printf(": cannot map mem space\n");
139 goto out;
140 }
141
142 if (bus_space_map(sc->sc.iot, aa->aa_dev->mem[1].addr,
143 aa->aa_dev->mem[1].size, 0, &sc->uhh_ioh)) {
144 printf(": cannot map mem space\n");
145 goto mem0;
146 }
147
148 if (sc->tll_avail &&
149 bus_space_map(sc->sc.iot, aa->aa_dev->mem[2].addr,
150 aa->aa_dev->mem[2].size, 0, &sc->tll_ioh)) {
151 printf(": cannot map mem space\n");
152 goto mem1;
153 }
154
155 printf("\n");
156
157 if (sc->early_init)
158 sc->early_init();
159
160 if (omehci_init(sc))
161 return;
162
163 /* Disable interrupts, so we don't get any spurious ones. */
164 sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH);
165 EOWRITE2(&sc->sc, EHCI_USBINTR, 0);
166
167 sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_USB,
168 ehci_intr, &sc->sc, devname);
169 if (sc->sc_ih == NULL) {
170 printf(": unable to establish interrupt\n");
171 printf("XXX - disable ehci and prcm");
172 goto mem2;
173 }
174
175 strlcpy(sc->sc.sc_vendor, "TI OMAP", sizeof(sc->sc.sc_vendor));
176 r = ehci_init(&sc->sc);
177 if (r != USBD_NORMAL_COMPLETION) {
178 printf("%s: init failed, error=%d\n", devname, r);
179 printf("XXX - disable ehci and prcm");
180 goto intr;
181 }
182
183 config_found(self, &sc->sc.sc_bus, usbctlprint);
184
185 goto out;
186
187 intr:
188 arm_intr_disestablish(sc->sc_ih);
189 sc->sc_ih = NULL;
190 mem2:
191 bus_space_unmap(sc->sc.iot, sc->tll_ioh, aa->aa_dev->mem[2].size);
192 mem1:
193 bus_space_unmap(sc->sc.iot, sc->uhh_ioh, aa->aa_dev->mem[1].size);
194 mem0:
195 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
196 sc->sc.sc_size = 0;
197 out:
198 return;
199 }
200
201 int
202 omehci_init(struct omehci_softc *sc)
203 {
204 uint32_t i = 0, reg;
205 uint32_t reset_performed = 0;
206 uint32_t timeout = 0;
207 uint32_t tll_ch_mask = 0;
208
209 /* enable high speed usb host clock */
210 prcm_enablemodule(PRCM_USB);
211
212 /* Hold the PHY in reset while configuring */
213 for (i = 0; i < OMAP_HS_USB_PORTS; i++) {
214 if (sc->phy_reset[i]) {
215 /* Configure the GPIO to drive low (hold in reset) */
216 if (sc->reset_gpio_pin[i] != -1) {
217 omgpio_set_dir(sc->reset_gpio_pin[i],
218 OMGPIO_DIR_OUT);
219 omgpio_clear_bit(sc->reset_gpio_pin[i]);
220 reset_performed = 1;
221 }
222 }
223 }
224
225 /* Hold the PHY in RESET for enough time till DIR is high */
226 if (reset_performed)
227 delay(10);
228
229 /* Read the UHH revision */
230 sc->ehci_rev = bus_space_read_4(sc->sc.iot, sc->uhh_ioh,
231 OMAP_USBHOST_UHH_REVISION);
232
233 /* Initilise the low level interface module(s) */
234 if (sc->ehci_rev == OMAP_EHCI_REV1) {
235 /* Enable the USB TLL */
236 prcm_enablemodule(PRCM_USBTLL);
237
238 /* Perform TLL soft reset, and wait until reset is complete */
239 bus_space_write_4(sc->sc.iot, sc->tll_ioh,
240 OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET);
241
242 /* Set the timeout to 100ms*/
243 timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
244
245 /* Wait for TLL reset to complete */
246 while ((bus_space_read_4(sc->sc.iot, sc->tll_ioh,
247 OMAP_USBTLL_SYSSTATUS) & TLL_SYSSTATUS_RESETDONE)
248 == 0x00) {
249
250 /* Sleep for a tick */
251 delay(10);
252
253 if (timeout-- == 0) {
254 return 1;
255 }
256 }
257
258 bus_space_write_4(sc->sc.iot, sc->tll_ioh,
259 OMAP_USBTLL_SYSCONFIG,
260 TLL_SYSCONFIG_ENAWAKEUP | TLL_SYSCONFIG_AUTOIDLE |
261 TLL_SYSCONFIG_SIDLE_SMART_IDLE | TLL_SYSCONFIG_CACTIVITY);
262 } else if (sc->ehci_rev == OMAP_EHCI_REV2) {
263 /* For OMAP44xx devices you have to enable the per-port clocks:
264 * PHY_MODE - External ULPI clock
265 * TTL_MODE - Internal UTMI clock
266 * HSIC_MODE - Internal 480Mhz and 60Mhz clocks
267 */
268 if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) {
269 //ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK);
270 prcm_enablemodule(PRCM_USBP1_PHY);
271 } else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
272 prcm_enablemodule(PRCM_USBP1_UTMI);
273 else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC)
274 prcm_enablemodule(PRCM_USBP1_HSIC);
275
276 if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) {
277 //ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK);
278 prcm_enablemodule(PRCM_USBP2_PHY);
279 } else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
280 prcm_enablemodule(PRCM_USBP2_UTMI);
281 else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC)
282 prcm_enablemodule(PRCM_USBP2_HSIC);
283 }
284
285 /* Put UHH in SmartIdle/SmartStandby mode */
286 reg = bus_space_read_4(sc->sc.iot, sc->uhh_ioh,
287 OMAP_USBHOST_UHH_SYSCONFIG);
288 if (sc->ehci_rev == OMAP_EHCI_REV1) {
289 reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK |
290 UHH_SYSCONFIG_MIDLEMODE_MASK);
291 reg |= (UHH_SYSCONFIG_ENAWAKEUP |
292 UHH_SYSCONFIG_AUTOIDLE |
293 UHH_SYSCONFIG_CLOCKACTIVITY |
294 UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE |
295 UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY);
296 } else if (sc->ehci_rev == OMAP_EHCI_REV2) {
297 reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK;
298 reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE;
299 reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK;
300 reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY;
301 }
302 bus_space_write_4(sc->sc.iot, sc->uhh_ioh, OMAP_USBHOST_UHH_SYSCONFIG,
303 reg);
304
305 reg = bus_space_read_4(sc->sc.iot, sc->uhh_ioh,
306 OMAP_USBHOST_UHH_HOSTCONFIG);
307
308 /* Setup ULPI bypass and burst configurations */
309 reg |= (UHH_HOSTCONFIG_ENA_INCR4 |
310 UHH_HOSTCONFIG_ENA_INCR8 |
311 UHH_HOSTCONFIG_ENA_INCR16);
312 reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN;
313
314 if (sc->ehci_rev == OMAP_EHCI_REV1) {
315 if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
316 reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS;
317 if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
318 reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS;
319 if (sc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
320 reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS;
321
322 /* Bypass the TLL module for PHY mode operation */
323 if ((sc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
324 (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
325 (sc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
326 reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS;
327 else
328 reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS;
329 } else if (sc->ehci_rev == OMAP_EHCI_REV2) {
330 reg |= UHH_HOSTCONFIG_APP_START_CLK;
331
332 /* Clear port mode fields for PHY mode*/
333 reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK;
334 reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK;
335
336 if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
337 reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY;
338 else if (sc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC)
339 reg |= UHH_HOSTCONFIG_P1_MODE_HSIC;
340
341 if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
342 reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY;
343 else if (sc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC)
344 reg |= UHH_HOSTCONFIG_P2_MODE_HSIC;
345 }
346
347 bus_space_write_4(sc->sc.iot, sc->uhh_ioh, OMAP_USBHOST_UHH_HOSTCONFIG, reg);
348
349 /* If any of the ports are configured in TLL mode, enable them */
350 for (i = 0; i < OMAP_HS_USB_PORTS; i++)
351 if (sc->port_mode[i] == EHCI_HCD_OMAP_MODE_PHY)
352 tll_ch_mask |= 1 << i;
353
354 /* Enable UTMI mode for required TLL channels */
355 #ifdef notyet
356 if (tll_ch_mask)
357 omap_ehci_utmi_init(sc, tll_ch_mask);
358 #endif
359
360 /* Release the PHY reset signal now we have configured everything */
361 if (reset_performed) {
362 /* Delay for 10ms */
363 delay(10000);
364
365 /* Release reset */
366 for (i = 0; i < 3; i++) {
367 if (sc->phy_reset[i] && (sc->reset_gpio_pin[i] != -1))
368 omgpio_set_bit(sc->reset_gpio_pin[i]);
369 }
370 }
371
372 /* Set the interrupt threshold control, it controls the maximum rate at
373 * which the host controller issues interrupts. We set it to 1 microframe
374 * at startup - the default is 8 mircoframes (equates to 1ms).
375 */
376 reg = bus_space_read_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_USBCMD);
377 reg &= 0xff00ffff;
378 reg |= (1 << 16);
379 bus_space_write_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_USBCMD, reg);
380
381 /* Soft reset the PHY using PHY reset command over ULPI */
382 for (i = 0; i < OMAP_HS_USB_PORTS; i++)
383 if (sc->port_mode[i] == EHCI_HCD_OMAP_MODE_PHY)
384 omehci_soft_phy_reset(sc, i);
385
386 return(0);
387 }
388
389 void
390 omehci_soft_phy_reset(struct omehci_softc *sc, unsigned int port)
391 {
392 unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
393 uint32_t reg;
394
395 reg = ULPI_FUNC_CTRL_RESET
396 /* FUNCTION_CTRL_SET register */
397 | (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT)
398 /* Write */
399 | (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT)
400 /* PORTn */
401 | ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT)
402 /* start ULPI access*/
403 | (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT);
404
405 bus_space_write_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_INSNREG05_ULPI, reg);
406
407 timeout += 1000000;
408 /* Wait for ULPI access completion */
409 while ((bus_space_read_4(sc->sc.iot, sc->sc.ioh, OMAP_USBHOST_INSNREG05_ULPI)
410 & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) {
411
412 /* Sleep for a tick */
413 delay(10);
414
415 if (timeout-- == 0) {
416 printf("PHY reset operation timed out\n");
417 break;
418 }
419 }
420 }
421
422 int
423 omehci_detach(struct device *self, int flags)
424 {
425 struct omehci_softc *sc = (struct omehci_softc *)self;
426 int rv;
427
428 rv = ehci_detach(self, flags);
429 if (rv)
430 return (rv);
431
432 if (sc->sc_ih != NULL) {
433 arm_intr_disestablish(sc->sc_ih);
434 sc->sc_ih = NULL;
435 }
436
437 if (sc->sc.sc_size) {
438 bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
439 sc->sc.sc_size = 0;
440 }
441
442 /* XXX: stop clock */
443
444 return (0);
445 }
446
447 int
448 omehci_activate(struct device *self, int act)
449 {
450 struct omehci_softc *sc = (struct omehci_softc *)self;
451
452 switch (act) {
453 case DVACT_SUSPEND:
454 sc->sc.sc_bus.use_polling++;
455 /* FIXME */
456 sc->sc.sc_bus.use_polling--;
457 break;
458 case DVACT_RESUME:
459 sc->sc.sc_bus.use_polling++;
460 /* FIXME */
461 sc->sc.sc_bus.use_polling--;
462 break;
463 case DVACT_POWERDOWN:
464 ehci_reset(&sc->sc);
465 break;
466 }
467 return 0;
468 }
469
470 void
471 omehci_v4_early_init()
472 {
473 omgpio_set_dir(1, OMGPIO_DIR_OUT);
474 omgpio_clear_bit(1);
475 omgpio_set_dir(62, OMGPIO_DIR_OUT);
476 omgpio_clear_bit(62);
477
478 /* wait for power down */
479 delay(1000);
480
481 omgpio_set_bit(1);
482 omgpio_set_bit(62);
483
484 /* wait until powered up */
485 delay(1000);
486 }