omap_ehci.c revision 239281
1239281Sgonzo/*-
2239281Sgonzo * Copyright (c) 2011
3239281Sgonzo *	Ben Gray <ben.r.gray@gmail.com>.
4239281Sgonzo * All rights reserved.
5239281Sgonzo *
6239281Sgonzo * Redistribution and use in source and binary forms, with or without
7239281Sgonzo * modification, are permitted provided that the following conditions
8239281Sgonzo * are met:
9239281Sgonzo * 1. Redistributions of source code must retain the above copyright
10239281Sgonzo *    notice, this list of conditions and the following disclaimer.
11239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12239281Sgonzo *    notice, this list of conditions and the following disclaimer in the
13239281Sgonzo *    documentation and/or other materials provided with the distribution.
14239281Sgonzo *
15239281Sgonzo * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16239281Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17239281Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18239281Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19239281Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20239281Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21239281Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22239281Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23239281Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24239281Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25239281Sgonzo * SUCH DAMAGE.
26239281Sgonzo */
27239281Sgonzo
28239281Sgonzo/**
29239281Sgonzo * Driver for the High Speed USB EHCI module on the TI OMAP3530 SoC.
30239281Sgonzo *
31239281Sgonzo * WARNING: I've only tried this driver on a limited number of USB peripherals,
32239281Sgonzo * it is still very raw and bound to have numerous bugs in it.
33239281Sgonzo *
34239281Sgonzo * This driver is based on the FreeBSD IXP4xx EHCI driver with a lot of the
35239281Sgonzo * setup sequence coming from the Linux community and their EHCI driver for
36239281Sgonzo * OMAP.  Without these as a base I don't think I would have been able to get
37239281Sgonzo * this driver working.
38239281Sgonzo *
39239281Sgonzo * The driver only contains the EHCI parts, the module also supports OHCI and
40239281Sgonzo * USB on-the-go (OTG), currently neither are supported.
41239281Sgonzo *
42239281Sgonzo * CAUTION: This driver was written to run on the beaglebaord dev board, so I
43239281Sgonzo * have made some assumptions about the type of PHY used and some of the other
44239281Sgonzo * settings.  Bare that in mind if you intend to use this driver on another
45239281Sgonzo * platform.
46239281Sgonzo *
47239281Sgonzo * NOTE: This module uses a few different clocks, one being a 60Mhz clock for
48239281Sgonzo * the TTL part of the module.  This clock is derived from DPPL5 which must be
49239281Sgonzo * configured prior to loading this driver - it is not configured by the
50239281Sgonzo * bootloader.  It took me a long time to figure this out, and caused much
51239281Sgonzo * frustration.  This PLL is now setup in the timer/clocks part of the BSP,
52239281Sgonzo * check out the omap_prcm_setup_dpll5() function in omap_prcm.c for more info.
53239281Sgonzo *
54239281Sgonzo */
55239281Sgonzo
56239281Sgonzo#include <sys/cdefs.h>
57239281Sgonzo__FBSDID("$FreeBSD: head/sys/arm/ti/usb/omap_ehci.c 239281 2012-08-15 06:31:32Z gonzo $");
58239281Sgonzo
59239281Sgonzo#include "opt_bus.h"
60239281Sgonzo
61239281Sgonzo#include <sys/stdint.h>
62239281Sgonzo#include <sys/stddef.h>
63239281Sgonzo#include <sys/param.h>
64239281Sgonzo#include <sys/queue.h>
65239281Sgonzo#include <sys/types.h>
66239281Sgonzo#include <sys/systm.h>
67239281Sgonzo#include <sys/kernel.h>
68239281Sgonzo#include <sys/bus.h>
69239281Sgonzo#include <sys/rman.h>
70239281Sgonzo#include <sys/linker_set.h>
71239281Sgonzo#include <sys/module.h>
72239281Sgonzo#include <sys/lock.h>
73239281Sgonzo#include <sys/mutex.h>
74239281Sgonzo#include <sys/condvar.h>
75239281Sgonzo#include <sys/sysctl.h>
76239281Sgonzo#include <sys/sx.h>
77239281Sgonzo#include <sys/unistd.h>
78239281Sgonzo#include <sys/callout.h>
79239281Sgonzo#include <sys/malloc.h>
80239281Sgonzo#include <sys/priv.h>
81239281Sgonzo#include <sys/gpio.h>
82239281Sgonzo
83239281Sgonzo#include <dev/fdt/fdt_common.h>
84239281Sgonzo#include <dev/ofw/openfirm.h>
85239281Sgonzo#include <dev/ofw/ofw_bus.h>
86239281Sgonzo#include <dev/ofw/ofw_bus_subr.h>
87239281Sgonzo
88239281Sgonzo#include <dev/usb/usb.h>
89239281Sgonzo#include <dev/usb/usbdi.h>
90239281Sgonzo
91239281Sgonzo#include <dev/usb/usb_core.h>
92239281Sgonzo#include <dev/usb/usb_busdma.h>
93239281Sgonzo#include <dev/usb/usb_process.h>
94239281Sgonzo#include <dev/usb/usb_util.h>
95239281Sgonzo
96239281Sgonzo#include <dev/usb/usb_controller.h>
97239281Sgonzo#include <dev/usb/usb_bus.h>
98239281Sgonzo#include <dev/usb/controller/ehci.h>
99239281Sgonzo#include <dev/usb/controller/ehcireg.h>
100239281Sgonzo
101239281Sgonzo#include <arm/ti/tivar.h>
102239281Sgonzo#include <arm/ti/ti_prcm.h>
103239281Sgonzo#include <arm/ti/ti_scm.h>
104239281Sgonzo
105239281Sgonzo#include <arm/ti/usb/omap_usb.h>
106239281Sgonzo
107239281Sgonzo#include "gpio_if.h"
108239281Sgonzo
109239281Sgonzostruct omap_ehci_softc {
110239281Sgonzo	ehci_softc_t        base;	/* storage for EHCI code */
111239281Sgonzo
112239281Sgonzo	device_t            sc_dev;
113239281Sgonzo	device_t            sc_gpio_dev;
114239281Sgonzo
115239281Sgonzo	/* TLL register set */
116239281Sgonzo	struct resource*    tll_mem_res;
117239281Sgonzo
118239281Sgonzo	/* UHH register set */
119239281Sgonzo	struct resource*    uhh_mem_res;
120239281Sgonzo
121239281Sgonzo	/* The revision of the HS USB HOST read from UHH_REVISION */
122239281Sgonzo	uint32_t            ehci_rev;
123239281Sgonzo
124239281Sgonzo	/* The following details are provided by conf hints */
125239281Sgonzo	int                 port_mode[3];
126239281Sgonzo	int                 phy_reset[3];
127239281Sgonzo	int                 reset_gpio_pin[3];
128239281Sgonzo};
129239281Sgonzo
130239281Sgonzostatic device_attach_t omap_ehci_attach;
131239281Sgonzostatic device_detach_t omap_ehci_detach;
132239281Sgonzostatic device_shutdown_t omap_ehci_shutdown;
133239281Sgonzostatic device_suspend_t omap_ehci_suspend;
134239281Sgonzostatic device_resume_t omap_ehci_resume;
135239281Sgonzo
136239281Sgonzo/**
137239281Sgonzo *	omap_tll_read_4 - read a 32-bit value from the USBTLL registers
138239281Sgonzo *	omap_tll_write_4 - write a 32-bit value from the USBTLL registers
139239281Sgonzo *	omap_tll_readb - read an 8-bit value from the USBTLL registers
140239281Sgonzo *	omap_tll_writeb - write an 8-bit value from the USBTLL registers
141239281Sgonzo *	@sc: omap ehci device context
142239281Sgonzo *	@off: byte offset within the register set to read from
143239281Sgonzo *	@val: the value to write into the register
144239281Sgonzo *
145239281Sgonzo *
146239281Sgonzo *	LOCKING:
147239281Sgonzo *	None
148239281Sgonzo *
149239281Sgonzo *	RETURNS:
150239281Sgonzo *	nothing in case of write function, if read function returns the value read.
151239281Sgonzo */
152239281Sgonzostatic inline uint32_t
153239281Sgonzoomap_tll_read_4(struct omap_ehci_softc *sc, bus_size_t off)
154239281Sgonzo{
155239281Sgonzo	return bus_read_4(sc->tll_mem_res, off);
156239281Sgonzo}
157239281Sgonzo
158239281Sgonzostatic inline void
159239281Sgonzoomap_tll_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val)
160239281Sgonzo{
161239281Sgonzo	bus_write_4(sc->tll_mem_res, off, val);
162239281Sgonzo}
163239281Sgonzo
164239281Sgonzostatic inline uint8_t
165239281Sgonzoomap_tll_readb(struct omap_ehci_softc *sc, bus_size_t off)
166239281Sgonzo{
167239281Sgonzo	return bus_read_1(sc->tll_mem_res, off);
168239281Sgonzo}
169239281Sgonzo
170239281Sgonzostatic inline void
171239281Sgonzoomap_tll_writeb(struct omap_ehci_softc *sc, bus_size_t off, uint8_t val)
172239281Sgonzo{
173239281Sgonzo	bus_write_1(sc->tll_mem_res, off, val);
174239281Sgonzo}
175239281Sgonzo
176239281Sgonzo/**
177239281Sgonzo *	omap_ehci_read_4 - read a 32-bit value from the EHCI registers
178239281Sgonzo *	omap_ehci_write_4 - write a 32-bit value from the EHCI registers
179239281Sgonzo *	@sc: omap ehci device context
180239281Sgonzo *	@off: byte offset within the register set to read from
181239281Sgonzo *	@val: the value to write into the register
182239281Sgonzo *
183239281Sgonzo *
184239281Sgonzo *	LOCKING:
185239281Sgonzo *	None
186239281Sgonzo *
187239281Sgonzo *	RETURNS:
188239281Sgonzo *	nothing in case of write function, if read function returns the value read.
189239281Sgonzo */
190239281Sgonzostatic inline uint32_t
191239281Sgonzoomap_ehci_read_4(struct omap_ehci_softc *sc, bus_size_t off)
192239281Sgonzo{
193239281Sgonzo	return (bus_read_4(sc->base.sc_io_res, off));
194239281Sgonzo}
195239281Sgonzostatic inline void
196239281Sgonzoomap_ehci_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val)
197239281Sgonzo{
198239281Sgonzo	bus_write_4(sc->base.sc_io_res, off, val);
199239281Sgonzo}
200239281Sgonzo
201239281Sgonzo/**
202239281Sgonzo *	omap_uhh_read_4 - read a 32-bit value from the UHH registers
203239281Sgonzo *	omap_uhh_write_4 - write a 32-bit value from the UHH registers
204239281Sgonzo *	@sc: omap ehci device context
205239281Sgonzo *	@off: byte offset within the register set to read from
206239281Sgonzo *	@val: the value to write into the register
207239281Sgonzo *
208239281Sgonzo *
209239281Sgonzo *	LOCKING:
210239281Sgonzo *	None
211239281Sgonzo *
212239281Sgonzo *	RETURNS:
213239281Sgonzo *	nothing in case of write function, if read function returns the value read.
214239281Sgonzo */
215239281Sgonzostatic inline uint32_t
216239281Sgonzoomap_uhh_read_4(struct omap_ehci_softc *sc, bus_size_t off)
217239281Sgonzo{
218239281Sgonzo	return bus_read_4(sc->uhh_mem_res, off);
219239281Sgonzo}
220239281Sgonzostatic inline void
221239281Sgonzoomap_uhh_write_4(struct omap_ehci_softc *sc, bus_size_t off, uint32_t val)
222239281Sgonzo{
223239281Sgonzo	bus_write_4(sc->uhh_mem_res, off, val);
224239281Sgonzo}
225239281Sgonzo
226239281Sgonzo/**
227239281Sgonzo *	omap_ehci_utmi_init - initialises the UTMI part of the controller
228239281Sgonzo *	@isc: omap ehci device context
229239281Sgonzo *
230239281Sgonzo *
231239281Sgonzo *
232239281Sgonzo *	LOCKING:
233239281Sgonzo *	none
234239281Sgonzo *
235239281Sgonzo *	RETURNS:
236239281Sgonzo *	nothing
237239281Sgonzo */
238239281Sgonzostatic void
239239281Sgonzoomap_ehci_utmi_init(struct omap_ehci_softc *isc, unsigned int en_mask)
240239281Sgonzo{
241239281Sgonzo	unsigned int i;
242239281Sgonzo	uint32_t reg;
243239281Sgonzo
244239281Sgonzo	/* There are 3 TLL channels, one per USB controller so set them all up the
245239281Sgonzo	 * same, SDR mode, bit stuffing and no autoidle.
246239281Sgonzo	 */
247239281Sgonzo	for (i=0; i<3; i++) {
248239281Sgonzo		reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i));
249239281Sgonzo
250239281Sgonzo		reg &= ~(TLL_CHANNEL_CONF_UTMIAUTOIDLE
251239281Sgonzo				 | TLL_CHANNEL_CONF_ULPINOBITSTUFF
252239281Sgonzo				 | TLL_CHANNEL_CONF_ULPIDDRMODE);
253239281Sgonzo
254239281Sgonzo		omap_tll_write_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg);
255239281Sgonzo	}
256239281Sgonzo
257239281Sgonzo	/* Program the common TLL register */
258239281Sgonzo	reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_SHARED_CONF);
259239281Sgonzo
260239281Sgonzo	reg &= ~( TLL_SHARED_CONF_USB_90D_DDR_EN
261239281Sgonzo			| TLL_SHARED_CONF_USB_DIVRATIO_MASK);
262239281Sgonzo	reg |=  ( TLL_SHARED_CONF_FCLK_IS_ON
263239281Sgonzo			| TLL_SHARED_CONF_USB_DIVRATIO_2
264239281Sgonzo			| TLL_SHARED_CONF_USB_180D_SDR_EN);
265239281Sgonzo
266239281Sgonzo	omap_tll_write_4(isc, OMAP_USBTLL_TLL_SHARED_CONF, reg);
267239281Sgonzo
268239281Sgonzo	/* Enable channels now */
269239281Sgonzo	for (i = 0; i < 3; i++) {
270239281Sgonzo		reg = omap_tll_read_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i));
271239281Sgonzo
272239281Sgonzo		/* Enable only the reg that is needed */
273239281Sgonzo		if ((en_mask & (1 << i)) == 0)
274239281Sgonzo			continue;
275239281Sgonzo
276239281Sgonzo		reg |= TLL_CHANNEL_CONF_CHANEN;
277239281Sgonzo		omap_tll_write_4(isc, OMAP_USBTLL_TLL_CHANNEL_CONF(i), reg);
278239281Sgonzo	}
279239281Sgonzo}
280239281Sgonzo
281239281Sgonzo/**
282239281Sgonzo *	omap_ehci_soft_phy_reset - resets the phy using the reset command
283239281Sgonzo *	@isc: omap ehci device context
284239281Sgonzo *	@port: port to send the reset over
285239281Sgonzo *
286239281Sgonzo *
287239281Sgonzo *	LOCKING:
288239281Sgonzo *	none
289239281Sgonzo *
290239281Sgonzo *	RETURNS:
291239281Sgonzo *	nothing
292239281Sgonzo */
293239281Sgonzostatic void
294239281Sgonzoomap_ehci_soft_phy_reset(struct omap_ehci_softc *isc, unsigned int port)
295239281Sgonzo{
296239281Sgonzo	unsigned long timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
297239281Sgonzo	uint32_t reg;
298239281Sgonzo
299239281Sgonzo	reg = ULPI_FUNC_CTRL_RESET
300239281Sgonzo		/* FUNCTION_CTRL_SET register */
301239281Sgonzo		| (ULPI_SET(ULPI_FUNC_CTRL) << OMAP_USBHOST_INSNREG05_ULPI_REGADD_SHIFT)
302239281Sgonzo		/* Write */
303239281Sgonzo		| (2 << OMAP_USBHOST_INSNREG05_ULPI_OPSEL_SHIFT)
304239281Sgonzo		/* PORTn */
305239281Sgonzo		| ((port + 1) << OMAP_USBHOST_INSNREG05_ULPI_PORTSEL_SHIFT)
306239281Sgonzo		/* start ULPI access*/
307239281Sgonzo		| (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT);
308239281Sgonzo
309239281Sgonzo	omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG05_ULPI, reg);
310239281Sgonzo
311239281Sgonzo	/* Wait for ULPI access completion */
312239281Sgonzo	while ((omap_ehci_read_4(isc, OMAP_USBHOST_INSNREG05_ULPI)
313239281Sgonzo	       & (1 << OMAP_USBHOST_INSNREG05_ULPI_CONTROL_SHIFT))) {
314239281Sgonzo
315239281Sgonzo		/* Sleep for a tick */
316239281Sgonzo		pause("USBPHY_RESET", 1);
317239281Sgonzo
318239281Sgonzo		if (timeout-- == 0) {
319239281Sgonzo			device_printf(isc->sc_dev, "PHY reset operation timed out\n");
320239281Sgonzo			break;
321239281Sgonzo		}
322239281Sgonzo	}
323239281Sgonzo}
324239281Sgonzo
325239281Sgonzo
326239281Sgonzo/**
327239281Sgonzo *	omap_ehci_init - initialises the USB host EHCI controller
328239281Sgonzo *	@isc: omap ehci device context
329239281Sgonzo *
330239281Sgonzo *	This initialisation routine is quite heavily based on the work done by the
331239281Sgonzo *	OMAP Linux team (for which I thank them very much).  The init sequence is
332239281Sgonzo *	almost identical, diverging only for the FreeBSD specifics.
333239281Sgonzo *
334239281Sgonzo *	LOCKING:
335239281Sgonzo *	none
336239281Sgonzo *
337239281Sgonzo *	RETURNS:
338239281Sgonzo *	0 on success, a negative error code on failure.
339239281Sgonzo */
340239281Sgonzostatic int
341239281Sgonzoomap_ehci_init(struct omap_ehci_softc *isc)
342239281Sgonzo{
343239281Sgonzo	unsigned long timeout;
344239281Sgonzo	int ret = 0;
345239281Sgonzo	uint8_t tll_ch_mask = 0;
346239281Sgonzo	uint32_t reg = 0;
347239281Sgonzo	int reset_performed = 0;
348239281Sgonzo	int i;
349239281Sgonzo
350239281Sgonzo	device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n");
351239281Sgonzo
352239281Sgonzo
353239281Sgonzo	/* Enable Clocks for high speed USBHOST */
354239281Sgonzo	ti_prcm_clk_enable(USBHSHOST_CLK);
355239281Sgonzo
356239281Sgonzo	/* Hold the PHY in reset while configuring */
357239281Sgonzo	for (int i = 0; i < 3; i++) {
358239281Sgonzo		if (isc->phy_reset[i]) {
359239281Sgonzo			/* Configure the GPIO to drive low (hold in reset) */
360239281Sgonzo			if ((isc->reset_gpio_pin[i] != -1) && (isc->sc_gpio_dev != NULL)) {
361239281Sgonzo				GPIO_PIN_SETFLAGS(isc->sc_gpio_dev, isc->reset_gpio_pin[i],
362239281Sgonzo				    GPIO_PIN_OUTPUT);
363239281Sgonzo				GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[i],
364239281Sgonzo				    GPIO_PIN_LOW);
365239281Sgonzo				reset_performed = 1;
366239281Sgonzo			}
367239281Sgonzo		}
368239281Sgonzo	}
369239281Sgonzo
370239281Sgonzo	/* Hold the PHY in RESET for enough time till DIR is high */
371239281Sgonzo	if (reset_performed)
372239281Sgonzo		DELAY(10);
373239281Sgonzo
374239281Sgonzo	/* Read the UHH revision */
375239281Sgonzo	isc->ehci_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION);
376239281Sgonzo	device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->ehci_rev);
377239281Sgonzo
378239281Sgonzo	/* Initilise the low level interface module(s) */
379239281Sgonzo	if (isc->ehci_rev == OMAP_EHCI_REV1) {
380239281Sgonzo
381239281Sgonzo		/* Enable the USB TLL */
382239281Sgonzo		ti_prcm_clk_enable(USBTLL_CLK);
383239281Sgonzo
384239281Sgonzo		/* Perform TLL soft reset, and wait until reset is complete */
385239281Sgonzo		omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET);
386239281Sgonzo
387239281Sgonzo		/* Set the timeout to 100ms*/
388239281Sgonzo		timeout = (hz < 10) ? 1 : ((100 * hz) / 1000);
389239281Sgonzo
390239281Sgonzo		/* Wait for TLL reset to complete */
391239281Sgonzo		while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) &
392239281Sgonzo		        TLL_SYSSTATUS_RESETDONE) == 0x00) {
393239281Sgonzo
394239281Sgonzo			/* Sleep for a tick */
395239281Sgonzo			pause("USBRESET", 1);
396239281Sgonzo
397239281Sgonzo			if (timeout-- == 0) {
398239281Sgonzo				device_printf(isc->sc_dev, "TLL reset operation timed out\n");
399239281Sgonzo				ret = EINVAL;
400239281Sgonzo				goto err_sys_status;
401239281Sgonzo			}
402239281Sgonzo		}
403239281Sgonzo
404239281Sgonzo		device_printf(isc->sc_dev, "TLL RESET DONE\n");
405239281Sgonzo
406239281Sgonzo		/* CLOCKACTIVITY = 1 : OCP-derived internal clocks ON during idle
407239281Sgonzo		 * SIDLEMODE = 2     : Smart-idle mode. Sidleack asserted after Idlereq
408239281Sgonzo		 *                     assertion when no more activity on the USB.
409239281Sgonzo		 * ENAWAKEUP = 1     : Wakeup generation enabled
410239281Sgonzo		 */
411239281Sgonzo		omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP |
412239281Sgonzo		                                            TLL_SYSCONFIG_AUTOIDLE |
413239281Sgonzo		                                            TLL_SYSCONFIG_SIDLE_SMART_IDLE |
414239281Sgonzo		                                            TLL_SYSCONFIG_CACTIVITY);
415239281Sgonzo
416239281Sgonzo	} else if (isc->ehci_rev == OMAP_EHCI_REV2) {
417239281Sgonzo
418239281Sgonzo		/* For OMAP44xx devices you have to enable the per-port clocks:
419239281Sgonzo		 *  PHY_MODE  - External ULPI clock
420239281Sgonzo		 *  TTL_MODE  - Internal UTMI clock
421239281Sgonzo		 *  HSIC_MODE - Internal 480Mhz and 60Mhz clocks
422239281Sgonzo		 */
423239281Sgonzo		if (isc->ehci_rev == OMAP_EHCI_REV2) {
424239281Sgonzo			if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) {
425239281Sgonzo				ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK);
426239281Sgonzo				ti_prcm_clk_enable(USBP1_PHY_CLK);
427239281Sgonzo			} else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
428239281Sgonzo				ti_prcm_clk_enable(USBP1_UTMI_CLK);
429239281Sgonzo			else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC)
430239281Sgonzo				ti_prcm_clk_enable(USBP1_HSIC_CLK);
431239281Sgonzo
432239281Sgonzo			if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) {
433239281Sgonzo				ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK);
434239281Sgonzo				ti_prcm_clk_enable(USBP2_PHY_CLK);
435239281Sgonzo			} else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
436239281Sgonzo				ti_prcm_clk_enable(USBP2_UTMI_CLK);
437239281Sgonzo			else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC)
438239281Sgonzo				ti_prcm_clk_enable(USBP2_HSIC_CLK);
439239281Sgonzo		}
440239281Sgonzo	}
441239281Sgonzo
442239281Sgonzo	/* Put UHH in SmartIdle/SmartStandby mode */
443239281Sgonzo	reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG);
444239281Sgonzo	if (isc->ehci_rev == OMAP_EHCI_REV1) {
445239281Sgonzo		reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK |
446239281Sgonzo		         UHH_SYSCONFIG_MIDLEMODE_MASK);
447239281Sgonzo		reg |= (UHH_SYSCONFIG_ENAWAKEUP |
448239281Sgonzo		        UHH_SYSCONFIG_AUTOIDLE |
449239281Sgonzo		        UHH_SYSCONFIG_CLOCKACTIVITY |
450239281Sgonzo		        UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE |
451239281Sgonzo		        UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY);
452239281Sgonzo	} else if (isc->ehci_rev == OMAP_EHCI_REV2) {
453239281Sgonzo		reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK;
454239281Sgonzo		reg |=  UHH_SYSCONFIG_IDLEMODE_NOIDLE;
455239281Sgonzo		reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK;
456239281Sgonzo		reg |=  UHH_SYSCONFIG_STANDBYMODE_NOSTDBY;
457239281Sgonzo	}
458239281Sgonzo	omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg);
459239281Sgonzo	device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg);
460239281Sgonzo
461239281Sgonzo	reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG);
462239281Sgonzo
463239281Sgonzo	/* Setup ULPI bypass and burst configurations */
464239281Sgonzo	reg |= (UHH_HOSTCONFIG_ENA_INCR4 |
465239281Sgonzo			UHH_HOSTCONFIG_ENA_INCR8 |
466239281Sgonzo			UHH_HOSTCONFIG_ENA_INCR16);
467239281Sgonzo	reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN;
468239281Sgonzo
469239281Sgonzo	if (isc->ehci_rev == OMAP_EHCI_REV1) {
470239281Sgonzo		if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN)
471239281Sgonzo			reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS;
472239281Sgonzo		if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN)
473239281Sgonzo			reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS;
474239281Sgonzo		if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN)
475239281Sgonzo			reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS;
476239281Sgonzo
477239281Sgonzo		/* Bypass the TLL module for PHY mode operation */
478239281Sgonzo		if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) ||
479239281Sgonzo		    (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) ||
480239281Sgonzo		    (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY))
481239281Sgonzo			reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS;
482239281Sgonzo		else
483239281Sgonzo			reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS;
484239281Sgonzo
485239281Sgonzo	} else if (isc->ehci_rev == OMAP_EHCI_REV2) {
486239281Sgonzo		reg |=  UHH_HOSTCONFIG_APP_START_CLK;
487239281Sgonzo
488239281Sgonzo		/* Clear port mode fields for PHY mode*/
489239281Sgonzo		reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK;
490239281Sgonzo		reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK;
491239281Sgonzo
492239281Sgonzo		if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
493239281Sgonzo			reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY;
494239281Sgonzo		else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC)
495239281Sgonzo			reg |= UHH_HOSTCONFIG_P1_MODE_HSIC;
496239281Sgonzo
497239281Sgonzo		if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
498239281Sgonzo			reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY;
499239281Sgonzo		else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC)
500239281Sgonzo			reg |= UHH_HOSTCONFIG_P2_MODE_HSIC;
501239281Sgonzo	}
502239281Sgonzo
503239281Sgonzo	omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg);
504239281Sgonzo	device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg);
505239281Sgonzo
506239281Sgonzo
507239281Sgonzo	/* I found the code and comments in the Linux EHCI driver - thanks guys :)
508239281Sgonzo	 *
509239281Sgonzo	 * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended
510239281Sgonzo	 * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared
511239281Sgonzo	 * (for example when we do ehci_bus_suspend). This breaks suspend-resume if
512239281Sgonzo	 * the root-hub is allowed to suspend. Writing 1 to this undocumented
513239281Sgonzo	 * register bit disables this feature and restores normal behavior."
514239281Sgonzo	 */
515239281Sgonzo#if 0
516239281Sgonzo	omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG04,
517239281Sgonzo	                 OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND);
518239281Sgonzo#endif
519239281Sgonzo
520239281Sgonzo	/* If any of the ports are configured in TLL mode, enable them */
521239281Sgonzo	if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) ||
522239281Sgonzo		(isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) ||
523239281Sgonzo		(isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) {
524239281Sgonzo
525239281Sgonzo		if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL)
526239281Sgonzo			tll_ch_mask |= 0x1;
527239281Sgonzo		if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL)
528239281Sgonzo			tll_ch_mask |= 0x2;
529239281Sgonzo		if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)
530239281Sgonzo			tll_ch_mask |= 0x4;
531239281Sgonzo
532239281Sgonzo		/* Enable UTMI mode for required TLL channels */
533239281Sgonzo		omap_ehci_utmi_init(isc, tll_ch_mask);
534239281Sgonzo	}
535239281Sgonzo
536239281Sgonzo
537239281Sgonzo	/* Release the PHY reset signal now we have configured everything */
538239281Sgonzo	if (reset_performed) {
539239281Sgonzo
540239281Sgonzo		/* Delay for 10ms */
541239281Sgonzo		DELAY(10000);
542239281Sgonzo
543239281Sgonzo		for (i = 0; i < 3; i++) {
544239281Sgonzo			/* Release reset */
545239281Sgonzo
546239281Sgonzo			if (isc->phy_reset[i] && (isc->reset_gpio_pin[i] != -1)
547239281Sgonzo			    && (isc->sc_gpio_dev != NULL)) {
548239281Sgonzo				GPIO_PIN_SET(isc->sc_gpio_dev,
549239281Sgonzo					isc->reset_gpio_pin[i], GPIO_PIN_HIGH);
550239281Sgonzo			}
551239281Sgonzo		}
552239281Sgonzo	}
553239281Sgonzo
554239281Sgonzo	/* Set the interrupt threshold control, it controls the maximum rate at
555239281Sgonzo	 * which the host controller issues interrupts.  We set it to 1 microframe
556239281Sgonzo	 * at startup - the default is 8 mircoframes (equates to 1ms).
557239281Sgonzo	 */
558239281Sgonzo	reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD);
559239281Sgonzo	reg &= 0xff00ffff;
560239281Sgonzo	reg |= (1 << 16);
561239281Sgonzo	omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg);
562239281Sgonzo
563239281Sgonzo	/* Soft reset the PHY using PHY reset command over ULPI */
564239281Sgonzo	if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
565239281Sgonzo		omap_ehci_soft_phy_reset(isc, 0);
566239281Sgonzo	if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
567239281Sgonzo		omap_ehci_soft_phy_reset(isc, 1);
568239281Sgonzo
569239281Sgonzo	return(0);
570239281Sgonzo
571239281Sgonzoerr_sys_status:
572239281Sgonzo
573239281Sgonzo	/* Disable the TLL clocks */
574239281Sgonzo	ti_prcm_clk_disable(USBTLL_CLK);
575239281Sgonzo
576239281Sgonzo	/* Disable Clocks for USBHOST */
577239281Sgonzo	ti_prcm_clk_disable(USBHSHOST_CLK);
578239281Sgonzo
579239281Sgonzo	return(ret);
580239281Sgonzo}
581239281Sgonzo
582239281Sgonzo
583239281Sgonzo/**
584239281Sgonzo *	omap_ehci_fini - shutdown the EHCI controller
585239281Sgonzo *	@isc: omap ehci device context
586239281Sgonzo *
587239281Sgonzo *
588239281Sgonzo *
589239281Sgonzo *	LOCKING:
590239281Sgonzo *	none
591239281Sgonzo *
592239281Sgonzo *	RETURNS:
593239281Sgonzo *	0 on success, a negative error code on failure.
594239281Sgonzo */
595239281Sgonzostatic void
596239281Sgonzoomap_ehci_fini(struct omap_ehci_softc *isc)
597239281Sgonzo{
598239281Sgonzo	unsigned long timeout;
599239281Sgonzo
600239281Sgonzo	device_printf(isc->sc_dev, "Stopping TI EHCI USB Controller\n");
601239281Sgonzo
602239281Sgonzo	/* Set the timeout */
603239281Sgonzo	if (hz < 10)
604239281Sgonzo		timeout = 1;
605239281Sgonzo	else
606239281Sgonzo		timeout = (100 * hz) / 1000;
607239281Sgonzo
608239281Sgonzo	/* Reset the UHH, OHCI and EHCI modules */
609239281Sgonzo	omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, 0x0002);
610239281Sgonzo	while ((omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSSTATUS) & 0x07) == 0x00) {
611239281Sgonzo		/* Sleep for a tick */
612239281Sgonzo		pause("USBRESET", 1);
613239281Sgonzo
614239281Sgonzo		if (timeout-- == 0) {
615239281Sgonzo			device_printf(isc->sc_dev, "operation timed out\n");
616239281Sgonzo			break;
617239281Sgonzo		}
618239281Sgonzo	}
619239281Sgonzo
620239281Sgonzo
621239281Sgonzo	/* Set the timeout */
622239281Sgonzo	if (hz < 10)
623239281Sgonzo		timeout = 1;
624239281Sgonzo	else
625239281Sgonzo		timeout = (100 * hz) / 1000;
626239281Sgonzo
627239281Sgonzo	/* Reset the TLL module */
628239281Sgonzo	omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, 0x0002);
629239281Sgonzo	while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) & (0x01)) == 0x00) {
630239281Sgonzo		/* Sleep for a tick */
631239281Sgonzo		pause("USBRESET", 1);
632239281Sgonzo
633239281Sgonzo		if (timeout-- == 0) {
634239281Sgonzo			device_printf(isc->sc_dev, "operation timed out\n");
635239281Sgonzo			break;
636239281Sgonzo		}
637239281Sgonzo	}
638239281Sgonzo
639239281Sgonzo
640239281Sgonzo	/* Disable functional and interface clocks for the TLL and HOST modules */
641239281Sgonzo	ti_prcm_clk_disable(USBTLL_CLK);
642239281Sgonzo	ti_prcm_clk_disable(USBHSHOST_CLK);
643239281Sgonzo
644239281Sgonzo	device_printf(isc->sc_dev, "Clock to USB host has been disabled\n");
645239281Sgonzo
646239281Sgonzo}
647239281Sgonzo
648239281Sgonzo
649239281Sgonzo
650239281Sgonzo/**
651239281Sgonzo *	omap_ehci_suspend - suspends the bus
652239281Sgonzo *	@dev: omap ehci device
653239281Sgonzo *
654239281Sgonzo *	Effectively boilerplate EHCI suspend code.
655239281Sgonzo *
656239281Sgonzo *	TODO: There is a lot more we could do here - i.e. force the controller into
657239281Sgonzo *	idle mode and disable all the clocks for start.
658239281Sgonzo *
659239281Sgonzo *	LOCKING:
660239281Sgonzo *	none
661239281Sgonzo *
662239281Sgonzo *	RETURNS:
663239281Sgonzo *	0 on success or a positive error code
664239281Sgonzo */
665239281Sgonzostatic int
666239281Sgonzoomap_ehci_suspend(device_t dev)
667239281Sgonzo{
668239281Sgonzo	ehci_softc_t *sc = device_get_softc(dev);
669239281Sgonzo	int err;
670239281Sgonzo
671239281Sgonzo	sc = sc;
672239281Sgonzo	err = bus_generic_suspend(dev);
673239281Sgonzo	if (err)
674239281Sgonzo		return (err);
675239281Sgonzo	return (0);
676239281Sgonzo}
677239281Sgonzo
678239281Sgonzo
679239281Sgonzo/**
680239281Sgonzo *	omap_ehci_resume - resumes a suspended bus
681239281Sgonzo *	@dev: omap ehci device
682239281Sgonzo *
683239281Sgonzo *	Effectively boilerplate EHCI resume code.
684239281Sgonzo *
685239281Sgonzo *	LOCKING:
686239281Sgonzo *	none
687239281Sgonzo *
688239281Sgonzo *	RETURNS:
689239281Sgonzo *	0 on success or a positive error code on failure
690239281Sgonzo */
691239281Sgonzostatic int
692239281Sgonzoomap_ehci_resume(device_t dev)
693239281Sgonzo{
694239281Sgonzo	ehci_softc_t *sc = device_get_softc(dev);
695239281Sgonzo	sc = sc;
696239281Sgonzo
697239281Sgonzo	bus_generic_resume(dev);
698239281Sgonzo
699239281Sgonzo	return (0);
700239281Sgonzo}
701239281Sgonzo
702239281Sgonzo
703239281Sgonzo/**
704239281Sgonzo *	omap_ehci_shutdown - starts the given command
705239281Sgonzo *	@dev:
706239281Sgonzo *
707239281Sgonzo *	Effectively boilerplate EHCI shutdown code.
708239281Sgonzo *
709239281Sgonzo *	LOCKING:
710239281Sgonzo *	none.
711239281Sgonzo *
712239281Sgonzo *	RETURNS:
713239281Sgonzo *	0 on success or a positive error code on failure
714239281Sgonzo */
715239281Sgonzostatic int
716239281Sgonzoomap_ehci_shutdown(device_t dev)
717239281Sgonzo{
718239281Sgonzo	ehci_softc_t *sc = device_get_softc(dev);
719239281Sgonzo	int err;
720239281Sgonzo
721239281Sgonzo	sc = sc;
722239281Sgonzo	err = bus_generic_shutdown(dev);
723239281Sgonzo	if (err)
724239281Sgonzo		return (err);
725239281Sgonzo
726239281Sgonzo	return (0);
727239281Sgonzo}
728239281Sgonzo
729239281Sgonzo
730239281Sgonzo/**
731239281Sgonzo *	omap_ehci_probe - starts the given command
732239281Sgonzo *	@dev:
733239281Sgonzo *
734239281Sgonzo *	Effectively boilerplate EHCI resume code.
735239281Sgonzo *
736239281Sgonzo *	LOCKING:
737239281Sgonzo *	Caller should be holding the OMAP3_MMC lock.
738239281Sgonzo *
739239281Sgonzo *	RETURNS:
740239281Sgonzo *	EH_HANDLED or EH_NOT_HANDLED
741239281Sgonzo */
742239281Sgonzostatic int
743239281Sgonzoomap_ehci_probe(device_t dev)
744239281Sgonzo{
745239281Sgonzo	if (!ofw_bus_is_compatible(dev, "ti,usb-ehci"))
746239281Sgonzo		return (ENXIO);
747239281Sgonzo
748239281Sgonzo	device_set_desc(dev, OMAP_EHCI_HC_DEVSTR);
749239281Sgonzo
750239281Sgonzo	return (BUS_PROBE_DEFAULT);
751239281Sgonzo}
752239281Sgonzo
753239281Sgonzo/**
754239281Sgonzo *	omap_ehci_attach - driver entry point, sets up the ECHI controller/driver
755239281Sgonzo *	@dev: the new device handle
756239281Sgonzo *
757239281Sgonzo *	Sets up bus spaces, interrupt handles, etc for the EHCI controller.  It also
758239281Sgonzo *	parses the resource hints and calls omap_ehci_init() to initialise the
759239281Sgonzo *	H/W.
760239281Sgonzo *
761239281Sgonzo *	LOCKING:
762239281Sgonzo *	none
763239281Sgonzo *
764239281Sgonzo *	RETURNS:
765239281Sgonzo *	0 on success or a positive error code on failure.
766239281Sgonzo */
767239281Sgonzostatic int
768239281Sgonzoomap_ehci_attach(device_t dev)
769239281Sgonzo{
770239281Sgonzo	struct omap_ehci_softc *isc = device_get_softc(dev);
771239281Sgonzo	phandle_t node;
772239281Sgonzo	/* 3 ports with 3 cells per port */
773239281Sgonzo	pcell_t phyconf[3 * 3];
774239281Sgonzo	pcell_t *phyconf_ptr;
775239281Sgonzo	ehci_softc_t *sc = &isc->base;
776239281Sgonzo	int err;
777239281Sgonzo	int rid;
778239281Sgonzo	int len, tuple_size;
779239281Sgonzo	int i;
780239281Sgonzo
781239281Sgonzo	/* initialise some bus fields */
782239281Sgonzo	sc->sc_bus.parent = dev;
783239281Sgonzo	sc->sc_bus.devices = sc->sc_devices;
784239281Sgonzo	sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
785239281Sgonzo
786239281Sgonzo	/* save the device */
787239281Sgonzo	isc->sc_dev = dev;
788239281Sgonzo
789239281Sgonzo	/* get all DMA memory */
790239281Sgonzo	if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev),
791239281Sgonzo	                          &ehci_iterate_hw_softc)) {
792239281Sgonzo		return (ENOMEM);
793239281Sgonzo	}
794239281Sgonzo
795239281Sgonzo	/* When the EHCI driver is added to the tree it is expected that 3
796239281Sgonzo	 * memory resources and 1 interrupt resource is assigned. The memory
797239281Sgonzo	 * resources should be:
798239281Sgonzo	 *   0 => EHCI register range
799239281Sgonzo	 *   1 => UHH register range
800239281Sgonzo	 *   2 => TLL register range
801239281Sgonzo	 *
802239281Sgonzo	 * The interrupt resource is just the single interupt for the controller.
803239281Sgonzo	 */
804239281Sgonzo
805239281Sgonzo	/* Allocate resource for the EHCI register set */
806239281Sgonzo	rid = 0;
807239281Sgonzo	sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
808239281Sgonzo	if (!sc->sc_io_res) {
809239281Sgonzo		device_printf(dev, "Error: Could not map EHCI memory\n");
810239281Sgonzo		goto error;
811239281Sgonzo	}
812239281Sgonzo	/* Request an interrupt resource */
813239281Sgonzo	rid = 0;
814239281Sgonzo	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
815239281Sgonzo	if (sc->sc_irq_res == NULL) {
816239281Sgonzo		device_printf(dev, "Error: could not allocate irq\n");
817239281Sgonzo		goto error;
818239281Sgonzo	}
819239281Sgonzo
820239281Sgonzo	/* Allocate resource for the UHH register set */
821239281Sgonzo	rid = 1;
822239281Sgonzo	isc->uhh_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
823239281Sgonzo	if (!isc->uhh_mem_res) {
824239281Sgonzo		device_printf(dev, "Error: Could not map UHH memory\n");
825239281Sgonzo		goto error;
826239281Sgonzo	}
827239281Sgonzo	/* Allocate resource for the TLL register set */
828239281Sgonzo	rid = 2;
829239281Sgonzo	isc->tll_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
830239281Sgonzo	if (!isc->tll_mem_res) {
831239281Sgonzo		device_printf(dev, "Error: Could not map TLL memory\n");
832239281Sgonzo		goto error;
833239281Sgonzo	}
834239281Sgonzo
835239281Sgonzo	/* Add this device as a child of the USBus device */
836239281Sgonzo	sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
837239281Sgonzo	if (!sc->sc_bus.bdev) {
838239281Sgonzo		device_printf(dev, "Error: could not add USB device\n");
839239281Sgonzo		goto error;
840239281Sgonzo	}
841239281Sgonzo
842239281Sgonzo	device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
843239281Sgonzo	device_set_desc(sc->sc_bus.bdev, OMAP_EHCI_HC_DEVSTR);
844239281Sgonzo
845239281Sgonzo	/* Set the vendor name */
846239281Sgonzo	sprintf(sc->sc_vendor, "Texas Instruments");
847239281Sgonzo
848239281Sgonzo	/* Get the GPIO device, we may need this if the driver needs to toggle
849239281Sgonzo	 * some pins for external PHY resets.
850239281Sgonzo	 */
851239281Sgonzo	isc->sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
852239281Sgonzo	if (isc->sc_gpio_dev == NULL) {
853239281Sgonzo		device_printf(dev, "Error: failed to get the GPIO device\n");
854239281Sgonzo		goto error;
855239281Sgonzo	}
856239281Sgonzo
857239281Sgonzo	/* Set the defaults for the hints */
858239281Sgonzo	for (i = 0; i < 3; i++) {
859239281Sgonzo		isc->phy_reset[i] = 0;
860239281Sgonzo		isc->port_mode[i] = EHCI_HCD_OMAP_MODE_UNKNOWN;
861239281Sgonzo		isc->reset_gpio_pin[i] = -1;
862239281Sgonzo	}
863239281Sgonzo
864239281Sgonzo	tuple_size = sizeof(pcell_t) * 3;
865239281Sgonzo	node = ofw_bus_get_node(dev);
866239281Sgonzo	len = OF_getprop(node, "phy-config", phyconf, sizeof(phyconf));
867239281Sgonzo	if (len > 0) {
868239281Sgonzo		if (len % tuple_size)
869239281Sgonzo			goto error;
870239281Sgonzo		if ((len / tuple_size) != 3)
871239281Sgonzo			goto error;
872239281Sgonzo
873239281Sgonzo		phyconf_ptr = phyconf;
874239281Sgonzo		for (i = 0; i < 3; i++) {
875239281Sgonzo			isc->port_mode[i] = fdt32_to_cpu(*phyconf_ptr);
876239281Sgonzo			isc->phy_reset[i] = fdt32_to_cpu(*(phyconf_ptr + 1));
877239281Sgonzo			isc->reset_gpio_pin[i] = fdt32_to_cpu(*(phyconf_ptr + 2));
878239281Sgonzo
879239281Sgonzo			phyconf_ptr += 3;
880239281Sgonzo		}
881239281Sgonzo	}
882239281Sgonzo
883239281Sgonzo	/* Initialise the ECHI registers */
884239281Sgonzo	err = omap_ehci_init(isc);
885239281Sgonzo	if (err) {
886239281Sgonzo		device_printf(dev, "Error: could not setup OMAP EHCI, %d\n", err);
887239281Sgonzo		goto error;
888239281Sgonzo	}
889239281Sgonzo
890239281Sgonzo
891239281Sgonzo	/* Set the tag and size of the register set in the EHCI context */
892239281Sgonzo	sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
893239281Sgonzo	sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
894239281Sgonzo	sc->sc_io_size = rman_get_size(sc->sc_io_res);
895239281Sgonzo
896239281Sgonzo
897239281Sgonzo	/* Setup the interrupt */
898239281Sgonzo	err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
899239281Sgonzo						 NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl);
900239281Sgonzo	if (err) {
901239281Sgonzo		device_printf(dev, "Error: could not setup irq, %d\n", err);
902239281Sgonzo		sc->sc_intr_hdl = NULL;
903239281Sgonzo		goto error;
904239281Sgonzo	}
905239281Sgonzo
906239281Sgonzo
907239281Sgonzo	/* Finally we are ready to kick off the ECHI host controller */
908239281Sgonzo	err = ehci_init(sc);
909239281Sgonzo	if (err == 0) {
910239281Sgonzo		err = device_probe_and_attach(sc->sc_bus.bdev);
911239281Sgonzo	}
912239281Sgonzo	if (err) {
913239281Sgonzo		device_printf(dev, "Error: USB init failed err=%d\n", err);
914239281Sgonzo		goto error;
915239281Sgonzo	}
916239281Sgonzo
917239281Sgonzo	return (0);
918239281Sgonzo
919239281Sgonzoerror:
920239281Sgonzo	omap_ehci_detach(dev);
921239281Sgonzo	return (ENXIO);
922239281Sgonzo}
923239281Sgonzo
924239281Sgonzo/**
925239281Sgonzo *	omap_ehci_detach - detach the device and cleanup the driver
926239281Sgonzo *	@dev: device handle
927239281Sgonzo *
928239281Sgonzo *	Clean-up routine where everything initialised in omap_ehci_attach is
929239281Sgonzo *	freed and cleaned up.  This function calls omap_ehci_fini() to shutdown
930239281Sgonzo *	the on-chip module.
931239281Sgonzo *
932239281Sgonzo *	LOCKING:
933239281Sgonzo *	none
934239281Sgonzo *
935239281Sgonzo *	RETURNS:
936239281Sgonzo *	Always returns 0 (success).
937239281Sgonzo */
938239281Sgonzostatic int
939239281Sgonzoomap_ehci_detach(device_t dev)
940239281Sgonzo{
941239281Sgonzo	struct omap_ehci_softc *isc = device_get_softc(dev);
942239281Sgonzo	ehci_softc_t *sc = &isc->base;
943239281Sgonzo	device_t bdev;
944239281Sgonzo	int err;
945239281Sgonzo
946239281Sgonzo	if (sc->sc_bus.bdev) {
947239281Sgonzo		bdev = sc->sc_bus.bdev;
948239281Sgonzo		device_detach(bdev);
949239281Sgonzo		device_delete_child(dev, bdev);
950239281Sgonzo	}
951239281Sgonzo
952239281Sgonzo	/* during module unload there are lots of children leftover */
953239281Sgonzo	device_delete_children(dev);
954239281Sgonzo
955239281Sgonzo	/*
956239281Sgonzo	 * disable interrupts that might have been switched on in ehci_init
957239281Sgonzo	 */
958239281Sgonzo	if (sc->sc_io_res) {
959239281Sgonzo		EWRITE4(sc, EHCI_USBINTR, 0);
960239281Sgonzo	}
961239281Sgonzo
962239281Sgonzo	if (sc->sc_irq_res && sc->sc_intr_hdl) {
963239281Sgonzo		/*
964239281Sgonzo		 * only call ehci_detach() after ehci_init()
965239281Sgonzo		 */
966239281Sgonzo		ehci_detach(sc);
967239281Sgonzo
968239281Sgonzo		err = bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl);
969239281Sgonzo		if (err)
970239281Sgonzo			device_printf(dev, "Error: could not tear down irq, %d\n", err);
971239281Sgonzo		sc->sc_intr_hdl = NULL;
972239281Sgonzo	}
973239281Sgonzo
974239281Sgonzo	/* Free the resources stored in the base EHCI handler */
975239281Sgonzo	if (sc->sc_irq_res) {
976239281Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
977239281Sgonzo		sc->sc_irq_res = NULL;
978239281Sgonzo	}
979239281Sgonzo	if (sc->sc_io_res) {
980239281Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res);
981239281Sgonzo		sc->sc_io_res = NULL;
982239281Sgonzo	}
983239281Sgonzo
984239281Sgonzo	/* Release the other register set memory maps */
985239281Sgonzo	if (isc->tll_mem_res) {
986239281Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->tll_mem_res);
987239281Sgonzo		isc->tll_mem_res = NULL;
988239281Sgonzo	}
989239281Sgonzo	if (isc->uhh_mem_res) {
990239281Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, isc->uhh_mem_res);
991239281Sgonzo		isc->uhh_mem_res = NULL;
992239281Sgonzo	}
993239281Sgonzo
994239281Sgonzo	usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
995239281Sgonzo
996239281Sgonzo	omap_ehci_fini(isc);
997239281Sgonzo
998239281Sgonzo	return (0);
999239281Sgonzo}
1000239281Sgonzo
1001239281Sgonzostatic device_method_t ehci_methods[] = {
1002239281Sgonzo	/* Device interface */
1003239281Sgonzo	DEVMETHOD(device_probe, omap_ehci_probe),
1004239281Sgonzo	DEVMETHOD(device_attach, omap_ehci_attach),
1005239281Sgonzo	DEVMETHOD(device_detach, omap_ehci_detach),
1006239281Sgonzo	DEVMETHOD(device_suspend, omap_ehci_suspend),
1007239281Sgonzo	DEVMETHOD(device_resume, omap_ehci_resume),
1008239281Sgonzo	DEVMETHOD(device_shutdown, omap_ehci_shutdown),
1009239281Sgonzo
1010239281Sgonzo	/* Bus interface */
1011239281Sgonzo	DEVMETHOD(bus_print_child, bus_generic_print_child),
1012239281Sgonzo
1013239281Sgonzo	{0, 0}
1014239281Sgonzo};
1015239281Sgonzo
1016239281Sgonzostatic driver_t ehci_driver = {
1017239281Sgonzo	"ehci",
1018239281Sgonzo	ehci_methods,
1019239281Sgonzo	sizeof(struct omap_ehci_softc),
1020239281Sgonzo};
1021239281Sgonzo
1022239281Sgonzostatic devclass_t ehci_devclass;
1023239281Sgonzo
1024239281SgonzoDRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
1025