add rkusbphy(4), a driver for the usb2phy on rockchip SoCs.
authordlg <dlg@openbsd.org>
Sun, 2 Apr 2023 01:21:39 +0000 (01:21 +0000)
committerdlg <dlg@openbsd.org>
Sun, 2 Apr 2023 01:21:39 +0000 (01:21 +0000)
the rkusbphy device has children nodes in the device tree which are
the actual phys. this driver mostly exists to wire those children
up as PHYs and turn the associated regulators on when a host
controller enables the PHYs. in the future it should enable clocks
and take ports out of suspend too.

i'm not enabling this yet because it's useless without some tweaks
in the usb host controller drivers.

ok kettenis@

sys/dev/fdt/files.fdt
sys/dev/fdt/rkusbphy.c [new file with mode: 0644]

index 9d79582..e653fbc 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.fdt,v 1.183 2023/04/01 08:39:05 kettenis Exp $
+#      $OpenBSD: files.fdt,v 1.184 2023/04/02 01:21:39 dlg Exp $
 #
 # Config file and device description for machine-independent FDT code.
 # Included by ports that need it.
@@ -345,6 +345,10 @@ device     rkcomphy
 attach rkcomphy at fdt
 file   dev/fdt/rkcomphy.c              rkcomphy
 
+device rkusbphy
+attach rkusbphy at fdt
+file   dev/fdt/rkusbphy.c              rkusbphy
+
 device rkdrm: drmbase, wsemuldisplaydev, rasops15, rasops16, rasops24, rasops32
 attach rkdrm at fdt
 file   dev/fdt/rkdrm.c                         rkdrm
diff --git a/sys/dev/fdt/rkusbphy.c b/sys/dev/fdt/rkusbphy.c
new file mode 100644 (file)
index 0000000..f3c63fb
--- /dev/null
@@ -0,0 +1,241 @@
+/*     $OpenBSD: rkusbphy.c,v 1.1 2023/04/02 01:21:39 dlg Exp $ */
+
+/*
+ * Copyright (c) 2023 David Gwynne <dlg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Rockchip USB2PHY with Innosilicon IP
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/ofw_regulator.h>
+#include <dev/ofw/ofw_misc.h>
+#include <dev/ofw/fdt.h>
+
+/*
+ * chip stuff
+ */
+
+struct rkusbphy_reg {
+       bus_size_t                      r_offs;
+};
+
+struct rkusbphy_port_regs {
+       struct rkusbphy_reg             pr_phy_sus;
+       struct rkusbphy_reg             pr_bvalid_det_en;
+};
+
+struct rkusbphy_chip {
+       bus_addr_t                      c_base_addr;
+       struct rkusbphy_port_regs       c_regs;
+};
+
+static const struct rkusbphy_chip rkusbphy_rk3568[] = {
+       {
+               .c_base_addr = 0xfe8a0000,
+       },
+       {
+               .c_base_addr = 0xfe8b0000,
+       },
+};
+
+/*
+ * driver stuff
+ */
+
+struct rkusbphy_softc {
+       struct device                    sc_dev;
+       const struct rkusbphy_chip      *sc_chip;
+       struct regmap                   *sc_grf;
+       int                              sc_node;
+
+       struct phy_device                sc_otg_phy;
+       struct phy_device                sc_host_phy;
+};
+#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
+
+static int     rkusbphy_match(struct device *, void *, void *);
+static void    rkusbphy_attach(struct device *, struct device *, void *);
+
+static int     rkusbphy_otg_phy_enable(void *, uint32_t *);
+static int     rkusbphy_host_phy_enable(void *, uint32_t *);
+
+struct rkusbphy_port_config {
+       const char                      *pc_name;
+       int (*pc_enable)(void *, uint32_t *);
+};
+
+static void    rkusbphy_register(struct rkusbphy_softc *,
+                   struct phy_device *, const struct rkusbphy_port_config *);
+
+static const struct rkusbphy_port_config rkusbphy_otg_config = {
+       .pc_name = "otg-port",
+       .pc_enable = rkusbphy_otg_phy_enable,
+};
+
+static const struct rkusbphy_port_config rkusbphy_host_config = {
+       .pc_name = "host-port",
+       .pc_enable = rkusbphy_host_phy_enable,
+};
+
+const struct cfattach rkusbphy_ca = {
+       sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach
+};
+
+struct cfdriver rkusbphy_cd = {
+       NULL, "rkusbphy", DV_DULL
+};
+
+struct rkusbphy_id {
+       const char                      *id_name;
+       const struct rkusbphy_chip      *id_chips;
+       size_t                           id_nchips;
+};
+
+#define RKUSBPHY_ID(_n, _c) { _n, _c, nitems(_c) }
+
+static const struct rkusbphy_id rkusbphy_ids[] = {
+       RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568),
+};
+
+static const struct rkusbphy_id *
+rkusbphy_lookup(struct fdt_attach_args *faa)
+{
+       size_t i;
+
+       for (i = 0; i < nitems(rkusbphy_ids); i++) {
+               const struct rkusbphy_id *id = &rkusbphy_ids[i];
+               if (OF_is_compatible(faa->fa_node, id->id_name))
+                       return (id);
+       }
+
+       return (NULL);
+}
+
+static int
+rkusbphy_match(struct device *parent, void *match, void *aux)
+{
+       struct fdt_attach_args *faa = aux;
+
+       return (rkusbphy_lookup(faa) != NULL ? 1 : 0);
+}
+
+static void
+rkusbphy_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct rkusbphy_softc *sc = (struct rkusbphy_softc *)self;
+       struct fdt_attach_args *faa = aux;
+       const struct rkusbphy_id *id = rkusbphy_lookup(faa);
+       size_t i;
+       uint32_t grfph;
+
+       if (faa->fa_nreg < 1) {
+               printf(": no registers\n");
+               return;
+       }
+
+       for (i = 0; i < id->id_nchips; i++) {
+               const struct rkusbphy_chip *c = &id->id_chips[i];
+               if (faa->fa_reg[0].addr == c->c_base_addr) {
+                       printf(": phy %zu\n", i);
+                       sc->sc_chip = c;
+                       break;
+               }
+       }
+       if (sc->sc_chip == NULL) {
+               printf(": unknown base address 0x%llu\n", faa->fa_reg[0].addr);
+               return;
+       }
+
+       sc->sc_node = faa->fa_node;
+
+       grfph = OF_getpropint(sc->sc_node, "rockchip,usbgrf", 0);
+       sc->sc_grf = regmap_byphandle(grfph);
+       if (sc->sc_grf == NULL) {
+               printf("%s: rockchip,usbgrf 0x%x not found\n", DEVNAME(sc),
+                   grfph);
+               return;
+       }
+
+       reset_assert_all(sc->sc_node);
+
+       rkusbphy_register(sc, &sc->sc_otg_phy, &rkusbphy_otg_config);
+       rkusbphy_register(sc, &sc->sc_host_phy, &rkusbphy_host_config);
+}
+
+static void
+rkusbphy_register(struct rkusbphy_softc *sc, struct phy_device *pd,
+    const struct rkusbphy_port_config *pc)
+{
+       char status[32];
+       int node;
+
+       node = OF_getnodebyname(sc->sc_node, pc->pc_name);
+       if (node == 0) {
+               printf("%s: cannot find %s\n", DEVNAME(sc), pc->pc_name);
+               return;
+       }
+
+       if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
+           strcmp(status, "disabled") == 0)
+               return;
+
+       pd->pd_node = node;
+       pd->pd_cookie = sc;
+       pd->pd_enable = pc->pc_enable;
+       phy_register(pd);
+}
+
+static void
+rkusbphy_phy_supply(struct rkusbphy_softc *sc, int node)
+{
+       int phandle;
+
+       phandle = OF_getpropint(node, "phy-supply", 0);
+       if (phandle == 0)
+               return;
+
+       regulator_enable(phandle);
+}
+
+static int
+rkusbphy_otg_phy_enable(void *cookie, uint32_t *cells)
+{
+       struct rkusbphy_softc *sc = cookie;
+
+       rkusbphy_phy_supply(sc, sc->sc_otg_phy.pd_node);
+
+       return (EINVAL);
+}
+
+static int
+rkusbphy_host_phy_enable(void *cookie, uint32_t *cells)
+{
+       struct rkusbphy_softc *sc = cookie;
+
+       rkusbphy_phy_supply(sc, sc->sc_host_phy.pd_node);
+
+       return (EINVAL);
+}