Merge branch 'linux-tegra-2.6.36' into android-tegra-2.6.36
authorColin Cross <ccross@android.com>
Tue, 26 Oct 2010 01:48:16 +0000 (18:48 -0700)
committerColin Cross <ccross@android.com>
Tue, 26 Oct 2010 01:48:16 +0000 (18:48 -0700)
19 files changed:
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/clock.c
arch/arm/mach-tegra/clock.h
arch/arm/mach-tegra/common.c
arch/arm/mach-tegra/cpu-tegra.c
arch/arm/mach-tegra/cpuidle.c
arch/arm/mach-tegra/dvfs.c [new file with mode: 0644]
arch/arm/mach-tegra/dvfs.h [new file with mode: 0644]
arch/arm/mach-tegra/headsmp-t2.S
arch/arm/mach-tegra/include/mach/clk.h
arch/arm/mach-tegra/irq.c
arch/arm/mach-tegra/platsmp.c
arch/arm/mach-tegra/power.h
arch/arm/mach-tegra/suspend.c
arch/arm/mach-tegra/tegra2_clocks.c
arch/arm/mach-tegra/tegra2_dvfs.c
arch/arm/mach-tegra/tegra2_dvfs.h [deleted file]
arch/arm/mach-tegra/timer.c
drivers/video/tegra/dc/dc.c

index 8872cd54337f1f191765489971c3b318380600d1..869dd4cafdc33b93ecad8bcff960c43f93ebb290 100644 (file)
@@ -2,6 +2,7 @@ obj-y                                   += common.o
 obj-y                                   += io.o
 obj-y                                   += irq.o legacy_irq.o
 obj-y                                   += clock.o
+obj-y                                   += dvfs.o
 obj-y                                   += timer.o
 obj-y                                   += gpio.o
 obj-y                                   += pinmux.o
@@ -24,10 +25,9 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)              += tegra2_save.o
 obj-$(CONFIG_CPU_V7)                   += cortex-a9.o
 
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += pinmux-t2-tables.o
-ifeq ($(CONFIG_SMP),y)
-obj-y                                   += platsmp.o localtimer.o
+obj-$(CONFIG_SMP)                       += localtimer.o
+obj-$(CONFIG_SMP)                       += platsmp.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += headsmp-t2.o
-endif
 obj-$(CONFIG_TEGRA_SYSTEM_DMA)         += dma.o
 obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o
 obj-$(CONFIG_CPU_IDLE)                 += cpuidle.o
index ae19f95585be119c51456a9d2d75183fc3b1ff4c..d34f0efbdd379abb720cba4b5621dd2fc88019d9 100644 (file)
 #include <linux/debugfs.h>
 #include <linux/slab.h>
 #include <linux/seq_file.h>
-#include <linux/regulator/consumer.h>
 #include <asm/clkdev.h>
+#include <mach/clk.h>
 
-#include "clock.h"
 #include "board.h"
-#include "fuse.h"
+#include "clock.h"
+#include "dvfs.h"
 
 static LIST_HEAD(clocks);
 
+/*
+ * clock_lock must be held when:
+ *   Accessing any clock register non-atomically
+ *     or
+ *   Relying on any state of a clk struct not to change, unless clk_is_dvfs
+ *   returns true on that clk struct, and dvfs_lock is held instead.
+ *
+ *   Any function that changes the state of a clk struct must hold
+ *   the dvfs_lock if clk_is_auto_dvfs(clk) is true, and the clock_lock.
+ *
+ *   When taking dvfs_lock and clock_lock, dvfs_lock must be taken first.
+ */
 static DEFINE_SPINLOCK(clock_lock);
-static DEFINE_MUTEX(dvfs_lock);
 
-static int clk_is_dvfs(struct clk *c)
+static inline bool clk_is_auto_dvfs(struct clk *c)
 {
-       return (c->dvfs != NULL);
+       smp_rmb();
+       return c->auto_dvfs;
 };
 
-static int dvfs_set_rate(struct dvfs *d, unsigned long rate)
-{
-       struct dvfs_table *t;
-
-       if (d->table == NULL)
-               return -ENODEV;
-
-       for (t = d->table; t->rate != 0; t++) {
-               if (rate <= t->rate) {
-                       if (!d->reg)
-                               return 0;
-
-                       return regulator_set_voltage(d->reg,
-                               t->millivolts * 1000,
-                               d->max_millivolts * 1000);
-               }
-       }
-
-       return -EINVAL;
-}
-
-static void dvfs_init(struct clk *c)
+static inline bool clk_is_dvfs(struct clk *c)
 {
-       int process_id;
-       int i;
-       struct dvfs_table *table;
-
-       process_id = c->dvfs->cpu ? tegra_core_process_id() :
-               tegra_cpu_process_id();
-
-       for (i = 0; i < c->dvfs->process_id_table_length; i++)
-               if (process_id == c->dvfs->process_id_table[i].process_id)
-                       c->dvfs->table = c->dvfs->process_id_table[i].table;
-
-       if (c->dvfs->table == NULL) {
-               pr_err("Failed to find dvfs table for clock %s process %d\n",
-                       c->name, process_id);
-               return;
-       }
-
-       c->dvfs->max_millivolts = 0;
-       for (table = c->dvfs->table; table->rate != 0; table++)
-               if (c->dvfs->max_millivolts < table->millivolts)
-                       c->dvfs->max_millivolts = table->millivolts;
-
-       c->dvfs->reg = regulator_get(NULL, c->dvfs->reg_id);
-
-       if (IS_ERR(c->dvfs->reg)) {
-               pr_err("Failed to get regulator %s for clock %s\n",
-                       c->dvfs->reg_id, c->name);
-               c->dvfs->reg = NULL;
-               return;
-       }
-
-       if (c->refcnt > 0)
-               dvfs_set_rate(c->dvfs, c->rate);
-}
+       smp_rmb();
+       return c->is_dvfs;
+};
 
 struct clk *tegra_get_clock_by_name(const char *name)
 {
@@ -115,22 +75,31 @@ struct clk *tegra_get_clock_by_name(const char *name)
        return ret;
 }
 
-static void clk_recalculate_rate(struct clk *c)
+static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
 {
        u64 rate;
 
-       if (!c->parent)
-               return;
-
-       rate = c->parent->rate;
+       rate = p->rate;
 
        if (c->mul != 0 && c->div != 0) {
                rate = rate * c->mul;
                do_div(rate, c->div);
        }
 
+       return rate;
+}
+
+static void clk_recalculate_rate(struct clk *c)
+{
+       unsigned long rate;
+
+       if (!c->parent)
+               return;
+
+       rate = clk_predict_rate_from_parent(c, c->parent);
+
        if (rate > c->max_rate)
-               pr_warn("clocks: Set clock %s to rate %llu, max is %lu\n",
+               pr_warn("clocks: Set clock %s to rate %lu, max is %lu\n",
                        c->name, rate, c->max_rate);
 
        c->rate = rate;
@@ -138,7 +107,6 @@ static void clk_recalculate_rate(struct clk *c)
 
 int clk_reparent(struct clk *c, struct clk *parent)
 {
-       pr_debug("%s: %s\n", __func__, c->name);
        c->parent = parent;
        list_del(&c->sibling);
        list_add_tail(&c->sibling, &parent->children);
@@ -148,9 +116,8 @@ int clk_reparent(struct clk *c, struct clk *parent)
 static void propagate_rate(struct clk *c)
 {
        struct clk *clkp;
-       pr_debug("%s: %s\n", __func__, c->name);
+
        list_for_each_entry(clkp, &c->children, sibling) {
-               pr_debug("   %s\n", clkp->name);
                clk_recalculate_rate(clkp);
                propagate_rate(clkp);
        }
@@ -160,16 +127,24 @@ void clk_init(struct clk *c)
 {
        unsigned long flags;
 
-       pr_debug("%s: %s\n", __func__, c->name);
-
        spin_lock_irqsave(&clock_lock, flags);
 
        INIT_LIST_HEAD(&c->children);
        INIT_LIST_HEAD(&c->sibling);
+       INIT_LIST_HEAD(&c->dvfs);
 
        if (c->ops && c->ops->init)
                c->ops->init(c);
 
+       if (!c->ops || !c->ops->enable) {
+               c->refcnt++;
+               c->set = true;
+               if (c->parent)
+                       c->state = c->parent->state;
+               else
+                       c->state = ON;
+       }
+
        clk_recalculate_rate(c);
 
        list_add(&c->node, &clocks);
@@ -183,7 +158,7 @@ void clk_init(struct clk *c)
 int clk_enable_locked(struct clk *c)
 {
        int ret;
-       pr_debug("%s: %s\n", __func__, c->name);
+
        if (c->refcnt == 0) {
                if (c->parent) {
                        ret = clk_enable_locked(c->parent);
@@ -199,9 +174,7 @@ int clk_enable_locked(struct clk *c)
                                return ret;
                        }
                        c->state = ON;
-#ifdef CONFIG_DEBUG_FS
-                       c->set = 1;
-#endif
+                       c->set = true;
                }
        }
        c->refcnt++;
@@ -209,45 +182,32 @@ int clk_enable_locked(struct clk *c)
        return 0;
 }
 
-int clk_enable_cansleep(struct clk *c)
-{
-       int ret;
-       unsigned long flags;
-
-       mutex_lock(&dvfs_lock);
-
-       if (clk_is_dvfs(c) && c->refcnt > 0)
-               dvfs_set_rate(c->dvfs, c->rate);
-
-       spin_lock_irqsave(&clock_lock, flags);
-       ret = clk_enable_locked(c);
-       spin_unlock_irqrestore(&clock_lock, flags);
-
-       mutex_unlock(&dvfs_lock);
-
-       return ret;
-}
-EXPORT_SYMBOL(clk_enable_cansleep);
-
 int clk_enable(struct clk *c)
 {
        int ret;
        unsigned long flags;
 
-       if (clk_is_dvfs(c))
-               BUG();
+       if (clk_is_auto_dvfs(c)) {
+               lock_dvfs();
+               ret = tegra_dvfs_set_rate(c, c->rate);
+               if (ret)
+                       goto out;
+       }
 
        spin_lock_irqsave(&clock_lock, flags);
        ret = clk_enable_locked(c);
        spin_unlock_irqrestore(&clock_lock, flags);
 
+out:
+       if (clk_is_auto_dvfs(c))
+               unlock_dvfs();
+
        return ret;
 }
 EXPORT_SYMBOL(clk_enable);
 
 void clk_disable_locked(struct clk *c)
 {
-       pr_debug("%s: %s\n", __func__, c->name);
        if (c->refcnt == 0) {
                WARN(1, "Attempting to disable clock %s with refcnt 0", c->name);
                return;
@@ -264,33 +224,22 @@ void clk_disable_locked(struct clk *c)
        c->refcnt--;
 }
 
-void clk_disable_cansleep(struct clk *c)
-{
-       unsigned long flags;
-
-       mutex_lock(&dvfs_lock);
-
-       spin_lock_irqsave(&clock_lock, flags);
-       clk_disable_locked(c);
-       spin_unlock_irqrestore(&clock_lock, flags);
-
-       if (clk_is_dvfs(c) && c->refcnt == 0)
-               dvfs_set_rate(c->dvfs, c->rate);
-
-       mutex_unlock(&dvfs_lock);
-}
-EXPORT_SYMBOL(clk_disable_cansleep);
-
 void clk_disable(struct clk *c)
 {
        unsigned long flags;
 
-       if (clk_is_dvfs(c))
-               BUG();
+       if (clk_is_auto_dvfs(c))
+               lock_dvfs();
 
        spin_lock_irqsave(&clock_lock, flags);
        clk_disable_locked(c);
        spin_unlock_irqrestore(&clock_lock, flags);
+
+       if (clk_is_auto_dvfs(c)) {
+               if (c->refcnt == 0)
+                       tegra_dvfs_set_rate(c, 0);
+               unlock_dvfs();
+       }
 }
 EXPORT_SYMBOL(clk_disable);
 
@@ -298,8 +247,6 @@ int clk_set_parent_locked(struct clk *c, struct clk *parent)
 {
        int ret;
 
-       pr_debug("%s: %s\n", __func__, c->name);
-
        if (!c->ops || !c->ops->set_parent)
                return -ENOSYS;
 
@@ -317,11 +264,32 @@ int clk_set_parent_locked(struct clk *c, struct clk *parent)
 
 int clk_set_parent(struct clk *c, struct clk *parent)
 {
-       int ret;
+       int ret = 0;
        unsigned long flags;
+       unsigned long new_rate = clk_predict_rate_from_parent(c, parent);
+
+
+       if (clk_is_auto_dvfs(c)) {
+               lock_dvfs();
+               if (c->refcnt > 0 && (!c->parent || new_rate > c->rate))
+                       ret = tegra_dvfs_set_rate(c, new_rate);
+               if (!ret)
+                       goto out;
+       }
+
        spin_lock_irqsave(&clock_lock, flags);
        ret = clk_set_parent_locked(c, parent);
        spin_unlock_irqrestore(&clock_lock, flags);
+       if (!ret)
+               goto out;
+
+       if (clk_is_auto_dvfs(c) && c->refcnt > 0)
+               ret = tegra_dvfs_set_rate(c, new_rate);
+
+out:
+       if (clk_is_auto_dvfs(c))
+               unlock_dvfs();
+
        return ret;
 }
 EXPORT_SYMBOL(clk_set_parent);
@@ -336,12 +304,17 @@ int clk_set_rate_locked(struct clk *c, unsigned long rate)
 {
        int ret;
 
+       if (rate == c->requested_rate)
+               return 0;
+
        if (rate > c->max_rate)
                rate = c->max_rate;
 
        if (!c->ops || !c->ops->set_rate)
                return -ENOSYS;
 
+       c->requested_rate = rate;
+
        ret = c->ops->set_rate(c, rate);
 
        if (ret)
@@ -354,19 +327,18 @@ int clk_set_rate_locked(struct clk *c, unsigned long rate)
        return 0;
 }
 
-int clk_set_rate_cansleep(struct clk *c, unsigned long rate)
+int clk_set_rate(struct clk *c, unsigned long rate)
 {
        int ret = 0;
        unsigned long flags;
 
-       pr_debug("%s: %s\n", __func__, c->name);
-
-       mutex_lock(&dvfs_lock);
-
-       if (rate > c->rate)
-               ret = dvfs_set_rate(c->dvfs, rate);
-       if (ret)
-               goto out;
+       if (clk_is_auto_dvfs(c)) {
+               lock_dvfs();
+               if (rate > c->rate && c->refcnt > 0)
+                       ret = tegra_dvfs_set_rate(c, rate);
+               if (ret)
+                       goto out;
+       }
 
        spin_lock_irqsave(&clock_lock, flags);
        ret = clk_set_rate_locked(c, rate);
@@ -375,28 +347,12 @@ int clk_set_rate_cansleep(struct clk *c, unsigned long rate)
        if (ret)
                goto out;
 
-       ret = dvfs_set_rate(c->dvfs, rate);
+       if (clk_is_auto_dvfs(c) && c->refcnt > 0)
+               ret = tegra_dvfs_set_rate(c, rate);
 
 out:
-       mutex_unlock(&dvfs_lock);
-       return ret;
-}
-EXPORT_SYMBOL(clk_set_rate_cansleep);
-
-int clk_set_rate(struct clk *c, unsigned long rate)
-{
-       int ret = 0;
-       unsigned long flags;
-
-       pr_debug("%s: %s\n", __func__, c->name);
-
-       if (clk_is_dvfs(c))
-               BUG();
-
-       spin_lock_irqsave(&clock_lock, flags);
-       ret = clk_set_rate_locked(c, rate);
-       spin_unlock_irqrestore(&clock_lock, flags);
-
+       if (clk_is_auto_dvfs(c))
+               unlock_dvfs();
        return ret;
 }
 EXPORT_SYMBOL(clk_set_rate);
@@ -408,8 +364,6 @@ unsigned long clk_get_rate(struct clk *c)
 
        spin_lock_irqsave(&clock_lock, flags);
 
-       pr_debug("%s: %s\n", __func__, c->name);
-
        ret = c->rate;
 
        spin_unlock_irqrestore(&clock_lock, flags);
@@ -419,8 +373,6 @@ EXPORT_SYMBOL(clk_get_rate);
 
 long clk_round_rate(struct clk *c, unsigned long rate)
 {
-       pr_debug("%s: %s\n", __func__, c->name);
-
        if (!c->ops || !c->ops->round_rate)
                return -ENOSYS;
 
@@ -509,31 +461,62 @@ void __init tegra_init_clock(void)
        tegra2_init_clocks();
 }
 
-int __init tegra_init_dvfs(void)
+void __init tegra_clk_set_dvfs_rates(void)
 {
-       struct clk *c, *safe;
+       struct clk *c;
+       list_for_each_entry(c, &clocks, node) {
+               if (clk_is_auto_dvfs(c)) {
+                       if (c->refcnt > 0)
+                               tegra_dvfs_set_rate(c, c->rate);
+                       else
+                               tegra_dvfs_set_rate(c, 0);
+               } else if (clk_is_dvfs(c)) {
+                       tegra_dvfs_set_rate(c, c->dvfs_rate);
+               }
+       }
+}
 
-       mutex_lock(&dvfs_lock);
+int __init tegra_disable_boot_clocks(void)
+{
+       unsigned long flags;
+       struct clk *c;
 
-       list_for_each_entry_safe(c, safe, &clocks, node)
-               if (c->dvfs)
-                       dvfs_init(c);
+       lock_dvfs();
+       spin_lock_irqsave(&clock_lock, flags);
 
-       mutex_unlock(&dvfs_lock);
+       list_for_each_entry(c, &clocks, node) {
+               if (c->refcnt == 0 && c->state == ON &&
+                               c->ops && c->ops->disable) {
+                       pr_warning("Disabling clock %s left on by bootloader\n",
+                               c->name);
+                       c->ops->disable(c);
+                       c->state = OFF;
+               }
+       }
 
+       spin_unlock_irqrestore(&clock_lock, flags);
+       unlock_dvfs();
        return 0;
 }
-
-late_initcall(tegra_init_dvfs);
+late_initcall(tegra_disable_boot_clocks);
 
 #ifdef CONFIG_DEBUG_FS
 static struct dentry *clk_debugfs_root;
 
+static void dvfs_show_one(struct seq_file *s, struct dvfs *d, int level)
+{
+       seq_printf(s, "%*s  %-*s%21s%d mV\n",
+                       level * 3 + 1, "",
+                       30 - level * 3, d->reg_id,
+                       "",
+                       d->cur_millivolts);
+}
 
 static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
 {
        struct clk *child;
        struct clk *safe;
+       struct dvfs *d;
        const char *state = "uninit";
        char div[8] = {0};
 
@@ -565,6 +548,10 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
                !c->set ? '*' : ' ',
                30 - level * 3, c->name,
                state, c->refcnt, div, c->rate);
+
+       list_for_each_entry(d, &c->dvfs, node)
+               dvfs_show_one(s, d, level + 1);
+
        list_for_each_entry_safe(child, safe, &c->children, sibling) {
                clock_tree_show_one(s, child, level + 1);
        }
@@ -694,6 +681,9 @@ static int __init clk_debugfs_init(void)
        if (!d)
                goto err_out;
 
+       if (dvfs_debugfs_init(clk_debugfs_root))
+               goto err_out;
+
        list_for_each_entry(c, &clocks, node) {
                err = clk_debugfs_register(c);
                if (err)
index 096922c4d6645a253510bda085163b8c461909ea..b13aab47083e40bc571de996efe1ea096b52b466 100644 (file)
 #define ENABLE_ON_INIT         (1 << 28)
 
 struct clk;
-struct regulator;
-
-struct dvfs_table {
-       unsigned long rate;
-       int millivolts;
-};
-
-struct dvfs_process_id_table {
-       int process_id;
-       struct dvfs_table *table;
-};
-
-
-struct dvfs {
-       struct regulator *reg;
-       struct dvfs_table *table;
-       int max_millivolts;
-
-       int process_id_table_length;
-       const char *reg_id;
-       bool cpu;
-       struct dvfs_process_id_table process_id_table[];
-};
 
 struct clk_mux_sel {
        struct clk      *input;
        u32             value;
 };
 
-struct clk_pll_table {
+struct clk_pll_freq_table {
        unsigned long   input_rate;
        unsigned long   output_rate;
        u16             n;
@@ -97,52 +74,69 @@ enum clk_state {
 
 struct clk {
        /* node for master clocks list */
-       struct list_head                node;
-       struct list_head                children;       /* list of children */
-       struct list_head                sibling;        /* node for children */
-#ifdef CONFIG_DEBUG_FS
-       struct dentry                   *dent;
-       struct dentry                   *parent_dent;
-#endif
-       struct clk_ops                  *ops;
-       struct clk                      *parent;
-       struct clk_lookup               lookup;
-       unsigned long                   rate;
-       unsigned long                   max_rate;
-       u32                             flags;
-       u32                             refcnt;
-       const char                      *name;
-       u32                             reg;
-       u32                             reg_shift;
-       unsigned int                    clk_num;
-       enum clk_state                  state;
+       struct list_head        node;           /* node for list of all clocks */
+       struct list_head        children;       /* list of children */
+       struct list_head        sibling;        /* node for children */
+       struct list_head        dvfs;           /* list of dvfs dependencies */
+       struct clk_lookup       lookup;
+
 #ifdef CONFIG_DEBUG_FS
-       bool                            set;
+       struct dentry           *dent;
+       struct dentry           *parent_dent;
 #endif
+       bool                    set;
+       struct clk_ops          *ops;
+       unsigned long           dvfs_rate;
+       unsigned long           rate;
+       unsigned long           max_rate;
+       bool                    is_dvfs;
+       bool                    auto_dvfs;
+       u32                     flags;
+       const char              *name;
+
+       u32                     refcnt;
+       unsigned long           requested_rate;
+       enum clk_state          state;
+       struct clk              *parent;
+       u32                     div;
+       u32                     mul;
 
-       /* PLL */
-       unsigned long                   input_min;
-       unsigned long                   input_max;
-       unsigned long                   cf_min;
-       unsigned long                   cf_max;
-       unsigned long                   vco_min;
-       unsigned long                   vco_max;
-       const struct clk_pll_table      *pll_table;
-
-       /* DIV */
-       u32                             div;
-       u32                             mul;
-
-       /* MUX */
        const struct clk_mux_sel        *inputs;
-       u32                             sel;
-       u32                             reg_mask;
-
-       /* Virtual cpu clock */
-       struct clk                      *main;
-       struct clk                      *backup;
+       u32                             reg;
+       u32                             reg_shift;
 
-       struct dvfs                     *dvfs;
+       union {
+               struct {
+                       unsigned int                    clk_num;
+               } periph;
+               struct {
+                       unsigned long                   input_min;
+                       unsigned long                   input_max;
+                       unsigned long                   cf_min;
+                       unsigned long                   cf_max;
+                       unsigned long                   vco_min;
+                       unsigned long                   vco_max;
+                       const struct clk_pll_freq_table *freq_table;
+                       int                             lock_delay;
+               } pll;
+               struct {
+                       u32                             sel;
+                       u32                             reg_mask;
+               } mux;
+               struct {
+                       struct clk                      *main;
+                       struct clk                      *backup;
+               } cpu;
+               struct {
+                       struct list_head                list;
+                       unsigned long                   min_rate;
+               } shared_bus;
+               struct {
+                       struct list_head                node;
+                       bool                            enabled;
+                       unsigned long                   rate;
+               } shared_bus_user;
+       } u;
 };
 
 
@@ -170,5 +164,6 @@ int clk_set_parent_locked(struct clk *c, struct clk *parent);
 int clk_set_rate_locked(struct clk *c, unsigned long rate);
 int clk_reparent(struct clk *c, struct clk *parent);
 void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
+void tegra_clk_set_dvfs_rates(void);
 
 #endif
index 09fdecfc24561bf65e74b65b97903d7360d58bd3..82bca0ccb223cde4e20efb97d6028cdcab507b58 100644 (file)
@@ -52,6 +52,7 @@ unsigned long tegra_grhost_aperture;
 void (*tegra_reset)(char mode, const char *cmd);
 
 static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
+       /* set up clocks that should always be on */
        /* name         parent          rate            enabled */
        { "clk_m",      NULL,           0,              true },
        { "pll_p",      "clk_m",        216000000,      true },
@@ -62,6 +63,14 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
        { "sclk",       "pll_m_out1",   240000000,      true },
        { "hclk",       "sclk",         240000000,      true },
        { "pclk",       "hclk",         120000000,      true },
+       { "pll_x",      NULL,           0,              true },
+       { "cpu",        NULL,           0,              true },
+       { "emc",        NULL,           0,              true },
+       { "csite",      NULL,           0,              true },
+       { "timer",      NULL,           0,              true },
+       { "rtc",        NULL,           0,              true },
+
+       /* set frequencies of some device clocks */
        { "pll_u",      "clk_m",        480000000,      false },
        { "sdmmc1",     "pll_p",        48000000,       false},
        { "sdmmc2",     "pll_p",        48000000,       false},
@@ -171,8 +180,6 @@ void tegra_move_framebuffer(unsigned long to, unsigned long from,
                return;
        }
 
-       pr_info("%s: %08lx %08lx %08lx %p", __func__, to, from, size, to_io);
-
        if (pfn_valid(page_to_pfn(phys_to_page(from)))) {
                for (i = 0 ; i < size; i += PAGE_SIZE) {
                        page = phys_to_page(from + i);
index 1a8a27a8649cbecbdafee297aa260242f8be05ac..035e1bc3146295894305a7dcf755ade742e7fcd3 100644 (file)
@@ -73,6 +73,7 @@ unsigned int tegra_getspeed(unsigned int cpu)
        return rate;
 }
 
+#ifdef CONFIG_HAVE_ARM_TWD
 static void tegra_cpufreq_rescale_twd_other_cpu(void *data) {
        unsigned long new_rate = *(unsigned long *)data;
        twd_recalc_prescaler(new_rate);
@@ -83,6 +84,11 @@ static void tegra_cpufreq_rescale_twds(unsigned long new_rate)
        twd_recalc_prescaler(new_rate);
        smp_call_function(tegra_cpufreq_rescale_twd_other_cpu, &new_rate, 1);
 }
+#else
+static inline void tegra_cpufreq_rescale_twds(unsigned long new_rate)
+{
+}
+#endif
 
 static int tegra_update_cpu_speed(unsigned long rate)
 {
@@ -106,7 +112,7 @@ static int tegra_update_cpu_speed(unsigned long rate)
               freqs.old, freqs.new);
 #endif
 
-       ret = clk_set_rate_cansleep(cpu_clk, freqs.new * 1000);
+       ret = clk_set_rate(cpu_clk, freqs.new * 1000);
        if (ret) {
                pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n",
                        freqs.new);
index d128ecea6ca4c69cbede9334bc0fcebdd981a4ee..765e368401aa158b5e4d56352e6635c9054a23d5 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/tick.h>
 
 #include <asm/cacheflush.h>
+#include <asm/hardware/gic.h>
 #include <asm/localtimer.h>
 
 #include <mach/iomap.h>
@@ -63,7 +64,7 @@ static bool lp2_in_idle __read_mostly = true;
 static bool lp2_disabled_by_suspend;
 module_param(lp2_in_idle, bool, 0644);
 
-static s64 tegra_cpu1_idle_time;
+static s64 tegra_cpu1_idle_time = LLONG_MAX;;
 static int tegra_lp2_exit_latency;
 static int tegra_lp2_power_off_time;
 
@@ -77,6 +78,8 @@ static struct {
        unsigned int lp2_completed_count;
        unsigned int lp2_count_bin[32];
        unsigned int lp2_completed_count_bin[32];
+       unsigned int lp2_int_count[NR_IRQS];
+       unsigned int last_lp2_int_count[NR_IRQS];
 } idle_stats;
 
 struct cpuidle_driver tegra_idle = {
@@ -97,17 +100,7 @@ static DEFINE_PER_CPU(struct cpuidle_device *, idle_devices);
 
 static inline unsigned int time_to_bin(unsigned int time)
 {
-       unsigned int bin = 0;
-       int i;
-
-       for (i = 4; i >= 0; i--) {
-               if (time > (1 << (1 << i)) - 1) {
-                       time >>= (1 << i);
-                       bin += (1 << i);
-               }
-       }
-
-       return bin;
+       return fls(time);
 }
 
 static inline void tegra_unmask_irq(int irq)
@@ -146,6 +139,7 @@ static inline void tegra_flow_wfi(struct cpuidle_device *dev)
        reg = __raw_readl(flow_ctrl);
 }
 
+#ifdef CONFIG_SMP
 static inline bool tegra_wait_for_both_idle(struct cpuidle_device *dev)
 {
        int wake_int;
@@ -183,18 +177,90 @@ static inline bool tegra_cpu_in_reset(int cpu)
        return !!(readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET) & (1 << cpu));
 }
 
+static int tegra_tear_down_cpu1(void)
+{
+       u32 reg;
+
+       /* Signal to CPU1 to tear down */
+       tegra_legacy_force_irq_set(TEGRA_CPUIDLE_TEAR_DOWN);
+
+       /* At this point, CPU0 can no longer abort LP2, but CP1 can */
+       /* TODO: any way not to poll here? Use the LP2 timer to wfi? */
+       /* takes ~80 us */
+       while (!tegra_cpu_in_reset(1) &&
+               tegra_legacy_force_irq_status(TEGRA_CPUIDLE_BOTH_IDLE))
+               cpu_relax();
+
+       tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_TEAR_DOWN);
+
+       /* If CPU1 aborted LP2, restart the process */
+       if (!tegra_legacy_force_irq_status(TEGRA_CPUIDLE_BOTH_IDLE))
+               return -EAGAIN;
+
+       /* CPU1 is ready for LP2, clock gate it */
+       reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+       writel(reg | (1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+
+       return 0;
+}
+
+static void tegra_wake_cpu1(void)
+{
+       unsigned long boot_vector;
+       unsigned long old_boot_vector;
+       unsigned long timeout;
+       u32 reg;
+
+       boot_vector = virt_to_phys(tegra_hotplug_startup);
+       old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
+       writel(boot_vector, EVP_CPU_RESET_VECTOR);
+
+       /* enable cpu clock on cpu */
+       reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+       writel(reg & ~(1 << (8 + 1)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+
+       reg = 0x1111 << 1;
+       writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+
+       /* unhalt the cpu */
+       writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14);
+
+       timeout = jiffies + msecs_to_jiffies(1000);
+       while (time_before(jiffies, timeout)) {
+               if (readl(EVP_CPU_RESET_VECTOR) != boot_vector)
+                       break;
+               udelay(10);
+       }
+
+       /* put the old boot vector back */
+       writel(old_boot_vector, EVP_CPU_RESET_VECTOR);
+
+       /* CPU1 is now started */
+}
+#else
+static inline bool tegra_wait_for_both_idle(struct cpuidle_device *dev)
+{
+       return true;
+}
+
+static inline int tegra_tear_down_cpu1(void)
+{
+       return 0;
+}
+
+static inline void tegra_wake_cpu1(void)
+{
+}
+#endif
+
 static void tegra_idle_enter_lp2_cpu0(struct cpuidle_device *dev,
        struct cpuidle_state *state)
 {
        s64 request;
-       u32 reg;
        ktime_t enter;
        ktime_t exit;
        bool sleep_completed = false;
        int bin;
-       unsigned long boot_vector;
-       unsigned long old_boot_vector;
-       unsigned long timeout;
 
 restart:
        if (!tegra_wait_for_both_idle(dev))
@@ -208,41 +274,24 @@ restart:
        /* CPU1 woke CPU0 because both are idle */
 
        request = ktime_to_us(tick_nohz_get_sleep_length());
-       if (request < tegra_lp2_exit_latency) {
+       if (request < state->target_residency) {
                /* Not enough time left to enter LP2 */
                tegra_flow_wfi(dev);
                return;
        }
 
-       /* Signal to CPU1 to tear down */
-       tegra_legacy_force_irq_set(TEGRA_CPUIDLE_TEAR_DOWN);
-
-       /* At this point, CPU0 can no longer abort LP2, but CP1 can */
-       /* TODO: any way not to poll here? Use the LP2 timer to wfi? */
-       /* takes ~80 us */
-       while (!tegra_cpu_in_reset(1) &&
-               tegra_legacy_force_irq_status(TEGRA_CPUIDLE_BOTH_IDLE))
-               cpu_relax();
-
-       tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_TEAR_DOWN);
-
        idle_stats.tear_down_count++;
 
-       /* If CPU1 aborted LP2, restart the process */
-       if (!tegra_legacy_force_irq_status(TEGRA_CPUIDLE_BOTH_IDLE))
+       if (tegra_tear_down_cpu1())
                goto restart;
 
-       /* CPU1 is ready for LP2, clock gate it */
-       reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-       writel(reg | (1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-
        /* Enter LP2 */
        request = ktime_to_us(tick_nohz_get_sleep_length());
        smp_rmb();
        request = min_t(s64, request, tegra_cpu1_idle_time);
 
        enter = ktime_get();
-       if (request > tegra_lp2_exit_latency + state->target_residency) {
+       if (request > state->target_residency) {
                s64 sleep_time = request - tegra_lp2_exit_latency;
 
                bin = time_to_bin((u32)request / 1000);
@@ -251,6 +300,8 @@ restart:
 
                if (tegra_suspend_lp2(sleep_time) == 0)
                        sleep_completed = true;
+               else
+                       idle_stats.lp2_int_count[tegra_pending_interrupt()]++;
        }
 
        /* Bring CPU1 out of LP2 */
@@ -259,31 +310,8 @@ restart:
 
        /* set the reset vector to point to the secondary_startup routine */
        smp_wmb();
-       boot_vector = virt_to_phys(tegra_hotplug_startup);
-       old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
-       writel(boot_vector, EVP_CPU_RESET_VECTOR);
-
-       /* enable cpu clock on cpu */
-       reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-       writel(reg & ~(1 << (8 + 1)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-
-       reg = 0x1111 << 1;
-       writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
 
-       /* unhalt the cpu */
-       writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14);
-
-       timeout = jiffies + msecs_to_jiffies(1000);
-       while (time_before(jiffies, timeout)) {
-               if (readl(EVP_CPU_RESET_VECTOR) != boot_vector)
-                       break;
-               udelay(10);
-       }
-
-       /* put the old boot vector back */
-       writel(old_boot_vector, EVP_CPU_RESET_VECTOR);
-
-       /* CPU1 is now started */
+       tegra_wake_cpu1();
 
        /*
         * TODO: is it worth going back to wfi if no interrupt is pending
@@ -312,6 +340,7 @@ restart:
        }
 }
 
+#ifdef CONFIG_SMP
 static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev,
        struct cpuidle_state *state)
 {
@@ -380,6 +409,7 @@ static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev,
 out:
        tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE);
 }
+#endif
 
 static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
        struct cpuidle_state *state)
@@ -416,10 +446,14 @@ static int tegra_idle_enter_lp2(struct cpuidle_device *dev,
 
        idle_stats.cpu_ready_count[dev->cpu]++;
 
+#ifdef CONFIG_SMP
        if (dev->cpu == 0)
                tegra_idle_enter_lp2_cpu0(dev, state);
        else
                tegra_idle_enter_lp2_cpu1(dev, state);
+#else
+       tegra_idle_enter_lp2_cpu0(dev, state);
+#endif
 
        exit = ktime_sub(ktime_get(), enter);
        us = ktime_to_us(exit);
@@ -476,6 +510,7 @@ static int tegra_idle_enter(unsigned int cpu)
        state->flags = CPUIDLE_FLAG_BALANCED | CPUIDLE_FLAG_TIME_VALID;
        state->enter = tegra_idle_enter_lp2;
 
+       dev->power_specified = 1;
        dev->safe_state = state;
        dev->state_count++;
 
@@ -577,6 +612,7 @@ module_exit(tegra_cpuidle_exit);
 static int tegra_lp2_debug_show(struct seq_file *s, void *data)
 {
        int bin;
+       int i;
        seq_printf(s, "                                    cpu0     cpu1\n");
        seq_printf(s, "-------------------------------------------------\n");
        seq_printf(s, "cpu ready:                      %8u %8u\n",
@@ -624,6 +660,21 @@ static int tegra_lp2_debug_show(struct seq_file *s, void *data)
                                idle_stats.lp2_count_bin[bin]);
        }
 
+       seq_printf(s, "\n");
+       seq_printf(s, "%3s %20s %6s %10s\n",
+               "int", "name", "count", "last count");
+       seq_printf(s, "--------------------------------------------\n");
+       for (i = 0; i < NR_IRQS; i++) {
+               if (idle_stats.lp2_int_count[i] == 0)
+                       continue;
+               seq_printf(s, "%3d %20s %6d %10d\n",
+                       i, irq_to_desc(i)->action ?
+                               irq_to_desc(i)->action->name ?: "???" : "???",
+                       idle_stats.lp2_int_count[i],
+                       idle_stats.lp2_int_count[i] -
+                               idle_stats.last_lp2_int_count[i]);
+               idle_stats.last_lp2_int_count[i] = idle_stats.lp2_int_count[i];
+       };
        return 0;
 }
 
diff --git a/arch/arm/mach-tegra/dvfs.c b/arch/arm/mach-tegra/dvfs.c
new file mode 100644 (file)
index 0000000..0a2135e
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *     Colin Cross <ccross@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/list_sort.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/regulator/consumer.h>
+#include <asm/clkdev.h>
+#include <mach/clk.h>
+
+#include "board.h"
+#include "clock.h"
+#include "dvfs.h"
+
+struct dvfs_reg {
+       struct list_head node;  /* node in dvfs_reg_list */
+       struct list_head dvfs;  /* list head of attached dvfs clocks */
+       const char *reg_id;
+       struct regulator *reg;
+       int max_millivolts;
+       int millivolts;
+};
+
+static LIST_HEAD(dvfs_list);
+static LIST_HEAD(dvfs_debug_list);
+static LIST_HEAD(dvfs_reg_list);
+
+static DEFINE_MUTEX(dvfs_lock);
+
+void lock_dvfs(void)
+{
+       mutex_lock(&dvfs_lock);
+}
+
+void unlock_dvfs(void)
+{
+       mutex_unlock(&dvfs_lock);
+}
+
+static int dvfs_reg_set_voltage(struct dvfs_reg *dvfs_reg)
+{
+       int millivolts = 0;
+       struct dvfs *d;
+
+       list_for_each_entry(d, &dvfs_reg->dvfs, reg_node)
+               millivolts = max(d->cur_millivolts, millivolts);
+
+       if (millivolts == dvfs_reg->millivolts)
+               return 0;
+
+       dvfs_reg->millivolts = millivolts;
+
+       return regulator_set_voltage(dvfs_reg->reg,
+               millivolts * 1000, dvfs_reg->max_millivolts * 1000);
+}
+
+static int dvfs_reg_get_voltage(struct dvfs_reg *dvfs_reg)
+{
+       int ret = regulator_get_voltage(dvfs_reg->reg);
+
+       if (ret > 0)
+               return ret / 1000;
+
+       return ret;
+}
+
+static struct dvfs_reg *get_dvfs_reg(struct dvfs *d)
+{
+       struct dvfs_reg *dvfs_reg;
+       struct regulator *reg;
+
+       list_for_each_entry(dvfs_reg, &dvfs_reg_list, node)
+               if (!strcmp(d->reg_id, dvfs_reg->reg_id))
+                       return dvfs_reg;
+
+       reg = regulator_get(NULL, d->reg_id);
+       if (IS_ERR(reg))
+               return NULL;
+
+       dvfs_reg = kzalloc(sizeof(struct dvfs_reg), GFP_KERNEL);
+       if (!dvfs_reg) {
+               pr_err("%s: Failed to allocate dvfs_reg\n", __func__);
+               regulator_put(reg);
+               return NULL;
+       }
+
+       INIT_LIST_HEAD(&dvfs_reg->dvfs);
+       dvfs_reg->reg = reg;
+       dvfs_reg->reg_id = kstrdup(d->reg_id, GFP_KERNEL);
+
+       list_add_tail(&dvfs_reg->node, &dvfs_reg_list);
+
+       return dvfs_reg;
+}
+
+static struct dvfs_reg *attach_dvfs_reg(struct dvfs *d)
+{
+       struct dvfs_reg *dvfs_reg;
+
+       dvfs_reg = get_dvfs_reg(d);
+       if (!dvfs_reg)
+               return NULL;
+
+       list_add_tail(&d->reg_node, &dvfs_reg->dvfs);
+       d->dvfs_reg = dvfs_reg;
+       if (d->max_millivolts > d->dvfs_reg->max_millivolts)
+               d->dvfs_reg->max_millivolts = d->max_millivolts;
+
+       d->cur_millivolts = dvfs_reg_get_voltage(d->dvfs_reg);
+
+       return dvfs_reg;
+}
+
+static int
+__tegra_dvfs_set_rate(struct clk *c, struct dvfs *d, unsigned long rate)
+{
+       int i = 0;
+       int ret;
+
+       if (d->freqs == NULL || d->millivolts == NULL)
+               return -ENODEV;
+
+       if (rate > d->freqs[d->num_freqs - 1]) {
+               pr_warn("tegra_dvfs: rate %lu too high for dvfs on %s\n", rate,
+                       c->name);
+               return -EINVAL;
+       }
+
+       if (rate == 0) {
+               d->cur_millivolts = 0;
+       } else {
+               while (i < d->num_freqs && rate > d->freqs[i])
+                       i++;
+
+               d->cur_millivolts = d->millivolts[i];
+       }
+
+       d->cur_rate = rate;
+
+       if (!d->dvfs_reg)
+               return 0;
+
+       ret = dvfs_reg_set_voltage(d->dvfs_reg);
+       if (ret)
+               pr_err("Failed to set regulator %s for clock %s to %d mV\n",
+                       d->dvfs_reg->reg_id, c->name, d->cur_millivolts);
+
+       return ret;
+}
+
+int tegra_dvfs_set_rate(struct clk *c, unsigned long rate)
+{
+       struct dvfs *d;
+       int ret = 0;
+       bool freq_up;
+
+       c->dvfs_rate = rate;
+
+       freq_up = (c->refcnt == 0) || (rate > c->rate);
+
+       list_for_each_entry(d, &c->dvfs, node) {
+               if (d->higher == freq_up)
+                       ret = __tegra_dvfs_set_rate(c, d, rate);
+               if (ret)
+                       return ret;
+       }
+
+       list_for_each_entry(d, &c->dvfs, node) {
+               if (d->higher != freq_up)
+                       ret = __tegra_dvfs_set_rate(c, d, rate);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(tegra_dvfs_set_rate);
+
+int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d)
+{
+       int i;
+       struct dvfs_reg *dvfs_reg;
+
+       dvfs_reg = attach_dvfs_reg(d);
+       if (!dvfs_reg) {
+               pr_err("Failed to get regulator %s for clock %s\n",
+                       d->reg_id, c->name);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < MAX_DVFS_FREQS; i++) {
+               if (d->millivolts[i] == 0)
+                       break;
+
+               d->freqs[i] *= d->freqs_mult;
+
+               /* If final frequencies are 0, pad with previous frequency */
+               if (d->freqs[i] == 0 && i > 1)
+                       d->freqs[i] = d->freqs[i - 1];
+       }
+       d->num_freqs = i;
+
+       if (d->auto_dvfs)
+               c->auto_dvfs = true;
+
+       c->is_dvfs = true;
+       smp_wmb();
+
+       list_add_tail(&d->node, &c->dvfs);
+
+       list_add_tail(&d->debug_node, &dvfs_debug_list);
+
+       return 0;
+}
+
+int __init tegra_init_dvfs(void)
+{
+       lock_dvfs();
+       tegra2_init_dvfs();
+
+       tegra_clk_set_dvfs_rates();
+       unlock_dvfs();
+
+       return 0;
+}
+late_initcall(tegra_init_dvfs);
+
+#ifdef CONFIG_DEBUG_FS
+static int dvfs_tree_sort_cmp(void *p, struct list_head *a, struct list_head *b)
+{
+       struct dvfs *da = list_entry(a, struct dvfs, debug_node);
+       struct dvfs *db = list_entry(b, struct dvfs, debug_node);
+       int ret;
+
+       ret = strcmp(da->reg_id, db->reg_id);
+       if (ret != 0)
+               return ret;
+
+       if (da->cur_millivolts < db->cur_millivolts)
+               return 1;
+       if (da->cur_millivolts > db->cur_millivolts)
+               return -1;
+
+       return strcmp(da->clk_name, db->clk_name);
+}
+
+static int dvfs_tree_show(struct seq_file *s, void *data)
+{
+       struct dvfs *d;
+       const char *last_reg = "";
+
+       seq_printf(s, "   clock      rate       mV\n");
+       seq_printf(s, "--------------------------------\n");
+
+       lock_dvfs();
+
+       list_sort(NULL, &dvfs_debug_list, dvfs_tree_sort_cmp);
+
+       list_for_each_entry(d, &dvfs_debug_list, debug_node) {
+               if (strcmp(last_reg, d->dvfs_reg->reg_id) != 0) {
+                       last_reg = d->dvfs_reg->reg_id;
+                       seq_printf(s, "%s %d mV:\n", d->dvfs_reg->reg_id,
+                               d->dvfs_reg->millivolts);
+               }
+
+               seq_printf(s, "   %-10s %-10lu %-4d mV\n", d->clk_name,
+                       d->cur_rate, d->cur_millivolts);
+       }
+
+       unlock_dvfs();
+
+       return 0;
+}
+
+static int dvfs_tree_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dvfs_tree_show, inode->i_private);
+}
+
+static const struct file_operations dvfs_tree_fops = {
+       .open           = dvfs_tree_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+int __init dvfs_debugfs_init(struct dentry *clk_debugfs_root)
+{
+       struct dentry *d;
+
+       d = debugfs_create_file("dvfs", S_IRUGO, clk_debugfs_root, NULL,
+               &dvfs_tree_fops);
+       if (!d)
+               return -ENOMEM;
+
+       return 0;
+}
+
+#endif
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
new file mode 100644 (file)
index 0000000..df6a386
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *     Colin Cross <ccross@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _TEGRA_DVFS_H_
+#define _TEGRA_DVFS_H_
+
+#define MAX_DVFS_FREQS 16
+
+struct clk;
+
+struct dvfs {
+       /* Used only by tegra2_clock.c */
+       const char *clk_name;
+       int process_id;
+       bool cpu;
+
+       /* Must be initialized before tegra_dvfs_init */
+       const char *reg_id;
+       int freqs_mult;
+       unsigned long freqs[MAX_DVFS_FREQS];
+       unsigned long millivolts[MAX_DVFS_FREQS];
+       bool auto_dvfs;
+       bool higher;
+
+       /* Filled in by tegra_dvfs_init */
+       int max_millivolts;
+       int num_freqs;
+       struct dvfs_reg *dvfs_reg;
+
+       int cur_millivolts;
+       unsigned long cur_rate;
+       struct list_head node;
+       struct list_head debug_node;
+       struct list_head reg_node;
+};
+
+void lock_dvfs(void);
+void unlock_dvfs(void);
+
+void tegra2_init_dvfs(void);
+int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d);
+int dvfs_debugfs_init(struct dentry *clk_debugfs_root);
+
+#endif
index fc1505559878081f20c444ecb2e8bfc1bb4676b6..e25a3597f7fcf56c5f9a9f8a5683bbb4666ce162 100644 (file)
@@ -48,6 +48,7 @@
        str     \val, [\tmp]
 .endm
 
+#ifdef CONFIG_SMP
 /*
  *     tegra_secondary_startup
  *
@@ -62,6 +63,7 @@ ENTRY(tegra_secondary_startup)
        poke_ev r0, r1
        b       secondary_startup
 ENDPROC(tegra_secondary_startup)
+#endif
 
 /*
  *     __restart_pllx
@@ -194,4 +196,4 @@ __tegra_hotplug_data:
        .long   tegra_pgd_phys
        .long   __cortex_a9_restore
        .size   __tegra_hotplug_data, . - __tegra_hotplug_data
-#endif
\ No newline at end of file
+#endif
index d7723955dac7486519618d3e2c3715267eee667d..04ff7b672ad8a4334d65d33b2ad552d5315d3e5d 100644 (file)
 #ifndef __MACH_CLK_H
 #define __MACH_CLK_H
 
+struct dvfs;
+
 void tegra_periph_reset_deassert(struct clk *c);
 void tegra_periph_reset_assert(struct clk *c);
 
-int clk_enable_cansleep(struct clk *clk);
-void clk_disable_cansleep(struct clk *clk);
-int clk_set_rate_cansleep(struct clk *clk, unsigned long rate);
-int clk_set_parent_cansleep(struct clk *clk, struct clk *parent);
+int tegra_dvfs_set_rate(struct clk *c, unsigned long rate);
 
 #endif
index 6936c8d5f2e456ade73d0d9cc06c2743b00bc06b..458f9c8e85cbada0c07914b1a0f20faf79cc9354 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/delay.h>
+#include <linux/debugfs.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/seq_file.h>
 
 #include <asm/hardware/gic.h>
 
@@ -46,6 +48,8 @@ static u32 tegra_lp0_wake_enb;
 static u32 tegra_lp0_wake_level;
 static u32 tegra_lp0_wake_level_any;
 
+static unsigned int tegra_wake_irq_count[32];
+
 /* ensures that sufficient time is passed for a register write to
  * serialize into the 32KHz domain */
 static void pmc_32kwritel(u32 val, unsigned long offs)
@@ -165,6 +169,8 @@ static void tegra_irq_handle_wake(void)
                pr_info("Resume caused by WAKE%d, %s\n", wake,
                        desc->action->name);
 
+               tegra_wake_irq_count[wake]++;
+
                generic_handle_irq(irq);
        }
 }
@@ -255,3 +261,63 @@ void tegra_irq_resume(void)
        tegra_legacy_irq_resume();
        tegra_irq_handle_wake();
 }
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_wake_irq_debug_show(struct seq_file *s, void *data)
+{
+       int wake;
+       int irq;
+       struct irq_desc *desc;
+       const char *irq_name;
+
+       seq_printf(s, "wake  irq  count  name\n");
+       seq_printf(s, "----------------------\n");
+       for (wake = 0; wake < 32; wake++) {
+               irq = tegra_wake_to_irq(wake);
+               if (irq < 0)
+                       continue;
+
+               desc = irq_to_desc(irq);
+               if (tegra_wake_irq_count[wake] == 0 && desc->action == NULL)
+                       continue;
+
+               if (!(desc->status & IRQ_WAKEUP))
+                       continue;
+
+               irq_name = (desc->action && desc->action->name) ?
+                       desc->action->name : "???";
+
+               seq_printf(s, "%4d  %3d  %5d  %s\n",
+                       wake, irq, tegra_wake_irq_count[wake], irq_name);
+       }
+       return 0;
+}
+
+static int tegra_wake_irq_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, tegra_wake_irq_debug_show, NULL);
+}
+
+static const struct file_operations tegra_wake_irq_debug_fops = {
+       .open           = tegra_wake_irq_debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init tegra_irq_debug_init(void)
+{
+       struct dentry *d;
+
+       d = debugfs_create_file("wake_irq", 0755, NULL, NULL,
+               &tegra_wake_irq_debug_fops);
+       if (!d) {
+               pr_info("Failed to create suspend_mode debug file\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+late_initcall(tegra_irq_debug_init);
+#endif
index 96c1172cd77759220ede32aed66bb94eeded2301..659c66967fb5b64b6c3741f97c70003577c243c7 100644 (file)
@@ -33,7 +33,6 @@
 #include <asm/smp_scu.h>
 #include <asm/cpu.h>
 #include <asm/mmu_context.h>
-#include <asm/pgalloc.h>
 
 #include <mach/iomap.h>
 
@@ -43,8 +42,6 @@ extern void tegra_secondary_startup(void);
 
 static DEFINE_SPINLOCK(boot_lock);
 static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
-extern void __cortex_a9_restore(void);
-extern void __shut_off_mmu(void);
 
 #ifdef CONFIG_HOTPLUG_CPU
 static DEFINE_PER_CPU(struct completion, cpu_killed);
@@ -64,10 +61,6 @@ const struct cpumask *const cpu_init_mask = to_cpumask(cpu_init_bits);
 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
 
-unsigned long tegra_pgd_phys;  /* pgd used by hotplug & LP2 bootup */
-static pgd_t *tegra_pgd;
-void *tegra_context_area = NULL;
-
 void __cpuinit platform_secondary_init(unsigned int cpu)
 {
        trace_hardirqs_off();
@@ -150,61 +143,6 @@ void __init smp_init_cpus(void)
                cpu_set(i, cpu_possible_map);
 }
 
-static int create_suspend_pgtable(void)
-{
-       int i;
-       pmd_t *pmd;
-       /* arrays of virtual-to-physical mappings which must be
-        * present to safely boot hotplugged / LP2-idled CPUs.
-        * tegra_hotplug_startup (hotplug reset vector) is mapped
-        * VA=PA so that the translation post-MMU is the same as
-        * pre-MMU, IRAM is mapped VA=PA so that SDRAM self-refresh
-        * can safely disable the MMU */
-       unsigned long addr_v[] = {
-               PHYS_OFFSET,
-               IO_IRAM_PHYS,
-               (unsigned long)tegra_context_area,
-               (unsigned long)virt_to_phys(tegra_hotplug_startup),
-               (unsigned long)__cortex_a9_restore,
-               (unsigned long)virt_to_phys(__shut_off_mmu),
-       };
-       unsigned long addr_p[] = {
-               PHYS_OFFSET,
-               IO_IRAM_PHYS,
-               (unsigned long)virt_to_phys(tegra_context_area),
-               (unsigned long)virt_to_phys(tegra_hotplug_startup),
-               (unsigned long)virt_to_phys(__cortex_a9_restore),
-               (unsigned long)virt_to_phys(__shut_off_mmu),
-       };
-       unsigned int flags = PMD_TYPE_SECT | PMD_SECT_AP_WRITE |
-               PMD_SECT_WBWA | PMD_SECT_S;
-
-       tegra_pgd = pgd_alloc(&init_mm);
-       if (!tegra_pgd)
-               return -ENOMEM;
-
-       for (i=0; i<ARRAY_SIZE(addr_p); i++) {
-               unsigned long v = addr_v[i];
-               pmd = pmd_offset(tegra_pgd + pgd_index(v), v);
-               *pmd = __pmd((addr_p[i] & PGDIR_MASK) | flags);
-               flush_pmd_entry(pmd);
-               outer_clean_range(__pa(pmd), __pa(pmd + 1));
-       }
-
-       tegra_pgd_phys = virt_to_phys(tegra_pgd);
-       __cpuc_flush_dcache_area(&tegra_pgd_phys,
-               sizeof(tegra_pgd_phys));
-       outer_clean_range(__pa(&tegra_pgd_phys),
-               __pa(&tegra_pgd_phys+1));
-
-       __cpuc_flush_dcache_area(&tegra_context_area,
-               sizeof(tegra_context_area));
-       outer_clean_range(__pa(&tegra_context_area),
-               __pa(&tegra_context_area+1));
-
-       return 0;
-}
-
 void __init smp_prepare_cpus(unsigned int max_cpus)
 {
        unsigned int ncores = scu_get_core_count(scu_base);
@@ -219,13 +157,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
        if (max_cpus > ncores)
                max_cpus = ncores;
 
-       tegra_context_area = kzalloc(CONTEXT_SIZE_BYTES * ncores, GFP_KERNEL);
-
-       if (tegra_context_area && create_suspend_pgtable()) {
-               kfree(tegra_context_area);
-               tegra_context_area = NULL;
-       }
-
        /*
         * Initialise the present map, which describes the set of CPUs
         * actually populated at the present time.
index 84d87f4ff8747e539c89df21315afd25ee27433e..37992415b9c6ae73e1a6a0aa011de2ff37182951 100644 (file)
 #define TEGRA_IRAM_CODE_SIZE           SZ_4K
 
 #ifndef __ASSEMBLY__
+extern void *tegra_context_area;
+
+u64 tegra_rtc_read_ms(void);
 void tegra_lp2_set_trigger(unsigned long cycles);
 unsigned long tegra_lp2_timer_remain(void);
 void __cortex_a9_save(unsigned int mode);
+void __cortex_a9_restore(void);
+void __shut_off_mmu(void);
 void tegra_lp2_startup(void);
 unsigned int tegra_suspend_lp2(unsigned int us);
 void tegra_hotplug_startup(void);
index 3b64aa56b0637b2f3c47aaf8aa544df57d02ca9f..06bafa1c45c637b7f44cc2d8c4194f330bff639a 100644 (file)
@@ -41,6 +41,7 @@
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/hardware/gic.h>
 #include <asm/localtimer.h>
+#include <asm/pgalloc.h>
 #include <asm/tlbflush.h>
 
 #include <mach/iomap.h>
@@ -68,13 +69,11 @@ struct suspend_context
 
 volatile struct suspend_context tegra_sctx;
 
-#ifdef CONFIG_HOTPLUG_CPU
 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
 static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
 static void __iomem *flow_ctrl = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE);
 static void __iomem *evp_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE)+0x100;
 static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
-#endif
 
 #define PMC_CTRL               0x0
 #define PMC_CTRL_LATCH_WAKEUPS (1 << 5)
@@ -112,11 +111,22 @@ static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE);
 #define FLOW_CTRL_CPU_CSR      0x8
 #define FLOW_CTRL_CPU1_CSR     0x18
 
+unsigned long tegra_pgd_phys;  /* pgd used by hotplug & LP2 bootup */
+static pgd_t *tegra_pgd;
+void *tegra_context_area = NULL;
+
 static struct clk *tegra_pclk = NULL;
 static const struct tegra_suspend_platform_data *pdata = NULL;
 static unsigned long wb0_restore = 0;
 static enum tegra_suspend_mode current_suspend_mode;
 
+static unsigned int tegra_time_in_suspend[32];
+
+static inline unsigned int time_to_bin(unsigned int time)
+{
+       return fls(time);
+}
+
 unsigned long tegra_cpu_power_good_time(void)
 {
        if (WARN_ON_ONCE(!pdata))
@@ -166,6 +176,67 @@ static void set_power_timers(unsigned long us_on, unsigned long us_off,
        last_pclk = pclk;
 }
 
+static int create_suspend_pgtable(void)
+{
+       int i;
+       pmd_t *pmd;
+       /* arrays of virtual-to-physical mappings which must be
+        * present to safely boot hotplugged / LP2-idled CPUs.
+        * tegra_hotplug_startup (hotplug reset vector) is mapped
+        * VA=PA so that the translation post-MMU is the same as
+        * pre-MMU, IRAM is mapped VA=PA so that SDRAM self-refresh
+        * can safely disable the MMU */
+       unsigned long addr_v[] = {
+               PHYS_OFFSET,
+               IO_IRAM_PHYS,
+               (unsigned long)tegra_context_area,
+#ifdef CONFIG_HOTPLUG_CPU
+               (unsigned long)virt_to_phys(tegra_hotplug_startup),
+#endif
+               (unsigned long)__cortex_a9_restore,
+               (unsigned long)virt_to_phys(__shut_off_mmu),
+       };
+       unsigned long addr_p[] = {
+               PHYS_OFFSET,
+               IO_IRAM_PHYS,
+               (unsigned long)virt_to_phys(tegra_context_area),
+#ifdef CONFIG_HOTPLUG_CPU
+               (unsigned long)virt_to_phys(tegra_hotplug_startup),
+#endif
+               (unsigned long)virt_to_phys(__cortex_a9_restore),
+               (unsigned long)virt_to_phys(__shut_off_mmu),
+       };
+       unsigned int flags = PMD_TYPE_SECT | PMD_SECT_AP_WRITE |
+               PMD_SECT_WBWA | PMD_SECT_S;
+
+       tegra_pgd = pgd_alloc(&init_mm);
+       if (!tegra_pgd)
+               return -ENOMEM;
+
+       for (i=0; i<ARRAY_SIZE(addr_p); i++) {
+               unsigned long v = addr_v[i];
+               pmd = pmd_offset(tegra_pgd + pgd_index(v), v);
+               *pmd = __pmd((addr_p[i] & PGDIR_MASK) | flags);
+               flush_pmd_entry(pmd);
+               outer_clean_range(__pa(pmd), __pa(pmd + 1));
+       }
+
+       tegra_pgd_phys = virt_to_phys(tegra_pgd);
+       __cpuc_flush_dcache_area(&tegra_pgd_phys,
+               sizeof(tegra_pgd_phys));
+       outer_clean_range(__pa(&tegra_pgd_phys),
+               __pa(&tegra_pgd_phys+1));
+
+       __cpuc_flush_dcache_area(&tegra_context_area,
+               sizeof(tegra_context_area));
+       outer_clean_range(__pa(&tegra_context_area),
+               __pa(&tegra_context_area+1));
+
+       return 0;
+}
+
+
+
 /*
  * suspend_cpu_complex
  *
@@ -199,8 +270,10 @@ static noinline void restore_cpu_complex(void)
        writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR);
        wmb();
 
+#ifdef CONFIG_HAVE_ARM_TWD
        writel(tegra_sctx.twd_ctrl, twd_base + 0x8);
        writel(tegra_sctx.twd_load, twd_base + 0);
+#endif
 
        gic_dist_restore(0);
        get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
@@ -224,9 +297,11 @@ static noinline void suspend_cpu_complex(void)
        tegra_sctx.pllx_misc = readl(clk_rst + CLK_RESET_PLLX_MISC);
        tegra_sctx.cclk_divider = readl(clk_rst + CLK_RESET_CCLK_DIVIDER);
 
+#ifdef CONFIG_HAVE_ARM_TWD
        tegra_sctx.twd_ctrl = readl(twd_base + 0x8);
        tegra_sctx.twd_load = readl(twd_base + 0);
        local_timer_stop();
+#endif
 
        reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR);
        /* clear any pending events, set the WFE bitmap to specify just
@@ -473,6 +548,10 @@ static int tegra_suspend_enter(suspend_state_t state)
        bool do_lp0 = (current_suspend_mode == TEGRA_SUSPEND_LP0);
        bool do_lp2 = (current_suspend_mode == TEGRA_SUSPEND_LP2);
        int lp_state;
+       u64 rtc_before;
+       u64 rtc_after;
+       u64 secs;
+       u32 ms;
 
        if (do_lp2)
                lp_state = 2;
@@ -505,11 +584,15 @@ static int tegra_suspend_enter(suspend_state_t state)
                }
        }
 
+       rtc_before = tegra_rtc_read_ms();
+
        if (do_lp2)
                tegra_suspend_lp2(0);
        else
                tegra_suspend_dram(do_lp0);
 
+       rtc_after = tegra_rtc_read_ms();
+
        for_each_irq_desc(irq, desc) {
                if ((desc->status & IRQ_WAKEUP) &&
                    (desc->status & IRQ_SUSPENDED)) {
@@ -534,6 +617,12 @@ static int tegra_suspend_enter(suspend_state_t state)
                tegra_irq_resume();
        }
 
+       secs = rtc_after - rtc_before;
+       ms = do_div(secs, 1000);
+       pr_info("Suspended for %llu.%03u seconds\n", secs, ms);
+
+       tegra_time_in_suspend[time_to_bin(secs)]++;
+
        local_irq_restore(flags);
 
        return 0;
@@ -565,6 +654,14 @@ void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
                plat->suspend_mode = TEGRA_SUSPEND_LP1;
        }
 
+       tegra_context_area = kzalloc(CONTEXT_SIZE_BYTES * NR_CPUS, GFP_KERNEL);
+       pr_info("%s: %p\n", __func__, tegra_context_area);
+
+       if (tegra_context_area && create_suspend_pgtable()) {
+               kfree(tegra_context_area);
+               tegra_context_area = NULL;
+       }
+
 #ifdef CONFIG_PM
        iram_save_size = (unsigned long)__tegra_iram_end;
        iram_save_size -= (unsigned long)__tegra_lp1_reset;
@@ -672,6 +769,33 @@ static const struct file_operations tegra_suspend_debug_fops = {
        .release        = single_release,
 };
 
+static int tegra_suspend_time_debug_show(struct seq_file *s, void *data)
+{
+       int bin;
+       seq_printf(s, "time (secs)  count\n");
+       seq_printf(s, "------------------\n");
+       for (bin = 0; bin < 32; bin++) {
+               if (tegra_time_in_suspend[bin] == 0)
+                       continue;
+               seq_printf(s, "%4d - %4d %4u\n",
+                       bin ? 1 << (bin - 1) : 0, 1 << bin,
+                       tegra_time_in_suspend[bin]);
+       }
+       return 0;
+}
+
+static int tegra_suspend_time_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, tegra_suspend_time_debug_show, NULL);
+}
+
+static const struct file_operations tegra_suspend_time_debug_fops = {
+       .open           = tegra_suspend_time_debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 static int __init tegra_suspend_debug_init(void)
 {
        struct dentry *d;
@@ -683,6 +807,13 @@ static int __init tegra_suspend_debug_init(void)
                return -ENOMEM;
        }
 
+       d = debugfs_create_file("suspend_time", 0755, NULL, NULL,
+               &tegra_suspend_time_debug_fops);
+       if (!d) {
+               pr_info("Failed to create suspend_time debug file\n");
+               return -ENOMEM;
+       }
+
        return 0;
 }
 
index 59ca5b0ec412d0c529009b0304951d24d648d401..4a9e3e0b7f2e90d865d51be0b9c6a004e4a09a7b 100644 (file)
@@ -32,7 +32,6 @@
 
 #include "clock.h"
 #include "fuse.h"
-#include "tegra2_dvfs.h"
 
 #define RST_DEVICES                    0x004
 #define RST_DEVICES_SET                        0x300
@@ -80,7 +79,6 @@
 #define PLL_BASE_ENABLE                        (1<<30)
 #define PLL_BASE_REF_ENABLE            (1<<29)
 #define PLL_BASE_OVERRIDE              (1<<28)
-#define PLL_BASE_LOCK                  (1<<27)
 #define PLL_BASE_DIVP_MASK             (0x7<<20)
 #define PLL_BASE_DIVP_SHIFT            20
 #define PLL_BASE_DIVN_MASK             (0x3FF<<8)
@@ -95,7 +93,6 @@
 #define PLL_OUT_RESET_DISABLE          (1<<0)
 
 #define PLL_MISC(c)                    (((c)->flags & PLL_ALT_MISC_REG) ? 0x4 : 0xc)
-#define PLL_MISC_LOCK_ENABLE(c)                (((c)->flags & PLLU) ? (1<<22) : (1<<18))
 
 #define PLL_MISC_DCCON_SHIFT           20
 #define PLL_MISC_CPCON_SHIFT           8
 #define PLLD_MISC_DIV_RST              (1<<23)
 #define PLLD_MISC_DCCON_SHIFT          12
 
-#define PERIPH_CLK_TO_ENB_REG(c)       ((c->clk_num / 32) * 4)
-#define PERIPH_CLK_TO_ENB_SET_REG(c)   ((c->clk_num / 32) * 8)
-#define PERIPH_CLK_TO_ENB_BIT(c)       (1 << (c->clk_num % 32))
+#define PERIPH_CLK_TO_ENB_REG(c)       ((c->u.periph.clk_num / 32) * 4)
+#define PERIPH_CLK_TO_ENB_SET_REG(c)   ((c->u.periph.clk_num / 32) * 8)
+#define PERIPH_CLK_TO_ENB_BIT(c)       (1 << (c->u.periph.clk_num % 32))
 
 #define SUPER_CLK_MUX                  0x00
 #define SUPER_STATE_SHIFT              28
 static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
 static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
 
+/*
+ * Some peripheral clocks share an enable bit, so refcount the enable bits
+ * in registers CLK_ENABLE_L, CLK_ENABLE_H, and CLK_ENABLE_U
+ */
+static int tegra_periph_clk_enable_refcount[3 * 32];
+
 #define clk_writel(value, reg) \
        __raw_writel(value, (u32)reg_clk_base + (reg))
 #define clk_readl(reg) \
@@ -299,6 +302,8 @@ static void tegra2_super_clk_init(struct clk *c)
        }
        BUG_ON(sel->input == NULL);
        c->parent = sel->input;
+
+       INIT_LIST_HEAD(&c->u.shared_bus.list);
 }
 
 static int tegra2_super_clk_enable(struct clk *c)
@@ -380,29 +385,36 @@ static void tegra2_cpu_clk_disable(struct clk *c)
 static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
 {
        int ret;
-       ret = clk_set_parent_locked(c->parent, c->backup);
+       /*
+        * Take an extra reference to the main pll so it doesn't turn
+        * off when we move the cpu off of it
+        */
+       clk_enable_locked(c->u.cpu.main);
+
+       ret = clk_set_parent_locked(c->parent, c->u.cpu.backup);
        if (ret) {
-               pr_err("Failed to switch cpu to clock %s\n", c->backup->name);
-               return ret;
+               pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
+               goto out;
        }
 
-       if (rate == c->backup->rate)
+       if (rate == c->u.cpu.backup->rate)
                goto out;
 
-       ret = clk_set_rate_locked(c->main, rate);
+       ret = clk_set_rate_locked(c->u.cpu.main, rate);
        if (ret) {
                pr_err("Failed to change cpu pll to %lu\n", rate);
-               return ret;
+               goto out;
        }
 
-       ret = clk_set_parent_locked(c->parent, c->main);
+       ret = clk_set_parent_locked(c->parent, c->u.cpu.main);
        if (ret) {
-               pr_err("Failed to switch cpu to clock %s\n", c->main->name);
-               return ret;
+               pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
+               goto out;
        }
 
 out:
-       return 0;
+       clk_disable_locked(c->u.cpu.main);
+       return ret;
 }
 
 static struct clk_ops tegra_cpu_ops = {
@@ -414,16 +426,6 @@ static struct clk_ops tegra_cpu_ops = {
 
 /* virtual cop clock functions. Used to acquire the fake 'cop' clock to
  * reset the COP block (i.e. AVP) */
-static void tegra2_cop_clk_init(struct clk *c)
-{
-       c->state = c->parent->state;
-}
-
-static int tegra2_cop_clk_enable(struct clk *c)
-{
-       return 0;
-}
-
 static void tegra2_cop_clk_reset(struct clk *c, bool assert)
 {
        unsigned long reg = assert ? RST_DEVICES_SET : RST_DEVICES_CLR;
@@ -433,8 +435,6 @@ static void tegra2_cop_clk_reset(struct clk *c, bool assert)
 }
 
 static struct clk_ops tegra_cop_ops = {
-       .init     = tegra2_cop_clk_init,
-       .enable   = tegra2_cop_clk_enable,
        .reset    = tegra2_cop_clk_reset,
 };
 
@@ -571,17 +571,7 @@ static struct clk_ops tegra_blink_clk_ops = {
 /* PLL Functions */
 static int tegra2_pll_clk_wait_for_lock(struct clk *c)
 {
-       ktime_t before;
-
-       before = ktime_get();
-
-       while (!(clk_readl(c->reg + PLL_BASE) & PLL_BASE_LOCK)) {
-               if (ktime_us_delta(ktime_get(), before) > 5000) {
-                       pr_err("Timed out waiting for lock bit on pll %s",
-                               c->name);
-                       return -1;
-               }
-       }
+       udelay(c->u.pll.lock_delay);
 
        return 0;
 }
@@ -619,10 +609,6 @@ static int tegra2_pll_clk_enable(struct clk *c)
        val |= PLL_BASE_ENABLE;
        clk_writel(val, c->reg + PLL_BASE);
 
-       val = clk_readl(c->reg + PLL_MISC(c));
-       val |= PLL_MISC_LOCK_ENABLE(c);
-       clk_writel(val, c->reg + PLL_MISC(c));
-
        tegra2_pll_clk_wait_for_lock(c);
 
        return 0;
@@ -642,13 +628,12 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
 {
        u32 val;
        unsigned long input_rate;
-       const struct clk_pll_table *sel;
+       const struct clk_pll_freq_table *sel;
 
        pr_debug("%s: %s %lu\n", __func__, c->name, rate);
-       BUG_ON(c->refcnt != 0);
 
        input_rate = c->parent->rate;
-       for (sel = c->pll_table; sel->input_rate != 0; sel++) {
+       for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
                if (sel->input_rate == input_rate && sel->output_rate == rate) {
                        c->mul = sel->n;
                        c->div = sel->m * sel->p;
@@ -884,6 +869,10 @@ static int tegra2_periph_clk_enable(struct clk *c)
        u32 val;
        pr_debug("%s on clock %s\n", __func__, c->name);
 
+       tegra_periph_clk_enable_refcount[c->u.periph.clk_num]++;
+       if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] > 1)
+               return 0;
+
        clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
                CLK_OUT_ENB_SET + PERIPH_CLK_TO_ENB_SET_REG(c));
        if (!(c->flags & PERIPH_NO_RESET) && !(c->flags & PERIPH_MANUAL_RESET))
@@ -903,8 +892,12 @@ static void tegra2_periph_clk_disable(struct clk *c)
 {
        pr_debug("%s on clock %s\n", __func__, c->name);
 
-       clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
-               CLK_OUT_ENB_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
+       if (c->refcnt)
+               tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
+
+       if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] == 0)
+               clk_writel(PERIPH_CLK_TO_ENB_BIT(c),
+                       CLK_OUT_ENB_CLR + PERIPH_CLK_TO_ENB_SET_REG(c));
 }
 
 static void tegra2_periph_clk_reset(struct clk *c, bool assert)
@@ -1148,6 +1141,66 @@ static struct clk_ops tegra_cdev_clk_ops = {
        .disable                = &tegra2_cdev_clk_disable,
 };
 
+/* shared bus ops */
+/*
+ * Some clocks may have multiple downstream users that need to request a
+ * higher clock rate.  Shared bus clocks provide a unique shared_bus_user
+ * clock to each user.  The frequency of the bus is set to the highest
+ * enabled shared_bus_user clock, with a minimum value set by the
+ * shared bus.
+ */
+static void tegra_clk_shared_bus_update(struct clk *bus)
+{
+       struct clk *c;
+       unsigned long rate = bus->u.shared_bus.min_rate;
+
+       list_for_each_entry(c, &bus->u.shared_bus.list, u.shared_bus_user.node)
+               if (c->u.shared_bus_user.enabled)
+                       rate = max(c->u.shared_bus_user.rate, rate);
+
+       if (rate != bus->rate)
+               clk_set_rate_locked(bus, rate);
+};
+
+static void tegra_clk_shared_bus_init(struct clk *c)
+{
+       c->max_rate = c->parent->max_rate;
+       c->u.shared_bus_user.rate = c->parent->max_rate;
+       c->state = OFF;
+       c->set = true;
+
+       list_add_tail(&c->u.shared_bus_user.node,
+               &c->parent->u.shared_bus.list);
+}
+
+static int tegra_clk_shared_bus_set_rate(struct clk *c, unsigned long rate)
+{
+       c->u.shared_bus_user.rate = rate;
+       tegra_clk_shared_bus_update(c->parent);
+       return 0;
+}
+
+static int tegra_clk_shared_bus_enable(struct clk *c)
+{
+       c->u.shared_bus_user.enabled = true;
+       tegra_clk_shared_bus_update(c->parent);
+       return 0;
+}
+
+static void tegra_clk_shared_bus_disable(struct clk *c)
+{
+       c->u.shared_bus_user.enabled = false;
+       tegra_clk_shared_bus_update(c->parent);
+}
+
+static struct clk_ops tegra_clk_shared_bus_ops = {
+       .init = tegra_clk_shared_bus_init,
+       .enable = tegra_clk_shared_bus_enable,
+       .disable = tegra_clk_shared_bus_disable,
+       .set_rate = tegra_clk_shared_bus_set_rate,
+};
+
+
 /* Clock definitions */
 static struct clk tegra_clk_32k = {
        .name = "clk_32k",
@@ -1156,7 +1209,7 @@ static struct clk tegra_clk_32k = {
        .max_rate = 32768,
 };
 
-static struct clk_pll_table tegra_pll_s_table[] = {
+static struct clk_pll_freq_table tegra_pll_s_freq_table[] = {
        {32768, 12000000, 366, 1, 1, 0},
        {32768, 13000000, 397, 1, 1, 0},
        {32768, 19200000, 586, 1, 1, 0},
@@ -1168,16 +1221,19 @@ static struct clk tegra_pll_s = {
        .name      = "pll_s",
        .flags     = PLL_ALT_MISC_REG,
        .ops       = &tegra_pll_ops,
-       .reg       = 0xf0,
-       .input_min = 32768,
-       .input_max = 32768,
        .parent    = &tegra_clk_32k,
-       .cf_min    = 0, /* FIXME */
-       .cf_max    = 0, /* FIXME */
-       .vco_min   = 12000000,
-       .vco_max   = 26000000,
-       .pll_table = tegra_pll_s_table,
        .max_rate  = 26000000,
+       .reg       = 0xf0,
+       .u.pll = {
+               .input_min = 32768,
+               .input_max = 32768,
+               .cf_min    = 0, /* FIXME */
+               .cf_max    = 0, /* FIXME */
+               .vco_min   = 12000000,
+               .vco_max   = 26000000,
+               .freq_table = tegra_pll_s_freq_table,
+               .lock_delay = 300,
+       },
 };
 
 static struct clk_mux_sel tegra_clk_m_sel[] = {
@@ -1185,18 +1241,18 @@ static struct clk_mux_sel tegra_clk_m_sel[] = {
        { .input = &tegra_pll_s,  .value = 1},
        { 0, 0},
 };
+
 static struct clk tegra_clk_m = {
        .name      = "clk_m",
        .flags     = ENABLE_ON_INIT,
        .ops       = &tegra_clk_m_ops,
        .inputs    = tegra_clk_m_sel,
        .reg       = 0x1fc,
-       .reg_mask  = (1<<28),
        .reg_shift = 28,
        .max_rate  = 26000000,
 };
 
-static struct clk_pll_table tegra_pll_c_table[] = {
+static struct clk_pll_freq_table tegra_pll_c_freq_table[] = {
        { 0, 0, 0, 0, 0, 0 },
 };
 
@@ -1205,15 +1261,18 @@ static struct clk tegra_pll_c = {
        .flags     = PLL_HAS_CPCON,
        .ops       = &tegra_pll_ops,
        .reg       = 0x80,
-       .input_min = 2000000,
-       .input_max = 31000000,
        .parent    = &tegra_clk_m,
-       .cf_min    = 1000000,
-       .cf_max    = 6000000,
-       .vco_min   = 20000000,
-       .vco_max   = 1400000000,
-       .pll_table = tegra_pll_c_table,
        .max_rate  = 600000000,
+       .u.pll = {
+               .input_min = 2000000,
+               .input_max = 31000000,
+               .cf_min    = 1000000,
+               .cf_max    = 6000000,
+               .vco_min   = 20000000,
+               .vco_max   = 1400000000,
+               .freq_table = tegra_pll_c_freq_table,
+               .lock_delay = 300,
+       },
 };
 
 static struct clk tegra_pll_c_out1 = {
@@ -1226,7 +1285,7 @@ static struct clk tegra_pll_c_out1 = {
        .max_rate  = 600000000,
 };
 
-static struct clk_pll_table tegra_pll_m_table[] = {
+static struct clk_pll_freq_table tegra_pll_m_freq_table[] = {
        { 12000000, 666000000, 666, 12, 1, 8},
        { 13000000, 666000000, 666, 13, 1, 8},
        { 19200000, 666000000, 555, 16, 1, 8},
@@ -1243,15 +1302,18 @@ static struct clk tegra_pll_m = {
        .flags     = PLL_HAS_CPCON,
        .ops       = &tegra_pll_ops,
        .reg       = 0x90,
-       .input_min = 2000000,
-       .input_max = 31000000,
        .parent    = &tegra_clk_m,
-       .cf_min    = 1000000,
-       .cf_max    = 6000000,
-       .vco_min   = 20000000,
-       .vco_max   = 1200000000,
-       .pll_table = tegra_pll_m_table,
        .max_rate  = 800000000,
+       .u.pll = {
+               .input_min = 2000000,
+               .input_max = 31000000,
+               .cf_min    = 1000000,
+               .cf_max    = 6000000,
+               .vco_min   = 20000000,
+               .vco_max   = 1200000000,
+               .freq_table = tegra_pll_m_freq_table,
+               .lock_delay = 300,
+       },
 };
 
 static struct clk tegra_pll_m_out1 = {
@@ -1264,7 +1326,7 @@ static struct clk tegra_pll_m_out1 = {
        .max_rate  = 600000000,
 };
 
-static struct clk_pll_table tegra_pll_p_table[] = {
+static struct clk_pll_freq_table tegra_pll_p_freq_table[] = {
        { 12000000, 216000000, 432, 12, 2, 8},
        { 13000000, 216000000, 432, 13, 2, 8},
        { 19200000, 216000000, 90,   4, 2, 1},
@@ -1281,15 +1343,18 @@ static struct clk tegra_pll_p = {
        .flags     = ENABLE_ON_INIT | PLL_FIXED | PLL_HAS_CPCON,
        .ops       = &tegra_pll_ops,
        .reg       = 0xa0,
-       .input_min = 2000000,
-       .input_max = 31000000,
        .parent    = &tegra_clk_m,
-       .cf_min    = 1000000,
-       .cf_max    = 6000000,
-       .vco_min   = 20000000,
-       .vco_max   = 1400000000,
-       .pll_table = tegra_pll_p_table,
        .max_rate  = 432000000,
+       .u.pll = {
+               .input_min = 2000000,
+               .input_max = 31000000,
+               .cf_min    = 1000000,
+               .cf_max    = 6000000,
+               .vco_min   = 20000000,
+               .vco_max   = 1400000000,
+               .freq_table = tegra_pll_p_freq_table,
+               .lock_delay = 300,
+       },
 };
 
 static struct clk tegra_pll_p_out1 = {
@@ -1332,7 +1397,7 @@ static struct clk tegra_pll_p_out4 = {
        .max_rate  = 432000000,
 };
 
-static struct clk_pll_table tegra_pll_a_table[] = {
+static struct clk_pll_freq_table tegra_pll_a_freq_table[] = {
        { 28800000, 56448000, 49, 25, 1, 1},
        { 28800000, 73728000, 64, 25, 1, 1},
        { 28800000, 11289600, 49, 25, 1, 1},
@@ -1346,15 +1411,18 @@ static struct clk tegra_pll_a = {
        .flags     = PLL_HAS_CPCON,
        .ops       = &tegra_pll_ops,
        .reg       = 0xb0,
-       .input_min = 2000000,
-       .input_max = 31000000,
        .parent    = &tegra_pll_p_out1,
-       .cf_min    = 1000000,
-       .cf_max    = 6000000,
-       .vco_min   = 20000000,
-       .vco_max   = 1400000000,
-       .pll_table = tegra_pll_a_table,
        .max_rate  = 56448000,
+       .u.pll = {
+               .input_min = 2000000,
+               .input_max = 31000000,
+               .cf_min    = 1000000,
+               .cf_max    = 6000000,
+               .vco_min   = 20000000,
+               .vco_max   = 1400000000,
+               .freq_table = tegra_pll_a_freq_table,
+               .lock_delay = 300,
+       },
 };
 
 static struct clk tegra_pll_a_out0 = {
@@ -1367,7 +1435,7 @@ static struct clk tegra_pll_a_out0 = {
        .max_rate  = 56448000,
 };
 
-static struct clk_pll_table tegra_pll_d_table[] = {
+static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
        { 12000000, 216000000, 216, 12, 1, 4},
        { 13000000, 216000000, 216, 13, 1, 4},
        { 19200000, 216000000, 135, 12, 1, 3},
@@ -1391,15 +1459,18 @@ static struct clk tegra_pll_d = {
        .flags     = PLL_HAS_CPCON | PLLD,
        .ops       = &tegra_pll_ops,
        .reg       = 0xd0,
-       .input_min = 2000000,
-       .input_max = 40000000,
        .parent    = &tegra_clk_m,
-       .cf_min    = 1000000,
-       .cf_max    = 6000000,
-       .vco_min   = 40000000,
-       .vco_max   = 1000000000,
-       .pll_table = tegra_pll_d_table,
        .max_rate  = 1000000000,
+       .u.pll = {
+               .input_min = 2000000,
+               .input_max = 40000000,
+               .cf_min    = 1000000,
+               .cf_max    = 6000000,
+               .vco_min   = 40000000,
+               .vco_max   = 1000000000,
+               .freq_table = tegra_pll_d_freq_table,
+               .lock_delay = 1000,
+       },
 };
 
 static struct clk tegra_pll_d_out0 = {
@@ -1410,7 +1481,7 @@ static struct clk tegra_pll_d_out0 = {
        .max_rate  = 500000000,
 };
 
-static struct clk_pll_table tegra_pll_u_table[] = {
+static struct clk_pll_freq_table tegra_pll_u_freq_table[] = {
        { 12000000, 480000000, 960, 12, 2, 0},
        { 13000000, 480000000, 960, 13, 2, 0},
        { 19200000, 480000000, 200, 4,  2, 0},
@@ -1423,18 +1494,21 @@ static struct clk tegra_pll_u = {
        .flags     = PLLU,
        .ops       = &tegra_pll_ops,
        .reg       = 0xc0,
-       .input_min = 2000000,
-       .input_max = 40000000,
        .parent    = &tegra_clk_m,
-       .cf_min    = 1000000,
-       .cf_max    = 6000000,
-       .vco_min   = 480000000,
-       .vco_max   = 960000000,
-       .pll_table = tegra_pll_u_table,
        .max_rate  = 480000000,
-};
-
-static struct clk_pll_table tegra_pll_x_table[] = {
+       .u.pll = {
+               .input_min = 2000000,
+               .input_max = 40000000,
+               .cf_min    = 1000000,
+               .cf_max    = 6000000,
+               .vco_min   = 480000000,
+               .vco_max   = 960000000,
+               .freq_table = tegra_pll_u_freq_table,
+               .lock_delay = 1000,
+       },
+};
+
+static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
        /* 1 GHz */
        { 12000000, 1000000000, 1000, 12, 1, 12},
        { 13000000, 1000000000, 1000, 13, 1, 12},
@@ -1485,44 +1559,53 @@ static struct clk tegra_pll_x = {
        .flags     = PLL_HAS_CPCON | PLL_ALT_MISC_REG,
        .ops       = &tegra_pllx_ops,
        .reg       = 0xe0,
-       .input_min = 2000000,
-       .input_max = 31000000,
        .parent    = &tegra_clk_m,
-       .cf_min    = 1000000,
-       .cf_max    = 6000000,
-       .vco_min   = 20000000,
-       .vco_max   = 1200000000,
-       .pll_table = tegra_pll_x_table,
        .max_rate  = 1000000000,
+       .u.pll = {
+               .input_min = 2000000,
+               .input_max = 31000000,
+               .cf_min    = 1000000,
+               .cf_max    = 6000000,
+               .vco_min   = 20000000,
+               .vco_max   = 1200000000,
+               .freq_table = tegra_pll_x_freq_table,
+               .lock_delay = 300,
+       },
 };
 
 static struct clk tegra_clk_d = {
        .name      = "clk_d",
        .flags     = PERIPH_NO_RESET,
        .ops       = &tegra_clk_double_ops,
-       .clk_num   = 90,
        .reg       = 0x34,
        .reg_shift = 12,
        .parent    = &tegra_clk_m,
        .max_rate  = 52000000,
+       .u.periph  = {
+               .clk_num = 90,
+       },
 };
 
 /* dap_mclk1, belongs to the cdev1 pingroup. */
 static struct clk tegra_dev1_clk = {
        .name      = "clk_dev1",
        .ops       = &tegra_cdev_clk_ops,
-       .clk_num   = 94,
        .rate      = 26000000,
        .max_rate  = 26000000,
+       .u.periph  = {
+               .clk_num = 94,
+       },
 };
 
 /* dap_mclk2, belongs to the cdev2 pingroup. */
 static struct clk tegra_dev2_clk = {
        .name      = "clk_dev2",
        .ops       = &tegra_cdev_clk_ops,
-       .clk_num   = 93,
        .rate      = 26000000,
        .max_rate  = 26000000,
+       .u.periph  = {
+               .clk_num   = 93,
+       },
 };
 
 /* initialized before peripheral clocks */
@@ -1557,10 +1640,12 @@ static struct clk tegra_clk_audio_2x = {
        .flags     = PERIPH_NO_RESET,
        .max_rate  = 48000000,
        .ops       = &tegra_clk_double_ops,
-       .clk_num   = 89,
        .reg       = 0x34,
        .reg_shift = 8,
        .parent    = &tegra_clk_audio,
+       .u.periph = {
+               .clk_num = 89,
+       },
 };
 
 struct clk_lookup tegra_audio_clk_lookups[] = {
@@ -1633,16 +1718,20 @@ static struct clk tegra_clk_sclk = {
        .reg    = 0x28,
        .ops    = &tegra_super_ops,
        .max_rate = 240000000,
+       .u.shared_bus = {
+               .min_rate = 120000000,
+       },
 };
 
 static struct clk tegra_clk_virtual_cpu = {
        .name      = "cpu",
        .parent    = &tegra_clk_cclk,
-       .main      = &tegra_pll_x,
-       .backup    = &tegra_pll_p,
        .ops       = &tegra_cpu_ops,
        .max_rate  = 1000000000,
-       .dvfs      = &tegra_dvfs_virtual_cpu_dvfs,
+       .u.cpu = {
+               .main      = &tegra_pll_x,
+               .backup    = &tegra_pll_p,
+       },
 };
 
 static struct clk tegra_clk_cop = {
@@ -1764,19 +1853,31 @@ static struct clk_mux_sel mux_clk_32k[] = {
                        .con_id    = _con,              \
                },                                      \
                .ops       = &tegra_periph_clk_ops,     \
-               .clk_num   = _clk_num,                  \
                .reg       = _reg,                      \
                .inputs    = _inputs,                   \
                .flags     = _flags,                    \
                .max_rate  = _max,                      \
+               .u.periph = {                           \
+                       .clk_num   = _clk_num,          \
+               },                                      \
+       }
+
+#define SHARED_CLK(_name, _dev, _con, _parent)         \
+       {                                               \
+               .name      = _name,                     \
+               .lookup    = {                          \
+                       .dev_id    = _dev,              \
+                       .con_id    = _con,              \
+               },                                      \
+               .ops       = &tegra_clk_shared_bus_ops, \
+               .parent = _parent,                      \
        }
 
-struct clk tegra_periph_clks[] = {
+struct clk tegra_list_clks[] = {
        PERIPH_CLK("rtc",       "rtc-tegra",            NULL,   4,      0,      32768,     mux_clk_32k,                 PERIPH_NO_RESET),
        PERIPH_CLK("timer",     "timer",                NULL,   5,      0,      26000000,  mux_clk_m,                   0),
        PERIPH_CLK("i2s1",      "i2s.0",                NULL,   11,     0x100,  26000000,  mux_pllaout0_audio2x_pllp_clkm,      MUX | DIV_U71),
        PERIPH_CLK("i2s2",      "i2s.1",                NULL,   18,     0x104,  26000000,  mux_pllaout0_audio2x_pllp_clkm,      MUX | DIV_U71),
-       /* FIXME: spdif has 2 clocks but 1 enable */
        PERIPH_CLK("spdif_out", "spdif_out",            NULL,   10,     0x108,  100000000, mux_pllaout0_audio2x_pllp_clkm,      MUX | DIV_U71),
        PERIPH_CLK("spdif_in",  "spdif_in",             NULL,   10,     0x10c,  100000000, mux_pllp_pllc_pllm,          MUX | DIV_U71),
        PERIPH_CLK("pwm",       "pwm",                  NULL,   17,     0x110,  432000000, mux_pllp_pllc_audio_clkm_clk32,      MUX | DIV_U71),
@@ -1789,12 +1890,13 @@ struct clk tegra_periph_clks[] = {
        PERIPH_CLK("sbc4",      "spi_tegra.3",          NULL,   68,     0x1b4,  160000000, mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71),
        PERIPH_CLK("ide",       "ide",                  NULL,   25,     0x144,  100000000, mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71), /* requires min voltage */
        PERIPH_CLK("ndflash",   "tegra_nand",           NULL,   13,     0x160,  164000000, mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71), /* scales with voltage */
-       /* FIXME: vfir shares an enable with uartb */
        PERIPH_CLK("vfir",      "vfir",                 NULL,   7,      0x168,  72000000,  mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71),
        PERIPH_CLK("sdmmc1",    "sdhci-tegra.0",        NULL,   14,     0x150,  52000000,  mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71), /* scales with voltage */
        PERIPH_CLK("sdmmc2",    "sdhci-tegra.1",        NULL,   9,      0x154,  52000000,  mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71), /* scales with voltage */
        PERIPH_CLK("sdmmc3",    "sdhci-tegra.2",        NULL,   69,     0x1bc,  52000000,  mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71), /* scales with voltage */
        PERIPH_CLK("sdmmc4",    "sdhci-tegra.3",        NULL,   15,     0x164,  52000000,  mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71), /* scales with voltage */
+       PERIPH_CLK("vcp",       "vcp",                  NULL,   29,     0,      250000000, mux_clk_m,                   0),
+       PERIPH_CLK("bsea",      "bsea",                 NULL,   62,     0,      250000000, mux_clk_m,                   0),
        PERIPH_CLK("vde",       "vde",                  NULL,   61,     0x1c8,  250000000, mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71), /* scales with voltage and process_id */
        PERIPH_CLK("csite",     "csite",                NULL,   73,     0x1d4,  144000000, mux_pllp_pllc_pllm_clkm,     MUX | DIV_U71), /* max rate ??? */
        /* FIXME: what is la? */
@@ -1817,13 +1919,11 @@ struct clk tegra_periph_clks[] = {
        PERIPH_CLK("uarte",     "uart.4",               NULL,   66,     0x1c4,  600000000, mux_pllp_pllc_pllm_clkm,     MUX),
        PERIPH_CLK("3d",        "3d",                   NULL,   24,     0x158,  300000000, mux_pllm_pllc_pllp_plla,     MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
        PERIPH_CLK("2d",        "2d",                   NULL,   21,     0x15c,  300000000, mux_pllm_pllc_pllp_plla,     MUX | DIV_U71), /* scales with voltage and process_id */
-       /* FIXME: vi and vi_sensor share an enable */
        PERIPH_CLK("vi",        "tegra_camera",         "vi",   20,     0x148,  150000000, mux_pllm_pllc_pllp_plla,     MUX | DIV_U71), /* scales with voltage and process_id */
        PERIPH_CLK("vi_sensor", "tegra_camera",         "vi_sensor",    20,     0x1a8,  150000000, mux_pllm_pllc_pllp_plla,     MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
        PERIPH_CLK("epp",       "epp",                  NULL,   19,     0x16c,  300000000, mux_pllm_pllc_pllp_plla,     MUX | DIV_U71), /* scales with voltage and process_id */
        PERIPH_CLK("mpe",       "mpe",                  NULL,   60,     0x170,  250000000, mux_pllm_pllc_pllp_plla,     MUX | DIV_U71), /* scales with voltage and process_id */
        PERIPH_CLK("host1x",    "host1x",               NULL,   28,     0x180,  166000000, mux_pllm_pllc_pllp_plla,     MUX | DIV_U71), /* scales with voltage and process_id */
-       /* FIXME: cve and tvo share an enable   */
        PERIPH_CLK("cve",       "cve",                  NULL,   49,     0x140,  250000000, mux_pllp_plld_pllc_clkm,     MUX | DIV_U71), /* requires min voltage */
        PERIPH_CLK("tvo",       "tvo",                  NULL,   49,     0x188,  250000000, mux_pllp_plld_pllc_clkm,     MUX | DIV_U71), /* requires min voltage */
        PERIPH_CLK("hdmi",      "hdmi",                 NULL,   51,     0x18c,  600000000, mux_pllp_plld_pllc_clkm,     MUX | DIV_U71), /* requires min voltage */
@@ -1838,6 +1938,8 @@ struct clk tegra_periph_clks[] = {
        PERIPH_CLK("csi",       "tegra_camera",         "csi",  52,     0,      72000000,  mux_pllp_out3,               0),
        PERIPH_CLK("isp",       "tegra_camera",         "isp",  23,     0,      150000000, mux_clk_m,                   0), /* same frequency as VI */
        PERIPH_CLK("csus",      "tegra_camera",         "csus", 92,     0,      150000000, mux_clk_m,                   PERIPH_NO_RESET),
+
+       SHARED_CLK("avp.sclk",  "tegra-avp",            "sclk", &tegra_clk_sclk),
 };
 
 #define CLK_DUPLICATE(_name, _dev, _con)               \
@@ -1868,6 +1970,12 @@ struct clk_duplicate tegra_clk_duplicates[] = {
        CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
        CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
        CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
+       CLK_DUPLICATE("host1x", "tegra_grhost", "host1x"),
+       CLK_DUPLICATE("2d", "tegra_grhost", "gr2d"),
+       CLK_DUPLICATE("3d", "tegra_grhost", "gr3d"),
+       CLK_DUPLICATE("epp", "tegra_grhost", "epp"),
+       CLK_DUPLICATE("mpe", "tegra_grhost", "mpe"),
+       CLK_DUPLICATE("cop", "tegra-avp", "cop"),
 };
 
 #define CLK(dev, con, ck)      \
@@ -1877,71 +1985,67 @@ struct clk_duplicate tegra_clk_duplicates[] = {
                .clk = ck,      \
        }
 
-struct clk_lookup tegra_clk_lookups[] = {
-       /* external root sources */
-       CLK(NULL,       "32k_clk",      &tegra_clk_32k),
-       CLK(NULL,       "pll_s",        &tegra_pll_s),
-       CLK(NULL,       "clk_m",        &tegra_clk_m),
-       CLK(NULL,       "pll_m",        &tegra_pll_m),
-       CLK(NULL,       "pll_m_out1",   &tegra_pll_m_out1),
-       CLK(NULL,       "pll_c",        &tegra_pll_c),
-       CLK(NULL,       "pll_c_out1",   &tegra_pll_c_out1),
-       CLK(NULL,       "pll_p",        &tegra_pll_p),
-       CLK(NULL,       "pll_p_out1",   &tegra_pll_p_out1),
-       CLK(NULL,       "pll_p_out2",   &tegra_pll_p_out2),
-       CLK(NULL,       "pll_p_out3",   &tegra_pll_p_out3),
-       CLK(NULL,       "pll_p_out4",   &tegra_pll_p_out4),
-       CLK(NULL,       "pll_a",        &tegra_pll_a),
-       CLK(NULL,       "pll_a_out0",   &tegra_pll_a_out0),
-       CLK(NULL,       "pll_d",        &tegra_pll_d),
-       CLK(NULL,       "pll_d_out0",   &tegra_pll_d_out0),
-       CLK(NULL,       "pll_u",        &tegra_pll_u),
-       CLK(NULL,       "pll_x",        &tegra_pll_x),
-       CLK(NULL,       "cclk",         &tegra_clk_cclk),
-       CLK(NULL,       "sclk",         &tegra_clk_sclk),
-       CLK(NULL,       "hclk",         &tegra_clk_hclk),
-       CLK(NULL,       "pclk",         &tegra_clk_pclk),
-       CLK(NULL,       "clk_d",        &tegra_clk_d),
-       CLK(NULL,       "clk_dev1",     &tegra_dev1_clk),
-       CLK(NULL,       "clk_dev2",     &tegra_dev2_clk),
-       CLK(NULL,       "cpu",          &tegra_clk_virtual_cpu),
-       CLK(NULL,       "blink",        &tegra_clk_blink),
-       CLK("tegra-avp", "cop",         &tegra_clk_cop),
-};
+struct clk *tegra_ptr_clks[] = {
+       &tegra_clk_32k,
+       &tegra_pll_s,
+       &tegra_clk_m,
+       &tegra_pll_m,
+       &tegra_pll_m_out1,
+       &tegra_pll_c,
+       &tegra_pll_c_out1,
+       &tegra_pll_p,
+       &tegra_pll_p_out1,
+       &tegra_pll_p_out2,
+       &tegra_pll_p_out3,
+       &tegra_pll_p_out4,
+       &tegra_pll_a,
+       &tegra_pll_a_out0,
+       &tegra_pll_d,
+       &tegra_pll_d_out0,
+       &tegra_pll_u,
+       &tegra_pll_x,
+       &tegra_clk_cclk,
+       &tegra_clk_sclk,
+       &tegra_clk_hclk,
+       &tegra_clk_pclk,
+       &tegra_clk_d,
+       &tegra_dev1_clk,
+       &tegra_dev2_clk,
+       &tegra_clk_virtual_cpu,
+       &tegra_clk_blink,
+       &tegra_clk_cop,
+};
+
+static void tegra2_init_one_clock(struct clk *c)
+{
+       clk_init(c);
+       if (!c->lookup.dev_id && !c->lookup.con_id)
+               c->lookup.con_id = c->name;
+       c->lookup.clk = c;
+       clkdev_add(&c->lookup);
+}
 
 void __init tegra2_init_clocks(void)
 {
        int i;
-       struct clk_lookup *cl;
        struct clk *c;
-       struct clk_duplicate *cd;
 
-       for (i = 0; i < ARRAY_SIZE(tegra_clk_lookups); i++) {
-               cl = &tegra_clk_lookups[i];
-               clk_init(cl->clk);
-               clkdev_add(cl);
-       }
+       for (i = 0; i < ARRAY_SIZE(tegra_ptr_clks); i++)
+               tegra2_init_one_clock(tegra_ptr_clks[i]);
 
-       for (i = 0; i < ARRAY_SIZE(tegra_periph_clks); i++) {
-               c = &tegra_periph_clks[i];
-               cl = &c->lookup;
-               cl->clk = c;
-
-               clk_init(cl->clk);
-               clkdev_add(cl);
-       }
+       for (i = 0; i < ARRAY_SIZE(tegra_list_clks); i++)
+               tegra2_init_one_clock(&tegra_list_clks[i]);
 
        for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
-               cd = &tegra_clk_duplicates[i];
-               c = tegra_get_clock_by_name(cd->name);
-               if (c) {
-                       cl = &cd->lookup;
-                       cl->clk = c;
-                       clkdev_add(cl);
-               } else {
+               c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
+               if (!c) {
                        pr_err("%s: Unknown duplicate clock %s\n", __func__,
-                               cd->name);
+                               tegra_clk_duplicates[i].name);
+                       continue;
                }
+
+               tegra_clk_duplicates[i].lookup.clk = c;
+               clkdev_add(&tegra_clk_duplicates[i].lookup);
        }
 
        init_audio_sync_clock_mux();
@@ -1954,7 +2058,6 @@ static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
 void tegra_clk_suspend(void)
 {
        unsigned long off, i;
-       u32 pllx_misc;
        u32 *ctx = clk_rst_suspend;
 
        *ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK;
@@ -1995,10 +2098,6 @@ void tegra_clk_suspend(void)
 
        *ctx++ = clk_readl(MISC_CLK_ENB);
        *ctx++ = clk_readl(CLK_MASK_ARM);
-
-       pllx_misc = clk_readl(tegra_pll_x.reg + PLL_MISC(&tegra_pll_x));
-       pllx_misc &= ~PLL_MISC_LOCK_ENABLE(&tegra_pll_x);
-       clk_writel(pllx_misc, tegra_pll_x.reg + PLL_MISC(&tegra_pll_x));
 }
 
 void tegra_clk_resume(void)
index 5529c238dd77d783f315cf2c893ef188c574b0a2..854bb77107df9c613fc2d4639c1c9c0b63182df1 100644 (file)
  */
 
 #include <linux/kernel.h>
+#include <linux/string.h>
 
 #include "clock.h"
-#include "tegra2_dvfs.h"
-
-static struct dvfs_table virtual_cpu_process_0[] = {
-       {314000000,  750},
-       {456000000,  825},
-       {608000000,  900},
-       {760000000,  975},
-       {817000000,  1000},
-       {912000000,  1050},
-       {1000000000, 1100},
-       {0, 0},
-};
+#include "dvfs.h"
+#include "fuse.h"
 
-static struct dvfs_table virtual_cpu_process_1[] = {
-       {314000000,  750},
-       {456000000,  825},
-       {618000000,  900},
-       {770000000,  975},
-       {827000000,  1000},
-       {922000000,  1050},
-       {1000000000, 1100},
-       {0, 0},
-};
+#define CORE_REGULATOR "vdd_core"
+#define CPU_REGULATOR "vdd_cpu"
 
-static struct dvfs_table virtual_cpu_process_2[] = {
-       {494000000,  750},
-       {675000000,  825},
-       {817000000,  875},
-       {922000000,  925},
-       {1000000000, 975},
-       {0, 0},
-};
+static const int core_millivolts[MAX_DVFS_FREQS] =
+       {950, 1000, 1100, 1200, 1275};
+static const int cpu_millivolts[MAX_DVFS_FREQS] =
+       {750, 775, 800, 825, 875,  900,  925,  975,  1000, 1050, 1100};
+static int cpu_core_millivolts[MAX_DVFS_FREQS];
 
-static struct dvfs_table virtual_cpu_process_3[] = {
-       {730000000,  750},
-       {760000000,  775},
-       {845000000,  800},
-       {1000000000, 875},
-       {0, 0},
-};
+#define CORE_MAX_MILLIVOLTS 1275
+#define CPU_MAX_MILLIVOLTS 1100
+
+#define KHZ 1000
+#define MHZ 1000000
+
+#define CPU_DVFS(_clk_name, _process_id, _mult, _freqs...)             \
+       {                                               \
+               .clk_name       = _clk_name,            \
+               .reg_id         = CORE_REGULATOR,       \
+               .cpu            = false,                \
+               .process_id     = _process_id,          \
+               .freqs          = {_freqs},             \
+               .freqs_mult     = _mult,                \
+               .auto_dvfs      = true,                 \
+               .higher         = true,                 \
+               .max_millivolts = CORE_MAX_MILLIVOLTS   \
+       },                                              \
+       {                                               \
+               .clk_name       = _clk_name,            \
+               .reg_id         = CPU_REGULATOR,        \
+               .cpu            = true,                 \
+               .process_id     = _process_id,          \
+               .freqs          = {_freqs},             \
+               .freqs_mult     = _mult,                \
+               .auto_dvfs      = true,                 \
+               .max_millivolts = CPU_MAX_MILLIVOLTS    \
+       }
+
+#define CORE_DVFS(_clk_name, _auto, _mult, _freqs...)  \
+       {                                               \
+               .clk_name       = _clk_name,            \
+               .reg_id         = CORE_REGULATOR,       \
+               .process_id     = -1,                   \
+               .freqs          = {_freqs},             \
+               .freqs_mult     = _mult,                \
+               .auto_dvfs      = _auto,                \
+               .max_millivolts = CORE_MAX_MILLIVOLTS   \
+       }
+
+static struct dvfs dvfs_init[] = {
+       /* Cpu voltages (mV):   750, 775, 800, 825, 875, 900, 925, 975, 1000, 1050, 1100 */
+       CPU_DVFS("cpu", 0, MHZ, 314, 314, 314, 456, 456, 608, 608, 760, 817,  912,  1000),
+       CPU_DVFS("cpu", 1, MHZ, 314, 314, 314, 456, 456, 618, 618, 770, 827,  922,  1000),
+       CPU_DVFS("cpu", 2, MHZ, 494, 675, 675, 675, 817, 817, 922, 1000),
+       CPU_DVFS("cpu", 3, MHZ, 730, 760, 845, 845, 1000),
+
+       /* Core voltages (mV):       950,    1000,   1100,   1200,   1275 */
+       CORE_DVFS("sdmmc1",  1, KHZ, 44000,  52000,  52000,  52000,  52000),
+       CORE_DVFS("sdmmc2",  1, KHZ, 44000,  52000,  52000,  52000,  52000),
+       CORE_DVFS("sdmmc3",  1, KHZ, 44000,  52000,  52000,  52000,  52000),
+       CORE_DVFS("sdmmc4",  1, KHZ, 44000,  52000,  52000,  52000,  52000),
+       CORE_DVFS("ndflash", 1, KHZ, 130000, 150000, 158000, 164000, 164000),
+       CORE_DVFS("nor",     1, KHZ, 0,      92000,  92000,  92000,  92000),
+       CORE_DVFS("ide",     1, KHZ, 0,      0,      100000, 100000, 100000),
+       CORE_DVFS("mipi",    1, KHZ, 0,      40000,  40000,  40000, 60000),
+       CORE_DVFS("usbd",    1, KHZ, 0,      0,      480000, 480000, 480000),
+       CORE_DVFS("usb2",    1, KHZ, 0,      0,      480000, 480000, 480000),
+       CORE_DVFS("usb3",    1, KHZ, 0,      0,      480000, 480000, 480000),
+       CORE_DVFS("pcie",    1, KHZ, 0,      0,      0,      250000, 250000),
+       CORE_DVFS("dsi",     1, KHZ, 100000, 100000, 100000, 500000, 500000),
+       CORE_DVFS("tvo",     1, KHZ, 0,      0,      0,      250000, 250000),
+       CORE_DVFS("hdmi",    1, KHZ, 0,      0,      0,      148500, 148500),
+
+       /*
+        * The clock rate for the display controllers that determines the
+        * necessary core voltage depends on a divider that is internal
+        * to the display block.  Disable auto-dvfs on the display clocks,
+        * and let the display driver call tegra_dvfs_set_rate manually
+        */
+       CORE_DVFS("disp1",   0, KHZ, 158000, 158000, 190000, 190000, 190000),
+       CORE_DVFS("disp2",   0, KHZ, 158000, 158000, 190000, 190000, 190000),
 
-struct dvfs tegra_dvfs_virtual_cpu_dvfs = {
-       .reg_id = "vdd_cpu",
-       .process_id_table = {
-               {
-                       .process_id = 0,
-                       .table = virtual_cpu_process_0,
-               },
-               {
-                       .process_id = 1,
-                       .table = virtual_cpu_process_1,
-               },
-               {
-                       .process_id = 2,
-                       .table = virtual_cpu_process_2,
-               },
-               {
-                       .process_id = 3,
-                       .table = virtual_cpu_process_3,
-               },
-       },
-       .process_id_table_length = 4,
-       .cpu = 1,
+       /*
+        * These clocks technically depend on the core process id,
+        * but just use the worst case value for now
+        */
+       CORE_DVFS("host1x",  1, KHZ, 104500, 133000, 166000, 166000, 166000),
+       CORE_DVFS("epp",     1, KHZ, 133000, 171000, 247000, 300000, 300000),
+       CORE_DVFS("2d",      1, KHZ, 133000, 171000, 247000, 300000, 300000),
+       CORE_DVFS("3d",      1, KHZ, 114000, 161500, 247000, 300000, 300000),
+       CORE_DVFS("mpe",     1, KHZ, 104500, 152000, 228000, 250000, 250000),
+       CORE_DVFS("vi",      1, KHZ, 85000,  100000, 150000, 150000, 150000),
+       CORE_DVFS("sclk",    1, KHZ, 95000,  133000, 190000, 250000, 250000),
+       CORE_DVFS("vde",     1, KHZ, 95000,  123500, 209000, 250000, 250000),
+       /* What is this? */
+       CORE_DVFS("NVRM_DEVID_CLK_SRC", 1, MHZ, 480, 600, 800, 1067, 1067),
 };
+
+void tegra2_init_dvfs(void)
+{
+       int i;
+       struct clk *c;
+       struct dvfs *d;
+       int process_id;
+       int ret;
+
+       int cpu_process_id = tegra_cpu_process_id();
+       int core_process_id = tegra_core_process_id();
+
+       /*
+        * VDD_CORE must always be at least 50 mV higher than VDD_CPU
+        * Fill out cpu_core_millivolts based on cpu_millivolts
+        */
+       for (i = 0; i < ARRAY_SIZE(cpu_millivolts); i++)
+               if (cpu_millivolts[i])
+                       cpu_core_millivolts[i] = cpu_millivolts[i] + 50;
+
+       for (i = 0; i < ARRAY_SIZE(dvfs_init); i++) {
+               d = &dvfs_init[i];
+
+               process_id = d->cpu ? cpu_process_id : core_process_id;
+               if (d->process_id != -1 && d->process_id != process_id) {
+                       pr_debug("tegra_dvfs: rejected %s %d, process_id %d\n",
+                               d->clk_name, d->process_id, process_id);
+                       continue;
+               }
+
+               c = tegra_get_clock_by_name(d->clk_name);
+
+               if (!c) {
+                       pr_debug("tegra_dvfs: no clock found for %s\n",
+                               d->clk_name);
+                       continue;
+               }
+
+               if (d->cpu)
+                       memcpy(d->millivolts, cpu_millivolts,
+                               sizeof(cpu_millivolts));
+               else if (!strcmp(d->clk_name, "cpu"))
+                       memcpy(d->millivolts, cpu_core_millivolts,
+                               sizeof(cpu_core_millivolts));
+               else
+                       memcpy(d->millivolts, core_millivolts,
+                               sizeof(core_millivolts));
+
+               ret = tegra_enable_dvfs_on_clk(c, d);
+               if (ret)
+                       pr_err("tegra_dvfs: failed to enable dvfs on %s\n",
+                               c->name);
+       }
+}
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.h b/arch/arm/mach-tegra/tegra2_dvfs.h
deleted file mode 100644 (file)
index f8c1adb..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * arch/arm/mach-tegra/tegra2_dvfs.h
- *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- *     Colin Cross <ccross@google.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-extern struct dvfs tegra_dvfs_virtual_cpu_dvfs;
index cc9ec7020533efb7ad8a4c29da248e99acf4fc16..dff49f201c7a15f4ad285265f87a35a4f336134b 100644 (file)
@@ -37,6 +37,7 @@
 
 #include "board.h"
 #include "clock.h"
+#include "power.h"
 
 #define RTC_SECONDS            0x08
 #define RTC_SHADOW_SECONDS     0x0c
@@ -104,19 +105,15 @@ static cycle_t tegra_clocksource_us_read(struct clocksource *cs)
 
 void tegra_clocksource_us_suspend(struct clocksource *cs)
 {
-       tegra_us_resume_offset = tegra_clocksource_us_read(cs);
+       tegra_us_resume_offset = tegra_clocksource_us_read(cs) -
+               tegra_rtc_read_ms() * 1000;
 }
 
 void tegra_clocksource_us_resume(struct clocksource *cs)
 {
-       tegra_us_clocksource_offset = tegra_us_resume_offset;
-}
-
-static cycle_t tegra_clocksource_32k_read(struct clocksource *cs)
-{
-       u32 ms = readl(rtc_base + RTC_MILLISECONDS);
-       u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
-       return (u64)s * 1000 + ms;
+       tegra_us_clocksource_offset += tegra_us_resume_offset +
+               tegra_rtc_read_ms() * 1000 -
+               tegra_clocksource_us_read(cs);
 }
 
 static struct clock_event_device tegra_clockevent = {
@@ -137,43 +134,47 @@ static struct clocksource tegra_clocksource_us = {
        .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
-static struct clocksource tegra_clocksource_32k = {
-       .name   = "rtc_32k",
-       .rating = 100,
-       .read   = tegra_clocksource_32k_read,
-       .mask   = 0x7FFFFFFFFFFFFFFFULL,
-       .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
-};
-
 unsigned long long sched_clock(void)
 {
        return tegra_clocksource_us.read(&tegra_clocksource_us) * 1000;
 }
 
-/**
+
+/*
+ * tegra_rtc_read - Reads the Tegra RTC registers
+ * Care must be taken that this funciton is not called while the
+ * tegra_rtc driver could be executing to avoid race conditions
+ * on the RTC shadow register
+ */
+u64 tegra_rtc_read_ms(void)
+{
+       u32 ms = readl(rtc_base + RTC_MILLISECONDS);
+       u32 s = readl(rtc_base + RTC_SHADOW_SECONDS);
+       return (u64)s * 1000 + ms;
+}
+
+/*
  * read_persistent_clock -  Return time from a persistent clock.
  *
  * Reads the time from a source which isn't disabled during PM, the
  * 32k sync timer.  Convert the cycles elapsed since last read into
  * nsecs and adds to a monotonically increasing timespec.
+ * Care must be taken that this funciton is not called while the
+ * tegra_rtc driver could be executing to avoid race conditions
+ * on the RTC shadow register
  */
 static struct timespec persistent_ts;
-static cycles_t cycles, last_cycles;
+static u64 persistent_ms, last_persistent_ms;
 void read_persistent_clock(struct timespec *ts)
 {
-       unsigned long long nsecs;
-       cycles_t delta;
+       u64 delta;
        struct timespec *tsp = &persistent_ts;
 
-       last_cycles = cycles;
-       cycles = tegra_clocksource_32k.read(&tegra_clocksource_32k);
-       delta = cycles - last_cycles;
-
-       nsecs = clocksource_cyc2ns(delta,
-                                  tegra_clocksource_32k.mult,
-                                  tegra_clocksource_32k.shift);
+       last_persistent_ms = persistent_ms;
+       persistent_ms = tegra_rtc_read_ms();
+       delta = persistent_ms - last_persistent_ms;
 
-       timespec_add_ns(tsp, nsecs);
+       timespec_add_ns(tsp, delta * 1000000);
        *ts = *tsp;
 }
 
@@ -238,11 +239,6 @@ static void __init tegra_init_timer(void)
                BUG();
        }
 
-       if (clocksource_register_hz(&tegra_clocksource_32k, 1000)) {
-               printk(KERN_ERR "Failed to register 32k clocksource\n");
-               BUG();
-       }
-
        ret = setup_irq(tegra_timer_irq.irq, &tegra_timer_irq);
        if (ret) {
                printk(KERN_ERR "Failed to register timer IRQ: %d\n", ret);
index ac8497fe9061075b33405e9dc9f3201f234197a2..cb7ef27f564b9f74670f755ad5b4997a332e37f0 100644 (file)
@@ -606,11 +606,27 @@ void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
        }
 }
 
+static unsigned long tegra_dc_pclk_round_rate(struct tegra_dc *dc, int pclk)
+{
+       unsigned long rate;
+       unsigned long div;
+
+       rate = clk_get_rate(dc->clk);
+
+       div = DIV_ROUND_CLOSEST(rate * 2, pclk);
+
+       if (div < 2)
+               return 0;
+
+       return rate * 2 / div;
+}
+
 static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
 {
        unsigned long val;
        unsigned long rate;
        unsigned long div;
+       unsigned long pclk;
 
        tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
        tegra_dc_writel(dc, mode->h_ref_to_sync | (mode->v_ref_to_sync << 16),
@@ -645,18 +661,19 @@ static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode
 
        rate = clk_get_rate(dc->clk);
 
-       div = ((rate * 2 + mode->pclk / 2) / mode->pclk) - 2;
-
-       if (rate * 2 / (div + 2) < (mode->pclk / 100 * 99) ||
-           rate * 2 / (div + 2) > (mode->pclk / 100 * 109)) {
+       pclk = tegra_dc_pclk_round_rate(dc, mode->pclk);
+       if (pclk < (mode->pclk / 100 * 99) ||
+           pclk > (mode->pclk / 100 * 109)) {
                dev_err(&dc->ndev->dev,
                        "can't divide %ld clock to %d -1/+9%% %ld %d %d\n",
                        rate, mode->pclk,
-                       rate / div, (mode->pclk / 100 * 99),
+                       pclk, (mode->pclk / 100 * 99),
                        (mode->pclk / 100 * 109));
                return -EINVAL;
        }
 
+       div = (rate * 2 / pclk) - 2;
+
        tegra_dc_writel(dc, 0x00010001,
                        DC_DISP_SHIFT_CLOCK_OPTIONS);
        tegra_dc_writel(dc, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(div),
@@ -820,6 +837,8 @@ static void tegra_dc_init(struct tegra_dc *dc)
 
 static bool _tegra_dc_enable(struct tegra_dc *dc)
 {
+       int pclk;
+
        if (dc->mode.pclk == 0)
                return false;
 
@@ -830,6 +849,9 @@ static bool _tegra_dc_enable(struct tegra_dc *dc)
 
        tegra_dc_setup_clk(dc, dc->clk);
 
+       pclk = tegra_dc_pclk_round_rate(dc, dc->mode.pclk);
+       tegra_dvfs_set_rate(dc->clk, pclk);
+
        clk_enable(dc->clk);
        enable_irq(dc->irq);
 
@@ -861,6 +883,7 @@ static void _tegra_dc_disable(struct tegra_dc *dc)
 
        disable_irq(dc->irq);
        clk_disable(dc->clk);
+       tegra_dvfs_set_rate(dc->clk, 0);
 
        if (dc->out && dc->out->disable)
                dc->out->disable();