Root/target/linux/ubicom32/files/arch/ubicom32/kernel/time.c

1/*
2 * arch/ubicom32/kernel/time.c
3 * Initialize the timer list and start the appropriate timers.
4 *
5 * (C) Copyright 2009, Ubicom, Inc.
6 * Copyright (C) 1991, 1992, 1995 Linus Torvalds
7 *
8 * This file is part of the Ubicom32 Linux Kernel Port.
9 *
10 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
11 * it and/or modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation, either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
16 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
17 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
18 * the GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with the Ubicom32 Linux Kernel Port. If not,
22 * see <http://www.gnu.org/licenses/>.
23 *
24 * Ubicom32 implementation derived from (with many thanks):
25 * arch/m68knommu
26 * arch/blackfin
27 * arch/parisc
28 */
29
30#include <linux/profile.h>
31#include <linux/smp.h>
32#include <asm/ip5000.h>
33#include <asm/machdep.h>
34
35/*
36 * A bitmap of the timers on the processor indicates
37 * that the timer is free or in-use.
38 */
39static unsigned int timers;
40
41/*
42 * timer_set()
43 * Init the specified compare register to go off <n> cycles from now.
44 */
45void timer_set(int timervector, unsigned int cycles)
46{
47    int idx = UBICOM32_VECTOR_TO_TIMER_INDEX(timervector);
48    UBICOM32_IO_TIMER->syscom[idx] =
49            UBICOM32_IO_TIMER->sysval + cycles;
50    ldsr_enable_vector(timervector);
51}
52
53/*
54 * timer_reset()
55 * Set/reset the timer to go off again.
56 *
57 * Because sysval is a continuous timer, this function is able
58 * to ensure that we do not have clock sku by using the previous
59 * value in syscom to set the next value for syscom.
60 *
61 * Returns the number of ticks that transpired since the last event.
62 */
63int timer_reset(int timervector, unsigned int cycles)
64{
65    /*
66     * Reset the timer in the LDSR thread to go off appropriately.
67     *
68     * Use the previous value of the timer to calculate the new stop
69     * time. This allows us to account for it taking an
70     * indeterminate amount of time to get here.
71     */
72    const int timer_index = UBICOM32_VECTOR_TO_TIMER_INDEX(timervector);
73    unsigned int prev = UBICOM32_IO_TIMER->syscom[timer_index];
74    unsigned int next = prev + cycles;
75    int scratchpad3;
76    int diff;
77    int ticks = 1;
78
79    /*
80     * If the difference is negative, we have missed at least one
81     * timer tick.
82     *
83     * TODO: Decide if we want to "ignore" time (as done below) or
84     * if we want to process time (unevenly) by calling timer_tick()
85     * lost_ticks times.
86     */
87    while (1) {
88        /*
89         * Set our future time first.
90         */
91        UBICOM32_IO_TIMER->syscom[timer_index] = next;
92
93        /*
94         * Then check if we are really set time in the futrue.
95         */
96        diff = (int)next - (int)UBICOM32_IO_TIMER->sysval;
97        if (diff >= 0) {
98            break;
99        }
100
101        /*
102         * Oops, we are too slow. Playing catch up.
103         *
104         * If the debugger is connected the there is a good
105         * chance that we lost time because we were in a
106         * break-point, so in this case we do not print out
107         * diagnostics.
108         */
109        asm volatile ("move.4 %0, scratchpad3"
110                  : "=r" (scratchpad3));
111        if ((scratchpad3 & 0x1) == 0) {
112            /*
113             * No debugger attached, print to the console
114             */
115            printk(KERN_EMERG "diff: %d, timer has lost %u "
116                   "ticks [rounded up]\n",
117                   -diff,
118                   (unsigned int)((-diff + cycles - 1) / cycles));
119        }
120
121        do {
122            next += cycles;
123            diff = (int)next - (int)UBICOM32_IO_TIMER->sysval;
124            ticks++;
125        } while (diff < 0);
126    }
127    return ticks;
128}
129
130/*
131 * sched_clock()
132 * Returns current time in nano-second units.
133 *
134 * Notes:
135 * 1) This is an override for the weak alias in
136 * kernel/sched_clock.c.
137 * 2) Do not use xtime_lock as this function is
138 * sometimes called with xtime_lock held.
139 * 3) We use a retry algorithm to ensure that
140 * we get a consistent value.
141 * 4) sched_clock must be overwritten if IRQ tracing
142 * is enabled because the default implementation uses
143 * the xtime_lock sequence while holding xtime_lock.
144 */
145unsigned long long sched_clock(void)
146{
147    unsigned long long my_jiffies;
148    unsigned long jiffies_top;
149    unsigned long jiffies_bottom;
150
151    do {
152        jiffies_top = jiffies_64 >> 32;
153        jiffies_bottom = jiffies_64 & 0xffffffff;
154    } while (unlikely(jiffies_top != (unsigned long)(jiffies_64 >> 32)));
155
156    my_jiffies = ((unsigned long long)jiffies_top << 32) | (jiffies_bottom);
157    return (my_jiffies - INITIAL_JIFFIES) * (NSEC_PER_SEC / HZ);
158}
159
160/*
161 * timer_free()
162 * Free a hardware timer.
163 */
164void timer_free(int interrupt)
165{
166    unsigned int bit = interrupt - TIMER_INT(0);
167
168    /*
169     * The timer had not been allocated.
170     */
171    BUG_ON(timers & (1 << bit));
172    timers |= (1 << bit);
173}
174
175/*
176 * timer_alloc()
177 * Allocate a hardware timer.
178 */
179int timer_alloc(void)
180{
181    unsigned int bit = find_first_bit((unsigned long *)&timers, 32);
182    if (!bit) {
183        printk(KERN_WARNING "no more free timers\n");
184        return -1;
185    }
186
187    timers &= ~(1 << bit);
188    return bit + TIMER_INT(0);
189}
190
191/*
192 * time_init()
193 * Time init function.
194 */
195void time_init(void)
196{
197    /*
198     * Find the processor node and determine what timers are
199     * available for us.
200     */
201    timers = processor_timers();
202    if (timers == 0) {
203        printk(KERN_WARNING "no timers are available for Linux\n");
204        return;
205    }
206
207#ifdef CONFIG_GENERIC_CLOCKEVENTS
208    timer_device_init();
209#else
210    timer_tick_init();
211#endif
212}
213

Archive Download this file



interactive