diff options
Diffstat (limited to 'sys/arch/arm/xscale')
-rw-r--r-- | sys/arch/arm/xscale/files.pxa2x0 | 4 | ||||
-rw-r--r-- | sys/arch/arm/xscale/pxa27x_udc.c | 817 | ||||
-rw-r--r-- | sys/arch/arm/xscale/pxa27x_udcreg.h | 21 |
3 files changed, 795 insertions, 47 deletions
diff --git a/sys/arch/arm/xscale/files.pxa2x0 b/sys/arch/arm/xscale/files.pxa2x0 index d37eb8edc0a..ec2b493eb36 100644 --- a/sys/arch/arm/xscale/files.pxa2x0 +++ b/sys/arch/arm/xscale/files.pxa2x0 @@ -1,4 +1,4 @@ -# $OpenBSD: files.pxa2x0,v 1.18 2005/12/20 02:37:09 drahn Exp $ +# $OpenBSD: files.pxa2x0,v 1.19 2006/11/25 18:10:29 uwe Exp $ # $NetBSD: files.pxa2x0,v 1.6 2004/05/01 19:09:14 thorpej Exp $ # # Configuration info for Intel PXA2[51]0 CPU support @@ -46,7 +46,7 @@ file arch/arm/xscale/pxa2x0_a4x_space.c com_pxaip file arch/arm/xscale/pxa2x0_a4x_io.S com_pxaip # PXA27x USB Device Controller -device pxaudc +device pxaudc: usbdev attach pxaudc at pxaip file arch/arm/xscale/pxa27x_udc.c pxaudc diff --git a/sys/arch/arm/xscale/pxa27x_udc.c b/sys/arch/arm/xscale/pxa27x_udc.c index 94f843422e2..e802860af51 100644 --- a/sys/arch/arm/xscale/pxa27x_udc.c +++ b/sys/arch/arm/xscale/pxa27x_udc.c @@ -1,7 +1,8 @@ -/* $OpenBSD: pxa27x_udc.c,v 1.5 2005/03/30 14:24:39 dlg Exp $ */ +/* $OpenBSD: pxa27x_udc.c,v 1.6 2006/11/25 18:10:29 uwe Exp $ */ /* * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> + * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,31 +21,98 @@ #include <sys/systm.h> #include <sys/device.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <machine/intr.h> #include <machine/bus.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usbf.h> +#include <dev/usb/usbfvar.h> + #include <arm/xscale/pxa2x0reg.h> #include <arm/xscale/pxa2x0var.h> #include <arm/xscale/pxa2x0_gpio.h> #include <arm/xscale/pxa27x_udcreg.h> +#define PXAUDC_EP0MAXP 16 /* XXX */ +#define PXAUDC_NEP 24 /* total number of endpoints */ -int pxaudc_match(struct device *, void *, void *); -void pxaudc_attach(struct device *, struct device *, void *); -int pxaudc_detach(struct device *, int); -void pxaudc_power(int, void *); +#include <machine/zaurus_reg.h> /* XXX */ -struct pxaudc_softc { - struct device sc_dev; - bus_space_tag_t sc_iot; - bus_space_handle_t sc_ioh; - bus_size_t sc_size; +struct pxaudc_xfer { + struct usbf_xfer xfer; + u_int16_t frmlen; +}; +struct pxaudc_pipe { + struct usbf_pipe pipe; + LIST_ENTRY(pxaudc_pipe) list; +}; + +struct pxaudc_softc { + struct usbf_bus sc_bus; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_size; + void *sc_ih; + void *sc_conn_ih; void *sc_powerhook; + SIMPLEQ_HEAD(,usbf_xfer) sc_free_xfers; /* recycled xfers */ + u_int32_t sc_icr0; /* enabled EP interrupts */ + u_int32_t sc_icr1; /* enabled EP interrupts */ + enum { + EP0_SETUP, + EP0_IN + } sc_ep0state; + u_int32_t sc_isr0; /* XXX deferred interrupts */ + u_int32_t sc_isr1; /* XXX deferred interrupts */ + u_int32_t sc_otgisr; /* XXX deferred interrupts */ + struct pxaudc_pipe *sc_pipe[PXAUDC_NEP]; }; -void pxaudc_enable(struct pxaudc_softc *); +int pxaudc_match(struct device *, void *, void *); +void pxaudc_attach(struct device *, struct device *, void *); +int pxaudc_detach(struct device *, int); +void pxaudc_power(int, void *); + +int pxaudc_is_host(void); +int pxaudc_is_device(void); +void pxaudc_setup(struct pxaudc_softc *); +void pxaudc_hide(struct pxaudc_softc *); +void pxaudc_show(struct pxaudc_softc *); + +void pxaudc_enable(struct pxaudc_softc *); +void pxaudc_disable(struct pxaudc_softc *); +void pxaudc_read_ep0(struct pxaudc_softc *, usbf_xfer_handle); +void pxaudc_write_ep0(struct pxaudc_softc *, usbf_xfer_handle); +void pxaudc_write(struct pxaudc_softc *, usbf_xfer_handle); + +int pxaudc_connect_intr(void *); +int pxaudc_intr(void *); +void pxaudc_intr1(struct pxaudc_softc *); +void pxaudc_ep0_intr(struct pxaudc_softc *); + +usbf_status pxaudc_open(struct usbf_pipe *); +void pxaudc_softintr(void *); +usbf_status pxaudc_allocm(struct usbf_bus *, usb_dma_t *, u_int32_t); +void pxaudc_freem(struct usbf_bus *, usb_dma_t *); +usbf_xfer_handle pxaudc_allocx(struct usbf_bus *); +void pxaudc_freex(struct usbf_bus *, usbf_xfer_handle); + +usbf_status pxaudc_ctrl_transfer(usbf_xfer_handle); +usbf_status pxaudc_ctrl_start(usbf_xfer_handle); +void pxaudc_ctrl_abort(usbf_xfer_handle); +void pxaudc_ctrl_done(usbf_xfer_handle); +void pxaudc_ctrl_close(usbf_pipe_handle); + +usbf_status pxaudc_bulk_transfer(usbf_xfer_handle); +usbf_status pxaudc_bulk_start(usbf_xfer_handle); +void pxaudc_bulk_abort(usbf_xfer_handle); +void pxaudc_bulk_done(usbf_xfer_handle); +void pxaudc_bulk_close(usbf_pipe_handle); struct cfattach pxaudc_ca = { sizeof(struct pxaudc_softc), pxaudc_match, pxaudc_attach, @@ -55,6 +123,52 @@ struct cfdriver pxaudc_cd = { NULL, "pxaudc", DV_DULL }; +struct usbf_bus_methods pxaudc_bus_methods = { + pxaudc_open, + pxaudc_softintr, + pxaudc_allocm, + pxaudc_freem, + pxaudc_allocx, + pxaudc_freex +}; + +struct usbf_pipe_methods pxaudc_ctrl_methods = { + pxaudc_ctrl_transfer, + pxaudc_ctrl_start, + pxaudc_ctrl_abort, + pxaudc_ctrl_done, + pxaudc_ctrl_close +}; + +struct usbf_pipe_methods pxaudc_bulk_methods = { + pxaudc_bulk_transfer, + pxaudc_bulk_start, + pxaudc_bulk_abort, + pxaudc_bulk_done, + pxaudc_bulk_close +}; + +#define DEVNAME(sc) USBDEVNAME((sc)->sc_bus.bdev) + +#define CSR_READ_4(sc, reg) \ + bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) +#define CSR_WRITE_4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define CSR_WRITE_1(sc, reg, val) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define CSR_SET_4(sc, reg, val) \ + CSR_WRITE_4((sc), (reg), CSR_READ_4((sc), (reg)) | (val)) +#define CSR_CLR_4(sc, reg, val) \ + CSR_WRITE_4((sc), (reg), CSR_READ_4((sc), (reg)) & ~(val)) + +#define PXAUDC_DEBUG +#ifndef PXAUDC_DEBUG +#define DPRINTF(l, x) do {} while (0) +#else +int pxaudcdebug = 0; +#define DPRINTF(l, x) if ((l) <= pxaudcdebug) printf x; else {} +#endif + int pxaudc_match(struct device *parent, void *match, void *aux) { @@ -71,9 +185,6 @@ pxaudc_attach(struct device *parent, struct device *self, void *aux) struct pxaip_attach_args *pxa = aux; sc->sc_iot = pxa->pxa_iot; - sc->sc_size = 0; - sc->sc_powerhook = NULL; - if (bus_space_map(sc->sc_iot, PXA2X0_USBDC_BASE, PXA2X0_USBDC_SIZE, 0, &sc->sc_ioh)) { printf(": cannot map mem space\n"); @@ -86,18 +197,51 @@ pxaudc_attach(struct device *parent, struct device *self, void *aux) bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, sc->sc_size, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE); - pxa2x0_gpio_set_function(35, GPIO_ALT_FN_2_IN); /* USB_P2_1 */ - pxa2x0_gpio_set_function(37, GPIO_ALT_FN_1_OUT); /* USB_P2_8 */ - pxa2x0_gpio_set_function(41, GPIO_ALT_FN_2_IN); /* USB_P2_7 */ - pxa2x0_gpio_set_function(89, GPIO_ALT_FN_2_OUT); /* USBHPEN<1> */ - pxa2x0_gpio_set_function(120, GPIO_ALT_FN_2_OUT); /* USBHPEN<2> */ + /* Set up GPIO pins and disable the controller. */ + pxaudc_setup(sc); + pxaudc_disable(sc); - pxa2x0_clkman_config(CKEN_USBDC, 0); - pxaudc_enable(sc); + /* Establish USB device interrupt. */ + sc->sc_ih = pxa2x0_intr_establish(PXA2X0_INT_USB, IPL_USB, + pxaudc_intr, sc, DEVNAME(sc)); + if (sc->sc_ih == NULL) { + printf(": unable to establish interrupt\n"); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); + sc->sc_size = 0; + return; + } - pxa2x0_gpio_set_bit(37); /* USB_P2_8 */ + /* Establish device connect interrupt. */ +#if 0 + sc->sc_conn_ih = pxa2x0_gpio_intr_establish(C3000_USB_DEVICE_PIN, /* XXX */ + IST_EDGE_BOTH, IPL_USB, pxaudc_connect_intr, sc, "usbc"); +#endif + sc->sc_conn_ih = pxa2x0_gpio_intr_establish(C3000_USB_CONNECT_PIN, + IST_EDGE_BOTH, IPL_USB, pxaudc_connect_intr, sc, "usbc"); + if (sc->sc_conn_ih == NULL) { + printf(": unable to establish connect interrupt\n"); + pxa2x0_intr_disestablish(sc->sc_ih); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); + sc->sc_ioh = NULL; + sc->sc_size = 0; + return; + } sc->sc_powerhook = powerhook_establish(pxaudc_power, sc); + + /* Set up the bus struct. */ + sc->sc_bus.methods = &pxaudc_bus_methods; + sc->sc_bus.pipe_size = sizeof(struct pxaudc_pipe); + sc->sc_bus.ep0_maxp = PXAUDC_EP0MAXP; + sc->sc_bus.usbrev = USBREV_1_1; + sc->sc_bus.dmatag = pxa->pxa_dmat; + + /* Attach logical device and function. */ + (void)config_found(self, &sc->sc_bus, NULL); + + /* Enable the controller unless we're now acting as a host. */ + if (!pxaudc_is_host()) + pxaudc_enable(sc); } int @@ -108,6 +252,12 @@ pxaudc_detach(struct device *self, int flags) if (sc->sc_powerhook != NULL) powerhook_disestablish(sc->sc_powerhook); + if (sc->sc_conn_ih != NULL) + pxa2x0_gpio_intr_disestablish(sc->sc_conn_ih); + + if (sc->sc_ih != NULL) + pxa2x0_intr_disestablish(sc->sc_ih); + if (sc->sc_size) { bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); sc->sc_size = 0; @@ -121,32 +271,611 @@ pxaudc_power(int why, void *arg) { struct pxaudc_softc *sc = (struct pxaudc_softc *)arg; - if (why == PWR_RESUME) + switch (why) { + case PWR_SUSPEND: + case PWR_STANDBY: + pxaudc_disable(sc); + break; + + case PWR_RESUME: pxaudc_enable(sc); + break; + } +} + +/* + * Machine-specific functions + */ + +/* XXX move to machine-specific file */ + +int +pxaudc_is_host(void) +{ + return (!pxa2x0_gpio_get_bit(C3000_USB_CONNECT_PIN) && + !pxa2x0_gpio_get_bit(C3000_USB_DEVICE_PIN)); +} + +int +pxaudc_is_device(void) +{ + return (pxa2x0_gpio_get_bit(C3000_USB_CONNECT_PIN) && + pxa2x0_gpio_get_bit(C3000_USB_DEVICE_PIN)); +} + +void +pxaudc_setup(struct pxaudc_softc *sc) +{ + pxa2x0_gpio_set_function(45, GPIO_OUT); + pxa2x0_gpio_set_function(C3000_USB_CONNECT_PIN, GPIO_IN); /* 41 */ + pxa2x0_gpio_set_function(40, GPIO_OUT); + pxa2x0_gpio_set_function(39, GPIO_IN); + pxa2x0_gpio_set_function(38, GPIO_IN); + pxa2x0_gpio_set_function(37, GPIO_OUT); + pxa2x0_gpio_set_function(36, GPIO_IN); + pxa2x0_gpio_set_function(C3000_USB_DEVICE_PIN, GPIO_IN); /* 35 */ + pxa2x0_gpio_set_function(34, GPIO_IN); + pxa2x0_gpio_set_function(89, GPIO_OUT); + pxa2x0_gpio_set_function(120, GPIO_OUT); +} + +/* Hide us from the host. */ +void +pxaudc_hide(struct pxaudc_softc *sc) +{ + pxa2x0_gpio_clear_bit(C3000_USB_PULLUP_PIN); +} + +/* Show us to the host. */ +void +pxaudc_show(struct pxaudc_softc *sc) +{ + pxa2x0_gpio_set_bit(C3000_USB_PULLUP_PIN); +} + +/* + * Register manipulation + */ + +#if 0 +static void +pxaudc_dump_regs(struct pxaudc_softc *sc) +{ + printf("UDCCR\t%b\n", CSR_READ_4(sc, USBDC_UDCCR), + USBDC_UDCCR_BITS); + printf("UDCICR0\t%b\n", CSR_READ_4(sc, USBDC_UDCICR0), + USBDC_UDCISR0_BITS); + printf("UDCICR1\t%b\n", CSR_READ_4(sc, USBDC_UDCICR1), + USBDC_UDCISR1_BITS); + printf("OTGICR\t%b\n", CSR_READ_4(sc, USBDC_UDCOTGICR), + USBDC_UDCOTGISR_BITS); } +#endif void pxaudc_enable(struct pxaudc_softc *sc) { - u_int32_t hr; - - /* disable the controller */ - hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UDCCR); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UDCCR, - hr & ~USBDC_UDCCR_UDE); - - hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UDCICR1); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UDCICR1, - hr | USBDC_UDCICR1_IERS); - - bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR, 0); - hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR, - hr | USBDC_UP2OCR_HXS); - hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR, - hr | USBDC_UP2OCR_HXOE); - hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR, - hr | USBDC_UP2OCR_DPPDE|USBDC_UP2OCR_DMPDE); + DPRINTF(0,("pxaudc_enable\n")); + + /* Start the clocks. */ + pxa2x0_clkman_config(CKEN_USBDC, 1); + + /* Configure Port 2 for USB device. */ + CSR_WRITE_4(sc, USBDC_UP2OCR, USBDC_UP2OCR_DMPUE | + USBDC_UP2OCR_DPPUE | USBDC_UP2OCR_HXOE); + + /* Enable interrupts for configured endpoints. */ + CSR_SET_4(sc, USBDC_UDCICR0, USBDC_UDCICR0_IE(0) | + sc->sc_icr0); + CSR_SET_4(sc, USBDC_UDCICR1, USBDC_UDCICR1_IERS | + USBDC_UDCICR1_IECC | sc->sc_icr1); + + /* XXX */ + CSR_WRITE_4(sc, USBDC_UDCICR0, 0x00000C3F); + CSR_WRITE_4(sc, USBDC_UDCECR(1), 0x0200D103); + CSR_WRITE_4(sc, USBDC_UDCECR(2), 0x02014103); + CSR_WRITE_4(sc, USBDC_UDCECR(3), 0x0201B403); + CSR_WRITE_4(sc, USBDC_UDCECR(4), 0x02022403); + CSR_WRITE_4(sc, USBDC_UDCECR(5), 0x0202F021); + + /* Enable the controller. */ + CSR_CLR_4(sc, USBDC_UDCCR, USBDC_UDCCR_EMCE); + CSR_SET_4(sc, USBDC_UDCCR, USBDC_UDCCR_UDE); + + /* Enable USB client on port 2. */ + pxa2x0_gpio_clear_bit(37); /* USB_P2_8 */ +} + +void +pxaudc_disable(struct pxaudc_softc *sc) +{ + DPRINTF(0,("pxaudc_disable\n")); + + /* Disable the controller. */ + CSR_CLR_4(sc, USBDC_UDCCR, USBDC_UDCCR_UDE); + + /* Disable all interrupts. */ + CSR_WRITE_4(sc, USBDC_UDCICR0, 0); + CSR_WRITE_4(sc, USBDC_UDCICR1, 0); + CSR_WRITE_4(sc, USBDC_UDCOTGICR, 0); + + /* Set Port 2 output to "Non-OTG Host with Differential Port". */ + CSR_WRITE_4(sc, USBDC_UP2OCR, USBDC_UP2OCR_HXS | + USBDC_UP2OCR_HXOE); + + /* Set "Host Port 2 Transceiver D Pull Down Enable". */ + CSR_SET_4(sc, USBDC_UP2OCR, USBDC_UP2OCR_DMPDE); + + /* Stop the clocks. */ + pxa2x0_clkman_config(CKEN_USBDC, 0); + + /* Enable USB host on port 2. */ + pxa2x0_gpio_set_bit(37); /* USB_P2_8 */ +} + +/* + * Endpoint FIFO handling + */ + +void +pxaudc_read_ep0(struct pxaudc_softc *sc, usbf_xfer_handle xfer) +{ + size_t len; + u_int8_t *p; + + xfer->actlen = CSR_READ_4(sc, USBDC_UDCBCR(0)); + len = MIN(xfer->actlen, xfer->length); + p = xfer->buffer; + + while (CSR_READ_4(sc, USBDC_UDCCSR0) & USBDC_UDCCSR0_RNE) { + u_int32_t v = CSR_READ_4(sc, USBDC_UDCDR(0)); + + if (len > 0) { + if (((unsigned)p & 0x3) == 0) + *(u_int32_t *)p = v; + else { + *(p+0) = v & 0xff; + *(p+1) = (v >> 8) & 0xff; + *(p+2) = (v >> 16) & 0xff; + *(p+3) = (v >> 24) & 0xff; + } + p += 4; + len -= 4; + } + } + + CSR_WRITE_4(sc, USBDC_UDCCSR0, USBDC_UDCCSR0_SA | USBDC_UDCCSR0_OPC); + + xfer->status = USBF_NORMAL_COMPLETION; + usbf_transfer_complete(xfer); +} + +void +pxaudc_write_ep0(struct pxaudc_softc *sc, usbf_xfer_handle xfer) +{ + struct pxaudc_xfer *lxfer = (struct pxaudc_xfer *)xfer; + u_int32_t len; + u_int8_t *p; + + if (lxfer->frmlen > 0) { + xfer->actlen += lxfer->frmlen; + lxfer->frmlen = 0; + } + + DPRINTF(1,("%s: ep0 ctrl-in, xfer=%p, len=%u, actlen=%u\n", + DEVNAME(sc), xfer, xfer->length, xfer->actlen)); + + if (xfer->actlen >= xfer->length) { + sc->sc_ep0state = EP0_SETUP; + usbf_transfer_complete(xfer); + return; + } + + sc->sc_ep0state = EP0_IN; + + p = (u_char *)xfer->buffer + xfer->actlen; + len = xfer->length - xfer->actlen; + len = MIN(len, PXAUDC_EP0MAXP); + lxfer->frmlen = len; + + while (len >= 4) { + u_int32_t v; + + if (((unsigned)p & 0x3) == 0) + v = *(u_int32_t *)p; + else { + v = *(p+0); + v |= *(p+1) << 8; + v |= *(p+2) << 16; + v |= *(p+3) << 24; + } + + CSR_WRITE_4(sc, USBDC_UDCDR(0), v); + len -= 4; + p += 4; + } + + while (len > 0) { + CSR_WRITE_1(sc, USBDC_UDCDR(0), *p); + len--; + p++; + } + + /* (12.6.7) Set IPR only for short packets. */ + if (lxfer->frmlen < PXAUDC_EP0MAXP) + CSR_SET_4(sc, USBDC_UDCCSR0, USBDC_UDCCSR0_IPR); +} + +void +pxaudc_write(struct pxaudc_softc *sc, usbf_xfer_handle xfer) +{ + printf("pxaudc_write: XXX\n"); +} + +/* + * Interrupt handling + */ + +int +pxaudc_connect_intr(void *v) +{ + struct pxaudc_softc *sc = v; + + DPRINTF(0,("pxaudc_connect_intr: connect=%d device=%d\n", + pxa2x0_gpio_get_bit(C3000_USB_CONNECT_PIN), + pxa2x0_gpio_get_bit(C3000_USB_DEVICE_PIN))); + + /* XXX only set a flag here */ + if (pxaudc_is_host()) { + pxaudc_disable(sc); + } else { + pxaudc_enable(sc); + } + + /* Claim this interrupt. */ + return 1; +} + +int +pxaudc_intr(void *v) +{ + struct pxaudc_softc *sc = v; + u_int32_t isr0, isr1, otgisr; + + isr0 = CSR_READ_4(sc, USBDC_UDCISR0); + isr1 = CSR_READ_4(sc, USBDC_UDCISR1); + otgisr = CSR_READ_4(sc, USBDC_UDCOTGISR); + + DPRINTF(1,("pxaudc_intr: isr0=%b, isr1=%b, otgisr=%b\n", + isr0, USBDC_UDCISR0_BITS, isr1, USBDC_UDCISR1_BITS, + otgisr, USBDC_UDCOTGISR_BITS)); + + if (isr0 || isr1 || otgisr) { + sc->sc_isr0 |= isr0; + sc->sc_isr1 |= isr1; + sc->sc_otgisr |= otgisr; + + //usbf_schedsoftintr(&sc->sc_bus); + pxaudc_intr1(sc); /* XXX */ + } + + CSR_WRITE_4(sc, USBDC_UDCISR0, isr0); + CSR_WRITE_4(sc, USBDC_UDCISR1, isr1); + CSR_WRITE_4(sc, USBDC_UDCOTGISR, otgisr); + + /* Claim this interrupt. */ + return 1; +} + +void +pxaudc_intr1(struct pxaudc_softc *sc) +{ + u_int32_t isr0, isr1, otgisr; + //int s; + + //s = splhardusb(); + isr0 = sc->sc_isr0; + isr1 = sc->sc_isr1; + otgisr = sc->sc_otgisr; + sc->sc_isr0 = 0; + sc->sc_isr1 = 0; + sc->sc_otgisr = 0; + //splx(s); + + sc->sc_bus.intr_context++; + + /* Handle USB RESET condition. */ + if (isr1 & USBDC_UDCISR1_IRRS) { + sc->sc_ep0state = EP0_SETUP; + usbf_host_reset(&sc->sc_bus); + /* Discard all other interrupts. */ + goto ret; + } + + /* Service control pipe interrupts. */ + if (isr0 & USBDC_UDCISR0_IR(0)) + pxaudc_ep0_intr(sc); + +ret: + sc->sc_bus.intr_context--; +} + +void +pxaudc_ep0_intr(struct pxaudc_softc *sc) +{ + struct pxaudc_pipe *ppipe; + usbf_pipe_handle pipe = NULL; + usbf_xfer_handle xfer = NULL; + u_int32_t csr0; + + csr0 = CSR_READ_4(sc, USBDC_UDCCSR0); + DPRINTF(1,("pxaudc_ep0_intr: csr0=%b\n", csr0, USBDC_UDCCSR0_BITS)); + + ppipe = sc->sc_pipe[0]; + if (ppipe != NULL) { + pipe = &ppipe->pipe; + xfer = SIMPLEQ_FIRST(&pipe->queue); + } + + if (sc->sc_ep0state == EP0_SETUP && (csr0 & USBDC_UDCCSR0_OPC)) { + if (pipe == NULL) { + DPRINTF(0,("pxaudc_ep0_intr: no control pipe\n")); + return; + } + + if (xfer == NULL) { + DPRINTF(0,("pxaudc_ep0_intr: no xfer\n")); + return; + } + + pxaudc_read_ep0(sc, xfer); + } else if (sc->sc_ep0state == EP0_IN && + (csr0 & USBDC_UDCCSR0_IPR) == 0 && xfer) { + pxaudc_write_ep0(sc, xfer); + } +} + +/* + * Bus methods + */ + +usbf_status +pxaudc_open(struct usbf_pipe *pipe) +{ + struct pxaudc_softc *sc = (struct pxaudc_softc *)pipe->device->bus; + struct pxaudc_pipe *ppipe = (struct pxaudc_pipe *)pipe; + int s; + + if (usbf_endpoint_index(pipe->endpoint) >= PXAUDC_NEP) + return USBF_BAD_ADDRESS; + + DPRINTF(1,("pxaudc_open\n")); + s = splhardusb(); + + switch (usbf_endpoint_type(pipe->endpoint)) { + case UE_CONTROL: + pipe->methods = &pxaudc_ctrl_methods; + break; + + case UE_BULK: + pipe->methods = &pxaudc_bulk_methods; + break; + + case UE_ISOCHRONOUS: + case UE_INTERRUPT: + default: + /* XXX */ + splx(s); + return USBF_BAD_ADDRESS; + } + + sc->sc_pipe[usbf_endpoint_index(pipe->endpoint)] = ppipe; + + splx(s); + return USBF_NORMAL_COMPLETION; +} + +void +pxaudc_softintr(void *v) +{ + struct pxaudc_softc *sc = v; + + pxaudc_intr1(sc); +} + +usbf_status +pxaudc_allocm(struct usbf_bus *bus, usb_dma_t *dmap, u_int32_t size) +{ + return usbf_allocmem(bus, size, 0, dmap); +} + +void +pxaudc_freem(struct usbf_bus *bus, usb_dma_t *dmap) +{ + usbf_freemem(bus, dmap); +} + +usbf_xfer_handle +pxaudc_allocx(struct usbf_bus *bus) +{ + struct pxaudc_softc *sc = (struct pxaudc_softc *)bus; + usbf_xfer_handle xfer; + + xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers); + if (xfer != NULL) + SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next); + else + xfer = malloc(sizeof(struct pxaudc_xfer), M_USB, M_NOWAIT); + if (xfer != NULL) + bzero(xfer, sizeof(struct pxaudc_xfer)); + return xfer; +} + +void +pxaudc_freex(struct usbf_bus *bus, usbf_xfer_handle xfer) +{ + struct pxaudc_softc *sc = (struct pxaudc_softc *)bus; + + SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next); +} + +/* + * Control pipe methods + */ + +usbf_status +pxaudc_ctrl_transfer(usbf_xfer_handle xfer) +{ + usbf_status err; + + /* Insert last in queue. */ + err = usbf_insert_transfer(xfer); + if (err) + return err; + + /* + * Pipe isn't running (otherwise err would be USBF_IN_PROGRESS), + * so start first. + */ + return pxaudc_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue)); +} + +usbf_status +pxaudc_ctrl_start(usbf_xfer_handle xfer) +{ + struct usbf_pipe *pipe = xfer->pipe; + struct pxaudc_softc *sc = (struct pxaudc_softc *)pipe->device->bus; + int iswrite = !(xfer->rqflags & URQ_REQUEST); + int s; + + s = splusb(); + xfer->status = USBF_IN_PROGRESS; + if (iswrite) + pxaudc_write_ep0(sc, xfer); + else { + /* XXX boring message, this case is normally reached if + * XXX the xfer for a device request is being queued. */ + DPRINTF(0,("%s: ep0 ctrl-out, xfer=%p, len=%u, " + "actlen=%u\n", DEVNAME(sc), xfer, xfer->length, + xfer->actlen)); + } + splx(s); + return USBF_IN_PROGRESS; +} + +/* (also used by bulk pipes) */ +void +pxaudc_ctrl_abort(usbf_xfer_handle xfer) +{ + struct usbf_pipe *pipe = xfer->pipe; + struct pxaudc_softc *sc = (struct pxaudc_softc *)pipe->device->bus; + int s; +#ifdef PXAUDC_DEBUG + int index = usbf_endpoint_index(pipe->endpoint); + int dir = usbf_endpoint_dir(pipe->endpoint); + int type = usbf_endpoint_type(pipe->endpoint); +#endif + + DPRINTF(0,("%s: ep%d %s-%s abort, xfer=%p\n", DEVNAME(sc), index, + type == UE_CONTROL ? "ctrl" : "bulk", dir == UE_DIR_IN ? + "in" : "out", xfer)); + + /* + * Step 1: Make soft interrupt routine and hardware ignore the xfer. + */ + s = splusb(); + xfer->status = USBF_CANCELLED; + usb_uncallout(xfer->timeout_handle, pxaudc_timeout, NULL); + splx(s); + + /* + * Step 2: Make sure hardware has finished any possible use of the + * xfer and the soft interrupt routine has run. + */ + s = splusb(); + /* XXX this does not seem right, what if there + * XXX are two xfers in the FIFO and we only want to + * XXX ignore one? */ +#ifdef notyet + pxaudc_flush(sc, usbf_endpoint_address(pipe->endpoint)); +#endif + /* XXX we're not doing DMA and the soft interrupt routine does not + XXX need to clean up anything. */ + splx(s); + + /* + * Step 3: Execute callback. + */ + s = splusb(); + usbf_transfer_complete(xfer); + splx(s); +} + +void +pxaudc_ctrl_done(usbf_xfer_handle xfer) +{ +} + +void +pxaudc_ctrl_close(usbf_pipe_handle pipe) +{ + /* XXX */ +} + +/* + * Bulk pipe methods + */ + +usbf_status +pxaudc_bulk_transfer(usbf_xfer_handle xfer) +{ + usbf_status err; + + /* Insert last in queue. */ + err = usbf_insert_transfer(xfer); + if (err) + return err; + + /* + * Pipe isn't running (otherwise err would be USBF_IN_PROGRESS), + * so start first. + */ + return pxaudc_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue)); +} + +usbf_status +pxaudc_bulk_start(usbf_xfer_handle xfer) +{ + struct usbf_pipe *pipe = xfer->pipe; + struct pxaudc_softc *sc = (struct pxaudc_softc *)pipe->device->bus; + int iswrite = usbf_endpoint_dir(pipe->endpoint) == UE_DIR_IN; + int s; + + DPRINTF(1,("%s: ep%d bulk-%s start, xfer=%p, len=%u\n", DEVNAME(sc), + usbf_endpoint_index(pipe->endpoint), iswrite ? "in" : "out", + xfer, xfer->length)); + + s = splusb(); + xfer->status = USBF_IN_PROGRESS; + if (iswrite) + pxaudc_write(sc, xfer); + splx(s); + return USBF_IN_PROGRESS; +} + +void +pxaudc_bulk_abort(usbf_xfer_handle xfer) +{ + pxaudc_ctrl_abort(xfer); +} + +void +pxaudc_bulk_done(usbf_xfer_handle xfer) +{ +} + +void +pxaudc_bulk_close(usbf_pipe_handle pipe) +{ + /* XXX */ } diff --git a/sys/arch/arm/xscale/pxa27x_udcreg.h b/sys/arch/arm/xscale/pxa27x_udcreg.h index f54a0e42296..b9e2e4ad76b 100644 --- a/sys/arch/arm/xscale/pxa27x_udcreg.h +++ b/sys/arch/arm/xscale/pxa27x_udcreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pxa27x_udcreg.h,v 1.1 2005/02/17 22:10:35 dlg Exp $ */ +/* $OpenBSD: pxa27x_udcreg.h,v 1.2 2006/11/25 18:10:29 uwe Exp $ */ /* * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> @@ -149,4 +149,23 @@ #define USBDC_UDCECR_IN (7<<22) /* Interface Number */ #define USBDC_UDCECR_CN (3<<25) /* Configuration Number */ +#define USBDC_UDCCR_BITS \ + "\20\001UDE\002UDA\003UDR\004EMCE\005SMAC\021DWRE" \ + "\035BHNP\036AHNP\037OEN" +#define USBDC_UDCISR0_BITS \ + "\20\0010P\0020F\003AP\004AF\005BP\006BF\007CP\010CF" \ + "\011DP\012DF\013EP\014EF\015FP\016FF\017GP\020GF" \ + "\031HP\032HF\033IP\034IF\035JP\036JF\037KP\030KF" \ + "\041LP\042LF\043MP\044MF\045NP\046NF\047PP\040PF" +#define USBDC_UDCISR1_BITS \ + "\20\001QP\002QF\003RP\004RF\005SP\006SF\007TP\010TF" \ + "\011UP\012UF\013VP\014VF\015WP\016WF\017XP\020XF" \ + "\034RS\035SU\036RU\037SOF\040CC" +#define USBDC_UDCOTGISR_BITS \ + "\20\001IRIDF\002IRIDR\003IRSDF\004IRSDR\005IRSVF\006IRSVR" \ + "\007IRVV44F\010IRVV44R\011IRVV40F\012IRVV40R" +#define USBDC_UDCCSR0_BITS \ + "\20\001OPC\002IPR\003FTF\004DME\005SST\006FST\007RNE" \ + "\010SA\011AREN\012ACM" + #endif /* _ARM_XSCALE_PXA27X_UDCREG_H_ */ |