diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h
index d66b26f18ef32369b1475204617f468d578856cc..317e5128ed2bc48342e163b120f81f05c87de9f7 100644
--- a/arch/arm/include/asm/arch-rockchip/clock.h
+++ b/arch/arm/include/asm/arch-rockchip/clock.h
@@ -62,18 +62,6 @@ static inline u32 clk_get_divisor(ulong input_rate, uint output_rate)
  */
 void *rockchip_get_cru(void);
 
-/**
- * rkclk_get_clk() - get a pointer to a given clock
- *
- * This is an internal function - use outside the clock subsystem indicates
- * that work is needed!
- *
- * @clk_id:	Clock requested
- * @devp:	Returns a pointer to that clock
- * @return 0 if OK, -ve on error
- */
-int rkclk_get_clk(enum rk_clk_id clk_id, struct udevice **devp);
-
 struct rk3288_cru;
 struct rk3288_grf;
 
diff --git a/arch/arm/mach-rockchip/board.c b/arch/arm/mach-rockchip/board.c
index 133d66341bf47838f4962e32499085c1628362f3..816540e5821459fbfff4b5d953f3ee2e70cf2cf7 100644
--- a/arch/arm/mach-rockchip/board.c
+++ b/arch/arm/mach-rockchip/board.c
@@ -9,6 +9,7 @@
 #include <dm.h>
 #include <ram.h>
 #include <asm/io.h>
+#include <asm/arch/clock.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -54,15 +55,43 @@ void lowlevel_init(void)
 static int do_clock(cmd_tbl_t *cmdtp, int flag, int argc,
 		       char * const argv[])
 {
+	static const struct {
+		char *name;
+		int id;
+	} clks[] = {
+		{ "osc", CLK_OSC },
+		{ "apll", CLK_ARM },
+		{ "dpll", CLK_DDR },
+		{ "cpll", CLK_CODEC },
+		{ "gpll", CLK_GENERAL },
+#ifdef CONFIG_ROCKCHIP_RK3036
+		{ "mpll", CLK_NEW },
+#else
+		{ "npll", CLK_NEW },
+#endif
+	};
+	int ret, i;
 	struct udevice *dev;
 
-	for (uclass_first_device(UCLASS_CLK, &dev);
-	     dev;
-	     uclass_next_device(&dev)) {
+	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
+	if (ret) {
+		printf("clk-uclass not found\n");
+		return 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(clks); i++) {
+		struct clk clk;
 		ulong rate;
 
-		rate = clk_get_rate(dev);
-		printf("%s: %lu\n", dev->name, rate);
+		clk.id = clks[i].id;
+		ret = clk_request(dev, &clk);
+		if (ret < 0)
+			continue;
+
+		rate = clk_get_rate(&clk);
+		printf("%s: %lu\n", clks[i].name, rate);
+
+		clk_free(&clk);
 	}
 
 	return 0;
diff --git a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
index 2e212823352e94dc6db61b958dc05238e42471e9..55ac73e9d25111eb96345e07d66cd7af9f80e9ed 100644
--- a/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
+++ b/arch/arm/mach-rockchip/rk3288/sdram_rk3288.c
@@ -36,7 +36,7 @@ struct chan_info {
 struct dram_info {
 	struct chan_info chan[2];
 	struct ram_info info;
-	struct udevice *ddr_clk;
+	struct clk ddr_clk;
 	struct rk3288_cru *cru;
 	struct rk3288_grf *grf;
 	struct rk3288_sgrf *sgrf;
@@ -576,7 +576,7 @@ static void dram_all_config(const struct dram_info *dram,
 	rk_clrsetreg(&dram->sgrf->soc_con2, 0x1f, sdram_params->base.stride);
 }
 
-static int sdram_init(const struct dram_info *dram,
+static int sdram_init(struct dram_info *dram,
 		      const struct rk3288_sdram_params *sdram_params)
 {
 	int channel;
@@ -592,8 +592,8 @@ static int sdram_init(const struct dram_info *dram,
 		return -E2BIG;
 	}
 
-	debug("ddr clk %s\n", dram->ddr_clk->name);
-	ret = clk_set_rate(dram->ddr_clk, sdram_params->base.ddr_freq);
+	debug("ddr clk dpll\n");
+	ret = clk_set_rate(&dram->ddr_clk, sdram_params->base.ddr_freq);
 	debug("ret=%d\n", ret);
 	if (ret) {
 		debug("Could not set DDR clock\n");
@@ -836,6 +836,7 @@ static int rk3288_dmc_probe(struct udevice *dev)
 	struct dram_info *priv = dev_get_priv(dev);
 	struct regmap *map;
 	int ret;
+	struct udevice *dev_clk;
 
 	map = syscon_get_regmap_by_driver_data(ROCKCHIP_SYSCON_NOC);
 	if (IS_ERR(map))
@@ -856,7 +857,11 @@ static int rk3288_dmc_probe(struct udevice *dev)
 	priv->chan[1].pctl = regmap_get_range(map, 2);
 	priv->chan[1].publ = regmap_get_range(map, 3);
 
-	ret = uclass_get_device(UCLASS_CLK, CLK_DDR, &priv->ddr_clk);
+	ret = uclass_get_device(UCLASS_CLK, 0, &dev_clk);
+	if (ret)
+		return ret;
+	priv->ddr_clk.id = CLK_DDR;
+	ret = clk_request(dev_clk, &priv->ddr_clk);
 	if (ret)
 		return ret;
 
diff --git a/arch/arm/mach-snapdragon/clock-apq8016.c b/arch/arm/mach-snapdragon/clock-apq8016.c
index d548d757d37bcbb8533affc1b74cf88b6e90129b..c2cf92494af39ae42139e85ffe61b839ddcb607a 100644
--- a/arch/arm/mach-snapdragon/clock-apq8016.c
+++ b/arch/arm/mach-snapdragon/clock-apq8016.c
@@ -9,7 +9,7 @@
  */
 
 #include <common.h>
-#include <clk.h>
+#include <clk-uclass.h>
 #include <dm.h>
 #include <errno.h>
 #include <asm/io.h>
@@ -212,11 +212,11 @@ static int clk_init_uart(struct msm_clk_priv *priv)
 	return 0;
 }
 
-ulong msm_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+ulong msm_set_rate(struct clk *clk, ulong rate)
 {
-	struct msm_clk_priv *priv = dev_get_priv(dev);
+	struct msm_clk_priv *priv = dev_get_priv(clk->dev);
 
-	switch (periph) {
+	switch (clk->id) {
 	case 0: /* SDC1 */
 		return clk_init_sdc(priv, 0, rate);
 		break;
@@ -243,7 +243,7 @@ static int msm_clk_probe(struct udevice *dev)
 }
 
 static struct clk_ops msm_clk_ops = {
-	.set_periph_rate = msm_set_periph_rate,
+	.set_rate = msm_set_rate,
 };
 
 static const struct udevice_id msm_clk_ids[] = {
diff --git a/arch/arm/mach-zynq/clk.c b/arch/arm/mach-zynq/clk.c
index 6444be8f032573c06e5b219ce6a930c93353b0a8..40383c11c914ff34c8add416bbe1772440cb2235 100644
--- a/arch/arm/mach-zynq/clk.c
+++ b/arch/arm/mach-zynq/clk.c
@@ -6,7 +6,6 @@
  */
 #include <common.h>
 #include <errno.h>
-#include <clk.h>
 #include <asm/io.h>
 #include <asm/arch/hardware.h>
 #include <asm/arch/clk.h>
diff --git a/arch/mips/mach-pic32/cpu.c b/arch/mips/mach-pic32/cpu.c
index f2ee911df4d183059eec5d6103c07870370a3b39..ac33391921a70b8208642e3ff80a67f7b26c43b5 100644
--- a/arch/mips/mach-pic32/cpu.c
+++ b/arch/mips/mach-pic32/cpu.c
@@ -23,18 +23,34 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-static ulong clk_get_cpu_rate(void)
+static ulong rate(int id)
 {
 	int ret;
 	struct udevice *dev;
+	struct clk clk;
+	ulong rate;
 
 	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
 	if (ret) {
-		panic("uclass-clk: device not found\n");
+		printf("clk-uclass not found\n");
 		return 0;
 	}
 
-	return clk_get_rate(dev);
+	clk.id = id;
+	ret = clk_request(dev, &clk);
+	if (ret < 0)
+		return ret;
+
+	rate = clk_get_rate(&clk);
+
+	clk_free(&clk);
+
+	return rate;
+}
+
+static ulong clk_get_cpu_rate(void)
+{
+	return rate(PB7CLK);
 }
 
 /* initialize prefetch module related to cpu_clk */
@@ -127,30 +143,25 @@ const char *get_core_name(void)
 }
 #endif
 #ifdef CONFIG_CMD_CLK
+
 int soc_clk_dump(void)
 {
-	int i, ret;
-	struct udevice *dev;
-
-	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
-	if (ret) {
-		printf("clk-uclass not found\n");
-		return ret;
-	}
+	int i;
 
 	printf("PLL Speed: %lu MHz\n",
-	       CLK_MHZ(clk_get_periph_rate(dev, PLLCLK)));
-	printf("CPU Speed: %lu MHz\n", CLK_MHZ(clk_get_rate(dev)));
-	printf("MPLL Speed: %lu MHz\n",
-	       CLK_MHZ(clk_get_periph_rate(dev, MPLL)));
+	       CLK_MHZ(rate(PLLCLK)));
+
+	printf("CPU Speed: %lu MHz\n", CLK_MHZ(rate(PB7CLK)));
+
+	printf("MPLL Speed: %lu MHz\n", CLK_MHZ(rate(MPLL)));
 
 	for (i = PB1CLK; i <= PB7CLK; i++)
 		printf("PB%d Clock Speed: %lu MHz\n", i - PB1CLK + 1,
-		       CLK_MHZ(clk_get_periph_rate(dev, i)));
+		       CLK_MHZ(rate(i)));
 
 	for (i = REF1CLK; i <= REF5CLK; i++)
 		printf("REFO%d Clock Speed: %lu MHz\n", i - REF1CLK + 1,
-		       CLK_MHZ(clk_get_periph_rate(dev, i)));
+		       CLK_MHZ(rate(i)));
 	return 0;
 }
 #endif
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 879b30e3ccc6f8a0566c44ff860b5433b65c600f..9e46f9e815a6628b16669fb427b7f178b1bd5808 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -108,8 +108,23 @@
 		compatible = "denx,u-boot-fdt-test";
 	};
 
-	clk@0 {
+	clk_fixed: clk-fixed {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <1234>;
+	};
+
+	clk_sandbox: clk-sbox {
 		compatible = "sandbox,clk";
+		#clock-cells = <1>;
+	};
+
+	clk-test {
+		compatible = "sandbox,clk-test";
+		clocks = <&clk_fixed>,
+			 <&clk_sandbox 1>,
+			 <&clk_sandbox 0>;
+		clock-names = "fixed", "i2c", "spi";
 	};
 
 	eth@10002000 {
diff --git a/arch/sandbox/include/asm/clk.h b/arch/sandbox/include/asm/clk.h
new file mode 100644
index 0000000000000000000000000000000000000000..9dc6c8184b30c23b2585c30fe8a5466cc1138310
--- /dev/null
+++ b/arch/sandbox/include/asm/clk.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef __SANDBOX_CLK_H
+#define __SANDBOX_CLK_H
+
+#include <common.h>
+
+struct udevice;
+
+/**
+ * enum sandbox_clk_id - Identity of clocks implemented by the sandbox clock
+ * provider.
+ *
+ * These IDs are within/relative-to the clock provider.
+ */
+enum sandbox_clk_id {
+	SANDBOX_CLK_ID_SPI,
+	SANDBOX_CLK_ID_I2C,
+
+	SANDBOX_CLK_ID_COUNT,
+};
+
+/**
+ * enum sandbox_clk_test_id - Identity of the clocks consumed by the sandbox
+ * clock test device.
+ *
+ * These are the IDs the clock consumer knows the clocks as.
+ */
+enum sandbox_clk_test_id {
+	SANDBOX_CLK_TEST_ID_FIXED,
+	SANDBOX_CLK_TEST_ID_SPI,
+	SANDBOX_CLK_TEST_ID_I2C,
+
+	SANDBOX_CLK_TEST_ID_COUNT,
+};
+
+/**
+ * sandbox_clk_query_rate - Query the current rate of a sandbox clock.
+ *
+ * @dev:	The sandbox clock provider device.
+ * @id:		The clock to query.
+ * @return:	The rate of the clock.
+ */
+ulong sandbox_clk_query_rate(struct udevice *dev, int id);
+/**
+ * sandbox_clk_query_enable - Query the enable state of a sandbox clock.
+ *
+ * @dev:	The sandbox clock provider device.
+ * @id:		The clock to query.
+ * @return:	The rate of the clock.
+ */
+int sandbox_clk_query_enable(struct udevice *dev, int id);
+
+/**
+ * sandbox_clk_test_get - Ask the sandbox clock test device to request its
+ * clocks.
+ *
+ * @dev:	The sandbox clock test (client) devivce.
+ * @return:	0 if OK, or a negative error code.
+ */
+int sandbox_clk_test_get(struct udevice *dev);
+/**
+ * sandbox_clk_test_get_rate - Ask the sandbox clock test device to query a
+ * clock's rate.
+ *
+ * @dev:	The sandbox clock test (client) devivce.
+ * @id:		The test device's clock ID to query.
+ * @return:	The rate of the clock.
+ */
+ulong sandbox_clk_test_get_rate(struct udevice *dev, int id);
+/**
+ * sandbox_clk_test_set_rate - Ask the sandbox clock test device to set a
+ * clock's rate.
+ *
+ * @dev:	The sandbox clock test (client) devivce.
+ * @id:		The test device's clock ID to configure.
+ * @return:	The new rate of the clock.
+ */
+ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate);
+/**
+ * sandbox_clk_test_enable - Ask the sandbox clock test device to enable a
+ * clock.
+ *
+ * @dev:	The sandbox clock test (client) devivce.
+ * @id:		The test device's clock ID to configure.
+ * @return:	0 if OK, or a negative error code.
+ */
+int sandbox_clk_test_enable(struct udevice *dev, int id);
+/**
+ * sandbox_clk_test_disable - Ask the sandbox clock test device to disable a
+ * clock.
+ *
+ * @dev:	The sandbox clock test (client) devivce.
+ * @id:		The test device's clock ID to configure.
+ * @return:	0 if OK, or a negative error code.
+ */
+int sandbox_clk_test_disable(struct udevice *dev, int id);
+/**
+ * sandbox_clk_test_free - Ask the sandbox clock test device to free its
+ * clocks.
+ *
+ * @dev:	The sandbox clock test (client) devivce.
+ * @return:	0 if OK, or a negative error code.
+ */
+int sandbox_clk_test_free(struct udevice *dev);
+
+#endif
diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h
index 224b0ebaf9551e8a35d089a4d2a9eb74634b9762..451a78e590e3cc39478ee8717bb7b06dea247a62 100644
--- a/arch/sandbox/include/asm/test.h
+++ b/arch/sandbox/include/asm/test.h
@@ -19,15 +19,6 @@
 
 #define SANDBOX_CLK_RATE		32768
 
-enum {
-	PERIPH_ID_FIRST = 0,
-	PERIPH_ID_SPI = PERIPH_ID_FIRST,
-	PERIPH_ID_I2C,
-	PERIPH_ID_PCI,
-
-	PERIPH_ID_COUNT,
-};
-
 /* System controller driver data */
 enum {
 	SYSCON0		= 32,
diff --git a/board/microchip/pic32mzda/pic32mzda.c b/board/microchip/pic32mzda/pic32mzda.c
index afe2ab8b7133941328dc590268b9c2fa06bae7d4..3d31d3d0620a8efad5d23f98c7a8e74ea167cec9 100644
--- a/board/microchip/pic32mzda/pic32mzda.c
+++ b/board/microchip/pic32mzda/pic32mzda.c
@@ -11,20 +11,31 @@
 #include <common.h>
 #include <dm.h>
 #include <clk.h>
+#include <dt-bindings/clock/microchip,clock.h>
 #include <mach/pic32.h>
 
 #ifdef CONFIG_DISPLAY_BOARDINFO
 int checkboard(void)
 {
-	ulong rate = 0;
+	ulong rate;
 	struct udevice *dev;
+	struct clk clk;
+	int ret;
 
 	printf("Core: %s\n", get_core_name());
 
-	if (!uclass_get_device(UCLASS_CLK, 0, &dev)) {
-		rate = clk_get_rate(dev);
-		printf("CPU Speed: %lu MHz\n", rate / 1000000);
-	}
+	if (uclass_get_device(UCLASS_CLK, 0, &dev))
+		return 0;
+
+	clk.id = PB7CLK;
+	ret = clk_request(dev, &clk);
+	if (ret < 0)
+		return 0;
+
+	rate = clk_get_rate(&clk);
+	printf("CPU Speed: %lu MHz\n", rate / 1000000);
+
+	clk_free(&clk);
 
 	return 0;
 }
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 81fe600188cc030cb9b585b987b5fc242a9a89c0..f7a88912e06a0b143b34826fc0e48e0ad7e13117 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CLK) += clk-uclass.o clk_fixed_rate.o
 obj-$(CONFIG_ROCKCHIP_RK3036) += clk_rk3036.o
 obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
 obj-$(CONFIG_SANDBOX) += clk_sandbox.o
+obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
 obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
 obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
 obj-$(CONFIG_CLK_EXYNOS) += exynos/
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index b483c1ef33bd7905b1467a272ddc79b2bce6ba16..6e4d67220a1685ccf8ce3dca91cd83300783203c 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -1,91 +1,78 @@
 /*
  * Copyright (C) 2015 Google, Inc
  * Written by Simon Glass <sjg@chromium.org>
+ * Copyright (c) 2016, NVIDIA CORPORATION.
  *
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
 #include <common.h>
 #include <clk.h>
+#include <clk-uclass.h>
 #include <dm.h>
 #include <errno.h>
-#include <dm/lists.h>
-#include <dm/root.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
-ulong clk_get_rate(struct udevice *dev)
+static inline struct clk_ops *clk_dev_ops(struct udevice *dev)
 {
-	struct clk_ops *ops = clk_get_ops(dev);
-
-	if (!ops->get_rate)
-		return -ENOSYS;
-
-	return ops->get_rate(dev);
+	return (struct clk_ops *)dev->driver->ops;
 }
 
-ulong clk_set_rate(struct udevice *dev, ulong rate)
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+#ifdef CONFIG_SPL_BUILD
+int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
 {
-	struct clk_ops *ops = clk_get_ops(dev);
+	int ret;
+	u32 cell[2];
 
-	if (!ops->set_rate)
+	if (index != 0)
 		return -ENOSYS;
-
-	return ops->set_rate(dev, rate);
+	assert(clk);
+	ret = uclass_get_device(UCLASS_CLK, 0, &clk->dev);
+	if (ret)
+		return ret;
+	ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clocks",
+				   cell, 2);
+	if (ret)
+		return ret;
+	clk->id = cell[1];
+	return 0;
 }
 
-int clk_enable(struct udevice *dev, int periph)
+int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk)
 {
-	struct clk_ops *ops = clk_get_ops(dev);
-
-	if (!ops->enable)
-		return -ENOSYS;
-
-	return ops->enable(dev, periph);
+	return -ENOSYS;
 }
-
-ulong clk_get_periph_rate(struct udevice *dev, int periph)
+#else
+static int clk_of_xlate_default(struct clk *clk,
+				struct fdtdec_phandle_args *args)
 {
-	struct clk_ops *ops = clk_get_ops(dev);
+	debug("%s(clk=%p)\n", __func__, clk);
 
-	if (!ops->get_periph_rate)
-		return -ENOSYS;
-
-	return ops->get_periph_rate(dev, periph);
-}
-
-ulong clk_set_periph_rate(struct udevice *dev, int periph, ulong rate)
-{
-	struct clk_ops *ops = clk_get_ops(dev);
+	if (args->args_count > 1) {
+		debug("Invaild args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
 
-	if (!ops->set_periph_rate)
-		return -ENOSYS;
+	if (args->args_count)
+		clk->id = args->args[0];
+	else
+		clk->id = 0;
 
-	return ops->set_periph_rate(dev, periph, rate);
+	return 0;
 }
 
-#if CONFIG_IS_ENABLED(OF_CONTROL)
-int clk_get_by_index(struct udevice *dev, int index, struct udevice **clk_devp)
+int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
 {
 	int ret;
-#ifdef CONFIG_SPL_BUILD
-	u32 cell[2];
-
-	if (index != 0)
-		return -ENOSYS;
-	assert(*clk_devp);
-	ret = uclass_get_device(UCLASS_CLK, 0, clk_devp);
-	if (ret)
-		return ret;
-	ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clocks",
-				   cell, 2);
-	if (ret)
-		return ret;
-	return cell[1];
-#else
 	struct fdtdec_phandle_args args;
+	struct udevice *dev_clk;
+	struct clk_ops *ops;
+
+	debug("%s(dev=%p, index=%d, clk=%p)\n", __func__, dev, index, clk);
 
-	assert(*clk_devp);
+	assert(clk);
 	ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset,
 					     "clocks", "#clock-cells", 0, index,
 					     &args);
@@ -95,16 +82,117 @@ int clk_get_by_index(struct udevice *dev, int index, struct udevice **clk_devp)
 		return ret;
 	}
 
-	ret = uclass_get_device_by_of_offset(UCLASS_CLK, args.node, clk_devp);
+	ret = uclass_get_device_by_of_offset(UCLASS_CLK, args.node, &dev_clk);
 	if (ret) {
 		debug("%s: uclass_get_device_by_of_offset failed: err=%d\n",
 		      __func__, ret);
 		return ret;
 	}
-	return args.args_count > 0 ? args.args[0] : 0;
-#endif
+	ops = clk_dev_ops(dev_clk);
+
+	if (ops->of_xlate)
+		ret = ops->of_xlate(clk, &args);
+	else
+		ret = clk_of_xlate_default(clk, &args);
+	if (ret) {
+		debug("of_xlate() failed: %d\n", ret);
+		return ret;
+	}
+
+	return clk_request(dev_clk, clk);
+}
+
+int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk)
+{
+	int index;
+
+	debug("%s(dev=%p, name=%s, clk=%p)\n", __func__, dev, name, clk);
+
+	index = fdt_find_string(gd->fdt_blob, dev->of_offset, "clock-names",
+				name);
+	if (index < 0) {
+		debug("fdt_find_string() failed: %d\n", index);
+		return index;
+	}
+
+	return clk_get_by_index(dev, index, clk);
 }
 #endif
+#endif
+
+int clk_request(struct udevice *dev, struct clk *clk)
+{
+	struct clk_ops *ops = clk_dev_ops(dev);
+
+	debug("%s(dev=%p, clk=%p)\n", __func__, dev, clk);
+
+	clk->dev = dev;
+
+	if (!ops->request)
+		return 0;
+
+	return ops->request(clk);
+}
+
+int clk_free(struct clk *clk)
+{
+	struct clk_ops *ops = clk_dev_ops(clk->dev);
+
+	debug("%s(clk=%p)\n", __func__, clk);
+
+	if (!ops->free)
+		return 0;
+
+	return ops->free(clk);
+}
+
+ulong clk_get_rate(struct clk *clk)
+{
+	struct clk_ops *ops = clk_dev_ops(clk->dev);
+
+	debug("%s(clk=%p)\n", __func__, clk);
+
+	if (!ops->get_rate)
+		return -ENOSYS;
+
+	return ops->get_rate(clk);
+}
+
+ulong clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_ops *ops = clk_dev_ops(clk->dev);
+
+	debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate);
+
+	if (!ops->set_rate)
+		return -ENOSYS;
+
+	return ops->set_rate(clk, rate);
+}
+
+int clk_enable(struct clk *clk)
+{
+	struct clk_ops *ops = clk_dev_ops(clk->dev);
+
+	debug("%s(clk=%p)\n", __func__, clk);
+
+	if (!ops->enable)
+		return -ENOSYS;
+
+	return ops->enable(clk);
+}
+
+int clk_disable(struct clk *clk)
+{
+	struct clk_ops *ops = clk_dev_ops(clk->dev);
+
+	debug("%s(clk=%p)\n", __func__, clk);
+
+	if (!ops->disable)
+		return -ENOSYS;
+
+	return ops->disable(clk);
+}
 
 UCLASS_DRIVER(clk) = {
 	.id		= UCLASS_CLK,
diff --git a/drivers/clk/clk_fixed_rate.c b/drivers/clk/clk_fixed_rate.c
index 8beda9cb55720e95fff39450a2d17567090c3dec..797e5379075da87e5dac71ed7d61106c8dd9fbff 100644
--- a/drivers/clk/clk_fixed_rate.c
+++ b/drivers/clk/clk_fixed_rate.c
@@ -5,7 +5,7 @@
  */
 
 #include <common.h>
-#include <clk.h>
+#include <clk-uclass.h>
 #include <dm/device.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -16,19 +16,16 @@ struct clk_fixed_rate {
 
 #define to_clk_fixed_rate(dev)	((struct clk_fixed_rate *)dev_get_platdata(dev))
 
-static ulong clk_fixed_rate_get_rate(struct udevice *dev)
+static ulong clk_fixed_rate_get_rate(struct clk *clk)
 {
-	return to_clk_fixed_rate(dev)->fixed_rate;
-}
+	if (clk->id != 0)
+		return -EINVAL;
 
-static ulong clk_fixed_rate_get_periph_rate(struct udevice *dev, int periph)
-{
-	return clk_fixed_rate_get_rate(dev);
+	return to_clk_fixed_rate(clk->dev)->fixed_rate;
 }
 
 const struct clk_ops clk_fixed_rate_ops = {
 	.get_rate = clk_fixed_rate_get_rate,
-	.get_periph_rate = clk_fixed_rate_get_periph_rate,
 };
 
 static int clk_fixed_rate_ofdata_to_platdata(struct udevice *dev)
diff --git a/drivers/clk/clk_pic32.c b/drivers/clk/clk_pic32.c
index 5d883544d5136e9677ca4c8630ed7ec3d9914f65..70ec3543cf90eadf9b79e8f01631e1a8a454e3d7 100644
--- a/drivers/clk/clk_pic32.c
+++ b/drivers/clk/clk_pic32.c
@@ -6,7 +6,7 @@
  */
 
 #include <common.h>
-#include <clk.h>
+#include <clk-uclass.h>
 #include <dm.h>
 #include <div64.h>
 #include <wait_bit.h>
@@ -339,24 +339,17 @@ static void pic32_clk_init(struct udevice *dev)
 	pic32_mpll_init(priv);
 }
 
-static ulong pic32_clk_get_rate(struct udevice *dev)
+static ulong pic32_get_rate(struct clk *clk)
 {
-	struct pic32_clk_priv *priv = dev_get_priv(dev);
-
-	return pic32_get_cpuclk(priv);
-}
-
-static ulong pic32_get_periph_rate(struct udevice *dev, int periph)
-{
-	struct pic32_clk_priv *priv = dev_get_priv(dev);
+	struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
 	ulong rate;
 
-	switch (periph) {
+	switch (clk->id) {
 	case PB1CLK ... PB7CLK:
-		rate = pic32_get_pbclk(priv, periph);
+		rate = pic32_get_pbclk(priv, clk->id);
 		break;
 	case REF1CLK ... REF5CLK:
-		rate = pic32_get_refclk(priv, periph);
+		rate = pic32_get_refclk(priv, clk->id);
 		break;
 	case PLLCLK:
 		rate = pic32_get_pll_rate(priv);
@@ -372,15 +365,15 @@ static ulong pic32_get_periph_rate(struct udevice *dev, int periph)
 	return rate;
 }
 
-static ulong pic32_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+static ulong pic32_set_rate(struct clk *clk, ulong rate)
 {
-	struct pic32_clk_priv *priv = dev_get_priv(dev);
+	struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
 	ulong pll_hz;
 
-	switch (periph) {
+	switch (clk->id) {
 	case REF1CLK ... REF5CLK:
 		pll_hz = pic32_get_pll_rate(priv);
-		pic32_set_refclk(priv, periph, pll_hz, rate, ROCLK_SRC_SPLL);
+		pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
 		break;
 	default:
 		break;
@@ -390,9 +383,8 @@ static ulong pic32_set_periph_rate(struct udevice *dev, int periph, ulong rate)
 }
 
 static struct clk_ops pic32_pic32_clk_ops = {
-	.get_rate = pic32_clk_get_rate,
-	.set_periph_rate = pic32_set_periph_rate,
-	.get_periph_rate = pic32_get_periph_rate,
+	.set_rate = pic32_set_rate,
+	.get_rate = pic32_get_rate,
 };
 
 static int pic32_clk_probe(struct udevice *dev)
diff --git a/drivers/clk/clk_rk3036.c b/drivers/clk/clk_rk3036.c
index 7ec65bdff0299482da68bbf83d661c8fc63e0123..6202c9da5da87524f2157bf188ce221f824d2929 100644
--- a/drivers/clk/clk_rk3036.c
+++ b/drivers/clk/clk_rk3036.c
@@ -5,7 +5,7 @@
  */
 
 #include <common.h>
-#include <clk.h>
+#include <clk-uclass.h>
 #include <dm.h>
 #include <errno.h>
 #include <syscon.h>
@@ -18,10 +18,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-struct rk3036_clk_plat {
-	enum rk_clk_id clk_id;
-};
-
 struct rk3036_clk_priv {
 	struct rk3036_cru *cru;
 	ulong rate;
@@ -315,31 +311,30 @@ static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate,
 	return rockchip_mmc_get_clk(cru, clk_general_rate, periph);
 }
 
-static ulong rk3036_clk_get_rate(struct udevice *dev)
-{
-	struct rk3036_clk_plat *plat = dev_get_platdata(dev);
-	struct rk3036_clk_priv *priv = dev_get_priv(dev);
-
-	debug("%s\n", dev->name);
-	return rkclk_pll_get_rate(priv->cru, plat->clk_id);
-}
-
-static ulong rk3036_clk_set_rate(struct udevice *dev, ulong rate)
+static ulong rk3036_clk_get_rate(struct clk *clk)
 {
-	debug("%s\n", dev->name);
+	struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
 
-	return 0;
+	switch (clk->id) {
+	case 0 ... 63:
+		return rkclk_pll_get_rate(priv->cru, clk->id);
+	default:
+		return -ENOENT;
+	}
 }
 
-static ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+static ulong rk3036_clk_set_rate(struct clk *clk, ulong rate)
 {
-	struct rk3036_clk_priv *priv = dev_get_priv(dev);
-	ulong new_rate;
+	struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong new_rate, gclk_rate;
 
-	switch (periph) {
+	gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
+	switch (clk->id) {
+	case 0 ... 63:
+		return 0;
 	case HCLK_EMMC:
-		new_rate = rockchip_mmc_set_clk(priv->cru, clk_get_rate(dev),
-						periph, rate);
+		new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate,
+						clk->id, rate);
 		break;
 	default:
 		return -ENOENT;
@@ -351,60 +346,21 @@ static ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate)
 static struct clk_ops rk3036_clk_ops = {
 	.get_rate	= rk3036_clk_get_rate,
 	.set_rate	= rk3036_clk_set_rate,
-	.set_periph_rate = rk3036_set_periph_rate,
 };
 
 static int rk3036_clk_probe(struct udevice *dev)
 {
-	struct rk3036_clk_plat *plat = dev_get_platdata(dev);
 	struct rk3036_clk_priv *priv = dev_get_priv(dev);
 
-	if (plat->clk_id != CLK_OSC) {
-		struct rk3036_clk_priv *parent_priv = dev_get_priv(dev->parent);
-
-		priv->cru = parent_priv->cru;
-		return 0;
-	}
 	priv->cru = (struct rk3036_cru *)dev_get_addr(dev);
 	rkclk_init(priv->cru);
 
 	return 0;
 }
 
-static const char *const clk_name[] = {
-	"osc",
-	"apll",
-	"dpll",
-	"cpll",
-	"gpll",
-	"mpll",
-};
-
 static int rk3036_clk_bind(struct udevice *dev)
 {
-	struct rk3036_clk_plat *plat = dev_get_platdata(dev);
-	int pll, ret;
-
-	/* We only need to set up the root clock */
-	if (dev->of_offset == -1) {
-		plat->clk_id = CLK_OSC;
-		return 0;
-	}
-
-	/* Create devices for P main clocks */
-	for (pll = 1; pll < CLK_COUNT; pll++) {
-		struct udevice *child;
-		struct rk3036_clk_plat *cplat;
-
-		debug("%s %s\n", __func__, clk_name[pll]);
-		ret = device_bind_driver(dev, "clk_rk3036", clk_name[pll],
-					 &child);
-		if (ret)
-			return ret;
-
-		cplat = dev_get_platdata(child);
-		cplat->clk_id = pll;
-	}
+	int ret;
 
 	/* The reset driver does not have a device node, so bind it here */
 	ret = device_bind_driver(gd->dm_root, "rk3036_sysreset", "reset", &dev);
@@ -424,7 +380,6 @@ U_BOOT_DRIVER(clk_rk3036) = {
 	.id		= UCLASS_CLK,
 	.of_match	= rk3036_clk_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3036_clk_priv),
-	.platdata_auto_alloc_size = sizeof(struct rk3036_clk_plat),
 	.ops		= &rk3036_clk_ops,
 	.bind		= rk3036_clk_bind,
 	.probe		= rk3036_clk_probe,
diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c
index d88893c8ea629198746fbf8a0cfb7cb8eaeb6839..2285453e8de284b93731370e73ee48490781b4f7 100644
--- a/drivers/clk/clk_rk3288.c
+++ b/drivers/clk/clk_rk3288.c
@@ -5,7 +5,7 @@
  */
 
 #include <common.h>
-#include <clk.h>
+#include <clk-uclass.h>
 #include <dm.h>
 #include <errno.h>
 #include <syscon.h>
@@ -21,10 +21,6 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-struct rk3288_clk_plat {
-	enum rk_clk_id clk_id;
-};
-
 struct rk3288_clk_priv {
 	struct rk3288_grf *grf;
 	struct rk3288_cru *cru;
@@ -135,34 +131,18 @@ static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 1);
 static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
 static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
 
-int rkclk_get_clk(enum rk_clk_id clk_id, struct udevice **devp)
-{
-	struct udevice *dev;
-
-	for (uclass_find_first_device(UCLASS_CLK, &dev);
-	     dev;
-	     uclass_find_next_device(&dev)) {
-		struct rk3288_clk_plat *plat = dev_get_platdata(dev);
-
-		if (plat->clk_id == clk_id) {
-			*devp = dev;
-			return device_probe(dev);
-		}
-	}
-
-	return -ENODEV;
-}
-
 void *rockchip_get_cru(void)
 {
 	struct rk3288_clk_priv *priv;
 	struct udevice *dev;
 	int ret;
 
-	ret = rkclk_get_clk(CLK_GENERAL, &dev);
+	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
 	if (ret)
 		return ERR_PTR(ret);
+
 	priv = dev_get_priv(dev);
+
 	return priv->cru;
 }
 
@@ -539,32 +519,6 @@ static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru,
 	}
 }
 
-static ulong rk3288_clk_get_rate(struct udevice *dev)
-{
-	struct rk3288_clk_plat *plat = dev_get_platdata(dev);
-	struct rk3288_clk_priv *priv = dev_get_priv(dev);
-
-	debug("%s\n", dev->name);
-	return rkclk_pll_get_rate(priv->cru, plat->clk_id);
-}
-
-static ulong rk3288_clk_set_rate(struct udevice *dev, ulong rate)
-{
-	struct rk3288_clk_plat *plat = dev_get_platdata(dev);
-	struct rk3288_clk_priv *priv = dev_get_priv(dev);
-
-	debug("%s\n", dev->name);
-	switch (plat->clk_id) {
-	case CLK_DDR:
-		rkclk_configure_ddr(priv->cru, priv->grf, rate);
-		break;
-	default:
-		return -ENOENT;
-	}
-
-	return 0;
-}
-
 static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint gclk_rate,
 				  int periph)
 {
@@ -710,27 +664,25 @@ static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint gclk_rate,
 	return rockchip_spi_get_clk(cru, gclk_rate, periph);
 }
 
-static ulong rk3288_get_periph_rate(struct udevice *dev, int periph)
+static ulong rk3288_clk_get_rate(struct clk *clk)
 {
-	struct rk3288_clk_priv *priv = dev_get_priv(dev);
-	struct udevice *gclk;
+	struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
 	ulong new_rate, gclk_rate;
-	int ret;
 
-	ret = rkclk_get_clk(CLK_GENERAL, &gclk);
-	if (ret)
-		return ret;
-	gclk_rate = clk_get_rate(gclk);
-	switch (periph) {
+	gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
+	switch (clk->id) {
+	case 0 ... 63:
+		new_rate = rkclk_pll_get_rate(priv->cru, clk->id);
+		break;
 	case HCLK_EMMC:
 	case HCLK_SDMMC:
 	case HCLK_SDIO0:
-		new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, periph);
+		new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, clk->id);
 		break;
 	case SCLK_SPI0:
 	case SCLK_SPI1:
 	case SCLK_SPI2:
-		new_rate = rockchip_spi_get_clk(priv->cru, gclk_rate, periph);
+		new_rate = rockchip_spi_get_clk(priv->cru, gclk_rate, clk->id);
 		break;
 	case PCLK_I2C0:
 	case PCLK_I2C1:
@@ -746,36 +698,34 @@ static ulong rk3288_get_periph_rate(struct udevice *dev, int periph)
 	return new_rate;
 }
 
-static ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate)
 {
-	struct rk3288_clk_priv *priv = dev_get_priv(dev);
+	struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
 	struct rk3288_cru *cru = priv->cru;
-	struct udevice *gclk;
 	ulong new_rate, gclk_rate;
-	int ret;
 
-	ret = rkclk_get_clk(CLK_GENERAL, &gclk);
-	if (ret)
-		return ret;
-	gclk_rate = clk_get_rate(gclk);
-	switch (periph) {
+	gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
+	switch (clk->id) {
+	case CLK_DDR:
+		new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate);
+		break;
 	case HCLK_EMMC:
 	case HCLK_SDMMC:
 	case HCLK_SDIO0:
-		new_rate = rockchip_mmc_set_clk(cru, gclk_rate, periph, rate);
+		new_rate = rockchip_mmc_set_clk(cru, gclk_rate, clk->id, rate);
 		break;
 	case SCLK_SPI0:
 	case SCLK_SPI1:
 	case SCLK_SPI2:
-		new_rate = rockchip_spi_set_clk(cru, gclk_rate, periph, rate);
+		new_rate = rockchip_spi_set_clk(cru, gclk_rate, clk->id, rate);
 		break;
 #ifndef CONFIG_SPL_BUILD
 	case SCLK_MAC:
-		new_rate = rockchip_mac_set_clk(priv->cru, periph, rate);
+		new_rate = rockchip_mac_set_clk(priv->cru, clk->id, rate);
 		break;
 	case DCLK_VOP0:
 	case DCLK_VOP1:
-		new_rate = rockchip_vop_set_clk(cru, priv->grf, periph, rate);
+		new_rate = rockchip_vop_set_clk(cru, priv->grf, clk->id, rate);
 		break;
 	case SCLK_EDP_24M:
 		/* clk_edp_24M source: 24M */
@@ -795,7 +745,7 @@ static ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate)
 		div = CPLL_HZ / rate;
 		assert((div - 1 < 64) && (div * rate == CPLL_HZ));
 
-		switch (periph) {
+		switch (clk->id) {
 		case ACLK_VOP0:
 			rk_clrsetreg(&cru->cru_clksel_con[31],
 				     3 << 6 | 0x1f << 0,
@@ -831,22 +781,12 @@ static ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate)
 static struct clk_ops rk3288_clk_ops = {
 	.get_rate	= rk3288_clk_get_rate,
 	.set_rate	= rk3288_clk_set_rate,
-	.set_periph_rate = rk3288_set_periph_rate,
-	.get_periph_rate = rk3288_get_periph_rate,
 };
 
 static int rk3288_clk_probe(struct udevice *dev)
 {
-	struct rk3288_clk_plat *plat = dev_get_platdata(dev);
 	struct rk3288_clk_priv *priv = dev_get_priv(dev);
 
-	if (plat->clk_id != CLK_OSC) {
-		struct rk3288_clk_priv *parent_priv = dev_get_priv(dev->parent);
-
-		priv->cru = parent_priv->cru;
-		priv->grf = parent_priv->grf;
-		return 0;
-	}
 	priv->cru = (struct rk3288_cru *)dev_get_addr(dev);
 	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
 #ifdef CONFIG_SPL_BUILD
@@ -856,39 +796,9 @@ static int rk3288_clk_probe(struct udevice *dev)
 	return 0;
 }
 
-static const char *const clk_name[CLK_COUNT] = {
-	"osc",
-	"apll",
-	"dpll",
-	"cpll",
-	"gpll",
-	"npll",
-};
-
 static int rk3288_clk_bind(struct udevice *dev)
 {
-	struct rk3288_clk_plat *plat = dev_get_platdata(dev);
-	int pll, ret;
-
-	/* We only need to set up the root clock */
-	if (dev->of_offset == -1) {
-		plat->clk_id = CLK_OSC;
-		return 0;
-	}
-
-	/* Create devices for P main clocks */
-	for (pll = 1; pll < CLK_COUNT; pll++) {
-		struct udevice *child;
-		struct rk3288_clk_plat *cplat;
-
-		debug("%s %s\n", __func__, clk_name[pll]);
-		ret = device_bind_driver(dev, "clk_rk3288", clk_name[pll],
-					 &child);
-		if (ret)
-			return ret;
-		cplat = dev_get_platdata(child);
-		cplat->clk_id = pll;
-	}
+	int ret;
 
 	/* The reset driver does not have a device node, so bind it here */
 	ret = device_bind_driver(gd->dm_root, "rk3288_sysreset", "reset", &dev);
@@ -908,7 +818,6 @@ U_BOOT_DRIVER(clk_rk3288) = {
 	.id		= UCLASS_CLK,
 	.of_match	= rk3288_clk_ids,
 	.priv_auto_alloc_size = sizeof(struct rk3288_clk_priv),
-	.platdata_auto_alloc_size = sizeof(struct rk3288_clk_plat),
 	.ops		= &rk3288_clk_ops,
 	.bind		= rk3288_clk_bind,
 	.probe		= rk3288_clk_probe,
diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c
index 367130f8b76b12a662d41086932e165bfb185390..c6bd7c64e2916c84f14404f50dd20ab56372d622 100644
--- a/drivers/clk/clk_sandbox.c
+++ b/drivers/clk/clk_sandbox.c
@@ -5,61 +5,63 @@
  */
 
 #include <common.h>
-#include <clk.h>
+#include <clk-uclass.h>
 #include <dm.h>
 #include <errno.h>
-#include <asm/test.h>
+#include <asm/clk.h>
 
 struct sandbox_clk_priv {
-	ulong rate;
-	ulong periph_rate[PERIPH_ID_COUNT];
+	ulong rate[SANDBOX_CLK_ID_COUNT];
+	bool enabled[SANDBOX_CLK_ID_COUNT];
 };
 
-static ulong sandbox_clk_get_rate(struct udevice *dev)
+static ulong sandbox_clk_get_rate(struct clk *clk)
 {
-	struct sandbox_clk_priv *priv = dev_get_priv(dev);
+	struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
+
+	if (clk->id < 0 || clk->id >= SANDBOX_CLK_ID_COUNT)
+		return -EINVAL;
 
-	return priv->rate;
+	return priv->rate[clk->id];
 }
 
-static ulong sandbox_clk_set_rate(struct udevice *dev, ulong rate)
+static ulong sandbox_clk_set_rate(struct clk *clk, ulong rate)
 {
-	struct sandbox_clk_priv *priv = dev_get_priv(dev);
+	struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong old_rate;
+
+	if (clk->id < 0 || clk->id >= SANDBOX_CLK_ID_COUNT)
+		return -EINVAL;
 
 	if (!rate)
 		return -EINVAL;
-	priv->rate = rate;
-	return 0;
-}
 
-static ulong sandbox_get_periph_rate(struct udevice *dev, int periph)
-{
-	struct sandbox_clk_priv *priv = dev_get_priv(dev);
+	old_rate = priv->rate[clk->id];
+	priv->rate[clk->id] = rate;
 
-	if (periph < PERIPH_ID_FIRST || periph >= PERIPH_ID_COUNT)
-		return -EINVAL;
-	return priv->periph_rate[periph];
+	return old_rate;
 }
 
-static ulong sandbox_set_periph_rate(struct udevice *dev, int periph,
-				     ulong rate)
+static int sandbox_clk_enable(struct clk *clk)
 {
-	struct sandbox_clk_priv *priv = dev_get_priv(dev);
-	ulong old_rate;
+	struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
 
-	if (periph < PERIPH_ID_FIRST || periph >= PERIPH_ID_COUNT)
+	if (clk->id < 0 || clk->id >= SANDBOX_CLK_ID_COUNT)
 		return -EINVAL;
-	old_rate = priv->periph_rate[periph];
-	priv->periph_rate[periph] = rate;
 
-	return old_rate;
+	priv->enabled[clk->id] = true;
+
+	return 0;
 }
 
-static int sandbox_clk_probe(struct udevice *dev)
+static int sandbox_clk_disable(struct clk *clk)
 {
-	struct sandbox_clk_priv *priv = dev_get_priv(dev);
+	struct sandbox_clk_priv *priv = dev_get_priv(clk->dev);
+
+	if (clk->id < 0 || clk->id >= SANDBOX_CLK_ID_COUNT)
+		return -EINVAL;
 
-	priv->rate = SANDBOX_CLK_RATE;
+	priv->enabled[clk->id] = false;
 
 	return 0;
 }
@@ -67,8 +69,8 @@ static int sandbox_clk_probe(struct udevice *dev)
 static struct clk_ops sandbox_clk_ops = {
 	.get_rate	= sandbox_clk_get_rate,
 	.set_rate	= sandbox_clk_set_rate,
-	.get_periph_rate = sandbox_get_periph_rate,
-	.set_periph_rate = sandbox_set_periph_rate,
+	.enable		= sandbox_clk_enable,
+	.disable	= sandbox_clk_disable,
 };
 
 static const struct udevice_id sandbox_clk_ids[] = {
@@ -82,5 +84,24 @@ U_BOOT_DRIVER(clk_sandbox) = {
 	.of_match	= sandbox_clk_ids,
 	.ops		= &sandbox_clk_ops,
 	.priv_auto_alloc_size = sizeof(struct sandbox_clk_priv),
-	.probe		= sandbox_clk_probe,
 };
+
+ulong sandbox_clk_query_rate(struct udevice *dev, int id)
+{
+	struct sandbox_clk_priv *priv = dev_get_priv(dev);
+
+	if (id < 0 || id >= SANDBOX_CLK_ID_COUNT)
+		return -EINVAL;
+
+	return priv->rate[id];
+}
+
+int sandbox_clk_query_enable(struct udevice *dev, int id)
+{
+	struct sandbox_clk_priv *priv = dev_get_priv(dev);
+
+	if (id < 0 || id >= SANDBOX_CLK_ID_COUNT)
+		return -EINVAL;
+
+	return priv->enabled[id];
+}
diff --git a/drivers/clk/clk_sandbox_test.c b/drivers/clk/clk_sandbox_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..999100de9d2f26b8acf263b01a1ccfe6272d2b45
--- /dev/null
+++ b/drivers/clk/clk_sandbox_test.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <clk.h>
+#include <asm/clk.h>
+
+struct sandbox_clk_test {
+	struct clk clks[SANDBOX_CLK_TEST_ID_COUNT];
+};
+
+static const char * const sandbox_clk_test_names[] = {
+	[SANDBOX_CLK_TEST_ID_FIXED] = "fixed",
+	[SANDBOX_CLK_TEST_ID_SPI] = "spi",
+	[SANDBOX_CLK_TEST_ID_I2C] = "i2c",
+};
+
+int sandbox_clk_test_get(struct udevice *dev)
+{
+	struct sandbox_clk_test *sbct = dev_get_priv(dev);
+	int i, ret;
+
+	for (i = 0; i < SANDBOX_CLK_TEST_ID_COUNT; i++) {
+		ret = clk_get_by_name(dev, sandbox_clk_test_names[i],
+				      &sbct->clks[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+ulong sandbox_clk_test_get_rate(struct udevice *dev, int id)
+{
+	struct sandbox_clk_test *sbct = dev_get_priv(dev);
+
+	if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
+		return -EINVAL;
+
+	return clk_get_rate(&sbct->clks[id]);
+}
+
+ulong sandbox_clk_test_set_rate(struct udevice *dev, int id, ulong rate)
+{
+	struct sandbox_clk_test *sbct = dev_get_priv(dev);
+
+	if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
+		return -EINVAL;
+
+	return clk_set_rate(&sbct->clks[id], rate);
+}
+
+int sandbox_clk_test_enable(struct udevice *dev, int id)
+{
+	struct sandbox_clk_test *sbct = dev_get_priv(dev);
+
+	if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
+		return -EINVAL;
+
+	return clk_enable(&sbct->clks[id]);
+}
+
+int sandbox_clk_test_disable(struct udevice *dev, int id)
+{
+	struct sandbox_clk_test *sbct = dev_get_priv(dev);
+
+	if (id < 0 || id >= SANDBOX_CLK_TEST_ID_COUNT)
+		return -EINVAL;
+
+	return clk_disable(&sbct->clks[id]);
+}
+
+int sandbox_clk_test_free(struct udevice *dev)
+{
+	struct sandbox_clk_test *sbct = dev_get_priv(dev);
+	int i, ret;
+
+	for (i = 0; i < SANDBOX_CLK_TEST_ID_COUNT; i++) {
+		ret = clk_free(&sbct->clks[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct udevice_id sandbox_clk_test_ids[] = {
+	{ .compatible = "sandbox,clk-test" },
+	{ }
+};
+
+U_BOOT_DRIVER(sandbox_clk_test) = {
+	.name = "sandbox_clk_test",
+	.id = UCLASS_MISC,
+	.of_match = sandbox_clk_test_ids,
+	.priv_auto_alloc_size = sizeof(struct sandbox_clk_test),
+};
diff --git a/drivers/clk/exynos/clk-exynos7420.c b/drivers/clk/exynos/clk-exynos7420.c
index bf5d0e6e6032ac563642f9d6500c830e7cd63e4c..1f017a307f38803eddab7a778de70c54a332de9a 100644
--- a/drivers/clk/exynos/clk-exynos7420.c
+++ b/drivers/clk/exynos/clk-exynos7420.c
@@ -9,7 +9,7 @@
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
-#include <clk.h>
+#include <clk-uclass.h>
 #include <asm/io.h>
 #include <dt-bindings/clock/exynos7420-clk.h>
 #include "clk-pll.h"
@@ -67,11 +67,11 @@ struct exynos7420_clk_top0_priv {
 	unsigned long sclk_uart2;
 };
 
-static ulong exynos7420_topc_get_periph_rate(struct udevice *dev, int periph)
+static ulong exynos7420_topc_get_rate(struct clk *clk)
 {
-	struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev);
+	struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev);
 
-	switch (periph) {
+	switch (clk->id) {
 	case DOUT_SCLK_BUS0_PLL:
 	case SCLK_BUS0_PLL_A:
 	case SCLK_BUS0_PLL_B:
@@ -86,14 +86,14 @@ static ulong exynos7420_topc_get_periph_rate(struct udevice *dev, int periph)
 }
 
 static struct clk_ops exynos7420_clk_topc_ops = {
-	.get_periph_rate	= exynos7420_topc_get_periph_rate,
+	.get_rate	= exynos7420_topc_get_rate,
 };
 
 static int exynos7420_clk_topc_probe(struct udevice *dev)
 {
 	struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev);
 	struct exynos7420_clk_cmu_topc *topc;
-	struct udevice *clk_dev;
+	struct clk in_clk;
 	unsigned long rate;
 	fdt_addr_t base;
 	int ret;
@@ -105,9 +105,9 @@ static int exynos7420_clk_topc_probe(struct udevice *dev)
 	topc = (struct exynos7420_clk_cmu_topc *)base;
 	priv->topc = topc;
 
-	ret = clk_get_by_index(dev, 0, &clk_dev);
+	ret = clk_get_by_index(dev, 0, &in_clk);
 	if (ret >= 0)
-		priv->fin_freq = clk_get_rate(clk_dev);
+		priv->fin_freq = clk_get_rate(&in_clk);
 
 	rate = pll145x_get_rate(&topc->bus0_pll_con[0], priv->fin_freq);
 	if (readl(&topc->mux_sel[1]) & (1 << 16))
@@ -122,12 +122,12 @@ static int exynos7420_clk_topc_probe(struct udevice *dev)
 	return 0;
 }
 
-static ulong exynos7420_top0_get_periph_rate(struct udevice *dev, int periph)
+static ulong exynos7420_top0_get_rate(struct clk *clk)
 {
-	struct exynos7420_clk_top0_priv *priv = dev_get_priv(dev);
+	struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev);
 	struct exynos7420_clk_cmu_top0 *top0 = priv->top0;
 
-	switch (periph) {
+	switch (clk->id) {
 	case CLK_SCLK_UART2:
 		return priv->mout_top0_bus0_pll_half /
 			DIVIDER(&top0->div_peric[3], 8, 0xf);
@@ -137,14 +137,14 @@ static ulong exynos7420_top0_get_periph_rate(struct udevice *dev, int periph)
 }
 
 static struct clk_ops exynos7420_clk_top0_ops = {
-	.get_periph_rate	= exynos7420_top0_get_periph_rate,
+	.get_rate	= exynos7420_top0_get_rate,
 };
 
 static int exynos7420_clk_top0_probe(struct udevice *dev)
 {
 	struct exynos7420_clk_top0_priv *priv;
 	struct exynos7420_clk_cmu_top0 *top0;
-	struct udevice *clk_dev;
+	struct clk in_clk;
 	fdt_addr_t base;
 	int ret;
 
@@ -159,10 +159,10 @@ static int exynos7420_clk_top0_probe(struct udevice *dev)
 	top0 = (struct exynos7420_clk_cmu_top0 *)base;
 	priv->top0 = top0;
 
-	ret = clk_get_by_index(dev, 1, &clk_dev);
+	ret = clk_get_by_index(dev, 1, &in_clk);
 	if (ret >= 0) {
 		priv->mout_top0_bus0_pll_half =
-			clk_get_periph_rate(clk_dev, ret);
+			clk_get_rate(&in_clk);
 		if (readl(&top0->mux_sel[1]) & (1 << 16))
 			priv->mout_top0_bus0_pll_half >>= 1;
 	}
@@ -170,18 +170,18 @@ static int exynos7420_clk_top0_probe(struct udevice *dev)
 	return 0;
 }
 
-static ulong exynos7420_peric1_get_periph_rate(struct udevice *dev, int periph)
+static ulong exynos7420_peric1_get_rate(struct clk *clk)
 {
-	struct udevice *clk_dev;
+	struct clk in_clk;
 	unsigned int ret;
 	unsigned long freq = 0;
 
-	switch (periph) {
+	switch (clk->id) {
 	case SCLK_UART2:
-		ret = clk_get_by_index(dev, 3, &clk_dev);
+		ret = clk_get_by_index(clk->dev, 3, &in_clk);
 		if (ret < 0)
 			return ret;
-		freq = clk_get_periph_rate(clk_dev, ret);
+		freq = clk_get_rate(&in_clk);
 		break;
 	}
 
@@ -189,7 +189,7 @@ static ulong exynos7420_peric1_get_periph_rate(struct udevice *dev, int periph)
 }
 
 static struct clk_ops exynos7420_clk_peric1_ops = {
-	.get_periph_rate	= exynos7420_peric1_get_periph_rate,
+	.get_rate	= exynos7420_peric1_get_rate,
 };
 
 static const struct udevice_id exynos7420_clk_topc_compat[] = {
diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c
index 25c163b395dd2058c3e43e89423c560cd182ab3c..2f5d4d839101440d946a29c8de9bdd0a9da57325 100644
--- a/drivers/clk/uniphier/clk-uniphier-core.c
+++ b/drivers/clk/uniphier/clk-uniphier-core.c
@@ -9,14 +9,14 @@
 #include <linux/bitops.h>
 #include <linux/io.h>
 #include <linux/sizes.h>
-#include <clk.h>
+#include <clk-uclass.h>
 #include <dm/device.h>
 
 #include "clk-uniphier.h"
 
-static int uniphier_clk_enable(struct udevice *dev, int index)
+static int uniphier_clk_enable(struct clk *clk)
 {
-	struct uniphier_clk_priv *priv = dev_get_priv(dev);
+	struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
 	struct uniphier_clk_gate_data *gate = priv->socdata->gate;
 	unsigned int nr_gate = priv->socdata->nr_gate;
 	void __iomem *reg;
@@ -24,7 +24,7 @@ static int uniphier_clk_enable(struct udevice *dev, int index)
 	int i;
 
 	for (i = 0; i < nr_gate; i++) {
-		if (gate[i].index != index)
+		if (gate[i].index != clk->id)
 			continue;
 
 		reg = priv->base + gate[i].reg;
@@ -41,9 +41,9 @@ static int uniphier_clk_enable(struct udevice *dev, int index)
 	return 0;
 }
 
-static ulong uniphier_clk_get_rate(struct udevice *dev, int index)
+static ulong uniphier_clk_get_rate(struct clk *clk)
 {
-	struct uniphier_clk_priv *priv = dev_get_priv(dev);
+	struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
 	struct uniphier_clk_rate_data *rdata = priv->socdata->rate;
 	unsigned int nr_rdata = priv->socdata->nr_rate;
 	void __iomem *reg;
@@ -52,7 +52,7 @@ static ulong uniphier_clk_get_rate(struct udevice *dev, int index)
 	int i;
 
 	for (i = 0; i < nr_rdata; i++) {
-		if (rdata[i].index != index)
+		if (rdata[i].index != clk->id)
 			continue;
 
 		if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED)
@@ -75,9 +75,9 @@ static ulong uniphier_clk_get_rate(struct udevice *dev, int index)
 	return matched_rate;
 }
 
-static ulong uniphier_clk_set_rate(struct udevice *dev, int index, ulong rate)
+static ulong uniphier_clk_set_rate(struct clk *clk, ulong rate)
 {
-	struct uniphier_clk_priv *priv = dev_get_priv(dev);
+	struct uniphier_clk_priv *priv = dev_get_priv(clk->dev);
 	struct uniphier_clk_rate_data *rdata = priv->socdata->rate;
 	unsigned int nr_rdata = priv->socdata->nr_rate;
 	void __iomem *reg;
@@ -87,7 +87,7 @@ static ulong uniphier_clk_set_rate(struct udevice *dev, int index, ulong rate)
 
 	/* first, decide the best match rate */
 	for (i = 0; i < nr_rdata; i++) {
-		if (rdata[i].index != index)
+		if (rdata[i].index != clk->id)
 			continue;
 
 		if (rdata[i].reg == UNIPHIER_CLK_RATE_IS_FIXED)
@@ -105,7 +105,7 @@ static ulong uniphier_clk_set_rate(struct udevice *dev, int index, ulong rate)
 
 	/* second, really set registers */
 	for (i = 0; i < nr_rdata; i++) {
-		if (rdata[i].index != index || rdata[i].rate != best_rate)
+		if (rdata[i].index != clk->id || rdata[i].rate != best_rate)
 			continue;
 
 		reg = priv->base + rdata[i].reg;
@@ -124,8 +124,8 @@ static ulong uniphier_clk_set_rate(struct udevice *dev, int index, ulong rate)
 
 const struct clk_ops uniphier_clk_ops = {
 	.enable = uniphier_clk_enable,
-	.get_periph_rate = uniphier_clk_get_rate,
-	.set_periph_rate = uniphier_clk_set_rate,
+	.get_rate = uniphier_clk_get_rate,
+	.set_rate = uniphier_clk_set_rate,
 };
 
 int uniphier_clk_probe(struct udevice *dev)
diff --git a/drivers/clk/uniphier/clk-uniphier-mio.c b/drivers/clk/uniphier/clk-uniphier-mio.c
index c6ecd119bd6f1496e0a8bb2520d1aba5895dce2e..2dd3fc074a731ad374a9dce0b7eb51b64bcdfef2 100644
--- a/drivers/clk/uniphier/clk-uniphier-mio.c
+++ b/drivers/clk/uniphier/clk-uniphier-mio.c
@@ -4,7 +4,6 @@
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
-#include <clk.h>
 #include <dm/device.h>
 
 #include "clk-uniphier.h"
diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c
index 3fceade61ec78a7ea69c07298a8ae09a695a1bc7..63b141838b6f14be71e0d38c708681d043ac6276 100644
--- a/drivers/i2c/rk_i2c.c
+++ b/drivers/i2c/rk_i2c.c
@@ -29,10 +29,9 @@ DECLARE_GLOBAL_DATA_PTR;
 #define RK_I2C_FIFO_SIZE	32
 
 struct rk_i2c {
-	struct udevice *clk;
+	struct clk clk;
 	struct i2c_regs *regs;
 	unsigned int speed;
-	int clk_id;
 };
 
 static inline void rk_i2c_get_div(int div, int *divh, int *divl)
@@ -55,7 +54,7 @@ static void rk_i2c_set_clk(struct rk_i2c *i2c, uint32_t scl_rate)
 	int div, divl, divh;
 
 	/* First get i2c rate from pclk */
-	i2c_rate = clk_get_periph_rate(i2c->clk, i2c->clk_id);
+	i2c_rate = clk_get_rate(&i2c->clk);
 
 	div = DIV_ROUND_UP(i2c_rate, scl_rate * 8) - 2;
 	divh = 0;
@@ -362,7 +361,6 @@ static int rockchip_i2c_ofdata_to_platdata(struct udevice *bus)
 		      bus->name, ret);
 		return ret;
 	}
-	priv->clk_id = ret;
 
 	return 0;
 }
diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c
index 1e2a29b825f33187454b256e1be6a7deea73cf0a..64bbf0cd2507fe57062017031c6419d4a756bec0 100644
--- a/drivers/mmc/msm_sdhci.c
+++ b/drivers/mmc/msm_sdhci.c
@@ -49,7 +49,8 @@ static int msm_sdc_clk_init(struct udevice *dev)
 					"clock-frequency", 400000);
 	uint clkd[2]; /* clk_id and clk_no */
 	int clk_offset;
-	struct udevice *clk;
+	struct udevice *clk_dev;
+	struct clk clk;
 	int ret;
 
 	ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clock", clkd,
@@ -61,11 +62,17 @@ static int msm_sdc_clk_init(struct udevice *dev)
 	if (clk_offset < 0)
 		return clk_offset;
 
-	ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk);
+	ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk_dev);
 	if (ret)
 		return ret;
 
-	ret = clk_set_periph_rate(clk, clkd[1], clk_rate);
+	clk.id = clkd[1];
+	ret = clk_request(clk_dev, &clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_set_rate(&clk, clk_rate);
+	clk_free(&clk);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c
index 750ab9f8c5f954d4f9d8b91099653e7db16ce8c1..d41d60ce3582767b96cb874d51d59448e4377484 100644
--- a/drivers/mmc/rockchip_dw_mmc.c
+++ b/drivers/mmc/rockchip_dw_mmc.c
@@ -24,8 +24,7 @@ struct rockchip_mmc_plat {
 };
 
 struct rockchip_dwmmc_priv {
-	struct udevice *clk;
-	int periph;
+	struct clk clk;
 	struct dwmci_host host;
 };
 
@@ -35,7 +34,7 @@ static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq)
 	struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
 	int ret;
 
-	ret = clk_set_periph_rate(priv->clk, priv->periph, freq);
+	ret = clk_set_rate(&priv->clk, freq);
 	if (ret < 0) {
 		debug("%s: err=%d\n", __func__, ret);
 		return ret;
@@ -81,7 +80,6 @@ static int rockchip_dwmmc_probe(struct udevice *dev)
 	ret = clk_get_by_index(dev, 0, &priv->clk);
 	if (ret < 0)
 		return ret;
-	priv->periph = ret;
 
 	if (fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
 				 "clock-freq-min-max", minmax, 2))
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c
index 4978cca76d898f712b6091409d7084a77817902d..152e9873970b3a80e6e14f44fba9c4598ad696c4 100644
--- a/drivers/mmc/uniphier-sd.c
+++ b/drivers/mmc/uniphier-sd.c
@@ -651,8 +651,7 @@ int uniphier_sd_probe(struct udevice *dev)
 	struct uniphier_sd_priv *priv = dev_get_priv(dev);
 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
 	fdt_addr_t base;
-	struct udevice *clk_dev;
-	int clk_id;
+	struct clk clk;
 	int ret;
 
 	priv->dev = dev;
@@ -665,20 +664,22 @@ int uniphier_sd_probe(struct udevice *dev)
 	if (!priv->regbase)
 		return -ENOMEM;
 
-	clk_id = clk_get_by_index(dev, 0, &clk_dev);
-	if (clk_id < 0) {
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret < 0) {
 		dev_err(dev, "failed to get host clock\n");
-		return clk_id;
+		return ret;
 	}
 
 	/* set to max rate */
-	priv->mclk = clk_set_periph_rate(clk_dev, clk_id, ULONG_MAX);
+	priv->mclk = clk_set_rate(&clk, ULONG_MAX);
 	if (IS_ERR_VALUE(priv->mclk)) {
 		dev_err(dev, "failed to set rate for host clock\n");
+		clk_free(&clk);
 		return priv->mclk;
 	}
 
-	ret = clk_enable(clk_dev, clk_id);
+	ret = clk_enable(&clk);
+	clk_free(&clk);
 	if (ret) {
 		dev_err(dev, "failed to enable host clock\n");
 		return ret;
diff --git a/drivers/serial/serial_msm.c b/drivers/serial/serial_msm.c
index 80fb89ea8b05ee872e3eae01864ca248c936515d..a7cab1346f228a5ff10c55c36338876a2ce28929 100644
--- a/drivers/serial/serial_msm.c
+++ b/drivers/serial/serial_msm.c
@@ -150,7 +150,8 @@ static int msm_uart_clk_init(struct udevice *dev)
 					"clock-frequency", 115200);
 	uint clkd[2]; /* clk_id and clk_no */
 	int clk_offset;
-	struct udevice *clk;
+	struct udevice *clk_dev;
+	struct clk clk;
 	int ret;
 
 	ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clock", clkd,
@@ -162,11 +163,17 @@ static int msm_uart_clk_init(struct udevice *dev)
 	if (clk_offset < 0)
 		return clk_offset;
 
-	ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk);
+	ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk_dev);
 	if (ret)
 		return ret;
 
-	ret = clk_set_periph_rate(clk, clkd[1], clk_rate);
+	clk.id = clkd[1];
+	ret = clk_request(clk_dev, &clk);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_set_rate(&clk, clk_rate);
+	clk_free(&clk);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/serial/serial_pic32.c b/drivers/serial/serial_pic32.c
index af9fbbf655447180b5904256b31bb1c6e956b1aa..c2141f0a083dfc8651d89c126eed36ccad018300 100644
--- a/drivers/serial/serial_pic32.c
+++ b/drivers/serial/serial_pic32.c
@@ -135,7 +135,7 @@ static int pic32_uart_getc(struct udevice *dev)
 static int pic32_uart_probe(struct udevice *dev)
 {
 	struct pic32_uart_priv *priv = dev_get_priv(dev);
-	struct udevice *clkdev;
+	struct clk clk;
 	fdt_addr_t addr;
 	fdt_size_t size;
 	int ret;
@@ -148,10 +148,11 @@ static int pic32_uart_probe(struct udevice *dev)
 	priv->base = ioremap(addr, size);
 
 	/* get clock rate */
-	ret = clk_get_by_index(dev, 0, &clkdev);
+	ret = clk_get_by_index(dev, 0, &clk);
 	if (ret < 0)
 		return ret;
-	priv->uartclk = clk_get_periph_rate(clkdev, ret);
+	priv->uartclk = clk_get_rate(&clk);
+	clk_free(&clk);
 
 	/* initialize serial */
 	return pic32_serial_init(priv->base, priv->uartclk, CONFIG_BAUDRATE);
diff --git a/drivers/serial/serial_s5p.c b/drivers/serial/serial_s5p.c
index cb55c5ab7196a38b783b1ff9934ee22999cceb2b..622547951e69cf57353dacab151b9b4ecbfb7c5d 100644
--- a/drivers/serial/serial_s5p.c
+++ b/drivers/serial/serial_s5p.c
@@ -94,13 +94,13 @@ int s5p_serial_setbrg(struct udevice *dev, int baudrate)
 	u32 uclk;
 
 #ifdef CONFIG_CLK_EXYNOS
-	struct udevice *clk_dev;
+	struct clk clk;
 	u32 ret;
 
-	ret = clk_get_by_index(dev, 1, &clk_dev);
+	ret = clk_get_by_index(dev, 1, &clk);
 	if (ret < 0)
 		return ret;
-	uclk = clk_get_periph_rate(clk_dev, ret);
+	uclk = clk_get_rate(&clk);
 #else
 	uclk = get_uart_clk(plat->port_id);
 #endif
diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c
index 9eff423acd12ad77f05c7e02d214b707b4bb672e..bc6dfd886297fe8efdd1c3a4f761bf08b7ba5792 100644
--- a/drivers/spi/rk_spi.c
+++ b/drivers/spi/rk_spi.c
@@ -35,8 +35,7 @@ struct rockchip_spi_platdata {
 
 struct rockchip_spi_priv {
 	struct rockchip_spi *regs;
-	struct udevice *clk;
-	int clk_id;
+	struct clk clk;
 	unsigned int max_freq;
 	unsigned int mode;
 	ulong last_transaction_us;	/* Time of last transaction end */
@@ -144,7 +143,6 @@ static int rockchip_spi_ofdata_to_platdata(struct udevice *bus)
 		      bus->name, ret);
 		return ret;
 	}
-	priv->clk_id = ret;
 
 	plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
 					 50000000);
@@ -175,7 +173,7 @@ static int rockchip_spi_probe(struct udevice *bus)
 	 * Use 99 MHz as our clock since it divides nicely into 594 MHz which
 	 * is the assumed speed for CLK_GENERAL.
 	 */
-	ret = clk_set_periph_rate(priv->clk, priv->clk_id, 99000000);
+	ret = clk_set_rate(&priv->clk, 99000000);
 	if (ret < 0) {
 		debug("%s: Failed to set clock: %d\n", __func__, ret);
 		return ret;
diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c
index 4444988e36fcace0566c8d99529cabf6b4969f1c..e0377ca1c9eefdc496eb7d3b267eea8aee602ab9 100644
--- a/drivers/usb/host/ehci-generic.c
+++ b/drivers/usb/host/ehci-generic.c
@@ -26,15 +26,15 @@ static int ehci_usb_probe(struct udevice *dev)
 	int i;
 
 	for (i = 0; ; i++) {
-		struct udevice *clk_dev;
-		int clk_id;
+		struct clk clk;
+		int ret;
 
-		clk_id = clk_get_by_index(dev, i, &clk_dev);
-		if (clk_id < 0)
+		ret = clk_get_by_index(dev, i, &clk);
+		if (ret < 0)
 			break;
-		if (clk_enable(clk_dev, clk_id))
-			printf("failed to enable clock (dev=%s, id=%d)\n",
-			       clk_dev->name, clk_id);
+		if (clk_enable(&clk))
+			printf("failed to enable clock %d\n", i);
+		clk_free(&clk);
 	}
 
 	hccr = map_physmem(dev_get_addr(dev), 0x100, MAP_NOCACHE);
diff --git a/drivers/video/rockchip/rk_edp.c b/drivers/video/rockchip/rk_edp.c
index 124ddf684b5131eb6fd8e3f84b12dd52ebd64374..7ece038c8fe45ece87d47b0f3400b7f7f9053a5b 100644
--- a/drivers/video/rockchip/rk_edp.c
+++ b/drivers/video/rockchip/rk_edp.c
@@ -1009,8 +1009,7 @@ int rk_edp_probe(struct udevice *dev)
 	struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
 	struct rk_edp_priv *priv = dev_get_priv(dev);
 	struct rk3288_edp *regs = priv->regs;
-	struct udevice *clk;
-	int periph;
+	struct clk clk;
 	int ret;
 
 	ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "rockchip,panel",
@@ -1026,8 +1025,8 @@ int rk_edp_probe(struct udevice *dev)
 
 	ret = clk_get_by_index(dev, 1, &clk);
 	if (ret >= 0) {
-		periph = ret;
-		ret = clk_set_periph_rate(clk, periph, 0);
+		ret = clk_set_rate(&clk, 0);
+		clk_free(&clk);
 	}
 	if (ret) {
 		debug("%s: Failed to set EDP clock: ret=%d\n", __func__, ret);
@@ -1036,8 +1035,8 @@ int rk_edp_probe(struct udevice *dev)
 
 	ret = clk_get_by_index(uc_plat->src_dev, 0, &clk);
 	if (ret >= 0) {
-		periph = ret;
-		ret = clk_set_periph_rate(clk, periph, 192000000);
+		ret = clk_set_rate(&clk, 192000000);
+		clk_free(&clk);
 	}
 	if (ret < 0) {
 		debug("%s: Failed to set clock in source device '%s': ret=%d\n",
diff --git a/drivers/video/rockchip/rk_hdmi.c b/drivers/video/rockchip/rk_hdmi.c
index 5fcb61ac2a3eddd6affc40c1db07bed6f49d467a..8dd2c870905ce9bb2516bc9639d87c56c8728c97 100644
--- a/drivers/video/rockchip/rk_hdmi.c
+++ b/drivers/video/rockchip/rk_hdmi.c
@@ -859,15 +859,15 @@ static int rk_hdmi_probe(struct udevice *dev)
 {
 	struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
 	struct rk_hdmi_priv *priv = dev_get_priv(dev);
-	struct udevice *reg, *clk;
-	int periph;
+	struct udevice *reg;
+	struct clk clk;
 	int ret;
 	int vop_id = uc_plat->source_id;
 
 	ret = clk_get_by_index(dev, 0, &clk);
 	if (ret >= 0) {
-		periph = ret;
-		ret = clk_set_periph_rate(clk, periph, 0);
+		ret = clk_set_rate(&clk, 0);
+		clk_free(&clk);
 	}
 	if (ret) {
 		debug("%s: Failed to set EDP clock: ret=%d\n", __func__, ret);
@@ -880,8 +880,8 @@ static int rk_hdmi_probe(struct udevice *dev)
 	 */
 	ret = clk_get_by_index(uc_plat->src_dev, 0, &clk);
 	if (ret >= 0) {
-		periph = ret;
-		ret = clk_set_periph_rate(clk, periph, 384000000);
+		ret = clk_set_rate(&clk, 384000000);
+		clk_free(&clk);
 	}
 	if (ret < 0) {
 		debug("%s: Failed to set clock in source device '%s': ret=%d\n",
diff --git a/drivers/video/rockchip/rk_lvds.c b/drivers/video/rockchip/rk_lvds.c
index dc10b866c9668f2f3c3a5e78a01f9d2d5d0820e9..fcbb4d63d25f3114d1929658ec4a088dfd96a060 100644
--- a/drivers/video/rockchip/rk_lvds.c
+++ b/drivers/video/rockchip/rk_lvds.c
@@ -5,7 +5,6 @@
  */
 
 #include <common.h>
-#include <clk.h>
 #include <display.h>
 #include <dm.h>
 #include <edid.h>
diff --git a/drivers/video/rockchip/rk_vop.c b/drivers/video/rockchip/rk_vop.c
index db09d9a41df76679aa2bcd1588231597fe7adbb6..cc26f1956d39a7569c70d7b3c49ed545c168ca55 100644
--- a/drivers/video/rockchip/rk_vop.c
+++ b/drivers/video/rockchip/rk_vop.c
@@ -195,7 +195,8 @@ int rk_display_init(struct udevice *dev, ulong fbbase,
 	struct udevice *disp;
 	int ret, remote, i, offset;
 	struct display_plat *disp_uc_plat;
-	struct udevice *clk;
+	struct udevice *dev_clk;
+	struct clk clk;
 
 	vop_id = fdtdec_get_int(blob, ep_node, "reg", -1);
 	debug("vop_id=%d\n", vop_id);
@@ -237,11 +238,13 @@ int rk_display_init(struct udevice *dev, ulong fbbase,
 		return ret;
 	}
 
-	ret = rkclk_get_clk(CLK_NEW, &clk);
+	ret = uclass_get_device(UCLASS_CLK, 0, &dev_clk);
 	if (!ret) {
-		ret = clk_set_periph_rate(clk, DCLK_VOP0 + remote_vop_id,
-					  timing.pixelclock.typ);
+		clk.id = DCLK_VOP0 + remote_vop_id;
+		ret = clk_request(dev_clk, &clk);
 	}
+	if (!ret)
+		ret = clk_set_rate(&clk, timing.pixelclock.typ);
 	if (ret) {
 		debug("%s: Failed to set pixel clock: ret=%d\n", __func__, ret);
 		return ret;
diff --git a/include/clk-uclass.h b/include/clk-uclass.h
new file mode 100644
index 0000000000000000000000000000000000000000..07c10654955cb7bdb75f4d2a923a4c1b5cdf3ff0
--- /dev/null
+++ b/include/clk-uclass.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef _CLK_UCLASS_H
+#define _CLK_UCLASS_H
+
+/* See clk.h for background documentation. */
+
+#include <clk.h>
+#include <fdtdec.h>
+
+/**
+ * struct clk_ops - The functions that a clock driver must implement.
+ */
+struct clk_ops {
+	/**
+	 * of_xlate - Translate a client's device-tree (OF) clock specifier.
+	 *
+	 * The clock core calls this function as the first step in implementing
+	 * a client's clk_get_by_*() call.
+	 *
+	 * If this function pointer is set to NULL, the clock core will use a
+	 * default implementation, which assumes #clock-cells = <1>, and that
+	 * the DT cell contains a simple integer clock ID.
+	 *
+	 * At present, the clock API solely supports device-tree. If this
+	 * changes, other xxx_xlate() functions may be added to support those
+	 * other mechanisms.
+	 *
+	 * @clock:	The clock struct to hold the translation result.
+	 * @args:	The clock specifier values from device tree.
+	 * @return 0 if OK, or a negative error code.
+	 */
+	int (*of_xlate)(struct clk *clock,
+			struct fdtdec_phandle_args *args);
+	/**
+	 * request - Request a translated clock.
+	 *
+	 * The clock core calls this function as the second step in
+	 * implementing a client's clk_get_by_*() call, following a successful
+	 * xxx_xlate() call, or as the only step in implementing a client's
+	 * clk_request() call.
+	 *
+	 * @clock:	The clock struct to request; this has been fille in by
+	 *		a previoux xxx_xlate() function call, or by the caller
+	 *		of clk_request().
+	 * @return 0 if OK, or a negative error code.
+	 */
+	int (*request)(struct clk *clock);
+	/**
+	 * free - Free a previously requested clock.
+	 *
+	 * This is the implementation of the client clk_free() API.
+	 *
+	 * @clock:	The clock to free.
+	 * @return 0 if OK, or a negative error code.
+	 */
+	int (*free)(struct clk *clock);
+	/**
+	 * get_rate() - Get current clock rate.
+	 *
+	 * @clk:	The clock to query.
+	 * @return clock rate in Hz, or -ve error code
+	 */
+	ulong (*get_rate)(struct clk *clk);
+	/**
+	 * set_rate() - Set current clock rate.
+	 *
+	 * @clk:	The clock to manipulate.
+	 * @rate:	New clock rate in Hz.
+	 * @return new rate, or -ve error code.
+	 */
+	ulong (*set_rate)(struct clk *clk, ulong rate);
+	/**
+	 * enable() - Enable a clock.
+	 *
+	 * @clk:	The clock to manipulate.
+	 * @return zero on success, or -ve error code.
+	 */
+	int (*enable)(struct clk *clk);
+	/**
+	 * disable() - Disable a clock.
+	 *
+	 * @clk:	The clock to manipulate.
+	 * @return zero on success, or -ve error code.
+	 */
+	int (*disable)(struct clk *clk);
+};
+
+#endif
diff --git a/include/clk.h b/include/clk.h
index ca20c3dd27c1c5de8a66c3049c6d042e2b05d433..2f31cf70e3de8dbf1cf3a5ee471911dede1b2ea1 100644
--- a/include/clk.h
+++ b/include/clk.h
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2015 Google, Inc
  * Written by Simon Glass <sjg@chromium.org>
+ * Copyright (c) 2016, NVIDIA CORPORATION.
  *
  * SPDX-License-Identifier:	GPL-2.0+
  */
@@ -8,125 +9,166 @@
 #ifndef _CLK_H_
 #define _CLK_H_
 
-#include <errno.h>
 #include <linux/types.h>
 
-struct udevice;
+/**
+ * A clock is a hardware signal that oscillates autonomously at a specific
+ * frequency and duty cycle. Most hardware modules require one or more clock
+ * signal to drive their operation. Clock signals are typically generated
+ * externally to the HW module consuming them, by an entity this API calls a
+ * clock provider. This API provides a standard means for drivers to enable and
+ * disable clocks, and to set the rate at which they oscillate.
+ *
+ * A driver that implements UCLASS_CLOCK is a clock provider. A provider will
+ * often implement multiple separate clocks, since the hardware it manages
+ * often has this capability. clock_uclass.h describes the interface which
+ * clock providers must implement.
+ *
+ * Clock consumers/clients are the HW modules driven by the clock signals. This
+ * header file describes the API used by drivers for those HW modules.
+ */
 
-int soc_clk_dump(void);
+struct udevice;
 
-struct clk_ops {
-	/**
-	 * get_rate() - Get current clock rate
-	 *
-	 * @dev:	Device to check (UCLASS_CLK)
-	 * @return clock rate in Hz, or -ve error code
-	 */
-	ulong (*get_rate)(struct udevice *dev);
-
-	/**
-	 * set_rate() - Set current clock rate
-	 *
-	 * @dev:	Device to adjust
-	 * @rate:	New clock rate in Hz
-	 * @return new rate, or -ve error code
-	 */
-	ulong (*set_rate)(struct udevice *dev, ulong rate);
-
-	/**
-	 * enable() - Enable the clock for a peripheral
-	 *
-	 * @dev:	clock provider
-	 * @periph:	Peripheral ID to enable
-	 * @return zero on success, or -ve error code
-	 */
-	int (*enable)(struct udevice *dev, int periph);
-
-	/**
-	 * get_periph_rate() - Get clock rate for a peripheral
-	 *
-	 * @dev:	Device to check (UCLASS_CLK)
-	 * @periph:	Peripheral ID to check
-	 * @return clock rate in Hz, or -ve error code
-	 */
-	ulong (*get_periph_rate)(struct udevice *dev, int periph);
-
-	/**
-	 * set_periph_rate() - Set current clock rate for a peripheral
-	 *
-	 * @dev:	Device to update (UCLASS_CLK)
-	 * @periph:	Peripheral ID to update
-	 * @return new clock rate in Hz, or -ve error code
+/**
+ * struct clk - A handle to (allowing control of) a single clock.
+ *
+ * Clients provide storage for clock handles. The content of the structure is
+ * managed solely by the clock API and clock drivers. A clock struct is
+ * initialized by "get"ing the clock struct. The clock struct is passed to all
+ * other clock APIs to identify which clock signal to operate upon.
+ *
+ * @dev: The device which implements the clock signal.
+ * @id: The clock signal ID within the provider.
+ *
+ * Currently, the clock API assumes that a single integer ID is enough to
+ * identify and configure any clock signal for any clock provider. If this
+ * assumption becomes invalid in the future, the struct could be expanded to
+ * either (a) add more fields to allow clock providers to store additional
+ * information, or (b) replace the id field with an opaque pointer, which the
+ * provider would dynamically allocated during its .of_xlate op, and process
+ * during is .request op. This may require the addition of an extra op to clean
+ * up the allocation.
+ */
+struct clk {
+	struct udevice *dev;
+	/*
+	 * Written by of_xlate. We assume a single id is enough for now. In the
+	 * future, we might add more fields here.
 	 */
-	ulong (*set_periph_rate)(struct udevice *dev, int periph, ulong rate);
+	unsigned long id;
 };
 
-#define clk_get_ops(dev)	((struct clk_ops *)(dev)->driver->ops)
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+/**
+ * clock_get_by_index - Get/request a clock by integer index.
+ *
+ * This looks up and requests a clock. The index is relative to the client
+ * device; each device is assumed to have n clocks associated with it somehow,
+ * and this function finds and requests one of them. The mapping of client
+ * device clock indices to provider clocks may be via device-tree properties,
+ * board-provided mapping tables, or some other mechanism.
+ *
+ * @dev:	The client device.
+ * @index:	The index of the clock to request, within the client's list of
+ *		clocks.
+ * @clock	A pointer to a clock struct to initialize.
+ * @return 0 if OK, or a negative error code.
+ */
+int clk_get_by_index(struct udevice *dev, int index, struct clk *clk);
 
 /**
- * clk_get_rate() - Get current clock rate
+ * clock_get_by_name - Get/request a clock by name.
  *
- * @dev:	Device to check (UCLASS_CLK)
- * @return clock rate in Hz, or -ve error code
+ * This looks up and requests a clock. The name is relative to the client
+ * device; each device is assumed to have n clocks associated with it somehow,
+ * and this function finds and requests one of them. The mapping of client
+ * device clock names to provider clocks may be via device-tree properties,
+ * board-provided mapping tables, or some other mechanism.
+ *
+ * @dev:	The client device.
+ * @name:	The name of the clock to request, within the client's list of
+ *		clocks.
+ * @clock:	A pointer to a clock struct to initialize.
+ * @return 0 if OK, or a negative error code.
  */
-ulong clk_get_rate(struct udevice *dev);
+int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk);
+#else
+static inline int clk_get_by_index(struct udevice *dev, int index,
+				   struct clk *clk)
+{
+	return -ENOSYS;
+}
+
+static int clk_get_by_name(struct udevice *dev, const char *name,
+			   struct clk *clk)
+{
+	return -ENOSYS;
+}
+#endif
 
 /**
- * clk_set_rate() - Set current clock rate
+ * clk_request - Request a clock by provider-specific ID.
  *
- * @dev:	Device to adjust
- * @rate:	New clock rate in Hz
- * @return new rate, or -ve error code
+ * This requests a clock using a provider-specific ID. Generally, this function
+ * should not be used, since clk_get_by_index/name() provide an interface that
+ * better separates clients from intimate knowledge of clock providers.
+ * However, this function may be useful in core SoC-specific code.
+ *
+ * @dev:	The clock provider device.
+ * @clock:	A pointer to a clock struct to initialize. The caller must
+ *		have already initialized any field in this struct which the
+ *		clock provider uses to identify the clock.
+ * @return 0 if OK, or a negative error code.
  */
-ulong clk_set_rate(struct udevice *dev, ulong rate);
+int clk_request(struct udevice *dev, struct clk *clk);
 
 /**
- * clk_enable() - Enable the clock for a peripheral
+ * clock_free - Free a previously requested clock.
  *
- * @dev:	clock provider
- * @periph:	Peripheral ID to enable
- * @return zero on success, or -ve error code
+ * @clock:	A clock struct that was previously successfully requested by
+ *		clk_request/get_by_*().
+ * @return 0 if OK, or a negative error code.
  */
-int clk_enable(struct udevice *dev, int periph);
+int clk_free(struct clk *clk);
 
 /**
- * clk_get_periph_rate() - Get current clock rate for a peripheral
+ * clk_get_rate() - Get current clock rate.
  *
- * @dev:	Device to check (UCLASS_CLK)
- * @return clock rate in Hz, -ve error code
+ * @clk:	A clock struct that was previously successfully requested by
+ *		clk_request/get_by_*().
+ * @return clock rate in Hz, or -ve error code.
  */
-ulong clk_get_periph_rate(struct udevice *dev, int periph);
+ulong clk_get_rate(struct clk *clk);
 
 /**
- * clk_set_periph_rate() - Set current clock rate for a peripheral
+ * clk_set_rate() - Set current clock rate.
  *
- * @dev:	Device to update (UCLASS_CLK)
- * @periph:	Peripheral ID to update
- * @return new clock rate in Hz, or -ve error code
+ * @clk:	A clock struct that was previously successfully requested by
+ *		clk_request/get_by_*().
+ * @rate:	New clock rate in Hz.
+ * @return new rate, or -ve error code.
  */
-ulong clk_set_periph_rate(struct udevice *dev, int periph, ulong rate);
+ulong clk_set_rate(struct clk *clk, ulong rate);
 
-#if CONFIG_IS_ENABLED(OF_CONTROL)
 /**
- * clk_get_by_index() - look up a clock referenced by a device
+ * clk_enable() - Enable (turn on) a clock.
  *
- * Parse a device's 'clocks' list, returning information on the indexed clock,
- * ensuring that it is activated.
+ * @clk:	A clock struct that was previously successfully requested by
+ *		clk_request/get_by_*().
+ * @return zero on success, or -ve error code.
+ */
+int clk_enable(struct clk *clk);
+
+/**
+ * clk_disable() - Disable (turn off) a clock.
  *
- * @dev:	Device containing the clock reference
- * @index:	Clock index to return (0 = first)
- * @clk_devp:	Returns clock device
- * @return:	Peripheral ID for the device to control. This is the first
- *		argument after the clock node phandle. If there is no arguemnt,
- *		returns 0. Return -ve error code on any error
+ * @clk:	A clock struct that was previously successfully requested by
+ *		clk_request/get_by_*().
+ * @return zero on success, or -ve error code.
  */
-int clk_get_by_index(struct udevice *dev, int index, struct udevice **clk_devp);
-#else
-static inline int clk_get_by_index(struct udevice *dev, int index,
-				   struct udevice **clk_devp)
-{
-	return -ENOSYS;
-}
-#endif
+int clk_disable(struct clk *clk);
 
-#endif /* _CLK_H_ */
+int soc_clk_dump(void);
+
+#endif
diff --git a/test/dm/clk.c b/test/dm/clk.c
index 9ff6d9510343fd46755476223f0d8d2d288cf8e3..712a1e674a5550c975f2e886f4ed5273cb074897 100644
--- a/test/dm/clk.c
+++ b/test/dm/clk.c
@@ -5,55 +5,99 @@
  */
 
 #include <common.h>
-#include <clk.h>
 #include <dm.h>
-#include <asm/test.h>
+#include <asm/clk.h>
 #include <dm/test.h>
 #include <linux/err.h>
 #include <test/ut.h>
 
-/* Test that we can find and adjust clocks */
-static int dm_test_clk_base(struct unit_test_state *uts)
+static int dm_test_clk(struct unit_test_state *uts)
 {
-	struct udevice *clk;
+	struct udevice *dev_fixed, *dev_clk, *dev_test;
 	ulong rate;
 
-	ut_assertok(uclass_get_device(UCLASS_CLK, 0, &clk));
-	rate = clk_get_rate(clk);
-	ut_asserteq(SANDBOX_CLK_RATE, rate);
-	ut_asserteq(-EINVAL, clk_set_rate(clk, 0));
-	ut_assertok(clk_set_rate(clk, rate * 2));
-	ut_asserteq(SANDBOX_CLK_RATE * 2, clk_get_rate(clk));
+	ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-fixed",
+					      &dev_fixed));
 
-	return 0;
-}
-DM_TEST(dm_test_clk_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+	ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox",
+					      &dev_clk));
+	ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+	ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+	ut_asserteq(0, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_SPI));
+	ut_asserteq(0, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_I2C));
 
-/* Test that peripheral clocks work as expected */
-static int dm_test_clk_periph(struct unit_test_state *uts)
-{
-	struct udevice *clk;
-	ulong rate;
+	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test",
+					      &dev_test));
+	ut_assertok(sandbox_clk_test_get(dev_test));
 
-	ut_assertok(uclass_get_device(UCLASS_CLK, 0, &clk));
-	rate = clk_set_periph_rate(clk, PERIPH_ID_COUNT, 123);
-	ut_asserteq(-EINVAL, rate);
-	ut_asserteq(1, IS_ERR_VALUE(rate));
+	ut_asserteq(1234,
+		    sandbox_clk_test_get_rate(dev_test,
+					      SANDBOX_CLK_TEST_ID_FIXED));
+	ut_asserteq(0, sandbox_clk_test_get_rate(dev_test,
+						 SANDBOX_CLK_TEST_ID_SPI));
+	ut_asserteq(0, sandbox_clk_test_get_rate(dev_test,
+						 SANDBOX_CLK_TEST_ID_I2C));
 
-	rate = clk_set_periph_rate(clk, PERIPH_ID_SPI, 123);
-	ut_asserteq(0, rate);
-	ut_asserteq(123, clk_get_periph_rate(clk, PERIPH_ID_SPI));
+	rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_FIXED,
+					 12345);
+	ut_assert(IS_ERR_VALUE(rate));
+	rate = sandbox_clk_test_get_rate(dev_test, SANDBOX_CLK_TEST_ID_FIXED);
+	ut_asserteq(1234, rate);
 
-	rate = clk_set_periph_rate(clk, PERIPH_ID_SPI, 1234);
-	ut_asserteq(123, rate);
+	ut_asserteq(0, sandbox_clk_test_set_rate(dev_test,
+						 SANDBOX_CLK_TEST_ID_SPI,
+						 1000));
+	ut_asserteq(0, sandbox_clk_test_set_rate(dev_test,
+						 SANDBOX_CLK_TEST_ID_I2C,
+						 2000));
 
-	rate = clk_set_periph_rate(clk, PERIPH_ID_I2C, 567);
+	ut_asserteq(1000, sandbox_clk_test_get_rate(dev_test,
+						    SANDBOX_CLK_TEST_ID_SPI));
+	ut_asserteq(2000, sandbox_clk_test_get_rate(dev_test,
+						    SANDBOX_CLK_TEST_ID_I2C));
 
-	rate = clk_set_periph_rate(clk, PERIPH_ID_SPI, 1234);
-	ut_asserteq(1234, rate);
+	ut_asserteq(1000, sandbox_clk_test_set_rate(dev_test,
+						    SANDBOX_CLK_TEST_ID_SPI,
+						    10000));
+	ut_asserteq(2000, sandbox_clk_test_set_rate(dev_test,
+						    SANDBOX_CLK_TEST_ID_I2C,
+						    20000));
+
+	rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_SPI, 0);
+	ut_assert(IS_ERR_VALUE(rate));
+	rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0);
+	ut_assert(IS_ERR_VALUE(rate));
+
+	ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test,
+						     SANDBOX_CLK_TEST_ID_SPI));
+	ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test,
+						     SANDBOX_CLK_TEST_ID_I2C));
+
+	ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+	ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+	ut_asserteq(10000, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_SPI));
+	ut_asserteq(20000, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_I2C));
+
+	ut_assertok(sandbox_clk_test_enable(dev_test, SANDBOX_CLK_TEST_ID_SPI));
+	ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+	ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+	ut_assertok(sandbox_clk_test_enable(dev_test, SANDBOX_CLK_TEST_ID_I2C));
+	ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+	ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+	ut_assertok(sandbox_clk_test_disable(dev_test,
+					     SANDBOX_CLK_TEST_ID_SPI));
+	ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+	ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+	ut_assertok(sandbox_clk_test_disable(dev_test,
+					     SANDBOX_CLK_TEST_ID_I2C));
+	ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+	ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
 
-	ut_asserteq(567, clk_get_periph_rate(clk, PERIPH_ID_I2C));
+	ut_assertok(sandbox_clk_test_free(dev_test));
 
 	return 0;
 }
-DM_TEST(dm_test_clk_periph, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+DM_TEST(dm_test_clk, DM_TESTF_SCAN_FDT);