diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index c0c465e..721bb2c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -999,6 +999,8 @@ config ARCH_VT8500
 	select ARCH_HAS_CPUFREQ
 	select GENERIC_CLOCKEVENTS
 	select ARCH_REQUIRE_GPIOLIB
+	select CLKDEV_LOOKUP
+	select COMMON_CLK
 	help
 	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
 
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
index f9fbeb2..ec84b2a 100644
--- a/arch/arm/mach-vt8500/bv07.c
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -24,6 +24,7 @@
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <mach/restart.h>
+#include <mach/clk.h>
 
 #include "devices.h"
 
@@ -67,6 +68,9 @@ void __init bv07_init(void)
 	vt8500_set_resources();
 	platform_add_devices(devices, ARRAY_SIZE(devices));
 	vt8500_gpio_init();
+
+	/* this call can be moved to timer_init once devicetree is ready */
+	vt8500_clk_init();
 }
 
 MACHINE_START(BV07, "Benign BV07 Mini Netbook")
diff --git a/arch/arm/mach-vt8500/include/mach/clk.h b/arch/arm/mach-vt8500/include/mach/clk.h
new file mode 100644
index 0000000..d4cc9c4
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/clk.h
@@ -0,0 +1,9 @@
+#ifndef __WMT_CLK
+#define __WMT_CLK
+
+void __init vt8500_clk_init(void);
+void __init wm8505_clk_init(void);
+/* WM8650 support not added yet, but included for completeness */
+void __init wm8650_clk_init(void);
+
+#endif
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
index db19886..d6049bb 100644
--- a/arch/arm/mach-vt8500/wm8505_7in.c
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -24,6 +24,7 @@
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <mach/restart.h>
+#include <mach/clk.h>
 
 #include "devices.h"
 
@@ -66,6 +67,9 @@ void __init wm8505_7in_init(void)
 	wm8505_set_resources();
 	platform_add_devices(devices, ARRAY_SIZE(devices));
 	vt8500_gpio_init();
+
+	/* this call can be moved to timer_init once devicetree is ready */
+	wm8505_clk_init();
 }
 
 MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 5869ea3..2d37177 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_ARCH_SOCFPGA)	+= socfpga/
 obj-$(CONFIG_PLAT_SPEAR)	+= spear/
 obj-$(CONFIG_ARCH_U300)		+= clk-u300.o
 obj-$(CONFIG_ARCH_INTEGRATOR)	+= versatile/
+obj-$(CONFIG_ARCH_VT8500)	+= vt8500/
 
 # Chip specific
 obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
diff --git a/drivers/clk/vt8500/Makefile b/drivers/clk/vt8500/Makefile
new file mode 100644
index 0000000..319f65b
--- /dev/null
+++ b/drivers/clk/vt8500/Makefile
@@ -0,0 +1,12 @@
+#
+# Wondermedia Clock specific Makefile
+#
+obj-y	+= clk-gatediv.o
+
+obj-$(CONFIG_VTWM_VERSION_VT8500)	+= vt8500_clk.o wm85xx-pll.o
+obj-$(CONFIG_VTWM_VERSION_WM8505)	+= wm8505_clk.o wm85xx-pll.o
+
+# WM8650 board support missing - has different clock functions so
+# added for completeness. Will be needed for devicetree support.
+obj-$(CONFIG_VTWM_VERSION_WM8650)	+= wm8650_clk.o wm86xx-pll.o
+
diff --git a/drivers/clk/vt8500/clk-gatediv.c b/drivers/clk/vt8500/clk-gatediv.c
new file mode 100644
index 0000000..fa6f2be
--- /dev/null
+++ b/drivers/clk/vt8500/clk-gatediv.c
@@ -0,0 +1,152 @@
+/* drivers/clk/wmt/clk-pll.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+
+#include "clk.h"
+
+#define to_clk_gatediv(_hw) container_of(_hw, struct clk_gatediv, hw)
+
+static long clk_gatediv_round_rate(struct clk_hw *hw, unsigned long drate,
+				unsigned long *prate)
+{
+	u32 div;
+	unsigned long parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+
+	if ((drate == 0) || (parent_rate == 0))
+		return 0;
+
+	if (parent_rate % drate)
+		div = (parent_rate / drate) + 1;
+	else
+		div = parent_rate / drate;
+
+	return parent_rate / div;
+}
+
+static unsigned long clk_gatediv_recalc_rate(struct clk_hw *hw, unsigned long
+		parent_rate)
+{
+	struct clk_gatediv *gatediv = to_clk_gatediv(hw);
+
+	u32 div = (readl(gatediv->div_reg) & 0x1F);
+
+	if (div == 0)
+		div = 32;
+
+	return parent_rate / div;
+}
+
+static int clk_gatediv_set_rate(struct clk_hw *hw, unsigned long drate,
+				unsigned long prate)
+{
+	u32 div;
+	struct clk_gatediv *gatediv = to_clk_gatediv(hw);
+	unsigned long parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+
+	if ((drate == 0) || (drate > prate) || (parent_rate == 0))
+		return -EINVAL;
+
+	if (parent_rate % drate)
+		div = (parent_rate / drate) + 1;
+	else
+		div = parent_rate / drate;
+
+	if (div > 32)
+		return -EINVAL;
+
+	if (div == 32)
+		div = 0;
+
+	writel(div, gatediv->div_reg);
+
+	return 0;
+}
+
+static int clk_gatediv_enable(struct clk_hw *hw)
+{
+	struct clk_gatediv *gatediv = to_clk_gatediv(hw);
+	u32 tmp = readl(gatediv->en_reg);
+
+	tmp |= BIT(gatediv->en_bit);
+	writel(tmp, gatediv->en_reg);
+
+	return 0;
+}
+
+static void clk_gatediv_disable(struct clk_hw *hw)
+{
+	struct clk_gatediv *gatediv = to_clk_gatediv(hw);
+	u32 tmp = readl(gatediv->en_reg);
+
+	tmp &= ~BIT(gatediv->en_bit);
+	writel(tmp, gatediv->en_reg);
+}
+
+static int clk_gatediv_is_enabled(struct clk_hw *hw)
+{
+	struct clk_gatediv *gatediv = to_clk_gatediv(hw);
+	u32 tmp = readl(gatediv->en_reg);
+	tmp &= BIT(gatediv->en_bit);
+
+	return tmp ? 1 : 0;
+}
+
+const struct clk_ops clk_gatediv_ops = {
+	.enable = clk_gatediv_enable,
+	.disable = clk_gatediv_disable,
+	.is_enabled = clk_gatediv_is_enabled,
+	.recalc_rate = clk_gatediv_recalc_rate,
+	.round_rate = clk_gatediv_round_rate,
+	.set_rate = clk_gatediv_set_rate,
+};
+
+struct clk *clk_register_gatediv(const char *name, const char *parent_name,
+		unsigned long flags, void __iomem *div_reg,
+		void __iomem *en_reg, u8 en_bit, spinlock_t *lock)
+{
+	struct clk_init_data init;
+	struct clk_gatediv *gatediv;
+	struct clk *clk;
+
+	/* allocate the gatediv */
+	gatediv = kzalloc(sizeof(struct clk_gatediv), GFP_KERNEL);
+	if (!gatediv) {
+		pr_err("%s: could not allocate gatediv clk\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &clk_gatediv_ops;
+	init.flags = flags;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	gatediv->div_reg = div_reg;
+	gatediv->en_reg = en_reg;
+	gatediv->en_bit = en_bit;
+	gatediv->lock = lock;
+	gatediv->hw.init = &init;
+
+	clk = clk_register(NULL, &gatediv->hw);
+
+	if (IS_ERR(clk))
+		kfree(gatediv);
+
+	return clk;
+}
diff --git a/drivers/clk/vt8500/clk.h b/drivers/clk/vt8500/clk.h
new file mode 100644
index 0000000..12f87a2
--- /dev/null
+++ b/drivers/clk/vt8500/clk.h
@@ -0,0 +1,36 @@
+/* drivers/clk/wmt/clk.h
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+struct clk_pll {
+	struct clk_hw hw;
+	void __iomem	*reg;
+	spinlock_t	*lock;
+};
+
+struct clk_gatediv {
+	struct clk_hw hw;
+	void __iomem	*en_reg;
+	u8		en_bit;
+	void __iomem	*div_reg;
+	spinlock_t	*lock;
+};
+
+struct clk *clk_register_pll_wm85xx(const char *name, const char *parent_name,
+		unsigned long flags, void __iomem *reg, spinlock_t *lock);
+struct clk *clk_register_pll_wm86xx(const char *name, const char *parent_name,
+		unsigned long flags, void __iomem *reg, spinlock_t *lock);
+struct clk *clk_register_gatediv(const char *name, const char *parent_name,
+		unsigned long flags, void __iomem *div_reg,
+		void __iomem *en_reg, u8 en_bit, spinlock_t *lock);
diff --git a/drivers/clk/vt8500/vt8500_clk.c b/drivers/clk/vt8500/vt8500_clk.c
new file mode 100644
index 0000000..0da5d92
--- /dev/null
+++ b/drivers/clk/vt8500/vt8500_clk.c
@@ -0,0 +1,108 @@
+/* drivers/clk/wmt/wm8505-clk.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+#include <mach/vt8500_regs.h>
+
+#include "clk.h"
+
+static DEFINE_SPINLOCK(_lock);
+
+#define CLK_PLLA_MULTIPLIER	0x200
+#define CLK_PLLB_MULTIPLIER	0x204
+#define CLK_PLLC_MULTIPLIER	0x208
+#define CLK_PLLD_MULTIPLIER	0x20C
+
+#define CLK_EN_LOW		0x250
+#define CLK_EN_HIGH		0x254
+
+/* PLL A Divisor Regisiters */
+#define CLK_ARM_DIVISOR		0x300
+#define CLK_AHB_DIVISOR		0x304
+#define CLK_APB_DIVISOR		0x350
+
+/* PLL B Divisor Registers */
+#define CLK_SF_DIVISOR		0x314
+#define CLK_SDMMC0_DIVISOR	0x328
+#define CLK_NAND_DIVISOR	0x330
+#define CLK_SPI0_DIVISOR	0x33C
+#define CLK_PWM_DIVISOR		0x348
+
+/* PLL C Divisor Registers */
+#define CLK_DDR_DIVISOR		0x310
+
+void __init vt8500_clk_init(void)
+{
+	struct clk *clk;
+	void __iomem *pmc_base = ioremap(VT8500_PMC_BASE, 0x380);
+
+	clk = clk_register_fixed_rate(NULL, "ref", NULL, CLK_IS_ROOT, 25000000);
+	clk_register_clkdev(clk, "ref", NULL);
+
+	clk = clk_register_pll_wm85xx("pll-a", "ref", 0,
+					pmc_base+CLK_PLLA_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-a", NULL);
+
+	clk = clk_register_pll_wm85xx("pll-b", "ref", 0,
+					pmc_base+CLK_PLLB_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-b", NULL);
+
+	clk = clk_register_pll_wm85xx("pll-c", "ref", 0,
+					pmc_base+CLK_PLLC_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-c", NULL);
+
+	/* PLL A derived */
+	clk = clk_register_divider(NULL, "arm", "pll-a", 0,
+				pmc_base+CLK_ARM_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "arm", NULL);
+
+	clk = clk_register_divider(NULL, "ahb", "pll-a", 0,
+				pmc_base+CLK_AHB_DIVISOR, 0, 3, 0, &_lock);
+	clk_register_clkdev(clk, "ahb", NULL);
+
+	clk = clk_register_divider(NULL, "apb", "pll-a", 0,
+				pmc_base+CLK_APB_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "apb", NULL);
+
+	/* PLL B derived */
+	clk = clk_register_gatediv("sf", "pll-b", 0,
+		pmc_base+CLK_SF_DIVISOR, pmc_base+CLK_EN_HIGH, 23, &_lock);
+	clk_register_clkdev(clk, "sf", NULL);
+
+	clk = clk_register_gatediv("sdmmc", "pll-b", 0,
+		pmc_base+CLK_SDMMC0_DIVISOR, pmc_base+CLK_EN_HIGH, 18, &_lock);
+	clk_register_clkdev(clk, "sdmmc", NULL);
+
+	clk = clk_register_gatediv("nand", "pll-b", 0,
+		pmc_base+CLK_NAND_DIVISOR, pmc_base+CLK_EN_HIGH, 16, &_lock);
+	clk_register_clkdev(clk, "nand", NULL);
+
+	clk = clk_register_gatediv("spi0", "pll-b", 0,
+		pmc_base+CLK_SPI0_DIVISOR, pmc_base+CLK_EN_LOW, 12, &_lock);
+	clk_register_clkdev(clk, "spi0", NULL);
+
+	clk = clk_register_gatediv("pwm", "pll-b", 0,
+		pmc_base+CLK_PWM_DIVISOR, pmc_base+CLK_EN_LOW, 10, &_lock);
+	clk_register_clkdev(clk, "pwm", NULL);
+
+	/* PLL C derived */
+	clk = clk_register_divider(NULL, "ddr", "pll-c", 0,
+				pmc_base+CLK_DDR_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "ddr", NULL);
+}
diff --git a/drivers/clk/vt8500/wm8505_clk.c b/drivers/clk/vt8500/wm8505_clk.c
new file mode 100644
index 0000000..8211721
--- /dev/null
+++ b/drivers/clk/vt8500/wm8505_clk.c
@@ -0,0 +1,122 @@
+/* drivers/clk/wmt/wm8505-clk.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+#include <mach/wm8505_regs.h>
+
+#include "clk.h"
+
+static DEFINE_SPINLOCK(_lock);
+
+#define CLK_PLLA_MULTIPLIER	0x200
+#define CLK_PLLB_MULTIPLIER	0x204
+#define CLK_PLLC_MULTIPLIER	0x208
+#define CLK_PLLD_MULTIPLIER	0x20C
+
+#define CLK_EN_LOW		0x250
+#define CLK_EN_HIGH		0x254
+
+/* PLL A Divisor Regisiters */
+#define CLK_ARM_DIVISOR		0x300
+#define CLK_AHB_DIVISOR		0x304
+#define CLK_APB_DIVISOR		0x350
+
+/* PLL B Divisor Registers */
+#define CLK_SF_DIVISOR		0x314
+#define CLK_SDMMC0_DIVISOR	0x328
+#define CLK_NAND_DIVISOR	0x330
+#define CLK_SPI0_DIVISOR	0x33C
+#define CLK_PWM_DIVISOR		0x348
+#define CLK_I2C0_DIVISOR	0x36C
+#define CLK_I2C1_DIVISOR	0x370
+
+/* PLL C Divisor Registers */
+#define CLK_DDR_DIVISOR		0x310
+
+void __init wm8505_clk_init(void)
+{
+	struct clk *clk;
+	void __iomem *pmc_base = ioremap(WM8505_PMC_BASE, 0x380);
+
+	clk = clk_register_fixed_rate(NULL, "ref", NULL, CLK_IS_ROOT, 25000000);
+	clk_register_clkdev(clk, "ref", NULL);
+
+	clk = clk_register_pll_wm85xx("pll-a", "ref", 0,
+					pmc_base+CLK_PLLA_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-a", NULL);
+
+	clk = clk_register_pll_wm85xx("pll-b", "ref", 0,
+					pmc_base+CLK_PLLB_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-b", NULL);
+
+	clk = clk_register_pll_wm85xx("pll-c", "ref", 0,
+					pmc_base+CLK_PLLC_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-c", NULL);
+
+	clk = clk_register_pll_wm85xx("pll-d", "ref", 0,
+					pmc_base+CLK_PLLD_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-d", NULL);
+
+	/* PLL A derived */
+	clk = clk_register_divider(NULL, "arm", "pll-a", 0,
+				pmc_base+CLK_ARM_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "arm", NULL);
+
+	clk = clk_register_divider(NULL, "ahb", "pll-a", 0,
+				pmc_base+CLK_AHB_DIVISOR, 0, 3, 0, &_lock);
+	clk_register_clkdev(clk, "ahb", NULL);
+
+	clk = clk_register_divider(NULL, "apb", "pll-a", 0,
+				pmc_base+CLK_APB_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "apb", NULL);
+
+	/* PLL B derived */
+	clk = clk_register_gatediv("sf", "pll-b", 0, pmc_base+CLK_SF_DIVISOR,
+			pmc_base+CLK_EN_HIGH, 23, &_lock);
+	clk_register_clkdev(clk, "sf", NULL);
+
+	clk = clk_register_gatediv("sdmmc", "pll-b", 0,
+		pmc_base+CLK_SDMMC0_DIVISOR, pmc_base+CLK_EN_HIGH, 18, &_lock);
+	clk_register_clkdev(clk, "sdmmc", NULL);
+
+	clk = clk_register_gatediv("nand", "pll-b", 0,
+		pmc_base+CLK_NAND_DIVISOR, pmc_base+CLK_EN_HIGH, 16, &_lock);
+	clk_register_clkdev(clk, "nand", NULL);
+
+	clk = clk_register_gatediv("spi0", "pll-b", 0,
+		pmc_base+CLK_SPI0_DIVISOR, pmc_base+CLK_EN_LOW, 12, &_lock);
+	clk_register_clkdev(clk, "spi0", NULL);
+
+	clk = clk_register_gatediv("pwm", "pll-b", 0,
+		pmc_base+CLK_PWM_DIVISOR, pmc_base+CLK_EN_LOW, 10, &_lock);
+	clk_register_clkdev(clk, "pwm", NULL);
+
+	clk = clk_register_gatediv("i2c0", "pll-b", 0,
+		pmc_base+CLK_I2C0_DIVISOR, pmc_base+CLK_EN_LOW, 5, &_lock);
+	clk_register_clkdev(clk, "i2c0", NULL);
+
+	clk = clk_register_gatediv("i2c1", "pll-b", 0,
+		pmc_base+CLK_I2C1_DIVISOR, pmc_base+CLK_EN_LOW, 9, &_lock);
+	clk_register_clkdev(clk, "i2c1", NULL);
+
+	/* PLL C derived */
+	clk = clk_register_divider(NULL, "ddr", "pll-c", 0,
+				pmc_base+CLK_DDR_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "ddr", NULL);
+}
diff --git a/drivers/clk/vt8500/wm85xx-pll.c b/drivers/clk/vt8500/wm85xx-pll.c
new file mode 100644
index 0000000..890e98f
--- /dev/null
+++ b/drivers/clk/vt8500/wm85xx-pll.c
@@ -0,0 +1,103 @@
+/* drivers/clk/wmt/wm85xx-pll.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+
+#include "clk.h"
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long drate,
+				unsigned long *prate)
+{
+	u32 mul;
+	unsigned long parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+
+	mul = (drate / parent_rate) & 0xFE;
+
+	if (mul > 62)
+		return -EINVAL;
+
+	return parent_rate * mul;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long
+		parent_rate)
+{
+	struct clk_pll *pll = to_clk_pll(hw);
+
+	u32 pll_val = readl(pll->reg);
+	u32 divisor = (pll_val & 0x100) ? 2 : 1;
+
+	return (parent_rate * ((pll_val & 0x1F) << 1)) / divisor;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate,
+				unsigned long prate)
+{
+	u32 mul;
+	struct clk_pll *pll = to_clk_pll(hw);
+	unsigned long parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+
+	mul = (drate / parent_rate) & 0xFE;
+
+	if (mul > 62)
+		return -EINVAL;
+
+	writel((mul >> 1), pll->reg);
+
+	return 0;
+}
+
+static const struct clk_ops clk_pll_ops = {
+	.recalc_rate = clk_pll_recalc_rate,
+	.round_rate = clk_pll_round_rate,
+	.set_rate = clk_pll_set_rate,
+};
+
+struct clk *clk_register_pll_wm85xx(const char *name, const char *parent_name,
+		unsigned long flags, void __iomem *reg, spinlock_t *lock)
+{
+	struct clk_init_data init;
+	struct clk_pll *pll;
+	struct clk *clk;
+
+	/* allocate the pll */
+	pll = kzalloc(sizeof(struct clk_pll), GFP_KERNEL);
+	if (!pll) {
+		pr_err("%s: could not allocate pll clk\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &clk_pll_ops;
+	init.flags = flags;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	pll->reg = reg;
+	pll->lock = lock;
+	pll->hw.init = &init;
+
+	clk = clk_register(NULL, &pll->hw);
+
+	if (IS_ERR(clk))
+		kfree(pll);
+
+	return clk;
+}
diff --git a/drivers/clk/vt8500/wm8650_clk.c b/drivers/clk/vt8500/wm8650_clk.c
new file mode 100644
index 0000000..eb8e242
--- /dev/null
+++ b/drivers/clk/vt8500/wm8650_clk.c
@@ -0,0 +1,127 @@
+/* drivers/clk/wmt/wm8650-clk.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+#include <mach/wm8650_regs.h>
+
+#include "clk.h"
+
+static DEFINE_SPINLOCK(_lock);
+
+#define CLK_PLLA_MULTIPLIER	0x200
+#define CLK_PLLB_MULTIPLIER	0x204
+#define CLK_PLLC_MULTIPLIER	0x208
+#define CLK_PLLD_MULTIPLIER	0x20C
+
+#define CLK_EN_LOW		0x250
+#define CLK_EN_HIGH		0x254
+
+/* PLL A Divisor Regisiters */
+#define CLK_ARM_DIVISOR		0x300
+#define CLK_AHB_DIVISOR		0x304
+#define CLK_APB_DIVISOR		0x320
+
+/* PLL B Divisor Registers */
+#define CLK_SF_DIVISOR		0x314
+#define CLK_SDMMC0_DIVISOR	0x328
+#define CLK_NAND_DIVISOR	0x330
+#define CLK_SPI0_DIVISOR	0x33C
+#define CLK_SDMMC1_DIVISOR	0x340
+#define CLK_PWM_DIVISOR		0x348
+#define CLK_I2C0_DIVISOR	0x36C
+#define CLK_I2C1_DIVISOR	0x378
+
+/* PLL C Divisor Registers */
+#define CLK_DDR_DIVISOR		0x310
+
+void __init wm8650_clk_init(void)
+{
+	struct clk *clk;
+	void __iomem *pmc_base = ioremap(WM8650_PMC_BASE, 0x380);
+
+	clk = clk_register_fixed_rate(NULL, "ref", NULL, CLK_IS_ROOT, 25000000);
+	clk_register_clkdev(clk, "ref", NULL);
+
+	clk = clk_register_pll_wm86xx("pll-a", "ref", 0,
+					pmc_base+CLK_PLLA_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-a", NULL);
+
+	clk = clk_register_pll_wm86xx("pll-b", "ref", 0,
+					pmc_base+CLK_PLLB_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-b", NULL);
+
+	clk = clk_register_pll_wm86xx("pll-c", "ref", 0,
+					pmc_base+CLK_PLLC_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-c", NULL);
+
+	clk = clk_register_pll_wm86xx("pll-d", "ref", 0,
+					pmc_base+CLK_PLLD_MULTIPLIER, &_lock);
+	clk_register_clkdev(clk, "pll-d", NULL);
+
+	/* PLL A derived */
+	clk = clk_register_divider(NULL, "arm", "pll-a", 0,
+				pmc_base+CLK_ARM_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "arm", NULL);
+
+	clk = clk_register_divider(NULL, "ahb", "pll-a", 0,
+				pmc_base+CLK_AHB_DIVISOR, 0, 3, 0, &_lock);
+	clk_register_clkdev(clk, "ahb", NULL);
+
+	clk = clk_register_divider(NULL, "apb", "pll-a", 0,
+				pmc_base+CLK_APB_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "apb", NULL);
+
+	/* PLL B derived */
+	clk = clk_register_gatediv("sf", "pll-b", 0,
+		pmc_base+CLK_SF_DIVISOR, pmc_base+CLK_EN_HIGH, 23, &_lock);
+	clk_register_clkdev(clk, "sf", NULL);
+
+	clk = clk_register_gatediv("sdmmc", "pll-b", 0,
+		pmc_base+CLK_SDMMC0_DIVISOR, pmc_base+CLK_EN_HIGH, 18, &_lock);
+	clk_register_clkdev(clk, "sdmmc", NULL);
+
+	clk = clk_register_gatediv("nand", "pll-b", 0,
+		pmc_base+CLK_NAND_DIVISOR, pmc_base+CLK_EN_HIGH, 16, &_lock);
+	clk_register_clkdev(clk, "nand", NULL);
+
+	clk = clk_register_gatediv("spi0", "pll-b", 0,
+		pmc_base+CLK_SPI0_DIVISOR, pmc_base+CLK_EN_LOW, 12, &_lock);
+	clk_register_clkdev(clk, "spi0", NULL);
+
+	clk = clk_register_gatediv("sdmmc1", "pll-b", 0,
+		pmc_base+CLK_SDMMC1_DIVISOR, pmc_base+CLK_EN_LOW, 4, &_lock);
+	clk_register_clkdev(clk, "sdmmc1", NULL);
+
+	clk = clk_register_gatediv("pwm", "pll-b", 0,
+		pmc_base+CLK_PWM_DIVISOR, pmc_base+CLK_EN_LOW, 10, &_lock);
+	clk_register_clkdev(clk, "pwm", NULL);
+
+	clk = clk_register_gatediv("i2c0", "pll-b", 0,
+		pmc_base+CLK_I2C0_DIVISOR, pmc_base+CLK_EN_LOW, 5, &_lock);
+	clk_register_clkdev(clk, "i2c0", NULL);
+
+	clk = clk_register_gatediv("i2c1", "pll-b", 0,
+		pmc_base+CLK_I2C1_DIVISOR, pmc_base+CLK_EN_LOW, 9, &_lock);
+	clk_register_clkdev(clk, "i2c1", NULL);
+
+	/* PLL D derived */
+	clk = clk_register_divider(NULL, "ddr", "pll-d", 0,
+		pmc_base+CLK_DDR_DIVISOR, 0, 5, 0, &_lock);
+	clk_register_clkdev(clk, "ddr", NULL);
+}
diff --git a/drivers/clk/vt8500/wm86xx-pll.c b/drivers/clk/vt8500/wm86xx-pll.c
new file mode 100644
index 0000000..a23f0eb
--- /dev/null
+++ b/drivers/clk/vt8500/wm86xx-pll.c
@@ -0,0 +1,135 @@
+/* drivers/clk/wmt/wm86xx-pll.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk-provider.h>
+
+#include "clk.h"
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+static long clk_pll_round_rate(struct clk_hw *hw, unsigned long drate,
+				unsigned long *prate)
+{
+	u32 mul;
+	u32 div;
+	unsigned long parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+
+	/* Multiplier only gives a range of (OSC..OSC*1023) */
+	mul = (drate / parent_rate);
+	if (mul < 0x400) {
+		/* No divider required */
+		return parent_rate * mul;
+	}
+
+	/* Simple divider gives a range of (OSC/7..OSC*511.5) */
+	for (div = 2; div < 8; div++) {
+		mul = (drate / div) / parent_rate;
+		if (mul < 0x400)
+			break;
+	}
+	if (mul < 0x400) {
+		/* Simple divider required */
+		return (parent_rate * mul) / div;
+	}
+
+	/*
+	 * TODO: Add support for extended divider.
+	 * Could be used to give better resolution for matches
+	 */
+
+	return -EINVAL;
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, unsigned long
+		parent_rate)
+{
+	struct clk_pll *pll = to_clk_pll(hw);
+
+	u32 pll_val = readl(pll->reg);
+	u32 divisor = ((pll_val >> 10) & 7) * (1 << ((pll_val >> 13) & 3));
+
+	return (parent_rate * (pll_val & 0x3FF)) / divisor;
+}
+
+static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate,
+				unsigned long prate)
+{
+	u32 mul;
+	u32 div;
+	struct clk_pll *pll = to_clk_pll(hw);
+	unsigned long parent_rate = __clk_get_rate(__clk_get_parent(hw->clk));
+
+	/* Multiplier only gives a range of (OSC..OSC*1023) */
+	mul = (drate / parent_rate);
+	if (mul < 0x400) {
+		/* No divider required */
+		div = 1;
+		goto set_clock;
+	}
+
+	/* Simple divider gives a range of (OSC/7..OSC*511.5) */
+	for (div = 2; div < 8; div++) {
+		mul = (drate / div) / parent_rate;
+		if (mul < 0x400)
+			break;
+	}
+	if (mul > 0x3FF)
+		return -EINVAL;
+
+set_clock:
+	writel((div << 10)|mul, pll->reg);
+	return 0;
+}
+
+static const struct clk_ops clk_pll_ops = {
+	.recalc_rate = clk_pll_recalc_rate,
+	.round_rate = clk_pll_round_rate,
+	.set_rate = clk_pll_set_rate,
+};
+
+struct clk *clk_register_pll_wm86xx(const char *name, const char *parent_name,
+		unsigned long flags, void __iomem *reg, spinlock_t *lock)
+{
+	struct clk_init_data init;
+	struct clk_pll *pll;
+	struct clk *clk;
+
+	/* allocate the pll */
+	pll = kzalloc(sizeof(struct clk_pll), GFP_KERNEL);
+	if (!pll) {
+		pr_err("%s: could not allocate pll clk\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &clk_pll_ops;
+	init.flags = flags;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	pll->reg = reg;
+	pll->lock = lock;
+	pll->hw.init = &init;
+
+	clk = clk_register(NULL, &pll->hw);
+
+	if (IS_ERR(clk))
+		kfree(pll);
+
+	return clk;
+}
