From fa827cb99589c0727824c7987771bef0bfe5e496 Mon Sep 17 00:00:00 2001 From: xxx Date: Fri, 23 Mar 2012 04:12:30 -0700 Subject: [PATCH] rk30: add clock support --- arch/arm/mach-rk30/Makefile | 2 + arch/arm/mach-rk30/board-rk30-sdk.c | 5 + arch/arm/mach-rk30/clock.c | 742 +++++- arch/arm/mach-rk30/clock.h | 98 + arch/arm/mach-rk30/clock_data.c | 3248 +++++++++++++++++++++++ arch/arm/mach-rk30/common.c | 2 +- arch/arm/mach-rk30/dvfs.c | 942 +++++++ arch/arm/mach-rk30/include/mach/board.h | 29 +- arch/arm/mach-rk30/include/mach/clock.h | 81 + arch/arm/mach-rk30/include/mach/cru.h | 21 +- arch/arm/mach-rk30/include/mach/dvfs.h | 102 + arch/arm/mach-rk30/timer.c | 2 + 12 files changed, 5205 insertions(+), 69 deletions(-) mode change 100644 => 100755 arch/arm/mach-rk30/clock.c create mode 100644 arch/arm/mach-rk30/clock.h create mode 100644 arch/arm/mach-rk30/clock_data.c create mode 100644 arch/arm/mach-rk30/dvfs.c create mode 100755 arch/arm/mach-rk30/include/mach/clock.h create mode 100644 arch/arm/mach-rk30/include/mach/dvfs.h mode change 100644 => 100755 arch/arm/mach-rk30/timer.c diff --git a/arch/arm/mach-rk30/Makefile b/arch/arm/mach-rk30/Makefile index c29cf061c864..129cfa1de19f 100644 --- a/arch/arm/mach-rk30/Makefile +++ b/arch/arm/mach-rk30/Makefile @@ -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 diff --git a/arch/arm/mach-rk30/board-rk30-sdk.c b/arch/arm/mach-rk30/board-rk30-sdk.c index 61440c89b8a9..e7684991f4d6 100755 --- a/arch/arm/mach-rk30/board-rk30-sdk.c +++ b/arch/arm/mach-rk30/board-rk30-sdk.c @@ -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, diff --git a/arch/arm/mach-rk30/clock.c b/arch/arm/mach-rk30/clock.c old mode 100644 new mode 100755 index 06dce19f3018..93a729d5bd4b --- a/arch/arm/mach-rk30/clock.c +++ b/arch/arm/mach-rk30/clock.c @@ -1,111 +1,729 @@ +/* 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 #include #include #include #include +#include #include +#include +#include +#include +#include "clock.h" +#include +#include -#include +#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 index 000000000000..7bf2696eaa48 --- /dev/null +++ b/arch/arm/mach-rk30/clock.h @@ -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 +struct clk *rk30_clk_get(struct device *dev, const char *con_id); +#endif + +#ifdef CONFIG_PROC_FS +#include +#include + +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 index 000000000000..172cc16bd941 --- /dev/null +++ b/arch/arm/mach-rk30/clock_data.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clock.h" + +#ifndef RK30_CLK_OFFBOARD_TEST +#include +#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 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]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 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(frefPLL_FREF_MAX) + continue; + + fvco=(fin_hz/nr)*nf; + if(fvcoPLL_FVCO_MAX) + continue; + fout=fvco/no; + if(foutPLL_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;iparent; +} + +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 + + diff --git a/arch/arm/mach-rk30/common.c b/arch/arm/mach-rk30/common.c index 33d5bb406212..38ab1e3ff4b5 100755 --- a/arch/arm/mach-rk30/common.c +++ b/arch/arm/mach-rk30/common.c @@ -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 index 000000000000..6283fda952b3 --- /dev/null +++ b/arch/arm/mach-rk30/dvfs.c @@ -0,0 +1,942 @@ +/* arch/arm/mach-rk30/rk30_dvfs.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 +#include +#include +#include +#include +#include +#include + +#include "clock.h" +#include +#include + +#if 0 +#define DVFS_DBG(fmt, args...) pr_debug(fmt, ##args) +#define DVFS_ERR(fmt, args...) pr_err(fmt, ##args) +#else +#define DEBUG_RK30_DVFS +#define DVFS_DBG(fmt, args...) printk(fmt, ##args) +#define DVFS_ERR(fmt, args...) printk(fmt, ##args) +#endif + +#ifndef CONFIG_ARCH_RK30 +#define DVFS_TEST_OFF_BOARD +#endif + + + +#ifdef DVFS_TEST_OFF_BOARD +/* Just for simulation */ + +struct regulator { + int min_uV; +}; +#if 0 +static void test_regulator_put(struct regulator *regulator) +{ + kfree(regulator); +} +#endif +struct regulator regulators[100]; +static struct regulator *test_regulator_get(struct device *dev, const char *id) { + static int ret_cnt = 0; + return ®ulators[ret_cnt++]; +} + +static int test_regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) +{ + regulator->min_uV = min_uV; + return 0; +} + +static int test_regulator_get_voltage(struct regulator *regulator) +{ + return regulator->min_uV; +} + +int rk30_clk_set_rate(struct clk *clk, unsigned long rate); +static void dump_dbg_map(void); +int rk30_dvfs_init_test(void); +int rk30_clk_enable(struct clk *clk); +int rk30_clk_disable(struct clk *clk); + +#define dvfs_regulator_get(dev,id) test_regulator_get((dev),(id)) +#define dvfs_regulator_put(regu) test_regulator_get((regu)) +#define dvfs_regulator_set_voltage(regu,min_uV,max_uV) test_regulator_set_voltage((regu),(min_uV),(max_uV)) +#define dvfs_regulator_get_voltage(regu) test_regulator_get_voltage((regu)) + +/* clock */ +#define dvfs_clk_get(a,b) rk30_clk_get((a),(b)) +#define dvfs_clk_set_rate(a,b) rk30_clk_set_rate((a),(b)) +#define dvfs_clk_enable(a) rk30_clk_enable((a)) +#define dvfs_clk_disable(a) rk30_clk_disable((a)) + +#else +/* board runing */ +#include + +#define dvfs_regulator_get(dev,id) regulator_get((dev),(id)) +#define dvfs_regulator_put(regu) regulator_get((regu)) +#define dvfs_regulator_set_voltage(regu,min_uV,max_uV) regulator_set_voltage((regu),(min_uV),(max_uV)) +#define dvfs_regulator_get_voltage(regu) regulator_get_voltage((regu)) + +#define dvfs_clk_get(a,b) clk_get((a),(b)) +#define dvfs_clk_set_rate(a,b) clk_set_rate((a),(b)) +#define dvfs_clk_enable(a) clk_enable((a)) +#define dvfs_clk_disable(a) clk_disable((a)) +#endif + + +static LIST_HEAD(rk_dvfs_tree); +static DEFINE_MUTEX(mutex); +/* +int dvfs_target_core(struct clk *clk, unsigned int rate); +int dvfs_target(struct clk *clk, unsigned int rate); +int dvfs_clk_set_rate(struct clk *clk, unsigned long rate); +*/ +extern int rk30_clk_notifier_register(struct clk *clk, struct notifier_block *nb); +extern int rk30_clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); + +#define FV_TABLE_END 0 +#define PD_ON 1 +#define PD_OFF 0 + + +static void dvfs_clk_scale_volt(struct clk_node *dvfs_clk, unsigned int volt); +static int dvfs_clk_get_volt(struct clk_node *dvfs_clk, unsigned long rate, + struct cpufreq_frequency_table *clk_fv); + +/** + * **************************FUNCTIONS*********************************** + */ + +#ifdef DEBUG_RK30_DVFS +/** + * dump_dbg_map() : Draw all informations of dvfs while debug + */ +static void dump_dbg_map(void) +{ + int i; + struct vd_node *vd; + struct pd_node *pd, *clkparent; + struct clk_list *child; + struct clk_node *dvfs_clk; + + DVFS_DBG("-------------DVFS DEBUG-----------\n\n\n"); + DVFS_DBG("RK30 DVFS TREE:\n"); + list_for_each_entry(vd, &rk_dvfs_tree, node) { + DVFS_DBG("|\n|- voltage domain:%s\n", vd->name); + DVFS_DBG("|- current voltage:%d\n", vd->cur_volt); + + list_for_each_entry(pd, &vd->pd_list, node) { + DVFS_DBG("| |\n| |- power domain:%s, status = %s, current volt = %d\n", + pd->name, (pd->pd_status == PD_ON) ? "ON" : "OFF", pd->cur_volt); + + list_for_each_entry(child, &pd->clk_list, node) { + dvfs_clk = child->dvfs_clk; + DVFS_DBG("| | |\n| | |- clock: %s current: rate %d, volt = %d, enable_dvfs = %s\n", + dvfs_clk->name, dvfs_clk->cur_freq, dvfs_clk->cur_volt, dvfs_clk->enable_dvfs == 0 ? "DISABLE" : "ENABLE"); + for (i = 0; dvfs_clk->pds[i].pd != NULL; i++) { + clkparent = dvfs_clk->pds[i].pd; + DVFS_DBG("| | | |- clock parents: %s, vd_parent = %s\n", clkparent->name, clkparent->vd->name); + } + + for (i = 0; (dvfs_clk->dvfs_table[i].frequency != FV_TABLE_END); i++) { + DVFS_DBG("| | | |- freq = %d, volt = %d\n", dvfs_clk->dvfs_table[i].frequency, dvfs_clk->dvfs_table[i].index); + + } + } + } + } + DVFS_DBG("-------------DVFS DEBUG END------------\n"); +} +#endif + +int is_support_dvfs(struct clk_node *dvfs_info) +{ + return (dvfs_info->vd && dvfs_info->vd->vd_dvfs_target && dvfs_info->enable_dvfs); +} +static int rk_dvfs_clk_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct clk_notifier_data *noti_info; + struct clk *clk; + struct clk_node *dvfs_clk; + noti_info = (struct clk_notifier_data *)ptr; + clk = noti_info->clk; + dvfs_clk = clk->dvfs_info; + + switch (event) { + case CLK_PRE_RATE_CHANGE: + DVFS_DBG("%s CLK_PRE_RATE_CHANGE\n", __func__); + break; + case CLK_POST_RATE_CHANGE: + DVFS_DBG("%s CLK_POST_RATE_CHANGE\n", __func__); + break; + case CLK_ABORT_RATE_CHANGE: + DVFS_DBG("%s CLK_ABORT_RATE_CHANGE\n", __func__); + break; + case CLK_PRE_ENABLE: + DVFS_DBG("%s CLK_PRE_ENABLE\n", __func__); + break; + case CLK_POST_ENABLE: + DVFS_DBG("%s CLK_POST_ENABLE\n", __func__); + break; + case CLK_ABORT_ENABLE: + DVFS_DBG("%s CLK_ABORT_ENABLE\n", __func__); + break; + case CLK_PRE_DISABLE: + DVFS_DBG("%s CLK_PRE_DISABLE\n", __func__); + break; + case CLK_POST_DISABLE: + DVFS_DBG("%s CLK_POST_DISABLE\n", __func__); + dvfs_clk->cur_freq = 0; + dvfs_clk_scale_volt(dvfs_clk, 0); + break; + case CLK_ABORT_DISABLE: + DVFS_DBG("%s CLK_ABORT_DISABLE\n", __func__); + + break; + default: + break; + } + return 0; +} +static struct notifier_block rk_dvfs_clk_notifier = { + .notifier_call = rk_dvfs_clk_notifier_event, +}; +int clk_disable_dvfs(struct clk *clk) +{ + struct clk_node *dvfs_clk; + dvfs_clk = clk->dvfs_info; + if(dvfs_clk->enable_dvfs - 1 < 0) { + DVFS_ERR("clk is already closed!\n"); + return -1; + } else { + DVFS_ERR("clk is disable now!\n"); + dvfs_clk->enable_dvfs--; + if(0 == dvfs_clk->enable_dvfs) { + DVFS_ERR("clk closed!\n"); + rk30_clk_notifier_unregister(clk, dvfs_clk->dvfs_nb); + DVFS_ERR("clk unregister nb!\n"); + dvfs_clk_scale_volt(dvfs_clk, 0); + } + } + dump_dbg_map(); + return 0; +} + +int clk_enable_dvfs(struct clk *clk) +{ + struct regulator *regulator; + struct clk_node *dvfs_clk; + struct cpufreq_frequency_table clk_fv; + + if(!clk->dvfs_info) { + DVFS_ERR("This clk(%s) not support dvfs!\n", clk->name); + return -1; + } + + dvfs_clk = clk->dvfs_info; + DVFS_ERR("dvfs clk enable dvfs %s\n", dvfs_clk->name); + if(0 == dvfs_clk->enable_dvfs) { + dvfs_clk->enable_dvfs++; + if(!dvfs_clk->vd->regulator) { + regulator = dvfs_regulator_get(NULL, dvfs_clk->vd->regulator_name); + if(regulator) + dvfs_clk->vd->regulator = regulator; + else + dvfs_clk->vd->regulator = NULL; + } + if(dvfs_clk->dvfs_nb) { + // must unregister when clk disable + rk30_clk_notifier_register(clk, dvfs_clk->dvfs_nb); + } + + if(!clk || IS_ERR(clk)) { + DVFS_ERR("%s get clk %s error\n", __func__, dvfs_clk->name); + return -1; + } + //DVFS_DBG("%s get clk %s rate = %lu\n", __func__, clk->name, clk->rate); + if(dvfs_clk->cur_freq == 0) + dvfs_clk_get_volt(dvfs_clk, clk->rate, &clk_fv); + else + dvfs_clk_get_volt(dvfs_clk, dvfs_clk->cur_freq, &clk_fv); + dvfs_clk->cur_volt = clk_fv.index; + dvfs_clk->cur_freq = clk_fv.frequency; + dvfs_clk_scale_volt(dvfs_clk, dvfs_clk->cur_volt); + dump_dbg_map(); + + } else { + DVFS_ERR("dvfs already enable clk enable = %d!\n", dvfs_clk->enable_dvfs); + dvfs_clk->enable_dvfs++; + } + return 0; +} + +int dvfs_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = 0; + struct vd_node *vd; + DVFS_DBG("%s dvfs start\n", clk->name); + if(!clk->dvfs_info) { + DVFS_ERR("%s :This clk do not support dvfs!\n", __func__); + ret = -1; + } else { + vd = clk->dvfs_info->vd; + mutex_lock(&vd->dvfs_mutex); + ret = vd->vd_dvfs_target(clk, rate); + mutex_unlock(&vd->dvfs_mutex); + } + return ret; +} + +/** + * get correspond voltage khz + */ +static int dvfs_clk_get_volt(struct clk_node *dvfs_clk, unsigned long rate, + struct cpufreq_frequency_table *clk_fv) +{ + int i = 0; + if (rate == 0) { + /* since no need*/ + return -1; + } + clk_fv->frequency = rate; + clk_fv->index = 0; + for(i = 0; (dvfs_clk->dvfs_table[i].frequency != FV_TABLE_END); i++) { + if(dvfs_clk->dvfs_table[i].frequency >= rate) { + clk_fv->frequency = dvfs_clk->dvfs_table[i].frequency; + clk_fv->index = dvfs_clk->dvfs_table[i].index; + DVFS_DBG("%s dvfs_clk_get_volt rate=%u hz ref vol=%d uV\n", dvfs_clk->name, clk_fv->frequency, clk_fv->index); + return 0; + } + } + clk_fv->frequency = 0; + clk_fv->index = 0; + DVFS_ERR("%s get corresponding voltage error! out of bound\n", dvfs_clk->name); + return -1; +} + +static int dvfs_clk_round_volt(struct clk_node *dvfs_clk, int volt) +{ + struct pd_node *pd; + struct clk_node *dvfs_clk_tmp; + int volt_max = 0; + int i; + + for(i = 0; (dvfs_clk->pds[i].pd != NULL); i++) { + pd = dvfs_clk->pds[i].pd; + if(volt > pd->cur_volt) { + /** + * if dvfs_clk parent power domain's voltage is smaller then + * this dvfs_clk's voltage ignore this power domain + */ + volt_max = max(volt_max, volt); + continue; + } + list_for_each_entry(dvfs_clk_tmp, &pd->clk_list, node) { + /** + * found the max voltage uninclude dvfs_clk + */ + if(dvfs_clk_tmp != dvfs_clk) { + volt_max = max(volt_max, dvfs_clk_tmp->cur_volt); + } + } + } + + volt_max = max(volt_max, volt); + return volt_max; +} + +static void dvfs_clk_scale_volt(struct clk_node *dvfs_clk, unsigned int volt) +{ + struct vd_node *vd; + struct pd_node *pd; + struct clk_list *child; + struct clk_node *dvfs_clk_tmp; + int volt_max_vd = 0, volt_max_pd = 0, i; + + dvfs_clk->cur_volt = volt;//set clk node volt + vd = dvfs_clk->vd;// vd + for(i = 0; (dvfs_clk->pds[i].pd != NULL); i++) { + pd = dvfs_clk->pds[i].pd; + volt_max_pd = 0; + /** + * set corresponding voltage, clk do not need to set voltage,just for + * powerdomain + */ + + if(volt > pd->cur_volt) { + pd->cur_volt = volt; + pd->pd_status = (pd->cur_volt == 0) ? PD_OFF : PD_ON; + continue; + } + + /* set power domain voltage */ + list_for_each_entry(child, &pd->clk_list, node) { + dvfs_clk_tmp = child->dvfs_clk; + if(dvfs_clk_tmp->enable_dvfs){ + volt_max_pd = max(volt_max_pd, dvfs_clk_tmp->cur_volt); + } + } + pd->cur_volt = volt_max_pd; + + pd->pd_status = (volt_max_pd == 0) ? PD_OFF : PD_ON; + } + + /* set voltage domain voltage */ + volt_max_vd = 0; + list_for_each_entry(pd, &vd->pd_list, node) { + volt_max_vd = max(volt_max_vd, pd->cur_volt); + } + vd->cur_volt = volt_max_vd; +} + +int dvfs_target_set_rate_core(struct clk *clk, unsigned long rate) +{ + struct clk_node *dvfs_clk; + int volt_new = 0, volt_old = 0; + struct cpufreq_frequency_table clk_fv; + int ret = 0; + dvfs_clk = clk_get_dvfs_info(clk); + + DVFS_ERR("%s get clk %s\n", __func__, clk->name); + if(dvfs_clk->vd->regulator == NULL) { + DVFS_ERR("%s can't get dvfs regulater\n", clk->name); + return -1; + } + + /* If power domain off do scale in the notify function */ + /* + if (rate == 0) { + dvfs_clk->cur_freq = 0; + dvfs_clk_scale_volt(dvfs_clk, 0); + return 0; + } + */ + /* need round rate */ + DVFS_ERR("%s going to round rate = %lu\n", clk->name, rate); + rate = clk_round_rate_nolock(clk, rate); + DVFS_ERR("%s round get rate = %lu\n", clk->name, rate); + /* find the clk corresponding voltage */ + if (0 != dvfs_clk_get_volt(dvfs_clk, rate, &clk_fv)) { + DVFS_ERR("%s rate %lukhz is larger,not support\n", clk->name, rate); + return -1; + } + volt_old = dvfs_clk->vd->cur_volt; + volt_new = clk_fv.index; + + DVFS_DBG("vol_new = %d mV(was %d mV)\n", volt_new, volt_old); + + /* if up the voltage*/ + if (volt_old < volt_new) { + if(dvfs_regulator_set_voltage(dvfs_clk->vd->regulator, volt_new, volt_new) < 0) { + DVFS_ERR("set voltage err\n"); + + return -1; + } + dvfs_clk->vd->cur_volt = volt_new; + /* CPU do not use power domain, so save scale times */ + //dvfs_clk_scale_volt(dvfs_clk, clk_fv.index); + } + + if(dvfs_clk->clk_dvfs_target) { + ret = dvfs_clk->clk_dvfs_target(clk, rate, clk_set_rate_locked); + } else { + ret = clk_set_rate_locked(clk, rate); + } + if (ret < 0) { + DVFS_ERR("set rate err\n"); + return -1; + } + dvfs_clk->cur_freq = rate; + dvfs_clk->cur_volt = volt_new; + + /* if down the voltage */ + if (volt_old > volt_new) { + if(dvfs_regulator_set_voltage(dvfs_clk->vd->regulator, volt_new, volt_new) < 0) { + DVFS_ERR("set voltage err\n"); + + return -1; + } + dvfs_clk->vd->cur_volt = volt_new; + /* CPU do not use power domain, so save scale times */ + //dvfs_clk_scale_volt(dvfs_clk, clk_fv.index); + } + + return ret; +} + +int dvfs_target_set_rate_normal(struct clk *clk, unsigned long rate) +{ + struct clk_node *dvfs_clk; + unsigned int volt_new = 0, volt_old = 0; + struct cpufreq_frequency_table clk_fv = {0, 0}; + int ret = 0; + + dvfs_clk = clk_get_dvfs_info(clk); + DVFS_ERR("%s get clk %s\n", __func__, clk->name); + if(dvfs_clk->vd->regulator == NULL) { + DVFS_DBG("%s can't get dvfs regulater\n", clk->name); + return -1; + } + + /* need round rate */ + DVFS_ERR("%s going to round rate = %lu\n", clk->name, rate); + rate = clk_round_rate_nolock(clk, rate); + DVFS_ERR("%s round get rate = %lu\n", clk->name, rate); + /* find the clk corresponding voltage */ + if (dvfs_clk_get_volt(dvfs_clk, rate, &clk_fv)) { + DVFS_DBG("dvfs_clk_get_volt:rate = Get corresponding voltage error!\n"); + return -1; + } + + volt_old = dvfs_clk->vd->cur_volt; + volt_new = dvfs_clk_round_volt(dvfs_clk, clk_fv.index); + + // if up the voltage + if (volt_old < volt_new) { + if(dvfs_regulator_set_voltage(dvfs_clk->vd->regulator, volt_new, volt_new) < 0) { + DVFS_DBG("set voltage err\n"); + return -1; + } + dvfs_clk_scale_volt(dvfs_clk, clk_fv.index); + } + + if(dvfs_clk->clk_dvfs_target) { + ret = dvfs_clk->clk_dvfs_target(clk, rate, clk_set_rate_locked); + } else { + ret = clk_set_rate_locked(clk, rate); + } + if (ret < 0) { + DVFS_ERR("set rate err\n"); + return -1; + } + dvfs_clk->cur_freq = rate; + dvfs_clk->cur_volt = volt_new; + + // if down the voltage + if (volt_old > volt_new) { + if(dvfs_regulator_set_voltage(dvfs_clk->vd->regulator, volt_new, volt_new) < 0) { + DVFS_DBG("set voltage err\n"); + return -1; + + } + dvfs_clk_scale_volt(dvfs_clk, clk_fv.index); + } + + return 0; +} + + +/*****************************init**************************/ +/** + * rate must be raising sequence + */ + +struct cpufreq_frequency_table cpu_dvfs_table[] = { + {.frequency = 126000000, .index = 800000}, + {.frequency = 252000000, .index = 850000}, + {.frequency = 504000000, .index = 900000}, + {.frequency = 816000000, .index = 1050000}, + {.frequency = 1008000000, .index = 1100000}, + {.frequency = 1200000000, .index = 1200000}, + {.frequency = FV_TABLE_END}, +}; +struct cpufreq_frequency_table ddr_dvfs_table[] = { + {.frequency = 24000000, .index = 600000}, + {.frequency = 64000000, .index = 700000}, + {.frequency = 126000000, .index = 800000}, + {.frequency = 252000000, .index = 850000}, + {.frequency = 504000000, .index = 900000}, + {.frequency = FV_TABLE_END}, +}; +struct cpufreq_frequency_table gpu_dvfs_table[] = { + {.frequency = 64000000, .index = 700000}, + {.frequency = 126000000, .index = 800000}, + {.frequency = 360000000, .index = 850000}, + {.frequency = FV_TABLE_END}, +}; + +static struct vd_node vd_cpu = { + .name = "vd_cpu", + .vd_dvfs_target = dvfs_target_set_rate_core, +}; + +static struct vd_node vd_core = { + .name = "vd_core", + .vd_dvfs_target = dvfs_target_set_rate_normal, +}; + +static struct vd_node vd_rtc = { + .name = "vd_rtc", + .vd_dvfs_target = NULL, +}; + +#define LOOKUP_VD(_pvd, _regulator_name) \ +{ \ + .vd = _pvd, \ + .regulator_name = _regulator_name, \ +} +static struct vd_node_lookup rk30_vds[] = { + LOOKUP_VD(&vd_cpu, "cpu"), + LOOKUP_VD(&vd_core, "core"), + LOOKUP_VD(&vd_rtc, "rtc"), +}; + +static struct pd_node pd_a9_0 = { + .name = "pd_a9_0", + .vd = &vd_cpu, +}; +static struct pd_node pd_a9_1 = { + .name = "pd_a9_1", + .vd = &vd_cpu, +}; +static struct pd_node pd_debug = { + .name = "pd_debug", + .vd = &vd_cpu, +}; +static struct pd_node pd_scu = { + .name = "pd_scu", + .vd = &vd_cpu, +}; +static struct pd_node pd_video = { + .name = "pd_video", + .vd = &vd_core, +}; +static struct pd_node pd_vio = { + .name = "pd_vio", + .vd = &vd_core, +}; +static struct pd_node pd_gpu = { + .name = "pd_gpu", + .vd = &vd_core, +}; +static struct pd_node pd_peri = { + .name = "pd_peri", + .vd = &vd_core, +}; +static struct pd_node pd_cpu = { + .name = "pd_cpu", + .vd = &vd_core, +}; +static struct pd_node pd_alive = { + .name = "pd_alive", + .vd = &vd_core, +}; +static struct pd_node pd_rtc = { + .name = "pd_rtc", + .vd = &vd_rtc, +}; +#define LOOKUP_PD(_ppd) \ +{ \ + .pd = _ppd, \ +} +static struct pd_node_lookup rk30_pds[] = { + LOOKUP_PD(&pd_a9_0), + LOOKUP_PD(&pd_a9_1), + LOOKUP_PD(&pd_debug), + LOOKUP_PD(&pd_scu), + LOOKUP_PD(&pd_video), + LOOKUP_PD(&pd_vio), + LOOKUP_PD(&pd_gpu), + LOOKUP_PD(&pd_peri), + LOOKUP_PD(&pd_cpu), + LOOKUP_PD(&pd_alive), + LOOKUP_PD(&pd_rtc), +}; + +#define CLK_PDS(_ppd) \ +{ \ + .pd = _ppd, \ +} + +static struct pds_list cpu_pds[] = { + CLK_PDS(&pd_a9_0), + CLK_PDS(&pd_a9_1), + CLK_PDS(NULL), +}; +static struct pds_list ddr_pds[] = { + CLK_PDS(&pd_cpu), + CLK_PDS(NULL), +}; +static struct pds_list gpu_pds[] = { + CLK_PDS(&pd_gpu), + CLK_PDS(NULL), +}; + +#define RK_CLKS(_clk_name, _ppds, _dvfs_table, _dvfs_nb) \ +{ \ + .name = _clk_name, \ + .pds = _ppds, \ + .dvfs_table = _dvfs_table, \ + .dvfs_nb = _dvfs_nb, \ +} +static struct clk_node rk30_clks[] = { + RK_CLKS("cpu", cpu_pds, cpu_dvfs_table, &rk_dvfs_clk_notifier), + RK_CLKS("ddr", ddr_pds, ddr_dvfs_table, &rk_dvfs_clk_notifier), + RK_CLKS("gpu", gpu_pds, gpu_dvfs_table, &rk_dvfs_clk_notifier), +}; +/** + * first scale regulator volt + */ +static int rk_dvfs_check_regulator_volt(void) +{ + struct vd_node *vd; + struct pd_node *pd; + struct clk_list *child; + struct clk_node *dvfs_clk; + struct clk *clk; + struct cpufreq_frequency_table clk_fv; + unsigned int vmax_pd = 0, vmax_vd = 0; + + list_for_each_entry(vd, &rk_dvfs_tree, node) { + vmax_vd = 0; + list_for_each_entry(pd, &vd->pd_list, node) { + vmax_pd = 0; + list_for_each_entry(child, &pd->clk_list, node) { + + dvfs_clk = child->dvfs_clk; + clk = dvfs_clk_get(NULL, dvfs_clk->name); + if(!clk || IS_ERR(clk)) { + DVFS_ERR("%s get clk %s error\n", __func__, dvfs_clk->name); + continue; + } + //DVFS_DBG("%s get clk %s rate = %lu\n", __func__, clk->name, clk->rate); + dvfs_clk_get_volt(dvfs_clk, clk->rate, &clk_fv); + dvfs_clk->cur_volt = clk_fv.index; + dvfs_clk->cur_freq = clk_fv.frequency; + vmax_pd = max(vmax_pd, clk_fv.index); + pd->pd_status = (vmax_pd == 0) ? PD_OFF : PD_ON; + } + pd->cur_volt = vmax_pd; + vmax_vd = max(vmax_vd, vmax_pd); + } + + vd->cur_volt = vmax_vd; + //DVFS_DBG("%s check error: %d, %d\n", vd->name, vd->cur_volt, dvfs_regulator_get_voltage(vd->regulator)); + //if (vd->cur_volt != dvfs_regulator_get_voltage(vd->regulator)) { + // DVFS_ERR("%s default voltage domain value error!\n", vd->name); + //} + } + return 0; +} + +static int rk_regist_vd(struct vd_node_lookup *vd_lookup) +{ + struct vd_node *vd; + if(!vd_lookup) + return -1; + vd = vd_lookup->vd; + vd->regulator_name = vd_lookup->regulator_name; + + mutex_lock(&mutex); + + mutex_init(&vd->dvfs_mutex); + list_add(&vd->node, &rk_dvfs_tree); + INIT_LIST_HEAD(&vd->pd_list); + + mutex_unlock(&mutex); + + return 0; +} +static int rk_regist_pd(struct pd_node_lookup *pd_lookup) +{ + struct vd_node *vd; + struct pd_node *pd; + + mutex_lock(&mutex); + pd = pd_lookup->pd; + + list_for_each_entry(vd, &rk_dvfs_tree, node) { + if (vd == pd->vd) { + list_add(&pd->node, &vd->pd_list); + INIT_LIST_HEAD(&pd->clk_list); + break; + } + } + mutex_unlock(&mutex); + return 0; +} +//extern int rk30_clk_notifier_register(struct clk *clk, struct notifier_block *nb); + +static int rk_regist_clk(struct clk_node *dvfs_clk) +{ + struct pd_node *pd; + struct clk_list *child; + struct clk *clk; + int i = 0; + + if(!dvfs_clk) + return -1; + + if(!dvfs_clk->pds) + return -1; + + mutex_lock(&mutex); + // set clk unsupport dvfs + dvfs_clk->enable_dvfs = 0; + dvfs_clk->vd = dvfs_clk->pds[0].pd->vd; + for (i = 0; dvfs_clk->pds[i].pd != NULL; i++) { + child = &(dvfs_clk->pds[i].clk_list); + child->dvfs_clk = dvfs_clk; + pd = dvfs_clk->pds[i].pd; + list_add(&child->node, &pd->clk_list); + } + clk = dvfs_clk_get(NULL, dvfs_clk->name); + clk_register_dvfs(dvfs_clk, clk); + mutex_unlock(&mutex); + return 0; +} +int rk30_dvfs_init(void) +{ + int i = 0; + for (i = 0; i < ARRAY_SIZE(rk30_vds); i++) { + rk_regist_vd(&rk30_vds[i]); + } + for (i = 0; i < ARRAY_SIZE(rk30_pds); i++) { + rk_regist_pd(&rk30_pds[i]); + } + for (i = 0; i < ARRAY_SIZE(rk30_clks); i++) { + rk_regist_clk(&rk30_clks[i]); + } + dump_dbg_map(); + //DVFS_DBG("%s dvfs tree create finish!\n", __func__); + //rk_dvfs_check_regulator_volt(); + return 0; +} + +void dvfs_clk_set_rate_callback(struct clk *clk, clk_dvfs_target_callback clk_dvfs_target) +{ + struct clk_node *dvfs_clk = clk_get_dvfs_info(clk); + dvfs_clk->clk_dvfs_target = clk_dvfs_target; +} +// + +/* + *cpufreq_frequency_table->index for cpufreq is index + *cpufreq_frequency_table->index for dvfstable is volt + */ +int cpufreq_dvfs_init(struct clk *clk, struct cpufreq_frequency_table **table, clk_dvfs_target_callback clk_dvfs_target) +{ + + struct cpufreq_frequency_table *freq_table; + struct clk_node *info = clk_get_dvfs_info(clk); + struct cpufreq_frequency_table *dvfs_table;//dvfs volt freq table + int i = 0; + DVFS_DBG("%s clk name %s\n", __func__, clk->name); + if(!info) { + return -1; + } + dvfs_table = info->dvfs_table; + + if(!dvfs_table) { + return -1; + } + + /********************************count table num****************************/ + i = 0; + while(dvfs_table[i].frequency != FV_TABLE_END) { + //DVFS_DBG("dvfs_table1 %lu\n",dvfs_table[i].frequency); + i++; + } + + freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) * (i + 1), GFP_KERNEL); + //last freq is end tab + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; + + //set freq table + i = 0; + while(dvfs_table[i].frequency != FV_TABLE_END) { + freq_table[i].index = i; + freq_table[i].frequency = dvfs_table[i].frequency; + //DVFS_DBG("dvfs_table %d %lu\n",i,dvfs_table[i].frequency); + i++; + } + *table = &freq_table[0]; + dvfs_clk_set_rate_callback(clk, clk_dvfs_target); + return 0; +} + +int clk_dvfs_set_dvfs_table(struct clk *clk, struct cpufreq_frequency_table *table) +{ + struct clk_node *info = clk_get_dvfs_info(clk); + if(!table || !info) + return -1; + info->dvfs_table = table; + return 0; +} +/********************************simulation cases****************************/ + +#ifdef DVFS_TEST_OFF_BOARD +int rk30_dvfs_init_test(void) +{ + struct clk *clk1; + DVFS_DBG("********************************simulation cases****************************\n"); +#ifdef DEBUG_RK30_DVFS + DVFS_DBG("\n\n"); + dump_dbg_map(); +#endif + clk1 = dvfs_clk_get(NULL, "cpu"); + if (clk1) { + dvfs_clk_set_rate(clk1, 1008000000); + dump_dbg_map(); + dvfs_clk_set_rate(clk1, 816000000); + dump_dbg_map(); + dvfs_clk_set_rate(clk1, 0); + dump_dbg_map(); + dvfs_clk_set_rate(clk1, 1200000000); + dump_dbg_map(); + dvfs_clk_set_rate(clk1, 1009000000); + dump_dbg_map(); + dvfs_clk_set_rate(clk1, 1416000000); + dump_dbg_map(); + + } else { + DVFS_DBG("\t\t%s:\t can not find clk cpu\n", __func__); + } + + clk1 = dvfs_clk_get(NULL, "gpu"); + if (clk1) { + dvfs_clk_set_rate(clk1, 120000000); + dump_dbg_map(); + dvfs_clk_enable(clk1); + dvfs_clk_disable(clk1); + dump_dbg_map(); + } else { + DVFS_DBG("\t\t%s:\t can not find clk gpu\n", __func__); + dump_dbg_map(); + } + + clk1 = dvfs_clk_get(NULL, "arm_pll"); + if (clk1) { + dvfs_clk_set_rate(clk1, 24000000); + dump_dbg_map(); + } else { + DVFS_DBG("\t\t%s:\t can not find clk arm_pll\n", __func__); + } + + DVFS_DBG("********************************simulation cases end***************************\n"); + + return 0; + +} +#endif + diff --git a/arch/arm/mach-rk30/include/mach/board.h b/arch/arm/mach-rk30/include/mach/board.h index 551e5da66f27..c458257c9927 100755 --- a/arch/arm/mach-rk30/include/mach/board.h +++ b/arch/arm/mach-rk30/include/mach/board.h @@ -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 index 000000000000..a94d6c851e3d --- /dev/null +++ b/arch/arm/mach-rk30/include/mach/clock.h @@ -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 + + + + + diff --git a/arch/arm/mach-rk30/include/mach/cru.h b/arch/arm/mach-rk30/include/mach/cru.h index 89b0a0cabd4a..1797efab27c7 100644 --- a/arch/arm/mach-rk30/include/mach/cru.h +++ b/arch/arm/mach-rk30/include/mach/cru.h @@ -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 index 000000000000..92cef0e0ff00 --- /dev/null +++ b/arch/arm/mach-rk30/include/mach/dvfs.h @@ -0,0 +1,102 @@ +/* arch/arm/mach-rk30/rk30_dvfs.h + * + * 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. + * + */ +#ifndef _RK30_DVFS_H_ +#define _RK30_DVFS_H_ + +typedef int (*vd_dvfs_target_callback)(struct clk *clk, unsigned long rate); + +typedef int (*dvfs_set_rate_callback)(struct clk *clk, unsigned long rate); + +typedef int (*clk_dvfs_target_callback)(struct clk *clk, unsigned long rate, + dvfs_set_rate_callback set_rate); + +/** + * struct vd_node: To Store All Voltage Domains' info + * @vd_name: Voltage Domain's Name + * @cur_volt: Voltage Domain's Current Voltage + * @vd_list: Point of he Voltage Domain List Node + * @pd_list: Head of Power Domain List Belongs to This Voltage Domain + * @vd_voltreq_list: Head of Voltage Request List for Voltage Domain + */ + +struct vd_node { + char *name; + char *regulator_name; + int cur_volt; + struct regulator *regulator; + struct mutex dvfs_mutex; + struct list_head node; + struct list_head pd_list; + vd_dvfs_target_callback vd_dvfs_target; +}; +struct vd_node_lookup { + struct vd_node *vd; + char *regulator_name; +}; +/** + * struct pd_node: To Store All Power Domains' info per Voltage Domain + * @pd_name: Power Domain's Name + * @cur_volt: Power Domain's Current Voltage + * @pd_list: Point of the Power Domain List Node + * @clk_list: Head of Power Domain's Clocks List + * @pd_status: If The Power Domain On: 1 means on, 0 means off + */ +struct pd_node { + char *name; + int cur_volt; + unsigned char pd_status; + struct vd_node *vd; + struct clk *pd_clk; + struct list_head node; + struct list_head clk_list; +}; + +struct pd_node_lookup { + struct pd_node* pd; +}; + +struct clk_list{ + struct clk_node *dvfs_clk; + struct list_head node; +}; + +struct pds_list { + struct clk_list clk_list; + struct pd_node *pd; +}; + +struct clk_node { + char *name; + int cur_freq; + int cur_volt; + int enable_dvfs; + struct pds_list *pds; + struct vd_node *vd; + struct cpufreq_frequency_table *dvfs_table; + struct notifier_block *dvfs_nb; + struct list_head node; + clk_dvfs_target_callback clk_dvfs_target; +}; + +int rk30_dvfs_init(void); +int is_support_dvfs(struct clk_node *dvfs_info); +int dvfs_set_rate(struct clk *clk, unsigned long rate); +void clk_set_dvfs_target_rate_callback(struct clk *ck, clk_dvfs_target_callback clk_dvfs_target); +int clk_enable_dvfs(struct clk *clk); +int clk_disable_dvfs(struct clk *clk); +int cpufreq_dvfs_init(struct clk *ck, struct cpufreq_frequency_table **table, clk_dvfs_target_callback clk_dvfs_target); +int clk_dvfs_set_dvfs_table(struct clk *clk,struct cpufreq_frequency_table *table); + +#endif diff --git a/arch/arm/mach-rk30/timer.c b/arch/arm/mach-rk30/timer.c old mode 100644 new mode 100755 index 23ab6ef39a35..422eb8c466c7 --- a/arch/arm/mach-rk30/timer.c +++ b/arch/arm/mach-rk30/timer.c @@ -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 -- 2.34.1