]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/arm/plat-omap/clock.c
OMAP2/3 clock: remove clk->owner
[linux-2.6-omap-h63xx.git] / arch / arm / plat-omap / clock.c
index bf6a10c5fc4ffdfd5f9abb74bec8da1c1140e57e..ab3d87910a17da2ecd73af4379e8afa6a7a430fe 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/cpufreq.h>
 #include <linux/debugfs.h>
 #include <linux/io.h>
+#include <linux/bootmem.h>
+#include <linux/slab.h>
 
 #include <mach/clock.h>
 
@@ -32,6 +34,142 @@ static DEFINE_SPINLOCK(clockfw_lock);
 
 static struct clk_functions *arch_clock;
 
+/**
+ * omap_clk_for_each_child - call callback on each child clock of clk
+ * @clk: struct clk * to use as the "parent"
+ * @parent_rate: rate of the parent of @clk to pass along
+ * @rate_storage: flag indicating whether current or temporary rates are used
+ * @cb: pointer to a callback function
+ *
+ * For each child clock of @clk, call the callback function @cb, passing
+ * along the contents of @parent_rate and @rate_storage.  If the callback
+ * function returns non-zero, terminate the function and pass along the
+ * return value.
+ */
+static int omap_clk_for_each_child(struct clk *clk, unsigned long parent_rate,
+                                  u8 rate_storage,
+                                  int (*cb)(struct clk *clk,
+                                            unsigned long parent_rate,
+                                            u8 rate_storage))
+{
+       struct clk_child *child;
+       int ret;
+
+       list_for_each_entry(child, &clk->children, node) {
+               ret = (*cb)(child->clk, parent_rate, rate_storage);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+/**
+ * omap_clk_has_children - does clk @clk have any child clocks?
+ * @clk: struct clk * to test for child clocks
+ *
+ * If clock @clk has any child clocks, return 1; otherwise, return 0.
+ */
+static int omap_clk_has_children(struct clk *clk)
+{
+       return (list_empty(&clk->children)) ? 0 : 1;
+}
+
+/**
+ * _do_propagate_rate - callback function for rate propagation
+ * @clk: struct clk * to recalc and propagate from
+ * @parent_rate: rate of the parent of @clk, to use in recalculation
+ * @rate_storage: flag indicating whether current or temporary rates are used
+ *
+ * If @clk has a recalc function, call it.  If @clk has any children,
+ * propagate @clk's rate.  Returns 0.
+ */
+static int _do_propagate_rate(struct clk *clk, unsigned long parent_rate,
+                             u8 rate_storage)
+{
+       if (clk->recalc)
+               clk->recalc(clk, parent_rate, rate_storage);
+       if (omap_clk_has_children(clk))
+               propagate_rate(clk, rate_storage);
+       return 0;
+}
+
+/**
+ * omap_clk_add_child - add a child clock @clk2 to @clk
+ * @clk: parent struct clk *
+ * @clk2: new child struct clk *
+ *
+ * Add a child clock @clk2 to the list of children of parent clock
+ * @clk.  Will potentially allocate memory from bootmem or, if
+ * available, from slab.  Must only be called with the clock framework
+ * spinlock held.  No return value.
+ */
+void omap_clk_add_child(struct clk *clk, struct clk *clk2)
+{
+       struct clk_child *child;
+       int reuse = 0;
+
+       if (!clk->children.next)
+               INIT_LIST_HEAD(&clk->children);
+
+       list_for_each_entry(child, &clk->children, node) {
+               if (child->flags & CLK_CHILD_DELETED) {
+                       reuse = 1;
+                       child->flags &= ~CLK_CHILD_DELETED;
+                       break;
+               }
+       }
+
+       if (!reuse) {
+               if (slab_is_available())
+                       child = kmalloc(sizeof(struct clk_child), GFP_ATOMIC);
+               else
+                       child = alloc_bootmem(sizeof(struct clk_child));
+
+               if (!child) {
+                       WARN_ON(1);
+                       return;
+               }
+
+               memset(child, 0, sizeof(struct clk_child));
+
+               if (slab_is_available())
+                       child->flags |= CLK_CHILD_SLAB_ALLOC;
+       }
+
+       child->clk = clk2;
+
+       list_add_tail(&child->node, &clk->children);
+}
+
+/**
+ * omap_clk_del_child - add a child clock @clk2 to @clk
+ * @clk: parent struct clk *
+ * @clk2: former child struct clk *
+ *
+ * Remove a child clock @clk2 from the list of children of parent
+ * clock @clk.  Must only be called with the clock framework spinlock
+ * held.  No return value.
+ */
+void omap_clk_del_child(struct clk *clk, struct clk *clk2)
+{
+       struct clk_child *child, *tmp;
+
+       /* Loop over all existing clk_childs, when found, deallocate */
+       list_for_each_entry_safe(child, tmp, &clk->children, node) {
+               if (child->clk == clk2) {
+                       list_del(&child->node);
+                       if (child->flags & CLK_CHILD_SLAB_ALLOC) {
+                               kfree(child);
+                       } else {
+                               child->clk = NULL;
+                               child->flags |= CLK_CHILD_DELETED;
+                       }
+                       break;
+               }
+       }
+}
+
 /*-------------------------------------------------------------------------
  * Standard clock functions defined in include/linux/clk.h
  *-------------------------------------------------------------------------*/
@@ -53,15 +191,14 @@ struct clk * clk_get(struct device *dev, const char *id)
        mutex_lock(&clocks_mutex);
 
        list_for_each_entry(p, &clocks, node) {
-               if (p->id == idno &&
-                   strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
+               if (p->id == idno && strcmp(id, p->name) == 0) {
                        clk = p;
                        goto found;
                }
        }
 
        list_for_each_entry(p, &clocks, node) {
-               if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
+               if (strcmp(id, p->name) == 0) {
                        clk = p;
                        break;
                }
@@ -83,8 +220,13 @@ int clk_enable(struct clk *clk)
                return -EINVAL;
 
        spin_lock_irqsave(&clockfw_lock, flags);
-       if (arch_clock->clk_enable)
+       if (arch_clock->clk_enable) {
                ret = arch_clock->clk_enable(clk);
+               if (ret == 0 && clk->flags & RECALC_ON_ENABLE)
+                       _do_propagate_rate(clk, clk->parent->rate,
+                                          CURRENT_RATE);
+       }
+
        spin_unlock_irqrestore(&clockfw_lock, flags);
 
        return ret;
@@ -106,8 +248,12 @@ void clk_disable(struct clk *clk)
                goto out;
        }
 
-       if (arch_clock->clk_disable)
+       if (arch_clock->clk_disable) {
                arch_clock->clk_disable(clk);
+               if (clk->flags & RECALC_ON_ENABLE)
+                       _do_propagate_rate(clk, clk->parent->rate,
+                                          CURRENT_RATE);
+       }
 
 out:
        spin_unlock_irqrestore(&clockfw_lock, flags);
@@ -148,8 +294,6 @@ EXPORT_SYMBOL(clk_get_rate);
 
 void clk_put(struct clk *clk)
 {
-       if (clk && !IS_ERR(clk))
-               module_put(clk->owner);
 }
 EXPORT_SYMBOL(clk_put);
 
@@ -183,8 +327,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
                return ret;
 
        spin_lock_irqsave(&clockfw_lock, flags);
-       if (arch_clock->clk_set_rate)
+
+       if (arch_clock->clk_set_rate) {
                ret = arch_clock->clk_set_rate(clk, rate);
+               if (ret == 0)
+                       _do_propagate_rate(clk, clk->parent->rate,
+                                          CURRENT_RATE);
+       }
+
        spin_unlock_irqrestore(&clockfw_lock, flags);
 
        return ret;
@@ -194,14 +344,25 @@ EXPORT_SYMBOL(clk_set_rate);
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
        unsigned long flags;
+       struct clk *prev_parent;
        int ret = -EINVAL;
 
        if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent))
                return ret;
 
        spin_lock_irqsave(&clockfw_lock, flags);
-       if (arch_clock->clk_set_parent)
-               ret =  arch_clock->clk_set_parent(clk, parent);
+
+       if (arch_clock->clk_set_parent) {
+               prev_parent = clk->parent;
+               ret = arch_clock->clk_set_parent(clk, parent);
+               if (ret == 0) {
+                       omap_clk_del_child(prev_parent, clk);
+                       omap_clk_add_child(parent, clk);
+                       _do_propagate_rate(clk, clk->parent->rate,
+                                          CURRENT_RATE);
+               }
+       }
+
        spin_unlock_irqrestore(&clockfw_lock, flags);
 
        return ret;
@@ -250,30 +411,30 @@ static int __init omap_clk_setup(char *str)
 __setup("mpurate=", omap_clk_setup);
 
 /* Used for clocks that always have same value as the parent clock */
-void followparent_recalc(struct clk *clk)
+void followparent_recalc(struct clk *clk, unsigned long new_parent_rate,
+                        u8 rate_storage)
 {
-       if (clk == NULL || IS_ERR(clk))
-               return;
-
-       clk->rate = clk->parent->rate;
-       if (unlikely(clk->flags & RATE_PROPAGATES))
-               propagate_rate(clk);
+       if (rate_storage == CURRENT_RATE)
+               clk->rate = new_parent_rate;
+       else if (rate_storage == TEMP_RATE)
+               clk->temp_rate = new_parent_rate;
 }
 
 /* Propagate rate to children */
-void propagate_rate(struct clk * tclk)
+void propagate_rate(struct clk *tclk, u8 rate_storage)
 {
-       struct clk *clkp;
+       unsigned long parent_rate = 0;
 
        if (tclk == NULL || IS_ERR(tclk))
                return;
 
-       list_for_each_entry(clkp, &clocks, node) {
-               if (likely(clkp->parent != tclk))
-                       continue;
-               if (likely((u32)clkp->recalc))
-                       clkp->recalc(clkp);
-       }
+       if (rate_storage == CURRENT_RATE)
+               parent_rate = tclk->rate;
+       else if (rate_storage == TEMP_RATE)
+               parent_rate = tclk->temp_rate;
+
+       omap_clk_for_each_child(tclk, parent_rate, rate_storage,
+                               _do_propagate_rate);
 }
 
 /**
@@ -287,10 +448,9 @@ void recalculate_root_clocks(void)
 {
        struct clk *clkp;
 
-       list_for_each_entry(clkp, &clocks, node) {
-               if (unlikely(!clkp->parent) && likely((u32)clkp->recalc))
-                       clkp->recalc(clkp);
-       }
+       list_for_each_entry(clkp, &clocks, node)
+               if (unlikely(!clkp->parent))
+                       _do_propagate_rate(clkp, 0, CURRENT_RATE);
 }
 
 int clk_register(struct clk *clk)
@@ -300,6 +460,10 @@ int clk_register(struct clk *clk)
 
        mutex_lock(&clocks_mutex);
        list_add(&clk->node, &clocks);
+       if (!clk->children.next)
+               INIT_LIST_HEAD(&clk->children);
+       if (clk->parent)
+               omap_clk_add_child(clk->parent, clk);
        if (clk->init)
                clk->init(clk);
        mutex_unlock(&clocks_mutex);
@@ -310,11 +474,18 @@ EXPORT_SYMBOL(clk_register);
 
 void clk_unregister(struct clk *clk)
 {
+       struct clk_child *child, *tmp;
+
        if (clk == NULL || IS_ERR(clk))
                return;
 
        mutex_lock(&clocks_mutex);
        list_del(&clk->node);
+       if (clk->parent)
+               omap_clk_del_child(clk->parent, clk);
+       list_for_each_entry_safe(child, tmp, &clk->children, node)
+               if (child->flags & CLK_CHILD_SLAB_ALLOC)
+                       kfree(child);
        mutex_unlock(&clocks_mutex);
 }
 EXPORT_SYMBOL(clk_unregister);
@@ -383,8 +554,11 @@ static int __init clk_disable_unused(void)
        unsigned long flags;
 
        list_for_each_entry(ck, &clocks, node) {
-               if (ck->usecount > 0 || (ck->flags & ALWAYS_ENABLED) ||
-                       ck->enable_reg == 0)
+               if (ck->usecount > 0 ||
+                   (ck->flags & (ALWAYS_ENABLED | PARENT_CONTROLS_CLOCK)))
+                       continue;
+
+               if (cpu_class_is_omap1() && ck->enable_reg == 0)
                        continue;
 
                spin_lock_irqsave(&clockfw_lock, flags);
@@ -428,23 +602,23 @@ static int clk_debugfs_register_one(struct clk *c)
        if (c->id != 0)
                sprintf(p, ":%d", c->id);
        d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root);
-       if (IS_ERR(d))
-               return PTR_ERR(d);
+       if (!d)
+               return -ENOMEM;
        c->dent = d;
 
        d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount);
-       if (IS_ERR(d)) {
-               err = PTR_ERR(d);
+       if (!d) {
+               err = -ENOMEM;
                goto err_out;
        }
        d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
-       if (IS_ERR(d)) {
-               err = PTR_ERR(d);
+       if (!d) {
+               err = -ENOMEM;
                goto err_out;
        }
        d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
-       if (IS_ERR(d)) {
-               err = PTR_ERR(d);
+       if (!d) {
+               err = -ENOMEM;
                goto err_out;
        }
        return 0;
@@ -483,8 +657,8 @@ static int __init clk_debugfs_init(void)
        int err;
 
        d = debugfs_create_dir("clock", NULL);
-       if (IS_ERR(d))
-               return PTR_ERR(d);
+       if (!d)
+               return -ENOMEM;
        clk_debugfs_root = d;
 
        list_for_each_entry(c, &clocks, node) {