unknown changes from android-tegra-nv-3.4
[linux-3.10.git] / arch / arm / mach-tegra / timer-t3.c
index 364fce3..52c8c44 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arch/mach-tegra/timer-t3.c
  *
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <asm/localtimer.h>
 #include <asm/sched_clock.h>
 
-#include <mach/hardware.h>
 #include <mach/iomap.h>
 #include <mach/irqs.h>
+#include <mach/hardware.h>
 
 #include "board.h"
 #include "clock.h"
 #include "cpuidle.h"
 #include "timer.h"
+#include "fuse.h"
 
 #define TEST_LP2_WAKE_TIMERS   0
 
@@ -70,6 +71,8 @@
 #define TIMER6_OFFSET (TEGRA_TMR6_BASE-TEGRA_TMR1_BASE)
 
 static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
+static cpumask_t wake_timer_ready;
+static cpumask_t wake_timer_canceled;
 
 #define timer_writel(value, reg) \
        __raw_writel(value, timer_reg_base + (reg))
@@ -80,9 +83,11 @@ static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
 #ifdef CONFIG_PM_SLEEP
 static u32 lp2_wake_timers[] = {
        TIMER3_OFFSET,
+#ifdef CONFIG_SMP
        TIMER4_OFFSET,
        TIMER5_OFFSET,
        TIMER6_OFFSET,
+#endif
 };
 
 static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id)
@@ -95,18 +100,20 @@ static irqreturn_t tegra_lp2wake_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-#define LP2_TIMER_IRQ_ACTION(n, i) {                           \
-       .name           = "tmr_lp2wake_cpu" __stringify(n),     \
+#define LP2_TIMER_IRQ_ACTION(cpu, irqnum) {                    \
+       .name           = "tmr_lp2wake_cpu" __stringify(cpu),   \
        .flags          = IRQF_DISABLED,                        \
        .handler        = tegra_lp2wake_interrupt,              \
-       .dev_id         = (void *)n,                            \
-       .irq            = i }
+       .dev_id         = (void*)cpu,                           \
+       .irq            = irqnum }
 
 static struct irqaction tegra_lp2wake_irq[] = {
        LP2_TIMER_IRQ_ACTION(0, INT_TMR3),
+#ifdef CONFIG_SMP
        LP2_TIMER_IRQ_ACTION(1, INT_TMR4),
        LP2_TIMER_IRQ_ACTION(2, INT_TMR5),
        LP2_TIMER_IRQ_ACTION(3, INT_TMR6),
+#endif
 };
 
 #ifdef CONFIG_SMP
@@ -153,6 +160,21 @@ static void test_lp2_wake_timer(unsigned int cpu)
 static inline void test_lp2_wake_timer(unsigned int cpu) {}
 #endif
 
+static int tegra3_resume_wake_timer(unsigned int cpu)
+{
+#ifdef CONFIG_SMP
+       int ret = irq_set_affinity(tegra_lp2wake_irq[cpu].irq, cpumask_of(cpu));
+       if (ret) {
+               pr_err("Failed to set affinity for LP2 timer IRQ to "
+                       "CPU %d: irq=%d, ret=%d\n", cpu,
+                       tegra_lp2wake_irq[cpu].irq, ret);
+               return ret;
+       }
+#endif
+       cpumask_set_cpu(cpu, &wake_timer_ready);
+       return 0;
+}
+
 static void tegra3_register_wake_timer(unsigned int cpu)
 {
        int ret;
@@ -165,13 +187,9 @@ static void tegra3_register_wake_timer(unsigned int cpu)
                goto fail;
        }
 
-       ret = irq_set_affinity(tegra_lp2wake_irq[cpu].irq, cpumask_of(cpu));
-       if (ret) {
-               pr_err("Failed to set affinity for LP2 timer IRQ to "
-                       "CPU %d: irq=%d, ret=%d\n", cpu,
-                       tegra_lp2wake_irq[cpu].irq, ret);
+       ret = tegra3_resume_wake_timer(cpu);
+       if (ret)
                goto fail;
-       }
 
        test_lp2_wake_timer(cpu);
        return;
@@ -179,14 +197,24 @@ fail:
        tegra_lp2_in_idle(false);
 }
 
-static void tegra3_unregister_wake_timer(unsigned int cpu)
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_HOTPLUG_CPU)
+static void tegra3_suspend_wake_timer(unsigned int cpu)
 {
+       cpumask_clear_cpu(cpu, &wake_timer_ready);
+#ifdef CONFIG_SMP
        /* Reassign the affinity of the wake IRQ to CPU 0. */
        (void)irq_set_affinity(tegra_lp2wake_irq[cpu].irq, cpumask_of(0));
+#endif
+}
+
+static void tegra3_unregister_wake_timer(unsigned int cpu)
+{
+       tegra3_suspend_wake_timer(cpu);
 
        /* Dispose of this IRQ. */
        remove_irq(tegra_lp2wake_irq[cpu].irq, &tegra_lp2wake_irq[cpu]);
 }
+#endif
 
 void tegra3_lp2_set_trigger(unsigned long cycles)
 {
@@ -206,59 +234,65 @@ unsigned long tegra3_lp2_timer_remain(void)
 {
        int cpu = cpu_number();
 
+       if (cpumask_test_and_clear_cpu(cpu, &wake_timer_canceled))
+               return -ETIME;
+
        return timer_readl(lp2_wake_timers[cpu] + TIMER_PCR) & 0x1ffffffful;
 }
-#endif
 
-void __init tegra3_init_timer(u32 *offset, int *irq, unsigned long rate)
+int tegra3_is_lp2_timer_ready(unsigned int cpu)
 {
-       switch (rate) {
-       case 12000000:
-               timer_writel(0x000b, TIMERUS_USEC_CFG);
-               break;
-       case 13000000:
-               timer_writel(0x000c, TIMERUS_USEC_CFG);
-               break;
-       case 19200000:
-               timer_writel(0x045f, TIMERUS_USEC_CFG);
-               break;
-       case 26000000:
-               timer_writel(0x0019, TIMERUS_USEC_CFG);
-               break;
-       case 16800000:
-               timer_writel(0x0453, TIMERUS_USEC_CFG);
-               break;
-       case 38400000:
-               timer_writel(0x04BF, TIMERUS_USEC_CFG);
-               break;
-       case 48000000:
-               timer_writel(0x002F, TIMERUS_USEC_CFG);
-               break;
-       default:
-               WARN(1, "Unknown clock rate");
+       return cpumask_test_cpu(cpu, &wake_timer_ready);
+}
+
+void tegra3_lp2_timer_cancel_secondary(void)
+{
+       int cpu;
+       int base;
+
+       for (cpu = 1; cpu < ARRAY_SIZE(lp2_wake_timers); cpu++) {
+               base = lp2_wake_timers[cpu];
+               cpumask_set_cpu(cpu, &wake_timer_canceled);
+               timer_writel(0, base + TIMER_PTV);
+               timer_writel(1<<30, base + TIMER_PCR);
        }
+}
+#endif
 
+void __init tegra30_init_timer(void)
+{
 #ifdef CONFIG_PM_SLEEP
-       /* For T30.A01 use INT_TMR_SHARED instead of INT_TMR6. */
-       if ((tegra_chip_id == TEGRA30) &&
+#ifdef CONFIG_SMP
+       /* For T30.A01 use INT_TMR_SHARED instead of INT_TMR6 for CPU3. */
+       if ((tegra_get_chipid() == TEGRA_CHIPID_TEGRA3) &&
                (tegra_revision == TEGRA_REVISION_A01))
                        tegra_lp2wake_irq[3].irq = INT_TMR_SHARED;
+#endif
 
        tegra3_register_wake_timer(0);
 #endif
-
-       *offset = TIMER1_OFFSET;
-       *irq = INT_TMR1;
 }
 
 #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_HOTPLUG_CPU)
 static int hotplug_notify(struct notifier_block *self,
                                      unsigned long action, void *cpu)
 {
-       if (action == CPU_ONLINE)
+       switch (action) {
+       case CPU_ONLINE:
                tegra3_register_wake_timer((unsigned int)cpu);
-       else if (action == CPU_DOWN_PREPARE)
+               break;
+       case CPU_ONLINE_FROZEN:
+               tegra3_resume_wake_timer((unsigned int)cpu);
+               break;
+       case CPU_DOWN_PREPARE:
                tegra3_unregister_wake_timer((unsigned int)cpu);
+               break;
+       case CPU_DOWN_PREPARE_FROZEN:
+               tegra3_suspend_wake_timer((unsigned int)cpu);
+               break;
+       default:
+               break;
+       }
 
        return NOTIFY_OK;
 }