diff options
author | jmcneill <jmcneill@FreeBSD.org> | 2016-03-01 22:54:30 +0000 |
---|---|---|
committer | jmcneill <jmcneill@FreeBSD.org> | 2016-03-01 22:54:30 +0000 |
commit | bfbda68e27410570c7574288c2a1ed27d795a743 (patch) | |
tree | 82819bdfbc28d12e2e11e6c693049e0a2a0647b0 /sys/arm/allwinner/aw_usbphy.c | |
parent | 58823d0b495fabfa74b326609295d68f71debcdb (diff) | |
download | FreeBSD-src-bfbda68e27410570c7574288c2a1ed27d795a743.zip FreeBSD-src-bfbda68e27410570c7574288c2a1ed27d795a743.tar.gz |
Add support for Allwinner A31/A31s EHCI controller and USB PHY.
Reviewed by: andrew, Emmanuel Vadot <manu@bidouilliste.com>
Approved by: gonzo (mentor)
Differential Revision: https://reviews.freebsd.org/D5467
Diffstat (limited to 'sys/arm/allwinner/aw_usbphy.c')
-rw-r--r-- | sys/arm/allwinner/aw_usbphy.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/sys/arm/allwinner/aw_usbphy.c b/sys/arm/allwinner/aw_usbphy.c new file mode 100644 index 0000000..f0ee79d --- /dev/null +++ b/sys/arm/allwinner/aw_usbphy.c @@ -0,0 +1,192 @@ +/*- + * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/gpio.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include "gpio_if.h" + +#define USBPHY_NUMOFF 3 +#define GPIO_POLARITY(flags) (((flags) & 1) ? GPIO_PIN_LOW : GPIO_PIN_HIGH) + +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 }, + { NULL, 0 } +}; + +static int +awusbphy_gpio_set(device_t dev, phandle_t node, const char *pname) +{ + pcell_t gpio_prop[4]; + phandle_t gpio_node; + device_t gpio_dev; + uint32_t pin, flags; + ssize_t len; + int val; + + len = OF_getencprop(node, pname, gpio_prop, sizeof(gpio_prop)); + if (len == -1) + return (0); + + if (len != sizeof(gpio_prop)) { + device_printf(dev, "property %s length was %d, expected %d\n", + pname, len, sizeof(gpio_prop)); + return (ENXIO); + } + + gpio_node = OF_node_from_xref(gpio_prop[0]); + gpio_dev = OF_device_from_xref(gpio_prop[0]); + if (gpio_dev == NULL) { + device_printf(dev, "failed to get the GPIO device for %s\n", + pname); + return (ENOENT); + } + + if (GPIO_MAP_GPIOS(gpio_dev, node, gpio_node, + sizeof(gpio_prop) / sizeof(gpio_prop[0]) - 1, gpio_prop + 1, + &pin, &flags) != 0) { + device_printf(dev, "failed to map the GPIO pin for %s\n", + pname); + return (ENXIO); + } + + val = GPIO_POLARITY(flags); + + GPIO_PIN_SETFLAGS(gpio_dev, pin, GPIO_PIN_OUTPUT); + GPIO_PIN_SET(gpio_dev, pin, val); + + return (0); +} + +static int +awusbphy_supply_set(device_t dev, const char *pname) +{ + phandle_t node, reg_node; + pcell_t reg_xref; + + node = ofw_bus_get_node(dev); + + if (OF_getencprop(node, pname, ®_xref, sizeof(reg_xref)) == -1) + return (0); + + reg_node = OF_node_from_xref(reg_xref); + + return (awusbphy_gpio_set(dev, reg_node, "gpio")); +} + +static int +awusbphy_init(device_t dev) +{ + char pname[20]; + phandle_t node; + int error, off; + + node = ofw_bus_get_node(dev); + + for (off = 0; off < USBPHY_NUMOFF; off++) { + snprintf(pname, sizeof(pname), "usb%d_id_det-gpio", off); + error = awusbphy_gpio_set(dev, node, pname); + if (error) + return (error); + + snprintf(pname, sizeof(pname), "usb%d_vbus_det-gpio", off); + error = awusbphy_gpio_set(dev, node, pname); + if (error) + return (error); + + snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off); + error = awusbphy_supply_set(dev, pname); + if (error) + 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); +} + +static device_method_t awusbphy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, awusbphy_probe), + DEVMETHOD(device_attach, awusbphy_attach), + + DEVMETHOD_END +}; + +static driver_t awusbphy_driver = { + "awusbphy", + awusbphy_methods, + 0, +}; + +static devclass_t awusbphy_devclass; + +DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass, 0, 0); +MODULE_VERSION(awusbphy, 1); |