Index: arm/conf/BEAGLEBONE =================================================================== --- arm/conf/BEAGLEBONE (revision 252430) +++ arm/conf/BEAGLEBONE (working copy) @@ -123,6 +123,10 @@ device miibus device axe # ASIX Electronics USB Ethernet +# Device mode support and USFS template +device usb_template # Control of the gadget +device usfs + # Flattened Device Tree options FDT options FDT_DTB_STATIC Index: arm/ti/am335x/am335x_usbss.c =================================================================== --- arm/ti/am335x/am335x_usbss.c (revision 0) +++ arm/ti/am335x/am335x_usbss.c (working copy) @@ -0,0 +1,481 @@ +/*- + * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define USB_DEBUG_VAR usbssdebug + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define AM335X_USB_PORTS 2 + +#define USBSS_REVREG 0x00 +#define USBSS_SYSCONFIG 0x10 +#define USBSS_SYSCONFIG_SRESET 1 + +#define USBCTRL_REV 0x00 +#define USBCTRL_CTRL 0x14 +#define USBCTRL_STAT 0x18 +#define USBCTRL_IRQ_STAT0 0x30 +#define IRQ_STAT0_RXSHIFT 16 +#define IRQ_STAT0_TXSHIFT 0 +#define USBCTRL_IRQ_STAT1 0x34 +#define IRQ_STAT1_DRVVBUS (1 << 8) +#define USBCTRL_INTEN_SET0 0x38 +#define USBCTRL_INTEN_SET1 0x3C +#define USBCTRL_INTEN_USB_ALL 0x1ff +#define USBCTRL_INTEN_USB_SOF (1 << 3) +#define USBCTRL_INTEN_CLR0 0x40 +#define USBCTRL_INTEN_CLR1 0x44 +#define USBCTRL_UTMI 0xE0 +#define USBCTRL_UTMI_FSDATAEXT (1 << 1) +#define USBCTRL_MODE 0xE8 +#define USBCTRL_MODE_IDDIG (1 << 8) +#define USBCTRL_MODE_IDDIGMUX (1 << 7) + +/* USBSS resource + 2 MUSB ports */ + +#define RES_USBSS 0 +#define RES_USBCTRL(i) (3*i+1) +#define RES_USBPHY(i) (3*i+2) +#define RES_USBCORE(i) (3*i+3) + +#define USB_WRITE4(sc, idx, reg, val) do { \ + bus_write_4((sc)->sc_mem_res[idx], (reg), (val)); \ +} while (0) + +#define USB_READ4(sc, idx, reg) bus_read_4((sc)->sc_mem_res[idx], (reg)) + +#define USBSS_WRITE4(sc, reg, val) \ + USB_WRITE4((sc), RES_USBSS, (reg), (val)) +#define USBSS_READ4(sc, reg) \ + USB_READ4((sc), RES_USBSS, (reg)) +#define USBCTRL_WRITE4(sc, unit, reg, val) \ + USB_WRITE4((sc), RES_USBCTRL(unit), (reg), (val)) +#define USBCTRL_READ4(sc, unit, reg) \ + USB_READ4((sc), RES_USBCTRL(unit), (reg)) +#define USBPHY_WRITE4(sc, unit, reg, val) \ + USB_WRITE4((sc), RES_USBPHY(unit), (reg), (val)) +#define USBPHY_READ4(sc, unit, reg) \ + USB_READ4((sc), RES_USBPHY(unit), (reg)) + +static struct resource_spec am335x_musbotg_mem_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_MEMORY, 2, RF_ACTIVE }, + { SYS_RES_MEMORY, 3, RF_ACTIVE }, + { SYS_RES_MEMORY, 4, RF_ACTIVE }, + { SYS_RES_MEMORY, 5, RF_ACTIVE }, + { SYS_RES_MEMORY, 6, RF_ACTIVE }, + { -1, 0, 0 } +}; + +static struct resource_spec am335x_musbotg_irq_spec[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { -1, 0, 0 } +}; + +#ifdef USB_DEBUG +static int usbssdebug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, am335x_usbss, CTLFLAG_RW, 0, "AM335x USBSS"); +SYSCTL_INT(_hw_usb_am335x_usbss, OID_AUTO, debug, CTLFLAG_RW, + &usbssdebug, 0, "Debug level"); +#endif + +static device_probe_t musbotg_probe; +static device_attach_t musbotg_attach; +static device_detach_t musbotg_detach; + +struct musbotg_super_softc { + struct musbotg_softc sc_otg[AM335X_USB_PORTS]; + struct resource *sc_mem_res[AM335X_USB_PORTS*3+1]; + struct resource *sc_irq_res[AM335X_USB_PORTS+1]; + void *sc_intr_hdl; +}; + +static void +musbotg_vbus_poll(struct musbotg_super_softc *sc, int port) +{ + uint32_t stat; + + if (sc->sc_otg[port].sc_mode == MUSB2_DEVICE_MODE) + musbotg_vbus_interrupt(&sc->sc_otg[port], 1); + else { + stat = USBCTRL_READ4(sc, port, USBCTRL_STAT); + musbotg_vbus_interrupt(&sc->sc_otg[port], stat & 1); + } +} + +/* + * Arg to musbotg_clocks_on and musbot_clocks_off is + * a uint32_t * pointing to the SCM register offset. + */ +static uint32_t USB_CTRL[] = {SCM_USB_CTRL0, SCM_USB_CTRL1}; + +static void +musbotg_clocks_on(void *arg) +{ + uint32_t c, reg = *(uint32_t *)arg; + + ti_scm_reg_read_4(reg, &c); + c &= ~3; /* Enable power */ + c |= 1 << 19; /* VBUS detect enable */ + c |= 1 << 20; /* Session end enable */ + ti_scm_reg_write_4(reg, c); +} + +static void +musbotg_clocks_off(void *arg) +{ + uint32_t c, reg = *(uint32_t *)arg; + + /* Disable power to PHY */ + ti_scm_reg_read_4(reg, &c); + ti_scm_reg_write_4(reg, c | 3); +} + +static void +musbotg_ep_int_set(struct musbotg_softc *sc, int ep, int on) +{ + struct musbotg_super_softc *ssc = sc->sc_platform_data; + uint32_t epmask; + + epmask = ((1 << ep) << IRQ_STAT0_RXSHIFT); + epmask |= ((1 << ep) << IRQ_STAT0_TXSHIFT); + if (on) + USBCTRL_WRITE4(ssc, sc->sc_id, + USBCTRL_INTEN_SET0, epmask); + else + USBCTRL_WRITE4(ssc, sc->sc_id, + USBCTRL_INTEN_CLR0, epmask); +} + +static void +musbotg_usbss_interrupt(void *arg) +{ + panic("USBSS real interrupt"); +} + +static void +musbotg_wrapper_interrupt(void *arg) +{ + struct musbotg_softc *sc = arg; + struct musbotg_super_softc *ssc = sc->sc_platform_data; + uint32_t stat, stat0, stat1; + stat = USBCTRL_READ4(ssc, sc->sc_id, USBCTRL_STAT); + stat0 = USBCTRL_READ4(ssc, sc->sc_id, USBCTRL_IRQ_STAT0); + stat1 = USBCTRL_READ4(ssc, sc->sc_id, USBCTRL_IRQ_STAT1); + if (stat0) + USBCTRL_WRITE4(ssc, sc->sc_id, USBCTRL_IRQ_STAT0, stat0); + if (stat1) + USBCTRL_WRITE4(ssc, sc->sc_id, USBCTRL_IRQ_STAT1, stat1); + + DPRINTFN(4, "port%d: stat0=%08x stat1=%08x, stat=%08x\n", + sc->sc_id, stat0, stat1, stat); + + if (stat1 & IRQ_STAT1_DRVVBUS) + musbotg_vbus_interrupt(sc, stat & 1); + + musbotg_interrupt(arg, ((stat0 >> 16) & 0xffff), + stat0 & 0xffff, stat1 & 0xff); +} + +static int +musbotg_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ti,musb-am33xx")) + return (ENXIO); + + device_set_desc(dev, "TI AM33xx integrated USB OTG controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +musbotg_attach(device_t dev) +{ + struct musbotg_super_softc *sc = device_get_softc(dev); + int err; + int i; + uint32_t rev, reg; + + /* Request the memory resources */ + err = bus_alloc_resources(dev, am335x_musbotg_mem_spec, + sc->sc_mem_res); + if (err) { + device_printf(dev, + "Error: could not allocate mem resources\n"); + return (ENXIO); + } + + /* Request the IRQ resources */ + err = bus_alloc_resources(dev, am335x_musbotg_irq_spec, + sc->sc_irq_res); + if (err) { + device_printf(dev, + "Error: could not allocate irq resources\n"); + return (ENXIO); + } + + /* + * Reset USBSS, USB0 and USB1 + */ + rev = USBSS_READ4(sc, USBSS_REVREG); + device_printf(dev, "TI AM335X USBSS v%d.%d.%d\n", + (rev >> 8) & 7, (rev >> 6) & 3, rev & 63); + + ti_prcm_clk_enable(MUSB0_CLK); + + USBSS_WRITE4(sc, USBSS_SYSCONFIG, + USBSS_SYSCONFIG_SRESET); + while (USBSS_READ4(sc, USBSS_SYSCONFIG) & + USBSS_SYSCONFIG_SRESET) + ; + + err = bus_setup_intr(dev, sc->sc_irq_res[0], + INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)musbotg_usbss_interrupt, sc, + &sc->sc_intr_hdl); + + if (err) { + sc->sc_intr_hdl = NULL; + device_printf(dev, "Failed to setup USBSS interrupt\n"); + goto error; + } + + for (i = 0; i < AM335X_USB_PORTS; i++) { + /* setup MUSB OTG USB controller interface softc */ + sc->sc_otg[i].sc_clocks_on = &musbotg_clocks_on; + sc->sc_otg[i].sc_clocks_off = &musbotg_clocks_off; + sc->sc_otg[i].sc_clocks_arg = &USB_CTRL[i]; + + sc->sc_otg[i].sc_ep_int_set = musbotg_ep_int_set; + + /* initialise some bus fields */ + sc->sc_otg[i].sc_bus.parent = dev; + sc->sc_otg[i].sc_bus.devices = sc->sc_otg[i].sc_devices; + sc->sc_otg[i].sc_bus.devices_max = MUSB2_MAX_DEVICES; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_otg[i].sc_bus, + USB_GET_DMA_TAG(dev), NULL)) { + device_printf(dev, + "Failed allocate bus mem for musb%d\n", i); + return (ENOMEM); + } + sc->sc_otg[i].sc_io_res = sc->sc_mem_res[RES_USBCORE(i)]; + sc->sc_otg[i].sc_io_tag = + rman_get_bustag(sc->sc_otg[i].sc_io_res); + sc->sc_otg[i].sc_io_hdl = + rman_get_bushandle(sc->sc_otg[i].sc_io_res); + sc->sc_otg[i].sc_io_size = + rman_get_size(sc->sc_otg[i].sc_io_res); + + sc->sc_otg[i].sc_irq_res = sc->sc_irq_res[i+1]; + + sc->sc_otg[i].sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!(sc->sc_otg[i].sc_bus.bdev)) { + device_printf(dev, "No busdev for musb%d\n", i); + goto error; + } + device_set_ivars(sc->sc_otg[i].sc_bus.bdev, + &sc->sc_otg[i].sc_bus); + + err = bus_setup_intr(dev, sc->sc_otg[i].sc_irq_res, + INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)musbotg_wrapper_interrupt, + &sc->sc_otg[i], &sc->sc_otg[i].sc_intr_hdl); + if (err) { + sc->sc_otg[i].sc_intr_hdl = NULL; + device_printf(dev, + "Failed to setup interrupt for musb%d\n", i); + goto error; + } + + sc->sc_otg[i].sc_id = i; + sc->sc_otg[i].sc_platform_data = sc; + if (i == 0) + sc->sc_otg[i].sc_mode = MUSB2_DEVICE_MODE; + else + sc->sc_otg[i].sc_mode = MUSB2_HOST_MODE; + + /* + * software-controlled function + */ + + if (sc->sc_otg[i].sc_mode == MUSB2_HOST_MODE) { + reg = USBCTRL_READ4(sc, i, USBCTRL_MODE); + reg |= USBCTRL_MODE_IDDIGMUX; + reg &= ~USBCTRL_MODE_IDDIG; + USBCTRL_WRITE4(sc, i, USBCTRL_MODE, reg); + USBCTRL_WRITE4(sc, i, USBCTRL_UTMI, + USBCTRL_UTMI_FSDATAEXT); + } else { + reg = USBCTRL_READ4(sc, i, USBCTRL_MODE); + reg |= USBCTRL_MODE_IDDIGMUX; + reg |= USBCTRL_MODE_IDDIG; + USBCTRL_WRITE4(sc, i, USBCTRL_MODE, reg); + } + + reg = USBCTRL_INTEN_USB_ALL & ~USBCTRL_INTEN_USB_SOF; + USBCTRL_WRITE4(sc, i, USBCTRL_INTEN_SET1, reg); + USBCTRL_WRITE4(sc, i, USBCTRL_INTEN_CLR0, 0xffffffff); + + err = musbotg_init(&sc->sc_otg[i]); + if (!err) + err = device_probe_and_attach(sc->sc_otg[i].sc_bus.bdev); + + if (err) + goto error; + + /* poll VBUS one time */ + musbotg_vbus_poll(sc, i); + } + + return (0); + +error: + musbotg_detach(dev); + return (ENXIO); +} + +static int +musbotg_detach(device_t dev) +{ + struct musbotg_super_softc *sc = device_get_softc(dev); + device_t bdev; + int err; + int i; + + for (i = 0; i < AM335X_USB_PORTS; i++) { + if (sc->sc_otg[i].sc_bus.bdev) { + bdev = sc->sc_otg[i].sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + + if (sc->sc_otg[i].sc_irq_res && sc->sc_otg[i].sc_intr_hdl) { + /* + * only call musbotg_uninit() after musbotg_init() + */ + musbotg_uninit(&sc->sc_otg[i]); + + err = bus_teardown_intr(dev, sc->sc_otg[i].sc_irq_res, + sc->sc_otg[i].sc_intr_hdl); + sc->sc_otg[i].sc_intr_hdl = NULL; + } + + usb_bus_mem_free_all(&sc->sc_otg[i].sc_bus, NULL); + } + + if (sc->sc_intr_hdl) { + bus_teardown_intr(dev, sc->sc_irq_res[0], + sc->sc_intr_hdl); + sc->sc_intr_hdl = NULL; + } + + + /* Free resources if any */ + if (sc->sc_mem_res[0]) + bus_release_resources(dev, am335x_musbotg_mem_spec, + sc->sc_mem_res); + + if (sc->sc_irq_res[0]) + bus_release_resources(dev, am335x_musbotg_irq_spec, + sc->sc_irq_res); + + /* during module unload there are lots of children leftover */ + device_delete_children(dev); + + return (0); +} + +static device_method_t musbotg_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, musbotg_probe), + DEVMETHOD(device_attach, musbotg_attach), + DEVMETHOD(device_detach, musbotg_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +static driver_t musbotg_driver = { + .name = "musbotg", + .methods = musbotg_methods, + .size = sizeof(struct musbotg_super_softc), +}; + +static devclass_t musbotg_devclass; + +DRIVER_MODULE(musbotg, simplebus, musbotg_driver, musbotg_devclass, 0, 0); +MODULE_DEPEND(musbotg, usb, 1, 1, 1); Index: arm/ti/am335x/files.am335x =================================================================== --- arm/ti/am335x/files.am335x (revision 252430) +++ arm/ti/am335x/files.am335x (working copy) @@ -7,6 +7,7 @@ arm/ti/am335x/am335x_lcd.c optional sc arm/ti/am335x/am335x_lcd_syscons.c optional sc arm/ti/am335x/am335x_pwm.c standard +arm/ti/am335x/am335x_usbss.c optional musb fdt arm/ti/ti_edma3.c standard arm/ti/ti_mmchs.c optional mmc arm/ti/cpsw/if_cpsw.c optional cpsw Index: boot/fdt/dts/am335x-evm.dts =================================================================== --- boot/fdt/dts/am335x-evm.dts (revision 252430) +++ boot/fdt/dts/am335x-evm.dts (working copy) @@ -106,6 +106,9 @@ "GPMC_BEn0_CLE", "timer5", "output", "GPMC_WEn", "timer6", "output", "GPMC_OEn_REn", "timer7", "output", + /* USB0 and USB1 */ + "USB0_DRVVBUS", "USB0_DRVVBUS", "output", + "USB1_DRVVBUS", "USB1_DRVVBUS", "output", /* LCD */ "GPMC_AD8", "lcd_data23", "output", "GPMC_AD9", "lcd_data22", "output", Index: boot/fdt/dts/am335x.dtsi =================================================================== --- boot/fdt/dts/am335x.dtsi (revision 252430) +++ boot/fdt/dts/am335x.dtsi (working copy) @@ -173,5 +173,23 @@ interrupts = <36>; interrupt-parent = <&AINTC>; }; + + usb@47400000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,musb-am33xx"; + reg =< 0x47400000 0x1000 /* USBSS */ + 0x47401000 0x300 /* USB0 */ + 0x47401300 0x100 /* USB0_PHY */ + 0x47401400 0x400 /* USB0_CORE */ + 0x47401800 0x300 /* USB1 */ + 0x47401B00 0x100 /* USB1_PHY */ + 0x47401C00 0x400 /* USB1_CORE */ + >; + interrupts = <17 18 19>; + interrupt-parent = <&AINTC>; + /* 1 - Host Mode, 0 - Device Mode */ + modemask = <2>; + }; }; }; Index: boot/fdt/dts/beaglebone-black.dts =================================================================== --- boot/fdt/dts/beaglebone-black.dts (revision 252430) +++ boot/fdt/dts/beaglebone-black.dts (working copy) @@ -123,6 +123,9 @@ "GPMC_BEn0_CLE", "timer5", "output", "GPMC_WEn", "timer6", "output", "GPMC_OEn_REn", "timer7", "output", + /* USB0 and USB1 */ + "USB0_DRVVBUS", "USB0_DRVVBUS", "output", + "USB1_DRVVBUS", "USB1_DRVVBUS", "output", /* PWM */ "GPMC_A2", "ehrpwm1A", "output", "GPMC_A3", "ehrpwm1B", "output", Index: boot/fdt/dts/beaglebone.dts =================================================================== --- boot/fdt/dts/beaglebone.dts (revision 252430) +++ boot/fdt/dts/beaglebone.dts (working copy) @@ -74,6 +74,9 @@ "MMC0_DAT1", "mmc0_dat1", "input_pullup", "MMC0_DAT2", "mmc0_dat2", "input_pullup", "MMC0_DAT3", "mmc0_dat3", "input_pullup", + /* USB0 and USB1 */ + "USB0_DRVVBUS", "USB0_DRVVBUS", "output", + "USB1_DRVVBUS", "USB1_DRVVBUS", "output", /* GPIO */ "ECAP0_IN_PWM0_OUT", "gpio0_7", "input_pulldown", "GPMC_AD10", "gpio0_26", "input_pulldown", Index: dev/usb/controller/musb_otg.c =================================================================== --- dev/usb/controller/musb_otg.c (revision 252430) +++ dev/usb/controller/musb_otg.c (working copy) @@ -95,6 +95,8 @@ &musbotgdebug, 0, "Debug level"); #endif +#define MAX_NAK_TO 16 + /* prototypes */ struct usb_bus_methods musbotg_bus_methods; @@ -103,17 +105,35 @@ struct usb_pipe_methods musbotg_device_intr_methods; struct usb_pipe_methods musbotg_device_isoc_methods; -static musbotg_cmd_t musbotg_setup_rx; -static musbotg_cmd_t musbotg_setup_data_rx; -static musbotg_cmd_t musbotg_setup_data_tx; -static musbotg_cmd_t musbotg_setup_status; -static musbotg_cmd_t musbotg_data_rx; -static musbotg_cmd_t musbotg_data_tx; +/* Control transfers: Device mode */ +static musbotg_cmd_t musbotg_dev_ctrl_setup_rx; +static musbotg_cmd_t musbotg_dev_ctrl_data_rx; +static musbotg_cmd_t musbotg_dev_ctrl_data_tx; +static musbotg_cmd_t musbotg_dev_ctrl_status; + +/* Control transfers: Host mode */ +static musbotg_cmd_t musbotg_host_ctrl_setup_tx; +static musbotg_cmd_t musbotg_host_ctrl_data_rx; +static musbotg_cmd_t musbotg_host_ctrl_data_tx; +static musbotg_cmd_t musbotg_host_ctrl_status_rx; +static musbotg_cmd_t musbotg_host_ctrl_status_tx; + +/* Bulk, Interrupt, Isochronous: Device mode */ +static musbotg_cmd_t musbotg_dev_data_rx; +static musbotg_cmd_t musbotg_dev_data_tx; + +/* Bulk, Interrupt, Isochronous: Host mode */ +static musbotg_cmd_t musbotg_host_data_rx; +static musbotg_cmd_t musbotg_host_data_tx; + static void musbotg_device_done(struct usb_xfer *, usb_error_t); static void musbotg_do_poll(struct usb_bus *); static void musbotg_standard_done(struct usb_xfer *); static void musbotg_interrupt_poll(struct musbotg_softc *); static void musbotg_root_intr(struct musbotg_softc *); +static int musbotg_channel_alloc(struct musbotg_softc *, struct musbotg_td *td); +static void musbotg_channel_free(struct musbotg_softc *, struct musbotg_td *td); +static void musbotg_ep_int_set(struct musbotg_softc *sc, int channel, int on); /* * Here is a configuration that the chip supports. @@ -128,6 +148,64 @@ } }; +static int +musbotg_channel_alloc(struct musbotg_softc *sc, struct musbotg_td *td) +{ + int ch; + int ep; + + ep = td->ep_no; + + /* In device mode each EP got its own channel */ + if (sc->sc_mode == MUSB2_DEVICE_MODE) { + musbotg_ep_int_set(sc, ep, 1); + return (ep); + } + + /* + * All control transactions go through EP0 + */ + if (ep == 0) { + if (sc->sc_channel_mask & (1 << 0)) + return (-1); + sc->sc_channel_mask |= (1 << 0); + musbotg_ep_int_set(sc, ep, 1); + return (0); + } + + for (ch = 1; ch < MUSB2_EP_MAX; ch++) { + if (!(sc->sc_channel_mask & (1 << ch))) { + sc->sc_channel_mask |= (1 << ch); + musbotg_ep_int_set(sc, ch, 1); + return (ch); + } + } + + DPRINTFN(-1, "No available channels. Mask: %04x\n", sc->sc_channel_mask); + + return (-1); +} + +static void +musbotg_channel_free(struct musbotg_softc *sc, struct musbotg_td *td) +{ + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + if (sc->sc_mode == MUSB2_DEVICE_MODE) + return; + + if (td == NULL) + return; + if (td->channel == -1) + return; + + musbotg_ep_int_set(sc, td->channel, 0); + sc->sc_channel_mask &= ~(1 << td->channel); + + td->channel = -1; +} + static void musbotg_get_hw_ep_profile(struct usb_device *udev, const struct usb_hw_ep_profile **ppf, uint8_t ep_addr) @@ -218,6 +296,46 @@ } static void +musbotg_suspend_host(struct musbotg_softc *sc) +{ + uint8_t temp; + + if (sc->sc_flags.status_suspend) { + return; + } + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp |= MUSB2_MASK_SUSPMODE; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + sc->sc_flags.status_suspend = 1; +} + +static void +musbotg_wakeup_host(struct musbotg_softc *sc) +{ + uint8_t temp; + + if (!(sc->sc_flags.status_suspend)) { + return; + } + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp &= ~MUSB2_MASK_SUSPMODE; + temp |= MUSB2_MASK_RESUME; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + + /* wait 20 milliseconds */ + /* Wait for reset to complete. */ + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50); + + temp = MUSB2_READ_1(sc, MUSB2_REG_POWER); + temp &= ~MUSB2_MASK_RESUME; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp); + + sc->sc_flags.status_suspend = 0; +} + +static void musbotg_wakeup_peer(struct musbotg_softc *sc) { uint8_t temp; @@ -248,7 +366,7 @@ } static uint8_t -musbotg_setup_rx(struct musbotg_td *td) +musbotg_dev_ctrl_setup_rx(struct musbotg_td *td) { struct musbotg_softc *sc; struct usb_device_request req; @@ -258,6 +376,15 @@ /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + /* select endpoint 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); @@ -274,8 +401,10 @@ /* do not stall at this point */ td->did_stall = 1; /* wait for interrupt */ + DPRINTFN(0, "CSR0 DATAEND\n"); goto not_complete; } + if (csr & MUSB2_MASK_CSR0L_SENTSTALL) { /* clear SENTSTALL */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); @@ -294,6 +423,7 @@ sc->sc_ep0_busy = 0; } if (sc->sc_ep0_busy) { + DPRINTFN(0, "EP0 BUSY\n"); goto not_complete; } if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) { @@ -342,6 +472,8 @@ } else { sc->sc_dv_addr = 0xFF; } + + musbotg_channel_free(sc, td); return (0); /* complete */ not_complete: @@ -355,10 +487,117 @@ return (1); /* not complete */ } +static uint8_t +musbotg_host_ctrl_setup_tx(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + struct usb_device_request req; + uint8_t csr, csrh; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + DPRINTFN(4, "csr=0x%02x\n", csr); + + /* Not ready yet yet */ + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) + return (1); + + /* Failed */ + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) + { + /* Clear status bit */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + DPRINTFN(1, "error bit set, csr=0x%02x\n", csr); + td->error = 1; + } + + if (csr & MUSB2_MASK_CSR0L_NAKTIMO) { + DPRINTFN(1, "NAK timeout\n"); + + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + csrh |= MUSB2_MASK_CSR0H_FFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + csrh |= MUSB2_MASK_CSR0H_FFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + } + } + + csr &= ~MUSB2_MASK_CSR0L_NAKTIMO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + td->error = 1; + } + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); + } + + /* Fifo is not empty and there is no NAK timeout */ + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) + return (1); + + /* check if we are complete */ + if (td->remainder == 0) { + /* we are complete */ + musbotg_channel_free(sc, td); + return (0); + } + + /* copy data into real buffer */ + usbd_copy_out(td->pc, 0, &req, sizeof(req)); + + /* send data */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), (void *)&req, sizeof(req)); + + /* update offset and remainder */ + td->offset += sizeof(req); + td->remainder -= sizeof(req); + + + MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO); + MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type); + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_TXPKTRDY | + MUSB2_MASK_CSR0L_SETUPPKT); + + /* Just to be consistent, not used above */ + td->transaction_started = 1; + + return (1); /* in progress */ +} + /* Control endpoint only data handling functions (RX/TX/SYNC) */ static uint8_t -musbotg_setup_data_rx(struct musbotg_td *td) +musbotg_dev_ctrl_data_rx(struct musbotg_td *td) { struct usb_page_search buf_res; struct musbotg_softc *sc; @@ -501,7 +740,7 @@ } static uint8_t -musbotg_setup_data_tx(struct musbotg_td *td) +musbotg_dev_ctrl_data_tx(struct musbotg_td *td) { struct usb_page_search buf_res; struct musbotg_softc *sc; @@ -619,17 +858,373 @@ } static uint8_t -musbotg_setup_status(struct musbotg_td *td) +musbotg_host_ctrl_data_rx(struct musbotg_td *td) { + struct usb_page_search buf_res; struct musbotg_softc *sc; + uint16_t count; uint8_t csr; + uint8_t got_short; /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + /* select endpoint 0 */ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "csr=0x%02x\n", csr); + + got_short = 0; + if (!td->transaction_started) { + td->transaction_started = 1; + + MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO); + + MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(0), + td->dev_addr); + MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type); + + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_REQPKT); + + return (1); + } + + if (csr & MUSB2_MASK_CSR0L_NAKTIMO) { + csr &= ~MUSB2_MASK_CSR0L_REQPKT; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + csr &= ~MUSB2_MASK_CSR0L_NAKTIMO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + td->error = 1; + } + + /* Failed */ + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) + { + /* Clear status bit */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + DPRINTFN(1, "error bit set, csr=0x%02x\n", csr); + td->error = 1; + } + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + + if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) + return (1); /* not yet */ + + /* get the packet byte count */ + count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); + + /* verify the packet byte count */ + if (count != td->max_frame_size) { + if (count < td->max_frame_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + } + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + while (count > 0) { + uint32_t temp; + + usbd_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + temp = count & ~3; + + if (temp) { + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf, + temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), + (void *)(&sc->sc_bounce_buf[count / 4]), temp); + } + usbd_copy_in(td->pc, td->offset, + sc->sc_bounce_buf, count); + + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* receive data */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + csr &= ~MUSB2_MASK_CSR0L_RXPKTRDY; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + + musbotg_channel_free(sc, td); + return (0); + } + /* else need to receive a zero length packet */ + } + + td->transaction_started = 1; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_REQPKT); + + return (1); /* not complete */ +} + +static uint8_t +musbotg_host_ctrl_data_tx(struct musbotg_td *td) +{ + struct usb_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr, csrh; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* No free EPs */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + DPRINTFN(4, "csr=0x%02x\n", csr); + + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) { + /* clear status bits */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + td->error = 1; + } + + if (csr & MUSB2_MASK_CSR0L_NAKTIMO ) { + + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + csrh |= MUSB2_MASK_CSR0H_FFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + csrh |= MUSB2_MASK_CSR0H_FFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + } + } + + csr &= ~MUSB2_MASK_CSR0L_NAKTIMO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + td->error = 1; + } + + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + /* + * Wait while FIFO is empty. + * Do not flush it because it will cause transactions + * with size more then packet size. It might upset + * some devices + */ + if (csr & MUSB2_MASK_CSR0L_TXFIFONEMPTY) + return (1); + + /* Packet still being processed */ + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) + return (1); + + if (td->transaction_started) { + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) { + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + /* else we need to transmit a short packet */ + } + + /* We're not complete - more transactions required */ + td->transaction_started = 0; + } + + /* check for short packet */ + count = td->max_frame_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } + + while (count > 0) { + uint32_t temp; + + usbd_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + usbd_copy_out(td->pc, td->offset, + sc->sc_bounce_buf, count); + + temp = count & ~3; + + if (temp) { + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, + sc->sc_io_hdl, MUSB2_REG_EPFIFO(0), + sc->sc_bounce_buf, temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), + ((void *)&sc->sc_bounce_buf[count / 4]), temp); + } + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* transmit data */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(0), buf_res.buffer, + buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* Function address */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type); + + /* TX NAK timeout */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO); + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_TXPKTRDY); + + td->transaction_started = 1; + + return (1); /* not complete */ +} + +static uint8_t +musbotg_dev_ctrl_status(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + uint8_t csr; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + if (sc->sc_ep0_busy) { sc->sc_ep0_busy = 0; sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND; @@ -649,12 +1244,167 @@ /* write function address */ musbotg_set_address(sc, sc->sc_dv_addr); } + + musbotg_channel_free(sc, td); return (0); /* complete */ } static uint8_t -musbotg_data_rx(struct musbotg_td *td) +musbotg_host_ctrl_status_rx(struct musbotg_td *td) { + struct musbotg_softc *sc; + uint8_t csr, csrh; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + if (!td->transaction_started) { + MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(0), + td->dev_addr); + + MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type); + + /* RX NAK timeout */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO); + + td->transaction_started = 1; + + /* Disable PING */ + csrh = MUSB2_READ_1(sc, MUSB2_REG_RXCSRH); + csrh |= MUSB2_MASK_CSR0H_PING_DIS; + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, csrh); + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_STATUSPKT | + MUSB2_MASK_CSR0L_REQPKT); + + return (1); /* Just started */ + + } + + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + + DPRINTFN(4, "IN STATUS csr=0x%02x\n", csr); + + if (csr & MUSB2_MASK_CSR0L_RXPKTRDY) { + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_RXPKTRDY_CLR); + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + if (csr & MUSB2_MASK_CSR0L_NAKTIMO) { + csr &= ~ (MUSB2_MASK_CSR0L_STATUSPKT | + MUSB2_MASK_CSR0L_REQPKT); + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + csr &= ~MUSB2_MASK_CSR0L_NAKTIMO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + td->error = 1; + } + + /* Failed */ + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) + { + /* Clear status bit */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + DPRINTFN(1, "error bit set, csr=0x%02x\n", csr); + td->error = 1; + } + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); + } + + return (1); /* Not ready yet */ +} + +static uint8_t +musbotg_host_ctrl_status_tx(struct musbotg_td *td) +{ + struct musbotg_softc *sc; + uint8_t csr; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d/%d [%d@%d.%d/%02x]\n", td->channel, td->transaction_started, + td->dev_addr,td->haddr,td->hport, td->transfer_type); + + /* select endpoint 0 */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0); + + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + DPRINTFN(4, "csr=0x%02x\n", csr); + + /* Not yet */ + if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) + return (1); + + /* Failed */ + if (csr & (MUSB2_MASK_CSR0L_RXSTALL | + MUSB2_MASK_CSR0L_ERROR)) + { + /* Clear status bit */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + DPRINTFN(1, "error bit set, csr=0x%02x\n", csr); + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + if (td->transaction_started) { + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, MUSB2_MASK_CSR0H_PING_DIS); + + MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(0), td->dev_addr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(0), td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(0), td->hport); + MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type); + + /* TX NAK timeout */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO); + + td->transaction_started = 1; + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSR0L_STATUSPKT | + MUSB2_MASK_CSR0L_TXPKTRDY); + + return (1); /* wait for interrupt */ +} + +static uint8_t +musbotg_dev_data_rx(struct musbotg_td *td) +{ struct usb_page_search buf_res; struct musbotg_softc *sc; uint16_t count; @@ -668,8 +1418,15 @@ /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + /* select endpoint */ - MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no); + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel); repeat: /* read out FIFO status */ @@ -683,10 +1440,11 @@ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, MUSB2_MASK_CSRL_RXPKTRDY); } + /* check status */ - if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) { - return (1); /* not complete */ - } + if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) + return (1); /* not complete */ + /* get the packet byte count */ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); @@ -703,6 +1461,7 @@ } else { /* invalid USB packet */ td->error = 1; + musbotg_channel_free(sc, td); return (0); /* we are complete */ } } @@ -710,6 +1469,7 @@ if (count > td->remainder) { /* invalid USB packet */ td->error = 1; + musbotg_channel_free(sc, td); return (0); /* we are complete */ } while (count > 0) { @@ -729,14 +1489,14 @@ if (temp) { /* receive data 4 bytes at a time */ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, - MUSB2_REG_EPFIFO(td->ep_no), sc->sc_bounce_buf, + MUSB2_REG_EPFIFO(td->channel), sc->sc_bounce_buf, temp / 4); } temp = count & 3; if (temp) { /* receive data 1 byte at a time */ bus_space_read_multi_1(sc->sc_io_tag, - sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), + sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel), ((void *)&sc->sc_bounce_buf[count / 4]), temp); } usbd_copy_in(td->pc, td->offset, @@ -752,7 +1512,7 @@ /* receive data 4 bytes at a time */ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, - MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, + MUSB2_REG_EPFIFO(td->channel), buf_res.buffer, buf_res.length / 4); temp = buf_res.length & ~3; @@ -765,7 +1525,7 @@ } /* receive data */ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, - MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, + MUSB2_REG_EPFIFO(td->channel), buf_res.buffer, buf_res.length); /* update counters */ @@ -781,6 +1541,7 @@ if ((td->remainder == 0) || got_short) { if (td->short_pkt) { /* we are complete */ + musbotg_channel_free(sc, td); return (0); } /* else need to receive a zero length packet */ @@ -792,7 +1553,7 @@ } static uint8_t -musbotg_data_tx(struct musbotg_td *td) +musbotg_dev_data_tx(struct musbotg_td *td) { struct usb_page_search buf_res; struct musbotg_softc *sc; @@ -805,8 +1566,15 @@ /* get pointer to softc */ sc = MUSBOTG_PC2SC(td->pc); + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* EP0 is busy, wait */ + if (td->channel == -1) + return (1); + /* select endpoint */ - MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no); + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel); repeat: @@ -850,14 +1618,14 @@ if (temp) { /* transmit data 4 bytes at a time */ bus_space_write_multi_4(sc->sc_io_tag, - sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no), + sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel), sc->sc_bounce_buf, temp / 4); } temp = count & 3; if (temp) { /* receive data 1 byte at a time */ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, - MUSB2_REG_EPFIFO(td->ep_no), + MUSB2_REG_EPFIFO(td->channel), ((void *)&sc->sc_bounce_buf[count / 4]), temp); } /* update offset and remainder */ @@ -870,7 +1638,7 @@ /* transmit data 4 bytes at a time */ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, - MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, + MUSB2_REG_EPFIFO(td->channel), buf_res.buffer, buf_res.length / 4); temp = buf_res.length & ~3; @@ -883,7 +1651,7 @@ } /* transmit data */ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, - MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer, + MUSB2_REG_EPFIFO(td->channel), buf_res.buffer, buf_res.length); /* update counters */ @@ -892,6 +1660,9 @@ td->remainder -= buf_res.length; } + /* Max packet size */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, td->max_packet); + /* write command */ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, MUSB2_MASK_CSRL_TXPKTRDY); @@ -899,6 +1670,7 @@ /* check remainder */ if (td->remainder == 0) { if (td->short_pkt) { + musbotg_channel_free(sc, td); return (0); /* complete */ } /* else we need to transmit a short packet */ @@ -910,19 +1682,443 @@ } static uint8_t +musbotg_host_data_rx(struct musbotg_td *td) +{ + struct usb_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr, csrh; + uint8_t to; + uint8_t got_short; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* No free EPs */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + to = 8; /* don't loop forever! */ + got_short = 0; + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel); + +repeat: + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL); + DPRINTFN(4, "csr=0x%02x\n", csr); + + if (!td->transaction_started) { + /* Function address */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXFADDR(td->channel), + td->dev_addr); + + /* SPLIT transaction */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXHADDR(td->channel), + td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_RXHUBPORT(td->channel), + td->hport); + + /* RX NAK timeout */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, MAX_NAK_TO); + + /* Protocol, speed, device endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXTI, td->transfer_type); + + /* Max packet size */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, td->max_packet); + + /* Data Toggle */ + csrh = MUSB2_READ_1(sc, MUSB2_REG_RXCSRH); + DPRINTFN(4, "csrh=0x%02x\n", csrh); + + csrh |= MUSB2_MASK_CSRH_RXDT_WREN; + if (td->toggle) + csrh |= MUSB2_MASK_CSRH_RXDT_VAL; + else + csrh &= ~MUSB2_MASK_CSRH_RXDT_VAL; + + /* Set data toggle */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, csrh); + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, + MUSB2_MASK_CSRL_RXREQPKT); + + td->transaction_started = 1; + return (1); + } + + /* clear NAK timeout */ + if (csr & MUSB2_MASK_CSRL_RXNAKTO) { + DPRINTFN(4, "NAK Timeout\n"); + if (csr & MUSB2_MASK_CSRL_RXREQPKT) { + csr &= ~MUSB2_MASK_CSRL_RXREQPKT; + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, csr); + + csr &= ~MUSB2_MASK_CSRL_RXNAKTO; + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, csr); + } + + td->error = 1; + } + + if (csr & MUSB2_MASK_CSRL_RXERROR) { + DPRINTFN(4, "RXERROR\n"); + td->error = 1; + } + + if (csr & MUSB2_MASK_CSRL_RXSTALL) { + DPRINTFN(4, "RXSTALL\n"); + td->error = 1; + } + + if (td->error) { + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + + if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) { + /* No data available yet */ + return (1); + } + + td->toggle ^= 1; + /* get the packet byte count */ + count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT); + DPRINTFN(4, "count=0x%04x\n", count); + + /* + * Check for short or invalid packet: + */ + if (count != td->max_frame_size) { + if (count < td->max_frame_size) { + /* we have a short packet */ + td->short_pkt = 1; + got_short = 1; + } else { + /* invalid USB packet */ + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + } + + /* verify the packet byte count */ + if (count > td->remainder) { + /* invalid USB packet */ + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* we are complete */ + } + + while (count > 0) { + uint32_t temp; + + usbd_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + temp = count & ~3; + + if (temp) { + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->channel), sc->sc_bounce_buf, + temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_read_multi_1(sc->sc_io_tag, + sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel), + ((void *)&sc->sc_bounce_buf[count / 4]), temp); + } + usbd_copy_in(td->pc, td->offset, + sc->sc_bounce_buf, count); + + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* receive data 4 bytes at a time */ + bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->channel), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* receive data */ + bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->channel), buf_res.buffer, + buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* clear status bits */ + MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0); + + /* check if we are complete */ + if ((td->remainder == 0) || got_short) { + if (td->short_pkt) { + /* we are complete */ + musbotg_channel_free(sc, td); + return (0); + } + /* else need to receive a zero length packet */ + } + + /* Reset transaction state and restart */ + td->transaction_started = 0; + + if (--to) + goto repeat; + + return (1); /* not complete */ +} + +static uint8_t +musbotg_host_data_tx(struct musbotg_td *td) +{ + struct usb_page_search buf_res; + struct musbotg_softc *sc; + uint16_t count; + uint8_t csr, csrh; + + /* get pointer to softc */ + sc = MUSBOTG_PC2SC(td->pc); + + if (td->channel == -1) + td->channel = musbotg_channel_alloc(sc, td); + + /* No free EPs */ + if (td->channel == -1) + return (1); + + DPRINTFN(1, "ep_no=%d\n", td->channel); + + /* select endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel); + + /* read out FIFO status */ + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + DPRINTFN(4, "csr=0x%02x\n", csr); + + if (csr & (MUSB2_MASK_CSRL_TXSTALLED | + MUSB2_MASK_CSRL_TXERROR)) { + /* clear status bits */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0); + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + if (csr & MUSB2_MASK_CSRL_TXNAKTO ) { + /* + * Flush TX FIFO before clearing NAK TO + */ + if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) { + csr |= MUSB2_MASK_CSRL_TXFFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) { + csr |= MUSB2_MASK_CSRL_TXFFLUSH; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL); + } + } + + csr &= ~MUSB2_MASK_CSRL_TXNAKTO; + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, csr); + + td->error = 1; + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + + /* + * Wait while FIFO is empty. + * Do not flush it because it will cause transactions + * with size more then packet size. It might upset + * some devices + */ + if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) + return (1); + + /* Packet still being processed */ + if (csr & MUSB2_MASK_CSRL_TXPKTRDY) + return (1); + + if (td->transaction_started) { + /* check remainder */ + if (td->remainder == 0) { + if (td->short_pkt) { + musbotg_channel_free(sc, td); + return (0); /* complete */ + } + /* else we need to transmit a short packet */ + } + + /* We're not complete - more transactions required */ + td->transaction_started = 0; + } + + /* check for short packet */ + count = td->max_frame_size; + if (td->remainder < count) { + /* we have a short packet */ + td->short_pkt = 1; + count = td->remainder; + } + + while (count > 0) { + uint32_t temp; + + usbd_get_page(td->pc, td->offset, &buf_res); + + /* get correct length */ + if (buf_res.length > count) { + buf_res.length = count; + } + /* check for unaligned memory address */ + if (USB_P2U(buf_res.buffer) & 3) { + + usbd_copy_out(td->pc, td->offset, + sc->sc_bounce_buf, count); + + temp = count & ~3; + + if (temp) { + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, + sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->channel), + sc->sc_bounce_buf, temp / 4); + } + temp = count & 3; + if (temp) { + /* receive data 1 byte at a time */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->channel), + ((void *)&sc->sc_bounce_buf[count / 4]), temp); + } + /* update offset and remainder */ + td->offset += count; + td->remainder -= count; + break; + } + /* check if we can optimise */ + if (buf_res.length >= 4) { + + /* transmit data 4 bytes at a time */ + bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->channel), buf_res.buffer, + buf_res.length / 4); + + temp = buf_res.length & ~3; + + /* update counters */ + count -= temp; + td->offset += temp; + td->remainder -= temp; + continue; + } + /* transmit data */ + bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl, + MUSB2_REG_EPFIFO(td->channel), buf_res.buffer, + buf_res.length); + + /* update counters */ + count -= buf_res.length; + td->offset += buf_res.length; + td->remainder -= buf_res.length; + } + + /* Function address */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXFADDR(td->channel), + td->dev_addr); + + /* SPLIT transaction */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXHADDR(td->channel), + td->haddr); + MUSB2_WRITE_1(sc, MUSB2_REG_TXHUBPORT(td->channel), + td->hport); + + /* TX NAK timeout */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, MAX_NAK_TO); + + /* Protocol, speed, device endpoint */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXTI, td->transfer_type); + + /* Max packet size */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, td->max_packet); + + if (!td->transaction_started) { + csrh = MUSB2_READ_1(sc, MUSB2_REG_TXCSRH); + DPRINTFN(4, "csrh=0x%02x\n", csrh); + + csrh |= MUSB2_MASK_CSRH_TXDT_WREN; + if (td->toggle) + csrh |= MUSB2_MASK_CSRH_TXDT_VAL; + else + csrh &= ~MUSB2_MASK_CSRH_TXDT_VAL; + + /* Set data toggle */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH, csrh); + } + + /* write command */ + MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, + MUSB2_MASK_CSRL_TXPKTRDY); + + /* Update Data Toggle */ + td->toggle ^= 1; + td->transaction_started = 1; + + return (1); /* not complete */ +} + +static uint8_t musbotg_xfer_do_fifo(struct usb_xfer *xfer) { struct musbotg_softc *sc; struct musbotg_td *td; DPRINTFN(8, "\n"); + sc = MUSBOTG_BUS2SC(xfer->xroot->bus); td = xfer->td_transfer_cache; while (1) { + if ((td->func) (td)) { /* operation in progress */ break; } + if (((void *)td) == xfer->td_transfer_last) { goto done; } @@ -944,13 +2140,10 @@ td = td->obj_next; xfer->td_transfer_cache = td; } + return (1); /* not complete */ - done: - sc = MUSBOTG_BUS2SC(xfer->xroot->bus); - /* compute all actual lengths */ - musbotg_standard_done(xfer); return (0); /* complete */ @@ -1000,8 +2193,20 @@ } void -musbotg_interrupt(struct musbotg_softc *sc) +musbotg_connect_interrupt(struct musbotg_softc *sc) { + USB_BUS_LOCK(&sc->sc_bus); + sc->sc_flags.change_connect = 1; + + /* complete root HUB interrupt endpoint */ + musbotg_root_intr(sc); + USB_BUS_UNLOCK(&sc->sc_bus); +} + +void +musbotg_interrupt(struct musbotg_softc *sc, + uint16_t rxstat, uint16_t txstat, uint8_t stat) +{ uint16_t rx_status; uint16_t tx_status; uint8_t usb_status; @@ -1018,11 +2223,20 @@ /* read all FIFO interrupts */ rx_status = MUSB2_READ_2(sc, MUSB2_REG_INTRX); tx_status = MUSB2_READ_2(sc, MUSB2_REG_INTTX); + rx_status |= rxstat; + tx_status |= txstat; + usb_status |= stat; + /* Clear platform flags after first time */ + rxstat = 0; + txstat = 0; + stat = 0; + /* check for any bus state change interrupts */ if (usb_status & (MUSB2_MASK_IRESET | - MUSB2_MASK_IRESUME | MUSB2_MASK_ISUSP)) { + MUSB2_MASK_IRESUME | MUSB2_MASK_ISUSP | + MUSB2_MASK_ICONN | MUSB2_MASK_IDISC)) { DPRINTFN(4, "real bus interrupt 0x%08x\n", usb_status); @@ -1085,6 +2299,21 @@ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp); } } + if (usb_status & + (MUSB2_MASK_ICONN | MUSB2_MASK_IDISC)) + sc->sc_flags.change_connect = 1; + + /* + * Host Mode: There is no IRESET so assume bus is + * always in reset state once device is connected. + */ + if (sc->sc_mode == MUSB2_HOST_MODE) { + if (usb_status & MUSB2_MASK_ICONN) + sc->sc_flags.status_bus_reset = 1; + if (usb_status & MUSB2_MASK_IDISC) + sc->sc_flags.status_bus_reset = 0; + } + /* complete root HUB interrupt endpoint */ musbotg_root_intr(sc); } @@ -1122,9 +2351,15 @@ td->offset = temp->offset; td->remainder = temp->len; td->error = 0; + td->transaction_started = 0; td->did_stall = temp->did_stall; td->short_pkt = temp->short_pkt; td->alt_next = temp->setup_alt_next; + td->channel = temp->channel; + td->dev_addr = temp->dev_addr; + td->haddr = temp->haddr; + td->hport = temp->hport; + td->transfer_type = temp->transfer_type; } static void @@ -1135,11 +2370,18 @@ struct musbotg_td *td; uint32_t x; uint8_t ep_no; + uint8_t xfer_type; + enum usb_dev_speed speed; + int tx; + int dev_addr; DPRINTFN(8, "addr=%d endpt=%d sumlen=%d speed=%d\n", xfer->address, UE_GET_ADDR(xfer->endpointno), xfer->sumlen, usbd_get_speed(xfer->xroot->udev)); + sc = MUSBOTG_BUS2SC(xfer->xroot->bus); + ep_no = (xfer->endpointno & UE_ADDR); + temp.max_frame_size = xfer->max_frame_size; td = xfer->td_start[0]; @@ -1147,23 +2389,75 @@ xfer->td_transfer_cache = td; /* setup temp */ + dev_addr = xfer->address; + xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE; + temp.pc = NULL; temp.td = NULL; temp.td_next = xfer->td_start[0]; temp.offset = 0; temp.setup_alt_next = xfer->flags_int.short_frames_ok; temp.did_stall = !xfer->flags_int.control_stall; + temp.channel = -1; + temp.dev_addr = dev_addr; + temp.haddr = xfer->xroot->udev->hs_hub_addr; + temp.hport = xfer->xroot->udev->hs_port_no; - sc = MUSBOTG_BUS2SC(xfer->xroot->bus); - ep_no = (xfer->endpointno & UE_ADDR); + if (xfer->flags_int.usb_mode == USB_MODE_HOST) { + speed = usbd_get_speed(xfer->xroot->udev); + xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE; + switch (speed) { + case USB_SPEED_LOW: + temp.transfer_type = MUSB2_MASK_TI_SPEED_LO; + break; + case USB_SPEED_FULL: + temp.transfer_type = MUSB2_MASK_TI_SPEED_FS; + break; + case USB_SPEED_HIGH: + temp.transfer_type = MUSB2_MASK_TI_SPEED_HS; + break; + default: + temp.transfer_type = 0; + DPRINTFN(-1, "Invalid USB speed: %d\n", speed); + break; + } + + switch (xfer_type) { + case UE_CONTROL: + temp.transfer_type |= MUSB2_MASK_TI_PROTO_CTRL; + break; + case UE_ISOCHRONOUS: + temp.transfer_type |= MUSB2_MASK_TI_PROTO_ISOC; + break; + case UE_BULK: + temp.transfer_type |= MUSB2_MASK_TI_PROTO_BULK; + break; + case UE_INTERRUPT: + temp.transfer_type |= MUSB2_MASK_TI_PROTO_INTR; + break; + default: + DPRINTFN(-1, "Invalid USB transfer type: %d\n", + xfer_type); + break; + } + + temp.transfer_type |= ep_no; + td->max_packet = xfer->max_packet_size; + td->toggle = xfer->endpoint->toggle_next; + } + /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { - temp.func = &musbotg_setup_rx; + if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) + temp.func = &musbotg_dev_ctrl_setup_rx; + else + temp.func = &musbotg_host_ctrl_setup_tx; + temp.len = xfer->frlengths[0]; temp.pc = xfer->frbuffers + 0; temp.short_pkt = temp.len ? 1 : 0; @@ -1176,16 +2470,38 @@ } if (x != xfer->nframes) { - if (xfer->endpointno & UE_DIR_IN) { - if (xfer->flags_int.control_xfr) - temp.func = &musbotg_setup_data_tx; - else - temp.func = &musbotg_data_tx; + tx = 0; + + if (xfer->endpointno & UE_DIR_IN) + tx = 1; + + if (xfer->flags_int.usb_mode == USB_MODE_HOST) { + tx = !tx; + + if (tx) { + if (xfer->flags_int.control_xfr) + temp.func = &musbotg_host_ctrl_data_tx; + else + temp.func = &musbotg_host_data_tx; + } else { + if (xfer->flags_int.control_xfr) + temp.func = &musbotg_host_ctrl_data_rx; + else + temp.func = &musbotg_host_data_rx; + } + } else { - if (xfer->flags_int.control_xfr) - temp.func = &musbotg_setup_data_rx; - else - temp.func = &musbotg_data_rx; + if (tx) { + if (xfer->flags_int.control_xfr) + temp.func = &musbotg_dev_ctrl_data_tx; + else + temp.func = &musbotg_dev_data_tx; + } else { + if (xfer->flags_int.control_xfr) + temp.func = &musbotg_dev_ctrl_data_rx; + else + temp.func = &musbotg_dev_data_rx; + } } /* setup "pc" pointer */ @@ -1246,7 +2562,14 @@ * Send a DATA1 message and invert the current * endpoint direction. */ - temp.func = &musbotg_setup_status; + if (sc->sc_mode == MUSB2_DEVICE_MODE) + temp.func = &musbotg_dev_ctrl_status; + else { + if (xfer->endpointno & UE_DIR_IN) + temp.func = musbotg_host_ctrl_status_tx; + else + temp.func = musbotg_host_ctrl_status_rx; + } musbotg_setup_standard_chain_sub(&temp); } } @@ -1269,18 +2592,21 @@ } static void -musbotg_ep_int_set(struct usb_xfer *xfer, uint8_t on) +musbotg_ep_int_set(struct musbotg_softc *sc, int channel, int on) { - struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus); uint16_t temp; - uint8_t ep_no = xfer->endpointno & UE_ADDR; /* * Only enable the endpoint interrupt when we are * actually waiting for data, hence we are dealing * with level triggered interrupts ! */ - if (ep_no == 0) { + DPRINTFN(1, "ep_no=%d, on=%d\n", channel, on); + + if (channel == -1) + return; + + if (channel == 0) { temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE); if (on) temp |= MUSB2_MASK_EPINT(0); @@ -1289,23 +2615,23 @@ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp); } else { - if (USB_GET_DATA_ISREAD(xfer)) { - temp = MUSB2_READ_2(sc, MUSB2_REG_INTRXE); - if (on) - temp |= MUSB2_MASK_EPINT(ep_no); - else - temp &= ~MUSB2_MASK_EPINT(ep_no); - MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, temp); + temp = MUSB2_READ_2(sc, MUSB2_REG_INTRXE); + if (on) + temp |= MUSB2_MASK_EPINT(channel); + else + temp &= ~MUSB2_MASK_EPINT(channel); + MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, temp); - } else { - temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE); - if (on) - temp |= MUSB2_MASK_EPINT(ep_no); - else - temp &= ~MUSB2_MASK_EPINT(ep_no); - MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp); - } + temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE); + if (on) + temp |= MUSB2_MASK_EPINT(channel); + else + temp &= ~MUSB2_MASK_EPINT(channel); + MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp); } + + if (sc->sc_ep_int_set) + sc->sc_ep_int_set(sc, channel, on); } static void @@ -1316,8 +2642,6 @@ /* poll one time */ if (musbotg_xfer_do_fifo(xfer)) { - musbotg_ep_int_set(xfer, 1); - DPRINTFN(14, "enabled interrupts on endpoint\n"); /* put transfer on interrupt queue */ @@ -1359,6 +2683,8 @@ do { len = td->remainder; + xfer->endpoint->toggle_next = td->toggle; + if (xfer->aframes != xfer->nframes) { /* * Verify the length and subtract @@ -1461,17 +2787,22 @@ static void musbotg_device_done(struct usb_xfer *xfer, usb_error_t error) { + struct musbotg_td *td; + struct musbotg_softc *sc; + USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); - DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n", + DPRINTFN(1, "xfer=%p, endpoint=%p, error=%d\n", xfer, xfer->endpoint, error); - if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { + DPRINTFN(14, "disabled interrupts on endpoint\n"); - musbotg_ep_int_set(xfer, 0); + sc = MUSBOTG_BUS2SC(xfer->xroot->bus); + td = xfer->td_transfer_cache; - DPRINTFN(14, "disabled interrupts on endpoint\n"); - } + if (td && (td->channel != -1)) + musbotg_channel_free(sc, td); + /* dequeue transfer and start next transfer */ usbd_transfer_done(xfer, error); } @@ -1716,11 +3047,15 @@ if (sc->sc_clocks_on) { (sc->sc_clocks_on) (sc->sc_clocks_arg); } + /* wait a little for things to stabilise */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); /* disable all interrupts */ + temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL); + DPRINTF("pre-DEVCTL=0x%02x\n", temp); + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0); MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0); MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0); @@ -1732,6 +3067,7 @@ /* wait a little bit (10ms) */ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100); + /* disable double packet buffering */ MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, 0xFFFF); MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, 0xFFFF); @@ -1741,11 +3077,20 @@ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, MUSB2_MASK_HSENAB | MUSB2_MASK_ISOUPD); - /* clear Session bit, if set */ + if (sc->sc_mode == MUSB2_DEVICE_MODE) { + /* clear Session bit, if set */ + temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL); + temp &= ~MUSB2_MASK_SESS; + MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp); + } else { + /* Enter session for Host mode */ + temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL); + temp |= MUSB2_MASK_SESS; + MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp); + } - temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL); - temp &= ~MUSB2_MASK_SESS; - MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp); + /* wait a little for things to stabilise */ + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10); DPRINTF("DEVCTL=0x%02x\n", temp); @@ -1881,8 +3226,11 @@ /* turn on default interrupts */ - MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, - MUSB2_MASK_IRESET); + if (sc->sc_mode == MUSB2_HOST_MODE) + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0xff); + else + MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, + MUSB2_MASK_IRESET); musbotg_clocks_off(sc); @@ -2229,6 +3577,7 @@ uint16_t len; uint16_t value; uint16_t index; + uint8_t reg; usb_error_t err; USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED); @@ -2492,20 +3841,33 @@ switch (value) { case UHF_PORT_SUSPEND: - musbotg_wakeup_peer(sc); + if (sc->sc_mode == MUSB2_HOST_MODE) + musbotg_wakeup_host(sc); + else + musbotg_wakeup_peer(sc); break; case UHF_PORT_ENABLE: sc->sc_flags.port_enabled = 0; break; - case UHF_PORT_TEST: - case UHF_PORT_INDICATOR: case UHF_C_PORT_ENABLE: + sc->sc_flags.change_enabled = 0; + break; + case UHF_C_PORT_OVER_CURRENT: + sc->sc_flags.change_over_current = 0; + break; + case UHF_C_PORT_RESET: + sc->sc_flags.change_reset = 0; + break; + + case UHF_PORT_TEST: + case UHF_PORT_INDICATOR: /* nops */ break; + case UHF_PORT_POWER: sc->sc_flags.port_powered = 0; musbotg_pull_down(sc); @@ -2534,7 +3896,35 @@ sc->sc_flags.port_enabled = 1; break; case UHF_PORT_SUSPEND: + if (sc->sc_mode == MUSB2_HOST_MODE) + musbotg_suspend_host(sc); + break; + case UHF_PORT_RESET: + if (sc->sc_mode == MUSB2_HOST_MODE) { + reg = MUSB2_READ_1(sc, MUSB2_REG_POWER); + reg |= MUSB2_MASK_RESET; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, reg); + + /* Wait for 20 msec */ + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 5); + + reg = MUSB2_READ_1(sc, MUSB2_REG_POWER); + reg &= ~MUSB2_MASK_RESET; + MUSB2_WRITE_1(sc, MUSB2_REG_POWER, reg); + + /* determine line speed */ + reg = MUSB2_READ_1(sc, MUSB2_REG_POWER); + if (reg & MUSB2_MASK_HSMODE) + sc->sc_flags.status_high_speed = 1; + else + sc->sc_flags.status_high_speed = 0; + + sc->sc_flags.change_reset = 1; + } else + err = USB_ERR_IOERROR; + break; + case UHF_PORT_TEST: case UHF_PORT_INDICATOR: /* nops */ @@ -2564,7 +3954,10 @@ } /* Select Device Side Mode */ - value = UPS_PORT_MODE_DEVICE; + if (sc->sc_mode == MUSB2_DEVICE_MODE) + value = UPS_PORT_MODE_DEVICE; + else + value = 0; if (sc->sc_flags.status_high_speed) { value |= UPS_HIGH_SPEED; @@ -2575,6 +3968,10 @@ if (sc->sc_flags.port_enabled) { value |= UPS_PORT_ENABLED; } + + if (sc->sc_flags.port_over_current) + value |= UPS_OVERCURRENT_INDICATOR; + if (sc->sc_flags.status_vbus && sc->sc_flags.status_bus_reset) { value |= UPS_CURRENT_CONNECT_STATUS; @@ -2589,16 +3986,22 @@ if (sc->sc_flags.change_connect) { value |= UPS_C_CONNECT_STATUS; - if (sc->sc_flags.status_vbus && - sc->sc_flags.status_bus_reset) { - /* reset EP0 state */ - sc->sc_ep0_busy = 0; - sc->sc_ep0_cmd = 0; + if (sc->sc_mode == MUSB2_DEVICE_MODE) { + if (sc->sc_flags.status_vbus && + sc->sc_flags.status_bus_reset) { + /* reset EP0 state */ + sc->sc_ep0_busy = 0; + sc->sc_ep0_cmd = 0; + } } } - if (sc->sc_flags.change_suspend) { + if (sc->sc_flags.change_suspend) value |= UPS_C_SUSPEND; - } + if (sc->sc_flags.change_reset) + value |= UPS_C_PORT_RESET; + if (sc->sc_flags.change_over_current) + value |= UPS_C_OVERCURRENT_INDICATOR; + USETW(sc->sc_hub_temp.ps.wPortChange, value); len = sizeof(sc->sc_hub_temp.ps); goto tr_valid; @@ -2734,6 +4137,17 @@ } static void +musbotg_get_dma_delay(struct usb_device *udev, uint32_t *pus) +{ + struct musbotg_softc *sc = MUSBOTG_BUS2SC(udev->bus); + + if (sc->sc_mode == MUSB2_HOST_MODE) + *pus = 2000; /* microseconds */ + else + *pus = 0; +} + +static void musbotg_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep) { @@ -2745,12 +4159,6 @@ sc->sc_rt_addr); if (udev->device_index != sc->sc_rt_addr) { - - if ((udev->speed != USB_SPEED_FULL) && - (udev->speed != USB_SPEED_HIGH)) { - /* not supported */ - return; - } switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_CONTROL: ep->methods = &musbotg_device_ctrl_methods; @@ -2794,6 +4202,7 @@ struct usb_bus_methods musbotg_bus_methods = { .endpoint_init = &musbotg_ep_init, + .get_dma_delay = &musbotg_get_dma_delay, .xfer_setup = &musbotg_xfer_setup, .xfer_unsetup = &musbotg_xfer_unsetup, .get_hw_ep_profile = &musbotg_get_hw_ep_profile, Index: dev/usb/controller/musb_otg.h =================================================================== --- dev/usb/controller/musb_otg.h (revision 252430) +++ dev/usb/controller/musb_otg.h (working copy) @@ -32,7 +32,7 @@ #ifndef _MUSB2_OTG_H_ #define _MUSB2_OTG_H_ -#define MUSB2_MAX_DEVICES (USB_MIN_DEVICES + 1) +#define MUSB2_MAX_DEVICES USB_MAX_DEVICES /* Common registers */ @@ -105,7 +105,8 @@ #define MUSB2_MASK_CSRL_TXSENTSTALL 0x20/* Device Mode */ #define MUSB2_MASK_CSRL_TXSTALLED 0x20 /* Host Mode */ #define MUSB2_MASK_CSRL_TXDT_CLR 0x40 -#define MUSB2_MASK_CSRL_TXINCOMP 0x80 +#define MUSB2_MASK_CSRL_TXINCOMP 0x80 /* Device mode */ +#define MUSB2_MASK_CSRL_TXNAKTO 0x80 /* Host mode */ /* Device Side Mode */ #define MUSB2_MASK_CSR0L_RXPKTRDY 0x01 @@ -118,6 +119,7 @@ #define MUSB2_MASK_CSR0L_SETUPEND_CLR 0x80 /* Host Side Mode */ +#define MUSB2_MASK_CSR0L_TXFIFONEMPTY 0x02 #define MUSB2_MASK_CSR0L_RXSTALL 0x04 #define MUSB2_MASK_CSR0L_SETUPPKT 0x08 #define MUSB2_MASK_CSR0L_ERROR 0x10 @@ -127,7 +129,7 @@ #define MUSB2_REG_TXCSRH (0x0003 + MUSB2_REG_INDEXED_CSR) #define MUSB2_MASK_CSRH_TXDT_VAL 0x01 /* Host Mode */ -#define MUSB2_MASK_CSRH_TXDT_WR 0x02 /* Host Mode */ +#define MUSB2_MASK_CSRH_TXDT_WREN 0x02 /* Host Mode */ #define MUSB2_MASK_CSRH_TXDMAREQMODE 0x04 #define MUSB2_MASK_CSRH_TXDT_SWITCH 0x08 #define MUSB2_MASK_CSRH_TXDMAREQENA 0x10 @@ -138,14 +140,16 @@ #define MUSB2_MASK_CSR0H_FFLUSH 0x01 /* Device Side flush FIFO */ #define MUSB2_MASK_CSR0H_DT 0x02 /* Host Side data toggle */ -#define MUSB2_MASK_CSR0H_DT_SET 0x04 /* Host Side */ +#define MUSB2_MASK_CSR0H_DT_WREN 0x04 /* Host Side */ #define MUSB2_MASK_CSR0H_PING_DIS 0x08 /* Host Side */ #define MUSB2_REG_RXCSRL (0x0006 + MUSB2_REG_INDEXED_CSR) #define MUSB2_MASK_CSRL_RXPKTRDY 0x01 #define MUSB2_MASK_CSRL_RXFIFOFULL 0x02 -#define MUSB2_MASK_CSRL_RXOVERRUN 0x04 -#define MUSB2_MASK_CSRL_RXDATAERR 0x08 +#define MUSB2_MASK_CSRL_RXOVERRUN 0x04 /* Device Mode */ +#define MUSB2_MASK_CSRL_RXERROR 0x04 /* Host Mode */ +#define MUSB2_MASK_CSRL_RXDATAERR 0x08 /* Device Mode */ +#define MUSB2_MASK_CSRL_RXNAKTO 0x08 /* Host Mode */ #define MUSB2_MASK_CSRL_RXFFLUSH 0x10 #define MUSB2_MASK_CSRL_RXSENDSTALL 0x20/* Device Mode */ #define MUSB2_MASK_CSRL_RXREQPKT 0x20 /* Host Mode */ @@ -156,7 +160,7 @@ #define MUSB2_REG_RXCSRH (0x0007 + MUSB2_REG_INDEXED_CSR) #define MUSB2_MASK_CSRH_RXINCOMP 0x01 #define MUSB2_MASK_CSRH_RXDT_VAL 0x02 /* Host Mode */ -#define MUSB2_MASK_CSRH_RXDT_SET 0x04 /* Host Mode */ +#define MUSB2_MASK_CSRH_RXDT_WREN 0x04 /* Host Mode */ #define MUSB2_MASK_CSRH_RXDMAREQMODE 0x08 #define MUSB2_MASK_CSRH_RXNYET 0x10 #define MUSB2_MASK_CSRH_RXDMAREQENA 0x20 @@ -273,10 +277,13 @@ #define MUSB2_REG_TXHUBPORT(n) (0x0083 + (8*(n))) #define MUSB2_REG_RXFADDR(n) (0x0084 + (8*(n))) #define MUSB2_REG_RXHADDR(n) (0x0086 + (8*(n))) -#define MUSB2_REG_RXHPORT(n) (0x0087 + (8*(n))) +#define MUSB2_REG_RXHUBPORT(n) (0x0087 + (8*(n))) #define MUSB2_EP_MAX 16 /* maximum number of endpoints */ +#define MUSB2_DEVICE_MODE 0 +#define MUSB2_HOST_MODE 1 + #define MUSB2_READ_2(sc, reg) \ bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) @@ -310,12 +317,20 @@ uint32_t remainder; uint16_t max_frame_size; /* packet_size * mult */ uint8_t ep_no; + uint8_t transfer_type; + uint8_t max_packet; uint8_t error:1; uint8_t alt_next:1; uint8_t short_pkt:1; uint8_t support_multi_buffer:1; uint8_t did_stall:1; uint8_t dma_enabled:1; + uint8_t transaction_started:1; + uint8_t dev_addr; + uint8_t toggle; + int8_t channel; + uint8_t haddr; + uint8_t hport; }; struct musbotg_std_temp { @@ -333,6 +348,11 @@ */ uint8_t setup_alt_next; uint8_t did_stall; + uint8_t dev_addr; + int8_t channel; + uint8_t haddr; + uint8_t hport; + uint8_t transfer_type; }; struct musbotg_config_desc { @@ -349,6 +369,9 @@ struct musbotg_flags { uint8_t change_connect:1; uint8_t change_suspend:1; + uint8_t change_reset:1; + uint8_t change_over_current:1; + uint8_t change_enabled:1; uint8_t status_suspend:1; /* set if suspended */ uint8_t status_vbus:1; /* set if present */ uint8_t status_bus_reset:1; /* set if reset complete */ @@ -358,6 +381,7 @@ uint8_t clocks_off:1; uint8_t port_powered:1; uint8_t port_enabled:1; + uint8_t port_over_current:1; uint8_t d_pulled_up:1; }; @@ -376,6 +400,7 @@ void (*sc_clocks_on) (void *arg); void (*sc_clocks_off) (void *arg); + void (*sc_ep_int_set) (struct musbotg_softc *sc, int ep, int on); void *sc_clocks_arg; uint32_t sc_bounce_buf[(1024 * 3) / 4]; /* bounce buffer */ @@ -390,15 +415,21 @@ uint8_t sc_conf_data; /* copy of hardware register */ uint8_t sc_hub_idata[1]; + uint16_t sc_channel_mask; /* 16 endpoints */ struct musbotg_flags sc_flags; + uint8_t sc_id; + uint8_t sc_mode; + void *sc_platform_data; }; /* prototypes */ usb_error_t musbotg_init(struct musbotg_softc *sc); void musbotg_uninit(struct musbotg_softc *sc); -void musbotg_interrupt(struct musbotg_softc *sc); +void musbotg_interrupt(struct musbotg_softc *sc, + uint16_t rxstat, uint16_t txstat, uint8_t stat); void musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on); +void musbotg_connect_interrupt(struct musbotg_softc *sc); #endif /* _MUSB2_OTG_H_ */ Index: dev/usb/controller/musb_otg_atmelarm.c =================================================================== --- dev/usb/controller/musb_otg_atmelarm.c (revision 252430) +++ dev/usb/controller/musb_otg_atmelarm.c (working copy) @@ -94,6 +94,26 @@ #endif } +static void +musbotg_wrapper_interrupt(void *arg) +{ + + /* + * Nothing to do. + * Main driver takes care about everything + */ + musbotg_interrupt(arg, 0, 0, 0); +} + +static void +musbotg_ep_int_set(struct musbotg_softc *sc, int ep, int on) +{ + /* + * Nothing to do. + * Main driver takes care about everything + */ +} + static int musbotg_probe(device_t dev) { @@ -147,12 +167,16 @@ } device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus); + sc->sc_otg.sc_id = 0; + sc->sc_otg.sc_platform_data = sc; + sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE; + #if (__FreeBSD_version >= 700031) err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, - NULL, (driver_intr_t *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); + NULL, (driver_intr_t *)musbotg_wrapper_interrupt, sc, &sc->sc_otg.sc_intr_hdl); #else err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, - (driver_intr_t *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl); + (driver_intr_t *)musbotg_wrapper_interrupt, sc, &sc->sc_otg.sc_intr_hdl); #endif if (err) { sc->sc_otg.sc_intr_hdl = NULL;