/* $OpenBSD: omohci.c,v 1.1 2009/05/08 03:13:26 drahn Exp $ */ /* * Copyright (c) 2005 David Gwynne * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HOSTUEADDR 0x0E0 #define HOSTUESTATUS 0x0E4 #define HOSTTIMEOUTCTRL 0x0E8 #define HOSTREVISION 0x0EC #define WHM_REVID_REGISTER 0x0F4 #define WHM_TEST_OBSV 0x0F8 #define WHM_TEST_CTL 0x0FC #define HHC_TEST_CFG 0x100 #define HHC_TEST_CTL 0x104 #define HHC_TEST_OBSV 0x108 #define REVDEV 0x200 /* 16 bit */ #define EP_NUM 0x204 /* 16 bit */ #define DATA 0x208 /* 16 bit */ #define CTRL 0x20C /* 16 bit */ #define STAT_FLG 0x210 /* 16 bit */ #define RXFSTAT 0x214 /* 16 bit */ #define SYSCON1 0x218 /* 16 bit */ #define SYSCON2 0x21C /* 16 bit */ #define DEVSTAT 0x220 /* 16 bit */ #define SOFREG 0x224 /* 16 bit */ #define IRQ_EN 0x228 /* 16 bit */ #define DMA_IRQ_EN 0x22C /* 16 bit */ #define IRQ_SRC 0x230 /* 16 bit */ #define EPN_STAT 0x234 /* 16 bit */ #define DMAN_STAT 0x238 /* 16 bit */ #define RXDMA_CFG 0x240 /* 16 bit */ #define TXDMA_CFG 0x244 /* 16 bit */ #define TXDMA0 0x250 #define TXDMA1 0x254 #define TXDMA2 0x258 #define RXDMA0 0x260 #define RXDMA1 0x264 #define RXDMA2 0x268 #define EP0 0x280 #define EP_RX(x) 0x280 + (x * 4) #define EP_TX(x) 0x2C0 + (x * 4) #define OTG_REV 0x300 #define OTG_SYSCON_1 0x304 #define OTG_SYSCON_2 0x308 #define OTG_SYSCON2_OTG_EN 0x80000000 #define OTG_SYSCON2_UHOST_EN 0x00000100 #define OTG_SYSCON2_MODE_DISABLED 0x00000000 #define OTG_SYSCON2_MODE_CLIENT 0x00000001 #define OTG_SYSCON2_MODE_HOST 0x00000004 #define OTG_CTRL 0x30C #if 0 #define OTG_IRQ_EN 0x310 /* 16 bit */ #define OTG_IRQ_SRC 0x314 /* 16 bit */ #define OTG_OUTCTRL 0x318 /* 16 bit */ #define OTG_TEST 0x320 /* 16 bit */ #endif #define OTG_VC 0x3FC int omohci_match(struct device *, void *, void *); void omohci_attach(struct device *, struct device *, void *); int omohci_detach(struct device *, int); void omohci_power(int, void *); struct omohci_softc { ohci_softc_t sc; void *sc_ihc0; void *sc_ihc1; void *sc_ihc2; void *sc_ih0; void *sc_ih1; void *sc_ihotg; }; void omohci_enable(struct omohci_softc *); void omohci_disable(struct omohci_softc *); struct cfattach omohci_ca = { sizeof (struct omohci_softc), omohci_match, omohci_attach, omohci_detach, ohci_activate }; int omohci_match(struct device *parent, void *match, void *aux) { #if 0 if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) != CPU_ID_PXA27X) return (0); #endif return (1); } void omohci_attach(struct device *parent, struct device *self, void *aux) { struct omohci_softc *sc = (struct omohci_softc *)self; struct ahb_attach_args *aa = aux; usbd_status r; sc->sc.iot = aa->aa_iot; sc->sc.sc_bus.dmatag = aa->aa_dmat; sc->sc_ih0 = NULL; sc->sc_ih1 = NULL; sc->sc_ihc0 = NULL; sc->sc_ihc1 = NULL; sc->sc_ihc2 = NULL; sc->sc_ihotg = NULL; sc->sc.sc_size = 0; /* Map I/O space */ if (bus_space_map(sc->sc.iot, aa->aa_addr, aa->aa_size, 0, &sc->sc.ioh)) { printf(": cannot map mem space\n"); return; } sc->sc.sc_size = aa->aa_size; /* XXX copied from ohci_pci.c. needed? */ bus_space_barrier(sc->sc.iot, sc->sc.ioh, 0, sc->sc.sc_size, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE); #if 0 /* start the usb clock */ pxa2x0_clkman_config(CKEN_USBHC, 1); #endif omohci_enable(sc); /* Disable interrupts, so we don't get any spurious ones. */ bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE, OHCI_MIE); sc->sc_ihc0 = intc_intr_establish(aa->aa_intr, IPL_USB, ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); sc->sc_ihc1 = intc_intr_establish(aa->aa_intr+1, IPL_USB, ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); sc->sc_ihc2 = intc_intr_establish(aa->aa_intr+2, IPL_USB, ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); sc->sc_ih0 = intc_intr_establish(aa->aa_intr+3, IPL_USB, ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); sc->sc_ih1 = intc_intr_establish(aa->aa_intr+4, IPL_USB, ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); sc->sc_ihotg = intc_intr_establish(aa->aa_intr+5, IPL_USB, ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname); if (sc->sc_ih0 == NULL || sc->sc_ih1 == NULL || sc->sc_ihc0 == NULL || sc->sc_ihc1 == NULL || sc->sc_ihc2 == NULL || sc->sc_ihotg == NULL) { printf(": unable to establish interrupt\n"); omohci_disable(sc); #if 0 pxa2x0_clkman_config(CKEN_USBHC, 0); #endif bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); sc->sc.sc_size = 0; return; } prcm_enableclock(PRCM_CLK_EN_USB); bus_space_write_4(sc->sc.iot, sc->sc.ioh, OTG_SYSCON_2, OTG_SYSCON2_UHOST_EN | OTG_SYSCON2_MODE_HOST); strlcpy(sc->sc.sc_vendor, "OMAP24xx", sizeof(sc->sc.sc_vendor)); r = ohci_init(&sc->sc); if (r != USBD_NORMAL_COMPLETION) { printf("%s: init failed, error=%d\n", sc->sc.sc_bus.bdev.dv_xname, r); intc_intr_disestablish(sc->sc_ih0); intc_intr_disestablish(sc->sc_ih1); intc_intr_disestablish(sc->sc_ihc0); intc_intr_disestablish(sc->sc_ihc1); intc_intr_disestablish(sc->sc_ihc2); intc_intr_disestablish(sc->sc_ihotg); sc->sc_ih0 = NULL; sc->sc_ih1 = NULL; sc->sc_ihc0 = NULL; sc->sc_ihc1 = NULL; sc->sc_ihc2 = NULL; sc->sc_ihotg = NULL; omohci_disable(sc); #if 0 pxa2x0_clkman_config(CKEN_USBHC, 0); #endif bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); sc->sc.sc_size = 0; return; } sc->sc.sc_powerhook = powerhook_establish(omohci_power, sc); if (sc->sc.sc_powerhook == NULL) printf("%s: cannot establish powerhook\n", sc->sc.sc_bus.bdev.dv_xname); sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus, usbctlprint); } int omohci_detach(struct device *self, int flags) { struct omohci_softc *sc = (struct omohci_softc *)self; int rv; rv = ohci_detach(&sc->sc, flags); if (rv) return (rv); if (sc->sc.sc_powerhook != NULL) { powerhook_disestablish(sc->sc.sc_powerhook); sc->sc.sc_powerhook = NULL; } if (sc->sc_ih0 != NULL) { intc_intr_disestablish(sc->sc_ih0); intc_intr_disestablish(sc->sc_ih1); intc_intr_disestablish(sc->sc_ihc0); intc_intr_disestablish(sc->sc_ihc1); intc_intr_disestablish(sc->sc_ihc2); intc_intr_disestablish(sc->sc_ihotg); sc->sc_ih0 = NULL; sc->sc_ih1 = NULL; sc->sc_ihc0 = NULL; sc->sc_ihc1 = NULL; sc->sc_ihc2 = NULL; sc->sc_ihotg = NULL; } omohci_disable(sc); /* stop clock */ #if 0 pxa2x0_clkman_config(CKEN_USBHC, 0); #endif if (sc->sc.sc_size) { bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); sc->sc.sc_size = 0; } return (0); } void omohci_power(int why, void *arg) { struct omohci_softc *sc = (struct omohci_softc *)arg; int s; s = splhardusb(); sc->sc.sc_bus.use_polling++; switch (why) { case PWR_STANDBY: case PWR_SUSPEND: ohci_power(why, &sc->sc); #if 0 pxa2x0_clkman_config(CKEN_USBHC, 0); #endif break; case PWR_RESUME: #if 0 pxa2x0_clkman_config(CKEN_USBHC, 1); #endif omohci_enable(sc); ohci_power(why, &sc->sc); break; } sc->sc.sc_bus.use_polling--; splx(s); } void omohci_enable(struct omohci_softc *sc) { #if 0 u_int32_t hr; /* Full host reset */ hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, (hr & USBHC_HR_MASK) | USBHC_HR_FHR); DELAY(USBHC_RST_WAIT); hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, (hr & USBHC_HR_MASK) & ~(USBHC_HR_FHR)); /* Force system bus interface reset */ hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, (hr & USBHC_HR_MASK) | USBHC_HR_FSBIR); while (bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR) & \ USBHC_HR_FSBIR) DELAY(3); /* Enable the ports (physically only one, only enable that one?) */ hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, (hr & USBHC_HR_MASK) & ~(USBHC_HR_SSE)); hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, (hr & USBHC_HR_MASK) & ~(USBHC_HR_SSEP2)); #endif } void omohci_disable(struct omohci_softc *sc) { #if 0 u_int32_t hr; /* Full host reset */ hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, (hr & USBHC_HR_MASK) | USBHC_HR_FHR); DELAY(USBHC_RST_WAIT); hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR); bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR, (hr & USBHC_HR_MASK) & ~(USBHC_HR_FHR)); #endif }