[PATCH 18/74] ST SPEAr: enhanced spear clock framework
Viresh KUMAR
viresh.kumar at st.com
Mon Aug 30 06:38:47 EDT 2010
From: Shiraz Hashim <shiraz.hashim at st.com>
Added support for
- recursive enabling of parent clock
- recursive disabling of parent clock
- recalc called only when clk is enabled in h/w
Signed-off-by: Shiraz Hashim <shiraz.hashim at st.com>
Signed-off-by: Viresh Kumar <viresh.kumar at st.com>
---
arch/arm/plat-spear/clock.c | 199 +++++++++++++++++++++++++++++--------------
1 files changed, 136 insertions(+), 63 deletions(-)
diff --git a/arch/arm/plat-spear/clock.c b/arch/arm/plat-spear/clock.c
index 89a0434..b4d8110 100644
--- a/arch/arm/plat-spear/clock.c
+++ b/arch/arm/plat-spear/clock.c
@@ -12,6 +12,7 @@
*/
#include <linux/bug.h>
+#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/list.h>
@@ -22,7 +23,24 @@
static DEFINE_SPINLOCK(clocks_lock);
static LIST_HEAD(root_clks);
-static void propagate_rate(struct list_head *);
+static void propagate_rate(struct clk *);
+
+static int clk_is_enabled(struct clk *clk)
+{
+ unsigned int val;
+
+ if (clk->flags & ALWAYS_ENABLED)
+ return 1;
+
+ BUG_ON(!clk->en_reg);
+ val = readl(clk->en_reg);
+ val = val & (1 << clk->en_reg_bit);
+
+ if (unlikely(clk->flags & RESET_TO_ENABLE))
+ return !val;
+ else
+ return !!val;
+}
static int generic_clk_enable(struct clk *clk)
{
@@ -85,7 +103,7 @@ static struct pclk_info *pclk_info_get(struct clk *clk)
* Set Update pclk, and pclk_info of clk and add clock sibling node to current
* parents children list
*/
-static void update_clk_tree(struct clk *clk, struct pclk_info *pclk_info)
+static void clk_reparent(struct clk *clk, struct pclk_info *pclk_info)
{
unsigned long flags;
@@ -97,6 +115,64 @@ static void update_clk_tree(struct clk *clk, struct pclk_info *pclk_info)
spin_unlock_irqrestore(&clocks_lock, flags);
}
+void do_clk_disable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (!clk->usage_count) {
+ WARN_ON(1);
+ return;
+ }
+
+ clk->usage_count--;
+
+ if (clk->usage_count == 0) {
+ /*
+ * Surely, there are no active childrens or direct users
+ * of this clock
+ */
+ if (clk->pclk)
+ do_clk_disable(clk->pclk);
+
+ if (clk->ops && clk->ops->disable)
+ clk->ops->disable(clk);
+ }
+}
+
+int do_clk_enable(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return -EFAULT;
+
+ if (clk->usage_count == 0) {
+ if (clk->pclk) {
+ ret = do_clk_enable(clk->pclk);
+ if (ret)
+ goto err;
+ }
+ if (clk->ops && clk->ops->enable) {
+ ret = clk->ops->enable(clk);
+ if (ret) {
+ if (clk->pclk)
+ do_clk_disable(clk->pclk);
+ goto err;
+ }
+ }
+ /*
+ * Since the clock is going to be used for the first
+ * time please reclac
+ */
+ if (clk->recalc)
+ clk->recalc(clk);
+ }
+ clk->usage_count++;
+err:
+ return ret;
+}
+
/*
* clk_enable - inform the system when the clock source should be running.
* @clk: clock source
@@ -110,17 +186,9 @@ int clk_enable(struct clk *clk)
unsigned long flags;
int ret = 0;
- if (!clk || IS_ERR(clk))
- return -EFAULT;
-
spin_lock_irqsave(&clocks_lock, flags);
- if (clk->usage_count == 0) {
- if (clk->ops && clk->ops->enable)
- ret = clk->ops->enable(clk);
- }
- clk->usage_count++;
+ ret = do_clk_enable(clk);
spin_unlock_irqrestore(&clocks_lock, flags);
-
return ret;
}
EXPORT_SYMBOL(clk_enable);
@@ -141,17 +209,8 @@ void clk_disable(struct clk *clk)
{
unsigned long flags;
- if (!clk || IS_ERR(clk))
- return;
-
- WARN_ON(clk->usage_count == 0);
-
spin_lock_irqsave(&clocks_lock, flags);
- clk->usage_count--;
- if (clk->usage_count == 0) {
- if (clk->ops && clk->ops->disable)
- clk->ops->disable(clk);
- }
+ do_clk_disable(clk);
spin_unlock_irqrestore(&clocks_lock, flags);
}
EXPORT_SYMBOL(clk_disable);
@@ -178,21 +237,22 @@ EXPORT_SYMBOL(clk_get_rate);
* @clk: clock source
* @parent: parent clock source
*
- * Returns success (0) or negative errno.
+ * Returns success (0) or negative errno. clk usage_count must be zero
+ * before calling this function.
*/
int clk_set_parent(struct clk *clk, struct clk *parent)
{
int i, found = 0, val = 0;
unsigned long flags;
- if (!clk || IS_ERR(clk) || !parent || IS_ERR(parent))
+ if (!clk || !parent)
return -EFAULT;
if (clk->usage_count)
- return -EBUSY;
- if (!clk->pclk_sel)
return -EPERM;
if (clk->pclk == parent)
return 0;
+ if (!clk->pclk_sel)
+ return -EPERM;
/* check if requested parent is in clk parent list */
for (i = 0; i < clk->pclk_sel->pclk_count; i++) {
@@ -214,10 +274,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
spin_unlock_irqrestore(&clocks_lock, flags);
/* reflect parent change in software */
- update_clk_tree(clk, &clk->pclk_sel->pclk_info[i]);
+ clk_reparent(clk, &clk->pclk_sel->pclk_info[i]);
- clk->recalc(clk);
- propagate_rate(&clk->children);
return 0;
}
EXPORT_SYMBOL(clk_set_parent);
@@ -239,11 +297,12 @@ EXPORT_SYMBOL(clk_set_rate);
/* registers clock in platform clock framework */
void clk_register(struct clk_lookup *cl)
{
- struct clk *clk = cl->clk;
+ struct clk *clk;
unsigned long flags;
- if (!clk || IS_ERR(clk))
+ if (!cl || !cl->clk)
return;
+ clk = cl->clk;
spin_lock_irqsave(&clocks_lock, flags);
@@ -265,7 +324,7 @@ void clk_register(struct clk_lookup *cl)
pclk_info = pclk_info_get(clk);
if (!pclk_info) {
- printk(KERN_ERR "CLKDEV: invalid pclk info of clk with"
+ pr_err("CLKDEV: invalid pclk info of clk with"
" %s dev_id and %s con_id\n",
cl->dev_id, cl->con_id);
} else {
@@ -273,6 +332,7 @@ void clk_register(struct clk_lookup *cl)
list_add(&clk->sibling, &pclk_info->pclk->children);
}
}
+
spin_unlock_irqrestore(&clocks_lock, flags);
/* add clock to arm clockdev framework */
@@ -280,22 +340,26 @@ void clk_register(struct clk_lookup *cl)
}
/**
- * propagate_rate - recalculate and propagate all clocks in list head
+ * propagate_rate - recalculate and propagate all clocks to children
*
- * Recalculates all root clocks in list head, which if the clock's .recalc is
- * set correctly, should also propagate their rates.
+ * Recalculates all children clocks
*/
-static void propagate_rate(struct list_head *lhead)
+void propagate_rate(struct clk *pclk)
{
- struct clk *clkp, *_temp;
-
- list_for_each_entry_safe(clkp, _temp, lhead, sibling) {
- if (clkp->recalc)
- clkp->recalc(clkp);
- propagate_rate(&clkp->children);
+ struct clk *clk, *_temp;
+
+ list_for_each_entry_safe(clk, _temp, &pclk->children, sibling) {
+ /* recalc and propogate only if clk is enabled */
+ if (clk_is_enabled(clk)) {
+ if (clk->recalc)
+ clk->recalc(clk);
+ propagate_rate(clk);
+ }
}
}
+/*All recalc functions are called with lock held */
+
/*
* calculates current programmed rate of pll1
*
@@ -309,9 +373,7 @@ void pll_clk_recalc(struct clk *clk)
{
struct pll_clk_config *config = clk->private_data;
unsigned int num = 2, den = 0, val, mode = 0;
- unsigned long flags;
- spin_lock_irqsave(&clocks_lock, flags);
mode = (readl(config->mode_reg) >> config->masks->mode_shift) &
config->masks->mode_mask;
@@ -333,8 +395,9 @@ void pll_clk_recalc(struct clk *clk)
den *= 256;
}
+ BUG_ON(!den);
+
clk->rate = (((clk->pclk->rate/10000) * num) / den) * 10000;
- spin_unlock_irqrestore(&clocks_lock, flags);
}
/* calculates current programmed rate of ahb or apb bus */
@@ -342,13 +405,13 @@ void bus_clk_recalc(struct clk *clk)
{
struct bus_clk_config *config = clk->private_data;
unsigned int div;
- unsigned long flags;
- spin_lock_irqsave(&clocks_lock, flags);
div = ((readl(config->reg) >> config->masks->shift) &
config->masks->mask) + 1;
+
+ BUG_ON(!div);
+
clk->rate = (unsigned long)clk->pclk->rate / div;
- spin_unlock_irqrestore(&clocks_lock, flags);
}
/*
@@ -365,9 +428,7 @@ void aux_clk_recalc(struct clk *clk)
{
struct aux_clk_config *config = clk->private_data;
unsigned int num = 1, den = 1, val, eqn;
- unsigned long flags;
- spin_lock_irqsave(&clocks_lock, flags);
val = readl(config->synth_reg);
eqn = (val >> config->masks->eq_sel_shift) &
@@ -382,10 +443,10 @@ void aux_clk_recalc(struct clk *clk)
/* calculate denominator */
den *= (val >> config->masks->yscale_sel_shift) &
config->masks->yscale_sel_mask;
- val = (((clk->pclk->rate/10000) * num) / den) * 10000;
- clk->rate = val;
- spin_unlock_irqrestore(&clocks_lock, flags);
+ BUG_ON(!den);
+
+ clk->rate = (((clk->pclk->rate/10000) * num) / den) * 10000;
}
/*
@@ -397,17 +458,16 @@ void gpt_clk_recalc(struct clk *clk)
{
struct gpt_clk_config *config = clk->private_data;
unsigned int div = 1, val;
- unsigned long flags;
- spin_lock_irqsave(&clocks_lock, flags);
val = readl(config->synth_reg);
div += (val >> config->masks->mscale_sel_shift) &
config->masks->mscale_sel_mask;
div *= 1 << (((val >> config->masks->nscale_sel_shift) &
config->masks->nscale_sel_mask) + 1);
+ BUG_ON(!div);
+
clk->rate = (unsigned long)clk->pclk->rate / div;
- spin_unlock_irqrestore(&clocks_lock, flags);
}
/*
@@ -425,18 +485,19 @@ void clcd_clk_recalc(struct clk *clk)
{
struct clcd_clk_config *config = clk->private_data;
unsigned int div = 1;
- unsigned long flags, prate;
+ unsigned long prate;
unsigned int val;
- spin_lock_irqsave(&clocks_lock, flags);
val = readl(config->synth_reg);
div = (val >> config->masks->div_factor_shift) &
config->masks->div_factor_mask;
prate = clk->pclk->rate / 1000; /* first level division, make it KHz */
- clk->rate = ((unsigned long)prate << 14 / 2 * div) >> 14;
+
+ BUG_ON(!div);
+
+ clk->rate = (((unsigned long)prate << 12) / (2 * div)) >> 12;
clk->rate *= 1000;
- spin_unlock_irqrestore(&clocks_lock, flags);
}
/*
@@ -445,12 +506,9 @@ void clcd_clk_recalc(struct clk *clk)
*/
void follow_parent(struct clk *clk)
{
- unsigned long flags;
unsigned int div_factor = (clk->div_factor < 1) ? 1 : clk->div_factor;
- spin_lock_irqsave(&clocks_lock, flags);
clk->rate = clk->pclk->rate/div_factor;
- spin_unlock_irqrestore(&clocks_lock, flags);
}
/**
@@ -461,5 +519,20 @@ void follow_parent(struct clk *clk)
*/
void recalc_root_clocks(void)
{
- propagate_rate(&root_clks);
+ struct clk *pclk;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ list_for_each_entry(pclk, &root_clks, sibling) {
+ /*
+ * It doesn't make a sense to recalc and propogate rate
+ * if clk is not enabled in hw.
+ */
+ if (clk_is_enabled(pclk)) {
+ if (pclk->recalc)
+ pclk->recalc(pclk);
+ propagate_rate(pclk);
+ }
+ }
+ spin_unlock_irqrestore(&clocks_lock, flags);
}
--
1.7.2.2
More information about the linux-arm-kernel
mailing list