summaryrefslogtreecommitdiffstats
path: root/sys/arm/allwinner/aw_usbphy.c
diff options
context:
space:
mode:
authorjmcneill <jmcneill@FreeBSD.org>2016-03-01 22:54:30 +0000
committerjmcneill <jmcneill@FreeBSD.org>2016-03-01 22:54:30 +0000
commitbfbda68e27410570c7574288c2a1ed27d795a743 (patch)
tree82819bdfbc28d12e2e11e6c693049e0a2a0647b0 /sys/arm/allwinner/aw_usbphy.c
parent58823d0b495fabfa74b326609295d68f71debcdb (diff)
downloadFreeBSD-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.c192
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, &reg_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);
OpenPOWER on IntegriCloud