summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/arm/allwinner/a10_ehci.c14
-rw-r--r--sys/arm/allwinner/aw_usbphy.c114
-rw-r--r--sys/dev/usb/controller/generic_ohci.c19
3 files changed, 134 insertions, 13 deletions
diff --git a/sys/arm/allwinner/a10_ehci.c b/sys/arm/allwinner/a10_ehci.c
index 6c91ba6..95e5990 100644
--- a/sys/arm/allwinner/a10_ehci.c
+++ b/sys/arm/allwinner/a10_ehci.c
@@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$");
#include <arm/allwinner/allwinner_machdep.h>
#include <dev/extres/clk/clk.h>
#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
#define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller"
@@ -94,6 +95,7 @@ struct aw_ehci_softc {
ehci_softc_t sc;
clk_t clk;
hwreset_t rst;
+ phy_t phy;
};
struct aw_ehci_conf {
@@ -225,6 +227,18 @@ a10_ehci_attach(device_t self)
goto error;
}
+ /* Enable USB PHY */
+ err = phy_get_by_ofw_name(self, "usb", &aw_sc->phy);
+ if (err != 0) {
+ device_printf(self, "Could not get phy\n");
+ goto error;
+ }
+ err = phy_enable(self, aw_sc->phy);
+ if (err != 0) {
+ device_printf(self, "Could not enable phy\n");
+ goto error;
+ }
+
/* Enable passby */
reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE);
reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */
diff --git a/sys/arm/allwinner/aw_usbphy.c b/sys/arm/allwinner/aw_usbphy.c
index 362da65..3eb3c65 100644
--- a/sys/arm/allwinner/aw_usbphy.c
+++ b/sys/arm/allwinner/aw_usbphy.c
@@ -43,12 +43,16 @@ __FBSDID("$FreeBSD$");
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/gpio/gpiobusvar.h>
#include <dev/extres/clk/clk.h>
#include <dev/extres/hwreset/hwreset.h>
#include <dev/extres/regulator/regulator.h>
+#include <dev/extres/phy/phy.h>
-#define USBPHY_NUMOFF 3
+#include "phy_if.h"
+
+#define USBPHY_NPHYS 4
static struct ofw_compat_data compat_data[] = {
{ "allwinner,sun4i-a10-usb-phy", 1 },
@@ -60,15 +64,28 @@ static struct ofw_compat_data compat_data[] = {
{ NULL, 0 }
};
+struct awusbphy_softc {
+ 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;
+};
+
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);
+
/* Enable clocks */
for (off = 0; clk_get_by_ofw_index(dev, off, &clk) == 0; off++) {
error = clk_enable(clk);
@@ -89,17 +106,80 @@ awusbphy_init(device_t dev)
}
}
- /* Enable regulator(s) */
- for (off = 0; off < USBPHY_NUMOFF; off++) {
+ /* 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, pname, &reg) != 0)
- continue;
- error = regulator_enable(reg);
- if (error != 0) {
- device_printf(dev, "couldn't enable regulator %s\n",
- pname);
+ if (regulator_get_by_ofw_property(dev, pname, &reg) == 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;
+
+ 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, int 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);
+
+ /* 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 %d\n",
+ enable ? "enable" : "disable", phy);
+ return (error);
}
return (0);
@@ -124,9 +204,13 @@ awusbphy_attach(device_t dev)
int error;
error = awusbphy_init(dev);
- if (error)
+ if (error) {
device_printf(dev, "failed to initialize USB PHY, error %d\n",
error);
+ return (error);
+ }
+
+ phy_register_provider(dev);
return (error);
}
@@ -136,16 +220,20 @@ static device_method_t awusbphy_methods[] = {
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,
- 0,
+ sizeof(struct awusbphy_softc)
};
static devclass_t awusbphy_devclass;
-DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass, 0, 0);
+EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass,
+ 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
MODULE_VERSION(awusbphy, 1);
diff --git a/sys/dev/usb/controller/generic_ohci.c b/sys/dev/usb/controller/generic_ohci.c
index 5d00c52..9656c8f 100644
--- a/sys/dev/usb/controller/generic_ohci.c
+++ b/sys/dev/usb/controller/generic_ohci.c
@@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$");
#ifdef EXT_RESOURCES
#include <dev/extres/clk/clk.h>
#include <dev/extres/hwreset/hwreset.h>
+#include <dev/extres/phy/phy.h>
#endif
#include "generic_usb_if.h"
@@ -76,6 +77,7 @@ struct generic_ohci_softc {
#ifdef EXT_RESOURCES
hwreset_t rst;
+ phy_t phy;
TAILQ_HEAD(, clk_list) clk_list;
#endif
};
@@ -180,6 +182,15 @@ generic_ohci_attach(device_t dev)
goto error;
}
}
+
+ /* Enable phy */
+ if (phy_get_by_ofw_name(dev, "usb", &sc->phy) == 0) {
+ err = phy_enable(dev, sc->phy);
+ if (err != 0) {
+ device_printf(dev, "Could not enable phy\n");
+ goto error;
+ }
+ }
#endif
if (GENERIC_USB_INIT(dev) != 0) {
@@ -253,6 +264,14 @@ generic_ohci_detach(device_t dev)
usb_bus_mem_free_all(&sc->ohci_sc.sc_bus, &ohci_iterate_hw_softc);
#ifdef EXT_RESOURCES
+ /* Disable phy */
+ if (sc->phy) {
+ err = phy_disable(dev, sc->phy);
+ if (err != 0)
+ device_printf(dev, "Could not disable phy\n");
+ phy_release(sc->phy);
+ }
+
/* Disable clock */
TAILQ_FOREACH_SAFE(clk, &sc->clk_list, next, clk_tmp) {
err = clk_disable(clk->clk);
OpenPOWER on IntegriCloud