Module Name:    src
Committed By:   hkenken
Date:           Thu Jun 20 08:16:20 UTC 2019

Modified Files:
        src/sys/arch/arm/imx: files.imx6 if_enet_imx6.c if_enetvar.h
            imx6_ahcisata.c imx6_ccm.c imx6_ccmreg.h imx6_ccmvar.h imx6_i2c.c
            imx6_pcie.c imx6_usb.c imx6_usbphy.c imx6_usdhc.c imxi2cvar.h
            imxusbvar.h
        src/sys/arch/evbarm/nitrogen6: nitrogen6_usb.c

Log Message:
Add support for clk subsystem in imx6 CCM driver.


To generate a diff of this commit:
cvs rdiff -u -r1.14 -r1.15 src/sys/arch/arm/imx/files.imx6
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/arm/imx/if_enet_imx6.c
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/imx/if_enetvar.h \
    src/sys/arch/arm/imx/imx6_i2c.c src/sys/arch/arm/imx/imxi2cvar.h
cvs rdiff -u -r1.8 -r1.9 src/sys/arch/arm/imx/imx6_ahcisata.c \
    src/sys/arch/arm/imx/imx6_pcie.c
cvs rdiff -u -r1.9 -r1.10 src/sys/arch/arm/imx/imx6_ccm.c \
    src/sys/arch/arm/imx/imx6_ccmreg.h
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/arm/imx/imx6_ccmvar.h
cvs rdiff -u -r1.4 -r1.5 src/sys/arch/arm/imx/imx6_usb.c \
    src/sys/arch/arm/imx/imxusbvar.h
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/imx/imx6_usbphy.c
cvs rdiff -u -r1.6 -r1.7 src/sys/arch/arm/imx/imx6_usdhc.c
cvs rdiff -u -r1.4 -r1.5 src/sys/arch/evbarm/nitrogen6/nitrogen6_usb.c

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

Modified files:

Index: src/sys/arch/arm/imx/files.imx6
diff -u src/sys/arch/arm/imx/files.imx6:1.14 src/sys/arch/arm/imx/files.imx6:1.15
--- src/sys/arch/arm/imx/files.imx6:1.14	Wed Jun 20 07:05:37 2018
+++ src/sys/arch/arm/imx/files.imx6	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-#	$NetBSD: files.imx6,v 1.14 2018/06/20 07:05:37 hkenken Exp $
+#	$NetBSD: files.imx6,v 1.15 2019/06/20 08:16:19 hkenken Exp $
 #
 # Configuration info for the Freescale i.MX6
 #
@@ -32,12 +32,15 @@ attach	imxpcie at axi
 file	arch/arm/imx/imx6_pcie.c		imxpcie
 
 # iMX6 Clock Control Module
-device	imxccm
+device	imxccm : clk
 attach	imxccm at axi
 file	arch/arm/imx/imx6_ccm.c			imxccm	needs-flag
 defflag opt_imx6clk.h				IMXCCMDEBUG
 defparam opt_imx6clk.h				IMX6_OSC_FREQ
 defparam opt_imx6clk.h				IMX6_CKIL_FREQ
+defparam opt_imx6clk.h				IMX6_CKIH_FREQ
+defparam opt_imx6clk.h				IMX6_ANACLK1_FREQ
+defparam opt_imx6clk.h				IMX6_ANACLK2_FREQ
 
 # iMX6 Enhanced Periodic Interrupt Timer
 device	imxclock

Index: src/sys/arch/arm/imx/if_enet_imx6.c
diff -u src/sys/arch/arm/imx/if_enet_imx6.c:1.3 src/sys/arch/arm/imx/if_enet_imx6.c:1.4
--- src/sys/arch/arm/imx/if_enet_imx6.c:1.3	Fri Jun  9 18:14:59 2017
+++ src/sys/arch/arm/imx/if_enet_imx6.c	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_enet_imx6.c,v 1.3 2017/06/09 18:14:59 ryo Exp $	*/
+/*	$NetBSD: if_enet_imx6.c,v 1.4 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2014 Ryo Shimizu <r...@nerv.org>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_enet_imx6.c,v 1.3 2017/06/09 18:14:59 ryo Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_enet_imx6.c,v 1.4 2019/06/20 08:16:19 hkenken Exp $");
 
 #include "locators.h"
 #include "imxccm.h"
@@ -47,6 +47,8 @@ __KERNEL_RCSID(0, "$NetBSD: if_enet_imx6
 #include <arm/imx/if_enetreg.h>
 #include <arm/imx/if_enetvar.h>
 
+static int enet_init_clocks(struct enet_softc *);
+
 int
 enet_match(device_t parent __unused, struct cfdata *match __unused, void *aux)
 {
@@ -108,28 +110,9 @@ enet_attach(device_t parent, device_t se
 	sc->sc_enaddr[5] = eaddr + sc->sc_unit;
 #endif
 
-#if NIMXCCM > 0
-	/* PLL power up */
-	if (imx6_pll_power(CCM_ANALOG_PLL_ENET, 1,
-	    CCM_ANALOG_PLL_ENET_ENABLE) != 0) {
-		aprint_error_dev(sc->sc_dev,
-		    "couldn't enable CCM_ANALOG_PLL_ENET\n");
-		return;
-	}
-
 	if (IMX6_CHIPID_MAJOR(imx6_chip_id()) == CHIPID_MAJOR_IMX6UL) {
 		uint32_t v;
 
-		/* iMX6UL */
-		if ((imx6_pll_power(CCM_ANALOG_PLL_ENET, 1,
-		    CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN) != 0) ||
-		    (imx6_pll_power(CCM_ANALOG_PLL_ENET, 1,
-		    CCM_ANALOG_PLL_ENET_ENET2_125M_EN) != 0)) {
-			aprint_error_dev(sc->sc_dev,
-			    "couldn't enable CCM_ANALOG_PLL_ENET\n");
-			return;
-		}
-
 		v = iomux_read(IMX6UL_IOMUX_GPR1);
 		switch (sc->sc_unit) {
 		case 0:
@@ -144,11 +127,42 @@ enet_attach(device_t parent, device_t se
 		iomux_write(IMX6UL_IOMUX_GPR1, v);
 	}
 
-	sc->sc_pllclock = imx6_get_clock(IMX6CLK_PLL6);
-#else
-	sc->sc_pllclock = 50000000;
-#endif
+	sc->sc_clk_enet = imx6_get_clock("enet");
+	if (sc->sc_clk_enet == NULL) {
+		aprint_error(": couldn't get clock enet\n");
+		return;
+	}
+	sc->sc_clk_enet_ref = imx6_get_clock("enet_ref");
+	if (sc->sc_clk_enet_ref == NULL) {
+		aprint_error(": couldn't get clock enet_ref\n");
+		return;
+	}
+	if (enet_init_clocks(sc) != 0) {
+		aprint_error_dev(self, "couldn't init clocks\n");
+		return;
+	}
+
+	sc->sc_pllclock = clk_get_rate(sc->sc_clk_enet_ref);
 
 	enet_attach_common(self, aa->aa_iot, aa->aa_dmat, aa->aa_addr,
 	    aa->aa_size, aa->aa_irq);
 }
+
+static int
+enet_init_clocks(struct enet_softc *sc)
+{
+	int error;
+
+	error = clk_enable(sc->sc_clk_enet);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable enet: %d\n", error);
+		return error;
+	}
+	error = clk_enable(sc->sc_clk_enet_ref);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable enet-ref: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}

Index: src/sys/arch/arm/imx/if_enetvar.h
diff -u src/sys/arch/arm/imx/if_enetvar.h:1.2 src/sys/arch/arm/imx/if_enetvar.h:1.3
--- src/sys/arch/arm/imx/if_enetvar.h:1.2	Fri Jun  9 18:14:59 2017
+++ src/sys/arch/arm/imx/if_enetvar.h	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_enetvar.h,v 1.2 2017/06/09 18:14:59 ryo Exp $	*/
+/*	$NetBSD: if_enetvar.h,v 1.3 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2014 Ryo Shimizu <r...@nerv.org>
@@ -61,6 +61,9 @@ struct enet_softc {
 	int sc_rgmii;
 	unsigned int sc_pllclock;
 
+	struct clk *sc_clk_enet;
+	struct clk *sc_clk_enet_ref;
+
 	/* interrupts */
 	void *sc_ih;
 	void *sc_ih2;	/* for i.MX7 */
Index: src/sys/arch/arm/imx/imx6_i2c.c
diff -u src/sys/arch/arm/imx/imx6_i2c.c:1.2 src/sys/arch/arm/imx/imx6_i2c.c:1.3
--- src/sys/arch/arm/imx/imx6_i2c.c:1.2	Fri Mar 27 05:31:23 2015
+++ src/sys/arch/arm/imx/imx6_i2c.c	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: imx6_i2c.c,v 1.2 2015/03/27 05:31:23 hkenken Exp $	*/
+/*	$NetBSD: imx6_i2c.c,v 1.3 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2014 Ryo Shimizu <r...@nerv.org>
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: imx6_i2c.c,v 1.2 2015/03/27 05:31:23 hkenken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: imx6_i2c.c,v 1.3 2019/06/20 08:16:19 hkenken Exp $");
 
 #include "opt_imx.h"
 
@@ -65,11 +65,34 @@ void
 imxi2c_attach(device_t parent __unused, device_t self, void *aux)
 {
 	struct axi_attach_args *aa = aux;
+	struct imxi2c_softc *sc = device_private(self);
 
 	if (aa->aa_size <= 0)
 		aa->aa_size = I2C_SIZE;
 
-	imxi2c_set_freq(self, imx6_get_clock(IMX6CLK_PERCLK), 400000);
+	switch (device_unit(self)) {
+	case 0:
+		sc->sc_clk = imx6_get_clock("i2c1");
+		break;
+	case 1:
+		sc->sc_clk = imx6_get_clock("i2c2");
+		break;
+	case 2:
+		sc->sc_clk = imx6_get_clock("i2c3");
+		break;
+	}
+	if (sc->sc_clk == NULL) {
+		aprint_error(": couldn't get clock sata\n");
+		return;
+	}
+
+	int error = clk_enable(sc->sc_clk);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable: %d\n", error);
+		return;
+	}
+
+	imxi2c_set_freq(self, clk_get_rate(sc->sc_clk), 400000);
 	imxi2c_attach_common(parent, self,
 	    aa->aa_iot, aa->aa_addr, aa->aa_size, aa->aa_irq, 0);
 }
Index: src/sys/arch/arm/imx/imxi2cvar.h
diff -u src/sys/arch/arm/imx/imxi2cvar.h:1.2 src/sys/arch/arm/imx/imxi2cvar.h:1.3
--- src/sys/arch/arm/imx/imxi2cvar.h:1.2	Fri Mar 27 05:31:23 2015
+++ src/sys/arch/arm/imx/imxi2cvar.h	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: imxi2cvar.h,v 1.2 2015/03/27 05:31:23 hkenken Exp $	*/
+/*	$NetBSD: imxi2cvar.h,v 1.3 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
 * Copyright (c) 2012, 2015 Genetec Corporation.  All rights reserved.
@@ -36,6 +36,8 @@ struct imxi2c_softc {
 	device_t sc_dev;
 	struct motoi2c_softc sc_motoi2c;
 	struct motoi2c_settings sc_motoi2c_settings;
+
+	struct clk *sc_clk;
 };
 
 int imxi2c_attach_common(device_t, device_t,

Index: src/sys/arch/arm/imx/imx6_ahcisata.c
diff -u src/sys/arch/arm/imx/imx6_ahcisata.c:1.8 src/sys/arch/arm/imx/imx6_ahcisata.c:1.9
--- src/sys/arch/arm/imx/imx6_ahcisata.c:1.8	Wed Jun 20 05:53:19 2018
+++ src/sys/arch/arm/imx/imx6_ahcisata.c	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: imx6_ahcisata.c,v 1.8 2018/06/20 05:53:19 hkenken Exp $	*/
+/*	$NetBSD: imx6_ahcisata.c,v 1.9 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2014 Ryo Shimizu <r...@nerv.org>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: imx6_ahcisata.c,v 1.8 2018/06/20 05:53:19 hkenken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: imx6_ahcisata.c,v 1.9 2019/06/20 08:16:19 hkenken Exp $");
 
 #include "locators.h"
 #include "opt_imx.h"
@@ -47,23 +47,28 @@ __KERNEL_RCSID(0, "$NetBSD: imx6_ahcisat
 #include <dev/ic/ahcisatavar.h>
 
 struct imx_ahci_softc {
+	struct ahci_softc sc_ahcisc;
+
 	device_t sc_dev;
 	bus_space_tag_t sc_iot;
 	bus_space_handle_t sc_ioh;
 	void *sc_ih;
 
-	struct ahci_softc sc_ahcisc;
+	struct clk *sc_clk_sata;
+	struct clk *sc_clk_sata_ref;
+	struct clk *sc_clk_ahb;
 };
 
 static int imx6_ahcisata_match(device_t, cfdata_t, void *);
 static void imx6_ahcisata_attach(device_t, device_t, void *);
 static int imx6_ahcisata_detach(device_t, int);
 
-static int imx6_ahcisata_init(struct imx_ahci_softc *);
 static int imx6_ahcisata_phy_ctrl(struct imx_ahci_softc *, uint32_t, int);
 static int imx6_ahcisata_phy_addr(struct imx_ahci_softc *, uint32_t);
 static int imx6_ahcisata_phy_write(struct imx_ahci_softc *, uint32_t, uint16_t);
 static int imx6_ahcisata_phy_read(struct imx_ahci_softc *, uint32_t);
+static int imx6_ahcisata_init(struct imx_ahci_softc *);
+static int imx6_ahcisata_init_clocks(struct imx_ahci_softc *);
 
 CFATTACH_DECL_NEW(imx6_ahcisata, sizeof(struct imx_ahci_softc),
     imx6_ahcisata_match, imx6_ahcisata_attach, imx6_ahcisata_detach, NULL);
@@ -114,6 +119,26 @@ imx6_ahcisata_attach(device_t parent, de
 		return;
 	}
 
+	sc->sc_clk_sata = imx6_get_clock("sata");
+	if (sc->sc_clk_sata == NULL) {
+		aprint_error(": couldn't get clock sata\n");
+		return;
+	}
+	sc->sc_clk_sata_ref = imx6_get_clock("sata_ref");
+	if (sc->sc_clk_sata_ref == NULL) {
+		aprint_error(": couldn't get clock sata_ref\n");
+		return;
+	}
+	sc->sc_clk_ahb = imx6_get_clock("ahb");
+	if (sc->sc_clk_ahb == NULL) {
+		aprint_error(": couldn't get clock ahb\n");
+		return;
+	}
+	if (imx6_ahcisata_init_clocks(sc) != 0) {
+		aprint_error_dev(self, "couldn't init clocks\n");
+		return;
+	}
+
 	if (imx6_ahcisata_init(sc) != 0) {
 		aprint_error_dev(self, "couldn't init ahci\n");
 		return;
@@ -262,21 +287,6 @@ imx6_ahcisata_init(struct imx_ahci_softc
 	uint32_t v;
 	int timeout, pllstat;
 
-	/* AHCISATA clock enable */
-	v = imx6_ccm_read(CCM_CCGR5);
-	imx6_ccm_write(CCM_CCGR5, v | __SHIFTIN(3, CCM_CCGR5_SATA_CLK_ENABLE));
-
-	/* PLL power up */
-	if (imx6_pll_power(CCM_ANALOG_PLL_ENET, 1,
-		CCM_ANALOG_PLL_ENET_ENABLE_100M) != 0) {
-		aprint_error_dev(sc->sc_dev,
-		    "couldn't enable CCM_ANALOG_PLL_ENET\n");
-		return -1;
-	}
-	v = imx6_ccm_analog_read(CCM_ANALOG_PLL_ENET);
-	v |= CCM_ANALOG_PLL_ENET_ENABLE_100M;
-	imx6_ccm_analog_write(CCM_ANALOG_PLL_ENET, v);
-
 	v = iomux_read(IOMUX_GPR13);
 	/* clear */
 	v &= ~(IOMUX_GPR13_SATA_PHY_8(7) |
@@ -332,7 +342,32 @@ imx6_ahcisata_init(struct imx_ahci_softc
 
 	/* set 1ms-timer = AHB clock / 1000 */
 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_TIMER1MS,
-	    imx6_get_clock(IMX6CLK_AHB) / 1000);
+	    clk_get_rate(sc->sc_clk_ahb) / 1000);
 
 	return 0;
 }
+
+static int
+imx6_ahcisata_init_clocks(struct imx_ahci_softc *sc)
+{
+	int error;
+
+	error = clk_enable(sc->sc_clk_sata);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable sata: %d\n", error);
+		return error;
+	}
+	error = clk_enable(sc->sc_clk_sata_ref);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable sata-ref: %d\n", error);
+		return error;
+	}
+	error = clk_enable(sc->sc_clk_ahb);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable anb: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
Index: src/sys/arch/arm/imx/imx6_pcie.c
diff -u src/sys/arch/arm/imx/imx6_pcie.c:1.8 src/sys/arch/arm/imx/imx6_pcie.c:1.9
--- src/sys/arch/arm/imx/imx6_pcie.c:1.8	Fri Mar  1 09:25:59 2019
+++ src/sys/arch/arm/imx/imx6_pcie.c	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: imx6_pcie.c,v 1.8 2019/03/01 09:25:59 msaitoh Exp $	*/
+/*	$NetBSD: imx6_pcie.c,v 1.9 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2016  Genetec Corporation.  All rights reserved.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: imx6_pcie.c,v 1.8 2019/03/01 09:25:59 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: imx6_pcie.c,v 1.9 2019/06/20 08:16:19 hkenken Exp $");
 
 #include "opt_pci.h"
 
@@ -104,6 +104,10 @@ struct imx6pcie_softc {
 	int32_t sc_gpio_reset_active;
 	int32_t sc_gpio_pwren;
 	int32_t sc_gpio_pwren_active;
+
+	struct clk *sc_clk_pcie_axi;
+	struct clk *sc_clk_lvds1_gate;
+	struct clk *sc_clk_pcie_ref;
 };
 
 #define PCIE_CONF_LOCK(s)	(s) = disable_interrupts(I32_bit)
@@ -160,41 +164,28 @@ imx6pcie_valid_device(struct imx6pcie_so
 	return 1;
 }
 
-static void
-imx6pcie_clock_enable(struct imx6pcie_softc *sc)
+static int
+imx6pcie_init_clocks(struct imx6pcie_softc *sc)
 {
-	uint32_t v;
+	int error;
 
-	v = imx6_ccm_analog_read(CCM_ANALOG_MISC1);
-	v &= ~CCM_ANALOG_MISC1_LVDS_CLK1_IBEN;
-	v &= ~CCM_ANALOG_MISC1_LVDS_CLK1_SRC;
-	v |= CCM_ANALOG_MISC1_LVDS_CLK1_OBEN;
-	v |= CCM_ANALOG_MISC1_LVDS_CLK1_SRC_SATA;
-	imx6_ccm_analog_write(CCM_ANALOG_MISC1, v);
-
-	/* select PCIe clock source from axi */
-	v = imx6_ccm_read(CCM_CBCMR);
-	v &= ~CCM_CBCMR_PCIE_AXI_CLK_SEL;
-	imx6_ccm_write(CCM_CBCMR, v);
-
-	/* AHCISATA clock enable */
-	v = imx6_ccm_read(CCM_CCGR5);
-	v |= __SHIFTIN(3, CCM_CCGR5_SATA_CLK_ENABLE);
-	imx6_ccm_write(CCM_CCGR5, v);
-
-	/* PCIe clock enable */
-	v = imx6_ccm_read(CCM_CCGR4);
-	v |= __SHIFTIN(3, CCM_CCGR4_PCIE_ROOT_ENABLE);
-	imx6_ccm_write(CCM_CCGR4, v);
-
-	/* PLL power up */
-	if (imx6_pll_power(CCM_ANALOG_PLL_ENET, 1,
-		CCM_ANALOG_PLL_ENET_ENABLE_125M |
-		CCM_ANALOG_PLL_ENET_ENABLE_100M) != 0) {
-		aprint_error_dev(sc->sc_dev,
-		    "couldn't enable CCM_ANALOG_PLL_ENET\n");
-		return;
+	error = clk_enable(sc->sc_clk_pcie_axi);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable pcie_axi: %d\n", error);
+		return error;
+	}
+	error = clk_enable(sc->sc_clk_lvds1_gate);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable lvds1_gate: %d\n", error);
+		return error;
 	}
+	error = clk_enable(sc->sc_clk_pcie_ref);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable pcie_ref: %d\n", error);
+		return error;
+	}
+
+	return 0;
 }
 
 static int
@@ -375,8 +366,6 @@ imx6pcie_deassert_core_reset(struct imx6
 	}
 #endif
 
-	imx6pcie_clock_enable(sc);
-
 	v = iomux_read(IOMUX_GPR1);
 
 #if defined(IMX6DQP)
@@ -545,6 +534,8 @@ imx6pcie_match(device_t parent, cfdata_t
 	switch (aa->aa_addr) {
 	case (IMX6_PCIE_BASE):
 		return 1;
+	default:
+		break;
 	}
 
 	return 0;
@@ -583,6 +574,24 @@ imx6pcie_attach(device_t parent, device_
 	imx6_set_gpio(self, "imx6pcie-pwren-gpio", &sc->sc_gpio_pwren,
 	    &sc->sc_gpio_pwren_active, GPIO_DIR_OUT);
 
+	sc->sc_clk_pcie_axi = imx6_get_clock("pcie_axi");
+	if (sc->sc_clk_pcie_axi == NULL) {
+		aprint_error(": couldn't get clock pcie_axi\n");
+		return;
+	}
+	sc->sc_clk_lvds1_gate = imx6_get_clock("lvds1_gate");
+	if (sc->sc_clk_lvds1_gate == NULL) {
+		aprint_error(": couldn't get clock lvds1_gate\n");
+		return;
+	}
+	sc->sc_clk_pcie_ref = imx6_get_clock("pcie_ref_125m");
+	if (sc->sc_clk_pcie_ref == NULL) {
+		aprint_error(": couldn't get clock pcie_ref\n");
+		return;
+	}
+
+	imx6pcie_init_clocks(sc);
+
 	imx6pcie_linkup(sc);
 
 	TAILQ_INIT(&sc->sc_intrs);

Index: src/sys/arch/arm/imx/imx6_ccm.c
diff -u src/sys/arch/arm/imx/imx6_ccm.c:1.9 src/sys/arch/arm/imx/imx6_ccm.c:1.10
--- src/sys/arch/arm/imx/imx6_ccm.c:1.9	Wed Jun 20 07:05:37 2018
+++ src/sys/arch/arm/imx/imx6_ccm.c	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: imx6_ccm.c,v 1.9 2018/06/20 07:05:37 hkenken Exp $	*/
+/*	$NetBSD: imx6_ccm.c,v 1.10 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2010-2012, 2014  Genetec Corporation.  All rights reserved.
@@ -25,13 +25,12 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
-
 /*
  * Clock Controller Module (CCM) for i.MX6
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: imx6_ccm.c,v 1.9 2018/06/20 07:05:37 hkenken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: imx6_ccm.c,v 1.10 2019/06/20 08:16:19 hkenken Exp $");
 
 #include "opt_imx.h"
 #include "opt_imx6clk.h"
@@ -59,37 +58,607 @@ __KERNEL_RCSID(0, "$NetBSD: imx6_ccm.c,v
 #include <arm/imx/imx6var.h>
 #include <arm/imx/imx6_reg.h>
 
+#include <dev/clk/clk_backend.h>
+
 struct imxccm_softc {
 	device_t sc_dev;
 	bus_space_tag_t sc_iot;
 	bus_space_handle_t sc_ioh;
 	bus_space_handle_t sc_ioh_analog;
 
-	/* for sysctl */
-	struct sysctllog *sc_log;
-	int sc_sysctlnode_pll1_arm;
-	int sc_sysctlnode_pll2_sys;
-	int sc_sysctlnode_pll3_usb1;
-	int sc_sysctlnode_pll7_usb2;
-	int sc_sysctlnode_pll4_audio;
-	int sc_sysctlnode_pll5_video;
-	int sc_sysctlnode_pll6_enet;
-/*	int sc_sysctlnode_pll8_mlb;	*/
-	int sc_sysctlnode_arm;
-	int sc_sysctlnode_periph;
-	int sc_sysctlnode_ahb;
-	int sc_sysctlnode_ipg;
-	int sc_sysctlnode_axi;
+	struct clk_domain sc_clkdom;
+};
+
+/* Clock Parents Tables */
+static const char *step_p[] = {
+	"osc",
+	"pll2_pfd2_396m"
+};
+
+static const char *pll1_sw_p[] = {
+	"pll1_sys",
+	"step"
+};
+
+static const char *periph_pre_p[] = {
+	"pll2_bus",
+	"pll2_pfd2_396m",
+	"pll2_pfd0_352m",
+	"pll2_198m"
+};
+
+static const char *periph_clk2_p[] = {
+	"pll3_usb_otg",
+	"osc",
+	"osc",
+	"dummy"
+};
+
+static const char *periph2_clk2_p[] = {
+	"pll3_usb_otg",
+	"pll2_bus"
+};
+
+static const char *axi_p[] = {
+	"periph",
+	"pll2_pfd2_396m",
+	"periph",
+	"pll3_pfd1_540m"
+};
+
+static const char *audio_p[] = {
+	"pll4_audio_div",
+	"pll3_pfd2_508m",
+	"pll3_pfd3_454m",
+	"pll3_usb_otg"
+};
+
+static const char *gpu2d_core_p[] = {
+	"axi",
+	"pll3_usb_otg",
+	"pll2_pfd0_352m",
+	"pll2_pfd2_396m"
+};
+
+static const char *gpu3d_core_p[] = {
+	"mmdc_ch0_axi",
+	"pll3_usb_otg",
+	"pll2_pfd1_594m",
+	"pll2_pfd2_396m"
+};
+
+static const char *gpu3d_shader_p[] = {
+	"mmdc_ch0_axi",
+	"pll3_usb_otg",
+	"pll2_pfd1_594m",
+	"pll3_pfd0_720m"
+};
+
+static const char *ipu_p[] = {
+	"mmdc_ch0_axi",
+	"pll2_pfd2_396m",
+	"pll3_120m",
+	"pll3_pfd1_540m"
+};
+
+static const char *pll_bypass_src_p[] = {
+	"osc",
+	"lvds1_in",
+	"lvds2_in",
+	"dummy"
+};
+
+static const char *pll1_bypass_p[] = {
+	"pll1",
+	"pll1_bypass_src"
+};
+
+static const char *pll2_bypass_p[] = {
+	"pll2",
+	"pll2_bypass_src"
+};
+
+static const char *pll3_bypass_p[] = {
+	"pll3",
+	"pll3_bypass_src"
+};
+
+static const char *pll4_bypass_p[] = {
+	"pll4",
+	"pll4_bypass_src"
+};
+
+static const char *pll5_bypass_p[] = {
+	"pll5",
+	"pll5_bypass_src"
+};
+
+static const char *pll6_bypass_p[] = {
+	"pll6",
+	"pll6_bypass_src"
+};
+
+static const char *pll7_bypass_p[] = {
+	"pll7",
+	"pll7_bypass_src"
 };
 
-struct imxccm_softc *ccm_softc;
+static const char *ipu_di_pre_p[] = {
+	"mmdc_ch0_axi",
+	"pll3_usb_otg",
+	"pll5_video_div",
+	"pll2_pfd0_352m",
+	"pll2_pfd2_396m",
+	"pll3_pfd1_540m"
+};
+
+static const char *ipu1_di0_p[] = {
+	"ipu1_di0_pre",
+	"dummy",
+	"dummy",
+	"ldb_di0",
+	"ldb_di1"
+};
+
+static const char *ipu1_di1_p[] = {
+	"ipu1_di1_pre",
+	"dummy",
+	"dummy",
+	"ldb_di0",
+	"ldb_di1"
+};
+
+static const char *ipu2_di0_p[] = {
+	"ipu2_di0_pre",
+	"dummy",
+	"dummy",
+	"ldb_di0",
+	"ldb_di1"
+};
+
+static const char *ipu2_di1_p[] = {
+	"ipu2_di1_pre",
+	"dummy",
+	"dummy",
+	"ldb_di0",
+	"ldb_di1"
+};
+
+static const char *ldb_di_p[] = {
+	"pll5_video_div",
+	"pll2_pfd0_352m",
+	"pll2_pfd2_396m",
+	"mmdc_ch1_axi",
+	"pll3_usb_otg"
+};
+
+static const char *periph_p[] = {
+	"periph_pre",
+	"periph_clk2"
+};
+
+static const char *periph2_p[] = {
+	"periph2_pre",
+	"periph2_clk2"
+};
+
+static const char *vdo_axi_p[] = {
+	"axi",
+	"ahb"
+};
+
+static const char *vpu_axi_p[] = {
+	"axi",
+	"pll2_pfd2_396m",
+	"pll2_pfd0_352m"
+};
+
+static const char *cko1_p[] = {
+	"pll3_usb_otg",
+	"pll2_bus",
+	"pll1_sys",
+	"pll5_video_div",
+	"dummy",
+	"axi",
+	"enfc",
+	"ipu1_di0",
+	"ipu1_di1",
+	"ipu2_di0",
+	"ipu2_di1",
+	"ahb",
+	"ipg",
+	"ipg_per",
+	"ckil",
+	"pll4_audio_div"
+};
+
+static const char *cko2_p[] = {
+	"mmdc_ch0_axi",
+	"mmdc_ch1_axi",
+	"usdhc4",
+	"usdhc1",
+	"gpu2d_axi",
+	"dummy",
+	"ecspi_root",
+	"gpu3d_axi",
+	"usdhc3",
+	"dummy",
+	"arm",
+	"ipu1",
+	"ipu2",
+	"vdo_axi",
+	"osc",
+	"gpu2d_core",
+	"gpu3d_core",
+	"usdhc2",
+	"ssi1",
+	"ssi2",
+	"ssi3",
+	"gpu3d_shader",
+	"vpu_axi",
+	"can_root",
+	"ldb_di0",
+	"ldb_di1",
+	"esai_extal",
+	"eim_slow",
+	"uart_serial",
+	"spdif",
+	"asrc",
+	"hsi_tx"
+};
+
+static const char *cko_p[] = {
+	"cko1",
+	"cko2"
+};
+
+static const char *hsi_tx_p[] = {
+	"pll3_120m",
+	"pll2_pfd2_396m"
+};
+
+static const char *pcie_axi_p[] = {
+	"axi",
+	"ahb"
+};
+
+static const char *ssi_p[] = {
+	"pll3_pfd2_508m",
+	"pll3_pfd3_454m",
+	"pll4_audio_div"
+};
+
+static const char *usdhc_p[] = {
+	"pll2_pfd2_396m",
+	"pll2_pfd0_352m"
+};
+
+static const char *eim_p[] = {
+	"pll2_pfd2_396m",
+	"pll3_usb_otg",
+	"axi",
+	"pll2_pfd0_352m"
+};
+
+static const char *eim_slow_p[] = {
+	"axi",
+	"pll3_usb_otg",
+	"pll2_pfd2_396m",
+	"pll2_pfd0_352m"
+};
+
+static const char *enfc_p[] = {
+	"pll2_pfd0_352m",
+	"pll2_bus",
+	"pll3_usb_otg",
+	"pll2_pfd2_396m"
+};
+
+static const char *lvds_p[] = {
+	"dummy",
+	"dummy",
+	"dummy",
+	"dummy",
+	"dummy",
+	"dummy",
+	"pll4_audio",
+	"pll5_video",
+	"pll8_mlb",
+	"enet_ref",
+	"pcie_ref_125m",
+	"sata_ref_100m"
+};
+
+/* Clock Divider Tables */
+static const int enet_ref_tbl[] = { 20, 10, 5, 4, 0 };
+static const int post_div_tbl[] = { 4, 2, 1, 0 };
+static const int audiovideo_div_tbl[] = { 1, 2, 1, 4, 0 };
+
+static struct imx6_clk imx6_clks[] = {
+	CLK_FIXED("dummy", 0),
+
+	CLK_FIXED("ckil", IMX6_CKIL_FREQ),
+	CLK_FIXED("ckih", IMX6_CKIH_FREQ),
+	CLK_FIXED("osc", IMX6_OSC_FREQ),
+	CLK_FIXED("anaclk1", IMX6_ANACLK1_FREQ),
+	CLK_FIXED("anaclk2", IMX6_ANACLK2_FREQ),
+
+	CLK_FIXED_FACTOR("sata_ref", "pll6_enet", 5, 1),
+	CLK_FIXED_FACTOR("pcie_ref", "pll6_enet", 4, 1),
+	CLK_FIXED_FACTOR("pll2_198m", "pll2_pfd2_396m", 2, 1),
+	CLK_FIXED_FACTOR("pll3_120m", "pll3_usb_otg", 4, 1),
+	CLK_FIXED_FACTOR("pll3_80m", "pll3_usb_otg", 6, 1),
+	CLK_FIXED_FACTOR("pll3_60m", "pll3_usb_otg", 8, 1),
+	CLK_FIXED_FACTOR("twd", "arm", 2, 1),
+	CLK_FIXED_FACTOR("gpt_3m", "osc", 8, 1),
+	CLK_FIXED_FACTOR("video_27m", "pll3_pfd1_540m", 20, 1),
+	CLK_FIXED_FACTOR("gpu2d_axi", "mmdc_ch0_axi_podf", 1, 1),
+	CLK_FIXED_FACTOR("gpu3d_axi", "mmdc_ch0_axi_podf", 1, 1),
+	CLK_FIXED_FACTOR("ldb_di0_div_3_5", "ldb_di0_sel", 7, 2),
+	CLK_FIXED_FACTOR("ldb_di1_div_3_5", "ldb_di1_sel", 7, 2),
+
+	CLK_PFD("pll2_pfd0_352m", "pll2_bus", PFD_528, 0),
+	CLK_PFD("pll2_pfd1_594m", "pll2_bus", PFD_528, 1),
+	CLK_PFD("pll2_pfd2_396m", "pll2_bus", PFD_528, 2),
+	CLK_PFD("pll3_pfd0_720m", "pll3_usb_otg", PFD_480, 0),
+	CLK_PFD("pll3_pfd1_540m", "pll3_usb_otg", PFD_480, 1),
+	CLK_PFD("pll3_pfd2_508m", "pll3_usb_otg", PFD_480, 2),
+	CLK_PFD("pll3_pfd3_454m", "pll3_usb_otg", PFD_480, 3),
+
+	CLK_PLL("pll1", "osc", SYS, PLL_ARM, DIV_SELECT, POWERDOWN, 0),
+	CLK_PLL("pll2", "osc", GENNERIC, PLL_SYS, DIV_SELECT, POWERDOWN, 0),
+	CLK_PLL("pll3", "osc", USB, PLL_USB1, DIV_SELECT, POWER, 0),
+	CLK_PLL("pll4", "osc", AUDIO_VIDEO, PLL_AUDIO, DIV_SELECT, POWERDOWN, 0),
+	CLK_PLL("pll5", "osc", AUDIO_VIDEO, PLL_VIDEO, DIV_SELECT, POWERDOWN, 0),
+	CLK_PLL("pll6", "osc", ENET, PLL_ENET, DIV_SELECT, POWERDOWN, 500000000),
+	CLK_PLL("pll7", "osc", USB, PLL_USB2, DIV_SELECT, POWER, 0),
+
+	CLK_DIV("periph_clk2", "periph_clk2_sel", CBCDR, PERIPH_CLK2_PODF),
+	CLK_DIV("periph2_clk2", "periph2_clk2_sel", CBCDR, PERIPH2_CLK2_PODF),
+	CLK_DIV("ipg", "ahb", CBCDR, IPG_PODF),
+	CLK_DIV("esai_pred", "esai_sel", CS1CDR, ESAI_CLK_PRED),
+	CLK_DIV("esai_podf", "esai_pred", CS1CDR, ESAI_CLK_PODF),
+	CLK_DIV("asrc_pred", "asrc_sel", CDCDR, SPDIF1_CLK_PRED),
+	CLK_DIV("asrc_podf", "asrc_pred", CDCDR, SPDIF1_CLK_PODF),
+	CLK_DIV("spdif_pred", "spdif_sel", CDCDR, SPDIF0_CLK_PRED),
+	CLK_DIV("spdif_podf", "spdif_pred", CDCDR, SPDIF0_CLK_PODF),
+	CLK_DIV("ecspi_root", "pll3_60m", CSCDR2, ECSPI_CLK_PODF),
+	CLK_DIV("can_root", "pll3_60m", CSCMR2, CAN_CLK_PODF),
+	CLK_DIV("uart_serial_podf", "pll3_80m", CSCDR1, UART_CLK_PODF),
+	CLK_DIV("gpu2d_core_podf", "gpu2d_core_sel", CBCMR, GPU2D_CORE_CLK_PODF),
+	CLK_DIV("gpu3d_core_podf", "gpu3d_core_sel", CBCMR, GPU3D_CORE_PODF),
+	CLK_DIV("gpu3d_shader", "gpu3d_shader_sel", CBCMR, GPU3D_SHADER_PODF),
+	CLK_DIV("ipu1_podf", "ipu1_sel", CSCDR3, IPU1_HSP_PODF),
+	CLK_DIV("ipu2_podf", "ipu2_sel", CSCDR3, IPU2_HSP_PODF),
+	CLK_DIV("ldb_di0_podf", "ldb_di0_div_3_5", CSCMR2, LDB_DI0_IPU_DIV),
+	CLK_DIV("ldb_di1_podf", "ldb_di1_div_3_5", CSCMR2, LDB_DI1_IPU_DIV),
+	CLK_DIV("ipu1_di0_pre", "ipu1_di0_pre_sel", CHSCCDR, IPU1_DI0_PODF),
+	CLK_DIV("ipu1_di1_pre", "ipu1_di1_pre_sel", CHSCCDR, IPU1_DI1_PODF),
+	CLK_DIV("ipu2_di0_pre", "ipu2_di0_pre_sel", CSCDR2, IPU2_DI0_PODF),
+	CLK_DIV("ipu2_di1_pre", "ipu2_di1_pre_sel", CSCDR2, IPU2_DI1_PODF),
+	CLK_DIV("hsi_tx_podf", "hsi_tx_sel", CDCDR, HSI_TX_PODF),
+	CLK_DIV("ssi1_pred", "ssi1_sel", CS1CDR, SSI1_CLK_PRED),
+	CLK_DIV("ssi1_podf", "ssi1_pred", CS1CDR, SSI1_CLK_PODF),
+	CLK_DIV("ssi2_pred", "ssi2_sel", CS2CDR, SSI2_CLK_PRED),
+	CLK_DIV("ssi2_podf", "ssi2_pred", CS2CDR, SSI2_CLK_PODF),
+	CLK_DIV("ssi3_pred", "ssi3_sel", CS1CDR, SSI3_CLK_PRED),
+	CLK_DIV("ssi3_podf", "ssi3_pred", CS1CDR, SSI3_CLK_PODF),
+	CLK_DIV("usdhc1_podf", "usdhc1_sel", CSCDR1, USDHC1_PODF),
+	CLK_DIV("usdhc2_podf", "usdhc2_sel", CSCDR1, USDHC2_PODF),
+	CLK_DIV("usdhc3_podf", "usdhc3_sel", CSCDR1, USDHC3_PODF),
+	CLK_DIV("usdhc4_podf", "usdhc4_sel", CSCDR1, USDHC4_PODF),
+	CLK_DIV("enfc_pred", "enfc_sel", CS2CDR, ENFC_CLK_PRED),
+	CLK_DIV("enfc_podf", "enfc_pred", CS2CDR, ENFC_CLK_PODF),
+	CLK_DIV("vpu_axi_podf", "vpu_axi_sel", CSCDR1, VPU_AXI_PODF),
+	CLK_DIV("cko1_podf", "cko1_sel", CCOSR, CLKO1_DIV),
+	CLK_DIV("cko2_podf", "cko2_sel", CCOSR, CLKO2_DIV),
+	CLK_DIV("ipg_per", "ipg", CSCMR1, PERCLK_PODF),
+	CLK_DIV("eim_podf", "eim_sel", CSCMR1, ACLK_PODF),
+	CLK_DIV("eim_slow_podf", "eim_slow_sel", CSCMR1, ACLK_EIM_SLOW_PODF),
+
+	CLK_DIV_BUSY("axi", "axi_sel", CBCDR, AXI_PODF, CDHIPR, AXI_PODF_BUSY),
+	CLK_DIV_BUSY("mmdc_ch0_axi_podf", "periph", CBCDR, MMDC_CH0_AXI_PODF, CDHIPR, MMDC_CH0_PODF_BUSY),
+	CLK_DIV_BUSY("mmdc_ch1_axi_podf", "periph2", CBCDR, MMDC_CH1_AXI_PODF, CDHIPR, MMDC_CH1_PODF_BUSY),
+	CLK_DIV_BUSY("arm", "pll1_sw", CACRR, ARM_PODF, CDHIPR, ARM_PODF_BUSY),
+	CLK_DIV_BUSY("ahb", "periph", CBCDR, AHB_PODF, CDHIPR, AHB_PODF_BUSY),
+
+	CLK_DIV_TABLE("pll4_post_div", "pll4_audio", PLL_AUDIO, POST_DIV_SELECT, post_div_tbl),
+	CLK_DIV_TABLE("pll4_audio_div", "pll4_post_div", MISC2, AUDIO_DIV_LSB, audiovideo_div_tbl),
+	CLK_DIV_TABLE("pll5_post_div", "pll5_video", PLL_VIDEO, POST_DIV_SELECT, post_div_tbl),
+	CLK_DIV_TABLE("pll5_video_div", "pll5_post_div", MISC2, VIDEO_DIV, audiovideo_div_tbl),
+	CLK_DIV_TABLE("enet_ref", "pll6_enet", PLL_ENET, DIV_SELECT, enet_ref_tbl),
+
+	CLK_MUX("step", step_p, CCM, CCSR, STEP_SEL),
+	CLK_MUX("pll1_sw", pll1_sw_p, CCM, CCSR, PLL1_SW_CLK_SEL),
+	CLK_MUX("periph_pre", periph_pre_p, CCM, CBCMR, PRE_PERIPH_CLK_SEL),
+	CLK_MUX("periph2_pre", periph_pre_p, CCM, CBCMR, PRE_PERIPH2_CLK_SEL),
+	CLK_MUX("periph_clk2_sel", periph_clk2_p, CCM,CBCMR, PERIPH_CLK2_SEL),
+	CLK_MUX("periph2_clk2_sel", periph2_clk2_p, CCM,CBCMR, PERIPH2_CLK2_SEL),
+	CLK_MUX("axi_sel", axi_p, CCM, CBCDR, AXI_SEL),
+	CLK_MUX("asrc_sel", audio_p, CCM, CDCDR, SPDIF1_CLK_SEL),
+	CLK_MUX("spdif_sel", audio_p, CCM, CDCDR, SPDIF0_CLK_SEL),
+	CLK_MUX("gpu2d_core_sel", gpu2d_core_p, CCM, CBCMR, GPU2D_CLK_SEL),
+	CLK_MUX("gpu3d_core_sel", gpu3d_core_p, CCM, CBCMR, GPU3D_CORE_CLK_SEL),
+	CLK_MUX("gpu3d_shader_sel", gpu3d_shader_p, CCM,CBCMR, GPU3D_SHADER_CLK_SEL),
+	CLK_MUX("esai_sel", audio_p, CCM, CSCMR2, ESAI_CLK_SEL),
+	CLK_MUX("ipu1_sel", ipu_p, CCM, CSCDR3, IPU1_HSP_CLK_SEL),
+	CLK_MUX("ipu2_sel", ipu_p, CCM, CSCDR3, IPU2_HSP_CLK_SEL),
+	CLK_MUX("ipu1_di0_pre_sel", ipu_di_pre_p, CCM, CHSCCDR, IPU1_DI0_PRE_CLK_SEL),
+	CLK_MUX("ipu1_di1_pre_sel", ipu_di_pre_p, CCM, CHSCCDR, IPU1_DI1_PRE_CLK_SEL),
+	CLK_MUX("ipu2_di0_pre_sel", ipu_di_pre_p, CCM, CSCDR2, IPU2_DI0_PRE_CLK_SEL),
+	CLK_MUX("ipu2_di1_pre_sel", ipu_di_pre_p, CCM, CSCDR2, IPU2_DI1_PRE_CLK_SEL),
+	CLK_MUX("ipu1_di0_sel", ipu1_di0_p, CCM, CHSCCDR, IPU1_DI0_CLK_SEL),
+	CLK_MUX("ipu1_di1_sel", ipu1_di1_p, CCM, CHSCCDR, IPU1_DI1_CLK_SEL),
+	CLK_MUX("ipu2_di0_sel", ipu2_di0_p, CCM, CSCDR2, IPU2_DI0_CLK_SEL),
+	CLK_MUX("ipu2_di1_sel", ipu2_di1_p, CCM, CSCDR2, IPU2_DI1_CLK_SEL),
+	CLK_MUX("ldb_di0_sel", ldb_di_p, CCM, CS2CDR, LDB_DI0_CLK_SEL),
+	CLK_MUX("ldb_di1_sel", ldb_di_p, CCM, CS2CDR, LDB_DI1_CLK_SEL),
+	CLK_MUX("vdo_axi_sel", vdo_axi_p, CCM, CBCMR, VDOAXI_CLK_SEL),
+	CLK_MUX("vpu_axi_sel", vpu_axi_p, CCM, CBCMR, VPU_AXI_CLK_SEL),
+	CLK_MUX("cko1_sel", cko1_p, CCM, CCOSR, CLKO1_SEL),
+	CLK_MUX("cko2_sel", cko2_p, CCM, CCOSR, CLKO2_SEL),
+	CLK_MUX("cko", cko_p, CCM, CCOSR, CLK_OUT_SEL),
+	CLK_MUX("hsi_tx_sel", hsi_tx_p, CCM, CDCDR, HSI_TX_CLK_SEL),
+	CLK_MUX("pcie_axi_sel", pcie_axi_p, CCM, CBCMR, PCIE_AXI_CLK_SEL),
+	CLK_MUX("ssi1_sel", ssi_p, CCM, CSCMR1, SSI1_CLK_SEL),
+	CLK_MUX("ssi2_sel", ssi_p, CCM, CSCMR1, SSI2_CLK_SEL),
+	CLK_MUX("ssi3_sel", ssi_p, CCM, CSCMR1, SSI3_CLK_SEL),
+	CLK_MUX("usdhc1_sel", usdhc_p, CCM, CSCMR1, USDHC1_CLK_SEL),
+	CLK_MUX("usdhc2_sel", usdhc_p, CCM, CSCMR1, USDHC2_CLK_SEL),
+	CLK_MUX("usdhc3_sel", usdhc_p, CCM, CSCMR1, USDHC3_CLK_SEL),
+	CLK_MUX("usdhc4_sel", usdhc_p, CCM, CSCMR1, USDHC4_CLK_SEL),
+	CLK_MUX("eim_sel", eim_p, CCM, CSCMR1, ACLK_SEL),
+	CLK_MUX("eim_slow_sel", eim_slow_p, CCM, CSCMR1, ACLK_EIM_SLOW_SEL),
+	CLK_MUX("enfc_sel", enfc_p, CCM, CS2CDR, ENFC_CLK_SEL),
+
+	CLK_MUX("pll1_bypass_src", pll_bypass_src_p, CCM_ANALOG, PLL_ARM, BYPASS_CLK_SRC),
+	CLK_MUX("pll2_bypass_src", pll_bypass_src_p, CCM_ANALOG, PLL_SYS, BYPASS_CLK_SRC),
+	CLK_MUX("pll3_bypass_src", pll_bypass_src_p, CCM_ANALOG, PLL_USB1, BYPASS_CLK_SRC),
+	CLK_MUX("pll4_bypass_src", pll_bypass_src_p, CCM_ANALOG, PLL_AUDIO, BYPASS_CLK_SRC),
+	CLK_MUX("pll5_bypass_src", pll_bypass_src_p, CCM_ANALOG, PLL_VIDEO, BYPASS_CLK_SRC),
+	CLK_MUX("pll6_bypass_src", pll_bypass_src_p, CCM_ANALOG, PLL_ENET, BYPASS_CLK_SRC),
+	CLK_MUX("pll7_bypass_src", pll_bypass_src_p, CCM_ANALOG, PLL_USB2, BYPASS_CLK_SRC),
+	CLK_MUX("pll1_bypass", pll1_bypass_p, CCM_ANALOG, PLL_ARM, BYPASS),
+	CLK_MUX("pll2_bypass", pll2_bypass_p, CCM_ANALOG, PLL_SYS, BYPASS),
+	CLK_MUX("pll3_bypass", pll3_bypass_p, CCM_ANALOG, PLL_USB1, BYPASS),
+	CLK_MUX("pll4_bypass", pll4_bypass_p, CCM_ANALOG, PLL_AUDIO, BYPASS),
+	CLK_MUX("pll5_bypass", pll5_bypass_p, CCM_ANALOG, PLL_VIDEO, BYPASS),
+	CLK_MUX("pll6_bypass", pll6_bypass_p, CCM_ANALOG, PLL_ENET, BYPASS),
+	CLK_MUX("pll7_bypass", pll7_bypass_p, CCM_ANALOG, PLL_USB2, BYPASS),
+
+	CLK_MUX("lvds1_sel", lvds_p, CCM_ANALOG, MISC1, LVDS_CLK1_SRC),
+	CLK_MUX("lvds2_sel", lvds_p, CCM_ANALOG, MISC1, LVDS_CLK2_SRC),
+
+	CLK_MUX_BUSY("periph", periph_p, CBCDR, PERIPH_CLK_SEL, CDHIPR, PERIPH_CLK_SEL_BUSY),
+	CLK_MUX_BUSY("periph2", periph2_p, CBCDR, PERIPH2_CLK_SEL, CDHIPR, PERIPH2_CLK_SEL_BUSY),
+
+	CLK_GATE("apbh_dma", "usdhc3", CCM, CCGR0, APBHDMA_HCLK_ENABLE),
+	CLK_GATE("asrc", "asrc_podf", CCM, CCGR0, ASRC_CLK_ENABLE),
+	CLK_GATE("asrc_ipg", "ahb", CCM, CCGR0, ASRC_CLK_ENABLE),
+	CLK_GATE("asrc_mem", "ahb", CCM, CCGR0, ASRC_CLK_ENABLE),
+	CLK_GATE("caam_mem", "ahb", CCM, CCGR0, CAAM_SECURE_MEM_CLK_ENABLE),
+	CLK_GATE("caam_aclk", "ahb", CCM, CCGR0, CAAM_WRAPPER_ACLK_ENABLE),
+	CLK_GATE("caam_ipg", "ipg", CCM, CCGR0, CAAM_WRAPPER_IPG_ENABLE),
+	CLK_GATE("can1_ipg", "ipg", CCM, CCGR0, CAN1_CLK_ENABLE),
+	CLK_GATE("can1_serial", "can_root", CCM, CCGR0, CAN1_SERIAL_CLK_ENABLE),
+	CLK_GATE("can2_ipg", "ipg", CCM, CCGR0, CAN2_CLK_ENABLE),
+	CLK_GATE("can2_serial", "can_root", CCM, CCGR0, CAN2_SERIAL_CLK_ENABLE),
+	CLK_GATE("ecspi1", "ecspi_root", CCM, CCGR1, ECSPI1_CLK_ENABLE),
+	CLK_GATE("ecspi2", "ecspi_root", CCM, CCGR1, ECSPI2_CLK_ENABLE),
+	CLK_GATE("ecspi3", "ecspi_root", CCM, CCGR1, ECSPI3_CLK_ENABLE),
+	CLK_GATE("ecspi4", "ecspi_root", CCM, CCGR1, ECSPI4_CLK_ENABLE),
+	CLK_GATE("ecspi5", "ecspi_root", CCM, CCGR1, ECSPI5_CLK_ENABLE),
+	CLK_GATE("enet", "ipg", CCM, CCGR1, ENET_CLK_ENABLE),
+	CLK_GATE("esai_extal", "esai_podf", CCM, CCGR1, ESAI_CLK_ENABLE),
+	CLK_GATE("esai_ipg", "ahb", CCM, CCGR1, ESAI_CLK_ENABLE),
+	CLK_GATE("esai_mem", "ahb", CCM, CCGR1, ESAI_CLK_ENABLE),
+	CLK_GATE("gpt_ipg", "ipg", CCM, CCGR1, GPT_CLK_ENABLE),
+	CLK_GATE("gpt_ipg_per", "ipg_per", CCM, CCGR1, GPT_SERIAL_CLK_ENABLE),
+	CLK_GATE("gpu2d_core", "gpu2d_core_podf", CCM, CCGR1, GPU2D_CLK_ENABLE),
+	CLK_GATE("gpu3d_core", "gpu3d_core_podf", CCM, CCGR1, GPU3D_CLK_ENABLE),
+	CLK_GATE("hdmi_iahb", "ahb", CCM, CCGR2, HDMI_TX_IAHBCLK_ENABLE),
+	CLK_GATE("hdmi_isfr", "video_27m", CCM, CCGR2, HDMI_TX_ISFRCLK_ENABLE),
+	CLK_GATE("i2c1", "ipg_per", CCM, CCGR2, I2C1_SERIAL_CLK_ENABLE),
+	CLK_GATE("i2c2", "ipg_per", CCM, CCGR2, I2C2_SERIAL_CLK_ENABLE),
+	CLK_GATE("i2c3", "ipg_per", CCM, CCGR2, I2C3_SERIAL_CLK_ENABLE),
+	CLK_GATE("iim", "ipg", CCM, CCGR2, IIM_CLK_ENABLE),
+	CLK_GATE("enfc", "enfc_podf", CCM, CCGR2, IOMUX_IPT_CLK_IO_CLK_ENABLE),
+	CLK_GATE("vdoa", "vdo_axi", CCM, CCGR2, IPSYNC_VDOA_IPG_CLK_ENABLE),
+	CLK_GATE("ipu1", "ipu1_podf", CCM, CCGR3, IPU1_IPU_CLK_ENABLE),
+	CLK_GATE("ipu1_di0", "ipu1_di0_sel", CCM, CCGR3, IPU1_IPU_DI0_CLK_ENABLE),
+	CLK_GATE("ipu1_di1", "ipu1_di1_sel", CCM, CCGR3, IPU1_IPU_DI1_CLK_ENABLE),
+	CLK_GATE("ipu2", "ipu2_podf", CCM, CCGR3, IPU2_IPU_CLK_ENABLE),
+	CLK_GATE("ipu2_di0", "ipu2_di0_sel", CCM, CCGR3, IPU2_IPU_DI0_CLK_ENABLE),
+	CLK_GATE("ldb_di0", "ldb_di0_podf", CCM, CCGR3, LDB_DI0_CLK_ENABLE),
+	CLK_GATE("ldb_di1", "ldb_di1_podf", CCM, CCGR3, LDB_DI1_CLK_ENABLE),
+	CLK_GATE("ipu2_di1", "ipu2_di1_sel", CCM, CCGR3, IPU2_IPU_DI1_CLK_ENABLE),
+	CLK_GATE("hsi_tx", "hsi_tx_podf", CCM, CCGR3, MIPI_CORE_CFG_CLK_ENABLE),
+	CLK_GATE("mipi_core_cfg", "video_27m", CCM, CCGR3, MIPI_CORE_CFG_CLK_ENABLE),
+	CLK_GATE("mipi_ipg", "ipg", CCM, CCGR3, MIPI_CORE_CFG_CLK_ENABLE),
+	CLK_GATE("mlb", "axi", CCM, CCGR3, MLB_CLK_ENABLE),
+	CLK_GATE("mmdc_ch0_axi", "mmdc_ch0_axi_podf", CCM, CCGR3, MMDC_CORE_ACLK_FAST_CORE_P0_ENABLE),
+	CLK_GATE("mmdc_ch1_axi", "mmdc_ch1_axi_podf", CCM, CCGR3, MMDC_CORE_ACLK_FAST_CORE_P1_ENABLE),
+	CLK_GATE("ocram", "ahb", CCM, CCGR3, OCRAM_CLK_ENABLE),
+	CLK_GATE("openvg_axi", "axi", CCM, CCGR3, OPENVGAXICLK_CLK_ROOT_ENABLE),
+	CLK_GATE("pcie_axi", "pcie_axi_sel", CCM, CCGR4, PCIE_ROOT_ENABLE),
+	CLK_GATE("per1_bch", "usdhc3", CCM, CCGR4, PL301_MX6QPER1_BCHCLK_ENABLE),
+	CLK_GATE("pwm1", "ipg_per", CCM, CCGR4, PWM1_CLK_ENABLE),
+	CLK_GATE("pwm2", "ipg_per", CCM, CCGR4, PWM2_CLK_ENABLE),
+	CLK_GATE("pwm3", "ipg_per", CCM, CCGR4, PWM3_CLK_ENABLE),
+	CLK_GATE("pwm4", "ipg_per", CCM, CCGR4, PWM4_CLK_ENABLE),
+	CLK_GATE("gpmi_bch_apb", "usdhc3", CCM, CCGR4, RAWNAND_U_BCH_INPUT_APB_CLK_ENABLE),
+	CLK_GATE("gpmi_bch", "usdhc4", CCM, CCGR4, RAWNAND_U_GPMI_BCH_INPUT_BCH_CLK_ENABLE),
+	CLK_GATE("gpmi_io", "enfc", CCM, CCGR4, RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_CLK_ENABLE),
+	CLK_GATE("gpmi_apb", "usdhc3", CCM, CCGR4, RAWNAND_U_GPMI_INPUT_APB_CLK_ENABLE),
+	CLK_GATE("rom", "ahb", CCM, CCGR5, ROM_CLK_ENABLE),
+	CLK_GATE("sata", "ahb", CCM, CCGR5, SATA_CLK_ENABLE),
+	CLK_GATE("sdma", "ahb", CCM, CCGR5, SDMA_CLK_ENABLE),
+	CLK_GATE("spba", "ipg", CCM, CCGR5, SPBA_CLK_ENABLE),
+	CLK_GATE("spdif", "spdif_podf", CCM, CCGR5, SPDIF_CLK_ENABLE),
+	CLK_GATE("spdif_gclk", "ipg", CCM, CCGR5, SPDIF_CLK_ENABLE),
+	CLK_GATE("ssi1_ipg", "ipg", CCM, CCGR5, SSI1_CLK_ENABLE),
+	CLK_GATE("ssi2_ipg", "ipg", CCM, CCGR5, SSI2_CLK_ENABLE),
+	CLK_GATE("ssi3_ipg", "ipg", CCM, CCGR5, SSI3_CLK_ENABLE),
+	CLK_GATE("ssi1", "ssi1_podf", CCM, CCGR5, SSI1_CLK_ENABLE),
+	CLK_GATE("ssi2", "ssi2_podf", CCM, CCGR5, SSI2_CLK_ENABLE),
+	CLK_GATE("ssi3", "ssi3_podf", CCM, CCGR5, SSI3_CLK_ENABLE),
+	CLK_GATE("uart_ipg", "ipg", CCM, CCGR5, UART_CLK_ENABLE),
+	CLK_GATE("uart_serial", "uart_serial_podf", CCM, CCGR5, UART_SERIAL_CLK_ENABLE),
+	CLK_GATE("usboh3", "ipg", CCM, CCGR6, USBOH3_CLK_ENABLE),
+	CLK_GATE("usdhc1", "usdhc1_podf", CCM, CCGR6, USDHC1_CLK_ENABLE),
+	CLK_GATE("usdhc2", "usdhc2_podf", CCM, CCGR6, USDHC2_CLK_ENABLE),
+	CLK_GATE("usdhc3", "usdhc3_podf", CCM, CCGR6, USDHC3_CLK_ENABLE),
+	CLK_GATE("usdhc4", "usdhc4_podf", CCM, CCGR6, USDHC4_CLK_ENABLE),
+	CLK_GATE("eim_slow", "eim_slow_podf", CCM, CCGR6, EIM_SLOW_CLK_ENABLE),
+	CLK_GATE("vdo_axi", "vdo_axi_sel", CCM, CCGR6, VDOAXICLK_CLK_ENABLE),
+	CLK_GATE("vpu_axi", "vpu_axi_podf", CCM, CCGR6, VPU_CLK_ENABLE),
+	CLK_GATE("cko1", "cko1_podf", CCM, CCOSR, CLKO1_EN),
+	CLK_GATE("cko2", "cko2_podf", CCM, CCOSR, CLKO2_EN),
+
+	CLK_GATE("sata_ref_100m", "sata_ref", CCM_ANALOG, PLL_ENET, ENABLE_100M),
+	CLK_GATE("pcie_ref_125m", "pcie_ref", CCM_ANALOG, PLL_ENET, ENABLE_125M),
+
+	CLK_GATE("pll1_sys", "pll1_bypass", CCM_ANALOG, PLL_ARM, ENABLE),
+	CLK_GATE("pll2_bus", "pll2_bypass", CCM_ANALOG, PLL_SYS, ENABLE),
+	CLK_GATE("pll3_usb_otg", "pll3_bypass", CCM_ANALOG, PLL_USB1, ENABLE),
+	CLK_GATE("pll4_audio", "pll4_bypass", CCM_ANALOG, PLL_AUDIO, ENABLE),
+	CLK_GATE("pll5_video", "pll5_bypass", CCM_ANALOG, PLL_VIDEO, ENABLE),
+	CLK_GATE("pll6_enet", "pll6_bypass", CCM_ANALOG, PLL_ENET, ENABLE),
+	CLK_GATE("pll7_usb_host", "pll7_bypass", CCM_ANALOG, PLL_USB2, ENABLE),
+
+	CLK_GATE("usbphy1", "pll3_usb_otg", CCM_ANALOG, PLL_USB1, RESERVED),
+	CLK_GATE("usbphy2", "pll7_usb_host", CCM_ANALOG, PLL_USB2, RESERVED),
+
+	CLK_GATE_EXCLUSIVE("lvds1_gate", "lvds1_sel", CCM_ANALOG, MISC1, LVDS_CLK1_OBEN, LVDS_CLK1_IBEN),
+	CLK_GATE_EXCLUSIVE("lvds2_gate", "lvds2_sel", CCM_ANALOG, MISC1, LVDS_CLK2_OBEN, LVDS_CLK2_IBEN),
+};
+
+static struct imx6_clk *imx6_clk_find(const char *);
+
+static void imxccm_init_clocks(struct imxccm_softc *);
+static struct clk *imxccm_clk_get(void *, const char *);
+static void imxccm_clk_put(void *, struct clk *);
+static u_int imxccm_clk_get_rate(void *, struct clk *);
+static int imxccm_clk_set_rate(void *, struct clk *, u_int);
+static int imxccm_clk_enable(void *, struct clk *);
+static int imxccm_clk_disable(void *, struct clk *);
+static int imxccm_clk_set_parent(void *, struct clk *, struct clk *);
+static struct clk *imxccm_clk_get_parent(void *, struct clk *);
+
+static const struct clk_funcs imxccm_clk_funcs = {
+	.get = imxccm_clk_get,
+	.put = imxccm_clk_put,
+	.get_rate = imxccm_clk_get_rate,
+	.set_rate = imxccm_clk_set_rate,
+	.enable = imxccm_clk_enable,
+	.disable = imxccm_clk_disable,
+	.set_parent = imxccm_clk_set_parent,
+	.get_parent = imxccm_clk_get_parent,
+};
 
 static int imxccm_match(device_t, cfdata_t, void *);
 static void imxccm_attach(device_t, device_t, void *);
 
-static int imxccm_sysctl_freq_helper(SYSCTLFN_PROTO);
-static int imxccm_sysctl_setup(struct imxccm_softc *);
-
 CFATTACH_DECL_NEW(imxccm, sizeof(struct imxccm_softc),
     imxccm_match, imxccm_attach, NULL, NULL);
 
@@ -98,9 +667,6 @@ imxccm_match(device_t parent, cfdata_t c
 {
 	struct axi_attach_args *aa = aux;
 
-	if (ccm_softc != NULL)
-		return 0;
-
 	if (aa->aa_addr == IMX6_AIPS1_BASE + AIPS1_CCM_BASE)
 		return 1;
 
@@ -114,10 +680,8 @@ imxccm_attach(device_t parent, device_t 
 	struct axi_attach_args *aa = aux;
 	bus_space_tag_t iot = aa->aa_iot;
 
-	ccm_softc = sc;
 	sc->sc_dev = self;
 	sc->sc_iot = iot;
-	sc->sc_log = NULL;
 
 	if (bus_space_map(iot, aa->aa_addr, AIPS1_CCM_SIZE, 0, &sc->sc_ioh)) {
 		aprint_error(": can't map CCM registers\n");
@@ -131,817 +695,571 @@ imxccm_attach(device_t parent, device_t 
 		return;
 	}
 
-	aprint_normal(": Clock Control Module\n");
 	aprint_naive("\n");
+	aprint_normal(": Clock Control Module\n");
+
+	sc->sc_clkdom.name = device_xname(self);
+	sc->sc_clkdom.funcs = &imxccm_clk_funcs;
+	sc->sc_clkdom.priv = sc;
+	for (u_int n = 0; n < __arraycount(imx6_clks); n++) {
+		imx6_clks[n].base.domain = &sc->sc_clkdom;
+		clk_attach(&imx6_clks[n].base);
+	}
 
-	imxccm_sysctl_setup(sc);
+	imxccm_init_clocks(sc);
 
-	aprint_verbose_dev(self, "PLL_ARM clock=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL1));
-	aprint_verbose_dev(self, "PLL_SYS clock=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL2));
-	aprint_verbose_dev(self, "PLL_USB1 clock=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL3));
-	aprint_verbose_dev(self, "PLL_USB2 clock=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL7));
-	aprint_verbose_dev(self, "PLL_AUDIO clock=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL4));
-	aprint_verbose_dev(self, "PLL_VIDEO clock=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL5));
-	aprint_verbose_dev(self, "PLL_ENET clock=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL6));
-	aprint_verbose_dev(self, "PLL_MLB clock=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL7));
-
-	aprint_verbose_dev(self, "IMX6CLK_PLL2_PFD0=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL2_PFD0));
-	aprint_verbose_dev(self, "IMX6CLK_PLL2_PFD1=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL2_PFD1));
-	aprint_verbose_dev(self, "IMX6CLK_PLL2_PFD2=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL2_PFD2));
-	aprint_verbose_dev(self, "IMX6CLK_PLL3_PFD0=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL3_PFD0));
-	aprint_verbose_dev(self, "IMX6CLK_PLL3_PFD1=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL3_PFD1));
-	aprint_verbose_dev(self, "IMX6CLK_PLL3_PFD2=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL3_PFD2));
-	aprint_verbose_dev(self, "IMX6CLK_PLL3_PFD3=%d\n",
-	    imx6_get_clock(IMX6CLK_PLL3_PFD3));
-	aprint_verbose_dev(self, "IMX6CLK_ARM_ROOT=%d\n",
-	    imx6_get_clock(IMX6CLK_ARM_ROOT));
-	aprint_verbose_dev(self, "IMX6CLK_PERIPH=%d\n",
-	    imx6_get_clock(IMX6CLK_PERIPH));
-	aprint_verbose_dev(self, "IMX6CLK_AHB=%d\n",
-	    imx6_get_clock(IMX6CLK_AHB));
-	aprint_verbose_dev(self, "IMX6CLK_IPG=%d\n",
-	    imx6_get_clock(IMX6CLK_IPG));
-	aprint_verbose_dev(self, "IMX6CLK_AXI=%d\n",
-	    imx6_get_clock(IMX6CLK_AXI));
-
-	aprint_verbose_dev(self, "IMX6CLK_USDHC1=%d\n",
-	    imx6_get_clock(IMX6CLK_USDHC1));
-	aprint_verbose_dev(self, "IMX6CLK_USDHC2=%d\n",
-	    imx6_get_clock(IMX6CLK_USDHC2));
-	aprint_verbose_dev(self, "IMX6CLK_USDHC3=%d\n",
-	    imx6_get_clock(IMX6CLK_USDHC3));
-	aprint_verbose_dev(self, "IMX6CLK_USDHC4=%d\n",
-	    imx6_get_clock(IMX6CLK_USDHC4));
+	for (int n = 0; n < __arraycount(imx6_clks); n++) {
+		struct clk *clk = &imx6_clks[n].base;
+		struct clk *clk_parent = clk_get_parent(clk);
+		const char *parent_str = clk_parent ? clk_parent->name : "none";
+		aprint_verbose_dev(self, "%s (%s): %u Hz\n", clk->name,
+		    parent_str, clk_get_rate(clk));
+	}
 }
 
-static int
-imxccm_sysctl_setup(struct imxccm_softc *sc)
+struct clk *
+imx6_get_clock(const char *name)
 {
-	const struct sysctlnode *node, *imxnode, *freqnode, *pllnode;
-	int rv;
+	struct imx6_clk *iclk;
+	iclk = imx6_clk_find(name);
 
-	rv = sysctl_createv(&sc->sc_log, 0, NULL, &node,
-	    CTLFLAG_PERMANENT, CTLTYPE_NODE,
-	    "machdep", NULL,
-	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &node, &imxnode,
-	    0, CTLTYPE_NODE,
-	    "imx6", NULL,
-	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &imxnode, &freqnode,
-	    0, CTLTYPE_NODE,
-	    "frequency", NULL,
-	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &pllnode,
-	    0, CTLTYPE_NODE,
-	    "pll", NULL,
-	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &pllnode, &node,
-	    CTLFLAG_READWRITE, CTLTYPE_INT,
-	    "arm", SYSCTL_DESCR("frequency of ARM clock (PLL1)"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_pll1_arm= node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &pllnode, &node,
-	    0, CTLTYPE_INT,
-	    "system", SYSCTL_DESCR("frequency of system clock (PLL2)"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_pll2_sys = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &pllnode, &node,
-	    0, CTLTYPE_INT,
-	    "usb1", SYSCTL_DESCR("frequency of USB1 clock (PLL3)"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_pll3_usb1 = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &pllnode, &node,
-	    0, CTLTYPE_INT,
-	    "usb2", SYSCTL_DESCR("frequency of USB2 clock (PLL7)"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_pll7_usb2 = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &pllnode, &node,
-	    0, CTLTYPE_INT,
-	    "audio", SYSCTL_DESCR("frequency of AUDIO clock (PLL4)"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_pll4_audio = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &pllnode, &node,
-	    0, CTLTYPE_INT,
-	    "video", SYSCTL_DESCR("frequency of VIDEO clock (PLL5)"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_pll5_video = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &pllnode, &node,
-	    0, CTLTYPE_INT,
-	    "enet", SYSCTL_DESCR("frequency of ENET clock (PLL6)"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_pll6_enet = node->sysctl_num;
-
-#if 0
-	rv = sysctl_createv(&sc->sc_log, 0, &pllnode, &node,
-	    0, CTLTYPE_INT,
-	    "mlb", SYSCTL_DESCR("frequency of MediaLinkBus clock (PLL8)"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_pll8_mlb = node->sysctl_num;
-#endif
+	if (iclk == NULL)
+		return NULL;
 
-	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
-	    CTLFLAG_READWRITE, CTLTYPE_INT,
-	    "arm", SYSCTL_DESCR("frequency of ARM Root clock"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_arm = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
-	    0, CTLTYPE_INT,
-	    "peripheral", SYSCTL_DESCR("current frequency of Peripheral clock"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_periph = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
-	    0, CTLTYPE_INT,
-	    "ahb", SYSCTL_DESCR("current frequency of AHB clock"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_ahb = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
-	    0, CTLTYPE_INT,
-	    "ipg", SYSCTL_DESCR("current frequency of IPG clock"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_ipg = node->sysctl_num;
-
-	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
-	    0, CTLTYPE_INT,
-	    "axi", SYSCTL_DESCR("current frequency of AXI clock"),
-	    imxccm_sysctl_freq_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
-	if (rv != 0)
-		goto fail;
-	sc->sc_sysctlnode_axi = node->sysctl_num;
+	return &iclk->base;
+}
 
-	return 0;
+static struct imx6_clk *
+imx6_clk_find(const char *name)
+{
+	if (name == NULL)
+		return NULL;
+
+	for (int n = 0; n < __arraycount(imx6_clks); n++) {
+		if (strcmp(imx6_clks[n].base.name, name) == 0)
+			return &imx6_clks[n];
+	}
+
+	return NULL;
+}
 
- fail:
-	aprint_error_dev(sc->sc_dev, "cannot initialize sysctl (err=%d)\n", rv);
+struct imxccm_init_parent {
+	const char *clock;
+	const char *parent;
+} imxccm_init_parents[] = {
+	{ "pll1_bypass",	"pll1" },
+	{ "pll2_bypass",	"pll2" },
+	{ "pll3_bypass",	"pll3" },
+	{ "pll4_bypass",	"pll4" },
+	{ "pll5_bypass",	"pll5" },
+	{ "pll6_bypass",	"pll6" },
+	{ "pll7_bypass",	"pll7" },
+	{ "lvds1_sel",		"sata_ref_100m" },
+};
 
-	sysctl_teardown(&sc->sc_log);
-	sc->sc_log = NULL;
+static void
+imxccm_init_clocks(struct imxccm_softc *sc)
+{
+	struct clk *clk;
+	struct clk *clk_parent;
 
-	return -1;
+	for (u_int n = 0; n < __arraycount(imxccm_init_parents); n++) {
+		clk = clk_get(&sc->sc_clkdom, imxccm_init_parents[n].clock);
+		KASSERT(clk != NULL);
+		clk_parent = clk_get(&sc->sc_clkdom, imxccm_init_parents[n].parent);
+		KASSERT(clk_parent != NULL);
+
+		int error = clk_set_parent(clk, clk_parent);
+		if (error) {
+			aprint_error_dev(sc->sc_dev,
+			    "couldn't set '%s' parent to '%s': %d\n",
+			    clk->name, clk_parent->name, error);
+		}
+		clk_put(clk_parent);
+		clk_put(clk);
+	}
 }
 
-static int
-imxccm_sysctl_freq_helper(SYSCTLFN_ARGS)
+static u_int
+imxccm_clk_get_rate_pll_generic(struct imxccm_softc *sc, struct imx6_clk *iclk,
+    const u_int rate_parent)
 {
-	struct sysctlnode node;
-	struct imxccm_softc *sc;
-	int value, ovalue, err;
-
-	node = *rnode;
-	sc = node.sysctl_data;
-
-	/* for sysctl read */
-	if (rnode->sysctl_num == sc->sc_sysctlnode_pll1_arm)
-		value = imx6_get_clock(IMX6CLK_PLL1);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_pll2_sys)
-		value = imx6_get_clock(IMX6CLK_PLL2);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_pll3_usb1)
-		value = imx6_get_clock(IMX6CLK_PLL3);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_pll7_usb2)
-		value = imx6_get_clock(IMX6CLK_PLL7);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_pll4_audio)
-		value = imx6_get_clock(IMX6CLK_PLL4);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_pll5_video)
-		value = imx6_get_clock(IMX6CLK_PLL5);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_pll6_enet)
-		value = imx6_get_clock(IMX6CLK_PLL6);
-#if 0
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_pll8_mlb)
-		value = imx6_get_clock(IMX6CLK_PLL8);
-#endif
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_arm)
-		value = imx6_get_clock(IMX6CLK_ARM_ROOT);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_periph)
-		value = imx6_get_clock(IMX6CLK_PERIPH);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_ipg)
-		value = imx6_get_clock(IMX6CLK_IPG);
-	else if (rnode->sysctl_num == sc->sc_sysctlnode_axi)
-		value = imx6_get_clock(IMX6CLK_AXI);
-	else
-		return EOPNOTSUPP;
+	struct imx6_clk_pll *pll = &iclk->clk.pll;
 
-#ifdef SYSCTL_BY_MHZ
-	value /= 1000 * 1000;	/* Hz -> MHz */
-#endif
-	ovalue = value;
+	KASSERT((pll->type == IMX6_CLK_PLL_GENNERIC) ||
+	    (pll->type == IMX6_CLK_PLL_USB));
 
-	node.sysctl_data = &value;
-	err = sysctl_lookup(SYSCTLFN_CALL(&node));
-	if (err != 0 || newp == NULL)
-		return err;
+	uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg);
+	uint32_t div = __SHIFTOUT(v, pll->mask);
 
-	/* for sysctl write */
-	if (value == ovalue)
-		return 0;
+	return rate_parent * ((div == 1) ? 22 : 20);
+}
 
-#ifdef SYSCTL_BY_MHZ
-	value *= 1000 * 1000;	/* MHz -> Hz */
-#endif
+static u_int
+imxccm_clk_get_rate_pll_sys(struct imxccm_softc *sc, struct imx6_clk *iclk,
+    const u_int rate_parent)
+{
+	struct imx6_clk_pll *pll = &iclk->clk.pll;
 
-	if (rnode->sysctl_num == sc->sc_sysctlnode_arm)
-		return imx6_set_clock(IMX6CLK_ARM_ROOT, value);
+	KASSERT(pll->type == IMX6_CLK_PLL_SYS);
 
-	return 0;
+	uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg);
+	uint32_t div = __SHIFTOUT(v, pll->mask);
+
+	return rate_parent * div / 2;
 }
 
+#define PLL_AUDIO_VIDEO_NUM_OFFSET	0x10
+#define PLL_AUDIO_VIDEO_DENOM_OFFSET	0x20
 
-uint32_t
-imx6_ccm_read(uint32_t reg)
+static u_int
+imxccm_clk_get_rate_pll_audio_video(struct imxccm_softc *sc,
+    struct imx6_clk *iclk, const u_int rate_parent)
 {
-	if (ccm_softc == NULL)
-		return 0;
+	struct imx6_clk_pll *pll = &iclk->clk.pll;
+	uint64_t freq;
+
+	KASSERT(pll->type == IMX6_CLK_PLL_AUDIO_VIDEO);
+
+	uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg);
+	uint32_t div = __SHIFTOUT(v, pll->mask);
+	uint32_t num = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog,
+	    pll->reg + PLL_AUDIO_VIDEO_NUM_OFFSET);
+	uint32_t denom = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog,
+	    pll->reg + PLL_AUDIO_VIDEO_DENOM_OFFSET);
+
+	uint64_t tmp = rate_parent * num / denom;
+	freq = (uint64_t)rate_parent * div + tmp;
 
-	return bus_space_read_4(ccm_softc->sc_iot, ccm_softc->sc_ioh, reg);
+	return freq;
 }
 
-void
-imx6_ccm_write(uint32_t reg, uint32_t val)
+static u_int
+imxccm_clk_get_rate_pll_enet(struct imxccm_softc *sc,
+    struct imx6_clk *iclk, const u_int rate_parent)
 {
-	if (ccm_softc == NULL)
-		return;
+	struct imx6_clk_pll *pll = &iclk->clk.pll;
 
-	bus_space_write_4(ccm_softc->sc_iot, ccm_softc->sc_ioh, reg, val);
-}
+	KASSERT(pll->type == IMX6_CLK_PLL_ENET);
 
+	return pll->ref;
+}
 
-uint32_t
-imx6_ccm_analog_read(uint32_t reg)
+static u_int
+imxccm_clk_get_rate_fixed_factor(struct imxccm_softc *sc, struct imx6_clk *iclk)
 {
-	if (ccm_softc == NULL)
-		return 0;
+	struct imx6_clk_fixed_factor *fixed_factor = &iclk->clk.fixed_factor;
+	struct imx6_clk *parent;
 
-	return bus_space_read_4(ccm_softc->sc_iot, ccm_softc->sc_ioh_analog,
-	    reg);
+	KASSERT(iclk->type == IMX6_CLK_FIXED_FACTOR);
+
+	parent = imx6_clk_find(iclk->parent);
+	KASSERT(parent != NULL);
+
+	const u_int rate_parent = imxccm_clk_get_rate(sc, &parent->base);
+
+	return rate_parent * fixed_factor->mult / fixed_factor->div;
 }
 
-void
-imx6_ccm_analog_write(uint32_t reg, uint32_t val)
+static u_int
+imxccm_clk_get_rate_pll(struct imxccm_softc *sc, struct imx6_clk *iclk)
 {
-	if (ccm_softc == NULL)
-		return;
+	struct imx6_clk_pll *pll = &iclk->clk.pll;
+	struct imx6_clk *parent;
+
+	KASSERT(iclk->type == IMX6_CLK_PLL);
 
-	bus_space_write_4(ccm_softc->sc_iot, ccm_softc->sc_ioh_analog, reg,
-	    val);
+	parent = imx6_clk_find(iclk->parent);
+	KASSERT(parent != NULL);
+
+	const u_int rate_parent = imxccm_clk_get_rate(sc, &parent->base);
+
+	switch(pll->type) {
+	case IMX6_CLK_PLL_GENNERIC:
+		return imxccm_clk_get_rate_pll_generic(sc, iclk, rate_parent);
+	case IMX6_CLK_PLL_SYS:
+		return imxccm_clk_get_rate_pll_sys(sc, iclk, rate_parent);
+	case IMX6_CLK_PLL_USB:
+		return imxccm_clk_get_rate_pll_generic(sc, iclk, rate_parent);
+	case IMX6_CLK_PLL_AUDIO_VIDEO:
+		return imxccm_clk_get_rate_pll_audio_video(sc, iclk, rate_parent);
+	case IMX6_CLK_PLL_ENET:
+		return imxccm_clk_get_rate_pll_enet(sc, iclk, rate_parent);
+	default:
+		panic("imx6: unknown pll type %d", iclk->type);
+	}
 }
 
-int
-imx6_set_clock(enum imx6_clock_id clk, uint32_t freq)
+static u_int
+imxccm_clk_get_rate_div(struct imxccm_softc *sc, struct imx6_clk *iclk)
 {
-	uint32_t v;
+	struct imx6_clk_div *div = &iclk->clk.div;
+	struct imx6_clk *parent;
 
-	if (ccm_softc == NULL)
-		return 0;
+	KASSERT(iclk->type == IMX6_CLK_DIV);
 
-	switch (clk) {
-	case IMX6CLK_ARM_ROOT:
-		{
-			uint32_t pll;
-			int cacrr;
-
-			for (cacrr = 7; cacrr >= 0; cacrr--) {
-				pll = (uint64_t)freq * (cacrr + 1) * 2 / IMX6_OSC_FREQ;
-				if (pll >= 54 && pll <= 108) {
-
-					v = imx6_ccm_read(CCM_CACRR);
-					v &= ~CCM_CACRR_ARM_PODF;
-					imx6_ccm_write(CCM_CACRR,
-					    v | __SHIFTIN(cacrr, CCM_CACRR_ARM_PODF));
-
-					v = imx6_ccm_analog_read(CCM_ANALOG_PLL_ARM);
-					v &= ~CCM_ANALOG_PLL_ARM_DIV_SELECT;
-					imx6_ccm_analog_write(CCM_ANALOG_PLL_ARM,
-					    v | __SHIFTIN(pll, CCM_ANALOG_PLL_ARM_DIV_SELECT));
+	parent = imx6_clk_find(iclk->parent);
+	KASSERT(parent != NULL);
 
-					v = imx6_get_clock(IMX6CLK_ARM_ROOT);
-					cpufreq_set_all(v);
-#ifdef CPU_CORTEXA9
-					a9tmr_update_freq(v / IMX6_PERIPHCLK_N);
-#endif
-					return 0;
-				}
-			}
-			return EINVAL;
-		}
-		break;
+	u_int rate = imxccm_clk_get_rate(sc, &parent->base);
 
-	default:
-		aprint_error_dev(ccm_softc->sc_dev,
-		    "clock %d: not supported yet\n", clk);
-		return EINVAL;
+	bus_space_handle_t ioh;
+	if (div->base == IMX6_CLK_REG_CCM_ANALOG)
+		ioh = sc->sc_ioh_analog;
+	else
+		ioh = sc->sc_ioh;
+
+	uint32_t v = bus_space_read_4(sc->sc_iot, ioh, div->reg);
+	uint32_t n = __SHIFTOUT(v, div->mask);
+
+	if (div->type == IMX6_CLK_DIV_TABLE) {
+		KASSERT(div->tbl != NULL);
+
+		for (int i = 0; div->tbl[i] != 0; i++)
+			if (div->tbl[i] == n)
+				rate /= div->tbl[i];
+	} else {
+		rate /= n + 1;
 	}
 
-	return 0;
+	return rate;
 }
 
-uint32_t
-imx6_get_clock(enum imx6_clock_id clk)
+static u_int
+imxccm_clk_get_rate_pfd(struct imxccm_softc *sc, struct imx6_clk *iclk)
 {
-	uint32_t d, denom, num, sel, v;
-	uint64_t freq;
+	struct imx6_clk_pfd *pfd = &iclk->clk.pfd;
+	struct imx6_clk *parent;
 
-	if (ccm_softc == NULL)
-		return 0;
+	KASSERT(iclk->type == IMX6_CLK_PFD);
 
-	switch (clk) {
-	/* CLOCK SWITCHER */
-	case IMX6CLK_PLL1:
-		v = imx6_ccm_analog_read(CCM_ANALOG_PLL_ARM);
-		freq = IMX6_OSC_FREQ * (v & CCM_ANALOG_PLL_ARM_DIV_SELECT) / 2;
-		break;
-	case IMX6CLK_PLL2:
-		v = imx6_ccm_analog_read(CCM_ANALOG_PLL_SYS);
-		freq = IMX6_OSC_FREQ * ((v & CCM_ANALOG_PLL_SYS_DIV_SELECT) ? 22 : 20);
-		break;
-	case IMX6CLK_PLL3:
-		v = imx6_ccm_analog_read(CCM_ANALOG_PLL_USB1);
-		freq = IMX6_OSC_FREQ * ((v & CCM_ANALOG_PLL_USB1_DIV_SELECT) ? 22 : 20);
-		break;
+	parent = imx6_clk_find(iclk->parent);
+	KASSERT(parent != NULL);
 
-	case IMX6CLK_PLL4:
-		v = imx6_ccm_analog_read(CCM_ANALOG_PLL_AUDIO);
-		d = __SHIFTOUT(v, CCM_ANALOG_PLL_AUDIO_DIV_SELECT);
-		num = imx6_ccm_analog_read(CCM_ANALOG_PLL_AUDIO_NUM);
-		denom = imx6_ccm_analog_read(CCM_ANALOG_PLL_AUDIO_DENOM);
-		freq = (uint64_t)IMX6_OSC_FREQ * (d + num / denom);
-		d = __SHIFTOUT(v, CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT);
-		freq = freq >> (2 - d);
-		break;
+	const u_int rate_parent = imxccm_clk_get_rate(sc, &parent->base);
 
-	case IMX6CLK_PLL5:
-		v = imx6_ccm_analog_read(CCM_ANALOG_PLL_VIDEO);
-		d = __SHIFTOUT(v, CCM_ANALOG_PLL_VIDEO_DIV_SELECT);
-		num = imx6_ccm_analog_read(CCM_ANALOG_PLL_VIDEO_NUM);
-		denom = imx6_ccm_analog_read(CCM_ANALOG_PLL_VIDEO_DENOM);
-		freq = (uint64_t)IMX6_OSC_FREQ * (d + num / denom);
-		d = __SHIFTOUT(v, CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT);
-		freq = freq >> (2 - d);
-		break;
+	uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pfd->reg);
+	uint32_t n = __SHIFTOUT(v, __BITS(5, 0) << (pfd->index * 8));
 
-	case IMX6CLK_PLL6:
-		/* XXX: iMX6UL has 2 div. which? */
-		v = imx6_ccm_analog_read(CCM_ANALOG_PLL_ENET);
-		switch (v & CCM_ANALOG_PLL_ENET_DIV_SELECT) {
-		case 0:
-			freq = 25 * 1000 * 1000;
-			break;
-		case 1:
-			freq = 50 * 1000 * 1000;
-			break;
-		case 2:
-			freq = 100 * 1000 * 1000;
-			break;
-		case 3:
-			freq = 125 * 1000 * 1000;
+	KASSERT(n != 0);
+
+	return (rate_parent * 18) / n;
+}
+
+static int
+imxccm_clk_mux_wait(struct imxccm_softc *sc, struct imx6_clk_mux *mux)
+{
+	KASSERT(mux->busy_reg == 0);
+	KASSERT(mux->busy_mask == 0);
+
+	bus_space_handle_t ioh;
+	if (mux->base == IMX6_CLK_REG_CCM_ANALOG)
+		ioh = sc->sc_ioh_analog;
+	else
+		ioh = sc->sc_ioh;
+
+	while (bus_space_read_4(sc->sc_iot, ioh, mux->busy_reg) & mux->busy_mask)
+		delay(10);
+
+	return 0;
+}
+
+static int
+imxccm_clk_set_parent_mux(struct imxccm_softc *sc,
+    struct imx6_clk *iclk, struct clk *parent)
+{
+	struct imx6_clk_mux *mux = &iclk->clk.mux;
+	const char *pname = parent->name;
+	u_int sel;
+
+	KASSERT(iclk->type == IMX6_CLK_MUX);
+
+	for (sel = 0; sel < mux->nparents; sel++)
+		if (strcmp(pname, mux->parents[sel]) == 0)
 			break;
-		}
-		break;
-	case IMX6CLK_PLL7:
-		v = imx6_ccm_analog_read(CCM_ANALOG_PLL_USB2);
-		freq = IMX6_OSC_FREQ * ((v & CCM_ANALOG_PLL_DIV_SELECT) ? 22 : 20);
-		break;
 
-#if 0
-	case IMX6CLK_PLL8:
-		/* XXX notyet */
-		break;
-#endif
+	if (sel == mux->nparents)
+		return EINVAL;
 
-	case IMX6CLK_PLL2_PFD0:
-		freq = imx6_get_clock(IMX6CLK_PLL2);
-		v = imx6_ccm_analog_read(CCM_ANALOG_PFD_528);
-		freq = freq * 18 / __SHIFTOUT(v, CCM_ANALOG_PFD_528_PFD0_FRAC);
-		break;
-	case IMX6CLK_PLL2_PFD1:
-		freq = imx6_get_clock(IMX6CLK_PLL2);
-		v = imx6_ccm_analog_read(CCM_ANALOG_PFD_528);
-		freq = freq * 18 / __SHIFTOUT(v, CCM_ANALOG_PFD_528_PFD1_FRAC);
-		break;
-	case IMX6CLK_PLL2_PFD2:
-		freq = imx6_get_clock(IMX6CLK_PLL2);
-		v = imx6_ccm_analog_read(CCM_ANALOG_PFD_528);
-		freq = freq * 18 / __SHIFTOUT(v, CCM_ANALOG_PFD_528_PFD2_FRAC);
-		break;
-	case IMX6CLK_PLL3_PFD3:
-		freq = imx6_get_clock(IMX6CLK_PLL3);
-		v = imx6_ccm_analog_read(CCM_ANALOG_PFD_480);
-		freq = freq * 18 / __SHIFTOUT(v, CCM_ANALOG_PFD_480_PFD3_FRAC);
-		break;
-	case IMX6CLK_PLL3_PFD2:
-		freq = imx6_get_clock(IMX6CLK_PLL3);
-		v = imx6_ccm_analog_read(CCM_ANALOG_PFD_480);
-		freq = freq * 18 / __SHIFTOUT(v, CCM_ANALOG_PFD_480_PFD2_FRAC);
-		break;
-	case IMX6CLK_PLL3_PFD1:
-		freq = imx6_get_clock(IMX6CLK_PLL3);
-		v = imx6_ccm_analog_read(CCM_ANALOG_PFD_480);
-		freq = freq * 18 / __SHIFTOUT(v, CCM_ANALOG_PFD_480_PFD1_FRAC);
-		break;
-	case IMX6CLK_PLL3_PFD0:
-		freq = imx6_get_clock(IMX6CLK_PLL3);
-		v = imx6_ccm_analog_read(CCM_ANALOG_PFD_480);
-		freq = freq * 18 / __SHIFTOUT(v, CCM_ANALOG_PFD_480_PFD0_FRAC);
-		break;
+	bus_space_handle_t ioh;
+	if (mux->base == IMX6_CLK_REG_CCM_ANALOG)
+		ioh = sc->sc_ioh_analog;
+	else
+		ioh = sc->sc_ioh;
 
-	/* CLOCK ROOT GEN */
-	case IMX6CLK_ARM_ROOT:
-		freq = imx6_get_clock(IMX6CLK_PLL1);
-		v = __SHIFTOUT(imx6_ccm_read(CCM_CACRR), CCM_CACRR_ARM_PODF);
-		freq = freq / (v + 1);
-		break;
+	uint32_t v = bus_space_read_4(sc->sc_iot, ioh, mux->reg);
+	v &= ~mux->mask;
+	v |= __SHIFTIN(sel, mux->mask);
 
-	case IMX6CLK_PERIPH:
-		v = imx6_ccm_read(CCM_CBCDR);
-		if (v & CCM_CBCDR_PERIPH_CLK_SEL) {
-			v = imx6_ccm_read(CCM_CBCMR);
-			sel = __SHIFTOUT(v, CCM_CBCMR_PERIPH_CLK2_SEL);
-			switch (sel) {
-			case 0:
-				freq = imx6_get_clock(IMX6CLK_PLL3);
-				break;
-			case 1:
-			case 2:
-				freq = IMX6_OSC_FREQ;
-				break;
-			case 3:
-				freq = 0;
-				aprint_error_dev(ccm_softc->sc_dev,
-				    "IMX6CLK_PERIPH: CCM_CBCMR:CCM_CBCMR_PERIPH_CLK2_SEL is set reserved value\n");
-				break;
-			}
-		} else {
-			v = imx6_ccm_read(CCM_CBCMR);
-			sel = __SHIFTOUT(v, CCM_CBCMR_PRE_PERIPH_CLK_SEL);
-			switch (sel) {
-			case 0:
-				freq = imx6_get_clock(IMX6CLK_PLL2);
-				break;
-			case 1:
-				freq = imx6_get_clock(IMX6CLK_PLL2_PFD2);
-				break;
-			case 2:
-				freq = imx6_get_clock(IMX6CLK_PLL2_PFD0);
-				break;
-			case 3:
-				freq = imx6_get_clock(IMX6CLK_PLL2_PFD2) / 2;
-				break;
-			}
-		}
-		break;
-	case IMX6CLK_AHB:
-		freq = imx6_get_clock(IMX6CLK_PERIPH);
-		v = imx6_ccm_read(CCM_CBCDR);
-		freq = freq / (__SHIFTOUT(v, CCM_CBCDR_AHB_PODF) + 1);
-		break;
-	case IMX6CLK_IPG:
-		freq = imx6_get_clock(IMX6CLK_AHB);
-		v = imx6_ccm_read(CCM_CBCDR);
-		freq = freq / (__SHIFTOUT(v, CCM_CBCDR_IPG_PODF) + 1);
-		break;
-	case IMX6CLK_AXI:
-		v = imx6_ccm_read(CCM_CBCDR);
-		if (v & CCM_CBCDR_AXI_SEL) {
-			if (v & CCM_CBCDR_AXI_ALT_SEL) {
-				freq = imx6_get_clock(IMX6CLK_PLL2_PFD2);
-			} else {
-				freq = imx6_get_clock(IMX6CLK_PLL3_PFD1);
-			}
-		} else {
-			freq = imx6_get_clock(IMX6CLK_PERIPH);
-			freq = freq / (__SHIFTOUT(v, CCM_CBCDR_AXI_PODF) + 1);
-		}
-		break;
+	bus_space_write_4(sc->sc_iot, ioh, mux->reg, v);
 
-	case IMX6CLK_USDHC1:
-		v = imx6_ccm_read(CCM_CSCMR1);
-		freq = imx6_get_clock((v & CCM_CSCMR1_USDHC1_CLK_SEL) ?
-		    IMX6CLK_PLL2_PFD0 : IMX6CLK_PLL2_PFD2);
-		v = imx6_ccm_read(CCM_CSCDR1);
-		freq = freq / (__SHIFTOUT(v, CCM_CSCDR1_USDHC1_PODF) + 1);
-		break;
-	case IMX6CLK_USDHC2:
-		v = imx6_ccm_read(CCM_CSCMR1);
-		freq = imx6_get_clock((v & CCM_CSCMR1_USDHC2_CLK_SEL) ?
-		    IMX6CLK_PLL2_PFD0 : IMX6CLK_PLL2_PFD2);
-		v = imx6_ccm_read(CCM_CSCDR1);
-		freq = freq / (__SHIFTOUT(v, CCM_CSCDR1_USDHC2_PODF) + 1);
-		break;
-	case IMX6CLK_USDHC3:
-		v = imx6_ccm_read(CCM_CSCMR1);
-		freq = imx6_get_clock((v & CCM_CSCMR1_USDHC3_CLK_SEL) ?
-		    IMX6CLK_PLL2_PFD0 : IMX6CLK_PLL2_PFD2);
-		v = imx6_ccm_read(CCM_CSCDR1);
-		freq = freq / (__SHIFTOUT(v, CCM_CSCDR1_USDHC3_PODF) + 1);
-		break;
-	case IMX6CLK_USDHC4:
-		v = imx6_ccm_read(CCM_CSCMR1);
-		freq = imx6_get_clock((v & CCM_CSCMR1_USDHC4_CLK_SEL) ?
-		    IMX6CLK_PLL2_PFD0 : IMX6CLK_PLL2_PFD2);
-		v = imx6_ccm_read(CCM_CSCDR1);
-		freq = freq / (__SHIFTOUT(v, CCM_CSCDR1_USDHC4_PODF) + 1);
-		break;
+	iclk->parent = pname;
 
-	case IMX6CLK_PERCLK:
-		freq = imx6_get_clock(IMX6CLK_IPG);
-		v = imx6_ccm_read(CCM_CSCMR1);
-		freq = freq / (__SHIFTOUT(v, CCM_CSCMR1_PERCLK_PODF) + 1);
-		break;
+	if (mux->type == IMX6_CLK_MUX_BUSY)
+		imxccm_clk_mux_wait(sc, mux);
 
-	case IMX6CLK_MMDC_CH1_CLK_ROOT:
-		freq = imx6_get_clock(IMX6CLK_MMDC_CH1);
-		v = __SHIFTOUT(imx6_ccm_read(CCM_CBCDR), CCM_CBCDR_MMDC_CH1_AXI_PODF);
-		freq = freq / (v + 1);
-		break;
+	return 0;
+}
 
-	case IMX6CLK_MMDC_CH0_CLK_ROOT:
-		freq = imx6_get_clock(IMX6CLK_MMDC_CH0);
-		v = __SHIFTOUT(imx6_ccm_read(CCM_CBCDR), CCM_CBCDR_MMDC_CH0_AXI_PODF);
-		freq = freq / (v + 1);
-		break;
+static struct imx6_clk *
+imxccm_clk_get_parent_mux(struct imxccm_softc *sc, struct imx6_clk *iclk)
+{
+	struct imx6_clk_mux *mux = &iclk->clk.mux;
 
-	case IMX6CLK_MMDC_CH0:
-	case IMX6CLK_MMDC_CH1:
-		v = imx6_ccm_read(CCM_CBCMR);
-		sel = (clk == IMX6CLK_MMDC_CH0) ?
-		    __SHIFTOUT(v, CCM_CBCMR_PRE_PERIPH_CLK_SEL) :
-		    __SHIFTOUT(v, CCM_CBCMR_PRE_PERIPH2_CLK_SEL);
-		switch (sel) {
-		case 0:
-			freq = imx6_get_clock(IMX6CLK_PLL2);
-			break;
-		case 1:
-			freq = imx6_get_clock(IMX6CLK_PLL2_PFD2);
-			break;
-		case 2:
-			freq = imx6_get_clock(IMX6CLK_PLL2_PFD0);
-			break;
-		case 3:
-			freq = imx6_get_clock(IMX6CLK_PLL2_PFD2) / 2;
-			break;
-		}
-		break;
+	KASSERT(iclk->type == IMX6_CLK_MUX);
 
-	case IMX6CLK_IPU1_HSP_CLK_ROOT:
-		v = imx6_ccm_read(CCM_CSCDR3);
-		switch (__SHIFTOUT(v, CCM_CSCDR3_IPU1_HSP_CLK_SEL)) {
-		case 0:
-			freq = imx6_get_clock(IMX6CLK_MMDC_CH0);
-			break;
-		case 1:
-			freq = imx6_get_clock(IMX6CLK_PLL2_PFD2);
-			break;
-		case 2:
-			freq = imx6_get_clock(IMX6CLK_PLL3) / 4;
-			break;
-		case 3:
-			freq = imx6_get_clock(IMX6CLK_PLL3_PFD1);
-			break;
-		}
-		v = __SHIFTOUT(v, CCM_CSCDR3_IPU1_HSP_CLK_SEL);
-		freq = freq / (v + 1);
-		break;
-	case IMX6CLK_IPU2_HSP_CLK_ROOT:
-		v = imx6_ccm_read(CCM_CSCDR3);
-		switch (__SHIFTOUT(v, CCM_CSCDR3_IPU2_HSP_CLK_SEL)) {
-		case 0:
-			freq = imx6_get_clock(IMX6CLK_MMDC_CH0);
-			break;
-		case 1:
-			freq = imx6_get_clock(IMX6CLK_PLL2_PFD2);
-			break;
-		case 2:
-			freq = imx6_get_clock(IMX6CLK_PLL3) / 4;
-			break;
-		case 3:
-			freq = imx6_get_clock(IMX6CLK_PLL3_PFD1);
-			break;
-		}
-		v = __SHIFTOUT(v, CCM_CSCDR3_IPU2_HSP_CLK_SEL);
-		freq = freq / (v + 1);
-		break;
+	bus_space_handle_t ioh;
+	if (mux->base == IMX6_CLK_REG_CCM_ANALOG)
+		ioh = sc->sc_ioh_analog;
+	else
+		ioh = sc->sc_ioh;
 
-	case IMX6CLK_IPU1_DI0_CLK_ROOT:
-	case IMX6CLK_IPU1_DI1_CLK_ROOT:
-		v = imx6_ccm_read(CCM_CHSCCDR);
-		sel = (clk == IMX6CLK_IPU1_DI0_CLK_ROOT) ?
-		    __SHIFTOUT(v, CCM_CHSCCDR_IPU1_DI0_CLK_SEL) :
-		    __SHIFTOUT(v, CCM_CHSCCDR_IPU1_DI1_CLK_SEL);
-		switch (sel) {
-		case 0:
-			sel = (clk == IMX6CLK_IPU1_DI0_CLK_ROOT) ?
-			    __SHIFTOUT(v, CCM_CHSCCDR_IPU1_DI0_PRE_CLK_SEL) :
-			    __SHIFTOUT(v, CCM_CHSCCDR_IPU1_DI1_PRE_CLK_SEL);
-			switch (sel) {
-			case 0:
-				freq = imx6_get_clock(IMX6CLK_MMDC_CH0);
-				break;
-			case 1:
-				freq = imx6_get_clock(IMX6CLK_PLL3);
-				break;
-			case 2:
-				freq = imx6_get_clock(IMX6CLK_PLL5);
-				break;
-			case 3:
-				freq = imx6_get_clock(IMX6CLK_PLL2_PFD0);
-				break;
-			case 4:
-				freq = imx6_get_clock(IMX6CLK_PLL2_PFD2);
-				break;
-			case 5:
-				freq = imx6_get_clock(IMX6CLK_PLL3_PFD1);
-				break;
-			default:
-				/* reserved */
-				freq = 0;
-			}
-			if (clk == IMX6CLK_IPU1_DI0_CLK_ROOT)
-				freq = freq / (__SHIFTOUT(v, CCM_CHSCCDR_IPU1_DI0_PODF) + 1);
-			else
-				freq = freq / (__SHIFTOUT(v, CCM_CHSCCDR_IPU1_DI1_PODF) + 1);
-			break;
-		case 1:
-		case 2:
-			/* IPP_DI[01]_CLK is an external clock */
-			freq = 0;
-			break;
-		case 3:
-			freq = imx6_get_clock(IMX6CLK_LDB_DI0_IPU);
-			break;
-		case 4:
-			freq = imx6_get_clock(IMX6CLK_LDB_DI1_IPU);
-			break;
-		default:
-			/* reserved */
-			freq = 0;
-		}
-		break;
+	uint32_t v = bus_space_read_4(sc->sc_iot, ioh, mux->reg);
+	u_int sel = __SHIFTOUT(v, mux->mask);
 
-	case IMX6CLK_LDB_DI0_SERIAL_CLK_ROOT:
-	case IMX6CLK_LDB_DI1_SERIAL_CLK_ROOT:
-		v = imx6_ccm_read(CCM_CS2CDR);
-		sel = (clk == IMX6CLK_LDB_DI0_SERIAL_CLK_ROOT) ?
-		    __SHIFTOUT(v, CCM_CS2CDR_LDB_DI0_CLK_SEL) :
-		    __SHIFTOUT(v, CCM_CS2CDR_LDB_DI1_CLK_SEL);
-		switch (sel) {
-		case 0:
-			freq = imx6_get_clock(IMX6CLK_PLL5);
-			break;
-		case 1:
-			freq = imx6_get_clock(IMX6CLK_PLL2_PFD0);
-			break;
-		case 2:
-			freq = imx6_get_clock(IMX6CLK_PLL2_PFD2);
-			break;
-		case 3:
-			freq = imx6_get_clock(IMX6CLK_MMDC_CH1);
-			break;
-		case 4:
-			freq = imx6_get_clock(IMX6CLK_PLL3);
-			break;
-		case 5:
-		default:
-			/* reserved */
-			freq = 0;
-		}
-		break;
+	KASSERT(sel < mux->nparents);
 
-	case IMX6CLK_LDB_DI0_IPU:
-		freq = imx6_get_clock(IMX6CLK_LDB_DI0_SERIAL_CLK_ROOT);
-		v = imx6_ccm_read(CCM_CSCMR2);
-		if (!ISSET(v, CCM_CSCMR2_LDB_DI0_IPU_DIV))
-			freq *= 2;
-		freq /= 7;
-		break;
-	case IMX6CLK_LDB_DI1_IPU:
-		freq = imx6_get_clock(IMX6CLK_LDB_DI1_SERIAL_CLK_ROOT);
-		v = imx6_ccm_read(CCM_CSCMR2);
-		if (!ISSET(v, CCM_CSCMR2_LDB_DI1_IPU_DIV))
-			freq *= 2;
-		freq /= 7;
-		break;
+	iclk->parent = mux->parents[sel];
+
+	return imx6_clk_find(iclk->parent);
+}
+
+static int
+imxccm_clk_set_rate_pll(struct imxccm_softc *sc,
+    struct imx6_clk *eclk, u_int rate)
+{
+	/* ToDo */
+
+	return EOPNOTSUPP;
+}
+
+/*
+ * CLK Driver APIs
+ */
+static struct clk *
+imxccm_clk_get(void *priv, const char *name)
+{
+	struct imx6_clk *iclk;
+
+	iclk = imx6_clk_find(name);
+	if (iclk == NULL)
+		return NULL;
+
+	atomic_inc_uint(&iclk->refcnt);
+
+	return &iclk->base;
+}
+
+static void
+imxccm_clk_put(void *priv, struct clk *clk)
+{
+	struct imx6_clk *iclk = (struct imx6_clk *)clk;
+
+	KASSERT(iclk->refcnt > 0);
 
+	atomic_dec_uint(&iclk->refcnt);
+}
+
+static u_int
+imxccm_clk_get_rate(void *priv, struct clk *clk)
+{
+	struct imx6_clk *iclk = (struct imx6_clk *)clk;
+	struct clk *parent;
+	struct imxccm_softc *sc = priv;
+
+	switch (iclk->type) {
+	case IMX6_CLK_FIXED:
+		return iclk->clk.fixed.rate;
+	case IMX6_CLK_FIXED_FACTOR:
+		return imxccm_clk_get_rate_fixed_factor(sc, iclk);
+	case IMX6_CLK_PLL:
+		return imxccm_clk_get_rate_pll(sc, iclk);
+	case IMX6_CLK_MUX:
+	case IMX6_CLK_GATE:
+		parent = imxccm_clk_get_parent(sc, clk);
+		return imxccm_clk_get_rate(sc, parent);
+	case IMX6_CLK_DIV:
+		return imxccm_clk_get_rate_div(sc, iclk);
+	case IMX6_CLK_PFD:
+		return imxccm_clk_get_rate_pfd(sc, iclk);
 	default:
-		aprint_error_dev(ccm_softc->sc_dev,
-		    "clock %d: not supported yet\n", clk);
-		return 0;
+		panic("imx6: unknown clk type %d", iclk->type);
 	}
+}
 
-	return freq;
+static int
+imxccm_clk_set_rate(void *priv, struct clk *clk, u_int rate)
+{
+	struct imx6_clk *iclk = (struct imx6_clk *)clk;
+	struct imxccm_softc *sc = priv;
+
+	switch (iclk->type) {
+	case IMX6_CLK_FIXED:
+	case IMX6_CLK_FIXED_FACTOR:
+		return EIO;
+	case IMX6_CLK_PLL:
+		return imxccm_clk_set_rate_pll(sc, iclk, rate);
+	case IMX6_CLK_MUX:
+		return EIO;
+	case IMX6_CLK_GATE:
+	case IMX6_CLK_DIV:
+	case IMX6_CLK_PFD:
+		return EINVAL;
+	default:
+		panic("imx6: unknown clk type %d", iclk->type);
+	}
 }
 
-int
-imx6_pll_power(uint32_t pllreg, int on, uint32_t en)
+static int
+imxccm_clk_enable_pll(struct imxccm_softc *sc, struct imx6_clk *iclk, bool enable)
 {
-	uint32_t v;
-	int timeout;
+	struct imx6_clk_pll *pll = &iclk->clk.pll;
 
-	switch (pllreg) {
-	case CCM_ANALOG_PLL_USB1:
-	case CCM_ANALOG_PLL_USB2:
-		v = imx6_ccm_analog_read(pllreg);
-		if (on) {
-			v |= en;
-			v &= ~CCM_ANALOG_PLL_BYPASS;
-		} else {
-			v &= ~en;
-		}
-		imx6_ccm_analog_write(pllreg, v);
+	KASSERT(iclk->type == IMX6_CLK_PLL);
+
+	/* Power up bit */
+	if (pll->type == IMX6_CLK_PLL_USB)
+		enable = !enable;
+
+	bus_space_handle_t ioh = sc->sc_ioh_analog;
+	uint32_t  v = bus_space_read_4(sc->sc_iot, ioh, pll->reg);
+	if (__SHIFTOUT(v, pll->powerdown) != enable)
 		return 0;
+	if (enable)
+		v &= ~pll->powerdown;
+	else
+		v |= pll->powerdown;
+	bus_space_write_4(sc->sc_iot, ioh, pll->reg, v);
 
-	case CCM_ANALOG_PLL_ENET:
-		v = imx6_ccm_analog_read(pllreg);
-		if (on)
-			v &= ~CCM_ANALOG_PLL_ENET_POWERDOWN;
-		else
-			v |= CCM_ANALOG_PLL_ENET_POWERDOWN;
-		imx6_ccm_analog_write(pllreg, v);
-
-		for (timeout = 100000; timeout > 0; timeout--) {
-			if (imx6_ccm_analog_read(pllreg) &
-			    CCM_ANALOG_PLL_ENET_LOCK)
-				break;
-		}
-		if (timeout <= 0)
-			break;
+	/* wait look */
+	while (!(bus_space_read_4(sc->sc_iot, ioh, pll->reg) & CCM_ANALOG_PLL_LOCK))
+		delay(10);
 
-		v |= CCM_ANALOG_PLL_ENET_ENABLE;
-		if (on) {
-			v &= ~CCM_ANALOG_PLL_ENET_BYPASS;
-			imx6_ccm_analog_write(pllreg, v);
-			v |= en;
-		} else {
-			v &= ~en;
-		}
-		imx6_ccm_analog_write(pllreg, v);
+	return 0;
+}
+
+static int
+imxccm_clk_enable_gate(struct imxccm_softc *sc, struct imx6_clk *iclk, bool enable)
+{
+	struct imx6_clk_gate *gate = &iclk->clk.gate;
+
+	KASSERT(iclk->type == IMX6_CLK_GATE);
+
+	bus_space_handle_t ioh;
+	if (gate->base == IMX6_CLK_REG_CCM_ANALOG)
+		ioh = sc->sc_ioh_analog;
+	else
+		ioh = sc->sc_ioh;
+
+	uint32_t v = bus_space_read_4(sc->sc_iot, ioh, gate->reg);
+	if (enable) {
+		if (gate->exclusive_mask)
+			v &= ~gate->exclusive_mask;
+		v |= gate->mask;
+	} else {
+		if (gate->exclusive_mask)
+			v |= gate->exclusive_mask;
+		v &= ~gate->mask;
+	}
+	bus_space_write_4(sc->sc_iot, ioh, gate->reg, v);
+
+	return 0;
+}
+
+static int
+imxccm_clk_enable(void *priv, struct clk *clk)
+{
+	struct imx6_clk *iclk = (struct imx6_clk *)clk;
+	struct imx6_clk *parent = NULL;
+	struct imxccm_softc *sc = priv;
+
+	if ((parent = imx6_clk_find(iclk->parent)) != NULL)
+		imxccm_clk_enable(sc, &parent->base);
+
+	switch (iclk->type) {
+	case IMX6_CLK_FIXED:
+	case IMX6_CLK_FIXED_FACTOR:
+		return 0;	/* always on */
+	case IMX6_CLK_PLL:
+		return imxccm_clk_enable_pll(sc, iclk, true);
+	case IMX6_CLK_MUX:
+	case IMX6_CLK_DIV:
+	case IMX6_CLK_PFD:
 		return 0;
+	case IMX6_CLK_GATE:
+		return imxccm_clk_enable_gate(sc, iclk, true);
+	default:
+		panic("imx6: unknown clk type %d", iclk->type);
+	}
+}
 
-	case CCM_ANALOG_PLL_ARM:
-	case CCM_ANALOG_PLL_SYS:
-	case CCM_ANALOG_PLL_AUDIO:
-	case CCM_ANALOG_PLL_VIDEO:
-	case CCM_ANALOG_PLL_MLB:
-		/* notyet */
+static int
+imxccm_clk_disable(void *priv, struct clk *clk)
+{
+	struct imx6_clk *iclk = (struct imx6_clk *)clk;
+	struct imxccm_softc *sc = priv;
+
+	switch (iclk->type) {
+	case IMX6_CLK_FIXED:
+	case IMX6_CLK_FIXED_FACTOR:
+		return EINVAL;	/* always on */
+	case IMX6_CLK_PLL:
+		return imxccm_clk_enable_pll(sc, iclk, false);
+	case IMX6_CLK_MUX:
+	case IMX6_CLK_DIV:
+	case IMX6_CLK_PFD:
+		return EINVAL;
+	case IMX6_CLK_GATE:
+		return imxccm_clk_enable_gate(sc, iclk, false);
 	default:
+		panic("imx6: unknown clk type %d", iclk->type);
+	}
+}
+
+static int
+imxccm_clk_set_parent(void *priv, struct clk *clk, struct clk *parent)
+{
+	struct imx6_clk *iclk = (struct imx6_clk *)clk;
+	struct imxccm_softc *sc = priv;
+
+	switch (iclk->type) {
+	case IMX6_CLK_FIXED:
+	case IMX6_CLK_FIXED_FACTOR:
+	case IMX6_CLK_PLL:
+	case IMX6_CLK_GATE:
+	case IMX6_CLK_DIV:
+	case IMX6_CLK_PFD:
+		return EINVAL;
+	case IMX6_CLK_MUX:
+		return imxccm_clk_set_parent_mux(sc, iclk, parent);
+	default:
+		panic("imx6: unknown clk type %d", iclk->type);
+	}
+}
+
+static struct clk *
+imxccm_clk_get_parent(void *priv, struct clk *clk)
+{
+	struct imx6_clk *iclk = (struct imx6_clk *)clk;
+	struct imx6_clk *parent = NULL;
+	struct imxccm_softc *sc = priv;
+
+	switch (iclk->type) {
+	case IMX6_CLK_FIXED:
+	case IMX6_CLK_FIXED_FACTOR:
+	case IMX6_CLK_PLL:
+	case IMX6_CLK_GATE:
+	case IMX6_CLK_DIV:
+	case IMX6_CLK_PFD:
+		if (iclk->parent != NULL)
+			parent = imx6_clk_find(iclk->parent);
 		break;
+	case IMX6_CLK_MUX:
+		parent = imxccm_clk_get_parent_mux(sc, iclk);
+		break;
+	default:
+		panic("imx6: unknown clk type %d", iclk->type);
 	}
 
-	return -1;
+	return (struct clk *)parent;
 }
Index: src/sys/arch/arm/imx/imx6_ccmreg.h
diff -u src/sys/arch/arm/imx/imx6_ccmreg.h:1.9 src/sys/arch/arm/imx/imx6_ccmreg.h:1.10
--- src/sys/arch/arm/imx/imx6_ccmreg.h:1.9	Sun Sep 16 09:25:46 2018
+++ src/sys/arch/arm/imx/imx6_ccmreg.h	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: imx6_ccmreg.h,v 1.9 2018/09/16 09:25:46 skrll Exp $	*/
+/*	$NetBSD: imx6_ccmreg.h,v 1.10 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2014 Ryo Shimizu <r...@nerv.org>
@@ -41,11 +41,20 @@
 #define IMX6_PERIPHCLK_N	2
 #endif
 
+#ifndef IMX6_CKIL_FREQ
+#define IMX6_CKIL_FREQ	32768
+#endif
+#ifndef IMX6_CKIH_FREQ
+#define IMX6_CKIH_FREQ	0
+#endif
 #ifndef IMX6_OSC_FREQ
 #define IMX6_OSC_FREQ	(24 * 1000 * 1000)	/* 24MHz */
 #endif
-#ifndef IMX6_CKIL_FREQ
-#define IMX6_CKIL_FREQ	32768
+#ifndef IMX6_ANACLK1_FREQ
+#define IMX6_ANACLK1_FREQ	0
+#endif
+#ifndef IMX6_ANACLK2_FREQ
+#define IMX6_ANACLK2_FREQ	0
 #endif
 
 #define CCM_CCR					0x00000000
@@ -321,6 +330,7 @@
 #define CCM_ANALOG_PLL_USB1_CLR			0x00000018
 #define CCM_ANALOG_PLL_USB1_TOG			0x0000001c
 #define  CCM_ANALOG_PLL_USB1_LOCK		__BIT(31)
+#define  CCM_ANALOG_PLL_USB1_RESERVED		__BIT(20)
 #define  CCM_ANALOG_PLL_USB1_BYPASS		__BIT(16)
 #define  CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC	__BITS(15, 14)
 #define  CCM_ANALOG_PLL_USB1_ENABLE		__BIT(13)
@@ -333,6 +343,7 @@
 #define CCM_ANALOG_PLL_USB2_CLR			0x00000028
 #define CCM_ANALOG_PLL_USB2_TOG			0x0000002c
 #define  CCM_ANALOG_PLL_USB2_LOCK		__BIT(31)
+#define  CCM_ANALOG_PLL_USB2_RESERVED		__BIT(20)
 #define  CCM_ANALOG_PLL_USB2_BYPASS		__BIT(16)
 #define  CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC	__BITS(15, 14)
 #define  CCM_ANALOG_PLL_USB2_ENABLE		__BIT(13)
@@ -436,6 +447,7 @@
 #define  CCM_ANALOG_MISC1_LVDS_CLK1_SRC		__BITS(4, 0)
 #define  CCM_ANALOG_MISC1_LVDS_CLK1_SRC_PCIE	__SHIFTIN(0xa, CCM_ANALOG_MISC1_LVDS_CLK1_SRC)
 #define  CCM_ANALOG_MISC1_LVDS_CLK1_SRC_SATA	__SHIFTIN(0xb, CCM_ANALOG_MISC1_LVDS_CLK1_SRC)
+#define  CCM_ANALOG_MISC1_LVDS_CLK2_SRC		__BITS(9, 5)
 #define  CCM_ANALOG_MISC1_LVDS_CLK1_OBEN	__BIT(10)
 #define  CCM_ANALOG_MISC1_LVDS_CLK2_OBEN	__BIT(11)
 #define  CCM_ANALOG_MISC1_LVDS_CLK1_IBEN	__BIT(12)

Index: src/sys/arch/arm/imx/imx6_ccmvar.h
diff -u src/sys/arch/arm/imx/imx6_ccmvar.h:1.5 src/sys/arch/arm/imx/imx6_ccmvar.h:1.6
--- src/sys/arch/arm/imx/imx6_ccmvar.h:1.5	Thu Nov  9 05:57:23 2017
+++ src/sys/arch/arm/imx/imx6_ccmvar.h	Thu Jun 20 08:16:19 2019
@@ -1,6 +1,6 @@
-/*	$NetBSD: imx6_ccmvar.h,v 1.5 2017/11/09 05:57:23 hkenken Exp $	*/
+/*	$NetBSD: imx6_ccmvar.h,v 1.6 2019/06/20 08:16:19 hkenken Exp $	*/
 /*
- * Copyright (c) 2012  Genetec Corporation.  All rights reserved.
+ * Copyright (c) 2012,2019  Genetec Corporation.  All rights reserved.
  * Written by Hashimoto Kenichi for Genetec Corporation.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,58 +28,530 @@
 #ifndef	_ARM_IMX_IMX6_CCMVAR_H_
 #define	_ARM_IMX_IMX6_CCMVAR_H_
 
-enum imx6_clock_id {
-	IMX6CLK_PLL1,		/* = PLL_ARM */
-	IMX6CLK_PLL2,		/* = PLL_SYS = 528_PLL (24MHz * 22) */
-	IMX6CLK_PLL3,		/* = PLL_USB1 = 480_PLL1 */
-				/*  (USB/OTG PHY, 480PFD0-480PFD3, 24MHz*20) */
-	IMX6CLK_PLL4,		/* = PLL_AUDIO */
-	IMX6CLK_PLL5,		/* = PLL_VIDEO */
-	IMX6CLK_PLL6,		/* = PLL_ENET (20MHz = 24MHz * 5/6) */
-	IMX6CLK_PLL7,		/* = PLL_USB2 (USB2 PHY, HOST PHY, 24MHz*20) */
-	IMX6CLK_PLL8,		/* = PLL_MLB (Media Link Bus) */
-	IMX6CLK_PLL2_PFD0,
-	IMX6CLK_PLL2_PFD1,
-	IMX6CLK_PLL2_PFD2,
-	IMX6CLK_PLL3_PFD0,
-	IMX6CLK_PLL3_PFD1,
-	IMX6CLK_PLL3_PFD2,
-	IMX6CLK_PLL3_PFD3,
-
-	IMX6CLK_ARM_ROOT,	/* CPU clock of ARM core */
-	IMX6CLK_PERIPH,
-	IMX6CLK_AHB,
-	IMX6CLK_IPG,
-	IMX6CLK_AXI,
-	IMX6CLK_MMDC_CH0,
-	IMX6CLK_MMDC_CH1,
-	IMX6CLK_MMDC_CH0_CLK_ROOT,
-	IMX6CLK_MMDC_CH1_CLK_ROOT,
-
-	IMX6CLK_USDHC1,
-	IMX6CLK_USDHC2,
-	IMX6CLK_USDHC3,
-	IMX6CLK_USDHC4,
-
-	IMX6CLK_PERCLK,
-
-	IMX6CLK_IPU1_HSP_CLK_ROOT,
-	IMX6CLK_IPU2_HSP_CLK_ROOT,
-	IMX6CLK_IPU1_DI0_CLK_ROOT,
-	IMX6CLK_IPU1_DI1_CLK_ROOT,
-	IMX6CLK_LDB_DI0_IPU,
-	IMX6CLK_LDB_DI0_SERIAL_CLK_ROOT,
-	IMX6CLK_LDB_DI1_IPU,
-	IMX6CLK_LDB_DI1_SERIAL_CLK_ROOT,
-};
-
-uint32_t imx6_get_clock(enum imx6_clock_id);
-int imx6_set_clock(enum imx6_clock_id, uint32_t);
-int imx6_pll_power(uint32_t, int, uint32_t);
-
-uint32_t imx6_ccm_read(uint32_t);
-void imx6_ccm_write(uint32_t, uint32_t);
-uint32_t imx6_ccm_analog_read(uint32_t);
-void imx6_ccm_analog_write(uint32_t, uint32_t);
+#include <dev/clk/clk.h>
+#include <dev/clk/clk_backend.h>
+
+struct clk *imx6_get_clock(const char *name);
+
+/* Clock IDs */
+#define IMX6CLK_DUMMY			0
+#define IMX6CLK_CKIL			1
+#define IMX6CLK_CKIH			2
+#define IMX6CLK_OSC			3
+#define IMX6CLK_PLL2_PFD0_352M		4
+#define IMX6CLK_PLL2_PFD1_594M		5
+#define IMX6CLK_PLL2_PFD2_396M		6
+#define IMX6CLK_PLL3_PFD0_720M		7
+#define IMX6CLK_PLL3_PFD1_540M		8
+#define IMX6CLK_PLL3_PFD2_508M		9
+#define IMX6CLK_PLL3_PFD3_454M		10
+#define IMX6CLK_PLL2_198M		11
+#define IMX6CLK_PLL3_120M		12
+#define IMX6CLK_PLL3_80M		13
+#define IMX6CLK_PLL3_60M		14
+#define IMX6CLK_TWD			15
+#define IMX6CLK_STEP			16
+#define IMX6CLK_PLL1_SW			17
+#define IMX6CLK_PERIPH_PRE		18
+#define IMX6CLK_PERIPH2_PRE		19
+#define IMX6CLK_PERIPH_CLK2_SEL		20
+#define IMX6CLK_PERIPH2_CLK2_SEL	21
+#define IMX6CLK_AXI_SEL			22
+#define IMX6CLK_ESAI_SEL		23
+#define IMX6CLK_ASRC_SEL		24
+#define IMX6CLK_SPDIF_SEL		25
+#define IMX6CLK_GPU2D_AXI		26
+#define IMX6CLK_GPU3D_AXI		27
+#define IMX6CLK_GPU2D_CORE_SEL		28
+#define IMX6CLK_GPU3D_CORE_SEL		29
+#define IMX6CLK_GPU3D_SHADER_SEL	30
+#define IMX6CLK_IPU1_SEL		31
+#define IMX6CLK_IPU2_SEL		32
+#define IMX6CLK_LDB_DI0_SEL		33
+#define IMX6CLK_LDB_DI1_SEL		34
+#define IMX6CLK_IPU1_DI0_PRE_SEL	35
+#define IMX6CLK_IPU1_DI1_PRE_SEL	36
+#define IMX6CLK_IPU2_DI0_PRE_SEL	37
+#define IMX6CLK_IPU2_DI1_PRE_SEL	38
+#define IMX6CLK_IPU1_DI0_SEL		39
+#define IMX6CLK_IPU1_DI1_SEL		40
+#define IMX6CLK_IPU2_DI0_SEL		41
+#define IMX6CLK_IPU2_DI1_SEL		42
+#define IMX6CLK_HSI_TX_SEL		43
+#define IMX6CLK_PCIE_AXI_SEL		44
+#define IMX6CLK_SSI1_SEL		45
+#define IMX6CLK_SSI2_SEL		46
+#define IMX6CLK_SSI3_SEL		47
+#define IMX6CLK_USDHC1_SEL		48
+#define IMX6CLK_USDHC2_SEL		49
+#define IMX6CLK_USDHC3_SEL		50
+#define IMX6CLK_USDHC4_SEL		51
+#define IMX6CLK_ENFC_SEL		52
+#define IMX6CLK_EIM_SEL			53
+#define IMX6CLK_EIM_SLOW_SEL		54
+#define IMX6CLK_VDO_AXI_SEL		55
+#define IMX6CLK_VPU_AXI_SEL		56
+#define IMX6CLK_CKO1_SEL		57
+#define IMX6CLK_PERIPH			58
+#define IMX6CLK_PERIPH2			59
+#define IMX6CLK_PERIPH_CLK2		60
+#define IMX6CLK_PERIPH2_CLK2		61
+#define IMX6CLK_IPG			62
+#define IMX6CLK_IPG_PER			63
+#define IMX6CLK_ESAI_PRED		64
+#define IMX6CLK_ESAI_PODF		65
+#define IMX6CLK_ASRC_PRED		66
+#define IMX6CLK_ASRC_PODF		67
+#define IMX6CLK_SPDIF_PRED		68
+#define IMX6CLK_SPDIF_PODF		69
+#define IMX6CLK_CAN_ROOT		70
+#define IMX6CLK_ECSPI_ROOT		71
+#define IMX6CLK_GPU2D_CORE_PODF		72
+#define IMX6CLK_GPU3D_CORE_PODF		73
+#define IMX6CLK_GPU3D_SHADER		74
+#define IMX6CLK_IPU1_PODF		75
+#define IMX6CLK_IPU2_PODF		76
+#define IMX6CLK_LDB_DI0_PODF		77
+#define IMX6CLK_LDB_DI1_PODF		78
+#define IMX6CLK_IPU1_DI0_PRE		79
+#define IMX6CLK_IPU1_DI1_PRE		80
+#define IMX6CLK_IPU2_DI0_PRE		81
+#define IMX6CLK_IPU2_DI1_PRE		82
+#define IMX6CLK_HSI_TX_PODF		83
+#define IMX6CLK_SSI1_PRED		84
+#define IMX6CLK_SSI1_PODF		85
+#define IMX6CLK_SSI2_PRED		86
+#define IMX6CLK_SSI2_PODF		87
+#define IMX6CLK_SSI3_PRED		88
+#define IMX6CLK_SSI3_PODF		89
+#define IMX6CLK_UART_SERIAL_PODF	90
+#define IMX6CLK_USDHC1_PODF		91
+#define IMX6CLK_USDHC2_PODF		92
+#define IMX6CLK_USDHC3_PODF		93
+#define IMX6CLK_USDHC4_PODF		94
+#define IMX6CLK_ENFC_PRED		95
+#define IMX6CLK_ENFC_PODF		96
+#define IMX6CLK_EIM_PODF		97
+#define IMX6CLK_EIM_SLOW_PODF		98
+#define IMX6CLK_VPU_AXI_PODF		99
+#define IMX6CLK_CKO1_PODF		100
+#define IMX6CLK_AXI			101
+#define IMX6CLK_MMDC_CH0_AXI_PODF	102
+#define IMX6CLK_MMDC_CH1_AXI_PODF	103
+#define IMX6CLK_ARM			104
+#define IMX6CLK_AHB			105
+#define IMX6CLK_APBH_DMA		106
+#define IMX6CLK_ASRC			107
+#define IMX6CLK_CAN1_IPG		108
+#define IMX6CLK_CAN1_SERIAL		109
+#define IMX6CLK_CAN2_IPG		110
+#define IMX6CLK_CAN2_SERIAL		111
+#define IMX6CLK_ECSPI1			112
+#define IMX6CLK_ECSPI2			113
+#define IMX6CLK_ECSPI3			114
+#define IMX6CLK_ECSPI4			115
+#define IMX6CLK_ECSPI5			116	/* i.MX6Q */
+#define IMX6CLK_I2C4			116	/* i.MX6DL */
+#define IMX6CLK_ENET			117
+#define IMX6CLK_ESAI_EXTAL		118
+#define IMX6CLK_GPT_IPG			119
+#define IMX6CLK_GPT_IPG_PER		120
+#define IMX6CLK_GPU2D_CORE		121
+#define IMX6CLK_GPU3D_CORE		122
+#define IMX6CLK_HDMI_IAHB		123
+#define IMX6CLK_HDMI_ISFR		124
+#define IMX6CLK_I2C1			125
+#define IMX6CLK_I2C2			126
+#define IMX6CLK_I2C3			127
+#define IMX6CLK_IIM			128
+#define IMX6CLK_ENFC			129
+#define IMX6CLK_IPU1			130
+#define IMX6CLK_IPU1_DI0		131
+#define IMX6CLK_IPU1_DI1		132
+#define IMX6CLK_IPU2			133
+#define IMX6CLK_IPU2_DI0		134
+#define IMX6CLK_LDB_DI0			135
+#define IMX6CLK_LDB_DI1			136
+#define IMX6CLK_IPU2_DI1		137
+#define IMX6CLK_HSI_TX			138
+#define IMX6CLK_MLB			139
+#define IMX6CLK_MMDC_CH0_AXI		140
+#define IMX6CLK_MMDC_CH1_AXI		141
+#define IMX6CLK_OCRAM			142
+#define IMX6CLK_OPENVG_AXI		143
+#define IMX6CLK_PCIE_AXI		144
+#define IMX6CLK_PWM1			145
+#define IMX6CLK_PWM2			146
+#define IMX6CLK_PWM3			147
+#define IMX6CLK_PWM4			148
+#define IMX6CLK_PER1_BCH		149
+#define IMX6CLK_GPMI_BCH_APB		150
+#define IMX6CLK_GPMI_BCH		151
+#define IMX6CLK_GPMI_IO			152
+#define IMX6CLK_GPMI_APB		153
+#define IMX6CLK_SATA			154
+#define IMX6CLK_SDMA			155
+#define IMX6CLK_SPBA			156
+#define IMX6CLK_SSI1			157
+#define IMX6CLK_SSI2			158
+#define IMX6CLK_SSI3			159
+#define IMX6CLK_UART_IPG		160
+#define IMX6CLK_UART_SERIAL		161
+#define IMX6CLK_USBOH3			162
+#define IMX6CLK_USDHC1			163
+#define IMX6CLK_USDHC2			164
+#define IMX6CLK_USDHC3			165
+#define IMX6CLK_USDHC4			166
+#define IMX6CLK_VDO_AXI			167
+#define IMX6CLK_VPU_AXI			168
+#define IMX6CLK_CKO1			169
+#define IMX6CLK_PLL1_SYS		170
+#define IMX6CLK_PLL2_BUS		171
+#define IMX6CLK_PLL3_USB_OTG		172
+#define IMX6CLK_PLL4_AUDIO		173
+#define IMX6CLK_PLL5_VIDEO		174
+#define IMX6CLK_PLL8_MLB		175
+#define IMX6CLK_PLL7_USB_HOST		176
+#define IMX6CLK_PLL6_ENET		177
+#define IMX6CLK_SSI1_IPG		178
+#define IMX6CLK_SSI2_IPG		179
+#define IMX6CLK_SSI3_IPG		180
+#define IMX6CLK_ROM			181
+#define IMX6CLK_USBPHY1			182
+#define IMX6CLK_USBPHY2			183
+#define IMX6CLK_LDB_DI0_DIV_3_5		184
+#define IMX6CLK_LDB_DI1_DIV_3_5		185
+#define IMX6CLK_SATA_REF		186
+#define IMX6CLK_SATA_REF_100M		187
+#define IMX6CLK_PCIE_REF		188
+#define IMX6CLK_PCIE_REF_125M		189
+#define IMX6CLK_ENET_REF		190
+#define IMX6CLK_USBPHY1_GATE		191
+#define IMX6CLK_USBPHY2_GATE		192
+#define IMX6CLK_PLL4_POST_DIV		193
+#define IMX6CLK_PLL5_POST_DIV		194
+#define IMX6CLK_PLL5_VIDEO_DIV		195
+#define IMX6CLK_EIM_SLOW		196
+#define IMX6CLK_SPDIF			197
+#define IMX6CLK_CKO2_SEL		198
+#define IMX6CLK_CKO2_PODF		199
+#define IMX6CLK_CKO2			200
+#define IMX6CLK_CKO			201
+#define IMX6CLK_VDOA			202
+#define IMX6CLK_PLL4_AUDIO_DIV		203
+#define IMX6CLK_LVDS1_SEL		204
+#define IMX6CLK_LVDS2_SEL		205
+#define IMX6CLK_LVDS1_GATE		206
+#define IMX6CLK_LVDS2_GATE		207
+#define IMX6CLK_ESAI_IPG		208
+#define IMX6CLK_ESAI_MEM		209
+#define IMX6CLK_ASRC_IPG		210
+#define IMX6CLK_ASRC_MEM		211
+#define IMX6CLK_LVDS1_IN		212
+#define IMX6CLK_LVDS2_IN		213
+#define IMX6CLK_ANACLK1			214
+#define IMX6CLK_ANACLK2			215
+#define IMX6CLK_PLL1_BYPASS_SRC		216
+#define IMX6CLK_PLL2_BYPASS_SRC		217
+#define IMX6CLK_PLL3_BYPASS_SRC		218
+#define IMX6CLK_PLL4_BYPASS_SRC		219
+#define IMX6CLK_PLL5_BYPASS_SRC		220
+#define IMX6CLK_PLL6_BYPASS_SRC		221
+#define IMX6CLK_PLL7_BYPASS_SRC		222
+#define IMX6CLK_PLL1			223
+#define IMX6CLK_PLL2			224
+#define IMX6CLK_PLL3			225
+#define IMX6CLK_PLL4			226
+#define IMX6CLK_PLL5			227
+#define IMX6CLK_PLL6			228
+#define IMX6CLK_PLL7			229
+#define IMX6CLK_PLL1_BYPASS		230
+#define IMX6CLK_PLL2_BYPASS		231
+#define IMX6CLK_PLL3_BYPASS		232
+#define IMX6CLK_PLL4_BYPASS		233
+#define IMX6CLK_PLL5_BYPASS		234
+#define IMX6CLK_PLL6_BYPASS		235
+#define IMX6CLK_PLL7_BYPASS		236
+#define IMX6CLK_GPT_3M			237
+#define IMX6CLK_VIDEO_27M		238
+#define IMX6CLK_MIPI_CORE_CFG		239
+#define IMX6CLK_MIPI_IPG		240
+#define IMX6CLK_CAAM_MEM		241
+#define IMX6CLK_CAAM_ACLK		242
+#define IMX6CLK_CAAM_IPG		243
+#define IMX6CLK_SPDIF_GCLK		244
+#define IMX6CLK_UART_SEL		245
+#define IMX6CLK_IPG_PER_SEL		246
+#define IMX6CLK_ECSPI_SEL		247
+#define IMX6CLK_CAN_SEL			248
+#define IMX6CLK_MMDC_CH1_AXI_CG		249
+#define IMX6CLK_PRE0			250
+#define IMX6CLK_PRE1			251
+#define IMX6CLK_PRE2			252
+#define IMX6CLK_PRE3			253
+#define IMX6CLK_PRG0_AXI		254
+#define IMX6CLK_PRG1_AXI		255
+#define IMX6CLK_PRG0_APB		256
+#define IMX6CLK_PRG1_APB		257
+#define IMX6CLK_PRE_AXI			258
+#define IMX6CLK_MLB_SEL			259
+#define IMX6CLK_MLB_PODF		260
+#define IMX6CLK_END			261
+
+enum imx6_clk_type {
+	IMX6_CLK_FIXED,
+	IMX6_CLK_FIXED_FACTOR,
+	IMX6_CLK_PLL,
+	IMX6_CLK_MUX,
+	IMX6_CLK_GATE,
+	IMX6_CLK_PFD,
+	IMX6_CLK_DIV,
+};
+
+enum imx6_clk_reg {
+	IMX6_CLK_REG_CCM,
+	IMX6_CLK_REG_CCM_ANALOG,
+};
+
+enum imx6_clk_pll_type {
+	IMX6_CLK_PLL_GENNERIC,
+	IMX6_CLK_PLL_SYS,
+	IMX6_CLK_PLL_USB,
+	IMX6_CLK_PLL_AUDIO_VIDEO,
+	IMX6_CLK_PLL_ENET,
+};
+
+enum imx6_clk_div_type {
+	IMX6_CLK_DIV_NORMAL,
+	IMX6_CLK_DIV_BUSY,
+	IMX6_CLK_DIV_TABLE,
+};
+
+enum imx6_clk_mux_type {
+	IMX6_CLK_MUX_NORMAL,
+	IMX6_CLK_MUX_BUSY,
+};
+
+struct imx6_clk_fixed {
+	u_int rate;
+};
+
+struct imx6_clk_fixed_factor {
+	u_int div;
+	u_int mult;
+};
+
+struct imx6_clk_pfd {
+	uint32_t reg;
+	int index;
+};
+
+struct imx6_clk_pll {
+	enum imx6_clk_pll_type type;
+	uint32_t reg;
+	uint32_t mask;
+	uint32_t powerdown;
+	unsigned long ref;
+};
+
+struct imx6_clk_div {
+	enum imx6_clk_div_type type;
+	enum imx6_clk_reg base;
+	uint32_t reg;
+	uint32_t mask;
+	uint32_t busy_reg;
+	uint32_t busy_mask;
+	const int *tbl;
+};
+
+struct imx6_clk_mux {
+	enum imx6_clk_mux_type type;
+	enum imx6_clk_reg base;
+	uint32_t reg;
+	uint32_t mask;
+	const char **parents;
+	u_int nparents;
+	uint32_t busy_reg;
+	uint32_t busy_mask;
+};
+
+struct imx6_clk_gate {
+	enum imx6_clk_reg base;
+	uint32_t reg;
+	uint32_t mask;
+	uint32_t exclusive_mask;
+};
+
+struct imx6_clk {
+	struct clk base;		/* must be first */
+
+	const char *parent;
+	u_int refcnt;
+
+	enum imx6_clk_type type;
+	union {
+		struct imx6_clk_fixed fixed;
+		struct imx6_clk_fixed_factor fixed_factor;
+		struct imx6_clk_pfd pfd;
+		struct imx6_clk_pll pll;
+		struct imx6_clk_div div;
+		struct imx6_clk_mux mux;
+		struct imx6_clk_gate gate;
+	} clk;
+};
+
+#define CLK_FIXED(_name, _rate) {				\
+	.base = { .name = (_name) },				\
+	.type = IMX6_CLK_FIXED,					\
+	.clk = {						\
+		.fixed = {					\
+			.rate = (_rate),			\
+		}						\
+	}							\
+}
+
+#define CLK_FIXED_FACTOR(_name, _parent, _div, _mult) {		\
+	.base = { .name = (_name) },				\
+	.type = IMX6_CLK_FIXED_FACTOR,				\
+	.parent = (_parent),					\
+	.clk = {						\
+		.fixed_factor = {				\
+			.div = (_div),				\
+			.mult = (_mult),			\
+		}						\
+	}							\
+}
+
+#define CLK_PFD(_name, _parent, _reg, _index) {			\
+	.base = { .name = (_name) },				\
+	.type = IMX6_CLK_PFD,					\
+	.parent = (_parent),					\
+	.clk = {						\
+		.pfd = {					\
+			.reg = (CCM_ANALOG_##_reg),		\
+			.index = (_index),			\
+		}						\
+	}							\
+}
+
+#define CLK_PLL(_name, _parent, _type, _reg, _mask, _powerdown, _ref) { \
+	.base = { .name = (_name) },				\
+	.type = IMX6_CLK_PLL,					\
+	.parent = (_parent),					\
+	.clk = {						\
+		.pll = {					\
+			.type = (IMX6_CLK_PLL_##_type),		\
+			.reg = (CCM_ANALOG_##_reg),		\
+			.mask = (CCM_ANALOG_##_reg##_##_mask),	\
+			.powerdown = (CCM_ANALOG_##_reg##_##_powerdown), \
+			.ref = (_ref),				\
+		}						\
+	}							\
+}
+
+#define CLK_DIV(_name, _parent, _reg, _mask) {			\
+	.base = { .name = (_name) },				\
+	.type = IMX6_CLK_DIV,					\
+	.parent = (_parent),					\
+	.clk = {						\
+		.div = {					\
+			.type = (IMX6_CLK_DIV_NORMAL),		\
+			.base = (IMX6_CLK_REG_CCM),		\
+			.reg = (CCM_##_reg),			\
+			.mask = (CCM_##_reg##_##_mask),		\
+		}						\
+	}							\
+}
+
+#define CLK_DIV_BUSY(_name, _parent, _reg, _mask, _busy_reg, _busy_mask) { \
+	.base = { .name = (_name) },				\
+	.type = IMX6_CLK_DIV,					\
+	.parent = (_parent),					\
+	.clk = {						\
+		.div = {					\
+			.type = (IMX6_CLK_DIV_BUSY),		\
+			.base = (IMX6_CLK_REG_CCM),		\
+			.reg = (CCM_##_reg),			\
+			.mask = (CCM_##_reg##_##_mask),	\
+			.busy_reg = (CCM_##_busy_reg),		\
+			.busy_mask = (CCM_##_busy_reg##_##_busy_mask) \
+		}						\
+	}							\
+}
+
+#define CLK_DIV_TABLE(_name, _parent, _reg, _mask, _tbl) {	\
+	.base = { .name = (_name) },				\
+	.type = IMX6_CLK_DIV,					\
+	.parent = (_parent),					\
+	.clk = {						\
+		.div = {					\
+			.type = (IMX6_CLK_DIV_TABLE),		\
+			.base = (IMX6_CLK_REG_CCM_ANALOG),	\
+			.reg = (CCM_ANALOG_##_reg),		\
+			.mask = (CCM_ANALOG_##_reg##_##_mask),	\
+			.tbl = (_tbl)				\
+		}						\
+	}							\
+}
+
+#define CLK_MUX(_name, _parents, _base, _reg, _mask) {		\
+	.base = { .name = (_name), .flags = CLK_SET_RATE_PARENT }, \
+	.type = IMX6_CLK_MUX,					\
+	.clk = {						\
+		.mux = {					\
+			.type = (IMX6_CLK_MUX_NORMAL),		\
+			.base = (IMX6_CLK_REG_##_base),		\
+			.reg = (_base##_##_reg),		\
+			.mask = (_base##_##_reg##_##_mask),	\
+			.parents = (_parents),			\
+			.nparents = __arraycount(_parents)	\
+		}						\
+	}							\
+}
+
+#define CLK_MUX_BUSY(_name, _parents, _reg, _mask, _busy_reg, _busy_mask) { \
+	.base = { .name = (_name), .flags = CLK_SET_RATE_PARENT }, \
+	.type = IMX6_CLK_MUX,					\
+	.clk = {						\
+		.mux = {					\
+			.type = (IMX6_CLK_MUX_BUSY),		\
+			.base = (IMX6_CLK_REG_CCM),		\
+			.reg = (CCM_##_reg),			\
+			.mask = (CCM_##_reg##_##_mask),		\
+			.parents = (_parents),			\
+			.nparents = __arraycount(_parents),	\
+			.busy_reg = (CCM_##_busy_reg),		\
+			.busy_mask = (CCM_##_busy_reg##_##_busy_mask) \
+		}						\
+	}							\
+}
+
+#define CLK_GATE(_name, _parent, _base, _reg, _mask) {		\
+	.base = { .name = (_name), .flags = CLK_SET_RATE_PARENT }, \
+	.type = IMX6_CLK_GATE,					\
+	.parent = (_parent),					\
+	.clk = {						\
+		.gate = {					\
+			.base = (IMX6_CLK_REG_##_base),		\
+			.reg = (_base##_##_reg),		\
+			.mask = (_base##_##_reg##_##_mask),	\
+			.exclusive_mask = 0			\
+		}						\
+	}							\
+}
+
+#define CLK_GATE_EXCLUSIVE(_name, _parent, _base, _reg, _mask, _exclusive_mask) {  \
+	.base = { .name = (_name), .flags = CLK_SET_RATE_PARENT }, \
+	.type = IMX6_CLK_GATE,					\
+	.parent = (_parent),					\
+	.clk = {						\
+		.gate = {					\
+			.base = (IMX6_CLK_REG_##_base),		\
+			.reg = (_base##_##_reg),		\
+			.mask = (_base##_##_reg##_##_mask),     \
+			.exclusive_mask = (_base##_##_reg##_##_exclusive_mask) \
+		}						\
+	}							\
+}
 
 #endif	/* _ARM_IMX_IMX6_CCMVAR_H_ */

Index: src/sys/arch/arm/imx/imx6_usb.c
diff -u src/sys/arch/arm/imx/imx6_usb.c:1.4 src/sys/arch/arm/imx/imx6_usb.c:1.5
--- src/sys/arch/arm/imx/imx6_usb.c:1.4	Wed May 23 10:42:05 2018
+++ src/sys/arch/arm/imx/imx6_usb.c	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: imx6_usb.c,v 1.4 2018/05/23 10:42:05 hkenken Exp $	*/
+/*	$NetBSD: imx6_usb.c,v 1.5 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2012  Genetec Corporation.  All rights reserved.
@@ -26,7 +26,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: imx6_usb.c,v 1.4 2018/05/23 10:42:05 hkenken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: imx6_usb.c,v 1.5 2019/06/20 08:16:19 hkenken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -48,31 +48,38 @@ __KERNEL_RCSID(0, "$NetBSD: imx6_usb.c,v
 #include <arm/imx/imx6var.h>
 #include <arm/imx/imx6_ccmreg.h>
 #include <arm/imx/imx6_ccmvar.h>
+#include <arm/imx/imx6_usbreg.h>
 #include <arm/imx/imxusbvar.h>
 
 #include "locators.h"
 
 static int imxusbc_search(device_t, cfdata_t, const int *, void *);
 static int imxusbc_print(void *, const char *);
+static int imxusbc_init_clocks(struct imxusbc_softc *);
 
 int
 imxusbc_attach_common(device_t parent, device_t self, bus_space_tag_t iot)
 {
 	struct imxusbc_softc *sc;
-	uint32_t v;
 
 	sc = device_private(self);
 	sc->sc_iot = iot;
 
 	/* Map entire USBOH registers.  Host controller drivers
 	 * re-use subregions of this. */
-	if (bus_space_map(iot, IMX6_AIPS2_BASE + AIPS2_USBOH_BASE, /* XXX */
+	if (bus_space_map(iot, IMX6_AIPS2_BASE + AIPS2_USBOH_BASE,
 	    AIPS2_USBOH_SIZE, 0, &sc->sc_ioh))
 		return -1;
 
-	/* USBOH3 clock enable */
-	v = imx6_ccm_read(CCM_CCGR6);
-	imx6_ccm_write(CCM_CCGR6, v | __SHIFTIN(3, CCM_CCGR6_USBOH3_CLK_ENABLE));
+	sc->sc_clk = imx6_get_clock("usboh3");
+	if (sc->sc_clk == NULL) {
+		aprint_error(": couldn't get clock usboh3\n");
+		return -1;
+	}
+	if (imxusbc_init_clocks(sc) != 0) {
+		aprint_error_dev(self, "couldn't init clocks\n");
+		return -1;
+	}
 
 	/* attach OTG/EHCI host controllers */
 	config_search_ia(imxusbc_search, self, "imxusbc", NULL);
@@ -110,3 +117,18 @@ imxusbc_print(void *aux, const char *nam
 	aprint_normal(" unit %d intr %d", iaa->aa_unit, iaa->aa_irq);
 	return UNCONF;
 }
+
+static int
+imxusbc_init_clocks(struct imxusbc_softc *sc)
+{
+	int error;
+
+	error = clk_enable(sc->sc_clk);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable usboh3: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
Index: src/sys/arch/arm/imx/imxusbvar.h
diff -u src/sys/arch/arm/imx/imxusbvar.h:1.4 src/sys/arch/arm/imx/imxusbvar.h:1.5
--- src/sys/arch/arm/imx/imxusbvar.h:1.4	Tue May 17 06:44:46 2016
+++ src/sys/arch/arm/imx/imxusbvar.h	Thu Jun 20 08:16:19 2019
@@ -1,3 +1,30 @@
+/*	$NetBSD: imxusbvar.h,v 1.5 2019/06/20 08:16:19 hkenken Exp $	*/
+/*
+ * Copyright (c) 2019  Genetec Corporation.  All rights reserved.
+ * Written by Hashimoto Kenichi for Genetec Corporation.
+ *
+ * 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 GENETEC CORPORATION ``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 GENETEC CORPORATION
+ * 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_IMX_IMXUSBVAR_H
 #define _ARM_IMX_IMXUSBVAR_H
 
@@ -13,6 +40,8 @@ struct imxusbc_softc {
 	bus_space_tag_t sc_iot;
 	bus_space_handle_t sc_ioh;
 
+	struct clk *sc_clk;
+
 	/* filled in by platform dependent param & routine */
 	bus_size_t sc_ehci_size;
 	void (* sc_init_md_hook)(struct imxehci_softc *);

Index: src/sys/arch/arm/imx/imx6_usbphy.c
diff -u src/sys/arch/arm/imx/imx6_usbphy.c:1.1 src/sys/arch/arm/imx/imx6_usbphy.c:1.2
--- src/sys/arch/arm/imx/imx6_usbphy.c:1.1	Thu Nov  9 05:57:23 2017
+++ src/sys/arch/arm/imx/imx6_usbphy.c	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: imx6_usbphy.c,v 1.1 2017/11/09 05:57:23 hkenken Exp $	*/
+/*	$NetBSD: imx6_usbphy.c,v 1.2 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2017  Genetec Corporation.  All rights reserved.
@@ -32,7 +32,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: imx6_usbphy.c,v 1.1 2017/11/09 05:57:23 hkenken Exp $");
+__KERNEL_RCSID(1, "$NetBSD: imx6_usbphy.c,v 1.2 2019/06/20 08:16:19 hkenken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -53,12 +53,15 @@ __KERNEL_RCSID(1, "$NetBSD: imx6_usbphy.
 #include <arm/imx/imx6_reg.h>
 #include <arm/imx/imx6var.h>
 
+#include <arm/imx/imx6_ccmvar.h>
 #include <arm/imx/imx6_usbphyreg.h>
 
 struct imx6_usbphy_softc {
 	device_t sc_dev;
 	bus_space_tag_t sc_bst;
 	bus_space_handle_t sc_bsh;
+
+	struct clk *sc_clk;
 };
 
 static int	imx6_usbphy_match(device_t, cfdata_t, void *);
@@ -73,7 +76,7 @@ CFATTACH_DECL_NEW(imxusbphy, sizeof(stru
 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
 
 static int
-imx6_usbphy_enable(device_t dev, void *priv, bool enable)
+imx6_usbphy_enable(device_t dev, bool enable)
 {
 	struct imx6_usbphy_softc * const sc = device_private(dev);
 
@@ -99,6 +102,20 @@ imx6_usbphy_enable(device_t dev, void *p
 }
 
 static int
+imx6_usbphy_init_clocks(device_t dev)
+{
+	struct imx6_usbphy_softc * const sc = device_private(dev);
+
+	int error = clk_enable(sc->sc_clk);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "couldn't enable: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int
 imx6_usbphy_match(device_t parent, cfdata_t cf, void *aux)
 {
 	struct axi_attach_args *aa = aux;
@@ -132,8 +149,18 @@ imx6_usbphy_attach(device_t parent, devi
 		return;
 	}
 
+	switch (device_unit(self)) {
+	case 0:
+		sc->sc_clk = imx6_get_clock("usbphy1");
+		break;
+	case 1:
+		sc->sc_clk = imx6_get_clock("usbphy2");
+		break;
+	}
+
 	aprint_naive("\n");
 	aprint_normal(": USB PHY\n");
 
-	imx6_usbphy_enable(self, NULL, true);
+	imx6_usbphy_init_clocks(self);
+	imx6_usbphy_enable(self, true);
 }

Index: src/sys/arch/arm/imx/imx6_usdhc.c
diff -u src/sys/arch/arm/imx/imx6_usdhc.c:1.6 src/sys/arch/arm/imx/imx6_usdhc.c:1.7
--- src/sys/arch/arm/imx/imx6_usdhc.c:1.6	Wed May 23 10:42:05 2018
+++ src/sys/arch/arm/imx/imx6_usdhc.c	Thu Jun 20 08:16:19 2019
@@ -1,5 +1,4 @@
-/*	$NetBSD: imx6_usdhc.c,v 1.6 2018/05/23 10:42:05 hkenken Exp $ */
-
+/*	$NetBSD: imx6_usdhc.c,v 1.7 2019/06/20 08:16:19 hkenken Exp $ */
 /*-
  * Copyright (c) 2012  Genetec Corporation.  All rights reserved.
  * Written by Hiroyuki Bessho for Genetec Corporation.
@@ -28,9 +27,8 @@
  * SUCH DAMAGE.
  */
 
-
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: imx6_usdhc.c,v 1.6 2018/05/23 10:42:05 hkenken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: imx6_usdhc.c,v 1.7 2019/06/20 08:16:19 hkenken Exp $");
 
 #include "imxgpio.h"
 
@@ -53,19 +51,23 @@ __KERNEL_RCSID(0, "$NetBSD: imx6_usdhc.c
 #include <arm/imx/imx6_ccmreg.h>
 #include <arm/imx/imxgpiovar.h>
 
-
 struct sdhc_axi_softc {
 	struct sdhc_softc sc_sdhc;
+
 	/* we have only one slot */
 	struct sdhc_host *sc_hosts[1];
 	int32_t sc_gpio_cd;
 	int32_t sc_gpio_cd_active;
+
 	void *sc_ih;
+
+	struct clk *sc_clk;
 };
 
 static int sdhc_match(device_t, cfdata_t, void *);
 static void sdhc_attach(device_t, device_t, void *);
 static int imx6_sdhc_card_detect(struct sdhc_softc *);
+static int imx6_sdhc_init_clocks(struct sdhc_axi_softc *);
 
 CFATTACH_DECL_NEW(sdhc_axi, sizeof(struct sdhc_axi_softc),
     sdhc_match, sdhc_attach, NULL, NULL);
@@ -89,25 +91,6 @@ sdhc_match(device_t parent, cfdata_t cf,
 	return 0;
 }
 
-static int
-imx6_sdhc_card_detect(struct sdhc_softc *ssc)
-{
-	int detect;
-#if NIMXGPIO > 0
-	struct sdhc_axi_softc *sc;
-
-	sc = device_private(ssc->sc_dev);
-	if (sc->sc_gpio_cd >= 0) {
-		detect = gpio_data_read(sc->sc_gpio_cd);
-		if (sc->sc_gpio_cd_active == GPIO_PIN_LOW)
-			detect = !detect;
-	} else
-#endif
-		detect = 1;
-
-	return detect;
-}
-
 static void
 sdhc_attach(device_t parent, device_t self, void *aux)
 {
@@ -115,7 +98,6 @@ sdhc_attach(device_t parent, device_t se
 	struct axi_attach_args *aa = aux;
 	bus_space_tag_t iot = aa->aa_iot;
 	bus_space_handle_t ioh;
-	u_int perclk = 0, v;
 
 	sc->sc_sdhc.sc_dev = self;
 	sc->sc_sdhc.sc_dmat = aa->aa_dmat;
@@ -131,36 +113,37 @@ sdhc_attach(device_t parent, device_t se
 
 	switch (aa->aa_addr) {
 	case IMX6_AIPS2_BASE + AIPS2_USDHC1_BASE:
-		v = imx6_ccm_read(CCM_CCGR6);
-		imx6_ccm_write(CCM_CCGR6, v | __SHIFTIN(3, CCM_CCGR6_USDHC1_CLK_ENABLE));
-		perclk = imx6_get_clock(IMX6CLK_USDHC1);
+		sc->sc_clk = imx6_get_clock("usdhc1");
 		imx6_set_gpio(self, "usdhc1-cd-gpio", &sc->sc_gpio_cd,
 		    &sc->sc_gpio_cd_active, GPIO_DIR_IN);
 		break;
 	case IMX6_AIPS2_BASE + AIPS2_USDHC2_BASE:
-		v = imx6_ccm_read(CCM_CCGR6);
-		imx6_ccm_write(CCM_CCGR6, v | __SHIFTIN(3, CCM_CCGR6_USDHC2_CLK_ENABLE));
-		perclk = imx6_get_clock(IMX6CLK_USDHC2);
+		sc->sc_clk = imx6_get_clock("usdhc2");
 		imx6_set_gpio(self, "usdhc2-cd-gpio", &sc->sc_gpio_cd,
 		    &sc->sc_gpio_cd_active, GPIO_DIR_IN);
 		break;
 	case IMX6_AIPS2_BASE + AIPS2_USDHC3_BASE:
-		v = imx6_ccm_read(CCM_CCGR6);
-		imx6_ccm_write(CCM_CCGR6, v | __SHIFTIN(3, CCM_CCGR6_USDHC3_CLK_ENABLE));
-		perclk = imx6_get_clock(IMX6CLK_USDHC3);
+		sc->sc_clk = imx6_get_clock("usdhc3");
 		imx6_set_gpio(self, "usdhc3-cd-gpio", &sc->sc_gpio_cd,
 		    &sc->sc_gpio_cd_active, GPIO_DIR_IN);
 		break;
 	case IMX6_AIPS2_BASE + AIPS2_USDHC4_BASE:
-		v = imx6_ccm_read(CCM_CCGR6);
-		imx6_ccm_write(CCM_CCGR6, v | __SHIFTIN(3, CCM_CCGR6_USDHC4_CLK_ENABLE));
-		perclk = imx6_get_clock(IMX6CLK_USDHC4);
+		sc->sc_clk = imx6_get_clock("usdhc4");
 		imx6_set_gpio(self, "usdhc4-cd-gpio", &sc->sc_gpio_cd,
 		    &sc->sc_gpio_cd_active, GPIO_DIR_IN);
 		break;
 	}
 
-	sc->sc_sdhc.sc_clkbase = perclk / 1000;
+	if (sc->sc_clk == NULL) {
+		aprint_error(": couldn't get clock usdhc\n");
+		return;
+	}
+	if (imx6_sdhc_init_clocks(sc) != 0) {
+		aprint_error_dev(self, "couldn't init clocks\n");
+		return;
+	}
+
+	sc->sc_sdhc.sc_clkbase = clk_get_rate(sc->sc_clk) / 1000;
 	sc->sc_sdhc.sc_flags |=
 	    SDHC_FLAG_USE_DMA |
 	    SDHC_FLAG_NO_PWR0 |
@@ -189,3 +172,36 @@ sdhc_attach(device_t parent, device_t se
 		aprint_error_dev(self, "can't establish power hook\n");
 	}
 }
+
+static int
+imx6_sdhc_card_detect(struct sdhc_softc *ssc)
+{
+	int detect;
+#if NIMXGPIO > 0
+	struct sdhc_axi_softc *sc;
+
+	sc = device_private(ssc->sc_dev);
+	if (sc->sc_gpio_cd >= 0) {
+		detect = gpio_data_read(sc->sc_gpio_cd);
+		if (sc->sc_gpio_cd_active == GPIO_PIN_LOW)
+			detect = !detect;
+	} else
+#endif
+		detect = 1;
+
+	return detect;
+}
+
+static int
+imx6_sdhc_init_clocks(struct sdhc_axi_softc *sc)
+{
+	int error;
+
+	error = clk_enable(sc->sc_clk);
+	if (error) {
+		aprint_error_dev(sc->sc_sdhc.sc_dev, "couldn't enable: %d\n", error);
+		return error;
+	}
+
+	return 0;
+}

Index: src/sys/arch/evbarm/nitrogen6/nitrogen6_usb.c
diff -u src/sys/arch/evbarm/nitrogen6/nitrogen6_usb.c:1.4 src/sys/arch/evbarm/nitrogen6/nitrogen6_usb.c:1.5
--- src/sys/arch/evbarm/nitrogen6/nitrogen6_usb.c:1.4	Wed Jun 20 07:05:37 2018
+++ src/sys/arch/evbarm/nitrogen6/nitrogen6_usb.c	Thu Jun 20 08:16:19 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: nitrogen6_usb.c,v 1.4 2018/06/20 07:05:37 hkenken Exp $	*/
+/*	$NetBSD: nitrogen6_usb.c,v 1.5 2019/06/20 08:16:19 hkenken Exp $	*/
 
 /*
  * Copyright (c) 2013  Genetec Corporation.  All rights reserved.
@@ -27,7 +27,7 @@
  *
  */
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nitrogen6_usb.c,v 1.4 2018/06/20 07:05:37 hkenken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nitrogen6_usb.c,v 1.5 2019/06/20 08:16:19 hkenken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -123,18 +123,6 @@ init_otg(struct imxehci_softc *sc)
 
 	sc->sc_iftype = IMXUSBC_IF_UTMI_WIDE;
 
-	/* USB1 power */
-	imx6_ccm_analog_write(USB_ANALOG_USB1_CHRG_DETECT,
-	    USB_ANALOG_USB_CHRG_DETECT_EN_B |
-	    USB_ANALOG_USB_CHRG_DETECT_CHK_CHRG_B);
-	imx6_pll_power(CCM_ANALOG_PLL_USB1, 1, CCM_ANALOG_PLL_ENABLE);
-	imx6_ccm_analog_write(CCM_ANALOG_PLL_USB1_CLR,
-	    CCM_ANALOG_PLL_BYPASS);
-	imx6_ccm_analog_write(CCM_ANALOG_PLL_USB1_SET,
-	   CCM_ANALOG_PLL_ENABLE |
-	   CCM_ANALOG_PLL_POWER |
-	   CCM_ANALOG_PLL_EN_USB_CLK);
-
 	imxehci_reset(sc);
 
 	v = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh, USBNC_USB_OTG_CTRL);
@@ -153,16 +141,6 @@ init_h1(struct imxehci_softc *sc)
 
 	sc->sc_iftype = IMXUSBC_IF_UTMI_WIDE;
 
-	imx6_ccm_analog_write(USB_ANALOG_USB2_CHRG_DETECT,
-	    USB_ANALOG_USB_CHRG_DETECT_EN_B |
-	    USB_ANALOG_USB_CHRG_DETECT_CHK_CHRG_B);
-	imx6_ccm_analog_write(CCM_ANALOG_PLL_USB2_CLR,
-	    CCM_ANALOG_PLL_BYPASS);
-	imx6_ccm_analog_write(CCM_ANALOG_PLL_USB2_SET,
-	    CCM_ANALOG_PLL_ENABLE |
-	    CCM_ANALOG_PLL_POWER |
-	    CCM_ANALOG_PLL_EN_USB_CLK);
-
 	v = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh, USBNC_USB_UH1_CTRL);
 	v |= USBNC_USB_UH1_CTRL_OVER_CUR_POL;
 	v |= USBNC_USB_UH1_CTRL_OVER_CUR_DIS;

Reply via email to