msm: generalize clock support.
[safe/jmp/linux-2.6] / arch / arm / mach-msm / clock.c
index 3b1ce36..34c6a52 100644 (file)
@@ -1,7 +1,7 @@
 /* arch/arm/mach-msm/clock.c
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2007 QUALCOMM Incorporated
+ * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/ctype.h>
+#include <linux/pm_qos_params.h>
+#include <mach/clk.h>
 
 #include "clock.h"
 #include "proc_comm.h"
 static DEFINE_MUTEX(clocks_mutex);
 static DEFINE_SPINLOCK(clocks_lock);
 static LIST_HEAD(clocks);
+struct clk *msm_clocks;
+unsigned msm_num_clocks;
 
 /*
- * glue for the proc_comm interface
+ * Bitmap of enabled clocks, excluding ACPU which is always
+ * enabled
  */
-static inline int pc_clk_enable(unsigned id)
-{
-       return msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL);
-}
-
-static inline void pc_clk_disable(unsigned id)
-{
-       msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL);
-}
-
-static inline int pc_clk_set_rate(unsigned id, unsigned rate)
-{
-       return msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate);
-}
-
-static inline int pc_clk_set_min_rate(unsigned id, unsigned rate)
-{
-       return msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate);
-}
-
-static inline int pc_clk_set_max_rate(unsigned id, unsigned rate)
-{
-       return msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &rate);
-}
-
-static inline int pc_clk_set_flags(unsigned id, unsigned flags)
-{
-       return msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags);
-}
-
-static inline unsigned pc_clk_get_rate(unsigned id)
-{
-       if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL))
-               return 0;
-       else
-               return id;
-}
-
-static inline unsigned pc_clk_is_enabled(unsigned id)
-{
-       if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL))
-               return 0;
-       else
-               return id;
-}
-
-static inline int pc_pll_request(unsigned id, unsigned on)
-{
-       on = !!on;
-       return msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on);
-}
+static DECLARE_BITMAP(clock_map_enabled, NR_CLKS);
+static DEFINE_SPINLOCK(clock_map_lock);
 
 /*
  * Standard clock functions defined in include/linux/clk.h
@@ -119,8 +77,12 @@ int clk_enable(struct clk *clk)
        unsigned long flags;
        spin_lock_irqsave(&clocks_lock, flags);
        clk->count++;
-       if (clk->count == 1)
-               pc_clk_enable(clk->id);
+       if (clk->count == 1) {
+               clk->ops->enable(clk->id);
+               spin_lock(&clock_map_lock);
+               clock_map_enabled[BIT_WORD(clk->id)] |= BIT_MASK(clk->id);
+               spin_unlock(&clock_map_lock);
+       }
        spin_unlock_irqrestore(&clocks_lock, flags);
        return 0;
 }
@@ -132,31 +94,54 @@ void clk_disable(struct clk *clk)
        spin_lock_irqsave(&clocks_lock, flags);
        BUG_ON(clk->count == 0);
        clk->count--;
-       if (clk->count == 0)
-               pc_clk_disable(clk->id);
+       if (clk->count == 0) {
+               clk->ops->disable(clk->id);
+               spin_lock(&clock_map_lock);
+               clock_map_enabled[BIT_WORD(clk->id)] &= ~BIT_MASK(clk->id);
+               spin_unlock(&clock_map_lock);
+       }
        spin_unlock_irqrestore(&clocks_lock, flags);
 }
 EXPORT_SYMBOL(clk_disable);
 
+int clk_reset(struct clk *clk, enum clk_reset_action action)
+{
+       if (!clk->ops->reset)
+               clk->ops->reset = &pc_clk_reset;
+       return clk->ops->reset(clk->remote_id, action);
+}
+EXPORT_SYMBOL(clk_reset);
+
 unsigned long clk_get_rate(struct clk *clk)
 {
-       return pc_clk_get_rate(clk->id);
+       return clk->ops->get_rate(clk->id);
 }
 EXPORT_SYMBOL(clk_get_rate);
 
 int clk_set_rate(struct clk *clk, unsigned long rate)
 {
-       int ret;
-       if (clk->flags & CLKFLAG_USE_MIN_MAX_TO_SET) {
-               ret = pc_clk_set_max_rate(clk->id, rate);
-               if (ret)
-                       return ret;
-               return pc_clk_set_min_rate(clk->id, rate);
-       }
-       return pc_clk_set_rate(clk->id, rate);
+       return clk->ops->set_rate(clk->id, rate);
 }
 EXPORT_SYMBOL(clk_set_rate);
 
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+       return clk->ops->round_rate(clk->id, rate);
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_min_rate(struct clk *clk, unsigned long rate)
+{
+       return clk->ops->set_min_rate(clk->id, rate);
+}
+EXPORT_SYMBOL(clk_set_min_rate);
+
+int clk_set_max_rate(struct clk *clk, unsigned long rate)
+{
+       return clk->ops->set_max_rate(clk->id, rate);
+}
+EXPORT_SYMBOL(clk_set_max_rate);
+
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
        return -ENOSYS;
@@ -173,22 +158,153 @@ int clk_set_flags(struct clk *clk, unsigned long flags)
 {
        if (clk == NULL || IS_ERR(clk))
                return -EINVAL;
-       return pc_clk_set_flags(clk->id, flags);
+       return clk->ops->set_flags(clk->id, flags);
 }
 EXPORT_SYMBOL(clk_set_flags);
 
+/* EBI1 is the only shared clock that several clients want to vote on as of
+ * this commit. If this changes in the future, then it might be better to
+ * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more
+ * generic to support different clocks.
+ */
+static struct clk *ebi1_clk;
 
-void __init msm_clock_init(void)
+static void __init set_clock_ops(struct clk *clk)
+{
+       if (!clk->ops) {
+               clk->ops = &clk_ops_pcom;
+               clk->id = clk->remote_id;
+       }
+}
+
+void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks)
 {
        unsigned n;
 
        spin_lock_init(&clocks_lock);
        mutex_lock(&clocks_mutex);
-       for (n = 0; n < msm_num_clocks; n++)
+       msm_clocks = clock_tbl;
+       msm_num_clocks = num_clocks;
+       for (n = 0; n < msm_num_clocks; n++) {
+               set_clock_ops(&msm_clocks[n]);
                list_add_tail(&msm_clocks[n].list, &clocks);
+       }
        mutex_unlock(&clocks_mutex);
+
+       ebi1_clk = clk_get(NULL, "ebi1_clk");
+       BUG_ON(ebi1_clk == NULL);
+
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static struct clk *msm_clock_get_nth(unsigned index)
+{
+       if (index < msm_num_clocks)
+               return msm_clocks + index;
+       else
+               return 0;
+}
+
+static int clock_debug_rate_set(void *data, u64 val)
+{
+       struct clk *clock = data;
+       int ret;
+
+       /* Only increases to max rate will succeed, but that's actually good
+        * for debugging purposes. So we don't check for error. */
+       if (clock->flags & CLK_MAX)
+               clk_set_max_rate(clock, val);
+       if (clock->flags & CLK_MIN)
+               ret = clk_set_min_rate(clock, val);
+       else
+               ret = clk_set_rate(clock, val);
+       if (ret != 0)
+               printk(KERN_ERR "clk_set%s_rate failed (%d)\n",
+                       (clock->flags & CLK_MIN) ? "_min" : "", ret);
+       return ret;
+}
+
+static int clock_debug_rate_get(void *data, u64 *val)
+{
+       struct clk *clock = data;
+       *val = clk_get_rate(clock);
+       return 0;
+}
+
+static int clock_debug_enable_set(void *data, u64 val)
+{
+       struct clk *clock = data;
+       int rc = 0;
+
+       if (val)
+               rc = clock->ops->enable(clock->id);
+       else
+               clock->ops->disable(clock->id);
+
+       return rc;
 }
 
+static int clock_debug_enable_get(void *data, u64 *val)
+{
+       struct clk *clock = data;
+
+       *val = clock->ops->is_enabled(clock->id);
+
+       return 0;
+}
+
+static int clock_debug_local_get(void *data, u64 *val)
+{
+       struct clk *clock = data;
+
+       *val = clock->ops != &clk_ops_pcom;
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get,
+                       clock_debug_rate_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get,
+                       clock_debug_enable_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(clock_local_fops, clock_debug_local_get,
+                       NULL, "%llu\n");
+
+static int __init clock_debug_init(void)
+{
+       struct dentry *dent_rate, *dent_enable, *dent_local;
+       struct clk *clock;
+       unsigned n = 0;
+       char temp[50], *ptr;
+
+       dent_rate = debugfs_create_dir("clk_rate", 0);
+       if (IS_ERR(dent_rate))
+               return PTR_ERR(dent_rate);
+
+       dent_enable = debugfs_create_dir("clk_enable", 0);
+       if (IS_ERR(dent_enable))
+               return PTR_ERR(dent_enable);
+
+       dent_local = debugfs_create_dir("clk_local", NULL);
+       if (IS_ERR(dent_local))
+               return PTR_ERR(dent_local);
+
+       while ((clock = msm_clock_get_nth(n++)) != 0) {
+               strncpy(temp, clock->dbg_name, ARRAY_SIZE(temp)-1);
+               for (ptr = temp; *ptr; ptr++)
+                       *ptr = tolower(*ptr);
+               debugfs_create_file(temp, 0644, dent_rate,
+                                   clock, &clock_rate_fops);
+               debugfs_create_file(temp, 0644, dent_enable,
+                                   clock, &clock_enable_fops);
+               debugfs_create_file(temp, S_IRUGO, dent_local,
+                                   clock, &clock_local_fops);
+       }
+       return 0;
+}
+
+device_initcall(clock_debug_init);
+#endif
+
 /* The bootloader and/or AMSS may have left various clocks enabled.
  * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have
  * not been explicitly enabled by a clk_enable() call.
@@ -205,7 +321,7 @@ static int __init clock_late_init(void)
                        spin_lock_irqsave(&clocks_lock, flags);
                        if (!clk->count) {
                                count++;
-                               pc_clk_disable(clk->id);
+                               clk->ops->auto_off(clk->id);
                        }
                        spin_unlock_irqrestore(&clocks_lock, flags);
                }
@@ -216,3 +332,4 @@ static int __init clock_late_init(void)
 }
 
 late_initcall(clock_late_init);
+