Module Name:    src
Committed By:   jmcneill
Date:           Sat Jun 16 00:19:04 UTC 2018

Modified Files:
        src/sys/arch/evbarm/conf: GENERIC64 files.generic64
        src/sys/dev/fdt: files.fdt
Added Files:
        src/sys/arch/arm/rockchip: files.rockchip rk3328_cru.c rk3328_cru.h
            rk3328_platform.h rk_cru.c rk_cru.h rk_cru_arm.c rk_cru_composite.c
            rk_cru_gate.c rk_cru_mux.c rk_cru_pll.c rk_gmac.c rk_gpio.c
            rk_iomux.c rk_platform.c rk_usb.c
        src/sys/dev/fdt: dwc2_fdt.c dwcmmc_fdt.c

Log Message:
Add initial support for Rockchip RK3328 SoC.


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.13 src/sys/arch/arm/rockchip/files.rockchip
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/rockchip/rk3328_cru.c \
    src/sys/arch/arm/rockchip/rk3328_cru.h \
    src/sys/arch/arm/rockchip/rk3328_platform.h \
    src/sys/arch/arm/rockchip/rk_cru.c src/sys/arch/arm/rockchip/rk_cru.h \
    src/sys/arch/arm/rockchip/rk_cru_arm.c \
    src/sys/arch/arm/rockchip/rk_cru_composite.c \
    src/sys/arch/arm/rockchip/rk_cru_gate.c \
    src/sys/arch/arm/rockchip/rk_cru_mux.c \
    src/sys/arch/arm/rockchip/rk_cru_pll.c \
    src/sys/arch/arm/rockchip/rk_gmac.c src/sys/arch/arm/rockchip/rk_gpio.c \
    src/sys/arch/arm/rockchip/rk_iomux.c \
    src/sys/arch/arm/rockchip/rk_platform.c \
    src/sys/arch/arm/rockchip/rk_usb.c
cvs rdiff -u -r1.16 -r1.17 src/sys/arch/evbarm/conf/GENERIC64
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/evbarm/conf/files.generic64
cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/dwc2_fdt.c \
    src/sys/dev/fdt/dwcmmc_fdt.c
cvs rdiff -u -r1.31 -r1.32 src/sys/dev/fdt/files.fdt

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/evbarm/conf/GENERIC64
diff -u src/sys/arch/evbarm/conf/GENERIC64:1.16 src/sys/arch/evbarm/conf/GENERIC64:1.17
--- src/sys/arch/evbarm/conf/GENERIC64:1.16	Thu Jun 14 10:56:39 2018
+++ src/sys/arch/evbarm/conf/GENERIC64	Sat Jun 16 00:19:04 2018
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: GENERIC64,v 1.16 2018/06/14 10:56:39 jmcneill Exp $
+#	$NetBSD: GENERIC64,v 1.17 2018/06/16 00:19:04 jmcneill Exp $
 #
 #	GENERIC ARM (aarch64) kernel
 #
@@ -14,10 +14,14 @@ include	"arch/evbarm/conf/GENERIC.common
 #
 makeoptions 	DTSARCH="arm aarch64"
 makeoptions	DTSGNUARCH="arm arm64"
-makeoptions	DTSSUBDIR="allwinner broadcom nvidia"
+makeoptions	DTSSUBDIR="allwinner broadcom nvidia rockchip"
 makeoptions	DTS="
 	bcm2837-rpi-3-b.dts
 
+	rk3328-evb.dts
+	rk3328-roc-cc.dts
+	rk3328-rock64.dts
+
 	sun50i-a64-bananapi-m64.dts
 	sun50i-a64-nanopi-a64.dts
 	sun50i-a64-olinuxino.dts
@@ -45,6 +49,7 @@ makeoptions	DTS="
 options 	CPU_CORTEXA53
 options 	CPU_CORTEXA57
 options 	SOC_BCM2837
+options 	SOC_RK3328
 options 	SOC_TEGRA210
 options 	SOC_SUN50I_A64
 options 	SOC_SUN50I_H5
@@ -62,6 +67,7 @@ options 	DEBUG
 options 	VERBOSE_INIT_ARM # verbose bootstrapping messages
 # EARLYCONS is required for early init messages from VERBOSE_INIT_ARM.
 #options 	EARLYCONS=bcm2837
+options 	EARLYCONS=rk3328, CONSADDR=0xff130000
 #options 	EARLYCONS=sunxi, CONSADDR=0x01c28000
 #options 	EARLYCONS=tegra, CONSADDR=0x70006000
 #options 	EARLYCONS=virt
@@ -131,6 +137,7 @@ tegrapmc*	at fdt? pass 4		# NVIDIA Tegra
 # Clock and Reset controller
 bcmcprman*	at fdt? pass 1		# Broadcom BCM283x Clock Manager
 bcmaux*		at fdt? pass 1		# Broadcom BCM283x Aux Periph Clocks
+rkcru*		at fdt? pass 2		# Rockchip RK3328 CRU
 sun8ih3ccu*	at fdt? pass 2		# Allwinner H3/H5 CCU
 sun8ih3rccu*	at fdt? pass 2		# Allwinner H3/H5 CCU (PRCM)
 sun50ia64ccu*	at fdt? pass 2		# Allwinner A64 CCU
@@ -146,12 +153,14 @@ tegra210car*	at fdt? pass 3		# NVIDIA Te
 bcmgpio*	at fdt?			# Broadcom BCM283x GPIO
 sunxigpio*	at fdt? pass 3		# Allwinner GPIO
 tegragpio*	at fdt? pass 2		# NVIDIA Tegra GPIO
+rkgpio*		at rkiomux?		# Rockchip GPIO
 gpio*		at gpiobus?
 
 # PWM controller
 sunxipwm*	at fdt? pass 3		# Allwinner PWM
 
 # MPIO / Pinmux
+rkiomux*	at fdt?	pass 2		# Rockchip IOMUX
 tegrapinmux*	at fdt?			# NVIDIA Tegra MPIO
 
 # XUSB PADCTL
@@ -165,6 +174,7 @@ ppb*		at pci? dev ? function ?
 pci*		at ppb?
 
 # Ethernet
+awge*		at fdt?				# DesignWare Gigabit Ethernet
 emac*		at fdt?				# Allwinner Gigabit Ethernet (EMAC)
 re*		at pci? dev ? function ?	# Realtek RTL8111GS
 
@@ -229,6 +239,7 @@ sunxirtc*	at fdt?			# Allwinner RTC
 tegrartc*	at fdt?			# NVIDIA Tegra RTC
 
 # SDMMC
+dwcmmc*		at fdt?			# Designware SD/MMC
 mmcpwrseq*	at fdt?			# Simple MMC power sequence provider
 sdhc*		at fdt?			# SD Host Controller Interface
 sdhost*		at fdt?			# Broadcom BCM283x SD Host Interface
@@ -302,6 +313,8 @@ sunxithermal*	at fdt?			# Allwinner ther
 tegrasoctherm*	at fdt?			# NVIDIA Tegra SOC_THERM
 
 # USB
+rkusb*		at fdt? pass 9		# Rockchip USB PHY
+rkusbphy*	at rkusb?
 sunxiusbphy*	at fdt? pass 9		# Allwinner USB PHY
 sunxiusb3phy*	at fdt? pass 9		# Allwinner USB3 PHY
 tegrausbphy*	at fdt?			# NVIDIA Tegra USB PHY

Index: src/sys/arch/evbarm/conf/files.generic64
diff -u src/sys/arch/evbarm/conf/files.generic64:1.2 src/sys/arch/evbarm/conf/files.generic64:1.3
--- src/sys/arch/evbarm/conf/files.generic64:1.2	Thu Jun 14 10:56:39 2018
+++ src/sys/arch/evbarm/conf/files.generic64	Sat Jun 16 00:19:04 2018
@@ -1,4 +1,4 @@
-#	$NetBSD: files.generic64,v 1.2 2018/06/14 10:56:39 jmcneill Exp $
+#	$NetBSD: files.generic64,v 1.3 2018/06/16 00:19:04 jmcneill Exp $
 #
 
 defparam opt_arm_debug.h	EARLYCONS
@@ -12,5 +12,6 @@ include "arch/evbarm/conf/files.fdt"
 #
 include "arch/arm/broadcom/files.bcm2835"
 include "arch/arm/nvidia/files.tegra"
+include "arch/arm/rockchip/files.rockchip"
 include "arch/arm/sunxi/files.sunxi"
 include "arch/arm/virt/files.virt"

Index: src/sys/dev/fdt/files.fdt
diff -u src/sys/dev/fdt/files.fdt:1.31 src/sys/dev/fdt/files.fdt:1.32
--- src/sys/dev/fdt/files.fdt:1.31	Fri Jun 15 19:52:01 2018
+++ src/sys/dev/fdt/files.fdt	Sat Jun 16 00:19:04 2018
@@ -1,4 +1,4 @@
-# $NetBSD: files.fdt,v 1.31 2018/06/15 19:52:01 jakllsch Exp $
+# $NetBSD: files.fdt,v 1.32 2018/06/16 00:19:04 jmcneill Exp $
 
 include	"external/bsd/libfdt/conf/files.libfdt"
 
@@ -88,10 +88,18 @@ file	dev/fdt/simplefb.c			simplefb
 attach	com at fdt with dw_apb_uart
 file	dev/fdt/dw_apb_uart.c			dw_apb_uart
 
+# Designware USB2 OTG
+attach	dwctwo at fdt with dwc2_fdt
+file	dev/fdt/dwc2_fdt.c			dwc2_fdt
+
 # Designware USB3 XHCI
 attach	xhci at fdt with dwc3_fdt
 file	dev/fdt/dwc3_fdt.c			dwc3_fdt
 
+# Designware SD/MMC
+attach	dwcmmc at fdt with dwcmmc_fdt
+file	dev/fdt/dwcmmc_fdt.c			dwcmmc_fdt
+
 # Virtio virtio,mmio
 attach	virtio at fdt with virtio_mmio_fdt:	virtio_mmio
 file	dev/fdt/virtio_mmio_fdt.c		virtio_mmio_fdt

Added files:

Index: src/sys/arch/arm/rockchip/files.rockchip
diff -u /dev/null src/sys/arch/arm/rockchip/files.rockchip:1.13
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/files.rockchip	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,45 @@
+#	$NetBSD: files.rockchip,v 1.13 2018/06/16 00:19:04 jmcneill Exp $
+#
+# Configuration info for Rockchip family SoCs
+#
+#
+
+file	arch/arm/rockchip/rk_platform.c		soc_rockchip
+
+# Clock and reset unit (CRU)
+device	rkcru: rk_cru
+file	arch/arm/rockchip/rk_cru.c		rk_cru
+file	arch/arm/rockchip/rk_cru_arm.c		rk_cru
+file	arch/arm/rockchip/rk_cru_composite.c	rk_cru
+file	arch/arm/rockchip/rk_cru_gate.c		rk_cru
+file	arch/arm/rockchip/rk_cru_mux.c		rk_cru
+file	arch/arm/rockchip/rk_cru_pll.c		rk_cru
+
+# RK3328 clock and reset unit
+attach	rkcru at fdt with rk3328_cru
+file	arch/arm/rockchip/rk3328_cru.c		rk3328_cru & soc_rk3328
+
+# IOMUX control
+device	rkiomux { }
+attach	rkiomux at fdt with rk_iomux
+file	arch/arm/rockchip/rk_iomux.c		rk_iomux
+
+# GPIO
+device	rkgpio: gpiobus
+attach	rkgpio at rkiomux with rk_gpio
+file	arch/arm/rockchip/rk_gpio.c		rk_gpio
+
+# USB PHY
+device	rkusb { }
+attach	rkusb at fdt with rk_usb
+device	rkusbphy
+attach	rkusbphy at rkusb with rk_usbphy
+file	arch/arm/rockchip/rk_usb.c		rk_usb | rk_usbphy
+
+# GMAC
+attach	awge at fdt with rk_gmac
+file	arch/arm/rockchip/rk_gmac.c		rk_gmac
+
+# SOC parameters
+defflag	opt_soc.h			SOC_ROCKCHIP
+defflag	opt_soc.h			SOC_RK3328: SOC_ROCKCHIP

Index: src/sys/arch/arm/rockchip/rk3328_cru.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk3328_cru.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk3328_cru.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,361 @@
+/* $NetBSD: rk3328_cru.c,v 1.1 2018/06/16 00:19: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(1, "$NetBSD: rk3328_cru.c,v 1.1 2018/06/16 00:19:04 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/rk3328_cru.h>
+
+static int rk3328_cru_match(device_t, cfdata_t, void *);
+static void rk3328_cru_attach(device_t, device_t, void *);
+
+static const char * const compatible[] = {
+	"rockchip,rk3328-cru",
+	NULL
+};
+
+CFATTACH_DECL_NEW(rk3328_cru, sizeof(struct rk_cru_softc),
+	rk3328_cru_match, rk3328_cru_attach, NULL, NULL);
+
+static const struct rk_cru_pll_rate pll_rates[] = {
+        RK_PLL_RATE(1608000000,  1,  67, 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,  6, 500, 2, 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,  6, 400, 2, 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( 600000000,  1,  75, 3, 1, 1, 0),
+        RK_PLL_RATE( 594000000,  2,  99, 2, 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( 216000000,  1,  72, 4, 2, 1, 0),
+        RK_PLL_RATE(  96000000,  1,  64, 4, 4, 1, 0),
+};
+
+static const struct rk_cru_pll_rate pll_frac_rates[] = {
+        RK_PLL_RATE(1016064000,  3, 127, 1, 1, 0, 134217),
+        RK_PLL_RATE( 983040000, 24, 983, 1, 1, 0, 671088),
+        RK_PLL_RATE( 491520000, 24, 983, 2, 1, 0, 671088),
+        RK_PLL_RATE(  61440000,  6, 215, 7, 2, 0, 671088),
+        RK_PLL_RATE(  56448000, 12, 451, 4, 4, 0, 9797894),
+        RK_PLL_RATE(  40960000, 12, 409, 4, 5, 0, 10066329),
+};
+
+static const struct rk_cru_pll_rate pll_norates[] = {
+};
+
+static const struct rk_cru_arm_rate armclk_rates[] = {
+	RK_ARM_RATE(1296000000, 1),
+	RK_ARM_RATE(1200000000, 1),
+	RK_ARM_RATE(1104000000, 1),
+	RK_ARM_RATE(1008000000, 1),
+	RK_ARM_RATE( 912000000, 1),
+	RK_ARM_RATE( 816000000, 1),
+	RK_ARM_RATE( 696000000, 1),
+	RK_ARM_RATE( 600000000, 1),
+	RK_ARM_RATE( 408000000, 1),
+	RK_ARM_RATE( 312000000, 1),
+	RK_ARM_RATE( 216000000, 1),
+	RK_ARM_RATE(  96000000, 1),
+};
+
+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" };
+static const char * aclk_peri_pre_parents[] = { "cpll", "gpll", "hdmiphy" };
+static const char * mmc_parents[] = { "cpll", "gpll", "xin24m", "usb480m" };
+static const char * phclk_peri_parents[] = { "aclk_peri_pre" };
+static const char * mux_usb480m_parents[] = { "usb480m_phy", "xin24m" };
+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_mac2io_src_parents[] = { "clk_mac2io_src", "gmac_clkin" };
+static const char * mux_mac2io_ext_parents[] = { "clk_mac2io", "gmac_clkin" };
+static const char * mux_2plls_parents[] = { "cpll", "gpll" };
+static const char * mux_2plls_hdmiphy_parents[] = { "cpll", "gpll", "dummy_hdmiphy" };
+static const char * comp_uart_parents[] = { "cpll", "gpll", "usb480m" };
+static const char * pclk_gmac_parents[] = { "aclk_gmac" };
+
+static struct rk_cru_clk rk3328_cru_clks[] = {
+	RK_PLL(RK3328_PLL_APLL, "apll", "xin24m",
+	       0x0000,			/* con_base */
+	       0x80,			/* mode_reg */
+	       __BIT(0),		/* mode_mask */
+	       __BIT(4),		/* lock_mask */
+	       pll_frac_rates),
+	RK_PLL(RK3328_PLL_DPLL, "dpll", "xin24m",
+	       0x0020,			/* con_base */
+	       0x80,			/* mode_reg */
+	       __BIT(4),		/* mode_mask */
+	       __BIT(3),		/* lock_mask */
+	       pll_norates),
+	RK_PLL(RK3328_PLL_CPLL, "cpll", "xin24m",
+	       0x0040,			/* con_base */
+	       0x80,			/* mode_reg */
+	       __BIT(8),		/* mode_mask */
+	       __BIT(2),		/* lock_mask */
+	       pll_rates),
+	RK_PLL(RK3328_PLL_GPLL, "gpll", "xin24m",
+	       0x0060,			/* con_base */
+	       0x80,			/* mode_reg */
+	       __BIT(12),		/* mode_mask */
+	       __BIT(1),		/* lock_mask */
+	       pll_frac_rates),
+	RK_PLL(RK3328_PLL_NPLL, "npll", "xin24m",
+	       0x00a0,			/* con_base */
+	       0x80,			/* mode_reg */
+	       __BIT(1),		/* mode_mask */
+	       __BIT(0),		/* lock_mask */
+	       pll_rates),
+
+	RK_ARM(RK3328_ARMCLK, "armclk", armclk_parents,
+	       0x0100,			/* reg */
+	       __BITS(7,6), 3, 0,	/* mux_mask, mux_main, mux_alt */
+	       __BITS(4,0),		/* div_mask */
+	       armclk_rates),
+
+	RK_COMPOSITE(RK3328_ACLK_BUS_PRE, "aclk_bus_pre", aclk_bus_pre_parents,
+		     0x0100,		/* muxdiv_reg */
+		     __BITS(14,13),	/* mux_mask */
+		     __BITS(12,8),	/* div_mask */
+		     0x0220,		/* gate_reg */
+		     __BIT(0),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_HCLK_BUS_PRE, "hclk_bus_pre", hclk_bus_pre_parents,
+		     0x0104,		/* muxdiv_reg */
+		     0,			/* mux_mask */
+		     __BITS(9,8),	/* div_mask */
+		     0x0220,		/* gate_reg */
+		     __BIT(1),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_PCLK_BUS_PRE, "pclk_bus_pre", hclk_bus_pre_parents,
+		     0x0104,		/* muxdiv_reg */
+		     0,			/* mux_mask */
+		     __BITS(14,12),	/* div_mask */
+		     0x0220,		/* gate_reg */
+		     __BIT(2),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_ACLK_PERI_PRE, "aclk_peri_pre", aclk_peri_pre_parents,
+		     0x0170,		/* muxdiv_reg */
+		     __BITS(7,6),	/* mux_mask */
+		     __BITS(4,0),	/* div_mask */
+		     0,	0,		/* gate_reg, gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_PCLK_PERI, "pclk_peri", phclk_peri_parents,
+		     0x0174,		/* muxdiv_reg */
+		     0,			/* mux_mask */
+		     __BITS(6,4),	/* div_mask */
+		     0x0228,		/* gate_reg */
+		     __BIT(2),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_HCLK_PERI, "hclk_peri", phclk_peri_parents,
+		     0x0174,		/* muxdiv_reg */
+		     0,			/* mux_mask */
+		     __BITS(1,0),	/* div_mask */
+		     0x0228,		/* gate_reg */
+		     __BIT(1),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_SCLK_SDMMC, "clk_sdmmc", mmc_parents,
+		     0x0178,		/* muxdiv_reg */
+		     __BITS(9,8),	/* mux_mask */
+		     __BITS(7,0),	/* div_mask */
+		     0x0210,		/* gate_reg */
+		     __BIT(3),		/* gate_mask */
+		     RK_COMPOSITE_ROUND_DOWN),
+	RK_COMPOSITE(RK3328_SCLK_SDIO, "clk_sdio", mmc_parents,
+		     0x0180,		/* muxdiv_reg */
+		     __BITS(9,8),	/* mux_mask */
+		     __BITS(7,0),	/* div_mask */
+		     0x0210,		/* gate_reg */
+		     __BIT(5),		/* gate_mask */
+		     RK_COMPOSITE_ROUND_DOWN),
+	RK_COMPOSITE(RK3328_SCLK_EMMC, "clk_emmc", mmc_parents,
+		     0x017c,		/* muxdiv_reg */
+		     __BITS(9,8),	/* mux_mask */
+		     __BITS(7,0),	/* div_mask */
+		     0x0210,		/* gate_reg */
+		     __BIT(4),		/* gate_mask */
+		     RK_COMPOSITE_ROUND_DOWN),
+	RK_COMPOSITE(0, "clk_uart0_div", comp_uart_parents,
+		     0x0138,		/* muxdiv_reg */
+		     __BITS(13,12),	/* mux_mask */
+		     __BITS(6,0),	/* div_mask */
+		     0x0204,		/* gate_reg */
+		     __BIT(14),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(0, "clk_uart1_div", comp_uart_parents,
+		     0x0140,		/* muxdiv_reg */
+		     __BITS(13,12),	/* mux_mask */
+		     __BITS(6,0),	/* div_mask */
+		     0x0208,		/* gate_reg */
+		     __BIT(0),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(0, "clk_uart2_div", comp_uart_parents,
+		     0x0148,		/* muxdiv_reg */
+		     __BITS(13,12),	/* mux_mask */
+		     __BITS(6,0),	/* div_mask */
+		     0x0208,		/* gate_reg */
+		     __BIT(2),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_ACLK_GMAC, "aclk_gmac", mux_2plls_hdmiphy_parents,
+		     0x018c,		/* muxdiv_reg */
+		     __BITS(7,6),	/* mux_mask */
+		     __BITS(4,0),	/* div_mask */
+		     0x020c,		/* gate_reg */
+		     __BIT(2),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_PCLK_GMAC, "pclk_gmac", pclk_gmac_parents,
+		     0x0164,		/* muxdiv_reg */
+		     0,			/* mux_mask */
+		     __BITS(10,8),	/* div_mask */
+		     0x0224,		/* gate_reg */
+		     __BIT(0),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_SCLK_MAC2IO_SRC, "clk_mac2io_src", mux_2plls_parents,
+		     0x016c,		/* muxdiv_reg */
+		     __BIT(7),		/* mux_mask */
+		     __BITS(4,0),	/* div_mask */
+		     0x020c,		/* gate_reg */
+		     __BIT(1),		/* gate_mask */
+		     0),
+	RK_COMPOSITE(RK3328_SCLK_MAC2IO_OUT, "clk_mac2io_out", mux_2plls_parents,
+		     0x016c,		/* muxdiv_reg */
+		     __BIT(15),		/* mux_mask */
+		     __BITS(12,8),	/* div_mask */
+		     0x020c,		/* gate_reg */
+		     __BIT(5),		/* gate_mask */
+		     0),
+
+	RK_GATE(0, "apll_core", "apll", 0x200, 0),
+	RK_GATE(0, "dpll_core", "dpll", 0x200, 1),
+	RK_GATE(0, "gpll_core", "gpll", 0x200, 2),
+	RK_GATE(0, "npll_core", "npll", 0x200, 12),
+	RK_GATE(0, "gpll_peri", "gpll", 0x210, 0),
+	RK_GATE(0, "cpll_peri", "cpll", 0x210, 1),
+	RK_GATE(0, "pclk_bus", "pclk_bus_pre", 0x220, 3),
+	RK_GATE(0, "pclk_phy_pre", "pclk_bus_pre", 0x220, 4),
+	RK_GATE(RK3328_ACLK_PERI, "aclk_peri", "aclk_peri_pre", 0x228, 0),
+	RK_GATE(RK3328_PCLK_GPIO0, "pclk_gpio0", "pclk_bus", 0x240, 7),
+	RK_GATE(RK3328_PCLK_GPIO1, "pclk_gpio1", "pclk_bus", 0x240, 8),
+	RK_GATE(RK3328_PCLK_GPIO2, "pclk_gpio2", "pclk_bus", 0x240, 9),
+	RK_GATE(RK3328_PCLK_GPIO3, "pclk_gpio3", "pclk_bus", 0x240, 10),
+	RK_GATE(RK3328_PCLK_UART0, "pclk_uart0", "pclk_bus", 0x240, 11),
+	RK_GATE(RK3328_PCLK_UART1, "pclk_uart1", "pclk_bus", 0x240, 12),
+	RK_GATE(RK3328_PCLK_UART2, "pclk_uart2", "pclk_bus", 0x240, 13),
+	RK_GATE(RK3328_SCLK_MAC2IO_REF, "clk_mac2io_ref", "clk_mac2io", 0x224, 7),
+	RK_GATE(RK3328_SCLK_MAC2IO_RX, "clk_mac2io_rx", "clk_mac2io", 0x224, 4),
+	RK_GATE(RK3328_SCLK_MAC2IO_TX, "clk_mac2io_tx", "clk_mac2io", 0x224, 5),
+	RK_GATE(RK3328_SCLK_MAC2IO_REFOUT, "clk_mac2io_refout", "clk_mac2io", 0x224, 6),
+	RK_GATE(RK3328_ACLK_USB3OTG, "aclk_usb3otg", "aclk_peri", 0x24c, 4),
+	RK_GATE(RK3328_HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 0x24c, 0),
+	RK_GATE(RK3328_HCLK_SDIO, "hclk_sdio", "hclk_peri", 0x24c, 1),
+	RK_GATE(RK3328_HCLK_EMMC, "hclk_emmc", "hclk_peri", 0x24c, 2),
+	RK_GATE(RK3328_HCLK_SDMMC_EXT, "hclk_sdmmc_ext", "hclk_peri", 0x24c, 15),
+	RK_GATE(RK3328_HCLK_HOST0, "hclk_host0", "hclk_peri", 0x24c, 6),
+	RK_GATE(RK3328_HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_peri", 0x24c, 7),
+	RK_GATE(RK3328_HCLK_OTG, "hclk_otg", "hclk_peri", 0x24c, 8),
+	RK_GATE(RK3328_HCLK_OTG_PMU, "hclk_otg_pmu", "hclk_peri", 0x24c, 9),
+	RK_GATE(RK3328_ACLK_MAC2IO, "aclk_mac2io", "aclk_gmac", 0x268, 2),
+	RK_GATE(RK3328_PCLK_MAC2IO, "pclk_mac2io", "pclk_gmac", 0x268, 3),
+
+	RK_MUX(RK3328_USB480M, "usb480m", mux_usb480m_parents, 0x0084, __BIT(13)),
+	RK_MUX(RK3328_SCLK_UART0, "sclk_uart0", mux_uart0_parents, 0x0138, __BITS(9,8)),
+	RK_MUX(RK3328_SCLK_UART1, "sclk_uart1", mux_uart1_parents, 0x0140, __BITS(9,8)),
+	RK_MUX(RK3328_SCLK_UART2, "sclk_uart2", mux_uart2_parents, 0x0148, __BITS(9,8)),
+	RK_MUXGRF(RK3328_SCLK_MAC2IO, "clk_mac2io", mux_mac2io_src_parents, 0x0904, __BIT(10)),
+	RK_MUXGRF(RK3328_SCLK_MAC2IO_EXT, "clk_mac2io_ext", mux_mac2io_ext_parents, 0x0410, __BIT(14)),
+};
+
+static int
+rk3328_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
+rk3328_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 = rk3328_cru_clks;
+	sc->sc_nclks = __arraycount(rk3328_cru_clks);
+
+	if (rk_cru_attach(sc) != 0)
+		return;
+
+	aprint_naive("\n");
+	aprint_normal(": RK3328 CRU\n");
+
+	rk_cru_print(sc);
+}
Index: src/sys/arch/arm/rockchip/rk3328_cru.h
diff -u /dev/null src/sys/arch/arm/rockchip/rk3328_cru.h:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk3328_cru.h	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,213 @@
+/* $NetBSD: rk3328_cru.h,v 1.1 2018/06/16 00:19: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.
+ */
+
+#ifndef _RK3328_CRU_H
+#define _RK3328_CRU_H
+
+#define RK3328_PLL_APLL			1
+#define RK3328_PLL_DPLL			2
+#define RK3328_PLL_CPLL			3
+#define RK3328_PLL_GPLL			4
+#define RK3328_PLL_NPLL			5
+#define RK3328_ARMCLK			6
+#define RK3328_SCLK_RTC32K		30
+#define RK3328_SCLK_SDMMC_EXT		31
+#define RK3328_SCLK_SPI			32
+#define RK3328_SCLK_SDMMC		33
+#define RK3328_SCLK_SDIO		34
+#define RK3328_SCLK_EMMC		35
+#define RK3328_SCLK_TSADC		36
+#define RK3328_SCLK_SARADC		37
+#define RK3328_SCLK_UART0		38
+#define RK3328_SCLK_UART1		39
+#define RK3328_SCLK_UART2		40
+#define RK3328_SCLK_I2S0		41
+#define RK3328_SCLK_I2S1		42
+#define RK3328_SCLK_I2S2		43
+#define RK3328_SCLK_I2S1_OUT		44
+#define RK3328_SCLK_I2S2_OUT		45
+#define RK3328_SCLK_SPDIF		46
+#define RK3328_SCLK_TIMER0		47
+#define RK3328_SCLK_TIMER1		48
+#define RK3328_SCLK_TIMER2		49
+#define RK3328_SCLK_TIMER3		50
+#define RK3328_SCLK_TIMER4		51
+#define RK3328_SCLK_TIMER5		52
+#define RK3328_SCLK_WIFI		53
+#define RK3328_SCLK_CIF_OUT		54
+#define RK3328_SCLK_I2C0		55
+#define RK3328_SCLK_I2C1		56
+#define RK3328_SCLK_I2C2		57
+#define RK3328_SCLK_I2C3		58
+#define RK3328_SCLK_CRYPTO		59
+#define RK3328_SCLK_PWM			60
+#define RK3328_SCLK_PDM			61
+#define RK3328_SCLK_EFUSE		62
+#define RK3328_SCLK_OTP			63
+#define RK3328_SCLK_DDRCLK		64
+#define RK3328_SCLK_VDEC_CABAC		65
+#define RK3328_SCLK_VDEC_CORE		66
+#define RK3328_SCLK_VENC_DSP		67
+#define RK3328_SCLK_VENC_CORE		68
+#define RK3328_SCLK_RGA			69
+#define RK3328_SCLK_HDMI_SFC		70
+#define RK3328_SCLK_HDMI_CEC		71
+#define RK3328_SCLK_USB3_REF		72
+#define RK3328_SCLK_USB3_SUSPEND	73
+#define RK3328_SCLK_SDMMC_DRV		74
+#define RK3328_SCLK_SDIO_DRV		75
+#define RK3328_SCLK_EMMC_DRV		76
+#define RK3328_SCLK_SDMMC_EXT_DRV	77
+#define RK3328_SCLK_SDMMC_SAMPLE	78
+#define RK3328_SCLK_SDIO_SAMPLE		79
+#define RK3328_SCLK_EMMC_SAMPLE		80
+#define RK3328_SCLK_SDMMC_EXT_SAMPLE	81
+#define RK3328_SCLK_VOP			82
+#define RK3328_SCLK_MAC2PHY_RXTX	83
+#define RK3328_SCLK_MAC2PHY_SRC		84
+#define RK3328_SCLK_MAC2PHY_REF		85
+#define RK3328_SCLK_MAC2PHY_OUT		86
+#define RK3328_SCLK_MAC2IO_RX		87
+#define RK3328_SCLK_MAC2IO_TX		88
+#define RK3328_SCLK_MAC2IO_REFOUT	89
+#define RK3328_SCLK_MAC2IO_REF		90
+#define RK3328_SCLK_MAC2IO_OUT		91
+#define RK3328_SCLK_TSP			92
+#define RK3328_SCLK_HSADC_TSP		93
+#define RK3328_SCLK_USB3PHY_REF		94
+#define RK3328_SCLK_REF_USB3OTG		95
+#define RK3328_SCLK_USB3OTG_REF		96
+#define RK3328_SCLK_USB3OTG_SUSPEND	97
+#define RK3328_SCLK_REF_USB3OTG_SRC	98
+#define RK3328_SCLK_MAC2IO_SRC		99
+#define RK3328_SCLK_MAC2IO		100
+#define RK3328_SCLK_MAC2PHY		101
+#define RK3328_SCLK_MAC2IO_EXT		102
+#define RK3328_DCLK_LCDC		120
+#define RK3328_DCLK_HDMIPHY		121
+#define RK3328_HDMIPHY			122
+#define RK3328_USB480M			123
+#define RK3328_DCLK_LCDC_SRC		124
+#define RK3328_ACLK_AXISRAM		130
+#define RK3328_ACLK_VOP_PRE		131
+#define RK3328_ACLK_USB3OTG		132
+#define RK3328_ACLK_RGA_PRE		133
+#define RK3328_ACLK_DMAC		134
+#define RK3328_ACLK_GPU			135
+#define RK3328_ACLK_BUS_PRE		136
+#define RK3328_ACLK_PERI_PRE		137
+#define RK3328_ACLK_RKVDEC_PRE		138
+#define RK3328_ACLK_RKVDEC		139
+#define RK3328_ACLK_RKVENC		140
+#define RK3328_ACLK_VPU_PRE		141
+#define RK3328_ACLK_VIO_PRE		142
+#define RK3328_ACLK_VPU			143
+#define RK3328_ACLK_VIO			144
+#define RK3328_ACLK_VOP			145
+#define RK3328_ACLK_GMAC		146
+#define RK3328_ACLK_H265		147
+#define RK3328_ACLK_H264		148
+#define RK3328_ACLK_MAC2PHY		149
+#define RK3328_ACLK_MAC2IO		150
+#define RK3328_ACLK_DCF			151
+#define RK3328_ACLK_TSP			152
+#define RK3328_ACLK_PERI		153
+#define RK3328_ACLK_RGA			154
+#define RK3328_ACLK_IEP			155
+#define RK3328_ACLK_CIF			156
+#define RK3328_ACLK_HDCP		157
+#define RK3328_PCLK_GPIO0		200
+#define RK3328_PCLK_GPIO1		201
+#define RK3328_PCLK_GPIO2		202
+#define RK3328_PCLK_GPIO3		203
+#define RK3328_PCLK_GRF			204
+#define RK3328_PCLK_I2C0		205
+#define RK3328_PCLK_I2C1		206
+#define RK3328_PCLK_I2C2		207
+#define RK3328_PCLK_I2C3		208
+#define RK3328_PCLK_SPI			209
+#define RK3328_PCLK_UART0		210
+#define RK3328_PCLK_UART1		211
+#define RK3328_PCLK_UART2		212
+#define RK3328_PCLK_TSADC		213
+#define RK3328_PCLK_PWM			214
+#define RK3328_PCLK_TIMER		215
+#define RK3328_PCLK_BUS_PRE		216
+#define RK3328_PCLK_PERI_PRE		217
+#define RK3328_PCLK_HDMI_CTRL		218
+#define RK3328_PCLK_HDMI_PHY		219
+#define RK3328_PCLK_GMAC		220
+#define RK3328_PCLK_H265		221
+#define RK3328_PCLK_MAC2PHY		222
+#define RK3328_PCLK_MAC2IO		223
+#define RK3328_PCLK_USB3PHY_OTG		224
+#define RK3328_PCLK_USB3PHY_PIPE	225
+#define RK3328_PCLK_USB3_GRF		226
+#define RK3328_PCLK_USB2_GRF		227
+#define RK3328_PCLK_HDMIPHY		228
+#define RK3328_PCLK_DDR			229
+#define RK3328_PCLK_PERI		230
+#define RK3328_PCLK_HDMI		231
+#define RK3328_PCLK_HDCP		232
+#define RK3328_PCLK_DCF			233
+#define RK3328_PCLK_SARADC		234
+#define RK3328_HCLK_PERI		308
+#define RK3328_HCLK_TSP			309
+#define RK3328_HCLK_GMAC		310
+#define RK3328_HCLK_I2S0_8CH		311
+#define RK3328_HCLK_I2S1_8CH		313
+#define RK3328_HCLK_I2S2_2CH		313
+#define RK3328_HCLK_SPDIF_8CH		314
+#define RK3328_HCLK_VOP			315
+#define RK3328_HCLK_NANDC		316
+#define RK3328_HCLK_SDMMC		317
+#define RK3328_HCLK_SDIO		318
+#define RK3328_HCLK_EMMC		319
+#define RK3328_HCLK_SDMMC_EXT		320
+#define RK3328_HCLK_RKVDEC_PRE		321
+#define RK3328_HCLK_RKVDEC		322
+#define RK3328_HCLK_RKVENC		323
+#define RK3328_HCLK_VPU_PRE		324
+#define RK3328_HCLK_VIO_PRE		325
+#define RK3328_HCLK_VPU			326
+#define RK3328_HCLK_BUS_PRE		328
+#define RK3328_HCLK_PERI_PRE		329
+#define RK3328_HCLK_H264		330
+#define RK3328_HCLK_CIF			331
+#define RK3328_HCLK_OTG_PMU		332
+#define RK3328_HCLK_OTG			333
+#define RK3328_HCLK_HOST0		334
+#define RK3328_HCLK_HOST0_ARB		335
+#define RK3328_HCLK_CRYPTO_MST		336
+#define RK3328_HCLK_CRYPTO_SLV		337
+#define RK3328_HCLK_PDM			338
+#define RK3328_HCLK_IEP			339
+#define RK3328_HCLK_RGA			340
+#define RK3328_HCLK_HDCP		341
+
+#endif /* !_RK3328_CRU_H */
Index: src/sys/arch/arm/rockchip/rk3328_platform.h
diff -u /dev/null src/sys/arch/arm/rockchip/rk3328_platform.h:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk3328_platform.h	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,40 @@
+/* $NetBSD: rk3328_platform.h,v 1.1 2018/06/16 00:19: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.
+ */
+
+#ifndef _ARM_RK3328_PLATFORM_H
+#define _ARM_RK3328_PLATFORM_H
+
+#include <arch/evbarm/fdt/platform.h>
+
+#define	RK3328_CORE_VBASE	KERNEL_IO_VBASE
+#define	RK3328_CORE_PBASE	0xff000000
+#define	RK3328_CORE_SIZE	0x01000000
+
+#define	RK3328_UART_FREQ	24000000
+
+#endif /* _ARM_RK3328_PLATFORM_H */
Index: src/sys/arch/arm/rockchip/rk_cru.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_cru.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_cru.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,379 @@
+/* $NetBSD: rk_cru.c,v 1.1 2018/06/16 00:19: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 "opt_soc.h"
+#include "opt_fdt_arm.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: rk_cru.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/device.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#include <dev/clk/clk_backend.h>
+
+#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)
+{
+	if (len != 4)
+		return NULL;
+
+	return (void *)(uintptr_t)be32dec(data);
+}
+
+static void
+rk_cru_reset_release(device_t dev, void *priv)
+{
+}
+
+static int
+rk_cru_reset_assert(device_t dev, void *priv)
+{
+	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 / 32) * 4;
+	const u_int shift = reset_id % 32;
+
+	CRU_WRITE(sc, reg, (1 << (shift + 16)) | (0 << shift));
+
+	return 0;
+}
+
+static int
+rk_cru_reset_deassert(device_t dev, void *priv)
+{
+	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 / 32) * 4;
+	const u_int shift = reset_id % 32;
+
+	CRU_WRITE(sc, reg, (1 << (shift + 16)) | (1 << shift));
+
+	return 0;
+}
+
+static const struct fdtbus_reset_controller_func rk_cru_fdtreset_funcs = {
+	.acquire = rk_cru_reset_acquire,
+	.release = rk_cru_reset_release,
+	.reset_assert = rk_cru_reset_assert,
+	.reset_deassert = rk_cru_reset_deassert,
+};
+
+static struct clk *
+rk_cru_clock_decode(device_t dev, const void *data, size_t len)
+{
+	struct rk_cru_softc * const sc = device_private(dev);
+	struct rk_cru_clk *clk;
+
+	if (len != 4)
+		return NULL;
+
+	const u_int clock_id = be32dec(data);
+
+	for (int i = 0; i < sc->sc_nclks; i++) {
+		clk = &sc->sc_clks[i];
+		if (clk->id == clock_id)
+			return &clk->base;
+	}
+
+	return NULL;
+}
+
+static const struct fdtbus_clock_controller_func rk_cru_fdtclock_funcs = {
+	.decode = rk_cru_clock_decode,
+};
+
+static struct clk *
+rk_cru_clock_get(void *priv, const char *name)
+{
+	struct rk_cru_softc * const sc = priv;
+	struct rk_cru_clk *clk;
+
+	clk = rk_cru_clock_find(sc, name);
+	if (clk == NULL)
+		return NULL;
+
+	return &clk->base;
+}
+
+static void
+rk_cru_clock_put(void *priv, struct clk *clk)
+{
+}
+
+static u_int
+rk_cru_clock_get_rate(void *priv, struct clk *clkp)
+{
+	struct rk_cru_softc * const sc = priv;
+	struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
+	struct clk *clkp_parent;
+
+	if (clk->get_rate)
+		return clk->get_rate(sc, clk);
+
+	clkp_parent = clk_get_parent(clkp);
+	if (clkp_parent == NULL) {
+		aprint_error("%s: no parent for %s\n", __func__, clk->base.name);
+		return 0;
+	}
+
+	return clk_get_rate(clkp_parent);
+}
+
+static int
+rk_cru_clock_set_rate(void *priv, struct clk *clkp, u_int rate)
+{
+	struct rk_cru_softc * const sc = priv;
+	struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
+	struct clk *clkp_parent;
+
+	if (clkp->flags & CLK_SET_RATE_PARENT) {
+		clkp_parent = clk_get_parent(clkp);
+		if (clkp_parent == NULL) {
+			aprint_error("%s: no parent for %s\n", __func__, clk->base.name);
+			return ENXIO;
+		}
+		return clk_set_rate(clkp_parent, rate);
+	}
+
+	if (clk->set_rate)
+		return clk->set_rate(sc, clk, rate);
+
+	return ENXIO;
+}
+
+static u_int
+rk_cru_clock_round_rate(void *priv, struct clk *clkp, u_int rate)
+{
+	struct rk_cru_softc * const sc = priv;
+	struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
+	struct clk *clkp_parent;
+
+	if (clkp->flags & CLK_SET_RATE_PARENT) {
+		clkp_parent = clk_get_parent(clkp);
+		if (clkp_parent == NULL) {
+			aprint_error("%s: no parent for %s\n", __func__, clk->base.name);
+			return 0;
+		}
+		return clk_round_rate(clkp_parent, rate);
+	}
+
+	if (clk->round_rate)
+		return clk->round_rate(sc, clk, rate);
+
+	return 0;
+}
+
+static int
+rk_cru_clock_enable(void *priv, struct clk *clkp)
+{
+	struct rk_cru_softc * const sc = priv;
+	struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
+	struct clk *clkp_parent;
+	int error = 0;
+
+	clkp_parent = clk_get_parent(clkp);
+	if (clkp_parent != NULL) {
+		error = clk_enable(clkp_parent);
+		if (error != 0)
+			return error;
+	}
+
+	if (clk->enable)
+		error = clk->enable(sc, clk, 1);
+
+	return error;
+}
+
+static int
+rk_cru_clock_disable(void *priv, struct clk *clkp)
+{
+	struct rk_cru_softc * const sc = priv;
+	struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
+	int error = EINVAL;
+
+	if (clk->enable)
+		error = clk->enable(sc, clk, 0);
+
+	return error;
+}
+
+static int
+rk_cru_clock_set_parent(void *priv, struct clk *clkp,
+    struct clk *clkp_parent)
+{
+	struct rk_cru_softc * const sc = priv;
+	struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
+
+	if (clk->set_parent == NULL)
+		return EINVAL;
+
+	return clk->set_parent(sc, clk, clkp_parent->name);
+}
+
+static struct clk *
+rk_cru_clock_get_parent(void *priv, struct clk *clkp)
+{
+	struct rk_cru_softc * const sc = priv;
+	struct rk_cru_clk *clk = (struct rk_cru_clk *)clkp;
+	struct rk_cru_clk *clk_parent;
+	const char *parent;
+
+	if (clk->get_parent == NULL)
+		return NULL;
+
+	parent = clk->get_parent(sc, clk);
+	if (parent == NULL)
+		return NULL;
+
+	clk_parent = rk_cru_clock_find(sc, parent);
+	if (clk_parent != NULL)
+		return &clk_parent->base;
+
+	/* No parent in this domain, try FDT */
+	return fdtbus_clock_byname(parent);
+}
+
+static const struct clk_funcs rk_cru_clock_funcs = {
+	.get = rk_cru_clock_get,
+	.put = rk_cru_clock_put,
+	.get_rate = rk_cru_clock_get_rate,
+	.set_rate = rk_cru_clock_set_rate,
+	.round_rate = rk_cru_clock_round_rate,
+	.enable = rk_cru_clock_enable,
+	.disable = rk_cru_clock_disable,
+	.set_parent = rk_cru_clock_set_parent,
+	.get_parent = rk_cru_clock_get_parent,
+};
+
+struct rk_cru_clk *
+rk_cru_clock_find(struct rk_cru_softc *sc, const char *name)
+{
+	for (int i = 0; i < sc->sc_nclks; i++) {
+		if (sc->sc_clks[i].base.name == NULL)
+			continue;
+		if (strcmp(sc->sc_clks[i].base.name, name) == 0)
+			return &sc->sc_clks[i];
+	}
+
+	return NULL;
+}
+
+int
+rk_cru_attach(struct rk_cru_softc *sc)
+{
+	bus_addr_t addr;
+	bus_size_t size;
+	int i;
+
+	if (of_hasprop(sc->sc_phandle, "rockchip,grf")) {
+		const int grf_phandle = fdtbus_get_phandle(sc->sc_phandle, "rockchip,grf");
+		if (grf_phandle == -1) {
+			aprint_error(": couldn't get grf phandle\n");
+			return ENXIO;
+		}
+
+		if (fdtbus_get_reg(grf_phandle, 0, &addr, &size) != 0) {
+			aprint_error(": couldn't get grf registers\n");
+			return ENXIO;
+		}
+
+		if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_grf) != 0) {
+			aprint_error(": couldn't map registers\n");
+			return ENXIO;
+		}
+	}
+
+	if (fdtbus_get_reg(sc->sc_phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return ENXIO;
+	}
+	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return ENXIO;
+	}
+
+	sc->sc_clkdom.name = device_xname(sc->sc_dev);
+	sc->sc_clkdom.funcs = &rk_cru_clock_funcs;
+	sc->sc_clkdom.priv = sc;
+	for (i = 0; i < sc->sc_nclks; i++) {
+		sc->sc_clks[i].base.domain = &sc->sc_clkdom;
+		clk_attach(&sc->sc_clks[i].base);
+	}
+
+	fdtbus_register_clock_controller(sc->sc_dev, sc->sc_phandle,
+	    &rk_cru_fdtclock_funcs);
+
+	fdtbus_register_reset_controller(sc->sc_dev, sc->sc_phandle,
+	    &rk_cru_fdtreset_funcs);
+
+	return 0;
+}
+
+void
+rk_cru_print(struct rk_cru_softc *sc)
+{
+	struct rk_cru_clk *clk;
+	struct clk *clkp_parent;
+	const char *type;
+	int i;
+
+	for (i = 0; i < sc->sc_nclks; i++) {
+		clk = &sc->sc_clks[i];
+		if (clk->type == RK_CRU_UNKNOWN)
+			continue;
+
+		clkp_parent = clk_get_parent(&clk->base);
+
+		switch (clk->type) {
+		case RK_CRU_PLL:		type = "pll"; break;
+		case RK_CRU_ARM:		type = "arm"; break;
+		case RK_CRU_COMPOSITE:		type = "comp"; break;
+		case RK_CRU_GATE:		type = "gate"; break;
+		case RK_CRU_MUX:		type = "mux"; break;
+		default:			type = "???"; break;
+		}
+
+        	device_printf(sc->sc_dev,
+		    "%3d %-14s %2s %-14s %-7s ",
+		    clk->id,
+        	    clk->base.name,
+        	    clkp_parent ? "<-" : "",
+        	    clkp_parent ? clkp_parent->name : "",
+        	    type);
+		printf("%10d Hz\n", clk_get_rate(&clk->base));
+	}
+}
Index: src/sys/arch/arm/rockchip/rk_cru.h
diff -u /dev/null src/sys/arch/arm/rockchip/rk_cru.h:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_cru.h	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,321 @@
+/* $NetBSD: rk_cru.h,v 1.1 2018/06/16 00:19: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.
+ */
+
+#ifndef _ARM_RK_CRU_H
+#define _ARM_RK_CRU_H
+
+#include <dev/clk/clk_backend.h>
+
+struct rk_cru_softc;
+struct rk_cru_clk;
+
+/*
+ * Clocks
+ */
+
+enum rk_cru_clktype {
+	RK_CRU_UNKNOWN,
+	RK_CRU_PLL,
+	RK_CRU_ARM,
+	RK_CRU_COMPOSITE,
+	RK_CRU_GATE,
+	RK_CRU_MUX,
+};
+
+/* PLL clocks */
+
+struct rk_cru_pll_rate {
+	u_int		rate;
+	u_int		refdiv;
+	u_int		fbdiv;
+	u_int		postdiv1;
+	u_int		postdiv2;
+	u_int		dsmpd;
+	u_int		fracdiv;
+};
+
+#define	RK_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _fracdiv) \
+	{							\
+		.rate = (_rate),				\
+		.refdiv = (_refdiv),				\
+		.fbdiv = (_fbdiv),				\
+		.postdiv1 = (_postdiv1),			\
+		.postdiv2 = (_postdiv2),			\
+		.dsmpd = (_dsmpd),				\
+		.fracdiv = (_fracdiv),				\
+	}
+
+struct rk_cru_pll {
+	bus_size_t	con_base;
+	bus_size_t	mode_reg;
+	uint32_t	mode_mask;
+	uint32_t	lock_mask;
+	const struct rk_cru_pll_rate *rates;
+	u_int		nrates;
+	const char	*parent;
+};
+
+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) \
+	{							\
+		.id = (_id),					\
+		.type = RK_CRU_PLL,				\
+		.base.name = (_name),				\
+		.base.flags = 0,				\
+		.u.pll.parent = (_parent),			\
+		.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 = rk_cru_pll_get_rate,		\
+		.set_rate = rk_cru_pll_set_rate,		\
+		.get_parent = rk_cru_pll_get_parent,		\
+	}
+
+/* ARM clocks */
+
+struct rk_cru_arm_rate {
+	u_int		rate;
+	u_int		div;
+};
+
+#define	RK_ARM_RATE(_rate, _div)				\
+	{							\
+		.rate = (_rate),				\
+		.div = (_div),					\
+	}
+
+struct rk_cru_arm {
+	bus_size_t	reg;
+	uint32_t	mux_mask;
+	u_int		mux_main;
+	u_int		mux_alt;
+	uint32_t	div_mask;
+	const char	**parents;
+	u_int		nparents;
+	const struct rk_cru_arm_rate *rates;
+	u_int		nrates;
+};
+
+u_int	rk_cru_arm_get_rate(struct rk_cru_softc *, struct rk_cru_clk *);
+int	rk_cru_arm_set_rate(struct rk_cru_softc *, struct rk_cru_clk *, u_int);
+const char *rk_cru_arm_get_parent(struct rk_cru_softc *, struct rk_cru_clk *);
+int	rk_cru_arm_set_parent(struct rk_cru_softc *, struct rk_cru_clk *, const char *);
+
+#define	RK_ARM(_id, _name, _parents, _reg, _mux_mask, _mux_main, _mux_alt, _div_mask, _rates) \
+	{							\
+		.id = (_id),					\
+		.type = RK_CRU_ARM,				\
+		.base.name = (_name),				\
+		.base.flags = 0,				\
+		.u.arm.parents = (_parents),			\
+		.u.arm.nparents = __arraycount(_parents),	\
+		.u.arm.reg = (_reg),				\
+		.u.arm.mux_mask = (_mux_mask),			\
+		.u.arm.mux_main = (_mux_main),			\
+		.u.arm.mux_alt = (_mux_alt),			\
+		.u.arm.div_mask = (_div_mask),			\
+		.u.arm.rates = (_rates),			\
+		.u.arm.nrates = __arraycount(_rates),		\
+		.get_rate = rk_cru_arm_get_rate,		\
+		.set_rate = rk_cru_arm_set_rate,		\
+		.get_parent = rk_cru_arm_get_parent,		\
+		.set_parent = rk_cru_arm_set_parent,		\
+	}
+
+/* Composite clocks */
+
+struct rk_cru_composite {
+	bus_size_t	muxdiv_reg;
+	uint32_t	mux_mask;
+	uint32_t	div_mask;
+	bus_size_t	gate_reg;
+	uint32_t	gate_mask;
+	const char	**parents;
+	u_int		nparents;
+	u_int		flags;
+#define	RK_COMPOSITE_ROUND_DOWN		0x01
+};
+
+int	rk_cru_composite_enable(struct rk_cru_softc *, struct rk_cru_clk *, int);
+u_int	rk_cru_composite_get_rate(struct rk_cru_softc *, struct rk_cru_clk *);
+int	rk_cru_composite_set_rate(struct rk_cru_softc *, struct rk_cru_clk *, u_int);
+const char *rk_cru_composite_get_parent(struct rk_cru_softc *, struct rk_cru_clk *);
+int	rk_cru_composite_set_parent(struct rk_cru_softc *, struct rk_cru_clk *, const char *);
+
+#define	RK_COMPOSITE(_id, _name, _parents, _muxdiv_reg, _mux_mask, _div_mask, _gate_reg, _gate_mask, _flags) \
+	{							\
+		.id = (_id),					\
+		.type = RK_CRU_COMPOSITE,			\
+		.base.name = (_name),				\
+		.base.flags = 0,				\
+		.u.composite.parents = (_parents),		\
+		.u.composite.nparents = __arraycount(_parents),	\
+		.u.composite.muxdiv_reg = (_muxdiv_reg),	\
+		.u.composite.mux_mask = (_mux_mask),		\
+		.u.composite.div_mask = (_div_mask),		\
+		.u.composite.gate_reg = (_gate_reg),		\
+		.u.composite.gate_mask = (_gate_mask),		\
+		.u.composite.flags = (_flags),			\
+		.enable = rk_cru_composite_enable,		\
+		.get_rate = rk_cru_composite_get_rate,		\
+		.set_rate = rk_cru_composite_set_rate,		\
+		.get_parent = rk_cru_composite_get_parent,	\
+		.set_parent = rk_cru_composite_set_parent,	\
+	}
+
+/* Gate clocks */
+
+struct rk_cru_gate {
+	bus_size_t	reg;
+	uint32_t	mask;
+	const char	*parent;
+};
+
+int	rk_cru_gate_enable(struct rk_cru_softc *,
+			   struct rk_cru_clk *, int);
+const char *rk_cru_gate_get_parent(struct rk_cru_softc *,
+				   struct rk_cru_clk *);
+
+#define	RK_GATE(_id, _name, _pname, _reg, _bit)			\
+	{							\
+		.id = (_id),					\
+		.type = RK_CRU_GATE,				\
+		.base.name = (_name),				\
+		.base.flags = CLK_SET_RATE_PARENT,		\
+		.u.gate.parent = (_pname),			\
+		.u.gate.reg = (_reg),				\
+		.u.gate.mask = __BIT(_bit),			\
+		.enable = rk_cru_gate_enable,			\
+		.get_parent = rk_cru_gate_get_parent,		\
+	}
+
+/* Mux clocks */
+
+struct rk_cru_mux {
+	bus_size_t	reg;
+	uint32_t	mask;
+	const char	**parents;
+	u_int		nparents;
+	u_int		flags;
+#define	RK_MUX_GRF			0x01
+};
+
+const char *rk_cru_mux_get_parent(struct rk_cru_softc *, struct rk_cru_clk *);
+int	rk_cru_mux_set_parent(struct rk_cru_softc *, struct rk_cru_clk *, const char *);
+
+#define	RK_MUX_FLAGS(_id, _name, _parents, _reg, _mask, _flags)	\
+	{							\
+		.id = (_id),					\
+		.type = RK_CRU_MUX,				\
+		.base.name = (_name),				\
+		.base.flags = CLK_SET_RATE_PARENT,		\
+		.u.mux.parents = (_parents),			\
+		.u.mux.nparents = __arraycount(_parents),	\
+		.u.mux.reg = (_reg),				\
+		.u.mux.mask = (_mask),				\
+		.u.mux.flags = (_flags),			\
+		.set_parent = rk_cru_mux_set_parent,		\
+		.get_parent = rk_cru_mux_get_parent,		\
+	}
+#define	RK_MUX(_id, _name, _parents, _reg, _mask)		\
+	RK_MUX_FLAGS(_id, _name, _parents, _reg, _mask, 0)
+#define	RK_MUXGRF(_id, _name, _parents, _reg, _mask)		\
+	RK_MUX_FLAGS(_id, _name, _parents, _reg, _mask, RK_MUX_GRF)
+
+/*
+ * Rockchip clock definition
+ */
+
+struct rk_cru_clk {
+	struct clk	base;
+	u_int		id;
+	enum rk_cru_clktype type;
+	union {
+		struct rk_cru_pll pll;
+		struct rk_cru_arm arm;
+		struct rk_cru_composite composite;
+		struct rk_cru_gate gate;
+		struct rk_cru_mux mux;
+	} u;
+
+	int		(*enable)(struct rk_cru_softc *,
+				  struct rk_cru_clk *, int);
+	u_int		(*get_rate)(struct rk_cru_softc *,
+				    struct rk_cru_clk *);
+	int		(*set_rate)(struct rk_cru_softc *,
+				    struct rk_cru_clk *, u_int);
+	u_int		(*round_rate)(struct rk_cru_softc *,
+				    struct rk_cru_clk *, u_int);
+	const char *	(*get_parent)(struct rk_cru_softc *,
+				      struct rk_cru_clk *);
+	int		(*set_parent)(struct rk_cru_softc *,
+				      struct rk_cru_clk *,
+				      const char *);
+};
+
+/*
+ * Driver state
+ */
+
+struct rk_cru_softc {
+	device_t		sc_dev;
+	int			sc_phandle;
+	bus_space_tag_t		sc_bst;
+	bus_space_handle_t	sc_bsh;
+	bus_space_handle_t	sc_bsh_grf;
+
+	struct clk_domain	sc_clkdom;
+
+	struct rk_cru_clk	*sc_clks;
+	u_int			sc_nclks;
+};
+
+int	rk_cru_attach(struct rk_cru_softc *);
+struct rk_cru_clk *rk_cru_clock_find(struct rk_cru_softc *,
+				     const char *);
+void	rk_cru_print(struct rk_cru_softc *);
+
+#define CRU_READ(sc, reg)	\
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define CRU_WRITE(sc, reg, val)	\
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+#define	GRF_READ(sc, reg)	\
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh_grf, (reg))
+#define GRF_WRITE(sc, reg, val)	\
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh_grf, (reg), (val))
+
+#define	HAS_GRF(sc)	((sc)->sc_bsh_grf != 0)
+
+#endif /* _ARM_RK_CRU_H */
Index: src/sys/arch/arm/rockchip/rk_cru_arm.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_cru_arm.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_cru_arm.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,139 @@
+/* $NetBSD: rk_cru_arm.c,v 1.1 2018/06/16 00:19: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: rk_cru_arm.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include <dev/clk/clk_backend.h>
+
+#include <arm/rockchip/rk_cru.h>
+
+u_int
+rk_cru_arm_get_rate(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_arm *arm = &clk->u.arm;
+	struct clk *clkp, *clkp_parent;
+
+	KASSERT(clk->type == RK_CRU_ARM);
+
+	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 val = CRU_READ(sc, arm->reg);
+	const u_int div = __SHIFTOUT(val, arm->div_mask) + 1;
+
+	return fref / div;
+}
+
+int
+rk_cru_arm_set_rate(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk, u_int rate)
+{
+	struct rk_cru_arm *arm = &clk->u.arm;
+	const struct rk_cru_arm_rate *arm_rate = NULL;
+	struct clk *clkp_parent;
+	int error;
+
+	KASSERT(clk->type == RK_CRU_ARM);
+
+	if (arm->rates == NULL || rate == 0)
+		return EIO;
+
+	for (int i = 0; i < arm->nrates; i++)
+		if (arm->rates[i].rate == rate) {
+			arm_rate = &arm->rates[i];
+			break;
+		}
+	if (arm_rate == NULL)
+		return EINVAL;
+
+	error = rk_cru_arm_set_parent(sc, clk, arm->parents[arm->mux_main]);
+	if (error != 0)
+		return error;
+
+	clkp_parent = clk_get_parent(&clk->base);
+	if (clkp_parent == NULL)
+		return ENXIO;
+
+	const u_int parent_rate = arm_rate->rate / arm_rate->div;
+
+	error = clk_set_rate(clkp_parent, parent_rate);
+	if (error != 0)
+		return error;
+
+	const uint32_t write_mask = arm->div_mask << 16;
+	const uint32_t write_val = __SHIFTIN(arm_rate->div - 1, arm->div_mask);
+
+	CRU_WRITE(sc, arm->reg, write_mask | write_val);
+
+	return 0;
+}
+
+const char *
+rk_cru_arm_get_parent(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_arm *arm = &clk->u.arm;
+
+	KASSERT(clk->type == RK_CRU_ARM);
+
+	const uint32_t val = CRU_READ(sc, arm->reg);
+	const u_int mux = __SHIFTOUT(val, arm->mux_mask);
+
+	return arm->parents[mux];
+}
+
+int
+rk_cru_arm_set_parent(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk, const char *parent)
+{
+	struct rk_cru_arm *arm = &clk->u.arm;
+
+	KASSERT(clk->type == RK_CRU_ARM);
+
+	for (u_int mux = 0; mux < arm->nparents; mux++)
+		if (strcmp(arm->parents[mux], parent) == 0) {
+			const uint32_t write_mask = arm->mux_mask << 16;
+			const uint32_t write_val = __SHIFTIN(mux, arm->mux_mask);
+
+			CRU_WRITE(sc, arm->reg, write_mask | write_val);
+			return 0;
+		}
+
+	return EINVAL;
+}
Index: src/sys/arch/arm/rockchip/rk_cru_composite.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_cru_composite.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_cru_composite.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,178 @@
+/* $NetBSD: rk_cru_composite.c,v 1.1 2018/06/16 00:19: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: rk_cru_composite.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include <dev/clk/clk_backend.h>
+
+#include <arm/rockchip/rk_cru.h>
+
+int
+rk_cru_composite_enable(struct rk_cru_softc *sc, struct rk_cru_clk *clk,
+    int enable)
+{
+	struct rk_cru_composite *composite = &clk->u.composite;
+
+	KASSERT(clk->type == RK_CRU_COMPOSITE);
+
+	if (composite->gate_mask == 0)
+		return enable ? 0 : ENXIO;
+
+	const uint32_t write_mask = composite->gate_mask << 16;
+	const uint32_t write_val = __SHIFTIN(!enable, composite->gate_mask);
+
+	CRU_WRITE(sc, composite->gate_reg, write_mask | write_val);
+
+	return 0;
+}
+
+u_int
+rk_cru_composite_get_rate(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_composite *composite = &clk->u.composite;
+	struct clk *clkp, *clkp_parent;
+
+	KASSERT(clk->type == RK_CRU_COMPOSITE);
+
+	clkp = &clk->base;
+	clkp_parent = clk_get_parent(clkp);
+	if (clkp_parent == NULL)
+		return 0;
+
+	const u_int prate = clk_get_rate(clkp_parent);
+	if (prate == 0)
+		return 0;
+
+	const uint32_t val = CRU_READ(sc, composite->muxdiv_reg);
+	const u_int div = __SHIFTOUT(val, composite->div_mask) + 1;
+
+	return prate / div;
+}
+
+int
+rk_cru_composite_set_rate(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk, u_int rate)
+{
+	struct rk_cru_composite *composite = &clk->u.composite;
+	u_int best_div, best_mux, best_diff;
+	struct rk_cru_clk *clk_parent;
+
+	KASSERT(clk->type == RK_CRU_COMPOSITE);
+
+	best_div = 0;
+	best_mux = 0;
+	best_diff = INT_MAX;
+	for (u_int mux = 0; mux < composite->nparents; mux++) {
+		clk_parent = rk_cru_clock_find(sc, composite->parents[mux]);
+		if (clk_parent == NULL)
+			continue;
+		const u_int prate = clk_get_rate(&clk_parent->base);
+		if (prate == 0)
+			continue;
+
+		for (u_int div = 1; div <= __SHIFTOUT_MASK(composite->div_mask) + 1; div++) {
+			const u_int cur_rate = prate / div;
+			const int diff = (int)rate - (int)cur_rate;
+			if (composite->flags & RK_COMPOSITE_ROUND_DOWN) {
+				if (diff >= 0 && diff < best_diff) {
+					best_diff = diff;
+					best_mux = mux;
+					best_div = div;
+				}
+			} else {
+				if (abs(diff) < best_diff) {
+					best_diff = abs(diff);
+					best_mux = mux;
+					best_div = div;
+				}
+			}
+		}
+	}
+	if (best_diff == INT_MAX)
+		return ERANGE;
+
+	uint32_t write_mask = composite->div_mask << 16;
+	uint32_t write_val = __SHIFTIN(best_div - 1, composite->div_mask);
+	if (composite->mux_mask) {
+		write_mask |= composite->mux_mask << 16;
+		write_val |= __SHIFTIN(best_mux, composite->mux_mask);
+	}
+
+	CRU_WRITE(sc, composite->muxdiv_reg, write_mask | write_val);
+
+	return 0;
+}
+
+const char *
+rk_cru_composite_get_parent(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_composite *composite = &clk->u.composite;
+	uint32_t val;
+	u_int mux;
+
+	KASSERT(clk->type == RK_CRU_COMPOSITE);
+
+	if (composite->mux_mask) {
+		val = CRU_READ(sc, composite->muxdiv_reg);
+		mux = __SHIFTOUT(val, composite->mux_mask);
+	} else {
+		mux = 0;
+	}
+
+	return composite->parents[mux];
+}
+
+int
+rk_cru_composite_set_parent(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk, const char *parent)
+{
+	struct rk_cru_composite *composite = &clk->u.composite;
+
+	KASSERT(clk->type == RK_CRU_COMPOSITE);
+
+	if (!composite->mux_mask)
+		return EINVAL;
+
+	for (u_int mux = 0; mux < composite->nparents; mux++) {
+		if (strcmp(composite->parents[mux], parent) == 0) {
+			const uint32_t write_mask = composite->mux_mask << 16;
+			const uint32_t write_val = __SHIFTIN(mux, composite->mux_mask);
+
+			CRU_WRITE(sc, composite->muxdiv_reg, write_mask | write_val);
+			return 0;
+		}
+	}
+
+	return EINVAL;
+}
Index: src/sys/arch/arm/rockchip/rk_cru_gate.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_cru_gate.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_cru_gate.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,64 @@
+/* $NetBSD: rk_cru_gate.c,v 1.1 2018/06/16 00:19: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: rk_cru_gate.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include <dev/clk/clk_backend.h>
+
+#include <arm/rockchip/rk_cru.h>
+
+int
+rk_cru_gate_enable(struct rk_cru_softc *sc, struct rk_cru_clk *clk,
+    int enable)
+{
+	struct rk_cru_gate *gate = &clk->u.gate;
+
+	KASSERT(clk->type == RK_CRU_GATE);
+
+	const uint32_t write_mask = gate->mask << 16;
+	const uint32_t write_val = __SHIFTIN(!enable, gate->mask);
+
+	CRU_WRITE(sc, gate->reg, write_mask | write_val);
+
+	return 0;
+}
+
+const char *
+rk_cru_gate_get_parent(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_gate *gate = &clk->u.gate;
+
+	KASSERT(clk->type == RK_CRU_GATE);
+
+	return gate->parent;
+}
Index: src/sys/arch/arm/rockchip/rk_cru_mux.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_cru_mux.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_cru_mux.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,84 @@
+/* $NetBSD: rk_cru_mux.c,v 1.1 2018/06/16 00:19: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: rk_cru_mux.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include <dev/clk/clk_backend.h>
+
+#include <arm/rockchip/rk_cru.h>
+
+const char *
+rk_cru_mux_get_parent(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_mux *mux = &clk->u.mux;
+	const bool mux_grf = (mux->flags & RK_MUX_GRF) != 0;
+
+	KASSERT(clk->type == RK_CRU_MUX);
+
+	if (mux_grf && !HAS_GRF(sc))
+		return NULL;
+
+	const uint32_t val = mux_grf ? GRF_READ(sc, mux->reg) : CRU_READ(sc, mux->reg);
+	const u_int index = __SHIFTOUT(val, mux->mask);
+
+	return mux->parents[index];
+}
+
+int
+rk_cru_mux_set_parent(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk, const char *parent)
+{
+	struct rk_cru_mux *mux = &clk->u.mux;
+	const bool mux_grf = (mux->flags & RK_MUX_GRF) != 0;
+
+	KASSERT(clk->type == RK_CRU_MUX);
+
+	if (mux_grf && !HAS_GRF(sc))
+		return ENXIO;
+
+	for (u_int index = 0; index < mux->nparents; index++) {
+		if (strcmp(mux->parents[index], parent) == 0) {
+			const uint32_t write_mask = mux->mask << 16;
+			const uint32_t write_val = __SHIFTIN(index, mux->mask);
+
+			if (mux_grf)
+				GRF_WRITE(sc, mux->reg, write_mask | write_val);
+			else
+				CRU_WRITE(sc, mux->reg, write_mask | write_val);
+
+			return 0;
+		}
+	}
+
+	return EINVAL;
+}
Index: src/sys/arch/arm/rockchip/rk_cru_pll.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_cru_pll.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_cru_pll.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,170 @@
+/* $NetBSD: rk_cru_pll.c,v 1.1 2018/06/16 00:19: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: rk_cru_pll.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include <dev/clk/clk_backend.h>
+
+#include <arm/rockchip/rk_cru.h>
+
+#define	PLL_CON0		0x00
+#define	 PLL_BYPASS		__BIT(15)
+#define	 PLL_POSTDIV1		__BITS(14,12)
+#define	 PLL_FBDIV		__BITS(11,0)
+
+#define	PLL_CON1		0x04
+#define	 PLL_PDSEL		__BIT(15)
+#define	 PLL_PD1		__BIT(14)
+#define	 PLL_PD0		__BIT(13)
+#define	 PLL_DSMPD		__BIT(12)
+#define	 PLL_LOCK		__BIT(10)
+#define	 PLL_POSTDIV2		__BITS(8,6)
+#define	 PLL_REFDIV		__BITS(5,0)
+
+#define	PLL_CON2		0x08
+#define	 PLL_FOUT4PHASEPD	__BIT(27)
+#define	 PLL_FOUTVCOPD		__BIT(26)
+#define	 PLL_FOUTPOSTDIVPD	__BIT(25)
+#define	 PLL_DACPD		__BIT(24)
+#define	 PLL_FRACDIV		__BITS(23,0)
+
+#define	PLL_WRITE_MASK		0xffff0000	/* for CON0 and CON1 */
+
+#define	GRF_SOC_STATUS0		0x0480
+
+u_int
+rk_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 u_int postdiv1 = __SHIFTOUT(con0, PLL_POSTDIV1);
+	const u_int fbdiv = __SHIFTOUT(con0, PLL_FBDIV);
+	const u_int dsmpd = __SHIFTOUT(con1, PLL_DSMPD);
+	const u_int refdiv = __SHIFTOUT(con1, PLL_REFDIV);
+	const u_int postdiv2 = __SHIFTOUT(con1, PLL_POSTDIV2);
+	const u_int fracdiv = __SHIFTOUT(con2, PLL_FRACDIV);
+
+	if (dsmpd == 1) {
+		/* integer mode */
+		foutvco = fref / refdiv * fbdiv;
+	} else {
+		/* fractional mode */
+		foutvco = fref / refdiv * (fbdiv + fracdiv / 224);
+	}
+	foutpostdiv = foutvco / postdiv1 / postdiv2;
+
+	return foutpostdiv;
+}
+
+int
+rk_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 || !HAS_GRF(sc))
+		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;
+
+	CRU_WRITE(sc, pll->con_base + PLL_CON0,
+	    __SHIFTIN(pll_rate->postdiv1, PLL_POSTDIV1) |
+	    __SHIFTIN(pll_rate->fbdiv, PLL_FBDIV) |
+	    PLL_WRITE_MASK);
+
+	CRU_WRITE(sc, pll->con_base + PLL_CON1,
+	    __SHIFTIN(pll_rate->dsmpd, PLL_DSMPD) |
+	    __SHIFTIN(pll_rate->postdiv2, PLL_POSTDIV2) |
+	    __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);
+
+	/* 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 (GRF_READ(sc, GRF_SOC_STATUS0) & pll->lock_mask)
+			break;
+		delay(1);
+	}
+	if (retry == 0)
+		device_printf(sc->sc_dev, "WARNING: %s failed to lock\n",
+		    clk->base.name);
+
+	return 0;
+}
+
+const char *
+rk_cru_pll_get_parent(struct rk_cru_softc *sc,
+    struct rk_cru_clk *clk)
+{
+	struct rk_cru_pll *pll = &clk->u.pll;
+
+	KASSERT(clk->type == RK_CRU_PLL);
+
+	return pll->parent;
+}
Index: src/sys/arch/arm/rockchip/rk_gmac.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_gmac.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_gmac.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,323 @@
+/* $NetBSD: rk_gmac.c,v 1.1 2018/06/16 00:19: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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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: rk_gmac.c,v 1.1 2018/06/16 00:19: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/gpio.h>
+
+#include <net/if.h>
+#include <net/if_ether.h>
+#include <net/if_media.h>
+
+#include <dev/mii/miivar.h>
+
+#include <dev/ic/dwc_gmac_var.h>
+#include <dev/ic/dwc_gmac_reg.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#define	RK3328_GRF_MAC_CON0	0x0900
+#define	 RK3328_GRF_MAC_CON0_RXDLY	__BITS(13,7)
+#define	 RK3328_GRF_MAC_CON0_TXDLY	__BITS(6,0)
+
+#define	RK3328_GRF_MAC_CON1	0x0904
+#define	 RK3328_GRF_MAC_CON1_CLKSEL	__BITS(12,11)
+#define	  RK3328_GRF_MAC_CON1_CLKSEL_125M	0
+#define	  RK3328_GRF_MAC_CON1_CLKSEL_2_5M	2
+#define	  RK3328_GRF_MAC_CON1_CLKSEL_25M	3
+#define	 RK3328_GRF_MAC_CON1_MODE	__BIT(9)
+#define	 RK3328_GRF_MAC_CON1_SEL	__BITS(6,4)
+#define	  RK3328_GRF_MAC_CON1_SEL_RGMII	1
+#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;
+	bus_space_handle_t	sc_grf_bsh;
+};
+
+static int
+rk_gmac_reset(const int phandle)
+{
+#if notyet
+	struct fdtbus_gpio_pin *pin_reset;
+	const u_int *reset_delay_us;
+	bool reset_active_low;
+	int len, val;
+
+	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");
+
+	val = reset_active_low ? 1 : 0;
+
+	fdtbus_gpio_write(pin_reset, val);
+	if (be32toh(reset_delay_us[0]) > 0)
+		delay(be32toh(reset_delay_us[0]));
+	fdtbus_gpio_write(pin_reset, !val);
+	if (be32toh(reset_delay_us[1]) > 0)
+		delay(be32toh(reset_delay_us[1]));
+	fdtbus_gpio_write(pin_reset, val);
+	if (be32toh(reset_delay_us[2]) > 0)
+		delay(be32toh(reset_delay_us[2]));
+#endif
+
+	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)
+{
+	struct rk_gmac_softc * const rk_sc = (struct rk_gmac_softc *)sc;
+
+	const uint32_t write_mask =
+	    (RK3328_GRF_MAC_CON1_MODE | RK3328_GRF_MAC_CON1_SEL |
+	     RK3328_GRF_MAC_CON1_RXDLY_EN | RK3328_GRF_MAC_CON1_TXDLY_EN) << 16;
+	const uint32_t write_val =
+	    __SHIFTIN(RK3328_GRF_MAC_CON1_SEL_RGMII, RK3328_GRF_MAC_CON1_SEL) |
+	    RK3328_GRF_MAC_CON1_RXDLY_EN | RK3328_GRF_MAC_CON1_TXDLY_EN;
+
+	bus_space_write_4(sc->sc_bst, rk_sc->sc_grf_bsh, RK3328_GRF_MAC_CON1, write_mask | write_val);
+	bus_space_write_4(sc->sc_bst, rk_sc->sc_grf_bsh, RK3328_GRF_MAC_CON0,
+	    (RK3328_GRF_MAC_CON0_TXDLY << 16) |
+	    (RK3328_GRF_MAC_CON0_RXDLY << 16) |
+	    __SHIFTIN(tx_delay, RK3328_GRF_MAC_CON0_TXDLY) |
+	    __SHIFTIN(rx_delay, RK3328_GRF_MAC_CON0_RXDLY));
+}
+
+static void
+rk3328_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 = RK3328_GRF_MAC_CON1_CLKSEL_2_5M;
+		break;
+	case IFM_100_TX:
+		clksel = RK3328_GRF_MAC_CON1_CLKSEL_25M;
+		break;
+	default:
+		clksel = RK3328_GRF_MAC_CON1_CLKSEL_125M;
+		break;
+	}
+
+	bus_space_write_4(sc->sc_bst, rk_sc->sc_grf_bsh, RK3328_GRF_MAC_CON1,
+	    (RK3328_GRF_MAC_CON1_CLKSEL << 16) |
+	    __SHIFTIN(RK3328_GRF_MAC_CON1_CLKSEL_125M, RK3328_GRF_MAC_CON1_CLKSEL));
+}
+
+static int
+rk_gmac_setup_clocks(int phandle)
+{
+	static const char * const clknames[] = {
+#if 0
+		"stmmaceth",
+		"mac_clk_rx", 
+		"mac_clk_tx",
+		"clk_mac_ref",
+		"clk_mac_refout",
+		"aclk_mac",
+		"pclk_mac"
+#else
+		"stmmaceth",
+		"aclk_mac",
+		"pclk_mac",
+		"mac_clk_tx",
+#endif
+	};
+	static const char * const rstnames[] = {
+		"stmmaceth"
+	};
+	struct fdtbus_reset *rst;
+	struct clk *clk;
+	int error, n;
+
+	fdtbus_clock_assign(phandle);
+
+	for (n = 0; n < __arraycount(clknames); n++) {
+		clk = fdtbus_clock_get(phandle, clknames[n]);
+		if (clk == NULL) {
+			aprint_error(": couldn't get %s clock\n", clknames[n]);
+			return ENXIO;
+		}
+		error = clk_enable(clk);
+		if (error != 0) {
+			aprint_error(": couldn't enable %s clock: %d\n",
+			    clknames[n], error);
+			return error;
+		}
+	}
+
+	for (n = 0; n < __arraycount(rstnames); n++) {
+		rst = fdtbus_reset_get(phandle, rstnames[n]);
+		if (rst == NULL) {
+			aprint_error(": couldn't get %s reset\n", rstnames[n]);
+			return ENXIO;
+		}
+		error = fdtbus_reset_deassert(rst);
+		if (error != 0) {
+			aprint_error(": couldn't de-assert %s reset: %d\n",
+			    rstnames[n], error);
+			return error;
+		}
+	}
+
+	delay(5000);
+
+	return 0;
+}
+
+static int
+rk_gmac_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
+rk_gmac_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk_gmac_softc * const rk_sc = device_private(self);
+	struct dwc_gmac_softc * const sc = &rk_sc->sc_base;
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	const char *phy_mode;
+	char intrstr[128];
+	bus_addr_t addr, grf_addr;
+	bus_size_t size, grf_size;
+	u_int tx_delay, rx_delay;
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	const int grf_phandle = fdtbus_get_phandle(phandle, "rockchip,grf");
+	if (grf_phandle == -1) {
+		aprint_error(": couldn't get grf phandle\n");
+		return;
+	}
+	if (fdtbus_get_reg(grf_phandle, 0, &grf_addr, &grf_size) != 0) {
+		aprint_error(": couldn't get grf registers\n");
+		return;
+	}
+	if (bus_space_map(faa->faa_bst, grf_addr, grf_size, 0, &rk_sc->sc_grf_bsh) != 0) {
+		aprint_error(": couldn't map grf registers\n");
+		return;
+	}
+
+	if (of_getprop_uint32(phandle, "tx_delay", &tx_delay) != 0)
+		tx_delay = RK_GMAC_TXDLY_DEFAULT;
+
+	if (of_getprop_uint32(phandle, "rx_delay", &rx_delay) != 0)
+		rx_delay = RK_GMAC_RXDLY_DEFAULT;
+
+	sc->sc_dev = self;
+	sc->sc_bst = faa->faa_bst;
+	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+	sc->sc_dmat = faa->faa_dmat;
+
+	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+		aprint_error(": failed to decode interrupt\n");
+		return;
+	}
+
+	if (rk_gmac_setup_clocks(phandle) != 0)
+		return;
+
+	if (rk_gmac_reset(phandle) != 0)
+		aprint_error_dev(self, "PHY reset failed\n");
+
+	if (of_hasprop(phandle, "snps,force_thresh_dma_mode"))
+		sc->sc_flags |= DWC_GMAC_FORCE_THRESH_DMA_MODE;
+
+	phy_mode = fdtbus_get_string(phandle, "phy-mode");
+	if (phy_mode == NULL) {
+		aprint_error(": missing 'phy-mode' property\n");
+		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;
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": GMAC\n");
+
+	if (fdtbus_intr_establish(phandle, 0, IPL_NET, 0, rk_gmac_intr, sc) == NULL) {
+		aprint_error_dev(self, "failed to establish interrupt on %s\n", intrstr);
+		return;
+	}
+	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
+
+	dwc_gmac_attach(sc, GMAC_MII_CLK_150_250M_DIV102);
+}
+
+CFATTACH_DECL_NEW(rk_gmac, sizeof(struct rk_gmac_softc),
+	rk_gmac_match, rk_gmac_attach, NULL, NULL);
Index: src/sys/arch/arm/rockchip/rk_gpio.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_gpio.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_gpio.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,323 @@
+/* $NetBSD: rk_gpio.c,v 1.1 2018/06/16 00:19: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: rk_gpio.c,v 1.1 2018/06/16 00:19: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/gpio.h>
+#include <sys/bitops.h>
+#include <sys/lwp.h>
+
+#include <dev/fdt/fdtvar.h>
+#include <dev/gpio/gpiovar.h>
+
+#define	GPIO_SWPORTA_DR_REG		0x0000
+#define	GPIO_SWPORTA_DDR_REG		0x0004
+#define	GPIO_INTEN_REG			0x0030
+#define	GPIO_INTMASK_REG		0x0034
+#define	GPIO_INTTYPE_LEVEL_REG		0x0038
+#define	GPIO_INT_POLARITY_REG		0x003c
+#define	GPIO_INT_STATUS_REG		0x0040
+#define	GPIO_INT_RAWSTATUS_REG		0x0044
+#define	GPIO_DEBOUNCE_REG		0x0048
+#define	GPIO_PORTA_EOI_REG		0x004c
+#define	GPIO_EXT_PORTA_REG		0x0050
+#define	GPIO_LS_SYNC_REG		0x0060
+
+static const char * const compatible[] = {
+	"rockchip,gpio-bank",
+	NULL
+};
+
+struct rk_gpio_softc {
+	device_t sc_dev;
+	bus_space_tag_t sc_bst;
+	bus_space_handle_t sc_bsh;
+	kmutex_t sc_lock;
+
+	struct gpio_chipset_tag sc_gp;
+	gpio_pin_t sc_pins[32];
+	device_t sc_gpiodev;
+};
+
+struct rk_gpio_pin {
+	struct rk_gpio_softc *pin_sc;
+	u_int pin_nr;
+	int pin_flags;
+	bool pin_actlo;
+};
+
+#define RD4(sc, reg) 		\
+    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define WR4(sc, reg, val) 	\
+    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static int	rk_gpio_match(device_t, cfdata_t, void *);
+static void	rk_gpio_attach(device_t, device_t, void *);
+
+CFATTACH_DECL_NEW(rk_gpio, sizeof(struct rk_gpio_softc),
+	rk_gpio_match, rk_gpio_attach, NULL, NULL);
+
+static int
+rk_gpio_ctl(struct rk_gpio_softc *sc, u_int pin, int flags)
+{
+	uint32_t ddr;
+
+	KASSERT(mutex_owned(&sc->sc_lock));
+
+	ddr = RD4(sc, GPIO_SWPORTA_DDR_REG);
+	if (flags & GPIO_PIN_INPUT)
+		ddr &= ~__BIT(pin);
+	else if (flags & GPIO_PIN_OUTPUT)
+		ddr |= __BIT(pin);
+	WR4(sc, GPIO_SWPORTA_DDR_REG, ddr);
+
+	return 0;
+}
+
+static void *
+rk_gpio_acquire(device_t dev, const void *data, size_t len, int flags)
+{
+	struct rk_gpio_softc * const sc = device_private(dev);
+	struct rk_gpio_pin *gpin;
+	const u_int *gpio = data;
+	int error;
+
+	if (len != 12)
+		return NULL;
+
+	const uint8_t pin = be32toh(gpio[1]) & 0xff;
+	const bool actlo = be32toh(gpio[2]) & 1;
+
+	if (pin >= __arraycount(sc->sc_pins))
+		return NULL;
+
+	mutex_enter(&sc->sc_lock);
+	error = rk_gpio_ctl(sc, pin, flags);
+	mutex_exit(&sc->sc_lock);
+
+	if (error != 0)
+		return NULL;
+
+	gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP);
+	gpin->pin_sc = sc;
+	gpin->pin_nr = pin;
+	gpin->pin_flags = flags;
+	gpin->pin_actlo = actlo;
+
+	return gpin;
+}
+
+static void
+rk_gpio_release(device_t dev, void *priv)
+{
+	struct rk_gpio_softc * const sc = device_private(dev);
+	struct rk_gpio_pin *pin = priv;
+
+	mutex_enter(&sc->sc_lock);
+	rk_gpio_ctl(pin->pin_sc, pin->pin_nr, GPIO_PIN_INPUT);
+	mutex_exit(&sc->sc_lock);
+
+	kmem_free(pin, sizeof(*pin));
+}
+
+static int
+rk_gpio_read(device_t dev, void *priv, bool raw)
+{
+	struct rk_gpio_softc * const sc = device_private(dev);
+	struct rk_gpio_pin *pin = priv;
+	uint32_t data;
+	int val;
+
+	KASSERT(sc == pin->pin_sc);
+
+	const uint32_t data_mask = __BIT(pin->pin_nr);
+
+	/* No lock required for reads */
+	data = RD4(sc, GPIO_EXT_PORTA_REG);
+	val = __SHIFTOUT(data, data_mask);
+	if (!raw && pin->pin_actlo)
+		val = !val;
+
+	return val;
+}
+
+static void
+rk_gpio_write(device_t dev, void *priv, int val, bool raw)
+{
+	struct rk_gpio_softc * const sc = device_private(dev);
+	struct rk_gpio_pin *pin = priv;
+	uint32_t data;
+
+	KASSERT(sc == pin->pin_sc);
+
+	const uint32_t data_mask = __BIT(pin->pin_nr);
+
+	if (!raw && pin->pin_actlo)
+		val = !val;
+
+	mutex_enter(&sc->sc_lock);
+	data = RD4(sc, GPIO_SWPORTA_DR_REG);
+	data &= ~data_mask;
+	data |= __SHIFTIN(val, data_mask);
+	WR4(sc, GPIO_SWPORTA_DR_REG, data);
+	mutex_exit(&sc->sc_lock);
+}
+
+static struct fdtbus_gpio_controller_func rk_gpio_funcs = {
+	.acquire = rk_gpio_acquire,
+	.release = rk_gpio_release,
+	.read = rk_gpio_read,
+	.write = rk_gpio_write,
+};
+
+static int
+rk_gpio_pin_read(void *priv, int pin)
+{
+	struct rk_gpio_softc * const sc = priv;
+	uint32_t data;
+	int val;
+
+	KASSERT(pin < __arraycount(sc->sc_pins));
+
+	const uint32_t data_mask = __BIT(pin);
+
+	/* No lock required for reads */
+	data = RD4(sc, GPIO_SWPORTA_DR_REG);
+	val = __SHIFTOUT(data, data_mask);
+
+	return val;
+}
+
+static void
+rk_gpio_pin_write(void *priv, int pin, int val)
+{
+	struct rk_gpio_softc * const sc = priv;
+	uint32_t data;
+
+	KASSERT(pin < __arraycount(sc->sc_pins));
+
+	const uint32_t data_mask = __BIT(pin);
+
+	mutex_enter(&sc->sc_lock);
+	data = RD4(sc, GPIO_SWPORTA_DR_REG);
+	if (val)
+		data |= data_mask;
+	else
+		data &= ~data_mask;
+	WR4(sc, GPIO_SWPORTA_DR_REG, data);
+	mutex_exit(&sc->sc_lock);
+}
+
+static void
+rk_gpio_pin_ctl(void *priv, int pin, int flags)
+{
+	struct rk_gpio_softc * const sc = priv;
+
+	KASSERT(pin < __arraycount(sc->sc_pins));
+
+	mutex_enter(&sc->sc_lock);
+	rk_gpio_ctl(sc, pin, flags);
+	mutex_exit(&sc->sc_lock);
+}
+
+static void
+rk_gpio_attach_ports(struct rk_gpio_softc *sc)
+{
+	struct gpio_chipset_tag *gp = &sc->sc_gp;
+	struct gpiobus_attach_args gba;
+	u_int pin;
+
+	gp->gp_cookie = sc;
+	gp->gp_pin_read = rk_gpio_pin_read;
+	gp->gp_pin_write = rk_gpio_pin_write;
+	gp->gp_pin_ctl = rk_gpio_pin_ctl;
+
+	for (pin = 0; pin < __arraycount(sc->sc_pins); pin++) {
+		sc->sc_pins[pin].pin_num = pin;
+		sc->sc_pins[pin].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+		sc->sc_pins[pin].pin_state = rk_gpio_pin_read(sc, pin);
+	}
+
+	memset(&gba, 0, sizeof(gba));
+	gba.gba_gc = gp;
+	gba.gba_pins = sc->sc_pins;
+	gba.gba_npins = __arraycount(sc->sc_pins);
+	sc->sc_gpiodev = config_found_ia(sc->sc_dev, "gpiobus", &gba, NULL);
+}
+
+static int
+rk_gpio_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
+rk_gpio_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk_gpio_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	struct clk *clk;
+	bus_addr_t addr;
+	bus_size_t size;
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	if ((clk = fdtbus_clock_get_index(phandle, 0)) == NULL || clk_enable(clk) != 0) {
+		aprint_error(": couldn't enable clock\n");
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_bst = faa->faa_bst;
+	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
+
+	aprint_naive("\n");
+	aprint_normal(": GPIO (%s)\n", fdtbus_get_string(phandle, "name"));
+
+	fdtbus_register_gpio_controller(self, phandle, &rk_gpio_funcs);
+
+	rk_gpio_attach_ports(sc);
+}
Index: src/sys/arch/arm/rockchip/rk_iomux.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_iomux.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_iomux.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,329 @@
+/* $NetBSD: rk_iomux.c,v 1.1 2018/06/16 00:19: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: rk_iomux.c,v 1.1 2018/06/16 00:19: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>
+
+#define	GRF_GPIO_IOMUX_REG(_bank, _idx)	(0x0000 + (_bank) * 0x10 + ((_idx) >> 3) * 4)
+#define	 GRF_GPIO_IOMUX_SEL(_idx)	(0x3 << (((_idx) & 7) * 2))
+#define	  GRF_GPIO_IOMUX_SEL_MASK	0x3
+#define	 GRF_GPIO_IOMUX_WRITE_EN(_idx)	(0x3 << (((_idx) & 7) * 2 + 16))
+
+#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 rk_iomux {
+	bus_size_t		base;
+	u_int			type;
+#define	RK_IOMUX_TYPE_3BIT	0x01
+};
+
+struct rk_iomux_bank {
+	struct rk_iomux		iomux[4];
+};
+
+#define	RK_IOMUX(_reg, _shift, _mask)	\
+	{ .reg = (_reg), .shift = (_shift), .mask = (_mask) }
+
+static const struct rk_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 = RK_IOMUX_TYPE_3BIT },
+			[2] = { .base = 0x002c, .type = RK_IOMUX_TYPE_3BIT },
+			[3] = { .base = 0x0034 },
+		},
+	},
+	[3] = {
+		.iomux = {
+			[0] = { .base = 0x0038, .type = RK_IOMUX_TYPE_3BIT },
+			[1] = { .base = 0x0040, .type = RK_IOMUX_TYPE_3BIT },
+			[2] = { .base = 0x0048 },
+			[3] = { .base = 0x004c },
+		},
+	},
+};
+
+struct rk_iomux_config {
+	const struct rk_iomux_bank *banks;
+	u_int nbanks;
+};
+
+static const struct rk_iomux_config rk3328_iomux_config = {
+	.banks = rk3328_iomux_banks,
+	.nbanks = __arraycount(rk3328_iomux_banks),
+};
+
+static const struct of_compat_data compat_data[] = {
+	{ "rockchip,rk3328-pinctrl",	(uintptr_t)&rk3328_iomux_config },
+	{ NULL }
+};
+
+struct rk_iomux_softc {
+	device_t sc_dev;
+	bus_space_tag_t sc_bst;
+	bus_space_handle_t sc_bsh;
+
+	const struct rk_iomux_config *sc_conf;
+};
+
+#define RD4(sc, reg) 		\
+    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define WR4(sc, reg, val) 	\
+    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static int	rk_iomux_match(device_t, cfdata_t, void *);
+static void	rk_iomux_attach(device_t, device_t, void *);
+
+CFATTACH_DECL_NEW(rk_iomux, sizeof(struct rk_iomux_softc),
+	rk_iomux_match, rk_iomux_attach, NULL, NULL);
+
+static void
+rk_iomux_calc_iomux_reg(struct rk_iomux_softc *sc, u_int bank, u_int pin, bus_size_t *reg, uint32_t *mask)
+{
+	const struct rk_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 & RK_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
+rk_iomux_set_bias(struct rk_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
+rk_iomux_set_drive_strength(struct rk_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
+rk_iomux_set_mux(struct rk_iomux_softc *sc, u_int bank, u_int idx, u_int mux)
+{
+	bus_size_t reg;
+	uint32_t mask;
+
+	rk_iomux_calc_iomux_reg(sc, bank, idx, &reg, &mask);
+
+	WR4(sc, reg, (mask << 16) | __SHIFTIN(mux, mask));
+}
+
+static int
+rk_iomux_config(struct rk_iomux_softc *sc, const int phandle, u_int bank, u_int idx, u_int mux)
+{
+	u_int drv;
+
+	if (of_hasprop(phandle, "bias-disable"))
+		rk_iomux_set_bias(sc, bank, idx, GRF_GPIO_P_CTL_Z);
+	else if (of_hasprop(phandle, "bias-pull-up"))
+		rk_iomux_set_bias(sc, bank, idx, GRF_GPIO_P_CTL_PULLUP);
+	else if (of_hasprop(phandle, "bias-pull-down"))
+		rk_iomux_set_bias(sc, bank, idx, GRF_GPIO_P_CTL_PULLDOWN);
+
+	if (of_getprop_uint32(phandle, "drive-strength", &drv) == 0) {
+		switch (drv) {
+		case 2:
+			rk_iomux_set_drive_strength(sc, bank, idx, GRF_GPIO_E_CTL_2MA);
+			break;
+		case 4:
+			rk_iomux_set_drive_strength(sc, bank, idx, GRF_GPIO_E_CTL_4MA);
+			break;
+		case 8:
+			rk_iomux_set_drive_strength(sc, bank, idx, GRF_GPIO_E_CTL_8MA);
+			break;
+		case 12:
+			rk_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"))
+		rk_iomux_set_direction(sc, bank, idx, GPIO_PIN_INPUT, -1);
+	else if (of_hasprop(phandle, "output-high"))
+		rk_iomux_set_direction(sc, bank, idx, GPIO_PIN_OUTPUT, GPIO_PIN_HIGH);
+	else if (of_hasprop(phandle, "output-low"))
+		rk_iomux_set_direction(sc, bank, idx, GPIO_PIN_OUTPUT, GPIO_PIN_LOW);
+#endif
+
+	rk_iomux_set_mux(sc, bank, idx, mux);
+
+	return 0;
+}
+
+static int
+rk_iomux_pinctrl_set_config(device_t dev, const void *data, size_t len)
+{
+	struct rk_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]));
+
+		rk_iomux_config(sc, cfg, bank, idx, mux);
+
+		pins_len -= 16;
+		pins += 4;
+	}
+
+	return 0;
+}
+
+static struct fdtbus_pinctrl_controller_func rk_iomux_pinctrl_funcs = {
+	.set_config = rk_iomux_pinctrl_set_config,
+};
+
+static int
+rk_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
+rk_iomux_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk_iomux_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	bus_addr_t addr;
+	bus_size_t size;
+	int child, sub;
+
+	const int grf_phandle = fdtbus_get_phandle(phandle, "rockchip,grf");
+	if (grf_phandle == -1) {
+		aprint_error(": couldn't get grf phandle\n");
+		return;
+	}
+
+	if (fdtbus_get_reg(grf_phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get grf registers\n");
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_bst = faa->faa_bst;
+	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+	sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data;
+
+	aprint_naive("\n");
+	aprint_normal(": 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, &rk_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/rk_platform.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_platform.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_platform.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,160 @@
+/* $NetBSD: rk_platform.c,v 1.1 2018/06/16 00:19: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 "opt_soc.h"
+#include "opt_multiprocessor.h"
+#include "opt_fdt_arm.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: rk_platform.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/device.h>
+#include <sys/termios.h>
+
+#include <dev/fdt/fdtvar.h>
+#include <arm/fdt/arm_fdtvar.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/bootconfig.h>
+#include <arm/cpufunc.h>
+
+#include <arm/cortex/gtmr_var.h>
+
+#include <dev/ic/ns16550reg.h>
+#include <dev/ic/comreg.h>
+
+#include <arm/arm/psci.h>
+#include <arm/fdt/psci_fdt.h>
+
+#include <libfdt.h>
+
+extern struct arm32_bus_dma_tag arm_generic_dma_tag;
+extern struct bus_space arm_generic_bs_tag;
+extern struct bus_space arm_generic_a4x_bs_tag;
+
+static void
+rk_platform_init_attach_args(struct fdt_attach_args *faa)
+{
+	faa->faa_bst = &arm_generic_bs_tag;
+	faa->faa_a4x_bst = &arm_generic_a4x_bs_tag;
+	faa->faa_dmat = &arm_generic_dma_tag;
+}
+
+static void
+rk_platform_device_register(device_t self, void *aux)
+{
+}
+
+static void
+rk_platform_bootstrap(void)
+{
+	void *fdt_data = __UNCONST(fdtbus_get_data());
+
+	psci_fdt_bootstrap();
+
+	const int chosen_off = fdt_path_offset(fdt_data, "/chosen");
+	if (chosen_off < 0)
+		return;
+
+	if (match_bootconf_option(boot_args, "console", "fb")) {
+		const int framebuffer_off =
+		    fdt_path_offset(fdt_data, "/chosen/framebuffer");
+		if (framebuffer_off >= 0) {
+			const char *status = fdt_getprop(fdt_data,
+			    framebuffer_off, "status", NULL);
+			if (status == NULL || strncmp(status, "ok", 2) == 0) {
+				fdt_setprop_string(fdt_data, chosen_off,
+				    "stdout-path", "/chosen/framebuffer");
+			}
+		}
+	} else if (match_bootconf_option(boot_args, "console", "serial")) {
+		fdt_setprop_string(fdt_data, chosen_off,
+		    "stdout-path", "serial0:115200n8");
+	}
+}
+
+#ifdef SOC_RK3328
+
+#include <arm/rockchip/rk3328_platform.h>
+
+static const struct pmap_devmap *
+rk3328_platform_devmap(void)
+{
+	static const struct pmap_devmap devmap[] = {
+		DEVMAP_ENTRY(RK3328_CORE_VBASE,
+			     RK3328_CORE_PBASE,
+			     RK3328_CORE_SIZE),
+		DEVMAP_ENTRY_END
+	};
+
+	return devmap;
+}
+
+void rk3328_platform_early_putchar(char);
+
+void
+rk3328_platform_early_putchar(char c)
+{
+#ifdef CONSADDR
+#define CONSADDR_VA	((CONSADDR - RK3328_CORE_PBASE) + RK3328_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
+rk3328_platform_uart_freq(void)
+{
+	return RK3328_UART_FREQ;
+}
+
+static const struct arm_platform rk3328_platform = {
+	.devmap = rk3328_platform_devmap,
+	.bootstrap = rk_platform_bootstrap,
+	.init_attach_args = rk_platform_init_attach_args,
+	.early_putchar = rk3328_platform_early_putchar,
+	.device_register = rk_platform_device_register,
+	.reset = psci_fdt_reset,
+	.delay = gtmr_delay,
+	.uart_freq = rk3328_platform_uart_freq,
+};
+
+ARM_PLATFORM(rk3328, "rockchip,rk3328", &rk3328_platform);
+
+#endif /* SOC_RK3328 */
Index: src/sys/arch/arm/rockchip/rk_usb.c
diff -u /dev/null src/sys/arch/arm/rockchip/rk_usb.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/arch/arm/rockchip/rk_usb.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,316 @@
+/* $NetBSD: rk_usb.c,v 1.1 2018/06/16 00:19: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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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: rk_usb.c,v 1.1 2018/06/16 00:19: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/time.h>
+#include <sys/kmem.h>
+
+#include <dev/clk/clk_backend.h>
+
+#include <dev/fdt/fdtvar.h>
+
+static int rk_usb_match(device_t, cfdata_t, void *);
+static void rk_usb_attach(device_t, device_t, void *);
+
+#define	CON0_REG	0x00
+#define	CON1_REG	0x04
+#define	CON2_REG	0x08
+#define	 USBPHY_COMMONONN	__BIT(4)
+
+enum rk_usb_type {
+	USB_RK3328 = 1,
+};
+
+static const struct of_compat_data compat_data[] = {
+	{ "rockchip,rk3328-usb2phy",		USB_RK3328 },
+	{ NULL }
+};
+
+struct rk_usb_clk {
+	struct clk		base;
+	bus_size_t		reg;
+};
+
+struct rk_usb_softc {
+	device_t		sc_dev;
+	bus_space_tag_t		sc_bst;
+	bus_space_handle_t	sc_bsh;
+	enum rk_usb_type	sc_type;
+
+	struct clk_domain	sc_clkdom;
+	struct rk_usb_clk	sc_usbclk;
+};
+
+#define USB_READ(sc, reg)			\
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define USB_WRITE(sc, reg, val)			\
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+CFATTACH_DECL_NEW(rk_usb, sizeof(struct rk_usb_softc),
+	rk_usb_match, rk_usb_attach, NULL, NULL);
+
+static struct clk *
+rk_usb_clk_get(void *priv, const char *name)
+{
+	struct rk_usb_softc * const sc = priv;
+
+	if (strcmp(name, sc->sc_usbclk.base.name) != 0)
+		return NULL;
+
+	return &sc->sc_usbclk.base;
+}
+
+static void
+rk_usb_clk_put(void *priv, struct clk *clk)
+{
+}
+
+static u_int
+rk_usb_clk_get_rate(void *priv, struct clk *clk)
+{
+	return 480000000;
+}
+
+static int
+rk_usb_clk_enable(void *priv, struct clk *clk)
+{
+	struct rk_usb_softc * const sc = priv;
+
+	const uint32_t write_mask = USBPHY_COMMONONN << 16;
+	const uint32_t write_val = 0;
+	USB_WRITE(sc, CON2_REG, write_mask | write_val);
+
+	return 0;
+}
+
+static int
+rk_usb_clk_disable(void *priv, struct clk *clk)
+{
+	struct rk_usb_softc * const sc = priv;
+
+	const uint32_t write_mask = USBPHY_COMMONONN << 16;
+	const uint32_t write_val = USBPHY_COMMONONN;
+	USB_WRITE(sc, CON2_REG, write_mask | write_val);
+
+	return 0;
+}
+
+static const struct clk_funcs rk_usb_clk_funcs = {
+	.get = rk_usb_clk_get,
+	.put = rk_usb_clk_put,
+	.get_rate = rk_usb_clk_get_rate,
+	.enable = rk_usb_clk_enable,
+	.disable = rk_usb_clk_disable,
+};
+
+static struct clk *
+rk_usb_fdt_decode(device_t dev, const void *data, size_t len)
+{
+	struct rk_usb_softc * const sc = device_private(dev);
+
+	if (len != 0)
+		return NULL;
+
+	return &sc->sc_usbclk.base;
+}
+
+static const struct fdtbus_clock_controller_func rk_usb_fdt_funcs = {
+	.decode = rk_usb_fdt_decode
+};
+
+static int
+rk_usb_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
+rk_usb_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk_usb_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	bus_addr_t grf_addr, phy_addr, phy_size;
+	int child;
+
+	sc->sc_dev = self;
+	sc->sc_bst = faa->faa_bst;
+	sc->sc_type = of_search_compatible(phandle, compat_data)->data;
+
+	if (fdtbus_get_reg(OF_parent(phandle), 0, &grf_addr, NULL) != 0) {
+		aprint_error(": couldn't get grf registers\n");
+	}
+	if (fdtbus_get_reg(phandle, 0, &phy_addr, &phy_size) != 0) {
+		aprint_error(": couldn't get phy registers\n");
+		return;
+	}
+	if (bus_space_map(sc->sc_bst, grf_addr + phy_addr, phy_size, 0, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map phy registers\n");
+		return;
+	}
+
+	const char *clkname = fdtbus_get_string(phandle, "clock-output-names");
+	if (clkname == NULL)
+		clkname = faa->faa_name;
+
+	sc->sc_clkdom.name = device_xname(self);
+	sc->sc_clkdom.funcs = &rk_usb_clk_funcs;
+	sc->sc_clkdom.priv = sc;
+	sc->sc_usbclk.base.domain = &sc->sc_clkdom;
+	sc->sc_usbclk.base.name = kmem_asprintf("%s", clkname);
+	clk_attach(&sc->sc_usbclk.base);
+
+	aprint_naive("\n");
+	aprint_normal(": USB2 PHY\n");
+
+	fdtbus_register_clock_controller(self, phandle, &rk_usb_fdt_funcs);
+
+	for (child = OF_child(phandle); child; child = OF_peer(child)) {
+		if (!fdtbus_status_okay(child))
+			continue;
+
+		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);
+	}
+}
+
+/*
+ * USB PHY
+ */
+
+static int rk_usbphy_match(device_t, cfdata_t, void *);
+static void rk_usbphy_attach(device_t, device_t, void *);
+
+struct rk_usbphy_softc {
+	device_t	sc_dev;
+	int		sc_phandle;
+};
+
+CFATTACH_DECL_NEW(rk_usbphy, sizeof(struct rk_usbphy_softc),
+	rk_usbphy_match, rk_usbphy_attach, NULL, NULL);
+
+static void *
+rk_usbphy_acquire(device_t dev, const void *data, size_t len)
+{
+	struct rk_usbphy_softc * const sc = device_private(dev);
+
+	if (len != 0)
+		return NULL;
+
+	return sc;
+}
+
+static void
+rk_usbphy_release(device_t dev, void *priv)
+{
+}
+
+static int
+rk_usbphy_otg_enable(device_t dev, void *priv, bool enable)
+{
+	struct rk_usb_softc * const usb_sc = device_private(device_parent(dev));
+
+	const uint32_t write_mask = 0x1ffU << 16;
+	const uint32_t write_val = enable ? 0 : 0x1d1;
+	USB_WRITE(usb_sc, CON0_REG, write_mask | write_val);
+
+	return 0;
+}
+
+static int
+rk_usbphy_host_enable(device_t dev, void *priv, bool enable)
+{
+	struct rk_usb_softc * const usb_sc = device_private(device_parent(dev));
+
+	const uint32_t write_mask = 0x1ffU << 16;
+	const uint32_t write_val = enable ? 0 : 0x1d1;
+	USB_WRITE(usb_sc, CON1_REG, write_mask | write_val);
+
+	return 0;
+}
+
+const struct fdtbus_phy_controller_func rk_usbphy_otg_funcs = {
+	.acquire = rk_usbphy_acquire,
+	.release = rk_usbphy_release,
+	.enable = rk_usbphy_otg_enable,
+};
+
+const struct fdtbus_phy_controller_func rk_usbphy_host_funcs = {
+	.acquire = rk_usbphy_acquire,
+	.release = rk_usbphy_release,
+	.enable = rk_usbphy_host_enable,
+};
+
+static int
+rk_usbphy_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	const char *name = fdtbus_get_string(phandle, "name");
+
+	if (strcmp(name, "otg-port") == 0 || strcmp(name, "host-port") == 0)
+		return 1;
+
+	return 0;
+}
+
+static void
+rk_usbphy_attach(device_t parent, device_t self, void *aux)
+{
+	struct rk_usbphy_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	const char *name = fdtbus_get_string(phandle, "name");
+
+	sc->sc_dev = self;
+	sc->sc_phandle = phandle;
+
+	aprint_naive("\n");
+
+	if (strcmp(name, "otg-port") == 0) {
+		aprint_normal(": USB2 OTG port\n");
+		fdtbus_register_phy_controller(self, phandle, &rk_usbphy_otg_funcs);
+	} else if (strcmp(name, "host-port") == 0) {
+		aprint_normal(": USB2 host port\n");
+		fdtbus_register_phy_controller(self, phandle, &rk_usbphy_host_funcs);
+	}
+}

Index: src/sys/dev/fdt/dwc2_fdt.c
diff -u /dev/null src/sys/dev/fdt/dwc2_fdt.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/dev/fdt/dwc2_fdt.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,207 @@
+/*	$NetBSD: dwc2_fdt.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $	*/
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nick Hudson
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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: dwc2_fdt.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/mutex.h>
+#include <sys/bus.h>
+#include <sys/workqueue.h>
+
+#include <arm/broadcom/bcm2835reg.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+
+#include <dwc2/dwc2var.h>
+
+#include <dwc2/dwc2.h>
+#include "dwc2_core.h"
+
+struct dwc2_fdt_softc {
+	struct dwc2_softc	sc_dwc2;
+
+	struct dwc2_core_params	sc_params;
+
+	void			*sc_ih;
+	int			sc_phandle;
+};
+
+static int dwc2_fdt_match(device_t, struct cfdata *, void *);
+static void dwc2_fdt_attach(device_t, device_t, void *);
+static void dwc2_fdt_deferred(device_t);
+
+static void dwc2_fdt_rockchip_params(struct dwc2_fdt_softc *, struct dwc2_core_params *);
+
+struct dwc2_fdt_config {
+	void	(*params)(struct dwc2_fdt_softc *, struct dwc2_core_params *);
+};
+
+static const struct dwc2_fdt_config dwc2_fdt_rk3066_config = {
+	.params = dwc2_fdt_rockchip_params,
+};
+
+static const struct of_compat_data compat_data[] = {
+	{ "rockchip,rk3066-usb",	(uintptr_t)&dwc2_fdt_rk3066_config },
+	{ NULL }
+};
+
+CFATTACH_DECL_NEW(dwc2_fdt, sizeof(struct dwc2_fdt_softc),
+    dwc2_fdt_match, dwc2_fdt_attach, NULL, NULL);
+
+/* ARGSUSED */
+static int
+dwc2_fdt_match(device_t parent, struct cfdata *match, void *aux)
+{
+	struct fdt_attach_args * const faa = aux;
+
+	return of_match_compat_data(faa->faa_phandle, compat_data);
+}
+
+/* ARGSUSED */
+static void
+dwc2_fdt_attach(device_t parent, device_t self, void *aux)
+{
+	struct dwc2_fdt_softc *sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	const struct dwc2_fdt_config *conf =
+	    (void *)of_search_compatible(phandle, compat_data)->data;
+	char intrstr[128];
+	struct fdtbus_phy *phy;
+	struct clk *clk;
+	bus_addr_t addr;
+	bus_size_t size;
+	int error;
+
+	const char *dr_mode = fdtbus_get_string(phandle, "dr_mode");
+	if (dr_mode == NULL || strcmp(dr_mode, "host") != 0) {
+		aprint_error(": mode '%s' not supported\n", dr_mode);
+		return;
+	}
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	clk = fdtbus_clock_get(phandle, "otg");
+	if (clk == NULL || clk_enable(clk) != 0) {
+		aprint_error(": couldn't enable otg clock\n");
+		return;
+	}
+
+	/* Enable optional phy */
+	phy = fdtbus_phy_get(phandle, "usb2-phy");
+	if (phy && fdtbus_phy_enable(phy, true) != 0) {
+		aprint_error(": couldn't enable phy\n");
+		return;
+	}
+
+	sc->sc_phandle = phandle;
+	sc->sc_dwc2.sc_dev = self;
+	sc->sc_dwc2.sc_iot = faa->faa_bst;
+	sc->sc_dwc2.sc_bus.ub_dmatag = faa->faa_dmat;
+
+	error = bus_space_map(faa->faa_bst, addr, size, 0, &sc->sc_dwc2.sc_ioh);
+	if (error) {
+		aprint_error(": couldn't map device\n");
+		return;
+	}
+
+	if (conf->params) {
+		conf->params(sc, &sc->sc_params);
+		sc->sc_dwc2.sc_params = &sc->sc_params;
+	}
+
+	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+		aprint_error(": failed to decode interrupt\n");
+		return;
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": DesignWare USB2 OTG\n");
+
+	sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_VM, FDT_INTR_MPSAFE,
+	    dwc2_intr, &sc->sc_dwc2);
+
+	if (sc->sc_ih == NULL) {
+		aprint_error_dev(self, "failed to establish interrupt %s\n",
+		    intrstr);
+		goto fail;
+	}
+	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
+	config_interrupts(self, dwc2_fdt_deferred);
+
+	return;
+
+fail:
+	if (sc->sc_ih) {
+		fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_ih);
+		sc->sc_ih = NULL;
+	}
+	bus_space_unmap(sc->sc_dwc2.sc_iot, sc->sc_dwc2.sc_ioh, size);
+}
+
+static void
+dwc2_fdt_deferred(device_t self)
+{
+	struct dwc2_fdt_softc *sc = device_private(self);
+	int error;
+
+	error = dwc2_init(&sc->sc_dwc2);
+	if (error != 0) {
+		aprint_error_dev(self, "couldn't initialize host, error=%d\n",
+		    error);
+		return;
+	}
+	sc->sc_dwc2.sc_child = config_found(sc->sc_dwc2.sc_dev,
+	    &sc->sc_dwc2.sc_bus, usbctlprint);
+}
+
+static void
+dwc2_fdt_rockchip_params(struct dwc2_fdt_softc *sc, struct dwc2_core_params *params)
+{
+	dwc2_set_all_params(params, -1);
+
+	params->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+	params->host_rx_fifo_size = 525;
+	params->host_nperio_tx_fifo_size = 128;
+	params->host_perio_tx_fifo_size = 256;
+	params->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
+}
Index: src/sys/dev/fdt/dwcmmc_fdt.c
diff -u /dev/null src/sys/dev/fdt/dwcmmc_fdt.c:1.1
--- /dev/null	Sat Jun 16 00:19:04 2018
+++ src/sys/dev/fdt/dwcmmc_fdt.c	Sat Jun 16 00:19:04 2018
@@ -0,0 +1,176 @@
+/* $NetBSD: dwcmmc_fdt.c,v 1.1 2018/06/16 00:19:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015-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: dwcmmc_fdt.c,v 1.1 2018/06/16 00:19: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/kernel.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/gpio.h>
+
+#include <dev/ic/dwc_mmc_var.h>
+#include <dev/fdt/fdtvar.h>
+
+static int	dwcmmc_fdt_match(device_t, cfdata_t, void *);
+static void	dwcmmc_fdt_attach(device_t, device_t, void *);
+
+static int	dwcmmc_fdt_card_detect(struct dwc_mmc_softc *);
+
+struct dwcmmc_fdt_config {
+	u_int		ciu_div;
+};
+
+static const struct dwcmmc_fdt_config dwcmmc_rk3328_config = {
+	.ciu_div = 2,
+};
+
+static const struct of_compat_data compat_data[] = {
+	{ "rockchip,rk3328-dw-mshc",	(uintptr_t)&dwcmmc_rk3328_config },
+	{ NULL }
+};
+
+struct dwcmmc_fdt_softc {
+	struct dwc_mmc_softc	sc;
+	struct clk		*sc_clk_biu;
+	struct clk		*sc_clk_ciu;
+	struct fdtbus_gpio_pin	*sc_pin_cd;
+	const struct dwcmmc_fdt_config *sc_conf;
+};
+
+CFATTACH_DECL_NEW(dwcmmc_fdt, sizeof(struct dwc_mmc_softc),
+	dwcmmc_fdt_match, dwcmmc_fdt_attach, NULL, NULL);
+
+static int
+dwcmmc_fdt_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
+dwcmmc_fdt_attach(device_t parent, device_t self, void *aux)
+{
+	struct dwcmmc_fdt_softc *esc = device_private(self);
+	struct dwc_mmc_softc *sc = &esc->sc;
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	char intrstr[128];
+	bus_addr_t addr;
+	bus_size_t size;
+	u_int fifo_depth;
+	int error;
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	if (of_getprop_uint32(phandle, "fifo-depth", &fifo_depth))
+		fifo_depth = 0;
+
+	esc->sc_clk_biu = fdtbus_clock_get(phandle, "biu");
+	if (esc->sc_clk_biu == NULL) {
+		aprint_error(": couldn't get clock biu\n");
+		return;
+	}
+	esc->sc_clk_ciu = fdtbus_clock_get(phandle, "ciu");
+	if (esc->sc_clk_ciu == NULL) {
+		aprint_error(": couldn't get clock ciu\n");
+		return;
+	}
+
+	error = clk_enable(esc->sc_clk_biu);
+	if (error) {
+		aprint_error(": couldn't enable clock biu: %d\n", error);
+		return;
+	}
+	error = clk_enable(esc->sc_clk_ciu);
+	if (error) {
+		aprint_error(": couldn't enable clock ciu: %d\n", error);
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_bst = faa->faa_bst;
+	sc->sc_dmat = faa->faa_dmat;
+	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
+	if (error) {
+		aprint_error(": couldn't map %#llx: %d\n",
+		    (uint64_t)addr, error);
+		return;
+	}
+	esc->sc_conf = of_search_compatible(phandle, compat_data)->data;
+
+	const u_int ciu_div = esc->sc_conf->ciu_div > 0 ? esc->sc_conf->ciu_div : 1;
+
+	sc->sc_clock_freq = clk_get_rate(esc->sc_clk_ciu) / (ciu_div + 1);
+	sc->sc_fifo_depth = fifo_depth;
+	sc->sc_flags = DWC_MMC_F_USE_HOLD_REG | DWC_MMC_F_DMA;
+
+	esc->sc_pin_cd = fdtbus_gpio_acquire(phandle, "cd-gpios",
+	    GPIO_PIN_INPUT);
+	if (esc->sc_pin_cd)
+		sc->sc_card_detect = dwcmmc_fdt_card_detect;
+
+	aprint_naive("\n");
+	aprint_normal(": SD/MMC controller (%u Hz)\n", sc->sc_clock_freq);
+
+	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+		aprint_error_dev(self, "failed to decode interrupt\n");
+		return;
+	}
+
+	if (dwc_mmc_init(sc) != 0)
+		return;
+
+	sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_BIO, 0,
+	    dwc_mmc_intr, sc);
+	if (sc->sc_ih == NULL) {
+		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
+		    intrstr);
+		return;
+	}
+	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
+}
+
+static int
+dwcmmc_fdt_card_detect(struct dwc_mmc_softc *sc)
+{
+	struct dwcmmc_fdt_softc *esc = device_private(sc->sc_dev);
+
+	KASSERT(esc->sc_pin_cd != NULL);
+
+	return fdtbus_gpio_read(esc->sc_pin_cd);
+}

Reply via email to