[PATCH 3/6] Import am335x usb driver file from FreeBSD.
Sichen Zhao
1473996754 at qq.com
Tue Jul 11 10:53:19 UTC 2017
---
freebsd/sys/arm/ti/am335x/am335x_musb.c | 421 +++
freebsd/sys/arm/ti/am335x/am335x_prcm.c | 857 ++++++
freebsd/sys/arm/ti/am335x/am335x_scm.h | 49 +
freebsd/sys/arm/ti/am335x/am335x_usbss.c | 226 ++
freebsd/sys/arm/ti/ti_cpuid.h | 83 +
freebsd/sys/arm/ti/ti_prcm.c | 357 +++
freebsd/sys/arm/ti/ti_prcm.h | 207 ++
freebsd/sys/arm/ti/ti_scm.c | 176 ++
freebsd/sys/arm/ti/ti_scm.h | 56 +
freebsd/sys/arm/ti/tivar.h | 41 +
freebsd/sys/dev/usb/controller/musb_otg.c | 4257 +++++++++++++++++++++++++++++
freebsd/sys/dev/usb/controller/musb_otg.h | 435 +++
12 files changed, 7165 insertions(+)
create mode 100644 freebsd/sys/arm/ti/am335x/am335x_musb.c
create mode 100644 freebsd/sys/arm/ti/am335x/am335x_prcm.c
create mode 100644 freebsd/sys/arm/ti/am335x/am335x_scm.h
create mode 100644 freebsd/sys/arm/ti/am335x/am335x_usbss.c
create mode 100644 freebsd/sys/arm/ti/ti_cpuid.h
create mode 100644 freebsd/sys/arm/ti/ti_prcm.c
create mode 100644 freebsd/sys/arm/ti/ti_prcm.h
create mode 100644 freebsd/sys/arm/ti/ti_scm.c
create mode 100644 freebsd/sys/arm/ti/ti_scm.h
create mode 100644 freebsd/sys/arm/ti/tivar.h
create mode 100644 freebsd/sys/dev/usb/controller/musb_otg.c
create mode 100644 freebsd/sys/dev/usb/controller/musb_otg.h
diff --git a/freebsd/sys/arm/ti/am335x/am335x_musb.c b/freebsd/sys/arm/ti/am335x/am335x_musb.c
new file mode 100644
index 0000000..52d799c
--- /dev/null
+++ b/freebsd/sys/arm/ti/am335x/am335x_musb.c
@@ -0,0 +1,421 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo at freebsd.org>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#define USB_DEBUG_VAR usbssdebug
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/musb_otg.h>
+#include <dev/usb/usb_debug.h>
+
+#include <sys/rman.h>
+
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/ti_scm.h>
+#include <arm/ti/am335x/am335x_scm.h>
+
+#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_USBCORE 0
+#define RES_USBCTRL 1
+
+#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 USBCTRL_WRITE4(sc, reg, val) \
+ USB_WRITE4((sc), RES_USBCTRL, (reg), (val))
+#define USBCTRL_READ4(sc, reg) \
+ USB_READ4((sc), RES_USBCTRL, (reg))
+
+static struct resource_spec am335x_musbotg_mem_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, 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;
+ struct resource *sc_mem_res[2];
+ int sc_irq_rid;
+};
+
+static void
+musbotg_vbus_poll(struct musbotg_super_softc *sc)
+{
+ uint32_t stat;
+
+ if (sc->sc_otg.sc_mode == MUSB2_DEVICE_MODE)
+ musbotg_vbus_interrupt(&sc->sc_otg, 1);
+ else {
+ stat = USBCTRL_READ4(sc, USBCTRL_STAT);
+ musbotg_vbus_interrupt(&sc->sc_otg, 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)
+{
+ struct musbotg_softc *sc;
+ uint32_t c, reg;
+
+ sc = arg;
+ reg = USB_CTRL[sc->sc_id];
+
+ 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)
+{
+ struct musbotg_softc *sc;
+ uint32_t c, reg;
+
+ sc = arg;
+ reg = USB_CTRL[sc->sc_id];
+
+ /* 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, USBCTRL_INTEN_SET0, epmask);
+ else
+ USBCTRL_WRITE4(ssc, USBCTRL_INTEN_CLR0, epmask);
+}
+
+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, USBCTRL_STAT);
+ stat0 = USBCTRL_READ4(ssc, USBCTRL_IRQ_STAT0);
+ stat1 = USBCTRL_READ4(ssc, USBCTRL_IRQ_STAT1);
+ if (stat0)
+ USBCTRL_WRITE4(ssc, USBCTRL_IRQ_STAT0, stat0);
+ if (stat1)
+ USBCTRL_WRITE4(ssc, 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_status_okay(dev))
+ return (ENXIO);
+
+ 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);
+ char mode[16];
+ int err;
+ uint32_t reg;
+
+ sc->sc_otg.sc_id = device_get_unit(dev);
+
+ /* 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 */
+ sc->sc_otg.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->sc_irq_rid, RF_ACTIVE);
+ if (sc->sc_otg.sc_irq_res == NULL) {
+ device_printf(dev,
+ "Error: could not allocate irq resources\n");
+ return (ENXIO);
+ }
+
+ /* setup MUSB OTG USB controller interface softc */
+ sc->sc_otg.sc_clocks_on = &musbotg_clocks_on;
+ sc->sc_otg.sc_clocks_off = &musbotg_clocks_off;
+ sc->sc_otg.sc_clocks_arg = &sc->sc_otg;
+
+ sc->sc_otg.sc_ep_int_set = musbotg_ep_int_set;
+
+ /* initialise some bus fields */
+ sc->sc_otg.sc_bus.parent = dev;
+ sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices;
+ sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES;
+ sc->sc_otg.sc_bus.dma_bits = 32;
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_otg.sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ device_printf(dev,
+ "Failed allocate bus mem for musb\n");
+ return (ENOMEM);
+ }
+ sc->sc_otg.sc_io_res = sc->sc_mem_res[RES_USBCORE];
+ sc->sc_otg.sc_io_tag =
+ rman_get_bustag(sc->sc_otg.sc_io_res);
+ sc->sc_otg.sc_io_hdl =
+ rman_get_bushandle(sc->sc_otg.sc_io_res);
+ sc->sc_otg.sc_io_size =
+ rman_get_size(sc->sc_otg.sc_io_res);
+
+ sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!(sc->sc_otg.sc_bus.bdev)) {
+ device_printf(dev, "No busdev for musb\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_otg.sc_bus.bdev,
+ &sc->sc_otg.sc_bus);
+
+ err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res,
+ INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)musbotg_wrapper_interrupt,
+ &sc->sc_otg, &sc->sc_otg.sc_intr_hdl);
+ if (err) {
+ sc->sc_otg.sc_intr_hdl = NULL;
+ device_printf(dev,
+ "Failed to setup interrupt for musb\n");
+ goto error;
+ }
+
+ sc->sc_otg.sc_platform_data = sc;
+ if (OF_getprop(ofw_bus_get_node(dev), "dr_mode", mode,
+ sizeof(mode)) > 0) {
+ if (strcasecmp(mode, "host") == 0)
+ sc->sc_otg.sc_mode = MUSB2_HOST_MODE;
+ else
+ sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE;
+ } else {
+ /* Beaglebone defaults: USB0 device, USB1 HOST. */
+ if (sc->sc_otg.sc_id == 0)
+ sc->sc_otg.sc_mode = MUSB2_DEVICE_MODE;
+ else
+ sc->sc_otg.sc_mode = MUSB2_HOST_MODE;
+ }
+
+ /*
+ * software-controlled function
+ */
+
+ if (sc->sc_otg.sc_mode == MUSB2_HOST_MODE) {
+ reg = USBCTRL_READ4(sc, USBCTRL_MODE);
+ reg |= USBCTRL_MODE_IDDIGMUX;
+ reg &= ~USBCTRL_MODE_IDDIG;
+ USBCTRL_WRITE4(sc, USBCTRL_MODE, reg);
+ USBCTRL_WRITE4(sc, USBCTRL_UTMI,
+ USBCTRL_UTMI_FSDATAEXT);
+ } else {
+ reg = USBCTRL_READ4(sc, USBCTRL_MODE);
+ reg |= USBCTRL_MODE_IDDIGMUX;
+ reg |= USBCTRL_MODE_IDDIG;
+ USBCTRL_WRITE4(sc, USBCTRL_MODE, reg);
+ }
+
+ reg = USBCTRL_INTEN_USB_ALL & ~USBCTRL_INTEN_USB_SOF;
+ USBCTRL_WRITE4(sc, USBCTRL_INTEN_SET1, reg);
+ USBCTRL_WRITE4(sc, USBCTRL_INTEN_CLR0, 0xffffffff);
+
+ err = musbotg_init(&sc->sc_otg);
+ if (!err)
+ err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev);
+
+ if (err)
+ goto error;
+
+ /* poll VBUS one time */
+ musbotg_vbus_poll(sc);
+
+ return (0);
+
+error:
+ musbotg_detach(dev);
+ return (ENXIO);
+}
+
+static int
+musbotg_detach(device_t dev)
+{
+ struct musbotg_super_softc *sc = device_get_softc(dev);
+ int err;
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) {
+ /*
+ * only call musbotg_uninit() after musbotg_init()
+ */
+ musbotg_uninit(&sc->sc_otg);
+
+ err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res,
+ sc->sc_otg.sc_intr_hdl);
+ sc->sc_otg.sc_intr_hdl = NULL;
+ }
+
+ usb_bus_mem_free_all(&sc->sc_otg.sc_bus, 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_otg.sc_irq_res)
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+ sc->sc_otg.sc_irq_res);
+
+ 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, usbss, musbotg_driver, musbotg_devclass, 0, 0);
+MODULE_DEPEND(musbotg, usbss, 1, 1, 1);
diff --git a/freebsd/sys/arm/ti/am335x/am335x_prcm.c b/freebsd/sys/arm/ti/am335x/am335x_prcm.c
new file mode 100644
index 0000000..1d10f7f
--- /dev/null
+++ b/freebsd/sys/arm/ti/am335x/am335x_prcm.c
@@ -0,0 +1,857 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2012 Damjan Marion <dmarion at Freebsd.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/ti/tivar.h>
+#include <arm/ti/ti_scm.h>
+#include <arm/ti/ti_prcm.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+
+#include "am335x_scm.h"
+
+#define CM_PER 0
+#define CM_PER_L4LS_CLKSTCTRL (CM_PER + 0x000)
+#define CM_PER_L3S_CLKSTCTRL (CM_PER + 0x004)
+#define CM_PER_L3_CLKSTCTRL (CM_PER + 0x00C)
+#define CM_PER_CPGMAC0_CLKCTRL (CM_PER + 0x014)
+#define CM_PER_LCDC_CLKCTRL (CM_PER + 0x018)
+#define CM_PER_USB0_CLKCTRL (CM_PER + 0x01C)
+#define CM_PER_TPTC0_CLKCTRL (CM_PER + 0x024)
+#define CM_PER_UART5_CLKCTRL (CM_PER + 0x038)
+#define CM_PER_MMC0_CLKCTRL (CM_PER + 0x03C)
+#define CM_PER_I2C2_CLKCTRL (CM_PER + 0x044)
+#define CM_PER_I2C1_CLKCTRL (CM_PER + 0x048)
+#define CM_PER_SPI0_CLKCTRL (CM_PER + 0x04C)
+#define CM_PER_SPI1_CLKCTRL (CM_PER + 0x050)
+#define CM_PER_UART1_CLKCTRL (CM_PER + 0x06C)
+#define CM_PER_UART2_CLKCTRL (CM_PER + 0x070)
+#define CM_PER_UART3_CLKCTRL (CM_PER + 0x074)
+#define CM_PER_UART4_CLKCTRL (CM_PER + 0x078)
+#define CM_PER_TIMER7_CLKCTRL (CM_PER + 0x07C)
+#define CM_PER_TIMER2_CLKCTRL (CM_PER + 0x080)
+#define CM_PER_TIMER3_CLKCTRL (CM_PER + 0x084)
+#define CM_PER_TIMER4_CLKCTRL (CM_PER + 0x088)
+#define CM_PER_GPIO1_CLKCTRL (CM_PER + 0x0AC)
+#define CM_PER_GPIO2_CLKCTRL (CM_PER + 0x0B0)
+#define CM_PER_GPIO3_CLKCTRL (CM_PER + 0x0B4)
+#define CM_PER_TPCC_CLKCTRL (CM_PER + 0x0BC)
+#define CM_PER_EPWMSS1_CLKCTRL (CM_PER + 0x0CC)
+#define CM_PER_EPWMSS0_CLKCTRL (CM_PER + 0x0D4)
+#define CM_PER_EPWMSS2_CLKCTRL (CM_PER + 0x0D8)
+#define CM_PER_L3_INSTR_CLKCTRL (CM_PER + 0x0DC)
+#define CM_PER_L3_CLKCTRL (CM_PER + 0x0E0)
+#define CM_PER_PRUSS_CLKCTRL (CM_PER + 0x0E8)
+#define CM_PER_TIMER5_CLKCTRL (CM_PER + 0x0EC)
+#define CM_PER_TIMER6_CLKCTRL (CM_PER + 0x0F0)
+#define CM_PER_MMC1_CLKCTRL (CM_PER + 0x0F4)
+#define CM_PER_MMC2_CLKCTRL (CM_PER + 0x0F8)
+#define CM_PER_TPTC1_CLKCTRL (CM_PER + 0x0FC)
+#define CM_PER_TPTC2_CLKCTRL (CM_PER + 0x100)
+#define CM_PER_SPINLOCK0_CLKCTRL (CM_PER + 0x10C)
+#define CM_PER_MAILBOX0_CLKCTRL (CM_PER + 0x110)
+#define CM_PER_OCPWP_L3_CLKSTCTRL (CM_PER + 0x12C)
+#define CM_PER_OCPWP_CLKCTRL (CM_PER + 0x130)
+#define CM_PER_CPSW_CLKSTCTRL (CM_PER + 0x144)
+#define CM_PER_PRUSS_CLKSTCTRL (CM_PER + 0x140)
+
+#define CM_WKUP 0x400
+#define CM_WKUP_CLKSTCTRL (CM_WKUP + 0x000)
+#define CM_WKUP_CONTROL_CLKCTRL (CM_WKUP + 0x004)
+#define CM_WKUP_GPIO0_CLKCTRL (CM_WKUP + 0x008)
+#define CM_WKUP_CM_L3_AON_CLKSTCTRL (CM_WKUP + 0x01C)
+#define CM_WKUP_CM_CLKSEL_DPLL_MPU (CM_WKUP + 0x02C)
+#define CM_WKUP_CM_IDLEST_DPLL_DISP (CM_WKUP + 0x048)
+#define CM_WKUP_CM_CLKSEL_DPLL_DISP (CM_WKUP + 0x054)
+#define CM_WKUP_CM_CLKDCOLDO_DPLL_PER (CM_WKUP + 0x07C)
+#define CM_WKUP_CM_CLKMODE_DPLL_DISP (CM_WKUP + 0x098)
+#define CM_WKUP_I2C0_CLKCTRL (CM_WKUP + 0x0B8)
+#define CM_WKUP_ADC_TSC_CLKCTRL (CM_WKUP + 0x0BC)
+
+#define CM_DPLL 0x500
+#define CLKSEL_TIMER7_CLK (CM_DPLL + 0x004)
+#define CLKSEL_TIMER2_CLK (CM_DPLL + 0x008)
+#define CLKSEL_TIMER3_CLK (CM_DPLL + 0x00C)
+#define CLKSEL_TIMER4_CLK (CM_DPLL + 0x010)
+#define CLKSEL_TIMER5_CLK (CM_DPLL + 0x018)
+#define CLKSEL_TIMER6_CLK (CM_DPLL + 0x01C)
+#define CLKSEL_PRUSS_OCP_CLK (CM_DPLL + 0x030)
+
+#define CM_RTC 0x800
+#define CM_RTC_RTC_CLKCTRL (CM_RTC + 0x000)
+#define CM_RTC_CLKSTCTRL (CM_RTC + 0x004)
+
+#define PRM_PER 0xC00
+#define PRM_PER_RSTCTRL (PRM_PER + 0x00)
+
+#define PRM_DEVICE_OFFSET 0xF00
+#define PRM_RSTCTRL (PRM_DEVICE_OFFSET + 0x00)
+
+struct am335x_prcm_softc {
+ struct resource * res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+static struct resource_spec am335x_prcm_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static struct am335x_prcm_softc *am335x_prcm_sc = NULL;
+
+static int am335x_clk_noop_activate(struct ti_clock_dev *clkdev);
+static int am335x_clk_generic_activate(struct ti_clock_dev *clkdev);
+static int am335x_clk_gpio_activate(struct ti_clock_dev *clkdev);
+static int am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev);
+static int am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev);
+static int am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc);
+static int am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+static int am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+static int am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+static int am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq);
+static int am335x_clk_set_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int freq);
+static void am335x_prcm_reset(void);
+static int am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev);
+static int am335x_clk_musb0_activate(struct ti_clock_dev *clkdev);
+static int am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev);
+static int am335x_clk_pruss_activate(struct ti_clock_dev *clkdev);
+
+#define AM335X_NOOP_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = am335x_clk_noop_activate, \
+ .clk_deactivate = am335x_clk_noop_deactivate, \
+ .clk_set_source = am335x_clk_noop_set_source, \
+ .clk_accessible = NULL, \
+ .clk_get_source_freq = NULL, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define AM335X_GENERIC_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = am335x_clk_generic_activate, \
+ .clk_deactivate = am335x_clk_generic_deactivate, \
+ .clk_set_source = am335x_clk_generic_set_source, \
+ .clk_accessible = NULL, \
+ .clk_get_source_freq = NULL, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define AM335X_GPIO_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = am335x_clk_gpio_activate, \
+ .clk_deactivate = am335x_clk_generic_deactivate, \
+ .clk_set_source = am335x_clk_generic_set_source, \
+ .clk_accessible = NULL, \
+ .clk_get_source_freq = NULL, \
+ .clk_set_source_freq = NULL \
+ }
+
+#define AM335X_MMCHS_CLOCK_DEV(i) \
+ { .id = (i), \
+ .clk_activate = am335x_clk_generic_activate, \
+ .clk_deactivate = am335x_clk_generic_deactivate, \
+ .clk_set_source = am335x_clk_generic_set_source, \
+ .clk_accessible = NULL, \
+ .clk_get_source_freq = am335x_clk_hsmmc_get_source_freq, \
+ .clk_set_source_freq = NULL \
+ }
+
+struct ti_clock_dev ti_am335x_clk_devmap[] = {
+ /* System clocks */
+ { .id = SYS_CLK,
+ .clk_activate = NULL,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = am335x_clk_get_sysclk_freq,
+ .clk_set_source_freq = NULL,
+ },
+ /* MPU (ARM) core clocks */
+ { .id = MPU_CLK,
+ .clk_activate = NULL,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = am335x_clk_get_arm_fclk_freq,
+ .clk_set_source_freq = NULL,
+ },
+ /* CPSW Ethernet Switch core clocks */
+ { .id = CPSW_CLK,
+ .clk_activate = am335x_clk_cpsw_activate,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = NULL,
+ .clk_set_source_freq = NULL,
+ },
+
+ /* Mentor USB HS controller core clocks */
+ { .id = MUSB0_CLK,
+ .clk_activate = am335x_clk_musb0_activate,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = NULL,
+ .clk_set_source_freq = NULL,
+ },
+
+ /* LCD controller clocks */
+ { .id = LCDC_CLK,
+ .clk_activate = am335x_clk_lcdc_activate,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = am335x_clk_get_arm_disp_freq,
+ .clk_set_source_freq = am335x_clk_set_arm_disp_freq,
+ },
+
+ /* UART */
+ AM335X_NOOP_CLOCK_DEV(UART1_CLK),
+ AM335X_GENERIC_CLOCK_DEV(UART2_CLK),
+ AM335X_GENERIC_CLOCK_DEV(UART3_CLK),
+ AM335X_GENERIC_CLOCK_DEV(UART4_CLK),
+ AM335X_GENERIC_CLOCK_DEV(UART5_CLK),
+ AM335X_GENERIC_CLOCK_DEV(UART6_CLK),
+
+ /* DMTimer */
+ AM335X_GENERIC_CLOCK_DEV(TIMER2_CLK),
+ AM335X_GENERIC_CLOCK_DEV(TIMER3_CLK),
+ AM335X_GENERIC_CLOCK_DEV(TIMER4_CLK),
+ AM335X_GENERIC_CLOCK_DEV(TIMER5_CLK),
+ AM335X_GENERIC_CLOCK_DEV(TIMER6_CLK),
+ AM335X_GENERIC_CLOCK_DEV(TIMER7_CLK),
+
+ /* GPIO, we use hwmods as reference, not units in spec */
+ AM335X_GPIO_CLOCK_DEV(GPIO1_CLK),
+ AM335X_GPIO_CLOCK_DEV(GPIO2_CLK),
+ AM335X_GPIO_CLOCK_DEV(GPIO3_CLK),
+ AM335X_GPIO_CLOCK_DEV(GPIO4_CLK),
+
+ /* I2C we use hwmods as reference, not units in spec */
+ AM335X_GENERIC_CLOCK_DEV(I2C1_CLK),
+ AM335X_GENERIC_CLOCK_DEV(I2C2_CLK),
+ AM335X_GENERIC_CLOCK_DEV(I2C3_CLK),
+
+ /* McSPI we use hwmods as reference, not units in spec */
+ AM335X_GENERIC_CLOCK_DEV(SPI0_CLK),
+ AM335X_GENERIC_CLOCK_DEV(SPI1_CLK),
+
+ /* TSC_ADC */
+ AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK),
+
+ /* EDMA */
+ AM335X_GENERIC_CLOCK_DEV(EDMA_TPCC_CLK),
+ AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC0_CLK),
+ AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC1_CLK),
+ AM335X_GENERIC_CLOCK_DEV(EDMA_TPTC2_CLK),
+
+ /* MMCHS */
+ AM335X_MMCHS_CLOCK_DEV(MMC1_CLK),
+ AM335X_MMCHS_CLOCK_DEV(MMC2_CLK),
+ AM335X_MMCHS_CLOCK_DEV(MMC3_CLK),
+
+ /* PWMSS */
+ AM335X_GENERIC_CLOCK_DEV(PWMSS0_CLK),
+ AM335X_GENERIC_CLOCK_DEV(PWMSS1_CLK),
+ AM335X_GENERIC_CLOCK_DEV(PWMSS2_CLK),
+
+ /* System Mailbox clock */
+ AM335X_GENERIC_CLOCK_DEV(MAILBOX0_CLK),
+
+ /* SPINLOCK */
+ AM335X_GENERIC_CLOCK_DEV(SPINLOCK0_CLK),
+
+ /* PRU-ICSS */
+ { .id = PRUSS_CLK,
+ .clk_activate = am335x_clk_pruss_activate,
+ .clk_deactivate = NULL,
+ .clk_set_source = NULL,
+ .clk_accessible = NULL,
+ .clk_get_source_freq = NULL,
+ .clk_set_source_freq = NULL,
+ },
+
+ /* RTC */
+ AM335X_GENERIC_CLOCK_DEV(RTC_CLK),
+
+ { INVALID_CLK_IDENT, NULL, NULL, NULL, NULL }
+};
+
+struct am335x_clk_details {
+ clk_ident_t id;
+ uint32_t clkctrl_reg;
+ uint32_t clksel_reg;
+};
+
+#define _CLK_DETAIL(i, c, s) \
+ { .id = (i), \
+ .clkctrl_reg = (c), \
+ .clksel_reg = (s), \
+ }
+
+static struct am335x_clk_details g_am335x_clk_details[] = {
+
+ /* UART. UART0 clock not controllable. */
+ _CLK_DETAIL(UART1_CLK, 0, 0),
+ _CLK_DETAIL(UART2_CLK, CM_PER_UART1_CLKCTRL, 0),
+ _CLK_DETAIL(UART3_CLK, CM_PER_UART2_CLKCTRL, 0),
+ _CLK_DETAIL(UART4_CLK, CM_PER_UART3_CLKCTRL, 0),
+ _CLK_DETAIL(UART5_CLK, CM_PER_UART4_CLKCTRL, 0),
+ _CLK_DETAIL(UART6_CLK, CM_PER_UART5_CLKCTRL, 0),
+
+ /* DMTimer modules */
+ _CLK_DETAIL(TIMER2_CLK, CM_PER_TIMER2_CLKCTRL, CLKSEL_TIMER2_CLK),
+ _CLK_DETAIL(TIMER3_CLK, CM_PER_TIMER3_CLKCTRL, CLKSEL_TIMER3_CLK),
+ _CLK_DETAIL(TIMER4_CLK, CM_PER_TIMER4_CLKCTRL, CLKSEL_TIMER4_CLK),
+ _CLK_DETAIL(TIMER5_CLK, CM_PER_TIMER5_CLKCTRL, CLKSEL_TIMER5_CLK),
+ _CLK_DETAIL(TIMER6_CLK, CM_PER_TIMER6_CLKCTRL, CLKSEL_TIMER6_CLK),
+ _CLK_DETAIL(TIMER7_CLK, CM_PER_TIMER7_CLKCTRL, CLKSEL_TIMER7_CLK),
+
+ /* GPIO modules, hwmods start with gpio1 */
+ _CLK_DETAIL(GPIO1_CLK, CM_WKUP_GPIO0_CLKCTRL, 0),
+ _CLK_DETAIL(GPIO2_CLK, CM_PER_GPIO1_CLKCTRL, 0),
+ _CLK_DETAIL(GPIO3_CLK, CM_PER_GPIO2_CLKCTRL, 0),
+ _CLK_DETAIL(GPIO4_CLK, CM_PER_GPIO3_CLKCTRL, 0),
+
+ /* I2C modules, hwmods start with i2c1 */
+ _CLK_DETAIL(I2C1_CLK, CM_WKUP_I2C0_CLKCTRL, 0),
+ _CLK_DETAIL(I2C2_CLK, CM_PER_I2C1_CLKCTRL, 0),
+ _CLK_DETAIL(I2C3_CLK, CM_PER_I2C2_CLKCTRL, 0),
+
+ /* McSPI modules, hwmods start with spi0 */
+ _CLK_DETAIL(SPI0_CLK, CM_PER_SPI0_CLKCTRL, 0),
+ _CLK_DETAIL(SPI1_CLK, CM_PER_SPI1_CLKCTRL, 0),
+
+ /* TSC_ADC module */
+ _CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0),
+
+ /* EDMA modules */
+ _CLK_DETAIL(EDMA_TPCC_CLK, CM_PER_TPCC_CLKCTRL, 0),
+ _CLK_DETAIL(EDMA_TPTC0_CLK, CM_PER_TPTC0_CLKCTRL, 0),
+ _CLK_DETAIL(EDMA_TPTC1_CLK, CM_PER_TPTC1_CLKCTRL, 0),
+ _CLK_DETAIL(EDMA_TPTC2_CLK, CM_PER_TPTC2_CLKCTRL, 0),
+
+ /* MMCHS modules, hwmods start with mmc1*/
+ _CLK_DETAIL(MMC1_CLK, CM_PER_MMC0_CLKCTRL, 0),
+ _CLK_DETAIL(MMC2_CLK, CM_PER_MMC1_CLKCTRL, 0),
+ _CLK_DETAIL(MMC3_CLK, CM_PER_MMC1_CLKCTRL, 0),
+
+ /* PWMSS modules */
+ _CLK_DETAIL(PWMSS0_CLK, CM_PER_EPWMSS0_CLKCTRL, 0),
+ _CLK_DETAIL(PWMSS1_CLK, CM_PER_EPWMSS1_CLKCTRL, 0),
+ _CLK_DETAIL(PWMSS2_CLK, CM_PER_EPWMSS2_CLKCTRL, 0),
+
+ _CLK_DETAIL(MAILBOX0_CLK, CM_PER_MAILBOX0_CLKCTRL, 0),
+ _CLK_DETAIL(SPINLOCK0_CLK, CM_PER_SPINLOCK0_CLKCTRL, 0),
+
+ /* RTC module */
+ _CLK_DETAIL(RTC_CLK, CM_RTC_RTC_CLKCTRL, 0),
+
+ { INVALID_CLK_IDENT, 0},
+};
+
+/* Read/Write macros */
+#define prcm_read_4(reg) \
+ bus_space_read_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg)
+#define prcm_write_4(reg, val) \
+ bus_space_write_4(am335x_prcm_sc->bst, am335x_prcm_sc->bsh, reg, val)
+
+void am335x_prcm_setup_dmtimer(int);
+
+static int
+am335x_prcm_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_is_compatible(dev, "ti,am3-prcm")) {
+ device_set_desc(dev, "AM335x Power and Clock Management");
+ return(BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static int
+am335x_prcm_attach(device_t dev)
+{
+ struct am335x_prcm_softc *sc = device_get_softc(dev);
+ unsigned int sysclk, fclk;
+
+ if (am335x_prcm_sc)
+ return (ENXIO);
+
+ if (bus_alloc_resources(dev, am335x_prcm_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ am335x_prcm_sc = sc;
+ ti_cpu_reset = am335x_prcm_reset;
+
+ if (am335x_clk_get_sysclk_freq(NULL, &sysclk) != 0)
+ sysclk = 0;
+ if (am335x_clk_get_arm_fclk_freq(NULL, &fclk) != 0)
+ fclk = 0;
+ if (sysclk && fclk)
+ device_printf(dev, "Clocks: System %u.%01u MHz, CPU %u MHz\n",
+ sysclk/1000000, (sysclk % 1000000)/100000, fclk/1000000);
+ else
+ device_printf(dev, "can't read frequencies yet (SCM device not ready?)\n");
+
+ return (0);
+}
+
+static device_method_t am335x_prcm_methods[] = {
+ DEVMETHOD(device_probe, am335x_prcm_probe),
+ DEVMETHOD(device_attach, am335x_prcm_attach),
+ { 0, 0 }
+};
+
+static driver_t am335x_prcm_driver = {
+ "am335x_prcm",
+ am335x_prcm_methods,
+ sizeof(struct am335x_prcm_softc),
+};
+
+static devclass_t am335x_prcm_devclass;
+
+DRIVER_MODULE(am335x_prcm, simplebus, am335x_prcm_driver,
+ am335x_prcm_devclass, 0, 0);
+MODULE_VERSION(am335x_prcm, 1);
+MODULE_DEPEND(am335x_prcm, ti_scm, 1, 1, 1);
+
+static struct am335x_clk_details*
+am335x_clk_details(clk_ident_t id)
+{
+ struct am335x_clk_details *walker;
+
+ for (walker = g_am335x_clk_details; walker->id != INVALID_CLK_IDENT; walker++) {
+ if (id == walker->id)
+ return (walker);
+ }
+
+ return NULL;
+}
+
+static int
+am335x_clk_noop_activate(struct ti_clock_dev *clkdev)
+{
+
+ return (0);
+}
+
+static int
+am335x_clk_generic_activate(struct ti_clock_dev *clkdev)
+{
+ struct am335x_prcm_softc *sc = am335x_prcm_sc;
+ struct am335x_clk_details* clk_details;
+
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_details = am335x_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */
+ prcm_write_4(clk_details->clkctrl_reg, 2);
+ while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 2)
+ DELAY(10);
+
+ return (0);
+}
+
+static int
+am335x_clk_gpio_activate(struct ti_clock_dev *clkdev)
+{
+ struct am335x_prcm_softc *sc = am335x_prcm_sc;
+ struct am335x_clk_details* clk_details;
+
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_details = am335x_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ /* set *_CLKCTRL register MODULEMODE[1:0] to enable(2) */
+ /* set *_CLKCTRL register OPTFCLKEN_GPIO_1_G DBCLK[18] to FCLK_EN(1) */
+ prcm_write_4(clk_details->clkctrl_reg, 2 | (1 << 18));
+ while ((prcm_read_4(clk_details->clkctrl_reg) &
+ (3 | (1 << 18) )) != (2 | (1 << 18)))
+ DELAY(10);
+
+ return (0);
+}
+
+static int
+am335x_clk_noop_deactivate(struct ti_clock_dev *clkdev)
+{
+
+ return(0);
+}
+
+static int
+am335x_clk_generic_deactivate(struct ti_clock_dev *clkdev)
+{
+ struct am335x_prcm_softc *sc = am335x_prcm_sc;
+ struct am335x_clk_details* clk_details;
+
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_details = am335x_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ /* set *_CLKCTRL register MODULEMODE[1:0] to disable(0) */
+ prcm_write_4(clk_details->clkctrl_reg, 0);
+ while ((prcm_read_4(clk_details->clkctrl_reg) & 0x3) != 0)
+ DELAY(10);
+
+ return (0);
+}
+
+static int
+am335x_clk_noop_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc)
+{
+
+ return (0);
+}
+
+static int
+am335x_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc)
+{
+ struct am335x_prcm_softc *sc = am335x_prcm_sc;
+ struct am335x_clk_details* clk_details;
+ uint32_t reg;
+
+ if (sc == NULL)
+ return ENXIO;
+
+ clk_details = am335x_clk_details(clkdev->id);
+
+ if (clk_details == NULL)
+ return (ENXIO);
+
+ switch (clksrc) {
+ case EXT_CLK:
+ reg = 0; /* SEL2: TCLKIN clock */
+ break;
+ case SYSCLK_CLK:
+ reg = 1; /* SEL1: CLK_M_OSC clock */
+ break;
+ case F32KHZ_CLK:
+ reg = 2; /* SEL3: CLK_32KHZ clock */
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ prcm_write_4(clk_details->clksel_reg, reg);
+ while ((prcm_read_4(clk_details->clksel_reg) & 0x3) != reg)
+ DELAY(10);
+
+ return (0);
+}
+
+static int
+am335x_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
+{
+ *freq = 96000000;
+ return (0);
+}
+
+static int
+am335x_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
+{
+ uint32_t ctrl_status;
+
+ /* Read the input clock freq from the control module. */
+ if (ti_scm_reg_read_4(SCM_CTRL_STATUS, &ctrl_status))
+ return (ENXIO);
+
+ switch ((ctrl_status>>22) & 0x3) {
+ case 0x0:
+ /* 19.2Mhz */
+ *freq = 19200000;
+ break;
+ case 0x1:
+ /* 24Mhz */
+ *freq = 24000000;
+ break;
+ case 0x2:
+ /* 25Mhz */
+ *freq = 25000000;
+ break;
+ case 0x3:
+ /* 26Mhz */
+ *freq = 26000000;
+ break;
+ }
+
+ return (0);
+}
+
+#define DPLL_BYP_CLKSEL(reg) ((reg>>23) & 1)
+#define DPLL_DIV(reg) ((reg & 0x7f)+1)
+#define DPLL_MULT(reg) ((reg>>8) & 0x7FF)
+#define DPLL_MAX_MUL 0x800
+#define DPLL_MAX_DIV 0x80
+
+static int
+am335x_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
+{
+ uint32_t reg;
+ uint32_t sysclk;
+
+ reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_MPU);
+
+ /*Check if we are running in bypass */
+ if (DPLL_BYP_CLKSEL(reg))
+ return ENXIO;
+
+ am335x_clk_get_sysclk_freq(NULL, &sysclk);
+ *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg));
+ return(0);
+}
+
+static int
+am335x_clk_get_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int *freq)
+{
+ uint32_t reg;
+ uint32_t sysclk;
+
+ reg = prcm_read_4(CM_WKUP_CM_CLKSEL_DPLL_DISP);
+
+ /*Check if we are running in bypass */
+ if (DPLL_BYP_CLKSEL(reg))
+ return ENXIO;
+
+ am335x_clk_get_sysclk_freq(NULL, &sysclk);
+ *freq = DPLL_MULT(reg) * (sysclk / DPLL_DIV(reg));
+ return(0);
+}
+
+static int
+am335x_clk_set_arm_disp_freq(struct ti_clock_dev *clkdev, unsigned int freq)
+{
+ uint32_t sysclk;
+ uint32_t mul, div;
+ uint32_t i, j;
+ unsigned int delta, min_delta;
+
+ am335x_clk_get_sysclk_freq(NULL, &sysclk);
+
+ /* Bypass mode */
+ prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x4);
+
+ /* Make sure it's in bypass mode */
+ while (!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP)
+ & (1 << 8)))
+ DELAY(10);
+
+ /* Dumb and non-optimal implementation */
+ min_delta = freq;
+ for (i = 1; i < DPLL_MAX_MUL; i++) {
+ for (j = 1; j < DPLL_MAX_DIV; j++) {
+ delta = abs(freq - i*(sysclk/j));
+ if (delta < min_delta) {
+ mul = i;
+ div = j;
+ min_delta = delta;
+ }
+ if (min_delta == 0)
+ break;
+ }
+ }
+
+ prcm_write_4(CM_WKUP_CM_CLKSEL_DPLL_DISP, (mul << 8) | (div - 1));
+
+ /* Locked mode */
+ prcm_write_4(CM_WKUP_CM_CLKMODE_DPLL_DISP, 0x7);
+
+ int timeout = 10000;
+ while ((!(prcm_read_4(CM_WKUP_CM_IDLEST_DPLL_DISP)
+ & (1 << 0))) && timeout--)
+ DELAY(10);
+
+ return(0);
+}
+
+static void
+am335x_prcm_reset(void)
+{
+ prcm_write_4(PRM_RSTCTRL, (1<<1));
+}
+
+static int
+am335x_clk_cpsw_activate(struct ti_clock_dev *clkdev)
+{
+ struct am335x_prcm_softc *sc = am335x_prcm_sc;
+
+ if (sc == NULL)
+ return ENXIO;
+
+ /* set MODULENAME to ENABLE */
+ prcm_write_4(CM_PER_CPGMAC0_CLKCTRL, 2);
+
+ /* wait for IDLEST to become Func(0) */
+ while(prcm_read_4(CM_PER_CPGMAC0_CLKCTRL) & (3<<16));
+
+ /*set CLKTRCTRL to SW_WKUP(2) */
+ prcm_write_4(CM_PER_CPSW_CLKSTCTRL, 2);
+
+ /* wait for 125 MHz OCP clock to become active */
+ while((prcm_read_4(CM_PER_CPSW_CLKSTCTRL) & (1<<4)) == 0);
+ return(0);
+}
+
+static int
+am335x_clk_musb0_activate(struct ti_clock_dev *clkdev)
+{
+ struct am335x_prcm_softc *sc = am335x_prcm_sc;
+
+ if (sc == NULL)
+ return ENXIO;
+
+ /* set ST_DPLL_CLKDCOLDO(9) to CLK_GATED(1) */
+ /* set DPLL_CLKDCOLDO_GATE_CTRL(8) to CLK_ENABLE(1)*/
+ prcm_write_4(CM_WKUP_CM_CLKDCOLDO_DPLL_PER, 0x300);
+
+ /*set MODULEMODE to ENABLE(2) */
+ prcm_write_4(CM_PER_USB0_CLKCTRL, 2);
+
+ /* wait for MODULEMODE to become ENABLE(2) */
+ while ((prcm_read_4(CM_PER_USB0_CLKCTRL) & 0x3) != 2)
+ DELAY(10);
+
+ /* wait for IDLEST to become Func(0) */
+ while(prcm_read_4(CM_PER_USB0_CLKCTRL) & (3<<16))
+ DELAY(10);
+
+ return(0);
+}
+
+static int
+am335x_clk_lcdc_activate(struct ti_clock_dev *clkdev)
+{
+ struct am335x_prcm_softc *sc = am335x_prcm_sc;
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ /*
+ * For now set frequency to 2*VGA_PIXEL_CLOCK
+ */
+ am335x_clk_set_arm_disp_freq(clkdev, 25175000*2);
+
+ /*set MODULEMODE to ENABLE(2) */
+ prcm_write_4(CM_PER_LCDC_CLKCTRL, 2);
+
+ /* wait for MODULEMODE to become ENABLE(2) */
+ while ((prcm_read_4(CM_PER_LCDC_CLKCTRL) & 0x3) != 2)
+ DELAY(10);
+
+ /* wait for IDLEST to become Func(0) */
+ while(prcm_read_4(CM_PER_LCDC_CLKCTRL) & (3<<16))
+ DELAY(10);
+
+ return (0);
+}
+
+static int
+am335x_clk_pruss_activate(struct ti_clock_dev *clkdev)
+{
+ struct am335x_prcm_softc *sc = am335x_prcm_sc;
+
+ if (sc == NULL)
+ return (ENXIO);
+
+ /* Set MODULEMODE to ENABLE(2) */
+ prcm_write_4(CM_PER_PRUSS_CLKCTRL, 2);
+
+ /* Wait for MODULEMODE to become ENABLE(2) */
+ while ((prcm_read_4(CM_PER_PRUSS_CLKCTRL) & 0x3) != 2)
+ DELAY(10);
+
+ /* Set CLKTRCTRL to SW_WKUP(2) */
+ prcm_write_4(CM_PER_PRUSS_CLKSTCTRL, 2);
+
+ /* Wait for the 200 MHz OCP clock to become active */
+ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<4)) == 0)
+ DELAY(10);
+
+ /* Wait for the 200 MHz IEP clock to become active */
+ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<5)) == 0)
+ DELAY(10);
+
+ /* Wait for the 192 MHz UART clock to become active */
+ while ((prcm_read_4(CM_PER_PRUSS_CLKSTCTRL) & (1<<6)) == 0)
+ DELAY(10);
+
+ /* Select L3F as OCP clock */
+ prcm_write_4(CLKSEL_PRUSS_OCP_CLK, 0);
+ while ((prcm_read_4(CLKSEL_PRUSS_OCP_CLK) & 0x3) != 0)
+ DELAY(10);
+
+ /* Clear the RESET bit */
+ prcm_write_4(PRM_PER_RSTCTRL, prcm_read_4(PRM_PER_RSTCTRL) & ~2);
+
+ return (0);
+}
diff --git a/freebsd/sys/arm/ti/am335x/am335x_scm.h b/freebsd/sys/arm/ti/am335x/am335x_scm.h
new file mode 100644
index 0000000..6116be0
--- /dev/null
+++ b/freebsd/sys/arm/ti/am335x/am335x_scm.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo at freebsd.org>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef __AM335X_SCM_H__
+#define __AM335X_SCM_H__
+
+/* AM335x-specific registers for control module (scm) */
+#define SCM_CTRL_STATUS 0x40
+#define SCM_BGAP_CTRL 0x448
+#define SCM_BGAP_TEMP_MASK 0xff
+#define SCM_BGAP_TEMP_SHIFT 8
+#define SCM_BGAP_BGOFF (1 << 6)
+#define SCM_BGAP_SOC (1 << 4)
+#define SCM_BGAP_CLRZ (1 << 3)
+#define SCM_BGAP_CONTCONV (1 << 2)
+#define SCM_BGAP_EOCZ (1 << 1)
+#define SCM_USB_CTRL0 0x620
+#define SCM_USB_STS0 0x624
+#define SCM_USB_CTRL1 0x628
+#define SCM_USB_STS1 0x62C
+#define SCM_MAC_ID0_LO 0x630
+#define SCM_MAC_ID0_HI 0x634
+#define SCM_PWMSS_CTRL 0x664
+
+#endif /* __AM335X_SCM_H__ */
diff --git a/freebsd/sys/arm/ti/am335x/am335x_usbss.c b/freebsd/sys/arm/ti/am335x/am335x_usbss.c
new file mode 100644
index 0000000..d7b43a2
--- /dev/null
+++ b/freebsd/sys/arm/ti/am335x/am335x_usbss.c
@@ -0,0 +1,226 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo at freebsd.org>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/fdt/simplebus.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/musb_otg.h>
+#include <dev/usb/usb_debug.h>
+
+#include <sys/rman.h>
+
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/ti_scm.h>
+#include <arm/ti/am335x/am335x_scm.h>
+
+#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)
+
+#define USBSS_WRITE4(sc, reg, val) \
+ bus_write_4((sc)->sc_mem_res, (reg), (val))
+#define USBSS_READ4(sc, reg) \
+ bus_read_4((sc)->sc_mem_res, (reg))
+
+static device_probe_t usbss_probe;
+static device_attach_t usbss_attach;
+static device_detach_t usbss_detach;
+
+struct usbss_softc {
+ struct simplebus_softc simplebus_sc;
+ struct resource *sc_mem_res;
+ int sc_mem_rid;
+};
+
+static int
+usbss_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "ti,am33xx-usb"))
+ return (ENXIO);
+
+ device_set_desc(dev, "TI AM33xx integrated USB OTG controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+usbss_attach(device_t dev)
+{
+ struct usbss_softc *sc = device_get_softc(dev);
+ int i;
+ uint32_t rev;
+ phandle_t node;
+
+ /* Request the memory resources */
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_mem_rid, RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev,
+ "Error: could not allocate mem resources\n");
+ return (ENXIO);
+ }
+
+ /* Enable device clocks. */
+ ti_prcm_clk_enable(MUSB0_CLK);
+
+ /*
+ * Reset USBSS, USB0 and USB1.
+ * The registers of USB subsystem must not be accessed while the
+ * reset pulse is active (200ns).
+ */
+ USBSS_WRITE4(sc, USBSS_SYSCONFIG, USBSS_SYSCONFIG_SRESET);
+ DELAY(100);
+ i = 10;
+ while (USBSS_READ4(sc, USBSS_SYSCONFIG) & USBSS_SYSCONFIG_SRESET) {
+ DELAY(100);
+ if (i-- == 0) {
+ device_printf(dev, "reset timeout.\n");
+ return (ENXIO);
+ }
+ }
+
+ /* Read the module revision. */
+ 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);
+
+ node = ofw_bus_get_node(dev);
+
+ if (node == -1) {
+ usbss_detach(dev);
+ return (ENXIO);
+ }
+
+ simplebus_init(dev, node);
+
+ /*
+ * Allow devices to identify.
+ */
+ bus_generic_probe(dev);
+
+ /*
+ * Now walk the OFW tree and attach top-level devices.
+ */
+ for (node = OF_child(node); node > 0; node = OF_peer(node))
+ simplebus_add_device(dev, node, 0, NULL, -1, NULL);
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+usbss_detach(device_t dev)
+{
+ struct usbss_softc *sc = device_get_softc(dev);
+
+ /* Free resources if any */
+ if (sc->sc_mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
+ sc->sc_mem_res);
+
+ /* during module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ return (0);
+}
+
+static device_method_t usbss_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, usbss_probe),
+ DEVMETHOD(device_attach, usbss_attach),
+ DEVMETHOD(device_detach, usbss_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(usbss, usbss_driver, usbss_methods,
+ sizeof(struct usbss_softc), simplebus_driver);
+static devclass_t usbss_devclass;
+DRIVER_MODULE(usbss, simplebus, usbss_driver, usbss_devclass, 0, 0);
+MODULE_DEPEND(usbss, usb, 1, 1, 1);
diff --git a/freebsd/sys/arm/ti/ti_cpuid.h b/freebsd/sys/arm/ti/ti_cpuid.h
new file mode 100644
index 0000000..715f080
--- /dev/null
+++ b/freebsd/sys/arm/ti/ti_cpuid.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2011
+ * Ben Gray <ben.r.gray at gmail.com>.
+ * 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 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 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TI_CPUID_H_
+#define _TI_CPUID_H_
+
+#define OMAP_MAKEREV(d, a, b, c) \
+ (uint32_t)(((d) << 16) | (((a) & 0xf) << 8) | (((b) & 0xf) << 4) | ((c) & 0xf))
+
+#define OMAP_REV_DEVICE(x) (((x) >> 16) & 0xffff)
+#define OMAP_REV_MAJOR(x) (((x) >> 8) & 0xf)
+#define OMAP_REV_MINOR(x) (((x) >> 4) & 0xf)
+#define OMAP_REV_MINOR_MINOR(x) (((x) >> 0) & 0xf)
+
+#define OMAP3350_DEV 0x3530
+#define OMAP3350_REV_ES1_0 OMAP_MAKEREV(OMAP3350_DEV, 1, 0, 0)
+#define OMAP3530_REV_ES2_0 OMAP_MAKEREV(OMAP3350_DEV, 2, 0, 0)
+#define OMAP3530_REV_ES2_1 OMAP_MAKEREV(OMAP3350_DEV, 2, 1, 0)
+#define OMAP3530_REV_ES3_0 OMAP_MAKEREV(OMAP3350_DEV, 3, 0, 0)
+#define OMAP3530_REV_ES3_1 OMAP_MAKEREV(OMAP3350_DEV, 3, 1, 0)
+#define OMAP3530_REV_ES3_1_2 OMAP_MAKEREV(OMAP3350_DEV, 3, 1, 2)
+
+#define OMAP4430_DEV 0x4430
+#define OMAP4430_REV_ES1_0 OMAP_MAKEREV(OMAP4430_DEV, 1, 0, 0)
+#define OMAP4430_REV_ES2_0 OMAP_MAKEREV(OMAP4430_DEV, 2, 0, 0)
+#define OMAP4430_REV_ES2_1 OMAP_MAKEREV(OMAP4430_DEV, 2, 1, 0)
+#define OMAP4430_REV_ES2_2 OMAP_MAKEREV(OMAP4430_DEV, 2, 2, 0)
+#define OMAP4430_REV_ES2_3 OMAP_MAKEREV(OMAP4430_DEV, 2, 3, 0)
+#define OMAP4430_REV_UNKNOWN OMAP_MAKEREV(OMAP4430_DEV, 9, 9, 9)
+
+#define OMAP4460_DEV 0x4460
+#define OMAP4460_REV_ES1_0 OMAP_MAKEREV(OMAP4460_DEV, 1, 0, 0)
+#define OMAP4460_REV_ES1_1 OMAP_MAKEREV(OMAP4460_DEV, 1, 1, 0)
+#define OMAP4460_REV_UNKNOWN OMAP_MAKEREV(OMAP4460_DEV, 9, 9, 9)
+
+#define OMAP4470_DEV 0x4470
+#define OMAP4470_REV_ES1_0 OMAP_MAKEREV(OMAP4470_DEV, 1, 0, 0)
+#define OMAP4470_REV_UNKNOWN OMAP_MAKEREV(OMAP4470_DEV, 9, 9, 9)
+
+#define OMAP_UNKNOWN_DEV OMAP_MAKEREV(0x9999, 9, 9, 9)
+
+#define AM335X_DEVREV(x) ((x) >> 28)
+
+#define CHIP_OMAP_4 0
+#define CHIP_AM335X 1
+
+extern int _ti_chip;
+
+static __inline int ti_chip(void)
+{
+ KASSERT(_ti_chip != -1, ("Can't determine TI Chip"));
+ return _ti_chip;
+}
+
+uint32_t ti_revision(void);
+
+#endif /* _TI_CPUID_H_ */
diff --git a/freebsd/sys/arm/ti/ti_prcm.c b/freebsd/sys/arm/ti/ti_prcm.c
new file mode 100644
index 0000000..b4dd03f
--- /dev/null
+++ b/freebsd/sys/arm/ti/ti_prcm.c
@@ -0,0 +1,357 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray at gmail.com>.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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.
+ */
+
+/**
+ * Power, Reset and Clock Management Module
+ *
+ * This is a very simple driver wrapper around the PRCM set of registers in
+ * the OMAP3 chip. It allows you to turn on and off things like the functional
+ * and interface clocks to the various on-chip modules.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <rtems/bsd/sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <arm/ti/ti_cpuid.h>
+#include <arm/ti/ti_prcm.h>
+
+/**
+ * ti_*_clk_devmap - Array of clock devices, should be defined one per SoC
+ *
+ * This array is typically defined in one of the targeted *_prcm_clk.c
+ * files and is specific to the given SoC platform. Each entry in the array
+ * corresponds to an individual clock device.
+ */
+extern struct ti_clock_dev ti_omap4_clk_devmap[];
+extern struct ti_clock_dev ti_am335x_clk_devmap[];
+
+/**
+ * ti_prcm_clk_dev - returns a pointer to the clock device with given id
+ * @clk: the ID of the clock device to get
+ *
+ * Simply iterates through the clk_devmap global array and returns a pointer
+ * to the clock device if found.
+ *
+ * LOCKING:
+ * None
+ *
+ * RETURNS:
+ * The pointer to the clock device on success, on failure NULL is returned.
+ */
+static struct ti_clock_dev *
+ti_prcm_clk_dev(clk_ident_t clk)
+{
+ struct ti_clock_dev *clk_dev;
+
+ /* Find the clock within the devmap - it's a bit inefficent having a for
+ * loop for this, but this function should only called when a driver is
+ * being activated so IMHO not a big issue.
+ */
+ clk_dev = NULL;
+ switch(ti_chip()) {
+#ifdef SOC_OMAP4
+ case CHIP_OMAP_4:
+ clk_dev = &(ti_omap4_clk_devmap[0]);
+ break;
+#endif
+#ifdef SOC_TI_AM335X
+ case CHIP_AM335X:
+ clk_dev = &(ti_am335x_clk_devmap[0]);
+ break;
+#endif
+ }
+ if (clk_dev == NULL)
+ panic("No clock devmap found");
+ while (clk_dev->id != INVALID_CLK_IDENT) {
+ if (clk_dev->id == clk) {
+ return (clk_dev);
+ }
+ clk_dev++;
+ }
+
+ /* Sanity check we managed to find the clock */
+ printf("ti_prcm: Failed to find clock device (%d)\n", clk);
+ return (NULL);
+}
+
+/**
+ * ti_prcm_clk_valid - enables a clock for a particular module
+ * @clk: identifier for the module to enable, see ti_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
+ *
+ * This function can enable either a functional or interface clock.
+ *
+ * The real work done to enable the clock is really done in the callback
+ * function associated with the clock, this function is simply a wrapper
+ * around that.
+ *
+ * LOCKING:
+ * Internally locks the driver context.
+ *
+ * RETURNS:
+ * Returns 0 on success or positive error code on failure.
+ */
+int
+ti_prcm_clk_valid(clk_ident_t clk)
+{
+ int ret = 0;
+
+ if (ti_prcm_clk_dev(clk) == NULL)
+ ret = EINVAL;
+
+ return (ret);
+}
+
+
+/**
+ * ti_prcm_clk_enable - enables a clock for a particular module
+ * @clk: identifier for the module to enable, see ti_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
+ *
+ * This function can enable either a functional or interface clock.
+ *
+ * The real work done to enable the clock is really done in the callback
+ * function associated with the clock, this function is simply a wrapper
+ * around that.
+ *
+ * LOCKING:
+ * Internally locks the driver context.
+ *
+ * RETURNS:
+ * Returns 0 on success or positive error code on failure.
+ */
+int
+ti_prcm_clk_enable(clk_ident_t clk)
+{
+ struct ti_clock_dev *clk_dev;
+ int ret;
+
+ /* Find the clock within the devmap - it's a bit inefficent having a for
+ * loop for this, but this function should only called when a driver is
+ * being activated so IMHO not a big issue.
+ */
+ clk_dev = ti_prcm_clk_dev(clk);
+
+ /* Sanity check we managed to find the clock */
+ if (clk_dev == NULL)
+ return (EINVAL);
+
+ /* Activate the clock */
+ if (clk_dev->clk_activate)
+ ret = clk_dev->clk_activate(clk_dev);
+ else
+ ret = EINVAL;
+
+ return (ret);
+}
+
+
+/**
+ * ti_prcm_clk_disable - disables a clock for a particular module
+ * @clk: identifier for the module to enable, see ti_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
+ *
+ * This function can enable either a functional or interface clock.
+ *
+ * The real work done to enable the clock is really done in the callback
+ * function associated with the clock, this function is simply a wrapper
+ * around that.
+ *
+ * LOCKING:
+ * Internally locks the driver context.
+ *
+ * RETURNS:
+ * Returns 0 on success or positive error code on failure.
+ */
+int
+ti_prcm_clk_disable(clk_ident_t clk)
+{
+ struct ti_clock_dev *clk_dev;
+ int ret;
+
+ /* Find the clock within the devmap - it's a bit inefficent having a for
+ * loop for this, but this function should only called when a driver is
+ * being activated so IMHO not a big issue.
+ */
+ clk_dev = ti_prcm_clk_dev(clk);
+
+ /* Sanity check we managed to find the clock */
+ if (clk_dev == NULL)
+ return (EINVAL);
+
+ /* Activate the clock */
+ if (clk_dev->clk_deactivate)
+ ret = clk_dev->clk_deactivate(clk_dev);
+ else
+ ret = EINVAL;
+
+ return (ret);
+}
+
+/**
+ * ti_prcm_clk_set_source - sets the source
+ * @clk: identifier for the module to enable, see ti_prcm.h for a list
+ * of possible modules.
+ * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK.
+ *
+ * This function can enable either a functional or interface clock.
+ *
+ * The real work done to enable the clock is really done in the callback
+ * function associated with the clock, this function is simply a wrapper
+ * around that.
+ *
+ * LOCKING:
+ * Internally locks the driver context.
+ *
+ * RETURNS:
+ * Returns 0 on success or positive error code on failure.
+ */
+int
+ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc)
+{
+ struct ti_clock_dev *clk_dev;
+ int ret;
+
+ /* Find the clock within the devmap - it's a bit inefficent having a for
+ * loop for this, but this function should only called when a driver is
+ * being activated so IMHO not a big issue.
+ */
+ clk_dev = ti_prcm_clk_dev(clk);
+
+ /* Sanity check we managed to find the clock */
+ if (clk_dev == NULL)
+ return (EINVAL);
+
+ /* Activate the clock */
+ if (clk_dev->clk_set_source)
+ ret = clk_dev->clk_set_source(clk_dev, clksrc);
+ else
+ ret = EINVAL;
+
+ return (ret);
+}
+
+
+/**
+ * ti_prcm_clk_get_source_freq - gets the source clock frequency
+ * @clk: identifier for the module to enable, see ti_prcm.h for a list
+ * of possible modules.
+ * @freq: pointer to an integer that upon return will contain the src freq
+ *
+ * This function returns the frequency of the source clock.
+ *
+ * The real work done to enable the clock is really done in the callback
+ * function associated with the clock, this function is simply a wrapper
+ * around that.
+ *
+ * LOCKING:
+ * Internally locks the driver context.
+ *
+ * RETURNS:
+ * Returns 0 on success or positive error code on failure.
+ */
+int
+ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq)
+{
+ struct ti_clock_dev *clk_dev;
+ int ret;
+
+ /* Find the clock within the devmap - it's a bit inefficent having a for
+ * loop for this, but this function should only called when a driver is
+ * being activated so IMHO not a big issue.
+ */
+ clk_dev = ti_prcm_clk_dev(clk);
+
+ /* Sanity check we managed to find the clock */
+ if (clk_dev == NULL)
+ return (EINVAL);
+
+ /* Get the source frequency of the clock */
+ if (clk_dev->clk_get_source_freq)
+ ret = clk_dev->clk_get_source_freq(clk_dev, freq);
+ else
+ ret = EINVAL;
+
+ return (ret);
+}
+
+/**
+ * ti_prcm_clk_set_source_freq - sets the source clock frequency as close to freq as possible
+ * @clk: identifier for the module to enable, see ti_prcm.h for a list
+ * of possible modules.
+ * @freq: requested freq
+ *
+ * LOCKING:
+ * Internally locks the driver context.
+ *
+ * RETURNS:
+ * Returns 0 on success or positive error code on failure.
+ */
+int
+ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq)
+{
+ struct ti_clock_dev *clk_dev;
+ int ret;
+
+ clk_dev = ti_prcm_clk_dev(clk);
+
+ /* Sanity check we managed to find the clock */
+ if (clk_dev == NULL)
+ return (EINVAL);
+
+ /* Get the source frequency of the clock */
+ if (clk_dev->clk_set_source_freq)
+ ret = clk_dev->clk_set_source_freq(clk_dev, freq);
+ else
+ ret = EINVAL;
+
+ return (ret);
+}
diff --git a/freebsd/sys/arm/ti/ti_prcm.h b/freebsd/sys/arm/ti/ti_prcm.h
new file mode 100644
index 0000000..61b6960
--- /dev/null
+++ b/freebsd/sys/arm/ti/ti_prcm.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray at gmail.com>.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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.
+ *
+ * $FreeBSD$
+ */
+
+
+/*
+ * Texas Instruments - OMAP3xxx series processors
+ *
+ * Reference:
+ * OMAP35x Applications Processor
+ * Technical Reference Manual
+ * (omap35xx_techref.pdf)
+ */
+#ifndef _TI_PRCM_H_
+#define _TI_PRCM_H_
+
+typedef enum {
+
+ INVALID_CLK_IDENT = 0,
+
+ /* System clocks, typically you can only call ti_prcm_clk_get_source_freq()
+ * on these clocks as they are enabled by default.
+ */
+ SYS_CLK = 1,
+
+ /* The MPU (ARM) core clock */
+ MPU_CLK = 20,
+
+ /* MMC modules */
+ MMC1_CLK = 100,
+ MMC2_CLK,
+ MMC3_CLK,
+ MMC4_CLK,
+ MMC5_CLK,
+ MMC6_CLK,
+
+ /* I2C modules */
+ I2C1_CLK = 200,
+ I2C2_CLK,
+ I2C3_CLK,
+ I2C4_CLK,
+ I2C5_CLK,
+
+ /* USB module(s) */
+ USBTLL_CLK = 300,
+ USBHSHOST_CLK,
+ USBFSHOST_CLK,
+ USBP1_PHY_CLK,
+ USBP2_PHY_CLK,
+ USBP1_UTMI_CLK,
+ USBP2_UTMI_CLK,
+ USBP1_HSIC_CLK,
+ USBP2_HSIC_CLK,
+
+ /* UART modules */
+ UART1_CLK = 400,
+ UART2_CLK,
+ UART3_CLK,
+ UART4_CLK,
+ UART5_CLK,
+ UART6_CLK,
+ UART7_CLK,
+ UART8_CLK,
+ UART9_CLK,
+
+ /* General purpose timer modules */
+ TIMER1_CLK = 500,
+ TIMER2_CLK,
+ TIMER3_CLK,
+ TIMER4_CLK,
+ TIMER5_CLK,
+ TIMER6_CLK,
+ TIMER7_CLK,
+ TIMER8_CLK,
+ TIMER9_CLK,
+ TIMER10_CLK,
+ TIMER11_CLK,
+ TIMER12_CLK,
+
+ /* McBSP module(s) */
+ MCBSP1_CLK = 600,
+ MCBSP2_CLK,
+ MCBSP3_CLK,
+ MCBSP4_CLK,
+ MCBSP5_CLK,
+
+ /* General purpose I/O modules */
+ GPIO1_CLK = 700,
+ GPIO2_CLK,
+ GPIO3_CLK,
+ GPIO4_CLK,
+ GPIO5_CLK,
+ GPIO6_CLK,
+ GPIO7_CLK,
+
+ /* sDMA module */
+ SDMA_CLK = 800,
+
+ /* CPSW modules */
+ CPSW_CLK = 1000,
+
+ /* Mentor USB modules */
+ MUSB0_CLK = 1100,
+
+ /* EDMA module */
+ EDMA_TPCC_CLK = 1200,
+ EDMA_TPTC0_CLK,
+ EDMA_TPTC1_CLK,
+ EDMA_TPTC2_CLK,
+
+ /* LCD controller module */
+ LCDC_CLK = 1300,
+
+ /* PWM modules */
+ PWMSS0_CLK = 1400,
+ PWMSS1_CLK,
+ PWMSS2_CLK,
+
+ /* Mailbox modules */
+ MAILBOX0_CLK = 1500,
+
+ /* Spinlock modules */
+ SPINLOCK0_CLK = 1600,
+
+ PRUSS_CLK = 1700,
+
+ TSC_ADC_CLK = 1800,
+
+ /* RTC module */
+ RTC_CLK = 1900,
+
+ /* McSPI */
+ SPI0_CLK = 2000,
+ SPI1_CLK,
+} clk_ident_t;
+
+/*
+ *
+ */
+typedef enum {
+ SYSCLK_CLK, /* System clock */
+ EXT_CLK,
+
+ F32KHZ_CLK, /* 32KHz clock */
+ F48MHZ_CLK, /* 48MHz clock */
+ F64MHZ_CLK, /* 64MHz clock */
+ F96MHZ_CLK, /* 96MHz clock */
+
+} clk_src_t;
+
+struct ti_clock_dev {
+ /* The profile of the timer */
+ clk_ident_t id;
+
+ /* A bunch of callbacks associated with the clock device */
+ int (*clk_activate)(struct ti_clock_dev *clkdev);
+ int (*clk_deactivate)(struct ti_clock_dev *clkdev);
+ int (*clk_set_source)(struct ti_clock_dev *clkdev,
+ clk_src_t clksrc);
+ int (*clk_accessible)(struct ti_clock_dev *clkdev);
+ int (*clk_set_source_freq)(struct ti_clock_dev *clkdev,
+ unsigned int freq);
+ int (*clk_get_source_freq)(struct ti_clock_dev *clkdev,
+ unsigned int *freq);
+};
+
+int ti_prcm_clk_valid(clk_ident_t clk);
+int ti_prcm_clk_enable(clk_ident_t clk);
+int ti_prcm_clk_disable(clk_ident_t clk);
+int ti_prcm_clk_accessible(clk_ident_t clk);
+int ti_prcm_clk_disable_autoidle(clk_ident_t clk);
+int ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc);
+int ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq);
+int ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq);
+void ti_prcm_reset(void);
+
+#endif /* _TI_PRCM_H_ */
diff --git a/freebsd/sys/arm/ti/ti_scm.c b/freebsd/sys/arm/ti/ti_scm.c
new file mode 100644
index 0000000..2e54262
--- /dev/null
+++ b/freebsd/sys/arm/ti/ti_scm.c
@@ -0,0 +1,176 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray at gmail.com>.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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.
+ */
+
+/**
+ * SCM - System Control Module
+ *
+ * Hopefully in the end this module will contain a bunch of utility functions
+ * for configuring and querying the general system control registers, but for
+ * now it only does pin(pad) multiplexing.
+ *
+ * This is different from the GPIO module in that it is used to configure the
+ * pins between modules not just GPIO input/output.
+ *
+ * This file contains the generic top level driver, however it relies on chip
+ * specific settings and therefore expects an array of ti_scm_padconf structs
+ * call ti_padconf_devmap to be located somewhere in the kernel.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <rtems/bsd/sys/resource.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/fdt/fdt_pinctrl.h>
+
+#include "ti_scm.h"
+
+static struct resource_spec ti_scm_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Control memory window */
+ { -1, 0 }
+};
+
+static struct ti_scm_softc *ti_scm_sc;
+
+#define ti_scm_read_4(sc, reg) \
+ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define ti_scm_write_4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+/*
+ * Device part of OMAP SCM driver
+ */
+static int
+ti_scm_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "syscon"))
+ return (ENXIO);
+
+ if (ti_scm_sc) {
+ return (EEXIST);
+ }
+
+ device_set_desc(dev, "TI Control Module");
+ return (BUS_PROBE_DEFAULT);
+}
+
+/**
+ * ti_scm_attach - attaches the timer to the simplebus
+ * @dev: new device
+ *
+ * Reserves memory and interrupt resources, stores the softc structure
+ * globally and registers both the timecount and eventtimer objects.
+ *
+ * RETURNS
+ * Zero on success or ENXIO if an error occuried.
+ */
+static int
+ti_scm_attach(device_t dev)
+{
+ struct ti_scm_softc *sc = device_get_softc(dev);
+
+ sc->sc_dev = dev;
+
+ if (bus_alloc_resources(dev, ti_scm_res_spec, sc->sc_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Global timer interface */
+ sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
+
+ ti_scm_sc = sc;
+
+ /* Attach platform extensions, if any. */
+ bus_generic_probe(dev);
+
+ return (bus_generic_attach(dev));
+}
+
+int
+ti_scm_reg_read_4(uint32_t reg, uint32_t *val)
+{
+ if (!ti_scm_sc)
+ return (ENXIO);
+
+ *val = ti_scm_read_4(ti_scm_sc, reg);
+ return (0);
+}
+
+int
+ti_scm_reg_write_4(uint32_t reg, uint32_t val)
+{
+ if (!ti_scm_sc)
+ return (ENXIO);
+
+ ti_scm_write_4(ti_scm_sc, reg, val);
+ return (0);
+}
+
+
+static device_method_t ti_scm_methods[] = {
+ DEVMETHOD(device_probe, ti_scm_probe),
+ DEVMETHOD(device_attach, ti_scm_attach),
+
+ { 0, 0 }
+};
+
+static driver_t ti_scm_driver = {
+ "ti_scm",
+ ti_scm_methods,
+ sizeof(struct ti_scm_softc),
+};
+
+static devclass_t ti_scm_devclass;
+
+EARLY_DRIVER_MODULE(ti_scm, simplebus, ti_scm_driver, ti_scm_devclass, 0, 0,
+ BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
diff --git a/freebsd/sys/arm/ti/ti_scm.h b/freebsd/sys/arm/ti/ti_scm.h
new file mode 100644
index 0000000..1342ddc
--- /dev/null
+++ b/freebsd/sys/arm/ti/ti_scm.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray at gmail.com>.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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.
+ *
+ * $FreeBSD$
+ */
+
+
+/**
+ * Functions to configure the PIN multiplexing on the chip.
+ *
+ * This is different from the GPIO module in that it is used to configure the
+ * pins between modules not just GPIO input output.
+ *
+ */
+#ifndef _TI_SCM_H_
+#define _TI_SCM_H_
+
+struct ti_scm_softc {
+ device_t sc_dev;
+ struct resource * sc_res[4];
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+};
+
+int ti_scm_reg_read_4(uint32_t reg, uint32_t *val);
+int ti_scm_reg_write_4(uint32_t reg, uint32_t val);
+
+#endif /* _TI_SCM_H_ */
diff --git a/freebsd/sys/arm/ti/tivar.h b/freebsd/sys/arm/ti/tivar.h
new file mode 100644
index 0000000..248ad90
--- /dev/null
+++ b/freebsd/sys/arm/ti/tivar.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010
+ * Ben Gray <ben.r.gray at gmail.com>.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Ben Gray.
+ * 4. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``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 BEN GRAY 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TIVAR_H_
+#define _TIVAR_H_
+
+/* board-dependent reset function implementation */
+extern void (*ti_cpu_reset)(void);
+
+#endif /* _TIVAR_H_ */
diff --git a/freebsd/sys/dev/usb/controller/musb_otg.c b/freebsd/sys/dev/usb/controller/musb_otg.c
new file mode 100644
index 0000000..0f1953b
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/musb_otg.c
@@ -0,0 +1,4257 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $FreeBSD$ */
+/*-
+ * 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.
+ */
+
+/*
+ * Thanks to Mentor Graphics for providing a reference driver for this USB chip
+ * at their homepage.
+ */
+
+/*
+ * This file contains the driver for the Mentor Graphics Inventra USB
+ * 2.0 High Speed Dual-Role controller.
+ *
+ */
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#define USB_DEBUG_VAR musbotgdebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#endif /* USB_GLOBAL_INCLUDE_FILE */
+
+#include <dev/usb/controller/musb_otg.h>
+
+#define MUSBOTG_INTR_ENDPT 1
+
+#define MUSBOTG_BUS2SC(bus) \
+ ((struct musbotg_softc *)(((uint8_t *)(bus)) - \
+ USB_P2U(&(((struct musbotg_softc *)0)->sc_bus))))
+
+#define MUSBOTG_PC2SC(pc) \
+ MUSBOTG_BUS2SC(USB_DMATAG_TO_XROOT((pc)->tag_parent)->bus)
+
+#ifdef USB_DEBUG
+static int musbotgdebug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, musbotg, CTLFLAG_RW, 0, "USB musbotg");
+SYSCTL_INT(_hw_usb_musbotg, OID_AUTO, debug, CTLFLAG_RWTUN,
+ &musbotgdebug, 0, "Debug level");
+#endif
+
+#define MAX_NAK_TO 16
+
+/* prototypes */
+
+static const struct usb_bus_methods musbotg_bus_methods;
+static const struct usb_pipe_methods musbotg_device_bulk_methods;
+static const struct usb_pipe_methods musbotg_device_ctrl_methods;
+static const struct usb_pipe_methods musbotg_device_intr_methods;
+static const struct usb_pipe_methods musbotg_device_isoc_methods;
+
+/* 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, uint8_t);
+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.
+ */
+static const struct usb_hw_ep_profile musbotg_ep_profile[1] = {
+
+ [0] = {
+ .max_in_frame_size = 64,/* fixed */
+ .max_out_frame_size = 64, /* fixed */
+ .is_simplex = 1,
+ .support_control = 1,
+ }
+};
+
+static int
+musbotg_channel_alloc(struct musbotg_softc *sc, struct musbotg_td *td, uint8_t is_tx)
+{
+ 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 = sc->sc_ep_max; ch != 0; ch--) {
+ if (sc->sc_channel_mask & (1 << ch))
+ continue;
+
+ /* check FIFO size requirement */
+ if (is_tx) {
+ if (td->max_frame_size >
+ sc->sc_hw_ep_profile[ch].max_in_frame_size)
+ continue;
+ } else {
+ if (td->max_frame_size >
+ sc->sc_hw_ep_profile[ch].max_out_frame_size)
+ continue;
+ }
+ 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)
+{
+ struct musbotg_softc *sc;
+
+ sc = MUSBOTG_BUS2SC(udev->bus);
+
+ if (ep_addr == 0) {
+ /* control endpoint */
+ *ppf = musbotg_ep_profile;
+ } else if (ep_addr <= sc->sc_ep_max) {
+ /* other endpoints */
+ *ppf = sc->sc_hw_ep_profile + ep_addr;
+ } else {
+ *ppf = NULL;
+ }
+}
+
+static void
+musbotg_clocks_on(struct musbotg_softc *sc)
+{
+ if (sc->sc_flags.clocks_off &&
+ sc->sc_flags.port_powered) {
+
+ DPRINTFN(4, "\n");
+
+ if (sc->sc_clocks_on) {
+ (sc->sc_clocks_on) (sc->sc_clocks_arg);
+ }
+ sc->sc_flags.clocks_off = 0;
+
+ /* XXX enable Transceiver */
+ }
+}
+
+static void
+musbotg_clocks_off(struct musbotg_softc *sc)
+{
+ if (!sc->sc_flags.clocks_off) {
+
+ DPRINTFN(4, "\n");
+
+ /* XXX disable Transceiver */
+
+ if (sc->sc_clocks_off) {
+ (sc->sc_clocks_off) (sc->sc_clocks_arg);
+ }
+ sc->sc_flags.clocks_off = 1;
+ }
+}
+
+static void
+musbotg_pull_common(struct musbotg_softc *sc, uint8_t on)
+{
+ uint8_t temp;
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ if (on)
+ temp |= MUSB2_MASK_SOFTC;
+ else
+ temp &= ~MUSB2_MASK_SOFTC;
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+}
+
+static void
+musbotg_pull_up(struct musbotg_softc *sc)
+{
+ /* pullup D+, if possible */
+
+ if (!sc->sc_flags.d_pulled_up &&
+ sc->sc_flags.port_powered) {
+ sc->sc_flags.d_pulled_up = 1;
+ musbotg_pull_common(sc, 1);
+ }
+}
+
+static void
+musbotg_pull_down(struct musbotg_softc *sc)
+{
+ /* pulldown D+, if possible */
+
+ if (sc->sc_flags.d_pulled_up) {
+ sc->sc_flags.d_pulled_up = 0;
+ musbotg_pull_common(sc, 0);
+ }
+}
+
+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;
+
+ if (!(sc->sc_flags.status_suspend)) {
+ return;
+ }
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ temp |= MUSB2_MASK_RESUME;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+
+ /* wait 8 milliseconds */
+ /* Wait for reset to complete. */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ temp &= ~MUSB2_MASK_RESUME;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+}
+
+static void
+musbotg_set_address(struct musbotg_softc *sc, uint8_t addr)
+{
+ DPRINTFN(4, "addr=%d\n", addr);
+ addr &= 0x7F;
+ MUSB2_WRITE_1(sc, MUSB2_REG_FADDR, addr);
+}
+
+static uint8_t
+musbotg_dev_ctrl_setup_rx(struct musbotg_td *td)
+{
+ struct musbotg_softc *sc;
+ struct usb_device_request req;
+ uint16_t count;
+ uint8_t csr;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td, 0);
+
+ /* 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);
+
+ /*
+ * NOTE: If DATAEND is set we should not call the
+ * callback, hence the status stage is not complete.
+ */
+ if (csr & MUSB2_MASK_CSR0L_DATAEND) {
+ /* do not stall at this point */
+ td->did_stall = 1;
+ /* wait for interrupt */
+ DPRINTFN(1, "CSR0 DATAEND\n");
+ goto not_complete;
+ }
+
+ if (csr & MUSB2_MASK_CSR0L_SENTSTALL) {
+ /* clear SENTSTALL */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ /* get latest status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ /* update EP0 state */
+ sc->sc_ep0_busy = 0;
+ }
+ if (csr & MUSB2_MASK_CSR0L_SETUPEND) {
+ /* clear SETUPEND */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_SETUPEND_CLR);
+ /* get latest status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ /* update EP0 state */
+ sc->sc_ep0_busy = 0;
+ }
+ if (sc->sc_ep0_busy) {
+ DPRINTFN(1, "EP0 BUSY\n");
+ goto not_complete;
+ }
+ if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) {
+ goto not_complete;
+ }
+ /* get the packet byte count */
+ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
+
+ /* verify data length */
+ if (count != td->remainder) {
+ DPRINTFN(1, "Invalid SETUP packet "
+ "length, %d bytes\n", count);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_RXPKTRDY_CLR);
+ /* don't clear stall */
+ td->did_stall = 1;
+ goto not_complete;
+ }
+ if (count != sizeof(req)) {
+ DPRINTFN(1, "Unsupported SETUP packet "
+ "length, %d bytes\n", count);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_RXPKTRDY_CLR);
+ /* don't clear stall */
+ td->did_stall = 1;
+ goto not_complete;
+ }
+ /* clear did stall flag */
+ td->did_stall = 0;
+
+ /* receive data */
+ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), (void *)&req, sizeof(req));
+
+ /* copy data into real buffer */
+ usbd_copy_in(td->pc, 0, &req, sizeof(req));
+
+ td->offset = sizeof(req);
+ td->remainder = 0;
+
+ /* set pending command */
+ sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_RXPKTRDY_CLR;
+
+ /* we need set stall or dataend after this */
+ sc->sc_ep0_busy = 1;
+
+ /* sneak peek the set address */
+ if ((req.bmRequestType == UT_WRITE_DEVICE) &&
+ (req.bRequest == UR_SET_ADDRESS)) {
+ sc->sc_dv_addr = req.wValue[0] & 0x7F;
+ } else {
+ sc->sc_dv_addr = 0xFF;
+ }
+
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+
+not_complete:
+ /* abort any ongoing transfer */
+ if (!td->did_stall) {
+ DPRINTFN(4, "stalling\n");
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_SENDSTALL);
+ td->did_stall = 1;
+ }
+ 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, 1);
+
+ /* 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_dev_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);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* check if a command is pending */
+ if (sc->sc_ep0_cmd) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
+ sc->sc_ep0_cmd = 0;
+ }
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ got_short = 0;
+
+ if (csr & (MUSB2_MASK_CSR0L_SETUPEND |
+ MUSB2_MASK_CSR0L_SENTSTALL)) {
+ if (td->remainder == 0) {
+ /*
+ * We are actually complete and have
+ * received the next SETUP
+ */
+ DPRINTFN(4, "faking complete\n");
+ return (0); /* complete */
+ }
+ /*
+ * USB Host Aborted the transfer.
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) {
+ return (1); /* not complete */
+ }
+ /* 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;
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ 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;
+ }
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+ sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_RXPKTRDY_CLR;
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ }
+ /* write command - need more data */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_RXPKTRDY_CLR);
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_dev_ctrl_data_tx(struct musbotg_td *td)
+{
+ struct usb_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* check if a command is pending */
+ if (sc->sc_ep0_cmd) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
+ sc->sc_ep0_cmd = 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_SETUPEND |
+ MUSB2_MASK_CSR0L_SENTSTALL)) {
+ /*
+ * The current transfer was aborted
+ * by the USB Host
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) {
+ return (1); /* not complete */
+ }
+ 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;
+ }
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_TXPKTRDY;
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_TXPKTRDY);
+
+ return (1); /* not complete */
+}
+
+static uint8_t
+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, 0);
+
+ /* 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, 1);
+
+ /* 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;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
+ sc->sc_ep0_cmd = 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_DATAEND) {
+ /* wait for interrupt */
+ return (1); /* not complete */
+ }
+ if (sc->sc_dv_addr != 0xFF) {
+ /* write function address */
+ musbotg_set_address(sc, sc->sc_dv_addr);
+ }
+
+ musbotg_channel_free(sc, td);
+ return (0); /* complete */
+}
+
+static uint8_t
+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, 0);
+
+ /* 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, 1);
+
+ /* 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;
+ uint8_t csr;
+ uint8_t to;
+ uint8_t got_short;
+
+ to = 8; /* don't loop forever! */
+ got_short = 0;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td, 0);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
+ /* 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);
+
+ /* clear overrun */
+ if (csr & MUSB2_MASK_CSRL_RXOVERRUN) {
+ /* make sure we don't clear "RXPKTRDY" */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXPKTRDY);
+ }
+
+ /* check status */
+ if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY))
+ return (1); /* not complete */
+
+ /* 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 */
+ }
+ if (--to) {
+ goto repeat;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_dev_data_tx(struct musbotg_td *td)
+{
+ struct usb_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr;
+ uint8_t to;
+
+ to = 8; /* don't loop forever! */
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ if (td->channel == -1)
+ td->channel = musbotg_channel_alloc(sc, td, 1);
+
+ /* EP0 is busy, wait */
+ if (td->channel == -1)
+ return (1);
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->channel);
+
+repeat:
+
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ if (csr & (MUSB2_MASK_CSRL_TXINCOMP |
+ MUSB2_MASK_CSRL_TXUNDERRUN)) {
+ /* clear status bits */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ }
+ if (csr & MUSB2_MASK_CSRL_TXPKTRDY) {
+ return (1); /* not complete */
+ }
+ /* 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;
+ }
+
+ /* Max packet size */
+ MUSB2_WRITE_2(sc, MUSB2_REG_TXMAXP, td->reg_max_packet);
+
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXPKTRDY);
+
+ /* 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 */
+ }
+ if (--to) {
+ goto repeat;
+ }
+ return (1); /* not complete */
+}
+
+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, 0);
+
+ /* 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 */
+ if (td->transfer_type & MUSB2_MASK_TI_PROTO_ISOC)
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXNAKLIMIT, 0);
+ else
+ 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_2(sc, MUSB2_REG_RXMAXP, td->reg_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, 1);
+
+ /* 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 */
+ if (td->transfer_type & MUSB2_MASK_TI_PROTO_ISOC)
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXNAKLIMIT, 0);
+ else
+ 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_2(sc, MUSB2_REG_TXMAXP, td->reg_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;
+ }
+ if (td->error) {
+ goto done;
+ } else if (td->remainder > 0) {
+ /*
+ * We had a short transfer. If there is no alternate
+ * next, stop processing !
+ */
+ if (!td->alt_next) {
+ goto done;
+ }
+ }
+ /*
+ * Fetch the next transfer descriptor and transfer
+ * some flags to the next transfer descriptor
+ */
+ td = td->obj_next;
+ xfer->td_transfer_cache = td;
+ }
+
+ return (1); /* not complete */
+done:
+ /* compute all actual lengths */
+ musbotg_standard_done(xfer);
+
+ return (0); /* complete */
+}
+
+static void
+musbotg_interrupt_poll(struct musbotg_softc *sc)
+{
+ struct usb_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (!musbotg_xfer_do_fifo(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+void
+musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on)
+{
+ DPRINTFN(4, "vbus = %u\n", is_on);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ if (is_on) {
+ if (!sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 1;
+
+ /* complete root HUB interrupt endpoint */
+ musbotg_root_intr(sc);
+ }
+ } else {
+ if (sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* complete root HUB interrupt endpoint */
+ musbotg_root_intr(sc);
+ }
+ }
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+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;
+ uint8_t temp;
+ uint8_t to = 2;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+repeat:
+
+ /* read all interrupt registers */
+ usb_status = MUSB2_READ_1(sc, MUSB2_REG_INTUSB);
+
+ /* 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_ICONN | MUSB2_MASK_IDISC |
+ MUSB2_MASK_IVBUSERR)) {
+
+ DPRINTFN(4, "real bus interrupt 0x%08x\n", usb_status);
+
+ if (usb_status & MUSB2_MASK_IRESET) {
+
+ /* set correct state */
+ sc->sc_flags.status_bus_reset = 1;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* determine line speed */
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ if (temp & MUSB2_MASK_HSMODE)
+ sc->sc_flags.status_high_speed = 1;
+ else
+ sc->sc_flags.status_high_speed = 0;
+
+ /*
+ * After reset all interrupts are on and we need to
+ * turn them off!
+ */
+ temp = MUSB2_MASK_IRESET;
+ /* disable resume interrupt */
+ temp &= ~MUSB2_MASK_IRESUME;
+ /* enable suspend interrupt */
+ temp |= MUSB2_MASK_ISUSP;
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp);
+ /* disable TX and RX interrupts */
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0);
+ }
+ /*
+ * If RXRSM and RXSUSP is set at the same time we interpret
+ * that like RESUME. Resume is set when there is at least 3
+ * milliseconds of inactivity on the USB BUS.
+ */
+ if (usb_status & MUSB2_MASK_IRESUME) {
+ if (sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 1;
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_INTUSBE);
+ /* disable resume interrupt */
+ temp &= ~MUSB2_MASK_IRESUME;
+ /* enable suspend interrupt */
+ temp |= MUSB2_MASK_ISUSP;
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp);
+ }
+ } else if (usb_status & MUSB2_MASK_ISUSP) {
+ if (!sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 1;
+ sc->sc_flags.change_suspend = 1;
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_INTUSBE);
+ /* disable suspend interrupt */
+ temp &= ~MUSB2_MASK_ISUSP;
+ /* enable resume interrupt */
+ temp |= MUSB2_MASK_IRESUME;
+ 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) {
+ /* check for VBUS error in USB host mode */
+ if (usb_status & MUSB2_MASK_IVBUSERR) {
+ temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
+ temp |= MUSB2_MASK_SESS;
+ MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp);
+ }
+ 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);
+ }
+ /* check for any endpoint interrupts */
+
+ if (rx_status || tx_status) {
+ DPRINTFN(4, "real endpoint interrupt "
+ "rx=0x%04x, tx=0x%04x\n", rx_status, tx_status);
+ }
+ /* poll one time regardless of FIFO status */
+
+ musbotg_interrupt_poll(sc);
+
+ if (--to)
+ goto repeat;
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+musbotg_setup_standard_chain_sub(struct musbotg_std_temp *temp)
+{
+ struct musbotg_td *td;
+
+ /* get current Transfer Descriptor */
+ td = temp->td_next;
+ temp->td = td;
+
+ /* prepare for next TD */
+ temp->td_next = td->obj_next;
+
+ /* fill out the Transfer Descriptor */
+ td->func = temp->func;
+ td->pc = temp->pc;
+ 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
+musbotg_setup_standard_chain(struct usb_xfer *xfer)
+{
+ struct musbotg_std_temp temp;
+ struct musbotg_softc *sc;
+ 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];
+ xfer->td_transfer_first = td;
+ 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 ||
+ xfer->flags_int.isochronous_xfr;
+ 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;
+
+ if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
+ speed = usbd_get_speed(xfer->xroot->udev);
+
+ 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->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) {
+
+ 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;
+
+ musbotg_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ tx = 0;
+
+ if (x != xfer->nframes) {
+ 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 (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 */
+ temp.pc = xfer->frbuffers + x;
+ }
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+
+ x++;
+
+ if (x == xfer->nframes) {
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_act) {
+ temp.setup_alt_next = 0;
+ }
+ } else {
+ temp.setup_alt_next = 0;
+ }
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.short_pkt = 0;
+
+ } else {
+
+ if (xfer->flags_int.isochronous_xfr) {
+ /* isochronous data transfer */
+ /* don't force short */
+ temp.short_pkt = 1;
+ } else {
+ /* regular data transfer */
+ temp.short_pkt = (xfer->flags.force_short_xfer ? 0 : 1);
+ }
+ }
+
+ musbotg_setup_standard_chain_sub(&temp);
+
+ if (xfer->flags_int.isochronous_xfr) {
+ temp.offset += temp.len;
+ } else {
+ /* get next Page Cache pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ }
+
+ /* check for control transfer */
+ if (xfer->flags_int.control_xfr) {
+
+ /* always setup a valid "pc" pointer for status and sync */
+ temp.pc = xfer->frbuffers + 0;
+ temp.len = 0;
+ temp.short_pkt = 0;
+ temp.setup_alt_next = 0;
+
+ /* check if we should append a status stage */
+ if (!xfer->flags_int.control_act) {
+ /*
+ * Send a DATA1 message and invert the current
+ * endpoint direction.
+ */
+ 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);
+ }
+ }
+ /* must have at least one frame! */
+ td = temp.td;
+ xfer->td_transfer_last = td;
+}
+
+static void
+musbotg_timeout(void *arg)
+{
+ struct usb_xfer *xfer = arg;
+
+ DPRINTFN(1, "xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ musbotg_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+musbotg_ep_int_set(struct musbotg_softc *sc, int channel, int on)
+{
+ uint16_t temp;
+
+ /*
+ * Only enable the endpoint interrupt when we are
+ * actually waiting for data, hence we are dealing
+ * with level triggered interrupts !
+ */
+ 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);
+ else
+ temp &= ~MUSB2_MASK_EPINT(0);
+
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp);
+ } else {
+ 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);
+
+ 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
+musbotg_start_standard_chain(struct usb_xfer *xfer)
+{
+ DPRINTFN(8, "\n");
+
+ /* poll one time */
+ if (musbotg_xfer_do_fifo(xfer)) {
+
+ DPRINTFN(14, "enabled interrupts on endpoint\n");
+
+ /* put transfer on interrupt queue */
+ usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usbd_transfer_timeout_ms(xfer,
+ &musbotg_timeout, xfer->timeout);
+ }
+ }
+}
+
+static void
+musbotg_root_intr(struct musbotg_softc *sc)
+{
+ DPRINTFN(8, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ /* set port bit */
+ sc->sc_hub_idata[0] = 0x02; /* we only have one port */
+
+ uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata,
+ sizeof(sc->sc_hub_idata));
+}
+
+static usb_error_t
+musbotg_standard_done_sub(struct usb_xfer *xfer)
+{
+ struct musbotg_td *td;
+ uint32_t len;
+ uint8_t error;
+
+ DPRINTFN(8, "\n");
+
+ td = xfer->td_transfer_cache;
+
+ do {
+ len = td->remainder;
+
+ xfer->endpoint->toggle_next = td->toggle;
+
+ if (xfer->aframes != xfer->nframes) {
+ /*
+ * Verify the length and subtract
+ * the remainder from "frlengths[]":
+ */
+ if (len > xfer->frlengths[xfer->aframes]) {
+ td->error = 1;
+ } else {
+ xfer->frlengths[xfer->aframes] -= len;
+ }
+ }
+ /* Check for transfer error */
+ if (td->error) {
+ /* the transfer is finished */
+ error = 1;
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok ||
+ xfer->flags_int.isochronous_xfr) {
+ /* follow alt next */
+ if (td->alt_next) {
+ td = td->obj_next;
+ } else {
+ td = NULL;
+ }
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ error = 0;
+ break;
+ }
+ td = td->obj_next;
+
+ /* this USB frame is complete */
+ error = 0;
+ break;
+
+ } while (0);
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ return (error ?
+ USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+musbotg_standard_done(struct usb_xfer *xfer)
+{
+ usb_error_t err = 0;
+
+ DPRINTFN(12, "xfer=%p endpoint=%p transfer done\n",
+ xfer, xfer->endpoint);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ err = musbotg_standard_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = musbotg_standard_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = musbotg_standard_done_sub(xfer);
+ }
+done:
+ musbotg_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * musbotg_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+ *------------------------------------------------------------------------*/
+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(1, "xfer=%p, endpoint=%p, error=%d\n",
+ xfer, xfer->endpoint, error);
+
+ DPRINTFN(14, "disabled interrupts on endpoint\n");
+
+ sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ td = xfer->td_transfer_cache;
+
+ if (td && (td->channel != -1))
+ musbotg_channel_free(sc, td);
+
+ /* dequeue transfer and start next transfer */
+ usbd_transfer_done(xfer, error);
+}
+
+static void
+musbotg_xfer_stall(struct usb_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_STALLED);
+}
+
+static void
+musbotg_set_stall(struct usb_device *udev,
+ struct usb_endpoint *ep, uint8_t *did_stall)
+{
+ struct musbotg_softc *sc;
+ uint8_t ep_no;
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ DPRINTFN(4, "endpoint=%p\n", ep);
+
+ /* set FORCESTALL */
+ sc = MUSBOTG_BUS2SC(udev->bus);
+
+ ep_no = (ep->edesc->bEndpointAddress & UE_ADDR);
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, ep_no);
+
+ if (ep->edesc->bEndpointAddress & UE_DIR_IN) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXSENDSTALL);
+ } else {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXSENDSTALL);
+ }
+}
+
+static void
+musbotg_clear_stall_sub(struct musbotg_softc *sc, uint16_t wMaxPacket,
+ uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir)
+{
+ uint16_t mps;
+ uint16_t temp;
+ uint8_t csr;
+
+ if (ep_type == UE_CONTROL) {
+ /* clearing stall is not needed */
+ return;
+ }
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, ep_no);
+
+ /* compute max frame size */
+ mps = wMaxPacket & 0x7FF;
+ switch ((wMaxPacket >> 11) & 3) {
+ case 1:
+ mps *= 2;
+ break;
+ case 2:
+ mps *= 3;
+ break;
+ default:
+ break;
+ }
+
+ if (ep_dir == UE_DIR_IN) {
+
+ temp = 0;
+
+ /* Configure endpoint */
+ switch (ep_type) {
+ case UE_INTERRUPT:
+ MUSB2_WRITE_2(sc, MUSB2_REG_TXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH,
+ MUSB2_MASK_CSRH_TXMODE | temp);
+ break;
+ case UE_ISOCHRONOUS:
+ MUSB2_WRITE_2(sc, MUSB2_REG_TXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH,
+ MUSB2_MASK_CSRH_TXMODE |
+ MUSB2_MASK_CSRH_TXISO | temp);
+ break;
+ case UE_BULK:
+ MUSB2_WRITE_2(sc, MUSB2_REG_TXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH,
+ MUSB2_MASK_CSRH_TXMODE | temp);
+ break;
+ default:
+ break;
+ }
+
+ /* Need to flush twice in case of double bufring */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXFFLUSH);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXFFLUSH);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ }
+ }
+ /* reset data toggle */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXDT_CLR);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ /* set double/single buffering */
+ temp = MUSB2_READ_2(sc, MUSB2_REG_TXDBDIS);
+ if (mps <= (sc->sc_hw_ep_profile[ep_no].
+ max_in_frame_size / 2)) {
+ /* double buffer */
+ temp &= ~(1 << ep_no);
+ } else {
+ /* single buffer */
+ temp |= (1 << ep_no);
+ }
+ MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, temp);
+
+ /* clear sent stall */
+ if (csr & MUSB2_MASK_CSRL_TXSENTSTALL) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ }
+ } else {
+
+ temp = 0;
+
+ /* Configure endpoint */
+ switch (ep_type) {
+ case UE_INTERRUPT:
+ MUSB2_WRITE_2(sc, MUSB2_REG_RXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH,
+ MUSB2_MASK_CSRH_RXNYET | temp);
+ break;
+ case UE_ISOCHRONOUS:
+ MUSB2_WRITE_2(sc, MUSB2_REG_RXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH,
+ MUSB2_MASK_CSRH_RXNYET |
+ MUSB2_MASK_CSRH_RXISO | temp);
+ break;
+ case UE_BULK:
+ MUSB2_WRITE_2(sc, MUSB2_REG_RXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, temp);
+ break;
+ default:
+ break;
+ }
+
+ /* Need to flush twice in case of double bufring */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+ if (csr & MUSB2_MASK_CSRL_RXPKTRDY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXFFLUSH);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+ if (csr & MUSB2_MASK_CSRL_RXPKTRDY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXFFLUSH);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+ }
+ }
+ /* reset data toggle */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXDT_CLR);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+
+ /* set double/single buffering */
+ temp = MUSB2_READ_2(sc, MUSB2_REG_RXDBDIS);
+ if (mps <= (sc->sc_hw_ep_profile[ep_no].
+ max_out_frame_size / 2)) {
+ /* double buffer */
+ temp &= ~(1 << ep_no);
+ } else {
+ /* single buffer */
+ temp |= (1 << ep_no);
+ }
+ MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, temp);
+
+ /* clear sent stall */
+ if (csr & MUSB2_MASK_CSRL_RXSENTSTALL) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0);
+ }
+ }
+}
+
+static void
+musbotg_clear_stall(struct usb_device *udev, struct usb_endpoint *ep)
+{
+ struct musbotg_softc *sc;
+ struct usb_endpoint_descriptor *ed;
+
+ DPRINTFN(4, "endpoint=%p\n", ep);
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ /* check mode */
+ if (udev->flags.usb_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ /* get softc */
+ sc = MUSBOTG_BUS2SC(udev->bus);
+
+ /* get endpoint descriptor */
+ ed = ep->edesc;
+
+ /* reset endpoint */
+ musbotg_clear_stall_sub(sc,
+ UGETW(ed->wMaxPacketSize),
+ (ed->bEndpointAddress & UE_ADDR),
+ (ed->bmAttributes & UE_XFERTYPE),
+ (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
+}
+
+usb_error_t
+musbotg_init(struct musbotg_softc *sc)
+{
+ struct usb_hw_ep_profile *pf;
+ uint16_t offset;
+ uint8_t nrx;
+ uint8_t ntx;
+ uint8_t temp;
+ uint8_t fsize;
+ uint8_t frx;
+ uint8_t ftx;
+ uint8_t dynfifo;
+
+ DPRINTFN(1, "start\n");
+
+ /* set up the bus structure */
+ sc->sc_bus.usbrev = USB_REV_2_0;
+ sc->sc_bus.methods = &musbotg_bus_methods;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* turn on clocks */
+
+ 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);
+
+ /* disable pullup */
+
+ musbotg_pull_common(sc, 0);
+
+ /* 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);
+
+ /* enable HighSpeed and ISO Update flags */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER,
+ MUSB2_MASK_HSENAB | MUSB2_MASK_ISOUPD);
+
+ 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);
+ }
+
+ /* wait a little for things to stabilise */
+ usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 10);
+
+ DPRINTF("DEVCTL=0x%02x\n", temp);
+
+ /* disable testmode */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_TESTMODE, 0);
+
+ /* set default value */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_MISC, 0);
+
+ /* select endpoint index 0 */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* read out number of endpoints */
+
+ nrx =
+ (MUSB2_READ_1(sc, MUSB2_REG_EPINFO) / 16);
+
+ ntx =
+ (MUSB2_READ_1(sc, MUSB2_REG_EPINFO) % 16);
+
+ /* these numbers exclude the control endpoint */
+
+ DPRINTFN(2, "RX/TX endpoints: %u/%u\n", nrx, ntx);
+
+ sc->sc_ep_max = (nrx > ntx) ? nrx : ntx;
+ if (sc->sc_ep_max == 0) {
+ DPRINTFN(2, "ERROR: Looks like the clocks are off!\n");
+ }
+ /* read out configuration data */
+
+ sc->sc_conf_data = MUSB2_READ_1(sc, MUSB2_REG_CONFDATA);
+
+ DPRINTFN(2, "Config Data: 0x%02x\n",
+ sc->sc_conf_data);
+
+ dynfifo = (sc->sc_conf_data & MUSB2_MASK_CD_DYNFIFOSZ) ? 1 : 0;
+
+ if (dynfifo) {
+ device_printf(sc->sc_bus.bdev, "Dynamic FIFO sizing detected, "
+ "assuming 16Kbytes of FIFO RAM\n");
+ }
+
+ DPRINTFN(2, "HW version: 0x%04x\n",
+ MUSB2_READ_1(sc, MUSB2_REG_HWVERS));
+
+ /* initialise endpoint profiles */
+
+ offset = 0;
+
+ for (temp = 1; temp <= sc->sc_ep_max; temp++) {
+ pf = sc->sc_hw_ep_profile + temp;
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, temp);
+
+ fsize = MUSB2_READ_1(sc, MUSB2_REG_FSIZE);
+ frx = (fsize & MUSB2_MASK_RX_FSIZE) / 16;
+ ftx = (fsize & MUSB2_MASK_TX_FSIZE);
+
+ DPRINTF("Endpoint %u FIFO size: IN=%u, OUT=%u, DYN=%d\n",
+ temp, ftx, frx, dynfifo);
+
+ if (dynfifo) {
+ if (frx && (temp <= nrx)) {
+ if (temp == 1) {
+ frx = 12; /* 4K */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ,
+ MUSB2_VAL_FIFOSZ_4096 |
+ MUSB2_MASK_FIFODB);
+ } else if (temp < 8) {
+ frx = 10; /* 1K */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ,
+ MUSB2_VAL_FIFOSZ_512 |
+ MUSB2_MASK_FIFODB);
+ } else {
+ frx = 7; /* 128 bytes */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXFIFOSZ,
+ MUSB2_VAL_FIFOSZ_128);
+ }
+
+ MUSB2_WRITE_2(sc, MUSB2_REG_RXFIFOADD,
+ offset >> 3);
+
+ offset += (1 << frx);
+ }
+ if (ftx && (temp <= ntx)) {
+ if (temp == 1) {
+ ftx = 12; /* 4K */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
+ MUSB2_VAL_FIFOSZ_4096 |
+ MUSB2_MASK_FIFODB);
+ } else if (temp < 8) {
+ ftx = 10; /* 1K */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
+ MUSB2_VAL_FIFOSZ_512 |
+ MUSB2_MASK_FIFODB);
+ } else {
+ ftx = 7; /* 128 bytes */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXFIFOSZ,
+ MUSB2_VAL_FIFOSZ_128);
+ }
+
+ MUSB2_WRITE_2(sc, MUSB2_REG_TXFIFOADD,
+ offset >> 3);
+
+ offset += (1 << ftx);
+ }
+ }
+
+ if (frx && ftx && (temp <= nrx) && (temp <= ntx)) {
+ pf->max_in_frame_size = 1 << ftx;
+ pf->max_out_frame_size = 1 << frx;
+ pf->is_simplex = 0; /* duplex */
+ pf->support_multi_buffer = 1;
+ pf->support_bulk = 1;
+ pf->support_interrupt = 1;
+ pf->support_isochronous = 1;
+ pf->support_in = 1;
+ pf->support_out = 1;
+ } else if (frx && (temp <= nrx)) {
+ pf->max_out_frame_size = 1 << frx;
+ pf->max_in_frame_size = 0;
+ pf->is_simplex = 1; /* simplex */
+ pf->support_multi_buffer = 1;
+ pf->support_bulk = 1;
+ pf->support_interrupt = 1;
+ pf->support_isochronous = 1;
+ pf->support_out = 1;
+ } else if (ftx && (temp <= ntx)) {
+ pf->max_in_frame_size = 1 << ftx;
+ pf->max_out_frame_size = 0;
+ pf->is_simplex = 1; /* simplex */
+ pf->support_multi_buffer = 1;
+ pf->support_bulk = 1;
+ pf->support_interrupt = 1;
+ pf->support_isochronous = 1;
+ pf->support_in = 1;
+ }
+ }
+
+ DPRINTFN(2, "Dynamic FIFO size = %d bytes\n", offset);
+
+ /* turn on default interrupts */
+
+ 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);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch any lost interrupts */
+
+ musbotg_do_poll(&sc->sc_bus);
+
+ return (0); /* success */
+}
+
+void
+musbotg_uninit(struct musbotg_softc *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* disable all interrupts */
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0);
+
+ sc->sc_flags.port_powered = 0;
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ musbotg_pull_down(sc);
+ musbotg_clocks_off(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+musbotg_do_poll(struct usb_bus *bus)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ musbotg_interrupt_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*------------------------------------------------------------------------*
+ * musbotg bulk support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_bulk_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_bulk_close(struct usb_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_device_bulk_enter(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_bulk_start(struct usb_xfer *xfer)
+{
+ /* setup TDs */
+ musbotg_setup_standard_chain(xfer);
+ musbotg_start_standard_chain(xfer);
+}
+
+static const struct usb_pipe_methods musbotg_device_bulk_methods =
+{
+ .open = musbotg_device_bulk_open,
+ .close = musbotg_device_bulk_close,
+ .enter = musbotg_device_bulk_enter,
+ .start = musbotg_device_bulk_start,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg control support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_ctrl_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_ctrl_close(struct usb_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_device_ctrl_enter(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_ctrl_start(struct usb_xfer *xfer)
+{
+ /* setup TDs */
+ musbotg_setup_standard_chain(xfer);
+ musbotg_start_standard_chain(xfer);
+}
+
+static const struct usb_pipe_methods musbotg_device_ctrl_methods =
+{
+ .open = musbotg_device_ctrl_open,
+ .close = musbotg_device_ctrl_close,
+ .enter = musbotg_device_ctrl_enter,
+ .start = musbotg_device_ctrl_start,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg interrupt support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_intr_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_intr_close(struct usb_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_device_intr_enter(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_intr_start(struct usb_xfer *xfer)
+{
+ /* setup TDs */
+ musbotg_setup_standard_chain(xfer);
+ musbotg_start_standard_chain(xfer);
+}
+
+static const struct usb_pipe_methods musbotg_device_intr_methods =
+{
+ .open = musbotg_device_intr_open,
+ .close = musbotg_device_intr_close,
+ .enter = musbotg_device_intr_enter,
+ .start = musbotg_device_intr_start,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg full speed isochronous support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_isoc_open(struct usb_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_isoc_close(struct usb_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_device_isoc_enter(struct usb_xfer *xfer)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ uint32_t temp;
+ uint32_t nframes;
+ uint32_t fs_frames;
+
+ DPRINTFN(5, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->endpoint->isoc_next, xfer->nframes);
+
+ /* get the current frame index */
+
+ nframes = MUSB2_READ_2(sc, MUSB2_REG_FRAME);
+
+ /*
+ * check if the frame index is within the window where the frames
+ * will be inserted
+ */
+ temp = (nframes - xfer->endpoint->isoc_next) & MUSB2_MASK_FRAME;
+
+ if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
+ fs_frames = (xfer->nframes + 7) / 8;
+ } else {
+ fs_frames = xfer->nframes;
+ }
+
+ if ((xfer->endpoint->is_synced == 0) ||
+ (temp < fs_frames)) {
+ /*
+ * If there is data underflow or the pipe queue is
+ * empty we schedule the transfer a few frames ahead
+ * of the current frame position. Else two isochronous
+ * transfers might overlap.
+ */
+ xfer->endpoint->isoc_next = (nframes + 3) & MUSB2_MASK_FRAME;
+ xfer->endpoint->is_synced = 1;
+ DPRINTFN(2, "start next=%d\n", xfer->endpoint->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ temp = (xfer->endpoint->isoc_next - nframes) & MUSB2_MASK_FRAME;
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb_isoc_time_expand(&sc->sc_bus, nframes) + temp +
+ fs_frames;
+
+ /* compute frame number for next insertion */
+ xfer->endpoint->isoc_next += fs_frames;
+
+ /* setup TDs */
+ musbotg_setup_standard_chain(xfer);
+}
+
+static void
+musbotg_device_isoc_start(struct usb_xfer *xfer)
+{
+ /* start TD chain */
+ musbotg_start_standard_chain(xfer);
+}
+
+static const struct usb_pipe_methods musbotg_device_isoc_methods =
+{
+ .open = musbotg_device_isoc_open,
+ .close = musbotg_device_isoc_close,
+ .enter = musbotg_device_isoc_enter,
+ .start = musbotg_device_isoc_start,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg root control support
+ *------------------------------------------------------------------------*
+ * Simulate a hardware HUB by handling all the necessary requests.
+ *------------------------------------------------------------------------*/
+
+static const struct usb_device_descriptor musbotg_devd = {
+ .bLength = sizeof(struct usb_device_descriptor),
+ .bDescriptorType = UDESC_DEVICE,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_HSHUBSTT,
+ .bMaxPacketSize = 64,
+ .bcdDevice = {0x00, 0x01},
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .bNumConfigurations = 1,
+};
+
+static const struct usb_device_qualifier musbotg_odevd = {
+ .bLength = sizeof(struct usb_device_qualifier),
+ .bDescriptorType = UDESC_DEVICE_QUALIFIER,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize0 = 0,
+ .bNumConfigurations = 0,
+};
+
+static const struct musbotg_config_desc musbotg_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(musbotg_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0,
+ },
+ .ifcd = {
+ .bLength = sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = 0,
+ },
+ .endpd = {
+ .bLength = sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = (UE_DIR_IN | MUSBOTG_INTR_ENDPT),
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8,
+ .bInterval = 255,
+ },
+};
+
+#define HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
+
+static const struct usb_hub_descriptor_min musbotg_hubd = {
+ .bDescLength = sizeof(musbotg_hubd),
+ .bDescriptorType = UDESC_HUB,
+ .bNbrPorts = 1,
+ HSETW(.wHubCharacteristics, (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL)),
+ .bPwrOn2PwrGood = 50,
+ .bHubContrCurrent = 0,
+ .DeviceRemovable = {0}, /* port is removable */
+};
+
+#define STRING_VENDOR \
+ "M\0e\0n\0t\0o\0r\0 \0G\0r\0a\0p\0h\0i\0c\0s"
+
+#define STRING_PRODUCT \
+ "O\0T\0G\0 \0R\0o\0o\0t\0 \0H\0U\0B"
+
+USB_MAKE_STRING_DESC(STRING_VENDOR, musbotg_vendor);
+USB_MAKE_STRING_DESC(STRING_PRODUCT, musbotg_product);
+
+static usb_error_t
+musbotg_roothub_exec(struct usb_device *udev,
+ struct usb_device_request *req, const void **pptr, uint16_t *plength)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(udev->bus);
+ const void *ptr;
+ 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);
+
+ /* buffer reset */
+ ptr = (const void *)&sc->sc_hub_temp;
+ len = 0;
+ err = 0;
+
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+
+ /* demultiplex the control request */
+
+ switch (req->bmRequestType) {
+ case UT_READ_DEVICE:
+ switch (req->bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_descriptor;
+ case UR_GET_CONFIG:
+ goto tr_handle_get_config;
+ case UR_GET_STATUS:
+ goto tr_handle_get_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_DEVICE:
+ switch (req->bRequest) {
+ case UR_SET_ADDRESS:
+ goto tr_handle_set_address;
+ case UR_SET_CONFIG:
+ goto tr_handle_set_config;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_DESCRIPTOR:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_ENDPOINT:
+ switch (req->bRequest) {
+ case UR_CLEAR_FEATURE:
+ switch (UGETW(req->wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_clear_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_clear_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SET_FEATURE:
+ switch (UGETW(req->wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_set_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_set_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SYNCH_FRAME:
+ goto tr_valid; /* nop */
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_ENDPOINT:
+ switch (req->bRequest) {
+ case UR_GET_STATUS:
+ goto tr_handle_get_ep_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_INTERFACE:
+ switch (req->bRequest) {
+ case UR_SET_INTERFACE:
+ goto tr_handle_set_interface;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_INTERFACE:
+ switch (req->bRequest) {
+ case UR_GET_INTERFACE:
+ goto tr_handle_get_interface;
+ case UR_GET_STATUS:
+ goto tr_handle_get_iface_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_INTERFACE:
+ case UT_WRITE_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_READ_CLASS_INTERFACE:
+ case UT_READ_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_WRITE_CLASS_DEVICE:
+ switch (req->bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_valid;
+ case UR_SET_DESCRIPTOR:
+ case UR_SET_FEATURE:
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_OTHER:
+ switch (req->bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_handle_clear_port_feature;
+ case UR_SET_FEATURE:
+ goto tr_handle_set_port_feature;
+ case UR_CLEAR_TT_BUFFER:
+ case UR_RESET_TT:
+ case UR_STOP_TT:
+ goto tr_valid;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_OTHER:
+ switch (req->bRequest) {
+ case UR_GET_TT_STATE:
+ goto tr_handle_get_tt_state;
+ case UR_GET_STATUS:
+ goto tr_handle_get_port_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_DEVICE:
+ switch (req->bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_class_descriptor;
+ case UR_GET_STATUS:
+ goto tr_handle_get_class_status;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_valid;
+
+tr_handle_get_descriptor:
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ len = sizeof(musbotg_devd);
+ ptr = (const void *)&musbotg_devd;
+ goto tr_valid;
+ case UDESC_DEVICE_QUALIFIER:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ len = sizeof(musbotg_odevd);
+ ptr = (const void *)&musbotg_odevd;
+ goto tr_valid;
+ case UDESC_CONFIG:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ len = sizeof(musbotg_confd);
+ ptr = (const void *)&musbotg_confd;
+ goto tr_valid;
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ len = sizeof(usb_string_lang_en);
+ ptr = (const void *)&usb_string_lang_en;
+ goto tr_valid;
+
+ case 1: /* Vendor */
+ len = sizeof(musbotg_vendor);
+ ptr = (const void *)&musbotg_vendor;
+ goto tr_valid;
+
+ case 2: /* Product */
+ len = sizeof(musbotg_product);
+ ptr = (const void *)&musbotg_product;
+ goto tr_valid;
+ default:
+ break;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_stalled;
+
+tr_handle_get_config:
+ len = 1;
+ sc->sc_hub_temp.wValue[0] = sc->sc_conf;
+ goto tr_valid;
+
+tr_handle_get_status:
+ len = 2;
+ USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED);
+ goto tr_valid;
+
+tr_handle_set_address:
+ if (value & 0xFF00) {
+ goto tr_stalled;
+ }
+ sc->sc_rt_addr = value;
+ goto tr_valid;
+
+tr_handle_set_config:
+ if (value >= 2) {
+ goto tr_stalled;
+ }
+ sc->sc_conf = value;
+ goto tr_valid;
+
+tr_handle_get_interface:
+ len = 1;
+ sc->sc_hub_temp.wValue[0] = 0;
+ goto tr_valid;
+
+tr_handle_get_tt_state:
+tr_handle_get_class_status:
+tr_handle_get_iface_status:
+tr_handle_get_ep_status:
+ len = 2;
+ USETW(sc->sc_hub_temp.wValue, 0);
+ goto tr_valid;
+
+tr_handle_set_halt:
+tr_handle_set_interface:
+tr_handle_set_wakeup:
+tr_handle_clear_wakeup:
+tr_handle_clear_halt:
+ goto tr_valid;
+
+tr_handle_clear_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(8, "UR_CLEAR_PORT_FEATURE on port %d\n", index);
+
+ switch (value) {
+ case UHF_PORT_SUSPEND:
+ 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_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);
+ musbotg_clocks_off(sc);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ sc->sc_flags.change_connect = 0;
+ break;
+ case UHF_C_PORT_SUSPEND:
+ sc->sc_flags.change_suspend = 0;
+ break;
+ default:
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_set_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(8, "UR_SET_PORT_FEATURE\n");
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ 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 */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 1;
+ break;
+ default:
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_get_port_status:
+
+ DPRINTFN(8, "UR_GET_PORT_STATUS\n");
+
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ if (sc->sc_flags.status_vbus) {
+ musbotg_clocks_on(sc);
+ musbotg_pull_up(sc);
+ } else {
+ musbotg_pull_down(sc);
+ musbotg_clocks_off(sc);
+ }
+
+ /* Select Device Side Mode */
+ 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;
+ }
+ if (sc->sc_flags.port_powered) {
+ value |= UPS_PORT_POWER;
+ }
+ 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;
+ }
+ if (sc->sc_flags.status_suspend) {
+ value |= UPS_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortStatus, value);
+
+ value = 0;
+
+ if (sc->sc_flags.change_connect) {
+ value |= UPS_C_CONNECT_STATUS;
+
+ 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)
+ 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;
+
+tr_handle_get_class_descriptor:
+ if (value & 0xFF) {
+ goto tr_stalled;
+ }
+ ptr = (const void *)&musbotg_hubd;
+ len = sizeof(musbotg_hubd);
+ goto tr_valid;
+
+tr_stalled:
+ err = USB_ERR_STALLED;
+tr_valid:
+done:
+ *plength = len;
+ *pptr = ptr;
+ return (err);
+}
+
+static void
+musbotg_xfer_setup(struct usb_setup_params *parm)
+{
+ struct musbotg_softc *sc;
+ struct usb_xfer *xfer;
+ void *last_obj;
+ uint32_t ntd;
+ uint32_t n;
+ uint8_t ep_no;
+
+ sc = MUSBOTG_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ /*
+ * NOTE: This driver does not use any of the parameters that
+ * are computed from the following values. Just set some
+ * reasonable dummies:
+ */
+ parm->hc_max_packet_size = 0x400;
+ parm->hc_max_frame_size = 0xc00;
+
+ if ((parm->methods == &musbotg_device_isoc_methods) ||
+ (parm->methods == &musbotg_device_intr_methods))
+ parm->hc_max_packet_count = 3;
+ else
+ parm->hc_max_packet_count = 1;
+
+ usbd_transfer_setup_sub(parm);
+
+ /*
+ * compute maximum number of TDs
+ */
+ if (parm->methods == &musbotg_device_ctrl_methods) {
+
+ ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &musbotg_device_bulk_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &musbotg_device_intr_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &musbotg_device_isoc_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else {
+
+ ntd = 0;
+ }
+
+ /*
+ * check if "usbd_transfer_setup_sub" set an error
+ */
+ if (parm->err) {
+ return;
+ }
+ /*
+ * allocate transfer descriptors
+ */
+ last_obj = NULL;
+
+ ep_no = xfer->endpointno & UE_ADDR;
+
+ /*
+ * Check for a valid endpoint profile in USB device mode:
+ */
+ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
+ const struct usb_hw_ep_profile *pf;
+
+ musbotg_get_hw_ep_profile(parm->udev, &pf, ep_no);
+
+ if (pf == NULL) {
+ /* should not happen */
+ parm->err = USB_ERR_INVAL;
+ return;
+ }
+ }
+
+ /* align data */
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+
+ for (n = 0; n != ntd; n++) {
+
+ struct musbotg_td *td;
+
+ if (parm->buf) {
+
+ td = USB_ADD_BYTES(parm->buf, parm->size[0]);
+
+ /* init TD */
+ td->max_frame_size = xfer->max_frame_size;
+ td->reg_max_packet = xfer->max_packet_size |
+ ((xfer->max_packet_count - 1) << 11);
+ td->ep_no = ep_no;
+ td->obj_next = last_obj;
+
+ last_obj = td;
+ }
+ parm->size[0] += sizeof(*td);
+ }
+
+ xfer->td_start[0] = last_obj;
+}
+
+static void
+musbotg_xfer_unsetup(struct usb_xfer *xfer)
+{
+ return;
+}
+
+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)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ ep, udev->address,
+ edesc->bEndpointAddress, udev->flags.usb_mode,
+ sc->sc_rt_addr);
+
+ if (udev->device_index != sc->sc_rt_addr) {
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ ep->methods = &musbotg_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ ep->methods = &musbotg_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ ep->methods = &musbotg_device_isoc_methods;
+ break;
+ case UE_BULK:
+ ep->methods = &musbotg_device_bulk_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+}
+
+static void
+musbotg_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(bus);
+
+ switch (state) {
+ case USB_HW_POWER_SUSPEND:
+ musbotg_uninit(sc);
+ break;
+ case USB_HW_POWER_SHUTDOWN:
+ musbotg_uninit(sc);
+ break;
+ case USB_HW_POWER_RESUME:
+ musbotg_init(sc);
+ break;
+ default:
+ break;
+ }
+}
+
+static const 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,
+ .xfer_stall = &musbotg_xfer_stall,
+ .set_stall = &musbotg_set_stall,
+ .clear_stall = &musbotg_clear_stall,
+ .roothub_exec = &musbotg_roothub_exec,
+ .xfer_poll = &musbotg_do_poll,
+ .set_hw_power_sleep = &musbotg_set_hw_power_sleep,
+};
diff --git a/freebsd/sys/dev/usb/controller/musb_otg.h b/freebsd/sys/dev/usb/controller/musb_otg.h
new file mode 100644
index 0000000..7fe19a7
--- /dev/null
+++ b/freebsd/sys/dev/usb/controller/musb_otg.h
@@ -0,0 +1,435 @@
+/* $FreeBSD$ */
+/*-
+ * 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.
+ */
+
+/*
+ * This header file defines the registers of the Mentor Graphics USB OnTheGo
+ * Inventra chip.
+ */
+
+#ifndef _MUSB2_OTG_H_
+#define _MUSB2_OTG_H_
+
+#define MUSB2_MAX_DEVICES USB_MAX_DEVICES
+
+/* Common registers */
+
+#define MUSB2_REG_FADDR 0x0000 /* function address register */
+#define MUSB2_MASK_FADDR 0x7F
+
+#define MUSB2_REG_POWER 0x0001 /* power register */
+#define MUSB2_MASK_SUSPM_ENA 0x01
+#define MUSB2_MASK_SUSPMODE 0x02
+#define MUSB2_MASK_RESUME 0x04
+#define MUSB2_MASK_RESET 0x08
+#define MUSB2_MASK_HSMODE 0x10
+#define MUSB2_MASK_HSENAB 0x20
+#define MUSB2_MASK_SOFTC 0x40
+#define MUSB2_MASK_ISOUPD 0x80
+
+/* Endpoint interrupt handling */
+
+#define MUSB2_REG_INTTX 0x0002 /* transmit interrupt register */
+#define MUSB2_REG_INTRX 0x0004 /* receive interrupt register */
+#define MUSB2_REG_INTTXE 0x0006 /* transmit interrupt enable register */
+#define MUSB2_REG_INTRXE 0x0008 /* receive interrupt enable register */
+#define MUSB2_MASK_EPINT(epn) (1 << (epn)) /* epn = [0..15] */
+
+/* Common interrupt handling */
+
+#define MUSB2_REG_INTUSB 0x000A /* USB interrupt register */
+#define MUSB2_MASK_ISUSP 0x01
+#define MUSB2_MASK_IRESUME 0x02
+#define MUSB2_MASK_IRESET 0x04
+#define MUSB2_MASK_IBABBLE 0x04
+#define MUSB2_MASK_ISOF 0x08
+#define MUSB2_MASK_ICONN 0x10
+#define MUSB2_MASK_IDISC 0x20
+#define MUSB2_MASK_ISESSRQ 0x40
+#define MUSB2_MASK_IVBUSERR 0x80
+
+#define MUSB2_REG_INTUSBE 0x000B /* USB interrupt enable register */
+#define MUSB2_REG_FRAME 0x000C /* USB frame register */
+#define MUSB2_MASK_FRAME 0x3FF /* 0..1023 */
+
+#define MUSB2_REG_EPINDEX 0x000E /* endpoint index register */
+#define MUSB2_MASK_EPINDEX 0x0F
+
+#define MUSB2_REG_TESTMODE 0x000F /* test mode register */
+#define MUSB2_MASK_TSE0_NAK 0x01
+#define MUSB2_MASK_TJ 0x02
+#define MUSB2_MASK_TK 0x04
+#define MUSB2_MASK_TPACKET 0x08
+#define MUSB2_MASK_TFORCE_HS 0x10
+#define MUSB2_MASK_TFORCE_LS 0x20
+#define MUSB2_MASK_TFIFO_ACC 0x40
+#define MUSB2_MASK_TFORCE_HC 0x80
+
+#define MUSB2_REG_INDEXED_CSR 0x0010 /* EP control status register offset */
+
+#define MUSB2_REG_TXMAXP (0x0000 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_REG_RXMAXP (0x0004 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_PKTSIZE 0x03FF /* in bytes, should be even */
+#define MUSB2_MASK_PKTMULT 0xFC00 /* HS packet multiplier: 0..2 */
+
+#define MUSB2_REG_TXCSRL (0x0002 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_CSRL_TXPKTRDY 0x01
+#define MUSB2_MASK_CSRL_TXFIFONEMPTY 0x02
+#define MUSB2_MASK_CSRL_TXUNDERRUN 0x04 /* Device Mode */
+#define MUSB2_MASK_CSRL_TXERROR 0x04 /* Host Mode */
+#define MUSB2_MASK_CSRL_TXFFLUSH 0x08
+#define MUSB2_MASK_CSRL_TXSENDSTALL 0x10/* Device Mode */
+#define MUSB2_MASK_CSRL_TXSETUPPKT 0x10 /* Host Mode */
+#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 /* Device mode */
+#define MUSB2_MASK_CSRL_TXNAKTO 0x80 /* Host mode */
+
+/* Device Side Mode */
+#define MUSB2_MASK_CSR0L_RXPKTRDY 0x01
+#define MUSB2_MASK_CSR0L_TXPKTRDY 0x02
+#define MUSB2_MASK_CSR0L_SENTSTALL 0x04
+#define MUSB2_MASK_CSR0L_DATAEND 0x08
+#define MUSB2_MASK_CSR0L_SETUPEND 0x10
+#define MUSB2_MASK_CSR0L_SENDSTALL 0x20
+#define MUSB2_MASK_CSR0L_RXPKTRDY_CLR 0x40
+#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
+#define MUSB2_MASK_CSR0L_REQPKT 0x20
+#define MUSB2_MASK_CSR0L_STATUSPKT 0x40
+#define MUSB2_MASK_CSR0L_NAKTIMO 0x80
+
+#define MUSB2_REG_TXCSRH (0x0003 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_CSRH_TXDT_VAL 0x01 /* 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
+#define MUSB2_MASK_CSRH_RXMODE 0x00
+#define MUSB2_MASK_CSRH_TXMODE 0x20
+#define MUSB2_MASK_CSRH_TXISO 0x40 /* Device Mode */
+#define MUSB2_MASK_CSRH_TXAUTOSET 0x80
+
+#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_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 /* 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 */
+#define MUSB2_MASK_CSRL_RXSENTSTALL 0x40/* Device Mode */
+#define MUSB2_MASK_CSRL_RXSTALL 0x40 /* Host Mode */
+#define MUSB2_MASK_CSRL_RXDT_CLR 0x80
+
+#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_WREN 0x04 /* Host Mode */
+#define MUSB2_MASK_CSRH_RXDMAREQMODE 0x08
+#define MUSB2_MASK_CSRH_RXNYET 0x10
+#define MUSB2_MASK_CSRH_RXDMAREQENA 0x20
+#define MUSB2_MASK_CSRH_RXISO 0x40 /* Device Mode */
+#define MUSB2_MASK_CSRH_RXAUTOREQ 0x40 /* Host Mode */
+#define MUSB2_MASK_CSRH_RXAUTOCLEAR 0x80
+
+#define MUSB2_REG_RXCOUNT (0x0008 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_RXCOUNT 0xFFFF
+
+#define MUSB2_REG_TXTI (0x000A + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_REG_RXTI (0x000C + MUSB2_REG_INDEXED_CSR)
+
+/* Host Mode */
+#define MUSB2_MASK_TI_SPEED 0xC0
+#define MUSB2_MASK_TI_SPEED_LO 0xC0
+#define MUSB2_MASK_TI_SPEED_FS 0x80
+#define MUSB2_MASK_TI_SPEED_HS 0x40
+#define MUSB2_MASK_TI_PROTO_CTRL 0x00
+#define MUSB2_MASK_TI_PROTO_ISOC 0x10
+#define MUSB2_MASK_TI_PROTO_BULK 0x20
+#define MUSB2_MASK_TI_PROTO_INTR 0x30
+#define MUSB2_MASK_TI_EP_NUM 0x0F
+
+#define MUSB2_REG_TXNAKLIMIT (0x000B /* EPN=0 */ + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_REG_RXNAKLIMIT (0x000D /* EPN=0 */ + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_NAKLIMIT 0xFF
+
+#define MUSB2_REG_FSIZE (0x000F + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_RX_FSIZE 0xF0 /* 3..13, 2**n bytes */
+#define MUSB2_MASK_TX_FSIZE 0x0F /* 3..13, 2**n bytes */
+
+#define MUSB2_REG_EPFIFO(n) (0x0020 + (4*(n)))
+
+#define MUSB2_REG_CONFDATA (0x000F + MUSB2_REG_INDEXED_CSR) /* EPN=0 */
+#define MUSB2_MASK_CD_UTMI_DW 0x01
+#define MUSB2_MASK_CD_SOFTCONE 0x02
+#define MUSB2_MASK_CD_DYNFIFOSZ 0x04
+#define MUSB2_MASK_CD_HBTXE 0x08
+#define MUSB2_MASK_CD_HBRXE 0x10
+#define MUSB2_MASK_CD_BIGEND 0x20
+#define MUSB2_MASK_CD_MPTXE 0x40
+#define MUSB2_MASK_CD_MPRXE 0x80
+
+/* Various registers */
+
+#define MUSB2_REG_DEVCTL 0x0060
+#define MUSB2_MASK_SESS 0x01
+#define MUSB2_MASK_HOSTREQ 0x02
+#define MUSB2_MASK_HOSTMD 0x04
+#define MUSB2_MASK_VBUS0 0x08
+#define MUSB2_MASK_VBUS1 0x10
+#define MUSB2_MASK_LSDEV 0x20
+#define MUSB2_MASK_FSDEV 0x40
+#define MUSB2_MASK_BDEV 0x80
+
+#define MUSB2_REG_MISC 0x0061
+#define MUSB2_MASK_RXEDMA 0x01
+#define MUSB2_MASK_TXEDMA 0x02
+
+#define MUSB2_REG_TXFIFOSZ 0x0062
+#define MUSB2_REG_RXFIFOSZ 0x0063
+#define MUSB2_MASK_FIFODB 0x10 /* set if double buffering, r/w */
+#define MUSB2_MASK_FIFOSZ 0x0F
+#define MUSB2_VAL_FIFOSZ_8 0
+#define MUSB2_VAL_FIFOSZ_16 1
+#define MUSB2_VAL_FIFOSZ_32 2
+#define MUSB2_VAL_FIFOSZ_64 3
+#define MUSB2_VAL_FIFOSZ_128 4
+#define MUSB2_VAL_FIFOSZ_256 5
+#define MUSB2_VAL_FIFOSZ_512 6
+#define MUSB2_VAL_FIFOSZ_1024 7
+#define MUSB2_VAL_FIFOSZ_2048 8
+#define MUSB2_VAL_FIFOSZ_4096 9
+
+#define MUSB2_REG_TXFIFOADD 0x0064
+#define MUSB2_REG_RXFIFOADD 0x0066
+#define MUSB2_MASK_FIFOADD 0xFFF /* unit is 8-bytes */
+
+#define MUSB2_REG_VSTATUS 0x0068
+#define MUSB2_REG_VCONTROL 0x0068
+#define MUSB2_REG_HWVERS 0x006C
+#define MUSB2_REG_ULPI_BASE 0x0070
+
+#define MUSB2_REG_EPINFO 0x0078
+#define MUSB2_MASK_NRXEP 0xF0
+#define MUSB2_MASK_NTXEP 0x0F
+
+#define MUSB2_REG_RAMINFO 0x0079
+#define MUSB2_REG_LINKINFO 0x007A
+
+#define MUSB2_REG_VPLEN 0x007B
+#define MUSB2_MASK_VPLEN 0xFF
+
+#define MUSB2_REG_HS_EOF1 0x007C
+#define MUSB2_REG_FS_EOF1 0x007D
+#define MUSB2_REG_LS_EOF1 0x007E
+#define MUSB2_REG_SOFT_RST 0x007F
+#define MUSB2_MASK_SRST 0x01
+#define MUSB2_MASK_SRSTX 0x02
+
+#define MUSB2_REG_RQPKTCOUNT(n) (0x0300 + (4*(n))
+#define MUSB2_REG_RXDBDIS 0x0340
+#define MUSB2_REG_TXDBDIS 0x0342
+#define MUSB2_MASK_DB(n) (1 << (n)) /* disable double buffer, n = [0..15] */
+
+#define MUSB2_REG_CHIRPTO 0x0344
+#define MUSB2_REG_HSRESUM 0x0346
+
+/* Host Mode only registers */
+
+#define MUSB2_REG_TXFADDR(n) (0x0080 + (8*(n)))
+#define MUSB2_REG_TXHADDR(n) (0x0082 + (8*(n)))
+#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_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)
+
+#define MUSB2_WRITE_2(sc, reg, data) \
+ bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data)
+
+#define MUSB2_READ_1(sc, reg) \
+ bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg)
+
+#define MUSB2_WRITE_1(sc, reg, data) \
+ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data)
+
+struct musbotg_td;
+struct musbotg_softc;
+
+typedef uint8_t (musbotg_cmd_t)(struct musbotg_td *td);
+
+struct musbotg_dma {
+ struct musbotg_softc *sc;
+ uint32_t dma_chan;
+ uint8_t busy:1;
+ uint8_t complete:1;
+ uint8_t error:1;
+};
+
+struct musbotg_td {
+ struct musbotg_td *obj_next;
+ musbotg_cmd_t *func;
+ struct usb_page_cache *pc;
+ uint32_t offset;
+ uint32_t remainder;
+ uint16_t max_frame_size; /* packet_size * mult */
+ uint16_t reg_max_packet;
+ uint8_t ep_no;
+ uint8_t transfer_type;
+ 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 {
+ musbotg_cmd_t *func;
+ struct usb_page_cache *pc;
+ struct musbotg_td *td;
+ struct musbotg_td *td_next;
+ uint32_t len;
+ uint32_t offset;
+ uint16_t max_frame_size;
+ uint8_t short_pkt;
+ /*
+ * short_pkt = 0: transfer should be short terminated
+ * short_pkt = 1: transfer should not be short terminated
+ */
+ 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 {
+ struct usb_config_descriptor confd;
+ struct usb_interface_descriptor ifcd;
+ struct usb_endpoint_descriptor endpd;
+} __packed;
+
+union musbotg_hub_temp {
+ uWord wValue;
+ struct usb_port_status ps;
+};
+
+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 */
+ uint8_t status_high_speed:1; /* set if High Speed is selected */
+ uint8_t remote_wakeup:1;
+ uint8_t self_powered:1;
+ 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;
+};
+
+struct musbotg_softc {
+ struct usb_bus sc_bus;
+ union musbotg_hub_temp sc_hub_temp;
+ struct usb_hw_ep_profile sc_hw_ep_profile[MUSB2_EP_MAX];
+
+ struct usb_device *sc_devices[MUSB2_MAX_DEVICES];
+ struct resource *sc_io_res;
+ struct resource *sc_irq_res;
+ void *sc_intr_hdl;
+ bus_size_t sc_io_size;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+
+ 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 */
+
+ uint8_t sc_ep_max; /* maximum number of RX and TX
+ * endpoints supported */
+ uint8_t sc_rt_addr; /* root HUB address */
+ uint8_t sc_dv_addr; /* device address */
+ uint8_t sc_conf; /* root HUB config */
+ uint8_t sc_ep0_busy; /* set if ep0 is busy */
+ uint8_t sc_ep0_cmd; /* pending commands */
+ 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,
+ 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_ */
--
2.7.4
More information about the devel
mailing list