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