]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - arch/mips/kernel/time.c
[MIPS] Fix "no space between function name and open parenthesis" warnings.
[mirror_ubuntu-zesty-kernel.git] / arch / mips / kernel / time.c
index e5e56bd498dbcf0d650fe22152ad98e6c5d62a8e..5892491b40eb1af7fabcdd6bb13276c58684d404 100644 (file)
  * Free Software Foundation;  either version 2 of the  License, or (at your
  * option) any later version.
  */
+#include <linux/clockchips.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/param.h>
+#include <linux/profile.h>
 #include <linux/time.h>
 #include <linux/timex.h>
 #include <linux/smp.h>
@@ -23,6 +25,7 @@
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/kallsyms.h>
 
 #include <asm/bootinfo.h>
 #include <asm/cache.h>
 #include <asm/cpu-features.h>
 #include <asm/div64.h>
 #include <asm/sections.h>
+#include <asm/smtc_ipi.h>
 #include <asm/time.h>
 
+#include <irq.h>
+
 /*
  * The integer part of the number of usecs per jiffy is taken from tick,
  * but the fractional part is not recorded, so we calculate it using the
  * forward reference
  */
 DEFINE_SPINLOCK(rtc_lock);
+EXPORT_SYMBOL(rtc_lock);
 
-/*
- * By default we provide the null RTC ops
- */
-static unsigned long null_rtc_get_time(void)
+int __weak rtc_mips_set_time(unsigned long sec)
 {
-       return mktime(2000, 1, 1, 0, 0, 0);
+       return 0;
 }
+EXPORT_SYMBOL(rtc_mips_set_time);
 
-static int null_rtc_set_time(unsigned long sec)
+int __weak rtc_mips_set_mmss(unsigned long nowtime)
 {
-       return 0;
+       return rtc_mips_set_time(nowtime);
 }
 
-unsigned long (*rtc_mips_get_time)(void) = null_rtc_get_time;
-int (*rtc_mips_set_time)(unsigned long) = null_rtc_set_time;
-int (*rtc_mips_set_mmss)(unsigned long);
-
+int update_persistent_clock(struct timespec now)
+{
+       return rtc_mips_set_mmss(now.tv_sec);
+}
 
 /* how many counter cycles in a jiffy */
 static unsigned long cycles_per_jiffy __read_mostly;
 
-/* expirelo is the count value for next CPU timer interrupt */
-static unsigned int expirelo;
-
-
 /*
  * Null timer ack for systems not needing one (e.g. i8254).
  */
@@ -92,18 +93,7 @@ static cycle_t null_hpt_read(void)
  */
 static void c0_timer_ack(void)
 {
-       unsigned int count;
-
-       /* Ack this timer interrupt and set the next one.  */
-       expirelo += cycles_per_jiffy;
-       write_c0_compare(expirelo);
-
-       /* Check to see if we have missed any timer interrupts.  */
-       while (((count = read_c0_count()) - expirelo) < 0x7fffffff) {
-               /* missed_timer_count++; */
-               expirelo = count + cycles_per_jiffy;
-               write_c0_compare(expirelo);
-       }
+       write_c0_compare(read_c0_compare());
 }
 
 /*
@@ -114,19 +104,9 @@ static cycle_t c0_hpt_read(void)
        return read_c0_count();
 }
 
-/* For use both as a high precision timer and an interrupt source.  */
-static void __init c0_hpt_timer_init(void)
-{
-       expirelo = read_c0_count() + cycles_per_jiffy;
-       write_c0_compare(expirelo);
-}
-
 int (*mips_timer_state)(void);
 void (*mips_timer_ack)(void);
 
-/* last time when xtime and rtc are sync'ed up */
-static long last_rtc_update;
-
 /*
  * local_timer_interrupt() does profiling and process accounting
  * on a per-CPU basis.
@@ -143,125 +123,63 @@ void local_timer_interrupt(int irq, void *dev_id)
        update_process_times(user_mode(get_irq_regs()));
 }
 
-/*
- * High-level timer interrupt service routines.  This function
- * is set as irqaction->handler and is invoked through do_IRQ.
- */
-irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-       write_seqlock(&xtime_lock);
-
-       mips_timer_ack();
-
-       /*
-        * call the generic timer interrupt handling
-        */
-       do_timer(1);
-
-       /*
-        * If we have an externally synchronized Linux clock, then update
-        * CMOS clock accordingly every ~11 minutes. rtc_mips_set_time() has to be
-        * called as close as possible to 500 ms before the new second starts.
-        */
-       if (ntp_synced() &&
-           xtime.tv_sec > last_rtc_update + 660 &&
-           (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
-           (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
-               if (rtc_mips_set_mmss(xtime.tv_sec) == 0) {
-                       last_rtc_update = xtime.tv_sec;
-               } else {
-                       /* do it again in 60 s */
-                       last_rtc_update = xtime.tv_sec - 600;
-               }
-       }
-
-       write_sequnlock(&xtime_lock);
-
-       /*
-        * In UP mode, we call local_timer_interrupt() to do profiling
-        * and process accouting.
-        *
-        * In SMP mode, local_timer_interrupt() is invoked by appropriate
-        * low-level local timer interrupt handler.
-        */
-       local_timer_interrupt(irq, dev_id);
-
-       return IRQ_HANDLED;
-}
-
 int null_perf_irq(void)
 {
        return 0;
 }
 
+EXPORT_SYMBOL(null_perf_irq);
+
 int (*perf_irq)(void) = null_perf_irq;
 
-EXPORT_SYMBOL(null_perf_irq);
 EXPORT_SYMBOL(perf_irq);
 
-asmlinkage void ll_timer_interrupt(int irq)
-{
-       int r2 = cpu_has_mips_r2;
+/*
+ * Timer interrupt
+ */
+int cp0_compare_irq;
 
-       irq_enter();
-       kstat_this_cpu.irqs[irq]++;
+/*
+ * Performance counter IRQ or -1 if shared with timer
+ */
+int cp0_perfcount_irq;
+EXPORT_SYMBOL_GPL(cp0_perfcount_irq);
 
+/*
+ * Possibly handle a performance counter interrupt.
+ * Return true if the timer interrupt should not be checked
+ */
+static inline int handle_perf_irq(int r2)
+{
        /*
-        * Suckage alert:
-        * Before R2 of the architecture there was no way to see if a
-        * performance counter interrupt was pending, so we have to run the
-        * performance counter interrupt handler anyway.
+        * The performance counter overflow interrupt may be shared with the
+        * timer interrupt (cp0_perfcount_irq < 0). If it is and a
+        * performance counter has overflowed (perf_irq() == IRQ_HANDLED)
+        * and we can't reliably determine if a counter interrupt has also
+        * happened (!r2) then don't check for a timer interrupt.
         */
-       if (!r2 || (read_c0_cause() & (1 << 26)))
-               if (perf_irq())
-                       goto out;
-
-       /* we keep interrupt disabled all the time */
-       if (!r2 || (read_c0_cause() & (1 << 30)))
-               timer_interrupt(irq, NULL);
-
-out:
-       irq_exit();
-}
-
-asmlinkage void ll_local_timer_interrupt(int irq)
-{
-       irq_enter();
-       if (smp_processor_id() != 0)
-               kstat_this_cpu.irqs[irq]++;
-
-       /* we keep interrupt disabled all the time */
-       local_timer_interrupt(irq, NULL);
-
-       irq_exit();
+       return (cp0_perfcount_irq < 0) &&
+               perf_irq() == IRQ_HANDLED &&
+               !r2;
 }
 
 /*
  * time_init() - it does the following things.
  *
- * 1) board_time_init() -
+ * 1) plat_time_init() -
  *     a) (optional) set up RTC routines,
  *      b) (optional) calibrate and set the mips_hpt_frequency
  *         (only needed if you intended to use cpu counter as timer interrupt
  *          source)
- * 2) setup xtime based on rtc_mips_get_time().
- * 3) calculate a couple of cached variables for later usage
- * 4) plat_timer_setup() -
+ * 2) calculate a couple of cached variables for later usage
+ * 3) plat_timer_setup() -
  *     a) (optional) over-write any choices made above by time_init().
  *     b) machine specific code should setup the timer irqaction.
  *     c) enable the timer interrupt
  */
 
-void (*board_time_init)(void);
-
 unsigned int mips_hpt_frequency;
 
-static struct irqaction timer_irqaction = {
-       .handler = timer_interrupt,
-       .flags = IRQF_DISABLED,
-       .name = "timer",
-};
-
 static unsigned int __init calibrate_hpt(void)
 {
        cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
@@ -306,10 +224,88 @@ static unsigned int __init calibrate_hpt(void)
 
 struct clocksource clocksource_mips = {
        .name           = "MIPS",
-       .mask           = 0xffffffff,
+       .mask           = CLOCKSOURCE_MASK(32),
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
+static int mips_next_event(unsigned long delta,
+                           struct clock_event_device *evt)
+{
+       unsigned int cnt;
+       int res;
+
+#ifdef CONFIG_MIPS_MT_SMTC
+       {
+       unsigned long flags, vpflags;
+       local_irq_save(flags);
+       vpflags = dvpe();
+#endif
+       cnt = read_c0_count();
+       cnt += delta;
+       write_c0_compare(cnt);
+       res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
+#ifdef CONFIG_MIPS_MT_SMTC
+       evpe(vpflags);
+       local_irq_restore(flags);
+       }
+#endif
+       return res;
+}
+
+static void mips_set_mode(enum clock_event_mode mode,
+                          struct clock_event_device *evt)
+{
+       /* Nothing to do ...  */
+}
+
+static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
+static int cp0_timer_irq_installed;
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+       const int r2 = cpu_has_mips_r2;
+       struct clock_event_device *cd;
+       int cpu = smp_processor_id();
+
+       /*
+        * Suckage alert:
+        * Before R2 of the architecture there was no way to see if a
+        * performance counter interrupt was pending, so we have to run
+        * the performance counter interrupt handler anyway.
+        */
+       if (handle_perf_irq(r2))
+               goto out;
+
+       /*
+        * The same applies to performance counter interrupts.  But with the
+        * above we now know that the reason we got here must be a timer
+        * interrupt.  Being the paranoiacs we are we check anyway.
+        */
+       if (!r2 || (read_c0_cause() & (1 << 30))) {
+               c0_timer_ack();
+#ifdef CONFIG_MIPS_MT_SMTC
+               if (cpu_data[cpu].vpe_id)
+                       goto out;
+               cpu = 0;
+#endif
+               cd = &per_cpu(mips_clockevent_device, cpu);
+               cd->event_handler(cd);
+       }
+
+out:
+       return IRQ_HANDLED;
+}
+
+static struct irqaction timer_irqaction = {
+       .handler = timer_interrupt,
+#ifdef CONFIG_MIPS_MT_SMTC
+       .flags = IRQF_DISABLED,
+#else
+       .flags = IRQF_DISABLED | IRQF_PERCPU,
+#endif
+       .name = "timer",
+};
+
 static void __init init_mips_clocksource(void)
 {
        u64 temp;
@@ -333,19 +329,127 @@ static void __init init_mips_clocksource(void)
        clocksource_register(&clocksource_mips);
 }
 
-void __init time_init(void)
+void __init __weak plat_time_init(void)
+{
+}
+
+void __init __weak plat_timer_setup(struct irqaction *irq)
+{
+}
+
+#ifdef CONFIG_MIPS_MT_SMTC
+DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
+
+static void smtc_set_mode(enum clock_event_mode mode,
+                          struct clock_event_device *evt)
+{
+}
+
+int dummycnt[NR_CPUS];
+
+static void mips_broadcast(cpumask_t mask)
+{
+       unsigned int cpu;
+
+       for_each_cpu_mask(cpu, mask)
+               smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
+}
+
+static void setup_smtc_dummy_clockevent_device(void)
+{
+       //uint64_t mips_freq = mips_hpt_^frequency;
+       unsigned int cpu = smp_processor_id();
+       struct clock_event_device *cd;
+
+       cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
+
+       cd->name                = "SMTC";
+       cd->features            = CLOCK_EVT_FEAT_DUMMY;
+
+       /* Calculate the min / max delta */
+       cd->mult        = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
+       cd->shift               = 0; //32;
+       cd->max_delta_ns        = 0; //clockevent_delta2ns(0x7fffffff, cd);
+       cd->min_delta_ns        = 0; //clockevent_delta2ns(0x30, cd);
+
+       cd->rating              = 200;
+       cd->irq                 = 17; //-1;
+//     if (cpu)
+//             cd->cpumask     = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
+//     else
+               cd->cpumask     = cpumask_of_cpu(cpu);
+
+       cd->set_mode            = smtc_set_mode;
+
+       cd->broadcast           = mips_broadcast;
+
+       clockevents_register_device(cd);
+}
+#endif
+
+static void mips_event_handler(struct clock_event_device *dev)
+{
+}
+
+void __cpuinit mips_clockevent_init(void)
 {
-       if (board_time_init)
-               board_time_init();
+       uint64_t mips_freq = mips_hpt_frequency;
+       unsigned int cpu = smp_processor_id();
+       struct clock_event_device *cd;
+       unsigned int irq = MIPS_CPU_IRQ_BASE + 7;
+
+       if (!cpu_has_counter)
+               return;
 
-       if (!rtc_mips_set_mmss)
-               rtc_mips_set_mmss = rtc_mips_set_time;
+#ifdef CONFIG_MIPS_MT_SMTC
+       setup_smtc_dummy_clockevent_device();
 
-       xtime.tv_sec = rtc_mips_get_time();
-       xtime.tv_nsec = 0;
+       /*
+        * On SMTC we only register VPE0's compare interrupt as clockevent
+        * device.
+        */
+       if (cpu)
+               return;
+#endif
+
+       cd = &per_cpu(mips_clockevent_device, cpu);
+
+       cd->name                = "MIPS";
+       cd->features            = CLOCK_EVT_FEAT_ONESHOT;
+
+       /* Calculate the min / max delta */
+       cd->mult        = div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
+       cd->shift               = 32;
+       cd->max_delta_ns        = clockevent_delta2ns(0x7fffffff, cd);
+       cd->min_delta_ns        = clockevent_delta2ns(0x30, cd);
+
+       cd->rating              = 300;
+       cd->irq                 = irq;
+#ifdef CONFIG_MIPS_MT_SMTC
+       cd->cpumask             = CPU_MASK_ALL;
+#else
+       cd->cpumask             = cpumask_of_cpu(cpu);
+#endif
+       cd->set_next_event      = mips_next_event;
+       cd->set_mode            = mips_set_mode;
+       cd->event_handler       = mips_event_handler;
+
+       clockevents_register_device(cd);
+
+       if (!cp0_timer_irq_installed) {
+#ifdef CONFIG_MIPS_MT_SMTC
+#define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq)
+               setup_irq_smtc(irq, &timer_irqaction, CPUCTR_IMASKBIT);
+#else
+               setup_irq(irq, &timer_irqaction);
+#endif /* CONFIG_MIPS_MT_SMTC */
+               cp0_timer_irq_installed = 1;
+       }
+}
 
-       set_normalized_timespec(&wall_to_monotonic,
-                               -xtime.tv_sec, -xtime.tv_nsec);
+void __init time_init(void)
+{
+       plat_time_init();
 
        /* Choose appropriate high precision timer routines.  */
        if (!cpu_has_counter && !clocksource_mips.read)
@@ -368,11 +472,6 @@ void __init time_init(void)
                                /* Calculate cache parameters.  */
                                cycles_per_jiffy =
                                        (mips_hpt_frequency + HZ / 2) / HZ;
-                               /*
-                                * This sets up the high precision
-                                * timer for the first interrupt.
-                                */
-                               c0_hpt_timer_init();
                        }
                }
                if (!mips_hpt_frequency)
@@ -382,6 +481,10 @@ void __init time_init(void)
                printk("Using %u.%03u MHz high precision timer.\n",
                       ((mips_hpt_frequency + 500) / 1000) / 1000,
                       ((mips_hpt_frequency + 500) / 1000) % 1000);
+
+#ifdef CONFIG_IRQ_CPU
+               setup_irq(MIPS_CPU_IRQ_BASE + 7, &timer_irqaction);
+#endif
        }
 
        if (!mips_timer_ack)
@@ -402,56 +505,5 @@ void __init time_init(void)
        plat_timer_setup(&timer_irqaction);
 
        init_mips_clocksource();
+       mips_clockevent_init();
 }
-
-#define FEBRUARY               2
-#define STARTOFTIME            1970
-#define SECDAY                 86400L
-#define SECYR                  (SECDAY * 365)
-#define leapyear(y)            ((!((y) % 4) && ((y) % 100)) || !((y) % 400))
-#define days_in_year(y)                (leapyear(y) ? 366 : 365)
-#define days_in_month(m)       (month_days[(m) - 1])
-
-static int month_days[12] = {
-       31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
-
-void to_tm(unsigned long tim, struct rtc_time *tm)
-{
-       long hms, day, gday;
-       int i;
-
-       gday = day = tim / SECDAY;
-       hms = tim % SECDAY;
-
-       /* Hours, minutes, seconds are easy */
-       tm->tm_hour = hms / 3600;
-       tm->tm_min = (hms % 3600) / 60;
-       tm->tm_sec = (hms % 3600) % 60;
-
-       /* Number of years in days */
-       for (i = STARTOFTIME; day >= days_in_year(i); i++)
-               day -= days_in_year(i);
-       tm->tm_year = i;
-
-       /* Number of months in days left */
-       if (leapyear(tm->tm_year))
-               days_in_month(FEBRUARY) = 29;
-       for (i = 1; day >= days_in_month(i); i++)
-               day -= days_in_month(i);
-       days_in_month(FEBRUARY) = 28;
-       tm->tm_mon = i - 1;             /* tm_mon starts from 0 to 11 */
-
-       /* Days are what is left over (+1) from all that. */
-       tm->tm_mday = day + 1;
-
-       /*
-        * Determine the day of week
-        */
-       tm->tm_wday = (gday + 4) % 7;   /* 1970/1/1 was Thursday */
-}
-
-EXPORT_SYMBOL(rtc_lock);
-EXPORT_SYMBOL(to_tm);
-EXPORT_SYMBOL(rtc_mips_set_time);
-EXPORT_SYMBOL(rtc_mips_get_time);