Index: head/sys/dev/usb/controller/musb_otg_allwinner.c =================================================================== --- head/sys/dev/usb/controller/musb_otg_allwinner.c (nonexistent) +++ head/sys/dev/usb/controller/musb_otg_allwinner.c (revision 365399) @@ -0,0 +1,543 @@ +/*- + * Copyright (c) 2016 Jared McNeill + * Copyright (c) 2018 Andrew Turner + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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 ``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 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$ + */ + +/* + * Allwinner USB Dual-Role Device (DRD) controller + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef __arm__ +#include +#include +#endif + +#define DRD_EP_MAX 5 + +#define MUSB2_REG_AWIN_VEND0 0x0043 +#define VEND0_PIO_MODE 0 + +#if defined(__arm__) +#define bs_parent_space(bs) ((bs)->bs_parent) +typedef bus_space_tag_t awusb_bs_tag; +#elif defined(__aarch64__) +#define bs_parent_space(bs) (bs) +typedef void * awusb_bs_tag; +#endif + +#define AWUSB_OKAY 0x01 +#define AWUSB_NO_CONFDATA 0x02 +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun4i-a10-musb", AWUSB_OKAY }, + { "allwinner,sun6i-a31-musb", AWUSB_OKAY }, + { "allwinner,sun8i-a33-musb", AWUSB_OKAY | AWUSB_NO_CONFDATA }, + { NULL, 0 } +}; + +static const struct musb_otg_ep_cfg musbotg_ep_allwinner[] = { + { + .ep_end = 5, + .ep_fifosz_shift = 9, + .ep_fifosz_reg = MUSB2_VAL_FIFOSZ_512, + }, + { + .ep_end = -1, + }, +}; + +struct awusbdrd_softc { + struct musbotg_softc sc; + struct resource *res[2]; + clk_t clk; + hwreset_t reset; + struct bus_space bs; + int flags; +}; + +static struct resource_spec awusbdrd_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define REMAPFLAG 0x8000 +#define REGDECL(a, b) [(a)] = ((b) | REMAPFLAG) + +/* Allwinner USB DRD register mappings */ +static const uint16_t awusbdrd_regmap[] = { + REGDECL(MUSB2_REG_EPFIFO(0), 0x0000), + REGDECL(MUSB2_REG_EPFIFO(1), 0x0004), + REGDECL(MUSB2_REG_EPFIFO(2), 0x0008), + REGDECL(MUSB2_REG_EPFIFO(3), 0x000c), + REGDECL(MUSB2_REG_EPFIFO(4), 0x0010), + REGDECL(MUSB2_REG_EPFIFO(5), 0x0014), + REGDECL(MUSB2_REG_POWER, 0x0040), + REGDECL(MUSB2_REG_DEVCTL, 0x0041), + REGDECL(MUSB2_REG_EPINDEX, 0x0042), + REGDECL(MUSB2_REG_INTTX, 0x0044), + REGDECL(MUSB2_REG_INTRX, 0x0046), + REGDECL(MUSB2_REG_INTTXE, 0x0048), + REGDECL(MUSB2_REG_INTRXE, 0x004a), + REGDECL(MUSB2_REG_INTUSB, 0x004c), + REGDECL(MUSB2_REG_INTUSBE, 0x0050), + REGDECL(MUSB2_REG_FRAME, 0x0054), + REGDECL(MUSB2_REG_TESTMODE, 0x007c), + REGDECL(MUSB2_REG_TXMAXP, 0x0080), + REGDECL(MUSB2_REG_TXCSRL, 0x0082), + REGDECL(MUSB2_REG_TXCSRH, 0x0083), + REGDECL(MUSB2_REG_RXMAXP, 0x0084), + REGDECL(MUSB2_REG_RXCSRL, 0x0086), + REGDECL(MUSB2_REG_RXCSRH, 0x0087), + REGDECL(MUSB2_REG_RXCOUNT, 0x0088), + REGDECL(MUSB2_REG_TXTI, 0x008c), + REGDECL(MUSB2_REG_TXNAKLIMIT, 0x008d), + REGDECL(MUSB2_REG_RXNAKLIMIT, 0x008f), + REGDECL(MUSB2_REG_RXTI, 0x008e), + REGDECL(MUSB2_REG_TXFIFOSZ, 0x0090), + REGDECL(MUSB2_REG_TXFIFOADD, 0x0092), + REGDECL(MUSB2_REG_RXFIFOSZ, 0x0094), + REGDECL(MUSB2_REG_RXFIFOADD, 0x0096), + REGDECL(MUSB2_REG_FADDR, 0x0098), + REGDECL(MUSB2_REG_TXFADDR(0), 0x0098), + REGDECL(MUSB2_REG_TXHADDR(0), 0x009a), + REGDECL(MUSB2_REG_TXHUBPORT(0), 0x009b), + REGDECL(MUSB2_REG_RXFADDR(0), 0x009c), + REGDECL(MUSB2_REG_RXHADDR(0), 0x009e), + REGDECL(MUSB2_REG_RXHUBPORT(0), 0x009f), + REGDECL(MUSB2_REG_TXFADDR(1), 0x0098), + REGDECL(MUSB2_REG_TXHADDR(1), 0x009a), + REGDECL(MUSB2_REG_TXHUBPORT(1), 0x009b), + REGDECL(MUSB2_REG_RXFADDR(1), 0x009c), + REGDECL(MUSB2_REG_RXHADDR(1), 0x009e), + REGDECL(MUSB2_REG_RXHUBPORT(1), 0x009f), + REGDECL(MUSB2_REG_TXFADDR(2), 0x0098), + REGDECL(MUSB2_REG_TXHADDR(2), 0x009a), + REGDECL(MUSB2_REG_TXHUBPORT(2), 0x009b), + REGDECL(MUSB2_REG_RXFADDR(2), 0x009c), + REGDECL(MUSB2_REG_RXHADDR(2), 0x009e), + REGDECL(MUSB2_REG_RXHUBPORT(2), 0x009f), + REGDECL(MUSB2_REG_TXFADDR(3), 0x0098), + REGDECL(MUSB2_REG_TXHADDR(3), 0x009a), + REGDECL(MUSB2_REG_TXHUBPORT(3), 0x009b), + REGDECL(MUSB2_REG_RXFADDR(3), 0x009c), + REGDECL(MUSB2_REG_RXHADDR(3), 0x009e), + REGDECL(MUSB2_REG_RXHUBPORT(3), 0x009f), + REGDECL(MUSB2_REG_TXFADDR(4), 0x0098), + REGDECL(MUSB2_REG_TXHADDR(4), 0x009a), + REGDECL(MUSB2_REG_TXHUBPORT(4), 0x009b), + REGDECL(MUSB2_REG_RXFADDR(4), 0x009c), + REGDECL(MUSB2_REG_RXHADDR(4), 0x009e), + REGDECL(MUSB2_REG_RXHUBPORT(4), 0x009f), + REGDECL(MUSB2_REG_TXFADDR(5), 0x0098), + REGDECL(MUSB2_REG_TXHADDR(5), 0x009a), + REGDECL(MUSB2_REG_TXHUBPORT(5), 0x009b), + REGDECL(MUSB2_REG_RXFADDR(5), 0x009c), + REGDECL(MUSB2_REG_RXHADDR(5), 0x009e), + REGDECL(MUSB2_REG_RXHUBPORT(5), 0x009f), + REGDECL(MUSB2_REG_CONFDATA, 0x00c0), +}; + +static bus_size_t +awusbdrd_reg(bus_size_t o) +{ + bus_size_t v; + + KASSERT(o < nitems(awusbdrd_regmap), + ("%s: Invalid register %#lx", __func__, o)); + if (o >= nitems(awusbdrd_regmap)) + return (o); + + v = awusbdrd_regmap[o]; + + KASSERT((v & REMAPFLAG) != 0, ("%s: reg %#lx not in regmap", + __func__, o)); + + return (v & ~REMAPFLAG); +} + +static int +awusbdrd_filt(bus_size_t o) +{ + switch (o) { + case MUSB2_REG_MISC: + case MUSB2_REG_RXDBDIS: + case MUSB2_REG_TXDBDIS: + return (1); + default: + return (0); + } +} + +static uint8_t +awusbdrd_bs_r_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) +{ + const struct bus_space *bs = t; + + switch (o) { + case MUSB2_REG_HWVERS: + return (0); /* no known equivalent */ + } + + return (bus_space_read_1(bs_parent_space(bs), h, awusbdrd_reg(o))); +} + +static uint8_t +awusbdrd_bs_r_1_noconf(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) +{ + + /* + * There is no confdata register on some SoCs, return the same + * magic value as Linux. + */ + if (o == MUSB2_REG_CONFDATA) + return (0xde); + + return (awusbdrd_bs_r_1(t, h, o)); +} + + +static uint16_t +awusbdrd_bs_r_2(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o) +{ + const struct bus_space *bs = t; + + return bus_space_read_2(bs_parent_space(bs), h, awusbdrd_reg(o)); +} + +static void +awusbdrd_bs_w_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, + uint8_t v) +{ + const struct bus_space *bs = t; + + if (awusbdrd_filt(o) != 0) + return; + + bus_space_write_1(bs_parent_space(bs), h, awusbdrd_reg(o), v); +} + +static void +awusbdrd_bs_w_2(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, + uint16_t v) +{ + const struct bus_space *bs = t; + + if (awusbdrd_filt(o) != 0) + return; + + bus_space_write_2(bs_parent_space(bs), h, awusbdrd_reg(o), v); +} + +static void +awusbdrd_bs_rm_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, + uint8_t *d, bus_size_t c) +{ + const struct bus_space *bs = t; + + bus_space_read_multi_1(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); +} + +static void +awusbdrd_bs_rm_4(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, + uint32_t *d, bus_size_t c) +{ + const struct bus_space *bs = t; + + bus_space_read_multi_4(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); +} + +static void +awusbdrd_bs_wm_1(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, + const uint8_t *d, bus_size_t c) +{ + const struct bus_space *bs = t; + + if (awusbdrd_filt(o) != 0) + return; + + bus_space_write_multi_1(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); +} + +static void +awusbdrd_bs_wm_4(awusb_bs_tag t, bus_space_handle_t h, bus_size_t o, + const uint32_t *d, bus_size_t c) +{ + const struct bus_space *bs = t; + + if (awusbdrd_filt(o) != 0) + return; + + bus_space_write_multi_4(bs_parent_space(bs), h, awusbdrd_reg(o), d, c); +} + +static void +awusbdrd_intr(void *arg) +{ + struct awusbdrd_softc *sc = arg; + uint8_t intusb; + uint16_t inttx, intrx; + + intusb = MUSB2_READ_1(&sc->sc, MUSB2_REG_INTUSB); + inttx = MUSB2_READ_2(&sc->sc, MUSB2_REG_INTTX); + intrx = MUSB2_READ_2(&sc->sc, MUSB2_REG_INTRX); + if (intusb == 0 && inttx == 0 && intrx == 0) + return; + + if (intusb) + MUSB2_WRITE_1(&sc->sc, MUSB2_REG_INTUSB, intusb); + if (inttx) + MUSB2_WRITE_2(&sc->sc, MUSB2_REG_INTTX, inttx); + if (intrx) + MUSB2_WRITE_2(&sc->sc, MUSB2_REG_INTRX, intrx); + + musbotg_interrupt(arg, intrx, inttx, intusb); +} + +static int +awusbdrd_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Allwinner USB DRD"); + return (BUS_PROBE_DEFAULT); +} + +static int +awusbdrd_attach(device_t dev) +{ + struct awusbdrd_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->flags = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + error = bus_alloc_resources(dev, awusbdrd_spec, sc->res); + if (error != 0) + return (error); + + /* AHB gate clock is required */ + error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); + if (error != 0) + goto fail; + + /* AHB reset is only present on some SoCs */ + (void)hwreset_get_by_ofw_idx(dev, 0, 0, &sc->reset); + + /* Enable clocks */ + error = clk_enable(sc->clk); + if (error != 0) { + device_printf(dev, "failed to enable clock: %d\n", error); + goto fail; + } + if (sc->reset != NULL) { + error = hwreset_deassert(sc->reset); + if (error != 0) { + device_printf(dev, "failed to de-assert reset: %d\n", + error); + goto fail; + } + } + + sc->sc.sc_bus.parent = dev; + sc->sc.sc_bus.devices = sc->sc.sc_devices; + sc->sc.sc_bus.devices_max = MUSB2_MAX_DEVICES; + sc->sc.sc_bus.dma_bits = 32; + + error = usb_bus_mem_alloc_all(&sc->sc.sc_bus, USB_GET_DMA_TAG(dev), + NULL); + if (error != 0) { + error = ENOMEM; + goto fail; + } + +#if defined(__arm__) + sc->bs.bs_parent = rman_get_bustag(sc->res[0]); +#elif defined(__aarch64__) + sc->bs.bs_cookie = rman_get_bustag(sc->res[0]); +#endif + + if ((sc->flags & AWUSB_NO_CONFDATA) == AWUSB_NO_CONFDATA) + sc->bs.bs_r_1 = awusbdrd_bs_r_1_noconf; + else + sc->bs.bs_r_1 = awusbdrd_bs_r_1; + sc->bs.bs_r_2 = awusbdrd_bs_r_2; + sc->bs.bs_w_1 = awusbdrd_bs_w_1; + sc->bs.bs_w_2 = awusbdrd_bs_w_2; + sc->bs.bs_rm_1 = awusbdrd_bs_rm_1; + sc->bs.bs_rm_4 = awusbdrd_bs_rm_4; + sc->bs.bs_wm_1 = awusbdrd_bs_wm_1; + sc->bs.bs_wm_4 = awusbdrd_bs_wm_4; + + sc->sc.sc_io_tag = &sc->bs; + sc->sc.sc_io_hdl = rman_get_bushandle(sc->res[0]); + sc->sc.sc_io_size = rman_get_size(sc->res[0]); + + sc->sc.sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (sc->sc.sc_bus.bdev == NULL) { + error = ENXIO; + goto fail; + } + device_set_ivars(sc->sc.sc_bus.bdev, &sc->sc.sc_bus); + sc->sc.sc_id = 0; + sc->sc.sc_platform_data = sc; + sc->sc.sc_mode = MUSB2_HOST_MODE; /* XXX HOST vs DEVICE mode */ + sc->sc.sc_ep_max = DRD_EP_MAX; + sc->sc.sc_ep_cfg = musbotg_ep_allwinner; + + error = bus_setup_intr(dev, sc->res[1], INTR_MPSAFE | INTR_TYPE_BIO, + NULL, awusbdrd_intr, sc, &sc->sc.sc_intr_hdl); + if (error != 0) + goto fail; + + /* Enable PIO mode */ + bus_write_1(sc->res[0], MUSB2_REG_AWIN_VEND0, VEND0_PIO_MODE); + +#ifdef __arm__ + /* Map SRAMD area to USB0 (sun4i/sun7i only) */ + switch (allwinner_soc_family()) { + case ALLWINNERSOC_SUN4I: + case ALLWINNERSOC_SUN7I: + a10_map_to_otg(); + break; + } +#endif + + error = musbotg_init(&sc->sc); + if (error != 0) + goto fail; + + error = device_probe_and_attach(sc->sc.sc_bus.bdev); + if (error != 0) + goto fail; + + musbotg_vbus_interrupt(&sc->sc, 1); /* XXX VBUS */ + + return (0); + +fail: + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->clk != NULL) + clk_release(sc->clk); + bus_release_resources(dev, awusbdrd_spec, sc->res); + return (error); +} + +static int +awusbdrd_detach(device_t dev) +{ + struct awusbdrd_softc *sc; + device_t bdev; + int error; + + sc = device_get_softc(dev); + + if (sc->sc.sc_bus.bdev != NULL) { + bdev = sc->sc.sc_bus.bdev; + device_detach(bdev); + device_delete_child(dev, bdev); + } + + musbotg_uninit(&sc->sc); + error = bus_teardown_intr(dev, sc->res[1], sc->sc.sc_intr_hdl); + if (error != 0) + return (error); + + usb_bus_mem_free_all(&sc->sc.sc_bus, NULL); + + if (sc->reset != NULL) + hwreset_release(sc->reset); + if (sc->clk != NULL) + clk_release(sc->clk); + + bus_release_resources(dev, awusbdrd_spec, sc->res); + + device_delete_children(dev); + + return (0); +} + +static device_method_t awusbdrd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, awusbdrd_probe), + DEVMETHOD(device_attach, awusbdrd_attach), + DEVMETHOD(device_detach, awusbdrd_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +static driver_t awusbdrd_driver = { + .name = "musbotg", + .methods = awusbdrd_methods, + .size = sizeof(struct awusbdrd_softc), +}; + +static devclass_t awusbdrd_devclass; + +DRIVER_MODULE(musbotg, simplebus, awusbdrd_driver, awusbdrd_devclass, 0, 0); +MODULE_DEPEND(musbotg, usb, 1, 1, 1); Property changes on: head/sys/dev/usb/controller/musb_otg_allwinner.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property