From: chenxing Date: Mon, 6 Aug 2012 06:51:32 +0000 (+0800) Subject: rk2928:sdk: add clock support X-Git-Tag: firefly_0821_release~8912^2~52 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=0f93e2344c6c7b1c91880645be2035fa2bb4d625;p=firefly-linux-kernel-4.4.55.git rk2928:sdk: add clock support --- diff --git a/arch/arm/mach-rk2928/Makefile b/arch/arm/mach-rk2928/Makefile index a9df193cfc0a..68053947e830 100644 --- a/arch/arm/mach-rk2928/Makefile +++ b/arch/arm/mach-rk2928/Makefile @@ -4,6 +4,8 @@ obj-y += reset.o obj-y += timer.o obj-y += devices.o obj-y += iomux.o +obj-y += clock.o +obj-y += clock_data.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_MACH_RK2928_FPGA) += board-rk2928-fpga.o board-rk2928-fpga-key.o diff --git a/arch/arm/mach-rk2928/board-rk2928-sdk.c b/arch/arm/mach-rk2928/board-rk2928-sdk.c index b01d4d92868b..2a433f41c3e8 100755 --- a/arch/arm/mach-rk2928/board-rk2928-sdk.c +++ b/arch/arm/mach-rk2928/board-rk2928-sdk.c @@ -1189,81 +1189,10 @@ static void __init rk2928_reserve(void) board_mem_reserved(); } -#if 1 -#include - -struct clk { - const char *name; - unsigned long rate; -}; - -static struct clk xin24m = { - .name = "xin24m", - .rate = 24000000, -}; - -#define CLK(dev, con, ck) \ - { \ - .dev_id = dev, \ - .con_id = con, \ - .clk = ck, \ - } - -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("rk29xx_spim.0", "spi", &xin24m), - CLK("rk29xx_spim.1", "spi", &xin24m), - - CLK("rk_serial.0", "uart_div", &xin24m), - CLK("rk_serial.0", "uart_frac_div", &xin24m), - CLK("rk_serial.0", "uart", &xin24m), - CLK("rk_serial.0", "pclk_uart", &xin24m), - CLK("rk_serial.1", "uart_div", &xin24m), - CLK("rk_serial.1", "uart_frac_div", &xin24m), - CLK("rk_serial.1", "uart", &xin24m), - CLK("rk_serial.1", "pclk_uart", &xin24m), - CLK("rk_serial.2", "uart_div", &xin24m), - CLK("rk_serial.2", "uart_frac_div", &xin24m), - CLK("rk_serial.2", "uart", &xin24m), - CLK("rk_serial.2", "pclk_uart", &xin24m), - - CLK("rk29_i2s.0", "i2s_div", &xin24m), - CLK("rk29_i2s.0", "i2s_frac_div", &xin24m), - CLK("rk29_i2s.0", "i2s", &xin24m), - CLK("rk29_i2s.0", "hclk_i2s", &xin24m), - CLK(NULL, "pd_lcdc0", &xin24m), - CLK(NULL, "hclk_lcdc0", &xin24m), - CLK(NULL, "aclk_lcdc0", &xin24m), - CLK(NULL, "dclk_lcdc0", &xin24m), - - CLK(NULL, "pd_cif0", &xin24m), - CLK(NULL, "aclk_cif0", &xin24m), - CLK(NULL, "hclk_cif0", &xin24m), - CLK(NULL, "cif0_in", &xin24m), - CLK(NULL, "cif0_out", &xin24m), -}; - -void __init rk30_clock_init(void) -{ - struct clk_lookup *lk; - - for (lk = clks; lk < clks + ARRAY_SIZE(clks); lk++) { - clkdev_add(lk); - } -} - void __init board_clock_init(void) { - rk30_clock_init(); + rk2928_clock_data_init(periph_pll_default, codec_pll_default, RK30_CLOCKS_DEFAULT_FLAGS); } -#else -void __init board_clock_init(void) -{ -} -#endif MACHINE_START(RK2928, "RK2928board") diff --git a/arch/arm/mach-rk2928/clock.c b/arch/arm/mach-rk2928/clock.c new file mode 100755 index 000000000000..02530f220ad0 --- /dev/null +++ b/arch/arm/mach-rk2928/clock.c @@ -0,0 +1,754 @@ +/* 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 + +#define CLOCK_PRINTK_DBG(fmt, args...) pr_debug(fmt, ## args); +#define CLOCK_PRINTK_ERR(fmt, args...) pr_err(fmt, ## args); +#define CLOCK_PRINTK_LOG(fmt, args...) pr_debug(fmt, ## args); + +/* Clock flags */ +/* bit 0 is free */ +#define RATE_FIXED (1 << 1) /* Fixed clock rate */ +#define CONFIG_PARTICIPANT (1 << 10) /* Fundamental clock */ + +#define MHZ (1000*1000) +#define KHZ (1000) + +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; + list_for_each_entry(ck, &clocks, node) { + if (ck->usecount > 0 || ck->mode == NULL || (ck->flags & IS_PD)) + continue; + 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; + + 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){ + printk("clk has parent\n"); + list_add(&clk->sibling, &clk->parent->children); + } + else{ + printk("clk has no parent\n"); + list_add(&clk->sibling, &root_clks); + } + list_add(&clk->node, &clocks); + mutex_unlock(&clocks_mutex); + return 0; +} + +/************************************************************/ +static void __clk_recalc(struct clk *clk) +{ + 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; +} + +/* 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_nolock(struct clk *clk) +{ + int ret = 0; + + 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); + printk("%s enabled\n", clk->name); + } + clk->usecount++; + + return ret; +} + +void clk_disable_nolock(struct clk *clk) +{ + if (clk->usecount == 0) { + CLOCK_PRINTK_ERR(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) + CLOCK_PRINTK_ERR("clock: clk_round_rate called on fixed-rate clock %s\n", clk->name); + + return clk->rate; +} +int is_suport_round_rate(struct clk *clk) +{ + return (clk->round_rate) ? 0:(-1); +} + +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; + + printk("**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); + printk("**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; +} + +int clk_set_parent_nolock(struct clk *clk, struct clk *parent) +{ + int ret; + int enabled = clk->usecount > 0; + struct clk *old_parent = clk->parent; + + 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****************************************************/ +#if 0 +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; +} +#endif + +/*------------------------------------------------------------------------- + * 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 ret; + + LOCK(); + ret = clk_round_rate_nolock(clk, rate); + UNLOCK(); + + return ret; +} + +#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 +{ + 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)){ + return ret; + } + if (rate == clk->rate) + return 0; +#if 0 + if (clk->dvfs_info!=NULL&&is_support_dvfs(clk->dvfs_info)) + return dvfs_set_rate(clk, rate); +#endif + 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; +} +int clk_set_parent_force(struct clk *clk, struct clk *parent) +{ + 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(); + ret = clk_set_parent_nolock(clk, parent); + 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 +{ + if (clk == NULL || IS_ERR(clk)) { + return ERR_PTR(-EINVAL); + } + 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); + +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); + diff --git a/arch/arm/mach-rk2928/clock.h b/arch/arm/mach-rk2928/clock.h new file mode 100644 index 000000000000..5e956f0c0705 --- /dev/null +++ b/arch/arm/mach-rk2928/clock.h @@ -0,0 +1,99 @@ +#ifndef __MACH_CLOCK_H__ +#define __MACH_CLOCK_H__ + +#ifndef CONFIG_ARCH_RK2928 +#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); +int is_suport_round_rate(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-rk2928/clock_data.c b/arch/arm/mach-rk2928/clock_data.c new file mode 100644 index 000000000000..8225d9d5b009 --- /dev/null +++ b/arch/arm/mach-rk2928/clock_data.c @@ -0,0 +1,2444 @@ +/* arch/arm/mach-rk2928/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 +#include +#include "clock.h" +//#include + +#define MHZ (1000 * 1000) +#define KHZ (1000) +#define CLK_LOOPS_JIFFY_REF 11996091ULL +#define CLK_LOOPS_RATE_REF (1200) //Mhz +#define CLK_LOOPS_RECALC(new_rate) div_u64(CLK_LOOPS_JIFFY_REF*(new_rate),CLK_LOOPS_RATE_REF*MHZ) + + +struct apll_clk_set { + unsigned long rate; + u32 pllcon0; + u32 pllcon1; + u32 pllcon2; //nb=bwadj+1;0:11;nb=nf/2 + u32 clksel0; + u32 clksel1; + u32 rst_dly;//us + unsigned long lpj; //loop per jeffise +}; + +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 +}; +#if 1 +#define CLKDATA_DBG(fmt, args...) printk("CLOCK_DATA:\t"fmt, ## args) +#define CLKDATA_LOG(fmt, args...) printk("CLOCK_DATA:\t"fmt, ## args) +#else +#define CLKDATA_DBG(fmt, args...) do {} while(0) +#define CLKDATA_LOG(fmt, args...) do {} while(0) +#endif +#define CLKDATA_ERR(fmt, args...) pr_err(fmt, ## args) + +//#define RK2928_TEST_MODE +#ifndef RK2928_TEST_MODE +#define cru_readl(offset) readl_relaxed(RK2928_CRU_BASE + offset) +#define cru_writel(v, offset) do { writel_relaxed(v, RK2928_CRU_BASE + offset); dsb(); } while (0) +#else +u32 TEST_CRU_REGS[500] = {0}; +#define cru_readl(offset) (TEST_CRU_REGS[offset / 4]) +#define cru_writel(v, offset) do { TEST_CRU_REGS[offset / 4] = v; } while (0) +#endif + +#define rk_clock_udelay(a) udelay(a); + +#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)))\ + && !(cru_readl(PLL_CONS(pll_id, 0)) & PLL_BYPASS)) + +#define get_cru_bits(con, mask, shift)\ + ((cru_readl((con)) >> (shift)) & (mask)) + +#define CRU_DIV_SET(mask, shift, max) \ + .div_mask = (mask),\ +.div_shift = (shift),\ +.div_max = (max) + +#define CRU_SRC_SET(mask, shift ) \ + .src_mask = (mask),\ +.src_shift = (shift) + +#define CRU_PARENTS_SET(parents_array) \ + .parents = (parents_array),\ +.parents_num = ARRAY_SIZE((parents_array)) + +#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 regfile_readl(offset) readl_relaxed(RK2928_GRF_BASE + offset) +#define regfile_writel(v, offset) do { writel_relaxed(v, RK2928_GRF_BASE + offset); dsb(); } while (0) +#define cru_writel_frac(v,offset) cru_writel((v),(offset)) +/*******************PLL CON0 BITS***************************/ +#define SET_PLL_DATA(_pll_id,_table) \ +{\ + .id=(_pll_id),\ + .table=(_table),\ +} + +#define GATE_CLK(NAME,PARENT,ID) \ + static struct clk clk_##NAME = { \ + .name = #NAME, \ + .parent = &PARENT, \ + .mode = gate_mode, \ + .gate_idx = CLK_GATE_##ID, \ + } + +//FIXME +//lpj +#define _APLL_SET_CLKS(_mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac, \ + _periph_div, _aclk_core_div, _axi_div, _apb_div, _ahb_div) \ +{ \ + .rate = (_mhz) * MHZ, \ + .pllcon0 = PLL_SET_POSTDIV1(_postdiv1) | PLL_SET_FBDIV(_fbdiv), \ + .pllcon1 = PLL_SET_DSMPD(_dsmpd) | PLL_SET_POSTDIV2(_postdiv2) | PLL_SET_REFDIV(_refdiv), \ + .pllcon2 = PLL_SET_FRAC(_frac), \ + .clksel0 = ACLK_CPU_DIV(RATIO_##_axi_div) | CLK_CORE_DIV(RATIO_##_aclk_core_div),\ + .clksel1 = PCLK_CPU_DIV(RATIO_##_apb_div) | HCLK_CPU_DIV(RATIO_##_ahb_div) \ + | ACLK_CORE_DIV(RATIO_##_aclk_core_div) | CLK_CORE_PERI_DIV(RATIO_##_periph_div), \ + .lpj = 1500, \ +} +static const struct apll_clk_set apll_clks[] = { + _APLL_SET_CLKS(650, 6, 325, 2, 1, 1, 0, 41, 21, 81, 21, 21), +}; + +#define _PLL_SET_CLKS(_mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac) \ +{ \ + .rate = (_mhz) * KHZ, \ + .pllcon0 = PLL_SET_POSTDIV1(_postdiv1) | PLL_SET_FBDIV(_fbdiv), \ + .pllcon1 = PLL_SET_DSMPD(_dsmpd) | PLL_SET_POSTDIV2(_postdiv2) | PLL_SET_REFDIV(_refdiv), \ + .pllcon2 = PLL_SET_FRAC(_frac), \ +} +static const struct pll_clk_set cpll_clks[] = { + _PLL_SET_CLKS(798000, 4, 133, 1, 1, 0, 0), + _PLL_SET_CLKS(1064000, 3, 133, 1, 1, 0, 0), +}; + +static const struct pll_clk_set gpll_clks[] = { + _PLL_SET_CLKS(297000, 2, 99, 4, 1, 0, 0), +}; + +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); + CLKDATA_DBG("frac_get_seting rate=%lu,parent=%lu,gcd=%d\n",rate_out,rate, gcd_vl); + + if (!gcd_vl) { + CLKDATA_ERR("gcd=0, i2s frac div is not be supported\n"); + return -ENOENT; + } + + *numerator = rate_out / gcd_vl; + *denominator = rate/ gcd_vl; + + CLKDATA_DBG("frac_get_seting numerator=%d,denominator=%d,times=%d\n", + *numerator, *denominator, *denominator / *numerator); + + if (*numerator > 0xffff || *denominator > 0xffff|| + (*denominator/(*numerator))<20) { + CLKDATA_ERR("can't get a available nume and deno\n"); + return -ENOENT; + } + + return 0; + +} +/************************option functions*****************/ +/************************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; + + CLKDATA_DBG("ENTER %s clk=%s\n", __func__, clk->name); + CLKDATA_DBG("%s new clock rate is %lu (div %u)\n", clk->name, rate, div); + return rate; +} + +//for div 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; + CLKDATA_DBG("ENTER %s clk=%s\n", __func__, clk->name); + CLKDATA_DBG("%s new clock rate is %lu (shift %u)\n", clk->name, rate, shift); + return rate; +} + +//for rate equal to parent +static unsigned long clksel_recalc_equal_parent(struct clk *clk) +{ + unsigned long rate = clk->parent->rate; + CLKDATA_DBG("ENTER %s clk=%s\n", __func__, clk->name); + CLKDATA_DBG("%s new clock rate is %lu (equal to parent)\n", clk->name, rate); + + return clk->parent->rate; +} + +//for Fixed divide ratio +static unsigned long clksel_recalc_fixed_div2(struct clk *clk) +{ + unsigned long rate = clk->parent->rate >> 1; + CLKDATA_DBG("ENTER %s clk=%s\n", __func__, clk->name); + CLKDATA_DBG("%s new clock rate is %lu (div %u)\n", clk->name, rate, 2); + + return rate; +} + +static unsigned long clksel_recalc_fixed_div4(struct clk *clk) +{ + unsigned long rate = clk->parent->rate >> 2; + CLKDATA_DBG("ENTER %s clk=%s\n", __func__, clk->name); + CLKDATA_DBG("%s new clock rate is %lu (div %u)\n", clk->name, rate, 4); + + return rate; +} + +static unsigned long clksel_recalc_frac(struct clk *clk) +{ + unsigned long rate; + u64 rate64; + u32 r = cru_readl(clk->clksel_con), numerator, denominator; + CLKDATA_DBG("ENTER %s clk=%s\n", __func__, clk->name); + 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; + CLKDATA_DBG("%s new clock rate is %lu (frac %u/%u)\n", clk->name, rate, numerator, denominator); + return rate; +} + +#define FRAC_MODE 0 +static unsigned long pll_clk_recalc(u8 pll_id, unsigned long parent_rate) +{ + unsigned long rate; + unsigned int dsmp = 0; + u64 rate64 = 0, frac_rate64 = 0; + dsmp = PLL_GET_DSMPD(cru_readl(PLL_CONS(pll_id, 1))); + + 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)); + //integer mode + rate64 = (u64)parent_rate * PLL_GET_FBDIV(pll_con1); + do_div(rate64, PLL_GET_REFDIV(pll_con1)); + + if (FRAC_MODE == dsmp) { + //fractional mode + frac_rate64 = (u64)parent_rate * PLL_GET_FRAC(pll_con1); + do_div(frac_rate64, PLL_GET_REFDIV(pll_con1)); + rate64 += frac_rate64 >> 24; + CLKDATA_DBG("%s id=%d frac_rate=%llu(0x%08x/2^24) by pass mode\n", + __func__, pll_id, frac_rate64, PLL_GET_FRAC(pll_con1)); + } + do_div(rate64, PLL_GET_POSTDIV1(pll_con0)); + do_div(rate64, PLL_GET_POSTDIV2(pll_con0)); + + rate = rate64; + } else { + rate = parent_rate; + CLKDATA_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); +} + +/************************clk set rate*********************************/ +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; + CLKDATA_DBG("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; + CLKDATA_DBG("clksel_set_rate_shift for clock %s to rate %ld (shift %d)\n", clk->name, rate, shift); + return 0; + } + } + return -ENOENT; +} +#if 0 +//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; + CLKDATA_DBG("clksel_set_rate_shift for clock %s to rate %ld (shift %d)\n", clk->name, rate, shift); + return 0; + } + } + return -ENOENT; +} +#endif +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 clk->parent; + 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,old_div; + int ret=0; + if(clk->rate==rate) + return 0; + p_clk=get_freediv_parents_div(clk,rate,&div); + + if(!p_clk) + return -ENOENT; + + CLKDATA_DBG("%s %lu,form %s\n",clk->name,rate,p_clk->name); + if (clk->parent != p_clk) + { + old_div=CRU_GET_REG_BITS_VAL(cru_readl(clk->clksel_con),clk->div_shift,clk->div_mask)+1; + + if(div>old_div) + { + set_cru_bits_w_msk(div-1,clk->div_mask,clk->div_shift,clk->clksel_con); + } + ret=clk_set_parent_nolock(clk,p_clk); + if(ret) + { + CLKDATA_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; +} +#if 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,old_div; + int ret; + 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) + { + old_div=CRU_GET_REG_BITS_VAL(cru_readl(clk->clksel_con), + clk->div_shift,clk->div_mask)+1; + if(div>old_div) + { + set_cru_bits_w_msk(div-1,clk->div_mask,clk->div_shift,clk->clksel_con); + } + ret=clk_set_parent_nolock(clk,p_clk); + if (ret) + { + CLKDATA_DBG("%s can't get rate%lu,reparent err\n",clk->name,rate); + return ret; + } + } + //set div + set_cru_bits_w_msk(div-1,clk->div_mask,clk->div_shift,clk->clksel_con); + return 0; +} +#endif +/************************round functions*****************/ +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; + if(clk->rate == rate) + return clk->rate; + p_clk=get_freediv_parents_div(clk,rate,&div); + if(!p_clk) + return 0; + return p_clk->rate/div; +} + +static const struct apll_clk_set* apll_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++; + } + //CLKDATA_DBG("arm pll best rate=%lu\n",ps->rate); + return ps; +} +static long apll_clk_round_rate(struct clk *clk, unsigned long rate) +{ + return apll_clk_get_best_pll_set(rate, clk->pll->table)->rate; +} + +/************************others functions*****************/ +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 = 24000000; + while (delay > 0) { + if (!(cru_readl(PLL_CONS(pll_idx, 1)) & (0x1 << PLL_LOCK_SHIFT))) { + //printk("%s %08x\n", __func__, cru_readl(PLL_CONS(pll_idx, 1)) & (0x1 << PLL_LOCK_SHIFT)); + //printk("%s ! %08x\n", __func__, !(cru_readl(PLL_CONS(pll_idx, 1)) & (0x1 << PLL_LOCK_SHIFT))); + break; + } + delay--; + } + if (delay == 0) { + CLKDATA_ERR("wait pll bit 0x%x time out!\n", bit); + while(1); + } +} +static int pll_clk_mode(struct clk *clk, int on) +{ + u8 pll_id = clk->pll->id; + // FIXME here 500 must be changed + u32 dly = 1500; + + CLKDATA_DBG("pll_mode %s(%d)", clk->name, on); + //FIXME + if (on) { + cru_writel(CRU_W_MSK_SETBIT(PLL_PWR_ON, PLL_LOCK_SHIFT), PLL_CONS(pll_id, 0)); + rk_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(CRU_W_MSK_SETBIT(PLL_PWR_DN, PLL_LOCK_SHIFT), PLL_CONS(pll_id, 0)); + } + return 0; +} +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; +} + +static int gate_mode(struct clk *clk, int on) +{ + int idx = clk->gate_idx; + CLKDATA_DBG("ENTER %s clk=%s, on=%d\n", __func__, clk->name, on); + if (idx >= CLK_GATE_MAX) + return -EINVAL; + 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)); + } + return 0; +} +#define PLL_INT_MODE 1 +#define PLL_FRAC_MODE 0 + +#define rk2928_clock_udelay(a) udelay(a); +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); + + 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)); + + //printk("id=%d,pllcon0%08x\n", pll_id, cru_readl(PLL_CONS(pll_id,0))); + //printk("id=%d,pllcon1%08x\n", pll_id, cru_readl(PLL_CONS(pll_id,1))); + //printk("id=%d,pllcon2%08x\n", pll_id, cru_readl(PLL_CONS(pll_id,2))); + //rk2928_clock_udelay(5); + + //wating lock state + rk2928_clock_udelay(clk_set->rst_dly); + pll_wait_lock(pll_id); + + //return form slow + cru_writel(PLL_MODE_NORM(pll_id), CRU_MODE_CON); + + 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_check_legality(unsigned long fin_hz,unsigned long fout_hz, + u32 refdiv, u32 fbdiv, u32 postdiv1, u32 postdiv2) +{ + fin_hz /= MHZ; + if (fin_hz < 1 || fin_hz > 800) { + CLKDATA_ERR("%s fbdiv out of [1, 800]MHz\n", __func__); + return -1; + } + + if (fbdiv < 16 || fbdiv > 1600) { + CLKDATA_ERR("%s fbdiv out of [16, 1600]MHz\n", __func__); + return -1; + } + + if (fin_hz / refdiv < 1 || fin_hz / refdiv > 40) { + CLKDATA_ERR("%s fin / refdiv out of [1, 40]MHz\n", __func__); + return -1; + } + + if (fin_hz * fbdiv / refdiv < 400 || fin_hz * fbdiv / refdiv > 1600) { + CLKDATA_ERR("%s fin_hz * fbdiv / refdiv out of [400, 1600]MHz\n", __func__); + return -1; + } + + if (fin_hz * fbdiv / refdiv / postdiv1 / postdiv2 < 8 + || fin_hz * fbdiv / refdiv / postdiv1 / postdiv2 > 1600) { + CLKDATA_ERR("%s fin_hz * fbdiv / refdiv / postdiv1 / postdiv2 out of [8, 1600]MHz\n", __func__); + return -1; + } + +} + +static int pll_clk_check_legality_frac(unsigned long fin_hz,unsigned long fout_hz, + u32 refdiv, u32 fbdiv, u32 postdiv1, u32 postdiv2, u32 frac) +{ + fin_hz /= MHZ; + if (fin_hz < 10 || fin_hz > 800) { + CLKDATA_ERR("%s fin_hz out of [10, 800]MHz\n", __func__); + return -1; + } + if (fbdiv < 19 || fbdiv > 160) { + CLKDATA_ERR("%s fbdiv out of [19, 160]MHz\n", __func__); + return -1; + } + + if (fin_hz / refdiv < 1 || fin_hz / refdiv > 40) { + CLKDATA_ERR("%s fin / refdiv out of [1, 40]MHz\n", __func__); + return -1; + } + + if (fin_hz * fbdiv / refdiv < 400 || fin_hz * fbdiv / refdiv > 1600) { + CLKDATA_ERR("%s fin_hz * fbdiv / refdiv out of [400, 1600]MHz\n", __func__); + return -1; + } + + if (fin_hz * fbdiv / refdiv / postdiv1 / postdiv2 < 8 + || fin_hz * fbdiv / refdiv / postdiv1 / postdiv2 > 1600) { + CLKDATA_ERR("%s fin_hz * fbdiv / refdiv / postdiv1 / postdiv2 out of [8, 1600]MHz\n", __func__); + return -1; + } + +} +static int pll_clk_get_set(unsigned long fin_hz,unsigned long fout_hz, + u32 *refdiv, u32 *fbdiv, u32 *postdiv1, u32 *postdiv2, u32 *frac) +{ + // FIXME set postdiv1/2 always 1 + u32 gcd; + + if(!fin_hz || !fout_hz || fout_hz == fin_hz) + return -1; + + fin_hz /= MHZ; + fout_hz /= MHZ; + gcd = clk_gcd(fin_hz, fout_hz); + refdiv = gcd; + fbdiv = fout_hz / gcd; + postdiv1 = 1; + postdiv2 = 1; + + frac = 0; + + CLKDATA_DBG("fin=%lu,fout=%lu,gcd=%lu,refdiv=%lu,fbdiv=%lu,postdiv1=%lu,postdiv2=%lu,frac=%lu\n", + fin_hz, fout_hz, gcd, refdiv, fbdiv, postdiv1, postdiv2, frac); + + return 0; +} +static int pll_set_con(u8 id, u32 refdiv, u32 fbdiv, u32 postdiv1, u32 postdiv2, u32 frac) +{ + struct pll_clk_set temp_clk_set; + temp_clk_set.pllcon0 = PLL_SET_FBDIV(fbdiv) | PLL_SET_POSTDIV1(postdiv1) ; + temp_clk_set.pllcon1 = PLL_SET_REFDIV(refdiv) | PLL_SET_POSTDIV2(postdiv2); + temp_clk_set.pllcon2 = PLL_SET_FRAC(frac); + temp_clk_set.rst_dly = 1500; + printk("setting....\n"); + return pll_clk_set_rate(&temp_clk_set, id); +} +static int apll_clk_set_rate(struct clk *clk, unsigned long rate) +{ + // FIXME + u32 refdiv, fbdiv, postdiv1, postdiv2, frac; + printk("%s %s %d\n", __func__, clk->name, rate); + pll_clk_get_set(clk->parent->rate, rate, &refdiv, &fbdiv, &postdiv1, &postdiv2, &frac); + pll_set_con(clk->pll->id, refdiv, fbdiv, postdiv1, postdiv2, frac); + + printk("setting OK\n"); + return 0; +} + +static int dpll_clk_set_rate(struct clk *clk, unsigned long rate) +{ + // FIXME do nothing here + printk("setting OK\n"); + return 0; +} + +static int cpll_clk_set_rate(struct clk *clk, unsigned long rate) +{ + // FIXME + struct _pll_data *pll_data=clk->pll; + struct pll_clk_set *clk_set=(struct pll_clk_set*)pll_data->table; + struct pll_clk_set temp_clk_set; + + u32 fin_hz, fout_hz; + u32 refdiv, fbdiv, postdiv1, postdiv2, frac; + fin_hz = clk->parent->rate; + fout_hz = rate; + + while(clk_set->rate) { + if (clk_set->rate == rate) { + break; + } + clk_set++; + } + + if(clk_set->rate==rate) { + CLKDATA_DBG("cpll get a rate\n"); + pll_clk_set_rate(clk_set, pll_data->id); + + } else { + CLKDATA_DBG("cpll get auto calc a rate\n"); + if(pll_clk_get_set(clk->parent->rate, &rate, &refdiv, &fbdiv, &postdiv1, &postdiv2, &frac) != 0) { + pr_err("cpll auto set rate error\n"); + return -ENOENT; + } + CLKDATA_DBG("%s get fin=%d, fout=%d, refdiv=%lu, fbdiv=%lu, postdiv1=%lu, postdiv2=%d", + __func__, fin_hz, fout_hz, rate, refdiv, fbdiv, postdiv1, postdiv2); + pll_set_con(pll_data->id, refdiv, fbdiv, postdiv1, postdiv2, frac); + } + + printk("setting OK\n"); + return 0; +} + +static int gpll_clk_set_rate(struct clk *clk, unsigned long rate) +{ + // FIXME + struct _pll_data *pll_data=clk->pll; + struct pll_clk_set *clk_set=(struct pll_clk_set*)pll_data->table; + + printk("******%s\n", __func__); + while(clk_set->rate) + { + printk("******%s clk_set->rate=%d\n", __func__, 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 + { + CLKDATA_ERR("gpll is no corresponding rate=%lu\n", rate); + return -1; + } + printk("******%s end\n", __func__); + + return 0; +} + +/**********************pll datas*************************/ +static u32 rk2928_clock_flags = 0; +static struct _pll_data apll_data = SET_PLL_DATA(APLL_ID, (void *)apll_clks); +static struct _pll_data dpll_data = SET_PLL_DATA(DPLL_ID, NULL); +static struct _pll_data cpll_data = SET_PLL_DATA(CPLL_ID, (void *)cpll_clks); +static struct _pll_data gpll_data = SET_PLL_DATA(GPLL_ID, (void *)gpll_clks); +/*********************************************************/ +/************************clocks***************************/ +/*********************************************************/ + +static struct clk xin24m = { + .name = "xin24m", + .rate = 24 * MHZ, + .flags = RATE_FIXED, +}; + +static struct clk clk_12m = { + .name = "clk_12m", + .parent = &xin24m, + .rate = 12 * MHZ, + .flags = RATE_FIXED, +}; +/************************plls***********************/ +static struct clk arm_pll_clk = { + .name = "arm_pll", + .parent = &xin24m, + .mode = pll_clk_mode, + .recalc = plls_clk_recalc, + .set_rate = apll_clk_set_rate, + .round_rate = apll_clk_round_rate, + .pll = &apll_data, +}; + +static struct clk ddr_pll_clk = { + .name = "ddr_pll", + .parent = &xin24m, + .mode = pll_clk_mode, + .recalc = plls_clk_recalc, + .set_rate = dpll_clk_set_rate, + .pll = &dpll_data, +}; + +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 struct clk general_pll_clk = { + .name = "general_pll", + .parent = &xin24m, + .mode = pll_clk_mode, + .gate_idx = CLK_GATE_CPU_GPLL, + .recalc = plls_clk_recalc, + .set_rate = gpll_clk_set_rate, + .pll = &gpll_data, +}; +#define SELECT_FROM_2PLLS {&general_pll_clk, &codec_pll_clk} +/*********ddr******/ +static int ddr_clk_set_rate(struct clk *c, unsigned long rate) +{ + // need to do nothing + return 0; +} + +static struct clk clk_ddrphy2x = { + .name = "ddrphy2x", + .parent = &ddr_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_DDRPHY_SRC, + .recalc = clksel_recalc_shift, + .set_rate = ddr_clk_set_rate, + .clksel_con = CRU_CLKSELS_CON(26), +}; + +static struct clk clk_ddrc = { + .name = "ddrc", + .parent = &clk_ddrphy2x, + .recalc = clksel_recalc_fixed_div2, +}; + +static struct clk clk_ddrphy = { + .name = "ddrphy", + .parent = &clk_ddrphy2x, + .recalc = clksel_recalc_fixed_div2, +}; + +/****************core*******************/ +#if 0 +static unsigned long 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; +} +#endif +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 int core_clksel_set_parent(struct clk *clk, struct clk *new_prt) +{ + // FIXME + u32 temp_div; + struct clk *old_prt; + + if(clk->parent==new_prt) + return 0; + if (unlikely(!clk->parents)) + return -EINVAL; + CLKDATA_DBG("%s,reparent %s\n",clk->name,new_prt->name); + //arm + old_prt=clk->parent; + + if(clk->parents[0]==new_prt) + { + new_prt->set_rate(new_prt,300*MHZ); + set_cru_bits_w_msk(0,clk->div_mask,clk->div_shift,clk->clksel_con); + } + else if(clk->parents[1]==new_prt) + { + + if(new_prt->rate>old_prt->rate) + { + temp_div=clk_get_freediv(old_prt->rate,new_prt->rate,clk->div_max); + set_cru_bits_w_msk(temp_div-1,clk->div_mask,clk->div_shift,clk->clksel_con); + } + set_cru_bits_w_msk(1,clk->src_mask,clk->src_shift,clk->clksel_con); + new_prt->set_rate(new_prt,300*MHZ); + } + else + return -1; + + return 0; +} + +static struct clk *clk_core_pre_parents[2] = {&arm_pll_clk, &general_pll_clk}; +// this clk is cpu? +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); + + CLKDATA_DBG("Failed to change clk pll %s to %lu\n",c->name,rate); + ret = clk_set_rate_nolock(c->parent, rate); + if (ret) { + CLKDATA_ERR("Failed to change clk pll %s to %lu\n",c->name,rate); + return ret; + } + CLKDATA_DBG("change clk pll %s to %lu OK\n",c->name,rate); + return 0; +} +static struct clk clk_core_pre = { + .name = "core_pre", + .parent = &arm_pll_clk, + .recalc = clksel_recalc_div, + .set_rate = arm_core_clk_set_rate, + .round_rate = core_clk_round_rate, + .set_parent = core_clksel_set_parent, + + .clksel_con = CRU_CLKSELS_CON(0), + CRU_DIV_SET(A9_CORE_DIV_MASK, A9_CORE_DIV_SHIFT, 32), + CRU_SRC_SET(0x1, CORE_CLK_PLL_SEL_SHIFT), + CRU_PARENTS_SET(clk_core_pre_parents), +}; + +static struct clk clk_core_periph = { + .name = "core_periph", + .parent = &clk_core_pre, + .mode = gate_mode, + .gate_idx = CLK_GATE_CORE_PERIPH, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(1), + CRU_DIV_SET(CORE_PERIPH_DIV_MASK, CORE_PERIPH_DIV_SHIFT, 16), +}; + +static struct clk clken_core_periph = { + .name = "core_periph_en", + .parent = &clk_core_periph, + .recalc = clksel_recalc_equal_parent, +}; + +static struct clk clk_l2c = { + .name = "l2c", + .parent = &clk_core_pre, + .mode = gate_mode, + .gate_idx = CLK_GATE_CLK_L2C, +}; + +static struct clk aclk_core_pre = { + .name = "aclk_core_pre", + .parent = &clk_core_pre, + .mode = gate_mode, + .gate_idx = CLK_GATE_ACLK_CORE, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(1), + CRU_DIV_SET(ACLK_CORE_DIV_MASK, ACLK_CORE_DIV_SHIFT, 2), +}; + +/****************cpu*******************/ + +static struct clk *clk_cpu_div_parents[] = {&arm_pll_clk, &general_pll_clk}; +/*seperate because of gating*/ +static struct clk clk_cpu_div = { + .name = "cpu_div", + .parent = &general_pll_clk, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(0), + CRU_DIV_SET(ACLK_CPU_DIV_MASK, ACLK_CPU_DIV_SHIFT, 32), + CRU_SRC_SET(0x1, CPU_CLK_PLL_SEL_SHIFT), + CRU_PARENTS_SET(clk_cpu_div_parents), +}; +static struct clk aclk_cpu_pre = { + .name = "aclk_cpu_pre", + .parent = &clk_cpu_div, + .mode = gate_mode, + .gate_idx = CLK_GATE_ACLK_CPU, + .recalc = clksel_recalc_equal_parent, +}; +static struct clk hclk_cpu_pre = { + .name = "hclk_cpu_pre", + .parent = &clk_cpu_div, + .mode = gate_mode, + .gate_idx = CLK_GATE_HCLK_CPU, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(1), + CRU_DIV_SET(HCLK_CPU_DIV_MASK, HCLK_CPU_DIV_SHIFT, 4), +}; +static struct clk pclk_cpu_pre = { + .name = "pclk_cpu_pre", + .parent = &clk_cpu_div, + .mode = gate_mode, + .gate_idx = CLK_GATE_PCLK_CPU, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(1), + CRU_DIV_SET(PCLK_CPU_DIV_MASK, PCLK_CPU_DIV_SHIFT, 8), +}; +/****************vcodec*******************/ +static struct clk *clk_aclk_vepu_parents[] = SELECT_FROM_2PLLS; +static struct clk *clk_aclk_vdpu_parents[] = SELECT_FROM_2PLLS; +static struct clk aclk_vepu = { + .name = "aclk_vepu", + .parent = &codec_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_ACLK_VEPU_SRC, + .recalc = clksel_recalc_div, + .clksel_con = CRU_CLKSELS_CON(32), + .set_rate = clkset_rate_freediv_autosel_parents, + CRU_DIV_SET(0x1f, 0, 32), + CRU_SRC_SET(0x1, 7), + CRU_PARENTS_SET(clk_aclk_vepu_parents), +}; +static struct clk aclk_vdpu = { + .name = "aclk_vdpu", + .parent = &clk_cpu_div, + .mode = gate_mode, + .gate_idx = CLK_GATE_ACLK_VDPU_SRC, + .recalc = clksel_recalc_div, + .set_rate = clkset_rate_freediv_autosel_parents, + .clksel_con = CRU_CLKSELS_CON(32), + CRU_DIV_SET(0x1f, 8, 32), + CRU_SRC_SET(0x1, 15), + CRU_PARENTS_SET(clk_aclk_vdpu_parents), +}; +static struct clk hclk_vepu = { + .name = "hclk_vepu", + .parent = &aclk_vepu, + .mode = gate_mode, + .gate_idx = CLK_GATE_HCLK_VEPU, + .recalc = clksel_recalc_fixed_div4, +}; +static struct clk hclk_vdpu = { + .name = "hclk_vdpu", + .parent = &aclk_vdpu, + .mode = gate_mode, + .gate_idx = CLK_GATE_HCLK_VDPU, + .recalc = clksel_recalc_fixed_div4, +}; + +/****************vio*******************/ +// name: lcdc0_aclk +static struct clk *clk_aclk_vio_pre_parents[] = SELECT_FROM_2PLLS; +static struct clk aclk_vio_pre = { + .name = "aclk_vio_pre", + .parent = &clk_cpu_div, + .mode = gate_mode, + .gate_idx = CLK_GATE_ACLK_VIO_SRC, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(31), + CRU_DIV_SET(0x1f, 0, 32), + CRU_PARENTS_SET(clk_aclk_vio_pre_parents), +}; +static struct clk hclk_vio_pre = { + .name = "hclk_vio_pre", + .parent = &aclk_vio_pre, + .mode = gate_mode, + .gate_idx = CLK_GATE_HCLK_VIO_PRE, + .recalc = clksel_recalc_fixed_div4, +}; + +/****************periph*******************/ +static struct clk *peri_aclk_parents[] = SELECT_FROM_2PLLS; +static struct clk peri_aclk = { + .name = "peri_aclk", + .parent = &general_pll_clk, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .mode = gate_mode, + .gate_idx = CLK_GATE_PERIPH_SRC, + .clksel_con = CRU_CLKSELS_CON(10), + CRU_DIV_SET(PERI_ACLK_DIV_MASK, PERI_ACLK_DIV_SHIFT, 32), + CRU_SRC_SET(0x1, PERI_PLL_SEL_SHIFT), + CRU_PARENTS_SET(peri_aclk_parents), +}; + +static struct clk peri_hclk = { + .name = "peri_hclk", + .parent = &peri_aclk, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_shift, + .clksel_con = CRU_CLKSELS_CON(10), + CRU_DIV_SET(PERI_HCLK_DIV_MASK, PERI_HCLK_DIV_SHIFT, 8), +}; + +static struct clk peri_pclk = { + .name = "peri_pclk", + .parent = &peri_aclk, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_shift, + .clksel_con = CRU_CLKSELS_CON(10), + CRU_DIV_SET(PERI_PCLK_DIV_MASK, PERI_PCLK_DIV_SHIFT, 4), +}; + +static struct clk aclk_periph_pre = { + .name = "aclk_periph_pre", + .parent = &peri_aclk, + .mode = gate_mode, + .gate_idx = CLK_GATE_ACLK_PERIPH, + .recalc = clksel_recalc_equal_parent, +}; + +static struct clk hclk_periph_pre = { + .name = "hclk_periph_pre", + .parent = &peri_hclk, + .mode = gate_mode, + .gate_idx = CLK_GATE_HCLK_PERIPH, + .recalc = clksel_recalc_equal_parent, +}; + +static struct clk pclk_periph_pre = { + .name = "pclk_periph_pre", + .parent = &peri_pclk, + .mode = gate_mode, + .gate_idx = CLK_GATE_PCLK_PERIPH, + .recalc = clksel_recalc_equal_parent, +}; +/****************timer*******************/ +static struct clk *clk_timer0_parents[] = {&xin24m, &peri_pclk}; +static struct clk *clk_timer1_parents[] = {&xin24m, &peri_pclk}; +static struct clk clk_timer0 = { + .name = "timer0", + .parent = &xin24m, + .mode = gate_mode, + .gate_idx = CLK_GATE_TIMER0, + .recalc = clksel_recalc_equal_parent, + .clksel_con = CRU_CLKSELS_CON(10), + CRU_SRC_SET(0x1, 4), + CRU_PARENTS_SET(clk_timer0_parents), +}; +static struct clk clk_timer1 = { + .name = "timer1", + .parent = &xin24m, + .mode = gate_mode, + .gate_idx = CLK_GATE_TIMER1, + .recalc = clksel_recalc_equal_parent, + .clksel_con = CRU_CLKSELS_CON(10), + CRU_SRC_SET(0x1, 5), + CRU_PARENTS_SET(clk_timer1_parents), +}; +/****************spi*******************/ +static struct clk clk_spi = { + .name = "spi", + .parent = &peri_pclk, + .mode = gate_mode, + .gate_idx = CLK_GATE_SPI0_SRC, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(25), + CRU_DIV_SET(0x7f, 0, 128), +}; +/****************sdmmc*******************/ +static struct clk *clk_sdmmc0_parents[] = SELECT_FROM_2PLLS; +static struct clk clk_sdmmc0 = { + .name = "sdmmc0", + .parent = &general_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_MMC0_SRC, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(11), + CRU_SRC_SET(0x1, 6), + CRU_DIV_SET(0x3f,0,64), + CRU_PARENTS_SET(clk_sdmmc0_parents), +}; +#if 0 +static struct clk clk_sdmmc0_sample = { + .name = "sdmmc0_sample", + .parent = &general_pll_clk, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +static struct clk clk_sdmmc0_drv = { + .name = "sdmmc0_drv", + .parent = &clk_sdmmc0, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +#endif +/****************sdio*******************/ +static struct clk *clk_sdio_parents[] = SELECT_FROM_2PLLS; +static struct clk clk_sdio = { + .name = "sdio", + .parent = &general_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_SDIO_SRC, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con = CRU_CLKSELS_CON(12), + CRU_DIV_SET(0x3f,0,64), + CRU_PARENTS_SET(clk_sdio_parents), +}; +#if 0 +static struct clk clk_sdio_sample = { + .name = "sdio_sample", + .parent = &general_pll_clk, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +static struct clk clk_sdio_drv = { + .name = "sdio_drv", + .parent = &clk_sdio, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +#endif +/****************emmc*******************/ +static struct clk *clk_emmc_parents[] = SELECT_FROM_2PLLS; +static struct clk clk_emmc = { + .name = "emmc", + .parent = &general_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_EMMC_SRC, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .clksel_con =CRU_CLKSELS_CON(12), + CRU_DIV_SET(0x3f,8,64), + CRU_PARENTS_SET(clk_emmc_parents), +}; +#if 0 +static struct clk clk_emmc_sample = { + .name = "emmc_sample", + .parent = &general_pll_clk, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +static struct clk clk_emmc_drv = { + .name = "emmc_drv", + .parent = &clk_emmc, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +#endif +/****************lcdc*******************/ +static struct clk *dclk_lcdc_parents[] = {&arm_pll_clk, &general_pll_clk, &codec_pll_clk}; +static struct clk dclk_lcdc = { + .name = "dclk_lcdc", + .parent = &general_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_DCLK_LCDC0_SRC, + .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(0x3, 0), + CRU_PARENTS_SET(dclk_lcdc_parents), +}; +static struct clk *sclk_lcdc_parents[] = SELECT_FROM_2PLLS; +static struct clk sclk_lcdc = { + .name = "sclk_lcdc", + .parent = &general_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_SCLK_LCDC_SRC, + .recalc = clksel_recalc_div, + .set_rate = clkset_rate_freediv_autosel_parents, + .clksel_con = CRU_CLKSELS_CON(28), + CRU_DIV_SET(0xff, 8, 256), + CRU_SRC_SET(0x1, 0), + CRU_PARENTS_SET(sclk_lcdc_parents), +}; +/****************gps*******************/ +#if 0 +static struct clk hclk_gps_parents = SELECT_FROM_2PLLS; +static struct clk hclk_gps = { + .name = "hclk_gps", + .parent = &general_pll_clk, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +#endif +/****************camera*******************/ +static struct clk *clk_cif_out_div_parents[] = SELECT_FROM_2PLLS; +static struct clk clk_cif_out_div = { + .name = "cif_out_div", + .parent = &general_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_CIF_OUT_SRC, + .recalc = clksel_recalc_div, + .set_rate = clkset_rate_freediv_autosel_parents, + .clksel_con = CRU_CLKSELS_CON(29), + CRU_SRC_SET(0x1, 0), + CRU_DIV_SET(0x1f, 1, 32), + CRU_PARENTS_SET(clk_cif_out_div_parents), +}; +static struct clk *clk_cif_out_parents[] = {&xin24m, &clk_cif_out_div}; +static struct clk clk_cif_out = { + .name = "cif0_out", + .parent = &clk_cif_out_div, + .clksel_con = CRU_CLKSELS_CON(29), + CRU_SRC_SET(0x1, 7), + CRU_PARENTS_SET(clk_cif_out_parents), +}; + +/*External clock*/ +static struct clk pclkin_cif0 = { + .name = "pclkin_cif0", + .mode = gate_mode, + .mode = gate_mode, + .gate_idx = CLK_GATE_PCLKIN_CIF, +}; + +static struct clk inv_cif0 = { + .name = "inv_cif0", + .parent = &pclkin_cif0, +}; + +static struct clk *cif0_in_parents[] = {&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), +}; + +/****************i2s*******************/ +#define I2S_SRC_12M (0x0) +#define I2S_SRC_DIV (0x1) +#define I2S_SRC_FRAC (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]; + } + + CLKDATA_DBG(" %s set rate=%lu parent %s(old %s)\n", + clk->name,rate,parent->name,clk->parent->name); + + if(parent!=clk->parents[I2S_SRC_12M]) + { + ret = clk_set_rate_nolock(parent,rate);//div 1:1 + if (ret) + { + CLKDATA_DBG("%s set rate%lu err\n",clk->name,rate); + return ret; + } + } + + if (clk->parent != parent) + { + ret = clk_set_parent_nolock(clk, parent); + if (ret) + { + CLKDATA_DBG("%s can't get rate%lu,reparent err\n",clk->name,rate); + return ret; + } + } + + return ret; +}; +static struct clk *clk_i2s_div_parents[] = SELECT_FROM_2PLLS; +static struct clk clk_i2s_pll = { + .name = "i2s_pll", + .parent = &general_pll_clk, + .clksel_con = CRU_CLKSELS_CON(2), + CRU_SRC_SET(0x1,15), + CRU_PARENTS_SET(clk_i2s_div_parents), +}; + +static struct clk clk_i2s_div = { + .name = "i2s_div", + .parent = &clk_i2s_pll, + .mode = gate_mode, + .gate_idx = CLK_GATE_I2S_SRC, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .round_rate = clksel_freediv_round_rate, + .clksel_con = CRU_CLKSELS_CON(3), + CRU_DIV_SET(0x7f, 0, 64), +}; +static struct clk clk_i2s_frac_div = { + .name = "i2s_frac_div", + .parent = &clk_i2s_div, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .round_rate = clksel_freediv_round_rate, + .mode = gate_mode, + .gate_idx = CLK_GATE_I2S_FRAC_SRC, + .clksel_con = CRU_CLKSELS_CON(7), +}; + +static struct clk *clk_i2s_parents[] = {&clk_12m, &clk_i2s_div, &clk_i2s_frac_div}; +static struct clk clk_i2s = { + .name = "i2s", + .parent = &clk_i2s_div, + .set_rate = i2s_set_rate, + .clksel_con = CRU_CLKSELS_CON(3), + CRU_SRC_SET(0x3, 8), + CRU_PARENTS_SET(clk_i2s_parents), +}; + +/****************otgphy*******************/ +#if 0 +static struct clk clk_otgphy0 = { + .name = "otgphy0", + .parent = &clk_12m, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +static struct clk clk_otgphy1 = { + .name = "otgphy1", + .parent = &clk_12m, + .recalc = , + //.set_rate = , + .clksel_con = , + CRU_DIV_SET(,,), +}; +#endif +GATE_CLK(otgphy0, clk_12m, OTGPHY0); +GATE_CLK(otgphy1, clk_12m, OTGPHY1); +/****************saradc*******************/ +static struct clk clk_saradc = { + .name = "saradc", + .parent = &xin24m, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .mode = gate_mode, + .gate_idx = CLK_GATE_SARADC_SRC, + .clksel_con = CRU_CLKSELS_CON(24), + CRU_DIV_SET(0xff,8,256), +}; +/****************gpu_pre*******************/ +// name: gpu_aclk +static struct clk *clk_gpu_pre_parents[] = SELECT_FROM_2PLLS; +static struct clk clk_gpu_pre = { + .name = "gpu_pre", + .parent = &general_pll_clk, + .mode = gate_mode, + .gate_idx = CLK_GATE_GPU_PRE, + .recalc = clksel_recalc_div, + .set_rate = clkset_rate_freediv_autosel_parents, + .round_rate = clk_freediv_round_autosel_parents_rate, + .clksel_con = CRU_CLKSELS_CON(34), + CRU_DIV_SET(0x1f, 0, 32), + CRU_PARENTS_SET(clk_gpu_pre_parents), +}; +/****************uart*******************/ +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); + + CLKDATA_DBG("%s set rate=%lu,is ok\n",clk->name,rate); + } + else + { + CLKDATA_ERR("clk_frac_div can't get rate=%lu,%s\n",rate,clk->name); + return -ENOENT; + } + return 0; +} +#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]; + } + + + + CLKDATA_DBG(" %s set rate=%lu parent %s(old %s)\n", + clk->name,rate,parent->name,clk->parent->name); + + + if(parent!=clk->parents[UART_SRC_24M]) + { + ret = clk_set_rate_nolock(parent,rate); + if (ret) + { + CLKDATA_DBG("%s set rate%lu err\n",clk->name,rate); + return ret; + } + } + + if (clk->parent != parent) + { + ret = clk_set_parent_nolock(clk, parent); + if (ret) + { + CLKDATA_DBG("%s can't get rate%lu,reparent err\n",clk->name,rate); + return ret; + } + } + + + return ret; +} + + +static struct clk *clk_uart_pll_src_parents[] = SELECT_FROM_2PLLS; +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_pll_src_parents), +}; +//static struct clk clk_uart0_div_parents = SELECT_FROM_2PLLS; +//static struct clk clk_uart1_div_parents = SELECT_FROM_2PLLS; +//static struct clk clk_uart2_div_parents = SELECT_FROM_2PLLS; +static struct clk clk_uart0_div = { + .name = "uart0_div", + .parent = &clk_uart_pll, + .mode = gate_mode, + .gate_idx = CLK_GATE_UART0_SRC, + .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_SRC, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .round_rate = clksel_freediv_round_rate, + .clksel_con = CRU_CLKSELS_CON(15), + 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_SRC, + .recalc = clksel_recalc_div, + .set_rate = clksel_set_rate_freediv, + .round_rate = clksel_freediv_round_rate, + .clksel_con = CRU_CLKSELS_CON(15), + CRU_DIV_SET(0x7f, 0, 64), +}; +static struct clk clk_uart0_frac_div = { + .name = "uart0_frac_div", + .parent = &clk_uart0_div, + .mode = gate_mode, + .gate_idx = CLK_GATE_UART0_FRAC_SRC, + .recalc = clksel_recalc_frac, + .set_rate = clk_uart_fracdiv_set_rate, + .clksel_con = CRU_CLKSELS_CON(17), +}; +static struct clk clk_uart1_frac_div = { + .name = "uart1_frac_div", + .parent = &clk_uart1_div, + .mode = gate_mode, + .gate_idx = CLK_GATE_UART1_FRAC_SRC, + .recalc = clksel_recalc_frac, + .set_rate = clk_uart_fracdiv_set_rate, + .clksel_con = CRU_CLKSELS_CON(18), +}; +static struct clk clk_uart2_frac_div = { + .name = "uart2_frac_div", + .parent = &clk_uart2_div, + .mode = gate_mode, + .gate_idx = CLK_GATE_UART2_FRAC_SRC, + .recalc = clksel_recalc_frac, + .set_rate = clk_uart_fracdiv_set_rate, + .clksel_con = CRU_CLKSELS_CON(19), +}; + +static struct clk *clk_uart0_parents[] = {&clk_uart0_div, &clk_uart0_frac_div, &xin24m}; +static struct clk *clk_uart1_parents[] = {&clk_uart1_div, &clk_uart1_frac_div, &xin24m}; +static struct clk *clk_uart2_parents[] = {&clk_uart2_div, &clk_uart2_frac_div, &xin24m}; +static struct clk clk_uart0= { + .name = "uart0", + .parent = &xin24m, + .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= { + .name = "uart1", + .parent = &xin24m, + .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= { + .name = "uart2", + .parent = &xin24m, + .set_rate = clk_uart_set_rate, + .clksel_con = CRU_CLKSELS_CON(15), + CRU_SRC_SET(0x3, 8), + CRU_PARENTS_SET(clk_uart2_parents), +}; +/****************sub clock---pre*******************/ +/*************************aclk_cpu***********************/ +GATE_CLK(aclk_intmem, aclk_cpu_pre, ACLK_INTMEM); +GATE_CLK(aclk_strc_sys, aclk_cpu_pre, ACLK_STRC_SYS); + +/*************************hclk_cpu***********************/ +//FIXME +//GATE_CLK(hclk_cpubus, hclk_cpu_pre, HCLK_CPUBUS); +GATE_CLK(hclk_rom, hclk_cpu_pre, HCLK_ROM); + +/*************************pclk_cpu***********************/ +//FIXME +//GATE_CLK(pclk_hdmi, pclk_cpu_pre, PCLK_HDMI); +GATE_CLK(pclk_ddrupctl, pclk_cpu_pre, PCLK_DDRUPCTL); +GATE_CLK(pclk_grf, pclk_cpu_pre, PCLK_GRF); +GATE_CLK(pclk_acodec, pclk_cpu_pre, PCLK_ACODEC); + +/*************************aclk_periph********************/ +GATE_CLK(aclk_dma2, aclk_periph_pre, ACLK_DMAC2); +GATE_CLK(aclk_peri_niu, aclk_periph_pre, ACLK_PERI_NIU); +GATE_CLK(aclk_cpu_peri, aclk_periph_pre, ACLK_CPU_PERI); +GATE_CLK(aclk_peri_axi_matrix, aclk_periph_pre, ACLK_PERI_AXI_MATRIX); +//FIXME +//GATE_CLK(aclk_gps, aclk_periph_pre, ACLK_GPS); + +/*************************hclk_periph***********************/ +GATE_CLK(hclk_peri_axi_matrix, hclk_periph_pre, HCLK_PERI_AXI_MATRIX); +GATE_CLK(hclk_peri_ahb_arbi, hclk_periph_pre, HCLK_PERI_ARBI); +GATE_CLK(hclk_nandc, hclk_periph_pre, HCLK_NANDC); +GATE_CLK(hclk_usb_peri, hclk_periph_pre, HCLK_USB_PERI); +GATE_CLK(hclk_otg0, hclk_periph_pre, HCLK_OTG0); +GATE_CLK(hclk_otg1, hclk_periph_pre, HCLK_OTG1); +GATE_CLK(hclk_i2s, hclk_periph_pre, HCLK_I2S); +GATE_CLK(hclk_sdmmc0, hclk_periph_pre, HCLK_SDMMC0); +GATE_CLK(hclk_sdio, hclk_periph_pre, HCLK_SDIO); +GATE_CLK(hclk_emmc, hclk_periph_pre, HCLK_EMMC); + +/*************************pclk_periph***********************/ +GATE_CLK(pclk_peri_axi_matrix, pclk_periph_pre, PCLK_PERI_AXI_MATRIX); +GATE_CLK(pclk_pwm01, pclk_periph_pre, PCLK_PWM01); +GATE_CLK(pclk_wdt, pclk_periph_pre, PCLK_WDT); +GATE_CLK(pclk_spi0, pclk_periph_pre, PCLK_SPI0); +GATE_CLK(pclk_uart0, pclk_periph_pre, PCLK_UART0); +GATE_CLK(pclk_uart1, pclk_periph_pre, PCLK_UART1); +GATE_CLK(pclk_uart2, pclk_periph_pre, PCLK_UART2); +GATE_CLK(pclk_i2c0, pclk_periph_pre, PCLK_I2C0); +GATE_CLK(pclk_i2c1, pclk_periph_pre, PCLK_I2C1); +GATE_CLK(pclk_i2c2, pclk_periph_pre, PCLK_I2C2); +GATE_CLK(pclk_i2c3, pclk_periph_pre, PCLK_I2C3); +GATE_CLK(pclk_timer0, pclk_periph_pre, PCLK_TIMER0); +GATE_CLK(pclk_timer1, pclk_periph_pre, PCLK_TIMER1); +GATE_CLK(pclk_gpio0, pclk_periph_pre, PCLK_GPIO0); +GATE_CLK(pclk_gpio1, pclk_periph_pre, PCLK_GPIO1); +GATE_CLK(pclk_gpio2, pclk_periph_pre, PCLK_GPIO2); +GATE_CLK(pclk_gpio3, pclk_periph_pre, PCLK_GPIO3); +GATE_CLK(pclk_saradc, pclk_periph_pre, PCLK_SARADC); +GATE_CLK(pclk_efuse, pclk_periph_pre, PCLK_EFUSE); + +/*************************aclk_vio***********************/ +GATE_CLK(aclk_vio0, aclk_vio_pre, ACLK_VIO0); +GATE_CLK(aclk_lcdc0, aclk_vio_pre, ACLK_LCDC0); +GATE_CLK(aclk_cif0, aclk_vio_pre, ACLK_CIF); +GATE_CLK(aclk_rga, aclk_vio_pre, ACLK_RGA); + +/*************************hclk_vio***********************/ +GATE_CLK(hclk_lcdc0, hclk_vio_pre, HCLK_LCDC0); +GATE_CLK(hclk_cif0, hclk_vio_pre, HCLK_CIF); +GATE_CLK(hclk_rga, hclk_vio_pre, HCLK_RGA); +GATE_CLK(hclk_vio_bus, hclk_vio_pre, HCLK_VIO_BUS); + +/* Power domain, not exist in fact*/ +enum pmu_power_domain { + PD_A9_0 = 0, + PD_A9_1, + PD_ALIVE, + PD_RTC, + PD_SCU, + PD_CPU, + PD_PERI = 6, + PD_VIO, + PD_VIDEO, + PD_VCODEC = PD_VIDEO, + PD_GPU, + PD_DBG, +}; + +static int pm_off_mode(struct clk *clk, int on) +{ + return 0; +} +static struct clk pd_peri = { + .name = "pd_peri", + .flags = IS_PD, + .mode = pm_off_mode, + .gate_idx = PD_PERI, +}; + +static int pd_display_mode(struct clk *clk, int on) +{ + return 0; +} + +static struct clk pd_display = { + .name = "pd_display", + .flags = IS_PD, + .mode = pd_display_mode, + .gate_idx = PD_VIO, +}; + +static struct clk pd_lcdc0 = { + .parent = &pd_display, + .name = "pd_lcdc0", +}; +static struct clk pd_lcdc1 = { + .parent = &pd_display, + .name = "pd_lcdc1", +}; +static struct clk pd_cif0 = { + .parent = &pd_display, + .name = "pd_cif0", +}; +static struct clk pd_cif1 = { + .parent = &pd_display, + .name = "pd_cif1", +}; +static struct clk pd_rga = { + .parent = &pd_display, + .name = "pd_rga", +}; +static struct clk pd_ipp = { + .parent = &pd_display, + .name = "pd_ipp", +}; +static int pd_video_mode(struct clk *clk, int on) +{ + return 0; +} + +static struct clk pd_video = { + .name = "pd_video", + .flags = IS_PD, + .mode = pd_video_mode, + .gate_idx = PD_VIDEO, +}; + +static int pd_gpu_mode(struct clk *clk, int on) +{ + return 0; +} + +static struct clk pd_gpu = { + .name = "pd_gpu", + .flags = IS_PD, + .mode = pd_gpu_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,\ +} +/* Power domain END, not exist in fact*/ + +#define CLK(dev, con, ck) \ +{\ + .dev_id = dev,\ + .con_id = con,\ + .clk = ck,\ +} + +#define CLK_GATE_NODEV(name) \ +{\ + .dev_id = NULL,\ + .con_id = #name,\ + .clk = &clk_##name,\ +} + +static struct clk_lookup clks[] = { + CLK(NULL, "xin24m", &xin24m), + 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, "ddrphy2x", &clk_ddrphy2x), + CLK(NULL, "ddrphy", &clk_ddrphy), + CLK(NULL, "ddrc", &clk_ddrc), + + CLK(NULL, "core_pre", &clk_core_pre), + CLK(NULL, "core_periph", &clk_core_periph), + CLK(NULL, "core_periph_en", &clken_core_periph), + CLK(NULL, "l2c", &clk_l2c), + CLK(NULL, "aclk_core_pre", &aclk_core_pre), + + CLK(NULL, "cpu_div", &clk_cpu_div), + CLK(NULL, "aclk_cpu_pre", &aclk_cpu_pre), + CLK(NULL, "pclk_cpu_pre", &pclk_cpu_pre), + CLK(NULL, "hclk_cpu_pre", &hclk_cpu_pre), + + CLK(NULL, "aclk_vepu", &aclk_vepu), + CLK(NULL, "aclk_vdpu", &aclk_vdpu), + CLK(NULL, "hclk_vepu", &hclk_vepu), + CLK(NULL, "hclk_vdpu", &hclk_vdpu), + + CLK(NULL, "aclk_vio_pre", &aclk_vio_pre), + CLK(NULL, "hclk_vio_pre", &hclk_vio_pre), + + CLK(NULL, "peri_aclk", &peri_aclk), + CLK(NULL, "peri_pclk", &peri_pclk), + CLK(NULL, "peri_hclk", &peri_hclk), + + CLK(NULL, "timer0", &clk_timer0), + CLK(NULL, "timer1", &clk_timer1), + + CLK("rk29xx_spim.0", "spi", &clk_spi), + + CLK("rk29_sdmmc.0", "mmc", &clk_sdmmc0), + //CLK("rk29_sdmmc.0", "mmc_sample", &clk_sdmmc0_sample), + //CLK("rk29_sdmmc.0", "mmc_drv", &clk_sdmmc0_drv), + + CLK("rk29_sdmmc.1", "mmc", &clk_sdio), + //CLK("rk29_sdmmc.1", "mmc_sample", &clk_sdio_sample), + //CLK("rk29_sdmmc.1", "mmc_drv", &clk_sdio_drv), + + CLK(NULL, "emmc", &clk_emmc), + //CLK(NULL, "emmc_sample", &clk_emmc_sample), + //CLK(NULL, "emmc_drv", &clk_emmc_drv), + + CLK(NULL, "dclk_lcdc0", &dclk_lcdc), + CLK(NULL, "sclk_lcdc0", &sclk_lcdc), + //FIXME + //CLK(NULL, "hclk_gps", &hclk_gps), + + CLK(NULL, "cif_out_div", &clk_cif_out_div), + CLK(NULL, "cif0_out", &clk_cif_out), + CLK(NULL, "pclkin_cif0", &pclkin_cif0), + CLK(NULL, "inv_cif0", &inv_cif0), + CLK(NULL, "cif0_in", &cif0_in), + + CLK(NULL, "i2s_pll", &clk_i2s_pll), + CLK("rk29_i2s.0", "i2s_div", &clk_i2s_div), + CLK("rk29_i2s.0", "i2s_frac_div", &clk_i2s_frac_div), + CLK("rk29_i2s.0", "i2s", &clk_i2s), + + CLK(NULL, "otgphy0", &clk_otgphy0), + CLK(NULL, "otgphy1", &clk_otgphy1), + CLK(NULL, "saradc", &clk_saradc), + CLK(NULL, "gpu_pre", &clk_gpu_pre), + + CLK(NULL, "uart_pll", &clk_uart_pll), + CLK("rk_serial.0", "uart_div", &clk_uart0_div), + CLK("rk_serial.1", "uart_div", &clk_uart1_div), + CLK("rk_serial.2", "uart_div", &clk_uart2_div), + CLK("rk_serial.0", "uart_frac_div", &clk_uart0_frac_div), + CLK("rk_serial.1", "uart_frac_div", &clk_uart1_frac_div), + CLK("rk_serial.2", "uart_frac_div", &clk_uart2_frac_div), + CLK("rk_serial.0", "uart", &clk_uart0), + CLK("rk_serial.1", "uart", &clk_uart1), + CLK("rk_serial.2", "uart", &clk_uart2), + + CLK(NULL, "aclk_periph_pre", &aclk_periph_pre), + CLK(NULL, "hclk_periph_pre", &hclk_periph_pre), + CLK(NULL, "pclk_periph_pre", &pclk_periph_pre), + + /*********fixed clock ******/ + CLK_GATE_NODEV(aclk_intmem), + CLK_GATE_NODEV(aclk_strc_sys), + + //FIXME + //CLK_GATE_NODEV(hclk_cpubus), + CLK_GATE_NODEV(hclk_rom), + + //FIXME + //CLK_GATE_NODEV(pclk_hdmi), + CLK_GATE_NODEV(pclk_ddrupctl), + CLK_GATE_NODEV(pclk_grf), + CLK_GATE_NODEV(pclk_acodec), + + CLK_GATE_NODEV(aclk_dma2), + CLK_GATE_NODEV(aclk_peri_niu), + CLK_GATE_NODEV(aclk_cpu_peri), + CLK_GATE_NODEV(aclk_peri_axi_matrix), + //FIXME + //CLK_GATE_NODEV(aclk_gps), + + CLK_GATE_NODEV(hclk_peri_axi_matrix), + CLK_GATE_NODEV(hclk_peri_ahb_arbi), + CLK_GATE_NODEV(hclk_nandc), + CLK_GATE_NODEV(hclk_usb_peri), + CLK_GATE_NODEV(hclk_otg0), + CLK_GATE_NODEV(hclk_otg1), + CLK_GATE_NODEV(hclk_i2s), + CLK("rk29_sdmmc.0", "hclk_mmc", &clk_hclk_sdmmc0), + CLK("rk29_sdmmc.1", "hclk_mmc", &clk_hclk_sdio), + CLK("rk29_sdmmc.2", "hclk_mmc", &clk_hclk_emmc), + + CLK_GATE_NODEV(pclk_peri_axi_matrix), + CLK(NULL, "pwm01", &clk_pclk_pwm01), + CLK_GATE_NODEV(pclk_wdt), + CLK_GATE_NODEV(pclk_spi0), + CLK("rk_serial.0", "pclk_uart", &clk_pclk_uart0), + CLK("rk_serial.1", "pclk_uart", &clk_pclk_uart1), + CLK("rk_serial.2", "pclk_uart", &clk_pclk_uart2), + CLK("rk30_i2c.0", "i2c", &clk_pclk_i2c0), + CLK("rk30_i2c.1", "i2c", &clk_pclk_i2c1), + CLK("rk30_i2c.2", "i2c", &clk_pclk_i2c2), + CLK("rk30_i2c.3", "i2c", &clk_pclk_i2c3), + CLK_GATE_NODEV(pclk_timer0), + CLK_GATE_NODEV(pclk_timer1), + CLK_GATE_NODEV(pclk_gpio0), + CLK_GATE_NODEV(pclk_gpio1), + CLK_GATE_NODEV(pclk_gpio2), + CLK_GATE_NODEV(pclk_gpio3), + CLK_GATE_NODEV(pclk_saradc), + CLK_GATE_NODEV(pclk_efuse), + + CLK_GATE_NODEV(aclk_vio0), + CLK_GATE_NODEV(aclk_lcdc0), + CLK_GATE_NODEV(aclk_cif0), + CLK_GATE_NODEV(aclk_rga), + + CLK_GATE_NODEV(hclk_lcdc0), + CLK_GATE_NODEV(hclk_cif0), + CLK_GATE_NODEV(hclk_rga), + CLK_GATE_NODEV(hclk_vio_bus), + + /* Power domain, not exist in fact*/ + PD_CLK(pd_peri), + PD_CLK(pd_display), + PD_CLK(pd_video), + PD_CLK(pd_lcdc0), + PD_CLK(pd_lcdc1), + PD_CLK(pd_cif0), + PD_CLK(pd_cif1), + PD_CLK(pd_rga), + PD_CLK(pd_ipp), + PD_CLK(pd_video), + PD_CLK(pd_gpu), + PD_CLK(pd_dbg), + +}; + +static void __init rk30_init_enable_clocks(void) +{ + CLKDATA_DBG("ENTER %s\n", __func__); + clk_enable_nolock(&clk_core_pre); //cpu + clk_enable_nolock(&clk_core_periph); + clk_enable_nolock(&aclk_cpu_pre); + clk_enable_nolock(&hclk_cpu_pre); + clk_enable_nolock(&pclk_cpu_pre); + + clk_enable_nolock(&aclk_periph_pre); + clk_enable_nolock(&pclk_periph_pre); + clk_enable_nolock(&hclk_periph_pre); + +#if CONFIG_RK_DEBUG_UART == 0 + clk_enable_nolock(&clk_uart0); + clk_enable_nolock(&clk_pclk_uart0); + +#elif CONFIG_RK_DEBUG_UART == 1 + clk_enable_nolock(&clk_uart1); + clk_enable_nolock(&clk_pclk_uart1); + +#elif CONFIG_RK_DEBUG_UART == 2 + clk_enable_nolock(&clk_uart2); + clk_enable_nolock(&clk_pclk_uart2); +#endif + + /*************************aclk_cpu***********************/ + clk_enable_nolock(&clk_aclk_intmem); + clk_enable_nolock(&clk_aclk_strc_sys); + + /*************************hclk_cpu***********************/ + clk_enable_nolock(&clk_hclk_rom); + + /*************************pclk_cpu***********************/ + clk_enable_nolock(&clk_pclk_ddrupctl); + clk_enable_nolock(&clk_pclk_grf); + + /*************************aclk_periph***********************/ + clk_enable_nolock(&clk_aclk_dma2); + clk_enable_nolock(&clk_aclk_peri_niu); + clk_enable_nolock(&clk_aclk_cpu_peri); + clk_enable_nolock(&clk_aclk_peri_axi_matrix); + + /*************************hclk_periph***********************/ + clk_enable_nolock(&clk_hclk_peri_axi_matrix); + clk_enable_nolock(&clk_hclk_peri_ahb_arbi); + clk_enable_nolock(&clk_hclk_nandc); + + /*************************pclk_periph***********************/ + clk_enable_nolock(&clk_pclk_peri_axi_matrix); + /*************************hclk_vio***********************/ + clk_enable_nolock(&clk_hclk_vio_bus); +} + +#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; + //CLKDATA_DBG("dump_clock %s\n",clk->name); + for (i = 0; i < deep; i++) + seq_printf(s, " "); + + seq_printf(s, "%-11s ", clk->name); + + 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 "); + if(cru_readl(PLL_CONS(pll_id,3)) & PLL_BYPASS) + seq_printf(s, "bypass "); + } + else if(clk == &ddr_pll_clk) { + rate = clk->recalc(clk); + } + + if (rate >= MHZ) { + if (rate % MHZ) + seq_printf(s, "%ld.%06ld MHz", rate / MHZ, rate % MHZ); + else + seq_printf(s, "%ld MHz", rate / MHZ); + } else if (rate >= KHZ) { + if (rate % KHZ) + seq_printf(s, "%ld.%03ld KHz", rate / KHZ, rate % KHZ); + else + seq_printf(s, "%ld KHz", rate / KHZ); + } else { + seq_printf(s, "%ld Hz", rate); + } + + seq_printf(s, " usecount = %d", clk->usecount); + + if (clk->parent) + seq_printf(s, " parent = %s", clk->parent->name); + + seq_printf(s, "\n"); + + list_for_each_entry(ck, root_clocks, node) { + if (ck->parent == clk) + dump_clock(s, ck, deep + 1,root_clocks); + } +} + +static void dump_regs(struct seq_file *s) +{ + int i=0; + seq_printf(s, "\nPLL(id=0 apll,id=1,dpll,id=2,cpll,id=3 cpll)\n"); + seq_printf(s, "\nPLLRegisters:\n"); + for(i=0;i>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; + + case 297 * MHZ: + aclk_p = ppll_rate>>1; + hclk_p = aclk_p>>0; + pclk_p = aclk_p>>1; + break; + + case 300 * MHZ: + aclk_p = ppll_rate>>1; + hclk_p = aclk_p>>0; + pclk_p = aclk_p>>1; + break; + default: + aclk_p = 150 * MHZ; + hclk_p = 150 * MHZ; + pclk_p = 75 * MHZ; + break; + } + clk_set_parent_nolock(&aclk_periph_pre, &general_pll_clk); + clk_set_rate_nolock(&aclk_periph_pre, aclk_p); + clk_set_rate_nolock(&hclk_periph_pre, hclk_p); + clk_set_rate_nolock(&pclk_periph_pre, pclk_p); +} + + +#define CLK_FLG_MAX_I2S_12288KHZ (1<<1) +#define CLK_FLG_MAX_I2S_22579_2KHZ (1<<2) +#define CLK_FLG_MAX_I2S_24576KHZ (1<<3) +#define CLK_FLG_MAX_I2S_49152KHZ (1<<4) + +void rk2928_clock_common_i2s_init(void) +{ + struct clk *max_clk,*min_clk; + unsigned long i2s_rate; + //20 times + if(rk2928_clock_flags&CLK_FLG_MAX_I2S_49152KHZ) + { + i2s_rate=49152000; + }else if(rk2928_clock_flags&CLK_FLG_MAX_I2S_24576KHZ) + { + i2s_rate=24576000; + } + else if(rk2928_clock_flags&CLK_FLG_MAX_I2S_22579_2KHZ) + { + i2s_rate=22579000; + } + else if(rk2928_clock_flags&CLK_FLG_MAX_I2S_12288KHZ) + { + i2s_rate=12288000; + } + else + { + i2s_rate=49152000; + } + + if(((i2s_rate*20)<=general_pll_clk.rate)||!(general_pll_clk.rate%i2s_rate)) + { + clk_set_parent_nolock(&clk_i2s_pll, &general_pll_clk); + } + else if(((i2s_rate*20)<=codec_pll_clk.rate)||!(codec_pll_clk.rate%i2s_rate)) + { + clk_set_parent_nolock(&clk_i2s_pll, &codec_pll_clk); + } + else + { + if(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 rk2928_clock_common_init(unsigned long gpll_rate,unsigned long cpll_rate) +{ + CLKDATA_DBG("ENTER %s\n", __func__); + + clk_set_rate_nolock(&clk_core_pre, 650 * 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 + rk2928_clock_common_i2s_init(); + + // spi + clk_set_rate_nolock(&clk_spi, clk_spi.parent->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 + // FIXME +#if 0 + if(!(gpll_rate%(50*MHZ))) + clk_set_parent_nolock(&clk_mac_pll_div, &general_pll_clk); + else if(!(ddr_pll_clk.rate%(50*MHZ))) + clk_set_parent_nolock(&clk_mac_pll_div, &ddr_pll_clk); + else + CRU_PRINTK_ERR("mac can't get 50mhz\n"); +#endif + //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(&clk_cif_out_div, &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); + // FIXME +#if 0 + clk_set_rate_nolock(&aclk_lcdc0_ipp_parent, 300*MHZ); + clk_set_rate_nolock(&aclk_lcdc1_rga_parent, 300*MHZ); +#endif + //axi vepu auto sel + //clk_set_parent_nolock(&aclk_vepu, &general_pll_clk); + //clk_set_parent_nolock(&aclk_vdpu, &general_pll_clk); + + clk_set_rate_nolock(&aclk_vepu, 300*MHZ); + clk_set_rate_nolock(&aclk_vdpu, 300*MHZ); + //gpu auto sel + //clk_set_parent_nolock(&clk_gpu, &general_pll_clk); + // +} +void __init _rk2928_clock_data_init(unsigned long gpll,unsigned long cpll,int flags) +{ + struct clk_lookup *clk; + clk_register_dump_ops(&dump_ops); + clk_register_default_ops_clk(&def_ops_clk); + + rk2928_clock_flags = flags; + + CLKDATA_DBG("%s total %d clks\n", __func__, ARRAY_SIZE(clks)); + for (clk = clks; clk < clks + ARRAY_SIZE(clks); clk++) { + CLKDATA_DBG("%s add dev_id=%s, con_id=%s\n", + __func__, clk->dev_id ? clk->dev_id : "NULL", clk->con_id ? clk->con_id : "NULL"); + clkdev_add(clk); + clk_register(clk->clk); + } + + CLKDATA_DBG("clk_recalculate_root_clocks_nolock\n"); + clk_recalculate_root_clocks_nolock(); + + 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(); + CLKDATA_DBG("rk2928_clock_common_init, gpll=%lu, cpll=%lu\n", gpll, cpll); + rk2928_clock_common_init(gpll, cpll); + preset_lpj = loops_per_jiffy; + + CLKDATA_DBG("%s clks init finish\n", __func__); +} + + +void __init rk2928_clock_data_init(unsigned long gpll,unsigned long cpll,u32 flags) +{ + printk("version: 2012-8-6\n"); + _rk2928_clock_data_init(gpll,cpll,flags); + //rk2928_dvfs_init(); +} + diff --git a/arch/arm/mach-rk2928/include/mach/board.h b/arch/arm/mach-rk2928/include/mach/board.h index 1e1059a7d3ba..9292adca4591 100644 --- a/arch/arm/mach-rk2928/include/mach/board.h +++ b/arch/arm/mach-rk2928/include/mach/board.h @@ -57,5 +57,35 @@ struct eeti_egalax_platform_data{ }; #endif +enum _periph_pll { + periph_pll_1485mhz = 148500000, + periph_pll_297mhz = 297000000, + periph_pll_300mhz = 300000000, + periph_pll_1188mhz = 1188000000, /* for box*/ +}; +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_742_5khz = 742500000, + codec_pll_798mhz = 798000000, + codec_pll_1064mhz = 1064000000, + codec_pll_1188mhz = 1188000000, +}; + +//max i2s rate +#define CLK_FLG_MAX_I2S_12288KHZ (1<<1) +#define CLK_FLG_MAX_I2S_22579_2KHZ (1<<2) +#define CLK_FLG_MAX_I2S_24576KHZ (1<<3) +#define CLK_FLG_MAX_I2S_49152KHZ (1<<4) + +#define RK30_CLOCKS_DEFAULT_FLAGS (CLK_FLG_MAX_I2S_12288KHZ/*|CLK_FLG_EXT_27MHZ*/) +#define periph_pll_default periph_pll_297mhz +#define codec_pll_default codec_pll_798mhz +//#define codec_pll_default codec_pll_1064mhz + #endif diff --git a/arch/arm/mach-rk2928/include/mach/clock.h b/arch/arm/mach-rk2928/include/mach/clock.h new file mode 100755 index 000000000000..a94d6c851e3d --- /dev/null +++ b/arch/arm/mach-rk2928/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-rk2928/include/mach/cru.h b/arch/arm/mach-rk2928/include/mach/cru.h new file mode 100755 index 000000000000..b22b9a7be564 --- /dev/null +++ b/arch/arm/mach-rk2928/include/mach/cru.h @@ -0,0 +1,563 @@ +#ifndef __MACH_CRU_H +#define __MACH_CRU_H + +enum rk_plls_id { + APLL_ID = 0, + DPLL_ID, + CPLL_ID, + GPLL_ID, + END_PLL_ID, +}; + +/*****cru reg offset*****/ + +#define CRU_MODE_CON 0x40 +#define CRU_CLKSEL_CON 0x44 +#define CRU_CLKGATE_CON 0xd0 +#define CRU_GLB_SRST_FST 0x100 +#define CRU_GLB_SRST_SND 0x104 +#define CRU_SOFTRST_CON 0x110 + +#define PLL_CONS(id, i) ((id) * 0x10 + ((i) * 4)) + +#define CRU_CLKSELS_CON_CNT (35) +#define CRU_CLKSELS_CON(i) (CRU_CLKSEL_CON + ((i) * 4)) + +#define CRU_CLKGATES_CON_CNT (10) +#define CRU_CLKGATES_CON(i) (CRU_CLKGATE_CON + ((i) * 4)) + +#define CRU_SOFTRSTS_CON_CNT (9) +#define CRU_SOFTRSTS_CON(i) (CRU_SOFTRST_CON + ((i) * 4)) + +#define CRU_MISC_CON (0x134) +#define CRU_GLB_CNT_TH (0x140) + +/*PLL_CON 0,1,2*/ +#define PLL_PWR_ON (1) +#define PLL_PWR_DN (0) +#define PLL_BYPASS (1 << 15) +#define PLL_NO_BYPASS (0 << 15) +//con0 +#define PLL_BYPASS_SHIFT (15) + +#define PLL_POSTDIV1_MASK (0x7) +#define PLL_POSTDIV1_SHIFT (12) +#define PLL_FBDIV_MASK (0xfff) +#define PLL_FBDIV_SHIFT (0) + +//con1 +#define PLL_RSTMODE_SHIFT (15) +#define PLL_RST_SHIFT (14) +#define PLL_PWR_DN_SHIFT (13) +#define PLL_DSMPD_SHIFT (12) +#define PLL_LOCK_SHIFT (10) + +#define PLL_POSTDIV2_MASK (0x7) +#define PLL_POSTDIV2_SHIFT (6) +#define PLL_REFDIV_MASK (0x3f) +#define PLL_REFDIV_SHIFT (0) + +//con2 +#define PLL_FOUT4PHASE_PWR_DN_SHIFT (27) +#define PLL_FOUTVCO_PWR_DN_SHIFT (26) +#define PLL_FOUTPOSTDIV_PWR_DN_SHIFT (25) +#define PLL_DAC_PWR_DN_SHIFT (24) + +#define PLL_FRAC_MASK (0xffffff) +#define PLL_FRAC_SHIFT (0) + +/********************************************************************/ +#define CRU_GET_REG_BIT_VAL(reg, bits_shift) (((reg) >> (bits_shift)) & (0x1)) +#define CRU_GET_REG_BITS_VAL(reg, bits_shift, msk) (((reg) >> (bits_shift)) & (msk)) +#define CRU_SET_BIT(val, bits_shift) (((val) & (0x1)) << (bits_shift)) +#define CRU_SET_BITS(val, bits_shift, msk) (((val) & (msk)) << (bits_shift)) +#define CRU_W_MSK(bits_shift, msk) ((msk) << ((bits_shift) + 16)) + +#define CRU_W_MSK_SETBITS(val, bits_shift, msk) (CRU_W_MSK(bits_shift, msk) \ + | CRU_SET_BITS(val, bits_shift, msk)) +#define CRU_W_MSK_SETBIT(val, bits_shift) (CRU_W_MSK(bits_shift, 0x1) \ + | CRU_SET_BIT(val, bits_shift)) + +#define PLL_SET_REFDIV(val) CRU_W_MSK_SETBITS(val, PLL_REFDIV_SHIFT, PLL_REFDIV_MASK) +#define PLL_SET_FBDIV(val) CRU_W_MSK_SETBITS(val, PLL_FBDIV_SHIFT, PLL_FBDIV_MASK) +#define PLL_SET_POSTDIV1(val) CRU_W_MSK_SETBITS(val, PLL_POSTDIV1_SHIFT, PLL_POSTDIV1_MASK) +#define PLL_SET_POSTDIV2(val) CRU_W_MSK_SETBITS(val, PLL_POSTDIV2_SHIFT, PLL_POSTDIV2_MASK) +#define PLL_SET_FRAC(val) CRU_SET_BITS(val, PLL_FRAC_SHIFT, PLL_FRAC_MASK) + +#define PLL_GET_REFDIV(reg) CRU_GET_REG_BITS_VAL(reg, PLL_REFDIV_SHIFT, PLL_REFDIV_MASK) +#define PLL_GET_FBDIV(reg) CRU_GET_REG_BITS_VAL(reg, PLL_FBDIV_SHIFT, PLL_FBDIV_MASK) +#define PLL_GET_POSTDIV1(reg) CRU_GET_REG_BITS_VAL(reg, PLL_POSTDIV1_SHIFT, PLL_POSTDIV1_MASK) +#define PLL_GET_POSTDIV2(reg) CRU_GET_REG_BITS_VAL(reg, PLL_POSTDIV2_SHIFT, PLL_POSTDIV2_MASK) +#define PLL_GET_FRAC(reg) CRU_GET_REG_BITS_VAL(reg, PLL_FRAC_SHIFT, PLL_FRAC_MASK) + +//#define APLL_SET_BYPASS(val) CRU_SET_BIT(val, PLL_BYPASS_SHIFT) +#define PLL_SET_DSMPD(val) CRU_SET_BIT(val, PLL_DSMPD_SHIFT) +#define PLL_GET_DSMPD(reg) CRU_GET_REG_BIT_VAL(reg, PLL_DSMPD_SHIFT) +/*******************MODE BITS***************************/ +#define PLL_MODE_MSK(id) (0x1 << ((id) * 4)) +#define PLL_MODE_SHIFT(id) ((id) * 4) +#define PLL_MODE_SLOW(id) (CRU_W_MSK_SETBIT(0x0, PLL_MODE_SHIFT(id))) +#define PLL_MODE_NORM(id) (CRU_W_MSK_SETBIT(0x1, PLL_MODE_SHIFT(id))) +/*******************CLKSEL0 BITS***************************/ +#define CLK_SET_DIV_CON_SUB1(val, bits_shift, msk) CRU_W_MSK_SETBITS((val - 1), bits_shift, msk) + +#define CPU_CLK_PLL_SEL_SHIFT (13) +#define CORE_CLK_PLL_SEL_SHIFT (7) +#define SEL_APLL (0) +#define SEL_GPLL (1) +#define CPU_SEL_PLL(plls) CRU_W_MSK_SETBIT(plls, CPU_CLK_PLL_SEL_SHIFT) +#define CORE_SEL_PLL(plls) CRU_W_MSK_SETBIT(plls, CORE_CLK_PLL_SEL_SHIFT) + +#define ACLK_CPU_DIV_MASK (0x1f) +#define ACLK_CPU_DIV_SHIFT (8) +#define A9_CORE_DIV_MASK (0x1f) +#define A9_CORE_DIV_SHIFT (0) + +#define ACLK_CPU_DIV(val) CLK_SET_DIV_CON_SUB1(val, ACLK_CPU_DIV_SHIFT, ACLK_CPU_DIV_MASK) +#define CLK_CORE_DIV(val) CLK_SET_DIV_CON_SUB1(val, A9_CORE_DIV_SHIFT, A9_CORE_DIV_MASK) +/*******************CLKSEL1 BITS***************************/ +#define PCLK_CPU_DIV_MASK (0x7) +#define PCLK_CPU_DIV_SHIFT (12) +#define HCLK_CPU_DIV_MASK (0x3) +#define HCLK_CPU_DIV_SHIFT (8) +#define ACLK_CORE_DIV_MASK (0x1) +#define ACLK_CORE_DIV_SHIFT (4) +#define CORE_PERIPH_DIV_MASK (0xf) +#define CORE_PERIPH_DIV_SHIFT (0) + +#define PCLK_CPU_DIV(val) CLK_SET_DIV_CON_SUB1(val, PCLK_CPU_DIV_SHIFT, PCLK_CPU_DIV_MASK) +#define HCLK_CPU_DIV(val) CLK_SET_DIV_CON_SUB1(val, HCLK_CPU_DIV_SHIFT, HCLK_CPU_DIV_MASK) +#define ACLK_CORE_DIV(val) CLK_SET_DIV_CON_SUB1(val, ACLK_CORE_DIV_SHIFT, ACLK_CORE_DIV_MASK) +#define CLK_CORE_PERI_DIV(val) CLK_SET_DIV_CON_SUB1(val, CORE_PERIPH_DIV_SHIFT, CORE_PERIPH_DIV_MASK) + +/*******************clksel10***************************/ +#define PERI_PLL_SEL_SHIFT 15 +#define PERI_PCLK_DIV_MASK (0x3) +#define PERI_PCLK_DIV_SHIFT (12) +#define PERI_HCLK_DIV_MASK (0x3) +#define PERI_HCLK_DIV_SHIFT (8) +#define PERI_ACLK_DIV_MASK (0x1f) +#define PERI_ACLK_DIV_SHIFT (0) + +#define SEL_2PLL_GPLL (0) +#define SEL_2PLL_CPLL (1) + +#define RATIO_11 (0) +#define RATIO_21 (1) +#define RATIO_41 (2) +#define RATIO_81 (3) + +#define PERI_CLK_SEL_PLL(plls) CRU_W_MSK_SETBIT(plls, PERI_PLL_SEL_SHIFT) +#define PERI_SET_A2P_RATIO(ratio) CRU_W_MSK_SETBITS(ratio, PERI_PCLK_DIV_SHIFT, PERI_PCLK_DIV_MASK) +#define PERI_SET_A2H_RATIO(ratio) CRU_W_MSK_SETBITS(ratio, PERI_HCLK_DIV_SHIFT, PERI_PCLK_DIV_MASK) +#define PERI_SET_ACLK_DIV(val) CRU_W_MSK_SETBITS(val, PERI_ACLK_DIV_SHIFT, PERI_ACLK_DIV_MASK) +/*******************gate BITS***************************/ +#define CLK_GATE_CLKID_CONS(i) CRU_CLKGATES_CON((i) / 16) + +#define CLK_GATE(i) (1 << ((i)%16)) +#define CLK_UN_GATE(i) (0) + +#define CLK_GATE_W_MSK(i) (1 << (((i) % 16) + 16)) +#define CLK_GATE_CLKID(i) (16 * (i)) + +enum cru_clk_gate { + /* SCU CLK GATE 0 CON */ + CLK_GATE_CORE_PERIPH = CLK_GATE_CLKID(0), + CLK_GATE_CPU_GPLL, + CLK_GATE_DDRPHY_SRC, + CLK_GATE_ACLK_CPU, + + CLK_GATE_HCLK_CPU, + CLK_GATE_PCLK_CPU, + CLK_GATE_0RES6, + CLK_GATE_ACLK_CORE, + + CLK_GATE_0RES8, + CLK_GATE_I2S_SRC, + CLK_GATE_I2S_FRAC_SRC, + CLK_GATE_HCLK_VIO_PRE, + + CLK_GATE_0RES12, + CLK_GATE_0RES13, + CLK_GATE_0RES14, + CLK_GATE_TESTCLK, + + CLK_GATE_TIMER0 = CLK_GATE_CLKID(1), + CLK_GATE_TIMER1, + CLK_GATE_1RES2, + CLK_GATE_JTAG, + + CLK_GATE_1RES4, + CLK_GATE_OTGPHY0, + CLK_GATE_OTGPHY1, + CLK_GATE_1RES7, + + CLK_GATE_UART0_SRC, + CLK_GATE_UART0_FRAC_SRC, + CLK_GATE_UART1_SRC, + CLK_GATE_UART1_FRAC_SRC, + + CLK_GATE_UART2_SRC, + CLK_GATE_UART2_FRAC_SRC, + CLK_GATE_1RES14, + CLK_GATE_1RES15, + + CLK_GATE_PERIPH_SRC = CLK_GATE_CLKID(2), + CLK_GATE_ACLK_PERIPH, + CLK_GATE_HCLK_PERIPH, + CLK_GATE_PCLK_PERIPH, + + CLK_GATE_2RES4, + CLK_GATE_2RES5, + CLK_GATE_2RES6, + CLK_GATE_2RES7, + + CLK_GATE_SARADC_SRC, + CLK_GATE_SPI0_SRC, + CLK_GATE_2RES10, + CLK_GATE_MMC0_SRC, + + CLK_GATE_2RES12, + CLK_GATE_SDIO_SRC, + CLK_GATE_EMMC_SRC, + CLK_GATE_2RES15, + + CLK_GATE_ACLK_VIO_SRC = CLK_GATE_CLKID(3), + CLK_GATE_DCLK_LCDC0_SRC, + CLK_GATE_SCLK_LCDC_SRC, + CLK_GATE_PCLKIN_CIF, + + CLK_GATE_ACLK_GPS, + CLK_GATE_3RES5, + CLK_GATE_3RES6, + CLK_GATE_CIF_OUT_SRC, + + CLK_GATE_PCLK_HDMI, + CLK_GATE_ACLK_VEPU_SRC, + CLK_GATE_HCLK_VEPU, + CLK_GATE_ACLK_VDPU_SRC, + + CLK_GATE_HCLK_VDPU, + CLK_GATE_GPU_PRE, + CLK_GATE_3RES14, + CLK_GATE_3RES15, + + CLK_GATE_HCLK_PERI_AXI_MATRIX = CLK_GATE_CLKID(4), + CLK_GATE_PCLK_PERI_AXI_MATRIX, + CLK_GATE_ACLK_CPU_PERI, + CLK_GATE_ACLK_PERI_AXI_MATRIX, + + CLK_GATE_4RES4, + CLK_GATE_4RES5, + CLK_GATE_4RES6, + CLK_GATE_4RES7, + + CLK_GATE_4RES8, + CLK_GATE_4RES9, + CLK_GATE_ACLK_STRC_SYS, + CLK_GATE_4RES11, + + CLK_GATE_ACLK_INTMEM, + CLK_GATE_4RES13, + CLK_GATE_4RES14, + CLK_GATE_4RES15, + + CLK_GATE_5RES0 = CLK_GATE_CLKID(5), + CLK_GATE_ACLK_DMAC2, + CLK_GATE_PCLK_EFUSE, + CLK_GATE_5RES3, + + CLK_GATE_PCLK_GRF, + CLK_GATE_5RES5, + CLK_GATE_HCLK_ROM, + CLK_GATE_PCLK_DDRUPCTL, + + CLK_GATE_5RES8, + CLK_GATE_HCLK_NANDC, + CLK_GATE_HCLK_SDMMC0, + CLK_GATE_HCLK_SDIO, + + CLK_GATE_5RES12, + CLK_GATE_HCLK_OTG0, + CLK_GATE_PCLK_ACODEC, + CLK_GATE_5RES15, + + CLK_GATE_ACLK_LCDC0 = CLK_GATE_CLKID(6), + CLK_GATE_HCLK_LCDC0, + CLK_GATE_6RES2, + CLK_GATE_6RES3, + + CLK_GATE_HCLK_CIF, + CLK_GATE_ACLK_CIF, + CLK_GATE_6RES6, + CLK_GATE_6RES7, + + CLK_GATE_6RES8, + CLK_GATE_6RES9, + CLK_GATE_HCLK_RGA, + CLK_GATE_ACLK_RGA, + + CLK_GATE_HCLK_VIO_BUS, + CLK_GATE_ACLK_VIO0, + CLK_GATE_6RES14, + CLK_GATE_6RES15, + + CLK_GATE_HCLK_EMMC = CLK_GATE_CLKID(7), + CLK_GATE_7RES1, + CLK_GATE_HCLK_I2S, + CLK_GATE_HCLK_OTG1, + + CLK_GATE_7RES4, + CLK_GATE_7RES5, + CLK_GATE_7RES6, + CLK_GATE_PCLK_TIMER0, + + CLK_GATE_PCLK_TIMER1, + CLK_GATE_7RES9, + CLK_GATE_PCLK_PWM01, + CLK_GATE_7RES11, + + CLK_GATE_PCLK_SPI0, + CLK_GATE_7RES13, + CLK_GATE_PCLK_SARADC, + CLK_GATE_PCLK_WDT, + + CLK_GATE_PCLK_UART0 = CLK_GATE_CLKID(8), + CLK_GATE_PCLK_UART1, + CLK_GATE_PCLK_UART2, + CLK_GATE_8RES3, + + CLK_GATE_PCLK_I2C0, + CLK_GATE_PCLK_I2C1, + CLK_GATE_PCLK_I2C2, + CLK_GATE_PCLK_I2C3, + + CLK_GATE_8RES8, + CLK_GATE_PCLK_GPIO0, + CLK_GATE_PCLK_GPIO1, + CLK_GATE_PCLK_GPIO2, + + CLK_GATE_PCLK_GPIO3, + CLK_GATE_8RES13, + CLK_GATE_8RES14, + CLK_GATE_8RES15, + + CLK_GATE_CLK_CORE_DBG = CLK_GATE_CLKID(9), + CLK_GATE_PCLK_DBG, + CLK_GATE_9RES2, + CLK_GATE_9RES3, + + CLK_GATE_CLK_L2C, + CLK_GATE_9RES5, + CLK_GATE_9RES6, + CLK_GATE_9RES7, + + CLK_GATE_9RES8, + CLK_GATE_9RES9, + CLK_GATE_HCLK_USB_PERI, + CLK_GATE_HCLK_PERI_ARBI, + + CLK_GATE_ACLK_PERI_NIU, + CLK_GATE_9RES13, + CLK_GATE_9RES14, + CLK_GATE_9RES15, + + CLK_GATE_MAX, +}; + +#define SOFT_RST_ID(i) (16 * (i)) + +enum cru_soft_reset { + SOFT_RST_CORE_SRST_WDT_SEL = SOFT_RST_ID(0), + SOFT_RST_ACLK_CORE, + SOFT_RST_MCORE, + SOFT_RST_0RES3, + + SOFT_RST_0RES4, + SOFT_RST_0RES5, + SOFT_RST_0RES6, + SOFT_RST_MCORE_DBG, + + SOFT_RST_0RES8, + SOFT_RST_0RES9, + SOFT_RST_0RES10, + SOFT_RST_0RES11, + + SOFT_RST_CORE0_WDT, + SOFT_RST_0RES13, + SOFT_RST_STRC_SYS_AXI, + SOFT_RST_0RES15, + + SOFT_RST_CPU_STRC_SYS_AXI = SOFT_RST_ID(1), + SOFT_RST_CPUSYS_AHB, + SOFT_RST_L2MEM_CON_AXI, + SOFT_RST_AHB2APB, + + SOFT_RST_1RES4, + SOFT_RST_INTMEM, + SOFT_RST_ROM, + SOFT_RST_PERI_NIU, + + SOFT_RST_I2S, + SOFT_RST_1RES9, + SOFT_RST_1RES10, + SOFT_RST_TIMER0, + + SOFT_RST_TIMER1, + SOFT_RST_1RES13, + SOFT_RST_EFUSE_APB, + SOFT_RST_ACODEC, + + SOFT_RST_GPIO0 = SOFT_RST_ID(2), + SOFT_RST_GPIO1, + SOFT_RST_GPIO2, + SOFT_RST_GPIO3, + + SOFT_RST_2RES4, + SOFT_RST_2RES5, + SOFT_RST_2RES6, + SOFT_RST_UART0, + + SOFT_RST_UART1, + SOFT_RST_UART2, + SOFT_RST_2RES10, + SOFT_RST_I2C0, + + SOFT_RST_I2C1, + SOFT_RST_I2C2, + SOFT_RST_I2C3, + SOFT_RST_2RES15, + + SOFT_RST_PWM0 = SOFT_RST_ID(3), + SOFT_RST_PWM1, + SOFT_RST_DAP_PO, + SOFT_RST_DAP, + + SOFT_RST_DAP_SYS, + SOFT_RST_3RES5, + SOFT_RST_3RES6, + SOFT_RST_GRF, + + SOFT_RST_I2C, + SOFT_RST_PERIPHSYS_AXI, + SOFT_RST_PERIPHSYS_AHB, + SOFT_RST_PERIPHSYS_APB, + + SOFT_RST_PWM2, + SOFT_RST_CPU_PERI, + SOFT_RST_EMEM_PERI, + SOFT_RST_USB_PERI, + + SOFT_RST_DMA2 = SOFT_RST_ID(4), + SOFT_RST_4RES1, + SOFT_RST_4RES2, + SOFT_RST_GPS, + + SOFT_RST_NANDC, + SOFT_RST_USBOTG0, + SOFT_RST_USBPHY0, + SOFT_RST_OTGC0, + + SOFT_RST_USBOTG1, + SOFT_RST_USBPHY1, + SOFT_RST_OTGC1, + SOFT_RST_4RES11, + + SOFT_RST_4RES12, + SOFT_RST_4RES13, + SOFT_RST_4RES14, + SOFT_RST_DDRMSCH, + + SOFT_RST_5RES0 = SOFT_RST_ID(5), + SOFT_RST_MMC0, + SOFT_RST_SDIO, + SOFT_RST_EMMC, + + SOFT_RST_SPI0, + SOFT_RST_5RES5, + SOFT_RST_WDT, + SOFT_RST_SARADC, + + SOFT_RST_DDRPHY, + SOFT_RST_DDRPHY_APB, + SOFT_RST_DDRCTRL, + SOFT_RST_DDRCTRL_APB, + + SOFT_RST_5RES12, + SOFT_RST_5RES13, + SOFT_RST_5RES14, + SOFT_RST_5RES15, + + SOFT_RST_HDMI_PCLK = SOFT_RST_ID(6), + SOFT_RST_HDMI_DCLK, + SOFT_RST_VIO0_AXI, + SOFT_RST_VIO_BUS_AHB, + + SOFT_RST_LCDC0_AXI, + SOFT_RST_LCDC0_AHB, + SOFT_RST_LCDC0_DCLK, + SOFT_RST_UTMI0, + + SOFT_RST_UTMI1, + SOFT_RST_USBPOR, + SOFT_RST_6RES10, + SOFT_RST_6RES11, + + SOFT_RST_RGA_AXI, + SOFT_RST_RGA_AHB, + SOFT_RST_CIF0, + SOFT_RST_LCDC_SCL, + + SOFT_RST_VCODEC_AXI = SOFT_RST_ID(7), + SOFT_RST_VCODEC_AHB, + SOFT_RST_VIO1_AXI, + SOFT_RST_CPU_VCODEC, + + SOFT_RST_VCODEC_NIU_AXI, + SOFT_RST_7RES5, + SOFT_RST_7RES6, + SOFT_RST_7RES7, + + SOFT_RST_GPU, + SOFT_RST_7RES9, + SOFT_RST_GPU_NIU_AXI, + SOFT_RST_7RES11, + + SOFT_RST_7RES12, + SOFT_RST_7RES13, + SOFT_RST_7RES14, + SOFT_RST_7RES15, + + SOFT_RST_8RES0 = SOFT_RST_ID(8), + SOFT_RST_8RES1, + SOFT_RST_CORE_DBG, + SOFT_RST_DBG_APB, + + SOFT_RST_8RES4, + SOFT_RST_8RES5, + SOFT_RST_8RES6, + SOFT_RST_8RES7, + + SOFT_RST_8RES8, + SOFT_RST_8RES9, + SOFT_RST_8RES10, + SOFT_RST_8RES11, + + SOFT_RST_8RES12, + SOFT_RST_8RES13, + SOFT_RST_8RES14, + SOFT_RST_8RES15, + + SOFT_RST_MAX, +}; + +/*****cru reg end*****/ +static inline void cru_set_soft_reset(enum cru_soft_reset idx, bool on) +{ + const void __iomem *reg = RK2928_CRU_BASE + CRU_SOFTRSTS_CON(idx >> 4); + u32 val = on ? 0x10001U << (idx & 0xf) : 0x10000U << (idx & 0xf); + writel_relaxed(val, reg); + dsb(); +} + +#endif