* [PATCH 1/3] VIA/Wondermedia clock support
From: Linux Mailing List Email Account @ 2011-02-27 0:05 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Tony Prisk (linux at prisktech.co.nz)
--- /dev/null 2011-02-26 19:04:30.530140059 +1300
+++ arch/arm/mach-vt8500/clock.h 2011-02-27 12:31:39.000000000 +1300
@@ -0,0 +1,136 @@
+/*
+ * 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 DEFINE_CKREF(_name, _rate) \
+struct clk clk_##_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_##_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_##_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_##_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_##_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 \
+}
+
+
+#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;
+};
+
+/* wm8505-clocks.c */
+extern int wmt_clock_init(void);
+
+int clk_register(struct clk *clk);
+
+#endif
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110227/f0e8e49d/attachment.html>
^ permalink raw reply
* [PATCH 3/3] VIA/Wondermedia clock support
From: Linux Mailing List Email Account @ 2011-02-27 0:06 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Tony Prisk (linux at prisktech.co.nz)
--- /dev/null 2011-02-26 19:04:30.530140059 +1300
+++ arch/arm/mach-vt8500/wm8505-clocks.c 2011-02-27 12:40:35.000000000 +1300
@@ -0,0 +1,624 @@
+/*
+ * arch/arm/mach-vt8500/wm8505_clocks.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 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
+
+#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_KBD_DIVISOR_PRE 0x318
+#define CLK_KBD_DIVISOR 0x31C
+#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
+#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
+
+void __iomem *clk_pmc_base;
+
+/*
+ * define our supported clocks
+ *
+ * Clock structure tree
+ *
+ * REF_CLK (25Mhz)
+ * |\ PLL_A
+ * | |- cpu
+ * | \- ahb
+ * | - apb
+ * |\ PLL_B
+ * | |\- nand flash
+ * | | - nor flash
+ * | |- sdmmc
+ * | |- lcd controller
+ * | |- pcm
+ * | |- pwm
+ * | |- spi
+ * | \- kbdc
+ * |
+ * \ PLL_C
+ * \- ddr
+ *
+ */
+
+static void wmt_pm_wait_update(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_UPDATE_MASK)
+ ;
+}
+
+static void wmt_pm_wait_busy(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY)
+ ;
+}
+
+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, clk_pmc_base + CLK_AHB_DIVISOR);
+
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+ } else {
+ /*
+ PLL, ARM, AHB
+ */
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, clk_pmc_base + CLK_AHB_DIVISOR);
+ }
+ wmt_pm_wait_update();
+}
+
+unsigned long getrate_pll(struct clk *clk)
+{
+ unsigned long prate = clk->parent->rate;
+ u32 pll_mul = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ u32 pll_prediv = (readl(clk_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;
+}
+
+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;
+}
+
+unsigned long getrate_ahb(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x07);
+ if (clk_div == 0)
+ clk_div = 8;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ wmt_pm_wait_update();
+
+ return 0;
+}
+
+unsigned long getrate_arm(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_sdmmc(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_stdmask(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+void enable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp | (1 << clk->en_bit), clk_pmc_base + clk->en_reg);
+}
+
+void disable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp & ~(1 << clk->en_bit), clk_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_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+DEFINE_CKPG(apb, &clk_arm, 0, &clkops_stdmask, CLK_APB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_pll_b, 0, &clkops_sdmmc, CLK_SDMMC_DIVISOR , \
+ CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_pll_b, 0, &clkops_stdmask, CLK_NAND_DIVISOR, \
+ CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(kbd_pre, &clk_pll_b, 0, &clkops_stdmask, CLK_KBD_DIVISOR_PRE, \
+ CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(kbd, &clk_pll_b, 0, &clkops_stdmask, CLK_KBD_DIVISOR, \
+ CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(sfm, &clk_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR, \
+ CLK_EN_HIGH, 23);
+DEFINE_CKPG(genet, &clk_pll_b, 0, &clkops_stdmask, CLK_GENET_DIVISOR);
+DEFINE_CKPGEN(nor, &clk_pll_b, 0, &clkops_stdmask, CLK_NOR_DIVISOR, \
+ CLK_EN_HIGH, 3);
+DEFINE_CKPGEN(spi0, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI0_DIVISOR, \
+ CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI1_DIVISOR, \
+ CLK_EN_LOW, 13);
+DEFINE_CKPGEN(spi2, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI2_DIVISOR, \
+ CLK_EN_LOW, 14);
+DEFINE_CKPGEN(pwm, &clk_pll_b, 0, &clkops_stdmask, CLK_PWM_DIVISOR, \
+ CLK_EN_LOW, 10);
+DEFINE_CKPGEN(na0, &clk_pll_b, 0, &clkops_stdmask, CLK_NA0_DIVISOR, \
+ CLK_EN_HIGH, 1);
+DEFINE_CKPGEN(na12, &clk_pll_b, 0, &clkops_stdmask, CLK_NA12_DIVISOR, \
+ CLK_EN_HIGH, 2);
+DEFINE_CKPGEN(i2c0, &clk_pll_b, 0, &clkops_stdmask, CLK_I2C0_DIVISOR, \
+ CLK_EN_LOW, 5);
+DEFINE_CKPGEN(i2c1, &clk_pll_b, 0, &clkops_stdmask, CLK_I2C1_DIVISOR, \
+ CLK_EN_LOW, 0);
+DEFINE_CKPGEN(dvo, &clk_pll_b, 0, &clkops_stdmask, CLK_DVO_DIVISOR, \
+ CLK_EN_LOW, 18);
+DEFINE_CKPG(ro1, &clk_pll_b, 0, &clkops_stdmask, CLK_RO1_DIVISOR);
+DEFINE_CKPG(ro2, &clk_pll_b, 0, &clkops_stdmask, CLK_RO2_DIVISOR);
+
+DEFINE_CKPG(pll_c, &clk_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_pll_c, 0, &clkops_ddr, CLK_DDR_DIVISOR, \
+ CLK_EN_HIGH, 0);
+
+DEFINE_CKPG(pll_d, &clk_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 wmt_clkregs[] = {
+ INIT_CLKREG(&clk_ref, NULL, "ref"),
+ INIT_CLKREG(&clk_pll_a, NULL, "pll_a"),
+ INIT_CLKREG(&clk_pll_b, NULL, "pll_b"),
+ INIT_CLKREG(&clk_pll_c, NULL, "pll_c"),
+ INIT_CLKREG(&clk_pll_d, NULL, "pll_d"),
+ INIT_CLKREG(&clk_arm, NULL, "arm"),
+ INIT_CLKREG(&clk_ahb, NULL, "ahb"),
+ INIT_CLKREG(&clk_ahb_bridge, NULL, "ahb_bridge"),
+ INIT_CLKREG(&clk_apb, NULL, "apb"),
+ INIT_CLKREG(&clk_ddr, NULL, "ddr"),
+
+ INIT_CLKREG(&clk_nor, NULL, "nor"),
+ INIT_CLKREG(&clk_nand, NULL, "nand"),
+ INIT_CLKREG(&clk_sdmmc, NULL, "sdmmc"),
+
+ INIT_CLKREG(&clk_keypad, NULL, "keypad"),
+ INIT_CLKREG(&clk_kbd_pre, NULL, "kbd_pre"),
+ INIT_CLKREG(&clk_kbd, NULL, "kbd"),
+ INIT_CLKREG(&clk_sfm, NULL, "sfm"),
+ INIT_CLKREG(&clk_genet, NULL, "genet"),
+
+ INIT_CLKREG(&clk_spi0, NULL, "spi0"),
+ INIT_CLKREG(&clk_spi1, NULL, "spi1"),
+ INIT_CLKREG(&clk_spi2, NULL, "spi2"),
+
+ INIT_CLKREG(&clk_govrhd, NULL, "govrhd"),
+ INIT_CLKREG(&clk_govw, NULL, "govw"),
+ INIT_CLKREG(&clk_ge, NULL, "ge"),
+ INIT_CLKREG(&clk_pwm, NULL, "pwm"),
+ INIT_CLKREG(&clk_dvo, NULL, "dvo"),
+ INIT_CLKREG(&clk_jenc, NULL, "jenc"),
+ INIT_CLKREG(&clk_sdtv, NULL, "sdtv"),
+
+ INIT_CLKREG(&clk_cir, NULL, "cir"),
+ INIT_CLKREG(&clk_gpio, NULL, "gpio"),
+ INIT_CLKREG(&clk_rtc, NULL, "rtc"),
+
+ INIT_CLKREG(&clk_i2c0, NULL, "i2c0"),
+ INIT_CLKREG(&clk_i2c1, NULL, "i2c1"),
+ INIT_CLKREG(&clk_i2c_slave, NULL, "i2c_slave"),
+
+ INIT_CLKREG(&clk_ac97, NULL, "ac97"),
+ INIT_CLKREG(&clk_i2s, NULL, "i2s"),
+
+ INIT_CLKREG(&clk_eth_phy, NULL, "eth_phy"),
+ INIT_CLKREG(&clk_eth_mac0, NULL, "eth_mac0"),
+
+ INIT_CLKREG(&clk_uart5, NULL, "uart5"),
+ INIT_CLKREG(&clk_uart4, NULL, "uart4"),
+ INIT_CLKREG(&clk_uart3, NULL, "uart3"),
+ INIT_CLKREG(&clk_uart2, NULL, "uart2"),
+ INIT_CLKREG(&clk_uart1, NULL, "uart1"),
+ INIT_CLKREG(&clk_uart0, NULL, "uart0"),
+
+ INIT_CLKREG(&clk_na0, NULL, "na0"),
+ INIT_CLKREG(&clk_na12, NULL, "na12"),
+ INIT_CLKREG(&clk_ro1, NULL, "ro1"),
+ INIT_CLKREG(&clk_ro2, NULL, "ro2"),
+ INIT_CLKREG(&clk_amp, NULL, "amp"),
+ INIT_CLKREG(&clk_scc, NULL, "scc"),
+ INIT_CLKREG(&clk_vpp, NULL, "vpp"),
+ INIT_CLKREG(&clk_vid, NULL, "vid"),
+ INIT_CLKREG(&clk_scl444u, NULL, "scl444u"),
+ INIT_CLKREG(&clk_sae, NULL, "sae"),
+ INIT_CLKREG(&clk_sys, NULL, "sys"),
+ INIT_CLKREG(&clk_udc, NULL, "udc"),
+ INIT_CLKREG(&clk_uhc, NULL, "uhc"),
+ INIT_CLKREG(&clk_dma, NULL, "dma"),
+ INIT_CLKREG(&clk_pdma, NULL, "pdma"),
+};
+
+int wmt_clock_init(void)
+{
+ struct clk_lookup *c;
+
+ /* map to PMC io memory */
+ clk_pmc_base = ioremap(WM8505_PMC_BASE, 0x380);
+
+ for (c = wmt_clkregs; c->clk; c++)
+ clk_register(c->clk);
+
+ clkdev_add_table(wmt_clkregs, ARRAY_SIZE(wmt_clkregs));
+
+ return 0;
+}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110227/29dc7dc7/attachment.html>
^ permalink raw reply
* Some trivial ARM fixes
From: Ryan Lortie @ 2011-02-27 0:41 UTC (permalink / raw)
To: linux-arm-kernel
Hello list,
I've been spending the past couple of days working on getting the kernel
working on an ARM board and I ran into a couple of trivial bugs in the
mean time.
Here are some fixes.
Cheers
ps: this is my first time using git-send-email. Please point out
anything I did wrong.
^ permalink raw reply
* [PATCH 1/2] ARM: imx35: fix ATA_DATA3 pad control address
From: Ryan Lortie @ 2011-02-27 0:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298767313-16351-1-git-send-email-desrt@desrt.ca>
According to the "i.MX35 (MCIMX35) Multimedia Applications Processor
Reference Manual" the correct address for the pad control register
IOMUXC_SW_PAD_CTL_PAD_ATA_DATA3 is 0x06ec, not 0x6e8.
Signed-off-by: Ryan Lortie <desrt@desrt.ca>
---
arch/arm/plat-mxc/include/mach/iomux-mx35.h | 14 +++++++-------
1 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx35.h b/arch/arm/plat-mxc/include/mach/iomux-mx35.h
index 2a24bae..3117c18 100644
--- a/arch/arm/plat-mxc/include/mach/iomux-mx35.h
+++ b/arch/arm/plat-mxc/include/mach/iomux-mx35.h
@@ -989,13 +989,13 @@
#define MX35_PAD_ATA_DATA2__IPU_DIAGB_9 IOMUX_PAD(0x6e8, 0x284, 6, 0x0, 0, NO_PAD_CTRL)
#define MX35_PAD_ATA_DATA2__ARM11P_TOP_TRACE_28 IOMUX_PAD(0x6e8, 0x284, 7, 0x0, 0, NO_PAD_CTRL)
-#define MX35_PAD_ATA_DATA3__ATA_DATA_3 IOMUX_PAD(0x6e8, 0x288, 0, 0x0, 0, NO_PAD_CTRL)
-#define MX35_PAD_ATA_DATA3__ESDHC3_CLK IOMUX_PAD(0x6e8, 0x288, 1, 0x814, 1, NO_PAD_CTRL)
-#define MX35_PAD_ATA_DATA3__USB_TOP_USBOTG_DATA_5 IOMUX_PAD(0x6e8, 0x288, 2, 0x9b8, 1, NO_PAD_CTRL)
-#define MX35_PAD_ATA_DATA3__CSPI2_SCLK IOMUX_PAD(0x6e8, 0x288, 4, 0x7e0, 2, NO_PAD_CTRL)
-#define MX35_PAD_ATA_DATA3__GPIO2_16 IOMUX_PAD(0x6e8, 0x288, 5, 0x884, 1, NO_PAD_CTRL)
-#define MX35_PAD_ATA_DATA3__IPU_DIAGB_10 IOMUX_PAD(0x6e8, 0x288, 6, 0x0, 0, NO_PAD_CTRL)
-#define MX35_PAD_ATA_DATA3__ARM11P_TOP_TRACE_29 IOMUX_PAD(0x6e8, 0x288, 7, 0x0, 0, NO_PAD_CTRL)
+#define MX35_PAD_ATA_DATA3__ATA_DATA_3 IOMUX_PAD(0x6ec, 0x288, 0, 0x0, 0, NO_PAD_CTRL)
+#define MX35_PAD_ATA_DATA3__ESDHC3_CLK IOMUX_PAD(0x6ec, 0x288, 1, 0x814, 1, NO_PAD_CTRL)
+#define MX35_PAD_ATA_DATA3__USB_TOP_USBOTG_DATA_5 IOMUX_PAD(0x6ec, 0x288, 2, 0x9b8, 1, NO_PAD_CTRL)
+#define MX35_PAD_ATA_DATA3__CSPI2_SCLK IOMUX_PAD(0x6ec, 0x288, 4, 0x7e0, 2, NO_PAD_CTRL)
+#define MX35_PAD_ATA_DATA3__GPIO2_16 IOMUX_PAD(0x6ec, 0x288, 5, 0x884, 1, NO_PAD_CTRL)
+#define MX35_PAD_ATA_DATA3__IPU_DIAGB_10 IOMUX_PAD(0x6ec, 0x288, 6, 0x0, 0, NO_PAD_CTRL)
+#define MX35_PAD_ATA_DATA3__ARM11P_TOP_TRACE_29 IOMUX_PAD(0x6ec, 0x288, 7, 0x0, 0, NO_PAD_CTRL)
#define MX35_PAD_ATA_DATA4__ATA_DATA_4 IOMUX_PAD(0x6f0, 0x28c, 0, 0x0, 0, NO_PAD_CTRL)
#define MX35_PAD_ATA_DATA4__ESDHC3_CMD IOMUX_PAD(0x6f0, 0x28c, 1, 0x818, 1, NO_PAD_CTRL)
--
1.7.4.1
^ permalink raw reply related
* [PATCH 2/2] ARM: imx35: fix trivial copy/paste error
From: Ryan Lortie @ 2011-02-27 0:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298767313-16351-1-git-send-email-desrt@desrt.ca>
Change imx31_add_imx_keypad() to imx35_add_imx_keypad() in
mach-mx3/devices-imx35.h.
Signed-off-by: Ryan Lortie <desrt@desrt.ca>
---
arch/arm/mach-mx3/devices-imx35.h | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-mx3/devices-imx35.h b/arch/arm/mach-mx3/devices-imx35.h
index 677b18a..d545d86 100644
--- a/arch/arm/mach-mx3/devices-imx35.h
+++ b/arch/arm/mach-mx3/devices-imx35.h
@@ -35,7 +35,7 @@ extern const struct imx_imx_i2c_data imx35_imx_i2c_data[] __initconst;
#define imx35_add_imx_i2c2(pdata) imx35_add_imx_i2c(2, pdata)
extern const struct imx_imx_keypad_data imx35_imx_keypad_data __initconst;
-#define imx31_add_imx_keypad(pdata) \
+#define imx35_add_imx_keypad(pdata) \
imx_add_imx_keypad(&imx35_imx_keypad_data, pdata)
extern const struct imx_imx_ssi_data imx35_imx_ssi_data[] __initconst;
--
1.7.4.1
^ permalink raw reply related
* [PATCH 1/2] ARM: imx35: fix ATA_DATA3 pad control address
From: Marc Reilly @ 2011-02-27 5:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298767313-16351-2-git-send-email-desrt@desrt.ca>
Hi Ryan,
Good find!
> According to the "i.MX35 (MCIMX35) Multimedia Applications Processor
> Reference Manual" the correct address for the pad control register
> IOMUXC_SW_PAD_CTL_PAD_ATA_DATA3 is 0x06ec, not 0x6e8.
>
> Signed-off-by: Ryan Lortie <desrt@desrt.ca>
Acked-by: Marc Reilly <marc@cpdesign.com.au>
Cheers
Marc
> ---
> arch/arm/plat-mxc/include/mach/iomux-mx35.h | 14 +++++++-------
> 1 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx35.h
> b/arch/arm/plat-mxc/include/mach/iomux-mx35.h index 2a24bae..3117c18
> 100644
> --- a/arch/arm/plat-mxc/include/mach/iomux-mx35.h
> +++ b/arch/arm/plat-mxc/include/mach/iomux-mx35.h
> @@ -989,13 +989,13 @@
> #define MX35_PAD_ATA_DATA2__IPU_DIAGB_9 IOMUX_PAD(0x6e8,
0x284, 6, 0x0,
> 0, NO_PAD_CTRL) #define
> MX35_PAD_ATA_DATA2__ARM11P_TOP_TRACE_28 IOMUX_PAD(0x6e8, 0x284, 7,
0x0,
> 0, NO_PAD_CTRL)
>
> -#define MX35_PAD_ATA_DATA3__ATA_DATA_3 IOMUX_PAD(0x6e8, 0x288,
0, 0x0,
> 0, NO_PAD_CTRL) -#define
> MX35_PAD_ATA_DATA3__ESDHC3_CLK IOMUX_PAD(0x6e8, 0x288, 1,
0x814, 1,
> NO_PAD_CTRL) -#define
> MX35_PAD_ATA_DATA3__USB_TOP_USBOTG_DATA_5 IOMUX_PAD(0x6e8, 0x288, 2,
> 0x9b8, 1, NO_PAD_CTRL) -#define
> MX35_PAD_ATA_DATA3__CSPI2_SCLK IOMUX_PAD(0x6e8, 0x288, 4,
0x7e0, 2,
> NO_PAD_CTRL) -#define MX35_PAD_ATA_DATA3__GPIO2_16
IOMUX_PAD(0x6e8,
> 0x288, 5, 0x884, 1, NO_PAD_CTRL) -#define
> MX35_PAD_ATA_DATA3__IPU_DIAGB_10 IOMUX_PAD(0x6e8, 0x288, 6, 0x0, 0,
> NO_PAD_CTRL) -#define
> MX35_PAD_ATA_DATA3__ARM11P_TOP_TRACE_29 IOMUX_PAD(0x6e8, 0x288, 7,
0x0,
> 0, NO_PAD_CTRL) +#define
> MX35_PAD_ATA_DATA3__ATA_DATA_3 IOMUX_PAD(0x6ec, 0x288, 0, 0x0,
0,
> NO_PAD_CTRL) +#define MX35_PAD_ATA_DATA3__ESDHC3_CLK
IOMUX_PAD(0x6ec,
> 0x288, 1, 0x814, 1, NO_PAD_CTRL) +#define
> MX35_PAD_ATA_DATA3__USB_TOP_USBOTG_DATA_5 IOMUX_PAD(0x6ec, 0x288, 2,
> 0x9b8, 1, NO_PAD_CTRL) +#define
> MX35_PAD_ATA_DATA3__CSPI2_SCLK IOMUX_PAD(0x6ec, 0x288, 4,
0x7e0, 2,
> NO_PAD_CTRL) +#define MX35_PAD_ATA_DATA3__GPIO2_16
IOMUX_PAD(0x6ec,
> 0x288, 5, 0x884, 1, NO_PAD_CTRL) +#define
> MX35_PAD_ATA_DATA3__IPU_DIAGB_10 IOMUX_PAD(0x6ec, 0x288, 6, 0x0, 0,
> NO_PAD_CTRL) +#define
> MX35_PAD_ATA_DATA3__ARM11P_TOP_TRACE_29 IOMUX_PAD(0x6ec, 0x288, 7,
0x0,
> 0, NO_PAD_CTRL)
>
> #define MX35_PAD_ATA_DATA4__ATA_DATA_4 IOMUX_PAD(0x6f0, 0x28c,
0, 0x0,
> 0, NO_PAD_CTRL) #define
> MX35_PAD_ATA_DATA4__ESDHC3_CMD IOMUX_PAD(0x6f0, 0x28c, 1,
0x818, 1,
> NO_PAD_CTRL)
^ permalink raw reply
* PATCH [0/1] Clock support for VT8500/WM8505
From: Linux Mailing List Email Account @ 2011-02-27 5:59 UTC (permalink / raw)
To: linux-arm-kernel
Patch to add clocks support for VT8500/WM8505 SoC machines.
Signed-off-by: Tony Prisk (linux at prisktech.co.nz<mailto:linux@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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110227/1f18aef0/attachment-0001.html>
^ permalink raw reply related
* PATCH [1/1] Clock support for VT8500/WM8505
From: Linux Mailing List Email Account @ 2011-02-27 5:59 UTC (permalink / raw)
To: linux-arm-kernel
Signed-off-by: Tony Prisk (linux at prisktech.co.nz)
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..b5a07de
--- /dev/null
+++ b/arch/arm/mach-vt8500/clock.h
@@ -0,0 +1,136 @@
+/*
+ * 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 DEFINE_CKREF(_name, _rate) \
+struct clk clk_##_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_##_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_##_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_##_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_##_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 \
+}
+
+
+#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;
+};
+
+/* wm8505-clocks.c */
+extern int wmt_clock_init(void);
+
+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..5081cbf
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-vt8500.c
@@ -0,0 +1,605 @@
+/*
+ * 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 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_EN_LOW 0x250
+#define CLK_EN_HIGH 0x254
+
+#define CLK_ARM_DIVISOR 0x300
+#define CLK_AHB_DIVISOR 0x304
+#define CLK_DSP_DIVISOR 0x308 /* new */
+#define CLK_LCD_DIVISOR 0x30C /* new */
+#define CLK_DDR_DIVISOR 0x310 /* mask = 0x1F special */
+#define CLK_SFM_DIVISOR 0x314
+#define CLK_PCM_DIVISOR 0x320 /* new */
+#define CLK_PWM1_DIVISOR 0x324 /* new */
+#define CLK_SDMMC_DIVISOR 0x328 /* mask = 0x1F special */
+#define CLK_MSPRO_DIVISOR 0x32C /* new name */
+#define CLK_NAND_DIVISOR 0x330
+#define CLK_LCD_DIVISOR_HIGH 0x334
+#define CLK_SPI0_DIVISOR 0x33C
+#define CLK_SPI1_DIVISOR 0x340
+#define CLK_SPI2_DIVISOR 0x344
+#define CLK_PWM2_DIVISOR 0x348
+
+void __iomem *clk_pmc_base;
+
+/*
+ * define our supported clocks
+ *
+ * Clock structure tree
+ *
+ * REF_CLK (25Mhz)
+ * |\ PLL_A
+ * | |- cpu
+ * | \- ahb
+ * | - apb
+ * |\ PLL_B
+ * | |\- nand flash
+ * | | - nor flash
+ * | |- sdmmc
+ * | |- lcd controller
+ * | |- pcm
+ * | |- pwm
+ * | |- spi
+ * | \- kbdc
+ * |
+ * \ PLL_C
+ * \- ddr
+ *
+ */
+
+static void wmt_pm_wait_update(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_UPDATE_MASK)
+ ;
+}
+
+static void wmt_pm_wait_busy(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY)
+ ;
+}
+
+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, clk_pmc_base + CLK_AHB_DIVISOR);
+
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+ } else {
+ /*
+ PLL, ARM, AHB
+ */
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, clk_pmc_base + CLK_AHB_DIVISOR);
+ }
+ wmt_pm_wait_update();
+}
+
+unsigned long getrate_pll(struct clk *clk)
+{
+ unsigned long prate = clk->parent->rate;
+ u32 pll_mul = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ u32 pll_prediv = (readl(clk_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;
+}
+
+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;
+}
+
+unsigned long getrate_ahb(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x07);
+ if (clk_div == 0)
+ clk_div = 8;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ wmt_pm_wait_update();
+
+ return 0;
+}
+
+unsigned long getrate_arm(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_sdmmc(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_stdmask(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+void enable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp | (1 << clk->en_bit), clk_pmc_base + clk->en_reg);
+}
+
+void disable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp & ~(1 << clk->en_bit), clk_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_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_pll_b, 0, &clkops_sdmmc, CLK_SDMMC_DIVISOR , \
+ CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_pll_b, 0, &clkops_stdmask, CLK_NAND_DIVISOR, \
+ CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(sfm, &clk_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR, \
+ CLK_EN_HIGH, 23);
+DEFINE_CKPGEN(mspro, &clk_pll_b, 0, &clkops_stdmask, CLK_MSPRO_DIVISOR, \
+ CLK_EN_HIGH, 17);
+DEFINE_CKPGEN(spi0, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI0_DIVISOR, \
+ CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI1_DIVISOR, \
+ CLK_EN_LOW, 13);
+DEFINE_CKPGEN(spi2, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI2_DIVISOR, \
+ CLK_EN_LOW, x);
+DEFINE_CKPGEN(pwm, &clk_pll_b, 0, &clkops_stdmask, CLK_PWM_DIVISOR, \
+ CLK_EN_LOW, 14);
+DEFINE_CKPGEN(dsp, &clk_pll_b, 0, &clkops_stdmask, CLK_DSP_DIVISOR, \
+ CLK_EN_HIGH, x);
+DEFINE_CKPGEN(lcd, &clk_pll_b, 0, &clkops_stdmask, CLK_LCD_DIVISOR, \
+ CLK_EN_HIGH, 10);
+DEFINE_CKPGEN(lcd_high, &clk_pll_b, 0, &clkops_stdmask, CLK_LCD_DIVISOR_HIGH,\
+ CLK_EN_HIGH, 10);
+DEFINE_CKPGEN(pcm, &clk_pll_b, 0, &clkops_stdmask, CLK_PCM_DIVISOR, \
+ CLK_EN_LOW, 16);
+DEFINE_CKPGEN(pwm1, &clk_pll_b, 0, &clkops_stdmask, CLK_PWM1_DIVISOR, \
+ CLK_EN_HIGH, x);
+DEFINE_CKPGEN(pwm2, &clk_pll_b, 0, &clkops_stdmask, CLK_PWM2_DIVISOR, \
+ CLK_EN_HIGH, x);
+
+DEFINE_CKPG(pll_c, &clk_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_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 wmt_clkregs[] = {
+ INIT_CLKREG(&clk_ref, NULL, "ref"),
+ INIT_CLKREG(&clk_pll_a, NULL, "pll_a"),
+ INIT_CLKREG(&clk_pll_b, NULL, "pll_b"),
+ INIT_CLKREG(&clk_pll_c, NULL, "pll_c"),
+ INIT_CLKREG(&clk_arm, NULL, "arm"),
+ INIT_CLKREG(&clk_ahb, NULL, "ahb"),
+ INIT_CLKREG(&clk_ahb_bridge, NULL, "ahb_bridge"),
+ INIT_CLKREG(&clk_ddr, NULL, "ddr"),
+
+ INIT_CLKREG(&clk_sdmmc, NULL, "sdmmc"),
+
+ INIT_CLKREG(&clk_ahb1, NULL, "ahb1"),
+ INIT_CLKREG(&clk_ahb2_1, NULL, "ahb2-1"),
+ INIT_CLKREG(&clk_ahb2_2, NULL, "ahb2-2"),
+ INIT_CLKREG(&clk_ahb2_3, NULL, "ahb2-3"),
+ INIT_CLKREG(&clk_ahb2_4, NULL, "ahb2-4"),
+ INIT_CLKREG(&clk_ahb2_5, NULL, "ahb2-5"),
+ INIT_CLKREG(&clk_ahb3, NULL, "ahb3"),
+
+ INIT_CLKREG(&clk_h264, NULL, "h264"),
+ INIT_CLKREG(&clk_vdu, NULL, "vdu"),
+ INIT_CLKREG(&clk_stri, NULL, "stri"),
+ INIT_CLKREG(&clk_tsbk, NULL, "tsbk"),
+
+ INIT_CLKREG(&clk_scc, NULL, "scc"),
+ INIT_CLKREG(&clk_keypad, NULL, "keypad"),
+ INIT_CLKREG(&clk_ir, NULL, "ir"),
+ INIT_CLKREG(&clk_ac97, NULL, "ac97"),
+ INIT_CLKREG(&clk_gpio, NULL, "gpio"),
+ INIT_CLKREG(&clk_aam3, NULL, "aam3"),
+ INIT_CLKREG(&clk_aam2, NULL, "aam2"),
+ INIT_CLKREG(&clk_aam1, NULL, "aam1"),
+ INIT_CLKREG(&clk_aam0, NULL, "aam0"),
+ INIT_CLKREG(&clk_i2c, NULL, "i2c"),
+ INIT_CLKREG(&clk_uart3, NULL, "uart3"),
+ INIT_CLKREG(&clk_uart2, NULL, "uart2"),
+ INIT_CLKREG(&clk_uart1, NULL, "uart1"),
+ INIT_CLKREG(&clk_uart0, NULL, "uart0"),
+
+ INIT_CLKREG(&clk_lpc, NULL, "lpc"),
+ INIT_CLKREG(&clk_eth_phy, NULL, "eth-phy"),
+ INIT_CLKREG(&clk_sae, NULL, "sae"),
+ INIT_CLKREG(&clk_usb_otg, NULL, "usb-otg"),
+ INIT_CLKREG(&clk_eth_mac, NULL, "eth-mac"),
+ INIT_CLKREG(&clk_pci_bridge, NULL, "pci-bridge"),
+ INIT_CLKREG(&clk_cf, NULL, "cf"),
+ INIT_CLKREG(&clk_ata, NULL, "ata"),
+ INIT_CLKREG(&clk_dma1, NULL, "dma1"),
+ INIT_CLKREG(&clk_dma0, NULL, "dma0"),
+
+ INIT_CLKREG(&clk_nand, NULL, "nand"),
+ INIT_CLKREG(&clk_sfm, NULL, "sfm"),
+ INIT_CLKREG(&clk_mspro, NULL, "mspro"),
+ INIT_CLKREG(&clk_spi0, NULL, "spi0"),
+ INIT_CLKREG(&clk_spi1, NULL, "spi1"),
+ INIT_CLKREG(&clk_spi2, NULL, "spi2"),
+ INIT_CLKREG(&clk_pwm, NULL, "pwm"),
+ INIT_CLKREG(&clk_dsp, NULL, "dsp"),
+ INIT_CLKREG(&clk_lcd, NULL, "lcd"),
+ INIT_CLKREG(&clk_lcd_high, NULL, "lcd-high"),
+ INIT_CLKREG(&clk_pcm, NULL, "pcm"),
+ INIT_CLKREG(&clk_pwm1, NULL, "pwm1"),
+ INIT_CLKREG(&clk_pwm2, NULL, "pwm2"),
+};
+
+int wmt_clock_init(void)
+{
+ struct clk_lookup *c;
+
+ /* map to PMC io memory */
+ clk_pmc_base = ioremap(VT8500_PMC_BASE, 0x380);
+
+ for (c = wmt_clkregs; c->clk; c++)
+ clk_register(c->clk);
+
+ clkdev_add_table(wmt_clkregs, ARRAY_SIZE(wmt_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..4ab0cb3
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-wm8505.c
@@ -0,0 +1,624 @@
+/*
+ * 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 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
+
+#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_KBD_DIVISOR_PRE 0x318
+#define CLK_KBD_DIVISOR 0x31C
+#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
+#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
+
+void __iomem *clk_pmc_base;
+
+/*
+ * define our supported clocks
+ *
+ * Clock structure tree
+ *
+ * REF_CLK (25Mhz)
+ * |\ PLL_A
+ * | |- cpu
+ * | \- ahb
+ * | - apb
+ * |\ PLL_B
+ * | |\- nand flash
+ * | | - nor flash
+ * | |- sdmmc
+ * | |- lcd controller
+ * | |- pcm
+ * | |- pwm
+ * | |- spi
+ * | \- kbdc
+ * |
+ * \ PLL_C
+ * \- ddr
+ *
+ */
+
+static void wmt_pm_wait_update(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_UPDATE_MASK)
+ ;
+}
+
+static void wmt_pm_wait_busy(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY)
+ ;
+}
+
+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, clk_pmc_base + CLK_AHB_DIVISOR);
+
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+ } else {
+ /*
+ PLL, ARM, AHB
+ */
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, clk_pmc_base + CLK_AHB_DIVISOR);
+ }
+ wmt_pm_wait_update();
+}
+
+unsigned long getrate_pll(struct clk *clk)
+{
+ unsigned long prate = clk->parent->rate;
+ u32 pll_mul = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ u32 pll_prediv = (readl(clk_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;
+}
+
+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;
+}
+
+unsigned long getrate_ahb(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x07);
+ if (clk_div == 0)
+ clk_div = 8;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ wmt_pm_wait_update();
+
+ return 0;
+}
+
+unsigned long getrate_arm(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_sdmmc(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_stdmask(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+void enable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp | (1 << clk->en_bit), clk_pmc_base + clk->en_reg);
+}
+
+void disable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp & ~(1 << clk->en_bit), clk_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_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+DEFINE_CKPG(apb, &clk_arm, 0, &clkops_stdmask, CLK_APB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_pll_b, 0, &clkops_sdmmc, CLK_SDMMC_DIVISOR , \
+ CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_pll_b, 0, &clkops_stdmask, CLK_NAND_DIVISOR, \
+ CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(kbd_pre, &clk_pll_b, 0, &clkops_stdmask, CLK_KBD_DIVISOR_PRE, \
+ CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(kbd, &clk_pll_b, 0, &clkops_stdmask, CLK_KBD_DIVISOR, \
+ CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(sfm, &clk_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR, \
+ CLK_EN_HIGH, 23);
+DEFINE_CKPG(genet, &clk_pll_b, 0, &clkops_stdmask, CLK_GENET_DIVISOR);
+DEFINE_CKPGEN(nor, &clk_pll_b, 0, &clkops_stdmask, CLK_NOR_DIVISOR, \
+ CLK_EN_HIGH, 3);
+DEFINE_CKPGEN(spi0, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI0_DIVISOR, \
+ CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI1_DIVISOR, \
+ CLK_EN_LOW, 13);
+DEFINE_CKPGEN(spi2, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI2_DIVISOR, \
+ CLK_EN_LOW, 14);
+DEFINE_CKPGEN(pwm, &clk_pll_b, 0, &clkops_stdmask, CLK_PWM_DIVISOR, \
+ CLK_EN_LOW, 10);
+DEFINE_CKPGEN(na0, &clk_pll_b, 0, &clkops_stdmask, CLK_NA0_DIVISOR, \
+ CLK_EN_HIGH, 1);
+DEFINE_CKPGEN(na12, &clk_pll_b, 0, &clkops_stdmask, CLK_NA12_DIVISOR, \
+ CLK_EN_HIGH, 2);
+DEFINE_CKPGEN(i2c0, &clk_pll_b, 0, &clkops_stdmask, CLK_I2C0_DIVISOR, \
+ CLK_EN_LOW, 5);
+DEFINE_CKPGEN(i2c1, &clk_pll_b, 0, &clkops_stdmask, CLK_I2C1_DIVISOR, \
+ CLK_EN_LOW, 0);
+DEFINE_CKPGEN(dvo, &clk_pll_b, 0, &clkops_stdmask, CLK_DVO_DIVISOR, \
+ CLK_EN_LOW, 18);
+DEFINE_CKPG(ro1, &clk_pll_b, 0, &clkops_stdmask, CLK_RO1_DIVISOR);
+DEFINE_CKPG(ro2, &clk_pll_b, 0, &clkops_stdmask, CLK_RO2_DIVISOR);
+
+DEFINE_CKPG(pll_c, &clk_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_pll_c, 0, &clkops_ddr, CLK_DDR_DIVISOR, \
+ CLK_EN_HIGH, 0);
+
+DEFINE_CKPG(pll_d, &clk_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 wmt_clkregs[] = {
+ INIT_CLKREG(&clk_ref, NULL, "ref"),
+ INIT_CLKREG(&clk_pll_a, NULL, "pll_a"),
+ INIT_CLKREG(&clk_pll_b, NULL, "pll_b"),
+ INIT_CLKREG(&clk_pll_c, NULL, "pll_c"),
+ INIT_CLKREG(&clk_pll_d, NULL, "pll_d"),
+ INIT_CLKREG(&clk_arm, NULL, "arm"),
+ INIT_CLKREG(&clk_ahb, NULL, "ahb"),
+ INIT_CLKREG(&clk_ahb_bridge, NULL, "ahb_bridge"),
+ INIT_CLKREG(&clk_apb, NULL, "apb"),
+ INIT_CLKREG(&clk_ddr, NULL, "ddr"),
+
+ INIT_CLKREG(&clk_nor, NULL, "nor"),
+ INIT_CLKREG(&clk_nand, NULL, "nand"),
+ INIT_CLKREG(&clk_sdmmc, NULL, "sdmmc"),
+
+ INIT_CLKREG(&clk_keypad, NULL, "keypad"),
+ INIT_CLKREG(&clk_kbd_pre, NULL, "kbd_pre"),
+ INIT_CLKREG(&clk_kbd, NULL, "kbd"),
+ INIT_CLKREG(&clk_sfm, NULL, "sfm"),
+ INIT_CLKREG(&clk_genet, NULL, "genet"),
+
+ INIT_CLKREG(&clk_spi0, NULL, "spi0"),
+ INIT_CLKREG(&clk_spi1, NULL, "spi1"),
+ INIT_CLKREG(&clk_spi2, NULL, "spi2"),
+
+ INIT_CLKREG(&clk_govrhd, NULL, "govrhd"),
+ INIT_CLKREG(&clk_govw, NULL, "govw"),
+ INIT_CLKREG(&clk_ge, NULL, "ge"),
+ INIT_CLKREG(&clk_pwm, NULL, "pwm"),
+ INIT_CLKREG(&clk_dvo, NULL, "dvo"),
+ INIT_CLKREG(&clk_jenc, NULL, "jenc"),
+ INIT_CLKREG(&clk_sdtv, NULL, "sdtv"),
+
+ INIT_CLKREG(&clk_cir, NULL, "cir"),
+ INIT_CLKREG(&clk_gpio, NULL, "gpio"),
+ INIT_CLKREG(&clk_rtc, NULL, "rtc"),
+
+ INIT_CLKREG(&clk_i2c0, NULL, "i2c0"),
+ INIT_CLKREG(&clk_i2c1, NULL, "i2c1"),
+ INIT_CLKREG(&clk_i2c_slave, NULL, "i2c_slave"),
+
+ INIT_CLKREG(&clk_ac97, NULL, "ac97"),
+ INIT_CLKREG(&clk_i2s, NULL, "i2s"),
+
+ INIT_CLKREG(&clk_eth_phy, NULL, "eth_phy"),
+ INIT_CLKREG(&clk_eth_mac0, NULL, "eth_mac0"),
+
+ INIT_CLKREG(&clk_uart5, NULL, "uart5"),
+ INIT_CLKREG(&clk_uart4, NULL, "uart4"),
+ INIT_CLKREG(&clk_uart3, NULL, "uart3"),
+ INIT_CLKREG(&clk_uart2, NULL, "uart2"),
+ INIT_CLKREG(&clk_uart1, NULL, "uart1"),
+ INIT_CLKREG(&clk_uart0, NULL, "uart0"),
+
+ INIT_CLKREG(&clk_na0, NULL, "na0"),
+ INIT_CLKREG(&clk_na12, NULL, "na12"),
+ INIT_CLKREG(&clk_ro1, NULL, "ro1"),
+ INIT_CLKREG(&clk_ro2, NULL, "ro2"),
+ INIT_CLKREG(&clk_amp, NULL, "amp"),
+ INIT_CLKREG(&clk_scc, NULL, "scc"),
+ INIT_CLKREG(&clk_vpp, NULL, "vpp"),
+ INIT_CLKREG(&clk_vid, NULL, "vid"),
+ INIT_CLKREG(&clk_scl444u, NULL, "scl444u"),
+ INIT_CLKREG(&clk_sae, NULL, "sae"),
+ INIT_CLKREG(&clk_sys, NULL, "sys"),
+ INIT_CLKREG(&clk_udc, NULL, "udc"),
+ INIT_CLKREG(&clk_uhc, NULL, "uhc"),
+ INIT_CLKREG(&clk_dma, NULL, "dma"),
+ INIT_CLKREG(&clk_pdma, NULL, "pdma"),
+};
+
+int wmt_clock_init(void)
+{
+ struct clk_lookup *c;
+
+ /* map to PMC io memory */
+ clk_pmc_base = ioremap(WM8505_PMC_BASE, 0x380);
+
+ for (c = wmt_clkregs; c->clk; c++)
+ clk_register(c->clk);
+
+ clkdev_add_table(wmt_clkregs, ARRAY_SIZE(wmt_clkregs));
+
+ return 0;
+}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110227/e95301ee/attachment-0001.html>
^ permalink raw reply related
* PATCH [1/1] V2 - Clock Support for VT8500/WM8505
From: Linux Mailing List Email Account @ 2011-02-27 6:28 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
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/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..b5a07de
--- /dev/null
+++ b/arch/arm/mach-vt8500/clock.h
@@ -0,0 +1,136 @@
+/*
+ * 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 DEFINE_CKREF(_name, _rate) \
+struct clk clk_##_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_##_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_##_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_##_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_##_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 \
+}
+
+
+#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;
+};
+
+/* wm8505-clocks.c */
+extern int wmt_clock_init(void);
+
+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..06740f5
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-vt8500.c
@@ -0,0 +1,602 @@
+/*
+ * 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 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_EN_LOW 0x250
+#define CLK_EN_HIGH 0x254
+
+#define CLK_ARM_DIVISOR 0x300
+#define CLK_AHB_DIVISOR 0x304
+#define CLK_DSP_DIVISOR 0x308 /* new */
+#define CLK_LCD_DIVISOR 0x30C /* new */
+#define CLK_DDR_DIVISOR 0x310 /* mask = 0x1F special */
+#define CLK_SFM_DIVISOR 0x314
+#define CLK_PCM_DIVISOR 0x320 /* new */
+#define CLK_PWM1_DIVISOR 0x324 /* new */
+#define CLK_SDMMC_DIVISOR 0x328 /* mask = 0x1F special */
+#define CLK_MSPRO_DIVISOR 0x32C /* new name */
+#define CLK_NAND_DIVISOR 0x330
+#define CLK_LCD_DIVISOR_HIGH 0x334
+#define CLK_SPI0_DIVISOR 0x33C
+#define CLK_SPI1_DIVISOR 0x340
+#define CLK_SPI2_DIVISOR 0x344
+#define CLK_PWM2_DIVISOR 0x348
+
+void __iomem *clk_pmc_base;
+
+/*
+ * define our supported clocks
+ *
+ * Clock structure tree
+ *
+ * REF_CLK (25Mhz)
+ * |\ PLL_A
+ * | |- cpu
+ * | \- ahb
+ * | - apb
+ * |\ PLL_B
+ * | |\- nand flash
+ * | | - nor flash
+ * | |- sdmmc
+ * | |- lcd controller
+ * | |- pcm
+ * | |- pwm
+ * | |- spi
+ * | \- kbdc
+ * |
+ * \ PLL_C
+ * \- ddr
+ *
+ */
+
+static void wmt_pm_wait_update(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_UPDATE_MASK)
+ ;
+}
+
+static void wmt_pm_wait_busy(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY)
+ ;
+}
+
+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, clk_pmc_base + CLK_AHB_DIVISOR);
+
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+ } else {
+ /*
+ PLL, ARM, AHB
+ */
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, clk_pmc_base + CLK_AHB_DIVISOR);
+ }
+ wmt_pm_wait_update();
+}
+
+unsigned long getrate_pll(struct clk *clk)
+{
+ unsigned long prate = clk->parent->rate;
+ u32 pll_mul = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ u32 pll_prediv = (readl(clk_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;
+}
+
+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;
+}
+
+unsigned long getrate_ahb(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x07);
+ if (clk_div == 0)
+ clk_div = 8;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ wmt_pm_wait_update();
+
+ return 0;
+}
+
+unsigned long getrate_arm(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_sdmmc(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_stdmask(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+void enable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp | (1 << clk->en_bit), clk_pmc_base + clk->en_reg);
+}
+
+void disable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp & ~(1 << clk->en_bit), clk_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_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_pll_b, 0, &clkops_sdmmc, CLK_SDMMC_DIVISOR , \
+ CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_pll_b, 0, &clkops_stdmask, CLK_NAND_DIVISOR, \
+ CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(sfm, &clk_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR, \
+ CLK_EN_HIGH, 23);
+DEFINE_CKPGEN(mspro, &clk_pll_b, 0, &clkops_stdmask, CLK_MSPRO_DIVISOR, \
+ CLK_EN_HIGH, 17);
+DEFINE_CKPGEN(spi0, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI0_DIVISOR, \
+ CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI1_DIVISOR, \
+ CLK_EN_LOW, 13);
+DEFINE_CKPG(spi2, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI2_DIVISOR);
+DEFINE_CKPG(dsp, &clk_pll_b, 0, &clkops_stdmask, CLK_DSP_DIVISOR);
+DEFINE_CKPGEN(lcd, &clk_pll_b, 0, &clkops_stdmask, CLK_LCD_DIVISOR, \
+ CLK_EN_HIGH, 10);
+DEFINE_CKPGEN(lcd_high, &clk_pll_b, 0, &clkops_stdmask, CLK_LCD_DIVISOR_HIGH,\
+ CLK_EN_HIGH, 10);
+DEFINE_CKPGEN(pcm, &clk_pll_b, 0, &clkops_stdmask, CLK_PCM_DIVISOR, \
+ CLK_EN_LOW, 16);
+DEFINE_CKPGEN(pwm1, &clk_pll_b, 0, &clkops_stdmask, CLK_PWM1_DIVISOR, \
+ CLK_EN_LOW, 14);
+DEFINE_CKPGEN(pwm2, &clk_pll_b, 0, &clkops_stdmask, CLK_PWM2_DIVISOR, \
+ CLK_EN_LOW, 14);
+
+DEFINE_CKPG(pll_c, &clk_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_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 wmt_clkregs[] = {
+ INIT_CLKREG(&clk_ref, NULL, "ref"),
+ INIT_CLKREG(&clk_pll_a, NULL, "pll_a"),
+ INIT_CLKREG(&clk_pll_b, NULL, "pll_b"),
+ INIT_CLKREG(&clk_pll_c, NULL, "pll_c"),
+ INIT_CLKREG(&clk_arm, NULL, "arm"),
+ INIT_CLKREG(&clk_ahb, NULL, "ahb"),
+ INIT_CLKREG(&clk_ahb_bridge, NULL, "ahb_bridge"),
+ INIT_CLKREG(&clk_ddr, NULL, "ddr"),
+
+ INIT_CLKREG(&clk_sdmmc, NULL, "sdmmc"),
+
+ INIT_CLKREG(&clk_ahb1, NULL, "ahb1"),
+ INIT_CLKREG(&clk_ahb2_1, NULL, "ahb2-1"),
+ INIT_CLKREG(&clk_ahb2_2, NULL, "ahb2-2"),
+ INIT_CLKREG(&clk_ahb2_3, NULL, "ahb2-3"),
+ INIT_CLKREG(&clk_ahb2_4, NULL, "ahb2-4"),
+ INIT_CLKREG(&clk_ahb2_5, NULL, "ahb2-5"),
+ INIT_CLKREG(&clk_ahb3, NULL, "ahb3"),
+
+ INIT_CLKREG(&clk_h264, NULL, "h264"),
+ INIT_CLKREG(&clk_vdu, NULL, "vdu"),
+ INIT_CLKREG(&clk_stri, NULL, "stri"),
+ INIT_CLKREG(&clk_tsbk, NULL, "tsbk"),
+
+ INIT_CLKREG(&clk_scc, NULL, "scc"),
+ INIT_CLKREG(&clk_keypad, NULL, "keypad"),
+ INIT_CLKREG(&clk_ir, NULL, "ir"),
+ INIT_CLKREG(&clk_ac97, NULL, "ac97"),
+ INIT_CLKREG(&clk_gpio, NULL, "gpio"),
+ INIT_CLKREG(&clk_aam3, NULL, "aam3"),
+ INIT_CLKREG(&clk_aam2, NULL, "aam2"),
+ INIT_CLKREG(&clk_aam1, NULL, "aam1"),
+ INIT_CLKREG(&clk_aam0, NULL, "aam0"),
+ INIT_CLKREG(&clk_i2c, NULL, "i2c"),
+ INIT_CLKREG(&clk_uart3, NULL, "uart3"),
+ INIT_CLKREG(&clk_uart2, NULL, "uart2"),
+ INIT_CLKREG(&clk_uart1, NULL, "uart1"),
+ INIT_CLKREG(&clk_uart0, NULL, "uart0"),
+
+ INIT_CLKREG(&clk_lpc, NULL, "lpc"),
+ INIT_CLKREG(&clk_eth_phy, NULL, "eth-phy"),
+ INIT_CLKREG(&clk_sae, NULL, "sae"),
+ INIT_CLKREG(&clk_usb_otg, NULL, "usb-otg"),
+ INIT_CLKREG(&clk_eth_mac, NULL, "eth-mac"),
+ INIT_CLKREG(&clk_pci_bridge, NULL, "pci-bridge"),
+ INIT_CLKREG(&clk_cf, NULL, "cf"),
+ INIT_CLKREG(&clk_ata, NULL, "ata"),
+ INIT_CLKREG(&clk_dma1, NULL, "dma1"),
+ INIT_CLKREG(&clk_dma0, NULL, "dma0"),
+
+ INIT_CLKREG(&clk_nand, NULL, "nand"),
+ INIT_CLKREG(&clk_sfm, NULL, "sfm"),
+ INIT_CLKREG(&clk_mspro, NULL, "mspro"),
+ INIT_CLKREG(&clk_spi0, NULL, "spi0"),
+ INIT_CLKREG(&clk_spi1, NULL, "spi1"),
+ INIT_CLKREG(&clk_spi2, NULL, "spi2"),
+ INIT_CLKREG(&clk_pwm1, NULL, "pwm1"),
+ INIT_CLKREG(&clk_pwm2, NULL, "pwm2"),
+ INIT_CLKREG(&clk_dsp, NULL, "dsp"),
+ INIT_CLKREG(&clk_lcd, NULL, "lcd"),
+ INIT_CLKREG(&clk_lcd_high, NULL, "lcd-high"),
+ INIT_CLKREG(&clk_pcm, NULL, "pcm"),
+ INIT_CLKREG(&clk_pwm1, NULL, "pwm1"),
+ INIT_CLKREG(&clk_pwm2, NULL, "pwm2"),
+};
+
+int wmt_clock_init(void)
+{
+ struct clk_lookup *c;
+
+ /* map to PMC io memory */
+ clk_pmc_base = ioremap(VT8500_PMC_BASE, 0x380);
+
+ for (c = wmt_clkregs; c->clk; c++)
+ clk_register(c->clk);
+
+ clkdev_add_table(wmt_clkregs, ARRAY_SIZE(wmt_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..4ab0cb3
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-wm8505.c
@@ -0,0 +1,624 @@
+/*
+ * 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 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
+
+#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_KBD_DIVISOR_PRE 0x318
+#define CLK_KBD_DIVISOR 0x31C
+#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
+#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
+
+void __iomem *clk_pmc_base;
+
+/*
+ * define our supported clocks
+ *
+ * Clock structure tree
+ *
+ * REF_CLK (25Mhz)
+ * |\ PLL_A
+ * | |- cpu
+ * | \- ahb
+ * | - apb
+ * |\ PLL_B
+ * | |\- nand flash
+ * | | - nor flash
+ * | |- sdmmc
+ * | |- lcd controller
+ * | |- pcm
+ * | |- pwm
+ * | |- spi
+ * | \- kbdc
+ * |
+ * \ PLL_C
+ * \- ddr
+ *
+ */
+
+static void wmt_pm_wait_update(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_UPDATE_MASK)
+ ;
+}
+
+static void wmt_pm_wait_busy(void)
+{
+ while (readl(clk_pmc_base + CLK_PM_STATUS_LOW) & CLK_PML_BUSY)
+ ;
+}
+
+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, clk_pmc_base + CLK_AHB_DIVISOR);
+
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+ } else {
+ /*
+ PLL, ARM, AHB
+ */
+ wmt_pm_wait_update();
+ writel(plla_mul, clk_pmc_base + CLK_PLLA_MULTIPLIER);
+
+ wmt_pm_wait_update();
+ writel(arm_div, clk_pmc_base + CLK_ARM_DIVISOR);
+
+ wmt_pm_wait_busy();
+ wmt_pm_wait_update();
+ writel(ahb_div, clk_pmc_base + CLK_AHB_DIVISOR);
+ }
+ wmt_pm_wait_update();
+}
+
+unsigned long getrate_pll(struct clk *clk)
+{
+ unsigned long prate = clk->parent->rate;
+ u32 pll_mul = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ u32 pll_prediv = (readl(clk_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;
+}
+
+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;
+}
+
+unsigned long getrate_ahb(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x07);
+ if (clk_div == 0)
+ clk_div = 8;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ wmt_pm_wait_update();
+
+ return 0;
+}
+
+unsigned long getrate_arm(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_sdmmc(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_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;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+unsigned long getrate_stdmask(struct clk *clk)
+{
+ unsigned long prate = clk_get_rate(clk->parent);
+ u32 clk_div = (readl(clk_pmc_base + clk->div_reg) & 0x1F);
+ if (clk_div == 0)
+ clk_div = 32;
+
+ clk->rate = prate / clk_div;
+ return clk->rate;
+}
+
+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, clk_pmc_base + clk->div_reg);
+
+ return 0;
+}
+
+void enable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp | (1 << clk->en_bit), clk_pmc_base + clk->en_reg);
+}
+
+void disable_std(struct clk *clk)
+{
+ u32 regtmp = readl(clk_pmc_base + clk->en_reg);
+ writel(regtmp & ~(1 << clk->en_bit), clk_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_ref, 0, &clkops_pll, CLK_PLLA_MULTIPLIER);
+DEFINE_CKPGNP(arm, &clk_pll_a, 0, &clkops_arm, CLK_ARM_DIVISOR);
+DEFINE_CKPG(ahb, &clk_arm, 0, &clkops_ahb, CLK_AHB_DIVISOR);
+DEFINE_CKPG(apb, &clk_arm, 0, &clkops_stdmask, CLK_APB_DIVISOR);
+
+DEFINE_CKPG(pll_b, &clk_ref, 0, &clkops_pll, CLK_PLLB_MULTIPLIER);
+DEFINE_CKPGEN(sdmmc, &clk_pll_b, 0, &clkops_sdmmc, CLK_SDMMC_DIVISOR , \
+ CLK_EN_HIGH, 18);
+DEFINE_CKPGEN(nand, &clk_pll_b, 0, &clkops_stdmask, CLK_NAND_DIVISOR, \
+ CLK_EN_HIGH, 16);
+DEFINE_CKPGEN(kbd_pre, &clk_pll_b, 0, &clkops_stdmask, CLK_KBD_DIVISOR_PRE, \
+ CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(kbd, &clk_pll_b, 0, &clkops_stdmask, CLK_KBD_DIVISOR, \
+ CLK_EN_HIGH, 4);
+DEFINE_CKPGEN(sfm, &clk_pll_b, 0, &clkops_stdmask, CLK_SFM_DIVISOR, \
+ CLK_EN_HIGH, 23);
+DEFINE_CKPG(genet, &clk_pll_b, 0, &clkops_stdmask, CLK_GENET_DIVISOR);
+DEFINE_CKPGEN(nor, &clk_pll_b, 0, &clkops_stdmask, CLK_NOR_DIVISOR, \
+ CLK_EN_HIGH, 3);
+DEFINE_CKPGEN(spi0, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI0_DIVISOR, \
+ CLK_EN_LOW, 12);
+DEFINE_CKPGEN(spi1, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI1_DIVISOR, \
+ CLK_EN_LOW, 13);
+DEFINE_CKPGEN(spi2, &clk_pll_b, 0, &clkops_stdmask, CLK_SPI2_DIVISOR, \
+ CLK_EN_LOW, 14);
+DEFINE_CKPGEN(pwm, &clk_pll_b, 0, &clkops_stdmask, CLK_PWM_DIVISOR, \
+ CLK_EN_LOW, 10);
+DEFINE_CKPGEN(na0, &clk_pll_b, 0, &clkops_stdmask, CLK_NA0_DIVISOR, \
+ CLK_EN_HIGH, 1);
+DEFINE_CKPGEN(na12, &clk_pll_b, 0, &clkops_stdmask, CLK_NA12_DIVISOR, \
+ CLK_EN_HIGH, 2);
+DEFINE_CKPGEN(i2c0, &clk_pll_b, 0, &clkops_stdmask, CLK_I2C0_DIVISOR, \
+ CLK_EN_LOW, 5);
+DEFINE_CKPGEN(i2c1, &clk_pll_b, 0, &clkops_stdmask, CLK_I2C1_DIVISOR, \
+ CLK_EN_LOW, 0);
+DEFINE_CKPGEN(dvo, &clk_pll_b, 0, &clkops_stdmask, CLK_DVO_DIVISOR, \
+ CLK_EN_LOW, 18);
+DEFINE_CKPG(ro1, &clk_pll_b, 0, &clkops_stdmask, CLK_RO1_DIVISOR);
+DEFINE_CKPG(ro2, &clk_pll_b, 0, &clkops_stdmask, CLK_RO2_DIVISOR);
+
+DEFINE_CKPG(pll_c, &clk_ref, 0, &clkops_pll, CLK_PLLC_MULTIPLIER);
+DEFINE_CKPGEN(ddr, &clk_pll_c, 0, &clkops_ddr, CLK_DDR_DIVISOR, \
+ CLK_EN_HIGH, 0);
+
+DEFINE_CKPG(pll_d, &clk_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 wmt_clkregs[] = {
+ INIT_CLKREG(&clk_ref, NULL, "ref"),
+ INIT_CLKREG(&clk_pll_a, NULL, "pll_a"),
+ INIT_CLKREG(&clk_pll_b, NULL, "pll_b"),
+ INIT_CLKREG(&clk_pll_c, NULL, "pll_c"),
+ INIT_CLKREG(&clk_pll_d, NULL, "pll_d"),
+ INIT_CLKREG(&clk_arm, NULL, "arm"),
+ INIT_CLKREG(&clk_ahb, NULL, "ahb"),
+ INIT_CLKREG(&clk_ahb_bridge, NULL, "ahb_bridge"),
+ INIT_CLKREG(&clk_apb, NULL, "apb"),
+ INIT_CLKREG(&clk_ddr, NULL, "ddr"),
+
+ INIT_CLKREG(&clk_nor, NULL, "nor"),
+ INIT_CLKREG(&clk_nand, NULL, "nand"),
+ INIT_CLKREG(&clk_sdmmc, NULL, "sdmmc"),
+
+ INIT_CLKREG(&clk_keypad, NULL, "keypad"),
+ INIT_CLKREG(&clk_kbd_pre, NULL, "kbd_pre"),
+ INIT_CLKREG(&clk_kbd, NULL, "kbd"),
+ INIT_CLKREG(&clk_sfm, NULL, "sfm"),
+ INIT_CLKREG(&clk_genet, NULL, "genet"),
+
+ INIT_CLKREG(&clk_spi0, NULL, "spi0"),
+ INIT_CLKREG(&clk_spi1, NULL, "spi1"),
+ INIT_CLKREG(&clk_spi2, NULL, "spi2"),
+
+ INIT_CLKREG(&clk_govrhd, NULL, "govrhd"),
+ INIT_CLKREG(&clk_govw, NULL, "govw"),
+ INIT_CLKREG(&clk_ge, NULL, "ge"),
+ INIT_CLKREG(&clk_pwm, NULL, "pwm"),
+ INIT_CLKREG(&clk_dvo, NULL, "dvo"),
+ INIT_CLKREG(&clk_jenc, NULL, "jenc"),
+ INIT_CLKREG(&clk_sdtv, NULL, "sdtv"),
+
+ INIT_CLKREG(&clk_cir, NULL, "cir"),
+ INIT_CLKREG(&clk_gpio, NULL, "gpio"),
+ INIT_CLKREG(&clk_rtc, NULL, "rtc"),
+
+ INIT_CLKREG(&clk_i2c0, NULL, "i2c0"),
+ INIT_CLKREG(&clk_i2c1, NULL, "i2c1"),
+ INIT_CLKREG(&clk_i2c_slave, NULL, "i2c_slave"),
+
+ INIT_CLKREG(&clk_ac97, NULL, "ac97"),
+ INIT_CLKREG(&clk_i2s, NULL, "i2s"),
+
+ INIT_CLKREG(&clk_eth_phy, NULL, "eth_phy"),
+ INIT_CLKREG(&clk_eth_mac0, NULL, "eth_mac0"),
+
+ INIT_CLKREG(&clk_uart5, NULL, "uart5"),
+ INIT_CLKREG(&clk_uart4, NULL, "uart4"),
+ INIT_CLKREG(&clk_uart3, NULL, "uart3"),
+ INIT_CLKREG(&clk_uart2, NULL, "uart2"),
+ INIT_CLKREG(&clk_uart1, NULL, "uart1"),
+ INIT_CLKREG(&clk_uart0, NULL, "uart0"),
+
+ INIT_CLKREG(&clk_na0, NULL, "na0"),
+ INIT_CLKREG(&clk_na12, NULL, "na12"),
+ INIT_CLKREG(&clk_ro1, NULL, "ro1"),
+ INIT_CLKREG(&clk_ro2, NULL, "ro2"),
+ INIT_CLKREG(&clk_amp, NULL, "amp"),
+ INIT_CLKREG(&clk_scc, NULL, "scc"),
+ INIT_CLKREG(&clk_vpp, NULL, "vpp"),
+ INIT_CLKREG(&clk_vid, NULL, "vid"),
+ INIT_CLKREG(&clk_scl444u, NULL, "scl444u"),
+ INIT_CLKREG(&clk_sae, NULL, "sae"),
+ INIT_CLKREG(&clk_sys, NULL, "sys"),
+ INIT_CLKREG(&clk_udc, NULL, "udc"),
+ INIT_CLKREG(&clk_uhc, NULL, "uhc"),
+ INIT_CLKREG(&clk_dma, NULL, "dma"),
+ INIT_CLKREG(&clk_pdma, NULL, "pdma"),
+};
+
+int wmt_clock_init(void)
+{
+ struct clk_lookup *c;
+
+ /* map to PMC io memory */
+ clk_pmc_base = ioremap(WM8505_PMC_BASE, 0x380);
+
+ for (c = wmt_clkregs; c->clk; c++)
+ clk_register(c->clk);
+
+ clkdev_add_table(wmt_clkregs, ARRAY_SIZE(wmt_clkregs));
+
+ return 0;
+}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110227/6ff72f6c/attachment-0001.html>
^ permalink raw reply related
* [PATCH 1/4] msm: scm: Mark inline asm as volatile
From: Will Deacon @ 2011-02-27 11:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8yapqqe3bs3.fsf@huya.qualcomm.com>
On Sat, 2011-02-26 at 18:12 +0000, David Brown wrote:
> On Fri, Feb 25 2011, Will Deacon wrote:
> > These asm blocks all have sensible looking output constraints. Why
> > do they need to be marked volatile?
>
> Without the volatile, the compiler is free to assume the only side
> effects of the asm are to modify the output registers. The volatile is
> needed to indicate to the compiler that the asm has other side effects.
As far as I know, volatile asm does two things:
(1) It stops the compiler from reordering the asm block with respect to
other volatile statements.
(2) It prevents the compiler from optimising the block away when
dataflow analysis indicates it's not required.
If side-effects need to be indicated, won't a memory clobber do the
trick?
Will
^ permalink raw reply
* [PATCH 1/2] ARM: l2x0: Errata fix for flush by Way operation can cause data corruption
From: Russell King - ARM Linux @ 2011-02-27 12:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298032525-21115-2-git-send-email-santosh.shilimkar@ti.com>
On Fri, Feb 18, 2011 at 06:05:24PM +0530, Santosh Shilimkar wrote:
> diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
> index 170c9bb..a8caee4 100644
> --- a/arch/arm/mm/cache-l2x0.c
> +++ b/arch/arm/mm/cache-l2x0.c
> @@ -67,18 +67,22 @@ static inline void l2x0_inv_line(unsigned long addr)
> writel_relaxed(addr, base + L2X0_INV_LINE_PA);
> }
>
> -#ifdef CONFIG_PL310_ERRATA_588369
> +#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
> static void debug_writel(unsigned long val)
> {
> - extern void omap_smc1(u32 fn, u32 arg);
> -
> - /*
> - * Texas Instrument secure monitor api to modify the
> - * PL310 Debug Control Register.
> - */
> - omap_smc1(0x100, val);
> + if (outer_cache.set_debug)
> + outer_cache.set_debug(val);
> + else
> + writel(val, l2x0_base + L2X0_DEBUG_CTRL);
> +}
I think changing this to:
#define debug_writel(val) outer_cache.set_debug(val)
static void l2x0_set_debug(unsigned long val)
{
writel(val, l2x0_base + L2X0_DEBUG_CTRL);
}
> +#else
> +/* Optimised out for non-errata case */
> +static inline void debug_writel(unsigned long val)
> +{
> }
#define l2x0_set_debug NULL
> +#endif
> @@ -329,6 +330,7 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
> outer_cache.flush_all = l2x0_flush_all;
> outer_cache.inv_all = l2x0_inv_all;
> outer_cache.disable = l2x0_disable;
> + outer_cache.set_debug = NULL;
outer_cache.set_debug = l2x0_set_debug;
may result in more efficient code as we're avoiding having to test the
value of outer_cache.set_debug each time we want to call it and branch
appropriately.
^ permalink raw reply
* [PATCH] colibri : don't register pxa2xx-pcmcia nodes on non-colibri platforms
From: Eric Miao @ 2011-02-27 13:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298417428-25064-1-git-send-email-dbaryshkov@gmail.com>
On Wed, Feb 23, 2011 at 7:30 AM, Dmitry Eremin-Solenikov
<dbaryshkov@gmail.com> wrote:
> PXA supports multi-machine kernels since long ago. However a kernel
> compiled with support for colibri and any other PXA machine and with
> PCMCIA enabled will barf at runtime about duplicate registration of
> pxa2xx-pcmcia device. Fix that.
>
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Applied to 'fix'.
> ---
> ?drivers/pcmcia/pxa2xx_colibri.c | ? ?3 +++
> ?1 files changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/pcmcia/pxa2xx_colibri.c b/drivers/pcmcia/pxa2xx_colibri.c
> index c3f7219..a520395 100644
> --- a/drivers/pcmcia/pxa2xx_colibri.c
> +++ b/drivers/pcmcia/pxa2xx_colibri.c
> @@ -181,6 +181,9 @@ static int __init colibri_pcmcia_init(void)
> ?{
> ? ? ? ?int ret;
>
> + ? ? ? if (!machine_is_colibri() && !machine_is_colibri320())
> + ? ? ? ? ? ? ? return -ENODEV;
> +
> ? ? ? ?colibri_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
> ? ? ? ?if (!colibri_pcmcia_device)
> ? ? ? ? ? ? ? ?return -ENOMEM;
> --
> 1.7.2.3
>
>
^ permalink raw reply
* [PATCH 1/3] pxa: Enable pxa-pcm-audio on pxa210/pxa25x platform
From: Eric Miao @ 2011-02-27 13:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <AANLkTimD28SYLA=NZyEO8SS==wrZ2mL+LG92e6FGHtom@mail.gmail.com>
On Fri, Feb 25, 2011 at 6:47 PM, Dmitry Eremin-Solenikov
<dbaryshkov@gmail.com> wrote:
> Eric, will it be possible to get at least this patch in the kernel?
>
> Otherwise all ASoC-based audio on pxa25x isn't working.
Hrm, this deserves as a fix. Applied.
>
> On 2/23/11, Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> wrote:
>> pxa25x platforms were left out of major ASoC Update patch.
>> Since f0fba2ad1b a registration of pxa-pcm-audio device is required for
>> ASoC to function on pxa platforms. Register one also for pxa210/pxa25x.
>>
>> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
>> Cc: Eric Miao <eric.y.miao@gmail.com>
>> ---
>> ?arch/arm/mach-pxa/pxa25x.c | ? ?1 +
>> ?1 files changed, 1 insertions(+), 0 deletions(-)
>>
>> diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
>> index fbc5b77..b166b1d 100644
>> --- a/arch/arm/mach-pxa/pxa25x.c
>> +++ b/arch/arm/mach-pxa/pxa25x.c
>> @@ -347,6 +347,7 @@ static struct platform_device *pxa25x_devices[]
>> __initdata = {
>> ? ? ? &pxa25x_device_assp,
>> ? ? ? &pxa25x_device_pwm0,
>> ? ? ? &pxa25x_device_pwm1,
>> + ? ? &pxa_device_asoc_platform,
>> ?};
>>
>> ?static struct sys_device pxa25x_sysdev[] = {
>> --
>> 1.7.2.3
>>
>>
>
>
> --
> With best wishes
> Dmitry
>
^ permalink raw reply
* [GIT PULL] pxa: fixes for v2.6.38-rc6
From: Eric Miao @ 2011-02-27 14:30 UTC (permalink / raw)
To: linux-arm-kernel
The following changes since commit f5412be599602124d2bdd49947b231dd77c0bf99:
Linux 2.6.38-rc6 (2011-02-21 17:25:52 -0800)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/ycmiao/pxa-linux-2.6.git fix
Dmitry Eremin-Solenikov (4):
ARM: pxa/tosa: drop setting LED trigger name, as it's unsupported now
ARM: pxa/colibri: don't register pxa2xx-pcmcia nodes on
non-colibri platforms
ARM: pxa: enable pxa-pcm-audio on pxa210/pxa25x platform
ARM: pxa/tosa: register wm9712 codec device
arch/arm/mach-pxa/pxa25x.c | 1 +
arch/arm/mach-pxa/tosa-bt.c | 2 --
arch/arm/mach-pxa/tosa.c | 6 ++++++
drivers/pcmcia/pxa2xx_colibri.c | 3 +++
4 files changed, 10 insertions(+), 2 deletions(-)
^ permalink raw reply
* PATCH [1/1] V2 - Clock Support for VT8500/WM8505
From: Alexey Charkov @ 2011-02-27 16:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <76F764B079F92A4E843589C893D0A0228D57@SERVER.prisktech.co.nz>
2011/2/27 Linux Mailing List Email Account <linux@prisktech.co.nz>:
> Patch to provide clock support on VT8500-/WM8505- based SoC.
>
> V2:? Included missing mach/clkdev.h
>
> ???? Fix errors in clocks-vt8500.c
>
Tony, you should probably consider using your real name instead of
"Linux Mailing List Email Account" here. Also, your sign-off is
missing in this submission.
As for the patch itself, it looks quite awesome to me. Just add error
handling to ioremap calls and count the time you are busy-waiting for
the status registers (and put cpu_relax inside the loop).
Have you tested this code with both VT8500 and WM8505 enabled in the
same kernel image?
Best regards,
Alexey
^ permalink raw reply
* [PATCH 1/4] msm: scm: Mark inline asm as volatile
From: David Brown @ 2011-02-27 17:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298805034.4626.4.camel@jazzbox>
On Sun, Feb 27 2011, Will Deacon wrote:
> On Sat, 2011-02-26 at 18:12 +0000, David Brown wrote:
>> On Fri, Feb 25 2011, Will Deacon wrote:
>
>> > These asm blocks all have sensible looking output constraints. Why
>> > do they need to be marked volatile?
>>
>> Without the volatile, the compiler is free to assume the only side
>> effects of the asm are to modify the output registers. The volatile is
>> needed to indicate to the compiler that the asm has other side effects.
>
> As far as I know, volatile asm does two things:
>
> (1) It stops the compiler from reordering the asm block with respect to
> other volatile statements.
>
> (2) It prevents the compiler from optimising the block away when
> dataflow analysis indicates it's not required.
>
> If side-effects need to be indicated, won't a memory clobber do the
> trick?
Per the gcc manual:
If your assembler instructions access memory in an unpredictable
fashion, add `memory' to the list of clobbered registers. This will
cause GCC to not keep memory values cached in registers across the
assembler instruction and not optimize stores or loads to that
memory. You will also want to add the `volatile' keyword if the
memory affected is not listed in the inputs or outputs of the `asm',
as the `memory' clobber does not count as a side-effect of the `asm'.
If you know how large the accessed memory is, you can add it as input
or output but if this is not known, you should add `memory'. As an
example, if you access ten bytes of a string, you can use a memory
input like:
The smc instruction is similar to a syscall. When in the secure world,
the processor is making state changes. It's not quite correct to
declare this as memory, because the memory used when secure isn't even
accessible to us. As far as I can tell, the volatile is the only way to
tell the compiler this.
David
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
^ permalink raw reply
* [PATCH 1/4] msm: scm: Mark inline asm as volatile
From: David Brown @ 2011-02-27 17:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <alpine.LFD.2.00.1102261439590.22034@xanadu.home>
On Sat, Feb 26 2011, Nicolas Pitre wrote:
> On Sat, 26 Feb 2011, David Brown wrote:
>
>> On Fri, Feb 25 2011, Will Deacon wrote:
>>
>> > On Thu, 2011-02-24 at 18:44 +0000, Stephen Boyd wrote:
>> >> We don't want the compiler to remove these asm statements or
>> >> reorder them in any way. Mark them as volatile to be sure.
>> >>
>> >> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
>> >> ---
>> >> arch/arm/mach-msm/scm.c | 4 ++--
>> >> 1 files changed, 2 insertions(+), 2 deletions(-)
>> >>
>> >> diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
>> >> index f4b9bc9..ba57b5a 100644
>> >> --- a/arch/arm/mach-msm/scm.c
>> >> +++ b/arch/arm/mach-msm/scm.c
>> >> @@ -174,7 +174,7 @@ static u32 smc(u32 cmd_addr)
>> >> register u32 r0 asm("r0") = 1;
>> >> register u32 r1 asm("r1") = (u32)&context_id;
>> >> register u32 r2 asm("r2") = cmd_addr;
>> >> - asm(
>> >> + asm volatile(
>> >> __asmeq("%0", "r0")
>> >> __asmeq("%1", "r0")
>> >> __asmeq("%2", "r1")
>> >> @@ -271,7 +271,7 @@ u32 scm_get_version(void)
>> >> return version;
>> >>
>> >> mutex_lock(&scm_lock);
>> >> - asm(
>> >> + asm volatile(
>> >> __asmeq("%0", "r1")
>> >> __asmeq("%1", "r0")
>> >> __asmeq("%2", "r1")
>> >
>> > These asm blocks all have sensible looking output constraints. Why
>> > do they need to be marked volatile?
>>
>> Without the volatile, the compiler is free to assume the only side
>> effects of the asm are to modify the output registers. The volatile is
>> needed to indicate to the compiler that the asm has other side effects.
>> There isn't enough optimization, yet, in gcc to change the generated
>> code in this case, so it happens to generate the correct code without
>> it.
>>
>> The second probably doesn't need it, unless we are expecting the version
>> to change dynamically. The volatile makes the scm_get_version()
>> function clearly a call to scm, though, so is probably useful to
>> document the intent.
>
> If the inline asm does have side effects which are not obvious other
> than producing a result for the output operand then it is a good idea to
> add a comment to that effect. Otherwise it is always best to omit the
> volatile and let gcc move the inline asm around or even delete it
> entirely when possible.
Would this be better as a comment by the assembly or for the whole file
or function? The entire purpose of this file is to communicate with
another logical processor, so it's all about producing side effects
other than just modifying the registers or the memory. Maybe a file
comment briefly explaining that SCM runs in TrustZone and a short
comment by each asm stating that it traps to the other logical cpu?
David
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
^ permalink raw reply
* PATCH [1/1] V3 - Clock Support for VT8500/WM8505
From: Tony Prisk @ 2011-02-27 20:43 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)
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..0e3ceab
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-vt8500.c
@@ -0,0 +1,618 @@
+/*
+ * 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);
+
+ 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..8be3fa9
--- /dev/null
+++ b/arch/arm/mach-vt8500/clocks-wm8505.c
@@ -0,0 +1,635 @@
+/*
+ * 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);
+
+ 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/20110227/0fbb8711/attachment-0001.html>
^ permalink raw reply related
* PATCH [1/1] V4 - Clock Support for VT8500/WM8505
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
* PATCH [1/1] V4 - Clock Support for VT8500/WM8505
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
* [PATCH] OMAP2+: sdrc: fix compile break on OMAP4-only config on current omap-for-linus
From: Paul Walmsley @ 2011-02-28 0:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4D6916D0.1080501@ru.mvista.com>
Hello Sergei
On Sat, 26 Feb 2011, Sergei Shtylyov wrote:
> How about empty inline instead?
Thanks for the review; that is indeed a better approach. Following is the
updated patch.
Tony, do you want to take this one?
- Paul
From: Paul Walmsley <paul@pwsan.com>
Date: Fri, 25 Feb 2011 17:38:25 -0700
Subject: [PATCH] OMAP2+: sdrc: fix compile break on OMAP4-only config on current omap-for-linus
On non-OMAP2 and non-OMAP3 kernel configs, turn omap2_sdrc_init() into
a no-op. Otherwise, compilation breaks on an OMAP4-only config with
the current omap-for-linus branch:
arch/arm/mach-omap2/built-in.o: In function `omap2_init_common_devices':
../mach-omap2/io.c:421: undefined reference to `omap2_sdrc_init'
Thanks to Sergei Shtylyov <sshtylyov@mvista.com> for suggesting the use
of a empty static inline function rather than a macro.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Sergei Shtylyov <sshtylyov@mvista.com>
---
arch/arm/plat-omap/include/plat/sdrc.h | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/arch/arm/plat-omap/include/plat/sdrc.h b/arch/arm/plat-omap/include/plat/sdrc.h
index efd87c8..2a34a65 100644
--- a/arch/arm/plat-omap/include/plat/sdrc.h
+++ b/arch/arm/plat-omap/include/plat/sdrc.h
@@ -124,8 +124,14 @@ struct omap_sdrc_params {
u32 mr;
};
+#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
void __init omap2_sdrc_init(struct omap_sdrc_params *sdrc_cs0,
struct omap_sdrc_params *sdrc_cs1);
+#else
+static inline void __init omap2_sdrc_init(struct omap_sdrc_params *sdrc_cs0,
+ struct omap_sdrc_params *sdrc_cs1) {};
+#endif
+
int omap2_sdrc_get_params(unsigned long r,
struct omap_sdrc_params **sdrc_cs0,
struct omap_sdrc_params **sdrc_cs1);
--
1.7.2.3
^ permalink raw reply related
* [PATCH 1/4] msm: scm: Mark inline asm as volatile
From: Nicolas Pitre @ 2011-02-28 2:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <8yahbbp2x4l.fsf@huya.qualcomm.com>
On Sun, 27 Feb 2011, David Brown wrote:
> On Sat, Feb 26 2011, Nicolas Pitre wrote:
>
> > On Sat, 26 Feb 2011, David Brown wrote:
> >
> >> On Fri, Feb 25 2011, Will Deacon wrote:
> >>
> >> > On Thu, 2011-02-24 at 18:44 +0000, Stephen Boyd wrote:
> >> >> We don't want the compiler to remove these asm statements or
> >> >> reorder them in any way. Mark them as volatile to be sure.
> >> >>
> >> >> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
> >> >> ---
> >> >> arch/arm/mach-msm/scm.c | 4 ++--
> >> >> 1 files changed, 2 insertions(+), 2 deletions(-)
> >> >>
> >> >> diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
> >> >> index f4b9bc9..ba57b5a 100644
> >> >> --- a/arch/arm/mach-msm/scm.c
> >> >> +++ b/arch/arm/mach-msm/scm.c
> >> >> @@ -174,7 +174,7 @@ static u32 smc(u32 cmd_addr)
> >> >> register u32 r0 asm("r0") = 1;
> >> >> register u32 r1 asm("r1") = (u32)&context_id;
> >> >> register u32 r2 asm("r2") = cmd_addr;
> >> >> - asm(
> >> >> + asm volatile(
> >> >> __asmeq("%0", "r0")
> >> >> __asmeq("%1", "r0")
> >> >> __asmeq("%2", "r1")
> >> >> @@ -271,7 +271,7 @@ u32 scm_get_version(void)
> >> >> return version;
> >> >>
> >> >> mutex_lock(&scm_lock);
> >> >> - asm(
> >> >> + asm volatile(
> >> >> __asmeq("%0", "r1")
> >> >> __asmeq("%1", "r0")
> >> >> __asmeq("%2", "r1")
> >> >
> >> > These asm blocks all have sensible looking output constraints. Why
> >> > do they need to be marked volatile?
> >>
> >> Without the volatile, the compiler is free to assume the only side
> >> effects of the asm are to modify the output registers. The volatile is
> >> needed to indicate to the compiler that the asm has other side effects.
> >> There isn't enough optimization, yet, in gcc to change the generated
> >> code in this case, so it happens to generate the correct code without
> >> it.
> >>
> >> The second probably doesn't need it, unless we are expecting the version
> >> to change dynamically. The volatile makes the scm_get_version()
> >> function clearly a call to scm, though, so is probably useful to
> >> document the intent.
> >
> > If the inline asm does have side effects which are not obvious other
> > than producing a result for the output operand then it is a good idea to
> > add a comment to that effect. Otherwise it is always best to omit the
> > volatile and let gcc move the inline asm around or even delete it
> > entirely when possible.
>
> Would this be better as a comment by the assembly or for the whole file
> or function? The entire purpose of this file is to communicate with
> another logical processor, so it's all about producing side effects
> other than just modifying the registers or the memory. Maybe a file
> comment briefly explaining that SCM runs in TrustZone and a short
> comment by each asm stating that it traps to the other logical cpu?
Now that I've looked more closely at the actual code, I think it is
obvious enough that the volatile is needed and no extra comment should
be required.
Nicolas
^ permalink raw reply
* Unsubscribe
From: Tomasz Fujak @ 2011-02-28 2:25 UTC (permalink / raw)
To: linux-arm-kernel
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110228/e9c6dffe/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 201102281125718_QKNMBDIF.gif
Type: image/gif
Size: 9524 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110228/e9c6dffe/attachment.gif>
^ permalink raw reply
* [PATCH V4 0/6] pxa3xx_nand update series set version 4
From: Lei Wen @ 2011-02-28 2:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1298630371.2798.43.camel@localhost>
On Fri, Feb 25, 2011 at 6:39 PM, Artem Bityutskiy <dedekind1@gmail.com> wrote:
> On Fri, 2011-02-18 at 16:57 +0800, Lei Wen wrote:
>> Change from previous version:
>> rework the patch set accroding to Eric's suggestion.
>> Move the recode judgement to the after command execution.
>>
>> Lei Wen (6):
>> ? pxa3xx_nand: make scan procedure more clear
>> ? pxa3xx_nand: rework irq logic
>> ? pxa3xx_nand: discard wait_for_event,write_cmd,__readid function
>> ? pxa3xx_nand: unify prepare command
>> ? pxa3xx_nand: mtd scan id process could be defined by driver itself
>> ? pxa3xx_nand: clean the keep configure code
>
> Hi, could you please make checkpatch.pl happy and re-send the patches?
>
> There are many issues. OK, some are fine, do not try to fix them, e.g.:
>
> WARNING: line over 80 characters
> #127: FILE: drivers/mtd/nand/pxa3xx_nand.c:221:
> +{ "512MiB 16-bit", 0xcc2c, ?64, 2048, 16, 16, 4096, &default_cmdset, &timing[2] },
>
> but some are clearly stylistic errors:
>
> ERROR: else should follow close brace '}'
> #249: FILE: drivers/mtd/nand/pxa3xx_nand.c:539:
> + ? ? ? ? ? ? ? ?}
> + ? ? ? ? ? ? ? ?else {
>
> etc.
>
Thanks for point that out...
I would post update patch which include the fix for that.
Best regards,
Lei
^ permalink raw reply
* [PATCH v2 5/5] mmc: sdhci-esdhc: enable esdhc on imx53
From: Zhu Richard-R65037 @ 2011-02-28 2:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110225203849.GC2192@pengutronix.de>
Hi WolfSang:
This phenomena is caused by the IC modifications on imx53 refer to imx51.
The details about this patch used for are listed below:
In order to generate the TC INT correctly, the CMD type of CMD12 should be set in the CMD register for
mass storage Multi-BLK IO(CMD18/CMD25), and the bit1 of the Vendor Spec register should be set/clear
at the begin/end of the Multi-BLK read.
Otherwise, there wouldn't TC INT generation in the mass storage Multi-BLK IO(CMD18/CMD25) and
SDIO Multi-BLK read operations.
That's all. How about add these description into the commit?
Best Regards
Richard Zhu
> -----Original Message-----
> From: Wolfram Sang [mailto:w.sang at pengutronix.de]
> Sent: Saturday, February 26, 2011 4:39 AM
> To: Zhu Richard-R65037
> Cc: linux-arm-kernel at lists.infradead.org; kernel at pengutronix.de; linux-
> mmc at vger.kernel.org; cjb at laptop.org; avorontsov at ru.mvista.com;
> eric at eukrea.com; linuxzsc at gmail.com; Zhao Richard-B20223
> Subject: Re: [PATCH v2 5/5] mmc: sdhci-esdhc: enable esdhc on imx53
>
> On Tue, Feb 22, 2011 at 06:13:26PM +0800, Richard Zhu wrote:
>
> > Fix the NO INT in the Multi-BLK IO in SD/MMC, and Multi-BLK read in
> > SDIO
>
> This description is too short. Why does it not work before, and why does
> this patch help?
>
> >
> > Signed-off-by: Richard Zhu <Hong-Xing.Zhu@freescale.com>
> > ---
> > drivers/mmc/host/sdhci-esdhc-imx.c | 41
> +++++++++++++++++++++++++++++++++++-
> > drivers/mmc/host/sdhci-esdhc.h | 5 ++++
> > 2 files changed, 45 insertions(+), 1 deletions(-)
> >
> > diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c
> > b/drivers/mmc/host/sdhci-esdhc-imx.c
> > index 9b82910..a09f786 100644
> > --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> > +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> > @@ -17,6 +17,8 @@
> > #include <linux/clk.h>
> > #include <linux/mmc/host.h>
> > #include <linux/mmc/sdhci-pltfm.h>
> > +#include <linux/mmc/mmc.h>
> > +#include <linux/mmc/sdio.h>
> > #include <mach/hardware.h>
> > #include "sdhci.h"
> > #include "sdhci-pltfm.h"
> > @@ -38,6 +40,27 @@ static u16 esdhc_readw_le(struct sdhci_host *host,
> int reg)
> > return readw(host->ioaddr + reg);
> > }
> >
> > +static void esdhc_writel_le(struct sdhci_host *host, u32 val, int
> > +reg) {
> > + switch (reg) {
> > + case SDHCI_INT_STATUS:
> > + /*
> > + * Fix no INT bug in SDIO MULTI-BLK read
> > + * clear bit1 of Vendor Spec registor after TC
> > + */
>
> Same for this comment. Make it more descriptive, please
>
> > + if (val & SDHCI_INT_DATA_END) {
> > + u32 v;
> > + v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
> > + if (v & 0x2) {
> > + v &= (~0x2);
>
> Braces not needed.
>
> > + writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
> > + }
>
> Can't you clear it unconditionally?
>
> > + }
> > + break;
> > + }
> > + writel(val, host->ioaddr + reg);
> > +}
> > +
> > static void esdhc_writew_le(struct sdhci_host *host, u16 val, int
> > reg) {
> > struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -45,12
> > +68,27 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val,
> int reg)
> > switch (reg) {
> > case SDHCI_TRANSFER_MODE:
> > /*
> > + * Fix no INT bug in SDIO MULTI-BLK read
> > + * set bit1 of Vendor Spec registor
> > + */
> > + if ((host->cmd->opcode == SD_IO_RW_EXTENDED)
> > + && (host->cmd->data->blocks > 1)
> > + && (host->cmd->data->flags & MMC_DATA_READ)) {
> > + u32 v;
> > + v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
> > + v |= 0x2;
> > + writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
> > + }
> > + /*
> > * Postpone this write, we must do it together with a
> > * command write that is down below.
> > */
> > pltfm_host->scratchpad = val;
> > return;
> > case SDHCI_COMMAND:
> > + /*Set the CMD_TYPE of the CMD12, fix no INT in MULTI_BLK IO
> */
> > + if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
> > + val |= SDHCI_CMD_ABORTCMD;
>
> Can't we handle it the same way than the SDIO case? I have to admit, even
> after reading the docs, I don't fully get what this bit1 is about.
>
> > writel(val << 16 | pltfm_host->scratchpad,
> > host->ioaddr + SDHCI_TRANSFER_MODE);
> > return;
> > @@ -113,7 +151,7 @@ static int esdhc_pltfm_init(struct sdhci_host *host,
> struct sdhci_pltfm_data *pd
> > clk_enable(clk);
> > pltfm_host->clk = clk;
> >
> > - if (cpu_is_mx35() || cpu_is_mx51())
> > + if (!cpu_is_mx25())
> > host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
> >
> > /* Fix errata ENGcm07207 which is present on i.MX25 and i.MX35 */
> @@
> > -133,6 +171,7 @@ static void esdhc_pltfm_exit(struct sdhci_host *host)
> >
> > static struct sdhci_ops sdhci_esdhc_ops = {
> > .read_w = esdhc_readw_le,
> > + .write_l = esdhc_writel_le,
>
> You are applying it for all imx-versions?
>
> > .write_w = esdhc_writew_le,
> > .write_b = esdhc_writeb_le,
> > .set_clock = esdhc_set_clock,
> > diff --git a/drivers/mmc/host/sdhci-esdhc.h
> > b/drivers/mmc/host/sdhci-esdhc.h index 303cde0..c93168c 100644
> > --- a/drivers/mmc/host/sdhci-esdhc.h
> > +++ b/drivers/mmc/host/sdhci-esdhc.h
> > @@ -43,6 +43,11 @@
> >
> > #define ESDHC_HOST_CONTROL_RES 0x05
> >
> > +/* Abort type definition in the command register */
> > +#define SDHCI_CMD_ABORTCMD 0xC0
>
> So, this is vendor-specific, too?
>
> > +/* VENDOR SPEC register */
> > +#define SDHCI_VENDOR_SPEC 0xC0
> > +
> > static inline void esdhc_set_clock(struct sdhci_host *host, unsigned
> > int clock) {
> > int pre_div = 2;
>
> Regards,
>
> Wolfram
>
> --
> Pengutronix e.K. | Wolfram Sang
> |
> Industrial Linux Solutions | http://www.pengutronix.de/
> |
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox