ARM: tegra: add avp platform_device
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-tegra / cpuidle.c
index d128ecea6ca4c69cbede9334bc0fcebdd981a4ee..a063c34ecf60aa142df1652ecb6b274794fe528e 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)
@@ -138,14 +131,17 @@ static inline void tegra_flow_wfi(struct cpuidle_device *dev)
 
        flow_ctrl = flow_ctrl + FLOW_CTRL_HALT_CPUx_EVENTS(dev->cpu);
 
+       stop_critical_timings();
        dsb();
        __raw_writel(reg, flow_ctrl);
        reg = __raw_readl(flow_ctrl);
        __asm__ volatile ("wfi");
        __raw_writel(0, flow_ctrl);
        reg = __raw_readl(flow_ctrl);
+       start_critical_timings();
 }
 
+#ifdef CONFIG_SMP
 static inline bool tegra_wait_for_both_idle(struct cpuidle_device *dev)
 {
        int wake_int;
@@ -183,18 +179,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 +276,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 +302,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 +312,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 +342,7 @@ restart:
        }
 }
 
+#ifdef CONFIG_SMP
 static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev,
        struct cpuidle_state *state)
 {
@@ -348,6 +379,7 @@ static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev,
 
        /* Prepare CPU1 for LP2 by putting it in reset */
 
+       stop_critical_timings();
        gic_cpu_exit(0);
        barrier();
        twd_ctrl = readl(twd_base + 0x8);
@@ -369,6 +401,7 @@ static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev,
        tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE);
 
        writel(smp_processor_id(), EVP_CPU_RESET_VECTOR);
+       start_critical_timings();
 
        /*
         * TODO: is it worth going back to wfi if no interrupt is pending
@@ -380,6 +413,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 +450,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);
@@ -440,7 +478,7 @@ static int tegra_idle_enter_lp2(struct cpuidle_device *dev,
        return (int)us;
 }
 
-static int tegra_idle_enter(unsigned int cpu)
+static int tegra_cpuidle_register_device(unsigned int cpu)
 {
        struct cpuidle_device *dev;
        struct cpuidle_state *state;
@@ -476,6 +514,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++;
 
@@ -554,7 +593,7 @@ static int __init tegra_cpuidle_init(void)
                return ret;
 
        for_each_possible_cpu(cpu) {
-               if (tegra_idle_enter(cpu))
+               if (tegra_cpuidle_register_device(cpu))
                        pr_err("CPU%u: error initializing idle loop\n", cpu);
        }
 
@@ -577,6 +616,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 +664,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;
 }