* PATCH [1/1] V4 - Clock Support for VT8500/WM8505
@ 2011-02-27 22:27 Tony Prisk
0 siblings, 0 replies; 2+ messages in thread
From: Tony Prisk @ 2011-02-27 22:27 UTC (permalink / raw)
To: linux-arm-kernel
Patch to provide clock support on VT8500-/WM8505- based SoC.
V2: Included missing mach/clkdev.h
Fix errors in clocks-vt8500.c
V3: Correct code to allow VT8500 + WM8505 selected concurrently (for
runtime detection)
Added patch to initialize the clocks from existing board
code (bv07.c, wm8505_7in.c)
V4: Changed sending email address as seems mailing list rejecting
Add error catch for failed ioremap
Signed-off-by: Tony Prisk (linux at prisktech.co.nz)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5e34579..0593323 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -878,6 +878,8 @@ config ARCH_VT8500
select GENERIC_CLOCKEVENTS
select ARCH_REQUIRE_GPIOLIB
select HAVE_PWM
+ select CLKDEV_LOOKUP
+ select HAVE_CLK
help
Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
endchoice
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
index 81aedb7..c0ae083 100644
--- a/arch/arm/mach-vt8500/Makefile
+++ b/arch/arm/mach-vt8500/Makefile
@@ -1,7 +1,7 @@
-obj-y += devices.o gpio.o irq.o timer.o
+obj-y += clock.o devices.o gpio.o irq.o timer.o
-obj-$(CONFIG_VTWM_VERSION_VT8500) += devices-vt8500.o
-obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
+obj-$(CONFIG_VTWM_VERSION_VT8500) += devices-vt8500.o clocks-vt8500.o
+obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o clocks-wm8505.o
obj-$(CONFIG_MACH_BV07) += bv07.o
obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
index 94a261d..b637167 100644
--- a/arch/arm/mach-vt8500/bv07.c
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -25,6 +25,7 @@
#include <asm/mach/arch.h>
#include "devices.h"
+#include "clock.h"
static void __iomem *pmc_hiber;
@@ -65,6 +66,7 @@ void __init bv07_init(void)
vt8500_set_resources();
platform_add_devices(devices, ARRAY_SIZE(devices));
vt8500_gpio_init();
+ vt8500_clock_init();
}
MACHINE_START(BV07, "Benign BV07 Mini Netbook")
diff --git a/arch/arm/mach-vt8500/clock.c b/arch/arm/mach-vt8500/clock.c
new file mode 100644
index 0000000..9ce3a74
--- /dev/null
+++ b/arch/arm/mach-vt8500/clock.c
@@ -0,0 +1,164 @@
+/*
+ * arch/arm/mach-vt8500/clock.c
+ *
+ * Copyright (C) 2011 Tony Prisk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+
+#include "clock.h"
+
+static LIST_HEAD(clocks);
+
+static DEFINE_SPINLOCK(clocks_lock);
+static DEFINE_MUTEX(clocks_mutex);
+
+static void __clk_enable(struct clk *clk)
+{
+ if (clk->parent)
+ __clk_enable(clk->parent);
+ if (clk->usecount++ == 0)
+ if (clk->type & CLK_ENABLE)
+ clk->ops->enable(clk);
+}
+
+static void __clk_disable(struct clk *clk)
+{
+ if (--clk->usecount == 0)
+ if (clk->type & CLK_ENABLE)
+ clk->ops->disable(clk);
+ if (clk->parent)
+ __clk_disable(clk->parent);
+}
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ __clk_enable(clk);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+ if (clk->delay)
+ udelay(clk->delay);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ WARN_ON(clk->usecount == 0);
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long rate;
+
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ rate = clk->rate;
+ if ((clk->ops->getrate) && (clk->type & CLK_PROGRAMABLE))
+ rate = clk->ops->getrate(clk);
+
+ return rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+static void propagate_rate(struct clk *root)
+{
+ struct clk *clk;
+
+ list_for_each_entry(clk, &root->children, childnode) {
+ if (clk->ops->setrate)
+ clk->ops->setrate(clk, clk->rate);
+ if (clk->ops->getrate)
+ clk->rate = clk->ops->getrate(clk);
+ propagate_rate(clk);
+ }
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags;
+
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ if (!(clk->type & CLK_PROGRAMABLE))
+ return -EINVAL;
+
+ if (clk->ops->setrate)
+ clk->ops->setrate(clk, rate);
+
+ if (clk->ops->getrate)
+ clk->rate = clk->ops->getrate(clk);
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ if (!(clk->type & CLK_NO_PROPAGATE))
+ propagate_rate(clk);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+int clk_register(struct clk *clk)
+{
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ if (clk->parent && !clk->parent->rate)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&clk->children);
+
+ mutex_lock(&clocks_mutex);
+ list_add_tail(&clk->node, &clocks);
+ if (clk->parent)
+ list_add_tail(&clk->childnode, &clk->parent->children);
+ mutex_unlock(&clocks_mutex);
+
+ /* if rate already set, use it */
+ if (clk->rate)
+ return 0;
+
+ /* see if we can calculate the rate */
+ if (clk->ops->getrate)
+ clk->ops->getrate(clk);
+ /* otherwise use the parents rate */
+ else if (clk->parent)
+ clk->rate = clk->parent->rate;
+
+ return 0;
+}
diff --git a/arch/arm/mach-vt8500/clock.h b/arch/arm/mach-vt8500/clock.h
new file mode 100644
index 0000000..a4bd94f
--- /dev/null
+++ b/arch/arm/mach-vt8500/clock.h
@@ -0,0 +1,130 @@
+/*
+ * arch/arm/mach-vt8500/clock.h
+ *
+ * Copyright (C) 2011 Tony Prisk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
+ */
+
+#ifndef __WMT_CLOCK_H
+#define __WMT_CLOCK_H
+
+#include <linux/list.h>
+#include <linux/clkdev.h>
+
+#define CLK_PRIMARY 0x00
+#define CLK_PROGRAMABLE 0x01
+#define CLK_ENABLE 0x02
+#define CLK_NO_PROPAGATE 0x04
+
+#define CLK_PM_STATUS_LOW 0x000
+#define CLK_PML_ANY_CLK_UPDATING 0x00000008
+#define CLK_PML_PLL_UPDATING 0x00000010
+#define CLK_PML_ARM_UPDATING 0x00000080
+#define CLK_PML_AHB_UPDATING 0x00000100
+#define CLK_PML_UPDATE_MASK 0x1FFF9B37
+#define CLK_PML_BUSY 0xFFFFFFFE
+#define CLK_PM_STATUS_HIGH 0x004
+
+
+#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
+
+/* common clocks */
+#define CLK_ARM_DIVISOR 0x300
+#define CLK_AHB_DIVISOR 0x304
+#define CLK_DDR_DIVISOR 0x310 /* mask = 0x1F special */
+#define CLK_SFM_DIVISOR 0x314
+#define CLK_SDMMC_DIVISOR 0x328 /* mask = 0x1F special */
+#define CLK_GENET_DIVISOR 0x32C
+#define CLK_NAND_DIVISOR 0x330
+#define CLK_NOR_DIVISOR 0x334
+#define CLK_SPI0_DIVISOR 0x33C
+#define CLK_SPI1_DIVISOR 0x340
+#define CLK_SPI2_DIVISOR 0x344
+#define CLK_PWM_DIVISOR 0x348
+
+/* VT8500 clocks */
+#define CLK_DSP_DIVISOR 0x308
+#define CLK_LCD_DIVISOR 0x30C
+#define CLK_PCM_DIVISOR 0x320
+#define CLK_PWM2_DIVISOR 0x324
+#define CLK_MSPRO_DIVISOR 0x32C
+#define CLK_NAND_DIVISOR 0x330
+#define CLK_LCD_DIVISOR_HIGH 0x334
+
+/* WM8505 clocks */
+#define CLK_KBD_DIVISOR_PRE 0x318
+#define CLK_KBD_DIVISOR 0x31C
+#define CLK_APB_DIVISOR 0x350
+#define CLK_NA0_DIVISOR 0x358
+#define CLK_NA12_DIVISOR 0x35C
+#define CLK_I2C0_DIVISOR 0x36C
+#define CLK_I2C1_DIVISOR 0x370
+#define CLK_DVO_DIVISOR 0x374
+#define CLK_RO1_DIVISOR 0x378
+#define CLK_RO2_DIVISOR 0x37C
+
+
+#define INIT_CLKREG(_clk, _devid, _conid) \
+{ \
+ .clk = _clk, \
+ .dev_id = _devid, \
+ .con_id = _conid \
+}
+
+struct clkops {
+ void (*enable)(struct clk *);
+ void (*disable)(struct clk *);
+
+ unsigned long (*getrate)(struct clk *);
+ int (*setrate)(struct clk *, unsigned long rate);
+};
+
+struct clk {
+ struct list_head node;
+ struct clk *parent;
+ const struct clkops *ops;
+ const char *name;
+ unsigned long rate;
+ unsigned int type;
+ unsigned int delay;
+ unsigned int usecount;
+
+ unsigned int en_reg;
+ unsigned int en_bit;
+
+ unsigned int div_reg;
+
+ struct list_head children;
+ struct list_head childnode;
+};
+
+#ifdef CONFIG_VTWM_VERSION_WM8505
+extern int wm8505_clock_init(void);
+#endif
+
+#ifdef CONFIG_VTWM_VERSION_VT8500
+extern int vt8500_clock_init(void);
+#endif
+
+int clk_register(struct clk *clk);
+
+#endif
diff --git a/arch/arm/mach-vt8500/clocks-vt8500.c
b/arch/arm/mach-vt8500/clocks-vt8500.c
new file mode 100644
index 0000000..e586b67
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-vt8500.c
@@ -0,0 +1,622 @@
+/*
+ * arch/arm/mach-vt8500/clocks-vt8500.c
+ *
+ * Copyright (C) 2011 Tony Prisk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <mach/vt8500_regs.h>
+#include "clock.h"
+
+#define DEFINE_CKREF(_name, _rate) \
+struct clk clk_vt8500_##_name = { \
+ .name = #_name, \
+ .parent = NULL, \
+ .ops = NULL, \
+ .rate = _rate, \
+ .type = CLK_PRIMARY, \
+ .delay = 0, \
+ .en_reg = 0, \
+ .en_bit = 0 \
+}
+
+#define DEFINE_CKEN(_name, _ops, _enreg, _enbit) \
+struct clk clk_vt8500_##_name = { \
+ .name = #_name, \
+ .parent = NULL, \
+ .ops = _ops, \
+ .rate = 0, \
+ .type = CLK_ENABLE, \
+ .delay = 0, \
+ .en_reg = _enreg, \
+ .en_bit = _enbit, \
+ .div_reg = 0 \
+}
+
+#define DEFINE_CKPG(_name, _parent, _delay, _ops, _reg) \
+struct clk clk_vt8500_##_name = { \
+ .name = #_name, \
+ .parent = _parent, \
+ .ops = _ops, \
+ .rate = 0, \
+ .type = CLK_PROGRAMABLE, \
+ .delay = _delay, \
+ .en_reg = 0, \
+ .en_bit = 0, \
+ .div_reg = _reg \
+}
+
+#define DEFINE_CKPGNP(_name, _parent, _delay, _ops, _reg) \
+struct clk clk_vt8500_##_name = { \
+ .name = #_name, \
+ .parent = _parent, \
+ .ops = _ops, \
+ .rate = 0, \
+ .type = CLK_PROGRAMABLE | CLK_NO_PROPAGATE, \
+ .delay = _delay, \
+ .en_reg = 0, \
+ .en_bit = 0, \
+ .div_reg = _reg \
+}
+
+#define DEFINE_CKPGEN(_name, _parent, _delay, _ops, _reg, _enreg, _enbit) \
+struct clk clk_vt8500_##_name = { \
+ .name = #_name, \
+ .parent = _parent, \
+ .ops = _ops, \
+ .rate = 0, \
+ .type = CLK_PROGRAMABLE | CLK_ENABLE, \
+ .delay = _delay, \
+ .en_reg = _enreg, \
+ .en_bit = _enbit, \
+ .div_reg = _reg \
+}
+
+void __iomem *vt8500_pmc_base;
+
+static void wmt_pm_wait_update(void)
+{
+ int cnt = 1000000;
+ while (readl(vt8500_pmc_base + CLK_PM_STATUS_LOW) &
CLK_PML_UPDATE_MASK) {
+ if (--cnt == 0) break;
+ cpu_relax();
+ }
+
+ if (cnt == 0)
+ printk(KERN_ERR "clock: pm_wait_update timeout\n");
+}
+
+static void wmt_pm_wait_busy(void)
+{
+ int cnt = 1000000;
+ while (readl(vt8500_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY) {
+ if (--cnt == 0) break;
+ cpu_relax();
+ }
+
+ if (cnt == 0)
+ printk(KERN_ERR "clock: pm_wait_busy timeout\n");
+}
+
+static void clk_cpu_speedstep(u32 plla_mul, u32 arm_div, u32 ahb_div)
+{
+ if (ahb_div > 1) {
+ /*
+ AHB, PLL, ARM
+ */
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, vt8500_pmc_base + CLK_AHB_DIVISOR);
+
+ wmt_pm_wait_update();
+ writel(plla_mul, vt8500_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, vt8500_pmc_base + CLK_ARM_DIVISOR);
+ } else {
+ /*
+ PLL, ARM, AHB
+ */
+ wmt_pm_wait_update();
+ writel(plla_mul, vt8500_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, vt8500_pmc_base + CLK_ARM_DIVISOR);
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, vt8500_pmc_base + CLK_AHB_DIVISOR);
+ }
+ wmt_pm_wait_update();
+}
+
+static unsigned long getrate_pll(struct clk *clk)
+{
+ unsigned long prate = clk->parent->rate;
+ u32 pll_mul = (readl(vt8500_pmc_base + clk->div_reg) & 0x1F);
+ u32 pll_prediv = (readl(vt8500_pmc_base + clk->div_reg) & 0x100) ? 1 :
2;
+ if (pll_mul < 4)
+ pll_mul = 1;
+ else
+ pll_mul *= 2;
+
+ clk->rate = prate * pll_mul / pll_prediv;
+ return clk->rate;
+}
+
+static int setrate_pll(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk->parent->rate;
+ unsigned long actual_plla_rate;
+ unsigned long actual_arm_rate;
+ u32 plla_mul;
+ u32 arm_div;
+ u32 ahb_div;
+ struct clk *arm_clk;
+ struct clk *ahb_clk;
+
+ arm_clk = clk_get(NULL, "arm");
+ ahb_clk = clk_get(NULL, "ahb");
+
+ /* calculate the PLL_A multiplier */
+ if (rate % prate)
+ plla_mul = rate / prate + 1;
+ else
+ plla_mul = rate / prate;
+
+ actual_plla_rate = prate * plla_mul;
+
+ /* calculate the new ARM divisor */
+ if (actual_plla_rate % arm_clk->rate)
+ arm_div = actual_plla_rate / arm_clk->rate + 1;
+ else
+ arm_div = actual_plla_rate / arm_clk->rate;
+
+ actual_arm_rate = actual_plla_rate / arm_div;
+
+ /* calculate the new AHB divisor */
+ if (actual_arm_rate % ahb_clk->rate)
+ ahb_div = actual_arm_rate / ahb_clk->rate + 1;
+ else
+ ahb_div = actual_arm_rate / ahb_clk->rate;
+
+ clk_cpu_speedstep(plla_mul, arm_div, ahb_div);
+
+ return 0;
+}
+
+static unsigned long getrate_ahb(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(vt8500_pmc_base + clk->div_reg) & 0x07);
+ if (clk_div == 0)
+ clk_div = 8;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+static int setrate_ahb(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div;
+
+ /* divisor only, so the clk can't be higher than the parent clk */
+ if (rate > prate)
+ return -EINVAL;
+
+ if (prate % rate)
+ clk_div = prate / rate + 1;
+ else
+ clk_div = prate / rate;
+
+ /* handle special case */
+ if (clk_div == 8)
+ clk_div = 0;
+
+ if (clk_div > 8)
+ return -ENOENT;
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(clk_div, vt8500_pmc_base + clk->div_reg);
+
+ wmt_pm_wait_update();
+
+ return 0;
+}
+
+static unsigned long getrate_arm(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(vt8500_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+static int setrate_arm(struct clk *clk, unsigned long rate)
+{
+ unsigned long actual_plla_rate;
+ unsigned long actual_arm_rate;
+
+ u32 plla_mul;
+ u32 arm_div;
+ u32 ahb_div;
+
+ struct clk *plla_clk;
+ struct clk *ahb_clk;
+
+ plla_clk = clk_get(NULL, "pll_a");
+ ahb_clk = clk_get(NULL, "ahb");
+
+ /*
+ calculate the PLL_A multiplier
+ PLL_A will be set to 2 * ARM_clock (if possible)
+ */
+ actual_plla_rate = 2 * rate;
+ if (actual_plla_rate % plla_clk->parent->rate)
+ plla_mul = actual_plla_rate / plla_clk->parent->rate + 1;
+ else
+ plla_mul = actual_plla_rate / plla_clk->parent->rate;
+ actual_plla_rate = plla_clk->parent->rate * plla_mul;
+
+ /* calculate the new ARM divisor */
+ if (actual_plla_rate % rate)
+ arm_div = actual_plla_rate / rate + 1;
+ else
+ arm_div = actual_plla_rate / rate;
+
+ actual_arm_rate = actual_plla_rate / arm_div;
+
+ /* calculate the new AHB divisor */
+ if (actual_arm_rate % ahb_clk->rate)
+ ahb_div = actual_arm_rate / ahb_clk->rate + 1;
+ else
+ ahb_div = actual_arm_rate / ahb_clk->rate;
+
+ clk_cpu_speedstep(plla_mul, arm_div, ahb_div);
+
+ return 0;
+}
+
+static int setrate_ddr(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div;
+
+ /* divisor only, so the clk can't be higher than the parent clk */
+ if (rate > prate)
+ return -EINVAL;
+
+ if (prate % rate)
+ clk_div = prate / rate + 1;
+ else
+ clk_div = prate / rate;
+
+ /* clk_div must be even if != 1, so +1 if odd */
+ if ((clk_div != 1) && (clk_div % 1))
+ clk_div++;
+
+ if (clk_div > 32)
+ return -ENOENT;
+
+ clk->rate = prate / clk_div;
+
+ if (clk_div == 32)
+ clk_div = 0;
+
+ wmt_pm_wait_update();
+ writel(clk_div, vt8500_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+static unsigned long getrate_sdmmc(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(vt8500_pmc_base + clk->div_reg) & 0x3F);
+
+ if (clk_div == 0)
+ clk_div = 32;
+
+ /* Check if fixed divisor is enabled (/64 fixed) */
+ if (clk_div & 0x20)
+ clk_div = 64;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+static int setrate_sdmmc(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div;
+
+ /*
+ divisor only, so the clk can't be higher than the parent clk
+ also reject rate == 0 for now
+ */
+ if ((rate > prate) || (rate == 0))
+ return -EINVAL;
+
+ if (prate % rate)
+ clk_div = prate / rate + 1;
+ else
+ clk_div = prate / rate;
+
+ /* if clk_div > 32, enable the fixed divisor */
+ if (clk_div > 32)
+ clk_div = 0x20;
+
+ clk->rate = prate / clk_div;
+
+ if (clk_div == 32)
+ clk_div = 0;
+
+ writel(clk_div, vt8500_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+static unsigned long getrate_stdmask(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(vt8500_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+static int setrate_stdmask(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div;
+
+ /* divisor only, so the clk can't be higher than the parent clk */
+ if (rate > prate)
+ return -EINVAL;
+
+ if (prate % rate)
+ clk_div = prate / rate + 1;
+ else
+ clk_div = prate / rate;
+
+ if (clk_div > 32)
+ return -ENOENT;
+
+ clk->rate = prate / clk_div;
+
+ if (clk_div == 32)
+ clk_div = 0;
+
+ wmt_pm_wait_update();
+ writel(clk_div, vt8500_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+static void enable_std(struct clk *clk)
+{
+ u32 regtmp = readl(vt8500_pmc_base + clk->en_reg);
+ writel(regtmp | (1 << clk->en_bit), vt8500_pmc_base + clk->en_reg);
+}
+
+static void disable_std(struct clk *clk)
+{
+ u32 regtmp = readl(vt8500_pmc_base + clk->en_reg);
+ writel(regtmp & ~(1 << clk->en_bit), vt8500_pmc_base + clk->en_reg);
+}
+
+
+static struct clkops clkops_pll = {
+ .getrate = &getrate_pll,
+ .setrate = &setrate_pll,
+};
+
+static struct clkops clkops_arm = {
+ .getrate = &getrate_arm,
+ .setrate = &setrate_arm,
+};
+
+static struct clkops clkops_ahb = {
+ .getrate = &getrate_ahb,
+ .setrate = &setrate_ahb,
+};
+
+static struct clkops clkops_ddr = {
+ .getrate = &getrate_stdmask,
+ .setrate = &setrate_ddr,
+};
+
+static struct clkops clkops_sdmmc = {
+ .getrate = &getrate_sdmmc,
+ .setrate = &setrate_sdmmc,
+ .enable = &enable_std,
+ .disable = &disable_std,
+};
+
+static struct clkops clkops_stdmask = {
+ .getrate = &getrate_stdmask,
+ .setrate = &setrate_stdmask,
+ .enable = &enable_std,
+ .disable = &disable_std,
+};
+
+DEFINE_CKREF(ref, 25000000);
+
+DEFINE_CKPGNP(pll_a, &clk_vt8500_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_vt8500_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_vt8500_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_vt8500_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_vt8500_pll_b, 0, &clkops_sdmmc,
CLK_SDMMC_DIVISOR, \
+ CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_NAND_DIVISOR, \
+ CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(sfm, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR,
\
+ CLK_EN_HIGH, 23);
+DEFINE_CKPGEN(mspro, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_MSPRO_DIVISOR,\
+ CLK_EN_HIGH, 17);
+DEFINE_CKPGEN(spi0, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_SPI0_DIVISOR, \
+ CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_SPI1_DIVISOR, \
+ CLK_EN_LOW, 13);
+DEFINE_CKPG(spi2, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_SPI2_DIVISOR);
+DEFINE_CKPG(dsp, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_DSP_DIVISOR);
+DEFINE_CKPGEN(lcd, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_LCD_DIVISOR,
\
+ CLK_EN_HIGH, 10);
+DEFINE_CKPGEN(lcd_high, &clk_vt8500_pll_b, 0, &clkops_stdmask, \
+ CLK_LCD_DIVISOR_HIGH, CLK_EN_HIGH, 10);
+DEFINE_CKPGEN(pcm, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_PCM_DIVISOR,
\
+ CLK_EN_LOW, 16);
+DEFINE_CKPGEN(pwm, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_PWM_DIVISOR,
\
+ CLK_EN_LOW, 14);
+DEFINE_CKPGEN(pwm2, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_PWM2_DIVISOR, \
+ CLK_EN_LOW, 14);
+
+DEFINE_CKPG(pll_c, &clk_vt8500_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_vt8500_pll_c, 0, &clkops_ddr, CLK_DDR_DIVISOR, \
+ CLK_EN_HIGH, 0);
+
+/* Enable/Disable clocks - Non-programmable */
+DEFINE_CKEN(ahb3, &clkops_stdmask, CLK_EN_LOW, 31);
+DEFINE_CKEN(ahb2_1, &clkops_stdmask, CLK_EN_LOW, 29);
+DEFINE_CKEN(h264, &clkops_stdmask, CLK_EN_LOW, 28);
+DEFINE_CKEN(ahb2_2, &clkops_stdmask, CLK_EN_LOW, 27);
+DEFINE_CKEN(ahb2_3, &clkops_stdmask, CLK_EN_LOW, 26);
+DEFINE_CKEN(ahb2_4, &clkops_stdmask, CLK_EN_LOW, 25);
+DEFINE_CKEN(ahb2_5, &clkops_stdmask, CLK_EN_LOW, 24);
+DEFINE_CKEN(vdu, &clkops_stdmask, CLK_EN_LOW, 23);
+DEFINE_CKEN(stri, &clkops_stdmask, CLK_EN_LOW, 22);
+DEFINE_CKEN(tsbk, &clkops_stdmask, CLK_EN_LOW, 21);
+DEFINE_CKEN(ahb1, &clkops_stdmask, CLK_EN_LOW, 20);
+DEFINE_CKEN(scc, &clkops_stdmask, CLK_EN_LOW, 19);
+DEFINE_CKEN(keypad, &clkops_stdmask, CLK_EN_LOW, 18);
+DEFINE_CKEN(ir, &clkops_stdmask, CLK_EN_LOW, 17);
+DEFINE_CKEN(ac97, &clkops_stdmask, CLK_EN_LOW, 15);
+DEFINE_CKEN(gpio, &clkops_stdmask, CLK_EN_LOW, 11);
+DEFINE_CKEN(aam3, &clkops_stdmask, CLK_EN_LOW, 10);
+DEFINE_CKEN(aam2, &clkops_stdmask, CLK_EN_LOW, 9);
+DEFINE_CKEN(aam1, &clkops_stdmask, CLK_EN_LOW, 8);
+DEFINE_CKEN(aam0, &clkops_stdmask, CLK_EN_LOW, 7);
+DEFINE_CKEN(i2c, &clkops_stdmask, CLK_EN_LOW, 5);
+DEFINE_CKEN(uart3, &clkops_stdmask, CLK_EN_LOW, 4);
+DEFINE_CKEN(uart2, &clkops_stdmask, CLK_EN_LOW, 3);
+DEFINE_CKEN(uart1, &clkops_stdmask, CLK_EN_LOW, 2);
+DEFINE_CKEN(uart0, &clkops_stdmask, CLK_EN_LOW, 1);
+DEFINE_CKEN(lpc, &clkops_stdmask, CLK_EN_HIGH, 27);
+DEFINE_CKEN(eth_phy, &clkops_stdmask, CLK_EN_HIGH, 26);
+DEFINE_CKEN(sae, &clkops_stdmask, CLK_EN_HIGH, 24);
+DEFINE_CKEN(usb_otg, &clkops_stdmask, CLK_EN_HIGH, 22);
+DEFINE_CKEN(eth_mac, &clkops_stdmask, CLK_EN_HIGH, 20);
+DEFINE_CKEN(pci_bridge, &clkops_stdmask, CLK_EN_HIGH, 19);
+DEFINE_CKEN(ahb_bridge, &clkops_stdmask, CLK_EN_HIGH, 13);
+DEFINE_CKEN(cf, &clkops_stdmask, CLK_EN_HIGH, 12);
+DEFINE_CKEN(ata, &clkops_stdmask, CLK_EN_HIGH, 11);
+DEFINE_CKEN(dma1, &clkops_stdmask, CLK_EN_HIGH, 9);
+DEFINE_CKEN(dma0, &clkops_stdmask, CLK_EN_HIGH, 8);
+
+static struct clk_lookup vt8500_clkregs[] = {
+ INIT_CLKREG(&clk_vt8500_ref, NULL, "ref"),
+ INIT_CLKREG(&clk_vt8500_pll_a, NULL, "pll_a"),
+ INIT_CLKREG(&clk_vt8500_pll_b, NULL, "pll_b"),
+ INIT_CLKREG(&clk_vt8500_pll_c, NULL, "pll_c"),
+ INIT_CLKREG(&clk_vt8500_arm, NULL, "arm"),
+ INIT_CLKREG(&clk_vt8500_ahb, NULL, "ahb"),
+ INIT_CLKREG(&clk_vt8500_ahb_bridge, NULL, "ahb_bridge"),
+ INIT_CLKREG(&clk_vt8500_ddr, NULL, "ddr"),
+
+ INIT_CLKREG(&clk_vt8500_sdmmc, NULL, "sdmmc"),
+
+ INIT_CLKREG(&clk_vt8500_ahb1, NULL, "ahb1"),
+ INIT_CLKREG(&clk_vt8500_ahb2_1, NULL, "ahb2-1"),
+ INIT_CLKREG(&clk_vt8500_ahb2_2, NULL, "ahb2-2"),
+ INIT_CLKREG(&clk_vt8500_ahb2_3, NULL, "ahb2-3"),
+ INIT_CLKREG(&clk_vt8500_ahb2_4, NULL, "ahb2-4"),
+ INIT_CLKREG(&clk_vt8500_ahb2_5, NULL, "ahb2-5"),
+ INIT_CLKREG(&clk_vt8500_ahb3, NULL, "ahb3"),
+
+ INIT_CLKREG(&clk_vt8500_h264, NULL, "h264"),
+ INIT_CLKREG(&clk_vt8500_vdu, NULL, "vdu"),
+ INIT_CLKREG(&clk_vt8500_stri, NULL, "stri"),
+ INIT_CLKREG(&clk_vt8500_tsbk, NULL, "tsbk"),
+
+ INIT_CLKREG(&clk_vt8500_scc, NULL, "scc"),
+ INIT_CLKREG(&clk_vt8500_keypad, NULL, "keypad"),
+ INIT_CLKREG(&clk_vt8500_ir, NULL, "ir"),
+ INIT_CLKREG(&clk_vt8500_ac97, NULL, "ac97"),
+ INIT_CLKREG(&clk_vt8500_gpio, NULL, "gpio"),
+ INIT_CLKREG(&clk_vt8500_aam3, NULL, "aam3"),
+ INIT_CLKREG(&clk_vt8500_aam2, NULL, "aam2"),
+ INIT_CLKREG(&clk_vt8500_aam1, NULL, "aam1"),
+ INIT_CLKREG(&clk_vt8500_aam0, NULL, "aam0"),
+ INIT_CLKREG(&clk_vt8500_i2c, NULL, "i2c"),
+ INIT_CLKREG(&clk_vt8500_uart3, NULL, "uart3"),
+ INIT_CLKREG(&clk_vt8500_uart2, NULL, "uart2"),
+ INIT_CLKREG(&clk_vt8500_uart1, NULL, "uart1"),
+ INIT_CLKREG(&clk_vt8500_uart0, NULL, "uart0"),
+
+ INIT_CLKREG(&clk_vt8500_lpc, NULL, "lpc"),
+ INIT_CLKREG(&clk_vt8500_eth_phy, NULL, "eth-phy"),
+ INIT_CLKREG(&clk_vt8500_sae, NULL, "sae"),
+ INIT_CLKREG(&clk_vt8500_usb_otg, NULL, "usb-otg"),
+ INIT_CLKREG(&clk_vt8500_eth_mac, NULL, "eth-mac"),
+ INIT_CLKREG(&clk_vt8500_pci_bridge, NULL, "pci-bridge"),
+ INIT_CLKREG(&clk_vt8500_cf, NULL, "cf"),
+ INIT_CLKREG(&clk_vt8500_ata, NULL, "ata"),
+ INIT_CLKREG(&clk_vt8500_dma1, NULL, "dma1"),
+ INIT_CLKREG(&clk_vt8500_dma0, NULL, "dma0"),
+
+ INIT_CLKREG(&clk_vt8500_nand, NULL, "nand"),
+ INIT_CLKREG(&clk_vt8500_sfm, NULL, "sfm"),
+ INIT_CLKREG(&clk_vt8500_mspro, NULL, "mspro"),
+ INIT_CLKREG(&clk_vt8500_spi0, NULL, "spi0"),
+ INIT_CLKREG(&clk_vt8500_spi1, NULL, "spi1"),
+ INIT_CLKREG(&clk_vt8500_spi2, NULL, "spi2"),
+ INIT_CLKREG(&clk_vt8500_pwm, NULL, "pwm"),
+ INIT_CLKREG(&clk_vt8500_pwm2, NULL, "pwm2"),
+ INIT_CLKREG(&clk_vt8500_dsp, NULL, "dsp"),
+ INIT_CLKREG(&clk_vt8500_lcd, NULL, "lcd"),
+ INIT_CLKREG(&clk_vt8500_lcd_high, NULL, "lcd-high"),
+ INIT_CLKREG(&clk_vt8500_pcm, NULL, "pcm"),
+};
+
+int vt8500_clock_init(void)
+{
+ struct clk_lookup *c;
+
+ /* map to PMC io memory */
+ vt8500_pmc_base = ioremap(VT8500_PMC_BASE, 0x380);
+ if (!vt8500_pmc_base) {
+ printk(KERN_ERR "clock: failed to remap io\n");
+ return -ENOMEM;
+ }
+
+ for (c = vt8500_clkregs; c->clk; c++)
+ clk_register(c->clk);
+
+ clkdev_add_table(vt8500_clkregs, ARRAY_SIZE(vt8500_clkregs));
+
+ return 0;
+}
diff --git a/arch/arm/mach-vt8500/clocks-wm8505.c
b/arch/arm/mach-vt8500/clocks-wm8505.c
new file mode 100644
index 0000000..8e64190
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-wm8505.c
@@ -0,0 +1,639 @@
+/*
+ * arch/arm/mach-vt8500/clocks-wm8505.c
+ *
+ * Copyright (C) 2011 Tony Prisk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <mach/wm8505_regs.h>
+#include "clock.h"
+
+#define DEFINE_CKREF(_name, _rate) \
+struct clk clk_wm8505_##_name = { \
+ .name = #_name, \
+ .parent = NULL, \
+ .ops = NULL, \
+ .rate = _rate, \
+ .type = CLK_PRIMARY, \
+ .delay = 0, \
+ .en_reg = 0, \
+ .en_bit = 0 \
+}
+
+#define DEFINE_CKEN(_name, _ops, _enreg, _enbit) \
+struct clk clk_wm8505_##_name = { \
+ .name = #_name, \
+ .parent = NULL, \
+ .ops = _ops, \
+ .rate = 0, \
+ .type = CLK_ENABLE, \
+ .delay = 0, \
+ .en_reg = _enreg, \
+ .en_bit = _enbit, \
+ .div_reg = 0 \
+}
+
+#define DEFINE_CKPG(_name, _parent, _delay, _ops, _reg) \
+struct clk clk_wm8505_##_name = { \
+ .name = #_name, \
+ .parent = _parent, \
+ .ops = _ops, \
+ .rate = 0, \
+ .type = CLK_PROGRAMABLE, \
+ .delay = _delay, \
+ .en_reg = 0, \
+ .en_bit = 0, \
+ .div_reg = _reg \
+}
+
+#define DEFINE_CKPGNP(_name, _parent, _delay, _ops, _reg) \
+struct clk clk_wm8505_##_name = { \
+ .name = #_name, \
+ .parent = _parent, \
+ .ops = _ops, \
+ .rate = 0, \
+ .type = CLK_PROGRAMABLE | CLK_NO_PROPAGATE, \
+ .delay = _delay, \
+ .en_reg = 0, \
+ .en_bit = 0, \
+ .div_reg = _reg \
+}
+
+#define DEFINE_CKPGEN(_name, _parent, _delay, _ops, _reg, _enreg, _enbit) \
+struct clk clk_wm8505_##_name = { \
+ .name = #_name, \
+ .parent = _parent, \
+ .ops = _ops, \
+ .rate = 0, \
+ .type = CLK_PROGRAMABLE | CLK_ENABLE, \
+ .delay = _delay, \
+ .en_reg = _enreg, \
+ .en_bit = _enbit, \
+ .div_reg = _reg \
+}
+
+void __iomem *wm8505_pmc_base;
+
+static void wmt_pm_wait_update(void)
+{
+ int cnt = 1000000;
+ while (readl(wm8505_pmc_base + CLK_PM_STATUS_LOW) &
CLK_PML_UPDATE_MASK) {
+ if (--cnt == 0) break;
+ cpu_relax();
+ }
+
+ if (cnt == 0)
+ printk(KERN_ERR "clock: pm_wait_update timeout\n");
+}
+
+static void wmt_pm_wait_busy(void)
+{
+ int cnt = 1000000;
+ while (readl(wm8505_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY) {
+ if (--cnt == 0) break;
+ cpu_relax();
+ }
+
+ if (cnt == 0)
+ printk(KERN_ERR "clock: pm_wait_busy timeout\n");
+}
+
+static void clk_cpu_speedstep(u32 plla_mul, u32 arm_div, u32 ahb_div)
+{
+ if (ahb_div > 1) {
+ /*
+ AHB, PLL, ARM
+ */
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, wm8505_pmc_base + CLK_AHB_DIVISOR);
+
+ wmt_pm_wait_update();
+ writel(plla_mul, wm8505_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, wm8505_pmc_base + CLK_ARM_DIVISOR);
+ } else {
+ /*
+ PLL, ARM, AHB
+ */
+ wmt_pm_wait_update();
+ writel(plla_mul, wm8505_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, wm8505_pmc_base + CLK_ARM_DIVISOR);
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, wm8505_pmc_base + CLK_AHB_DIVISOR);
+ }
+ wmt_pm_wait_update();
+}
+
+static unsigned long getrate_pll(struct clk *clk)
+{
+ unsigned long prate = clk->parent->rate;
+ u32 pll_mul = (readl(wm8505_pmc_base + clk->div_reg) & 0x1F);
+ u32 pll_prediv = (readl(wm8505_pmc_base + clk->div_reg) & 0x100) ? 1 :
2;
+ if (pll_mul < 4)
+ pll_mul = 1;
+ else
+ pll_mul *= 2;
+
+ clk->rate = prate * pll_mul / pll_prediv;
+ return clk->rate;
+}
+
+static int setrate_pll(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk->parent->rate;
+ unsigned long actual_plla_rate;
+ unsigned long actual_arm_rate;
+ u32 plla_mul;
+ u32 arm_div;
+ u32 ahb_div;
+ struct clk *arm_clk;
+ struct clk *ahb_clk;
+
+ arm_clk = clk_get(NULL, "arm");
+ ahb_clk = clk_get(NULL, "ahb");
+
+ /* calculate the PLL_A multiplier */
+ if (rate % prate)
+ plla_mul = rate / prate + 1;
+ else
+ plla_mul = rate / prate;
+
+ actual_plla_rate = prate * plla_mul;
+
+ /* calculate the new ARM divisor */
+ if (actual_plla_rate % arm_clk->rate)
+ arm_div = actual_plla_rate / arm_clk->rate + 1;
+ else
+ arm_div = actual_plla_rate / arm_clk->rate;
+
+ actual_arm_rate = actual_plla_rate / arm_div;
+
+ /* calculate the new AHB divisor */
+ if (actual_arm_rate % ahb_clk->rate)
+ ahb_div = actual_arm_rate / ahb_clk->rate + 1;
+ else
+ ahb_div = actual_arm_rate / ahb_clk->rate;
+
+ clk_cpu_speedstep(plla_mul, arm_div, ahb_div);
+
+ return 0;
+}
+
+static unsigned long getrate_ahb(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(wm8505_pmc_base + clk->div_reg) & 0x07);
+ if (clk_div == 0)
+ clk_div = 8;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+static int setrate_ahb(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div;
+
+ /* divisor only, so the clk can't be higher than the parent clk */
+ if (rate > prate)
+ return -EINVAL;
+
+ if (prate % rate)
+ clk_div = prate / rate + 1;
+ else
+ clk_div = prate / rate;
+
+ /* handle special case */
+ if (clk_div == 8)
+ clk_div = 0;
+
+ if (clk_div > 8)
+ return -ENOENT;
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(clk_div, wm8505_pmc_base + clk->div_reg);
+
+ wmt_pm_wait_update();
+
+ return 0;
+}
+
+static unsigned long getrate_arm(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(wm8505_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+static int setrate_arm(struct clk *clk, unsigned long rate)
+{
+ unsigned long actual_plla_rate;
+ unsigned long actual_arm_rate;
+
+ u32 plla_mul;
+ u32 arm_div;
+ u32 ahb_div;
+
+ struct clk *plla_clk;
+ struct clk *ahb_clk;
+
+ plla_clk = clk_get(NULL, "pll_a");
+ ahb_clk = clk_get(NULL, "ahb");
+
+ /*
+ calculate the PLL_A multiplier
+ PLL_A will be set to 2 * ARM_clock (if possible)
+ */
+ actual_plla_rate = 2 * rate;
+ if (actual_plla_rate % plla_clk->parent->rate)
+ plla_mul = actual_plla_rate / plla_clk->parent->rate + 1;
+ else
+ plla_mul = actual_plla_rate / plla_clk->parent->rate;
+ actual_plla_rate = plla_clk->parent->rate * plla_mul;
+
+ /* calculate the new ARM divisor */
+ if (actual_plla_rate % rate)
+ arm_div = actual_plla_rate / rate + 1;
+ else
+ arm_div = actual_plla_rate / rate;
+
+ actual_arm_rate = actual_plla_rate / arm_div;
+
+ /* calculate the new AHB divisor */
+ if (actual_arm_rate % ahb_clk->rate)
+ ahb_div = actual_arm_rate / ahb_clk->rate + 1;
+ else
+ ahb_div = actual_arm_rate / ahb_clk->rate;
+
+ clk_cpu_speedstep(plla_mul, arm_div, ahb_div);
+
+ return 0;
+}
+
+static int setrate_ddr(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div;
+
+ /* divisor only, so the clk can't be higher than the parent clk */
+ if (rate > prate)
+ return -EINVAL;
+
+ if (prate % rate)
+ clk_div = prate / rate + 1;
+ else
+ clk_div = prate / rate;
+
+ /* clk_div must be even if != 1, so +1 if odd */
+ if ((clk_div != 1) && (clk_div % 1))
+ clk_div++;
+
+ if (clk_div > 32)
+ return -ENOENT;
+
+ clk->rate = prate / clk_div;
+
+ if (clk_div == 32)
+ clk_div = 0;
+
+ wmt_pm_wait_update();
+ writel(clk_div, wm8505_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+static unsigned long getrate_sdmmc(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(wm8505_pmc_base + clk->div_reg) & 0x3F);
+
+ if (clk_div == 0)
+ clk_div = 32;
+
+ /* Check if fixed divisor is enabled (/64 fixed) */
+ if (clk_div & 0x20)
+ clk_div = 64;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+static int setrate_sdmmc(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div;
+
+ /*
+ divisor only, so the clk can't be higher than the parent clk
+ also reject rate == 0 for now
+ */
+ if ((rate > prate) || (rate == 0))
+ return -EINVAL;
+
+ if (prate % rate)
+ clk_div = prate / rate + 1;
+ else
+ clk_div = prate / rate;
+
+ /* if clk_div > 32, enable the fixed divisor */
+ if (clk_div > 32)
+ clk_div = 0x20;
+
+ clk->rate = prate / clk_div;
+
+ if (clk_div == 32)
+ clk_div = 0;
+
+ writel(clk_div, wm8505_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+static unsigned long getrate_stdmask(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(wm8505_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+static int setrate_stdmask(struct clk *clk, unsigned long rate)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div;
+
+ /* divisor only, so the clk can't be higher than the parent clk */
+ if (rate > prate)
+ return -EINVAL;
+
+ if (prate % rate)
+ clk_div = prate / rate + 1;
+ else
+ clk_div = prate / rate;
+
+ if (clk_div > 32)
+ return -ENOENT;
+
+ clk->rate = prate / clk_div;
+
+ if (clk_div == 32)
+ clk_div = 0;
+
+ wmt_pm_wait_update();
+ writel(clk_div, wm8505_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+static void enable_std(struct clk *clk)
+{
+ u32 regtmp = readl(wm8505_pmc_base + clk->en_reg);
+ writel(regtmp | (1 << clk->en_bit), wm8505_pmc_base + clk->en_reg);
+}
+
+static void disable_std(struct clk *clk)
+{
+ u32 regtmp = readl(wm8505_pmc_base + clk->en_reg);
+ writel(regtmp & ~(1 << clk->en_bit), wm8505_pmc_base + clk->en_reg);
+}
+
+
+static struct clkops clkops_pll = {
+ .getrate = &getrate_pll,
+ .setrate = &setrate_pll,
+};
+
+static struct clkops clkops_arm = {
+ .getrate = &getrate_arm,
+ .setrate = &setrate_arm,
+};
+
+static struct clkops clkops_ahb = {
+ .getrate = &getrate_ahb,
+ .setrate = &setrate_ahb,
+};
+
+static struct clkops clkops_ddr = {
+ .getrate = &getrate_stdmask,
+ .setrate = &setrate_ddr,
+};
+
+static struct clkops clkops_sdmmc = {
+ .getrate = &getrate_sdmmc,
+ .setrate = &setrate_sdmmc,
+ .enable = &enable_std,
+ .disable = &disable_std,
+};
+
+static struct clkops clkops_stdmask = {
+ .getrate = &getrate_stdmask,
+ .setrate = &setrate_stdmask,
+ .enable = &enable_std,
+ .disable = &disable_std,
+};
+
+
+DEFINE_CKREF(ref, 25000000);
+
+DEFINE_CKPGNP(pll_a, &clk_wm8505_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_wm8505_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_wm8505_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+DEFINE_CKPG(apb, &clk_wm8505_arm, 0, &clkops_stdmask, CLK_APB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_wm8505_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_wm8505_pll_b, 0, &clkops_sdmmc,
CLK_SDMMC_DIVISOR, \
+ CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_NAND_DIVISOR, \
+ CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(kbd_pre, &clk_wm8505_pll_b, 0, &clkops_stdmask, \
+ CLK_KBD_DIVISOR_PRE, CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(kbd, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_KBD_DIVISOR,
\
+ CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(sfm, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR,
\
+ CLK_EN_HIGH, 23);
+DEFINE_CKPG(genet, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_GENET_DIVISOR);
+DEFINE_CKPGEN(nor, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_NOR_DIVISOR,
\
+ CLK_EN_HIGH, 3);
+DEFINE_CKPGEN(spi0, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_SPI0_DIVISOR, \
+ CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_SPI1_DIVISOR, \
+ CLK_EN_LOW, 13);
+DEFINE_CKPGEN(spi2, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_SPI2_DIVISOR, \
+ CLK_EN_LOW, 14);
+DEFINE_CKPGEN(pwm, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_PWM_DIVISOR,
\
+ CLK_EN_LOW, 10);
+DEFINE_CKPGEN(na0, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_NA0_DIVISOR,
\
+ CLK_EN_HIGH, 1);
+DEFINE_CKPGEN(na12, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_NA12_DIVISOR, \
+ CLK_EN_HIGH, 2);
+DEFINE_CKPGEN(i2c0, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_I2C0_DIVISOR, \
+ CLK_EN_LOW, 5);
+DEFINE_CKPGEN(i2c1, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_I2C1_DIVISOR, \
+ CLK_EN_LOW, 0);
+DEFINE_CKPGEN(dvo, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_DVO_DIVISOR,
\
+ CLK_EN_LOW, 18);
+DEFINE_CKPG(ro1, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_RO1_DIVISOR);
+DEFINE_CKPG(ro2, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_RO2_DIVISOR);
+
+DEFINE_CKPG(pll_c, &clk_wm8505_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_wm8505_pll_c, 0, &clkops_ddr, CLK_DDR_DIVISOR, \
+ CLK_EN_HIGH, 0);
+
+DEFINE_CKPG(pll_d, &clk_wm8505_ref, 0, &clkops_pll, CLK_PLLD_MULTIPLIER);
+
+
+/* Enable/Disable clocks - Non-programmable */
+DEFINE_CKEN(govrhd, &clkops_stdmask, CLK_EN_LOW, 30);
+DEFINE_CKEN(ge, &clkops_stdmask, CLK_EN_LOW, 29);
+DEFINE_CKEN(jenc, &clkops_stdmask, CLK_EN_LOW, 27);
+DEFINE_CKEN(amp, &clkops_stdmask, CLK_EN_LOW, 24);
+DEFINE_CKEN(uart5, &clkops_stdmask, CLK_EN_LOW, 23);
+DEFINE_CKEN(uart4, &clkops_stdmask, CLK_EN_LOW, 22);
+DEFINE_CKEN(scc, &clkops_stdmask, CLK_EN_LOW, 21);
+DEFINE_CKEN(ac97, &clkops_stdmask, CLK_EN_LOW, 19);
+DEFINE_CKEN(cir, &clkops_stdmask, CLK_EN_LOW, 17);
+DEFINE_CKEN(i2s, &clkops_stdmask, CLK_EN_LOW, 16);
+DEFINE_CKEN(gpio, &clkops_stdmask, CLK_EN_LOW, 11);
+DEFINE_CKEN(keypad, &clkops_stdmask, CLK_EN_LOW, 9);
+DEFINE_CKEN(rtc, &clkops_stdmask, CLK_EN_LOW, 7);
+DEFINE_CKEN(i2c_slave, &clkops_stdmask, CLK_EN_LOW, 6);
+DEFINE_CKEN(uart3, &clkops_stdmask, CLK_EN_LOW, 4);
+DEFINE_CKEN(uart2, &clkops_stdmask, CLK_EN_LOW, 3);
+DEFINE_CKEN(uart1, &clkops_stdmask, CLK_EN_LOW, 2);
+DEFINE_CKEN(uart0, &clkops_stdmask, CLK_EN_LOW, 1);
+DEFINE_CKEN(vpp, &clkops_stdmask, CLK_EN_HIGH, 31);
+DEFINE_CKEN(vid, &clkops_stdmask, CLK_EN_HIGH, 30);
+DEFINE_CKEN(govw, &clkops_stdmask, CLK_EN_HIGH, 29);
+DEFINE_CKEN(scl444u, &clkops_stdmask, CLK_EN_HIGH, 28);
+DEFINE_CKEN(eth_phy, &clkops_stdmask, CLK_EN_HIGH, 26);
+DEFINE_CKEN(sae, &clkops_stdmask, CLK_EN_HIGH, 24);
+DEFINE_CKEN(sys, &clkops_stdmask, CLK_EN_HIGH, 21);
+DEFINE_CKEN(eth_mac0, &clkops_stdmask, CLK_EN_HIGH, 20);
+DEFINE_CKEN(sdtv, &clkops_stdmask, CLK_EN_HIGH, 14);
+DEFINE_CKEN(ahb_bridge, &clkops_stdmask, CLK_EN_HIGH, 13);
+DEFINE_CKEN(pdma, &clkops_stdmask, CLK_EN_HIGH, 9);
+DEFINE_CKEN(udc, &clkops_stdmask, CLK_EN_HIGH, 8);
+DEFINE_CKEN(uhc, &clkops_stdmask, CLK_EN_HIGH, 7);
+DEFINE_CKEN(dma, &clkops_stdmask, CLK_EN_HIGH, 5);
+
+static struct clk_lookup wm8505_clkregs[] = {
+ INIT_CLKREG(&clk_wm8505_ref, NULL, "ref"),
+ INIT_CLKREG(&clk_wm8505_pll_a, NULL, "pll_a"),
+ INIT_CLKREG(&clk_wm8505_pll_b, NULL, "pll_b"),
+ INIT_CLKREG(&clk_wm8505_pll_c, NULL, "pll_c"),
+ INIT_CLKREG(&clk_wm8505_pll_d, NULL, "pll_d"),
+ INIT_CLKREG(&clk_wm8505_arm, NULL, "arm"),
+ INIT_CLKREG(&clk_wm8505_ahb, NULL, "ahb"),
+ INIT_CLKREG(&clk_wm8505_ahb_bridge, NULL, "ahb_bridge"),
+ INIT_CLKREG(&clk_wm8505_apb, NULL, "apb"),
+ INIT_CLKREG(&clk_wm8505_ddr, NULL, "ddr"),
+
+ INIT_CLKREG(&clk_wm8505_nor, NULL, "nor"),
+ INIT_CLKREG(&clk_wm8505_nand, NULL, "nand"),
+ INIT_CLKREG(&clk_wm8505_sdmmc, NULL, "sdmmc"),
+
+ INIT_CLKREG(&clk_wm8505_keypad, NULL, "keypad"),
+ INIT_CLKREG(&clk_wm8505_kbd_pre, NULL, "kbd_pre"),
+ INIT_CLKREG(&clk_wm8505_kbd, NULL, "kbd"),
+ INIT_CLKREG(&clk_wm8505_sfm, NULL, "sfm"),
+ INIT_CLKREG(&clk_wm8505_genet, NULL, "genet"),
+
+ INIT_CLKREG(&clk_wm8505_spi0, NULL, "spi0"),
+ INIT_CLKREG(&clk_wm8505_spi1, NULL, "spi1"),
+ INIT_CLKREG(&clk_wm8505_spi2, NULL, "spi2"),
+
+ INIT_CLKREG(&clk_wm8505_govrhd, NULL, "govrhd"),
+ INIT_CLKREG(&clk_wm8505_govw, NULL, "govw"),
+ INIT_CLKREG(&clk_wm8505_ge, NULL, "ge"),
+ INIT_CLKREG(&clk_wm8505_pwm, NULL, "pwm"),
+ INIT_CLKREG(&clk_wm8505_dvo, NULL, "dvo"),
+ INIT_CLKREG(&clk_wm8505_jenc, NULL, "jenc"),
+ INIT_CLKREG(&clk_wm8505_sdtv, NULL, "sdtv"),
+
+ INIT_CLKREG(&clk_wm8505_cir, NULL, "cir"),
+ INIT_CLKREG(&clk_wm8505_gpio, NULL, "gpio"),
+ INIT_CLKREG(&clk_wm8505_rtc, NULL, "rtc"),
+
+ INIT_CLKREG(&clk_wm8505_i2c0, NULL, "i2c0"),
+ INIT_CLKREG(&clk_wm8505_i2c1, NULL, "i2c1"),
+ INIT_CLKREG(&clk_wm8505_i2c_slave, NULL, "i2c_slave"),
+
+ INIT_CLKREG(&clk_wm8505_ac97, NULL, "ac97"),
+ INIT_CLKREG(&clk_wm8505_i2s, NULL, "i2s"),
+
+ INIT_CLKREG(&clk_wm8505_eth_phy, NULL, "eth_phy"),
+ INIT_CLKREG(&clk_wm8505_eth_mac0, NULL, "eth_mac0"),
+
+ INIT_CLKREG(&clk_wm8505_uart5, NULL, "uart5"),
+ INIT_CLKREG(&clk_wm8505_uart4, NULL, "uart4"),
+ INIT_CLKREG(&clk_wm8505_uart3, NULL, "uart3"),
+ INIT_CLKREG(&clk_wm8505_uart2, NULL, "uart2"),
+ INIT_CLKREG(&clk_wm8505_uart1, NULL, "uart1"),
+ INIT_CLKREG(&clk_wm8505_uart0, NULL, "uart0"),
+
+ INIT_CLKREG(&clk_wm8505_na0, NULL, "na0"),
+ INIT_CLKREG(&clk_wm8505_na12, NULL, "na12"),
+ INIT_CLKREG(&clk_wm8505_ro1, NULL, "ro1"),
+ INIT_CLKREG(&clk_wm8505_ro2, NULL, "ro2"),
+ INIT_CLKREG(&clk_wm8505_amp, NULL, "amp"),
+ INIT_CLKREG(&clk_wm8505_scc, NULL, "scc"),
+ INIT_CLKREG(&clk_wm8505_vpp, NULL, "vpp"),
+ INIT_CLKREG(&clk_wm8505_vid, NULL, "vid"),
+ INIT_CLKREG(&clk_wm8505_scl444u, NULL, "scl444u"),
+ INIT_CLKREG(&clk_wm8505_sae, NULL, "sae"),
+ INIT_CLKREG(&clk_wm8505_sys, NULL, "sys"),
+ INIT_CLKREG(&clk_wm8505_udc, NULL, "udc"),
+ INIT_CLKREG(&clk_wm8505_uhc, NULL, "uhc"),
+ INIT_CLKREG(&clk_wm8505_dma, NULL, "dma"),
+ INIT_CLKREG(&clk_wm8505_pdma, NULL, "pdma"),
+};
+
+int wm8505_clock_init(void)
+{
+ struct clk_lookup *c;
+
+ /* map to PMC io memory */
+ wm8505_pmc_base = ioremap(WM8505_PMC_BASE, 0x380);
+ if (!wm8505_pmc_base) {
+ printk(KERN_ERR "clock: failed to remap io\n");
+ return -ENOMEM;
+ }
+
+ for (c = wm8505_clkregs; c->clk; c++)
+ clk_register(c->clk);
+
+ clkdev_add_table(wm8505_clkregs, ARRAY_SIZE(wm8505_clkregs));
+
+ return 0;
+}
diff --git a/arch/arm/mach-vt8500/include/mach/clkdev.h
b/arch/arm/mach-vt8500/include/mach/clkdev.h
new file mode 100644
index 0000000..62b0a6a
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/clkdev.h
@@ -0,0 +1,7 @@
+#ifndef __WMT_CLKDEV_H
+#define __WMT_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c
b/arch/arm/mach-vt8500/wm8505_7in.c
index e73aadb..ce75c77 100644
--- a/arch/arm/mach-vt8500/wm8505_7in.c
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -25,6 +25,7 @@
#include <asm/mach/arch.h>
#include "devices.h"
+#include "clock.h"
static void __iomem *pmc_hiber;
@@ -65,6 +66,7 @@ void __init wm8505_7in_init(void)
wm8505_set_resources();
platform_add_devices(devices, ARRAY_SIZE(devices));
vt8500_gpio_init();
+ wm8505_clock_init();
}
MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110228/1057380b/attachment-0001.html>
^ permalink raw reply [flat|nested] 2+ messages in thread
* PATCH [1/1] V4 - Clock Support for VT8500/WM8505
@ 2011-02-27 23:33 Tony Prisk
0 siblings, 0 replies; 2+ messages in thread
From: Tony Prisk @ 2011-02-27 23:33 UTC (permalink / raw)
To: linux-arm-kernel
Patch to provide clock support on VT8500-/WM8505- based SoC.
V2: Included missing mach/clkdev.h
Fix errors in clocks-vt8500.c
V3: Correct code to allow VT8500 + WM8505 selected concurrently (for
runtime detection)
Added patch to initialize the clocks from existing board code
(bv07.c, wm8505_7in.c)
V4: Add error catch for failed ioremap
Signed-off-by: Tony Prisk (linux at prisktech.co.nz)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5e34579..0593323 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -878,6 +878,8 @@ config ARCH_VT8500
??? select GENERIC_CLOCKEVENTS
??? select ARCH_REQUIRE_GPIOLIB
??? select HAVE_PWM
+??? select CLKDEV_LOOKUP
+??? select HAVE_CLK
??? help
??? ??Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
endchoice
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
index 81aedb7..c0ae083 100644
--- a/arch/arm/mach-vt8500/Makefile
+++ b/arch/arm/mach-vt8500/Makefile
@@ -1,7 +1,7 @@
-obj-y += devices.o gpio.o irq.o timer.o
+obj-y += clock.o devices.o gpio.o irq.o timer.o
-obj-$(CONFIG_VTWM_VERSION_VT8500) += devices-vt8500.o
-obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
+obj-$(CONFIG_VTWM_VERSION_VT8500) += devices-vt8500.o clocks-vt8500.o
+obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o clocks-wm8505.o
?obj-$(CONFIG_MACH_BV07) += bv07.o
obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
index 94a261d..b637167 100644
--- a/arch/arm/mach-vt8500/bv07.c
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -25,6 +25,7 @@
#include <asm/mach/arch.h>
?#include "devices.h"
+#include "clock.h"
?static void __iomem *pmc_hiber;
@@ -65,6 +66,7 @@ void __init bv07_init(void)
??? vt8500_set_resources();
??? platform_add_devices(devices, ARRAY_SIZE(devices));
??? vt8500_gpio_init();
+??? vt8500_clock_init();
}
?MACHINE_START(BV07, "Benign BV07 Mini Netbook")
diff --git a/arch/arm/mach-vt8500/clock.c b/arch/arm/mach-vt8500/clock.c
new file mode 100644
index 0000000..9ce3a74
--- /dev/null
+++ b/arch/arm/mach-vt8500/clock.c
@@ -0,0 +1,164 @@
+/*
+ *? arch/arm/mach-vt8500/clock.c
+ *
+ *? Copyright (C) 2011 Tony Prisk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA? 02111-1307?
USA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+
+#include "clock.h"
+
+static LIST_HEAD(clocks);
+
+static DEFINE_SPINLOCK(clocks_lock);
+static DEFINE_MUTEX(clocks_mutex);
+
+static void __clk_enable(struct clk *clk)
+{
+??? if (clk->parent)
+????????? __clk_enable(clk->parent);
+??? if (clk->usecount++ == 0)
+????????? if (clk->type & CLK_ENABLE)
+?????????????? clk->ops->enable(clk);
+}
+
+static void __clk_disable(struct clk *clk)
+{
+??? if (--clk->usecount == 0)
+????????? if (clk->type & CLK_ENABLE)
+?????????????? clk->ops->disable(clk);
+??? if (clk->parent)
+????????? __clk_disable(clk->parent);
+}
+
+int clk_enable(struct clk *clk)
+{
+??? unsigned long flags;
+
+??? spin_lock_irqsave(&clocks_lock, flags);
+??? __clk_enable(clk);
+??? spin_unlock_irqrestore(&clocks_lock, flags);
+
+??? if (clk->delay)
+????????? udelay(clk->delay);
+
+??? return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+??? unsigned long flags;
+
+??? WARN_ON(clk->usecount == 0);
+
+??? spin_lock_irqsave(&clocks_lock, flags);
+??? __clk_disable(clk);
+??? spin_unlock_irqrestore(&clocks_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+??? unsigned long rate;
+
+??? if (clk == NULL || IS_ERR(clk))
+????????? return -EINVAL;
+
+??? rate = clk->rate;
+??? if ((clk->ops->getrate) && (clk->type & CLK_PROGRAMABLE))
+????????? rate = clk->ops->getrate(clk);
+
+??? return rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+static void propagate_rate(struct clk *root)
+{
+??? struct clk *clk;
+
+??? list_for_each_entry(clk, &root->children, childnode) {
+????????? if (clk->ops->setrate)
+?????????????? clk->ops->setrate(clk, clk->rate);
+????????? if (clk->ops->getrate)
+?????????????? clk->rate = clk->ops->getrate(clk);
+????????? propagate_rate(clk);
+??? }
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+??? unsigned long flags;
+
+??? if (clk == NULL || IS_ERR(clk))
+????????? return -EINVAL;
+
+??? if (!(clk->type & CLK_PROGRAMABLE))
+????????? return -EINVAL;
+
+??? if (clk->ops->setrate)
+????????? clk->ops->setrate(clk, rate);
+
+??? if (clk->ops->getrate)
+????????? clk->rate = clk->ops->getrate(clk);
+
+??? spin_lock_irqsave(&clocks_lock, flags);
+??? if (!(clk->type & CLK_NO_PROPAGATE))
+????????? propagate_rate(clk);
+??? spin_unlock_irqrestore(&clocks_lock, flags);
+
+??? return 0;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+int clk_register(struct clk *clk)
+{
+??? if (clk == NULL || IS_ERR(clk))
+????????? return -EINVAL;
+
+??? if (clk->parent && !clk->parent->rate)
+????????? return -EINVAL;
+
+??? INIT_LIST_HEAD(&clk->children);
+
+??? mutex_lock(&clocks_mutex);
+??? list_add_tail(&clk->node, &clocks);
+??? if (clk->parent)
+????????? list_add_tail(&clk->childnode, &clk->parent->children);
+??? mutex_unlock(&clocks_mutex);
+
+??? /* if rate already set, use it */
+??? if (clk->rate)
+????????? return 0;
+
+??? /* see if we can calculate the rate */
+??? if (clk->ops->getrate)
+????????? clk->ops->getrate(clk);
+??? /* otherwise use the parents rate */
+??? else if (clk->parent)
+????????? clk->rate = clk->parent->rate;
+
+??? return 0;
+}
diff --git a/arch/arm/mach-vt8500/clock.h b/arch/arm/mach-vt8500/clock.h
new file mode 100644
index 0000000..a4bd94f
--- /dev/null
+++ b/arch/arm/mach-vt8500/clock.h
@@ -0,0 +1,130 @@
+/*
+ *? arch/arm/mach-vt8500/clock.h
+ *
+ *? Copyright (C) 2011 Tony Prisk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA? 02111-1307?
USA
+ */
+
+#ifndef __WMT_CLOCK_H
+#define __WMT_CLOCK_H
+
+#include <linux/list.h>
+#include <linux/clkdev.h>
+
+#define CLK_PRIMARY?????? 0x00
+#define CLK_PROGRAMABLE??????? 0x01
+#define CLK_ENABLE??????? 0x02
+#define CLK_NO_PROPAGATE? 0x04
+
+#define CLK_PM_STATUS_LOW 0x000
+#define CLK_PML_ANY_CLK_UPDATING??? 0x00000008
+#define CLK_PML_PLL_UPDATING??????? 0x00000010
+#define CLK_PML_ARM_UPDATING??????? 0x00000080
+#define CLK_PML_AHB_UPDATING??????? 0x00000100
+#define CLK_PML_UPDATE_MASK???????? 0x1FFF9B37
+#define CLK_PML_BUSY?????????? 0xFFFFFFFE
+#define CLK_PM_STATUS_HIGH???? 0x004
+
+
+#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
+
+/* common clocks */
+#define CLK_ARM_DIVISOR??????? 0x300
+#define CLK_AHB_DIVISOR??????? 0x304
+#define CLK_DDR_DIVISOR??????? 0x310 /* mask = 0x1F special */
+#define CLK_SFM_DIVISOR??????? 0x314
+#define CLK_SDMMC_DIVISOR 0x328 /* mask = 0x1F special */
+#define CLK_GENET_DIVISOR 0x32C
+#define CLK_NAND_DIVISOR? 0x330
+#define CLK_NOR_DIVISOR??????? 0x334
+#define CLK_SPI0_DIVISOR? 0x33C
+#define CLK_SPI1_DIVISOR? 0x340
+#define CLK_SPI2_DIVISOR? 0x344
+#define CLK_PWM_DIVISOR??????? 0x348
+
+/* VT8500 clocks */
+#define CLK_DSP_DIVISOR??????? 0x308
+#define CLK_LCD_DIVISOR??????? 0x30C
+#define CLK_PCM_DIVISOR??????? 0x320
+#define CLK_PWM2_DIVISOR? 0x324
+#define CLK_MSPRO_DIVISOR 0x32C
+#define CLK_NAND_DIVISOR? 0x330
+#define CLK_LCD_DIVISOR_HIGH?? 0x334
+
+/* WM8505 clocks */
+#define CLK_KBD_DIVISOR_PRE??? 0x318
+#define CLK_KBD_DIVISOR??????? 0x31C
+#define CLK_APB_DIVISOR??????? 0x350
+#define CLK_NA0_DIVISOR??????? 0x358
+#define CLK_NA12_DIVISOR? 0x35C
+#define CLK_I2C0_DIVISOR? 0x36C
+#define CLK_I2C1_DIVISOR? 0x370
+#define CLK_DVO_DIVISOR??????? 0x374
+#define CLK_RO1_DIVISOR??????? 0x378
+#define CLK_RO2_DIVISOR??????? 0x37C
+
+
+#define INIT_CLKREG(_clk, _devid, _conid)????? \
+{?????????????????????????????????? \
+??? .clk = _clk,???????????????????????? \
+??? .dev_id = _devid,??????????????????? \
+??? .con_id = _conid???????????????????? \
+}
+
+struct clkops {
+??? void?????? (*enable)(struct clk *);
+??? void?????? (*disable)(struct clk *);
+
+??? unsigned long?? (*getrate)(struct clk *);
+??? int??????? (*setrate)(struct clk *, unsigned long rate);
+};
+
+struct clk {
+??? struct list_head???? node;
+??? struct clk????? *parent;
+??? const struct clkops? *ops;
+??? const char????? *name;
+??? unsigned long??????? rate;
+??? unsigned int???????? type;
+??? unsigned int???????? delay;
+??? unsigned int???????? usecount;
+
+??? unsigned int???????? en_reg;
+??? unsigned int???????? en_bit;
+
+??? unsigned int???????? div_reg;
+
+??? struct list_head???? children;
+??? struct list_head???? childnode;
+};
+
+#ifdef CONFIG_VTWM_VERSION_WM8505
+extern int wm8505_clock_init(void);
+#endif
+
+#ifdef CONFIG_VTWM_VERSION_VT8500
+extern int vt8500_clock_init(void);
+#endif
+
+int clk_register(struct clk *clk);
+
+#endif
diff --git a/arch/arm/mach-vt8500/clocks-vt8500.c
b/arch/arm/mach-vt8500/clocks-vt8500.c
new file mode 100644
index 0000000..e586b67
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-vt8500.c
@@ -0,0 +1,622 @@
+/*
+ *? arch/arm/mach-vt8500/clocks-vt8500.c
+ *
+ *? Copyright (C) 2011 Tony Prisk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA? 02111-1307?
USA
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <mach/vt8500_regs.h>
+#include "clock.h"
+
+#define DEFINE_CKREF(_name, _rate)??????? \
+struct clk clk_vt8500_##_name = {???????? \
+??? .name = #_name,??????????? \
+??? .parent = NULL,???????????????? \
+??? .ops = NULL,??????????????????? \
+??? .rate = _rate,???????????? \
+??? .type = CLK_PRIMARY,???????????? \
+??? .delay? = 0,??????????????????? \
+??? .en_reg = 0,??????????????????? \
+??? .en_bit = 0???????????????????? \
+}
+
+#define DEFINE_CKEN(_name, _ops, _enreg, _enbit)??? \
+struct clk clk_vt8500_##_name = {????????????? \
+??? .name = #_name,????????????????? \
+??? .parent = NULL,????????????????????? \
+??? .ops = _ops,???????????????????????? \
+??? .rate = 0,?????????????????????? \
+??? .type = CLK_ENABLE,?????????????????? \
+??? .delay? = 0,???????????????????????? \
+??? .en_reg = _enreg,??????????????????? \
+??? .en_bit = _enbit,??????????????????? \
+??? .div_reg = 0???????????????????????? \
+}
+
+#define DEFINE_CKPG(_name, _parent, _delay, _ops, _reg)?? \
+struct clk clk_vt8500_##_name = {????????????? \
+??? .name = #_name,????????????????? \
+??? .parent = _parent,?????????????????? \
+??? .ops = _ops,???????????????????????? \
+??? .rate = 0,?????????????????????? \
+??? .type = CLK_PROGRAMABLE,????????????? \
+??? .delay? = _delay,??????????????????? \
+??? .en_reg = 0,???????????????????????? \
+??? .en_bit = 0,???????????????????????? \
+??? .div_reg = _reg????????????????????? \
+}
+
+#define DEFINE_CKPGNP(_name, _parent, _delay, _ops, _reg) \
+struct clk clk_vt8500_##_name = {????????????? \
+??? .name = #_name,????????????????? \
+??? .parent = _parent,?????????????????? \
+??? .ops = _ops,???????????????????????? \
+??? .rate = 0,?????????????????????? \
+??? .type = CLK_PROGRAMABLE | CLK_NO_PROPAGATE, \
+??? .delay? = _delay,??????????????????? \
+??? .en_reg = 0,???????????????????????? \
+??? .en_bit = 0,???????????????????????? \
+??? .div_reg = _reg????????????????????? \
+}
+
+#define DEFINE_CKPGEN(_name, _parent, _delay, _ops, _reg, _enreg, _enbit) \
+struct clk clk_vt8500_##_name = {????????????? \
+??? .name = #_name,????????????????? \
+??? .parent = _parent,?????????????????? \
+??? .ops = _ops,???????????????????????? \
+??? .rate = 0,?????????????????????? \
+??? .type = CLK_PROGRAMABLE | CLK_ENABLE,????? \
+??? .delay? = _delay,??????????????????? \
+??? .en_reg = _enreg,??????????????????? \
+??? .en_bit = _enbit,??????????????????? \
+??? .div_reg = _reg????????????????????? \
+}
+
+void __iomem *vt8500_pmc_base;
+
+static void wmt_pm_wait_update(void)
+{
+??? int cnt = 1000000;
+??? while (readl(vt8500_pmc_base + CLK_PM_STATUS_LOW) &
CLK_PML_UPDATE_MASK) {
+????????? if (--cnt == 0) break;
+????????? cpu_relax();
+??? }
+
+??? if (cnt == 0)
+????????? printk(KERN_ERR "clock: pm_wait_update timeout\n");
+}
+
+static void wmt_pm_wait_busy(void)
+{
+??? int cnt = 1000000;
+??? while (readl(vt8500_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY) {
+????????? if (--cnt == 0) break;
+????????? cpu_relax();
+??? }
+
+??? if (cnt == 0)
+????????? printk(KERN_ERR "clock: pm_wait_busy timeout\n");
+}
+
+static void clk_cpu_speedstep(u32 plla_mul, u32 arm_div, u32 ahb_div)
+{
+??? if (ahb_div > 1) {
+????????? /*
+????????? ?? AHB, PLL, ARM
+????????? */
+????????? wmt_pm_wait_busy();
+????????? wmt_pm_wait_update();
+????????? writel(ahb_div, vt8500_pmc_base + CLK_AHB_DIVISOR);
+
+????????? wmt_pm_wait_update();
+????????? writel(plla_mul, vt8500_pmc_base + CLK_PLLA_MULTIPLIER);
+
+????????? wmt_pm_wait_update();
+????????? writel(arm_div, vt8500_pmc_base + CLK_ARM_DIVISOR);
+??? } else {
+????????? /*
+????????? ?? PLL, ARM, AHB
+????????? */
+????????? wmt_pm_wait_update();
+????????? writel(plla_mul, vt8500_pmc_base + CLK_PLLA_MULTIPLIER);
+
+????????? wmt_pm_wait_update();
+????????? writel(arm_div, vt8500_pmc_base + CLK_ARM_DIVISOR);
+
+????????? wmt_pm_wait_busy();
+????????? wmt_pm_wait_update();
+????????? writel(ahb_div, vt8500_pmc_base + CLK_AHB_DIVISOR);
+??? }
+??? wmt_pm_wait_update();
+}
+
+static unsigned long getrate_pll(struct clk *clk)
+{
+??? unsigned long prate = clk->parent->rate;
+??? u32 pll_mul = (readl(vt8500_pmc_base + clk->div_reg) & 0x1F);
+??? u32 pll_prediv = (readl(vt8500_pmc_base + clk->div_reg) & 0x100) ? 1 :
2;
+??? if (pll_mul < 4)
+????????? pll_mul = 1;
+??? else
+????????? pll_mul *= 2;
+
+??? clk->rate = prate * pll_mul / pll_prediv;
+??? return clk->rate;
+}
+
+static int setrate_pll(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk->parent->rate;
+??? unsigned long actual_plla_rate;
+??? unsigned long actual_arm_rate;
+??? u32 plla_mul;
+??? u32 arm_div;
+??? u32 ahb_div;
+??? struct clk *arm_clk;
+??? struct clk *ahb_clk;
+
+??? arm_clk = clk_get(NULL, "arm");
+??? ahb_clk = clk_get(NULL, "ahb");
+
+??? /* calculate the PLL_A multiplier */
+??? if (rate % prate)
+????????? plla_mul = rate / prate + 1;
+??? else
+????????? plla_mul = rate / prate;
+
+??? actual_plla_rate = prate * plla_mul;
+
+??? /* calculate the new ARM divisor */
+??? if (actual_plla_rate % arm_clk->rate)
+????????? arm_div = actual_plla_rate / arm_clk->rate + 1;
+??? else
+????????? arm_div = actual_plla_rate / arm_clk->rate;
+
+??? actual_arm_rate = actual_plla_rate / arm_div;
+
+??? /* calculate the new AHB divisor */
+??? if (actual_arm_rate % ahb_clk->rate)
+????????? ahb_div = actual_arm_rate / ahb_clk->rate + 1;
+??? else
+????????? ahb_div = actual_arm_rate / ahb_clk->rate;
+
+??? clk_cpu_speedstep(plla_mul, arm_div, ahb_div);
+
+??? return 0;
+}
+
+static unsigned long getrate_ahb(struct clk *clk)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div = (readl(vt8500_pmc_base + clk->div_reg) & 0x07);
+??? if (clk_div == 0)
+????????? clk_div = 8;
+
+??? clk->rate = prate / clk_div;
+??? return clk->rate;
+}
+
+static int setrate_ahb(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div;
+
+??? /* divisor only, so the clk can't be higher than the parent clk */
+??? if (rate > prate)
+????????? return -EINVAL;
+
+??? if (prate % rate)
+????????? clk_div = prate / rate + 1;
+??? else
+????????? clk_div = prate / rate;
+
+??? /* handle special case */
+??? if (clk_div == 8)
+????????? clk_div = 0;
+
+??? if (clk_div > 8)
+????????? return -ENOENT;
+
+??? wmt_pm_wait_busy();
+??? wmt_pm_wait_update();
+??? writel(clk_div, vt8500_pmc_base + clk->div_reg);
+
+??? wmt_pm_wait_update();
+
+??? return 0;
+}
+
+static unsigned long getrate_arm(struct clk *clk)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div = (readl(vt8500_pmc_base + clk->div_reg) & 0x1F);
+??? if (clk_div == 0)
+????????? clk_div = 32;
+
+??? clk->rate = prate / clk_div;
+??? return clk->rate;
+}
+
+static int setrate_arm(struct clk *clk, unsigned long rate)
+{
+??? unsigned long actual_plla_rate;
+??? unsigned long actual_arm_rate;
+
+??? u32 plla_mul;
+??? u32 arm_div;
+??? u32 ahb_div;
+
+??? struct clk *plla_clk;
+??? struct clk *ahb_clk;
+
+??? plla_clk = clk_get(NULL, "pll_a");
+??? ahb_clk = clk_get(NULL, "ahb");
+
+??? /*
+??? ?? calculate the PLL_A multiplier
+??? ?? PLL_A will be set to 2 * ARM_clock (if possible)
+??? */
+??? actual_plla_rate = 2 * rate;
+??? if (actual_plla_rate % plla_clk->parent->rate)
+????????? plla_mul = actual_plla_rate / plla_clk->parent->rate + 1;
+??? else
+????????? plla_mul = actual_plla_rate / plla_clk->parent->rate;
+??? actual_plla_rate = plla_clk->parent->rate * plla_mul;
+
+??? /* calculate the new ARM divisor */
+??? if (actual_plla_rate % rate)
+????????? arm_div = actual_plla_rate / rate + 1;
+??? else
+????????? arm_div = actual_plla_rate / rate;
+
+??? actual_arm_rate = actual_plla_rate / arm_div;
+
+??? /* calculate the new AHB divisor */
+??? if (actual_arm_rate % ahb_clk->rate)
+????????? ahb_div = actual_arm_rate / ahb_clk->rate + 1;
+??? else
+????????? ahb_div = actual_arm_rate / ahb_clk->rate;
+
+??? clk_cpu_speedstep(plla_mul, arm_div, ahb_div);
+
+??? return 0;
+}
+
+static int setrate_ddr(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div;
+
+??? /* divisor only, so the clk can't be higher than the parent clk */
+??? if (rate > prate)
+????????? return -EINVAL;
+
+??? if (prate % rate)
+????????? clk_div = prate / rate + 1;
+??? else
+????????? clk_div = prate / rate;
+
+??? /* clk_div must be even if != 1, so +1 if odd */
+??? if ((clk_div != 1) && (clk_div % 1))
+????????? clk_div++;
+
+??? if (clk_div > 32)
+????????? return -ENOENT;
+
+??? clk->rate = prate / clk_div;
+
+??? if (clk_div == 32)
+????????? clk_div = 0;
+
+??? wmt_pm_wait_update();
+??? writel(clk_div, vt8500_pmc_base + clk->div_reg);
+
+??? return 0;
+}
+
+static unsigned long getrate_sdmmc(struct clk *clk)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div = (readl(vt8500_pmc_base + clk->div_reg) & 0x3F);
+
+??? if (clk_div == 0)
+????????? clk_div = 32;
+
+??? /* Check if fixed divisor is enabled (/64 fixed) */
+??? if (clk_div & 0x20)
+????????? clk_div = 64;
+
+??? clk->rate = prate / clk_div;
+??? return clk->rate;
+}
+
+static int setrate_sdmmc(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div;
+
+??? /*
+??? ?? divisor only, so the clk can't be higher than the parent clk
+??? ?? also reject rate == 0 for now
+??? */
+??? if ((rate > prate) || (rate == 0))
+????????? return -EINVAL;
+
+??? if (prate % rate)
+????????? clk_div = prate / rate + 1;
+??? else
+????????? clk_div = prate / rate;
+
+??? /* if clk_div > 32, enable the fixed divisor */
+??? if (clk_div > 32)
+????????? clk_div = 0x20;
+
+??? clk->rate = prate / clk_div;
+
+??? if (clk_div == 32)
+????????? clk_div = 0;
+
+??? writel(clk_div, vt8500_pmc_base + clk->div_reg);
+
+??? return 0;
+}
+
+static unsigned long getrate_stdmask(struct clk *clk)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div = (readl(vt8500_pmc_base + clk->div_reg) & 0x1F);
+??? if (clk_div == 0)
+????????? clk_div = 32;
+
+??? clk->rate = prate / clk_div;
+??? return clk->rate;
+}
+
+static int setrate_stdmask(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div;
+
+??? /* divisor only, so the clk can't be higher than the parent clk */
+??? if (rate > prate)
+????????? return -EINVAL;
+
+??? if (prate % rate)
+????????? clk_div = prate / rate + 1;
+??? else
+????????? clk_div = prate / rate;
+
+??? if (clk_div > 32)
+????????? return -ENOENT;
+
+??? clk->rate = prate / clk_div;
+
+??? if (clk_div == 32)
+????????? clk_div = 0;
+
+??? wmt_pm_wait_update();
+??? writel(clk_div, vt8500_pmc_base + clk->div_reg);
+
+??? return 0;
+}
+
+static void enable_std(struct clk *clk)
+{
+??? u32 regtmp = readl(vt8500_pmc_base + clk->en_reg);
+??? writel(regtmp | (1 << clk->en_bit), vt8500_pmc_base + clk->en_reg);
+}
+
+static void disable_std(struct clk *clk)
+{
+??? u32 regtmp = readl(vt8500_pmc_base + clk->en_reg);
+??? writel(regtmp & ~(1 << clk->en_bit), vt8500_pmc_base + clk->en_reg);
+}
+
+
+static struct clkops clkops_pll = {
+??? .getrate?? = &getrate_pll,
+??? .setrate?? = &setrate_pll,
+};
+
+static struct clkops clkops_arm = {
+??? .getrate?? = &getrate_arm,
+??? .setrate?? = &setrate_arm,
+};
+
+static struct clkops clkops_ahb = {
+??? .getrate?? = &getrate_ahb,
+??? .setrate?? = &setrate_ahb,
+};
+
+static struct clkops clkops_ddr = {
+??? .getrate?? = &getrate_stdmask,
+??? .setrate?? = &setrate_ddr,
+};
+
+static struct clkops clkops_sdmmc = {
+??? .getrate?? = &getrate_sdmmc,
+??? .setrate?? = &setrate_sdmmc,
+??? .enable???????? = &enable_std,
+??? .disable?? = &disable_std,
+};
+
+static struct clkops clkops_stdmask = {
+??? .getrate?? = &getrate_stdmask,
+??? .setrate?? = &setrate_stdmask,
+??? .enable???????? = &enable_std,
+??? .disable?? = &disable_std,
+};
+
+DEFINE_CKREF(ref, 25000000);
+
+DEFINE_CKPGNP(pll_a, &clk_vt8500_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_vt8500_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_vt8500_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_vt8500_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_vt8500_pll_b, 0, &clkops_sdmmc,
CLK_SDMMC_DIVISOR, \
+?????????????? CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_NAND_DIVISOR, \
+?????????????? CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(sfm, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR,
\
+?????????????? CLK_EN_HIGH, 23);
+DEFINE_CKPGEN(mspro, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_MSPRO_DIVISOR,\
+?????????????? CLK_EN_HIGH, 17);
+DEFINE_CKPGEN(spi0, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_SPI0_DIVISOR, \
+?????????????? CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_SPI1_DIVISOR, \
+?????????????? CLK_EN_LOW, 13);
+DEFINE_CKPG(spi2, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_SPI2_DIVISOR);
+DEFINE_CKPG(dsp, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_DSP_DIVISOR);
+DEFINE_CKPGEN(lcd, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_LCD_DIVISOR,
\
+?????????????? CLK_EN_HIGH, 10);
+DEFINE_CKPGEN(lcd_high, &clk_vt8500_pll_b, 0, &clkops_stdmask, \
+?????????????? CLK_LCD_DIVISOR_HIGH, CLK_EN_HIGH, 10);
+DEFINE_CKPGEN(pcm, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_PCM_DIVISOR,
\
+?????????????? CLK_EN_LOW, 16);
+DEFINE_CKPGEN(pwm, &clk_vt8500_pll_b, 0, &clkops_stdmask, CLK_PWM_DIVISOR,
\
+?????????????? CLK_EN_LOW, 14);
+DEFINE_CKPGEN(pwm2, &clk_vt8500_pll_b, 0, &clkops_stdmask,
CLK_PWM2_DIVISOR, \
+?????????????? CLK_EN_LOW, 14);
+
+DEFINE_CKPG(pll_c, &clk_vt8500_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_vt8500_pll_c, 0, &clkops_ddr, CLK_DDR_DIVISOR, \
+????????? CLK_EN_HIGH, 0);
+
+/* Enable/Disable clocks - Non-programmable */
+DEFINE_CKEN(ahb3,?? &clkops_stdmask,???? CLK_EN_LOW, 31);
+DEFINE_CKEN(ahb2_1, &clkops_stdmask,???? CLK_EN_LOW, 29);
+DEFINE_CKEN(h264,?? &clkops_stdmask,???? CLK_EN_LOW, 28);
+DEFINE_CKEN(ahb2_2, &clkops_stdmask,???? CLK_EN_LOW, 27);
+DEFINE_CKEN(ahb2_3, &clkops_stdmask,???? CLK_EN_LOW, 26);
+DEFINE_CKEN(ahb2_4, &clkops_stdmask,???? CLK_EN_LOW, 25);
+DEFINE_CKEN(ahb2_5, &clkops_stdmask,???? CLK_EN_LOW, 24);
+DEFINE_CKEN(vdu,??? &clkops_stdmask,???? CLK_EN_LOW, 23);
+DEFINE_CKEN(stri,?? &clkops_stdmask,???? CLK_EN_LOW, 22);
+DEFINE_CKEN(tsbk,?? &clkops_stdmask,???? CLK_EN_LOW, 21);
+DEFINE_CKEN(ahb1,?? &clkops_stdmask,???? CLK_EN_LOW, 20);
+DEFINE_CKEN(scc,??? &clkops_stdmask,???? CLK_EN_LOW, 19);
+DEFINE_CKEN(keypad, &clkops_stdmask,???? CLK_EN_LOW, 18);
+DEFINE_CKEN(ir,?????????? &clkops_stdmask,???? CLK_EN_LOW, 17);
+DEFINE_CKEN(ac97,?? &clkops_stdmask,???? CLK_EN_LOW, 15);
+DEFINE_CKEN(gpio,?? &clkops_stdmask,???? CLK_EN_LOW, 11);
+DEFINE_CKEN(aam3,?? &clkops_stdmask,???? CLK_EN_LOW, 10);
+DEFINE_CKEN(aam2,?? &clkops_stdmask,???? CLK_EN_LOW, 9);
+DEFINE_CKEN(aam1,?? &clkops_stdmask,???? CLK_EN_LOW, 8);
+DEFINE_CKEN(aam0,?? &clkops_stdmask,???? CLK_EN_LOW, 7);
+DEFINE_CKEN(i2c,??? &clkops_stdmask,???? CLK_EN_LOW, 5);
+DEFINE_CKEN(uart3,? &clkops_stdmask,???? CLK_EN_LOW, 4);
+DEFINE_CKEN(uart2,? &clkops_stdmask,???? CLK_EN_LOW, 3);
+DEFINE_CKEN(uart1,? &clkops_stdmask,???? CLK_EN_LOW, 2);
+DEFINE_CKEN(uart0,? &clkops_stdmask,???? CLK_EN_LOW, 1);
+DEFINE_CKEN(lpc,??? &clkops_stdmask,???? CLK_EN_HIGH, 27);
+DEFINE_CKEN(eth_phy, &clkops_stdmask,???? CLK_EN_HIGH, 26);
+DEFINE_CKEN(sae,??? &clkops_stdmask,???? CLK_EN_HIGH, 24);
+DEFINE_CKEN(usb_otg, &clkops_stdmask,???? CLK_EN_HIGH, 22);
+DEFINE_CKEN(eth_mac, &clkops_stdmask,???? CLK_EN_HIGH, 20);
+DEFINE_CKEN(pci_bridge,?? &clkops_stdmask,???? CLK_EN_HIGH, 19);
+DEFINE_CKEN(ahb_bridge,?? &clkops_stdmask,???? CLK_EN_HIGH, 13);
+DEFINE_CKEN(cf,?????????? &clkops_stdmask,???? CLK_EN_HIGH, 12);
+DEFINE_CKEN(ata,??? &clkops_stdmask,???? CLK_EN_HIGH, 11);
+DEFINE_CKEN(dma1,?? &clkops_stdmask,???? CLK_EN_HIGH, 9);
+DEFINE_CKEN(dma0,?? &clkops_stdmask,???? CLK_EN_HIGH, 8);
+
+static struct clk_lookup vt8500_clkregs[] = {
+??? INIT_CLKREG(&clk_vt8500_ref,???????? NULL, "ref"),
+??? INIT_CLKREG(&clk_vt8500_pll_a,?????? NULL, "pll_a"),
+??? INIT_CLKREG(&clk_vt8500_pll_b,?????? NULL, "pll_b"),
+??? INIT_CLKREG(&clk_vt8500_pll_c,?????? NULL, "pll_c"),
+??? INIT_CLKREG(&clk_vt8500_arm,???????? NULL, "arm"),
+??? INIT_CLKREG(&clk_vt8500_ahb,???????? NULL, "ahb"),
+??? INIT_CLKREG(&clk_vt8500_ahb_bridge,? NULL, "ahb_bridge"),
+??? INIT_CLKREG(&clk_vt8500_ddr,???????? NULL, "ddr"),
+
+??? INIT_CLKREG(&clk_vt8500_sdmmc,?????? NULL, "sdmmc"),
+
+??? INIT_CLKREG(&clk_vt8500_ahb1,??????? NULL, "ahb1"),
+??? INIT_CLKREG(&clk_vt8500_ahb2_1,????? NULL, "ahb2-1"),
+??? INIT_CLKREG(&clk_vt8500_ahb2_2,????? NULL, "ahb2-2"),
+??? INIT_CLKREG(&clk_vt8500_ahb2_3,????? NULL, "ahb2-3"),
+??? INIT_CLKREG(&clk_vt8500_ahb2_4,????? NULL, "ahb2-4"),
+??? INIT_CLKREG(&clk_vt8500_ahb2_5,????? NULL, "ahb2-5"),
+??? INIT_CLKREG(&clk_vt8500_ahb3,??????? NULL, "ahb3"),
+
+??? INIT_CLKREG(&clk_vt8500_h264,??????? NULL, "h264"),
+??? INIT_CLKREG(&clk_vt8500_vdu,???????? NULL, "vdu"),
+??? INIT_CLKREG(&clk_vt8500_stri,??????? NULL, "stri"),
+??? INIT_CLKREG(&clk_vt8500_tsbk,??????? NULL, "tsbk"),
+
+??? INIT_CLKREG(&clk_vt8500_scc,???????? NULL, "scc"),
+??? INIT_CLKREG(&clk_vt8500_keypad,????? NULL, "keypad"),
+??? INIT_CLKREG(&clk_vt8500_ir,????????? NULL, "ir"),
+??? INIT_CLKREG(&clk_vt8500_ac97,??????? NULL, "ac97"),
+??? INIT_CLKREG(&clk_vt8500_gpio,??????? NULL, "gpio"),
+??? INIT_CLKREG(&clk_vt8500_aam3,??????? NULL, "aam3"),
+??? INIT_CLKREG(&clk_vt8500_aam2,??????? NULL, "aam2"),
+??? INIT_CLKREG(&clk_vt8500_aam1,??????? NULL, "aam1"),
+??? INIT_CLKREG(&clk_vt8500_aam0,??????? NULL, "aam0"),
+??? INIT_CLKREG(&clk_vt8500_i2c,???????? NULL, "i2c"),
+??? INIT_CLKREG(&clk_vt8500_uart3,?????? NULL, "uart3"),
+??? INIT_CLKREG(&clk_vt8500_uart2,?????? NULL, "uart2"),
+??? INIT_CLKREG(&clk_vt8500_uart1,?????? NULL, "uart1"),
+??? INIT_CLKREG(&clk_vt8500_uart0,?????? NULL, "uart0"),
+
+??? INIT_CLKREG(&clk_vt8500_lpc,???????? NULL, "lpc"),
+??? INIT_CLKREG(&clk_vt8500_eth_phy,???? NULL, "eth-phy"),
+??? INIT_CLKREG(&clk_vt8500_sae,???????? NULL, "sae"),
+??? INIT_CLKREG(&clk_vt8500_usb_otg,???? NULL, "usb-otg"),
+??? INIT_CLKREG(&clk_vt8500_eth_mac,???? NULL, "eth-mac"),
+??? INIT_CLKREG(&clk_vt8500_pci_bridge,? NULL, "pci-bridge"),
+??? INIT_CLKREG(&clk_vt8500_cf,????????? NULL, "cf"),
+??? INIT_CLKREG(&clk_vt8500_ata,???????? NULL, "ata"),
+??? INIT_CLKREG(&clk_vt8500_dma1,??????? NULL, "dma1"),
+??? INIT_CLKREG(&clk_vt8500_dma0,??????? NULL, "dma0"),
+
+??? INIT_CLKREG(&clk_vt8500_nand,??????? NULL, "nand"),
+??? INIT_CLKREG(&clk_vt8500_sfm,???????? NULL, "sfm"),
+??? INIT_CLKREG(&clk_vt8500_mspro,?????? NULL, "mspro"),
+??? INIT_CLKREG(&clk_vt8500_spi0,??????? NULL, "spi0"),
+??? INIT_CLKREG(&clk_vt8500_spi1,??????? NULL, "spi1"),
+??? INIT_CLKREG(&clk_vt8500_spi2,??????? NULL, "spi2"),
+??? INIT_CLKREG(&clk_vt8500_pwm,???????? NULL, "pwm"),
+??? INIT_CLKREG(&clk_vt8500_pwm2,??????? NULL, "pwm2"),
+??? INIT_CLKREG(&clk_vt8500_dsp,???????? NULL, "dsp"),
+??? INIT_CLKREG(&clk_vt8500_lcd,???????? NULL, "lcd"),
+??? INIT_CLKREG(&clk_vt8500_lcd_high,??? NULL, "lcd-high"),
+??? INIT_CLKREG(&clk_vt8500_pcm,???????? NULL, "pcm"),
+};
+
+int vt8500_clock_init(void)
+{
+??? struct clk_lookup *c;
+
+??? /* map to PMC io memory */
+??? vt8500_pmc_base = ioremap(VT8500_PMC_BASE, 0x380);
+??? if (!vt8500_pmc_base) {
+????????? printk(KERN_ERR "clock: failed to remap io\n");
+????????? return -ENOMEM;
+??? }
+
+??? for (c = vt8500_clkregs; c->clk; c++)
+????????? clk_register(c->clk);
+
+??? clkdev_add_table(vt8500_clkregs, ARRAY_SIZE(vt8500_clkregs));
+
+??? return 0;
+}
diff --git a/arch/arm/mach-vt8500/clocks-wm8505.c
b/arch/arm/mach-vt8500/clocks-wm8505.c
new file mode 100644
index 0000000..8e64190
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-wm8505.c
@@ -0,0 +1,639 @@
+/*
+ *? arch/arm/mach-vt8500/clocks-wm8505.c
+ *
+ *? Copyright (C) 2011 Tony Prisk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA? 02111-1307?
USA
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <mach/wm8505_regs.h>
+#include "clock.h"
+
+#define DEFINE_CKREF(_name, _rate)??????? \
+struct clk clk_wm8505_##_name = {???????? \
+??? .name = #_name,??????????? \
+??? .parent = NULL,???????????????? \
+??? .ops = NULL,??????????????????? \
+??? .rate = _rate,???????????? \
+??? .type = CLK_PRIMARY,???????????? \
+??? .delay? = 0,??????????????????? \
+??? .en_reg = 0,??????????????????? \
+??? .en_bit = 0???????????????????? \
+}
+
+#define DEFINE_CKEN(_name, _ops, _enreg, _enbit)??? \
+struct clk clk_wm8505_##_name = {????????????? \
+??? .name = #_name,????????????????? \
+??? .parent = NULL,????????????????????? \
+??? .ops = _ops,???????????????????????? \
+??? .rate = 0,?????????????????????? \
+??? .type = CLK_ENABLE,?????????????????? \
+??? .delay? = 0,???????????????????????? \
+??? .en_reg = _enreg,??????????????????? \
+??? .en_bit = _enbit,??????????????????? \
+??? .div_reg = 0???????????????????????? \
+}
+
+#define DEFINE_CKPG(_name, _parent, _delay, _ops, _reg)?? \
+struct clk clk_wm8505_##_name = {????????????? \
+??? .name = #_name,????????????????? \
+??? .parent = _parent,?????????????????? \
+??? .ops = _ops,???????????????????????? \
+??? .rate = 0,?????????????????????? \
+??? .type = CLK_PROGRAMABLE,????????????? \
+??? .delay? = _delay,??????????????????? \
+??? .en_reg = 0,???????????????????????? \
+??? .en_bit = 0,???????????????????????? \
+??? .div_reg = _reg????????????????????? \
+}
+
+#define DEFINE_CKPGNP(_name, _parent, _delay, _ops, _reg) \
+struct clk clk_wm8505_##_name = {????????????? \
+??? .name = #_name,????????????????? \
+??? .parent = _parent,?????????????????? \
+??? .ops = _ops,???????????????????????? \
+??? .rate = 0,?????????????????????? \
+??? .type = CLK_PROGRAMABLE | CLK_NO_PROPAGATE, \
+??? .delay? = _delay,??????????????????? \
+??? .en_reg = 0,???????????????????????? \
+??? .en_bit = 0,???????????????????????? \
+??? .div_reg = _reg????????????????????? \
+}
+
+#define DEFINE_CKPGEN(_name, _parent, _delay, _ops, _reg, _enreg, _enbit) \
+struct clk clk_wm8505_##_name = {????????????? \
+??? .name = #_name,????????????????? \
+??? .parent = _parent,?????????????????? \
+??? .ops = _ops,???????????????????????? \
+??? .rate = 0,?????????????????????? \
+??? .type = CLK_PROGRAMABLE | CLK_ENABLE,????? \
+??? .delay? = _delay,??????????????????? \
+??? .en_reg = _enreg,??????????????????? \
+??? .en_bit = _enbit,??????????????????? \
+??? .div_reg = _reg????????????????????? \
+}
+
+void __iomem *wm8505_pmc_base;
+
+static void wmt_pm_wait_update(void)
+{
+??? int cnt = 1000000;
+??? while (readl(wm8505_pmc_base + CLK_PM_STATUS_LOW) &
CLK_PML_UPDATE_MASK) {
+????????? if (--cnt == 0) break;
+????????? cpu_relax();
+??? }
+
+??? if (cnt == 0)
+????????? printk(KERN_ERR "clock: pm_wait_update timeout\n");
+}
+
+static void wmt_pm_wait_busy(void)
+{
+??? int cnt = 1000000;
+??? while (readl(wm8505_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY) {
+????????? if (--cnt == 0) break;
+????????? cpu_relax();
+??? }
+
+??? if (cnt == 0)
+????????? printk(KERN_ERR "clock: pm_wait_busy timeout\n");
+}
+
+static void clk_cpu_speedstep(u32 plla_mul, u32 arm_div, u32 ahb_div)
+{
+??? if (ahb_div > 1) {
+????????? /*
+????????? ?? AHB, PLL, ARM
+????????? */
+????????? wmt_pm_wait_busy();
+????????? wmt_pm_wait_update();
+????????? writel(ahb_div, wm8505_pmc_base + CLK_AHB_DIVISOR);
+
+????????? wmt_pm_wait_update();
+????????? writel(plla_mul, wm8505_pmc_base + CLK_PLLA_MULTIPLIER);
+
+????????? wmt_pm_wait_update();
+????????? writel(arm_div, wm8505_pmc_base + CLK_ARM_DIVISOR);
+??? } else {
+????????? /*
+????????? ?? PLL, ARM, AHB
+????????? */
+????????? wmt_pm_wait_update();
+????????? writel(plla_mul, wm8505_pmc_base + CLK_PLLA_MULTIPLIER);
+
+????????? wmt_pm_wait_update();
+????????? writel(arm_div, wm8505_pmc_base + CLK_ARM_DIVISOR);
+
+????????? wmt_pm_wait_busy();
+????????? wmt_pm_wait_update();
+????????? writel(ahb_div, wm8505_pmc_base + CLK_AHB_DIVISOR);
+??? }
+??? wmt_pm_wait_update();
+}
+
+static unsigned long getrate_pll(struct clk *clk)
+{
+??? unsigned long prate = clk->parent->rate;
+??? u32 pll_mul = (readl(wm8505_pmc_base + clk->div_reg) & 0x1F);
+??? u32 pll_prediv = (readl(wm8505_pmc_base + clk->div_reg) & 0x100) ? 1 :
2;
+??? if (pll_mul < 4)
+????????? pll_mul = 1;
+??? else
+????????? pll_mul *= 2;
+
+??? clk->rate = prate * pll_mul / pll_prediv;
+??? return clk->rate;
+}
+
+static int setrate_pll(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk->parent->rate;
+??? unsigned long actual_plla_rate;
+??? unsigned long actual_arm_rate;
+??? u32 plla_mul;
+??? u32 arm_div;
+??? u32 ahb_div;
+??? struct clk *arm_clk;
+??? struct clk *ahb_clk;
+
+??? arm_clk = clk_get(NULL, "arm");
+??? ahb_clk = clk_get(NULL, "ahb");
+
+??? /* calculate the PLL_A multiplier */
+??? if (rate % prate)
+????????? plla_mul = rate / prate + 1;
+??? else
+????????? plla_mul = rate / prate;
+
+??? actual_plla_rate = prate * plla_mul;
+
+??? /* calculate the new ARM divisor */
+??? if (actual_plla_rate % arm_clk->rate)
+????????? arm_div = actual_plla_rate / arm_clk->rate + 1;
+??? else
+????????? arm_div = actual_plla_rate / arm_clk->rate;
+
+??? actual_arm_rate = actual_plla_rate / arm_div;
+
+??? /* calculate the new AHB divisor */
+??? if (actual_arm_rate % ahb_clk->rate)
+????????? ahb_div = actual_arm_rate / ahb_clk->rate + 1;
+??? else
+????????? ahb_div = actual_arm_rate / ahb_clk->rate;
+
+??? clk_cpu_speedstep(plla_mul, arm_div, ahb_div);
+
+??? return 0;
+}
+
+static unsigned long getrate_ahb(struct clk *clk)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div = (readl(wm8505_pmc_base + clk->div_reg) & 0x07);
+??? if (clk_div == 0)
+????????? clk_div = 8;
+
+??? clk->rate = prate / clk_div;
+??? return clk->rate;
+}
+
+static int setrate_ahb(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div;
+
+??? /* divisor only, so the clk can't be higher than the parent clk */
+??? if (rate > prate)
+????????? return -EINVAL;
+
+??? if (prate % rate)
+????????? clk_div = prate / rate + 1;
+??? else
+????????? clk_div = prate / rate;
+
+??? /* handle special case */
+??? if (clk_div == 8)
+????????? clk_div = 0;
+
+??? if (clk_div > 8)
+????????? return -ENOENT;
+
+??? wmt_pm_wait_busy();
+??? wmt_pm_wait_update();
+??? writel(clk_div, wm8505_pmc_base + clk->div_reg);
+
+??? wmt_pm_wait_update();
+
+??? return 0;
+}
+
+static unsigned long getrate_arm(struct clk *clk)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div = (readl(wm8505_pmc_base + clk->div_reg) & 0x1F);
+??? if (clk_div == 0)
+????????? clk_div = 32;
+
+??? clk->rate = prate / clk_div;
+??? return clk->rate;
+}
+
+static int setrate_arm(struct clk *clk, unsigned long rate)
+{
+??? unsigned long actual_plla_rate;
+??? unsigned long actual_arm_rate;
+
+??? u32 plla_mul;
+??? u32 arm_div;
+??? u32 ahb_div;
+
+??? struct clk *plla_clk;
+??? struct clk *ahb_clk;
+
+??? plla_clk = clk_get(NULL, "pll_a");
+??? ahb_clk = clk_get(NULL, "ahb");
+
+??? /*
+??? ?? calculate the PLL_A multiplier
+??? ?? PLL_A will be set to 2 * ARM_clock (if possible)
+??? */
+??? actual_plla_rate = 2 * rate;
+??? if (actual_plla_rate % plla_clk->parent->rate)
+????????? plla_mul = actual_plla_rate / plla_clk->parent->rate + 1;
+??? else
+????????? plla_mul = actual_plla_rate / plla_clk->parent->rate;
+??? actual_plla_rate = plla_clk->parent->rate * plla_mul;
+
+??? /* calculate the new ARM divisor */
+??? if (actual_plla_rate % rate)
+????????? arm_div = actual_plla_rate / rate + 1;
+??? else
+????????? arm_div = actual_plla_rate / rate;
+
+??? actual_arm_rate = actual_plla_rate / arm_div;
+
+??? /* calculate the new AHB divisor */
+??? if (actual_arm_rate % ahb_clk->rate)
+????????? ahb_div = actual_arm_rate / ahb_clk->rate + 1;
+??? else
+????????? ahb_div = actual_arm_rate / ahb_clk->rate;
+
+??? clk_cpu_speedstep(plla_mul, arm_div, ahb_div);
+
+??? return 0;
+}
+
+static int setrate_ddr(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div;
+
+??? /* divisor only, so the clk can't be higher than the parent clk */
+??? if (rate > prate)
+????????? return -EINVAL;
+
+??? if (prate % rate)
+????????? clk_div = prate / rate + 1;
+??? else
+????????? clk_div = prate / rate;
+
+??? /* clk_div must be even if != 1, so +1 if odd */
+??? if ((clk_div != 1) && (clk_div % 1))
+????????? clk_div++;
+
+??? if (clk_div > 32)
+????????? return -ENOENT;
+
+??? clk->rate = prate / clk_div;
+
+??? if (clk_div == 32)
+????????? clk_div = 0;
+
+??? wmt_pm_wait_update();
+??? writel(clk_div, wm8505_pmc_base + clk->div_reg);
+
+??? return 0;
+}
+
+static unsigned long getrate_sdmmc(struct clk *clk)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div = (readl(wm8505_pmc_base + clk->div_reg) & 0x3F);
+
+??? if (clk_div == 0)
+????????? clk_div = 32;
+
+??? /* Check if fixed divisor is enabled (/64 fixed) */
+??? if (clk_div & 0x20)
+????????? clk_div = 64;
+
+??? clk->rate = prate / clk_div;
+??? return clk->rate;
+}
+
+static int setrate_sdmmc(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div;
+
+??? /*
+??? ?? divisor only, so the clk can't be higher than the parent clk
+??? ?? also reject rate == 0 for now
+??? */
+??? if ((rate > prate) || (rate == 0))
+????????? return -EINVAL;
+
+??? if (prate % rate)
+????????? clk_div = prate / rate + 1;
+??? else
+????????? clk_div = prate / rate;
+
+??? /* if clk_div > 32, enable the fixed divisor */
+??? if (clk_div > 32)
+????????? clk_div = 0x20;
+
+??? clk->rate = prate / clk_div;
+
+??? if (clk_div == 32)
+????????? clk_div = 0;
+
+??? writel(clk_div, wm8505_pmc_base + clk->div_reg);
+
+??? return 0;
+}
+
+static unsigned long getrate_stdmask(struct clk *clk)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div = (readl(wm8505_pmc_base + clk->div_reg) & 0x1F);
+??? if (clk_div == 0)
+????????? clk_div = 32;
+
+??? clk->rate = prate / clk_div;
+??? return clk->rate;
+}
+
+static int setrate_stdmask(struct clk *clk, unsigned long rate)
+{
+??? unsigned long prate = clk_get_rate(clk->parent);
+??? u32 clk_div;
+
+??? /* divisor only, so the clk can't be higher than the parent clk */
+??? if (rate > prate)
+????????? return -EINVAL;
+
+??? if (prate % rate)
+????????? clk_div = prate / rate + 1;
+??? else
+????????? clk_div = prate / rate;
+
+??? if (clk_div > 32)
+????????? return -ENOENT;
+
+??? clk->rate = prate / clk_div;
+
+??? if (clk_div == 32)
+????????? clk_div = 0;
+
+??? wmt_pm_wait_update();
+??? writel(clk_div, wm8505_pmc_base + clk->div_reg);
+
+??? return 0;
+}
+
+static void enable_std(struct clk *clk)
+{
+??? u32 regtmp = readl(wm8505_pmc_base + clk->en_reg);
+??? writel(regtmp | (1 << clk->en_bit), wm8505_pmc_base + clk->en_reg);
+}
+
+static void disable_std(struct clk *clk)
+{
+??? u32 regtmp = readl(wm8505_pmc_base + clk->en_reg);
+??? writel(regtmp & ~(1 << clk->en_bit), wm8505_pmc_base + clk->en_reg);
+}
+
+
+static struct clkops clkops_pll = {
+??? .getrate?? = &getrate_pll,
+??? .setrate?? = &setrate_pll,
+};
+
+static struct clkops clkops_arm = {
+??? .getrate?? = &getrate_arm,
+??? .setrate?? = &setrate_arm,
+};
+
+static struct clkops clkops_ahb = {
+??? .getrate?? = &getrate_ahb,
+??? .setrate?? = &setrate_ahb,
+};
+
+static struct clkops clkops_ddr = {
+??? .getrate?? = &getrate_stdmask,
+??? .setrate?? = &setrate_ddr,
+};
+
+static struct clkops clkops_sdmmc = {
+??? .getrate?? = &getrate_sdmmc,
+??? .setrate?? = &setrate_sdmmc,
+??? .enable???????? = &enable_std,
+??? .disable?? = &disable_std,
+};
+
+static struct clkops clkops_stdmask = {
+??? .getrate?? = &getrate_stdmask,
+??? .setrate?? = &setrate_stdmask,
+??? .enable???????? = &enable_std,
+??? .disable?? = &disable_std,
+};
+
+
+DEFINE_CKREF(ref, 25000000);
+
+DEFINE_CKPGNP(pll_a, &clk_wm8505_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_wm8505_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_wm8505_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+DEFINE_CKPG(apb, &clk_wm8505_arm, 0, &clkops_stdmask, CLK_APB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_wm8505_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_wm8505_pll_b, 0, &clkops_sdmmc,
CLK_SDMMC_DIVISOR, \
+?????????????? CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_NAND_DIVISOR, \
+?????????????? CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(kbd_pre, &clk_wm8505_pll_b, 0, &clkops_stdmask, \
+?????????????? CLK_KBD_DIVISOR_PRE, CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(kbd, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_KBD_DIVISOR,
\
+?????????????? CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(sfm, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR,
\
+?????????????? CLK_EN_HIGH, 23);
+DEFINE_CKPG(genet, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_GENET_DIVISOR);
+DEFINE_CKPGEN(nor, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_NOR_DIVISOR,
\
+?????????????? CLK_EN_HIGH, 3);
+DEFINE_CKPGEN(spi0, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_SPI0_DIVISOR, \
+?????????????? CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_SPI1_DIVISOR, \
+?????????????? CLK_EN_LOW, 13);
+DEFINE_CKPGEN(spi2, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_SPI2_DIVISOR, \
+?????????????? CLK_EN_LOW, 14);
+DEFINE_CKPGEN(pwm, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_PWM_DIVISOR,
\
+?????????????? CLK_EN_LOW, 10);
+DEFINE_CKPGEN(na0, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_NA0_DIVISOR,
\
+?????????????? CLK_EN_HIGH, 1);
+DEFINE_CKPGEN(na12, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_NA12_DIVISOR, \
+?????????????? CLK_EN_HIGH, 2);
+DEFINE_CKPGEN(i2c0, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_I2C0_DIVISOR, \
+?????????????? CLK_EN_LOW, 5);
+DEFINE_CKPGEN(i2c1, &clk_wm8505_pll_b, 0, &clkops_stdmask,
CLK_I2C1_DIVISOR, \
+?????????????? CLK_EN_LOW, 0);
+DEFINE_CKPGEN(dvo, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_DVO_DIVISOR,
\
+?????????????? CLK_EN_LOW, 18);
+DEFINE_CKPG(ro1, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_RO1_DIVISOR);
+DEFINE_CKPG(ro2, &clk_wm8505_pll_b, 0, &clkops_stdmask, CLK_RO2_DIVISOR);
+
+DEFINE_CKPG(pll_c, &clk_wm8505_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_wm8505_pll_c, 0, &clkops_ddr, CLK_DDR_DIVISOR, \
+????????? CLK_EN_HIGH, 0);
+
+DEFINE_CKPG(pll_d, &clk_wm8505_ref, 0, &clkops_pll, CLK_PLLD_MULTIPLIER);
+
+
+/* Enable/Disable clocks - Non-programmable */
+DEFINE_CKEN(govrhd, &clkops_stdmask,???? CLK_EN_LOW, 30);
+DEFINE_CKEN(ge,?????????? &clkops_stdmask,???? CLK_EN_LOW, 29);
+DEFINE_CKEN(jenc,?? &clkops_stdmask,???? CLK_EN_LOW, 27);
+DEFINE_CKEN(amp,??? &clkops_stdmask,???? CLK_EN_LOW, 24);
+DEFINE_CKEN(uart5,? &clkops_stdmask,???? CLK_EN_LOW, 23);
+DEFINE_CKEN(uart4,? &clkops_stdmask,???? CLK_EN_LOW, 22);
+DEFINE_CKEN(scc,??? &clkops_stdmask,???? CLK_EN_LOW, 21);
+DEFINE_CKEN(ac97,?? &clkops_stdmask,???? CLK_EN_LOW, 19);
+DEFINE_CKEN(cir,??? &clkops_stdmask,???? CLK_EN_LOW, 17);
+DEFINE_CKEN(i2s,??? &clkops_stdmask,???? CLK_EN_LOW, 16);
+DEFINE_CKEN(gpio,?? &clkops_stdmask,???? CLK_EN_LOW, 11);
+DEFINE_CKEN(keypad, &clkops_stdmask,???? CLK_EN_LOW, 9);
+DEFINE_CKEN(rtc,??? &clkops_stdmask,???? CLK_EN_LOW, 7);
+DEFINE_CKEN(i2c_slave,??? &clkops_stdmask,???? CLK_EN_LOW, 6);
+DEFINE_CKEN(uart3,? &clkops_stdmask,???? CLK_EN_LOW, 4);
+DEFINE_CKEN(uart2,? &clkops_stdmask,???? CLK_EN_LOW, 3);
+DEFINE_CKEN(uart1,? &clkops_stdmask,???? CLK_EN_LOW, 2);
+DEFINE_CKEN(uart0,? &clkops_stdmask,???? CLK_EN_LOW, 1);
+DEFINE_CKEN(vpp,??? &clkops_stdmask,???? CLK_EN_HIGH, 31);
+DEFINE_CKEN(vid,??? &clkops_stdmask,???? CLK_EN_HIGH, 30);
+DEFINE_CKEN(govw,?? &clkops_stdmask,???? CLK_EN_HIGH, 29);
+DEFINE_CKEN(scl444u, &clkops_stdmask,???? CLK_EN_HIGH, 28);
+DEFINE_CKEN(eth_phy, &clkops_stdmask,???? CLK_EN_HIGH, 26);
+DEFINE_CKEN(sae,??? &clkops_stdmask,???? CLK_EN_HIGH, 24);
+DEFINE_CKEN(sys,??? &clkops_stdmask,???? CLK_EN_HIGH, 21);
+DEFINE_CKEN(eth_mac0,???? &clkops_stdmask,???? CLK_EN_HIGH, 20);
+DEFINE_CKEN(sdtv,?? &clkops_stdmask,???? CLK_EN_HIGH, 14);
+DEFINE_CKEN(ahb_bridge,?? &clkops_stdmask,???? CLK_EN_HIGH, 13);
+DEFINE_CKEN(pdma,?? &clkops_stdmask,???? CLK_EN_HIGH, 9);
+DEFINE_CKEN(udc,??? &clkops_stdmask,???? CLK_EN_HIGH, 8);
+DEFINE_CKEN(uhc,??? &clkops_stdmask,???? CLK_EN_HIGH, 7);
+DEFINE_CKEN(dma,??? &clkops_stdmask,???? CLK_EN_HIGH, 5);
+
+static struct clk_lookup wm8505_clkregs[] = {
+??? INIT_CLKREG(&clk_wm8505_ref,???????? NULL, "ref"),
+??? INIT_CLKREG(&clk_wm8505_pll_a,?????? NULL, "pll_a"),
+??? INIT_CLKREG(&clk_wm8505_pll_b,?????? NULL, "pll_b"),
+??? INIT_CLKREG(&clk_wm8505_pll_c,?????? NULL, "pll_c"),
+??? INIT_CLKREG(&clk_wm8505_pll_d,?????? NULL, "pll_d"),
+??? INIT_CLKREG(&clk_wm8505_arm,???????? NULL, "arm"),
+??? INIT_CLKREG(&clk_wm8505_ahb,???????? NULL, "ahb"),
+??? INIT_CLKREG(&clk_wm8505_ahb_bridge,? NULL, "ahb_bridge"),
+??? INIT_CLKREG(&clk_wm8505_apb,???????? NULL, "apb"),
+??? INIT_CLKREG(&clk_wm8505_ddr,???????? NULL, "ddr"),
+
+??? INIT_CLKREG(&clk_wm8505_nor,???????? NULL, "nor"),
+??? INIT_CLKREG(&clk_wm8505_nand,??????? NULL, "nand"),
+??? INIT_CLKREG(&clk_wm8505_sdmmc,?????? NULL, "sdmmc"),
+
+??? INIT_CLKREG(&clk_wm8505_keypad, NULL, "keypad"),
+??? INIT_CLKREG(&clk_wm8505_kbd_pre,???? NULL, "kbd_pre"),
+??? INIT_CLKREG(&clk_wm8505_kbd,???????? NULL, "kbd"),
+??? INIT_CLKREG(&clk_wm8505_sfm,???????? NULL, "sfm"),
+??? INIT_CLKREG(&clk_wm8505_genet,?????? NULL, "genet"),
+
+??? INIT_CLKREG(&clk_wm8505_spi0,??????? NULL, "spi0"),
+??? INIT_CLKREG(&clk_wm8505_spi1,??????? NULL, "spi1"),
+??? INIT_CLKREG(&clk_wm8505_spi2,??????? NULL, "spi2"),
+
+??? INIT_CLKREG(&clk_wm8505_govrhd,????? NULL, "govrhd"),
+??? INIT_CLKREG(&clk_wm8505_govw,??????? NULL, "govw"),
+??? INIT_CLKREG(&clk_wm8505_ge,????????? NULL, "ge"),
+??? INIT_CLKREG(&clk_wm8505_pwm,???????? NULL, "pwm"),
+??? INIT_CLKREG(&clk_wm8505_dvo,???????? NULL, "dvo"),
+??? INIT_CLKREG(&clk_wm8505_jenc,??????? NULL, "jenc"),
+??? INIT_CLKREG(&clk_wm8505_sdtv,??????? NULL, "sdtv"),
+
+??? INIT_CLKREG(&clk_wm8505_cir,???????? NULL, "cir"),
+??? INIT_CLKREG(&clk_wm8505_gpio,??????? NULL, "gpio"),
+??? INIT_CLKREG(&clk_wm8505_rtc,???????? NULL, "rtc"),
+
+??? INIT_CLKREG(&clk_wm8505_i2c0,??????? NULL, "i2c0"),
+??? INIT_CLKREG(&clk_wm8505_i2c1,??????? NULL, "i2c1"),
+??? INIT_CLKREG(&clk_wm8505_i2c_slave,?? NULL, "i2c_slave"),
+
+??? INIT_CLKREG(&clk_wm8505_ac97,??????? NULL, "ac97"),
+??? INIT_CLKREG(&clk_wm8505_i2s,???????? NULL, "i2s"),
+
+??? INIT_CLKREG(&clk_wm8505_eth_phy,???? NULL, "eth_phy"),
+??? INIT_CLKREG(&clk_wm8505_eth_mac0,??? NULL, "eth_mac0"),
+
+??? INIT_CLKREG(&clk_wm8505_uart5,?????? NULL, "uart5"),
+??? INIT_CLKREG(&clk_wm8505_uart4,?????? NULL, "uart4"),
+??? INIT_CLKREG(&clk_wm8505_uart3,?????? NULL, "uart3"),
+??? INIT_CLKREG(&clk_wm8505_uart2,?????? NULL, "uart2"),
+??? INIT_CLKREG(&clk_wm8505_uart1,?????? NULL, "uart1"),
+??? INIT_CLKREG(&clk_wm8505_uart0,?????? NULL, "uart0"),
+
+??? INIT_CLKREG(&clk_wm8505_na0,???????? NULL, "na0"),
+??? INIT_CLKREG(&clk_wm8505_na12,??????? NULL, "na12"),
+??? INIT_CLKREG(&clk_wm8505_ro1,???????? NULL, "ro1"),
+??? INIT_CLKREG(&clk_wm8505_ro2,???????? NULL, "ro2"),
+??? INIT_CLKREG(&clk_wm8505_amp,???????? NULL, "amp"),
+??? INIT_CLKREG(&clk_wm8505_scc,???????? NULL, "scc"),
+??? INIT_CLKREG(&clk_wm8505_vpp,???????? NULL, "vpp"),
+??? INIT_CLKREG(&clk_wm8505_vid,???????? NULL, "vid"),
+??? INIT_CLKREG(&clk_wm8505_scl444u,???? NULL, "scl444u"),
+??? INIT_CLKREG(&clk_wm8505_sae,???????? NULL, "sae"),
+??? INIT_CLKREG(&clk_wm8505_sys,???????? NULL, "sys"),
+??? INIT_CLKREG(&clk_wm8505_udc,???????? NULL, "udc"),
+??? INIT_CLKREG(&clk_wm8505_uhc,???????? NULL, "uhc"),
+??? INIT_CLKREG(&clk_wm8505_dma,???????? NULL, "dma"),
+??? INIT_CLKREG(&clk_wm8505_pdma,??????? NULL, "pdma"),
+};
+
+int wm8505_clock_init(void)
+{
+??? struct clk_lookup *c;
+
+??? /* map to PMC io memory */
+??? wm8505_pmc_base = ioremap(WM8505_PMC_BASE, 0x380);
+??? if (!wm8505_pmc_base) {
+????????? printk(KERN_ERR "clock: failed to remap io\n");
+????????? return -ENOMEM;
+??? }
+
+??? for (c = wm8505_clkregs; c->clk; c++)
+????????? clk_register(c->clk);
+
+??? clkdev_add_table(wm8505_clkregs, ARRAY_SIZE(wm8505_clkregs));
+
+??? return 0;
+}
diff --git a/arch/arm/mach-vt8500/include/mach/clkdev.h
b/arch/arm/mach-vt8500/include/mach/clkdev.h
new file mode 100644
index 0000000..62b0a6a
--- /dev/null
+++ b/arch/arm/mach-vt8500/include/mach/clkdev.h
@@ -0,0 +1,7 @@
+#ifndef __WMT_CLKDEV_H
+#define __WMT_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c
b/arch/arm/mach-vt8500/wm8505_7in.c
index e73aadb..ce75c77 100644
--- a/arch/arm/mach-vt8500/wm8505_7in.c
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -25,6 +25,7 @@
#include <asm/mach/arch.h>
?#include "devices.h"
+#include "clock.h"
?static void __iomem *pmc_hiber;
@@ -65,6 +66,7 @@ void __init wm8505_7in_init(void)
??? wm8505_set_resources();
??? platform_add_devices(devices, ARRAY_SIZE(devices));
??? vt8500_gpio_init();
+??? wm8505_clock_init();
}
?MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2011-02-27 23:33 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-02-27 22:27 PATCH [1/1] V4 - Clock Support for VT8500/WM8505 Tony Prisk
-- strict thread matches above, loose matches on Subject: below --
2011-02-27 23:33 Tony Prisk
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).