1/*	$Id: obio_ohci.c,v 1.6 2012/02/13 17:34:21 matt Exp $	*/
2
3/* adapted from: */
4/*	$NetBSD: obio_ohci.c,v 1.5 2011/07/01 20:30:21 dyoung Exp $	*/
5/*	$OpenBSD: pxa2x0_ohci.c,v 1.19 2005/04/08 02:32:54 dlg Exp $ */
6
7/*
8 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
9 *
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include "opt_omap.h"
24
25#include <sys/cdefs.h>
26__KERNEL_RCSID(0, "$NetBSD: obio_ohci.c,v 1.5 2011/07/01 20:30:21 dyoung Exp $");
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/kernel.h>
31#include <sys/intr.h>
32#include <sys/bus.h>
33#include <sys/device.h>
34#include <sys/kernel.h>
35
36#include <dev/usb/usb.h>
37#include <dev/usb/usbdi.h>
38#include <dev/usb/usbdivar.h>
39#include <dev/usb/usb_mem.h>
40
41#include <dev/usb/ohcireg.h>
42#include <dev/usb/ohcivar.h>
43
44#include <arm/omap/omap2_reg.h>
45#include <arm/omap/omap2_obiovar.h>
46#include <arm/omap/omap2_obioreg.h>
47
48
49struct obioohci_softc {
50	ohci_softc_t	sc;
51
52	void		*sc_ih;
53	bus_addr_t	sc_addr;
54	bus_addr_t	sc_size;
55	void		*sc_powerhook;
56};
57
58static int	obioohci_match(struct device *, struct cfdata *, void *);
59static void	obioohci_attach(struct device *, struct device *, void *);
60static int	obioohci_detach(struct device *, int);
61void *		obioohci_fake_intr_establish(int (*)(void *), void *);
62void		obioohci_fake_intr(void);
63
64CFATTACH_DECL_NEW(obioohci, sizeof(struct obioohci_softc),
65    obioohci_match, obioohci_attach, obioohci_detach, ohci_activate);
66
67static void	obioohci_clkinit(struct obio_attach_args *);
68static void	obioohci_power(int, void *);
69static void	obioohci_enable(struct obioohci_softc *);
70static void	obioohci_disable(struct obioohci_softc *);
71
72#define	HREAD4(sc,r)	bus_space_read_4((sc)->sc.iot, (sc)->sc.ioh, (r))
73#define	HWRITE4(sc,r,v)	bus_space_write_4((sc)->sc.iot, (sc)->sc.ioh, (r), (v))
74
75static int
76obioohci_match(device_t parent, cfdata_t cf, void *aux)
77{
78
79	struct obio_attach_args *obio = aux;
80
81	if ((obio->obio_addr == -1)
82	|| (obio->obio_size == 0)
83	|| (obio->obio_intr == -1))
84		panic("obioohci must have addr, size and intr"
85			" specified in config.");
86
87	obioohci_clkinit(obio);
88
89	return 1;	/* XXX */
90}
91
92static void
93obioohci_attach(device_t parent, device_t self, void *aux)
94{
95	struct obioohci_softc *sc = device_private(self);
96	struct obio_attach_args *obio = aux;
97	usbd_status r;
98
99	sc->sc.sc_size = 0;
100	sc->sc_ih = NULL;
101	sc->sc.sc_bus.dmatag = 0;
102
103	/* Map I/O space */
104	if (bus_space_map(obio->obio_iot, obio->obio_addr, obio->obio_size, 0,
105	    &sc->sc.ioh)) {
106		aprint_error(": couldn't map memory space\n");
107		return;
108	}
109	sc->sc.iot = obio->obio_iot;
110	sc->sc_addr = obio->obio_addr;
111	sc->sc.sc_size = obio->obio_size;
112	sc->sc.sc_bus.dmatag = obio->obio_dmat;
113
114	/* XXX copied from ohci_pci.c. needed? */
115	bus_space_barrier(sc->sc.iot, sc->sc.ioh, 0, sc->sc.sc_size,
116	    BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
117
118	/* start the usb clock */
119#ifdef NOTYET
120	pxa2x0_clkman_config(CKEN_USBHC, 1);
121#endif
122	obioohci_enable(sc);
123
124	/* Disable interrupts, so we don't get any spurious ones. */
125	bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE,
126	    OHCI_MIE);
127
128#if 1
129	sc->sc_ih = intr_establish(obio->obio_intr, IPL_USB, IST_LEVEL,
130		ohci_intr, &sc->sc);
131	if (sc->sc_ih == NULL) {
132		aprint_error(": unable to establish interrupt\n");
133		goto free_map;
134	}
135#else
136	sc->sc_ih = obioohci_fake_intr_establish(ohci_intr, &sc->sc);
137#endif
138
139	strlcpy(sc->sc.sc_vendor, "OMAP2", sizeof(sc->sc.sc_vendor));
140	r = ohci_init(&sc->sc);
141	if (r != USBD_NORMAL_COMPLETION) {
142		aprint_error_dev(self, "init failed, error=%d\n", r);
143		goto free_intr;
144	}
145
146	sc->sc_powerhook = powerhook_establish(device_xname(self),
147	    obioohci_power, sc);
148	if (sc->sc_powerhook == NULL) {
149		aprint_error_dev(self, "cannot establish powerhook\n");
150	}
151
152	sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus, usbctlprint);
153
154	return;
155
156free_intr:
157#ifdef NOTYET
158	obio_gpio_intr_disestablish(sc->sc_ih);
159#endif
160	sc->sc_ih = NULL;
161free_map:
162	obioohci_disable(sc);
163#ifdef NOTYET
164	pxa2x0_clkman_config(CKEN_USBHC, 0);
165#endif
166	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
167	sc->sc.sc_size = 0;
168}
169
170static int
171obioohci_detach(device_t self, int flags)
172{
173	struct obioohci_softc *sc = device_private(self);
174	int error;
175
176	error = ohci_detach(&sc->sc, flags);
177	if (error)
178		return error;
179
180	if (sc->sc_powerhook) {
181		powerhook_disestablish(sc->sc_powerhook);
182		sc->sc_powerhook = NULL;
183	}
184
185	if (sc->sc_ih) {
186#ifdef NOTYET
187		obio_gpio_intr_disestablish(sc->sc_ih);
188#endif
189		sc->sc_ih = NULL;
190	}
191
192	obioohci_disable(sc);
193
194#ifdef NOTYET
195	/* stop clock */
196#ifdef DEBUG
197	pxa2x0_clkman_config(CKEN_USBHC, 0);
198#endif
199#endif
200
201	if (sc->sc.sc_size) {
202		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
203		sc->sc.sc_size = 0;
204	}
205
206	return 0;
207}
208
209static void
210obioohci_power(int why, void *arg)
211{
212	struct obioohci_softc *sc = (struct obioohci_softc *)arg;
213	int s;
214
215	s = splhardusb();
216	sc->sc.sc_bus.use_polling++;
217	switch (why) {
218	case PWR_STANDBY:
219	case PWR_SUSPEND:
220#if 0
221		ohci_power(why, &sc->sc);
222#endif
223#ifdef NOTYET
224		pxa2x0_clkman_config(CKEN_USBHC, 0);
225#endif
226		break;
227
228	case PWR_RESUME:
229#ifdef NOTYET
230		pxa2x0_clkman_config(CKEN_USBHC, 1);
231#endif
232		obioohci_enable(sc);
233#if 0
234		ohci_power(why, &sc->sc);
235#endif
236		break;
237	}
238	sc->sc.sc_bus.use_polling--;
239	splx(s);
240}
241
242static void
243obioohci_enable(struct obioohci_softc *sc)
244{
245	printf("%s: TBD\n", __func__);
246}
247
248static void
249obioohci_disable(struct obioohci_softc *sc)
250{
251#ifdef NOTYET
252	uint32_t hr;
253
254	/* Full host reset */
255	hr = HREAD4(sc, USBHC_HR);
256	HWRITE4(sc, USBHC_HR, (hr & USBHC_HR_MASK) | USBHC_HR_FHR);
257
258	DELAY(USBHC_RST_WAIT);
259
260	hr = HREAD4(sc, USBHC_HR);
261	HWRITE4(sc, USBHC_HR, (hr & USBHC_HR_MASK) & ~(USBHC_HR_FHR));
262#endif
263}
264
265static void
266obioohci_clkinit(struct obio_attach_args *obio)
267{
268
269	bus_space_handle_t ioh;
270	uint32_t r;
271	int err;
272
273	err = bus_space_map(obio->obio_iot, OMAP2_CM_BASE,
274		OMAP2_CM_SIZE, 0, &ioh);
275	if (err != 0)
276		panic("%s: cannot map OMAP2_CM_BASE at %#x, error %d\n",
277			__func__, OMAP2_CM_BASE, err);
278
279	r = bus_space_read_4(obio->obio_iot, ioh, OMAP2_CM_FCLKEN2_CORE);
280	r |= OMAP2_CM_FCLKEN2_CORE_EN_USB;
281	bus_space_write_4(obio->obio_iot, ioh, OMAP2_CM_FCLKEN2_CORE, r);
282
283	r = bus_space_read_4(obio->obio_iot, ioh, OMAP2_CM_ICLKEN2_CORE);
284	r |= OMAP2_CM_ICLKEN2_CORE_EN_USB;
285	r &= ~OMAP2_CM_ICLKEN2_CORE_EN_USBHS;	/* force FS for ohci */
286	bus_space_write_4(obio->obio_iot, ioh, OMAP2_CM_ICLKEN2_CORE, r);
287
288	bus_space_unmap(obio->obio_iot, ioh, OMAP2_CM_SIZE);
289}
290
291#if 0
292int (*obioohci_fake_intr_func)(void *);
293void *obioohci_fake_intr_arg;
294
295void *
296obioohci_fake_intr_establish(int (*func)(void *), void *arg)
297{
298	obioohci_fake_intr_func = func;
299	obioohci_fake_intr_arg = arg;
300	return (void *)1;
301}
302
303void
304obioohci_fake_intr(void)
305{
306	(void)(*obioohci_fake_intr_func)(obioohci_fake_intr_arg);
307}
308#endif
309