Hi,
i got a branch i would like to have this for =]
this works for me as is, and already does more than the current code
in tree, which this diff removes from sys/dev/fdt/ehci_fdt.c.
i took a look at how netbsd does this, but went essentially with what
linux does for these bindings (excluding some otg-only stuff for now).
fwiw., i haven't tested the type>=H3 paths so far(no hw). any comments?
-Artturi
diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC
index 57ad904ed22..175074a4651 100644
--- a/sys/arch/arm64/conf/GENERIC
+++ b/sys/arch/arm64/conf/GENERIC
@@ -199,6 +199,7 @@ sxirtc* at fdt? early 1 # Real Time Clock
sximmc* at fdt? # SD/MMC card controller
sdmmc* at sximmc? # SD/MMC bus
sxisyscon* at fdt? early 1 # System controller
+sxiusbphy* at fdt? early 1 # Allwinner USB PHY
sxitemp* at fdt? # Temperature sensor
sxitwi* at fdt? # I2C controller
iic* at sxitwi? # I2C bus
diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK
index 2a87cc9a88a..1542743469c 100644
--- a/sys/arch/arm64/conf/RAMDISK
+++ b/sys/arch/arm64/conf/RAMDISK
@@ -185,6 +185,7 @@ sxirtc* at fdt? early 1 # Real Time Clock
sximmc* at fdt? # SD/MMC card controller
sdmmc* at sximmc? # SD/MMC bus
sxisyscon* at fdt? early 1 # System controller
+sxiusbphy* at fdt? early 1 # Allwinner USB PHY
sxitwi* at fdt? # I2C controller
iic* at sxitwi? # I2C bus
dwxe* at fdt?
diff --git a/sys/arch/armv7/conf/GENERIC b/sys/arch/armv7/conf/GENERIC
index 1fe66198653..f02788df236 100644
--- a/sys/arch/armv7/conf/GENERIC
+++ b/sys/arch/armv7/conf/GENERIC
@@ -115,6 +115,7 @@ usb* at ehci? #flags 0x1
ohci* at fdt?
usb* at ohci?
sxisyscon* at fdt? early 1 # System controller
+sxiusbphy* at fdt? early 1 # Allwinner USB PHY
sxitemp* at fdt? # Temperature sensor
sxits* at fdt? # Touchpad controller
sxitwi* at fdt? # Two-Wire Serial Interface
diff --git a/sys/arch/armv7/conf/RAMDISK b/sys/arch/armv7/conf/RAMDISK
index 05067ae11ea..022cbd2262c 100644
--- a/sys/arch/armv7/conf/RAMDISK
+++ b/sys/arch/armv7/conf/RAMDISK
@@ -106,6 +106,7 @@ usb* at ehci? #flags 0x1
ohci* at fdt?
usb* at ohci?
sxisyscon* at fdt? early 1 # System controller
+sxiusbphy* at fdt? early 1 # Allwinner USB PHY
sxitwi* at fdt? # Two-Wire Serial Interface
iic* at sxitwi? # I2C bus
diff --git a/sys/dev/fdt/ehci_fdt.c b/sys/dev/fdt/ehci_fdt.c
index 807fac3d441..6f12c684925 100644
--- a/sys/dev/fdt/ehci_fdt.c
+++ b/sys/dev/fdt/ehci_fdt.c
@@ -28,6 +28,7 @@
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/ofw_regulator.h>
#include <dev/ofw/fdt.h>
@@ -109,6 +110,7 @@ ehci_fdt_attach(struct device *parent, struct device *self,
void *aux)
printf("\n");
ehci_init_phys(sc);
+ phy_enable_idx(sc->sc_node, 0);
strlcpy(sc->sc.sc_vendor, "Generic", sizeof(sc->sc.sc_vendor));
r = ehci_init(&sc->sc);
@@ -161,20 +163,9 @@ struct ehci_phy {
void (*init)(struct ehci_fdt_softc *, uint32_t *);
};
-void sun4i_phy_init(struct ehci_fdt_softc *, uint32_t *);
void sun9i_phy_init(struct ehci_fdt_softc *, uint32_t *);
struct ehci_phy ehci_phys[] = {
- { "allwinner,sun4i-a10-usb-phy", sun4i_phy_init },
- { "allwinner,sun5i-a13-usb-phy", sun4i_phy_init },
- { "allwinner,sun6i-a31-usb-phy", sun4i_phy_init },
- { "allwinner,sun7i-a20-usb-phy", sun4i_phy_init },
- { "allwinner,sun8i-a23-usb-phy", sun4i_phy_init },
- { "allwinner,sun8i-a33-usb-phy", sun4i_phy_init },
- { "allwinner,sun8i-h3-usb-phy", sun4i_phy_init },
- { "allwinner,sun8i-r40-usb-phy", sun4i_phy_init },
- { "allwinner,sun8i-v3s-usb-phy", sun4i_phy_init },
- { "allwinner,sun50i-a64-usb-phy", sun4i_phy_init },
{ "allwinner,sun9i-a80-usb-phy", sun9i_phy_init },
};
@@ -250,60 +241,6 @@ ehci_init_phys(struct ehci_fdt_softc *sc)
#define SUNXI_AHB_INCR8 (1 << 10)
#define SUNXI_AHB_INCR16 (1 << 11)
-void
-sun4i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells)
-{
- uint32_t vbus_supply;
- char name[32];
- uint32_t val;
- int node;
-
- node = OF_getnodebyphandle(cells[0]);
- if (node == -1)
- return;
-
- val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR);
- val |= SUNXI_AHB_INCR8 | SUNXI_AHB_INCR4;
- val |= SUNXI_AHB_INCRX_ALIGN;
- val |= SUNXI_ULPI_BYPASS;
- bus_space_write_4(sc->sc.iot, sc->sc.ioh, SUNXI_HCI_ICR, val);
-
- /*
- * We need to poke an undocumented register to make the PHY
- * work on Allwinner A64/H3/H5/R40.
- */
- if (OF_is_compatible(node, "allwinner,sun8i-h3-usb-phy") ||
- OF_is_compatible(node, "allwinner,sun8i-r40-usb-phy") ||
- OF_is_compatible(node, "allwinner,sun50i-a64-usb-phy")) {
- val = bus_space_read_4(sc->sc.iot, sc->sc.ioh, 0x810);
- val &= ~(1 << 1);
- bus_space_write_4(sc->sc.iot, sc->sc.ioh, 0x810, val);
- }
-
- pinctrl_byname(node, "default");
-
- /*
- * On sun4i, sun5i and sun7i, there is a single clock. The
- * more recent SoCs have a separate clock for each PHY.
- */
- if (OF_is_compatible(node, "allwinner,sun4i-a10-usb-phy") ||
- OF_is_compatible(node, "allwinner,sun5i-a13-usb-phy") ||
- OF_is_compatible(node, "allwinner,sun7i-a20-usb-phy")) {
- clock_enable(node, "usb_phy");
- } else {
- snprintf(name, sizeof(name), "usb%d_phy", cells[1]);
- clock_enable(node, name);
- }
-
- snprintf(name, sizeof(name), "usb%d_reset", cells[1]);
- reset_deassert(node, name);
-
- snprintf(name, sizeof(name), "usb%d_vbus-supply", cells[1]);
- vbus_supply = OF_getpropint(node, name, 0);
- if (vbus_supply)
- regulator_enable(vbus_supply);
-}
-
void
sun9i_phy_init(struct ehci_fdt_softc *sc, uint32_t *cells)
{
diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt
index 28390fee6dc..9e41d862e68 100644
--- a/sys/dev/fdt/files.fdt
+++ b/sys/dev/fdt/files.fdt
@@ -36,6 +36,10 @@ device sxisyscon: fdt
attach sxisyscon at fdt
file dev/fdt/sxisyscon.c sxisyscon
+device sxiusbphy: fdt
+attach sxiusbphy at fdt
+file dev/fdt/sxiusbphy.c sxiusbphy
+
device sxitemp
attach sxitemp at fdt
file dev/fdt/sxitemp.c sxitemp
diff --git a/sys/dev/fdt/sxiusbphy.c b/sys/dev/fdt/sxiusbphy.c
new file mode 100644
index 00000000000..23f94b9c7d9
--- /dev/null
+++ b/sys/dev/fdt/sxiusbphy.c
@@ -0,0 +1,424 @@
+/* $OpenBSD: amlusbphy.c,v 1.1 2019/08/29 17:20:03 kettenis Exp $ */
+/*
+ * Copyright (c) 2019 Mark Kettenis <[email protected]>
+ *
+ * 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.
+ */
+
+#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_misc.h>
+#include <dev/ofw/ofw_regulator.h>
+#include <dev/ofw/fdt.h>
+
+/* Registers */
+#define PHY_ISCR 0x00
+#define PHY_ISCR_DPDM_CHANGE_DETECT_EN (1u << 0)
+#define PHY_ISCR_ID_CHANGE_DETECT_EN (1u << 1)
+#define PHY_ISCR_VBUS_CHANGE_DETECT_EN (1u << 2)
+#define PHY_ISCR_IRQ_ENABLE (1u << 3)
+#define PHY_ISCR_DPDM_CHANGE_DETECT (1u << 4) /* w1c
*/
+#define PHY_ISCR_ID_CHANGE_DETECT (1u << 5) /* w1c
*/
+#define PHY_ISCR_VBUS_CHANGE_DETECT (1u << 6) /* w1c
*/
+#define PHY_ISCR_HOSC_EN (1u << 7)
+/*efine PHY_ISCR_VBUS_VALID_SRC_DISABLE XXX unknown */
+/*efine PHY_ISCR_VBUS_VALID_SRC_LOW (2u << 10)*/
+/*efine PHY_ISCR_VBUS_VALID_SRC_HIGH (3u << 10)*/
+#define PHY_ISCR_VBUS_VALID_SRC_MASK (3u << 10)
+#define PHY_ISCR_FORCE_VBUS_VALID_DIS (0u << 12)
+#define PHY_ISCR_FORCE_VBUS_VALID_LOW (2u << 12)
+#define PHY_ISCR_FORCE_VBUS_VALID_HIGH (3u << 12)
+#define PHY_ISCR_FORCE_VBUS_VALID_MASK (3u << 12)
+#define PHY_ISCR_FORCE_ID_DISABLE (0u << 15)
+#define PHY_ISCR_FORCE_ID_LOW (2u << 15)
+#define PHY_ISCR_FORCE_ID_HIGH (3u << 15)
+#define PHY_ISCR_FORCE_ID_MASK (3u << 15)
+#define PHY_ISCR_DPDM_PULLUP_EN (1u << 16)
+#define PHY_ISCR_ID_PULLUP_EN (1u << 17)
+#define PHY_ISCR_MERGED_ID_STATUS (1u << 24)
+#define PHY_ISCR_MERGED_VBUS_STATUS (1u << 25)
+#define PHY_ISCR_EXT_DP_STATUS (1u << 26)
+#define PHY_ISCR_EXT_DM_STATUS (1u << 27)
+#define PHY_ISCR_EXT_ID_STATUS (1u << 28)
+#define PHY_ISCR_VBUS_VALID_FROM_VBUS (1u << 29)
+#define PHY_ISCR_VBUS_VALID_FROM_DATA (1u << 30)
+
+#define PHY_CTRL_A10 0x04
+#define PHY_CTRL_A33 0x10
+#define PHY_CTRL_USBCn(i) (1u << ((i) * 2))
+#define PHY_CTRL_USBC_MASK 0x7f
+#define PHY_CTRL_DATA (1u << 7)
+#define PHY_CTRL_DATA_SHIFT 7
+#define PHY_CTRL_ADDR 0xff00
+#define PHY_CTRL_ADDR_SHIFT 8
+#define PHY_CTRL_SET_PLL_BW 0x03
+#define PHY_CTRL_SET_PLL_BW_MASK 0x3
+#define PHY_CTRL_RES45_CAL_EN 0x0c
+#define PHY_CTRL_SET_TX_AMPLITUDE_TUNE 0x20
+#define PHY_CTRL_SET_TX_AMPLITUDE_TUNE_MASK 0x3
+#define PHY_CTRL_SET_TX_SLEWRATE_TUNE 0x22
+#define PHY_CTRL_SET_TX_SLEWRATE_TUNE_MASK 0x7
+#define PHY_CTRL_SET_VBUS_VALID_THRESHOLD 0x25
+#define PHY_CTRL_SET_VBUS_VALID_THRESHOLD_MASK 0x3
+#define PHY_CTRL_OTG_FUNC_ENABLE 0x28
+#define PHY_CTRL_VBUS_DET_ENABLE 0x29
+#define PHY_CTRL_SET_DISCON_DET_THRESHOLD 0x2a
+#define PHY_CTRL_SET_DISCON_DET_THRESHOLD_MASK 0x3
+
+#define PHY_CTRL_SIDDQ (1u << 3)
+#define PHY_CTRL_VBUSVLDEXT (1u << 5)
+
+#define PHY_OTGCTRL 0x20
+#define PHY_OTGCTRL_ROUTE_MUSB (1u << 0)
+
+#define HCI_ICR 0x00
+#define ULPI_BYPASS (1u << 0)
+#define A83T_HSIC (1u << 1)
+#define AHB_INCRX_ALIGN (1u << 8)
+#define AHB_INCR4 (1u << 9)
+#define AHB_INCR8 (1u << 10)
+#define A83T_HSIC_CON_INT (1u << 16)
+#define A83T_HSIC_CON_DET (1u << 17)
+#define A83T_EHCI_HS_FORCE (1u << 20)
+
+#define HREAD4(sc, reg)
\
+ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
+#define HWRITE4(sc, reg, val)
\
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+#define HSET4(sc, reg, bits)
\
+ HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
+#define HCLR4(sc, reg, bits)
\
+ HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
+#define HCMS4(sc, reg, mask, bits)
\
+ HWRITE4((sc), (reg), (HREAD4((sc), (reg)) & ~(mask)) | (bits))
+
+#define MAXUSBPHY 4
+
+struct sxiusbphy_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh; /* phy ctrl */
+
+ bus_space_handle_t sc_phy_ioh[MAXUSBPHY]; /* pmu */
+ bus_size_t sc_phy_iosize[MAXUSBPHY];
+ uint32_t sc_phy_vbus_supply[MAXUSBPHY];
+ int sc_phytyp;
+ bus_size_t sc_phyreg;
+ int sc_nphys;
+
+#ifdef notyet
+ uint32_t sc_usb0_id_det[4];
+ uint32_t sc_usb0_vbus_det[4];
+#endif
+
+ struct phy_device sc_pd;
+};
+
+int sxiusbphy_match(struct device *, void *, void *);
+void sxiusbphy_attach(struct device *, struct device *, void *);
+
+struct cfattach sxiusbphy_ca = {
+ sizeof (struct sxiusbphy_softc), sxiusbphy_match, sxiusbphy_attach
+};
+
+struct cfdriver sxiusbphy_cd = {
+ NULL, "sxiusbphy", DV_DULL
+};
+
+int sxiusbphy_enable(void *, uint32_t *);
+void sxiusbphy_write(struct sxiusbphy_softc *, u_int, u_int, u_int, u_int);
+static inline int sxiusbphy_nphys(int);
+static inline u_int sxiusbphy_disc_det_treshold(int);
+
+enum {
+ SXIUSBPHY_T_A10 = 0,
+ SXIUSBPHY_T_A13,
+ SXIUSBPHY_T_A31,
+ SXIUSBPHY_T_A20,
+ SXIUSBPHY_T_A23,
+ SXIUSBPHY_T_A33,
+ SXIUSBPHY_T_A83,
+ SXIUSBPHY_T_H3,
+ SXIUSBPHY_T_R40,
+ SXIUSBPHY_T_V3S,
+ SXIUSBPHY_T_A64,
+ SXIUSBPHY_T_H6,
+ SXIUSBPHY_T_MAX,
+};
+const char *sxiusb_phys[] = {
+ [SXIUSBPHY_T_A10] = "allwinner,sun4i-a10-usb-phy",
+ [SXIUSBPHY_T_A13] = "allwinner,sun5i-a13-usb-phy",
+ [SXIUSBPHY_T_A31] = "allwinner,sun6i-a31-usb-phy",
+ [SXIUSBPHY_T_A20] = "allwinner,sun7i-a20-usb-phy",
+ [SXIUSBPHY_T_A23] = "allwinner,sun8i-a23-usb-phy",
+ [SXIUSBPHY_T_A33] = "allwinner,sun8i-a33-usb-phy",
+ [SXIUSBPHY_T_A83] = "allwinner,sun8i-a83t-usb-phy",
+ [SXIUSBPHY_T_H3] = "allwinner,sun8i-h3-usb-phy",
+ [SXIUSBPHY_T_R40] = "allwinner,sun8i-r40-usb-phy",
+ [SXIUSBPHY_T_V3S] = "allwinner,sun8i-v3s-usb-phy",
+ [SXIUSBPHY_T_A64] = "allwinner,sun50i-a64-usb-phy",
+ [SXIUSBPHY_T_H6] = "allwinner,sun50i-h6-usb-phy",
+};
+
+int
+sxiusbphy_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+ int i = 0;
+
+ for (; i < nitems(sxiusb_phys); i++)
+ if (OF_is_compatible(faa->fa_node, sxiusb_phys[i]))
+ return 1;
+ return 0;
+}
+
+void
+sxiusbphy_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct sxiusbphy_softc *sc = (struct sxiusbphy_softc *)self;
+ struct fdt_attach_args *faa = aux;
+ struct fdt_reg dtr;
+ int node = faa->fa_node;
+ uint32_t node_phandle = OF_getpropint(node, "phandle", 0);
+ void *dtnode = node_phandle ? fdt_find_phandle(node_phandle) : NULL;
+ int i, ptyp, regidx;
+ char vsname[32];
+ char regname[32];
+
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
+ return;
+ }
+
+ if (!dtnode) {
+ printf(": can't get phandle\n");
+ return;
+ }
+
+ for (ptyp = 0; ptyp < nitems(sxiusb_phys); ptyp++)
+ if (OF_is_compatible(faa->fa_node, sxiusb_phys[ptyp]))
+ break;
+ sc->sc_phytyp = ptyp;
+ sc->sc_phyreg = (ptyp < SXIUSBPHY_T_A33) ? PHY_CTRL_A10 : PHY_CTRL_A33;
+ sc->sc_nphys = sxiusbphy_nphys(ptyp);
+
+ sc->sc_iot = faa->fa_iot;
+ /**/
+ regidx = OF_getindex(node, "phy_ctrl", "reg-names");
+ if (regidx == -1) {
+ printf(": can't get phy_ctrl register index\n");
+ return;
+ } else if (fdt_get_reg(dtnode, regidx, &dtr)) {
+ printf(": can't get registers\n");
+ return;
+ }
+ if (bus_space_map(sc->sc_iot, dtr.addr, dtr.size, 0, &sc->sc_ioh)) {
+ printf(": can't map registers\n");
+ return;
+ }
+
+ for (i = 0; i < sc->sc_nphys; i++) {
+ /* H6 has two missing phys */
+ if (ptyp == SXIUSBPHY_T_H6 && ((1u << i) & 0x06))
+ continue;
+
+ snprintf(vsname, sizeof(vsname), "usb%d_vbus-supply", i);
+ sc->sc_phy_vbus_supply[i] = OF_getpropint(node, vsname, 0);
+
+ if (i == 0 && ptyp < SXIUSBPHY_T_H3)
+ continue; /* otg phy0 && !dualroute */
+
+ snprintf(regname, sizeof(regname), "pmu%d", i);
+ regidx = OF_getindex(node, regname, "reg-names");
+ if (regidx == -1 || fdt_get_reg(dtnode, regidx, &dtr))
+ continue;
+ if (bus_space_map(sc->sc_iot, dtr.addr, dtr.size, 0,
+ &sc->sc_phy_ioh[i])) {
+ printf(": can't map registers\n");
+ return;
+ }
+ sc->sc_phy_iosize[i] = dtr.size;
+ }
+
+ printf("\n");
+
+#ifdef notyet
+/*
+ * usb0_id_det-gpios =gpio phandle for reading the otg id pin value
+ * usb0_vbus_det-gpios =gpio phandle for detecting the presence of
usb0 vbus
+ * usb0_vbus_power-supply =power-supply phandle for usb0 vbus presence
detect
+ * usb*_vbus-supply =regulator phandle for controller usb{0-3} vbus
+ */
+ OF_getpropintarray(node, "usb0_id_det-gpios",
+ sc->sc_usb0_id_det, sizeof(sc->sc_usb0_id_det));
+ gpio_controller_config_pin(sc->sc_usb0_id_det, GPIO_CONFIG_INPUT);
+
+ OF_getpropintarray(node, "usb0_vbus_det-gpios",
+ sc->sc_usb0_vbus_det, sizeof(sc->sc_usb0_vbus_det));
+ gpio_controller_config_pin(sc->sc_usb0_vbus_det, GPIO_CONFIG_INPUT);
+
+ /* XXX */
+ pinctrl_byname(faa->fa_node, "default");
+#endif
+
+ sc->sc_pd.pd_node = faa->fa_node;
+ sc->sc_pd.pd_cookie = sc;
+ sc->sc_pd.pd_enable = sxiusbphy_enable;
+ phy_register(&sc->sc_pd);
+}
+
+int
+sxiusbphy_nphys(int ptyp)
+{
+ if (ptyp == SXIUSBPHY_T_A13 ||
+ ptyp == SXIUSBPHY_T_A23 ||
+ ptyp == SXIUSBPHY_T_A33 ||
+ ptyp == SXIUSBPHY_T_A64)
+ return 2;
+ if (ptyp == SXIUSBPHY_T_H3 ||
+ ptyp == SXIUSBPHY_T_H6)
+ return 4;
+ return 3;
+}
+
+u_int
+sxiusbphy_disc_det_treshold(int ptyp)
+{
+ if (ptyp == SXIUSBPHY_T_A13 ||
+ ptyp == SXIUSBPHY_T_A20)
+ return 0x2;
+ if (ptyp == SXIUSBPHY_T_A83)
+ return 0x0;
+ return 0x3;
+}
+
+int
+sxiusbphy_enable(void *cookie, uint32_t *cells)
+{
+ struct sxiusbphy_softc *sc = cookie;
+ int idx = cells[0];
+ int node = sc->sc_pd.pd_node;
+ int ptyp = sc->sc_phytyp;
+ bus_space_tag_t iot = sc->sc_iot;
+ bus_space_handle_t ioh = sc->sc_phy_ioh[idx];
+ uint32_t bits = AHB_INCR8 | AHB_INCR4 | AHB_INCRX_ALIGN | ULPI_BYPASS;
+ uint32_t phy_supply;
+ char name[32];
+
+ /*
+ * On sun4i, sun5i and sun7i, there is a single clock. The
+ * more recent SoCs have a separate clock for each PHY.
+ */
+ if (ptyp == SXIUSBPHY_T_A10 ||
+ ptyp == SXIUSBPHY_T_A13 ||
+ ptyp == SXIUSBPHY_T_A20)
+ clock_enable(node, "usb_phy");
+ else {
+ snprintf(name, sizeof(name), "usb%d_phy", idx);
+ clock_enable(node, name);
+
+ if (ptyp == SXIUSBPHY_T_A83 && idx == 2) {
+ /* there's a second clock on sun8i-a83t */
+ snprintf(name, sizeof(name), "usb%d_hsic_12M", idx);
+ clock_enable(node, name);
+ }
+ }
+
+ snprintf(name, sizeof(name), "usb%d_reset", idx);
+ reset_deassert(node, name);
+
+ if (ptyp == SXIUSBPHY_T_A83 ||
+ ptyp == SXIUSBPHY_T_H6) {
+ if (idx == 0)
+ HCMS4(sc, sc->sc_phyreg,
+ PHY_CTRL_SIDDQ, PHY_CTRL_VBUSVLDEXT);
+ } else {
+ /*
+ * We need to poke an undocumented register to make the PHY
+ * work on Allwinner A64/H3/H5/R40.
+ */
+ if (ioh && ptyp >= SXIUSBPHY_T_H3)
+ bus_space_write_4(iot, ioh, 0x10,
+ bus_space_read_4(iot, ioh, 0x10) & ~(1u << 1));
+
+ if (idx == 0)
+ sxiusbphy_write(sc, idx,
+ PHY_CTRL_RES45_CAL_EN, 0x1, 1);
+
+ sxiusbphy_write(sc, idx,
+ PHY_CTRL_SET_TX_AMPLITUDE_TUNE, 0x14, 5);
+
+ sxiusbphy_write(sc, idx, PHY_CTRL_SET_DISCON_DET_THRESHOLD,
+ sxiusbphy_disc_det_treshold(ptyp), 2);
+ }
+
+ /* enable passby etc. */
+ if (ioh) {
+ if (ptyp == SXIUSBPHY_T_A83 && idx == 2)
+ bits |= A83T_HSIC | A83T_HSIC_CON_INT |
+ A83T_HSIC_CON_DET | A83T_EHCI_HS_FORCE;
+
+ bus_space_write_4(iot, ioh, HCI_ICR,
+ bus_space_read_4(iot, ioh, HCI_ICR) | bits);
+ }
+
+ if (idx == 0) {
+#if 1
+ HSET4(sc, PHY_ISCR, PHY_ISCR_DPDM_PULLUP_EN);
+ HSET4(sc, PHY_ISCR, PHY_ISCR_ID_PULLUP_EN);
+#else
+ /* iirc. what netbsd does here */
+ HCMS4(sc, PHY_ISCR,
+ PHY_ISCR_FORCE_ID_MASK | PHY_ISCR_FORCE_VBUS_VALID_MASK,
+ PHY_ISCR_FORCE_ID_LOW | PHY_ISCR_FORCE_VBUS_VALID_HIGH |
+ PHY_ISCR_DPDM_PULLUP_EN | PHY_ISCR_ID_PULLUP_EN);
+#endif
+ /* phy0 has "dualroute" to both *HCI and MUSB */
+ if (ptyp >= SXIUSBPHY_T_H3) /* route away from MUSB */
+ HCLR4(sc, PHY_OTGCTRL, PHY_OTGCTRL_ROUTE_MUSB);
+ }
+
+ /* XXX */
+ phy_supply = sc->sc_phy_vbus_supply[idx];
+ if (phy_supply)
+ regulator_enable(phy_supply);
+
+ return 0;
+}
+
+void
+sxiusbphy_write(struct sxiusbphy_softc *sc, u_int phy_index,
+ u_int bit_addr, u_int bits, u_int len)
+{
+ const uint32_t usbc_mask = PHY_CTRL_USBCn(phy_index);
+ bus_size_t reg = sc->sc_phyreg;
+
+ if (reg == PHY_CTRL_A33)
+ HWRITE4(sc, reg, 0);
+ for (; len-- > 0; bits >>= 1) {
+ HCMS4(sc, reg, PHY_CTRL_ADDR,
+ (bit_addr++ << PHY_CTRL_ADDR_SHIFT));
+ HCMS4(sc, reg, PHY_CTRL_DATA,
+ ((bits & 1) << PHY_CTRL_DATA_SHIFT));
+ HSET4(sc, reg, usbc_mask);
+ HCLR4(sc, reg, usbc_mask);
+ }
+}