[ubicom32]: move new files out from platform support patch
[openwrt/svn-archive/archive.git] / target / linux / ubicom32 / files / arch / ubicom32 / kernel / time.c
diff --git a/target/linux/ubicom32/files/arch/ubicom32/kernel/time.c b/target/linux/ubicom32/files/arch/ubicom32/kernel/time.c
new file mode 100644 (file)
index 0000000..4a99284
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * arch/ubicom32/kernel/time.c
+ *     Initialize the timer list and start the appropriate timers.
+ *
+ * (C) Copyright 2009, Ubicom, Inc.
+ * Copyright (C) 1991, 1992, 1995  Linus Torvalds
+ *
+ * This file is part of the Ubicom32 Linux Kernel Port.
+ *
+ * The Ubicom32 Linux Kernel Port is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Ubicom32 Linux Kernel Port is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the Ubicom32 Linux Kernel Port.  If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Ubicom32 implementation derived from (with many thanks):
+ *   arch/m68knommu
+ *   arch/blackfin
+ *   arch/parisc
+ */
+
+#include <linux/profile.h>
+#include <linux/smp.h>
+#include <asm/ip5000.h>
+#include <asm/machdep.h>
+
+/*
+ * A bitmap of the timers on the processor indicates
+ * that the timer is free or in-use.
+ */
+static unsigned int timers;
+
+/*
+ * timer_set()
+ *     Init the specified compare register to go off <n> cycles from now.
+ */
+void timer_set(int timervector, unsigned int cycles)
+{
+       int idx = UBICOM32_VECTOR_TO_TIMER_INDEX(timervector);
+       UBICOM32_IO_TIMER->syscom[idx] =
+                       UBICOM32_IO_TIMER->sysval + cycles;
+       ldsr_enable_vector(timervector);
+}
+
+/*
+ * timer_reset()
+ *     Set/reset the timer to go off again.
+ *
+ * Because sysval is a continuous timer, this function is able
+ * to ensure that we do not have clock sku by using the previous
+ * value in syscom to set the next value for syscom.
+ *
+ * Returns the number of ticks that transpired since the last event.
+ */
+int timer_reset(int timervector, unsigned int cycles)
+{
+       /*
+        * Reset the timer in the LDSR thread to go off appropriately.
+        *
+        * Use the previous value of the timer to calculate the new stop
+        * time.  This allows us to account for it taking an
+        * indeterminate amount of time to get here.
+        */
+       const int timer_index = UBICOM32_VECTOR_TO_TIMER_INDEX(timervector);
+       unsigned int prev = UBICOM32_IO_TIMER->syscom[timer_index];
+       unsigned int next = prev + cycles;
+       int scratchpad3;
+       int diff;
+       int ticks = 1;
+
+       /*
+        * If the difference is negative, we have missed at least one
+        * timer tick.
+        *
+        * TODO: Decide if we want to "ignore" time (as done below) or
+        * if we want to process time (unevenly) by calling timer_tick()
+        * lost_ticks times.
+        */
+       while (1) {
+               /*
+                * Set our future time first.
+                */
+               UBICOM32_IO_TIMER->syscom[timer_index] = next;
+
+               /*
+                * Then check if we are really set time in the futrue.
+                */
+               diff = (int)next - (int)UBICOM32_IO_TIMER->sysval;
+               if (diff >= 0) {
+                       break;
+               }
+
+               /*
+                * Oops, we are too slow. Playing catch up.
+                *
+                * If the debugger is connected the there is a good
+                * chance that we lost time because we were in a
+                * break-point, so in this case we do not print out
+                * diagnostics.
+                */
+               asm volatile ("move.4 %0, scratchpad3"
+                             : "=r" (scratchpad3));
+               if ((scratchpad3 & 0x1) == 0) {
+                       /*
+                        * No debugger attached, print to the console
+                        */
+                       printk(KERN_EMERG "diff: %d, timer has lost %u "
+                              "ticks [rounded up]\n",
+                              -diff,
+                              (unsigned int)((-diff + cycles - 1) / cycles));
+               }
+
+               do {
+                       next += cycles;
+                       diff = (int)next - (int)UBICOM32_IO_TIMER->sysval;
+                       ticks++;
+               } while (diff < 0);
+       }
+       return ticks;
+}
+
+/*
+ * sched_clock()
+ *     Returns current time in nano-second units.
+ *
+ * Notes:
+ * 1) This is an override for the weak alias in
+ * kernel/sched_clock.c.
+ * 2) Do not use xtime_lock as this function is
+ * sometimes called with xtime_lock held.
+ * 3) We use a retry algorithm to ensure that
+ * we get a consistent value.
+ * 4) sched_clock must be overwritten if IRQ tracing
+ * is enabled because the default implementation uses
+ * the xtime_lock sequence while holding xtime_lock.
+ */
+unsigned long long sched_clock(void)
+{
+       unsigned long long my_jiffies;
+       unsigned long jiffies_top;
+       unsigned long jiffies_bottom;
+
+       do {
+               jiffies_top = jiffies_64 >> 32;
+               jiffies_bottom = jiffies_64 & 0xffffffff;
+       } while (unlikely(jiffies_top != (unsigned long)(jiffies_64 >> 32)));
+
+       my_jiffies = ((unsigned long long)jiffies_top << 32) | (jiffies_bottom);
+       return (my_jiffies - INITIAL_JIFFIES) * (NSEC_PER_SEC / HZ);
+}
+
+/*
+ * timer_free()
+ *     Free a hardware timer.
+ */
+void timer_free(int interrupt)
+{
+       unsigned int bit = interrupt - TIMER_INT(0);
+
+       /*
+        * The timer had not been allocated.
+        */
+       BUG_ON(timers & (1 << bit));
+       timers |= (1 << bit);
+}
+
+/*
+ * timer_alloc()
+ *     Allocate a hardware timer.
+ */
+int timer_alloc(void)
+{
+       unsigned int bit = find_first_bit((unsigned long *)&timers, 32);
+       if (!bit) {
+               printk(KERN_WARNING "no more free timers\n");
+               return -1;
+       }
+
+       timers &= ~(1 << bit);
+       return bit + TIMER_INT(0);
+}
+
+/*
+ * time_init()
+ *     Time init function.
+ */
+void time_init(void)
+{
+       /*
+        * Find the processor node and determine what timers are
+        * available for us.
+        */
+       timers = processor_timers();
+       if (timers == 0) {
+               printk(KERN_WARNING "no timers are available for Linux\n");
+               return;
+       }
+
+#ifdef CONFIG_GENERIC_CLOCKEVENTS
+       timer_device_init();
+#else
+       timer_tick_init();
+#endif
+}