Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | /* * arch/v850/kernel/highres_timer.c -- High resolution timing routines * * Copyright (C) 2001,02,03 NEC Electronics Corporation * Copyright (C) 2001,02,03 Miles Bader <miles@gnu.org> * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this * archive for more details. * * Written by Miles Bader <miles@gnu.org> */ #include <asm/system.h> #include <asm/v850e_timer_d.h> #include <asm/highres_timer.h> #define HIGHRES_TIMER_USEC_SHIFT 12 /* Pre-calculated constant used for converting ticks to real time units. We initialize it to prevent it being put into BSS. */ static u32 highres_timer_usec_prescale = 1; void highres_timer_slow_tick_irq (void) __attribute__ ((noreturn)); void highres_timer_slow_tick_irq (void) { /* This is an interrupt handler, so it must be very careful to not to trash any registers. At this point, the stack-pointer (r3) has been saved in the chip ram location ENTRY_SP by the interrupt vector, so we can use it as a scratch register; we must also restore it before returning. */ asm ("ld.w %0[r0], sp;" "add 1, sp;" "st.w sp, %0[r0];" "ld.w %1[r0], sp;" /* restore pre-irq stack-pointer */ "reti" :: "i" (HIGHRES_TIMER_SLOW_TICKS_ADDR), "i" (ENTRY_SP_ADDR) : "memory"); } void highres_timer_reset (void) { V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT) = 0; HIGHRES_TIMER_SLOW_TICKS = 0; } void highres_timer_start (void) { u32 fast_tick_rate; /* Start hardware timer. */ v850e_timer_d_configure (HIGHRES_TIMER_TIMER_D_UNIT, HIGHRES_TIMER_SLOW_TICK_RATE); fast_tick_rate = (V850E_TIMER_D_BASE_FREQ >> V850E_TIMER_D_DIVLOG2 (HIGHRES_TIMER_TIMER_D_UNIT)); /* The obvious way of calculating microseconds from fast ticks is to do: usec = fast_ticks * 10^6 / fast_tick_rate However, divisions are much slower than multiplications, and the above calculation can overflow, so we do this instead: usec = fast_ticks * (10^6 * 2^12 / fast_tick_rate) / 2^12 since we can pre-calculate (10^6 * (2^12 / fast_tick_rate)) and use a shift for dividing by 2^12, this avoids division, and is almost as accurate (it differs by about 2 microseconds at the extreme value of the fast-tick counter's ranger). */ highres_timer_usec_prescale = ((1000000 << HIGHRES_TIMER_USEC_SHIFT) / fast_tick_rate); /* Enable the interrupt (which is hardwired to this use), and give it the highest priority. */ V850E_INTC_IC (IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT)) = 0; } void highres_timer_stop (void) { /* Stop the timer. */ V850E_TIMER_D_TMCD (HIGHRES_TIMER_TIMER_D_UNIT) = V850E_TIMER_D_TMCD_CAE; /* Disable its interrupt, just in case. */ v850e_intc_disable_irq (IRQ_INTCMD (HIGHRES_TIMER_TIMER_D_UNIT)); } inline void highres_timer_read_ticks (u32 *slow_ticks, u32 *fast_ticks) { int flags; u32 fast_ticks_1, fast_ticks_2, _slow_ticks; local_irq_save (flags); fast_ticks_1 = V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT); _slow_ticks = HIGHRES_TIMER_SLOW_TICKS; fast_ticks_2 = V850E_TIMER_D_TMD (HIGHRES_TIMER_TIMER_D_UNIT); local_irq_restore (flags); if (fast_ticks_2 < fast_ticks_1) _slow_ticks++; *slow_ticks = _slow_ticks; *fast_ticks = fast_ticks_2; } inline void highres_timer_ticks_to_timeval (u32 slow_ticks, u32 fast_ticks, struct timeval *tv) { unsigned long sec, sec_rem, usec; usec = ((fast_ticks * highres_timer_usec_prescale) >> HIGHRES_TIMER_USEC_SHIFT); sec = slow_ticks / HIGHRES_TIMER_SLOW_TICK_RATE; sec_rem = slow_ticks % HIGHRES_TIMER_SLOW_TICK_RATE; usec += sec_rem * (1000000 / HIGHRES_TIMER_SLOW_TICK_RATE); tv->tv_sec = sec; tv->tv_usec = usec; } void highres_timer_read (struct timeval *tv) { u32 fast_ticks, slow_ticks; highres_timer_read_ticks (&slow_ticks, &fast_ticks); highres_timer_ticks_to_timeval (slow_ticks, fast_ticks, tv); } |