unknown changes from android-tegra-nv-3.4
[linux-3.10.git] / arch / arm / mach-tegra / timer-t3.c
index e9e43d1..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
@@ -30,6 +30,7 @@
 #include <linux/io.h>
 #include <linux/smp.h>
 #include <linux/syscore_ops.h>
+#include <linux/cpu.h>
 #include <linux/export.h>
 
 #include <asm/mach/time.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
 
 /*
  * Timers usage:
- * TMR1 - used as general cpu timer.
+ * TMR1 - used as general CPU timer.
  * TMR2 - used by AVP.
- * TMR3 - used by CPU0 for Lp2 wakeup.
- * TMR4 - used by CPU1 for Lp2 wakeup.
- * TMR5 - used by CPU2 for Lp2 wakeup.
- * TMR6 - used by CPU3 for Lp2 wakeup.
+ * TMR3 - used by CPU0 for LP2 wakeup.
+ * TMR4 - used by CPU1 for LP2 wakeup.
+ * TMR5 - used by CPU2 for LP2 wakeup.
+ * TMR6 - used by CPU3 for LP2 wakeup.
  * TMR7 - Free.
  * TMR8 - Free.
  * TMR9 - Free.
- * TMR10 - used as src for watchdog controller 0.
+ * TMR10 - used as source for watchdog controller 0.
 */
 
 #define TIMER1_OFFSET (TEGRA_TMR1_BASE-TEGRA_TMR1_BASE)
@@ -65,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))
@@ -72,190 +80,230 @@ static void __iomem *timer_reg_base = IO_ADDRESS(TEGRA_TMR1_BASE);
        __raw_readl(timer_reg_base + (reg))
 
 
-#if 0
-static int lp2_wake_timers[] = {
+#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)
 {
        int cpu = (int)dev_id;
-       int timer_base;
+       int base;
 
-       timer_base = lp2_wake_timers[cpu];
-       timer_writel(1<<30, timer_base + TIMER_PCR);
+       base = lp2_wake_timers[cpu];
+       timer_writel(1<<30, base + TIMER_PCR);
        return IRQ_HANDLED;
 }
 
-#define LP2_TIMER_IRQ_ACTION(n, i) \
-static struct irqaction tegra_lp2wake_irq_cpu##n = { \
-       .name           = "tmr_lp2wake_cpu" __stringify(n), \
-       .flags          = IRQF_DISABLED, \
-       .handler        = tegra_lp2wake_interrupt, \
-       .dev_id         = (void*)n, \
-       .irq            = i, \
-};
+#define LP2_TIMER_IRQ_ACTION(cpu, irqnum) {                    \
+       .name           = "tmr_lp2wake_cpu" __stringify(cpu),   \
+       .flags          = IRQF_DISABLED,                        \
+       .handler        = tegra_lp2wake_interrupt,              \
+       .dev_id         = (void*)cpu,                           \
+       .irq            = irqnum }
 
-#define LP2_TIMER_IRQ_ACTIONS() \
-       LP2_TIMER_IRQ_ACTION(0, INT_TMR3); \
-       LP2_TIMER_IRQ_ACTION(1, INT_TMR4); \
-       LP2_TIMER_IRQ_ACTION(2, INT_TMR5); \
-       LP2_TIMER_IRQ_ACTION(3, INT_TMR6);
-
-LP2_TIMER_IRQ_ACTIONS();
-
-#define REGISTER_LP2_WAKE_IRQ(n) \
-       ret = setup_irq(tegra_lp2wake_irq_cpu##n.irq, &tegra_lp2wake_irq_cpu##n); \
-       if (ret) { \
-               printk(KERN_ERR "Failed to register LP2 timer IRQ: " \
-                       "irq=%d, ret=%d\n", tegra_lp2wake_irq_cpu##n.irq, ret); \
-               BUG(); \
-       } \
-       ret = irq_set_affinity(tegra_lp2wake_irq_cpu##n.irq, cpumask_of(n)); \
-       if (ret) { \
-               printk(KERN_ERR "Failed to set affinity for LP2 timer IRQ: " \
-                       "irq=%d, ret=%d\n", tegra_lp2wake_irq_cpu##n.irq, ret); \
-               BUG(); \
-       }
+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
+};
 
-#define REGISTER_LP2_WAKE_IRQS() \
-do { \
-       REGISTER_LP2_WAKE_IRQ(0); \
-       REGISTER_LP2_WAKE_IRQ(1); \
-       REGISTER_LP2_WAKE_IRQ(2); \
-       REGISTER_LP2_WAKE_IRQ(3); \
-} while (0)
+#ifdef CONFIG_SMP
+#define hard_smp_processor_id()                                                \
+       ({                                                              \
+               unsigned int cpunum;                                    \
+               __asm__("\n"                                            \
+                       "1:     mrc p15, 0, %0, c0, c0, 5\n"            \
+                       "       .pushsection \".alt.smp.init\", \"a\"\n"\
+                       "       .long   1b\n"                           \
+                       "       mov     %0, #0\n"                       \
+                       "       .popsection"                            \
+                       : "=r" (cpunum));                               \
+               cpunum &= 0x0F;                                         \
+       })
+#define cpu_number()   hard_smp_processor_id()
+#else
+#define cpu_number()   0
+#endif
 
 /*
- * To sanity test timer interrupts for cpu 0-3, enable this flag and check
- * /proc/interrupts for timer interrupts. Cpu's 0-3 would have one interrupt
- * counted against them for tmr_lp2wake_cpu0,1,2,3.
+ * To sanity test LP2 timer interrupts for CPU 0-3, enable this flag and check
+ * /proc/interrupts for timer interrupts. CPUs 0-3 should have one interrupt
+ * counted against them for tmr_lp2wake_cpu<n>, where <n> is the CPU number.
  */
-#define TEST_LP2_WAKE_TIMERS 0
 #if TEST_LP2_WAKE_TIMERS
-static void test_lp2_wake_timers(void)
+static void test_lp2_wake_timer(unsigned int cpu)
 {
-       unsigned int cpu;
-       unsigned int timer_base;
        unsigned long cycles = 50000;
+       unsigned int base = lp2_wake_timers[cpu];
+       static bool tested[4] = {false, false, false, false};
 
-       for_each_possible_cpu(cpu) {
-               timer_base = lp2_wake_timers[cpu];
-               timer_writel(0, timer_base + TIMER_PTV);
+       /* Don't repeat the test process on hotplug restart. */
+       if (!tested[cpu]) {
+               timer_writel(0, base + TIMER_PTV);
                if (cycles) {
                        u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
-                       timer_writel(reg, timer_base + TIMER_PTV);
+                       timer_writel(reg, base + TIMER_PTV);
+                       tested[cpu] = true;
                }
        }
 }
 #else
-static void test_lp2_wake_timers(void){}
+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;
+}
 
-void __init tegra3_init_timer(u32 *offset, int *irq, unsigned long rate)
+static void tegra3_register_wake_timer(unsigned int cpu)
 {
-       void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804;
-       unsigned long id;
+       int ret;
 
-       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");
+       ret = setup_irq(tegra_lp2wake_irq[cpu].irq, &tegra_lp2wake_irq[cpu]);
+       if (ret) {
+               pr_err("Failed to register LP2 timer IRQ for CPU %d: "
+                       "irq=%d, ret=%d\n", cpu,
+                       tegra_lp2wake_irq[cpu].irq, ret);
+               goto fail;
        }
 
-#if 0
-       /* For T30.A01 use INT_TMR_SHARED instead of INT_TMR6. */
-       id = readl(chip_id);
-       if (((id & 0xFF00) >> 8) == 0x30) {
-#ifndef CONFIG_TEGRA_FPGA_PLATFORM
-               if (((id >> 16) & 0xf) == 1) {
-                       tegra_lp2wake_irq_cpu3.irq = INT_TMR_SHARED;
-               }
-#else
-               void __iomem *emu_rev = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x860;
-               unsigned long reg = readl(emu_rev);
-               unsigned long netlist = reg & 0xFFFF;
-               unsigned long patch = (reg >> 16) & 0xFF;
-               if ((netlist == 12) && (patch < 14)) {
-                       tegra_lp2wake_irq_cpu3.irq = INT_TMR_SHARED;
-               }
-#endif
-       }
+       ret = tegra3_resume_wake_timer(cpu);
+       if (ret)
+               goto fail;
 
-       REGISTER_LP2_WAKE_IRQS();
-#endif
+       test_lp2_wake_timer(cpu);
+       return;
+fail:
+       tegra_lp2_in_idle(false);
+}
 
-#if 0
-       test_lp2_wake_timers();
+#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
-
-       *offset = TIMER1_OFFSET;
-       *irq = INT_TMR1;
 }
 
-#if 0
-#ifdef CONFIG_SMP
-#define hard_smp_processor_id()                                                \
-       ({                                                              \
-               unsigned int cpunum;                                    \
-               __asm__("\n"                                            \
-                       "1:     mrc p15, 0, %0, c0, c0, 5\n"            \
-                       "       .pushsection \".alt.smp.init\", \"a\"\n"\
-                       "       .long   1b\n"                           \
-                       "       mov     %0, #0\n"                       \
-                       "       .popsection"                            \
-                       : "=r" (cpunum));                               \
-               cpunum &= 0x0F;                                         \
-       })
-#define cpu_number()   hard_smp_processor_id()
-#else
-#define cpu_number()   0
+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 tegra_lp2_set_trigger(unsigned long cycles)
+void tegra3_lp2_set_trigger(unsigned long cycles)
 {
        int cpu = cpu_number();
-       int timer_base;
+       int base;
 
-       timer_base = lp2_wake_timers[cpu];
-       timer_writel(0, timer_base + TIMER_PTV);
+       base = lp2_wake_timers[cpu];
+       timer_writel(0, base + TIMER_PTV);
        if (cycles) {
                u32 reg = 0x80000000ul | min(0x1ffffffful, cycles);
-               timer_writel(reg, timer_base + TIMER_PTV);
+               timer_writel(reg, base + TIMER_PTV);
        }
 }
-EXPORT_SYMBOL(tegra_lp2_set_trigger);
+EXPORT_SYMBOL(tegra3_lp2_set_trigger);
 
-unsigned long tegra_lp2_timer_remain(void)
+unsigned long tegra3_lp2_timer_remain(void)
 {
        int cpu = cpu_number();
-       int timer_base;
 
-       timer_base = lp2_wake_timers[cpu];
-       return timer_readl(timer_base + TIMER_PCR) & 0x1ffffffful;
+       if (cpumask_test_and_clear_cpu(cpu, &wake_timer_canceled))
+               return -ETIME;
+
+       return timer_readl(lp2_wake_timers[cpu] + TIMER_PCR) & 0x1ffffffful;
+}
+
+int tegra3_is_lp2_timer_ready(unsigned int cpu)
+{
+       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
+#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
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_HOTPLUG_CPU)
+static int hotplug_notify(struct notifier_block *self,
+                                     unsigned long action, void *cpu)
+{
+       switch (action) {
+       case CPU_ONLINE:
+               tegra3_register_wake_timer((unsigned int)cpu);
+               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;
+}
+
+static struct notifier_block __cpuinitdata hotplug_notifier_block = {
+       .notifier_call = hotplug_notify,
+};
+
+static int __init hotplug_cpu_register(void)
+{
+       return register_cpu_notifier(&hotplug_notifier_block);
 }
+early_initcall(hotplug_cpu_register);
 #endif