ARM: tegra12: set CPU rate to 2.2GHz for sku 0x87
[linux-3.10.git] / arch / arm / mach-tegra / powergate.c
index 1e79489..2778e8c 100644 (file)
@@ -2,7 +2,7 @@
  * arch/arm/mach-tegra/powergate.c
  *
  * Copyright (c) 2010 Google, Inc
- * Copyright (c) 2011 - 2013, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2011 - 2014, NVIDIA CORPORATION.  All rights reserved.
  *
  * Author:
  *     Colin Cross <ccross@google.com>
@@ -21,6 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/clk.h>
+#include <linux/clk/tegra.h>
 #include <linux/string.h>
 #include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/seq_file.h>
 #include <linux/spinlock.h>
 #include <linux/clk/tegra.h>
+#include <linux/tegra-powergate.h>
+#include <linux/tegra-soc.h>
 #include <trace/events/power.h>
 #include <asm/atomic.h>
 
-#include <mach/powergate.h>
-#include <mach/hardware.h>
-
 #include "clock.h"
-#include "fuse.h"
 #include "iomap.h"
 #include "powergate-priv.h"
+#include "common.h"
 
 static struct powergate_ops *pg_ops;
 
-#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
 static spinlock_t *tegra_get_powergate_lock(void)
 {
        if (pg_ops && pg_ops->get_powergate_lock)
@@ -53,14 +52,13 @@ static spinlock_t *tegra_get_powergate_lock(void)
 
        return NULL;
 }
-#endif
 
 int tegra_powergate_set(int id, bool new_state)
 {
-#ifndef CONFIG_TEGRA_SIMULATION_PLATFORM
        bool status;
        unsigned long flags;
-       spinlock_t *lock = tegra_get_powergate_lock();
+       spinlock_t *lock;
+       u32 reg;
 
        /* 10us timeout for toggle operation if it takes affect*/
        int toggle_timeout = 10;
@@ -69,6 +67,11 @@ int tegra_powergate_set(int id, bool new_state)
           of contention with h/w initiated CPU power gating */
        int contention_timeout = 100;
 
+       if (tegra_cpu_is_asim())
+               return 0;
+
+       lock = tegra_get_powergate_lock();
+
        spin_lock_irqsave(lock, flags);
 
        status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
@@ -86,7 +89,38 @@ int tegra_powergate_set(int id, bool new_state)
                return 0;
        }
 
+#if !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       /* Wait if PMC is already processing some other power gating request */
+       do {
+               udelay(1);
+               reg = pmc_read(PWRGATE_TOGGLE);
+               contention_timeout--;
+       } while ((contention_timeout > 0) && (reg & PWRGATE_TOGGLE_START));
+
+       if (contention_timeout <= 0)
+               pr_err(" Timed out waiting for PMC to submit \
+                               new power gate request \n");
+       contention_timeout = 100;
+#endif
+
+       /* Submit power gate request */
        pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+
+#if !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       /* Wait while PMC accepts the request */
+       do {
+               udelay(1);
+               reg = pmc_read(PWRGATE_TOGGLE);
+               contention_timeout--;
+       } while ((contention_timeout > 0) && (reg & PWRGATE_TOGGLE_START));
+
+       if (contention_timeout <= 0)
+               pr_err(" Timed out waiting for PMC to accept \
+                               new power gate request \n");
+       contention_timeout = 100;
+#endif
+
+       /* Check power gate status */
        do {
                do {
                        udelay(1);
@@ -107,7 +141,6 @@ int tegra_powergate_set(int id, bool new_state)
 
        trace_power_domain_target(tegra_powergate_get_name(id), new_state,
                        raw_smp_processor_id());
-#endif
 
        return 0;
 }
@@ -329,13 +362,19 @@ int tegra_powergate_remove_clamping(int id)
                udelay(1);
                contention_timeout--;
        } while ((contention_timeout > 0)
-                       && (pmc_read(REMOVE_CLAMPING) & mask));
+                       && (pmc_read(PWRGATE_CLAMP_STATUS) & BIT(id)));
 
        WARN(contention_timeout <= 0, "Couldn't remove clamping");
 
        return 0;
 }
 
+static inline bool tegra_powergate_check_skip_list(int id)
+{
+       return pg_ops->powergate_skip ?
+               pg_ops->powergate_skip(id) : false;
+}
+
 /* EXTERNALY VISIBLE APIS */
 
 bool tegra_powergate_is_powered(int id)
@@ -343,13 +382,18 @@ bool tegra_powergate_is_powered(int id)
        u32 status;
 
        if (!pg_ops) {
-               pr_info("This SOC doesn't support powergating\n");
+               pr_debug("This SOC doesn't support powergating\n");
                return -EINVAL;
        }
 
        if (id < 0 || id >= pg_ops->num_powerdomains)
                return -EINVAL;
 
+       if (pg_ops->powergate_is_powered)
+               return pg_ops->powergate_is_powered(id);
+       else
+               status = pmc_read(PWRGATE_STATUS) & (1 << id);
+
        status = pmc_read(PWRGATE_STATUS) & (1 << id);
 
        return !!status;
@@ -389,6 +433,10 @@ int tegra_powergate_partition(int id)
                return -EINVAL;
        }
 
+       if (tegra_powergate_check_skip_list(id))
+               printk_once("%s: %s is in powergate skip list\n", __func__,
+                       tegra_powergate_get_name(id));
+
        if (pg_ops->powergate_partition)
                return pg_ops->powergate_partition(id);
        else
@@ -410,6 +458,10 @@ int tegra_unpowergate_partition(int id)
                return -EINVAL;
        }
 
+       if (tegra_powergate_check_skip_list(id))
+               printk_once("%s: %s is in powergate skip list\n", __func__,
+                       tegra_powergate_get_name(id));
+
        if (pg_ops->unpowergate_partition)
                return pg_ops->unpowergate_partition(id);
        else
@@ -431,6 +483,10 @@ int tegra_powergate_partition_with_clk_off(int id)
                return -EINVAL;
        }
 
+       if (tegra_powergate_check_skip_list(id))
+               printk_once("%s: %s is in powergate skip list\n", __func__,
+                       tegra_powergate_get_name(id));
+
        if (pg_ops->powergate_partition_with_clk_off)
                return pg_ops->powergate_partition_with_clk_off(id);
        else
@@ -452,6 +508,10 @@ int tegra_unpowergate_partition_with_clk_on(int id)
                return -EINVAL;
        }
 
+       if (tegra_powergate_check_skip_list(id))
+               printk_once("%s: %s is in powergate skip list\n", __func__,
+                       tegra_powergate_get_name(id));
+
        if (pg_ops->unpowergate_partition_with_clk_on)
                return pg_ops->unpowergate_partition_with_clk_on(id);
        else
@@ -576,7 +636,7 @@ int tegra_powergate_init_refcount(void)
 
 int __init tegra_powergate_init(void)
 {
-       switch (tegra_chip_id) {
+       switch (tegra_get_chip_id()) {
                case TEGRA_CHIPID_TEGRA2:
                        pg_ops = tegra2_powergate_init_chip_support();
                        break;
@@ -593,6 +653,10 @@ int __init tegra_powergate_init(void)
                        pg_ops = tegra14x_powergate_init_chip_support();
                        break;
 
+               case TEGRA_CHIPID_TEGRA12:
+                       pg_ops = tegra12x_powergate_init_chip_support();
+                       break;
+
                default:
                        pg_ops = NULL;
                        pr_info("%s: Unknown Tegra variant. Disabling powergate\n", __func__);
@@ -612,6 +676,7 @@ static int powergate_show(struct seq_file *s, void *data)
 {
        int i;
        const char *name;
+       bool is_pg_skip;
 
        if (!pg_ops) {
                seq_printf(s, "This SOC doesn't support powergating\n");
@@ -623,9 +688,13 @@ static int powergate_show(struct seq_file *s, void *data)
 
        for (i = 0; i < pg_ops->num_powerdomains; i++) {
                name = tegra_powergate_get_name(i);
-               if (name)
+               if (name) {
+                       is_pg_skip = tegra_powergate_check_skip_list(i);
                        seq_printf(s, " %9s %7s\n", name,
-                               tegra_powergate_is_powered(i) ? "yes" : "no");
+                               (is_pg_skip ? "skip" : \
+                               (tegra_powergate_is_powered(i) ? \
+                               "yes" : "no")));
+               }
        }
 
        return 0;