highres_timer.c 3.9 KB

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