summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2017-03-12 11:46:22 +0000
committerkettenis <kettenis@openbsd.org>2017-03-12 11:46:22 +0000
commit351b29ba7c0d365bfd7fc0e25c2654287e3ca629 (patch)
treeac50d972fc746c5e0bb81eb6efd5d451d3398414
parentIntroduce OF_getindex() API and use it to replace multiple instances of (diff)
downloadwireguard-openbsd-351b29ba7c0d365bfd7fc0e25c2654287e3ca629.tar.xz
wireguard-openbsd-351b29ba7c0d365bfd7fc0e25c2654287e3ca629.zip
Add code to initialize the USB 3 PHY on Exynos 5.
ok visa@
-rw-r--r--sys/dev/fdt/xhci_fdt.c180
1 files changed, 175 insertions, 5 deletions
diff --git a/sys/dev/fdt/xhci_fdt.c b/sys/dev/fdt/xhci_fdt.c
index a9f9dbd181b..b30e1324e4b 100644
--- a/sys/dev/fdt/xhci_fdt.c
+++ b/sys/dev/fdt/xhci_fdt.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xhci_fdt.c,v 1.1 2017/03/09 19:26:39 kettenis Exp $ */
+/* $OpenBSD: xhci_fdt.c,v 1.2 2017/03/12 11:46:22 kettenis Exp $ */
/*
* Copyright (c) 2017 Mark kettenis <kettenis@openbsd.org>
*
@@ -15,14 +15,16 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/param.h>
+#include <sys/types.h>
#include <sys/systm.h>
#include <sys/device.h>
+#include <sys/malloc.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/fdt.h>
#include <dev/usb/usb.h>
@@ -35,6 +37,8 @@
struct xhci_fdt_softc {
struct xhci_softc sc;
+ int sc_node;
+ bus_space_handle_t ph_ioh;
void *sc_ih;
};
@@ -45,6 +49,8 @@ struct cfattach xhci_fdt_ca = {
sizeof(struct xhci_fdt_softc), xhci_fdt_match, xhci_fdt_attach
};
+void xhci_init_phys(struct xhci_fdt_softc *);
+
int
xhci_fdt_match(struct device *parent, void *match, void *aux)
{
@@ -60,16 +66,21 @@ xhci_fdt_attach(struct device *parent, struct device *self, void *aux)
struct fdt_attach_args *faa = aux;
int error;
- if (faa->fa_nreg < 1)
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
return;
+ }
+ sc->sc_node = faa->fa_node;
sc->sc.iot = faa->fa_iot;
sc->sc.sc_size = faa->fa_reg[0].size;
sc->sc.sc_bus.dmatag = faa->fa_dmat;
if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
- faa->fa_reg[0].size, 0, &sc->sc.ioh))
- panic("%s: bus_space_map failed!", __func__);
+ faa->fa_reg[0].size, 0, &sc->sc.ioh)) {
+ printf(": can't map registers\n");
+ return;
+ }
sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_USB,
xhci_intr, sc, sc->sc.sc_bus.bdev.dv_xname);
@@ -80,6 +91,8 @@ xhci_fdt_attach(struct device *parent, struct device *self, void *aux)
printf("\n");
+ xhci_init_phys(sc);
+
if ((error = xhci_init(&sc->sc)) != 0) {
printf("%s: init failed, error=%d\n",
sc->sc.sc_bus.bdev.dv_xname, error);
@@ -99,3 +112,160 @@ disestablish_ret:
unmap:
bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
}
+
+struct xhci_phy {
+ const char *compat;
+ void (*init)(struct xhci_fdt_softc *, uint32_t *);
+};
+
+void exynos5_usbdrd_init(struct xhci_fdt_softc *, uint32_t *);
+
+struct xhci_phy xhci_phys[] = {
+ { "samsung,exynos5250-usbdrd-phy", exynos5_usbdrd_init },
+ { "samsung,exynos5420-usbdrd-phy", exynos5_usbdrd_init }
+};
+
+uint32_t *
+xhci_next_phy(uint32_t *cells)
+{
+ uint32_t phandle = cells[0];
+ int node, ncells;
+
+ node = OF_getnodebyphandle(phandle);
+ if (node == 0)
+ return NULL;
+
+ ncells = OF_getpropint(node, "#phy-cells", 0);
+ return cells + ncells + 1;
+}
+
+void
+xhci_init_phy(struct xhci_fdt_softc *sc, uint32_t *cells)
+{
+ int node;
+ int i;
+
+ node = OF_getnodebyphandle(cells[0]);
+ if (node == 0)
+ return;
+
+ for (i = 0; i < nitems(xhci_phys); i++) {
+ if (OF_is_compatible(node, xhci_phys[i].compat)) {
+ xhci_phys[i].init(sc, cells);
+ return;
+ }
+ }
+}
+
+void
+xhci_init_phys(struct xhci_fdt_softc *sc)
+{
+ uint32_t *phys;
+ uint32_t *phy;
+ int len, idx;
+
+ /* XXX Only initialize the USB 3 PHY for now. */
+ idx = OF_getindex(sc->sc_node, "usb3-phy", "phy-names");
+ if (idx < 0)
+ return;
+
+ len = OF_getproplen(sc->sc_node, "phys");
+ if (len <= 0)
+ return;
+
+ phys = malloc(len, M_TEMP, M_WAITOK);
+ OF_getpropintarray(sc->sc_node, "phys", phys, len);
+
+ phy = phys;
+ while (phy && phy < phys + (len / sizeof(uint32_t))) {
+ if (idx == 0) {
+ xhci_init_phy(sc, phy);
+ free(phys, M_TEMP, len);
+ return;
+ }
+
+ phy = xhci_next_phy(phy);
+ idx--;
+ }
+ free(phys, M_TEMP, len);
+}
+
+/*
+ * Samsung Exynos 5 PHYs.
+ */
+
+/* Registers */
+#define EXYNOS5_PHYUTMI 0x0008
+#define EXYNOS5_PHYUTMI_OTGDISABLE (1 << 6)
+#define EXYNOS5_PHYCLKRST 0x0010
+#define EXYNOS5_PHYCLKRST_SSC_EN (1 << 20)
+#define EXYNOS5_PHYCLKRST_REF_SSP_EN (1 << 19)
+#define EXYNOS5_PHYCLKRST_PORTRESET (1 << 1)
+#define EXYNOS5_PHYCLKRST_COMMONONN (1 << 0)
+#define EXYNOS5_PHYTEST 0x0028
+#define EXYNOS5_PHYTEST_POWERDOWN_SSP (1 << 3)
+#define EXYNOS5_PHYTEST_POWERDOWN_HSP (1 << 2)
+
+/* PMU registers */
+#define EXYNOS5_USBDRD0_POWER 0x0704
+#define EXYNOS5420_USBDRD1_POWER 0x0708
+#define EXYNOS5_USBDRD_POWER_EN (1 << 0)
+
+void
+exynos5_usbdrd_init(struct xhci_fdt_softc *sc, uint32_t *cells)
+{
+ uint32_t phy_reg[2];
+ struct regmap *pmurm;
+ uint32_t pmureg;
+ uint32_t val;
+ bus_size_t offset;
+ int node;
+
+ node = OF_getnodebyphandle(cells[0]);
+ KASSERT(node != 0);
+
+ if (OF_getpropintarray(node, "reg", phy_reg,
+ sizeof(phy_reg)) != sizeof(phy_reg))
+ return;
+
+ if (bus_space_map(sc->sc.iot, phy_reg[0],
+ phy_reg[1], 0, &sc->ph_ioh)) {
+ printf("%s: can't map PHY registers\n",
+ sc->sc.sc_bus.bdev.dv_xname);
+ return;
+ }
+
+ /* Power up the PHY block. */
+ pmureg = OF_getpropint(node, "samsung,pmu-syscon", 0);
+ pmurm = regmap_byphandle(pmureg);
+ if (pmurm) {
+ node = OF_getnodebyphandle(pmureg);
+ if (sc->sc.sc_bus.bdev.dv_unit == 0)
+ offset = EXYNOS5_USBDRD0_POWER;
+ else
+ offset = EXYNOS5420_USBDRD1_POWER;
+
+ val = regmap_read_4(pmurm, offset);
+ val |= EXYNOS5_USBDRD_POWER_EN;
+ regmap_write_4(pmurm, offset, val);
+ }
+
+ /* Initialize the PHY. Assumes U-Boot has done initial setup. */
+ val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYTEST);
+ CLR(val, EXYNOS5_PHYTEST_POWERDOWN_SSP);
+ CLR(val, EXYNOS5_PHYTEST_POWERDOWN_HSP);
+ bus_space_write_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYTEST, val);
+
+ bus_space_write_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYUTMI,
+ EXYNOS5_PHYUTMI_OTGDISABLE);
+
+ val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYCLKRST);
+ SET(val, EXYNOS5_PHYCLKRST_SSC_EN);
+ SET(val, EXYNOS5_PHYCLKRST_REF_SSP_EN);
+ SET(val, EXYNOS5_PHYCLKRST_COMMONONN);
+ SET(val, EXYNOS5_PHYCLKRST_PORTRESET);
+ bus_space_write_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYCLKRST, val);
+ delay(10);
+ CLR(val, EXYNOS5_PHYCLKRST_PORTRESET);
+ bus_space_write_4(sc->sc.iot, sc->ph_ioh, EXYNOS5_PHYCLKRST, val);
+}