]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/sh/clk.c
sh: clkfwk: Add a helper for rate rounding by divisor ranges.
[mirror_ubuntu-bionic-kernel.git] / drivers / sh / clk.c
index 5d84adac9ec499beebd50c303a5b0fb71f2cca5d..018be37ef3392d157f0bafef7d91cd8008721415 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/sh/clk.c - SuperH clock framework
  *
- *  Copyright (C) 2005 - 2009  Paul Mundt
+ *  Copyright (C) 2005 - 2010  Paul Mundt
  *
  * This clock framework is derived from the OMAP version by:
  *
@@ -14,6 +14,8 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
+#define pr_fmt(fmt) "clock: " fmt
+
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -23,7 +25,7 @@
 #include <linux/sysdev.h>
 #include <linux/seq_file.h>
 #include <linux/err.h>
-#include <linux/platform_device.h>
+#include <linux/io.h>
 #include <linux/debugfs.h>
 #include <linux/cpufreq.h>
 #include <linux/clk.h>
@@ -43,6 +45,8 @@ void clk_rate_table_build(struct clk *clk,
        unsigned long freq;
        int i;
 
+       clk->nr_freqs = nr_freqs;
+
        for (i = 0; i < nr_freqs; i++) {
                div = 1;
                mult = 1;
@@ -67,29 +71,39 @@ void clk_rate_table_build(struct clk *clk,
        freq_table[i].frequency = CPUFREQ_TABLE_END;
 }
 
-long clk_rate_table_round(struct clk *clk,
-                         struct cpufreq_frequency_table *freq_table,
-                         unsigned long rate)
+struct clk_rate_round_data;
+
+struct clk_rate_round_data {
+       unsigned long rate;
+       unsigned int min, max;
+       long (*func)(unsigned int, struct clk_rate_round_data *);
+       void *arg;
+};
+
+#define for_each_frequency(pos, r, freq)                       \
+       for (pos = r->min, freq = r->func(pos, r->arg);         \
+            pos < r->max; pos++, freq = r->func(pos, r))       \
+               if (unlikely(freq == 0))                        \
+                       ;                                       \
+               else
+
+static long clk_rate_round_helper(struct clk_rate_round_data *rounder)
 {
        unsigned long rate_error, rate_error_prev = ~0UL;
-       unsigned long rate_best_fit = rate;
-       unsigned long highest, lowest;
+       unsigned long rate_best_fit = rounder->rate;
+       unsigned long highest, lowest, freq;
        int i;
 
-       highest = lowest = 0;
-
-       for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               unsigned long freq = freq_table[i].frequency;
-
-               if (freq == CPUFREQ_ENTRY_INVALID)
-                       continue;
+       highest = 0;
+       lowest = ~0UL;
 
+       for_each_frequency(i, rounder, freq) {
                if (freq > highest)
                        highest = freq;
                if (freq < lowest)
                        lowest = freq;
 
-               rate_error = abs(freq - rate);
+               rate_error = abs(freq - rounder->rate);
                if (rate_error < rate_error_prev) {
                        rate_best_fit = freq;
                        rate_error_prev = rate_error;
@@ -99,14 +113,61 @@ long clk_rate_table_round(struct clk *clk,
                        break;
        }
 
-       if (rate >= highest)
+       if (rounder->rate >= highest)
                rate_best_fit = highest;
-       if (rate <= lowest)
+       if (rounder->rate <= lowest)
                rate_best_fit = lowest;
 
        return rate_best_fit;
 }
 
+static long clk_rate_table_iter(unsigned int pos,
+                               struct clk_rate_round_data *rounder)
+{
+       struct cpufreq_frequency_table *freq_table = rounder->arg;
+       unsigned long freq = freq_table[pos].frequency;
+
+       if (freq == CPUFREQ_ENTRY_INVALID)
+               freq = 0;
+
+       return freq;
+}
+
+long clk_rate_table_round(struct clk *clk,
+                         struct cpufreq_frequency_table *freq_table,
+                         unsigned long rate)
+{
+       struct clk_rate_round_data table_round = {
+               .min    = 0,
+               .max    = clk->nr_freqs,
+               .func   = clk_rate_table_iter,
+               .arg    = freq_table,
+               .rate   = rate,
+       };
+
+       return clk_rate_round_helper(&table_round);
+}
+
+static long clk_rate_div_range_iter(unsigned int pos,
+                                   struct clk_rate_round_data *rounder)
+{
+       return clk_get_rate(rounder->arg) / pos;
+}
+
+long clk_rate_div_range_round(struct clk *clk, unsigned int div_min,
+                             unsigned int div_max, unsigned long rate)
+{
+       struct clk_rate_round_data div_range_round = {
+               .min    = div_min,
+               .max    = div_max,
+               .func   = clk_rate_div_range_iter,
+               .arg    = clk_get_parent(clk),
+               .rate   = rate,
+       };
+
+       return clk_rate_round_helper(&div_range_round);
+}
+
 int clk_rate_table_find(struct clk *clk,
                        struct cpufreq_frequency_table *freq_table,
                        unsigned long rate)
@@ -160,8 +221,8 @@ void propagate_rate(struct clk *tclk)
 
 static void __clk_disable(struct clk *clk)
 {
-       if (WARN(!clk->usecount, "Trying to disable clock %s with 0 usecount\n",
-                clk->name))
+       if (WARN(!clk->usecount, "Trying to disable clock %p with 0 usecount\n",
+                clk))
                return;
 
        if (!(--clk->usecount)) {
@@ -248,8 +309,88 @@ void recalculate_root_clocks(void)
        }
 }
 
+static struct clk_mapping dummy_mapping;
+
+static struct clk *lookup_root_clock(struct clk *clk)
+{
+       while (clk->parent)
+               clk = clk->parent;
+
+       return clk;
+}
+
+static int clk_establish_mapping(struct clk *clk)
+{
+       struct clk_mapping *mapping = clk->mapping;
+
+       /*
+        * Propagate mappings.
+        */
+       if (!mapping) {
+               struct clk *clkp;
+
+               /*
+                * dummy mapping for root clocks with no specified ranges
+                */
+               if (!clk->parent) {
+                       clk->mapping = &dummy_mapping;
+                       return 0;
+               }
+
+               /*
+                * If we're on a child clock and it provides no mapping of its
+                * own, inherit the mapping from its root clock.
+                */
+               clkp = lookup_root_clock(clk);
+               mapping = clkp->mapping;
+               BUG_ON(!mapping);
+       }
+
+       /*
+        * Establish initial mapping.
+        */
+       if (!mapping->base && mapping->phys) {
+               kref_init(&mapping->ref);
+
+               mapping->base = ioremap_nocache(mapping->phys, mapping->len);
+               if (unlikely(!mapping->base))
+                       return -ENXIO;
+       } else if (mapping->base) {
+               /*
+                * Bump the refcount for an existing mapping
+                */
+               kref_get(&mapping->ref);
+       }
+
+       clk->mapping = mapping;
+       return 0;
+}
+
+static void clk_destroy_mapping(struct kref *kref)
+{
+       struct clk_mapping *mapping;
+
+       mapping = container_of(kref, struct clk_mapping, ref);
+
+       iounmap(mapping->base);
+}
+
+static void clk_teardown_mapping(struct clk *clk)
+{
+       struct clk_mapping *mapping = clk->mapping;
+
+       /* Nothing to do */
+       if (mapping == &dummy_mapping)
+               return;
+
+       kref_put(&mapping->ref, clk_destroy_mapping);
+       clk->mapping = NULL;
+}
+
 int clk_register(struct clk *clk)
 {
+       int ret;
+
        if (clk == NULL || IS_ERR(clk))
                return -EINVAL;
 
@@ -264,6 +405,10 @@ int clk_register(struct clk *clk)
        INIT_LIST_HEAD(&clk->children);
        clk->usecount = 0;
 
+       ret = clk_establish_mapping(clk);
+       if (unlikely(ret))
+               goto out_unlock;
+
        if (clk->parent)
                list_add(&clk->sibling, &clk->parent->children);
        else
@@ -272,9 +417,11 @@ int clk_register(struct clk *clk)
        list_add(&clk->node, &clock_list);
        if (clk->ops && clk->ops->init)
                clk->ops->init(clk);
+
+out_unlock:
        mutex_unlock(&clock_list_sem);
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(clk_register);
 
@@ -283,6 +430,7 @@ void clk_unregister(struct clk *clk)
        mutex_lock(&clock_list_sem);
        list_del(&clk->sibling);
        list_del(&clk->node);
+       clk_teardown_mapping(clk);
        mutex_unlock(&clock_list_sem);
 }
 EXPORT_SYMBOL_GPL(clk_unregister);
@@ -354,10 +502,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
                        ret = clk_reparent(clk, parent);
 
                if (ret == 0) {
-                       pr_debug("clock: set parent of %s to %s (new rate %ld)\n",
-                                clk->name, clk->parent->name, clk->rate);
                        if (clk->ops->recalc)
                                clk->rate = clk->ops->recalc(clk);
+                       pr_debug("set parent of %p to %p (new rate %ld)\n",
+                                clk, clk->parent, clk->rate);
                        propagate_rate(clk);
                }
        } else
@@ -469,9 +617,7 @@ static int clk_debugfs_register_one(struct clk *c)
        char s[255];
        char *p = s;
 
-       p += sprintf(p, "%s", c->name);
-       if (c->id >= 0)
-               sprintf(p, ":%d", c->id);
+       p += sprintf(p, "%p", c);
        d = debugfs_create_dir(s, pa ? pa->dentry : clk_debugfs_root);
        if (!d)
                return -ENOMEM;
@@ -513,7 +659,7 @@ static int clk_debugfs_register(struct clk *c)
                        return err;
        }
 
-       if (!c->dentry && c->name) {
+       if (!c->dentry) {
                err = clk_debugfs_register_one(c);
                if (err)
                        return err;