From: 黄涛 Date: Tue, 11 Sep 2012 05:40:14 +0000 (+0800) Subject: rk30: move clock.h/clock.c to plat-rk X-Git-Tag: firefly_0821_release~8660 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=1d638c2d90ff9aed92977f14491ffd8ebdf33b0b;p=firefly-linux-kernel-4.4.55.git rk30: move clock.h/clock.c to plat-rk --- diff --git a/arch/arm/mach-rk30/Makefile b/arch/arm/mach-rk30/Makefile index 11b2f7fe594a..71a2aa7ef01b 100755 --- a/arch/arm/mach-rk30/Makefile +++ b/arch/arm/mach-rk30/Makefile @@ -1,6 +1,6 @@ EXTRA_CFLAGS += -Os ifneq ($(CONFIG_RK_FPGA),y) -obj-y += clock.o +obj-y += ../plat-rk/clock.o obj-$(CONFIG_ARCH_RK30XX) += clock_data.o obj-$(CONFIG_ARCH_RK3066B) += clock_data-rk3066b.o endif diff --git a/arch/arm/mach-rk30/clock.c b/arch/arm/mach-rk30/clock.c deleted file mode 100755 index a763cc74e953..000000000000 --- a/arch/arm/mach-rk30/clock.c +++ /dev/null @@ -1,751 +0,0 @@ -/* 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) - list_add(&clk->sibling, &clk->parent->children); - else - list_add(&clk->sibling, &root_clks); - list_add(&clk->node, &clocks); - mutex_unlock(&clocks_mutex); - return 0; -} - -/************************************************************/ -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); - } - 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; - - //CLOCK_PRINTK_LOG("**will set %s rate %lu\n", clk->name, rate); - - old_rate = clk->rate; - if (clk->notifier_count) - clk_notify(clk, CLK_PRE_RATE_CHANGE, old_rate, rate); - - ret = clk->set_rate(clk, rate); - - if (ret == 0) { - __clk_recalc(clk); - CLOCK_PRINTK_LOG("**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****************************************************/ - -struct clk_node *clk_get_dvfs_info(struct clk *clk) -{ - return clk->dvfs_info; -} - -int clk_set_rate_locked(struct clk * clk,unsigned long rate) -{ - int ret; - //CLOCK_PRINTK_DBG("%s dvfs clk_set_locked\n",clk->name); - LOCK(); - ret=clk_set_rate_nolock(clk, rate);; - UNLOCK(); - return ret; - -} -void clk_register_dvfs(struct clk_node *dvfs_clk, struct clk *clk) -{ - clk->dvfs_info = dvfs_clk; -} - - -/*------------------------------------------------------------------------- - * Optional clock functions defined in include/linux/clk.h - *-------------------------------------------------------------------------*/ -#ifdef RK30_CLK_OFFBOARD_TEST -long rk30_clk_round_rate(struct clk *clk, unsigned long rate) -#else -long clk_round_rate(struct clk *clk, unsigned long rate) -#endif -{ - long ret = 0; - - if (clk == NULL || IS_ERR(clk)) - return 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 (clk->dvfs_info!=NULL&&is_support_dvfs(clk->dvfs_info)) - return dvfs_set_rate(clk, rate); - - LOCK(); - ret = clk_set_rate_nolock(clk, rate); - UNLOCK(); - - return ret; -} -#ifdef RK30_CLK_OFFBOARD_TEST -EXPORT_SYMBOL(rk30_clk_set_rate); -#else -EXPORT_SYMBOL(clk_set_rate); -#endif - - -#ifdef RK30_CLK_OFFBOARD_TEST -int rk30_clk_set_parent(struct clk *clk, struct clk *parent) -#else -int clk_set_parent(struct clk *clk, struct clk *parent) -#endif -{ - int ret = -EINVAL; - - if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent)) - return ret; - - if (clk->set_parent==NULL||clk->parents == NULL) - return ret; - - LOCK(); - if (clk->usecount == 0) - ret = clk_set_parent_nolock(clk, parent); - else - ret = -EBUSY; - UNLOCK(); - - return ret; -} -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; -} - -#ifdef CONFIG_RK_CLOCK_PROC -static int proc_clk_show(struct seq_file *s, void *v) -{ - struct clk* clk; - - if(!dump_def_ops) - return 0; - - if(dump_def_ops->dump_clk) - { - mutex_lock(&clocks_mutex); - list_for_each_entry(clk, &clocks, node) { - if (!clk->parent) - { - dump_def_ops->dump_clk(s, clk, 0,&clocks); - } - } - mutex_unlock(&clocks_mutex); - } - if(dump_def_ops->dump_regs) - dump_def_ops->dump_regs(s); - return 0; -} - - -static int proc_clk_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_clk_show, NULL); -} - -static const struct file_operations proc_clk_fops = { - .open = proc_clk_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int __init clk_proc_init(void) -{ - proc_create("clocks", 0, NULL, &proc_clk_fops); - return 0; - -} -late_initcall(clk_proc_init); -#endif /* CONFIG_RK_CLOCK_PROC */ - diff --git a/arch/arm/mach-rk30/clock.h b/arch/arm/mach-rk30/clock.h deleted file mode 100644 index fdecd15d4975..000000000000 --- a/arch/arm/mach-rk30/clock.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef __MACH_CLOCK_H__ -#define __MACH_CLOCK_H__ - -#ifndef CONFIG_ARCH_RK30 -#define RK30_CLK_OFFBOARD_TEST -#endif - - -/* Clock flags */ -/* bit 0 is free */ -#define RATE_FIXED (1 << 1) /* Fixed clock rate */ -#define CONFIG_PARTICIPANT (1 << 10) /* Fundamental clock */ -#define IS_PD (1 << 2) /* Power Domain */ - -enum _clk_i2s_rate_support { - i2s_8192khz = 8192000, - i2s_11289_6khz = 11289600, - i2s_12288khz = 12288000, - i2s_22579_2khz = 22579200, - i2s_24576khz = 24576000,//HDMI - i2s_49152khz = 24576000,//HDMI -}; - -struct _pll_data{ - u8 id; - void *table; -}; -//struct clk_node; -struct clk { - struct list_head node; - const char *name; - struct clk *parent; - struct list_head children; - struct list_head sibling; /* node for children */ - - int (*mode)(struct clk *clk, int on); - unsigned long (*recalc)(struct clk *); /* if null, follow parent */ - int (*set_rate)(struct clk *, unsigned long); - long (*round_rate)(struct clk *, unsigned long); - struct clk* (*get_parent)(struct clk *); /* get clk's parent from the hardware. default is clksel_get_parent if parents present */ - int (*set_parent)(struct clk *, struct clk *); /* default is clksel_set_parent if parents present */ - - unsigned long rate; - u32 flags; - s16 usecount; - u16 notifier_count; - u8 gate_idx; - struct _pll_data *pll; - u32 clksel_con; - u32 div_mask; - u32 div_shift; - u32 div_max; - u32 src_mask; - u32 src_shift; - - struct clk **parents; - u8 parents_num; - struct clk_node *dvfs_info; - -}; - -int __init clk_disable_unused(void); -void clk_recalculate_root_clocks_nolock(void); -void clk_recalculate_root_clocks(void); -int clk_register(struct clk *clk); -void clk_register_default_ops_clk(struct clk *clk); - -int clk_enable_nolock(struct clk *clk); -void clk_disable_nolock(struct clk *clk); -long clk_round_rate_nolock(struct clk *clk, unsigned long rate); -int clk_set_rate_nolock(struct clk *clk, unsigned long rate); -int clk_set_parent_nolock(struct clk *clk, struct clk *parent); -int clk_set_rate_locked(struct clk * clk,unsigned long rate); -void clk_register_dvfs(struct clk_node *dvfs_clk, struct clk *clk); -struct clk_node *clk_get_dvfs_info(struct clk *clk); -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-rk30/clock_data-rk3066b.c b/arch/arm/mach-rk30/clock_data-rk3066b.c index e0b57663173e..70c0671d23d7 100644 --- a/arch/arm/mach-rk30/clock_data-rk3066b.c +++ b/arch/arm/mach-rk30/clock_data-rk3066b.c @@ -23,7 +23,7 @@ #include #include #include -#include "clock.h" +#include #include #include #include diff --git a/arch/arm/mach-rk30/clock_data.c b/arch/arm/mach-rk30/clock_data.c index fe432f068cee..7b9e563e45ec 100755 --- a/arch/arm/mach-rk30/clock_data.c +++ b/arch/arm/mach-rk30/clock_data.c @@ -23,7 +23,7 @@ #include #include #include -#include "clock.h" +#include #include #include #include diff --git a/arch/arm/mach-rk30/dvfs.c b/arch/arm/mach-rk30/dvfs.c index 4bc6752ca626..ebe038dd92cf 100755 --- a/arch/arm/mach-rk30/dvfs.c +++ b/arch/arm/mach-rk30/dvfs.c @@ -20,7 +20,6 @@ #include #include #include -#include "clock.h" #include #include #include diff --git a/arch/arm/mach-rk30/include/mach/clock.h b/arch/arm/mach-rk30/include/mach/clock.h index a94d6c851e3d..94b35428fd3c 100755 --- a/arch/arm/mach-rk30/include/mach/clock.h +++ b/arch/arm/mach-rk30/include/mach/clock.h @@ -1,81 +1 @@ -/* 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 - - - - - +#include diff --git a/arch/arm/plat-rk/clock.c b/arch/arm/plat-rk/clock.c new file mode 100755 index 000000000000..42f2c7f97350 --- /dev/null +++ b/arch/arm/plat-rk/clock.c @@ -0,0 +1,750 @@ +/* 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 +#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) + list_add(&clk->sibling, &clk->parent->children); + else + list_add(&clk->sibling, &root_clks); + list_add(&clk->node, &clocks); + mutex_unlock(&clocks_mutex); + return 0; +} + +/************************************************************/ +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); + } + 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; + + //CLOCK_PRINTK_LOG("**will set %s rate %lu\n", clk->name, rate); + + old_rate = clk->rate; + if (clk->notifier_count) + clk_notify(clk, CLK_PRE_RATE_CHANGE, old_rate, rate); + + ret = clk->set_rate(clk, rate); + + if (ret == 0) { + __clk_recalc(clk); + CLOCK_PRINTK_LOG("**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****************************************************/ + +struct clk_node *clk_get_dvfs_info(struct clk *clk) +{ + return clk->dvfs_info; +} + +int clk_set_rate_locked(struct clk * clk,unsigned long rate) +{ + int ret; + //CLOCK_PRINTK_DBG("%s dvfs clk_set_locked\n",clk->name); + LOCK(); + ret=clk_set_rate_nolock(clk, rate);; + UNLOCK(); + return ret; + +} +void clk_register_dvfs(struct clk_node *dvfs_clk, struct clk *clk) +{ + clk->dvfs_info = dvfs_clk; +} + + +/*------------------------------------------------------------------------- + * Optional clock functions defined in include/linux/clk.h + *-------------------------------------------------------------------------*/ +#ifdef RK30_CLK_OFFBOARD_TEST +long rk30_clk_round_rate(struct clk *clk, unsigned long rate) +#else +long clk_round_rate(struct clk *clk, unsigned long rate) +#endif +{ + long ret = 0; + + if (clk == NULL || IS_ERR(clk)) + return 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 (clk->dvfs_info!=NULL&&is_support_dvfs(clk->dvfs_info)) + return dvfs_set_rate(clk, rate); + + LOCK(); + ret = clk_set_rate_nolock(clk, rate); + UNLOCK(); + + return ret; +} +#ifdef RK30_CLK_OFFBOARD_TEST +EXPORT_SYMBOL(rk30_clk_set_rate); +#else +EXPORT_SYMBOL(clk_set_rate); +#endif + + +#ifdef RK30_CLK_OFFBOARD_TEST +int rk30_clk_set_parent(struct clk *clk, struct clk *parent) +#else +int clk_set_parent(struct clk *clk, struct clk *parent) +#endif +{ + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent)) + return ret; + + if (clk->set_parent==NULL||clk->parents == NULL) + return ret; + + LOCK(); + if (clk->usecount == 0) + ret = clk_set_parent_nolock(clk, parent); + else + ret = -EBUSY; + UNLOCK(); + + return ret; +} +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; +} + +#ifdef CONFIG_RK_CLOCK_PROC +static int proc_clk_show(struct seq_file *s, void *v) +{ + struct clk* clk; + + if(!dump_def_ops) + return 0; + + if(dump_def_ops->dump_clk) + { + mutex_lock(&clocks_mutex); + list_for_each_entry(clk, &clocks, node) { + if (!clk->parent) + { + dump_def_ops->dump_clk(s, clk, 0,&clocks); + } + } + mutex_unlock(&clocks_mutex); + } + if(dump_def_ops->dump_regs) + dump_def_ops->dump_regs(s); + return 0; +} + + +static int proc_clk_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_clk_show, NULL); +} + +static const struct file_operations proc_clk_fops = { + .open = proc_clk_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init clk_proc_init(void) +{ + proc_create("clocks", 0, NULL, &proc_clk_fops); + return 0; + +} +late_initcall(clk_proc_init); +#endif /* CONFIG_RK_CLOCK_PROC */ + diff --git a/arch/arm/plat-rk/include/plat/clock.h b/arch/arm/plat-rk/include/plat/clock.h new file mode 100644 index 000000000000..09286d43ff84 --- /dev/null +++ b/arch/arm/plat-rk/include/plat/clock.h @@ -0,0 +1,154 @@ +#ifndef __PLAT_CLOCK_H__ +#define __PLAT_CLOCK_H__ + +#ifndef CONFIG_ARCH_RK30 +#define RK30_CLK_OFFBOARD_TEST +#endif + + +/* Clock flags */ +/* bit 0 is free */ +#define RATE_FIXED (1 << 1) /* Fixed clock rate */ +#define CONFIG_PARTICIPANT (1 << 10) /* Fundamental clock */ +#define IS_PD (1 << 2) /* Power Domain */ + +enum _clk_i2s_rate_support { + i2s_8192khz = 8192000, + i2s_11289_6khz = 11289600, + i2s_12288khz = 12288000, + i2s_22579_2khz = 22579200, + i2s_24576khz = 24576000,//HDMI + i2s_49152khz = 24576000,//HDMI +}; + +struct _pll_data{ + u8 id; + void *table; +}; +//struct clk_node; +struct clk { + struct list_head node; + const char *name; + struct clk *parent; + struct list_head children; + struct list_head sibling; /* node for children */ + + int (*mode)(struct clk *clk, int on); + unsigned long (*recalc)(struct clk *); /* if null, follow parent */ + int (*set_rate)(struct clk *, unsigned long); + long (*round_rate)(struct clk *, unsigned long); + struct clk* (*get_parent)(struct clk *); /* get clk's parent from the hardware. default is clksel_get_parent if parents present */ + int (*set_parent)(struct clk *, struct clk *); /* default is clksel_set_parent if parents present */ + + unsigned long rate; + u32 flags; + s16 usecount; + u16 notifier_count; + u8 gate_idx; + struct _pll_data *pll; + u32 clksel_con; + u32 div_mask; + u32 div_shift; + u32 div_max; + u32 src_mask; + u32 src_shift; + + struct clk **parents; + u8 parents_num; + struct clk_node *dvfs_info; + +}; + +int __init clk_disable_unused(void); +void clk_recalculate_root_clocks_nolock(void); +void clk_recalculate_root_clocks(void); +int clk_register(struct clk *clk); +void clk_register_default_ops_clk(struct clk *clk); + +int clk_enable_nolock(struct clk *clk); +void clk_disable_nolock(struct clk *clk); +long clk_round_rate_nolock(struct clk *clk, unsigned long rate); +int clk_set_rate_nolock(struct clk *clk, unsigned long rate); +int clk_set_parent_nolock(struct clk *clk, struct clk *parent); +int clk_set_rate_locked(struct clk * clk,unsigned long rate); +void clk_register_dvfs(struct clk_node *dvfs_clk, struct clk *clk); +struct clk_node *clk_get_dvfs_info(struct clk *clk); +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 inline void clk_register_dump_ops(struct clk_dump_ops *ops) {} +#endif + +/** + * 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