]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'clk-next-unregister' into clk-next
authorMike Turquette <mturquette@linaro.org>
Tue, 31 Dec 2013 19:35:12 +0000 (11:35 -0800)
committerMike Turquette <mturquette@linaro.org>
Tue, 31 Dec 2013 19:35:12 +0000 (11:35 -0800)
Conflicts:
drivers/clk/clk.c

1  2 
drivers/clk/clk.c
include/linux/clk-private.h

diff --combined drivers/clk/clk.c
index b7f6e99e61ebff9d49ba457923ad9f68c580d24c,da7b33e4c5a25bf6d934dfabdd396bd1bf46b9c6..e3e03270b95e06498b1f650860f6dcc9b1da6f9a
@@@ -21,6 -21,8 +21,8 @@@
  #include <linux/init.h>
  #include <linux/sched.h>
  
+ #include "clk.h"
  static DEFINE_SPINLOCK(enable_lock);
  static DEFINE_MUTEX(prepare_lock);
  
@@@ -92,7 -94,7 +94,7 @@@ static void clk_enable_unlock(unsigned 
  
  /***        debugfs support        ***/
  
 -#ifdef CONFIG_COMMON_CLK_DEBUG
 +#ifdef CONFIG_DEBUG_FS
  #include <linux/debugfs.h>
  
  static struct dentry *rootdir;
@@@ -104,11 -106,10 +106,11 @@@ static void clk_summary_show_one(struc
        if (!c)
                return;
  
 -      seq_printf(s, "%*s%-*s %-11d %-12d %-10lu",
 +      seq_printf(s, "%*s%-*s %-11d %-12d %-10lu %-11lu",
                   level * 3 + 1, "",
                   30 - level * 3, c->name,
 -                 c->enable_count, c->prepare_count, clk_get_rate(c));
 +                 c->enable_count, c->prepare_count, clk_get_rate(c),
 +                 clk_get_accuracy(c));
        seq_printf(s, "\n");
  }
  
@@@ -130,8 -131,8 +132,8 @@@ static int clk_summary_show(struct seq_
  {
        struct clk *c;
  
 -      seq_printf(s, "   clock                        enable_cnt  prepare_cnt  rate\n");
 -      seq_printf(s, "---------------------------------------------------------------------\n");
 +      seq_printf(s, "   clock                        enable_cnt  prepare_cnt  rate        accuracy\n");
 +      seq_printf(s, "---------------------------------------------------------------------------------\n");
  
        clk_prepare_lock();
  
@@@ -168,7 -169,6 +170,7 @@@ static void clk_dump_one(struct seq_fil
        seq_printf(s, "\"enable_count\": %d,", c->enable_count);
        seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
        seq_printf(s, "\"rate\": %lu", clk_get_rate(c));
 +      seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c));
  }
  
  static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level)
@@@ -250,11 -250,6 +252,11 @@@ static int clk_debug_create_one(struct 
        if (!d)
                goto err_out;
  
 +      d = debugfs_create_u32("clk_accuracy", S_IRUGO, clk->dentry,
 +                      (u32 *)&clk->accuracy);
 +      if (!d)
 +              goto err_out;
 +
        d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry,
                        (u32 *)&clk->flags);
        if (!d)
@@@ -350,6 -345,21 +352,21 @@@ out
        return ret;
  }
  
+  /**
+  * clk_debug_unregister - remove a clk node from the debugfs clk tree
+  * @clk: the clk being removed from the debugfs clk tree
+  *
+  * Dynamically removes a clk and all it's children clk nodes from the
+  * debugfs clk tree if clk->dentry points to debugfs created by
+  * clk_debug_register in __clk_init.
+  *
+  * Caller must hold prepare_lock.
+  */
+ static void clk_debug_unregister(struct clk *clk)
+ {
+       debugfs_remove_recursive(clk->dentry);
+ }
  /**
   * clk_debug_reparent - reparent clk node in the debugfs clk tree
   * @clk: the clk being reparented
@@@ -440,6 -450,9 +457,9 @@@ static inline int clk_debug_register(st
  static inline void clk_debug_reparent(struct clk *clk, struct clk *new_parent)
  {
  }
+ static inline void clk_debug_unregister(struct clk *clk)
+ {
+ }
  #endif
  
  /* caller must hold prepare_lock */
@@@ -610,14 -623,6 +630,14 @@@ out
        return ret;
  }
  
 +unsigned long __clk_get_accuracy(struct clk *clk)
 +{
 +      if (!clk)
 +              return 0;
 +
 +      return clk->accuracy;
 +}
 +
  unsigned long __clk_get_flags(struct clk *clk)
  {
        return !clk ? 0 : clk->flags;
@@@ -1031,59 -1036,6 +1051,59 @@@ static int __clk_notify(struct clk *clk
        return ret;
  }
  
 +/**
 + * __clk_recalc_accuracies
 + * @clk: first clk in the subtree
 + *
 + * Walks the subtree of clks starting with clk and recalculates accuracies as
 + * it goes.  Note that if a clk does not implement the .recalc_accuracy
 + * callback then it is assumed that the clock will take on the accuracy of it's
 + * parent.
 + *
 + * Caller must hold prepare_lock.
 + */
 +static void __clk_recalc_accuracies(struct clk *clk)
 +{
 +      unsigned long parent_accuracy = 0;
 +      struct clk *child;
 +
 +      if (clk->parent)
 +              parent_accuracy = clk->parent->accuracy;
 +
 +      if (clk->ops->recalc_accuracy)
 +              clk->accuracy = clk->ops->recalc_accuracy(clk->hw,
 +                                                        parent_accuracy);
 +      else
 +              clk->accuracy = parent_accuracy;
 +
 +      hlist_for_each_entry(child, &clk->children, child_node)
 +              __clk_recalc_accuracies(child);
 +}
 +
 +/**
 + * clk_get_accuracy - return the accuracy of clk
 + * @clk: the clk whose accuracy is being returned
 + *
 + * Simply returns the cached accuracy of the clk, unless
 + * CLK_GET_ACCURACY_NOCACHE flag is set, which means a recalc_rate will be
 + * issued.
 + * If clk is NULL then returns 0.
 + */
 +long clk_get_accuracy(struct clk *clk)
 +{
 +      unsigned long accuracy;
 +
 +      clk_prepare_lock();
 +      if (clk && (clk->flags & CLK_GET_ACCURACY_NOCACHE))
 +              __clk_recalc_accuracies(clk);
 +
 +      accuracy = __clk_get_accuracy(clk);
 +      clk_prepare_unlock();
 +
 +      return accuracy;
 +}
 +EXPORT_SYMBOL_GPL(clk_get_accuracy);
 +
  /**
   * __clk_recalc_rates
   * @clk: first clk in the subtree
@@@ -1620,7 -1572,6 +1640,7 @@@ void __clk_reparent(struct clk *clk, st
  {
        clk_reparent(clk, new_parent);
        clk_debug_reparent(clk, new_parent);
 +      __clk_recalc_accuracies(clk);
        __clk_recalc_rates(clk, POST_RATE_CHANGE);
  }
  
@@@ -1691,13 -1642,11 +1711,13 @@@ int clk_set_parent(struct clk *clk, str
        /* do the re-parent */
        ret = __clk_set_parent(clk, parent, p_index);
  
 -      /* propagate rate recalculation accordingly */
 -      if (ret)
 +      /* propagate rate an accuracy recalculation accordingly */
 +      if (ret) {
                __clk_recalc_rates(clk, ABORT_RATE_CHANGE);
 -      else
 +      } else {
                __clk_recalc_rates(clk, POST_RATE_CHANGE);
 +              __clk_recalc_accuracies(clk);
 +      }
  
  out:
        clk_prepare_unlock();
@@@ -1801,21 -1750,6 +1821,21 @@@ int __clk_init(struct device *dev, stru
        else
                hlist_add_head(&clk->child_node, &clk_orphan_list);
  
 +      /*
 +       * Set clk's accuracy.  The preferred method is to use
 +       * .recalc_accuracy. For simple clocks and lazy developers the default
 +       * fallback is to use the parent's accuracy.  If a clock doesn't have a
 +       * parent (or is orphaned) then accuracy is set to zero (perfect
 +       * clock).
 +       */
 +      if (clk->ops->recalc_accuracy)
 +              clk->accuracy = clk->ops->recalc_accuracy(clk->hw,
 +                                      __clk_get_accuracy(clk->parent));
 +      else if (clk->parent)
 +              clk->accuracy = clk->parent->accuracy;
 +      else
 +              clk->accuracy = 0;
 +
        /*
         * Set clk's rate.  The preferred method is to use .recalc_rate.  For
         * simple clocks and lazy developers the default fallback is to use the
        else
                clk->rate = 0;
  
 +      clk_debug_register(clk);
        /*
         * walk the list of orphan clocks and reparent any that are children of
         * this clock
        if (clk->ops->init)
                clk->ops->init(clk->hw);
  
 -      clk_debug_register(clk);
 -
+       kref_init(&clk->ref);
  out:
        clk_prepare_unlock();
  
@@@ -1896,6 -1832,10 +1917,10 @@@ struct clk *__clk_register(struct devic
        clk->flags = hw->init->flags;
        clk->parent_names = hw->init->parent_names;
        clk->num_parents = hw->init->num_parents;
+       if (dev && dev->driver)
+               clk->owner = dev->driver->owner;
+       else
+               clk->owner = NULL;
  
        ret = __clk_init(dev, clk);
        if (ret)
@@@ -1916,6 -1856,8 +1941,8 @@@ static int _clk_register(struct device 
                goto fail_name;
        }
        clk->ops = hw->init->ops;
+       if (dev && dev->driver)
+               clk->owner = dev->driver->owner;
        clk->hw = hw;
        clk->flags = hw->init->flags;
        clk->num_parents = hw->init->num_parents;
@@@ -1990,13 -1932,104 +2017,104 @@@ fail_out
  }
  EXPORT_SYMBOL_GPL(clk_register);
  
+ /*
+  * Free memory allocated for a clock.
+  * Caller must hold prepare_lock.
+  */
+ static void __clk_release(struct kref *ref)
+ {
+       struct clk *clk = container_of(ref, struct clk, ref);
+       int i = clk->num_parents;
+       kfree(clk->parents);
+       while (--i >= 0)
+               kfree(clk->parent_names[i]);
+       kfree(clk->parent_names);
+       kfree(clk->name);
+       kfree(clk);
+ }
+ /*
+  * Empty clk_ops for unregistered clocks. These are used temporarily
+  * after clk_unregister() was called on a clock and until last clock
+  * consumer calls clk_put() and the struct clk object is freed.
+  */
+ static int clk_nodrv_prepare_enable(struct clk_hw *hw)
+ {
+       return -ENXIO;
+ }
+ static void clk_nodrv_disable_unprepare(struct clk_hw *hw)
+ {
+       WARN_ON_ONCE(1);
+ }
+ static int clk_nodrv_set_rate(struct clk_hw *hw, unsigned long rate,
+                                       unsigned long parent_rate)
+ {
+       return -ENXIO;
+ }
+ static int clk_nodrv_set_parent(struct clk_hw *hw, u8 index)
+ {
+       return -ENXIO;
+ }
+ static const struct clk_ops clk_nodrv_ops = {
+       .enable         = clk_nodrv_prepare_enable,
+       .disable        = clk_nodrv_disable_unprepare,
+       .prepare        = clk_nodrv_prepare_enable,
+       .unprepare      = clk_nodrv_disable_unprepare,
+       .set_rate       = clk_nodrv_set_rate,
+       .set_parent     = clk_nodrv_set_parent,
+ };
  /**
   * clk_unregister - unregister a currently registered clock
   * @clk: clock to unregister
-  *
-  * Currently unimplemented.
   */
- void clk_unregister(struct clk *clk) {}
+ void clk_unregister(struct clk *clk)
+ {
+       unsigned long flags;
+        if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
+                return;
+       clk_prepare_lock();
+       if (clk->ops == &clk_nodrv_ops) {
+               pr_err("%s: unregistered clock: %s\n", __func__, clk->name);
+               goto out;
+       }
+       /*
+        * Assign empty clock ops for consumers that might still hold
+        * a reference to this clock.
+        */
+       flags = clk_enable_lock();
+       clk->ops = &clk_nodrv_ops;
+       clk_enable_unlock(flags);
+       if (!hlist_empty(&clk->children)) {
+               struct clk *child;
+               /* Reparent all children to the orphan list. */
+               hlist_for_each_entry(child, &clk->children, child_node)
+                       clk_set_parent(child, NULL);
+       }
+       clk_debug_unregister(clk);
+       hlist_del_init(&clk->child_node);
+       if (clk->prepare_count)
+               pr_warn("%s: unregistering prepared clock: %s\n",
+                                       __func__, clk->name);
+       kref_put(&clk->ref, __clk_release);
+ out:
+       clk_prepare_unlock();
+ }
  EXPORT_SYMBOL_GPL(clk_unregister);
  
  static void devm_clk_release(struct device *dev, void *res)
@@@ -2056,6 -2089,31 +2174,31 @@@ void devm_clk_unregister(struct device 
  }
  EXPORT_SYMBOL_GPL(devm_clk_unregister);
  
+ /*
+  * clkdev helpers
+  */
+ int __clk_get(struct clk *clk)
+ {
+       if (clk && !try_module_get(clk->owner))
+               return 0;
+       kref_get(&clk->ref);
+       return 1;
+ }
+ void __clk_put(struct clk *clk)
+ {
+       if (WARN_ON_ONCE(IS_ERR(clk)))
+               return;
+       clk_prepare_lock();
+       kref_put(&clk->ref, __clk_release);
+       clk_prepare_unlock();
+       if (clk)
+               module_put(clk->owner);
+ }
  /***        clk rate change notifiers        ***/
  
  /**
@@@ -2196,7 -2254,18 +2339,18 @@@ static const struct of_device_id __clk_
        __used __section(__clk_of_table_end);
  
  static LIST_HEAD(of_clk_providers);
- static DEFINE_MUTEX(of_clk_lock);
+ static DEFINE_MUTEX(of_clk_mutex);
+ /* of_clk_provider list locking helpers */
+ void of_clk_lock(void)
+ {
+       mutex_lock(&of_clk_mutex);
+ }
+ void of_clk_unlock(void)
+ {
+       mutex_unlock(&of_clk_mutex);
+ }
  
  struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
                                     void *data)
@@@ -2240,9 -2309,9 +2394,9 @@@ int of_clk_add_provider(struct device_n
        cp->data = data;
        cp->get = clk_src_get;
  
-       mutex_lock(&of_clk_lock);
+       mutex_lock(&of_clk_mutex);
        list_add(&cp->link, &of_clk_providers);
-       mutex_unlock(&of_clk_lock);
+       mutex_unlock(&of_clk_mutex);
        pr_debug("Added clock from %s\n", np->full_name);
  
        return 0;
@@@ -2257,7 -2326,7 +2411,7 @@@ void of_clk_del_provider(struct device_
  {
        struct of_clk_provider *cp;
  
-       mutex_lock(&of_clk_lock);
+       mutex_lock(&of_clk_mutex);
        list_for_each_entry(cp, &of_clk_providers, link) {
                if (cp->node == np) {
                        list_del(&cp->link);
                        break;
                }
        }
-       mutex_unlock(&of_clk_lock);
+       mutex_unlock(&of_clk_mutex);
  }
  EXPORT_SYMBOL_GPL(of_clk_del_provider);
  
- struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
+ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec)
  {
        struct of_clk_provider *provider;
        struct clk *clk = ERR_PTR(-ENOENT);
  
        /* Check if we have such a provider in our array */
-       mutex_lock(&of_clk_lock);
        list_for_each_entry(provider, &of_clk_providers, link) {
                if (provider->node == clkspec->np)
                        clk = provider->get(clkspec, provider->data);
                if (!IS_ERR(clk))
                        break;
        }
-       mutex_unlock(&of_clk_lock);
+       return clk;
+ }
+ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
+ {
+       struct clk *clk;
+       mutex_lock(&of_clk_mutex);
+       clk = __of_clk_get_from_provider(clkspec);
+       mutex_unlock(&of_clk_mutex);
  
        return clk;
  }
index 5fb086b06c833d0078aa2a827b5aaef6dc3509d6,72c65e05450b2d48df70716d2a694b4d624177a9..efbf70b9fd847f01fb5b623210ad60ee5dc54bdf
@@@ -12,6 -12,7 +12,7 @@@
  #define __LINUX_CLK_PRIVATE_H
  
  #include <linux/clk-provider.h>
+ #include <linux/kref.h>
  #include <linux/list.h>
  
  /*
  
  #ifdef CONFIG_COMMON_CLK
  
+ struct module;
  struct clk {
        const char              *name;
        const struct clk_ops    *ops;
        struct clk_hw           *hw;
+       struct module           *owner;
        struct clk              *parent;
        const char              **parent_names;
        struct clk              **parents;
        unsigned long           flags;
        unsigned int            enable_count;
        unsigned int            prepare_count;
 +      unsigned long           accuracy;
        struct hlist_head       children;
        struct hlist_node       child_node;
        unsigned int            notifier_count;
 -#ifdef CONFIG_COMMON_CLK_DEBUG
 +#ifdef CONFIG_DEBUG_FS
        struct dentry           *dentry;
  #endif
+       struct kref             ref;
  };
  
  /*