Module Name:    src
Committed By:   jmcneill
Date:           Sun Aug 12 16:48:05 UTC 2018

Modified Files:
        src/sys/arch/arm/rockchip: files.rockchip rk3328_cru.c rk_cru.c
            rk_cru.h rk_cru_pll.c rk_gmac.c rk_platform.c rk_usb.c
Added Files:
        src/sys/arch/arm/rockchip: rk3328_iomux.c rk3399_cru.c rk3399_cru.h
            rk3399_iomux.c rk3399_platform.h rk3399_pmucru.c rk3399_pmucru.h
Removed Files:
        src/sys/arch/arm/rockchip: rk_iomux.c

Log Message:
Add support for Rockchip RK3399 SoC.


To generate a diff of this commit:
cvs rdiff -u -r1.14 -r1.15 src/sys/arch/arm/rockchip/files.rockchip
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/arm/rockchip/rk3328_cru.c \
    src/sys/arch/arm/rockchip/rk_cru_pll.c src/sys/arch/arm/rockchip/rk_usb.c
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/rockchip/rk3328_iomux.c \
    src/sys/arch/arm/rockchip/rk3399_cru.c \
    src/sys/arch/arm/rockchip/rk3399_cru.h \
    src/sys/arch/arm/rockchip/rk3399_iomux.c \
    src/sys/arch/arm/rockchip/rk3399_platform.h \
    src/sys/arch/arm/rockchip/rk3399_pmucru.c \
    src/sys/arch/arm/rockchip/rk3399_pmucru.h
cvs rdiff -u -r1.4 -r1.5 src/sys/arch/arm/rockchip/rk_cru.c
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/rockchip/rk_cru.h \
    src/sys/arch/arm/rockchip/rk_platform.c
cvs rdiff -u -r1.7 -r1.8 src/sys/arch/arm/rockchip/rk_gmac.c
cvs rdiff -u -r1.3 -r0 src/sys/arch/arm/rockchip/rk_iomux.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/rockchip/files.rockchip
diff -u src/sys/arch/arm/rockchip/files.rockchip:1.14 src/sys/arch/arm/rockchip/files.rockchip:1.15
--- src/sys/arch/arm/rockchip/files.rockchip:1.14	Sun Jul  1 18:16:58 2018
+++ src/sys/arch/arm/rockchip/files.rockchip	Sun Aug 12 16:48:04 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: files.rockchip,v 1.14 2018/07/01 18:16:58 jmcneill Exp $
+#	$NetBSD: files.rockchip,v 1.15 2018/08/12 16:48:04 jmcneill Exp $
 #
 # Configuration info for Rockchip family SoCs
 #
@@ -19,10 +19,18 @@ file	arch/arm/rockchip/rk_cru_pll.c		rk_
 attach	rkcru at fdt with rk3328_cru
 file	arch/arm/rockchip/rk3328_cru.c		rk3328_cru & soc_rk3328
 
+# RK3399 clock and reset unit
+attach	rkcru at fdt with rk3399_cru
+file	arch/arm/rockchip/rk3399_cru.c		rk3399_cru & soc_rk3399
+attach	rkcru at fdt with rk3399_pmucru
+file	arch/arm/rockchip/rk3399_pmucru.c	rk3399_pmucru & soc_rk3399
+
 # IOMUX control
 device	rkiomux { }
-attach	rkiomux at fdt with rk_iomux
-file	arch/arm/rockchip/rk_iomux.c		rk_iomux
+attach	rkiomux at fdt with rk3328_iomux
+file	arch/arm/rockchip/rk3328_iomux.c	rk3328_iomux & soc_rk3328
+attach	rkiomux at fdt with rk3399_iomux
+file	arch/arm/rockchip/rk3399_iomux.c	rk3399_iomux & soc_rk3399
 
 # GPIO
 device	rkgpio: gpiobus
@@ -48,3 +56,4 @@ file	arch/arm/rockchip/rk_gmac.c		rk_gma
 # SOC parameters
 defflag	opt_soc.h			SOC_ROCKCHIP
 defflag	opt_soc.h			SOC_RK3328: SOC_ROCKCHIP
+defflag	opt_soc.h			SOC_RK3399: SOC_ROCKCHIP

Index: src/sys/arch/arm/rockchip/rk3328_cru.c
diff -u src/sys/arch/arm/rockchip/rk3328_cru.c:1.3 src/sys/arch/arm/rockchip/rk3328_cru.c:1.4
--- src/sys/arch/arm/rockchip/rk3328_cru.c:1.3	Sun Jul  1 18:15:19 2018
+++ src/sys/arch/arm/rockchip/rk3328_cru.c	Sun Aug 12 16:48:04 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: rk3328_cru.c,v 1.3 2018/07/01 18:15:19 jmcneill Exp $ */
+/* $NetBSD: rk3328_cru.c,v 1.4 2018/08/12 16:48:04 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca>
@@ -28,7 +28,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: rk3328_cru.c,v 1.3 2018/07/01 18:15:19 jmcneill Exp $");
+__KERNEL_RCSID(1, "$NetBSD: rk3328_cru.c,v 1.4 2018/08/12 16:48:04 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -44,6 +44,7 @@ __KERNEL_RCSID(1, "$NetBSD: rk3328_cru.c
 #define	MISC_CON	0x0084	
 #define	CLKSEL_CON(n)	(0x0100 + (n) * 4)
 #define	CLKGATE_CON(n)	(0x0200 + (n) * 4)
+#define	SOFTRST_CON(n)	(0x0300 + (n) * 4)
 
 #define	GRF_SOC_CON4	0x0410
 #define	GRF_MAC_CON1	0x0904
@@ -131,6 +132,7 @@ static const struct rk_cru_arm_rate armc
 	RK_ARM_RATE(  96000000, 1),
 };
 
+static const char * pll_parents[] = { "xin24m" };
 static const char * armclk_parents[] = { "apll", "gpll", "dpll", "npll" };
 static const char * aclk_bus_pre_parents[] = { "cpll", "gpll", "hdmiphy" };
 static const char * hclk_bus_pre_parents[] = { "aclk_bus_pre" };
@@ -151,31 +153,31 @@ static const char * comp_uart_parents[] 
 static const char * pclk_gmac_parents[] = { "aclk_gmac" };
 
 static struct rk_cru_clk rk3328_cru_clks[] = {
-	RK_PLL(RK3328_PLL_APLL, "apll", "xin24m",
+	RK_PLL(RK3328_PLL_APLL, "apll", pll_parents,
 	       PLL_CON(0),		/* con_base */
 	       0x80,			/* mode_reg */
 	       __BIT(0),		/* mode_mask */
 	       __BIT(4),		/* lock_mask */
 	       pll_frac_rates),
-	RK_PLL(RK3328_PLL_DPLL, "dpll", "xin24m",
+	RK_PLL(RK3328_PLL_DPLL, "dpll", pll_parents,
 	       PLL_CON(8),		/* con_base */
 	       0x80,			/* mode_reg */
 	       __BIT(4),		/* mode_mask */
 	       __BIT(3),		/* lock_mask */
 	       pll_norates),
-	RK_PLL(RK3328_PLL_CPLL, "cpll", "xin24m",
+	RK_PLL(RK3328_PLL_CPLL, "cpll", pll_parents,
 	       PLL_CON(16),		/* con_base */
 	       0x80,			/* mode_reg */
 	       __BIT(8),		/* mode_mask */
 	       __BIT(2),		/* lock_mask */
 	       pll_rates),
-	RK_PLL(RK3328_PLL_GPLL, "gpll", "xin24m",
+	RK_PLL(RK3328_PLL_GPLL, "gpll", pll_parents,
 	       PLL_CON(24),		/* con_base */
 	       0x80,			/* mode_reg */
 	       __BIT(12),		/* mode_mask */
 	       __BIT(1),		/* lock_mask */
 	       pll_frac_rates),
-	RK_PLL(RK3328_PLL_NPLL, "npll", "xin24m",
+	RK_PLL(RK3328_PLL_NPLL, "npll", pll_parents,
 	       PLL_CON(40),		/* con_base */
 	       0x80,			/* mode_reg */
 	       __BIT(1),		/* mode_mask */
@@ -398,6 +400,8 @@ rk3328_cru_attach(device_t parent, devic
 	sc->sc_clks = rk3328_cru_clks;
 	sc->sc_nclks = __arraycount(rk3328_cru_clks);
 
+	sc->sc_softrst_base = SOFTRST_CON(0);
+
 	if (rk_cru_attach(sc) != 0)
 		return;
 
Index: src/sys/arch/arm/rockchip/rk_cru_pll.c
diff -u src/sys/arch/arm/rockchip/rk_cru_pll.c:1.3 src/sys/arch/arm/rockchip/rk_cru_pll.c:1.4
--- src/sys/arch/arm/rockchip/rk_cru_pll.c:1.3	Sat Jun 30 17:54:07 2018
+++ src/sys/arch/arm/rockchip/rk_cru_pll.c	Sun Aug 12 16:48:05 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: rk_cru_pll.c,v 1.3 2018/06/30 17:54:07 jmcneill Exp $ */
+/* $NetBSD: rk_cru_pll.c,v 1.4 2018/08/12 16:48:05 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: rk_cru_pll.c,v 1.3 2018/06/30 17:54:07 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rk_cru_pll.c,v 1.4 2018/08/12 16:48:05 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -169,5 +169,5 @@ rk_cru_pll_get_parent(struct rk_cru_soft
 
 	KASSERT(clk->type == RK_CRU_PLL);
 
-	return pll->parent;
+	return pll->parents[0];
 }
Index: src/sys/arch/arm/rockchip/rk_usb.c
diff -u src/sys/arch/arm/rockchip/rk_usb.c:1.3 src/sys/arch/arm/rockchip/rk_usb.c:1.4
--- src/sys/arch/arm/rockchip/rk_usb.c:1.3	Sat Jun 30 18:07:32 2018
+++ src/sys/arch/arm/rockchip/rk_usb.c	Sun Aug 12 16:48:05 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: rk_usb.c,v 1.3 2018/06/30 18:07:32 jmcneill Exp $ */
+/* $NetBSD: rk_usb.c,v 1.4 2018/08/12 16:48:05 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca>
@@ -28,7 +28,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(0, "$NetBSD: rk_usb.c,v 1.3 2018/06/30 18:07:32 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rk_usb.c,v 1.4 2018/08/12 16:48:05 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -46,17 +46,31 @@ __KERNEL_RCSID(0, "$NetBSD: rk_usb.c,v 1
 static int rk_usb_match(device_t, cfdata_t, void *);
 static void rk_usb_attach(device_t, device_t, void *);
 
-#define	CON0_REG	0x100
-#define	CON1_REG	0x104
-#define	CON2_REG	0x108
-#define	 USBPHY_COMMONONN	__BIT(4)
+#define	RK3328_CON0_REG			0x100
+#define	RK3328_CON1_REG			0x104
+#define	RK3328_CON2_REG			0x108
+#define	 RK3328_USBPHY_COMMONONN	__BIT(4)
+
+#define	RK3399_GRF_USB20_PHY0_CON0_REG	0x0e450
+#define	RK3399_GRF_USB20_PHY1_CON0_REG	0x0e460
+#define	 RK3399_USBPHY_COMMONONN	__BIT(4)
+#define	RK3399_GRF_USB20_PHY0_CON1_REG	0x0e454
+#define	RK3399_GRF_USB20_PHY1_CON1_REG	0x0e464
+#define	RK3399_GRF_USB20_PHY0_CON2_REG	0x0e458
+#define	RK3399_GRF_USB20_PHY1_CON2_REG	0x0e468
+#define	 RK3399_USBPHY_SUSPEND_N	__BIT(1)
+#define	 RK3399_USBPHY_UTMI_SEL		__BIT(0)
+
+#define	RK3399_PHY_NO(_sc)	((_sc)->sc_reg == 0xe450 ? 0 : 1)
 
 enum rk_usb_type {
 	USB_RK3328 = 1,
+	USB_RK3399,
 };
 
 static const struct of_compat_data compat_data[] = {
 	{ "rockchip,rk3328-usb2phy",		USB_RK3328 },
+	{ "rockchip,rk3399-usb2phy",		USB_RK3399 },
 	{ NULL }
 };
 
@@ -71,6 +85,8 @@ struct rk_usb_softc {
 
 	struct clk_domain	sc_clkdom;
 	struct rk_usb_clk	sc_usbclk;
+
+	bus_addr_t		sc_reg;
 };
 
 CFATTACH_DECL_NEW(rk_usb, sizeof(struct rk_usb_softc),
@@ -102,12 +118,27 @@ static int
 rk_usb_clk_enable(void *priv, struct clk *clk)
 {
 	struct rk_usb_softc * const sc = priv;
+	uint32_t reg, write_mask, write_val;
 
-	const uint32_t write_mask = USBPHY_COMMONONN << 16;
-	const uint32_t write_val = 0;
+	switch (sc->sc_type) {
+	case USB_RK3328:
+		reg = RK3328_CON2_REG;
+		write_mask = RK3328_USBPHY_COMMONONN << 16;
+		write_val = 0;
+		break;
+	case USB_RK3399:
+		reg = RK3399_PHY_NO(sc) == 0 ?
+		    RK3399_GRF_USB20_PHY0_CON0_REG :
+		    RK3399_GRF_USB20_PHY1_CON0_REG;
+		write_mask = RK3399_USBPHY_COMMONONN << 16;
+		write_val = 0;
+		break;
+	default:
+		return ENXIO;
+	}
 
 	syscon_lock(sc->sc_syscon);
-	syscon_write_4(sc->sc_syscon, CON2_REG, write_mask | write_val);
+	syscon_write_4(sc->sc_syscon, reg, write_mask | write_val);
 	syscon_unlock(sc->sc_syscon);
 
 	return 0;
@@ -117,12 +148,27 @@ static int
 rk_usb_clk_disable(void *priv, struct clk *clk)
 {
 	struct rk_usb_softc * const sc = priv;
+	uint32_t reg, write_mask, write_val;
 
-	const uint32_t write_mask = USBPHY_COMMONONN << 16;
-	const uint32_t write_val = USBPHY_COMMONONN;
+	switch (sc->sc_type) {
+	case USB_RK3328:
+		reg = RK3328_CON2_REG;
+		write_mask = RK3328_USBPHY_COMMONONN << 16;
+		write_val = RK3328_USBPHY_COMMONONN;
+		break;
+	case USB_RK3399:
+		reg = RK3399_PHY_NO(sc) == 0 ?
+		    RK3399_GRF_USB20_PHY0_CON0_REG :
+		    RK3399_GRF_USB20_PHY1_CON0_REG;
+		write_mask = RK3399_USBPHY_COMMONONN << 16;
+		write_val = RK3399_USBPHY_COMMONONN;
+		break;
+	default:
+		return ENXIO;
+	}
 
 	syscon_lock(sc->sc_syscon);
-	syscon_write_4(sc->sc_syscon, CON2_REG, write_mask | write_val);
+	syscon_write_4(sc->sc_syscon, reg, write_mask | write_val);
 	syscon_unlock(sc->sc_syscon);
 
 	return 0;
@@ -165,8 +211,22 @@ rk_usb_attach(device_t parent, device_t 
 	struct rk_usb_softc * const sc = device_private(self);
 	struct fdt_attach_args * const faa = aux;
 	const int phandle = faa->faa_phandle;
+	struct clk *clk;
 	int child;
 
+	/* Cache the base address of this PHY so we know which instance we are */
+	if (fdtbus_get_reg(phandle, 0, &sc->sc_reg, NULL) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	clk = fdtbus_clock_get(phandle, "phyclk");
+KASSERT(clk != NULL);
+	if (clk && clk_enable(clk) != 0) {
+		aprint_error(": couldn't enable phy clock\n");
+		return;
+	}
+
 	sc->sc_dev = self;
 	sc->sc_type = of_search_compatible(phandle, compat_data)->data;
 	sc->sc_syscon = fdtbus_syscon_lookup(OF_parent(phandle));
@@ -214,6 +274,7 @@ static void rk_usbphy_attach(device_t, d
 struct rk_usbphy_softc {
 	device_t	sc_dev;
 	int		sc_phandle;
+	struct fdtbus_regulator *sc_supply;
 };
 
 CFATTACH_DECL_NEW(rk_usbphy, sizeof(struct rk_usbphy_softc),
@@ -238,13 +299,37 @@ rk_usbphy_release(device_t dev, void *pr
 static int
 rk_usbphy_otg_enable(device_t dev, void *priv, bool enable)
 {
+	struct rk_usbphy_softc * const sc = device_private(dev);
 	struct rk_usb_softc * const usb_sc = device_private(device_parent(dev));
+	uint32_t reg, write_mask, write_val;
+	int error;
 
-	const uint32_t write_mask = 0x1ffU << 16;
-	const uint32_t write_val = enable ? 0 : 0x1d1;
+	switch (usb_sc->sc_type) {
+	case USB_RK3328:
+		reg = RK3328_CON0_REG;
+		write_mask = 0x1ffU << 16;
+		write_val = enable ? 0 : 0x1d1;
+		break;
+	case USB_RK3399:
+		reg = RK3399_PHY_NO(usb_sc) == 0 ?
+		    RK3399_GRF_USB20_PHY0_CON1_REG :
+		    RK3399_GRF_USB20_PHY1_CON1_REG;
+		write_mask = (RK3399_USBPHY_SUSPEND_N|RK3399_USBPHY_UTMI_SEL) << 16;
+		write_val = RK3399_USBPHY_UTMI_SEL;
+		break;
+	default:
+		return ENXIO;
+	}
+
+	if (sc->sc_supply) {
+		error = enable ? fdtbus_regulator_enable(sc->sc_supply) :
+				 fdtbus_regulator_disable(sc->sc_supply);
+		if (error != 0)
+			return error;
+	}
 
 	syscon_lock(usb_sc->sc_syscon);
-	syscon_write_4(usb_sc->sc_syscon, CON0_REG, write_mask | write_val);
+	syscon_write_4(usb_sc->sc_syscon, reg, write_mask | write_val);
 	syscon_unlock(usb_sc->sc_syscon);
 
 	return 0;
@@ -253,13 +338,37 @@ rk_usbphy_otg_enable(device_t dev, void 
 static int
 rk_usbphy_host_enable(device_t dev, void *priv, bool enable)
 {
+	struct rk_usbphy_softc * const sc = device_private(dev);
 	struct rk_usb_softc * const usb_sc = device_private(device_parent(dev));
+	uint32_t reg, write_mask, write_val;
+	int error;
 
-	const uint32_t write_mask = 0x1ffU << 16;
-	const uint32_t write_val = enable ? 0 : 0x1d1;
+	switch (usb_sc->sc_type) {
+	case USB_RK3328:
+		reg = RK3328_CON1_REG;
+		write_mask = 0x1ffU << 16;
+		write_val = enable ? 0 : 0x1d1;
+		break;
+	case USB_RK3399:
+		reg = RK3399_PHY_NO(usb_sc) == 0 ?
+		    RK3399_GRF_USB20_PHY0_CON2_REG :
+		    RK3399_GRF_USB20_PHY1_CON2_REG;
+		write_mask = (RK3399_USBPHY_SUSPEND_N|RK3399_USBPHY_UTMI_SEL) << 16;
+		write_val = RK3399_USBPHY_UTMI_SEL;
+		break;
+	default:
+		return ENXIO;
+	}
+
+	if (sc->sc_supply) {
+		error = enable ? fdtbus_regulator_enable(sc->sc_supply) :
+				 fdtbus_regulator_disable(sc->sc_supply);
+		if (error != 0)
+			return error;
+	}
 
 	syscon_lock(usb_sc->sc_syscon);
-	syscon_write_4(usb_sc->sc_syscon, CON1_REG, write_mask | write_val);
+	syscon_write_4(usb_sc->sc_syscon, reg, write_mask | write_val);
 	syscon_unlock(usb_sc->sc_syscon);
 
 	return 0;
@@ -300,6 +409,13 @@ rk_usbphy_attach(device_t parent, device
 
 	sc->sc_dev = self;
 	sc->sc_phandle = phandle;
+	if (of_hasprop(phandle, "phy-supply")) {
+		sc->sc_supply = fdtbus_regulator_acquire(phandle, "phy-supply");
+		if (sc->sc_supply == NULL) {
+			aprint_error(": couldn't acquire regulator\n");
+			return;
+		}
+	}
 
 	aprint_naive("\n");
 

Index: src/sys/arch/arm/rockchip/rk_cru.c
diff -u src/sys/arch/arm/rockchip/rk_cru.c:1.4 src/sys/arch/arm/rockchip/rk_cru.c:1.5
--- src/sys/arch/arm/rockchip/rk_cru.c:1.4	Sat Jun 30 17:54:07 2018
+++ src/sys/arch/arm/rockchip/rk_cru.c	Sun Aug 12 16:48:05 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: rk_cru.c,v 1.4 2018/06/30 17:54:07 jmcneill Exp $ */
+/* $NetBSD: rk_cru.c,v 1.5 2018/08/12 16:48:05 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca>
@@ -30,7 +30,7 @@
 #include "opt_fdt_arm.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: rk_cru.c,v 1.4 2018/06/30 17:54:07 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rk_cru.c,v 1.5 2018/08/12 16:48:05 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -43,8 +43,6 @@ __KERNEL_RCSID(0, "$NetBSD: rk_cru.c,v 1
 
 #include <arm/rockchip/rk_cru.h>
 
-#define	CRU_SOFTRST_CON0	0x0300
-
 static void *
 rk_cru_reset_acquire(device_t dev, const void *data, size_t len)
 {
@@ -64,7 +62,7 @@ rk_cru_reset_assert(device_t dev, void *
 {
 	struct rk_cru_softc * const sc = device_private(dev);
 	const uintptr_t reset_id = (uintptr_t)priv;
-	const bus_size_t reg = CRU_SOFTRST_CON0 + (reset_id / 16) * 4;
+	const bus_size_t reg = sc->sc_softrst_base + (reset_id / 16) * 4;
 	const u_int shift = reset_id % 16;
 
 	CRU_WRITE(sc, reg, (1 << (shift + 16)) | (1 << shift));
@@ -77,7 +75,7 @@ rk_cru_reset_deassert(device_t dev, void
 {
 	struct rk_cru_softc * const sc = device_private(dev);
 	const uintptr_t reset_id = (uintptr_t)priv;
-	const bus_size_t reg = CRU_SOFTRST_CON0 + (reset_id / 16) * 4;
+	const bus_size_t reg = sc->sc_softrst_base + (reset_id / 16) * 4;
 	const u_int shift = reset_id % 16;
 
 	CRU_WRITE(sc, reg, (1 << (shift + 16)) | (0 << shift));
@@ -357,13 +355,13 @@ rk_cru_print(struct rk_cru_softc *sc)
 		default:			type = "???"; break;
 		}
 
-        	aprint_debug_dev(sc->sc_dev,
+        	aprint_normal_dev(sc->sc_dev,
 		    "%3d %-14s %2s %-14s %-7s ",
 		    clk->id,
         	    clk->base.name,
         	    clkp_parent ? "<-" : "",
         	    clkp_parent ? clkp_parent->name : "",
         	    type);
-		aprint_debug("%10d Hz\n", clk_get_rate(&clk->base));
+		aprint_normal("%10d Hz\n", clk_get_rate(&clk->base));
 	}
 }

Index: src/sys/arch/arm/rockchip/rk_cru.h
diff -u src/sys/arch/arm/rockchip/rk_cru.h:1.2 src/sys/arch/arm/rockchip/rk_cru.h:1.3
--- src/sys/arch/arm/rockchip/rk_cru.h:1.2	Sat Jun 30 17:54:07 2018
+++ src/sys/arch/arm/rockchip/rk_cru.h	Sun Aug 12 16:48:05 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: rk_cru.h,v 1.2 2018/06/30 17:54:07 jmcneill Exp $ */
+/* $NetBSD: rk_cru.h,v 1.3 2018/08/12 16:48:05 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca>
@@ -78,20 +78,22 @@ struct rk_cru_pll {
 	uint32_t	lock_mask;
 	const struct rk_cru_pll_rate *rates;
 	u_int		nrates;
-	const char	*parent;
+	const char	**parents;
+	u_int		nparents;
 };
 
 u_int	rk_cru_pll_get_rate(struct rk_cru_softc *, struct rk_cru_clk *);
 int	rk_cru_pll_set_rate(struct rk_cru_softc *, struct rk_cru_clk *, u_int);
 const char *rk_cru_pll_get_parent(struct rk_cru_softc *, struct rk_cru_clk *);
 
-#define	RK_PLL(_id, _name, _parent, _con_base, _mode_reg, _mode_mask, _lock_mask, _rates) \
+#define	RK_PLL(_id, _name, _parents, _con_base, _mode_reg, _mode_mask, _lock_mask, _rates) \
 	{							\
 		.id = (_id),					\
 		.type = RK_CRU_PLL,				\
 		.base.name = (_name),				\
 		.base.flags = 0,				\
-		.u.pll.parent = (_parent),			\
+		.u.pll.parents = (_parents),			\
+		.u.pll.nparents = __arraycount(_parents),	\
 		.u.pll.con_base = (_con_base),			\
 		.u.pll.mode_reg = (_mode_reg),			\
 		.u.pll.mode_mask = (_mode_mask),		\
@@ -195,6 +197,15 @@ int	rk_cru_composite_set_parent(struct r
 		.set_parent = rk_cru_composite_set_parent,	\
 	}
 
+#define	RK_COMPOSITE_NOMUX(_id, _name, _parent, _div_reg, _div_mask, _gate_reg, _gate_mask, _flags) \
+	RK_COMPOSITE(_id, _name, (const char *[]){ _parent }, _div_reg, 0, _div_mask, _gate_reg, _gate_mask, _flags)
+
+#define	RK_COMPOSITE_NOGATE(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, _flags) \
+	RK_COMPOSITE(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, 0, 0, _flags)
+
+#define	RK_DIV(_id, _name, _parent, _div_reg, _div_mask, _flags) \
+	RK_COMPOSITE(_id, _name, (const char *[]){ _parent }, _div_reg, 0, _div_mask, 0, 0, _flags)
+
 /* Gate clocks */
 
 struct rk_cru_gate {
@@ -300,6 +311,8 @@ struct rk_cru_softc {
 
 	struct rk_cru_clk	*sc_clks;
 	u_int			sc_nclks;
+
+	bus_size_t		sc_softrst_base;
 };
 
 int	rk_cru_attach(struct rk_cru_softc *);
Index: src/sys/arch/arm/rockchip/rk_platform.c
diff -u src/sys/arch/arm/rockchip/rk_platform.c:1.2 src/sys/arch/arm/rockchip/rk_platform.c:1.3
--- src/sys/arch/arm/rockchip/rk_platform.c:1.2	Sun Aug  5 14:02:35 2018
+++ src/sys/arch/arm/rockchip/rk_platform.c	Sun Aug 12 16:48:05 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: rk_platform.c,v 1.2 2018/08/05 14:02:35 skrll Exp $ */
+/* $NetBSD: rk_platform.c,v 1.3 2018/08/12 16:48:05 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca>
@@ -31,7 +31,7 @@
 #include "opt_fdt_arm.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: rk_platform.c,v 1.2 2018/08/05 14:02:35 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rk_platform.c,v 1.3 2018/08/12 16:48:05 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -158,3 +158,61 @@ static const struct arm_platform rk3328_
 ARM_PLATFORM(rk3328, "rockchip,rk3328", &rk3328_platform);
 
 #endif /* SOC_RK3328 */
+
+
+#ifdef SOC_RK3399
+
+#include <arm/rockchip/rk3399_platform.h>
+
+static const struct pmap_devmap *
+rk3399_platform_devmap(void)
+{
+	static const struct pmap_devmap devmap[] = {
+		DEVMAP_ENTRY(RK3399_CORE_VBASE,
+			     RK3399_CORE_PBASE,
+			     RK3399_CORE_SIZE),
+		DEVMAP_ENTRY_END
+	};
+
+	return devmap;
+}
+
+void rk3399_platform_early_putchar(char);
+
+void
+rk3399_platform_early_putchar(char c)
+{
+#ifdef CONSADDR
+#define CONSADDR_VA	((CONSADDR - RK3399_CORE_PBASE) + RK3399_CORE_VBASE)
+	volatile uint32_t *uartaddr = cpu_earlydevice_va_p() ?
+	    (volatile uint32_t *)CONSADDR_VA :
+	    (volatile uint32_t *)CONSADDR;
+
+	while ((le32toh(uartaddr[com_lsr]) & LSR_TXRDY) == 0)
+		;
+
+	uartaddr[com_data] = htole32(c);
+#undef CONSADDR_VA
+#endif
+}
+
+static u_int
+rk3399_platform_uart_freq(void)
+{
+	return RK3399_UART_FREQ;
+}
+
+static const struct arm_platform rk3399_platform = {
+	.ap_devmap = rk3399_platform_devmap,
+	.ap_bootstrap = rk_platform_bootstrap,
+	.ap_init_attach_args = rk_platform_init_attach_args,
+	.ap_early_putchar = rk3399_platform_early_putchar,
+	.ap_device_register = rk_platform_device_register,
+	.ap_reset = psci_fdt_reset,
+	.ap_delay = gtmr_delay,
+	.ap_uart_freq = rk3399_platform_uart_freq,
+};
+
+ARM_PLATFORM(rk3399, "rockchip,rk3399", &rk3399_platform);
+
+#endif /* SOC_RK3399 */

Index: src/sys/arch/arm/rockchip/rk_gmac.c
diff -u src/sys/arch/arm/rockchip/rk_gmac.c:1.7 src/sys/arch/arm/rockchip/rk_gmac.c:1.8
--- src/sys/arch/arm/rockchip/rk_gmac.c:1.7	Mon Jul 16 23:11:47 2018
+++ src/sys/arch/arm/rockchip/rk_gmac.c	Sun Aug 12 16:48:05 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: rk_gmac.c,v 1.7 2018/07/16 23:11:47 christos Exp $ */
+/* $NetBSD: rk_gmac.c,v 1.8 2018/08/12 16:48:05 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca>
@@ -28,7 +28,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(0, "$NetBSD: rk_gmac.c,v 1.7 2018/07/16 23:11:47 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rk_gmac.c,v 1.8 2018/08/12 16:48:05 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -49,6 +49,30 @@ __KERNEL_RCSID(0, "$NetBSD: rk_gmac.c,v 
 #include <dev/fdt/fdtvar.h>
 #include <dev/fdt/syscon.h>
 
+#define	RK_GMAC_TXDLY_DEFAULT	0x30
+#define	RK_GMAC_RXDLY_DEFAULT	0x10
+
+enum rk_gmac_type {
+	GMAC_RK3328 = 1,
+	GMAC_RK3399
+};
+
+static const struct of_compat_data compat_data[] = {
+	{ "rockchip,rk3328-gmac",	GMAC_RK3328 },
+	{ "rockchip,rk3399-gmac",	GMAC_RK3399 },
+	{ NULL }
+};
+
+struct rk_gmac_softc {
+	struct dwc_gmac_softc	sc_base;
+	struct syscon		*sc_syscon;
+	enum rk_gmac_type	sc_type;
+};
+
+/*
+ * RK3328 specific
+ */
+
 #define	RK3328_GRF_MAC_CON0	0x0900
 #define	 RK3328_GRF_MAC_CON0_RXDLY	__BITS(13,7)
 #define	 RK3328_GRF_MAC_CON0_TXDLY	__BITS(6,0)
@@ -64,56 +88,6 @@ __KERNEL_RCSID(0, "$NetBSD: rk_gmac.c,v 
 #define	 RK3328_GRF_MAC_CON1_RXDLY_EN	__BIT(1)
 #define	 RK3328_GRF_MAC_CON1_TXDLY_EN	__BIT(0)
 
-#define	RK_GMAC_TXDLY_DEFAULT	0x30
-#define	RK_GMAC_RXDLY_DEFAULT	0x10
-
-static const char * compatible[] = {
-	"rockchip,rk3328-gmac",
-	NULL
-};
-
-struct rk_gmac_softc {
-	struct dwc_gmac_softc	sc_base;
-	struct syscon		*sc_syscon;
-};
-
-static int
-rk_gmac_reset(const int phandle)
-{
-	struct fdtbus_gpio_pin *pin_reset;
-	const u_int *reset_delay_us;
-	bool reset_active_low;
-	int len;
-
-	if (!of_hasprop(phandle, "snps,reset-gpio"))
-		return 0;
-
-	pin_reset = fdtbus_gpio_acquire(phandle, "snps,reset-gpio", GPIO_PIN_OUTPUT);
-	if (pin_reset == NULL)
-		return ENOENT;
-
-	reset_delay_us = fdtbus_get_prop(phandle, "snps,reset-delays-us", &len);
-	if (reset_delay_us == NULL || len != 12)
-		return ENXIO;
-
-	reset_active_low = of_hasprop(phandle, "snps,reset-active-low");
-
-	fdtbus_gpio_write_raw(pin_reset, reset_active_low ? 1 : 0);
-	delay(be32toh(reset_delay_us[0]));
-	fdtbus_gpio_write_raw(pin_reset, reset_active_low ? 0 : 1);
-	delay(be32toh(reset_delay_us[1]));
-	fdtbus_gpio_write_raw(pin_reset, reset_active_low ? 1 : 0);
-	delay(be32toh(reset_delay_us[2]));
-
-	return 0;
-}
-
-static int
-rk_gmac_intr(void *arg)
-{
-	return dwc_gmac_intr(arg);
-}
-
 static void
 rk3328_gmac_set_mode_rgmii(struct dwc_gmac_softc *sc, u_int tx_delay, u_int rx_delay)
 {
@@ -167,6 +141,112 @@ rk3328_gmac_set_speed_rgmii(struct dwc_g
 	syscon_unlock(rk_sc->sc_syscon);
 }
 
+/*
+ * RK3399 specific
+ */
+
+#define	RK3399_GRF_SOC_CON5		0x0c214
+#define	 RK3399_GRF_SOC_CON5_GMAC_PHY_INTF_SEL	__BITS(11,9)
+#define	 RK3399_GRF_SOC_CON5_GMAC_FLOWCTRL	__BIT(8)
+#define	 RK3399_GRF_SOC_CON5_GMAC_SPEED		__BIT(7)
+#define	 RK3399_GRF_SOC_CON5_RMII_MODE		__BIT(6)
+#define	 RK3399_GRF_SOC_CON5_GMAC_CLK_SEL	__BITS(5,4)
+#define	  RK3399_GRF_SOC_CON5_GMAC_CLK_SEL_125M	0
+#define	  RK3399_GRF_SOC_CON5_GMAC_CLK_SEL_25M	1
+#define	  RK3399_GRF_SOC_CON5_GMAC_CLK_SEL_2_5M	2
+#define	 RK3399_GRF_SOC_CON5_RMII_CLK_SEL	__BIT(3)
+#define	RK3399_GRF_SOC_CON6		0x0c218
+#define	 RK3399_GRF_SOC_CON6_GMAC_RXCLK_DLY_ENA	__BIT(15)
+#define	 RK3399_GRF_SOC_CON6_GMAC_CLK_RX_DL_CFG	__BITS(14,8)
+#define	 RK3399_GRF_SOC_CON6_GMAC_TXCLK_DLY_ENA	__BIT(7)
+#define	 RK3399_GRF_SOC_CON6_GMAC_CLK_TX_DL_CFG	__BITS(6,0)
+
+static void
+rk3399_gmac_set_mode_rgmii(struct dwc_gmac_softc *sc, u_int tx_delay, u_int rx_delay)
+{
+	struct rk_gmac_softc * const rk_sc = (struct rk_gmac_softc *)sc;
+
+	const uint32_t con5_mask =
+	    (RK3399_GRF_SOC_CON5_RMII_MODE | RK3399_GRF_SOC_CON5_GMAC_PHY_INTF_SEL) << 16;
+	const uint32_t con5 = __SHIFTIN(1, RK3399_GRF_SOC_CON5_GMAC_PHY_INTF_SEL);
+
+	const uint32_t con6_mask =
+	    (RK3399_GRF_SOC_CON6_GMAC_CLK_RX_DL_CFG | RK3399_GRF_SOC_CON6_GMAC_CLK_TX_DL_CFG) << 16;
+	const uint32_t con6 =
+	    __SHIFTIN(rx_delay, RK3399_GRF_SOC_CON6_GMAC_CLK_RX_DL_CFG) |
+	    __SHIFTIN(tx_delay, RK3399_GRF_SOC_CON6_GMAC_CLK_TX_DL_CFG);
+
+	syscon_lock(rk_sc->sc_syscon);
+	syscon_write_4(rk_sc->sc_syscon, RK3399_GRF_SOC_CON5, con5 | con5_mask);
+	syscon_write_4(rk_sc->sc_syscon, RK3399_GRF_SOC_CON6, con6 | con6_mask);
+	syscon_unlock(rk_sc->sc_syscon);
+}
+
+static void
+rk3399_gmac_set_speed_rgmii(struct dwc_gmac_softc *sc, int speed)
+{
+	struct rk_gmac_softc * const rk_sc = (struct rk_gmac_softc *)sc;
+	u_int clksel;
+
+	switch (speed) {
+	case IFM_10_T:
+		clksel = RK3399_GRF_SOC_CON5_GMAC_CLK_SEL_2_5M;
+		break;
+	case IFM_100_TX:
+		clksel = RK3399_GRF_SOC_CON5_GMAC_CLK_SEL_25M;
+		break;
+	default:
+		clksel = RK3399_GRF_SOC_CON5_GMAC_CLK_SEL_125M;
+		break;
+	}
+
+	const uint32_t con5_mask =
+	    RK3399_GRF_SOC_CON5_GMAC_CLK_SEL << 16;
+	const uint32_t con5 =
+	    __SHIFTIN(clksel, RK3399_GRF_SOC_CON5_GMAC_CLK_SEL);
+
+	syscon_lock(rk_sc->sc_syscon);
+	syscon_write_4(rk_sc->sc_syscon, RK3399_GRF_SOC_CON5, con5 | con5_mask);
+	syscon_unlock(rk_sc->sc_syscon);
+}
+
+static int
+rk_gmac_reset(const int phandle)
+{
+	struct fdtbus_gpio_pin *pin_reset;
+	const u_int *reset_delay_us;
+	bool reset_active_low;
+	int len;
+
+	if (!of_hasprop(phandle, "snps,reset-gpio"))
+		return 0;
+
+	pin_reset = fdtbus_gpio_acquire(phandle, "snps,reset-gpio", GPIO_PIN_OUTPUT);
+	if (pin_reset == NULL)
+		return ENOENT;
+
+	reset_delay_us = fdtbus_get_prop(phandle, "snps,reset-delays-us", &len);
+	if (reset_delay_us == NULL || len != 12)
+		return ENXIO;
+
+	reset_active_low = of_hasprop(phandle, "snps,reset-active-low");
+
+	fdtbus_gpio_write_raw(pin_reset, reset_active_low ? 1 : 0);
+	delay(be32toh(reset_delay_us[0]));
+	fdtbus_gpio_write_raw(pin_reset, reset_active_low ? 0 : 1);
+	delay(be32toh(reset_delay_us[1]));
+	fdtbus_gpio_write_raw(pin_reset, reset_active_low ? 1 : 0);
+	delay(be32toh(reset_delay_us[2]));
+
+	return 0;
+}
+
+static int
+rk_gmac_intr(void *arg)
+{
+	return dwc_gmac_intr(arg);
+}
+
 static int
 rk_gmac_setup_clocks(int phandle)
 {
@@ -234,7 +314,7 @@ rk_gmac_match(device_t parent, cfdata_t 
 {
 	struct fdt_attach_args * const faa = aux;
 
-	return of_match_compatible(faa->faa_phandle, compatible);
+	return of_match_compat_data(faa->faa_phandle, compat_data);
 }
 
 static void
@@ -255,6 +335,8 @@ rk_gmac_attach(device_t parent, device_t
 		return;
 	}
 
+	rk_sc->sc_type = of_search_compatible(phandle, compat_data)->data;
+
 	rk_sc->sc_syscon = fdtbus_syscon_acquire(phandle, "rockchip,grf");
 	if (rk_sc->sc_syscon == NULL) {
 		aprint_error(": couldn't get grf syscon\n");
@@ -300,13 +382,27 @@ rk_gmac_attach(device_t parent, device_t
 		return;
 	}
 
-	if (strcmp(phy_mode, "rgmii") == 0) {
-		rk3328_gmac_set_mode_rgmii(sc, tx_delay, rx_delay);
-
-		sc->sc_set_speed = rk3328_gmac_set_speed_rgmii;
-	} else {
-		aprint_error(": unsupported phy-mode '%s'\n", phy_mode);
-		return;
+	switch (rk_sc->sc_type) {
+	case GMAC_RK3328:
+		if (strcmp(phy_mode, "rgmii") == 0) {
+			rk3328_gmac_set_mode_rgmii(sc, tx_delay, rx_delay);
+
+			sc->sc_set_speed = rk3328_gmac_set_speed_rgmii;
+		} else {
+			aprint_error(": unsupported phy-mode '%s'\n", phy_mode);
+			return;
+		}
+		break;
+	case GMAC_RK3399:
+		if (strcmp(phy_mode, "rgmii") == 0) {
+			rk3399_gmac_set_mode_rgmii(sc, tx_delay, rx_delay);
+
+			sc->sc_set_speed = rk3399_gmac_set_speed_rgmii;
+		} else {
+			aprint_error(": unsupported phy-mode '%s'\n", phy_mode);
+			return;
+		}
+		break;
 	}
 
 	aprint_naive("\n");

Added files:

Index: src/sys/arch/arm/rockchip/rk3328_iomux.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk3328_iomux.c:1.1
--- /dev/null	Sun Aug 12 16:48:05 2018
+++ src/sys/arch/arm/rockchip/rk3328_iomux.c	Sun Aug 12 16:48:04 2018
@@ -0,0 +1,314 @@
+/* $NetBSD: rk3328_iomux.c,v 1.1 2018/08/12 16:48:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2018 Jared McNeill <jmcne...@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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: rk3328_iomux.c,v 1.1 2018/08/12 16:48:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/kmem.h>
+#include <sys/lwp.h>
+
+#include <dev/fdt/fdtvar.h>
+#include <dev/fdt/syscon.h>
+
+#define	GRF_GPIO_P_REG(_bank, _idx)	(0x0100 + (_bank) * 0x10 + ((_idx) >> 3) * 4)
+#define	 GRF_GPIO_P_CTL(_idx)		(0x3 << (((_idx) & 7) * 2))
+#define	  GRF_GPIO_P_CTL_Z		0
+#define	  GRF_GPIO_P_CTL_PULLUP		1
+#define	  GRF_GPIO_P_CTL_PULLDOWN	2
+#define	  GRF_GPIO_P_CTL_REPEATER	3
+#define	  GRF_GPIO_P_CTL_MASK		0x3
+#define	 GRF_GPIO_P_WRITE_EN(_idx)	(0x3 << (((_idx) & 7) * 2 + 16))
+
+#define	GRF_GPIO_E_REG(_bank, _idx)	(0x0200 + (_bank) * 0x10 + ((_idx) >> 3) * 4)
+#define	 GRF_GPIO_E_CTL(_idx)		(0x3 << (((_idx) & 7) * 2))
+#define	  GRF_GPIO_E_CTL_2MA		0
+#define	  GRF_GPIO_E_CTL_4MA		1
+#define	  GRF_GPIO_E_CTL_8MA		2
+#define	  GRF_GPIO_E_CTL_12MA		3
+#define	  GRF_GPIO_E_CTL_MASK		0x3
+#define	 GRF_GPIO_E_WRITE_EN(_idx)	(0x3 << (((_idx) & 7) * 2 + 16))
+
+struct rk3328_iomux {
+	bus_size_t		base;
+	u_int			type;
+#define	RK3328_IOMUX_TYPE_3BIT	0x01
+};
+
+struct rk3328_iomux_bank {
+	struct rk3328_iomux		iomux[4];
+};
+
+static const struct rk3328_iomux_bank rk3328_iomux_banks[] = {
+	[0] = {
+		.iomux = {
+			[0] = { .base = 0x0000 },
+			[1] = { .base = 0x0004 },
+			[2] = { .base = 0x0008 },
+			[3] = { .base = 0x000c },
+		},
+	},
+	[1] = {
+		.iomux = {
+			[0] = { .base = 0x0010 },
+			[1] = { .base = 0x0014 },
+			[2] = { .base = 0x0018 },
+			[3] = { .base = 0x001c },
+		}
+	},
+	[2] = {
+		.iomux = {
+			[0] = { .base = 0x0020 },
+			[1] = { .base = 0x0024, .type = RK3328_IOMUX_TYPE_3BIT },
+			[2] = { .base = 0x002c, .type = RK3328_IOMUX_TYPE_3BIT },
+			[3] = { .base = 0x0034 },
+		},
+	},
+	[3] = {
+		.iomux = {
+			[0] = { .base = 0x0038, .type = RK3328_IOMUX_TYPE_3BIT },
+			[1] = { .base = 0x0040, .type = RK3328_IOMUX_TYPE_3BIT },
+			[2] = { .base = 0x0048 },
+			[3] = { .base = 0x004c },
+		},
+	},
+};
+
+struct rk3328_iomux_conf {
+	const struct rk3328_iomux_bank *banks;
+	u_int nbanks;
+};
+
+static const struct rk3328_iomux_conf rk3328_iomux_conf = {
+	.banks = rk3328_iomux_banks,
+	.nbanks = __arraycount(rk3328_iomux_banks),
+};
+
+static const struct of_compat_data compat_data[] = {
+	{ "rockchip,rk3328-pinctrl",	(uintptr_t)&rk3328_iomux_conf },
+	{ NULL }
+};
+
+struct rk3328_iomux_softc {
+	device_t sc_dev;
+	struct syscon *sc_syscon;
+
+	const struct rk3328_iomux_conf *sc_conf;
+};
+
+#define	LOCK(sc)		\
+	syscon_lock((sc)->sc_syscon)
+#define	UNLOCK(sc)		\
+	syscon_unlock((sc)->sc_syscon)
+#define	RD4(sc, reg) 		\
+	syscon_read_4((sc)->sc_syscon, (reg))
+#define	WR4(sc, reg, val) 	\
+	syscon_write_4((sc)->sc_syscon, (reg), (val))
+
+static int	rk3328_iomux_match(device_t, cfdata_t, void *);
+static void	rk3328_iomux_attach(device_t, device_t, void *);
+
+CFATTACH_DECL_NEW(rk3328_iomux, sizeof(struct rk3328_iomux_softc),
+	rk3328_iomux_match, rk3328_iomux_attach, NULL, NULL);
+
+static void
+rk3328_iomux_calc_iomux_reg(struct rk3328_iomux_softc *sc, u_int bank, u_int pin, bus_size_t *reg, uint32_t *mask)
+{
+	const struct rk3328_iomux_bank *banks = sc->sc_conf->banks;
+
+	KASSERT(bank < sc->sc_conf->nbanks);
+
+	*reg = banks[bank].iomux[pin / 8].base;
+	if (banks[bank].iomux[pin / 8].type & RK3328_IOMUX_TYPE_3BIT) {
+		if ((pin % 8) >= 5)
+			*reg += 0x04;
+		const u_int bit = (pin % 8 % 5) * 3;
+		*mask = 7 << bit;
+	} else {
+		const u_int bit = (pin % 8) * 2;
+		*mask = 3 << bit;
+	}
+}
+
+static void
+rk3328_iomux_set_bias(struct rk3328_iomux_softc *sc, u_int bank, u_int idx, u_int bias)
+{
+	WR4(sc, GRF_GPIO_P_REG(bank, idx),
+	    __SHIFTIN(GRF_GPIO_P_CTL_MASK, GRF_GPIO_P_WRITE_EN(idx)) |
+	    __SHIFTIN(bias, GRF_GPIO_P_CTL(idx)));
+}
+
+static void
+rk3328_iomux_set_drive_strength(struct rk3328_iomux_softc *sc, u_int bank, u_int idx, u_int drv)
+{
+	WR4(sc, GRF_GPIO_E_REG(bank, idx),
+	    __SHIFTIN(GRF_GPIO_E_CTL_MASK, GRF_GPIO_E_WRITE_EN(idx)) |
+	    __SHIFTIN(drv, GRF_GPIO_E_CTL(idx)));
+}
+
+static void
+rk3328_iomux_set_mux(struct rk3328_iomux_softc *sc, u_int bank, u_int idx, u_int mux)
+{
+	bus_size_t reg;
+	uint32_t mask;
+
+	rk3328_iomux_calc_iomux_reg(sc, bank, idx, &reg, &mask);
+
+	WR4(sc, reg, (mask << 16) | __SHIFTIN(mux, mask));
+}
+
+static int
+rk3328_iomux_config(struct rk3328_iomux_softc *sc, const int phandle, u_int bank, u_int idx, u_int mux)
+{
+	u_int drv;
+
+	if (of_hasprop(phandle, "bias-disable"))
+		rk3328_iomux_set_bias(sc, bank, idx, GRF_GPIO_P_CTL_Z);
+	else if (of_hasprop(phandle, "bias-pull-up"))
+		rk3328_iomux_set_bias(sc, bank, idx, GRF_GPIO_P_CTL_PULLUP);
+	else if (of_hasprop(phandle, "bias-pull-down"))
+		rk3328_iomux_set_bias(sc, bank, idx, GRF_GPIO_P_CTL_PULLDOWN);
+
+	if (of_getprop_uint32(phandle, "drive-strength", &drv) == 0) {
+		switch (drv) {
+		case 2:
+			rk3328_iomux_set_drive_strength(sc, bank, idx, GRF_GPIO_E_CTL_2MA);
+			break;
+		case 4:
+			rk3328_iomux_set_drive_strength(sc, bank, idx, GRF_GPIO_E_CTL_4MA);
+			break;
+		case 8:
+			rk3328_iomux_set_drive_strength(sc, bank, idx, GRF_GPIO_E_CTL_8MA);
+			break;
+		case 12:
+			rk3328_iomux_set_drive_strength(sc, bank, idx, GRF_GPIO_E_CTL_12MA);
+			break;
+		default:
+			aprint_error_dev(sc->sc_dev, "unsupported drive-strength %u\n", drv);
+			return EINVAL;
+		}
+	}
+
+#if notyet
+	if (of_hasprop(phandle, "input-enable"))
+		rk3328_iomux_set_direction(sc, bank, idx, GPIO_PIN_INPUT, -1);
+	else if (of_hasprop(phandle, "output-high"))
+		rk3328_iomux_set_direction(sc, bank, idx, GPIO_PIN_OUTPUT, GPIO_PIN_HIGH);
+	else if (of_hasprop(phandle, "output-low"))
+		rk3328_iomux_set_direction(sc, bank, idx, GPIO_PIN_OUTPUT, GPIO_PIN_LOW);
+#endif
+
+	rk3328_iomux_set_mux(sc, bank, idx, mux);
+
+	return 0;
+}
+
+static int
+rk3328_iomux_pinctrl_set_config(device_t dev, const void *data, size_t len)
+{
+	struct rk3328_iomux_softc * const sc = device_private(dev);
+	int pins_len;
+
+	if (len != 4)
+		return -1;
+
+	const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
+	const u_int *pins = fdtbus_get_prop(phandle, "rockchip,pins", &pins_len);
+
+	while (pins_len >= 16) {
+		const u_int bank = be32toh(pins[0]);
+		const u_int idx = be32toh(pins[1]);
+		const u_int mux = be32toh(pins[2]);
+		const int cfg = fdtbus_get_phandle_from_native(be32toh(pins[3]));
+
+		LOCK(sc);
+		rk3328_iomux_config(sc, cfg, bank, idx, mux);
+		UNLOCK(sc);
+
+		pins_len -= 16;
+		pins += 4;
+	}
+
+	return 0;
+}
+
+static struct fdtbus_pinctrl_controller_func rk3328_iomux_pinctrl_funcs = {
+	.set_config = rk3328_iomux_pinctrl_set_config,
+};
+
+static int
+rk3328_iomux_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct fdt_attach_args * const faa = aux;
+
+	return of_match_compat_data(faa->faa_phandle, compat_data);
+}
+
+static void
+rk3328_iomux_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk3328_iomux_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	int child, sub;
+
+	sc->sc_dev = self;
+	sc->sc_syscon = fdtbus_syscon_acquire(phandle, "rockchip,grf");
+	if (sc->sc_syscon == NULL) {
+		aprint_error(": couldn't acquire grf syscon\n");
+		return;
+	}
+	sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data;
+
+	aprint_naive("\n");
+	aprint_normal(": RK3328 IOMUX control\n");
+
+	for (child = OF_child(phandle); child; child = OF_peer(child)) {
+		for (sub = OF_child(child); sub; sub = OF_peer(sub)) {
+			if (!of_hasprop(sub, "rockchip,pins"))
+				continue;
+			fdtbus_register_pinctrl_config(self, sub, &rk3328_iomux_pinctrl_funcs);
+		}
+	}
+
+	fdtbus_pinctrl_configure();
+
+	for (child = OF_child(phandle); child; child = OF_peer(child)) {
+		struct fdt_attach_args cfaa = *faa;
+		cfaa.faa_phandle = child;
+		cfaa.faa_name = fdtbus_get_string(child, "name");
+		cfaa.faa_quiet = false;
+
+		config_found(self, &cfaa, NULL);
+	}
+}
Index: src/sys/arch/arm/rockchip/rk3399_cru.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk3399_cru.c:1.1
--- /dev/null	Sun Aug 12 16:48:05 2018
+++ src/sys/arch/arm/rockchip/rk3399_cru.c	Sun Aug 12 16:48:05 2018
@@ -0,0 +1,636 @@
+/* $NetBSD: rk3399_cru.c,v 1.1 2018/08/12 16:48:05 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2018 Jared McNeill <jmcne...@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.
+ */
+
+#include <sys/cdefs.h>
+
+__KERNEL_RCSID(1, "$NetBSD: rk3399_cru.c,v 1.1 2018/08/12 16:48:05 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#include <arm/rockchip/rk_cru.h>
+#include <arm/rockchip/rk3399_cru.h>
+
+#define	PLL_CON(n)	(0x0000 + (n) * 4)
+#define	CLKSEL_CON(n)	(0x0100 + (n) * 4)
+#define	CLKGATE_CON(n)	(0x0300 + (n) * 4)
+#define	SOFTRST_CON(n)	(0x0400 + (n) * 4)
+
+static int rk3399_cru_match(device_t, cfdata_t, void *);
+static void rk3399_cru_attach(device_t, device_t, void *);
+
+static const char * const compatible[] = {
+	"rockchip,rk3399-cru",
+	NULL
+};
+
+CFATTACH_DECL_NEW(rk3399_cru, sizeof(struct rk_cru_softc),
+	rk3399_cru_match, rk3399_cru_attach, NULL, NULL);
+
+static const struct rk_cru_pll_rate pll_rates[] = {
+	RK_PLL_RATE(2208000000,  1,  92, 1, 1, 1, 0),
+	RK_PLL_RATE(2184000000,  1,  91, 1, 1, 1, 0),
+	RK_PLL_RATE(2160000000,  1,  90, 1, 1, 1, 0),
+	RK_PLL_RATE(2136000000,  1,  89, 1, 1, 1, 0),
+	RK_PLL_RATE(2112000000,  1,  88, 1, 1, 1, 0),
+	RK_PLL_RATE(2088000000,  1,  87, 1, 1, 1, 0),
+	RK_PLL_RATE(2064000000,  1,  86, 1, 1, 1, 0),
+	RK_PLL_RATE(2040000000,  1,  85, 1, 1, 1, 0),
+	RK_PLL_RATE(2016000000,  1,  84, 1, 1, 1, 0),
+	RK_PLL_RATE(1992000000,  1,  83, 1, 1, 1, 0),
+	RK_PLL_RATE(1968000000,  1,  82, 1, 1, 1, 0),
+	RK_PLL_RATE(1944000000,  1,  81, 1, 1, 1, 0),
+	RK_PLL_RATE(1920000000,  1,  80, 1, 1, 1, 0),
+	RK_PLL_RATE(1896000000,  1,  79, 1, 1, 1, 0),
+	RK_PLL_RATE(1872000000,  1,  78, 1, 1, 1, 0),
+	RK_PLL_RATE(1848000000,  1,  77, 1, 1, 1, 0),
+	RK_PLL_RATE(1824000000,  1,  76, 1, 1, 1, 0),
+	RK_PLL_RATE(1800000000,  1,  75, 1, 1, 1, 0),
+	RK_PLL_RATE(1776000000,  1,  74, 1, 1, 1, 0),
+	RK_PLL_RATE(1752000000,  1,  73, 1, 1, 1, 0),
+	RK_PLL_RATE(1728000000,  1,  72, 1, 1, 1, 0),
+	RK_PLL_RATE(1704000000,  1,  71, 1, 1, 1, 0),
+	RK_PLL_RATE(1680000000,  1,  70, 1, 1, 1, 0),
+	RK_PLL_RATE(1656000000,  1,  69, 1, 1, 1, 0),
+	RK_PLL_RATE(1632000000,  1,  68, 1, 1, 1, 0),
+	RK_PLL_RATE(1608000000,  1,  67, 1, 1, 1, 0),
+	RK_PLL_RATE(1600000000,  3, 200, 1, 1, 1, 0),
+	RK_PLL_RATE(1584000000,  1,  66, 1, 1, 1, 0),
+	RK_PLL_RATE(1560000000,  1,  65, 1, 1, 1, 0),
+	RK_PLL_RATE(1536000000,  1,  64, 1, 1, 1, 0),
+	RK_PLL_RATE(1512000000,  1,  63, 1, 1, 1, 0),
+	RK_PLL_RATE(1488000000,  1,  62, 1, 1, 1, 0),
+	RK_PLL_RATE(1464000000,  1,  61, 1, 1, 1, 0),
+	RK_PLL_RATE(1440000000,  1,  60, 1, 1, 1, 0),
+	RK_PLL_RATE(1416000000,  1,  59, 1, 1, 1, 0),
+	RK_PLL_RATE(1392000000,  1,  58, 1, 1, 1, 0),
+	RK_PLL_RATE(1368000000,  1,  57, 1, 1, 1, 0),
+	RK_PLL_RATE(1344000000,  1,  56, 1, 1, 1, 0),
+	RK_PLL_RATE(1320000000,  1,  55, 1, 1, 1, 0),
+	RK_PLL_RATE(1296000000,  1,  54, 1, 1, 1, 0),
+	RK_PLL_RATE(1272000000,  1,  53, 1, 1, 1, 0),
+	RK_PLL_RATE(1248000000,  1,  52, 1, 1, 1, 0),
+	RK_PLL_RATE(1200000000,  1,  50, 1, 1, 1, 0),
+	RK_PLL_RATE(1188000000,  2,  99, 1, 1, 1, 0),
+	RK_PLL_RATE(1104000000,  1,  46, 1, 1, 1, 0),
+	RK_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0),
+	RK_PLL_RATE(1008000000,  1,  84, 2, 1, 1, 0),
+	RK_PLL_RATE(1000000000,  1, 125, 3, 1, 1, 0),
+	RK_PLL_RATE( 984000000,  1,  82, 2, 1, 1, 0),
+	RK_PLL_RATE( 960000000,  1,  80, 2, 1, 1, 0),
+	RK_PLL_RATE( 936000000,  1,  78, 2, 1, 1, 0),
+	RK_PLL_RATE( 912000000,  1,  76, 2, 1, 1, 0),
+	RK_PLL_RATE( 900000000,  4, 300, 2, 1, 1, 0),
+	RK_PLL_RATE( 888000000,  1,  74, 2, 1, 1, 0),
+	RK_PLL_RATE( 864000000,  1,  72, 2, 1, 1, 0),
+	RK_PLL_RATE( 840000000,  1,  70, 2, 1, 1, 0),
+	RK_PLL_RATE( 816000000,  1,  68, 2, 1, 1, 0),
+	RK_PLL_RATE( 800000000,  1, 100, 3, 1, 1, 0),
+	RK_PLL_RATE( 700000000,  6, 350, 2, 1, 1, 0),
+	RK_PLL_RATE( 696000000,  1,  58, 2, 1, 1, 0),
+	RK_PLL_RATE( 676000000,  3, 169, 2, 1, 1, 0),
+	RK_PLL_RATE( 600000000,  1,  75, 3, 1, 1, 0),
+	RK_PLL_RATE( 594000000,  1,  99, 4, 1, 1, 0),
+	RK_PLL_RATE( 533250000,  8, 711, 4, 1, 1, 0),
+	RK_PLL_RATE( 504000000,  1,  63, 3, 1, 1, 0),
+	RK_PLL_RATE( 500000000,  6, 250, 2, 1, 1, 0),
+	RK_PLL_RATE( 408000000,  1,  68, 2, 2, 1, 0),
+	RK_PLL_RATE( 312000000,  1,  52, 2, 2, 1, 0),
+	RK_PLL_RATE( 297000000,  1,  99, 4, 2, 1, 0),
+	RK_PLL_RATE( 216000000,  1,  72, 4, 2, 1, 0),
+	RK_PLL_RATE( 148500000,  1,  99, 4, 4, 1, 0),
+	RK_PLL_RATE( 106500000,  1,  71, 4, 4, 1, 0),
+	RK_PLL_RATE(  96000000,  1,  64, 4, 4, 1, 0),
+	RK_PLL_RATE(  74250000,  2,  99, 4, 4, 1, 0),
+	RK_PLL_RATE(  65000000,  1,  65, 6, 4, 1, 0),
+	RK_PLL_RATE(  54000000,  1,  54, 6, 4, 1, 0),
+	RK_PLL_RATE(  27000000,  1,  27, 6, 4, 1, 0),
+};
+
+static const struct rk_cru_pll_rate pll_norates[] = {
+};
+
+#define	PLL_CON0	0x00
+#define	 PLL_FBDIV	__BITS(11,0)
+
+#define	PLL_CON1	0x04
+#define	 PLL_POSTDIV2	__BITS(14,12)
+#define	 PLL_POSTDIV1	__BITS(10,8)
+#define	 PLL_REFDIV	__BITS(5,0)
+
+#define	PLL_CON2	0x08
+#define	 PLL_LOCK	__BIT(31)
+#define	 PLL_FRACDIV	__BITS(23,0)
+
+#define	PLL_CON3	0x0c
+#define	 PLL_WORK_MODE	__BITS(9,8)
+#define	  PLL_WORK_MODE_SLOW		0
+#define	  PLL_WORK_MODE_NORMAL		1
+#define	  PLL_WORK_MODE_DEEP_SLOW	2
+#define	 PLL_DSMPD	__BIT(3)
+
+#define	PLL_WRITE_MASK	0xffff0000
+
+static u_int
+rk3399_cru_pll_get_rate(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_pll *pll = &clk->u.pll;
+	struct clk *clkp, *clkp_parent;
+	u_int foutvco, foutpostdiv;
+
+	KASSERT(clk->type == RK_CRU_PLL);
+
+	clkp = &clk->base;
+	clkp_parent = clk_get_parent(clkp);
+	if (clkp_parent == NULL)
+		return 0;
+
+	const u_int fref = clk_get_rate(clkp_parent);
+	if (fref == 0)
+		return 0;
+
+	const uint32_t con0 = CRU_READ(sc, pll->con_base + PLL_CON0);
+	const uint32_t con1 = CRU_READ(sc, pll->con_base + PLL_CON1);
+	const uint32_t con2 = CRU_READ(sc, pll->con_base + PLL_CON2);
+	const uint32_t con3 = CRU_READ(sc, pll->con_base + PLL_CON3);
+
+	const u_int fbdiv = __SHIFTOUT(con0, PLL_FBDIV);
+	const u_int postdiv2 = __SHIFTOUT(con1, PLL_POSTDIV2);
+	const u_int postdiv1 = __SHIFTOUT(con1, PLL_POSTDIV1);
+	const u_int refdiv = __SHIFTOUT(con1, PLL_REFDIV);
+	const u_int fracdiv = __SHIFTOUT(con2, PLL_FRACDIV);
+	const u_int dsmpd = __SHIFTOUT(con3, PLL_DSMPD);
+
+	if (dsmpd == 1) {
+		/* integer mode */
+		foutvco = fref / refdiv * fbdiv;
+	} else {
+		/* fractional mode */
+		foutvco = fref / refdiv * fbdiv + ((fref * fracdiv) >> 24);
+	}
+	foutpostdiv = foutvco / postdiv1 / postdiv2;
+
+	return foutpostdiv;
+}
+
+static int
+rk3399_cru_pll_set_rate(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk, u_int rate)
+{
+	struct rk_cru_pll *pll = &clk->u.pll;
+	const struct rk_cru_pll_rate *pll_rate = NULL;
+	uint32_t val;
+	int retry;
+
+	KASSERT(clk->type == RK_CRU_PLL);
+
+	if (pll->rates == NULL || rate == 0)
+		return EIO;
+
+	for (int i = 0; i < pll->nrates; i++)
+		if (pll->rates[i].rate == rate) {
+			pll_rate = &pll->rates[i];
+			break;
+		}
+	if (pll_rate == NULL)
+		return EINVAL;
+
+	val = __SHIFTIN(PLL_WORK_MODE_SLOW, PLL_WORK_MODE) | (PLL_WORK_MODE << 16);
+	CRU_WRITE(sc, pll->con_base + PLL_CON3, val);
+
+	CRU_WRITE(sc, pll->con_base + PLL_CON0,
+	    __SHIFTIN(pll_rate->fbdiv, PLL_FBDIV) |
+	    PLL_WRITE_MASK);
+
+	CRU_WRITE(sc, pll->con_base + PLL_CON1,
+	    __SHIFTIN(pll_rate->postdiv2, PLL_POSTDIV2) |
+	    __SHIFTIN(pll_rate->postdiv1, PLL_POSTDIV1) |
+	    __SHIFTIN(pll_rate->refdiv, PLL_REFDIV) |
+	    PLL_WRITE_MASK);
+
+	val = CRU_READ(sc, pll->con_base + PLL_CON2);
+	val &= ~PLL_FRACDIV;
+	val |= __SHIFTIN(pll_rate->fracdiv, PLL_FRACDIV);
+	CRU_WRITE(sc, pll->con_base + PLL_CON2, val);
+
+	val = __SHIFTIN(pll_rate->dsmpd, PLL_DSMPD) | (PLL_DSMPD << 16);
+	CRU_WRITE(sc, pll->con_base + PLL_CON3, val);
+
+	/* Set PLL work mode to normal */
+	const uint32_t write_mask = pll->mode_mask << 16;
+	const uint32_t write_val = pll->mode_mask;
+	CRU_WRITE(sc, pll->mode_reg, write_mask | write_val);
+
+	for (retry = 1000; retry > 0; retry--) {
+		if (CRU_READ(sc, pll->con_base + PLL_CON2) & pll->lock_mask)
+			break;
+		delay(1);
+	}
+
+	if (retry == 0)
+		device_printf(sc->sc_dev, "WARNING: %s failed to lock\n",
+		    clk->base.name);
+
+	val = __SHIFTIN(PLL_WORK_MODE_NORMAL, PLL_WORK_MODE) | (PLL_WORK_MODE << 16);
+	CRU_WRITE(sc, pll->con_base + PLL_CON3, val);
+
+	return 0;
+}
+
+#define RK3399_PLL(_id, _name, _parents, _con_base, _mode_reg, _mode_mask, _lock_mask, _rates) \
+        {                                                       \
+                .id = (_id),                                    \
+                .type = RK_CRU_PLL,                             \
+                .base.name = (_name),                           \
+                .base.flags = 0,                                \
+                .u.pll.parents = (_parents),                    \
+                .u.pll.nparents = __arraycount(_parents),       \
+                .u.pll.con_base = (_con_base),                  \
+                .u.pll.mode_reg = (_mode_reg),                  \
+                .u.pll.mode_mask = (_mode_mask),                \
+                .u.pll.lock_mask = (_lock_mask),                \
+                .u.pll.rates = (_rates),                        \
+                .u.pll.nrates = __arraycount(_rates),           \
+                .get_rate = rk3399_cru_pll_get_rate,            \
+                .set_rate = rk3399_cru_pll_set_rate,            \
+                .get_parent = rk_cru_pll_get_parent,            \
+        }
+
+static const char * pll_parents[] = { "xin24m", "xin32k" };
+static const char * mux_pll_src_cpll_gpll_parents[] = { "cpll", "gpll" };
+static const char * mux_pll_src_cpll_gpll_npll_parents[] = { "cpll", "gpll", "npll" };
+static const char * mux_pll_src_cpll_gpll_upll_parents[] = { "cpll", "gpll", "upll" };
+static const char * mux_pll_src_cpll_gpll_npll_ppll_upll_24m_parents[] = { "cpll", "gpll", "npll", "ppll", "upll", "xin24m" };
+static const char * mux_aclk_perilp0_parents[] = { "cpll_aclk_perilp0_src", "gpll_aclk_perilp0_src" };
+static const char * mux_hclk_perilp1_parents[] = { "cpll_hclk_perilp1_src", "gpll_hclk_perilp1_src" };
+static const char * mux_aclk_perihp_parents[] = { "cpll_aclk_perihp_src", "gpll_aclk_perihp_src" };
+static const char * mux_aclk_cci_parents[] = { "cpll_aclk_cci_src", "gpll_aclk_cci_src", "npll_aclk_cci_src", "vpll_aclk_cci_src" };
+static const char * mux_uart0_parents[] = { "clk_uart0_div", "clk_uart0_frac", "xin24m" };
+static const char * mux_uart1_parents[] = { "clk_uart1_div", "clk_uart1_frac", "xin24m" };
+static const char * mux_uart2_parents[] = { "clk_uart2_div", "clk_uart2_frac", "xin24m" };
+static const char * mux_uart3_parents[] = { "clk_uart3_div", "clk_uart3_frac", "xin24m" };
+static const char * mux_rmii_parents[] = { "clk_gmac", "clkin_gmac" };
+static const char * mux_aclk_gmac_parents[] = { "cpll_aclk_gmac_src", "gpll_aclk_gmac_src" };
+
+static struct rk_cru_clk rk3399_cru_clks[] = {
+	RK3399_PLL(RK3399_PLL_APLLL, "lpll", pll_parents,
+		   PLL_CON(0),		/* con_base */
+		   PLL_CON(3),		/* mode_reg */
+		   __BIT(8),		/* mode_mask */
+		   __BIT(31),		/* lock_mask */
+		   pll_rates),
+	RK3399_PLL(RK3399_PLL_APLLB, "bpll", pll_parents,
+		   PLL_CON(8),		/* con_base */
+		   PLL_CON(11),		/* mode_reg */
+		   __BIT(8),		/* mode_mask */
+		   __BIT(31),		/* lock_mask */
+		   pll_rates),
+	RK3399_PLL(RK3399_PLL_DPLL, "dpll", pll_parents,
+		   PLL_CON(16),		/* con_base */
+		   PLL_CON(19),		/* mode_reg */
+		   __BIT(8),		/* mode_mask */
+		   __BIT(31),		/* lock_mask */
+		   pll_norates),
+	RK3399_PLL(RK3399_PLL_CPLL, "cpll", pll_parents,
+		   PLL_CON(24),		/* con_base */
+		   PLL_CON(27),		/* mode_reg */
+		   __BIT(8),		/* mode_mask */
+		   __BIT(31),		/* lock_mask */
+		   pll_rates),
+	RK3399_PLL(RK3399_PLL_GPLL, "gpll", pll_parents,
+		   PLL_CON(32),		/* con_base */
+		   PLL_CON(35),		/* mode_reg */
+		   __BIT(8),		/* mode_mask */
+		   __BIT(31),		/* lock_mask */
+		   pll_rates),
+	RK3399_PLL(RK3399_PLL_NPLL, "npll", pll_parents,
+		   PLL_CON(40),		/* con_base */
+		   PLL_CON(43),		/* mode_reg */
+		   __BIT(8),		/* mode_mask */
+		   __BIT(31),		/* lock_mask */
+		   pll_rates),
+	RK3399_PLL(RK3399_PLL_VPLL, "vpll", pll_parents,
+		   PLL_CON(43),		/* con_base */
+		   PLL_CON(51),		/* mode_reg */
+		   __BIT(8),		/* mode_mask */
+		   __BIT(31),		/* lock_mask */
+		   pll_rates),
+
+	/*
+	 * perilp0
+	 */
+	RK_GATE(0, "gpll_aclk_perilp0_src", "gpll", CLKGATE_CON(7), 0),
+	RK_GATE(0, "cpll_aclk_perilp0_src", "cpll", CLKGATE_CON(7), 1),
+	RK_COMPOSITE(RK3399_ACLK_PERILP0, "aclk_perilp0", mux_aclk_perilp0_parents,
+		     CLKSEL_CON(23),	/* muxdiv_reg */
+		     __BIT(7),		/* mux_mask */
+		     __BITS(4,0),	/* div_mask */
+		     CLKGATE_CON(7),	/* gate_reg */
+		     __BIT(2),		/* gate_mask */
+		     0),
+	RK_COMPOSITE_NOMUX(RK3399_HCLK_PERILP0, "hclk_perilp0", "aclk_perilp0",
+			   CLKSEL_CON(23),	/* div_reg */
+			   __BITS(10,8),	/* div_mask */
+			   CLKGATE_CON(7),	/* gate_reg */
+			   __BIT(3),		/* gate_mask */
+			   0),
+	RK_COMPOSITE_NOMUX(RK3399_PCLK_PERILP0, "pclk_perilp0", "aclk_perilp0",
+			   CLKSEL_CON(23),	/* div_reg */
+			   __BITS(14,12),	/* div_mask */
+			   CLKGATE_CON(7),	/* gate_reg */
+			   __BIT(4),		/* gate_mask */
+			   0),
+
+	/*
+	 * perilp1
+	 */
+	RK_GATE(0, "gpll_hclk_perilp1_src", "gpll", CLKGATE_CON(8), 0),
+	RK_GATE(0, "cpll_hclk_perilp1_src", "cpll", CLKGATE_CON(8), 1),
+	RK_COMPOSITE_NOGATE(RK3399_HCLK_PERILP1, "hclk_perilp1", mux_hclk_perilp1_parents,
+			    CLKSEL_CON(25),	/* muxdiv_reg */
+			    __BITS(10,8),	/* mux_mask */
+			    __BITS(4,0),	/* div_mask */
+			    0),
+	RK_COMPOSITE_NOMUX(RK3399_PCLK_PERILP1, "pclk_perilp1", "hclk_perilp1",
+			   CLKSEL_CON(25),	/* div_reg */
+			   __BITS(10,8),	/* div_mask */
+			   CLKGATE_CON(8),	/* gate_reg */
+			   __BIT(2),		/* gate_mask */
+			   0),
+
+	/*
+	 * perihp
+	 */
+	RK_GATE(0, "gpll_aclk_perihp_src", "gpll", CLKGATE_CON(5), 0),
+	RK_GATE(0, "cpll_aclk_perihp_src", "cpll", CLKGATE_CON(5), 1),
+	RK_COMPOSITE(RK3399_ACLK_PERIHP, "aclk_perihp", mux_aclk_perihp_parents,
+		     CLKSEL_CON(14),	/* muxdiv_reg */
+		     __BIT(7),		/* mux_mask */
+		     __BITS(4,0),	/* div_mask */
+		     CLKGATE_CON(5),	/* gate_reg */
+		     __BIT(2),		/* gate_mask */
+		     0),
+	RK_COMPOSITE_NOMUX(RK3399_HCLK_PERIHP, "hclk_perihp", "aclk_perihp",
+			   CLKSEL_CON(14),	/* div_reg */
+			   __BITS(10,8),	/* div_mask */
+			   CLKGATE_CON(5),	/* gate_reg */
+			   __BIT(3),		/* gate_mask */
+			   0),
+	RK_COMPOSITE_NOMUX(RK3399_PCLK_PERIHP, "pclk_perihp", "aclk_perihp",
+			   CLKSEL_CON(14),	/* div_reg */
+			   __BITS(14,12),	/* div_mask */
+			   CLKGATE_CON(5),	/* gate_reg */
+			   __BIT(4),		/* gate_mask */
+			   0),
+
+	/*
+	 * CCI
+	 */
+	RK_GATE(0, "cpll_aclk_cci_src", "cpll", CLKGATE_CON(2), 0),
+	RK_GATE(0, "gpll_aclk_cci_src", "gpll", CLKGATE_CON(2), 1),
+	RK_GATE(0, "npll_aclk_cci_src", "npll", CLKGATE_CON(2), 2),
+	RK_GATE(0, "vpll_aclk_cci_src", "vpll", CLKGATE_CON(2), 3),
+	RK_COMPOSITE(0, "aclk_cci_pre", mux_aclk_cci_parents,
+		     CLKSEL_CON(5),	/* muxdiv_reg */
+		     __BITS(7,6),	/* mux_mask */
+		     __BITS(4,0),	/* div_mask */
+		     CLKGATE_CON(2),	/* gate_reg */
+		     __BIT(4),		/* gate_mask */
+		     0),
+	RK_GATE(RK3399_ACLK_CCI, "aclk_cci", "aclk_cci_pre", CLKGATE_CON(15), 2),
+
+	/*
+	 * GIC
+	 */
+	RK_COMPOSITE(RK3399_ACLK_GIC_PRE, "aclk_gic_pre", mux_pll_src_cpll_gpll_parents,
+		     CLKSEL_CON(56),	/* muxdiv_reg */
+		     __BIT(15),		/* mux_mask */
+		     __BITS(12,8),	/* div_mask */
+		     CLKGATE_CON(12),	/* gate_reg */
+		     __BIT(12),		/* gate_mask */
+		     0),
+
+	/*
+	 * DDR
+	 */
+	RK_COMPOSITE(RK3399_PCLK_DDR, "pclk_ddr", mux_pll_src_cpll_gpll_parents,
+		     CLKSEL_CON(6),	/* muxdiv_reg */
+		     __BIT(15),		/* mux_mask */
+		     __BITS(12,8),	/* div_mask */
+		     CLKGATE_CON(3),	/* gate_reg */
+		     __BIT(4),		/* gate_mask */
+		     0),
+
+	/*
+	 * alive
+	 */
+	RK_DIV(RK3399_PCLK_ALIVE, "pclk_alive", "gpll", CLKSEL_CON(57), __BITS(4,0), 0),
+
+	/*
+	 * GPIO
+	 */
+	RK_GATE(RK3399_PCLK_GPIO2, "pclk_gpio2", "pclk_alive", CLKGATE_CON(31), 3),
+	RK_GATE(RK3399_PCLK_GPIO3, "pclk_gpio3", "pclk_alive", CLKGATE_CON(31), 4),
+	RK_GATE(RK3399_PCLK_GPIO4, "pclk_gpio4", "pclk_alive", CLKGATE_CON(31), 5),
+
+	/*
+	 * UART
+	 */
+	RK_MUX(0, "clk_uart0_src", mux_pll_src_cpll_gpll_upll_parents, CLKSEL_CON(33), __BITS(13,12)),
+	RK_MUX(0, "clk_uart_src", mux_pll_src_cpll_gpll_parents, CLKSEL_CON(33), __BIT(15)),
+	RK_COMPOSITE_NOMUX(0, "clk_uart0_div", "clk_uart0_src",
+			   CLKSEL_CON(33),	/* div_reg */
+			   __BITS(6,0),		/* div_mask */
+			   CLKGATE_CON(9),	/* gate_reg */
+			   __BIT(0),		/* gate_mask */
+			   0),
+	RK_COMPOSITE_NOMUX(0, "clk_uart1_div", "clk_uart_src",
+			   CLKSEL_CON(34),	/* div_reg */
+			   __BITS(6,0),		/* div_mask */
+			   CLKGATE_CON(9),	/* gate_reg */
+			   __BIT(2),		/* gate_mask */
+			   0),
+	RK_COMPOSITE_NOMUX(0, "clk_uart2_div", "clk_uart_src",
+			   CLKSEL_CON(35),	/* div_reg */
+			   __BITS(6,0),		/* div_mask */
+			   CLKGATE_CON(9),	/* gate_reg */
+			   __BIT(4),		/* gate_mask */
+			   0),
+	RK_COMPOSITE_NOMUX(0, "clk_uart3_div", "clk_uart_src",
+			   CLKSEL_CON(36),	/* div_reg */
+			   __BITS(6,0),		/* div_mask */
+			   CLKGATE_CON(9),	/* gate_reg */
+			   __BIT(6),		/* gate_mask */
+			   0),
+	RK_MUX(RK3399_SCLK_UART0, "clk_uart0", mux_uart0_parents, CLKSEL_CON(33), __BITS(9,8)),
+	RK_MUX(RK3399_SCLK_UART1, "clk_uart1", mux_uart1_parents, CLKSEL_CON(34), __BITS(9,8)),
+	RK_MUX(RK3399_SCLK_UART2, "clk_uart2", mux_uart2_parents, CLKSEL_CON(35), __BITS(9,8)),
+	RK_MUX(RK3399_SCLK_UART3, "clk_uart3", mux_uart3_parents, CLKSEL_CON(36), __BITS(9,8)),
+	RK_GATE(RK3399_PCLK_UART0, "pclk_uart0", "pclk_perilp1", CLKGATE_CON(22), 0),
+	RK_GATE(RK3399_PCLK_UART1, "pclk_uart1", "pclk_perilp1", CLKGATE_CON(22), 1),
+	RK_GATE(RK3399_PCLK_UART2, "pclk_uart2", "pclk_perilp1", CLKGATE_CON(22), 2),
+	RK_GATE(RK3399_PCLK_UART3, "pclk_uart3", "pclk_perilp1", CLKGATE_CON(22), 3),
+
+	/*
+	 * SDMMC/SDIO
+	 */
+	RK_COMPOSITE(RK3399_HCLK_SD, "hclk_sd", mux_pll_src_cpll_gpll_parents,
+		     CLKSEL_CON(13),	/* muxdiv_reg */
+		     __BIT(15),		/* mux_mask */
+		     __BITS(12,8),	/* div_mask */
+		     CLKGATE_CON(12),	/* gate_reg */
+		     __BIT(13),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3399_SCLK_SDIO, "clk_sdio", mux_pll_src_cpll_gpll_npll_ppll_upll_24m_parents,
+		     CLKSEL_CON(15),	/* muxdiv_reg */
+		     __BITS(10,8),	/* mux_mask */
+		     __BITS(6,0),	/* div_mask */
+		     CLKGATE_CON(6),	/* gate_reg */
+		     __BIT(0),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3399_SCLK_SDMMC, "clk_sdmmc", mux_pll_src_cpll_gpll_npll_ppll_upll_24m_parents,
+		     CLKSEL_CON(16),	/* muxdiv_reg */
+		     __BITS(10,8),	/* mux_mask */
+		     __BITS(6,0),	/* div_mask */
+		     CLKGATE_CON(6),	/* gate_reg */
+		     __BIT(1),		/* gate_mask */
+		     0),
+	RK_GATE(RK3399_HCLK_SDMMC, "hclk_sdmmc", "hclk_sd", CLKGATE_CON(33), 8),
+	RK_GATE(RK3399_HCLK_SDIO, "hclk_sdio", "pclk_perilp1", CLKGATE_CON(34), 4),
+
+	/*
+	 * GMAC
+	 */
+	RK_COMPOSITE(RK3399_SCLK_MAC, "clk_gmac", mux_pll_src_cpll_gpll_npll_parents,
+		     CLKSEL_CON(20),	/* muxdiv_reg */
+		     __BITS(15,14),	/* mux_mask */
+		     __BITS(12,8),	/* div_mask */
+		     CLKGATE_CON(5),	/* gate_reg */
+		     __BIT(5),		/* gate_mask */
+		     0),
+	RK_MUX(RK3399_SCLK_RMII_SRC, "clk_rmii_src", mux_rmii_parents, CLKSEL_CON(19), __BIT(4)),
+	RK_GATE(RK3399_SCLK_MACREF_OUT, "clk_mac_refout", "clk_rmii_src", CLKGATE_CON(5), 6),
+	RK_GATE(RK3399_SCLK_MACREF, "clk_mac_ref", "clk_rmii_src", CLKGATE_CON(5), 7),
+	RK_GATE(RK3399_SCLK_MAC_RX, "clk_rmii_rx", "clk_rmii_src", CLKGATE_CON(5), 8),
+	RK_GATE(RK3399_SCLK_MAC_TX, "clk_rmii_tx", "clk_rmii_src", CLKGATE_CON(5), 9),
+	RK_GATE(0, "gpll_aclk_gmac_src", "gpll", CLKGATE_CON(6), 8),
+	RK_GATE(0, "cpll_aclk_gmac_src", "cpll", CLKGATE_CON(6), 9),
+	RK_COMPOSITE(0, "aclk_gmac_pre", mux_aclk_gmac_parents,
+		     CLKSEL_CON(20),	/* muxdiv_reg */
+		     __BIT(17),		/* mux_mask */
+		     __BITS(4,0),	/* div_mask */
+		     CLKGATE_CON(6),	/* gate_reg */
+		     __BIT(10),		/* gate_mask */
+		     0),
+	RK_GATE(RK3399_ACLK_GMAC, "aclk_gmac", "aclk_gmac_pre", CLKGATE_CON(32), 0),
+	RK_COMPOSITE_NOMUX(0, "pclk_gmac_pre", "aclk_gmac_pre",
+			   CLKSEL_CON(19),	/* div_reg */
+			   __BITS(10,8),	/* div_mask */
+			   CLKGATE_CON(6),	/* gate_reg */
+			   __BIT(11),		/* gate_mask */
+			   0),
+	RK_GATE(RK3399_PCLK_GMAC, "pclk_gmac", "pclk_gmac_pre", CLKGATE_CON(32), 2),
+
+	/*
+	 * USB2
+	 */
+	RK_GATE(RK3399_HCLK_HOST0, "hclk_host0", "hclk_perihp", CLKGATE_CON(20), 5),
+	RK_GATE(RK3399_HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_perihp", CLKGATE_CON(20), 6),
+	RK_GATE(RK3399_HCLK_HOST1, "hclk_host1", "hclk_perihp", CLKGATE_CON(20), 7),
+	RK_GATE(RK3399_HCLK_HOST1_ARB, "hclk_host1_arb", "hclk_perihp", CLKGATE_CON(20), 8),
+	RK_GATE(RK3399_SCLK_USB2PHY0_REF, "clk_usb2phy0_ref", "xin24m", CLKGATE_CON(6), 5),
+	RK_GATE(RK3399_SCLK_USB2PHY1_REF, "clk_usb2phy1_ref", "xin24m", CLKGATE_CON(6), 6),
+
+	/*
+	 * USB3
+	 */
+	RK_GATE(RK3399_SCLK_USB3OTG0_REF, "clk_usb3otg0_ref", "xin24m", CLKGATE_CON(12), 1),
+	RK_GATE(RK3399_SCLK_USB3OTG1_REF, "clk_usb3otg1_ref", "xin24m", CLKGATE_CON(12), 2),
+	RK_COMPOSITE(RK3399_SCLK_USB3OTG0_SUSPEND, "clk_usb3otg0_suspend", pll_parents,
+		     CLKSEL_CON(40),	/* muxdiv_reg */
+		     __BIT(15),		/* mux_mask */
+		     __BITS(9,0),	/* div_mask */
+		     CLKGATE_CON(12),	/* gate_reg */
+		     __BIT(3),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3399_SCLK_USB3OTG1_SUSPEND, "clk_usb3otg1_suspend", pll_parents,
+		     CLKSEL_CON(41),	/* muxdiv_reg */
+		     __BIT(15),		/* mux_mask */
+		     __BITS(9,0),	/* div_mask */
+		     CLKGATE_CON(12),	/* gate_reg */
+		     __BIT(4),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3399_ACLK_USB3, "aclk_usb3", mux_pll_src_cpll_gpll_npll_parents,
+		     CLKSEL_CON(39),	/* muxdiv_reg */
+		     __BITS(7,6),	/* mux_mask */
+		     __BITS(4,0),	/* div_mask */
+		     CLKGATE_CON(12),	/* gate_reg */
+		     __BIT(0),		/* gate_mask */
+		     0),
+	RK_GATE(RK3399_ACLK_USB3OTG0, "aclk_usb3otg0", "aclk_usb3", CLKGATE_CON(30), 1),
+	RK_GATE(RK3399_ACLK_USB3OTG1, "aclk_usb3otg1", "aclk_usb3", CLKGATE_CON(30), 2),
+	RK_GATE(RK3399_ACLK_USB3_RKSOC_AXI_PERF, "aclk_usb3_rksoc_axi_perf", "aclk_usb3", CLKGATE_CON(30), 3),
+	RK_GATE(RK3399_ACLK_USB3_GRF, "aclk_usb3_grf", "aclk_usb3", CLKGATE_CON(30), 4),
+};
+
+static int
+rk3399_cru_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct fdt_attach_args * const faa = aux;
+
+	return of_match_compatible(faa->faa_phandle, compatible);
+}
+
+static void
+rk3399_cru_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk_cru_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+
+	sc->sc_dev = self;
+	sc->sc_phandle = faa->faa_phandle;
+	sc->sc_bst = faa->faa_bst;
+
+	sc->sc_clks = rk3399_cru_clks;
+	sc->sc_nclks = __arraycount(rk3399_cru_clks);
+
+	sc->sc_softrst_base = SOFTRST_CON(0);
+
+	if (rk_cru_attach(sc) != 0)
+		return;
+
+	aprint_naive("\n");
+	aprint_normal(": RK3399 CRU\n");
+
+	rk_cru_print(sc);
+}
Index: src/sys/arch/arm/rockchip/rk3399_cru.h
diff -u /dev/null src/sys/arch/arm/rockchip/rk3399_cru.h:1.1
--- /dev/null	Sun Aug 12 16:48:05 2018
+++ src/sys/arch/arm/rockchip/rk3399_cru.h	Sun Aug 12 16:48:05 2018
@@ -0,0 +1,353 @@
+/* $NetBSD: rk3399_cru.h,v 1.1 2018/08/12 16:48:05 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2018 Jared McNeill <jmcne...@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.
+ */
+
+#ifndef _RK3399_CRU_H
+#define _RK3399_CRU_H
+
+/*
+ * Clocks
+ */
+
+#define	RK3399_PLL_APLLL			1
+#define	RK3399_PLL_APLLB			2
+#define	RK3399_PLL_DPLL				3
+#define	RK3399_PLL_CPLL				4
+#define	RK3399_PLL_GPLL				5
+#define	RK3399_PLL_NPLL				6
+#define	RK3399_PLL_VPLL				7
+#define	RK3399_ARMCLKL				8
+#define	RK3399_ARMCLKB				9
+#define	RK3399_SCLK_I2C1			65
+#define	RK3399_SCLK_I2C2			66
+#define	RK3399_SCLK_I2C3			67
+#define	RK3399_SCLK_I2C5			68
+#define	RK3399_SCLK_I2C6			69
+#define	RK3399_SCLK_I2C7			70
+#define	RK3399_SCLK_SPI0			71
+#define	RK3399_SCLK_SPI1			72
+#define	RK3399_SCLK_SPI2			73
+#define	RK3399_SCLK_SPI4			74
+#define	RK3399_SCLK_SPI5			75
+#define	RK3399_SCLK_SDMMC			76
+#define	RK3399_SCLK_SDIO			77
+#define	RK3399_SCLK_EMMC			78
+#define	RK3399_SCLK_TSADC			79
+#define	RK3399_SCLK_SARADC			80
+#define	RK3399_SCLK_UART0			81
+#define	RK3399_SCLK_UART1			82
+#define	RK3399_SCLK_UART2			83
+#define	RK3399_SCLK_UART3			84
+#define	RK3399_SCLK_SPDIF_8CH			85
+#define	RK3399_SCLK_I2S0_8CH			86
+#define	RK3399_SCLK_I2S1_8CH			87
+#define	RK3399_SCLK_I2S2_8CH			88
+#define	RK3399_SCLK_I2S_8CH_OUT			89
+#define	RK3399_SCLK_TIMER00			90
+#define	RK3399_SCLK_TIMER01			91
+#define	RK3399_SCLK_TIMER02			92
+#define	RK3399_SCLK_TIMER03			93
+#define	RK3399_SCLK_TIMER04			94
+#define	RK3399_SCLK_TIMER05			95
+#define	RK3399_SCLK_TIMER06			96
+#define	RK3399_SCLK_TIMER07			97
+#define	RK3399_SCLK_TIMER08			98
+#define	RK3399_SCLK_TIMER09			99
+#define	RK3399_SCLK_TIMER10			100
+#define	RK3399_SCLK_TIMER11			101
+#define	RK3399_SCLK_MACREF			102
+#define	RK3399_SCLK_MAC_RX			103
+#define	RK3399_SCLK_MAC_TX			104
+#define	RK3399_SCLK_MAC				105
+#define	RK3399_SCLK_MACREF_OUT			106
+#define	RK3399_SCLK_VOP0_PWM			107
+#define	RK3399_SCLK_VOP1_PWM			108
+#define	RK3399_SCLK_RGA_CORE			109
+#define	RK3399_SCLK_ISP0			110
+#define	RK3399_SCLK_ISP1			111
+#define	RK3399_SCLK_HDMI_CEC			112
+#define	RK3399_SCLK_HDMI_SFR			113
+#define	RK3399_SCLK_DP_CORE			114
+#define	RK3399_SCLK_PVTM_CORE_L			115
+#define	RK3399_SCLK_PVTM_CORE_B			116
+#define	RK3399_SCLK_PVTM_GPU			117
+#define	RK3399_SCLK_PVTM_DDR			118
+#define	RK3399_SCLK_MIPIDPHY_REF		119
+#define	RK3399_SCLK_MIPIDPHY_CFG		120
+#define	RK3399_SCLK_HSICPHY			121
+#define	RK3399_SCLK_USBPHY480M			122
+#define	RK3399_SCLK_USB2PHY0_REF		123
+#define	RK3399_SCLK_USB2PHY1_REF		124
+#define	RK3399_SCLK_UPHY0_TCPDPHY_REF		125
+#define	RK3399_SCLK_UPHY0_TCPDCORE		126
+#define	RK3399_SCLK_UPHY1_TCPDPHY_REF		127
+#define	RK3399_SCLK_UPHY1_TCPDCORE		128
+#define	RK3399_SCLK_USB3OTG0_REF		129
+#define	RK3399_SCLK_USB3OTG1_REF		130
+#define	RK3399_SCLK_USB3OTG0_SUSPEND		131
+#define	RK3399_SCLK_USB3OTG1_SUSPEND		132
+#define	RK3399_SCLK_CRYPTO0			133
+#define	RK3399_SCLK_CRYPTO1			134
+#define	RK3399_SCLK_CCI_TRACE			135
+#define	RK3399_SCLK_CS				136
+#define	RK3399_SCLK_CIF_OUT			137
+#define	RK3399_SCLK_PCIEPHY_REF			138
+#define	RK3399_SCLK_PCIE_CORE			139
+#define	RK3399_SCLK_M0_PERILP			140
+#define	RK3399_SCLK_M0_PERILP_DEC		141
+#define	RK3399_SCLK_CM0S			142
+#define	RK3399_SCLK_DBG_NOC			143
+#define	RK3399_SCLK_DBG_PD_CORE_B		144
+#define	RK3399_SCLK_DBG_PD_CORE_L		145
+#define	RK3399_SCLK_DFIMON0_TIMER		146
+#define	RK3399_SCLK_DFIMON1_TIMER		147
+#define	RK3399_SCLK_INTMEM0			148
+#define	RK3399_SCLK_INTMEM1			149
+#define	RK3399_SCLK_INTMEM2			150
+#define	RK3399_SCLK_INTMEM3			151
+#define	RK3399_SCLK_INTMEM4			152
+#define	RK3399_SCLK_INTMEM5			153
+#define	RK3399_SCLK_SDMMC_DRV			154
+#define	RK3399_SCLK_SDMMC_SAMPLE		155
+#define	RK3399_SCLK_SDIO_DRV			156
+#define	RK3399_SCLK_SDIO_SAMPLE			157
+#define	RK3399_SCLK_VDU_CORE			158
+#define	RK3399_SCLK_VDU_CA			159
+#define	RK3399_SCLK_PCIE_PM			160
+#define	RK3399_SCLK_SPDIF_REC_DPTX		161
+#define	RK3399_SCLK_DPHY_PLL			162
+#define	RK3399_SCLK_DPHY_TX0_CFG		163
+#define	RK3399_SCLK_DPHY_TX1RX1_CFG		164
+#define	RK3399_SCLK_DPHY_RX0_CFG		165
+#define	RK3399_SCLK_RMII_SRC			166
+#define	RK3399_SCLK_PCIEPHY_REF100M		167
+#define	RK3399_SCLK_DDRC			168
+#define	RK3399_SCLK_TESTCLKOUT1			169
+#define	RK3399_SCLK_TESTCLKOUT2			170
+#define	RK3399_DCLK_VOP0			180
+#define	RK3399_DCLK_VOP1			181
+#define	RK3399_DCLK_VOP0_DIV			182
+#define	RK3399_DCLK_VOP1_DIV			183
+#define	RK3399_DCLK_M0_PERILP			184
+#define	RK3399_DCLK_VOP0_FRAC			185
+#define	RK3399_DCLK_VOP1_FRAC			186
+#define	RK3399_FCLK_CM0S			190
+#define	RK3399_ACLK_PERIHP			192
+#define	RK3399_ACLK_PERIHP_NOC			193
+#define	RK3399_ACLK_PERILP0			194
+#define	RK3399_ACLK_PERILP0_NOC			195
+#define	RK3399_ACLK_PERF_PCIE			196
+#define	RK3399_ACLK_PCIE			197
+#define	RK3399_ACLK_INTMEM			198
+#define	RK3399_ACLK_TZMA			199
+#define	RK3399_ACLK_DCF				200
+#define	RK3399_ACLK_CCI				201
+#define	RK3399_ACLK_CCI_NOC0			202
+#define	RK3399_ACLK_CCI_NOC1			203
+#define	RK3399_ACLK_CCI_GRF			204
+#define	RK3399_ACLK_CENTER			205
+#define	RK3399_ACLK_CENTER_MAIN_NOC		206
+#define	RK3399_ACLK_CENTER_PERI_NOC		207
+#define	RK3399_ACLK_GPU				208
+#define	RK3399_ACLK_PERF_GPU			209
+#define	RK3399_ACLK_GPU_GRF			210
+#define	RK3399_ACLK_DMAC0_PERILP		211
+#define	RK3399_ACLK_DMAC1_PERILP		212
+#define	RK3399_ACLK_GMAC			213
+#define	RK3399_ACLK_GMAC_NOC			214
+#define	RK3399_ACLK_PERF_GMAC			215
+#define	RK3399_ACLK_VOP0_NOC			216
+#define	RK3399_ACLK_VOP0			217
+#define	RK3399_ACLK_VOP1_NOC			218
+#define	RK3399_ACLK_VOP1			219
+#define	RK3399_ACLK_RGA				220
+#define	RK3399_ACLK_RGA_NOC			221
+#define	RK3399_ACLK_HDCP			222
+#define	RK3399_ACLK_HDCP_NOC			223
+#define	RK3399_ACLK_HDCP22			224
+#define	RK3399_ACLK_IEP				225
+#define	RK3399_ACLK_IEP_NOC			226
+#define	RK3399_ACLK_VIO				227
+#define	RK3399_ACLK_VIO_NOC			228
+#define	RK3399_ACLK_ISP0			229
+#define	RK3399_ACLK_ISP1			230
+#define	RK3399_ACLK_ISP0_NOC			231
+#define	RK3399_ACLK_ISP1_NOC			232
+#define	RK3399_ACLK_ISP0_WRAPPER		233
+#define	RK3399_ACLK_ISP1_WRAPPER		234
+#define	RK3399_ACLK_VCODEC			235
+#define	RK3399_ACLK_VCODEC_NOC			236
+#define	RK3399_ACLK_VDU				237
+#define	RK3399_ACLK_VDU_NOC			238
+#define	RK3399_ACLK_PERI			239
+#define	RK3399_ACLK_EMMC			240
+#define	RK3399_ACLK_EMMC_CORE			241
+#define	RK3399_ACLK_EMMC_NOC			242
+#define	RK3399_ACLK_EMMC_GRF			243
+#define	RK3399_ACLK_USB3			244
+#define	RK3399_ACLK_USB3_NOC			245
+#define	RK3399_ACLK_USB3OTG0			246
+#define	RK3399_ACLK_USB3OTG1			247
+#define	RK3399_ACLK_USB3_RKSOC_AXI_PERF		248
+#define	RK3399_ACLK_USB3_GRF			249
+#define	RK3399_ACLK_GIC				250
+#define	RK3399_ACLK_GIC_NOC			251
+#define	RK3399_ACLK_GIC_ADB400_CORE_L_2_GIC	252
+#define	RK3399_ACLK_GIC_ADB400_CORE_B_2_GIC	253
+#define	RK3399_ACLK_GIC_ADB400_GIC_2_CORE_L	254
+#define	RK3399_ACLK_GIC_ADB400_GIC_2_CORE_B	255
+#define	RK3399_ACLK_CORE_ADB400_CORE_L_2_CCI500 256
+#define	RK3399_ACLK_CORE_ADB400_CORE_B_2_CCI500 257
+#define	RK3399_ACLK_ADB400M_PD_CORE_L		258
+#define	RK3399_ACLK_ADB400M_PD_CORE_B		259
+#define	RK3399_ACLK_PERF_CORE_L			260
+#define	RK3399_ACLK_PERF_CORE_B			261
+#define	RK3399_ACLK_GIC_PRE			262
+#define	RK3399_ACLK_VOP0_PRE			263
+#define	RK3399_ACLK_VOP1_PRE			264
+#define	RK3399_PCLK_PERIHP			320
+#define	RK3399_PCLK_PERIHP_NOC			321
+#define	RK3399_PCLK_PERILP0			322
+#define	RK3399_PCLK_PERILP1			323
+#define	RK3399_PCLK_PERILP1_NOC			324
+#define	RK3399_PCLK_PERILP_SGRF			325
+#define	RK3399_PCLK_PERIHP_GRF			326
+#define	RK3399_PCLK_PCIE			327
+#define	RK3399_PCLK_SGRF			328
+#define	RK3399_PCLK_INTR_ARB			329
+#define	RK3399_PCLK_CENTER_MAIN_NOC		330
+#define	RK3399_PCLK_CIC				331
+#define	RK3399_PCLK_COREDBG_B			332
+#define	RK3399_PCLK_COREDBG_L			333
+#define	RK3399_PCLK_DBG_CXCS_PD_CORE_B		334
+#define	RK3399_PCLK_DCF				335
+#define	RK3399_PCLK_GPIO2			336
+#define	RK3399_PCLK_GPIO3			337
+#define	RK3399_PCLK_GPIO4			338
+#define	RK3399_PCLK_GRF				339
+#define	RK3399_PCLK_HSICPHY			340
+#define	RK3399_PCLK_I2C1			341
+#define	RK3399_PCLK_I2C2			342
+#define	RK3399_PCLK_I2C3			343
+#define	RK3399_PCLK_I2C5			344
+#define	RK3399_PCLK_I2C6			345
+#define	RK3399_PCLK_I2C7			346
+#define	RK3399_PCLK_SPI0			347
+#define	RK3399_PCLK_SPI1			348
+#define	RK3399_PCLK_SPI2			349
+#define	RK3399_PCLK_SPI4			350
+#define	RK3399_PCLK_SPI5			351
+#define	RK3399_PCLK_UART0			352
+#define	RK3399_PCLK_UART1			353
+#define	RK3399_PCLK_UART2			354
+#define	RK3399_PCLK_UART3			355
+#define	RK3399_PCLK_TSADC			356
+#define	RK3399_PCLK_SARADC			357
+#define	RK3399_PCLK_GMAC			358
+#define	RK3399_PCLK_GMAC_NOC			359
+#define	RK3399_PCLK_TIMER0			360
+#define	RK3399_PCLK_TIMER1			361
+#define	RK3399_PCLK_EDP				362
+#define	RK3399_PCLK_EDP_NOC			363
+#define	RK3399_PCLK_EDP_CTRL			364
+#define	RK3399_PCLK_VIO				365
+#define	RK3399_PCLK_VIO_NOC			366
+#define	RK3399_PCLK_VIO_GRF			367
+#define	RK3399_PCLK_MIPI_DSI0			368
+#define	RK3399_PCLK_MIPI_DSI1			369
+#define	RK3399_PCLK_HDCP			370
+#define	RK3399_PCLK_HDCP_NOC			371
+#define	RK3399_PCLK_HDMI_CTRL			372
+#define	RK3399_PCLK_DP_CTRL			373
+#define	RK3399_PCLK_HDCP22			374
+#define	RK3399_PCLK_GASKET			375
+#define	RK3399_PCLK_DDR				376
+#define	RK3399_PCLK_DDR_MON			377
+#define	RK3399_PCLK_DDR_SGRF			378
+#define	RK3399_PCLK_ISP1_WRAPPER		379
+#define	RK3399_PCLK_WDT				380
+#define	RK3399_PCLK_EFUSE1024NS			381
+#define	RK3399_PCLK_EFUSE1024S			382
+#define	RK3399_PCLK_PMU_INTR_ARB		383
+#define	RK3399_PCLK_MAILBOX0			384
+#define	RK3399_PCLK_USBPHY_MUX_G		385
+#define	RK3399_PCLK_UPHY0_TCPHY_G		386
+#define	RK3399_PCLK_UPHY0_TCPD_G		387
+#define	RK3399_PCLK_UPHY1_TCPHY_G		388
+#define	RK3399_PCLK_UPHY1_TCPD_G		389
+#define	RK3399_PCLK_ALIVE			390
+#define	RK3399_HCLK_PERIHP			448
+#define	RK3399_HCLK_PERILP0			449
+#define	RK3399_HCLK_PERILP1			450
+#define	RK3399_HCLK_PERILP0_NOC			451
+#define	RK3399_HCLK_PERILP1_NOC			452
+#define	RK3399_HCLK_M0_PERILP			453
+#define	RK3399_HCLK_M0_PERILP_NOC		454
+#define	RK3399_HCLK_AHB1TOM			455
+#define	RK3399_HCLK_HOST0			456
+#define	RK3399_HCLK_HOST0_ARB			457
+#define	RK3399_HCLK_HOST1			458
+#define	RK3399_HCLK_HOST1_ARB			459
+#define	RK3399_HCLK_HSIC			460
+#define	RK3399_HCLK_SD				461
+#define	RK3399_HCLK_SDMMC			462
+#define	RK3399_HCLK_SDMMC_NOC			463
+#define	RK3399_HCLK_M_CRYPTO0			464
+#define	RK3399_HCLK_M_CRYPTO1			465
+#define	RK3399_HCLK_S_CRYPTO0			466
+#define	RK3399_HCLK_S_CRYPTO1			467
+#define	RK3399_HCLK_I2S0_8CH			468
+#define	RK3399_HCLK_I2S1_8CH			469
+#define	RK3399_HCLK_I2S2_8CH			470
+#define	RK3399_HCLK_SPDIF			471
+#define	RK3399_HCLK_VOP0_NOC			472
+#define	RK3399_HCLK_VOP0			473
+#define	RK3399_HCLK_VOP1_NOC			474
+#define	RK3399_HCLK_VOP1			475
+#define	RK3399_HCLK_ROM				476
+#define	RK3399_HCLK_IEP				477
+#define	RK3399_HCLK_IEP_NOC			478
+#define	RK3399_HCLK_ISP0			479
+#define	RK3399_HCLK_ISP1			480
+#define	RK3399_HCLK_ISP0_NOC			481
+#define	RK3399_HCLK_ISP1_NOC			482
+#define	RK3399_HCLK_ISP0_WRAPPER		483
+#define	RK3399_HCLK_ISP1_WRAPPER		484
+#define	RK3399_HCLK_RGA				485
+#define	RK3399_HCLK_RGA_NOC			486
+#define	RK3399_HCLK_HDCP			487
+#define	RK3399_HCLK_HDCP_NOC			488
+#define	RK3399_HCLK_HDCP22			489
+#define	RK3399_HCLK_VCODEC			490
+#define	RK3399_HCLK_VCODEC_NOC			491
+#define	RK3399_HCLK_VDU				492
+#define	RK3399_HCLK_VDU_NOC			493
+#define	RK3399_HCLK_SDIO			494
+#define	RK3399_HCLK_SDIO_NOC			495
+#define	RK3399_HCLK_SDIOAUDIO_NOC		496
+
+#endif /* !_RK3399_CRU_H */
Index: src/sys/arch/arm/rockchip/rk3399_iomux.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk3399_iomux.c:1.1
--- /dev/null	Sun Aug 12 16:48:05 2018
+++ src/sys/arch/arm/rockchip/rk3399_iomux.c	Sun Aug 12 16:48:05 2018
@@ -0,0 +1,464 @@
+/* $NetBSD: rk3399_iomux.c,v 1.1 2018/08/12 16:48:05 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2018 Jared McNeill <jmcne...@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.
+ */
+
+//#define RK3399_IOMUX_DEBUG
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: rk3399_iomux.c,v 1.1 2018/08/12 16:48:05 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/kmem.h>
+#include <sys/gpio.h>
+#include <sys/lwp.h>
+
+#include <dev/fdt/fdtvar.h>
+#include <dev/fdt/syscon.h>
+
+/* PU/PD control */
+#define	 GRF_GPIO_P_CTL(_idx)		(0x3 << (((_idx) & 7) * 2))
+#define	 GRF_GPIO_P_WRITE_EN(_idx)	(0x3 << (((_idx) & 7) * 2 + 16))
+/* Different bias value mapping for GRF and PMU registers */
+#define	  GRF_GPIO_P_CTL_Z		0
+#define	  GRF_GPIO_P_CTL_PULLDOWN	1
+#define	  GRF_GPIO_P_CTL_Z_ALT		2
+#define	  GRF_GPIO_P_CTL_PULLUP		3
+#define	  PMU_GPIO_P_CTL_Z		0
+#define	  PMU_GPIO_P_CTL_PULLUP		1
+#define	  PMU_GPIO_P_CTL_PULLDOWN	2
+#define	  PMU_GPIO_P_CTL_RESERVED	3
+
+/* Drive strength control */
+/* Different drive strength value mapping for GRF and PMU registers */
+#define	  GRF_GPIO_E_CTL_2MA		0
+#define	  GRF_GPIO_E_CTL_4MA		1
+#define	  GRF_GPIO_E_CTL_8MA		2
+#define	  GRF_GPIO_E_CTL_12MA		3
+#define	  PMU_GPIO_E_CTL_5MA		0
+#define	  PMU_GPIO_E_CTL_10MA		1
+#define	  PMU_GPIO_E_CTL_15MA		2
+#define	  PMU_GPIO_E_CTL_20MA		3
+
+enum rk3399_drv_type {
+	RK3399_DRV_TYPE_IO_DEFAULT,
+	RK3399_DRV_TYPE_IO_1V8_3V0,
+	RK3399_DRV_TYPE_IO_1V8,
+	RK3399_DRV_TYPE_IO_1V8_3V0_AUTO,
+	RK3399_DRV_TYPE_IO_3V3,
+};
+
+static int rk3399_drv_strength[5][9] = {
+	[RK3399_DRV_TYPE_IO_DEFAULT] =		{ 2, 4, 8, 12, -1 },
+	[RK3399_DRV_TYPE_IO_1V8_3V0] =		{ 3, 6, 9, 12, -1 },
+	[RK3399_DRV_TYPE_IO_1V8] =		{ 5, 10, 15, 20, -1 },
+	[RK3399_DRV_TYPE_IO_1V8_3V0_AUTO] =	{ 4, 6, 8, 10, 12, 14, 16, 18, -1 },
+	[RK3399_DRV_TYPE_IO_3V3] =		{ 4, 7, 10, 13, 16, 19, 22, 26, -1 },
+};
+
+struct rk3399_iomux {
+	enum rk3399_drv_type	drv_type;
+};
+
+struct rk3399_iomux_bank {
+	struct rk3399_iomux	iomux[5];
+	u_int			regs;
+#define	RK_IOMUX_REGS_GRF	0
+#define	RK_IOMUX_REGS_PMU	1
+};
+
+static const struct rk3399_iomux_bank rk3399_iomux_banks[] = {
+	[0] = {
+		.regs = RK_IOMUX_REGS_PMU,
+		.iomux = {
+			[0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
+			[1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
+			[2] = { .drv_type = RK3399_DRV_TYPE_IO_DEFAULT },
+			[3] = { .drv_type = RK3399_DRV_TYPE_IO_DEFAULT },
+		},
+	},
+	[1] = {
+		.regs = RK_IOMUX_REGS_PMU,
+		.iomux = {
+			[0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+			[1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+			[2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+			[3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+		}
+	},
+	[2] = {
+		.regs = RK_IOMUX_REGS_GRF,
+		.iomux = {
+			[0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+			[1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+			[2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
+			[3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
+		},
+	},
+	[3] = {
+		.regs = RK_IOMUX_REGS_GRF,
+		.iomux = {
+			[0] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
+			[1] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
+			[2] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
+			[3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+		},
+	},
+	[4] = {
+		.regs = RK_IOMUX_REGS_GRF,
+		.iomux = {
+			[0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+			[1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0_AUTO },
+			[2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+			[3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
+		},
+	},
+};
+
+#define	RK3399_IOMUX_BANK_IS_PMU(_bank)	(rk3399_iomux_banks[(_bank)].regs == RK_IOMUX_REGS_PMU)
+
+struct rk3399_iomux_conf {
+	const struct rk3399_iomux_bank *banks;
+	u_int nbanks;
+};
+
+static const struct rk3399_iomux_conf rk3399_iomux_conf = {
+	.banks = rk3399_iomux_banks,
+	.nbanks = __arraycount(rk3399_iomux_banks),
+};
+
+static const struct of_compat_data compat_data[] = {
+	{ "rockchip,rk3399-pinctrl",	(uintptr_t)&rk3399_iomux_conf },
+	{ NULL }
+};
+
+struct rk3399_iomux_softc {
+	device_t sc_dev;
+	struct syscon *sc_syscon[2];
+
+	const struct rk3399_iomux_conf *sc_conf;
+};
+
+#define	LOCK(syscon) 		\
+	syscon_lock(syscon)
+#define	UNLOCK(syscon)		\
+	syscon_unlock(syscon)
+#define	RD4(syscon, reg) 	\
+	syscon_read_4(syscon, (reg))
+#define	WR4(syscon, reg, val) 	\
+	syscon_write_4(syscon, (reg), (val))
+
+static int	rk3399_iomux_match(device_t, cfdata_t, void *);
+static void	rk3399_iomux_attach(device_t, device_t, void *);
+
+CFATTACH_DECL_NEW(rk3399_iomux, sizeof(struct rk3399_iomux_softc),
+	rk3399_iomux_match, rk3399_iomux_attach, NULL, NULL);
+
+static void
+rk3399_iomux_set_bias(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, int flags)
+{
+	const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
+	bus_size_t reg;
+	u_int bias;
+
+	KASSERT(bank < sc->sc_conf->nbanks);
+
+	struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
+	if (RK3399_IOMUX_BANK_IS_PMU(bank)) {
+		reg = 0x00040 + (0x10 * bank);
+	} else {
+		reg = 0x0e040 + (0x10 * (bank - 2));
+	}
+	reg += 0x4 * (idx / 8);
+
+	if (flags == GPIO_PIN_PULLUP) {
+		bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_PULLUP : GRF_GPIO_P_CTL_PULLUP;
+	} else if (flags == GPIO_PIN_PULLDOWN) {
+		bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_PULLDOWN : GRF_GPIO_P_CTL_PULLDOWN;
+	} else {
+		bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_Z : GRF_GPIO_P_CTL_Z;
+	}
+
+	const uint32_t bias_val = __SHIFTIN(bias, GRF_GPIO_P_CTL(idx));
+	const uint32_t bias_mask = GRF_GPIO_P_WRITE_EN(idx);
+
+#ifdef RK3399_IOMUX_DEBUG
+	printf("%s: bank %d idx %d flags %#x: %08x -> ", __func__, bank, idx, flags, RD4(syscon, reg));
+#endif
+	WR4(syscon, reg, bias_val | bias_mask);
+#ifdef RK3399_IOMUX_DEBUG
+	printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
+#endif
+}
+
+static int
+rk3399_iomux_map_drive_strength(struct rk3399_iomux_softc *sc, enum rk3399_drv_type drv_type, u_int val)
+{
+	for (int n = 0; rk3399_drv_strength[drv_type][n] != -1; n++)
+		if (rk3399_drv_strength[drv_type][n] == val)
+			return n;
+	return -1;
+}
+
+static int 
+rk3399_iomux_set_drive_strength(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, u_int val)
+{
+	const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
+	uint32_t drv_mask, drv_val;
+	bus_size_t reg;
+
+	KASSERT(bank < sc->sc_conf->nbanks);
+
+	if (idx >= 32)
+		return EINVAL;
+
+	const int drv = rk3399_iomux_map_drive_strength(sc, banks[bank].iomux[idx / 8].drv_type, val);
+	if (drv == -1)
+		return EINVAL;
+
+	struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
+	switch (bank) {
+	case 0:
+	case 1:
+		reg = 0x00040 + (0x10 * bank) + 0x4 * (idx / 4);
+		drv_mask = 0x3 << ((idx & 7) * 2);
+		break;
+	case 2:
+		reg = 0x0e100 + 0x4 * (idx / 4);
+		drv_mask = 0x3 << ((idx & 7) * 2);
+		break;
+	case 3:
+		switch (idx / 8) {
+		case 0:
+		case 1:
+		case 2:
+			reg = 0x0e110 + 0x8 * (idx / 4);
+			drv_mask = 0x7 << ((idx & 7) * 3);
+			break;
+		case 3:
+			reg = 0x0e128;
+			drv_mask = 0x3 << ((idx & 7) * 2);
+			break;
+		default:
+			return EINVAL;
+		}
+		break;
+	case 4:
+		switch (idx / 8) {
+		case 0:
+			reg = 0x0e12c;
+			drv_mask = 0x3 << ((idx & 7) * 2);
+			break;
+		case 1:
+			reg = 0x0e130;
+			drv_mask = 0x7 << ((idx & 7) * 3);
+			break;
+		case 2:
+			reg = 0x0e138;
+			drv_mask = 0x3 << ((idx & 7) * 2);
+			break;
+		case 3:
+			reg = 0x0e13c;
+			drv_mask = 0x3 << ((idx & 7) * 2);
+			break;
+		default:
+			return EINVAL;
+		}	
+		break;
+	default:
+		return EINVAL;
+	}
+	drv_val = __SHIFTIN(val, drv_mask);
+
+	while (drv_mask) {
+		const uint32_t write_val = drv_val & 0xffff;
+		const uint32_t write_mask = (drv_mask & 0xffff) << 16;
+		if (write_mask) {
+#ifdef RK3399_IOMUX_DEBUG
+			printf("%s: bank %d idx %d val %d: %08x -> ", __func__, bank, idx, val, RD4(syscon, reg));
+#endif
+			WR4(syscon, reg, write_val | write_mask);
+#ifdef RK3399_IOMUX_DEBUG
+			printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
+#endif
+		}
+		reg += 0x4;
+		drv_val >>= 16;
+		drv_mask >>= 16;
+	}
+
+	return 0;
+}
+
+static void
+rk3399_iomux_set_mux(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, u_int mux)
+{
+	const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
+	bus_size_t reg;
+	uint32_t mask;
+
+	KASSERT(bank < sc->sc_conf->nbanks);
+
+	struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
+	if (RK3399_IOMUX_BANK_IS_PMU(bank)) {
+		reg = 0x00000 + (0x10 * bank);
+	} else {
+		reg = 0x0e000 + (0x10 * (bank - 2));
+	}
+	reg += 0x4 * (idx / 4);
+	mask = 3 << ((idx & 7) * 2);
+
+#ifdef RK3399_IOMUX_DEBUG
+	printf("%s: bank %d idx %d mux %#x: %08x -> ", __func__, bank, idx, mux, RD4(syscon, reg));
+#endif
+	WR4(syscon, reg, (mask << 16) | __SHIFTIN(mux, mask));
+#ifdef RK3399_IOMUX_DEBUG
+	printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
+#endif
+}
+
+static int
+rk3399_iomux_config(struct rk3399_iomux_softc *sc, const int phandle, u_int bank, u_int idx, u_int mux)
+{
+	u_int drv;
+
+	if (of_hasprop(phandle, "bias-disable"))
+		rk3399_iomux_set_bias(sc, bank, idx, 0);
+	else if (of_hasprop(phandle, "bias-pull-up"))
+		rk3399_iomux_set_bias(sc, bank, idx, GPIO_PIN_PULLUP);
+	else if (of_hasprop(phandle, "bias-pull-down"))
+		rk3399_iomux_set_bias(sc, bank, idx, GPIO_PIN_PULLDOWN);
+
+	if (of_getprop_uint32(phandle, "drive-strength", &drv) == 0) {
+		if (rk3399_iomux_set_drive_strength(sc, bank, idx, drv) != 0)
+			return EINVAL;
+	}
+
+#if notyet
+	if (of_hasprop(phandle, "input-enable"))
+		rk3399_iomux_set_direction(sc, bank, idx, GPIO_PIN_INPUT, -1);
+	else if (of_hasprop(phandle, "output-high"))
+		rk3399_iomux_set_direction(sc, bank, idx, GPIO_PIN_OUTPUT, GPIO_PIN_HIGH);
+	else if (of_hasprop(phandle, "output-low"))
+		rk3399_iomux_set_direction(sc, bank, idx, GPIO_PIN_OUTPUT, GPIO_PIN_LOW);
+#endif
+
+	rk3399_iomux_set_mux(sc, bank, idx, mux);
+
+	return 0;
+}
+
+static int
+rk3399_iomux_pinctrl_set_config(device_t dev, const void *data, size_t len)
+{
+	struct rk3399_iomux_softc * const sc = device_private(dev);
+	const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
+	int pins_len;
+
+	if (len != 4)
+		return -1;
+
+	const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
+	const u_int *pins = fdtbus_get_prop(phandle, "rockchip,pins", &pins_len);
+
+	while (pins_len >= 16) {
+		const u_int bank = be32toh(pins[0]);
+		const u_int idx = be32toh(pins[1]);
+		const u_int mux = be32toh(pins[2]);
+		const int cfg = fdtbus_get_phandle_from_native(be32toh(pins[3]));
+
+		struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
+		LOCK(syscon);
+		rk3399_iomux_config(sc, cfg, bank, idx, mux);
+		UNLOCK(syscon);
+
+		pins_len -= 16;
+		pins += 4;
+	}
+
+	return 0;
+}
+
+static struct fdtbus_pinctrl_controller_func rk3399_iomux_pinctrl_funcs = {
+	.set_config = rk3399_iomux_pinctrl_set_config,
+};
+
+static int
+rk3399_iomux_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct fdt_attach_args * const faa = aux;
+
+	return of_match_compat_data(faa->faa_phandle, compat_data);
+}
+
+static void
+rk3399_iomux_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk3399_iomux_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	int child, sub;
+
+	sc->sc_dev = self;
+	sc->sc_syscon[RK_IOMUX_REGS_GRF] = fdtbus_syscon_acquire(phandle, "rockchip,grf");
+	if (sc->sc_syscon[RK_IOMUX_REGS_GRF] == NULL) {
+		aprint_error(": couldn't acquire grf syscon\n");
+		return;
+	}
+	sc->sc_syscon[RK_IOMUX_REGS_PMU] = fdtbus_syscon_acquire(phandle, "rockchip,pmu");
+	if (sc->sc_syscon[RK_IOMUX_REGS_PMU] == NULL) {
+		aprint_error(": couldn't acquire pmu syscon\n");
+		return;
+	}
+	sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data;
+
+	aprint_naive("\n");
+	aprint_normal(": RK3399 IOMUX control\n");
+
+	for (child = OF_child(phandle); child; child = OF_peer(child)) {
+		for (sub = OF_child(child); sub; sub = OF_peer(sub)) {
+			if (!of_hasprop(sub, "rockchip,pins"))
+				continue;
+			fdtbus_register_pinctrl_config(self, sub, &rk3399_iomux_pinctrl_funcs);
+		}
+	}
+
+	fdtbus_pinctrl_configure();
+
+	for (child = OF_child(phandle); child; child = OF_peer(child)) {
+		struct fdt_attach_args cfaa = *faa;
+		cfaa.faa_phandle = child;
+		cfaa.faa_name = fdtbus_get_string(child, "name");
+		cfaa.faa_quiet = false;
+
+		config_found(self, &cfaa, NULL);
+	}
+}
Index: src/sys/arch/arm/rockchip/rk3399_platform.h
diff -u /dev/null src/sys/arch/arm/rockchip/rk3399_platform.h:1.1
--- /dev/null	Sun Aug 12 16:48:05 2018
+++ src/sys/arch/arm/rockchip/rk3399_platform.h	Sun Aug 12 16:48:05 2018
@@ -0,0 +1,40 @@
+/* $NetBSD: rk3399_platform.h,v 1.1 2018/08/12 16:48:05 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2018 Jared McNeill <jmcne...@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.
+ */
+
+#ifndef _ARM_RK3399_PLATFORM_H
+#define _ARM_RK3399_PLATFORM_H
+
+#include <arch/evbarm/fdt/platform.h>
+
+#define	RK3399_CORE_VBASE	KERNEL_IO_VBASE
+#define	RK3399_CORE_PBASE	0xf8000000
+#define	RK3399_CORE_SIZE	0x08000000
+
+#define	RK3399_UART_FREQ	24000000
+
+#endif /* _ARM_RK3399_PLATFORM_H */
Index: src/sys/arch/arm/rockchip/rk3399_pmucru.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk3399_pmucru.c:1.1
--- /dev/null	Sun Aug 12 16:48:05 2018
+++ src/sys/arch/arm/rockchip/rk3399_pmucru.c	Sun Aug 12 16:48:05 2018
@@ -0,0 +1,333 @@
+/* $NetBSD: rk3399_pmucru.c,v 1.1 2018/08/12 16:48:05 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2018 Jared McNeill <jmcne...@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.
+ */
+
+#include <sys/cdefs.h>
+
+__KERNEL_RCSID(1, "$NetBSD: rk3399_pmucru.c,v 1.1 2018/08/12 16:48:05 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#include <arm/rockchip/rk_cru.h>
+#include <arm/rockchip/rk3399_pmucru.h>
+
+#define	PLL_CON(n)	(0x0000 + (n) * 4)
+#define	CLKSEL_CON(n)	(0x0080 + (n) * 4)
+#define	CLKGATE_CON(n)	(0x0100 + (n) * 4)
+#define	SOFTRST_CON(n)	(0x0110 + (n) * 4)
+
+static int rk3399_pmucru_match(device_t, cfdata_t, void *);
+static void rk3399_pmucru_attach(device_t, device_t, void *);
+
+static const char * const compatible[] = {
+	"rockchip,rk3399-pmucru",
+	NULL
+};
+
+CFATTACH_DECL_NEW(rk3399_pmucru, sizeof(struct rk_cru_softc),
+	rk3399_pmucru_match, rk3399_pmucru_attach, NULL, NULL);
+
+static const struct rk_cru_pll_rate pll_rates[] = {
+	RK_PLL_RATE(2208000000,  1,  92, 1, 1, 1, 0),
+	RK_PLL_RATE(2184000000,  1,  91, 1, 1, 1, 0),
+	RK_PLL_RATE(2160000000,  1,  90, 1, 1, 1, 0),
+	RK_PLL_RATE(2136000000,  1,  89, 1, 1, 1, 0),
+	RK_PLL_RATE(2112000000,  1,  88, 1, 1, 1, 0),
+	RK_PLL_RATE(2088000000,  1,  87, 1, 1, 1, 0),
+	RK_PLL_RATE(2064000000,  1,  86, 1, 1, 1, 0),
+	RK_PLL_RATE(2040000000,  1,  85, 1, 1, 1, 0),
+	RK_PLL_RATE(2016000000,  1,  84, 1, 1, 1, 0),
+	RK_PLL_RATE(1992000000,  1,  83, 1, 1, 1, 0),
+	RK_PLL_RATE(1968000000,  1,  82, 1, 1, 1, 0),
+	RK_PLL_RATE(1944000000,  1,  81, 1, 1, 1, 0),
+	RK_PLL_RATE(1920000000,  1,  80, 1, 1, 1, 0),
+	RK_PLL_RATE(1896000000,  1,  79, 1, 1, 1, 0),
+	RK_PLL_RATE(1872000000,  1,  78, 1, 1, 1, 0),
+	RK_PLL_RATE(1848000000,  1,  77, 1, 1, 1, 0),
+	RK_PLL_RATE(1824000000,  1,  76, 1, 1, 1, 0),
+	RK_PLL_RATE(1800000000,  1,  75, 1, 1, 1, 0),
+	RK_PLL_RATE(1776000000,  1,  74, 1, 1, 1, 0),
+	RK_PLL_RATE(1752000000,  1,  73, 1, 1, 1, 0),
+	RK_PLL_RATE(1728000000,  1,  72, 1, 1, 1, 0),
+	RK_PLL_RATE(1704000000,  1,  71, 1, 1, 1, 0),
+	RK_PLL_RATE(1680000000,  1,  70, 1, 1, 1, 0),
+	RK_PLL_RATE(1656000000,  1,  69, 1, 1, 1, 0),
+	RK_PLL_RATE(1632000000,  1,  68, 1, 1, 1, 0),
+	RK_PLL_RATE(1608000000,  1,  67, 1, 1, 1, 0),
+	RK_PLL_RATE(1600000000,  3, 200, 1, 1, 1, 0),
+	RK_PLL_RATE(1584000000,  1,  66, 1, 1, 1, 0),
+	RK_PLL_RATE(1560000000,  1,  65, 1, 1, 1, 0),
+	RK_PLL_RATE(1536000000,  1,  64, 1, 1, 1, 0),
+	RK_PLL_RATE(1512000000,  1,  63, 1, 1, 1, 0),
+	RK_PLL_RATE(1488000000,  1,  62, 1, 1, 1, 0),
+	RK_PLL_RATE(1464000000,  1,  61, 1, 1, 1, 0),
+	RK_PLL_RATE(1440000000,  1,  60, 1, 1, 1, 0),
+	RK_PLL_RATE(1416000000,  1,  59, 1, 1, 1, 0),
+	RK_PLL_RATE(1392000000,  1,  58, 1, 1, 1, 0),
+	RK_PLL_RATE(1368000000,  1,  57, 1, 1, 1, 0),
+	RK_PLL_RATE(1344000000,  1,  56, 1, 1, 1, 0),
+	RK_PLL_RATE(1320000000,  1,  55, 1, 1, 1, 0),
+	RK_PLL_RATE(1296000000,  1,  54, 1, 1, 1, 0),
+	RK_PLL_RATE(1272000000,  1,  53, 1, 1, 1, 0),
+	RK_PLL_RATE(1248000000,  1,  52, 1, 1, 1, 0),
+	RK_PLL_RATE(1200000000,  1,  50, 1, 1, 1, 0),
+	RK_PLL_RATE(1188000000,  2,  99, 1, 1, 1, 0),
+	RK_PLL_RATE(1104000000,  1,  46, 1, 1, 1, 0),
+	RK_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0),
+	RK_PLL_RATE(1008000000,  1,  84, 2, 1, 1, 0),
+	RK_PLL_RATE(1000000000,  1, 125, 3, 1, 1, 0),
+	RK_PLL_RATE( 984000000,  1,  82, 2, 1, 1, 0),
+	RK_PLL_RATE( 960000000,  1,  80, 2, 1, 1, 0),
+	RK_PLL_RATE( 936000000,  1,  78, 2, 1, 1, 0),
+	RK_PLL_RATE( 912000000,  1,  76, 2, 1, 1, 0),
+	RK_PLL_RATE( 900000000,  4, 300, 2, 1, 1, 0),
+	RK_PLL_RATE( 888000000,  1,  74, 2, 1, 1, 0),
+	RK_PLL_RATE( 864000000,  1,  72, 2, 1, 1, 0),
+	RK_PLL_RATE( 840000000,  1,  70, 2, 1, 1, 0),
+	RK_PLL_RATE( 816000000,  1,  68, 2, 1, 1, 0),
+	RK_PLL_RATE( 800000000,  1, 100, 3, 1, 1, 0),
+	RK_PLL_RATE( 700000000,  6, 350, 2, 1, 1, 0),
+	RK_PLL_RATE( 696000000,  1,  58, 2, 1, 1, 0),
+	RK_PLL_RATE( 676000000,  3, 169, 2, 1, 1, 0),
+	RK_PLL_RATE( 600000000,  1,  75, 3, 1, 1, 0),
+	RK_PLL_RATE( 594000000,  1,  99, 4, 1, 1, 0),
+	RK_PLL_RATE( 533250000,  8, 711, 4, 1, 1, 0),
+	RK_PLL_RATE( 504000000,  1,  63, 3, 1, 1, 0),
+	RK_PLL_RATE( 500000000,  6, 250, 2, 1, 1, 0),
+	RK_PLL_RATE( 408000000,  1,  68, 2, 2, 1, 0),
+	RK_PLL_RATE( 312000000,  1,  52, 2, 2, 1, 0),
+	RK_PLL_RATE( 297000000,  1,  99, 4, 2, 1, 0),
+	RK_PLL_RATE( 216000000,  1,  72, 4, 2, 1, 0),
+	RK_PLL_RATE( 148500000,  1,  99, 4, 4, 1, 0),
+	RK_PLL_RATE( 106500000,  1,  71, 4, 4, 1, 0),
+	RK_PLL_RATE(  96000000,  1,  64, 4, 4, 1, 0),
+	RK_PLL_RATE(  74250000,  2,  99, 4, 4, 1, 0),
+	RK_PLL_RATE(  65000000,  1,  65, 6, 4, 1, 0),
+	RK_PLL_RATE(  54000000,  1,  54, 6, 4, 1, 0),
+	RK_PLL_RATE(  27000000,  1,  27, 6, 4, 1, 0),
+};
+
+#define	PLL_CON0	0x00
+#define	 PLL_FBDIV	__BITS(11,0)
+
+#define	PLL_CON1	0x04
+#define	 PLL_POSTDIV2	__BITS(14,12)
+#define	 PLL_POSTDIV1	__BITS(10,8)
+#define	 PLL_REFDIV	__BITS(5,0)
+
+#define	PLL_CON2	0x08
+#define	 PLL_LOCK	__BIT(31)
+#define	 PLL_FRACDIV	__BITS(23,0)
+
+#define	PLL_CON3	0x0c
+#define	 PLL_WORK_MODE	__BITS(9,8)
+#define	  PLL_WORK_MODE_SLOW		0
+#define	  PLL_WORK_MODE_NORMAL		1
+#define	  PLL_WORK_MODE_DEEP_SLOW	2
+#define	 PLL_DSMPD	__BIT(3)
+
+#define	PLL_WRITE_MASK	0xffff0000
+
+static u_int
+rk3399_pmucru_pll_get_rate(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_pll *pll = &clk->u.pll;
+	struct clk *clkp, *clkp_parent;
+	u_int foutvco, foutpostdiv;
+
+	KASSERT(clk->type == RK_CRU_PLL);
+
+	clkp = &clk->base;
+	clkp_parent = clk_get_parent(clkp);
+	if (clkp_parent == NULL)
+		return 0;
+
+	const u_int fref = clk_get_rate(clkp_parent);
+	if (fref == 0)
+		return 0;
+
+	const uint32_t con0 = CRU_READ(sc, pll->con_base + PLL_CON0);
+	const uint32_t con1 = CRU_READ(sc, pll->con_base + PLL_CON1);
+	const uint32_t con2 = CRU_READ(sc, pll->con_base + PLL_CON2);
+	const uint32_t con3 = CRU_READ(sc, pll->con_base + PLL_CON3);
+
+	const u_int fbdiv = __SHIFTOUT(con0, PLL_FBDIV);
+	const u_int postdiv2 = __SHIFTOUT(con1, PLL_POSTDIV2);
+	const u_int postdiv1 = __SHIFTOUT(con1, PLL_POSTDIV1);
+	const u_int refdiv = __SHIFTOUT(con1, PLL_REFDIV);
+	const u_int fracdiv = __SHIFTOUT(con2, PLL_FRACDIV);
+	const u_int dsmpd = __SHIFTOUT(con3, PLL_DSMPD);
+
+	if (dsmpd == 1) {
+		/* integer mode */
+		foutvco = fref / refdiv * fbdiv;
+	} else {
+		/* fractional mode */
+		foutvco = fref / refdiv * fbdiv + ((fref * fracdiv) >> 24);
+	}
+	foutpostdiv = foutvco / postdiv1 / postdiv2;
+
+	return foutpostdiv;
+}
+
+static int
+rk3399_pmucru_pll_set_rate(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk, u_int rate)
+{
+	struct rk_cru_pll *pll = &clk->u.pll;
+	const struct rk_cru_pll_rate *pll_rate = NULL;
+	uint32_t val;
+	int retry;
+
+	KASSERT(clk->type == RK_CRU_PLL);
+
+	if (pll->rates == NULL || rate == 0)
+		return EIO;
+
+	for (int i = 0; i < pll->nrates; i++)
+		if (pll->rates[i].rate == rate) {
+			pll_rate = &pll->rates[i];
+			break;
+		}
+	if (pll_rate == NULL)
+		return EINVAL;
+
+	val = __SHIFTIN(PLL_WORK_MODE_SLOW, PLL_WORK_MODE) | (PLL_WORK_MODE << 16);
+	CRU_WRITE(sc, pll->con_base + PLL_CON3, val);
+
+	CRU_WRITE(sc, pll->con_base + PLL_CON0,
+	    __SHIFTIN(pll_rate->fbdiv, PLL_FBDIV) |
+	    PLL_WRITE_MASK);
+
+	CRU_WRITE(sc, pll->con_base + PLL_CON1,
+	    __SHIFTIN(pll_rate->postdiv2, PLL_POSTDIV2) |
+	    __SHIFTIN(pll_rate->postdiv1, PLL_POSTDIV1) |
+	    __SHIFTIN(pll_rate->refdiv, PLL_REFDIV) |
+	    PLL_WRITE_MASK);
+
+	val = CRU_READ(sc, pll->con_base + PLL_CON2);
+	val &= ~PLL_FRACDIV;
+	val |= __SHIFTIN(pll_rate->fracdiv, PLL_FRACDIV);
+	CRU_WRITE(sc, pll->con_base + PLL_CON2, val);
+
+	val = __SHIFTIN(pll_rate->dsmpd, PLL_DSMPD) | (PLL_DSMPD << 16);
+	CRU_WRITE(sc, pll->con_base + PLL_CON3, val);
+
+	/* Set PLL work mode to normal */
+	const uint32_t write_mask = pll->mode_mask << 16;
+	const uint32_t write_val = pll->mode_mask;
+	CRU_WRITE(sc, pll->mode_reg, write_mask | write_val);
+
+	for (retry = 1000; retry > 0; retry--) {
+		if (CRU_READ(sc, pll->con_base + PLL_CON2) & pll->lock_mask)
+			break;
+		delay(1);
+	}
+
+	if (retry == 0)
+		device_printf(sc->sc_dev, "WARNING: %s failed to lock\n",
+		    clk->base.name);
+
+	val = __SHIFTIN(PLL_WORK_MODE_NORMAL, PLL_WORK_MODE) | (PLL_WORK_MODE << 16);
+	CRU_WRITE(sc, pll->con_base + PLL_CON3, val);
+
+	return 0;
+}
+
+#define RK3399_PLL(_id, _name, _parents, _con_base, _mode_reg, _mode_mask, _lock_mask, _rates) \
+        {                                                       \
+                .id = (_id),                                    \
+                .type = RK_CRU_PLL,                             \
+                .base.name = (_name),                           \
+                .base.flags = 0,                                \
+                .u.pll.parents = (_parents),                    \
+                .u.pll.nparents = __arraycount(_parents),       \
+                .u.pll.con_base = (_con_base),                  \
+                .u.pll.mode_reg = (_mode_reg),                  \
+                .u.pll.mode_mask = (_mode_mask),                \
+                .u.pll.lock_mask = (_lock_mask),                \
+                .u.pll.rates = (_rates),                        \
+                .u.pll.nrates = __arraycount(_rates),           \
+                .get_rate = rk3399_pmucru_pll_get_rate,            \
+                .set_rate = rk3399_pmucru_pll_set_rate,            \
+                .get_parent = rk_cru_pll_get_parent,            \
+        }
+
+static const char * pll_parents[] = { "xin24m", "xin32k" };
+
+static struct rk_cru_clk rk3399_pmucru_clks[] = {
+	RK3399_PLL(RK3399_PLL_PPLL, "ppll", pll_parents,
+		   PLL_CON(0),		/* con_base */
+		   PLL_CON(3),		/* mode_reg */
+		   __BIT(8),		/* mode_mask */
+		   __BIT(31),		/* lock_mask */
+		   pll_rates),
+
+	RK_DIV(RK3399_PCLK_SRC_PMU, "pclk_pmu_src", "ppll", CLKSEL_CON(0), __BITS(4,0), 0),
+
+	RK_GATE(RK3399_PCLK_PMU, "pclk_pmu", "pclk_pmu_src", CLKGATE_CON(1), 0),
+	RK_GATE(RK3399_PCLK_GPIO0_PMU, "pclk_gpio0_pmu", "pclk_pmu_src", CLKGATE_CON(1), 3),
+	RK_GATE(RK3399_PCLK_GPIO1_PMU, "pclk_gpio1_pmu", "pclk_pmu_src", CLKGATE_CON(1), 4),
+};
+
+static int
+rk3399_pmucru_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct fdt_attach_args * const faa = aux;
+
+	return of_match_compatible(faa->faa_phandle, compatible);
+}
+
+static void
+rk3399_pmucru_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk_cru_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+
+	sc->sc_dev = self;
+	sc->sc_phandle = faa->faa_phandle;
+	sc->sc_bst = faa->faa_bst;
+
+	sc->sc_clks = rk3399_pmucru_clks;
+	sc->sc_nclks = __arraycount(rk3399_pmucru_clks);
+
+	sc->sc_softrst_base = SOFTRST_CON(0);
+
+	if (rk_cru_attach(sc) != 0)
+		return;
+
+	aprint_naive("\n");
+	aprint_normal(": RK3399 PMU CRU\n");
+
+	rk_cru_print(sc);
+}
Index: src/sys/arch/arm/rockchip/rk3399_pmucru.h
diff -u /dev/null src/sys/arch/arm/rockchip/rk3399_pmucru.h:1.1
--- /dev/null	Sun Aug 12 16:48:05 2018
+++ src/sys/arch/arm/rockchip/rk3399_pmucru.h	Sun Aug 12 16:48:05 2018
@@ -0,0 +1,72 @@
+/* $NetBSD: rk3399_pmucru.h,v 1.1 2018/08/12 16:48:05 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2018 Jared McNeill <jmcne...@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.
+ */
+
+#ifndef _RK3399_PMUCRU_H
+#define	_RK3399_PMUCRU_H
+
+/*
+ * Clocks
+ */
+
+#define	RK3399_PLL_PPLL			1
+#define	RK3399_SCLK_32K_SUSPEND_PMU	2
+#define	RK3399_SCLK_SPI3_PMU		3
+#define	RK3399_SCLK_TIMER12_PMU		4
+#define	RK3399_SCLK_TIMER13_PMU		5
+#define	RK3399_SCLK_UART4_PMU		6
+#define	RK3399_SCLK_PVTM_PMU		7
+#define	RK3399_SCLK_WIFI_PMU		8
+#define	RK3399_SCLK_I2C0_PMU		9
+#define	RK3399_SCLK_I2C4_PMU		10
+#define	RK3399_SCLK_I2C8_PMU		11
+#define	RK3399_PCLK_SRC_PMU		19
+#define	RK3399_PCLK_PMU			20
+#define	RK3399_PCLK_PMUGRF_PMU		21
+#define	RK3399_PCLK_INTMEM1_PMU		22
+#define	RK3399_PCLK_GPIO0_PMU		23
+#define	RK3399_PCLK_GPIO1_PMU		24
+#define	RK3399_PCLK_SGRF_PMU		25
+#define	RK3399_PCLK_NOC_PMU		26
+#define	RK3399_PCLK_I2C0_PMU		27
+#define	RK3399_PCLK_I2C4_PMU		28
+#define	RK3399_PCLK_I2C8_PMU		29
+#define	RK3399_PCLK_RKPWM_PMU		30
+#define	RK3399_PCLK_SPI3_PMU		31
+#define	RK3399_PCLK_TIMER_PMU		32
+#define	RK3399_PCLK_MAILBOX_PMU		33
+#define	RK3399_PCLK_UART4_PMU		34
+#define	RK3399_PCLK_WDT_M0_PMU		35
+#define	RK3399_FCLK_CM0S_SRC_PMU	44
+#define	RK3399_FCLK_CM0S_PMU		45
+#define	RK3399_SCLK_CM0S_PMU		46
+#define	RK3399_HCLK_CM0S_PMU		47
+#define	RK3399_DCLK_CM0S_PMU		48
+#define	RK3399_PCLK_INTR_ARB_PMU	49
+#define	RK3399_HCLK_NOC_PMU		50
+
+#endif /* !_RK3399_PMUCRU_H */

Reply via email to