ARM: tegra: power: Wait for power-gate toggle completion
Alex Frid [Tue, 9 Aug 2011 04:02:51 +0000 (21:02 -0700)]
Bug 857044

Original-Change-Id: I80c8c2183426fbaa8b7d5316c09709c9de7ea39d
Reviewed-on: http://git-master/r/45970
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Tested-by: Terje Bergstrom <tbergstrom@nvidia.com>
Reviewed-by: Karan Jhavar <kjhavar@nvidia.com>
Reviewed-by: Narendra Damahe <ndamahe@nvidia.com>
Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>

Rebase-Id: Rd8c46b56d4ca22e05e40c664329d64f7bd6710f7

arch/arm/mach-tegra/powergate.c

index 67bcf75..7a439bb 100644 (file)
@@ -44,6 +44,9 @@
 static int tegra_num_powerdomains;
 static int tegra_num_cpu_domains;
 static u8 *tegra_cpu_domains;
+static u8 tegra20_cpu_domains[] = {
+       TEGRA_POWERGATE_CPU,
+};
 static u8 tegra30_cpu_domains[] = {
        TEGRA_POWERGATE_CPU0,
        TEGRA_POWERGATE_CPU1,
@@ -418,10 +421,26 @@ static void mc_flush(int id) {}
 static void mc_flush_done(int id) {}
 #endif
 
+static bool tegra_is_cpu_powergate_id(int id)
+{
+       int i;
+
+       for (i = 0; i < tegra_num_cpu_domains; i++)
+               if (tegra_cpu_domains[i] == id)
+                       return true;
+
+       return false;
+}
+
 static int tegra_powergate_set(int id, bool new_state)
 {
        bool status;
        unsigned long flags;
+       /* 10us timeout for toggle operation if it takes affect*/
+       int toggle_timeout = 10;
+       /* 100 * 10 = 1000us timeout for toggle command to take affect in case
+          of contention with h/w initiated CPU power gating */
+       int contention_timeout = 100;
 
        spin_lock_irqsave(&tegra_powergate_lock, flags);
 
@@ -432,10 +451,33 @@ static int tegra_powergate_set(int id, bool new_state)
                return -EINVAL;
        }
 
-       pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+       if (tegra_is_cpu_powergate_id(id)) {
+               /* CPU ungated in s/w only during boot/resume with outer
+                  waiting loop and no contention from other CPUs */
+               pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+               spin_unlock_irqrestore(&tegra_powergate_lock, flags);
+               return 0;
+       }
+
+       do {
+               pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE);
+               do {
+                       udelay(1);
+                       status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
+
+                       toggle_timeout--;
+               } while ((status != new_state) && (toggle_timeout > 0));
+
+               contention_timeout--;
+       } while ((status != new_state) && (contention_timeout > 0));
 
        spin_unlock_irqrestore(&tegra_powergate_lock, flags);
 
+       if (status != new_state) {
+               WARN(1, "Could not set powergate %d to %d", id, new_state);
+               return -EBUSY;
+       }
+
        return 0;
 }
 
@@ -802,6 +844,8 @@ int __init tegra_powergate_init(void)
        switch (tegra_chip_id) {
        case TEGRA20:
                tegra_num_powerdomains = 7;
+               tegra_num_cpu_domains = 1;
+               tegra_cpu_domains = tegra20_cpu_domains;
                break;
        case TEGRA30:
                tegra_num_powerdomains = 14;