time.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. * Copyright 2001 MontaVista Software Inc.
  3. * Author: jsun@mvista.com or jsun@junsun.net
  4. *
  5. * rtc and time ops for vr4181. Part of code is drived from
  6. * linux-vr, originally written by Bradley D. LaRonde & Michael Klar.
  7. *
  8. * This program is free software; you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation; either version 2 of the License, or (at your
  11. * option) any later version.
  12. *
  13. */
  14. #include <linux/kernel.h>
  15. #include <linux/spinlock.h>
  16. #include <linux/param.h> /* for HZ */
  17. #include <linux/time.h>
  18. #include <linux/interrupt.h>
  19. #include <asm/system.h>
  20. #include <asm/time.h>
  21. #include <asm/vr4181/vr4181.h>
  22. #define COUNTS_PER_JIFFY ((32768 + HZ/2) / HZ)
  23. /*
  24. * RTC ops
  25. */
  26. DEFINE_SPINLOCK(rtc_lock);
  27. /* per VR41xx docs, bad data can be read if between 2 counts */
  28. static inline unsigned short
  29. read_time_reg(volatile unsigned short *reg)
  30. {
  31. unsigned short value;
  32. do {
  33. value = *reg;
  34. barrier();
  35. } while (value != *reg);
  36. return value;
  37. }
  38. static unsigned long
  39. vr4181_rtc_get_time(void)
  40. {
  41. unsigned short regh, regm, regl;
  42. // why this crazy order, you ask? to guarantee that neither m
  43. // nor l wrap before all 3 read
  44. do {
  45. regm = read_time_reg(VR4181_ETIMEMREG);
  46. barrier();
  47. regh = read_time_reg(VR4181_ETIMEHREG);
  48. barrier();
  49. regl = read_time_reg(VR4181_ETIMELREG);
  50. } while (regm != read_time_reg(VR4181_ETIMEMREG));
  51. return ((regh << 17) | (regm << 1) | (regl >> 15));
  52. }
  53. static int
  54. vr4181_rtc_set_time(unsigned long timeval)
  55. {
  56. unsigned short intreg;
  57. unsigned long flags;
  58. spin_lock_irqsave(&rtc_lock, flags);
  59. intreg = *VR4181_RTCINTREG & 0x05;
  60. barrier();
  61. *VR4181_ETIMELREG = timeval << 15;
  62. *VR4181_ETIMEMREG = timeval >> 1;
  63. *VR4181_ETIMEHREG = timeval >> 17;
  64. barrier();
  65. // assume that any ints that just triggered are invalid, since the
  66. // time value is written non-atomically in 3 separate regs
  67. *VR4181_RTCINTREG = 0x05 ^ intreg;
  68. spin_unlock_irqrestore(&rtc_lock, flags);
  69. return 0;
  70. }
  71. /*
  72. * timer interrupt routine (wrapper)
  73. *
  74. * we need our own interrupt routine because we need to clear
  75. * RTC1 interrupt.
  76. */
  77. static void
  78. vr4181_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  79. {
  80. /* Clear the interrupt. */
  81. *VR4181_RTCINTREG = 0x2;
  82. /* call the generic one */
  83. timer_interrupt(irq, dev_id, regs);
  84. }
  85. /*
  86. * vr4181_time_init:
  87. *
  88. * We pick the following choices:
  89. * . we use elapsed timer as the RTC. We set some reasonable init data since
  90. * it does not persist across reset
  91. * . we use RTC1 as the system timer interrupt source.
  92. * . we use CPU counter for fast_gettimeoffset and we calivrate the cpu
  93. * frequency. In other words, we use calibrate_div64_gettimeoffset().
  94. * . we use our own timer interrupt routine which clears the interrupt
  95. * and then calls the generic high-level timer interrupt routine.
  96. *
  97. */
  98. extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
  99. static void
  100. vr4181_timer_setup(struct irqaction *irq)
  101. {
  102. /* over-write the handler to be our own one */
  103. irq->handler = vr4181_timer_interrupt;
  104. /* sets up the frequency */
  105. *VR4181_RTCL1LREG = COUNTS_PER_JIFFY;
  106. *VR4181_RTCL1HREG = 0;
  107. /* and ack any pending ints */
  108. *VR4181_RTCINTREG = 0x2;
  109. /* setup irqaction */
  110. setup_irq(VR4181_IRQ_INT1, irq);
  111. }
  112. void
  113. vr4181_init_time(void)
  114. {
  115. /* setup hookup functions */
  116. rtc_get_time = vr4181_rtc_get_time;
  117. rtc_set_time = vr4181_rtc_set_time;
  118. board_timer_setup = vr4181_timer_setup;
  119. }