Index: head/sys/arm/allwinner/aw_usbphy.c =================================================================== --- head/sys/arm/allwinner/aw_usbphy.c (revision 305119) +++ head/sys/arm/allwinner/aw_usbphy.c (revision 305120) @@ -1,240 +1,314 @@ /*- * Copyright (c) 2016 Jared McNeill * 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 ``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 PHY */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include "phy_if.h" #define USBPHY_NPHYS 4 +#define USBPHY_NRES USBPHY_NPHYS +enum awusbphy_type { + AWUSBPHY_TYPE_A10 = 1, + AWUSBPHY_TYPE_A13, + AWUSBPHY_TYPE_A20, + AWUSBPHY_TYPE_A31, + AWUSBPHY_TYPE_A83T, + AWUSBPHY_TYPE_H3, + AWUSBPHY_TYPE_A64 +}; + static struct ofw_compat_data compat_data[] = { - { "allwinner,sun4i-a10-usb-phy", 1 }, - { "allwinner,sun5i-a13-usb-phy", 1 }, - { "allwinner,sun6i-a31-usb-phy", 1 }, - { "allwinner,sun7i-a20-usb-phy", 1 }, - { "allwinner,sun8i-a83t-usb-phy", 1 }, - { "allwinner,sun8i-h3-usb-phy", 1 }, + { "allwinner,sun4i-a10-usb-phy", AWUSBPHY_TYPE_A10 }, + { "allwinner,sun5i-a13-usb-phy", AWUSBPHY_TYPE_A13 }, + { "allwinner,sun6i-a31-usb-phy", AWUSBPHY_TYPE_A31 }, + { "allwinner,sun7i-a20-usb-phy", AWUSBPHY_TYPE_A20 }, + { "allwinner,sun8i-a83t-usb-phy", AWUSBPHY_TYPE_A83T }, + { "allwinner,sun8i-h3-usb-phy", AWUSBPHY_TYPE_H3 }, + { "allwinner,sun50i-a64-usb-phy", AWUSBPHY_TYPE_A64 }, { NULL, 0 } }; struct awusbphy_softc { + struct resource * res[USBPHY_NRES]; regulator_t reg[USBPHY_NPHYS]; gpio_pin_t id_det_pin; int id_det_valid; gpio_pin_t vbus_det_pin; int vbus_det_valid; + enum awusbphy_type phy_type; }; +static struct resource_spec awusbphy_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL }, + { SYS_RES_MEMORY, 3, RF_ACTIVE | RF_OPTIONAL }, + { -1, 0 } +}; + +#define RD4(sc, i, o) bus_read_4((sc)->res[(i)], (o)) +#define WR4(sc, i, o, v) bus_write_4((sc)->res[(i)], (o), (v)) +#define CLR4(sc, i, o, m) WR4(sc, i, o, RD4(sc, i, o) & ~(m)) +#define SET4(sc, i, o, m) WR4(sc, i, o, RD4(sc, i, o) | (m)) + +#define OTG_PHY_CFG 0x20 +#define OTG_PHY_ROUTE_OTG (1 << 0) +#define PMU_IRQ_ENABLE 0x00 +#define PMU_AHB_INCR8 (1 << 10) +#define PMU_AHB_INCR4 (1 << 9) +#define PMU_AHB_INCRX_ALIGN (1 << 8) +#define PMU_ULPI_BYPASS (1 << 0) +#define PMU_UNK_H3 0x10 +#define PMU_UNK_H3_CLR 0x2 + +static void +awusbphy_configure(device_t dev, int phyno) +{ + struct awusbphy_softc *sc; + + sc = device_get_softc(dev); + + if (sc->res[phyno] == NULL) + return; + + if (sc->phy_type == AWUSBPHY_TYPE_A64) { + CLR4(sc, phyno, PMU_UNK_H3, PMU_UNK_H3_CLR); + + /* EHCI0 and OTG share a PHY */ + if (phyno == 0) + SET4(sc, 0, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG); + else if (phyno == 1) + CLR4(sc, 0, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG); + } + + if (phyno > 0) { + /* Enable passby */ + SET4(sc, phyno, PMU_IRQ_ENABLE, PMU_ULPI_BYPASS | + PMU_AHB_INCR8 | PMU_AHB_INCR4 | PMU_AHB_INCRX_ALIGN); + } +} + static int awusbphy_init(device_t dev) { struct awusbphy_softc *sc; phandle_t node; char pname[20]; int error, off; regulator_t reg; hwreset_t rst; clk_t clk; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); + sc->phy_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + /* Enable clocks */ for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) { error = clk_enable(clk); if (error != 0) { device_printf(dev, "couldn't enable clock %s\n", clk_get_name(clk)); return (error); } } /* De-assert resets */ for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) { error = hwreset_deassert(rst); if (error != 0) { device_printf(dev, "couldn't de-assert reset %d\n", off); return (error); } } /* Get regulators */ for (off = 0; off < USBPHY_NPHYS; off++) { snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off); if (regulator_get_by_ofw_property(dev, 0, pname, ®) == 0) sc->reg[off] = reg; } /* Get GPIOs */ error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios", &sc->id_det_pin); if (error == 0) sc->id_det_valid = 1; error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios", &sc->vbus_det_pin); if (error == 0) sc->vbus_det_valid = 1; + /* Allocate resources */ + if (bus_alloc_resources(dev, awusbphy_spec, sc->res) != 0) + device_printf(dev, "couldn't allocate resources\n"); + return (0); } static int awusbphy_vbus_detect(device_t dev, int *val) { struct awusbphy_softc *sc; bool active; int error; sc = device_get_softc(dev); if (sc->vbus_det_valid) { error = gpio_pin_is_active(sc->vbus_det_pin, &active); if (error != 0) return (error); *val = active; return (0); } *val = 1; return (0); } static int awusbphy_phy_enable(device_t dev, intptr_t phy, bool enable) { struct awusbphy_softc *sc; regulator_t reg; int error, vbus_det; if (phy < 0 || phy >= USBPHY_NPHYS) return (ERANGE); sc = device_get_softc(dev); + + /* Configure PHY */ + awusbphy_configure(dev, phy); /* Regulators are optional. If not found, return success. */ reg = sc->reg[phy]; if (reg == NULL) return (0); if (enable) { /* If an external vbus is detected, do not enable phy 0 */ if (phy == 0) { error = awusbphy_vbus_detect(dev, &vbus_det); if (error == 0 && vbus_det == 1) return (0); } else error = 0; if (error == 0) error = regulator_enable(reg); } else error = regulator_disable(reg); if (error != 0) { device_printf(dev, "couldn't %s regulator for phy %jd\n", enable ? "enable" : "disable", (intmax_t)phy); return (error); } return (0); } static int awusbphy_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 PHY"); return (BUS_PROBE_DEFAULT); } static int awusbphy_attach(device_t dev) { int error; error = awusbphy_init(dev); if (error) { device_printf(dev, "failed to initialize USB PHY, error %d\n", error); return (error); } phy_register_provider(dev); return (error); } static device_method_t awusbphy_methods[] = { /* Device interface */ DEVMETHOD(device_probe, awusbphy_probe), DEVMETHOD(device_attach, awusbphy_attach), /* PHY interface */ DEVMETHOD(phy_enable, awusbphy_phy_enable), DEVMETHOD_END }; static driver_t awusbphy_driver = { "awusbphy", awusbphy_methods, sizeof(struct awusbphy_softc) }; static devclass_t awusbphy_devclass; EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(awusbphy, 1);