lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Sat, 25 Jun 2016 18:37:21 +0800
From:	Wan Zongshun <vw@...mu.org>
To:	linux-arm-kernel@...ts.infradead.org,
	Russell King <linux@...linux.org.uk>,
	devicetree@...r.kernel.org
Cc:	Arnd Bergmann <arnd@...db.de>,
	Daniel Lezcano <daniel.lezcano@...aro.org>,
	Thomas Gleixner <tglx@...utronix.de>,
	linux-kernel@...r.kernel.org, Wan Zongshun <vw@...mu.org>,
	Wan Zongshun <mcuos.com@...il.com>
Subject: [PATCH 5/6] clk: add Clock driver for nuc970

Signed-off-by: Wan Zongshun <mcuos.com@...il.com>
---
 drivers/clk/Makefile            |   1 +
 drivers/clk/nuc900/Makefile     |   6 +
 drivers/clk/nuc900/clk-apll.c   | 168 ++++++++
 drivers/clk/nuc900/clk-ccf.h    |  53 +++
 drivers/clk/nuc900/clk-nuc970.c | 920 ++++++++++++++++++++++++++++++++++++++++
 drivers/clk/nuc900/clk-upll.c   |  83 ++++
 6 files changed, 1231 insertions(+)
 create mode 100644 drivers/clk/nuc900/Makefile
 create mode 100644 drivers/clk/nuc900/clk-apll.c
 create mode 100644 drivers/clk/nuc900/clk-ccf.h
 create mode 100644 drivers/clk/nuc900/clk-nuc970.c
 create mode 100644 drivers/clk/nuc900/clk-upll.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index dcc5e69..042377d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -88,3 +88,4 @@ obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
 obj-$(CONFIG_H8300)		+= h8300/
 obj-$(CONFIG_ARC_PLAT_AXS10X)		+= axs10x/
+obj-$(CONFIG_ARCH_W90X900)		+= nuc900/
diff --git a/drivers/clk/nuc900/Makefile b/drivers/clk/nuc900/Makefile
new file mode 100644
index 0000000..a6785ab
--- /dev/null
+++ b/drivers/clk/nuc900/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for nuvoton specific clk
+#
+
+obj-$(CONFIG_SOC_NUC970) += clk-apll.o clk-upll.o clk-nuc970.o
+
diff --git a/drivers/clk/nuc900/clk-apll.c b/drivers/clk/nuc900/clk-apll.c
new file mode 100644
index 0000000..4e1c202
--- /dev/null
+++ b/drivers/clk/nuc900/clk-apll.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuos.com@...il.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+
+#include "clk-ccf.h"
+
+struct clk_apll {
+	struct clk_hw	hw;
+	void __iomem	*base;
+};
+
+#define to_clk_apll(clk) (container_of(clk, struct clk_apll, clk))
+
+static int clk_apll_set_rate(struct clk_hw *hw, unsigned long rate,
+			     unsigned long parent_rate)
+{
+	struct clk_apll *pll = to_clk_apll(hw);
+	unsigned long reg;
+
+	reg = readl(pll->base) & ~0x0FFFFFFF;
+
+	switch (rate) {
+	/*usbh*/
+	case 96000000:
+		reg |= 0x8027;
+		break;
+	/*i2s*/
+	case 98400000:
+		reg |= 0x8028;
+		break;
+	/*i2s*/
+	case 169500000:
+		reg |= 0x21f0;
+		break;
+	/*system default, 264MHz*/
+	case 264000000:
+		reg |= 0x15;
+		break;
+	case 300000000:
+		reg |= 0x18;
+		break;
+	default:
+		reg |= 0x15;
+		break;
+	}
+
+	writel(reg, pll->base);
+
+	return 0;
+}
+
+static unsigned long clk_apll_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	struct clk_apll *pll = to_clk_apll(hw);
+	unsigned long reg = readl(pll->base) & 0x0FFFFFFF;
+	unsigned long rate;
+
+	if (parent_rate != 12000000)
+		return 0;
+
+	switch (reg) {
+	/*system default, 264MHz*/
+	case 0x15:
+		rate = 264000000;
+		break;
+	case 0x18:
+		rate = 300000000;
+		break;
+	/*usbh*/
+	case 0x8027:
+		rate = 96000000;
+		break;
+	/*i2s*/
+	case 0x8028:
+		rate = 98400000;
+		break;
+	/*i2s*/
+	case 0x21f0:
+		rate = 169500000;
+		break;
+	default:
+		rate = 264000000;
+		break;
+	}
+
+	return rate;
+}
+
+static long clk_apll_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
+{
+	return rate;
+}
+
+static int clk_apll_enable(struct clk_hw *hw)
+{
+	struct clk_apll *pll = to_clk_apll(hw);
+	unsigned long val;
+
+	val = __raw_readl(pll->base);
+	val &= ~0x10000000;
+	val |= 0x40000000;
+	__raw_writel(val, pll->base);
+
+	return 0;
+}
+
+static void clk_apll_disable(struct clk_hw *hw)
+{
+	struct clk_apll *pll = to_clk_apll(hw);
+	unsigned long val;
+
+	val = __raw_readl(pll->base);
+	val |= 0x10000000;
+	val &= ~0x40000000;
+	__raw_writel(val, pll->base);
+}
+
+static struct clk_ops clk_apll_ops = {
+	.recalc_rate = clk_apll_recalc_rate,
+	.enable = clk_apll_enable,
+	.disable = clk_apll_disable,
+	.set_rate = clk_apll_set_rate,
+	.round_rate = clk_apll_round_rate,
+};
+
+struct clk *nuc970_clk_apll(const char *name, const char *parent,
+			    void __iomem *base)
+{
+	struct clk_apll *pll;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	pll = kmalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	pll->base = base;
+
+	init.name = name;
+	init.ops = &clk_apll_ops;
+	init.flags = 0;
+	init.parent_names = &parent;
+	init.num_parents = 1;
+
+	pll->hw.init = &init;
+
+	clk = clk_register(NULL, &pll->hw);
+	if (IS_ERR(clk))
+		kfree(pll);
+
+	return clk;
+}
diff --git a/drivers/clk/nuc900/clk-ccf.h b/drivers/clk/nuc900/clk-ccf.h
new file mode 100644
index 0000000..2808933
--- /dev/null
+++ b/drivers/clk/nuc900/clk-ccf.h
@@ -0,0 +1,53 @@
+#ifndef __MACH_NUC970_CLK_H
+#define __MACH_NUC970_CLK_H
+
+#include <linux/spinlock.h>
+#include <linux/clk-provider.h>
+
+static spinlock_t nuc970_lock;
+
+extern struct clk *nuc970_clk_apll(const char *name, const char *parent,
+				   void __iomem *base);
+extern struct clk *nuc970_clk_upll(const char *name, const char *parent,
+				   void __iomem *base);
+
+static inline struct clk *nuc970_clk_fixed(const char *name, int rate)
+{
+	return clk_register_fixed_rate(NULL, name, NULL, 0, rate);
+}
+
+static inline struct clk *nuc970_clk_mux(const char *name, void __iomem *reg,
+					 u8 shift, u8 width,
+					 const char * const *parents,
+					 int num_parents)
+{
+	return clk_register_mux(NULL, name, parents, num_parents, 0, reg, shift,
+				width, 0, &nuc970_lock);
+}
+
+static inline struct clk *nuc970_clk_divider(const char *name,
+					     const char *parent,
+					     void __iomem *reg, u8 shift,
+					     u8 width)
+{
+	return clk_register_divider(NULL, name, parent, 0,
+				    reg, shift, width, 0, &nuc970_lock);
+}
+
+static inline struct clk *nuc970_clk_fixed_factor(const char *name,
+						  const char *parent,
+						  unsigned int mult,
+						  unsigned int div)
+{
+	return clk_register_fixed_factor(NULL, name, parent,
+					 CLK_SET_RATE_PARENT, mult, div);
+}
+
+static inline struct clk *nuc970_clk_gate(const char *name, const char *parent,
+					  void __iomem *reg, u8 shift)
+{
+	return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+				 shift, 0, &nuc970_lock);
+}
+
+#endif
diff --git a/drivers/clk/nuc900/clk-nuc970.c b/drivers/clk/nuc900/clk-nuc970.c
new file mode 100644
index 0000000..a4faf0d
--- /dev/null
+++ b/drivers/clk/nuc900/clk-nuc970.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuos.com@...il.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <dt-bindings/clock/nuc970-clock.h>
+#include "clk-ccf.h"
+
+/* Clock Control Registers  */
+static void __iomem *clkctrl;
+#define CLK_BA clkctrl
+
+#define REG_CLK_PMCON	(CLK_BA + 0x000)
+#define REG_CLK_HCLKEN	(CLK_BA + 0x010)
+#define REG_CLK_PCLKEN0	(CLK_BA + 0x018)
+#define REG_CLK_PCLKEN1	(CLK_BA + 0x01C)
+#define REG_CLK_DIV0	(CLK_BA + 0x020)
+#define REG_CLK_DIV1	(CLK_BA + 0x024)
+#define REG_CLK_DIV2	(CLK_BA + 0x028)
+#define REG_CLK_DIV3	(CLK_BA + 0x02C)
+#define REG_CLK_DIV4	(CLK_BA + 0x030)
+#define REG_CLK_DIV5	(CLK_BA + 0x034)
+#define REG_CLK_DIV6	(CLK_BA + 0x038)
+#define REG_CLK_DIV7	(CLK_BA + 0x03C)
+#define REG_CLK_DIV8	(CLK_BA + 0x040)
+#define REG_CLK_DIV9	(CLK_BA + 0x044)
+#define REG_CLK_APLLCON	(CLK_BA + 0x060)
+#define REG_CLK_UPLLCON	(CLK_BA + 0x064)
+
+static const char *const sys_sel_clks[] = { "xin",
+					    "dummy",
+					    "apll",
+					    "upll" };
+
+static const char *const lcd_sel_clks[] = { "xin",
+					    "dummy",
+					    "lcd_aplldiv",
+					    "lcd_uplldiv" };
+
+static const char *const audio_sel_clks[] = { "xin",
+					      "dummy",
+					      "audio_aplldiv",
+					      "audio_uplldiv" };
+
+static const char *const usb_sel_clks[] = { "xin",
+					    "dummy",
+					    "usb_aplldiv",
+					    "usb_uplldiv" };
+
+static const char *const adc_sel_clks[] = { "xin",
+					    "dummy",
+					    "adc_aplldiv",
+					    "adc_uplldiv" };
+
+static const char *const cap_sel_clks[] = { "xin",
+					    "dummy",
+					    "cap_aplldiv",
+					    "cap_uplldiv" };
+
+static const char *const sdh_sel_clks[] = { "xin",
+					    "dummy",
+					    "sdh_aplldiv",
+					    "sdh_uplldiv" };
+
+static const char *const emmc_sel_clks[] = { "xin",
+					     "dummy",
+					     "emmc_aplldiv",
+					     "emmc_uplldiv" };
+
+static const char *const uart0_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart0_aplldiv",
+					      "uart0_uplldiv" };
+
+static const char *const uart1_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart1_aplldiv",
+					      "uart1_uplldiv" };
+
+static const char *const uart2_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart2_aplldiv",
+					      "uart2_uplldiv" };
+
+static const char *const uart3_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart3_aplldiv",
+					      "uart3_uplldiv" };
+
+static const char *const uart4_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart4_aplldiv",
+					      "uart4_uplldiv" };
+
+static const char *const uart5_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart5_aplldiv",
+					      "uart5_uplldiv" };
+
+static const char *const uart6_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart6_aplldiv",
+					      "uart6_uplldiv" };
+
+static const char *const uart7_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart7_aplldiv",
+					      "uart7_uplldiv" };
+
+static const char *const uart8_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart8_aplldiv",
+					      "uart8_uplldiv" };
+
+static const char *const uart9_sel_clks[] = { "xin",
+					      "dummy",
+					      "uart9_aplldiv",
+					      "uart9_uplldiv" };
+
+static const char *const uart10_sel_clks[] = { "xin",
+					       "dummy",
+					       "uart10_aplldiv",
+					       "uart10_uplldiv" };
+
+static const char *const system_sel_clks[] = { "xin",
+					       "dummy",
+					       "system_aplldiv",
+					       "system_uplldiv" };
+
+static const char *const gpio_sel_clks[] = { "xin", "xin32k"};
+static const char *const kpi_sel_clks[] = { "xin", "xin32k"};
+static const char *const etimer_sel_clks[] = { "xin",
+					       "pclk_div",
+					      "pclk4096_div",
+					      "xin32k" };
+
+static const char *const wwdt_sel_clks[] = { "xin",
+					     "xin128_div",
+					     "pclk4096_div",
+					     "xin32k" };
+
+static struct clk *clk[NUC970_CLK_MAX];
+static struct clk_onecell_data clk_data;
+
+static void __init nuc970_clocks_init(struct device_node *np)
+{
+	int i;
+
+	clkctrl = of_iomap(np, 0);
+	WARN_ON(!clkctrl);
+
+	/* source */
+	clk[XIN]	= nuc970_clk_fixed("xin", 12000000);
+	clk[XIN32K]	= nuc970_clk_fixed("xin32k", 32768);
+	clk[APLL]	= nuc970_clk_apll("apll", "xin", REG_CLK_APLLCON);
+	clk[UPLL]	= nuc970_clk_upll("upll", "xin", REG_CLK_UPLLCON);
+	clk[XIN128_DIV]	= nuc970_clk_fixed_factor("xin128_div", "xin", 1, 128);
+	clk[SYS_MUX]	= nuc970_clk_mux("sys_mux", REG_CLK_DIV0, 3, 2,
+					 sys_sel_clks,
+					 ARRAY_SIZE(sys_sel_clks));
+	clk[SYS_DIV]	= nuc970_clk_divider("sys_div", "sys_mux",
+					     REG_CLK_DIV0, 0, 2);
+	clk[DDR_GATE]	= nuc970_clk_gate("ddr_gate", "sys_div",
+					  REG_CLK_HCLKEN, 10);
+	/* CPU */
+	clk[CPU_DIV]  = nuc970_clk_divider("cpu_div", "sys_div",
+					   REG_CLK_DIV0, 16, 1);
+	clk[CPU_GATE] = nuc970_clk_gate("cpu_gate", "cpu_div",
+					REG_CLK_HCLKEN, 0);
+	/*HCLK1 & PCLK*/
+	clk[HCLK1_DIV]	= nuc970_clk_fixed_factor("hclk1_div", "cpu_div", 1, 2);
+	clk[GDMA_GATE]	= nuc970_clk_gate("gdma_hclk_gate", "hclk1_div",
+					  REG_CLK_HCLKEN, 12);
+	clk[EBI_GATE]	= nuc970_clk_gate("ebi_hclk_gate", "hclk1_div",
+					  REG_CLK_HCLKEN, 9);
+	clk[TIC_GATE]	= nuc970_clk_gate("tic_hclk_gate", "hclk1_div",
+					  REG_CLK_HCLKEN, 7);
+	/* HCLK & HCLK234 */
+	clk[HCLKN_DIV]		= nuc970_clk_fixed_factor("hclkn_div",
+							  "sys_div", 1, 2);
+	clk[DRAM_GATE]		= nuc970_clk_gate("dram_gate", "hclkn_div",
+						  REG_CLK_HCLKEN, 10);
+	clk[HCLK_GATE]		= nuc970_clk_gate("hclk_gate", "hclkn_div",
+						  REG_CLK_HCLKEN, 1);
+	clk[SRAM_GATE]		= nuc970_clk_gate("sram_gate", "hclk_gate",
+						  REG_CLK_HCLKEN, 8);
+	clk[HCLK234_DIV]	= nuc970_clk_divider("hclk234_div", "hclkn_div",
+						     REG_CLK_DIV0, 20, 4);
+	/* HCLK3 */
+	clk[USBH_GATE]		= nuc970_clk_gate("usbh_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 18);
+	clk[USBD_GATE]		= nuc970_clk_gate("usbd_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 19);
+	clk[FMI_GATE]		= nuc970_clk_gate("fmi_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 20);
+	clk[NAND_GATE]		= nuc970_clk_gate("nand_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 21);
+	clk[EMMC_GATE]		= nuc970_clk_gate("emmc_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 22);
+	clk[CRYPTO_GATE]	= nuc970_clk_gate("crypto_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 23);
+	clk[EMAC1_GATE]		= nuc970_clk_gate("emac1_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 17);
+	clk[EMAC1_ECLK_DIV]	= nuc970_clk_divider("emac1_eclk_div",
+						     "hclk234_div",
+						     REG_CLK_DIV8, 0, 8);
+	clk[EMAC1_ECLK_GATE]	= nuc970_clk_gate("emac1_eclk_gate",
+						  "emac1_eclk_div",
+						  REG_CLK_HCLKEN, 17);
+	clk[JPEG_GATE]		= nuc970_clk_gate("jpeg_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 29);
+	clk[JPEG_ECLK_DIV]	= nuc970_clk_divider("jpeg_eclk_div",
+						     "hclk234_div",
+						     REG_CLK_DIV3, 28, 3);
+	clk[JPEG_ECLK_GATE]	= nuc970_clk_gate("jpeg_eclk_gate",
+						  "jpeg_eclk_div",
+						  REG_CLK_HCLKEN, 29);
+	clk[GE2D_GATE]		= nuc970_clk_gate("ge2d_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 28);
+	clk[GE2D_ECLK_DIV]	= nuc970_clk_divider("ge2d_eclk_div",
+						     "hclk234_div",
+						     REG_CLK_DIV2, 28, 2);
+	clk[GE2D_ECLK_GATE]	= nuc970_clk_gate("ge2d_eclk_gate",
+						  "ge2d_eclk_div",
+						  REG_CLK_HCLKEN, 28);
+	/* HCLK4 */
+	clk[SDH_GATE]		= nuc970_clk_gate("sdh_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 30);
+	clk[AUDIO_GATE]		= nuc970_clk_gate("audio_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 24);
+	clk[LCD_GATE]		= nuc970_clk_gate("lcd_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 25);
+	clk[CAP_GATE]		= nuc970_clk_gate("cap_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 26);
+	clk[SENSOR_GATE]	= nuc970_clk_gate("sensor_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 27);
+	clk[EMAC0_GATE]		= nuc970_clk_gate("emac0_hclk_gate",
+						  "hclk234_div",
+						  REG_CLK_HCLKEN, 16);
+	clk[EMAC0_ECLK_DIV]	= nuc970_clk_divider("emac0_eclk_div",
+						     "hclk234_div",
+						     REG_CLK_DIV8, 0, 8);
+	clk[EMAC0_ECLK_GATE]	= nuc970_clk_gate("emac0_eclk_gate",
+						  "emac0_eclk_div",
+						  REG_CLK_HCLKEN, 16);
+	/* ECLK */
+	/* USB */
+	clk[USB_APLLDIV]	= nuc970_clk_divider("usb_aplldiv", "apll",
+						     REG_CLK_DIV2, 0, 3);
+	clk[USB_UPLLDIV]	= nuc970_clk_divider("usb_uplldiv", "upll",
+						     REG_CLK_DIV2, 0, 3);
+	clk[USB_ECLK_MUX]	= nuc970_clk_mux("usb_eclk_mux", REG_CLK_DIV2,
+						 3, 2, usb_sel_clks,
+						 ARRAY_SIZE(usb_sel_clks));
+	clk[USB_ECLK_DIV]	= nuc970_clk_divider("usb_eclk_div",
+						     "usb_eclk_mux",
+						     REG_CLK_DIV2, 8, 4);
+	clk[USB_ECLK_GATE]	= nuc970_clk_gate("usb_eclk_gate",
+						  "usb_eclk_div",
+						  REG_CLK_HCLKEN, 18);
+	/* SDH */
+	clk[SDH_APLLDIV]	= nuc970_clk_divider("sdh_aplldiv", "apll",
+						     REG_CLK_DIV9, 0, 3);
+	clk[SDH_UPLLDIV]	= nuc970_clk_divider("sdh_uplldiv", "upll",
+						     REG_CLK_DIV9, 0, 3);
+	clk[SDH_ECLK_MUX]	= nuc970_clk_mux("sdh_eclk_mux", REG_CLK_DIV9,
+						 3, 2, sdh_sel_clks,
+						 ARRAY_SIZE(sdh_sel_clks));
+	clk[SDH_ECLK_DIV]	= nuc970_clk_divider("sdh_eclk_div",
+						     "sdh_eclk_mux",
+						     REG_CLK_DIV9, 8, 8);
+	clk[SDH_ECLK_GATE]	= nuc970_clk_gate("sdh_eclk_gate",
+						  "sdh_eclk_div",
+						  REG_CLK_HCLKEN, 30);
+	/* EMMC */
+	clk[EMMC_APLLDIV]	= nuc970_clk_divider("emmc_aplldiv", "apll",
+						     REG_CLK_DIV3, 0, 3);
+	clk[EMMC_UPLLDIV]	= nuc970_clk_divider("emmc_uplldiv", "upll",
+						     REG_CLK_DIV3, 0, 3);
+	clk[EMMC_ECLK_MUX]	= nuc970_clk_mux("emmc_eclk_mux", REG_CLK_DIV3,
+						 3, 2, emmc_sel_clks,
+						 ARRAY_SIZE(emmc_sel_clks));
+	clk[EMMC_ECLK_DIV]	= nuc970_clk_divider("emmc_eclk_div",
+						     "emmc_eclk_mux",
+						     REG_CLK_DIV3, 8, 8);
+	clk[EMMC_ECLK_GATE]	= nuc970_clk_gate("emmc_eclk_gate",
+						  "emmc_eclk_div",
+						  REG_CLK_HCLKEN, 22);
+	/* ADC */
+	clk[ADC_APLLDIV]	= nuc970_clk_divider("adc_aplldiv", "apll",
+						     REG_CLK_DIV7, 16, 3);
+	clk[ADC_UPLLDIV]	= nuc970_clk_divider("adc_uplldiv", "upll",
+						     REG_CLK_DIV7, 16, 3);
+	clk[ADC_ECLK_MUX]	= nuc970_clk_mux("adc_eclk_mux", REG_CLK_DIV7,
+						 19, 2, adc_sel_clks,
+						 ARRAY_SIZE(adc_sel_clks));
+	clk[ADC_ECLK_DIV]	= nuc970_clk_divider("adc_eclk_div",
+						     "adc_eclk_mux",
+						     REG_CLK_DIV7, 24, 8);
+	clk[ADC_ECLK_GATE]	= nuc970_clk_gate("adc_eclk_gate",
+						  "adc_eclk_div",
+						  REG_CLK_PCLKEN1, 24);
+	/* LCD */
+	clk[LCD_APLLDIV]	= nuc970_clk_divider("lcd_aplldiv", "apll",
+						     REG_CLK_DIV1, 0, 3);
+	clk[LCD_UPLLDIV]	= nuc970_clk_divider("lcd_uplldiv", "upll",
+					     REG_CLK_DIV1, 0, 3);
+	clk[LCD_ECLK_MUX]	= nuc970_clk_mux("lcd_eclk_mux", REG_CLK_DIV1,
+						 3, 2, lcd_sel_clks,
+						 ARRAY_SIZE(lcd_sel_clks));
+	clk[LCD_ECLK_DIV]	= nuc970_clk_divider("lcd_eclk_div",
+						     "lcd_eclk_mux",
+						     REG_CLK_DIV1, 8, 8);
+	clk[LCD_ECLK_GATE]	= nuc970_clk_gate("lcd_eclk_gate",
+						  "lcd_eclk_div",
+						  REG_CLK_HCLKEN, 25);
+	/* AUDIO */
+	clk[AUDIO_APLLDIV]	= nuc970_clk_divider("audio_aplldiv", "apll",
+						     REG_CLK_DIV1, 16, 3);
+	clk[AUDIO_UPLLDIV]	= nuc970_clk_divider("audio_uplldiv", "upll",
+						     REG_CLK_DIV1, 16, 3);
+	clk[AUDIO_ECLK_MUX]	= nuc970_clk_mux("audio_eclk_mux", REG_CLK_DIV1,
+						 19, 2, audio_sel_clks,
+						 ARRAY_SIZE(audio_sel_clks));
+	clk[AUDIO_ECLK_DIV]	= nuc970_clk_divider("audio_eclk_div",
+						     "audio_eclk_mux",
+						     REG_CLK_DIV1, 24, 8);
+	clk[AUDIO_ECLK_GATE]	= nuc970_clk_gate("audio_eclk_gate",
+						  "audio_eclk_div",
+						  REG_CLK_HCLKEN, 24);
+	/* CAP */
+	clk[CAP_APLLDIV]	= nuc970_clk_divider("cap_aplldiv", "apll",
+						     REG_CLK_DIV3, 16, 3);
+	clk[CAP_UPLLDIV]	= nuc970_clk_divider("cap_uplldiv", "upll",
+						     REG_CLK_DIV3, 16, 3);
+	clk[CAP_ECLK_MUX]	= nuc970_clk_mux("cap_eclk_mux", REG_CLK_DIV3,
+						 19, 2, cap_sel_clks,
+						 ARRAY_SIZE(cap_sel_clks));
+	clk[CAP_ECLK_DIV]	= nuc970_clk_divider("cap_eclk_div",
+						     "cap_eclk_mux",
+						     REG_CLK_DIV3, 24, 4);
+	clk[CAP_ECLK_GATE]	= nuc970_clk_gate("cap_eclk_gate",
+						  "cap_eclk_div",
+						  REG_CLK_HCLKEN, 26);
+	/* UART0 */
+	clk[UART0_APLLDIV]	= nuc970_clk_divider("uart0_aplldiv",
+						     "apll", REG_CLK_DIV4,
+						     0, 3);
+	clk[UART0_UPLLDIV]	= nuc970_clk_divider("uart0_uplldiv", "upll",
+						     REG_CLK_DIV4, 0, 3);
+	clk[UART0_ECLK_MUX]	= nuc970_clk_mux("uart0_eclk_mux", REG_CLK_DIV4,
+						 3, 2, uart0_sel_clks,
+						 ARRAY_SIZE(uart0_sel_clks));
+	clk[UART0_ECLK_DIV]	= nuc970_clk_divider("uart0_eclk_div",
+						     "uart0_eclk_mux",
+						     REG_CLK_DIV4, 5, 3);
+	clk[UART0_ECLK_GATE]	= nuc970_clk_gate("uart0_eclk_gate",
+						  "uart0_eclk_div",
+						  REG_CLK_PCLKEN0, 16);
+	/* UART1 */
+	clk[UART1_APLLDIV]	= nuc970_clk_divider("uart1_aplldiv", "apll",
+						     REG_CLK_DIV4, 8, 3);
+	clk[UART1_UPLLDIV]	= nuc970_clk_divider("uart1_uplldiv", "upll",
+						     REG_CLK_DIV4, 8, 3);
+	clk[UART1_ECLK_MUX]	= nuc970_clk_mux("uart1_eclk_mux", REG_CLK_DIV4,
+						 11, 2, uart1_sel_clks,
+						 ARRAY_SIZE(uart1_sel_clks));
+	clk[UART1_ECLK_DIV]	= nuc970_clk_divider("uart1_eclk_div",
+						     "uart1_eclk_mux",
+						     REG_CLK_DIV4, 13, 3);
+	clk[UART1_ECLK_GATE]	= nuc970_clk_gate("uart1_eclk_gate",
+						  "uart1_eclk_div",
+						  REG_CLK_PCLKEN0, 17);
+	/* UART2 */
+	clk[UART2_APLLDIV]	= nuc970_clk_divider("uart2_aplldiv", "apll",
+						     REG_CLK_DIV4, 16, 3);
+	clk[UART2_UPLLDIV]	= nuc970_clk_divider("uart2_uplldiv", "upll",
+						     REG_CLK_DIV4, 16, 3);
+	clk[UART2_ECLK_MUX]	= nuc970_clk_mux("uart2_eclk_mux", REG_CLK_DIV4,
+						 19, 2, uart2_sel_clks,
+						 ARRAY_SIZE(uart2_sel_clks));
+	clk[UART2_ECLK_DIV]	= nuc970_clk_divider("uart2_eclk_div",
+						     "uart2_eclk_mux",
+						     REG_CLK_DIV4, 21, 3);
+	clk[UART2_ECLK_GATE]	= nuc970_clk_gate("uart2_eclk_gate",
+						  "uart2_eclk_div",
+						  REG_CLK_PCLKEN0, 18);
+	/* UART3 */
+	clk[UART3_APLLDIV]	= nuc970_clk_divider("uart3_aplldiv", "apll",
+						     REG_CLK_DIV4, 24, 3);
+	clk[UART3_UPLLDIV]	= nuc970_clk_divider("uart3_uplldiv", "upll",
+						     REG_CLK_DIV4, 24, 3);
+	clk[UART3_ECLK_MUX]	= nuc970_clk_mux("uart3_eclk_mux", REG_CLK_DIV4,
+						 27, 2, uart3_sel_clks,
+						 ARRAY_SIZE(uart3_sel_clks));
+	clk[UART3_ECLK_DIV]	= nuc970_clk_divider("uart3_eclk_div",
+						     "uart3_eclk_mux",
+						     REG_CLK_DIV4, 29, 3);
+	clk[UART3_ECLK_GATE]	= nuc970_clk_gate("uart3_eclk_gate",
+						  "uart3_eclk_div",
+						  REG_CLK_PCLKEN0, 19);
+	/* UART4 */
+	clk[UART4_APLLDIV]	= nuc970_clk_divider("uart4_aplldiv", "apll",
+						     REG_CLK_DIV5, 0, 3);
+	clk[UART4_UPLLDIV]	= nuc970_clk_divider("uart4_uplldiv", "upll",
+						     REG_CLK_DIV5, 0, 3);
+	clk[UART4_ECLK_MUX]	= nuc970_clk_mux("uart4_eclk_mux", REG_CLK_DIV5,
+						 3, 2, uart4_sel_clks,
+						 ARRAY_SIZE(uart4_sel_clks));
+	clk[UART4_ECLK_DIV]	= nuc970_clk_divider("uart4_eclk_div",
+						     "uart4_eclk_mux",
+						     REG_CLK_DIV5, 5, 3);
+	clk[UART4_ECLK_GATE]	= nuc970_clk_gate("uart4_eclk_gate",
+						  "uart4_eclk_div",
+						  REG_CLK_PCLKEN0, 20);
+	/* UART5 */
+	clk[UART5_APLLDIV]	= nuc970_clk_divider("uart5_aplldiv", "apll",
+						     REG_CLK_DIV5, 8, 3);
+	clk[UART5_UPLLDIV]	= nuc970_clk_divider("uart5_uplldiv", "upll",
+						     REG_CLK_DIV5, 8, 3);
+	clk[UART5_ECLK_MUX]	= nuc970_clk_mux("uart5_eclk_mux", REG_CLK_DIV5,
+						 11, 2, uart5_sel_clks,
+						 ARRAY_SIZE(uart5_sel_clks));
+	clk[UART5_ECLK_DIV]	= nuc970_clk_divider("uart5_eclk_div",
+						     "uart5_eclk_mux",
+						     REG_CLK_DIV5, 13, 3);
+	clk[UART5_ECLK_GATE]	= nuc970_clk_gate("uart5_eclk_gate",
+						  "uart5_eclk_div",
+						  REG_CLK_PCLKEN0, 21);
+	/* UART6 */
+	clk[UART6_APLLDIV]	= nuc970_clk_divider("uart6_aplldiv", "apll",
+						     REG_CLK_DIV5, 16, 3);
+	clk[UART6_UPLLDIV]	= nuc970_clk_divider("uart6_uplldiv", "upll",
+						     REG_CLK_DIV5, 16, 3);
+	clk[UART6_ECLK_MUX]	= nuc970_clk_mux("uart6_eclk_mux", REG_CLK_DIV5,
+						 19, 2, uart6_sel_clks,
+						 ARRAY_SIZE(uart6_sel_clks));
+	clk[UART6_ECLK_DIV]	= nuc970_clk_divider("uart6_eclk_div",
+						     "uart6_eclk_mux",
+						     REG_CLK_DIV5, 21, 3);
+	clk[UART6_ECLK_GATE]	= nuc970_clk_gate("uart6_eclk_gate",
+						  "uart6_eclk_div",
+						  REG_CLK_PCLKEN0, 22);
+	/* UART7 */
+	clk[UART7_APLLDIV]	= nuc970_clk_divider("uart7_aplldiv", "apll",
+						     REG_CLK_DIV5, 24, 3);
+	clk[UART7_UPLLDIV]	= nuc970_clk_divider("uart7_uplldiv", "upll",
+						     REG_CLK_DIV5, 24, 3);
+	clk[UART7_ECLK_MUX]	= nuc970_clk_mux("uart7_eclk_mux", REG_CLK_DIV5,
+						 27, 2, uart7_sel_clks,
+						 ARRAY_SIZE(uart7_sel_clks));
+	clk[UART7_ECLK_DIV]	= nuc970_clk_divider("uart7_eclk_div",
+						     "uart7_eclk_mux",
+						     REG_CLK_DIV5, 29, 3);
+	clk[UART7_ECLK_GATE]	= nuc970_clk_gate("uart7_eclk_gate",
+						  "uart7_eclk_div",
+						  REG_CLK_PCLKEN0, 23);
+	/* UART8 */
+	clk[UART8_APLLDIV]	= nuc970_clk_divider("uart8_aplldiv", "apll",
+						     REG_CLK_DIV6, 0, 3);
+	clk[UART8_UPLLDIV]	= nuc970_clk_divider("uart8_uplldiv", "upll",
+						     REG_CLK_DIV6, 0, 3);
+	clk[UART8_ECLK_MUX]	= nuc970_clk_mux("uart8_eclk_mux", REG_CLK_DIV6,
+						 3, 2, uart8_sel_clks,
+						 ARRAY_SIZE(uart8_sel_clks));
+	clk[UART8_ECLK_DIV]	= nuc970_clk_divider("uart8_eclk_div",
+						     "uart8_eclk_mux",
+						     REG_CLK_DIV6, 5, 3);
+	clk[UART8_ECLK_GATE]	= nuc970_clk_gate("uart8_eclk_gate",
+						  "uart8_eclk_div",
+						  REG_CLK_PCLKEN0, 24);
+	/* UART9 */
+	clk[UART9_APLLDIV]	= nuc970_clk_divider("uart9_aplldiv", "apll",
+						     REG_CLK_DIV6, 8, 3);
+	clk[UART9_UPLLDIV]	= nuc970_clk_divider("uart9_uplldiv", "upll",
+						     REG_CLK_DIV6, 8, 3);
+	clk[UART9_ECLK_MUX]	= nuc970_clk_mux("uart9_eclk_mux", REG_CLK_DIV6,
+						 11, 2, uart9_sel_clks,
+						 ARRAY_SIZE(uart9_sel_clks));
+	clk[UART9_ECLK_DIV]	= nuc970_clk_divider("uart9_eclk_div",
+						     "uart9_eclk_mux",
+						     REG_CLK_DIV6, 13, 3);
+	clk[UART9_ECLK_GATE]	= nuc970_clk_gate("uart9_eclk_gate",
+						  "uart9_eclk_div",
+						  REG_CLK_PCLKEN0, 25);
+	/* UART10 */
+	clk[UART10_APLLDIV]	 = nuc970_clk_divider("uart10_aplldiv", "apll",
+						      REG_CLK_DIV6, 16, 3);
+	clk[UART10_UPLLDIV]	 = nuc970_clk_divider("uart10_uplldiv", "upll",
+						      REG_CLK_DIV6, 16, 3);
+	clk[UART10_ECLK_MUX]	= nuc970_clk_mux("uart10_eclk_mux",
+						 REG_CLK_DIV6, 19, 2,
+						 uart10_sel_clks,
+						 ARRAY_SIZE(uart10_sel_clks));
+	clk[UART10_ECLK_DIV]	= nuc970_clk_divider("uart10_eclk_div",
+						     "uart10_eclk_mux",
+						     REG_CLK_DIV6, 21, 3);
+	clk[UART10_ECLK_GATE]	= nuc970_clk_gate("uart10_eclk_gate",
+						  "uart10_eclk_div",
+						  REG_CLK_PCLKEN0, 26);
+	/* SYSTEM */
+	clk[SYSTEM_APLLDIV]	= nuc970_clk_divider("system_aplldiv", "apll",
+						     REG_CLK_DIV0, 0, 3);
+	clk[SYSTEM_UPLLDIV]	= nuc970_clk_divider("system_uplldiv", "upll",
+						     REG_CLK_DIV0, 0, 3);
+	clk[SYSTEM_ECLK_MUX]	= nuc970_clk_mux("system_eclk_mux",
+						 REG_CLK_DIV0, 3, 2,
+						 system_sel_clks,
+						 ARRAY_SIZE(system_sel_clks));
+	clk[SYSTEM_ECLK_DIV]	= nuc970_clk_divider("system_eclk_div",
+						     "system_eclk_mux",
+						     REG_CLK_DIV0, 8, 4);
+	/* GPIO */
+	clk[GPIO_ECLK_MUX]	= nuc970_clk_mux("gpio_eclk_mux", REG_CLK_DIV7,
+						 7, 1, gpio_sel_clks,
+						 ARRAY_SIZE(gpio_sel_clks));
+	clk[GPIO_ECLK_DIV]	= nuc970_clk_divider("gpio_eclk_div",
+						     "gpio_eclk_mux",
+						     REG_CLK_DIV7, 0, 7);
+	clk[GPIO_ECLK_GATE]	= nuc970_clk_gate("gpio_eclk_gate",
+						  "gpio_eclk_div",
+						  REG_CLK_PCLKEN0, 3);
+	/* KPI */
+	clk[KPI_ECLK_MUX]	= nuc970_clk_mux("kpi_eclk_mux", REG_CLK_DIV7,
+						 15, 1, kpi_sel_clks,
+						 ARRAY_SIZE(kpi_sel_clks));
+	clk[KPI_ECLK_DIV]	= nuc970_clk_divider("kpi_eclk_div",
+						     "kpi_eclk_mux",
+						     REG_CLK_DIV7, 8, 7);
+	clk[KPI_ECLK_GATE]	= nuc970_clk_gate("kpi_eclk_gate",
+						  "kpi_eclk_div",
+						  REG_CLK_PCLKEN1, 25);
+	/* ETIMER0 */
+	clk[ETIMER0_ECLK_MUX]	= nuc970_clk_mux("etimer0_eclk_mux",
+						 REG_CLK_DIV8, 16, 2,
+						 etimer_sel_clks,
+						 ARRAY_SIZE(etimer_sel_clks));
+	clk[ETIMER0_ECLK_GATE]	= nuc970_clk_gate("etimer0_eclk_gate",
+						  "etimer0_eclk_mux",
+						  REG_CLK_PCLKEN0, 4);
+	/* ETIMER1 */
+	clk[ETIMER1_ECLK_MUX]	= nuc970_clk_mux("etimer1_eclk_mux",
+						 REG_CLK_DIV8, 18, 2,
+						 etimer_sel_clks,
+						 ARRAY_SIZE(etimer_sel_clks));
+	clk[ETIMER1_ECLK_GATE]	= nuc970_clk_gate("etimer1_eclk_gate",
+						  "etimer1_eclk_mux",
+						  REG_CLK_PCLKEN0, 5);
+	/* ETIMER2 */
+	clk[ETIMER2_ECLK_MUX]	= nuc970_clk_mux("etimer2_eclk_mux",
+						 REG_CLK_DIV8, 20, 2,
+						 etimer_sel_clks,
+						 ARRAY_SIZE(etimer_sel_clks));
+	clk[ETIMER2_ECLK_GATE]	= nuc970_clk_gate("etimer2_eclk_gate",
+						  "etimer2_eclk_mux",
+						  REG_CLK_PCLKEN0, 6);
+	/* ETIMER3 */
+	clk[ETIMER3_ECLK_MUX]	= nuc970_clk_mux("etimer3_eclk_mux",
+						 REG_CLK_DIV8, 22, 2,
+						 etimer_sel_clks,
+						 ARRAY_SIZE(etimer_sel_clks));
+	clk[ETIMER3_ECLK_GATE]	= nuc970_clk_gate("etimer3_eclk_gate",
+						  "etimer3_eclk_mux",
+						  REG_CLK_PCLKEN0, 7);
+	/* WWDT */
+	clk[WWDT_ECLK_MUX]	= nuc970_clk_mux("wwdt_eclk_mux", REG_CLK_DIV8,
+						 10, 2, wwdt_sel_clks,
+						 ARRAY_SIZE(wwdt_sel_clks));
+	clk[WWDT_ECLK_GATE]	= nuc970_clk_gate("wwdt_eclk_gate",
+						  "wwdt_eclk_mux",
+						  REG_CLK_PCLKEN0, 1);
+	/* WDT */
+	clk[WDT_ECLK_MUX]	= nuc970_clk_mux("wdt_eclk_mux", REG_CLK_DIV8,
+						 8, 2, wwdt_sel_clks,
+						 ARRAY_SIZE(wwdt_sel_clks));
+	clk[WDT_ECLK_GATE]	= nuc970_clk_gate("wdt_eclk_gate",
+						  "wdt_eclk_mux",
+						  REG_CLK_PCLKEN0, 0);
+	/* SMARTCARD */
+	clk[SMC0_ECLK_DIV]	= nuc970_clk_divider("smc0_eclk_div", "xin",
+						     REG_CLK_DIV6, 24, 4);
+	clk[SMC0_ECLK_GATE]	= nuc970_clk_gate("smc0_eclk_gate",
+						  "smc0_eclk_div",
+						  REG_CLK_PCLKEN1, 12);
+	clk[SMC1_ECLK_DIV]	= nuc970_clk_divider("smc1_eclk_div", "xin",
+						     REG_CLK_DIV6, 28, 4);
+	clk[SMC1_ECLK_GATE]	= nuc970_clk_gate("smc1_eclk_gate",
+						  "smc1_eclk_div",
+						  REG_CLK_PCLKEN1, 13);
+	/* PCLK */
+	clk[PCLK_DIV]		= nuc970_clk_divider("pclk_div", "hclk1_div",
+						     REG_CLK_DIV0, 24, 4);
+	clk[PCLK4096_DIV]	= nuc970_clk_fixed_factor("pclk4096_div",
+							  "pclk_div", 1, 4096);
+	clk[I2C0_GATE]		= nuc970_clk_gate("i2c0_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 0);
+	clk[I2C1_GATE]		= nuc970_clk_gate("i2c1_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 1);
+	clk[SPI0_GATE]		= nuc970_clk_gate("spi0_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 4);
+	clk[SPI1_GATE]		= nuc970_clk_gate("spi1_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 5);
+	clk[UART0_GATE]		= nuc970_clk_gate("uart0_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 16);
+	clk[UART1_GATE]		= nuc970_clk_gate("uart1_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 17);
+	clk[UART2_GATE]		= nuc970_clk_gate("uart2_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 18);
+	clk[UART3_GATE]		= nuc970_clk_gate("uart3_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 19);
+	clk[UART4_GATE]		= nuc970_clk_gate("uart4_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 20);
+	clk[UART5_GATE]		= nuc970_clk_gate("uart5_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 21);
+	clk[UART6_GATE]		= nuc970_clk_gate("uart6_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 22);
+	clk[UART7_GATE]		= nuc970_clk_gate("uart7_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 23);
+	clk[UART8_GATE]		= nuc970_clk_gate("uart8_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 24);
+	clk[UART9_GATE]		= nuc970_clk_gate("uart9_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 25);
+	clk[UART10_GATE]	= nuc970_clk_gate("uart10_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 26);
+	clk[WDT_GATE]		= nuc970_clk_gate("wdt_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 0);
+	clk[WWDT_GATE]		= nuc970_clk_gate("wwdt_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 1);
+	clk[RTC_GATE]		= nuc970_clk_gate("rtc_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 2);
+	clk[GPIO_GATE]		= nuc970_clk_gate("gpio_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 3);
+	clk[ADC_GATE]		= nuc970_clk_gate("adc_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 24);
+	clk[KPI_GATE]		= nuc970_clk_gate("kpi_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 25);
+	clk[MTPC_GATE]		= nuc970_clk_gate("mtpc_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 26);
+	clk[PWM_GATE]		= nuc970_clk_gate("pwm_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 27);
+	clk[ETIMER0_GATE]	= nuc970_clk_gate("etimer0_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 4);
+	clk[ETIMER1_GATE]	= nuc970_clk_gate("etimer1_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 5);
+	clk[ETIMER2_GATE]	= nuc970_clk_gate("etimer2_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 6);
+	clk[ETIMER3_GATE]	= nuc970_clk_gate("etimer3_gate", "pclk_div",
+						  REG_CLK_PCLKEN0, 7);
+	clk[CAN0_GATE]		= nuc970_clk_gate("can0_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 8);
+	clk[CAN1_GATE]		= nuc970_clk_gate("can1_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 9);
+	clk[TIMER0_GATE]	= nuc970_clk_gate("timer0_gate", "xin",
+						  REG_CLK_PCLKEN0, 8);
+	clk[TIMER1_GATE]	= nuc970_clk_gate("timer1_gate", "xin",
+						  REG_CLK_PCLKEN0, 9);
+	clk[TIMER2_GATE]	= nuc970_clk_gate("timer2_gate", "xin",
+						  REG_CLK_PCLKEN0, 10);
+	clk[TIMER3_GATE]	= nuc970_clk_gate("timer3_gate", "xin",
+						  REG_CLK_PCLKEN0, 11);
+	clk[TIMER4_GATE]	= nuc970_clk_gate("timer4_gate", "xin",
+						  REG_CLK_PCLKEN0, 12);
+	clk[SMC0_GATE]		= nuc970_clk_gate("smc0_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 12);
+	clk[SMC1_GATE]		= nuc970_clk_gate("smc1_gate", "pclk_div",
+						  REG_CLK_PCLKEN1, 13);
+
+	for (i = 0; i < ARRAY_SIZE(clk); i++)
+		if (IS_ERR(clk[i]))
+			pr_err("nuc970 clk %d: register failed with %ld\n",
+				i, PTR_ERR(clk[i]));
+
+	clk_data.clks = clk;
+	clk_data.clk_num = ARRAY_SIZE(clk);
+	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+	/* Register clock device */
+	clk_register_clkdev(clk[TIMER0_GATE], "timer0", NULL);
+	clk_register_clkdev(clk[TIMER1_GATE], "timer1", NULL);
+	clk_register_clkdev(clk[PCLK4096_DIV], "pclk4096_div", NULL);
+	clk_register_clkdev(clk[XIN], "xin", NULL);
+	clk_register_clkdev(clk[XIN32K], "xin32k", NULL);
+	clk_register_clkdev(clk[APLL], "apll", NULL);
+	clk_register_clkdev(clk[UPLL], "upll", NULL);
+	clk_register_clkdev(clk[SYS_MUX], "sysmux", NULL);
+	clk_register_clkdev(clk[SYS_DIV], "sysdiv", NULL);
+	clk_register_clkdev(clk[XIN128_DIV], "xin128div", NULL);
+	/* CPU */
+	clk_register_clkdev(clk[CPU_DIV], "cpudiv", NULL);
+	clk_register_clkdev(clk[CPU_GATE], "cpu", NULL);
+	/* HCLK1 */
+	clk_register_clkdev(clk[HCLK_GATE], "hclk", NULL);
+	clk_register_clkdev(clk[SRAM_GATE], "sram", NULL);
+	clk_register_clkdev(clk[HCLK1_DIV], "hclk1div", NULL);
+	clk_register_clkdev(clk[DDR_GATE], "ddr_hclk", NULL);
+	clk_register_clkdev(clk[GDMA_GATE], "gdma_hclk", NULL);
+	clk_register_clkdev(clk[EBI_GATE], "ebi_hclk", NULL);
+	clk_register_clkdev(clk[TIC_GATE], "tic_hclk", NULL);
+	/* HCLK234 */
+	clk_register_clkdev(clk[HCLKN_DIV], "hclkndiv", NULL);
+	clk_register_clkdev(clk[DRAM_GATE], "dram", NULL);
+	clk_register_clkdev(clk[HCLK234_DIV], "hclk234div", NULL);
+	/* HCLK3 */
+	clk_register_clkdev(clk[USBH_GATE], "usbh_hclk", NULL);
+	clk_register_clkdev(clk[EMAC1_GATE], "emac1_hclk", NULL);
+	clk_register_clkdev(clk[EMAC1_ECLK_DIV], "emac1_eclk_div", NULL);
+	clk_register_clkdev(clk[EMAC1_ECLK_GATE], "emac1_eclk", NULL);
+	clk_register_clkdev(clk[USBD_GATE], "usbd_hclk", NULL);
+	clk_register_clkdev(clk[FMI_GATE], "fmi_hclk", NULL);
+	clk_register_clkdev(clk[NAND_GATE], "nand_hclk", NULL);
+	clk_register_clkdev(clk[EMMC_GATE], "emmc_hclk", NULL);
+	clk_register_clkdev(clk[CRYPTO_GATE], "crypto_hclk", NULL);
+	clk_register_clkdev(clk[JPEG_GATE], "jpeg_hclk", NULL);
+	clk_register_clkdev(clk[JPEG_ECLK_DIV], "jpeg_eclk_div", NULL);
+	clk_register_clkdev(clk[JPEG_ECLK_GATE], "jpeg_eclk", NULL);
+	clk_register_clkdev(clk[GE2D_GATE], "ge2d_hclk", NULL);
+	clk_register_clkdev(clk[GE2D_ECLK_DIV], "ge2d_eclk_div", NULL);
+	clk_register_clkdev(clk[GE2D_ECLK_GATE], "ge2d_eclk", NULL);
+	/* HCLK4 */
+	clk_register_clkdev(clk[EMAC0_GATE], "emac0_hclk", NULL);
+	clk_register_clkdev(clk[EMAC0_ECLK_DIV], "emac0_eclk_div", NULL);
+	clk_register_clkdev(clk[EMAC0_ECLK_GATE], "emac0_eclk", NULL);
+	clk_register_clkdev(clk[SDH_GATE], "sdh_hclk", NULL);
+	clk_register_clkdev(clk[AUDIO_GATE], "audio_hclk", NULL);
+	clk_register_clkdev(clk[LCD_GATE], "lcd_hclk", NULL);
+	clk_register_clkdev(clk[SENSOR_GATE], "sensor_hclk", NULL);
+	clk_register_clkdev(clk[CAP_GATE], "cap_hclk", NULL);
+	/* ECLK */
+	clk_register_clkdev(clk[LCD_APLLDIV], "lcd_aplldiv", NULL);
+	clk_register_clkdev(clk[LCD_UPLLDIV], "lcd_uplldiv", NULL);
+	clk_register_clkdev(clk[LCD_ECLK_MUX], "lcd_eclk_mux", NULL);
+	clk_register_clkdev(clk[LCD_ECLK_DIV], "lcd_eclk_div", NULL);
+	clk_register_clkdev(clk[LCD_ECLK_GATE], "lcd_eclk", NULL);
+	clk_register_clkdev(clk[AUDIO_APLLDIV], "audio_aplldiv", NULL);
+	clk_register_clkdev(clk[AUDIO_UPLLDIV], "audio_uplldiv", NULL);
+	clk_register_clkdev(clk[AUDIO_ECLK_MUX], "audio_eclk_mux", NULL);
+	clk_register_clkdev(clk[AUDIO_ECLK_DIV], "audio_eclk_div", NULL);
+	clk_register_clkdev(clk[AUDIO_ECLK_GATE], "audio_eclk", NULL);
+	clk_register_clkdev(clk[USB_APLLDIV], "usb_aplldiv", NULL);
+	clk_register_clkdev(clk[USB_UPLLDIV], "usb_uplldiv", NULL);
+	clk_register_clkdev(clk[USB_ECLK_MUX], "usb_eclk_mux", NULL);
+	clk_register_clkdev(clk[USB_ECLK_DIV], "usb_eclk_div", NULL);
+	clk_register_clkdev(clk[USB_ECLK_GATE], "usb_eclk", NULL);
+	clk_register_clkdev(clk[SDH_APLLDIV], "sdh_aplldiv", NULL);
+	clk_register_clkdev(clk[SDH_UPLLDIV], "sdh_uplldiv", NULL);
+	clk_register_clkdev(clk[SDH_ECLK_MUX], "sdh_eclk_mux", NULL);
+	clk_register_clkdev(clk[SDH_ECLK_DIV], "sdh_eclk_div", NULL);
+	clk_register_clkdev(clk[SDH_ECLK_GATE], "sdh_eclk", NULL);
+	clk_register_clkdev(clk[EMMC_APLLDIV], "emmc_aplldiv", NULL);
+	clk_register_clkdev(clk[EMMC_UPLLDIV], "emmc_uplldiv", NULL);
+	clk_register_clkdev(clk[EMMC_ECLK_MUX], "emmc_eclk_mux", NULL);
+	clk_register_clkdev(clk[EMMC_ECLK_DIV], "emmc_eclk_div", NULL);
+	clk_register_clkdev(clk[EMMC_ECLK_GATE], "emmc_eclk", NULL);
+	clk_register_clkdev(clk[ADC_APLLDIV], "adc_aplldiv", NULL);
+	clk_register_clkdev(clk[ADC_UPLLDIV], "adc_uplldiv", NULL);
+	clk_register_clkdev(clk[ADC_ECLK_MUX], "adc_eclk_mux", NULL);
+	clk_register_clkdev(clk[ADC_ECLK_DIV], "adc_eclk_div", NULL);
+	clk_register_clkdev(clk[ADC_ECLK_GATE], "adc_eclk", NULL);
+	clk_register_clkdev(clk[CAP_APLLDIV], "cap_aplldiv", NULL);
+	clk_register_clkdev(clk[CAP_UPLLDIV], "cap_uplldiv", NULL);
+	clk_register_clkdev(clk[CAP_ECLK_MUX], "cap_eclk_mux", NULL);
+	clk_register_clkdev(clk[CAP_ECLK_DIV], "cap_eclk_div", NULL);
+	clk_register_clkdev(clk[CAP_ECLK_GATE], "cap_eclk", NULL);
+	clk_register_clkdev(clk[UART0_APLLDIV], "uart0_aplldiv", NULL);
+	clk_register_clkdev(clk[UART0_UPLLDIV], "uart0_uplldiv", NULL);
+	clk_register_clkdev(clk[UART0_ECLK_MUX], "uart0_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART0_ECLK_DIV], "uart0_eclk_div", NULL);
+	clk_register_clkdev(clk[UART0_ECLK_GATE], "uart0_eclk", NULL);
+	clk_register_clkdev(clk[UART1_APLLDIV], "uart1_aplldiv", NULL);
+	clk_register_clkdev(clk[UART1_UPLLDIV], "uart1_uplldiv", NULL);
+	clk_register_clkdev(clk[UART1_ECLK_MUX], "uart1_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART1_ECLK_DIV], "uart1_eclk_div", NULL);
+	clk_register_clkdev(clk[UART1_ECLK_GATE], "uart1_eclk", NULL);
+	clk_register_clkdev(clk[UART2_APLLDIV], "uart2_aplldiv", NULL);
+	clk_register_clkdev(clk[UART2_UPLLDIV], "uart2_uplldiv", NULL);
+	clk_register_clkdev(clk[UART2_ECLK_MUX], "uart2_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART2_ECLK_DIV], "uart2_eclk_div", NULL);
+	clk_register_clkdev(clk[UART2_ECLK_GATE], "uart2_eclk", NULL);
+	clk_register_clkdev(clk[UART3_APLLDIV], "uart3_aplldiv", NULL);
+	clk_register_clkdev(clk[UART3_UPLLDIV], "uart3_uplldiv", NULL);
+	clk_register_clkdev(clk[UART3_ECLK_MUX], "uart3_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART3_ECLK_DIV], "uart3_eclk_div", NULL);
+	clk_register_clkdev(clk[UART3_ECLK_GATE], "uart3_eclk", NULL);
+	clk_register_clkdev(clk[UART4_APLLDIV], "uart4_aplldiv", NULL);
+	clk_register_clkdev(clk[UART4_UPLLDIV], "uart4_uplldiv", NULL);
+	clk_register_clkdev(clk[UART4_ECLK_MUX], "uart4_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART4_ECLK_DIV], "uart4_eclk_div", NULL);
+	clk_register_clkdev(clk[UART4_ECLK_GATE], "uart4_eclk", NULL);
+	clk_register_clkdev(clk[UART5_APLLDIV], "uart5_aplldiv", NULL);
+	clk_register_clkdev(clk[UART5_UPLLDIV], "uart5_uplldiv", NULL);
+	clk_register_clkdev(clk[UART5_ECLK_MUX], "uart5_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART5_ECLK_DIV], "uart5_eclk_div", NULL);
+	clk_register_clkdev(clk[UART5_ECLK_GATE], "uart5_eclk", NULL);
+	clk_register_clkdev(clk[UART6_APLLDIV], "uart6_aplldiv", NULL);
+	clk_register_clkdev(clk[UART6_UPLLDIV], "uart6_uplldiv", NULL);
+	clk_register_clkdev(clk[UART6_ECLK_MUX], "uart6_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART6_ECLK_DIV], "uart6_eclk_div", NULL);
+	clk_register_clkdev(clk[UART6_ECLK_GATE], "uart6_eclk", NULL);
+	clk_register_clkdev(clk[UART7_APLLDIV], "uart7_aplldiv", NULL);
+	clk_register_clkdev(clk[UART7_UPLLDIV], "uart7_uplldiv", NULL);
+	clk_register_clkdev(clk[UART7_ECLK_MUX], "uart7_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART7_ECLK_DIV], "uart7_eclk_div", NULL);
+	clk_register_clkdev(clk[UART7_ECLK_GATE], "uart7_eclk", NULL);
+	clk_register_clkdev(clk[UART8_APLLDIV], "uart8_aplldiv", NULL);
+	clk_register_clkdev(clk[UART8_UPLLDIV], "uart8_uplldiv", NULL);
+	clk_register_clkdev(clk[UART8_ECLK_MUX], "uart8_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART8_ECLK_DIV], "uart8_eclk_div", NULL);
+	clk_register_clkdev(clk[UART8_ECLK_GATE], "uart8_eclk", NULL);
+	clk_register_clkdev(clk[UART9_APLLDIV], "uart9_aplldiv", NULL);
+	clk_register_clkdev(clk[UART9_UPLLDIV], "uart9_uplldiv", NULL);
+	clk_register_clkdev(clk[UART9_ECLK_MUX], "uart9_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART9_ECLK_DIV], "uart9_eclk_div", NULL);
+	clk_register_clkdev(clk[UART9_ECLK_GATE], "uart9_eclk", NULL);
+	clk_register_clkdev(clk[UART10_APLLDIV], "uart10_aplldiv", NULL);
+	clk_register_clkdev(clk[UART10_UPLLDIV], "uart10_uplldiv", NULL);
+	clk_register_clkdev(clk[UART10_ECLK_MUX], "uart10_eclk_mux", NULL);
+	clk_register_clkdev(clk[UART10_ECLK_DIV], "uart10_eclk_div", NULL);
+	clk_register_clkdev(clk[UART10_ECLK_GATE], "uart10_eclk", NULL);
+	clk_register_clkdev(clk[SYSTEM_APLLDIV], "system_aplldiv", NULL);
+	clk_register_clkdev(clk[SYSTEM_UPLLDIV], "system_uplldiv", NULL);
+	clk_register_clkdev(clk[SYSTEM_ECLK_MUX], "system_eclk_mux", NULL);
+	clk_register_clkdev(clk[SYSTEM_ECLK_DIV], "system_eclk_div", NULL);
+	clk_register_clkdev(clk[SYSTEM_ECLK_GATE], "system_eclk", NULL);
+	clk_register_clkdev(clk[GPIO_ECLK_MUX], "gpio_eclk_mux", NULL);
+	clk_register_clkdev(clk[GPIO_ECLK_DIV], "gpio_eclk_div", NULL);
+	clk_register_clkdev(clk[GPIO_ECLK_GATE], "gpio_eclk", NULL);
+	clk_register_clkdev(clk[KPI_ECLK_MUX], "kpi_eclk_mux", NULL);
+	clk_register_clkdev(clk[KPI_ECLK_DIV], "kpi_eclk_div", NULL);
+	clk_register_clkdev(clk[KPI_ECLK_GATE], "kpi_eclk", NULL);
+	clk_register_clkdev(clk[ETIMER0_ECLK_MUX], "etmr0_eclk_mux", NULL);
+	clk_register_clkdev(clk[ETIMER0_ECLK_GATE], "etmr0_eclk", NULL);
+	clk_register_clkdev(clk[ETIMER1_ECLK_MUX], "etmr1_eclk_mux", NULL);
+	clk_register_clkdev(clk[ETIMER1_ECLK_GATE], "etmr1_eclk", NULL);
+	clk_register_clkdev(clk[ETIMER2_ECLK_MUX], "etmr2_eclk_mux", NULL);
+	clk_register_clkdev(clk[ETIMER2_ECLK_GATE], "etmr2_eclk", NULL);
+	clk_register_clkdev(clk[ETIMER3_ECLK_MUX], "etmr3_eclk_mux", NULL);
+	clk_register_clkdev(clk[ETIMER3_ECLK_GATE], "etmr3_eclk", NULL);
+	clk_register_clkdev(clk[WWDT_ECLK_MUX], "wwdt_eclk_mux", NULL);
+	clk_register_clkdev(clk[WWDT_ECLK_GATE], "wwdt_eclk", NULL);
+	clk_register_clkdev(clk[WDT_ECLK_MUX], "wdt_eclk_mux", NULL);
+	clk_register_clkdev(clk[WDT_ECLK_GATE], "wdt_eclk", NULL);
+	clk_register_clkdev(clk[SMC0_ECLK_DIV], "smc0_eclk_div", NULL);
+	clk_register_clkdev(clk[SMC0_ECLK_GATE], "smc0_eclk", NULL);
+	clk_register_clkdev(clk[SMC1_ECLK_DIV], "smc1_eclk_div", NULL);
+	clk_register_clkdev(clk[SMC1_ECLK_GATE], "smc1_eclk", NULL);
+	/* PCLK */
+	clk_register_clkdev(clk[PCLK_DIV], "pclkdiv", NULL);
+	clk_register_clkdev(clk[RTC_GATE], "rtc", NULL);
+	clk_register_clkdev(clk[I2C0_GATE], "i2c0", NULL);
+	clk_register_clkdev(clk[I2C1_GATE], "i2c1", NULL);
+	clk_register_clkdev(clk[SPI0_GATE], "spi0", NULL);
+	clk_register_clkdev(clk[SPI1_GATE], "spi1", NULL);
+	clk_register_clkdev(clk[UART0_GATE], "uart0", NULL);
+	clk_register_clkdev(clk[UART1_GATE], "uart1", NULL);
+	clk_register_clkdev(clk[UART2_GATE], "uart2", NULL);
+	clk_register_clkdev(clk[UART3_GATE], "uart3", NULL);
+	clk_register_clkdev(clk[UART4_GATE], "uart4", NULL);
+	clk_register_clkdev(clk[UART5_GATE], "uart5", NULL);
+	clk_register_clkdev(clk[UART6_GATE], "uart6", NULL);
+	clk_register_clkdev(clk[UART7_GATE], "uart7", NULL);
+	clk_register_clkdev(clk[UART8_GATE], "uart8", NULL);
+	clk_register_clkdev(clk[UART9_GATE], "uart9", NULL);
+	clk_register_clkdev(clk[UART10_GATE], "uart10", NULL);
+	clk_register_clkdev(clk[WDT_GATE], "wdt", NULL);
+	clk_register_clkdev(clk[WWDT_GATE], "wwdt", NULL);
+	clk_register_clkdev(clk[GPIO_GATE], "gpio", NULL);
+	clk_register_clkdev(clk[SMC0_GATE], "smc0", NULL);
+	clk_register_clkdev(clk[SMC1_GATE], "smc1", NULL);
+	clk_register_clkdev(clk[ADC_GATE], "adc", NULL);
+	clk_register_clkdev(clk[KPI_GATE], "kpi", NULL);
+	clk_register_clkdev(clk[MTPC_GATE], "mtpc", NULL);
+	clk_register_clkdev(clk[PWM_GATE], "pwm", NULL);
+	clk_register_clkdev(clk[ETIMER0_GATE], "etimer0", NULL);
+	clk_register_clkdev(clk[ETIMER1_GATE], "etimer1", NULL);
+	clk_register_clkdev(clk[ETIMER2_GATE], "etimer2", NULL);
+	clk_register_clkdev(clk[ETIMER3_GATE], "etimer3", NULL);
+	clk_register_clkdev(clk[TIMER2_GATE], "timer2", NULL);
+	clk_register_clkdev(clk[TIMER3_GATE], "timer3", NULL);
+	clk_register_clkdev(clk[TIMER4_GATE], "timer4", NULL);
+	clk_register_clkdev(clk[CAN0_GATE], "can0", NULL);
+	clk_register_clkdev(clk[CAN1_GATE], "can1", NULL);
+
+	/* enable some important clocks */
+	clk_prepare_enable(clk_get(NULL, "cpu"));
+	clk_prepare_enable(clk_get(NULL, "hclk"));
+	clk_prepare_enable(clk_get(NULL, "sram"));
+	clk_prepare_enable(clk_get(NULL, "dram"));
+	clk_prepare_enable(clk_get(NULL, "ddr_hclk"));
+}
+
+CLK_OF_DECLARE(nuc970_clk, "nuvoton,clk", nuc970_clocks_init);
diff --git a/drivers/clk/nuc900/clk-upll.c b/drivers/clk/nuc900/clk-upll.c
new file mode 100644
index 0000000..d0c6fc2
--- /dev/null
+++ b/drivers/clk/nuc900/clk-upll.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 Wan Zongshun <mcuos.com@...il.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+
+#include "clk-ccf.h"
+
+struct clk_upll {
+	struct clk_hw	hw;
+	void __iomem	*base;
+};
+
+#define to_clk_upll(clk) (container_of(clk, struct clk_upll, clk))
+
+static unsigned long clk_upll_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	struct clk_upll *pll = to_clk_upll(hw);
+	unsigned long rate;
+	unsigned long reg = readl(pll->base) & 0x0FFFFFFF;
+
+	if (parent_rate != 12000000)
+		return 0;
+
+	switch (reg) {
+	case 0x15:
+		rate = 264000000;
+		break;
+	case 0x18:
+		rate = 300000000;
+		break;
+	default:
+		rate = 264000000;
+		break;
+	}
+
+	return rate;
+}
+
+static struct clk_ops clk_upll_ops = {
+	.recalc_rate = clk_upll_recalc_rate,
+};
+
+struct clk *nuc970_clk_upll(const char *name, const char *parent,
+			    void __iomem *base)
+{
+	struct clk_upll *pll;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	pll = kmalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	pll->base = base;
+
+	init.name = name;
+	init.ops = &clk_upll_ops;
+	init.flags = 0;
+	init.parent_names = &parent;
+	init.num_parents = 1;
+
+	pll->hw.init = &init;
+
+	clk = clk_register(NULL, &pll->hw);
+	if (IS_ERR(clk))
+		kfree(pll);
+
+	return clk;
+}
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ