rk30: add clock support
authorxxx <xxx@rock-chips.com>
Fri, 23 Mar 2012 11:12:30 +0000 (04:12 -0700)
committerxxx <xxx@rock-chips.com>
Fri, 23 Mar 2012 11:12:48 +0000 (04:12 -0700)
12 files changed:
arch/arm/mach-rk30/Makefile
arch/arm/mach-rk30/board-rk30-sdk.c
arch/arm/mach-rk30/clock.c [changed mode: 0644->0755]
arch/arm/mach-rk30/clock.h [new file with mode: 0644]
arch/arm/mach-rk30/clock_data.c [new file with mode: 0644]
arch/arm/mach-rk30/common.c
arch/arm/mach-rk30/dvfs.c [new file with mode: 0644]
arch/arm/mach-rk30/include/mach/board.h
arch/arm/mach-rk30/include/mach/clock.h [new file with mode: 0755]
arch/arm/mach-rk30/include/mach/cru.h
arch/arm/mach-rk30/include/mach/dvfs.h [new file with mode: 0644]
arch/arm/mach-rk30/timer.c [changed mode: 0644->0755]

index c29cf061c864a2fdfa57ce515b42c12a1e7cc279..129cfa1de19fd5ed34b147f2bf717e1f23d0c1d8 100644 (file)
@@ -1,4 +1,6 @@
 obj-y += clock.o
+obj-y += clock_data.o
+obj-y += dvfs.o
 obj-y += common.o
 obj-y += devices.o
 obj-y += io.o
index 61440c89b8a9dba6deadc26c3b33a94f067d63ea..e7684991f4d673402fc0c63f757f6bb81f81be0b 100755 (executable)
@@ -1060,6 +1060,11 @@ static void __init rk30_reserve(void)
        board_mem_reserved();
 }
 
+void __init board_clock_init(void)
+{
+       rk30_clock_data_init(periph_pll_297mhz, codec_pll_360mhz, max_i2s_12288khz);
+}
+
 MACHINE_START(RK30, "RK30board")
        .boot_params    = PLAT_PHYS_OFFSET + 0x800,
        .fixup          = rk30_fixup,
old mode 100644 (file)
new mode 100755 (executable)
index 06dce19..93a729d
+/* linux/arch/arm/mach-rk30/clock.c
+ *
+ * Copyright (C) 2012 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
 #include <linux/clk.h>
 #include <linux/clkdev.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/list.h>
 #include <linux/module.h>
+#include <linux/hardirq.h>
+#include <linux/delay.h>
+#include <mach/clock.h>
+#include "clock.h"
+#include <mach/dvfs.h>
+#include <linux/delay.h>
 
-#include <mach/board.h>
+#define CLOCK_PRINTK_DBG(fmt, args...) printk(fmt, ## args);
+#define CLOCK_PRINTK_ERR(fmt, args...) printk(fmt, ## args);
 
 /* Clock flags */
 /* bit 0 is free */
 #define RATE_FIXED             (1 << 1)        /* Fixed clock rate */
 #define CONFIG_PARTICIPANT     (1 << 10)       /* Fundamental clock */
-#define IS_PD                  (1 << 2)        /* Power Domain */
 
 #define MHZ                    (1000*1000)
-#define KHZ                    1000
-
-struct clk {
-       struct list_head        node;
-       const char              *name;
-       struct clk              *parent;
-       struct list_head        children;
-       struct list_head        sibling;        /* node for children */
-       unsigned long           rate;
-       u32                     flags;
-       int                     (*mode)(struct clk *clk, int on);
-       unsigned long           (*recalc)(struct clk *);        /* if null, follow parent */
-       int                     (*set_rate)(struct clk *, unsigned long);
-       long                    (*round_rate)(struct clk *, unsigned long);
-       struct clk*             (*get_parent)(struct clk *);    /* get clk's parent from the hardware. default is clksel_get_parent if parents present */
-       int                     (*set_parent)(struct clk *, struct clk *);      /* default is clksel_set_parent if parents present */
-       s16                     usecount;
-       u16                     notifier_count;
-       u8                      gate_idx;
-       u8                      pll_idx;
-       u8                      clksel_con;
-       u8                      clksel_mask;
-       u8                      clksel_shift;
-       u8                      clksel_maxdiv;
-       u8                      clksel_parent_mask;
-       u8                      clksel_parent_shift;
-       struct clk              **parents;
-};
+#define KHZ                    (1000)
 
-static struct clk xin24m = {
-       .name           = "xin24m",
-       .rate           = 24 * MHZ,
-       .flags          = RATE_FIXED,
-};
+static void __clk_recalc(struct clk *clk);
+static void __propagate_rate(struct clk *tclk);
+static void __clk_reparent(struct clk *child, struct clk *parent);
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+static DEFINE_SPINLOCK(clockfw_lock);
+static LIST_HEAD(root_clks);
+static void clk_notify(struct clk *clk, unsigned long msg,
+                      unsigned long old_rate, unsigned long new_rate);
+
+#define LOCK() do { WARN_ON(in_irq()); if (!irqs_disabled()) spin_lock_bh(&clockfw_lock); } while (0)
+#define UNLOCK() do { if (!irqs_disabled()) spin_unlock_bh(&clockfw_lock); } while (0)
+/**********************************************for clock data****************************************************/
+static struct clk *def_ops_clk=NULL;
+
+void clk_register_default_ops_clk(struct clk *clk)
+{
+       def_ops_clk=clk;
+}
+
+static struct clk *clk_default_get_parent(struct clk *clk)
+{
+       if(def_ops_clk&&def_ops_clk->get_parent)
+               return def_ops_clk->get_parent(clk);
+       else return NULL;
+
+
+
+}
+static int clk_default_set_parent(struct clk *clk, struct clk *parent)
+{
+       if(def_ops_clk&&def_ops_clk->set_parent)
+               return def_ops_clk->set_parent(clk,parent);
+       else
+               return -EINVAL;
+}
+
+int __init clk_disable_unused(void)
+{
+       struct clk *ck;
+       CLOCK_PRINTK_DBG("clk_disable_unused in\n");
 
-#define CLK(dev, con, ck) \
-       { \
-               .dev_id = dev, \
-               .con_id = con, \
-               .clk = ck, \
+       list_for_each_entry(ck, &clocks, node) {
+       if (ck->usecount > 0 || ck->mode == NULL || (ck->flags & IS_PD))
+               continue;
+               CLOCK_PRINTK_DBG("disbale %s\n",ck->name);
+               LOCK();
+               clk_enable_nolock(ck);
+               //clk_disable_nolock(ck);
+               UNLOCK();
        }
+       return 0;
+}
+/**
+ * recalculate_root_clocks - recalculate and propagate all root clocks
+ *
+ * Recalculates all root clocks (clocks with no parent), which if the
+ * clock's .recalc is set correctly, should also propagate their rates.
+ * Called at init.
+ */
+void clk_recalculate_root_clocks_nolock(void)
+{
+       struct clk *clkp;
 
-static struct clk_lookup clks[] = {
-       CLK("rk30_i2c.0", "i2c", &xin24m),
-       CLK("rk30_i2c.1", "i2c", &xin24m),
-       CLK("rk30_i2c.2", "i2c", &xin24m),
-       CLK("rk30_i2c.3", "i2c", &xin24m),
-       CLK("rk30_i2c.4", "i2c", &xin24m),
-       CLK("rk29xx_spim.0", "spi", &xin24m),
-       CLK("rk29xx_spim.1", "spi", &xin24m),
-};
+       list_for_each_entry(clkp, &root_clks, sibling) {
+               __clk_recalc(clkp);
+               __propagate_rate(clkp);
+       }
+}
+/*
+void clk_recalculate_root_clocks(void)
+{
+       LOCK();
+       clk_recalculate_root_clocks_nolock();
+       UNLOCK();
+}*/
+       
+/**
+ * clk_preinit - initialize any fields in the struct clk before clk init
+ * @clk: struct clk * to initialize
+ *
+ * Initialize any struct clk fields needed before normal clk initialization
+ * can run.  No return value.
+ */
+int clk_register(struct clk *clk)
+{
+       if (clk == NULL || IS_ERR(clk))
+               return -EINVAL;
+       //INIT_LIST_HEAD(&clk->sibling);
+       INIT_LIST_HEAD(&clk->children);
+
+       /*
+        * trap out already registered clocks
+        */
+       if (clk->node.next || clk->node.prev)
+               return 0;
+
+       mutex_lock(&clocks_mutex);
+       if (clk->get_parent)
+               clk->parent = clk->get_parent(clk);
+       else if (clk->parents)
+               clk->parent =clk_default_get_parent(clk);
+       
+       if (clk->parent)
+               list_add(&clk->sibling, &clk->parent->children);
+       else
+               list_add(&clk->sibling, &root_clks);
+       list_add(&clk->node, &clocks);
+       mutex_unlock(&clocks_mutex);    
+       return 0;
+}
 
-void __init rk30_clock_init(void)
+/************************************************************/
+static void __clk_recalc(struct clk *clk)
 {
-       struct clk_lookup *lk;
+       if (unlikely(clk->flags & RATE_FIXED))
+               return;
+       if (clk->recalc)
+               clk->rate = clk->recalc(clk);
+       else if (clk->parent)
+               clk->rate = clk->parent->rate;
+}
+static void __clk_reparent(struct clk *child, struct clk *parent)
+{
+       if (child->parent == parent)
+               return;
+       CLOCK_PRINTK_DBG("%s reparent to %s (was %s)\n", child->name, parent->name, ((child->parent) ? child->parent->name : "NULL"));
+
+       list_del_init(&child->sibling);
+       if (parent)
+               list_add(&child->sibling, &parent->children);
+       child->parent = parent;
+}
 
-       for (lk = clks; lk < clks + ARRAY_SIZE(clks); lk++) {
-               clkdev_add(lk);
+/* Propagate rate to children */
+static void __propagate_rate(struct clk *tclk)
+{
+       struct clk *clkp;
+       
+       //CLOCK_PRINTK_DBG("propagate_rate clk %s\n",clkp->name);
+       
+       list_for_each_entry(clkp, &tclk->children, sibling) {
+               __clk_recalc(clkp);
+               __propagate_rate(clkp);
        }
+       //CLOCK_PRINTK_DBG("propagate_rate clk %s end\n",clkp->name);
 }
 
-int clk_enable(struct clk *clk)
+int clk_enable_nolock(struct clk *clk)
 {
        int ret = 0;
 
-       if (clk == NULL || IS_ERR(clk))
+       if (clk->usecount == 0) {
+               if (clk->parent) {
+                       ret = clk_enable_nolock(clk->parent);
+                       if (ret)
+                               return ret;
+               }
+
+               if (clk->notifier_count)
+                       clk_notify(clk, CLK_PRE_ENABLE, clk->rate, clk->rate);
+               if (clk->mode)
+                       ret = clk->mode(clk, 1);
+               if (clk->notifier_count)
+                       clk_notify(clk, ret ? CLK_ABORT_ENABLE : CLK_POST_ENABLE, clk->rate, clk->rate);
+               if (ret) {
+                       if (clk->parent)
+                               clk_disable_nolock(clk->parent);
+                       return ret;
+               }
+               pr_debug("%s enabled\n", clk->name);
+       }
+       clk->usecount++;
+
+       return ret;
+}
+void clk_disable_nolock(struct clk *clk)
+{
+       if (clk->usecount == 0) {
+               printk(KERN_ERR "Trying disable clock %s with 0 usecount\n", clk->name);
+               WARN_ON(1);
+               return;
+       }
+       if (--clk->usecount == 0) {
+               int ret = 0;
+               if (clk->notifier_count)
+                       clk_notify(clk, CLK_PRE_DISABLE, clk->rate, clk->rate);
+               if (clk->mode)
+                       ret = clk->mode(clk, 0);
+               if (clk->notifier_count)
+                       clk_notify(clk, ret ? CLK_ABORT_DISABLE : CLK_POST_DISABLE, clk->rate, clk->rate);
+               pr_debug("%s disabled\n", clk->name);
+               if (ret == 0 && clk->parent)
+                       clk_disable_nolock(clk->parent);
+       }
+}
+/* Given a clock and a rate apply a clock specific rounding function */
+long clk_round_rate_nolock(struct clk *clk, unsigned long rate)
+{
+       if (clk->round_rate)
+               return clk->round_rate(clk, rate);
+
+       if (clk->flags & RATE_FIXED)
+               printk(KERN_ERR "clock: clk_round_rate called on fixed-rate clock %s\n", clk->name);
+
+       return clk->rate;
+}
+int clk_set_rate_nolock(struct clk *clk, unsigned long rate)
+{
+       int ret;
+       unsigned long old_rate;
+
+       if (rate == clk->rate)
+               return 0;
+       if (clk->flags & CONFIG_PARTICIPANT)
                return -EINVAL;
 
+       if (!clk->set_rate)
+               return -EINVAL;
+       
+       //CLOCK_PRINTK_DBG("**will set %s rate %lu\n", clk->name, rate);
+
+       old_rate = clk->rate;
+       if (clk->notifier_count)
+               clk_notify(clk, CLK_PRE_RATE_CHANGE, old_rate, rate);
+
+       ret = clk->set_rate(clk, rate);
+
+       if (ret == 0) {
+               __clk_recalc(clk);
+               //CLOCK_PRINTK_DBG("**set %s rate recalc=%lu\n",clk->name,clk->rate);
+               __propagate_rate(clk);
+       }
+
+       if (clk->notifier_count)
+               clk_notify(clk, ret ? CLK_ABORT_RATE_CHANGE : CLK_POST_RATE_CHANGE, old_rate, clk->rate);
+
        return ret;
 }
-EXPORT_SYMBOL(clk_enable);
+int clk_set_parent_nolock(struct clk *clk, struct clk *parent)
+{
+       int ret;
+       int enabled = clk->usecount > 0;
+       struct clk *old_parent = clk->parent;
 
-void clk_disable(struct clk *clk)
+       if (clk->parent == parent)
+               return 0;
+
+       /* if clk is already enabled, enable new parent first and disable old parent later. */
+       if (enabled)
+               clk_enable_nolock(parent);
+
+       if (clk->set_parent)
+               ret = clk->set_parent(clk, parent);
+       else
+               ret = clk_default_set_parent(clk,parent);
+
+       if (ret == 0) {
+               /* OK */
+               
+               //CLOCK_PRINTK_DBG("set_parent %s reparent\n",clk->name,parent->name);
+               __clk_reparent(clk, parent);
+               __clk_recalc(clk);
+               __propagate_rate(clk);
+               if (enabled)
+                       clk_disable_nolock(old_parent);
+       } else {
+               //CLOCK_PRINTK_DBG("set_parent err\n",clk->name,parent->name);
+               if (enabled)
+                       clk_disable_nolock(parent);
+       }
+
+       return ret;
+}
+/**********************************dvfs****************************************************/
+
+struct clk_node *clk_get_dvfs_info(struct clk *clk)
 {
+    return clk->dvfs_info;
+}
+
+int clk_set_rate_locked(struct clk * clk,unsigned long rate)
+{
+       int ret;
+       CLOCK_PRINTK_DBG("%s dvfs clk_set_locked\n",clk->name);
+       LOCK();
+    ret=clk_set_rate_nolock(clk, rate);;
+    UNLOCK();
+       return ret;
+       
+}
+void clk_register_dvfs(struct clk_node *dvfs_clk, struct clk *clk)
+{
+    clk->dvfs_info = dvfs_clk;
+}
+
+
+/*-------------------------------------------------------------------------
+ * Optional clock functions defined in include/linux/clk.h
+ *-------------------------------------------------------------------------*/
+#ifdef RK30_CLK_OFFBOARD_TEST
+long rk30_clk_round_rate(struct clk *clk, unsigned long rate)
+#else
+long clk_round_rate(struct clk *clk, unsigned long rate)
+#endif
+{
+       long ret = 0;
+
        if (clk == NULL || IS_ERR(clk))
-               return;
+               return ret;
+
+       LOCK();
+       ret = clk_round_rate_nolock(clk, rate);
+       UNLOCK();
+
+       return ret;
 }
-EXPORT_SYMBOL(clk_disable);
 
+#ifdef RK30_CLK_OFFBOARD_TEST
+EXPORT_SYMBOL(rk30_clk_round_rate);
+#else
+EXPORT_SYMBOL(clk_round_rate);
+#endif
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+unsigned long rk30_clk_get_rate(struct clk *clk)
+#else
 unsigned long clk_get_rate(struct clk *clk)
+#endif
 {
-       return 24000000;
+       if (clk == NULL || IS_ERR(clk))
+               return 0;
+
+       return clk->rate;
 }
+#ifdef RK30_CLK_OFFBOARD_TEST
+EXPORT_SYMBOL(rk30_clk_get_rate);
+#else
 EXPORT_SYMBOL(clk_get_rate);
+#endif
+
 
+/* Set the clock rate for a clock source */
+#ifdef RK30_CLK_OFFBOARD_TEST
+int rk30_clk_set_rate(struct clk *clk, unsigned long rate)
+#else
 int clk_set_rate(struct clk *clk, unsigned long rate)
+#endif
 {
        int ret = -EINVAL;
-
-       if (clk == NULL || IS_ERR(clk))
+       if (clk == NULL || IS_ERR(clk)){
                return ret;
+       }
+       if (clk->dvfs_info!=NULL&&is_support_dvfs(clk->dvfs_info))
+               return dvfs_set_rate(clk, rate);
+
+       LOCK();
+       ret = clk_set_rate_nolock(clk, rate);
+       UNLOCK();
 
        return ret;
 }
+#ifdef RK30_CLK_OFFBOARD_TEST
+EXPORT_SYMBOL(rk30_clk_set_rate);
+#else
 EXPORT_SYMBOL(clk_set_rate);
+#endif
+
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+int rk30_clk_set_parent(struct clk *clk, struct clk *parent)
+#else
+int clk_set_parent(struct clk *clk, struct clk *parent)
+#endif
+{
+       int ret = -EINVAL;
+
+       if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent))
+               return ret;
+
+       if (clk->set_parent==NULL||clk->parents == NULL)
+               return ret;
+
+       LOCK();
+       if (clk->usecount == 0)
+               ret = clk_set_parent_nolock(clk, parent);
+       else
+               ret = -EBUSY;
+       UNLOCK();
+
+       return ret;
+}
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+EXPORT_SYMBOL(rk30_clk_set_parent);
+#else
+EXPORT_SYMBOL(clk_set_parent);
+#endif
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+struct clk *rk30_clk_get_parent(struct clk *clk)
+#else
+struct clk *clk_get_parent(struct clk *clk)
+#endif
+{
+       return clk->parent;
+}
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+EXPORT_SYMBOL(rk30_clk_get_parent);
+#else
+EXPORT_SYMBOL(clk_get_parent);
+#endif
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+void rk30_clk_disable(struct clk *clk)
+#else
+void clk_disable(struct clk *clk)
+#endif
+{
+       if (clk == NULL || IS_ERR(clk))
+               return;
+
+       LOCK();
+       clk_disable_nolock(clk);
+       UNLOCK();
+}
+#ifdef RK30_CLK_OFFBOARD_TEST
+EXPORT_SYMBOL(rk30_clk_disable);
+#else
+EXPORT_SYMBOL(clk_disable);
+#endif
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+int rk30_clk_enable(struct clk *clk)
+#else
+int  clk_enable(struct clk *clk)
+#endif
+{
+       int ret = 0;
+
+       if (clk == NULL || IS_ERR(clk))
+               return -EINVAL;
+
+       LOCK();
+       ret = clk_enable_nolock(clk);
+       UNLOCK();
+
+       return ret;
+}
+#ifdef RK30_CLK_OFFBOARD_TEST
+EXPORT_SYMBOL(rk30_clk_enable);
+#else
+EXPORT_SYMBOL(clk_enable);
+#endif
+
+/* Clk notifier implementation */
+
+/**
+ * struct clk_notifier - associate a clk with a notifier
+ * @clk: struct clk * to associate the notifier with
+ * @notifier_head: a raw_notifier_head for this clk
+ * @node: linked list pointers
+ *
+ * A list of struct clk_notifier is maintained by the notifier code.
+ * An entry is created whenever code registers the first notifier on a
+ * particular @clk.  Future notifiers on that @clk are added to the
+ * @notifier_head.
+ */
+struct clk_notifier {
+       struct clk                      *clk;
+       struct raw_notifier_head        notifier_head;
+       struct list_head                node;
+};
+static LIST_HEAD(clk_notifier_list);
+/**
+ * _clk_free_notifier_chain - safely remove struct clk_notifier
+ * @cn: struct clk_notifier *
+ *
+ * Removes the struct clk_notifier @cn from the clk_notifier_list and
+ * frees it.
+ */
+static void _clk_free_notifier_chain(struct clk_notifier *cn)
+{
+       list_del(&cn->node);
+       kfree(cn);
+}
+
+/**
+ * clk_notify - call clk notifier chain
+ * @clk: struct clk * that is changing rate
+ * @msg: clk notifier type (i.e., CLK_POST_RATE_CHANGE; see mach/clock.h)
+ * @old_rate: old rate
+ * @new_rate: new rate
+ *
+ * Triggers a notifier call chain on the post-clk-rate-change notifier
+ * for clock 'clk'.  Passes a pointer to the struct clk and the
+ * previous and current rates to the notifier callback.  Intended to be
+ * called by internal clock code only.  No return value.
+ */
+static void clk_notify(struct clk *clk, unsigned long msg,
+                      unsigned long old_rate, unsigned long new_rate)
+{
+       struct clk_notifier *cn;
+       struct clk_notifier_data cnd;
+
+       cnd.clk = clk;
+       cnd.old_rate = old_rate;
+       cnd.new_rate = new_rate;
+
+       UNLOCK();
+       list_for_each_entry(cn, &clk_notifier_list, node) {
+               if (cn->clk == clk) {
+                       pr_debug("%s msg %lu rate %lu -> %lu\n", clk->name, msg, old_rate, new_rate);
+                       raw_notifier_call_chain(&cn->notifier_head, msg, &cnd);
+                       break;
+               }
+       }
+       LOCK();
+}
+
+/**
+ * clk_notifier_register - add a clock parameter change notifier
+ * @clk: struct clk * to watch
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request notification for changes to the clock 'clk'.  This uses a
+ * blocking notifier.  Callback code must not call into the clock
+ * framework, as clocks_mutex is held.  Pre-notifier callbacks will be
+ * passed the previous and new rate of the clock.
+ *
+ * clk_notifier_register() must be called from process
+ * context.  Returns -EINVAL if called with null arguments, -ENOMEM
+ * upon allocation failure; otherwise, passes along the return value
+ * of blocking_notifier_chain_register().
+ */
+int rk30_clk_notifier_register(struct clk *clk, struct notifier_block *nb)
+{
+       struct clk_notifier *cn = NULL, *cn_new = NULL;
+       int r;
+       struct clk *clkp;
+
+       if (!clk || IS_ERR(clk) || !nb)
+               return -EINVAL;
+
+       mutex_lock(&clocks_mutex);
+
+       list_for_each_entry(cn, &clk_notifier_list, node)
+               if (cn->clk == clk)
+                       break;
+
+       if (cn->clk != clk) {
+               cn_new = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL);
+               if (!cn_new) {
+                       r = -ENOMEM;
+                       goto cnr_out;
+               };
+
+               cn_new->clk = clk;
+               RAW_INIT_NOTIFIER_HEAD(&cn_new->notifier_head);
+
+               list_add(&cn_new->node, &clk_notifier_list);
+               cn = cn_new;
+       }
+
+       r = raw_notifier_chain_register(&cn->notifier_head, nb);
+       if (!IS_ERR_VALUE(r)) {
+               clkp = clk;
+               do {
+                       clkp->notifier_count++;
+               } while ((clkp = clkp->parent));
+       } else {
+               if (cn_new)
+                       _clk_free_notifier_chain(cn);
+       }
+
+cnr_out:
+       mutex_unlock(&clocks_mutex);
+
+       return r;
+}
+EXPORT_SYMBOL(rk30_clk_notifier_register);
+
+/**
+ * clk_notifier_unregister - remove a clock change notifier
+ * @clk: struct clk *
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request no further notification for changes to clock 'clk'.
+ * Returns -EINVAL if called with null arguments; otherwise, passes
+ * along the return value of blocking_notifier_chain_unregister().
+ */
+int rk30_clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
+{
+       struct clk_notifier *cn = NULL;
+       struct clk *clkp;
+       int r = -EINVAL;
+
+       if (!clk || IS_ERR(clk) || !nb)
+               return -EINVAL;
+
+       mutex_lock(&clocks_mutex);
+
+       list_for_each_entry(cn, &clk_notifier_list, node)
+               if (cn->clk == clk)
+                       break;
+
+       if (cn->clk != clk) {
+               r = -ENOENT;
+               goto cnu_out;
+       };
+
+       r = raw_notifier_chain_unregister(&cn->notifier_head, nb);
+       if (!IS_ERR_VALUE(r)) {
+               clkp = clk;
+               do {
+                       clkp->notifier_count--;
+               } while ((clkp = clkp->parent));
+       }
+
+       /*
+        * XXX ugh, layering violation.  There should be some
+        * support in the notifier code for this.
+        */
+       if (!cn->notifier_head.head)
+               _clk_free_notifier_chain(cn);
+
+cnu_out:
+       mutex_unlock(&clocks_mutex);
+
+       return r;
+}
+EXPORT_SYMBOL(rk30_clk_notifier_unregister);
+
+#ifdef CONFIG_PROC_FS
+static struct clk_dump_ops *dump_def_ops;
+
+void clk_register_dump_ops(struct clk_dump_ops *ops)
+{
+       dump_def_ops=ops;
+}
+       
+static int proc_clk_show(struct seq_file *s, void *v)
+{
+       struct clk* clk;
+       
+       if(!dump_def_ops)
+               return 0;
+
+       if(dump_def_ops->dump_clk)
+       {
+               mutex_lock(&clocks_mutex);
+               list_for_each_entry(clk, &clocks, node) {
+                       if (!clk->parent)
+                       {
+                               dump_def_ops->dump_clk(s, clk, 0,&clocks);
+                       }
+               }
+               mutex_unlock(&clocks_mutex);
+       }
+       if(dump_def_ops->dump_regs)
+               dump_def_ops->dump_regs(s);
+       return 0;
+}
+
+
+static int proc_clk_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_clk_show, NULL);
+}
+
+static const struct file_operations proc_clk_fops = {
+       .open           = proc_clk_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init clk_proc_init(void)
+{
+       proc_create("clocks", 0, NULL, &proc_clk_fops);
+       return 0;
+
+}
+late_initcall(clk_proc_init);
+#endif /* CONFIG_PROC_FS */
+
diff --git a/arch/arm/mach-rk30/clock.h b/arch/arm/mach-rk30/clock.h
new file mode 100644 (file)
index 0000000..7bf2696
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef __MACH_CLOCK_H__
+#define __MACH_CLOCK_H__
+
+#ifndef CONFIG_ARCH_RK30
+#define RK30_CLK_OFFBOARD_TEST
+#endif
+
+
+/* Clock flags */
+/* bit 0 is free */
+#define RATE_FIXED             (1 << 1)        /* Fixed clock rate */
+#define CONFIG_PARTICIPANT     (1 << 10)       /* Fundamental clock */
+#define IS_PD                  (1 << 2)        /* Power Domain */
+
+enum _clk_i2s_rate_support {
+       i2s_8192khz = 8192000,
+       i2s_11289_6khz = 11289600,
+       i2s_12288khz = 12288000,
+       i2s_22579_2khz = 22579200,
+       i2s_24576khz = 24576000,//HDMI
+       i2s_49152khz = 24576000,//HDMI
+};
+
+struct _pll_data{
+       u8 id;
+       void *table;
+};
+//struct clk_node;
+struct clk {
+       struct list_head        node;
+       const char              *name;
+       struct clk              *parent;
+       struct list_head        children;
+       struct list_head        sibling;        /* node for children */
+       
+       int                     (*mode)(struct clk *clk, int on);
+       unsigned long           (*recalc)(struct clk *);        /* if null, follow parent */
+       int                     (*set_rate)(struct clk *, unsigned long);
+       long                    (*round_rate)(struct clk *, unsigned long);
+       struct clk*             (*get_parent)(struct clk *);    /* get clk's parent from the hardware. default is clksel_get_parent if parents present */
+       int                     (*set_parent)(struct clk *, struct clk *);      /* default is clksel_set_parent if parents present */
+
+       unsigned long           rate;
+       u32                     flags;
+       s16                     usecount;
+       u16                     notifier_count;
+       u8                      gate_idx;
+       struct _pll_data *pll;
+       u32                     clksel_con;
+       u32                     div_mask;
+       u32                     div_shift;
+       u32                     div_max;
+       u32                     src_mask;
+       u32                     src_shift;
+       
+       struct clk      **parents;
+       u8      parents_num;
+       struct clk_node *dvfs_info;
+       
+};
+
+int __init clk_disable_unused(void);
+void clk_recalculate_root_clocks_nolock(void);
+void clk_recalculate_root_clocks(void);
+int clk_register(struct clk *clk);
+void clk_register_default_ops_clk(struct clk *clk);
+
+int clk_enable_nolock(struct clk *clk);
+void clk_disable_nolock(struct clk *clk);
+long clk_round_rate_nolock(struct clk *clk, unsigned long rate);
+int clk_set_rate_nolock(struct clk *clk, unsigned long rate);
+int clk_set_parent_nolock(struct clk *clk, struct clk *parent);
+int clk_set_rate_locked(struct clk * clk,unsigned long rate);
+void clk_register_dvfs(struct clk_node *dvfs_clk, struct clk *clk);
+struct clk_node *clk_get_dvfs_info(struct clk *clk);
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+#include <linux/device.h>
+struct clk *rk30_clk_get(struct device *dev, const char *con_id);
+#endif
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+struct clk_dump_ops {
+       void (*dump_clk)(struct seq_file *s, struct clk *clk, int deep,const struct list_head *root_clocks);
+       void (*dump_regs)(struct seq_file *s);
+};
+
+void clk_register_dump_ops(struct clk_dump_ops *ops);
+#else
+static void clk_register_dump_ops(struct clk_dump_ops *ops){
+}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-rk30/clock_data.c b/arch/arm/mach-rk30/clock_data.c
new file mode 100644 (file)
index 0000000..172cc16
--- /dev/null
@@ -0,0 +1,3248 @@
+/* linux/arch/arm/mach-rk30/clock_data.c
+ *
+ * Copyright (C) 2012 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/hardirq.h>
+#include <mach/cru.h>
+#include "clock.h"
+
+#ifndef RK30_CLK_OFFBOARD_TEST
+#include <mach/pmu.h>
+#endif
+
+
+#define MHZ                    (1000*1000)
+#define KHZ                    (1000)
+
+//#define CLK_LPJ_CALC
+#ifdef CLK_LPJ_CALC
+static unsigned long _clk_loops_per_jiffy;
+static unsigned long _clk_loops_rate_ref;
+
+#define CLK_LOOPS_JIFFY_REF _clk_loops_per_jiffy
+#define CLK_LOOPS_RARE_REF     _clk_loops_rate_ref
+
+#else
+
+#define CLK_LOOPS_JIFFY_REF 2*2998368ULL
+#define CLK_LOOPS_RARE_REF (600*MHZ)
+
+#endif
+//define CLK_LOOPS_RECALC(new_rate) cpufreq_scale(CLK_LOOPS_JIFFY_REF,CLK_LOOPS_RARE_REF,(new_rate))
+#define CLK_LOOPS_RECALC(new_rate)  div_u64(CLK_LOOPS_JIFFY_REF*(new_rate),CLK_LOOPS_RARE_REF)
+struct apll_clk_set {
+       unsigned long rate;
+       u32     pllcon0;
+       u32     pllcon1; 
+       u32     pllcon2; //nb=bwadj+1;0:11;nb=nf/2
+       u32 rst_dly;//us
+       u32     clksel0;
+       u32     clksel1;
+       unsigned long lpj;
+};
+struct pll_clk_set {
+       unsigned long rate;
+       u32     pllcon0;
+       u32     pllcon1; 
+       u32     pllcon2; //nb=bwadj+1;0:11;nb=nf/2
+       u32 rst_dly;//us
+};
+
+#define SET_PLL_DATA(_pll_id,_table) \
+{\
+       .id=(_pll_id),\
+       .table=(_table),\
+}
+
+
+#define _PLL_SET_CLKS(_mhz, nr, nf, no) \
+{ \
+       .rate   = (_mhz) * KHZ, \
+       .pllcon0 = PLL_CLKR_SET(nr)|PLL_CLKOD_SET(no), \
+       .pllcon1 = PLL_CLKF_SET(nf),\
+       .pllcon2 = PLL_CLK_BWADJ_SET(nf/2-1),\
+       .rst_dly=((nr*500)/24+1),\
+       }
+#ifndef CLK_LPJ_CALC
+#define _APLL_SET_LPJ(_mhz) \
+       .lpj= CLK_LOOPS_JIFFY_REF * _mhz/CLK_LOOPS_RARE_REF
+#else
+#define _APLL_SET_LPJ(_mhz) \
+       .lpj=0
+#endif
+
+#define _APLL_SET_CLKS(_mhz, nr, nf, no, _periph_div,_axi_div, _ahb_div, _apb_div) \
+       { \
+       .rate   = _mhz * MHZ, \
+       .pllcon0 = PLL_CLKR_SET(nr)|PLL_CLKOD_SET(no),\
+       .pllcon1 = PLL_CLKF_SET(nf),\
+       .pllcon2 = PLL_CLK_BWADJ_SET(nf>>1),\
+       .clksel0 = CORE_PERIPH_W_MSK|CORE_PERIPH_##_periph_div,\
+       .clksel1 = CORE_ACLK_W_MSK|CORE_ACLK_##_axi_div|\
+       ACLK_HCLK_W_MSK|ACLK_HCLK_##_ahb_div,\
+       ACLK_PCLK_W_MSK|ACLK_PCLK_##_apb_div,\
+       _APLL_SET_LPJ(_mhz),\
+       .rst_dly=((nr*500)/24+1),\
+       }
+
+#define CRU_DIV_SET(mask,shift,max) \
+       .div_mask=(mask),\
+       .div_shift=(shift),\
+       .div_max=(max)
+
+
+#define CRU_SRC_SET(mask,shift ) \
+       .src_shift=(mask),\
+       .div_shift=(shift)
+
+#define CRU_PARENTS_SET(parents_array) \
+       .parents=(parents_array),\
+       .parents_num=ARRAY_SIZE((parents_array))
+
+#define CRU_GATE_MODE_SET(_func,_IDX) \
+       .mode=_func,\
+       .gate_idx=(_IDX)
+
+struct clk_src_sel {
+       struct clk      *parent;
+       u8      value;//crt bit
+       u8      flag;
+//selgate
+};
+
+#define GATE_CLK(NAME,PARENT,ID) \
+static struct clk clk_##NAME = { \
+       .name           = #NAME, \
+       .parent         = &PARENT, \
+       .mode           = gate_mode, \
+       .gate_idx       = CLK_GATE_##ID, \
+}
+
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+fsdfdsf
+u32 TEST_GRF_REG[0x240];
+u32 TEST_CRU_REG[0x240];
+#define cru_readl(offset)      (TEST_CRU_REG[offset/4])
+
+u32 cru_writel_is_pr(u32 offset)
+{
+       return (offset==0x4000);
+}
+void cru_writel(u32 v, u32 offset)
+{
+       
+       u32 mask_v=v>>16;
+       TEST_CRU_REG[offset/4]&=(~mask_v);
+       
+       v&=(mask_v);
+
+       TEST_CRU_REG[offset/4]|=v;
+       TEST_CRU_REG[offset/4]&=0x0000ffff;
+
+       if(cru_writel_is_pr(offset))
+       {
+               printk("cru w offset=%d,set=%x,reg=%x\n",offset,v,TEST_CRU_REG[offset/4]);
+
+       }
+       
+}
+void cru_writel_i2s(u32 v, u32 offset)
+{
+       TEST_CRU_REG[offset/4]=v;
+}
+#define cru_writel_frac(v,offset) cru_writel_i2s((v),(offset))
+
+#define regfile_readl(offset)  (0xffffffff)
+//#define pmu_readl(offset)       readl(RK30_GRF_BASE + offset)
+void rk30_clkdev_add(struct clk_lookup *cl);
+#else
+#define regfile_readl(offset)  readl_relaxed(RK30_GRF_BASE + offset)
+#define cru_readl(offset)      readl_relaxed(RK30_CRU_BASE + offset)
+#define cru_writel(v, offset)  do { writel_relaxed(v, RK30_CRU_BASE + offset); dsb(); } while (0)
+
+#define cru_writel_frac(v,offset) cru_writel((v),(offset))
+#endif
+
+
+#define CRU_PRINTK_DBG(fmt, args...) printk(fmt, ## args);
+#define CRU_PRINTK_ERR(fmt, args...) printk(fmt, ## args);
+
+
+#define get_cru_bits(con,mask,shift)\
+       ((cru_readl((con)) >> (shift)) & (mask))
+
+#define set_cru_bits_w_msk(val,mask,shift,con)\
+       cru_writel(((mask)<<(shift+16))|((val)<<(shift)),(con))
+
+
+#define PLLS_IN_NORM(pll_id) (((cru_readl(CRU_MODE_CON)&PLL_MODE_MSK(pll_id))==(PLL_MODE_NORM(pll_id)&PLL_MODE_MSK(pll_id)))\
+       &&!(PLL_CONS(pll_id,3)&PLL_BYPASS))
+
+
+
+static struct clk codec_pll_clk;
+static struct clk general_pll_clk;
+static struct clk arm_pll_clk;
+static unsigned long lpj_gpll;
+static unsigned int __initdata armclk = 504*MHZ;
+
+
+/************************calc_lpj*********************************/
+
+void calc_lpj_ref(void)
+{
+
+#ifdef CLK_LPJ_CALC
+       arm_pll_clk.rate=arm_pll_clk.recalc(&arm_pll_clk);
+       calibrate_delay();
+       _clk_loops_per_jiffy=loops_per_jiffy;
+       _clk_loops_rate_ref=arm_pll_clk.rate;
+       CRU_PRINTK_DBG("loops_per_jiffy=%lu,rate=%lu\n",_clk_loops_per_jiffy,_clk_loops_rate_ref);
+#endif
+}
+
+
+/************************clk recalc div rate*********************************/
+
+//for free div
+static unsigned long clksel_recalc_div(struct clk *clk)
+{
+       u32 div = get_cru_bits(clk->clksel_con,clk->div_mask,clk->div_shift) + 1;
+       
+       unsigned long rate = clk->parent->rate / div;
+       pr_debug("%s new clock rate is %lu (div %u)\n", clk->name, rate, div);
+       return rate;
+}
+
+//for div 1 2 4 2^n
+static unsigned long clksel_recalc_shift(struct clk *clk)
+{
+       u32 shift = get_cru_bits(clk->clksel_con,clk->div_mask,clk->div_shift);
+       unsigned long rate = clk->parent->rate >> shift;
+       pr_debug("%s new clock rate is %lu (shift %u)\n", clk->name, rate, shift);
+       return rate;
+}
+
+
+static unsigned long clksel_recalc_shift_2(struct clk *clk)
+{
+       u32 shift = get_cru_bits(clk->clksel_con,clk->div_mask,clk->div_shift)+1;
+       unsigned long rate = clk->parent->rate >> shift;
+       pr_debug("%s new clock rate is %lu (shift %u)\n", clk->name, rate, shift);
+       return rate;
+}
+
+static unsigned long clksel_recalc_parent_rate(struct clk *clk)
+{
+       unsigned long rate = clk->parent->rate;
+       pr_debug("%s new clock rate is %lu\n", clk->name, rate);
+       return rate;
+}
+/********************************set div rate***********************************/
+
+//for free div
+static int clksel_set_rate_freediv(struct clk *clk, unsigned long rate)
+{
+       u32 div;
+       for (div = 0; div < clk->div_max; div++) {
+               u32 new_rate = clk->parent->rate / (div + 1);
+               if (new_rate <= rate) {
+                       set_cru_bits_w_msk(div,clk->div_mask,clk->div_shift,clk->clksel_con);
+                       //clk->rate = new_rate;
+                       pr_debug("clksel_set_rate_freediv for clock %s to rate %ld (div %d)\n", clk->name, rate, div + 1);
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
+//for div 1 2 4 2^n
+static int clksel_set_rate_shift(struct clk *clk, unsigned long rate)
+{
+       u32 shift;
+       for (shift = 0; (1 << shift) < clk->div_max; shift++) {
+               u32 new_rate = clk->parent->rate >> shift;
+               if (new_rate <= rate) {
+                       set_cru_bits_w_msk(shift,clk->div_mask,clk->div_shift,clk->clksel_con);
+                       clk->rate = new_rate;
+                       pr_debug("clksel_set_rate_shift for clock %s to rate %ld (shift %d)\n", clk->name, rate, shift);
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
+//for div 2 4 2^n
+static int clksel_set_rate_shift_2(struct clk *clk, unsigned long rate)
+{
+       u32 shift;
+
+       for (shift = 1; (1 << shift) < clk->div_max; shift++) {
+               u32 new_rate = clk->parent->rate >> shift;
+               if (new_rate <= rate) {
+                       set_cru_bits_w_msk(shift-1,clk->div_mask,clk->div_shift,clk->clksel_con);
+                       clk->rate = new_rate;
+                       pr_debug("clksel_set_rate_shift for clock %s to rate %ld (shift %d)\n", clk->name, rate, shift);
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+static u32 clk_get_freediv(unsigned long rate_out, unsigned long rate ,u32 div_max)
+{
+       u32 div;
+       unsigned long new_rate;
+       for (div = 0; div <div_max; div++) {
+               new_rate = rate / (div + 1);
+               if (new_rate <= rate_out) {
+                       return div+1;
+               }
+       }       
+       return div_max?div_max:1;
+}
+struct clk *get_freediv_parents_div(struct clk *clk,unsigned long rate,u32 *div_out)
+{
+       u32 div[2]={0,0};
+       unsigned long new_rate[2]={0,0};
+       u32 i;
+       
+       if(clk->rate==rate)
+               return 0;
+       for(i=0;i<2;i++)
+       {
+               div[i]=clk_get_freediv(rate,clk->parents[i]->rate,clk->div_max);
+               new_rate[i] = clk->parents[i]->rate/div[i];
+               if(new_rate[i]==rate)
+               {
+                       *div_out=div[i];
+                       return clk->parents[i];
+               }       
+       }
+       if(new_rate[0]<new_rate[1])
+               i=1;
+       else
+               i=0;
+       *div_out=div[i];
+       return clk->parents[i];
+}
+
+static int clkset_rate_freediv_autosel_parents(struct clk *clk, unsigned long rate)
+{
+       struct clk *p_clk;
+       u32 div;
+       int ret=0;
+       p_clk=get_freediv_parents_div(clk,rate,&div);
+       
+       CRU_PRINTK_ERR("%s %lu,form %s\n",clk->name,rate,p_clk->name);
+       if(!p_clk)
+               return -ENOENT;
+       
+       if (clk->parent != p_clk)
+       {
+               ret=clk_set_parent_nolock(clk,p_clk);
+               if(ret)
+               {
+                       CRU_PRINTK_ERR("%s can't set %lu,reparent err\n",clk->name,rate);
+                       return -ENOENT;
+               }
+       }
+       //set div
+       set_cru_bits_w_msk(div-1,clk->div_mask,clk->div_shift,clk->clksel_con);
+       return 0;       
+}
+
+//rate==div rate //hdmi
+static int clk_freediv_autosel_parents_set_fixed_rate(struct clk *clk, unsigned long rate)
+{
+       struct clk *p_clk;
+       u32 div;
+       p_clk=get_freediv_parents_div(clk,rate,&div);
+
+       if(!p_clk)
+       return -ENOENT;
+
+       if((p_clk->rate/div)!=rate||(p_clk->rate%div))
+       return -ENOENT;
+       
+       if (clk->parent != p_clk)
+       return clk_set_parent_nolock(clk,p_clk);
+       //set div
+       set_cru_bits_w_msk(div-1,clk->div_mask,clk->div_shift,clk->clksel_con);
+       return 0;       
+}
+
+/***************************round********************************/
+
+static long clksel_freediv_round_rate(struct clk *clk, unsigned long rate)
+{
+       return clk->parent->rate/clk_get_freediv(rate,clk->parent->rate,clk->div_max);
+}
+
+static long clk_freediv_round_autosel_parents_rate(struct clk *clk, unsigned long rate)
+{
+       u32 div;
+       struct clk *p_clk;
+       p_clk=get_freediv_parents_div(clk,rate,&div);
+       if(!p_clk)
+               return 0;
+       return p_clk->rate/div;
+}
+
+/**************************************others seting************************************/
+
+static struct clk* clksel_get_parent(struct clk *clk)
+{
+       return clk->parents[(cru_readl(clk->clksel_con) >> clk->src_shift) & clk->src_mask];
+}
+static int clksel_set_parent(struct clk *clk, struct clk *parent)
+{
+       u32 i;
+       if (unlikely(!clk->parents))
+               return -EINVAL;
+       for (i = 0; (i <clk->parents_num); i++) {
+               if (clk->parents[i]!= parent)
+               continue;
+               set_cru_bits_w_msk(i,clk->src_mask,clk->src_shift,clk->clksel_con);
+               return 0;
+       }
+       return -EINVAL;
+}                            
+/* Work around CRU_CLKGATE3_CON bit21~20 bug */
+static int gate_mode(struct clk *clk, int on)
+{
+       unsigned long flags;
+
+       int idx = clk->gate_idx;
+       if (idx >= CLK_GATE_MAX)
+               return -EINVAL;
+       /* ddr reconfig may change gate */
+       local_irq_save(flags);
+       if(on)
+               cru_writel(CLK_GATE_W_MSK(idx)|CLK_UN_GATE(idx), CLK_GATE_CLKID_CONS(idx));
+       else
+               cru_writel(CLK_GATE_W_MSK(idx)|CLK_GATE(idx), CLK_GATE_CLKID_CONS(idx));
+       local_irq_restore(flags);
+       return 0;
+}
+/*****************************frac set******************************************/
+
+static unsigned long clksel_recalc_frac(struct clk *clk)
+{
+       unsigned long rate;
+       u64 rate64;
+       u32 r = cru_readl(clk->clksel_con), numerator, denominator;
+       if (r == 0) // FPGA ?
+               return clk->parent->rate;
+       numerator = r >> 16;
+       denominator = r & 0xFFFF;
+       rate64 = (u64)clk->parent->rate * numerator;
+       do_div(rate64, denominator);
+       rate = rate64;
+       pr_debug("%s new clock rate is %lu (frac %u/%u)\n", clk->name, rate, numerator, denominator);
+       return rate;
+}
+
+static u32 clk_gcd(u32 numerator, u32 denominator)
+{
+       u32 a, b;
+
+       if (!numerator || !denominator)
+               return 0;
+       if (numerator > denominator) {
+               a = numerator;
+               b = denominator;
+       } else {
+               a = denominator;
+               b = numerator;
+       }
+       while (b != 0) {
+               int r = b;
+               b = a % b;
+               a = r;
+       }
+
+       return a;
+}
+
+static int frac_div_get_seting(unsigned long rate_out,unsigned long rate,
+       u32 *numerator,u32 *denominator)
+{
+       u32 gcd_vl;
+       gcd_vl = clk_gcd(rate, rate_out);
+       CRU_PRINTK_DBG("frac_get_seting rate=%lu,parent=%lu,gcd=%d\n",rate_out,rate, gcd_vl);
+
+       if (!gcd_vl) {
+               CRU_PRINTK_ERR("gcd=0, i2s frac div is not be supported\n");
+               return -ENOENT;
+       }
+
+       *numerator = rate_out / gcd_vl;
+       *denominator = rate/ gcd_vl;
+       
+       CRU_PRINTK_DBG("frac_get_seting numerator=%d,denominator=%d,times=%d\n",
+                       *numerator, *denominator, *denominator / *numerator);
+       
+       if (*numerator > 0xffff || *denominator > 0xffff||
+               (*denominator/(*numerator))<20) {
+               CRU_PRINTK_ERR("can't get a available nume and deno\n");
+               return -ENOENT;
+       }       
+       
+       return 0;
+
+}
+/* *********************pll **************************/
+
+
+#define rk30_clock_udelay(a) udelay(a);
+
+/*********************pll lock status**********************************/
+#define GRF_SOC_CON0       0x15c
+static void pll_wait_lock(int pll_idx)
+{
+       u32 pll_state[4]={1,0,2,3};
+       u32 bit = 0x10u << pll_state[pll_idx];
+       int delay = 2400000;
+       while (delay > 0) {
+               if (regfile_readl(GRF_SOC_CON0) & bit)
+                       break;
+               delay--;
+       }
+       if (delay == 0) {
+               CRU_PRINTK_ERR("wait pll bit 0x%x time out!\n", bit);
+       }
+}
+
+
+
+/***************************pll function**********************************/
+static unsigned long pll_clk_recalc(u32 pll_id,unsigned long parent_rate)
+{
+       unsigned long rate;
+       
+       if (PLLS_IN_NORM(pll_id)) {
+               u32 pll_con0 = cru_readl(PLL_CONS(pll_id,0));
+               u32 pll_con1 = cru_readl(PLL_CONS(pll_id,1));
+
+               
+               u64 rate64 = (u64)parent_rate*PLL_NF(pll_con1);
+
+               
+               CRU_PRINTK_DBG("selcon con0(%x) %x,con1(%x)%x, rate64 %llu\n",PLL_CONS(pll_id,0),pll_con0
+                       ,PLL_CONS(pll_id,1),pll_con1, rate64);
+                       
+
+               
+               //CRU_PRINTK_DBG("pll id=%d con0=%x,con1=%x,parent=%lu\n",pll_id,pll_con0,pll_con1,parent_rate);
+       //CRU_PRINTK_DBG("first pll id=%d rate is %lu (NF %d NR %d NO %d)\n",
+               //pll_id, rate, PLL_NF(pll_con1), PLL_NR(pll_con0), 1 << PLL_NO(pll_con0));
+               
+               do_div(rate64, PLL_NR(pll_con0));
+               do_div(rate64, PLL_NO(pll_con0));
+               
+               rate = rate64;
+               
+               CRU_PRINTK_DBG("pll_clk_recalc id=%d rate=%lu (NF %d NR %d NO %d) rate64=%llu\n",
+                       pll_id, rate, PLL_NF(pll_con1), PLL_NR(pll_con0),PLL_NO(pll_con0), rate64);
+               
+       } else {
+               rate = parent_rate;     
+               CRU_PRINTK_DBG("pll_clk_recalc id=%d rate=%lu by pass mode\n",pll_id,rate);     
+       }
+       return rate;
+}
+static unsigned long plls_clk_recalc(struct clk *clk)
+{
+       return pll_clk_recalc(clk->pll->id,clk->parent->rate);  
+}
+
+static int pll_clk_set_rate(struct pll_clk_set *clk_set,u8 pll_id)
+{
+       //enter slowmode
+       cru_writel(PLL_MODE_SLOW(pll_id), CRU_MODE_CON);
+       //enter rest
+       cru_writel(PLL_REST_W_MSK|PLL_REST, PLL_CONS(pll_id,3));
+       cru_writel(clk_set->pllcon0, PLL_CONS(pll_id,0));
+       cru_writel(clk_set->pllcon1, PLL_CONS(pll_id,1));
+       cru_writel(clk_set->pllcon2, PLL_CONS(pll_id,2));
+       rk30_clock_udelay(5);
+       
+       //return form rest
+       cru_writel(PLL_REST_W_MSK|PLL_REST_RESM, PLL_CONS(pll_id,3));
+       
+       //wating lock state
+       rk30_clock_udelay(clk_set->rst_dly);
+       pll_wait_lock(pll_id);
+
+       //return form slow
+       cru_writel(PLL_MODE_NORM(pll_id), CRU_MODE_CON);
+       
+       /*
+       CRU_PRINTK_ERR("pll reg id=%d,con0=%x,con1=%x,mode=%x\n",pll_id,
+               cru_readl(PLL_CONS(pll_id,0)),(PLL_CONS(pll_id,1)),cru_readl(CRU_MODE_CON));
+       */
+
+       
+       return 0;
+}
+static int gpll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       struct _pll_data *pll_data=c->pll;
+       struct pll_clk_set *clk_set=(struct pll_clk_set*)pll_data->table;
+
+       while(clk_set->rate)
+       {
+               if (clk_set->rate == rate) {
+                       break;
+               }
+               clk_set++;
+       }
+       if(clk_set->rate== rate)
+       {
+               pll_clk_set_rate(clk_set,pll_data->id);
+               lpj_gpll = CLK_LOOPS_RECALC(rate);
+       }
+       else
+       {
+               CRU_PRINTK_ERR("gpll is no corresponding rate=%lu\n", rate);
+               return -1;
+       }
+       return 0;       
+}
+
+#define PLL_FREF_MIN (183*KHZ)
+#define PLL_FREF_MAX (1500*MHZ)
+
+#define PLL_FVCO_MIN (300*MHZ)
+#define PLL_FVCO_MAX (1500*MHZ)
+
+#define PLL_FOUT_MIN (18750*KHZ)
+#define PLL_FOUT_MAX (1500*MHZ)
+
+#define PLL_NF_MAX (4096)
+#define PLL_NR_MAX (64)
+#define PLL_NO_MAX (16)
+
+static int pll_clk_get_set(unsigned long fin_hz,unsigned long fout_hz,u32 *clk_nr,u32 *clk_nf,u32 *clk_no)
+{
+       u32 nr,nf,no,nonr;
+       u32 n;
+       u32 YFfenzi;
+       u32 YFfenmu;
+       unsigned long fref,fvco,fout;
+       u32 gcd_val=0;
+       
+       CRU_PRINTK_DBG("pll_clk_get_set fin=%lu,fout=%lu\n",fin_hz,fout_hz);
+       if(!fin_hz||!fout_hz||fout_hz==fin_hz)
+               return 0;
+       gcd_val=clk_gcd(fin_hz,fout_hz);
+       YFfenzi=fout_hz/gcd_val;
+       YFfenmu=fin_hz/gcd_val;
+               
+       for(n=1;;n++)
+       {
+               nf=YFfenzi*n;
+               nonr=YFfenmu*n;
+               if(nf>PLL_NF_MAX||nonr>(PLL_NO_MAX*PLL_NR_MAX))
+                break;
+               for(no=1;no<=PLL_NO_MAX;no++)
+               {
+                       if(!(no==1||!(no%2)))
+                               continue;
+
+                       if(nonr%no)
+                               continue;
+                       nr=nonr/no;
+
+                       if(nr>PLL_NR_MAX)//PLL_NR_MAX
+                               continue;
+
+                       fref=fin_hz/nr;
+                       if(fref<PLL_FREF_MIN||fref>PLL_FREF_MAX)
+                               continue;
+                       
+                       fvco=(fin_hz/nr)*nf;
+                       if(fvco<PLL_FVCO_MIN||fvco>PLL_FVCO_MAX)
+                               continue;
+                       fout=fvco/no;
+                       if(fout<PLL_FOUT_MIN||fout>PLL_FOUT_MAX)
+                               continue;
+                       *clk_nr=nr;
+                       *clk_no=no;
+                       *clk_nf=nf;
+                       return 1;
+                       
+               }
+               
+       }
+       return 0;
+}
+
+static int pll_clk_mode(struct clk *clk, int on)
+{
+       u8 pll_id=clk->pll->id;
+       u32 nr=PLL_NR(cru_readl(PLL_CONS(pll_id,0)));
+       u32 dly= (nr*500)/24+1;
+       
+       if (on) {
+               cru_writel(PLL_PWR_ON|PLL_PWR_DN_W_MSK,PLL_CONS(pll_id,3));
+               rk30_clock_udelay(dly);
+               pll_wait_lock(pll_id);
+               cru_writel(PLL_MODE_NORM(pll_id), CRU_MODE_CON);
+       } else {
+               cru_writel(PLL_MODE_SLOW(pll_id), CRU_MODE_CON);
+               cru_writel(PLL_PWR_DN|PLL_PWR_DN_W_MSK, PLL_CONS(pll_id,3));
+       }
+       return 0;
+}
+
+static int cpll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       struct _pll_data *pll_data=c->pll;
+       struct pll_clk_set *clk_set=(struct pll_clk_set*)pll_data->table;
+       struct pll_clk_set temp_clk_set;
+       u32 clk_nr,clk_nf,clk_no;
+
+       
+       while(clk_set->rate)
+       {
+               if (clk_set->rate == rate) {
+                       break;
+               }
+               clk_set++;
+       }
+       if(clk_set->rate==rate)
+       {
+               CRU_PRINTK_DBG("cpll get a rate\n");
+               pll_clk_set_rate(clk_set,pll_data->id);
+       
+       }
+       else
+       {
+               CRU_PRINTK_DBG("cpll get auto calc a rate\n");
+               if(pll_clk_get_set(c->parent->rate,rate,&clk_nr,&clk_nf,&clk_no)==0)
+               {
+                       pr_err("cpll auto set rate error\n");
+                       return -ENOENT;
+               }
+               CRU_PRINTK_DBG("cpll auto ger rate set nr=%d,nf=%d,no=%d\n",clk_nr,clk_nf,clk_no);
+               temp_clk_set.pllcon0=PLL_CLKR_SET(clk_nr)|PLL_CLKOD_SET(clk_no);
+               temp_clk_set.pllcon1=PLL_CLKF_SET(clk_nf);
+               temp_clk_set.pllcon2=PLL_CLK_BWADJ_SET(clk_nf/2-1);
+               temp_clk_set.rst_dly=(clk_nr*500)/24+1;
+               pll_clk_set_rate(&temp_clk_set,pll_data->id);
+       
+       }
+       return 0;       
+}
+
+
+/* ******************fixed input clk ***********************************************/
+static struct clk xin24m = {
+       .name           = "xin24m",
+       .rate           = 24 * MHZ,
+       .flags          = RATE_FIXED,
+};
+static struct clk xin27m = {
+       .name           = "xin27m",
+       .rate           = 27 * MHZ,
+       //CLK_GATE_XIN27M
+       .flags          = RATE_FIXED,
+       
+};
+static struct clk clk_12m = {
+       .name           = "clk_12m",
+       .parent         =&xin24m,
+       .rate           = 12 * MHZ,
+       .flags          = RATE_FIXED,
+};
+
+/************************************pll func***************************/
+static const struct apll_clk_set* arm_pll_clk_get_best_pll_set(unsigned long rate,
+       struct apll_clk_set *tables)
+{
+       const struct apll_clk_set *ps, *pt;
+
+       /* find the arm_pll we want. */
+       ps = pt = tables;
+       while (pt->rate) {
+               if (pt->rate == rate) {
+                       ps = pt;
+                       break;
+               }
+               // we are sorted, and ps->rate > pt->rate.
+               if ((pt->rate > rate || (rate - pt->rate < ps->rate - rate)))
+                       ps = pt;
+               if (pt->rate < rate)
+                       break;
+               pt++;
+       }
+
+       CRU_PRINTK_DBG("arm pll best rate=%lu\n",ps->rate);
+       return ps;
+}
+static long arm_pll_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+       return arm_pll_clk_get_best_pll_set(rate,clk->pll->table)->rate;
+}
+
+static int arm_pll_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+       unsigned long flags;
+       const struct apll_clk_set *ps;
+       u32 pll_id=clk->pll->id;
+       u32 temp_div=0;
+
+
+
+       ps = arm_pll_clk_get_best_pll_set(rate,(struct apll_clk_set *)clk->pll->table);
+       
+       printk("sel %x,%x\n",ps->clksel0,ps->clksel1);
+
+       if(general_pll_clk.rate>clk->rate)
+       {
+               temp_div=general_pll_clk.rate/clk->rate+1;
+               cru_writel(CORE_CLK_DIV(temp_div)|CORE_CLK_DIV_W_MSK, CRU_CLKSELS_CON(0));
+       }
+       
+       // open gpu gpll path
+       cru_writel(CLK_GATE_W_MSK(CLK_GATE_CPU_GPLL_PATH)|CLK_UN_GATE(CLK_GATE_CPU_GPLL_PATH)
+               , CLK_GATE_CLKID_CONS(CLK_GATE_CPU_GPLL_PATH));
+       
+       
+       local_irq_save(flags);
+       cru_writel(CORE_SEL_GPLL|CORE_SEL_PLL_W_MSK, CRU_CLKSELS_CON(0));
+       //loops_per_jiffy = lpj_gpll;
+       
+       /*if core src don't select gpll ,apll neet to enter slow mode */
+       cru_writel(PLL_MODE_SLOW(APLL_ID), CRU_MODE_CON);
+
+       //enter rest
+       cru_writel(PLL_REST_W_MSK|PLL_REST, PLL_CONS(pll_id,3));        
+       cru_writel(ps->pllcon0, PLL_CONS(pll_id,0));
+       cru_writel(ps->pllcon1, PLL_CONS(pll_id,1));
+       cru_writel(ps->pllcon2, PLL_CONS(pll_id,2));// ÏµÍ³ÓÐÎÊÌâ
+
+       local_irq_restore(flags);
+       rk30_clock_udelay(5);
+
+       //return form rest
+       cru_writel(PLL_REST_W_MSK|PLL_REST_RESM, PLL_CONS(pll_id,3));
+
+       //wating lock state
+       rk30_clock_udelay(ps->rst_dly);
+       pll_wait_lock(pll_id);
+
+       local_irq_save(flags);
+
+       //return form slow
+       cru_writel(PLL_MODE_NORM(APLL_ID), CRU_MODE_CON);
+       
+       //a/h/p clk sel
+       cru_writel((ps->clksel1), CRU_CLKSELS_CON(1));
+       cru_writel((ps->clksel0)|CORE_CLK_DIV(1)|CORE_CLK_DIV_W_MSK, CRU_CLKSELS_CON(0));
+       
+       //reparent to apll
+       cru_writel(CORE_SEL_PLL_W_MSK|CORE_SEL_APLL, CRU_CLKSELS_CON(0));
+       #ifndef CLK_LPJ_CALC
+       //loops_per_jiffy = ps->lpj;
+       #else
+       //loops_per_jiffy = CLK_LOOPS_RECALC(rate);
+       #endif
+       local_irq_restore(flags);
+
+       //gate gpll path
+       cru_writel(CLK_GATE_W_MSK(CLK_GATE_CPU_GPLL_PATH)|CLK_GATE(CLK_GATE_CPU_GPLL_PATH)
+               , CLK_GATE_CLKID_CONS(CLK_GATE_CPU_GPLL_PATH));
+
+/*
+       printk("apll %x,%x,%x,%x\n",cru_readl(PLL_CONS(pll_id,0)),
+               cru_readl(PLL_CONS(pll_id,1)),cru_readl(PLL_CONS(pll_id,2)),
+               cru_readl(PLL_CONS(pll_id,3)));
+       
+       printk("sel %x,%x\n",cru_readl(CRU_CLKSELS_CON(0)),
+                       cru_readl(CRU_CLKSELS_CON(1)));
+*/
+       
+       return 0;
+}
+
+
+/************************************pll clocks***************************/
+
+static const struct apll_clk_set apll_clks[] = {
+       _APLL_SET_CLKS(1416, 1, 59, 1, 8, 31, 21, 81),
+       _APLL_SET_CLKS(1200, 1, 50, 1, 8, 31, 21, 81),
+       _APLL_SET_CLKS(1008, 1, 42, 1, 8, 21, 21, 81),
+       //_APLL_SET_CLKS(816 , 1, 34, 1, 8, 21, 21, 81),
+       _APLL_SET_CLKS(800 , 24, 800, 1, 8, 41, 21, 81),
+       _APLL_SET_CLKS(504 , 1, 21, 1, 4, 21, 21, 81),
+       _APLL_SET_CLKS(252 , 1, 21, 2, 2, 21, 21, 41),
+       _APLL_SET_CLKS(126 , 1, 21, 4, 2, 21, 21, 41),
+       _APLL_SET_CLKS(0   , 1, 21, 4, 2, 21, 21, 41),
+};
+static struct _pll_data apll_data=SET_PLL_DATA(APLL_ID,(void *)apll_clks);
+static struct clk arm_pll_clk ={
+       .name           = "arm_pll",
+       .parent         = &xin24m,
+       .recalc         = plls_clk_recalc,
+       .set_rate       = arm_pll_clk_set_rate,
+       .round_rate     = arm_pll_clk_round_rate,
+       .pll=&apll_data,
+ };
+
+static int ddr_pll_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+       /* do nothing here */
+       return 0;
+}
+static struct _pll_data dpll_data=SET_PLL_DATA(DPLL_ID,NULL);
+static struct clk ddr_pll_clk = {
+       .name           = "ddr_pll",
+       .parent         = &xin24m,
+       .recalc         = plls_clk_recalc,
+       //.set_rate     = ddr_pll_clk_set_rate,
+       .pll=&dpll_data,
+};
+
+static const struct pll_clk_set cpll_clks[] = {
+       _PLL_SET_CLKS(360000, 1,  15, 1),
+       _PLL_SET_CLKS(408000, 1,  17, 1),
+       _PLL_SET_CLKS(456000, 1,  19, 1),
+       _PLL_SET_CLKS(504000, 1,  21, 1),
+       _PLL_SET_CLKS(552000, 1,  23, 1),
+       _PLL_SET_CLKS(600000, 1,  25, 1),
+       _PLL_SET_CLKS(     0, 1,  23, 1),
+};
+static struct _pll_data cpll_data=SET_PLL_DATA(CPLL_ID,(void *)cpll_clks);
+static struct clk codec_pll_clk = {
+       .name           = "codec_pll",
+       .parent         = &xin24m,
+       //.mode         = pll_clk_mode,
+       .recalc         = plls_clk_recalc,
+       .set_rate       = cpll_clk_set_rate,
+       .pll= &cpll_data,
+};
+
+static const struct pll_clk_set gpll_clks[] = {
+       _PLL_SET_CLKS(148500,   4,      99,     4),
+       _PLL_SET_CLKS(297000,   4,      99,     2),
+       _PLL_SET_CLKS(1188000,  2,      99,     2),
+       _PLL_SET_CLKS(0,                0,       0,     0),
+};
+static struct _pll_data gpll_data=SET_PLL_DATA(GPLL_ID,(void *)gpll_clks);
+static struct clk general_pll_clk = {
+       .name           = "general_pll",
+       .parent         = &xin24m,
+       .recalc         = plls_clk_recalc,
+       .set_rate       = gpll_clk_set_rate,
+       .pll= &gpll_data
+};
+/********************************clocks***********************************/
+
+static struct clk *clk_ddr_parents[2] = {&ddr_pll_clk, &general_pll_clk};
+static struct clk clk_ddr = {
+       .name           = "ddr",        
+       .parent         = &ddr_pll_clk,
+       .recalc         = clksel_recalc_shift,
+       .clksel_con     = CRU_CLKSELS_CON(26),
+       //CRU_DIV_SET(0x3,0,4),
+       //CRU_SRC_SET(1,8),
+       //CRU_PARENTS_SET(clk_ddr_parents),
+};
+static int arm_core_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       int ret;
+       //set arm pll div 1
+       set_cru_bits_w_msk(0,c->div_mask,c->div_shift,c->clksel_con);
+       
+       ret = clk_set_rate_nolock(c->parent, rate);
+       if (ret) {
+               CRU_PRINTK_ERR("Failed to change clk pll %s to %lu\n",c->name,rate);
+               return ret;
+       }
+       return 0;
+}
+static unsigned long arm_core_clk_get_rate(struct clk *c)
+{
+       u32 div=(get_cru_bits(c->clksel_con,c->div_mask,c->div_shift)+1);
+       //c->parent->rate=c->parent->recalc(c->parent);
+       return c->parent->rate/div;
+}
+static long core_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+       u32 div=(get_cru_bits(clk->clksel_con,clk->div_mask,clk->div_shift)+1);
+       return clk_round_rate_nolock(clk->parent,rate)/div;
+}
+
+/*
+static struct clk clk_cpu_gpll_path = {
+       .name   =  "core_gpll_path",
+       .parent =  &general_pll_clk,
+       CRU_GATE_MODE_SET(gate_mode,CLK_GATE_CPU_GPLL_PATH),
+};
+*/
+//static struct clk *clk_cpu_parents[2] = {&arm_pll_clk,&clk_cpu_gpll_path};
+
+static struct clk clk_cpu = {
+       .name   =  "cpu",
+       .parent =  &arm_pll_clk,
+       .set_rate =     arm_core_clk_set_rate,
+       .recalc =  arm_core_clk_get_rate,
+       .round_rate     = core_clk_round_rate,
+       .clksel_con     = CRU_CLKSELS_CON(0),
+       //CRU_DIV_SET(0x1f,0,32),
+       //CRU_SRC_SET(1,8),
+       //CRU_PARENTS_SET(clk_cpu_parents),
+};
+static unsigned long aclk_cpu_recalc(struct clk *clk)
+{
+       unsigned long rate;
+       u32 div = get_cru_bits(clk->clksel_con,clk->div_mask,clk->div_shift)+1;
+
+       BUG_ON(div > 5);
+       if (div >= 5)
+               div = 8;
+       rate = clk->parent->rate / div;
+       pr_debug("%s new clock rate is %ld (div %d)\n", clk->name, rate, div);
+
+       return rate;
+};
+static struct clk core_periph = {
+       .name           = "core_periph",
+       .parent         = &clk_cpu,
+       .recalc         = clksel_recalc_shift_2,
+       .clksel_con     = CRU_CLKSELS_CON(0),
+       CRU_DIV_SET(0x3,6,16),  
+};
+
+static struct clk aclk_cpu = {
+       .name           = "aclk_cpu",
+       .parent         = &clk_cpu,
+       .recalc         = aclk_cpu_recalc,
+       .clksel_con     = CRU_CLKSELS_CON(1),
+       CRU_DIV_SET(0x7,0,8),
+};
+
+static struct clk hclk_cpu = {
+       .name           = "hclk_cpu",
+       .parent         = &aclk_cpu,
+       .recalc         = clksel_recalc_shift,
+       //.set_rate     = clksel_set_rate_shift,
+       .clksel_con     = CRU_CLKSELS_CON(1),
+       CRU_DIV_SET(0x3,8,4),
+
+};
+
+static struct clk pclk_cpu = {
+       .name           = "pclk_cpu",
+       .parent         = &aclk_cpu,
+       .recalc         = clksel_recalc_shift,
+       //.set_rate     = clksel_set_rate_shift,
+       .clksel_con     = CRU_CLKSELS_CON(1),
+       CRU_DIV_SET(0x3,12,8),
+};
+
+static struct clk atclk_cpu = {
+       .name           = "atclk_cpu",
+       .parent         = &pclk_cpu,
+};
+
+static struct clk *clk_i2s_div_parents[]={&general_pll_clk,&codec_pll_clk};
+static struct clk clk_i2s_pll = {
+       .name           = "i2s_pll",
+       .parent         = &general_pll_clk,
+       .clksel_con     = CRU_CLKSELS_CON(2),
+       CRU_SRC_SET(0x1,16),
+       CRU_PARENTS_SET(clk_i2s_div_parents),
+};
+
+static struct clk clk_i2s0_div = {
+       .name           = "i2s0_div",   
+       .parent = &clk_i2s_pll,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .round_rate     =clksel_freediv_round_rate,
+       .gate_idx       = CLK_GATE_I2S0,
+       .clksel_con     = CRU_CLKSELS_CON(2),
+       CRU_DIV_SET(0x7f,0,64),
+};
+
+static struct clk clk_i2s1_div = {
+       .name           = "i2s1_div",   
+       .parent = &clk_i2s_pll,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .round_rate     =clksel_freediv_round_rate,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_I2S1,
+       .clksel_con     = CRU_CLKSELS_CON(3),
+       CRU_DIV_SET(0x7f,0,64),
+};
+
+
+static struct clk clk_i2s2_div = {
+       .name           = "i2s2_div",
+       .parent = &clk_i2s_pll,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .round_rate     =clksel_freediv_round_rate,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_I2S2,
+       .clksel_con     = CRU_CLKSELS_CON(4),
+       CRU_DIV_SET(0x7f,0,64),
+};
+static struct clk clk_spdif_div = {
+       .name           = "spdif_div",  
+       .parent = &clk_i2s_pll,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .round_rate     =clksel_freediv_round_rate,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_SPDIF,
+       .clksel_con     = CRU_CLKSELS_CON(5),
+       CRU_DIV_SET(0x7f,0,64),
+};
+static int clk_i2s_fracdiv_set_rate(struct clk *clk, unsigned long rate)
+{
+       u32 numerator, denominator;
+       //clk_i2s_div->clk_i2s_pll->gpll/cpll
+       //clk->parent->parent
+       if(frac_div_get_seting(rate,clk->parent->parent->rate,
+                       &numerator,&denominator)==0)
+       {
+               clk_set_rate_nolock(clk->parent,clk->parent->parent->rate);//PLL:DIV 1:
+               cru_writel_frac(numerator << 16 | denominator, clk->clksel_con);
+               CRU_PRINTK_DBG("%s set rate=%lu,is ok\n",clk->name,rate);
+       }
+       else
+       {
+               CRU_PRINTK_ERR("clk_frac_div can't get rate=%lu,%s\n",rate,clk->name);
+               return -ENOENT;
+       } 
+       return 0;
+}
+
+
+static struct clk clk_i2s0_frac_div = {
+       .name           = "i2s0_frac_div",
+       .parent         = &clk_i2s0_div,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_I2S0_FRAC,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_i2s_fracdiv_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(6),
+};
+
+static struct clk clk_i2s1_frac_div = {
+       .name           = "i2s1_frac_div",
+       .parent         = &clk_i2s1_div,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_I2S1_FRAC,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_i2s_fracdiv_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(7),
+};
+
+static struct clk clk_i2s2_frac_div = {
+       .name           = "i2s2_frac_div",
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_I2S2_FRAC,
+       .parent         = &clk_i2s2_div,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_i2s_fracdiv_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(7),
+};
+static struct clk clk_spdif_frac_div = {
+       .name           = "spdif_frac_div",
+       .parent         = &clk_spdif_div,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_SPDIF_FRAC,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_i2s_fracdiv_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(9),
+};
+
+#define I2S_SRC_DIV  (0x0)
+#define I2S_SRC_FRAC  (0x1)
+#define I2S_SRC_12M  (0x2)
+
+static int i2s_set_rate(struct clk *clk, unsigned long rate)
+{
+       int ret = -EINVAL;
+       struct clk *parent;
+
+       if (rate == clk->parents[I2S_SRC_12M]->rate){
+               parent = clk->parents[I2S_SRC_12M];
+       }else if((long)clk_round_rate_nolock(clk->parents[I2S_SRC_DIV],rate)==rate)
+       {
+               parent = clk->parents[I2S_SRC_DIV]; 
+       }
+       else 
+       {
+               parent =clk->parents[I2S_SRC_FRAC];
+       }
+
+       CRU_PRINTK_DBG(" %s set rate=%lu parent %s(old %s)\n",
+               clk->name,rate,parent->name,clk->parent->name);
+
+       if (clk->parent != parent)
+       {
+               ret = clk_set_parent_nolock(clk, parent);
+               if (ret)
+               {
+                       CRU_PRINTK_DBG("%s can't get rate%lu,reparent err\n",clk->name,rate);
+                       return ret;
+               }
+       }
+       if(parent!=clk->parents[I2S_SRC_12M])
+       {
+               ret = clk_set_rate_nolock(parent,rate);//div 1:1
+       }
+       
+       return ret;
+};
+
+static struct clk *clk_i2s0_parents[3]={&clk_i2s0_div,&clk_i2s0_frac_div,&clk_12m};
+
+static struct clk clk_i2s0 = {
+       .name           = "i2s0",
+       .set_rate       = i2s_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(2),
+       CRU_SRC_SET(0x3,8),
+       CRU_PARENTS_SET(clk_i2s0_parents),
+};
+
+static struct clk *clk_i2s1_parents[3]={&clk_i2s1_div,&clk_i2s1_frac_div,&clk_12m};
+
+static struct clk clk_i2s1 = {
+       .name           = "i2s1",
+       .set_rate       = i2s_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(3),
+       CRU_SRC_SET(0x3,8),
+       CRU_PARENTS_SET(clk_i2s1_parents),
+};
+
+static struct clk *clk_i2s2_parents[3]={&clk_i2s2_div,&clk_i2s2_frac_div,&clk_12m};
+
+static struct clk clk_i2s2 = {
+       .name           = "i2s2",
+       .set_rate       = i2s_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(3),
+       CRU_SRC_SET(0x3,8),
+       CRU_PARENTS_SET(clk_i2s2_parents),
+};
+
+static struct clk *clk_spdif_parents[3]={&clk_spdif_div,&clk_spdif_frac_div,&clk_12m};
+
+static struct clk clk_spdif = {
+       .name           = "spdif",
+       .parent         = &clk_spdif_frac_div,
+       .set_rate       = i2s_set_rate,
+       .clksel_con = CRU_CLKSELS_CON(4),
+       CRU_SRC_SET(0x3,8),
+       CRU_PARENTS_SET(clk_spdif_parents),
+};
+
+static struct clk *aclk_periph_parents[2]={&general_pll_clk,&codec_pll_clk};
+
+static struct clk aclk_periph = {
+       .name           = "aclk_periph",
+       .parent         = &general_pll_clk,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_ACLK_PEIRPH,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .clksel_con     = CRU_CLKSELS_CON(10),
+       CRU_DIV_SET(0x1f,0,32),
+       CRU_SRC_SET(1,15),
+       CRU_PARENTS_SET(aclk_periph_parents),
+};
+
+static struct clk pclk_periph = {
+       .name           = "pclk_periph",
+       .parent         = &aclk_periph,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_PCLK_PEIRPH,
+       .recalc         = clksel_recalc_shift,
+       .set_rate       = clksel_set_rate_shift,
+       .clksel_con     = CRU_CLKSELS_CON(10),
+       CRU_DIV_SET(0x3,12,8),
+};
+
+static struct clk hclk_periph = {
+       .name           = "hclk_periph",
+       .parent         = &aclk_periph,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_HCLK_PEIRPH,
+       .recalc         = clksel_recalc_shift,
+       .set_rate       = clksel_set_rate_shift,
+       .clksel_con = CRU_CLKSELS_CON(10),
+       CRU_DIV_SET(0x3,8,4),
+};
+
+static struct clk clk_spi0 = {
+       .name           = "spi0",
+       .parent         = &pclk_periph,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       = CLK_GATE_SPI0,
+       .clksel_con     = CRU_CLKSELS_CON(25),
+       CRU_DIV_SET(0x7f,0,128),
+};
+
+static struct clk clk_spi1 = {
+       .name           = "spi1",
+       .parent         = &pclk_periph,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       = CLK_GATE_SPI1,
+       .clksel_con     = CRU_CLKSELS_CON(25),
+       CRU_DIV_SET(0x7f,8,128),
+};
+
+static struct clk clk_saradc = {
+       .name           = "saradc",
+       .parent         = &xin24m,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       =  CLK_GATE_SARADC,
+       .clksel_con     =CRU_CLKSELS_CON(24),
+       CRU_DIV_SET(0xff,8,256),
+};
+static struct clk clk_tsadc = {
+       .name           = "tsadc",
+       .parent         = &xin24m,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       = CLK_GATE_TSADC,
+       .clksel_con =CRU_CLKSELS_CON(34),
+       CRU_DIV_SET(0xffff,0,65536),
+};
+GATE_CLK(otgphy0, xin24m, OTGPHY0);
+GATE_CLK(otgphy1, xin24m, OTGPHY1);
+
+
+GATE_CLK(smc, pclk_periph, SMC);//smc
+
+static struct clk clk_sdmmc = {
+       .name           = "sdmmc",
+       .parent         = &hclk_periph,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       = CLK_GATE_MMC0,
+       .clksel_con =CRU_CLKSELS_CON(11),
+       CRU_DIV_SET(0x3f,0,64),
+};
+
+static struct clk clk_sdio = {
+       .name           = "sdio",
+       .parent         = &hclk_periph,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       = CLK_GATE_SDIO,
+       .clksel_con =CRU_CLKSELS_CON(12),
+       CRU_DIV_SET(0x3f,0,64),
+
+};
+
+static struct clk clk_emmc = {
+       .name           = "emmc",
+       .parent         = &hclk_periph,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       = CLK_GATE_EMMC,
+       .clksel_con =CRU_CLKSELS_CON(12),
+       CRU_DIV_SET(0x3f,8,64),
+};
+
+static struct clk *clk_uart_src_parents[2]={&general_pll_clk,&codec_pll_clk};
+static struct clk clk_uart_pll = {
+       .name           = "uart_pll",
+       .parent         = &general_pll_clk,
+       .clksel_con =CRU_CLKSELS_CON(12),
+       CRU_SRC_SET(0x1,15),
+       CRU_PARENTS_SET(clk_uart_src_parents),
+};
+static struct clk clk_uart0_div = {
+       .name           = "uart0_div",
+       .parent         = &clk_uart_pll,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_UART0,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .round_rate     =clksel_freediv_round_rate,
+       .clksel_con     = CRU_CLKSELS_CON(13),
+       CRU_DIV_SET(0x7f,0,64), 
+};
+static struct clk clk_uart1_div = {
+       .name           = "uart1_div",
+       .parent         = &clk_uart_pll,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_UART1,
+       .recalc         = clksel_recalc_div,
+       .round_rate     =clksel_freediv_round_rate,
+       .set_rate       = clksel_set_rate_freediv,      
+       .clksel_con     = CRU_CLKSELS_CON(14),
+       CRU_DIV_SET(0x7f,0,64), 
+};
+
+static struct clk clk_uart2_div = {
+       .name           = "uart2_div",
+       .parent         = &clk_uart_pll,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_UART2,
+       .recalc         = clksel_recalc_div,
+       .round_rate     =clksel_freediv_round_rate,
+       .set_rate       = clksel_set_rate_freediv,
+       .clksel_con     = CRU_CLKSELS_CON(15),
+       CRU_DIV_SET(0x7f,0,64), 
+};
+
+static struct clk clk_uart3_div = {
+       .name           = "uart3_div",
+       .parent         = &clk_uart_pll,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_UART3,
+       .recalc         = clksel_recalc_div,
+       .round_rate     =clksel_freediv_round_rate,
+       .set_rate       = clksel_set_rate_freediv,
+       .clksel_con     = CRU_CLKSELS_CON(16),
+       CRU_DIV_SET(0x7f,0,64), 
+};
+static int clk_uart_fracdiv_set_rate(struct clk *clk, unsigned long rate)
+{
+       u32 numerator, denominator;
+       //clk_uart0_div->clk_uart_pll->gpll/cpll
+       //clk->parent->parent
+       if(frac_div_get_seting(rate,clk->parent->parent->rate,
+                       &numerator,&denominator)==0)
+       {
+               clk_set_rate_nolock(clk->parent,clk->parent->parent->rate);//PLL:DIV 1:
+               
+               cru_writel_frac(numerator << 16 | denominator, clk->clksel_con);
+               
+               CRU_PRINTK_DBG("%s set rate=%lu,is ok\n",clk->name,rate);
+       }
+       else
+       {
+               CRU_PRINTK_ERR("clk_frac_div can't get rate=%lu,%s\n",rate,clk->name);
+               return -ENOENT;
+       } 
+       return 0;
+}
+
+static struct clk clk_uart0_frac_div = {
+       .name           = "uart0_frac_div",
+       .parent         = &clk_uart0_div,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_uart_fracdiv_set_rate,
+       .gate_idx       = CLK_GATE_FRAC_UART0,
+       .clksel_con     = CRU_CLKSELS_CON(17),
+};
+static struct clk clk_uart1_frac_div = {
+       .name           = "uart1_frac_div",
+       .parent         = &clk_uart1_div,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_uart_fracdiv_set_rate,
+       .gate_idx       = CLK_GATE_FRAC_UART1,
+       .clksel_con     = CRU_CLKSELS_CON(18),
+};
+static struct clk clk_uart2_frac_div = {
+       .name           = "uart2_frac_div",
+       .mode           = gate_mode,
+       .parent         = &clk_uart2_div,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_uart_fracdiv_set_rate,
+       .gate_idx       = CLK_GATE_FRAC_UART2,
+       .clksel_con     = CRU_CLKSELS_CON(19),
+};
+static struct clk clk_uart3_frac_div = {
+       .name           = "uart3_frac_div",
+       .parent         = &clk_uart3_div,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_uart_fracdiv_set_rate,
+       .gate_idx       = CLK_GATE_FRAC_UART3,
+       .clksel_con     = CRU_CLKSELS_CON(20),
+};
+
+
+#define UART_SRC_DIV 0
+#define UART_SRC_FRAC 1
+#define UART_SRC_24M 2
+
+static int clk_uart_set_rate(struct clk *clk, unsigned long rate)
+{
+       int ret = 0;
+       struct clk *parent;
+
+       if(rate==clk->parents[UART_SRC_24M]->rate)//24m
+       {       
+               parent = clk->parents[UART_SRC_24M];
+       }
+       else if((long)clk_round_rate_nolock(clk->parents[UART_SRC_DIV], rate)==rate)
+       {
+               parent = clk->parents[UART_SRC_DIV];
+       }
+       else
+       {
+               parent = clk->parents[UART_SRC_FRAC];
+       }
+
+
+       
+       CRU_PRINTK_DBG(" %s set rate=%lu parent %s(old %s)\n",
+               clk->name,rate,parent->name,clk->parent->name);
+
+       
+       if (clk->parent != parent)
+       {
+               ret = clk_set_parent_nolock(clk, parent);
+               if (ret)
+               {
+                       CRU_PRINTK_DBG("%s can't get rate%lu,reparent err\n",clk->name,rate);
+                       return ret;
+               }
+       }
+       
+       if(parent!=clk->parents[UART_SRC_24M])
+       {
+               ret = clk_set_rate_nolock(parent,rate); 
+       }
+
+       return ret;
+}
+
+
+static struct clk *clk_uart0_parents[3]={&clk_uart0_div,&clk_uart0_frac_div,&xin24m};
+static struct clk clk_uart0 = {
+       .name           = "uart0",
+       .set_rate       = clk_uart_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(13),
+       CRU_SRC_SET(0x3,8),     
+       CRU_PARENTS_SET(clk_uart0_parents),
+};
+
+static struct clk *clk_uart1_parents[3]={&clk_uart1_div,&clk_uart1_frac_div,&xin24m};
+static struct clk clk_uart1 = {
+       .name           = "uart1",
+       .set_rate       = clk_uart_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(14),
+       CRU_SRC_SET(0x3,8),     
+       CRU_PARENTS_SET(clk_uart1_parents),
+};
+
+static struct clk *clk_uart2_parents[3]={&clk_uart2_div,&clk_uart2_frac_div,&xin24m};
+static struct clk clk_uart2 = {
+       .name           = "uart2",
+       .set_rate       = clk_uart_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(15),
+       CRU_SRC_SET(0x3,8),     
+       CRU_PARENTS_SET(clk_uart2_parents),
+};
+static struct clk *clk_uart3_parents[3]={&clk_uart3_div,&clk_uart3_frac_div,&xin24m};
+static struct clk clk_uart3 = {
+       .name           = "uart3",
+       .set_rate       = clk_uart_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(16),
+       CRU_SRC_SET(0x3,8),     
+       CRU_PARENTS_SET(clk_uart3_parents),
+};
+
+GATE_CLK(timer0, xin24m, TIMER0);
+GATE_CLK(timer1, xin24m, TIMER1);
+GATE_CLK(timer2, xin24m, TIMER2);
+
+static struct clk rmii_clkin = {
+       .name           = "rmii_clkin",
+};
+static struct clk *clk_mac_ref_div_parents[2]={&general_pll_clk,&ddr_pll_clk};
+static struct clk clk_mac_pll_div = {
+       .name           = "mac_pll_div",
+       .parent         = &ddr_pll_clk,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_MAC,
+       .recalc         = clksel_recalc_div,
+       .set_rate       =clksel_set_rate_freediv,
+       //.set_rate     = clksel_set_rate_freediv,
+       .clksel_con     =CRU_CLKSELS_CON(21),
+       CRU_DIV_SET(0x1f,8,32), 
+       CRU_SRC_SET(0x1,0),
+       CRU_PARENTS_SET(clk_mac_ref_div_parents),
+};
+
+static int clksel_mac_ref_set_rate(struct clk *clk, unsigned long rate)
+{
+
+       if(clk->parent==clk->parents[1])
+       {
+               CRU_PRINTK_DBG("mac_ref clk is form mii clkin,can't set it\n" );
+               return -ENOENT;
+       }
+       else if(clk->parent==clk->parents[0])
+       {
+       return clk_set_rate_nolock(clk->parents[0],rate);
+       }
+       return -ENOENT;
+}
+
+static struct clk *clk_mac_ref_parents[2]={&clk_mac_pll_div,&rmii_clkin};
+
+static struct clk clk_mac_ref = {
+       .name           = "mac_ref",
+       .parent         = &clk_mac_pll_div,
+       .set_rate       = clksel_mac_ref_set_rate,
+       .clksel_con =CRU_CLKSELS_CON(21),
+       CRU_SRC_SET(0x1,4),
+       CRU_PARENTS_SET(clk_mac_ref_parents),
+};
+
+static int clk_set_mii_tx_parent(struct clk *clk, struct clk *parent)
+{
+       return clk_set_parent_nolock(clk->parent,parent);
+}
+
+static struct clk clk_mii_tx = {
+       .name           = "mii_tx",
+       .parent         = &clk_mac_ref, 
+       //.set_parent   = clk_set_mii_tx_parent,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_MAC_LBTEST,//???
+};
+
+static struct clk *clk_hsadc_pll_parents[2]={&general_pll_clk,&codec_pll_clk};
+static struct clk clk_hsadc_pll_div = {
+       .name           = "hsadc_pll_div",
+       .parent         = &general_pll_clk,
+       .mode           = gate_mode,
+       .gate_idx       = CLK_GATE_SARADC,
+       .recalc         = clksel_recalc_div,
+       .round_rate     =clk_freediv_round_autosel_parents_rate,
+       .set_rate       = clkset_rate_freediv_autosel_parents,
+       //.round_rate =clksel_freediv_round_rate,
+       //.set_rate     = clksel_set_rate_freediv,
+       .clksel_con =CRU_CLKSELS_CON(22),
+       CRU_DIV_SET(0xff,8,256), 
+       CRU_SRC_SET(0x1,0),
+       CRU_PARENTS_SET(clk_hsadc_pll_parents),
+};
+
+static int clk_hsadc_fracdiv_set_rate_fixed_parent(struct clk *clk, unsigned long rate)
+{
+       u32 numerator, denominator;
+       //        clk_hsadc_pll_div->gpll/cpll
+       //clk->parent->parent
+       if(frac_div_get_seting(rate,clk->parent->parent->rate,
+                       &numerator,&denominator)==0)
+       {
+               clk_set_rate_nolock(clk->parent,clk->parent->parent->rate);//PLL:DIV 1:
+               
+               cru_writel_frac(numerator << 16 | denominator, clk->clksel_con);
+               
+               CRU_PRINTK_DBG("%s set rate=%lu,is ok\n",clk->name,rate);
+       }
+       else
+       {
+               CRU_PRINTK_ERR("clk_frac_div can't get rate=%lu,%s\n",rate,clk->name);
+               return -ENOENT;
+       } 
+       return 0;
+}
+static int clk_hsadc_fracdiv_set_rate_auto_parents(struct clk *clk, unsigned long rate)
+{
+       u32 numerator, denominator;
+       u32 i,ret=0;    
+       //        clk_hsadc_pll_div->gpll/cpll
+       //clk->parent->parent
+       for(i=0;i<2;i++)
+       {
+               if(frac_div_get_seting(rate,clk->parent->parents[i]->rate,
+                       &numerator,&denominator)==0)
+                       break;
+       }
+       if(i>=2)
+               return -ENOENT;
+       
+       if(clk->parent->parent!=clk->parent->parents[i])
+               ret=clk_set_parent_nolock(clk->parent, clk->parent->parents[i]);
+       if(ret==0)
+       {
+               clk_set_rate_nolock(clk->parent,clk->parent->parents[i]->rate);//PLL:DIV 1:
+               
+               cru_writel_frac(numerator << 16 | denominator, clk->clksel_con);
+
+               CRU_PRINTK_DBG("clk_frac_div %s, rate=%lu\n",clk->name,rate);
+       }
+       else
+       {
+               CRU_PRINTK_ERR("clk_frac_div can't get rate=%lu,%s\n",rate,clk->name);
+               return -ENOENT;
+       } 
+       return 0;
+}
+
+static long clk_hsadc_fracdiv_round_rate(struct clk *clk, unsigned long rate)
+{
+       u32 numerator, denominator;
+       
+       CRU_PRINTK_ERR("clk_hsadc_fracdiv_round_rate\n");
+       if(frac_div_get_seting(rate,clk->parent->parent->rate,
+                       &numerator,&denominator)==0)
+               return rate;
+       
+       return 0;
+}
+static struct clk clk_hsadc_frac_div = {
+       .name           = "hsadc_frac_div",
+       .parent         = &clk_hsadc_pll_div,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_frac,
+       .set_rate       = clk_hsadc_fracdiv_set_rate_auto_parents,
+       .round_rate     =clk_hsadc_fracdiv_round_rate,
+       .gate_idx       = CLK_GATE_HSADC_FRAC,
+       .clksel_con     = CRU_CLKSELS_CON(23),
+};
+
+#define HSADC_SRC_DIV 0x0
+#define HSADC_SRC_FRAC 0x1
+#define HSADC_SRC_EXT 0x2
+static int clk_hsadc_set_rate(struct clk *clk, unsigned long rate)
+{
+       int ret = -EINVAL;
+       struct clk *parent;
+
+       if(clk->parent == clk->parents[HSADC_SRC_EXT]){
+               CRU_PRINTK_DBG("hsadc clk is form ext\n");
+               return 0;
+       }
+       else if((long)clk_round_rate_nolock(clk->parents[HSADC_SRC_DIV],rate)==rate)
+       {
+               parent =clk->parents[HSADC_SRC_DIV];
+       }
+       else if((long)clk_round_rate_nolock(clk->parents[HSADC_SRC_FRAC],rate)==rate)
+       {
+               parent = clk->parents[HSADC_SRC_FRAC]; 
+       }
+       else
+               parent =clk->parents[HSADC_SRC_DIV];
+
+       CRU_PRINTK_DBG(" %s set rate=%lu parent %s(old %s)\n",
+               clk->name,rate,parent->name,clk->parent->name);
+       
+       if (clk->parent != parent)
+       {
+               ret = clk_set_parent_nolock(clk, parent);
+               if (ret)
+               {
+                       CRU_PRINTK_ERR("%s can't get rate%lu,reparent err\n",clk->name,rate);
+                       return ret;
+               }
+       }
+       ret = clk_set_rate_nolock(parent,rate);
+       return ret;
+}
+
+static struct clk clk_hsadc_ext = {
+       .name           = "hsadc_ext",
+};
+
+static struct clk *clk_hsadc_parents[3]={&clk_hsadc_pll_div,&clk_hsadc_frac_div,&clk_hsadc_ext};
+static struct clk clk_hsadc = {
+       .name           = "hsadc",
+       .parent         = &clk_hsadc_pll_div,
+       .set_rate       = clk_hsadc_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(22),
+       CRU_SRC_SET(0x3,4),     
+       CRU_PARENTS_SET(clk_hsadc_parents),
+};
+
+static struct clk *dclk_lcdc_div_parents[]={&codec_pll_clk,&general_pll_clk};
+static struct clk dclk_lcdc0_div = {
+       .name           = "dclk_lcdc0_div",
+       .parent         = &general_pll_clk,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clkset_rate_freediv_autosel_parents,
+       .clksel_con     = CRU_CLKSELS_CON(27),
+       CRU_DIV_SET(0xff,8,256),
+       CRU_SRC_SET(0x1,0),
+       CRU_PARENTS_SET(dclk_lcdc_div_parents),
+};
+
+static int clksel_set_rate_hdmi(struct clk *clk, unsigned long rate)
+{
+       u32 div;
+       div=clk_get_freediv(rate,clk->parent->rate,clk->div_max);
+       if(rate==(clk->parent->rate/div)&&!(clk->parent->rate%div))
+               return 0;
+       return -ENOENT;
+}
+//hdmi
+static struct clk dclk_lcdc1_div = {
+       .name           = "dclk_lcdc1_div",
+       .parent         = &general_pll_clk,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_hdmi,//clk_freediv_autosel_parents_set_fixed_rate
+       .clksel_con     = CRU_CLKSELS_CON(28),
+       CRU_DIV_SET(0xff,8,256),
+       CRU_SRC_SET(0x1,0),
+       CRU_PARENTS_SET(dclk_lcdc_div_parents),
+};
+
+static int dclk_lcdc_set_rate(struct clk *clk, unsigned long rate)
+{
+       int ret = 0;
+       struct clk *parent;
+
+       if (rate == 27 * MHZ) {
+               parent =clk->parents[1];
+       } else {
+               parent=clk->parents[0];
+               
+       }
+       CRU_PRINTK_DBG(" %s set rate=%lu parent %s(old %s)\n",
+                       clk->name,rate,parent->name,clk->parent->name);
+
+       if (clk->parent != parent)
+       {
+               ret = clk_set_parent_nolock(clk, parent);
+               if (ret)
+               {
+                       CRU_PRINTK_DBG("%s can't get rate%lu,reparent err\n",clk->name,rate);
+                       return ret;
+               }
+       }
+       if(parent!=clk->parents[1])
+       {
+               ret = clk_set_rate_nolock(parent,rate);//div 1:1
+       }
+       return ret;
+}
+
+static struct clk *dclk_lcdc0_parents[2]={&dclk_lcdc0_div,&xin27m};
+static struct clk dclk_lcdc0 = {
+       .name           = "dclk_lcdc0",
+       .mode           = gate_mode,
+       .set_rate       = dclk_lcdc_set_rate,
+       .gate_idx       = CLK_GATE_DCLK_LCDC0,
+       .clksel_con     = CRU_CLKSELS_CON(27),
+       CRU_SRC_SET(0x1,4),
+       CRU_PARENTS_SET(dclk_lcdc0_parents),
+};
+
+static struct clk *dclk_lcdc1_parents[2]={&dclk_lcdc1_div,&xin27m};
+static struct clk dclk_lcdc1 = {
+       .name           = "dclk_lcdc1",
+       .mode           = gate_mode,
+       .set_rate       = dclk_lcdc_set_rate,
+       .gate_idx       = CLK_GATE_DCLK_LCDC1,
+       .clksel_con     = CRU_CLKSELS_CON(28),
+       CRU_SRC_SET(0x1,4),
+       CRU_PARENTS_SET(dclk_lcdc1_parents),
+};
+
+
+static struct clk *cifout_sel_pll_parents[2]={&codec_pll_clk,&general_pll_clk};
+static struct clk cif_out_pll = {
+       .name           = "cif_out_pll",
+       .parent         = &general_pll_clk,
+       .clksel_con     = CRU_CLKSELS_CON(29),
+       CRU_SRC_SET(0x1,0),
+       CRU_PARENTS_SET(cifout_sel_pll_parents),
+};
+
+static struct clk cif0_out_div = {
+       .name           = "cif0_out_div",
+       .parent         = &cif_out_pll,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       =CLK_GATE_CIF0_OUT,
+       .clksel_con     = CRU_CLKSELS_CON(29),
+       CRU_DIV_SET(0x1f,1,32),
+};
+
+static struct clk cif1_out_div = {
+       .name           = "cif1_out_div",
+       .parent         = &cif_out_pll,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clksel_set_rate_freediv,
+       .gate_idx       =       CLK_GATE_CIF1_OUT,
+       .clksel_con     = CRU_CLKSELS_CON(29),
+       CRU_DIV_SET(0x1f,8,32),
+};
+
+
+static int cif_out_set_rate(struct clk *clk, unsigned long rate)
+{
+       int ret = 0;
+       struct clk *parent;
+
+       if (rate == 24 * MHZ) {
+               parent =clk->parents[1];
+       } else {
+               parent=clk->parents[0];
+               ret = clk_set_rate_nolock(parent, rate);
+               if (ret)
+                       return ret;
+       }
+       if (clk->parent != parent)
+               ret = clk_set_parent_nolock(clk, parent);
+
+       return ret;
+}
+
+static struct clk *cif0_out_parents[2]={&cif0_out_div,&xin24m};
+static struct clk cif0_out = {
+       .name           = "cif0_out",
+       .parent         = &cif0_out_div,
+       .set_rate       = cif_out_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(29),
+       CRU_SRC_SET(0x1,7),
+       CRU_PARENTS_SET(cif0_out_parents),
+};
+static struct clk *cif1_out_parents[2]={&cif1_out_div,&xin24m};
+
+static struct clk cif1_out = {
+       .name           = "cif1_out",
+       .parent         = &cif1_out_div,
+       .set_rate       = cif_out_set_rate,
+       .clksel_con     = CRU_CLKSELS_CON(29),
+       CRU_SRC_SET(0x1,15),
+       CRU_PARENTS_SET(cif1_out_parents),
+};
+
+static struct clk pclkin_cif0 = {
+       .name           = "pclkin_cif0",
+       .mode           = gate_mode,
+       .gate_idx       =CLK_GATE_PCLKIN_CIF0,  
+};
+
+static struct clk inv_cif0 = {
+       .name           = "inv_cif0",
+       .parent         = &pclkin_cif0,
+};
+
+static struct clk *cif0_in_parents[2]={&pclkin_cif0,&inv_cif0};
+static struct clk cif0_in = {
+       .name           = "cif0_in",
+       .parent         = &pclkin_cif0,
+       .clksel_con     = CRU_CLKSELS_CON(30),
+       CRU_SRC_SET(0x1,8),
+       CRU_PARENTS_SET(cif0_in_parents),
+};
+
+static struct clk pclkin_cif1 = {
+       .name           = "pclkin_cif1",
+       .mode           = gate_mode,
+       .gate_idx       =CLK_GATE_PCLKIN_CIF1,  
+};
+
+static struct clk inv_cif1 = {
+       .name           = "inv_cif1",
+       .parent         = &pclkin_cif1,
+};
+static struct clk *cif1_in_parents[2]={&pclkin_cif1,&inv_cif1};
+
+static struct clk cif1_in = {
+       .name           = "cif1_in",
+       .parent         = &pclkin_cif1,
+       .clksel_con     = CRU_CLKSELS_CON(30),
+       CRU_SRC_SET(0x1,12),
+       CRU_PARENTS_SET(cif1_in_parents),
+};
+
+static struct clk *aclk_lcdc0_parents[]={&codec_pll_clk,&general_pll_clk};
+
+static struct clk aclk_lcdc0 = {
+       .name           = "aclk_lcdc0",
+       .parent         = &codec_pll_clk,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clkset_rate_freediv_autosel_parents,
+       //.set_rate     = clksel_set_rate_freediv,
+       .gate_idx       = CLK_GATE_ACLK_LCDC0_SRC,
+       .clksel_con     = CRU_CLKSELS_CON(31),
+       CRU_DIV_SET(0x1f,0,32),
+       CRU_SRC_SET(0x1,7),
+       CRU_PARENTS_SET(aclk_lcdc0_parents),
+};
+
+static struct clk *aclk_lcdc1_parents[]={&codec_pll_clk,&general_pll_clk};
+
+static struct clk aclk_lcdc1 = {
+       .name           = "aclk_lcdc1",
+       .parent         = &codec_pll_clk,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clkset_rate_freediv_autosel_parents,
+       .gate_idx       = CLK_GATE_ACLK_LCDC1_SRC,
+       .clksel_con     = CRU_CLKSELS_CON(31),
+       CRU_DIV_SET(0x1f,8,32),
+       CRU_SRC_SET(0x1,15),
+       CRU_PARENTS_SET(aclk_lcdc1_parents),
+};
+
+
+//for free div
+static unsigned long clksel_recalc_vpu_hclk(struct clk *clk)
+{
+       unsigned long rate = clk->parent->rate / 4;
+       pr_debug("%s new clock rate is %lu (div %u)\n", clk->name, rate, 4);
+       return rate;
+}
+
+static struct clk *aclk_vepu_parents[2]={&codec_pll_clk,&general_pll_clk};
+
+static struct clk aclk_vepu = {
+       .name           = "aclk_vepu",
+       .parent         = &codec_pll_clk,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       //.set_rate     = clksel_set_rate_freediv,
+       .set_rate       =clkset_rate_freediv_autosel_parents,
+       .clksel_con     = CRU_CLKSELS_CON(32),
+       .gate_idx       = CLK_GATE_ACLK_VEPU,
+       CRU_DIV_SET(0x1f,0,32),
+       CRU_SRC_SET(0x1,7),
+       CRU_PARENTS_SET(aclk_vepu_parents),
+};
+
+static struct clk hclk_vepu = {
+       .name           = "hclk_vepu",
+       .parent         = &aclk_vepu,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_vpu_hclk,
+       .clksel_con     = CRU_CLKSELS_CON(32),
+       .gate_idx       = CLK_GATE_HCLK_VEPU,
+};
+
+static struct clk *aclk_vdpu_parents[2]={&codec_pll_clk,&general_pll_clk};
+
+static struct clk aclk_vdpu = {
+       .name           = "aclk_vdpu",
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       //.set_rate     = clksel_set_rate_freediv,
+       .set_rate       =clkset_rate_freediv_autosel_parents,
+       .clksel_con     = CRU_CLKSELS_CON(32),
+       .gate_idx       = CLK_GATE_ACLK_VDPU,
+       CRU_DIV_SET(0x1f,8,32),
+       CRU_SRC_SET(0x1,15),
+       CRU_PARENTS_SET(aclk_vdpu_parents),
+};
+static struct clk hclk_vdpu = {
+       .name           = "hclk_vdpu",
+       .parent         = &aclk_vdpu,
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_vpu_hclk,
+       .clksel_con     = CRU_CLKSELS_CON(32),
+       .gate_idx       = CLK_GATE_HCLK_VDPU,
+};
+
+
+static int clk_gpu_set_rate(struct clk *clk, unsigned long rate)
+{
+       unsigned long max_rate = rate / 100 * 105;      /* +5% */
+       return clkset_rate_freediv_autosel_parents(clk,max_rate);
+};
+
+static struct clk *gpu_parents[2]={&codec_pll_clk,&general_pll_clk};
+
+static struct clk clk_gpu = {
+       .name           = "gpu",
+       .mode           = gate_mode,
+       .recalc         = clksel_recalc_div,
+       .set_rate       = clkset_rate_freediv_autosel_parents,
+       .clksel_con = CRU_CLKSELS_CON(33),
+       .gate_idx       =  CLK_GATE_GPU_SRC,
+       CRU_DIV_SET(0x1f,0,32),
+       CRU_SRC_SET(0x1,8),
+       CRU_PARENTS_SET(gpu_parents),
+};
+
+/*********************power domain*******************************/
+#ifdef RK30_CLK_OFFBOARD_TEST
+void pmu_set_power_domain_test(enum pmu_power_domain pd, bool on){};
+       #define _pmu_set_power_domain pmu_set_power_domain_test//rk30_pmu_set_power_domain
+#else
+void pmu_set_power_domain(enum pmu_power_domain pd, bool on);
+       #define _pmu_set_power_domain pmu_set_power_domain
+#endif
+static int pm_off_mode(struct clk *clk, int on)
+{
+        _pmu_set_power_domain(clk->gate_idx,1);//on 1
+        return 0;
+}
+static struct clk pd_peri = {
+       .name   = "pd_peri",
+       .flags  = IS_PD,
+       .mode   = pm_off_mode,
+       .gate_idx       = PD_PERI,
+};
+static struct clk pd_display = {
+       .name   = "pd_display",
+       .flags  = IS_PD,
+       .mode   = pm_off_mode,
+       .gate_idx       = PD_VIO,
+};
+
+static struct clk pd_lcdc0 = {
+       .parent         = &pd_display,
+       .name   = "pd_lcdc0",
+       .flags  = IS_PD,
+};
+static struct clk pd_lcdc1 = {
+       .parent         = &pd_display,
+       .name   = "pd_lcdc1",
+       .flags  = IS_PD,        
+};
+static struct clk pd_cif0 = {
+       .parent         = &pd_display,
+       .name   = "pd_cif0",
+       .flags  = IS_PD,        
+};
+static struct clk pd_cif1 = {
+       .parent         = &pd_display,
+       .name   = "pd_cif1",
+       .flags  = IS_PD,        
+};
+static struct clk pd_rga = {
+       .parent         = &pd_display,
+       .name   = "pd_rga",
+       .flags  = IS_PD,        
+};
+static struct clk pd_ipp = {
+       .parent         = &pd_display,
+       .name   = "pd_ipp",
+       .flags  = IS_PD,        
+};
+
+static struct clk pd_video = {
+       .name   = "pd_video",
+       .flags  = IS_PD,
+       .mode   = pm_off_mode,
+       .gate_idx       = PD_VIDEO,
+};
+static struct clk pd_gpu = {
+       .name   = "pd_gpu",
+       .flags  = IS_PD,
+       .mode   = pm_off_mode,
+       .gate_idx       = PD_GPU,
+};
+static struct clk pd_dbg = {
+       .name   = "pd_dbg",
+       .flags  = IS_PD,
+       .mode   = pm_off_mode,
+       .gate_idx       = PD_DBG,
+};
+
+#define PD_CLK(name) \
+{\
+       .dev_id = NULL,\
+       .con_id = #name,\
+       .clk = &name,\
+}
+
+
+/************************rk30 fixed div clock****************************************/
+
+/*************************aclk_cpu***********************/
+
+GATE_CLK(dma1, aclk_cpu,       ACLK_DMAC1);
+GATE_CLK(l2mem_con, aclk_cpu,  ACLK_L2MEM_CON);
+GATE_CLK(intmem, aclk_cpu,     ACLK_INTMEM);
+GATE_CLK(aclk_strc_sys, aclk_cpu,      ACLK_STRC_SYS);
+
+/*************************hclk_cpu***********************/
+
+GATE_CLK(rom, hclk_cpu,        HCLK_ROM);
+GATE_CLK(hclk_i2s0_2ch, hclk_cpu,      HCLK_I2S0_2CH);
+GATE_CLK(hclk_i2s1_2ch, hclk_cpu,      HCLK_I2S1_2CH);
+GATE_CLK(hclk_spdif, hclk_cpu, HCLK_SPDIF);
+GATE_CLK(hclk_i2s_8ch, hclk_cpu, HCLK_I2S_8CH);
+GATE_CLK(hclk_cpubus, hclk_cpu, HCLK_CPUBUS);
+GATE_CLK(hclk_ahb2apb, hclk_cpu, HCLK_AHB2APB);
+GATE_CLK(hclk_vio_bus, hclk_cpu, HCLK_VIO_BUS);
+GATE_CLK(hclk_lcdc0,   hclk_cpu, HCLK_LCDC0);
+GATE_CLK(hclk_lcdc1,   hclk_cpu, HCLK_LCDC1);
+GATE_CLK(hclk_cif0,    hclk_cpu, HCLK_CIF0);
+GATE_CLK(hclk_cif1,    hclk_cpu, HCLK_CIF1);
+GATE_CLK(hclk_ipp,             hclk_cpu, HCLK_IPP);
+GATE_CLK(hclk_rga,             hclk_cpu, HCLK_RGA);
+GATE_CLK(hclk_hdmi,    hclk_cpu, HCLK_HDMI);
+//GATE_CLK(hclk_vidoe_h2h,     hclk_cpu, ); ???
+/*************************pclk_cpu***********************/
+GATE_CLK(pwm01,        pclk_cpu, PCLK_PWM01);//pwm 0¡¢1
+//GATE_CLK(pclk_pwm1,  pclk_cpu, PCLK_PWM01);//pwm 0¡¢1
+GATE_CLK(pclk_timer0,  pclk_cpu, PCLK_TIMER0);
+GATE_CLK(pclk_timer1,  pclk_cpu, PCLK_TIMER1);
+GATE_CLK(pclk_timer2,  pclk_cpu, PCLK_TIMER2);
+GATE_CLK(i2c0, pclk_cpu, PCLK_I2C0);
+GATE_CLK(i2c1, pclk_cpu, PCLK_I2C1);
+GATE_CLK(gpio0,        pclk_cpu, PCLK_GPIO0);
+GATE_CLK(gpio1,        pclk_cpu, PCLK_GPIO1);
+GATE_CLK(gpio2,        pclk_cpu, PCLK_GPIO2);
+GATE_CLK(gpio6,        pclk_cpu, PCLK_GPIO6);
+GATE_CLK(efuse,        pclk_cpu, PCLK_EFUSE);
+GATE_CLK(tzpc, pclk_cpu, PCLK_TZPC);
+GATE_CLK(pclk_uart0,   pclk_cpu, PCLK_UART0);
+GATE_CLK(pclk_uart1,   pclk_cpu, PCLK_UART1);
+GATE_CLK(pclk_ddrupctl,        pclk_cpu, PCLK_DDRUPCTL);
+GATE_CLK(pclk_ddrpubl, pclk_cpu, PCLK_PUBL);
+GATE_CLK(dbg,  pclk_cpu, PCLK_DBG);
+GATE_CLK(grf,  pclk_cpu, PCLK_GRF);
+GATE_CLK(pmu,  pclk_cpu, PCLK_PMU);
+
+/*************************aclk_periph***********************/
+
+GATE_CLK(dma2, aclk_periph,ACLK_DMAC2);
+GATE_CLK(aclk_smc, aclk_periph, ACLK_SMC);
+GATE_CLK(aclk_peri_niu, aclk_periph, ACLK_PEI_NIU);
+GATE_CLK(aclk_cpu_peri, aclk_periph, ACLK_CPU_PERI);
+GATE_CLK(aclk_peri_axi_matrix, aclk_periph, ACLK_PERI_AXI_MATRIX);
+
+/*************************hclk_periph***********************/
+GATE_CLK(hclk_peri_axi_matrix, hclk_periph, HCLK_PERI_AXI_MATRIX);
+GATE_CLK(hclk_peri_ahb_arbi, hclk_periph, HCLK_PERI_AHB_ARBI);
+GATE_CLK(hclk_emem_peri, hclk_periph, HCLK_EMEM_PERI);
+GATE_CLK(hclk_emac, hclk_periph, HCLK_EMAC);
+GATE_CLK(nandc, hclk_periph, HCLK_NANDC);
+GATE_CLK(hclk_usb_peri, hclk_periph, HCLK_USB_PERI);
+GATE_CLK(hclk_otg0, clk_hclk_usb_peri, HCLK_OTG0);
+GATE_CLK(hclk_otg1, clk_hclk_usb_peri, HCLK_OTG1);
+GATE_CLK(hclk_hsadc, hclk_periph, HCLK_HSADC);
+GATE_CLK(hclk_pidfilter, hclk_periph, HCLK_PIDF);
+GATE_CLK(hclk_sdmmc, hclk_periph, HCLK_SDMMC0);
+GATE_CLK(hclk_sdio, hclk_periph, HCLK_SDIO);
+GATE_CLK(hclk_emmc, hclk_periph, HCLK_EMMC);
+/*************************pclk_periph***********************/
+GATE_CLK(pclk_peri_axi_matrix, pclk_periph, PCLK_PERI_AXI_MATRIX);
+GATE_CLK(pwm23, pclk_periph, PCLK_PWM23);
+//GATE_CLK(pclk_pwm3, pclk_periph, PCLK_PWM3);
+GATE_CLK(wdt, pclk_periph, PCLK_WDT);
+GATE_CLK(pclk_spi0, pclk_periph, PCLK_SPI0);
+GATE_CLK(pclk_spi1, pclk_periph, PCLK_SPI1);
+GATE_CLK(pclk_uart2, pclk_periph, PCLK_UART2);
+GATE_CLK(pclk_uart3, pclk_periph, PCLK_UART3);
+GATE_CLK(i2c2, pclk_periph, PCLK_I2C2);
+GATE_CLK(i2c3, pclk_periph, PCLK_I2C3);
+GATE_CLK(i2c4, pclk_periph, PCLK_I2C4);
+GATE_CLK(gpio3, pclk_periph, PCLK_GPIO3);
+GATE_CLK(gpio4, pclk_periph, PCLK_GPIO4);
+GATE_CLK(pclk_saradc, pclk_periph, PCLK_SARADC);
+GATE_CLK(pclk_tsadc, pclk_periph, PCLK_TSADC);
+/*************************aclk_lcdc0***********************/
+GATE_CLK(aclk_vio0, aclk_lcdc0, ACLK_VIO0);
+GATE_CLK(aclk_cif0, aclk_lcdc0, ACLK_CIF0);
+GATE_CLK(aclk_ipp, aclk_lcdc0, ACLK_IPP);
+
+/*************************aclk_lcdc0***********************/
+GATE_CLK(aclk_vio1, aclk_lcdc1, ACLK_VIO1);
+GATE_CLK(aclk_cif1, aclk_lcdc1, ACLK_CIF0);
+GATE_CLK(aclk_rga, aclk_lcdc1, ACLK_RGA);
+
+
+#if 1
+#define CLK(dev, con, ck) \
+ {\
+       .dev_id = dev,\
+       .con_id = con,\
+       .clk = ck,\
+ }
+
+
+#define CLK1(name) \
+       {\
+       .dev_id = NULL,\
+       .con_id = #name,\
+       .clk = &clk_##name,\
+       }
+
+#endif
+
+
+
+
+static struct clk_lookup clks[] = {
+#if 1
+       CLK(NULL, "xin24m", &xin24m),
+       CLK(NULL, "xin27m", &xin27m),
+       CLK(NULL, "xin12m", &clk_12m),
+       CLK(NULL, "arm_pll", &arm_pll_clk),
+       CLK(NULL, "ddr_pll", &ddr_pll_clk),
+       CLK(NULL, "codec_pll", &codec_pll_clk),
+       CLK(NULL, "general_pll", &general_pll_clk),
+
+       CLK(NULL, "ddr", &clk_ddr),
+       //CLK(NULL, "core_gpll_path", &clk_cpu_gpll_path),
+       CLK(NULL, "cpu", &clk_cpu),
+       CLK(NULL, "smp_twd", &core_periph),
+       CLK(NULL, "aclk_cpu", &aclk_cpu),
+       CLK(NULL, "hclk_cpu", &hclk_cpu),
+       CLK(NULL, "pclk_cpu", &pclk_cpu),
+       CLK(NULL, "atclk_cpu", &atclk_cpu),
+
+       
+       CLK1(i2s_pll),
+       CLK("rk30_i2s.0", "i2s_div", &clk_i2s0_div),
+       CLK("rk30_i2s.0", "i2s_frac_div", &clk_i2s0_frac_div),
+       CLK("rk30_i2s.0", "i2s", &clk_i2s0),
+       CLK("rk30_i2s.0", "hclk_i2s", &clk_hclk_i2s0_2ch),
+
+       CLK("rk30_i2s.1", "i2s_div", &clk_i2s1_div),
+       CLK("rk30_i2s.1", "i2s_frac_div", &clk_i2s1_frac_div),
+       CLK("rk30_i2s.1", "i2s", &clk_i2s1),
+       CLK("rk30_i2s.1", "hclk_i2s", &clk_hclk_i2s1_2ch),
+
+       CLK("rk30_i2s.2", "i2s_div", &clk_i2s2_div),
+       CLK("rk30_i2s.2", "i2s_frac_div", &clk_i2s2_frac_div),
+       CLK("rk30_i2s.2", "i2s", &clk_i2s2),
+       CLK("rk30_i2s.2", "hclk_i2s", &clk_hclk_i2s_8ch),
+       
+       CLK1(spdif_div),
+       CLK1(spdif_frac_div),
+       CLK1(spdif),    
+       CLK1(hclk_spdif),
+
+       CLK(NULL, "aclk_periph", &aclk_periph),
+       CLK(NULL, "pclk_periph", &pclk_periph),
+       CLK(NULL, "hclk_periph", &hclk_periph),
+
+       CLK("rk30xx_spim.0", "spi", &clk_spi0),
+       CLK("rk30xx_spim.0", "pclk_spi", &clk_pclk_spi0),
+       
+       CLK("rk30xx_spim.1", "spi", &clk_spi1),
+       CLK("rk30xx_spim.1", "pclk_spi", &clk_pclk_spi1),
+       
+       CLK1(saradc),
+       CLK1(pclk_saradc),
+       CLK1(tsadc),
+       CLK1(pclk_tsadc),
+       CLK1(otgphy0),
+       CLK1(otgphy1),
+       CLK1(hclk_usb_peri),
+       CLK1(hclk_otg0),
+       CLK1(hclk_otg1),
+
+
+
+
+
+       CLK1(smc),
+       CLK1(aclk_smc),
+       
+       CLK1(sdmmc),
+       CLK1(hclk_sdmmc),
+       
+       CLK1(sdio),
+       CLK1(hclk_sdio),
+       
+       CLK1(emmc),
+       CLK1(hclk_emmc),
+
+
+       CLK1(uart_pll),
+       CLK("rk30_serial.0", "uart_div", &clk_uart0_div),
+       CLK("rk30_serial.0", "uart_frac_div", &clk_uart0_frac_div),
+       CLK("rk30_serial.0", "uart", &clk_uart0),
+       CLK("rk30_serial.0", "pclk_uart", &clk_pclk_uart0),
+       CLK("rk30_serial.1", "uart_div", &clk_uart1_div),
+       CLK("rk30_serial.1", "uart_frac_div", &clk_uart1_frac_div),
+       CLK("rk30_serial.1", "uart", &clk_uart1),
+       CLK("rk30_serial.1", "pclk_uart", &clk_pclk_uart1),
+       CLK("rk30_serial.2", "uart_div", &clk_uart2_div),
+       CLK("rk30_serial.2", "uart_frac_div", &clk_uart2_frac_div),
+       CLK("rk30_serial.2", "uart", &clk_uart2),
+       CLK("rk30_serial.2", "pclk_uart", &clk_pclk_uart2),
+       CLK("rk30_serial.3", "uart_div", &clk_uart3_div),
+       CLK("rk30_serial.3", "uart_frac_div", &clk_uart3_frac_div),
+       CLK("rk30_serial.3", "uart", &clk_uart3),
+       CLK("rk30_serial.3", "pclk_uart", &clk_pclk_uart3),
+
+       CLK1(timer0),
+       CLK1(pclk_timer0),
+       
+       CLK1(timer1),
+       CLK1(pclk_timer1),
+       
+       CLK1(timer2),
+       CLK1(pclk_timer2),
+
+       CLK(NULL, "rmii_clkin", &rmii_clkin),
+       CLK1(mac_pll_div),
+       CLK1(mac_ref),
+       CLK1(mii_tx),
+       CLK1(hsadc_pll_div),
+       CLK1(hsadc_frac_div),
+       CLK1(hsadc_ext),
+       CLK1(hsadc),
+       CLK1(hclk_hsadc),
+
+
+       CLK(NULL, "dclk_lcdc0_div", &dclk_lcdc0_div),
+       CLK(NULL, "dclk_lcdc1_div", &dclk_lcdc1_div),
+       
+       CLK(NULL, "dclk_lcdc0", &dclk_lcdc0),
+       CLK(NULL, "aclk_lcdc0", &aclk_lcdc0),
+       CLK1(hclk_lcdc0),
+       
+       CLK(NULL, "dclk_lcdc1", &dclk_lcdc1),
+       CLK(NULL, "aclk_lcdc1", &aclk_lcdc1),
+       CLK1(hclk_lcdc1),
+       
+       CLK(NULL, "cif_out_pll", &cif_out_pll),
+       CLK(NULL, "cif0_out_div", &cif0_out_div),
+       CLK(NULL, "cif1_out_div", &cif1_out_div),
+       
+       CLK(NULL, "cif0_out", &cif0_out),
+       CLK1(hclk_cif0),
+       
+       CLK(NULL, "cif1_out", &cif1_out),
+       CLK1(hclk_cif1),
+
+       CLK1(hclk_ipp),
+       CLK1(hclk_rga),
+       CLK1(hclk_hdmi),
+
+       CLK(NULL, "pclkin_cif0",        &pclkin_cif0),
+       CLK(NULL, "inv_cif0",                   &inv_cif0),
+       CLK(NULL, "cif0_in",                    &cif0_in),
+       CLK(NULL, "pclkin_cif1", &pclkin_cif1),
+       CLK(NULL, "inv_cif1",   &inv_cif1),
+       CLK(NULL, "cif1_in",    &cif1_in),
+       //CLK(NULL, "aclk_lcdc0",       &aclk_lcdc0),
+       //CLK(NULL, "aclk_lcdc1",       &aclk_lcdc1),
+       CLK(NULL, "aclk_vepu",  &aclk_vepu),
+       CLK(NULL, "hclk_vepu",  &hclk_vepu),
+       CLK(NULL, "aclk_vdpu",  &aclk_vdpu),
+       CLK(NULL, "hclk_vdpu",  &hclk_vdpu),
+       CLK1(gpu),
+       CLK1(dma1),
+       CLK1(l2mem_con),
+       CLK1(intmem),
+
+       CLK1(aclk_strc_sys),
+
+       /*************************hclk_cpu***********************/
+
+       CLK1(rom),
+       //CLK1(hclk_i2s0_2ch),
+       //CLK1(hclk_i2s1_2ch),
+       //CLK1(hclk_spdif),
+       //CLK1(hclk_i2s_8ch),
+       CLK1(hclk_cpubus),
+       CLK1(hclk_ahb2apb),
+       CLK1(hclk_vio_bus),
+       //CLK1(hclk_lcdc0),
+       //CLK1(hclk_lcdc1),
+       //CLK1(hclk_cif0),
+       //CLK1(hclk_cif1),
+       //CLK1(hclk_ipp),
+       //CLK1(hclk_rga),
+       //CLK1(hclk_hdmi),
+       //CLK1(hclk_vidoe_h2h,  hclk_cpu, ); ???
+       /*************************pclk_cpu***********************/
+       CLK1(pwm01),//pwm 0¡¢1
+
+       //CLK1(pclk_timer0),
+       //CLK1(pclk_timer1),
+       //CLK1(pclk_timer2),
+
+       CLK("rk30_i2c.0", "i2c", &clk_i2c0),
+       CLK("rk30_i2c.1", "i2c", &clk_i2c1),
+       
+       CLK1(gpio0),
+       CLK1(gpio1),
+       CLK1(gpio2),
+       CLK1(gpio6),
+       CLK1(efuse),
+       CLK1(tzpc),
+       //CLK1(pclk_uart0),
+       //CLK1(pclk_uart1),
+       CLK1(pclk_ddrupctl),
+       CLK1(pclk_ddrpubl),
+       CLK1(dbg),
+       CLK1(grf),
+       CLK1(pmu),
+
+       /*************************aclk_periph***********************/
+
+       CLK1(dma2),
+       //CLK1(aclk_smc),
+       CLK1(aclk_peri_niu),
+       CLK1(aclk_cpu_peri),
+       CLK1(aclk_peri_axi_matrix),
+
+       /*************************hclk_periph***********************/
+       CLK1(hclk_peri_axi_matrix),
+       CLK1(hclk_peri_ahb_arbi),
+       CLK1(hclk_emem_peri),
+       CLK1(hclk_emac),
+       CLK1(nandc),
+       //CLK1(hclk_usb_peri),
+       //CLK1(hclk_usbotg0),
+       //CLK1(hclk_usbotg1),
+       //CLK1(hclk_hsadc),
+       CLK1(hclk_pidfilter),
+       
+       //CLK1(hclk_emmc),
+       /*************************pclk_periph***********************/
+       CLK1(pclk_peri_axi_matrix),
+       CLK1(pwm23),
+
+       CLK1(wdt),
+       //CLK1(pclk_spi0),
+       //CLK1(pclk_spi1),
+       //CLK1(pclk_uart2),
+       //CLK1(pclk_uart3),
+       
+       CLK("rk30_i2c.2", "i2c", &clk_i2c2),
+       CLK("rk30_i2c.3", "i2c", &clk_i2c3),
+       CLK("rk30_i2c.4", "i2c", &clk_i2c4),
+       
+       CLK1(gpio3),
+       CLK1(gpio4),
+       
+       /*************************aclk_lcdc0***********************/
+       CLK1(aclk_vio0),
+       CLK1(aclk_cif0),
+       CLK1(aclk_ipp),
+
+       /*************************aclk_lcdc0***********************/
+       CLK1(aclk_vio1),
+       CLK1(aclk_cif1),
+       CLK1(aclk_rga),
+       /************************power domain**********************/
+       #if 0
+       PD_CLK(pd_peri),
+       PD_CLK(pd_display),
+       PD_CLK(pd_video),
+       PD_CLK(pd_gpu),
+       PD_CLK(pd_dbg),
+       #endif
+       #endif
+};
+static void __init rk30_init_enable_clocks(void)
+{
+/**************************************/
+       clk_enable_nolock(&pd_peri);
+       clk_enable_nolock(&pd_display);
+       clk_enable_nolock(&pd_video);
+       clk_enable_nolock(&pd_dbg);
+/****************clocks*****************/
+       //ddr pll
+       clk_enable_nolock(&clk_ddr);
+
+
+       //apll 
+       clk_enable_nolock(&core_periph);
+       clk_enable_nolock(&hclk_cpu);
+       clk_enable_nolock(&pclk_cpu);
+       clk_enable_nolock(&atclk_cpu);
+
+       //usb
+       clk_enable_nolock(&clk_otgphy0);
+       clk_enable_nolock(&clk_otgphy1);
+
+
+       //periph clk
+       clk_enable_nolock(&hclk_periph);
+       clk_enable_nolock(&pclk_periph);
+
+       //uart
+       #if 1
+       clk_enable_nolock(&clk_uart0);
+       clk_enable_nolock(&clk_pclk_uart0);
+       clk_enable_nolock(&clk_uart0_frac_div);
+       
+       clk_enable_nolock(&clk_uart1);
+       clk_enable_nolock(&clk_pclk_uart1);
+       clk_enable_nolock(&clk_uart1_frac_div);
+
+       clk_enable_nolock(&clk_uart2);
+       clk_enable_nolock(&clk_pclk_uart2);
+       clk_enable_nolock(&clk_uart2_frac_div);
+
+       clk_enable_nolock(&clk_uart3);
+       clk_enable_nolock(&clk_pclk_uart3);
+       clk_enable_nolock(&clk_uart3_frac_div);
+       #endif
+       
+
+       // timer ???
+       clk_enable_nolock(&clk_timer0);
+       clk_enable_nolock(&clk_timer1);
+       clk_enable_nolock(&clk_timer2);
+       clk_enable_nolock(&clk_pclk_timer0);
+       clk_enable_nolock(&clk_pclk_timer1);
+       clk_enable_nolock(&clk_pclk_timer2);
+
+
+
+       // aclk cpu
+       clk_enable_nolock(&clk_dma1);
+       clk_enable_nolock(&clk_l2mem_con);
+       clk_enable_nolock(&clk_intmem);
+       clk_enable_nolock(&clk_aclk_strc_sys);
+       //hclk cpu
+       clk_enable_nolock(&clk_rom);
+       clk_enable_nolock(&clk_hclk_cpubus);
+       clk_enable_nolock(&clk_hclk_ahb2apb);
+       clk_enable_nolock(&clk_hclk_vio_bus);
+       //pclk_cpu
+       clk_enable_nolock(&clk_tzpc);
+       clk_enable_nolock(&clk_pclk_ddrupctl);
+       clk_enable_nolock(&clk_pclk_ddrpubl);
+       
+       clk_enable_nolock(&clk_dbg);
+       clk_enable_nolock(&clk_grf);
+       clk_enable_nolock(&clk_pmu);
+
+       //hclk periph
+       clk_enable_nolock(&clk_hclk_peri_axi_matrix);
+       clk_enable_nolock(&clk_hclk_peri_ahb_arbi);
+       clk_enable_nolock(&clk_hclk_emem_peri);
+       clk_enable_nolock(&clk_nandc);
+       clk_enable_nolock(&clk_hclk_otg0);
+       clk_enable_nolock(&clk_hclk_otg1);
+       //aclk periph
+       clk_enable_nolock(&clk_dma2);
+       clk_enable_nolock(&clk_aclk_peri_niu);
+       clk_enable_nolock(&clk_aclk_cpu_peri);
+       clk_enable_nolock(&clk_aclk_peri_axi_matrix);
+       //pclk periph
+       clk_enable_nolock(&clk_pclk_peri_axi_matrix);
+
+       
+       clk_enable_nolock(&clk_hclk_hdmi);
+       clk_enable_nolock(&clk_hclk_rga);
+       clk_enable_nolock(&clk_hclk_ipp);
+       clk_enable_nolock(&clk_hclk_cif1);
+       clk_enable_nolock(&clk_hclk_cif0);
+       
+       clk_enable_nolock(&cif1_out);
+       clk_enable_nolock(&cif0_out);
+       clk_enable_nolock(&cif1_in);
+       clk_enable_nolock(&cif0_in);
+
+
+       
+
+
+}
+static void periph_clk_set_init(void)
+{
+       unsigned long aclk_p, hclk_p, pclk_p;
+       unsigned long ppll_rate=general_pll_clk.rate;
+       //aclk 148.5
+       
+       /* general pll */
+       switch (ppll_rate) {
+       case 148500* KHZ:
+               aclk_p = 148500*KHZ;
+               hclk_p = aclk_p>>1;
+               pclk_p = aclk_p>>2;
+               break;
+       case 1188*MHZ:
+               aclk_p = aclk_p>>3;// 0 
+               hclk_p = aclk_p>>1;
+               pclk_p = aclk_p>>2;
+       default:
+               ppll_rate = 297 * MHZ;
+       case 297 * MHZ:
+               aclk_p = ppll_rate>>1;
+               hclk_p = aclk_p>>0;
+               pclk_p = aclk_p>>1;
+               break;
+       }
+       
+       clk_set_parent_nolock(&aclk_periph, &general_pll_clk);
+       clk_set_rate_nolock(&aclk_periph, aclk_p);
+       clk_set_rate_nolock(&hclk_periph, hclk_p);
+       clk_set_rate_nolock(&pclk_periph, pclk_p);
+}
+
+
+void rk30_clock_common_i2s_init(unsigned long i2s_rate)
+{
+       if(i2s_rate<=i2s_12288khz||general_pll_clk.rate>=codec_pll_clk.rate)
+               clk_set_parent_nolock(&clk_i2s_pll, &general_pll_clk);
+       else
+       {
+               clk_set_parent_nolock(&clk_i2s_pll, &codec_pll_clk);
+       }
+}
+static void __init rk30_clock_common_init(unsigned long gpll_rate,unsigned long cpll_rate,
+                                       unsigned long i2s_rate)
+{
+
+       //clk_set_rate_nolock(&clk_cpu, 816*MHZ);//816
+       //general
+       clk_set_rate_nolock(&general_pll_clk, gpll_rate);
+       //code pll
+       clk_set_rate_nolock(&codec_pll_clk, cpll_rate);
+
+       //periph clk
+       periph_clk_set_init();
+       
+       //i2s
+       rk30_clock_common_i2s_init(i2s_rate);
+       
+       // uart
+       #if 0 
+       clk_set_parent_nolock(&clk_uart_pll, &codec_pll_clk);
+       #else
+       clk_set_parent_nolock(&clk_uart_pll, &general_pll_clk);
+       #endif
+       
+       //mac   
+       clk_set_parent_nolock(&clk_mac_pll_div, &ddr_pll_clk);
+       
+       //hsadc
+       //auto pll sel
+       //clk_set_parent_nolock(&clk_hsadc_pll_div, &general_pll_clk);
+
+       //lcdc1  hdmi
+       clk_set_parent_nolock(&dclk_lcdc1_div, &general_pll_clk);
+       
+       //lcdc0 lcd auto sel pll
+       //clk_set_parent_nolock(&dclk_lcdc0_div, &general_pll_clk);
+
+       //cif
+       clk_set_parent_nolock(&cif_out_pll, &general_pll_clk);
+
+       //axi lcdc auto sel
+       //clk_set_parent_nolock(&aclk_lcdc0, &general_pll_clk);
+       //clk_set_parent_nolock(&aclk_lcdc1, &general_pll_clk);
+
+       //axi vepu auto sel
+       //clk_set_parent_nolock(&aclk_vepu, &general_pll_clk);
+       //clk_set_parent_nolock(&aclk_vdpu, &general_pll_clk);
+
+       //gpu auto sel
+       //clk_set_parent_nolock(&clk_gpu, &general_pll_clk);
+
+}
+
+static struct clk def_ops_clk={
+       .get_parent=clksel_get_parent,
+       .set_parent=clksel_set_parent,
+};
+
+#ifdef CONFIG_PROC_FS
+struct clk_dump_ops dump_ops;
+#endif
+
+static void clk_dump_regs(void);
+
+void __init rk30_clock_data_init(unsigned long gpll,unsigned long cpll,unsigned long max_i2s_rate)
+{
+       struct clk_lookup *lk;
+       
+       clk_register_dump_ops(&dump_ops);
+       clk_register_default_ops_clk(&def_ops_clk);
+       
+       for (lk = clks; lk < clks + ARRAY_SIZE(clks); lk++) {
+               #ifdef RK30_CLK_OFFBOARD_TEST
+               fdsf
+                       rk30_clkdev_add(lk);
+               #else
+                       clkdev_add(lk);
+               #endif
+               clk_register(lk->clk);
+       }
+       clk_recalculate_root_clocks_nolock();
+       calc_lpj_ref();
+       loops_per_jiffy = CLK_LOOPS_RECALC(arm_pll_clk.rate);
+
+       /*
+        * Only enable those clocks we will need, let the drivers
+        * enable other clocks as necessary
+        */
+       rk30_init_enable_clocks();
+
+       /*
+        * Disable any unused clocks left on by the bootloader
+        */
+       //clk_disable_unused();
+       rk30_clock_common_init(gpll,cpll,max_i2s_rate);
+       //preset_lpj = loops_per_jiffy;
+       //clk_dump_regs();
+}
+extern int rk30_dvfs_init(void);
+
+/*
+ * You can override arm_clk rate with armclk= cmdline option.
+ */
+static int __init armclk_setup(char *str)
+{
+       get_option(&str, &armclk);
+
+       if (!armclk)
+               return 0;
+       if (armclk < 10000)
+               armclk *= MHZ;
+       //clk_set_rate_nolock(&arm_pll_clk, armclk);
+       return 0;
+}
+#ifndef RK30_CLK_OFFBOARD_TEST
+early_param("armclk", armclk_setup);
+#endif
+
+
+
+#ifdef CONFIG_PROC_FS
+
+static void dump_clock(struct seq_file *s, struct clk *clk, int deep,const struct list_head *root_clocks)
+{
+       struct clk* ck;
+       int i;
+       unsigned long rate = clk->rate;
+       //CRU_PRINTK_DBG("dump_clock %s\n",clk->name);
+       for (i = 0; i < deep; i++)
+               seq_printf(s, "    ");
+
+       seq_printf(s, "%-11s ", clk->name);
+#ifndef RK30_CLK_OFFBOARD_TEST
+       if (clk->flags & IS_PD) {
+                       seq_printf(s, "%s ", pmu_power_domain_is_on(clk->gate_idx) ? "on " : "off");
+       }
+#endif
+       if ((clk->mode == gate_mode) && (clk->gate_idx < CLK_GATE_MAX)) {
+               int idx = clk->gate_idx;
+               u32 v;
+               v = cru_readl(CLK_GATE_CLKID_CONS(idx))&((0x1)<<(idx%16));
+               seq_printf(s, "%s ", v ? "off" : "on ");
+       }
+
+       if (clk->pll)
+       {
+               u32 pll_mode;
+               u32 pll_id=clk->pll->id;
+               pll_mode=cru_readl(CRU_MODE_CON)&PLL_MODE_MSK(pll_id);
+               if(pll_mode==PLL_MODE_SLOW(pll_id))
+                       seq_printf(s, "slow   ");
+               else if(pll_mode==PLL_MODE_NORM(pll_id))
+                       seq_printf(s, "normal ");
+               else if(pll_mode==PLL_MODE_DEEP(pll_id))
+                       seq_printf(s, "deep   ");
+
+               if(cru_readl(PLL_CONS(pll_id,3)) & PLL_BYPASS) 
+                       seq_printf(s, "bypass ");
+       }
+       else if(clk == &clk_ddr) {
+               rate = clk->recalc(clk);
+       }
+
+       if (rate >= MHZ) {
+               if (rate % MHZ)
+                       seq_printf(s, "%ld.%06ld MHz", rate / MHZ, rate % MHZ);
+               else
+                       seq_printf(s, "%ld MHz", rate / MHZ);
+       } else if (rate >= KHZ) {
+               if (rate % KHZ)
+                       seq_printf(s, "%ld.%03ld KHz", rate / KHZ, rate % KHZ);
+               else
+                       seq_printf(s, "%ld KHz", rate / KHZ);
+       } else {
+               seq_printf(s, "%ld Hz", rate);
+       }
+
+       seq_printf(s, " usecount = %d", clk->usecount);
+
+       if (clk->parent)
+               seq_printf(s, " parent = %s", clk->parent->name);
+
+       seq_printf(s, "\n");
+
+       list_for_each_entry(ck, root_clocks, node) {
+               if (ck->parent == clk)
+                       dump_clock(s, ck, deep + 1,root_clocks);
+       }
+}
+
+static void dump_regs(struct seq_file *s)
+{
+       int i=0;
+       seq_printf(s, "\nPLL(id=0 apll,id=1,dpll,id=2,cpll,id=3 cpll)\n");
+       seq_printf(s, "\nPLLRegisters:\n");
+       for(i=0;i<END_PLL_ID;i++)
+       {
+               seq_printf(s,"pll%d        :cons:%x,%x,%x,%x\n",i,
+                       cru_readl(PLL_CONS(i,0)),
+                       cru_readl(PLL_CONS(i,1)),
+                       cru_readl(PLL_CONS(i,2)),
+                       cru_readl(PLL_CONS(i,3))
+                       );
+       }
+               seq_printf(s, "MODE        :%x\n", cru_readl(CRU_MODE_CON));
+
+       for(i=0;i<CRU_CLKSELS_CON_CNT;i++)
+       {
+               seq_printf(s,"CLKSEL%d     :%x\n",i,cru_readl(CRU_CLKSELS_CON(i)));
+       }
+       for(i=0;i<CRU_CLKGATES_CON_CNT;i++)
+       {
+               seq_printf(s,"CLKGATE%d           :%x\n",i,cru_readl(CRU_CLKGATES_CON(i)));
+       }
+       seq_printf(s,"GLB_SRST_FST:%x\n",cru_readl(CRU_GLB_SRST_FST));
+       seq_printf(s,"GLB_SRST_SND:%x\n",cru_readl(CRU_GLB_SRST_SND));
+
+       for(i=0;i<CRU_SOFTRSTS_CON_CNT;i++)
+       {
+               seq_printf(s,"CLKGATE%d           :%x\n",i,cru_readl(CRU_SOFTRSTS_CON(i)));
+       }
+       seq_printf(s,"CRU MISC    :%x\n",cru_readl(CRU_MISC_CON));
+       seq_printf(s,"GLB_CNT_TH  :%x\n",cru_readl(CRU_GLB_CNT_TH));
+
+}
+
+static void clk_dump_regs(void)
+{
+       int i=0;
+       printk("\nPLL(id=0 apll,id=1,dpll,id=2,cpll,id=3 cpll)\n");
+       printk("\nPLLRegisters:\n");
+       for(i=0;i<END_PLL_ID;i++)
+       {
+               printk("pll%d        :cons:%x,%x,%x,%x\n",i,
+                       cru_readl(PLL_CONS(i,0)),
+                       cru_readl(PLL_CONS(i,1)),
+                       cru_readl(PLL_CONS(i,2)),
+                       cru_readl(PLL_CONS(i,3))
+                       );
+       }
+               printk("MODE        :%x\n", cru_readl(CRU_MODE_CON));
+
+       for(i=0;i<CRU_CLKSELS_CON_CNT;i++)
+       {
+               printk("CLKSEL%d           :%x\n",i,cru_readl(CRU_CLKSELS_CON(i)));
+       }
+       for(i=0;i<CRU_CLKGATES_CON_CNT;i++)
+       {
+               printk("CLKGATE%d         :%x\n",i,cru_readl(CRU_CLKGATES_CON(i)));
+       }
+       printk("GLB_SRST_FST:%x\n",cru_readl(CRU_GLB_SRST_FST));
+       printk("GLB_SRST_SND:%x\n",cru_readl(CRU_GLB_SRST_SND));
+
+       for(i=0;i<CRU_SOFTRSTS_CON_CNT;i++)
+       {
+               printk("CLKGATE%d         :%x\n",i,cru_readl(CRU_SOFTRSTS_CON(i)));
+       }
+       printk("CRU MISC    :%x\n",cru_readl(CRU_MISC_CON));
+       printk("GLB_CNT_TH  :%x\n",cru_readl(CRU_GLB_CNT_TH));
+
+}
+
+
+#ifdef CONFIG_PROC_FS
+static void dump_clock(struct seq_file *s, struct clk *clk, int deep,const struct list_head *root_clocks);
+struct clk_dump_ops dump_ops={
+       .dump_clk=dump_clock,
+       .dump_regs=dump_regs,
+};
+#endif
+
+
+#endif /* CONFIG_PROC_FS */
+
+
+
+
+#ifdef RK30_CLK_OFFBOARD_TEST
+struct clk *test_get_parent(struct clk *clk)
+{
+       return clk->parent;
+}
+
+void i2s_test(void)
+{
+       struct clk *i2s_clk=&clk_i2s0;
+       
+       clk_enable_nolock(i2s_clk);
+       
+       clk_set_rate_nolock(i2s_clk, 12288000);
+       printk("int %s parent is %s\n",i2s_clk->name,test_get_parent(i2s_clk)->name);
+       clk_set_rate_nolock(i2s_clk, 297*MHZ/2);
+       printk("int%s parent is %s\n",i2s_clk->name,test_get_parent(i2s_clk)->name);
+       clk_set_rate_nolock(i2s_clk, 12*MHZ);
+       printk("int%s parent is %s\n",i2s_clk->name,test_get_parent(i2s_clk)->name);
+
+}
+
+void uart_test(void)
+{
+       struct clk *uart_clk=&clk_uart0;
+       
+       clk_enable_nolock(uart_clk);
+       
+       clk_set_rate_nolock(uart_clk, 12288000);
+       printk("int %s parent is %s\n",uart_clk->name,test_get_parent(uart_clk)->name);
+       clk_set_rate_nolock(uart_clk, 297*MHZ/2);
+       printk("int%s parent is %s\n",uart_clk->name,test_get_parent(uart_clk)->name);
+       clk_set_rate_nolock(uart_clk, 12*MHZ);
+       printk("int%s parent is %s\n",uart_clk->name,test_get_parent(uart_clk)->name);
+
+}
+void hsadc_test(void)
+{
+       struct clk *hsadc_clk=&clk_hsadc;
+
+       printk("******************hsadc_test**********************\n");
+       clk_enable_nolock(hsadc_clk);
+
+       clk_set_rate_nolock(hsadc_clk, 12288000);
+       printk("****end %s parent is %s\n",hsadc_clk->name,test_get_parent(hsadc_clk)->name);
+       
+       
+       clk_set_rate_nolock(hsadc_clk, 297*MHZ/2);
+       printk("****end %s parent is %s\n",hsadc_clk->name,test_get_parent(hsadc_clk)->name);
+
+       clk_set_rate_nolock(hsadc_clk, 300*MHZ/2);
+
+       clk_set_rate_nolock(hsadc_clk, 296*MHZ/2);
+
+       printk("******************hsadc out clock**********************\n");
+
+       clk_set_parent_nolock(hsadc_clk, &clk_hsadc_ext);
+       printk("****end %s parent is %s\n",hsadc_clk->name,test_get_parent(hsadc_clk)->name);
+       clk_set_rate_nolock(hsadc_clk, 297*MHZ/2);
+       printk("****end %s parent is %s\n",hsadc_clk->name,test_get_parent(hsadc_clk)->name);
+
+       
+
+}
+
+static void __init rk30_clock_test_init(unsigned long ppll_rate)
+{
+       //arm
+       printk("*********arm_pll_clk***********\n");
+       clk_set_rate_nolock(&arm_pll_clk, 816*MHZ);
+       
+       printk("*********set clk_cpu parent***********\n");
+       clk_set_parent_nolock(&clk_cpu, &arm_pll_clk);
+       clk_set_rate_nolock(&clk_cpu, 504*MHZ);
+
+       //general
+       printk("*********general_pll_clk***********\n");
+       clk_set_rate_nolock(&general_pll_clk, ppll_rate);
+       
+       //code pll
+       printk("*********codec_pll_clk***********\n");
+       clk_set_rate_nolock(&codec_pll_clk, 600*MHZ);
+
+       
+       printk("*********periph_clk_set_init***********\n");
+       clk_set_parent_nolock(&aclk_periph, &general_pll_clk);
+       periph_clk_set_init();
+
+       #if 0 //
+               clk_set_parent_nolock(&clk_i2s_pll, &codec_pll_clk);
+       #else
+               printk("*********clk i2s***********\n");
+               clk_set_parent_nolock(&clk_i2s_pll, &general_pll_clk);
+               printk("common %s parent is %s\n",clk_i2s_pll.name,test_get_parent(&clk_i2s_pll)->name);
+               i2s_test();
+       #endif
+// spi
+       clk_enable_nolock(&clk_spi0);
+       clk_set_rate_nolock(&clk_spi0, 30*MHZ);
+       printk("common %s parent is %s\n",clk_spi0.name,test_get_parent(&clk_spi0)->name);
+//saradc
+       clk_enable_nolock(&clk_saradc);
+       clk_set_rate_nolock(&clk_saradc, 6*MHZ);
+       printk("common %s parent is %s\n",clk_saradc.name,test_get_parent(&clk_saradc)->name);
+//sdio 
+       clk_enable_nolock(&clk_sdio);
+       clk_set_rate_nolock(&clk_sdio, 50*MHZ);
+       printk("common %s parent is %s\n",clk_sdio.name,test_get_parent(&clk_sdio)->name);
+// uart
+       clk_set_parent_nolock(&clk_uart_pll, &general_pll_clk);
+       uart_test();
+//mac  
+       printk("*********mac***********\n");
+
+       clk_set_parent_nolock(&clk_mac_pll_div, &general_pll_clk);
+       printk("common %s parent is %s\n",clk_mac_pll_div.name,test_get_parent(&clk_mac_pll_div)->name);
+
+       //clk_set_parent_nolock(&clk_mac_ref, &clk_mac_pll_div);
+       clk_set_rate_nolock(&clk_mac_ref, 50*MHZ);
+       printk("common %s parent is %s\n",clk_mac_ref.name,test_get_parent(&clk_mac_ref)->name);
+       
+       printk("*********mac mii set***********\n");
+       clk_set_parent_nolock(&clk_mac_ref, &rmii_clkin);
+       clk_set_rate_nolock(&clk_mac_ref, 20*MHZ);
+       printk("common %s parent is %s\n",clk_mac_ref.name,test_get_parent(&clk_mac_ref)->name);
+//hsadc
+       printk("*********hsadc 1***********\n");
+       //auto pll
+       hsadc_test();
+//lcdc
+       clk_enable_nolock(&dclk_lcdc0);
+
+       clk_set_rate_nolock(&dclk_lcdc0, 60*MHZ);
+       clk_set_rate_nolock(&dclk_lcdc0, 27*MHZ);
+
+//cif
+       clk_enable_nolock(&cif0_out);
+
+       clk_set_parent_nolock(&cif_out_pll, &general_pll_clk);
+       printk("common %s parent is %s\n",cif_out_pll.name,test_get_parent(&cif_out_pll)->name);
+
+       clk_set_rate_nolock(&cif0_out, 60*MHZ);
+       printk("common %s parent is %s\n",cif0_out.name,test_get_parent(&cif0_out)->name);
+
+       clk_set_rate_nolock(&cif0_out, 24*MHZ);
+       printk("common %s parent is %s\n",cif0_out.name,test_get_parent(&cif0_out)->name);
+//cif_in
+       clk_enable_nolock(&cif0_in);
+       clk_set_rate_nolock(&cif0_in, 24*MHZ);
+//axi lcdc
+       clk_enable_nolock(&aclk_lcdc0);
+       clk_set_rate_nolock(&aclk_lcdc0, 150*MHZ);
+       printk("common %s parent is %s\n",aclk_lcdc0.name,test_get_parent(&aclk_lcdc0)->name);
+//axi vepu
+       clk_enable_nolock(&aclk_vepu);
+       clk_set_rate_nolock(&aclk_vepu, 300*MHZ);
+       printk("common %s parent is %s\n",aclk_vepu.name,test_get_parent(&aclk_vepu)->name);
+
+       clk_set_rate_nolock(&hclk_vepu, 300*MHZ);
+       printk("common %s parent is %s\n",hclk_vepu.name,test_get_parent(&hclk_vepu)->name);
+
+       printk("test end\n");
+
+       /* arm pll 
+       clk_set_rate_nolock(&arm_pll_clk, armclk);
+       clk_set_rate_nolock(&clk_cpu,   armclk);//pll:core =1:1
+       */
+       //
+       //clk_set_rate_nolock(&codec_pll_clk, ppll_rate*2);
+       //
+       //clk_set_rate_nolock(&aclk_vepu, 300 * MHZ);
+       //clk_set_rate_nolock(&clk_gpu, 300 * MHZ);
+       
+}
+
+
+
+
+
+static LIST_HEAD(rk30_clocks);
+static DEFINE_MUTEX(rk30_clocks_mutex);
+
+static inline int __rk30clk_get(struct clk *clk)
+{
+       return 1;
+}
+void rk30_clkdev_add(struct clk_lookup *cl)
+{
+       mutex_lock(&rk30_clocks_mutex);
+       list_add_tail(&cl->node, &rk30_clocks);
+       mutex_unlock(&rk30_clocks_mutex);
+}
+static struct clk_lookup *rk30_clk_find(const char *dev_id, const char *con_id)
+{
+       struct clk_lookup *p, *cl = NULL;
+       int match, best = 0;
+
+       list_for_each_entry(p, &rk30_clocks, node) {
+               match = 0;
+               if (p->dev_id) {
+                       if (!dev_id || strcmp(p->dev_id, dev_id))
+                               continue;
+                       match += 2;
+               }
+               if (p->con_id) {
+                       if (!con_id || strcmp(p->con_id, con_id))
+                               continue;
+                       match += 1;
+               }
+
+               if (match > best) {
+                       cl = p;
+                       if (match != 3)
+                               best = match;
+                       else
+                               break;
+               }
+       }
+       return cl;
+}
+
+struct clk *rk30_clk_get_sys(const char *dev_id, const char *con_id)
+{
+       struct clk_lookup *cl;
+
+       mutex_lock(&rk30_clocks_mutex);
+       cl = rk30_clk_find(dev_id, con_id);
+       if (cl && !__rk30clk_get(cl->clk))
+               cl = NULL;
+       mutex_unlock(&rk30_clocks_mutex);
+
+       return cl ? cl->clk : ERR_PTR(-ENOENT);
+}
+//EXPORT_SYMBOL(rk30_clk_get_sys);
+
+struct clk *rk30_clk_get(struct device *dev, const char *con_id)
+{
+       const char *dev_id = dev ? dev_name(dev) : NULL;
+       return rk30_clk_get_sys(dev_id, con_id);
+}
+//EXPORT_SYMBOL(rk30_clk_get);
+
+
+int rk30_clk_set_rate(struct clk *clk, unsigned long rate);
+
+void rk30_clocks_test(void)
+{
+    struct clk *test_gpll;
+       test_gpll=rk30_clk_get(NULL,"general_pll");
+       if(test_gpll)
+       {
+               rk30_clk_set_rate(test_gpll,297*2*MHZ);
+               printk("gpll rate=%lu\n",test_gpll->rate);              
+       }
+       //while(1);
+}
+
+void __init rk30_clock_init_test(void){
+
+       rk30_clock_init(periph_pll_297mhz,codec_pll_360mhz,max_i2s_12288khz);
+       //while(1);
+}
+
+
+#endif
+
+
index 33d5bb40621268e60144fac34842a0a3aca367f0..38ab1e3ff4b5b877a0a5cbdd5e4f64a49fc0b322 100755 (executable)
@@ -65,7 +65,7 @@ void __init rk30_map_io(void)
        rk30_map_common_io();
        rk29_setup_early_printk();
        rk29_sram_init();
-       rk30_clock_init();
+       board_clock_init();
        rk30_l2_cache_init();
        rk30_iomux_init();
 }
diff --git a/arch/arm/mach-rk30/dvfs.c b/arch/arm/mach-rk30/dvfs.c
new file mode 100644 (file)
index 0000000..6283fda
--- /dev/null
@@ -0,0 +1,942 @@
+/* arch/arm/mach-rk30/rk30_dvfs.c\r
+ *\r
+ * Copyright (C) 2012 ROCKCHIP, Inc.\r
+ *\r
+ * This software is licensed under the terms of the GNU General Public\r
+ * License version 2, as published by the Free Software Foundation, and\r
+ * may be copied, distributed, and modified under those terms.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ */\r
+\r
+#include <linux/kernel.h>\r
+#include <linux/err.h>\r
+#include <linux/spinlock.h>\r
+#include <linux/list.h>\r
+#include <linux/slab.h>\r
+#include <linux/clk.h>\r
+#include <linux/cpufreq.h>\r
+\r
+#include "clock.h"\r
+#include <mach/dvfs.h>\r
+#include <mach/clock.h>\r
+\r
+#if 0\r
+#define DVFS_DBG(fmt, args...) pr_debug(fmt, ##args)\r
+#define DVFS_ERR(fmt, args...) pr_err(fmt, ##args)\r
+#else\r
+#define DEBUG_RK30_DVFS\r
+#define DVFS_DBG(fmt, args...) printk(fmt, ##args)\r
+#define DVFS_ERR(fmt, args...) printk(fmt, ##args)\r
+#endif\r
+\r
+#ifndef CONFIG_ARCH_RK30\r
+#define DVFS_TEST_OFF_BOARD\r
+#endif\r
+\r
+\r
+\r
+#ifdef DVFS_TEST_OFF_BOARD\r
+/* Just for simulation */\r
+\r
+struct regulator {\r
+    int min_uV;\r
+};\r
+#if 0\r
+static void test_regulator_put(struct regulator *regulator)\r
+{\r
+    kfree(regulator);\r
+}\r
+#endif\r
+struct regulator regulators[100];\r
+static struct regulator *test_regulator_get(struct device *dev, const char *id) {\r
+    static int ret_cnt = 0;\r
+    return &regulators[ret_cnt++];\r
+}\r
+\r
+static int test_regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)\r
+{\r
+    regulator->min_uV = min_uV;\r
+    return 0;\r
+}\r
+\r
+static int test_regulator_get_voltage(struct regulator *regulator)\r
+{\r
+    return regulator->min_uV;\r
+}\r
+\r
+int rk30_clk_set_rate(struct clk *clk, unsigned long rate);\r
+static void dump_dbg_map(void);\r
+int rk30_dvfs_init_test(void);\r
+int rk30_clk_enable(struct clk *clk);\r
+int rk30_clk_disable(struct clk *clk);\r
+\r
+#define dvfs_regulator_get(dev,id) test_regulator_get((dev),(id))\r
+#define dvfs_regulator_put(regu) test_regulator_get((regu))\r
+#define dvfs_regulator_set_voltage(regu,min_uV,max_uV) test_regulator_set_voltage((regu),(min_uV),(max_uV))\r
+#define dvfs_regulator_get_voltage(regu) test_regulator_get_voltage((regu))\r
+\r
+/* clock */\r
+#define dvfs_clk_get(a,b) rk30_clk_get((a),(b))\r
+#define dvfs_clk_set_rate(a,b) rk30_clk_set_rate((a),(b))\r
+#define dvfs_clk_enable(a) rk30_clk_enable((a))\r
+#define dvfs_clk_disable(a) rk30_clk_disable((a))\r
+\r
+#else\r
+/* board runing */\r
+#include <linux/regulator/consumer.h>\r
+\r
+#define dvfs_regulator_get(dev,id) regulator_get((dev),(id))\r
+#define dvfs_regulator_put(regu) regulator_get((regu))\r
+#define dvfs_regulator_set_voltage(regu,min_uV,max_uV) regulator_set_voltage((regu),(min_uV),(max_uV))\r
+#define dvfs_regulator_get_voltage(regu) regulator_get_voltage((regu))\r
+\r
+#define dvfs_clk_get(a,b) clk_get((a),(b))\r
+#define dvfs_clk_set_rate(a,b) clk_set_rate((a),(b))\r
+#define dvfs_clk_enable(a) clk_enable((a))\r
+#define dvfs_clk_disable(a) clk_disable((a))\r
+#endif\r
+\r
+\r
+static LIST_HEAD(rk_dvfs_tree);\r
+static DEFINE_MUTEX(mutex);\r
+/*\r
+int dvfs_target_core(struct clk *clk, unsigned int rate);\r
+int dvfs_target(struct clk *clk, unsigned int rate);\r
+int dvfs_clk_set_rate(struct clk *clk, unsigned long rate);\r
+*/\r
+extern int rk30_clk_notifier_register(struct clk *clk, struct notifier_block *nb);\r
+extern int rk30_clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);\r
+\r
+#define FV_TABLE_END 0\r
+#define PD_ON  1\r
+#define PD_OFF 0\r
+\r
+\r
+static void dvfs_clk_scale_volt(struct clk_node *dvfs_clk, unsigned int volt);\r
+static int dvfs_clk_get_volt(struct clk_node *dvfs_clk, unsigned long rate,\r
+                             struct cpufreq_frequency_table *clk_fv);\r
+\r
+/**\r
+ * **************************FUNCTIONS***********************************\r
+ */\r
+\r
+#ifdef DEBUG_RK30_DVFS\r
+/**\r
+ * dump_dbg_map() : Draw all informations of dvfs while debug\r
+ */\r
+static void dump_dbg_map(void)\r
+{\r
+    int i;\r
+    struct vd_node     *vd;\r
+    struct pd_node     *pd, *clkparent;\r
+    struct clk_list    *child;\r
+    struct clk_node    *dvfs_clk;\r
+\r
+    DVFS_DBG("-------------DVFS DEBUG-----------\n\n\n");\r
+    DVFS_DBG("RK30 DVFS TREE:\n");\r
+    list_for_each_entry(vd, &rk_dvfs_tree, node) {\r
+        DVFS_DBG("|\n|- voltage domain:%s\n", vd->name);\r
+        DVFS_DBG("|- current voltage:%d\n", vd->cur_volt);\r
+\r
+        list_for_each_entry(pd, &vd->pd_list, node) {\r
+            DVFS_DBG("|  |\n|  |- power domain:%s, status = %s, current volt = %d\n",\r
+                     pd->name, (pd->pd_status == PD_ON) ? "ON" : "OFF", pd->cur_volt);\r
+\r
+            list_for_each_entry(child, &pd->clk_list, node) {\r
+                dvfs_clk = child->dvfs_clk;\r
+                DVFS_DBG("|  |  |\n|  |  |- clock: %s current: rate %d, volt = %d, enable_dvfs = %s\n",\r
+                         dvfs_clk->name, dvfs_clk->cur_freq, dvfs_clk->cur_volt, dvfs_clk->enable_dvfs == 0 ? "DISABLE" : "ENABLE");\r
+                for (i = 0; dvfs_clk->pds[i].pd != NULL; i++) {\r
+                    clkparent = dvfs_clk->pds[i].pd;\r
+                    DVFS_DBG("|  |  |  |- clock parents: %s, vd_parent = %s\n", clkparent->name, clkparent->vd->name);\r
+                }\r
+\r
+                for (i = 0; (dvfs_clk->dvfs_table[i].frequency != FV_TABLE_END); i++) {\r
+                    DVFS_DBG("|  |  |  |- freq = %d, volt = %d\n", dvfs_clk->dvfs_table[i].frequency, dvfs_clk->dvfs_table[i].index);\r
+\r
+                }\r
+            }\r
+        }\r
+    }\r
+    DVFS_DBG("-------------DVFS DEBUG END------------\n");\r
+}\r
+#endif\r
+\r
+int is_support_dvfs(struct clk_node *dvfs_info)\r
+{\r
+    return (dvfs_info->vd && dvfs_info->vd->vd_dvfs_target && dvfs_info->enable_dvfs);\r
+}\r
+static int rk_dvfs_clk_notifier_event(struct notifier_block *this,\r
+                                      unsigned long event, void *ptr)\r
+{\r
+    struct clk_notifier_data *noti_info;\r
+    struct clk *clk;\r
+    struct clk_node *dvfs_clk;\r
+    noti_info = (struct clk_notifier_data *)ptr;\r
+    clk = noti_info->clk;\r
+    dvfs_clk = clk->dvfs_info;\r
+\r
+    switch (event) {\r
+    case CLK_PRE_RATE_CHANGE:\r
+        DVFS_DBG("%s CLK_PRE_RATE_CHANGE\n", __func__);\r
+        break;\r
+    case CLK_POST_RATE_CHANGE:\r
+        DVFS_DBG("%s CLK_POST_RATE_CHANGE\n", __func__);\r
+        break;\r
+    case CLK_ABORT_RATE_CHANGE:\r
+        DVFS_DBG("%s CLK_ABORT_RATE_CHANGE\n", __func__);\r
+        break;\r
+    case CLK_PRE_ENABLE:\r
+        DVFS_DBG("%s CLK_PRE_ENABLE\n", __func__);\r
+        break;\r
+    case CLK_POST_ENABLE:\r
+        DVFS_DBG("%s CLK_POST_ENABLE\n", __func__);\r
+        break;\r
+    case CLK_ABORT_ENABLE:\r
+        DVFS_DBG("%s CLK_ABORT_ENABLE\n", __func__);\r
+        break;\r
+    case CLK_PRE_DISABLE:\r
+        DVFS_DBG("%s CLK_PRE_DISABLE\n", __func__);\r
+        break;\r
+    case CLK_POST_DISABLE:\r
+        DVFS_DBG("%s CLK_POST_DISABLE\n", __func__);\r
+        dvfs_clk->cur_freq = 0;\r
+        dvfs_clk_scale_volt(dvfs_clk, 0);\r
+        break;\r
+    case CLK_ABORT_DISABLE:\r
+        DVFS_DBG("%s CLK_ABORT_DISABLE\n", __func__);\r
+\r
+        break;\r
+    default:\r
+        break;\r
+    }\r
+    return 0;\r
+}\r
+static struct notifier_block rk_dvfs_clk_notifier = {\r
+    .notifier_call = rk_dvfs_clk_notifier_event,\r
+};\r
+int clk_disable_dvfs(struct clk *clk)\r
+{\r
+    struct clk_node *dvfs_clk;\r
+    dvfs_clk = clk->dvfs_info;\r
+    if(dvfs_clk->enable_dvfs - 1 < 0) {\r
+        DVFS_ERR("clk is already closed!\n");\r
+        return -1;\r
+    } else {\r
+        DVFS_ERR("clk is disable now!\n");\r
+        dvfs_clk->enable_dvfs--;\r
+        if(0 == dvfs_clk->enable_dvfs) {\r
+            DVFS_ERR("clk closed!\n");\r
+            rk30_clk_notifier_unregister(clk, dvfs_clk->dvfs_nb);\r
+            DVFS_ERR("clk unregister nb!\n");\r
+            dvfs_clk_scale_volt(dvfs_clk, 0);\r
+        }\r
+    }\r
+    dump_dbg_map();\r
+    return 0;\r
+}\r
+\r
+int clk_enable_dvfs(struct clk *clk)\r
+{\r
+    struct regulator *regulator;\r
+    struct clk_node *dvfs_clk;\r
+    struct cpufreq_frequency_table clk_fv;\r
+\r
+    if(!clk->dvfs_info) {\r
+        DVFS_ERR("This clk(%s) not support dvfs!\n", clk->name);\r
+        return -1;\r
+    }\r
+\r
+    dvfs_clk = clk->dvfs_info;\r
+    DVFS_ERR("dvfs clk enable dvfs %s\n", dvfs_clk->name);\r
+    if(0 == dvfs_clk->enable_dvfs) {\r
+        dvfs_clk->enable_dvfs++;\r
+        if(!dvfs_clk->vd->regulator) {\r
+            regulator = dvfs_regulator_get(NULL, dvfs_clk->vd->regulator_name);\r
+            if(regulator)\r
+                dvfs_clk->vd->regulator = regulator;\r
+            else\r
+                dvfs_clk->vd->regulator = NULL;\r
+        }\r
+        if(dvfs_clk->dvfs_nb) {\r
+            // must unregister when clk disable\r
+            rk30_clk_notifier_register(clk, dvfs_clk->dvfs_nb);\r
+        }\r
+\r
+        if(!clk || IS_ERR(clk)) {\r
+            DVFS_ERR("%s get clk %s error\n", __func__, dvfs_clk->name);\r
+            return -1;\r
+        }\r
+        //DVFS_DBG("%s get clk %s rate = %lu\n", __func__, clk->name, clk->rate);\r
+               if(dvfs_clk->cur_freq == 0)\r
+               dvfs_clk_get_volt(dvfs_clk, clk->rate, &clk_fv);\r
+               else\r
+                       dvfs_clk_get_volt(dvfs_clk, dvfs_clk->cur_freq, &clk_fv);\r
+        dvfs_clk->cur_volt = clk_fv.index;\r
+        dvfs_clk->cur_freq = clk_fv.frequency;\r
+        dvfs_clk_scale_volt(dvfs_clk, dvfs_clk->cur_volt);\r
+               dump_dbg_map();\r
+\r
+    } else {\r
+        DVFS_ERR("dvfs already enable clk enable = %d!\n", dvfs_clk->enable_dvfs);\r
+        dvfs_clk->enable_dvfs++;\r
+    }\r
+    return 0;\r
+}\r
+\r
+int dvfs_set_rate(struct clk *clk, unsigned long rate)\r
+{\r
+    int ret = 0;\r
+    struct vd_node *vd;\r
+    DVFS_DBG("%s dvfs start\n", clk->name);\r
+    if(!clk->dvfs_info) {\r
+        DVFS_ERR("%s :This clk do not support dvfs!\n", __func__);\r
+        ret = -1;\r
+    } else {\r
+        vd = clk->dvfs_info->vd;\r
+        mutex_lock(&vd->dvfs_mutex);\r
+        ret = vd->vd_dvfs_target(clk, rate);\r
+        mutex_unlock(&vd->dvfs_mutex);\r
+    }\r
+    return ret;\r
+}\r
+\r
+/**\r
+ * get correspond voltage khz\r
+ */\r
+static int dvfs_clk_get_volt(struct clk_node *dvfs_clk, unsigned long rate,\r
+                             struct cpufreq_frequency_table *clk_fv)\r
+{\r
+    int i = 0;\r
+    if (rate == 0) {\r
+        /* since no need*/\r
+        return -1;\r
+    }\r
+    clk_fv->frequency = rate;\r
+    clk_fv->index = 0;\r
+    for(i = 0; (dvfs_clk->dvfs_table[i].frequency != FV_TABLE_END); i++) {\r
+        if(dvfs_clk->dvfs_table[i].frequency >= rate) {\r
+            clk_fv->frequency = dvfs_clk->dvfs_table[i].frequency;\r
+            clk_fv->index = dvfs_clk->dvfs_table[i].index;\r
+            DVFS_DBG("%s dvfs_clk_get_volt rate=%u hz ref vol=%d uV\n", dvfs_clk->name, clk_fv->frequency, clk_fv->index);\r
+            return 0;\r
+        }\r
+    }\r
+    clk_fv->frequency = 0;\r
+    clk_fv->index = 0;\r
+    DVFS_ERR("%s get corresponding voltage error! out of bound\n", dvfs_clk->name);\r
+    return -1;\r
+}\r
+\r
+static int dvfs_clk_round_volt(struct clk_node *dvfs_clk, int volt)\r
+{\r
+    struct pd_node     *pd;\r
+    struct clk_node    *dvfs_clk_tmp;\r
+    int volt_max = 0;\r
+    int i;\r
+\r
+    for(i = 0; (dvfs_clk->pds[i].pd != NULL); i++) {\r
+        pd = dvfs_clk->pds[i].pd;\r
+        if(volt > pd->cur_volt) {\r
+            /**\r
+             * if dvfs_clk parent power domain's voltage is smaller then\r
+             * this dvfs_clk's voltage ignore this power domain\r
+             */\r
+            volt_max = max(volt_max, volt);\r
+            continue;\r
+        }\r
+        list_for_each_entry(dvfs_clk_tmp, &pd->clk_list, node) {\r
+            /**\r
+             * found the max voltage uninclude dvfs_clk\r
+             */\r
+            if(dvfs_clk_tmp != dvfs_clk) {\r
+                volt_max = max(volt_max, dvfs_clk_tmp->cur_volt);\r
+            }\r
+        }\r
+    }\r
+\r
+    volt_max = max(volt_max, volt);\r
+    return volt_max;\r
+}\r
+\r
+static void dvfs_clk_scale_volt(struct clk_node *dvfs_clk, unsigned int volt)\r
+{\r
+    struct vd_node *vd;\r
+    struct pd_node *pd;\r
+    struct clk_list    *child;\r
+    struct clk_node    *dvfs_clk_tmp;\r
+    int volt_max_vd = 0, volt_max_pd = 0, i;\r
+\r
+    dvfs_clk->cur_volt = volt;//set  clk node volt\r
+    vd = dvfs_clk->vd;// vd\r
+    for(i = 0; (dvfs_clk->pds[i].pd != NULL); i++) {\r
+        pd = dvfs_clk->pds[i].pd;\r
+        volt_max_pd = 0;\r
+        /**\r
+         * set corresponding voltage, clk do not need to set voltage,just for\r
+         * powerdomain\r
+         */\r
+\r
+        if(volt > pd->cur_volt) {\r
+            pd->cur_volt = volt;\r
+            pd->pd_status = (pd->cur_volt == 0) ? PD_OFF : PD_ON;\r
+            continue;\r
+        }\r
+\r
+        /* set power domain voltage */\r
+        list_for_each_entry(child, &pd->clk_list, node) {\r
+            dvfs_clk_tmp = child->dvfs_clk;\r
+                       if(dvfs_clk_tmp->enable_dvfs){\r
+               volt_max_pd = max(volt_max_pd, dvfs_clk_tmp->cur_volt);\r
+                       }\r
+        }\r
+        pd->cur_volt = volt_max_pd;\r
+\r
+        pd->pd_status = (volt_max_pd == 0) ? PD_OFF : PD_ON;\r
+    }\r
+\r
+    /* set voltage domain voltage */\r
+    volt_max_vd = 0;\r
+    list_for_each_entry(pd, &vd->pd_list, node) {\r
+        volt_max_vd = max(volt_max_vd, pd->cur_volt);\r
+    }\r
+    vd->cur_volt = volt_max_vd;\r
+}\r
+\r
+int dvfs_target_set_rate_core(struct clk *clk, unsigned long rate)\r
+{\r
+    struct clk_node *dvfs_clk;\r
+    int volt_new = 0, volt_old = 0;\r
+    struct cpufreq_frequency_table clk_fv;\r
+    int ret = 0;\r
+    dvfs_clk = clk_get_dvfs_info(clk);\r
+\r
+    DVFS_ERR("%s get clk %s\n", __func__, clk->name);\r
+    if(dvfs_clk->vd->regulator == NULL) {\r
+        DVFS_ERR("%s can't get dvfs regulater\n", clk->name);\r
+        return -1;\r
+    }\r
+\r
+    /* If power domain off do scale in the notify function */\r
+    /*\r
+       if (rate == 0) {\r
+       dvfs_clk->cur_freq = 0;\r
+       dvfs_clk_scale_volt(dvfs_clk, 0);\r
+       return 0;\r
+       }\r
+       */\r
+    /* need round rate */\r
+    DVFS_ERR("%s going to round rate = %lu\n", clk->name, rate);\r
+    rate = clk_round_rate_nolock(clk, rate);\r
+    DVFS_ERR("%s round get rate = %lu\n", clk->name, rate);\r
+    /* find the clk corresponding voltage */\r
+    if (0 != dvfs_clk_get_volt(dvfs_clk, rate, &clk_fv)) {\r
+        DVFS_ERR("%s rate %lukhz is larger,not support\n", clk->name, rate);\r
+        return -1;\r
+    }\r
+    volt_old = dvfs_clk->vd->cur_volt;\r
+    volt_new = clk_fv.index;\r
+\r
+    DVFS_DBG("vol_new = %d mV(was %d mV)\n", volt_new, volt_old);\r\r
+\r
+    /* if up the voltage*/\r
+    if (volt_old < volt_new) {\r
+        if(dvfs_regulator_set_voltage(dvfs_clk->vd->regulator, volt_new, volt_new) < 0) {\r
+            DVFS_ERR("set voltage err\n");\r
+\r
+            return -1;\r
+        }\r
+        dvfs_clk->vd->cur_volt = volt_new;\r
+        /* CPU do not use power domain, so save scale times */\r
+        //dvfs_clk_scale_volt(dvfs_clk, clk_fv.index);\r
+    }\r
+\r
+    if(dvfs_clk->clk_dvfs_target) {\r
+        ret = dvfs_clk->clk_dvfs_target(clk, rate, clk_set_rate_locked);\r
+    } else {\r
+        ret = clk_set_rate_locked(clk, rate);\r
+    }\r
+    if (ret < 0) {\r
+        DVFS_ERR("set rate err\n");\r
+        return -1;\r
+    }\r
+    dvfs_clk->cur_freq = rate;\r
+    dvfs_clk->cur_volt = volt_new;\r
+\r
+    /* if down the voltage */\r
+    if (volt_old > volt_new) {\r
+        if(dvfs_regulator_set_voltage(dvfs_clk->vd->regulator, volt_new, volt_new) < 0) {\r
+            DVFS_ERR("set voltage err\n");\r
+\r
+            return -1;\r
+        }\r
+        dvfs_clk->vd->cur_volt = volt_new;\r
+        /* CPU do not use power domain, so save scale times */\r
+        //dvfs_clk_scale_volt(dvfs_clk, clk_fv.index);\r
+    }\r
+\r
+    return ret;\r
+}\r
+\r
+int dvfs_target_set_rate_normal(struct clk *clk, unsigned long rate)\r
+{\r
+    struct clk_node *dvfs_clk;\r
+    unsigned int volt_new = 0, volt_old = 0;\r
+    struct cpufreq_frequency_table clk_fv = {0, 0};\r
+    int ret = 0;\r
+\r
+    dvfs_clk = clk_get_dvfs_info(clk);\r
+    DVFS_ERR("%s get clk %s\n", __func__, clk->name);\r
+    if(dvfs_clk->vd->regulator == NULL) {\r
+        DVFS_DBG("%s can't get dvfs regulater\n", clk->name);\r
+        return -1;\r
+    }\r
+\r
+    /* need round rate */\r
+    DVFS_ERR("%s going to round rate = %lu\n", clk->name, rate);\r
+    rate = clk_round_rate_nolock(clk, rate);\r
+    DVFS_ERR("%s round get rate = %lu\n", clk->name, rate);\r
+    /* find the clk corresponding voltage */\r
+    if (dvfs_clk_get_volt(dvfs_clk, rate, &clk_fv)) {\r
+        DVFS_DBG("dvfs_clk_get_volt:rate = Get corresponding voltage error!\n");\r
+        return -1;\r
+    }\r
+\r
+    volt_old = dvfs_clk->vd->cur_volt;\r
+    volt_new = dvfs_clk_round_volt(dvfs_clk, clk_fv.index);\r
+\r
+    // if up the voltage\r
+    if (volt_old < volt_new) {\r
+        if(dvfs_regulator_set_voltage(dvfs_clk->vd->regulator, volt_new, volt_new) < 0) {\r
+            DVFS_DBG("set voltage err\n");\r\r
+            return -1;\r
+        }\r
+        dvfs_clk_scale_volt(dvfs_clk, clk_fv.index);\r
+    }\r
+\r
+    if(dvfs_clk->clk_dvfs_target) {\r
+        ret = dvfs_clk->clk_dvfs_target(clk, rate, clk_set_rate_locked);\r
+    } else {\r
+        ret = clk_set_rate_locked(clk, rate);\r\r
+    }\r
+    if (ret < 0) {\r
+        DVFS_ERR("set rate err\n");\r
+        return -1;\r
+    }\r
+    dvfs_clk->cur_freq = rate;\r
+    dvfs_clk->cur_volt = volt_new;\r
+\r
+    // if down the voltage\r
+    if (volt_old > volt_new) {\r
+        if(dvfs_regulator_set_voltage(dvfs_clk->vd->regulator, volt_new, volt_new) < 0) {\r
+            DVFS_DBG("set voltage err\n");\r
+                       return -1;\r
+\r
+        }\r
+        dvfs_clk_scale_volt(dvfs_clk, clk_fv.index);\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+\r
+/*****************************init**************************/\r
+/**\r
+ * rate must be raising sequence\r
+ */\r
+\r
+struct cpufreq_frequency_table cpu_dvfs_table[] = {\r
+    {.frequency        = 126000000, .index     = 800000},\r
+    {.frequency        = 252000000, .index     = 850000},\r
+    {.frequency        = 504000000, .index     = 900000},\r
+    {.frequency        = 816000000, .index     = 1050000},\r
+    {.frequency        = 1008000000, .index    = 1100000},\r
+    {.frequency        = 1200000000, .index    = 1200000},\r
+    {.frequency        = FV_TABLE_END},\r
+};\r
+struct cpufreq_frequency_table ddr_dvfs_table[] = {\r
+    {.frequency        = 24000000, .index              = 600000},\r
+    {.frequency        = 64000000, .index              = 700000},\r
+    {.frequency        = 126000000, .index     = 800000},\r
+    {.frequency        = 252000000, .index     = 850000},\r
+    {.frequency        = 504000000, .index     = 900000},\r
+    {.frequency        = FV_TABLE_END},\r
+};\r
+struct cpufreq_frequency_table gpu_dvfs_table[] = {\r
+    {.frequency        = 64000000, .index              = 700000},\r
+    {.frequency        = 126000000, .index     = 800000},\r
+    {.frequency        = 360000000, .index     = 850000},\r
+    {.frequency        = FV_TABLE_END},\r
+};\r
+\r
+static struct vd_node vd_cpu = {\r
+    .name                      = "vd_cpu",\r
+    .vd_dvfs_target    = dvfs_target_set_rate_core,\r
+};\r
+\r
+static struct vd_node vd_core = {\r
+    .name                      = "vd_core",\r
+    .vd_dvfs_target    = dvfs_target_set_rate_normal,\r
+};\r
+\r
+static struct vd_node vd_rtc = {\r
+    .name                      = "vd_rtc",\r
+    .vd_dvfs_target    = NULL,\r
+};\r
+\r
+#define LOOKUP_VD(_pvd, _regulator_name)       \\r
+{      \\r
+       .vd                             = _pvd, \\r
+       .regulator_name = _regulator_name,      \\r
+}\r
+static struct vd_node_lookup rk30_vds[] = {\r
+    LOOKUP_VD(&vd_cpu, "cpu"),\r
+    LOOKUP_VD(&vd_core, "core"),\r
+    LOOKUP_VD(&vd_rtc, "rtc"),\r
+};\r
+\r
+static struct pd_node pd_a9_0 = {\r
+    .name                      = "pd_a9_0",\r
+    .vd                                = &vd_cpu,\r
+};\r
+static struct pd_node pd_a9_1 = {\r
+    .name                      = "pd_a9_1",\r
+    .vd                                = &vd_cpu,\r
+};\r
+static struct pd_node pd_debug = {\r
+    .name                      = "pd_debug",\r
+    .vd                                = &vd_cpu,\r
+};\r
+static struct pd_node pd_scu = {\r
+    .name                      = "pd_scu",\r
+    .vd                                = &vd_cpu,\r
+};\r
+static struct pd_node pd_video = {\r
+    .name                      = "pd_video",\r
+    .vd                                = &vd_core,\r
+};\r
+static struct pd_node pd_vio = {\r
+    .name                      = "pd_vio",\r
+    .vd                                = &vd_core,\r
+};\r
+static struct pd_node pd_gpu = {\r
+    .name                      = "pd_gpu",\r
+    .vd                                = &vd_core,\r
+};\r
+static struct pd_node pd_peri = {\r
+    .name                      = "pd_peri",\r
+    .vd                                = &vd_core,\r
+};\r
+static struct pd_node pd_cpu = {\r
+    .name                      = "pd_cpu",\r
+    .vd                                = &vd_core,\r
+};\r
+static struct pd_node pd_alive = {\r
+    .name                      = "pd_alive",\r
+    .vd                                = &vd_core,\r
+};\r
+static struct pd_node pd_rtc = {\r
+    .name                      = "pd_rtc",\r
+    .vd                                = &vd_rtc,\r
+};\r
+#define LOOKUP_PD(_ppd)        \\r
+{      \\r
+       .pd     = _ppd, \\r
+}\r
+static struct pd_node_lookup rk30_pds[] = {\r
+    LOOKUP_PD(&pd_a9_0),\r
+    LOOKUP_PD(&pd_a9_1),\r
+    LOOKUP_PD(&pd_debug),\r
+    LOOKUP_PD(&pd_scu),\r
+    LOOKUP_PD(&pd_video),\r
+    LOOKUP_PD(&pd_vio),\r
+    LOOKUP_PD(&pd_gpu),\r
+    LOOKUP_PD(&pd_peri),\r
+    LOOKUP_PD(&pd_cpu),\r
+    LOOKUP_PD(&pd_alive),\r
+    LOOKUP_PD(&pd_rtc),\r
+};\r
+\r
+#define CLK_PDS(_ppd) \\r
+{      \\r
+       .pd     = _ppd, \\r
+}\r
+\r
+static struct pds_list cpu_pds[] = {\r
+    CLK_PDS(&pd_a9_0),\r
+    CLK_PDS(&pd_a9_1),\r
+    CLK_PDS(NULL),\r
+};\r
+static struct pds_list ddr_pds[] = {\r
+    CLK_PDS(&pd_cpu),\r
+    CLK_PDS(NULL),\r
+};\r
+static struct pds_list gpu_pds[] = {\r
+    CLK_PDS(&pd_gpu),\r
+    CLK_PDS(NULL),\r
+};\r
+\r
+#define RK_CLKS(_clk_name, _ppds, _dvfs_table, _dvfs_nb)       \\r
+{      \\r
+       .name   = _clk_name,    \\r
+       .pds            = _ppds,        \\r
+       .dvfs_table = _dvfs_table,      \\r
+       .dvfs_nb        = _dvfs_nb,     \\r
+}\r
+static struct clk_node rk30_clks[] = {\r
+    RK_CLKS("cpu", cpu_pds, cpu_dvfs_table, &rk_dvfs_clk_notifier),\r
+    RK_CLKS("ddr", ddr_pds, ddr_dvfs_table, &rk_dvfs_clk_notifier),\r
+    RK_CLKS("gpu", gpu_pds, gpu_dvfs_table, &rk_dvfs_clk_notifier),\r
+};\r
+/**\r
+ * first scale regulator volt\r
+ */\r
+static int rk_dvfs_check_regulator_volt(void)\r
+{\r
+    struct vd_node     *vd;\r
+    struct pd_node     *pd;\r
+    struct clk_list    *child;\r
+    struct clk_node    *dvfs_clk;\r
+    struct clk                 *clk;\r
+    struct cpufreq_frequency_table clk_fv;\r
+    unsigned int vmax_pd = 0, vmax_vd = 0;\r
+\r
+    list_for_each_entry(vd, &rk_dvfs_tree, node) {\r
+        vmax_vd = 0;\r
+        list_for_each_entry(pd, &vd->pd_list, node) {\r
+            vmax_pd = 0;\r
+            list_for_each_entry(child, &pd->clk_list, node) {\r
+\r
+                dvfs_clk = child->dvfs_clk;\r
+                clk = dvfs_clk_get(NULL, dvfs_clk->name);\r
+                if(!clk || IS_ERR(clk)) {\r
+                    DVFS_ERR("%s get clk %s error\n", __func__, dvfs_clk->name);\r
+                    continue;\r
+                }\r
+                //DVFS_DBG("%s get clk %s rate = %lu\n", __func__, clk->name, clk->rate);\r
+                dvfs_clk_get_volt(dvfs_clk, clk->rate, &clk_fv);\r
+                dvfs_clk->cur_volt = clk_fv.index;\r
+                dvfs_clk->cur_freq = clk_fv.frequency;\r
+                vmax_pd = max(vmax_pd, clk_fv.index);\r
+                pd->pd_status = (vmax_pd == 0) ? PD_OFF : PD_ON;\r
+            }\r
+            pd->cur_volt = vmax_pd;\r
+            vmax_vd = max(vmax_vd, vmax_pd);\r
+        }\r
+\r
+        vd->cur_volt = vmax_vd;\r
+        //DVFS_DBG("%s check error: %d, %d\n", vd->name, vd->cur_volt, dvfs_regulator_get_voltage(vd->regulator));\r
+        //if (vd->cur_volt != dvfs_regulator_get_voltage(vd->regulator)) {\r
+        //     DVFS_ERR("%s default voltage domain value error!\n", vd->name);\r
+        //}\r
+    }\r
+    return 0;\r
+}\r
+\r
+static int rk_regist_vd(struct vd_node_lookup *vd_lookup)\r
+{\r
+    struct vd_node *vd;\r
+    if(!vd_lookup)\r
+        return -1;\r
+    vd = vd_lookup->vd;\r
+    vd->regulator_name = vd_lookup->regulator_name;\r
+\r
+    mutex_lock(&mutex);\r
+\r
+    mutex_init(&vd->dvfs_mutex);\r
+    list_add(&vd->node, &rk_dvfs_tree);\r
+    INIT_LIST_HEAD(&vd->pd_list);\r
+\r
+    mutex_unlock(&mutex);\r
+\r
+    return 0;\r
+}\r
+static int rk_regist_pd(struct pd_node_lookup *pd_lookup)\r
+{\r
+    struct vd_node     *vd;\r
+    struct pd_node     *pd;\r
+\r
+    mutex_lock(&mutex);\r
+    pd = pd_lookup->pd;\r
+\r
+    list_for_each_entry(vd, &rk_dvfs_tree, node) {\r
+        if (vd == pd->vd) {\r
+            list_add(&pd->node, &vd->pd_list);\r
+            INIT_LIST_HEAD(&pd->clk_list);\r
+            break;\r
+        }\r
+    }\r
+    mutex_unlock(&mutex);\r
+    return 0;\r
+}\r
+//extern int rk30_clk_notifier_register(struct clk *clk, struct notifier_block *nb);\r
+\r
+static int rk_regist_clk(struct clk_node *dvfs_clk)\r
+{\r
+    struct pd_node     *pd;\r
+    struct clk_list    *child;\r
+    struct clk *clk;\r
+    int i = 0;\r
+\r
+    if(!dvfs_clk)\r
+        return -1;\r
+\r
+    if(!dvfs_clk->pds)\r
+        return -1;\r
+\r
+    mutex_lock(&mutex);\r
+    // set clk unsupport dvfs\r
+    dvfs_clk->enable_dvfs = 0;\r
+    dvfs_clk->vd = dvfs_clk->pds[0].pd->vd;\r
+    for (i = 0; dvfs_clk->pds[i].pd != NULL; i++) {\r
+        child = &(dvfs_clk->pds[i].clk_list);\r
+        child->dvfs_clk = dvfs_clk;\r
+        pd = dvfs_clk->pds[i].pd;\r
+        list_add(&child->node, &pd->clk_list);\r
+    }\r
+    clk = dvfs_clk_get(NULL, dvfs_clk->name);\r
+    clk_register_dvfs(dvfs_clk, clk);\r
+    mutex_unlock(&mutex);\r
+    return 0;\r
+}\r
+int rk30_dvfs_init(void)\r
+{\r
+    int i = 0;\r
+    for (i = 0; i < ARRAY_SIZE(rk30_vds); i++) {\r
+        rk_regist_vd(&rk30_vds[i]);\r
+    }\r
+    for (i = 0; i < ARRAY_SIZE(rk30_pds); i++) {\r
+        rk_regist_pd(&rk30_pds[i]);\r
+    }\r
+    for (i = 0; i < ARRAY_SIZE(rk30_clks); i++) {\r
+        rk_regist_clk(&rk30_clks[i]);\r
+    }\r
+    dump_dbg_map();\r
+    //DVFS_DBG("%s dvfs tree create finish!\n", __func__);\r
+    //rk_dvfs_check_regulator_volt();\r
+    return 0;\r
+}\r
+\r
+void dvfs_clk_set_rate_callback(struct clk *clk, clk_dvfs_target_callback clk_dvfs_target)\r
+{\r
+    struct clk_node *dvfs_clk = clk_get_dvfs_info(clk);\r
+    dvfs_clk->clk_dvfs_target = clk_dvfs_target;\r
+}\r
+//\r
+\r
+/*\r
+ *cpufreq_frequency_table->index for cpufreq is index\r
+ *cpufreq_frequency_table->index for dvfstable is volt\r
+ */\r
+int cpufreq_dvfs_init(struct clk *clk, struct cpufreq_frequency_table **table, clk_dvfs_target_callback clk_dvfs_target)\r
+{\r
+\r
+    struct cpufreq_frequency_table *freq_table;\r
+    struct clk_node *info = clk_get_dvfs_info(clk);\r
+    struct cpufreq_frequency_table *dvfs_table;//dvfs volt freq table\r
+    int i = 0;\r
+    DVFS_DBG("%s clk name %s\n", __func__, clk->name);\r
+    if(!info) {\r
+        return -1;\r
+    }\r
+    dvfs_table = info->dvfs_table;\r
+\r
+    if(!dvfs_table) {\r
+        return -1;\r
+    }\r
+\r
+    /********************************count table num****************************/\r
+    i = 0;\r
+    while(dvfs_table[i].frequency != FV_TABLE_END) {\r
+        //DVFS_DBG("dvfs_table1 %lu\n",dvfs_table[i].frequency);\r
+        i++;\r
+    }\r
+\r
+    freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * (i + 1), GFP_KERNEL);\r
+    //last freq is end tab\r
+    freq_table[i].index = i;\r
+    freq_table[i].frequency = CPUFREQ_TABLE_END;\r
+\r
+    //set freq table\r
+    i = 0;\r
+    while(dvfs_table[i].frequency != FV_TABLE_END) {\r
+        freq_table[i].index = i;\r
+        freq_table[i].frequency = dvfs_table[i].frequency;\r
+        //DVFS_DBG("dvfs_table %d %lu\n",i,dvfs_table[i].frequency);\r
+        i++;\r
+    }\r
+    *table = &freq_table[0];\r
+    dvfs_clk_set_rate_callback(clk, clk_dvfs_target);\r
+    return 0;\r
+}\r
+\r
+int clk_dvfs_set_dvfs_table(struct clk *clk, struct cpufreq_frequency_table *table)\r
+{\r
+    struct clk_node *info = clk_get_dvfs_info(clk);\r
+    if(!table || !info)\r
+        return -1;\r
+    info->dvfs_table = table;\r
+    return 0;\r
+}\r
+/********************************simulation cases****************************/\r
+\r
+#ifdef DVFS_TEST_OFF_BOARD\r
+int rk30_dvfs_init_test(void)\r
+{\r
+    struct clk *clk1;\r
+    DVFS_DBG("********************************simulation cases****************************\n");\r
+#ifdef DEBUG_RK30_DVFS\r
+    DVFS_DBG("\n\n");\r
+    dump_dbg_map();\r
+#endif\r
+    clk1 = dvfs_clk_get(NULL, "cpu");\r
+    if (clk1) {\r
+        dvfs_clk_set_rate(clk1, 1008000000);\r
+        dump_dbg_map();\r
+        dvfs_clk_set_rate(clk1, 816000000);\r
+        dump_dbg_map();\r
+        dvfs_clk_set_rate(clk1, 0);\r
+        dump_dbg_map();\r
+        dvfs_clk_set_rate(clk1, 1200000000);\r
+        dump_dbg_map();\r
+        dvfs_clk_set_rate(clk1, 1009000000);\r
+        dump_dbg_map();\r
+        dvfs_clk_set_rate(clk1, 1416000000);\r
+        dump_dbg_map();\r
+\r
+    } else {\r
+        DVFS_DBG("\t\t%s:\t can not find clk cpu\n", __func__);\r
+    }\r
+\r
+    clk1 = dvfs_clk_get(NULL, "gpu");\r
+    if (clk1) {\r
+        dvfs_clk_set_rate(clk1, 120000000);\r
+        dump_dbg_map();\r
+        dvfs_clk_enable(clk1);\r
+        dvfs_clk_disable(clk1);\r
+        dump_dbg_map();\r
+    } else {\r
+        DVFS_DBG("\t\t%s:\t can not find clk gpu\n", __func__);\r
+        dump_dbg_map();\r
+    }\r
+\r
+    clk1 = dvfs_clk_get(NULL, "arm_pll");\r
+    if (clk1) {\r
+        dvfs_clk_set_rate(clk1, 24000000);\r
+        dump_dbg_map();\r
+    } else {\r
+        DVFS_DBG("\t\t%s:\t can not find clk arm_pll\n", __func__);\r
+    }\r
+\r
+    DVFS_DBG("********************************simulation cases end***************************\n");\r
+\r
+    return 0;\r
+\r
+}\r
+#endif\r
+\r
index 551e5da66f279a1ab61c3cf66a1c16da805b1f55..c458257c992714f9b24fb400498d28a2b6ae112d 100755 (executable)
@@ -30,8 +30,35 @@ void __init rk30_init_irq(void);
 void __init rk30_map_io(void);
 struct machine_desc;
 void __init rk30_fixup(struct machine_desc *desc, struct tag *tags, char **cmdline, struct meminfo *mi);
-void __init rk30_clock_init(void);
+void __init rk30_clock_data_init(unsigned long gpll,unsigned long cpll,unsigned long max_i2s_rate);
+void __init board_clock_init(void);
 
 extern struct sys_timer rk30_timer;
 
+enum _periph_pll {
+       periph_pll_1485mhz = 148500000,
+       periph_pll_297mhz = 297000000,
+       periph_pll_1188mhz = 1188000000, /* for box*/
+       periph_pll_default = periph_pll_297mhz,
+};
+enum _codec_pll {
+       codec_pll_360mhz = 360000000, /* for HDMI */
+       codec_pll_408mhz = 408000000,
+       codec_pll_456mhz = 456000000,
+       codec_pll_504mhz = 504000000,
+       codec_pll_552mhz = 552000000, /* for HDMI */
+       codec_pll_600mhz = 600000000,
+       codec_pll_default = codec_pll_360mhz,
+};
+enum _max_i2s_rate {
+       max_i2s_8192khz = 8192000,
+       max_i2s_11289_6khz = 11289600,
+       max_i2s_12288khz = 12288000,
+       max_i2s_22579_2khz = 22579200,
+       max_i2s_24576khz = 24576000,//HDMI
+       max_i2s_49152khz = 24576000,//HDMI
+       max_i2s_default = max_i2s_12288khz,
+};
+
+
 #endif
diff --git a/arch/arm/mach-rk30/include/mach/clock.h b/arch/arm/mach-rk30/include/mach/clock.h
new file mode 100755 (executable)
index 0000000..a94d6c8
--- /dev/null
@@ -0,0 +1,81 @@
+/* arch/arm/mach-rk29/include/mach/clock.h
+ *
+ * Copyright (C) 2011 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ASM_ARCH_RK30_CLOCK_H
+#define __ASM_ARCH_RK30_CLOCK_H
+
+/**
+ * struct clk_notifier_data - rate data to pass to the notifier callback
+ * @clk: struct clk * being changed
+ * @old_rate: previous rate of this clock
+ * @new_rate: new rate of this clock
+ *
+ * For a pre-notifier, old_rate is the clock's rate before this rate
+ * change, and new_rate is what the rate will be in the future.  For a
+ * post-notifier, old_rate and new_rate are both set to the clock's
+ * current rate (this was done to optimize the implementation).
+ */
+struct clk_notifier_data {
+       struct clk              *clk;
+       unsigned long           old_rate;
+       unsigned long           new_rate;
+};
+
+/*
+ * Clk notifier callback types
+ *
+ * Since the notifier is called with interrupts disabled, any actions
+ * taken by callbacks must be extremely fast and lightweight.
+ *
+ * CLK_PRE_RATE_CHANGE - called after all callbacks have approved the
+ *     rate change, immediately before the clock rate is changed, to
+ *     indicate that the rate change will proceed.  Drivers must
+ *     immediately terminate any operations that will be affected by
+ *     the rate change.  Callbacks must always return NOTIFY_DONE.
+ *
+ * CLK_ABORT_RATE_CHANGE: called if the rate change failed for some
+ *     reason after CLK_PRE_RATE_CHANGE.  In this case, all registered
+ *     notifiers on the clock will be called with
+ *     CLK_ABORT_RATE_CHANGE. Callbacks must always return
+ *     NOTIFY_DONE.
+ *
+ * CLK_POST_RATE_CHANGE - called after the clock rate change has
+ *     successfully completed.  Callbacks must always return
+ *     NOTIFY_DONE.
+ *
+ */
+#define CLK_PRE_RATE_CHANGE            1
+#define CLK_POST_RATE_CHANGE           2
+#define CLK_ABORT_RATE_CHANGE          3
+
+#define CLK_PRE_ENABLE                 4
+#define CLK_POST_ENABLE                        5
+#define CLK_ABORT_ENABLE               6
+
+#define CLK_PRE_DISABLE                        7
+#define CLK_POST_DISABLE               8
+#define CLK_ABORT_DISABLE              9
+
+struct notifier_block;
+
+extern int clk_notifier_register(struct clk *clk, struct notifier_block *nb);
+extern int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
+
+#endif
+
+
+
+
+
index 89b0a0cabd4a9a3071835429260060f7203a59fa..1797efab27c7a9b38428647b79589c11f68a56c2 100644 (file)
@@ -35,6 +35,7 @@ enum rk_plls_id {
 /********************************************************************/
 
 #define CRU_W_MSK(bits_shift, msk)     ((msk) << ((bits_shift) + 16))
+#define CRU_SET_VAL_BITS(val,bits_shift,msk) (((msk)<<((bits_shift)+16))|(val)) 
 
 /*******************PLL CON0 BITS***************************/
 
@@ -45,7 +46,7 @@ enum rk_plls_id {
        ((((reg) >> (shift)) & (msk)) + 1)
 
 #define PLL_OD_MSK             (0xf)
-#define PLL_OD_SHIFT           (0)
+#define PLL_OD_SHIFT (0x0)
 
 #define PLL_CLKOD(val)         PLL_CLKFACTOR_SET(val, PLL_OD_SHIFT, PLL_OD_MSK)
 #define PLL_NO(reg)            PLL_CLKFACTOR_GET(reg, PLL_OD_SHIFT, PLL_OD_MSK)
@@ -135,11 +136,21 @@ enum rk_plls_id {
 
 /*******************MODE BITS***************************/
 
-#define PLL_MODE_W_MSK(id)     (0x3 << (16 + (id) * 4))
 #define PLL_MODE_MSK(id)       (0x3 << ((id) * 4))
-#define PLL_MODE_SLOW(id)      (0x0 << ((id) * 4))
-#define PLL_MODE_NORM(id)      (0x1 << ((id) * 4))
-#define PLL_MODE_DEEP(id)      (0x2 << ((id) * 4))
+#define PLL_MODE_SLOW(id)      ((0x0<<((id)*4))|(0x3<<(16+(id)*4)))
+#define PLL_MODE_NORM(id)      ((0x1<<((id)*4))|(0x3<<(16+(id)*4)))
+#define PLL_MODE_DEEP(id)      ((0x2<<((id)*4))|(0x3<<(16+(id)*4)))
+
+/*******************clksel10***************************/
+
+#define PERI_ACLK_DIV_MASK 0x1f
+#define PERI_ACLK_DIV_OFF 0
+
+#define PERI_HCLK_DIV_MASK 0x3
+#define PERI_HCLK_DIV_OFF 8
+
+#define PERI_PCLK_DIV_MASK 0x3
+#define PERI_PCLK_DIV_OFF 12
 
 /*******************gate BITS***************************/
 
diff --git a/arch/arm/mach-rk30/include/mach/dvfs.h b/arch/arm/mach-rk30/include/mach/dvfs.h
new file mode 100644 (file)
index 0000000..92cef0e
--- /dev/null
@@ -0,0 +1,102 @@
+/* arch/arm/mach-rk30/rk30_dvfs.h\r
+ *\r
+ * Copyright (C) 2012 ROCKCHIP, Inc.\r
+ *\r
+ * This software is licensed under the terms of the GNU General Public\r
+ * License version 2, as published by the Free Software Foundation, and\r
+ * may be copied, distributed, and modified under those terms.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ */\r
+#ifndef _RK30_DVFS_H_\r
+#define _RK30_DVFS_H_\r
+\r
+typedef int (*vd_dvfs_target_callback)(struct clk *clk, unsigned long rate);\r
+\r
+typedef int (*dvfs_set_rate_callback)(struct clk *clk, unsigned long rate);\r
+\r
+typedef int (*clk_dvfs_target_callback)(struct clk *clk, unsigned long rate,\r
+                                        dvfs_set_rate_callback set_rate);\r
+\r
+/**\r
+ * struct vd_node:     To Store All Voltage Domains' info\r
+ * @vd_name:   Voltage Domain's Name\r
+ * @cur_volt:  Voltage Domain's Current Voltage\r
+ * @vd_list:   Point of he Voltage Domain List Node\r
+ * @pd_list:   Head of Power Domain List Belongs to This Voltage Domain\r
+ * @vd_voltreq_list:   Head of Voltage Request List for Voltage Domain\r
+ */\r
+\r
+struct vd_node {\r
+       char    *name;\r
+       char    *regulator_name;\r
+       int             cur_volt;\r
+       struct regulator        *regulator;\r
+       struct mutex            dvfs_mutex;\r
+       struct list_head        node;\r
+       struct list_head        pd_list;\r
+       vd_dvfs_target_callback vd_dvfs_target;\r
+};\r
+struct vd_node_lookup {\r
+       struct vd_node  *vd;\r
+       char                    *regulator_name;\r
+};\r
+/**\r
+ * struct pd_node:     To Store All Power Domains' info per Voltage Domain\r
+ * @pd_name:   Power Domain's Name\r
+ * @cur_volt:  Power Domain's Current Voltage\r
+ * @pd_list:   Point of the Power Domain List Node\r
+ * @clk_list:  Head of Power Domain's Clocks List\r
+ * @pd_status:         If The Power Domain On: 1 means on, 0 means off\r
+ */\r
+struct pd_node {\r
+       char    *name;\r
+       int             cur_volt;\r
+       unsigned char   pd_status;\r
+       struct vd_node  *vd;\r
+       struct  clk             *pd_clk;\r
+       struct list_head        node;\r
+       struct list_head        clk_list;\r
+};\r
+\r
+struct pd_node_lookup {\r
+       struct pd_node* pd;\r
+};\r
+\r
+struct clk_list{\r
+       struct clk_node *dvfs_clk;\r
+       struct list_head node;\r
+};\r
+\r
+struct pds_list {\r
+       struct clk_list clk_list;\r
+       struct pd_node *pd;\r
+};\r
+\r
+struct clk_node {\r
+       char    *name;\r
+       int             cur_freq;\r
+       int             cur_volt;\r
+       int             enable_dvfs;\r
+       struct pds_list         *pds;\r
+       struct vd_node          *vd;\r
+       struct cpufreq_frequency_table  *dvfs_table;\r
+       struct notifier_block *dvfs_nb;\r
+       struct list_head        node;\r
+       clk_dvfs_target_callback clk_dvfs_target;\r
+};\r
+\r
+int rk30_dvfs_init(void);\r
+int is_support_dvfs(struct clk_node *dvfs_info);\r
+int dvfs_set_rate(struct clk *clk, unsigned long rate);\r
+void clk_set_dvfs_target_rate_callback(struct clk *ck, clk_dvfs_target_callback clk_dvfs_target);\r
+int clk_enable_dvfs(struct clk *clk);\r
+int clk_disable_dvfs(struct clk *clk);\r
+int cpufreq_dvfs_init(struct clk *ck, struct cpufreq_frequency_table **table, clk_dvfs_target_callback clk_dvfs_target);\r
+int clk_dvfs_set_dvfs_table(struct clk *clk,struct cpufreq_frequency_table *table);\r
+\r
+#endif\r
old mode 100644 (file)
new mode 100755 (executable)
index 23ab6ef..422eb8c
@@ -203,6 +203,8 @@ static void __init rk30_sched_clock_init(void)
 
 static void __init rk30_timer_init(void)
 {
+
+printk("rk30_timer_init\n");
 #ifdef CONFIG_HAVE_ARM_TWD
        twd_base = RK30_PTIMER_BASE;
 #endif