time.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:10k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  *
  3.  * Copyright (C) 2001 MontaVista Software, ppopov@mvista.com
  4.  * Copied and modified Carsten Langgaard's time.c
  5.  *
  6.  * Carsten Langgaard, carstenl@mips.com
  7.  * Copyright (C) 1999,2000 MIPS Technologies, Inc.  All rights reserved.
  8.  *
  9.  * ########################################################################
  10.  *
  11.  *  This program is free software; you can distribute it and/or modify it
  12.  *  under the terms of the GNU General Public License (Version 2) as
  13.  *  published by the Free Software Foundation.
  14.  *
  15.  *  This program is distributed in the hope it will be useful, but WITHOUT
  16.  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  17.  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  18.  *  for more details.
  19.  *
  20.  *  You should have received a copy of the GNU General Public License along
  21.  *  with this program; if not, write to the Free Software Foundation, Inc.,
  22.  *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  23.  *
  24.  * ########################################################################
  25.  *
  26.  * Setting up the clock on the MIPS boards.
  27.  *
  28.  */
  29. #include <linux/types.h>
  30. #include <linux/config.h>
  31. #include <linux/init.h>
  32. #include <linux/kernel_stat.h>
  33. #include <linux/sched.h>
  34. #include <linux/spinlock.h>
  35. #include <asm/mipsregs.h>
  36. #include <asm/ptrace.h>
  37. #include <asm/time.h>
  38. #include <asm/hardirq.h>
  39. #include <asm/div64.h>
  40. #include <asm/au1000.h>
  41. #include <linux/mc146818rtc.h>
  42. #include <linux/timex.h>
  43. extern void startup_match20_interrupt(void);
  44. extern volatile unsigned long wall_jiffies;
  45. unsigned long missed_heart_beats = 0;
  46. static unsigned long r4k_offset; /* Amount to increment compare reg each time */
  47. static unsigned long r4k_cur;    /* What counter should be at next timer irq */
  48. extern rwlock_t xtime_lock;
  49. unsigned int mips_counter_frequency = 0;
  50. /* Cycle counter value at the previous timer interrupt.. */
  51. static unsigned int timerhi = 0, timerlo = 0;
  52. #ifdef CONFIG_PM
  53. #define MATCH20_INC 328
  54. extern void startup_match20_interrupt(void);
  55. static unsigned long last_pc0, last_match20;
  56. #endif
  57. static inline void ack_r4ktimer(unsigned long newval)
  58. {
  59. write_32bit_cp0_register(CP0_COMPARE, newval);
  60. }
  61. /*
  62.  * There are a lot of conceptually broken versions of the MIPS timer interrupt
  63.  * handler floating around.  This one is rather different, but the algorithm
  64.  * is provably more robust.
  65.  */
  66. unsigned long wtimer;
  67. void mips_timer_interrupt(struct pt_regs *regs)
  68. {
  69. int irq = 63;
  70. unsigned long count;
  71. int cpu = smp_processor_id();
  72. irq_enter(cpu, irq);
  73. kstat.irqs[cpu][irq]++;
  74. #ifdef CONFIG_PM
  75. printk(KERN_ERR "Unexpected CP0 interruptn");
  76. regs->cp0_status &= ~IE_IRQ5; /* disable CP0 interrupt */
  77. return;
  78. #endif
  79. if (r4k_offset == 0)
  80. goto null;
  81. do {
  82. count = read_32bit_cp0_register(CP0_COUNT);
  83. timerhi += (count < timerlo);   /* Wrap around */
  84. timerlo = count;
  85. kstat.irqs[0][irq]++;
  86. do_timer(regs);
  87. r4k_cur += r4k_offset;
  88. ack_r4ktimer(r4k_cur);
  89. } while (((unsigned long)read_32bit_cp0_register(CP0_COUNT)
  90.          - r4k_cur) < 0x7fffffff);
  91. irq_exit(cpu, irq);
  92. if (softirq_pending(cpu))
  93. do_softirq();
  94. return;
  95. null:
  96. ack_r4ktimer(0);
  97. }
  98. #ifdef CONFIG_PM
  99. void counter0_irq(int irq, void *dev_id, struct pt_regs *regs)
  100. {
  101. unsigned long pc0;
  102. int time_elapsed;
  103. static int jiffie_drift = 0;
  104. kstat.irqs[0][irq]++;
  105. if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) {
  106. /* should never happen! */
  107. printk(KERN_WARNING "counter 0 w status erorn");
  108. return;
  109. }
  110. pc0 = au_readl(SYS_TOYREAD);
  111. if (pc0 < last_match20) {
  112. /* counter overflowed */
  113. time_elapsed = (0xffffffff - last_match20) + pc0;
  114. }
  115. else {
  116. time_elapsed = pc0 - last_match20;
  117. }
  118. while (time_elapsed > 0) {
  119. do_timer(regs);
  120. time_elapsed -= MATCH20_INC;
  121. last_match20 += MATCH20_INC;
  122. jiffie_drift++;
  123. }
  124. last_pc0 = pc0;
  125. au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
  126. au_sync();
  127. /* our counter ticks at 10.009765625 ms/tick, we we're running
  128.  * almost 10uS too slow per tick.
  129.  */
  130. if (jiffie_drift >= 999) {
  131. jiffie_drift -= 999;
  132. do_timer(regs); /* increment jiffies by one */
  133. }
  134. }
  135. #endif
  136. /*
  137.  * Figure out the r4k offset, the amount to increment the compare
  138.  * register for each time tick.
  139.  * Use the Programmable Counter 1 to do this.
  140.  */
  141. unsigned long cal_r4koff(void)
  142. {
  143. unsigned long count;
  144. unsigned long cpu_speed;
  145. unsigned long start, end;
  146. unsigned long counter;
  147. int trim_divide = 16;
  148. unsigned long flags;
  149. save_and_cli(flags);
  150. counter = au_readl(SYS_COUNTER_CNTRL);
  151. au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL);
  152. while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
  153. au_writel(trim_divide-1, SYS_RTCTRIM); /* RTC now ticks at 32.768/16 kHz */
  154. while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
  155. while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
  156. au_writel (0, SYS_TOYWRITE);
  157. while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
  158. start = au_readl(SYS_RTCREAD);
  159. start += 2;
  160. /* wait for the beginning of a new tick */
  161. while (au_readl(SYS_RTCREAD) < start);
  162. /* Start r4k counter. */
  163. write_32bit_cp0_register(CP0_COUNT, 0);
  164. end = start + (32768 / trim_divide)/2; /* wait 0.5 seconds */
  165. while (end > au_readl(SYS_RTCREAD));
  166. count = read_32bit_cp0_register(CP0_COUNT);
  167. cpu_speed = count * 2;
  168. mips_counter_frequency = count;
  169. set_au1000_uart_baud_base(((cpu_speed) / 4) / 16);
  170. restore_flags(flags);
  171. return (cpu_speed / HZ);
  172. }
  173. void __init time_init(void)
  174. {
  175.         unsigned int est_freq;
  176. printk("calculating r4koff... ");
  177. r4k_offset = cal_r4koff();
  178. printk("%08lx(%d)n", r4k_offset, (int) r4k_offset);
  179. //est_freq = 2*r4k_offset*HZ;
  180. est_freq = r4k_offset*HZ;
  181. est_freq += 5000;    /* round */
  182. est_freq -= est_freq%10000;
  183. printk("CPU frequency %d.%02d MHzn", est_freq/1000000,
  184.        (est_freq%1000000)*100/1000000);
  185. set_au1000_speed(est_freq);
  186. set_au1000_lcd_clock(); // program the LCD clock
  187. r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset);
  188. write_32bit_cp0_register(CP0_COMPARE, r4k_cur);
  189. /* no RTC on the pb1000 */
  190. xtime.tv_sec = 0;
  191. xtime.tv_usec = 0;
  192. #ifdef CONFIG_PM
  193. /*
  194.  * setup counter 0, since it keeps ticking after a
  195.  * 'wait' instruction has been executed. The CP0 timer and
  196.  * counter 1 do NOT continue running after 'wait'
  197.  *
  198.  * It's too early to call request_irq() here, so we handle
  199.  * counter 0 interrupt as a special irq and it doesn't show
  200.  * up under /proc/interrupts.
  201.  */
  202. while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
  203. au_writel(0, SYS_TOYWRITE);
  204. while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
  205. au_writel(au_readl(SYS_WAKEMSK) | (1<<8), SYS_WAKEMSK);
  206. au_writel(~0, SYS_WAKESRC);
  207. au_sync();
  208. while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
  209. /* setup match20 to interrupt once every 10ms */
  210. last_pc0 = last_match20 = au_readl(SYS_TOYREAD);
  211. au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
  212. au_sync();
  213. while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
  214. startup_match20_interrupt();
  215. #endif
  216. //set_cp0_status(ALLINTS);
  217. au_sync();
  218. }
  219. /* This is for machines which generate the exact clock. */
  220. #define USECS_PER_JIFFY (1000000/HZ)
  221. #define USECS_PER_JIFFY_FRAC (0x100000000*1000000/HZ&0xffffffff)
  222. static unsigned long
  223. div64_32(unsigned long v1, unsigned long v2, unsigned long v3)
  224. {
  225. unsigned long r0;
  226. do_div64_32(r0, v1, v2, v3);
  227. return r0;
  228. }
  229. static unsigned long do_fast_gettimeoffset(void)
  230. {
  231. #ifdef CONFIG_PM
  232. unsigned long pc0;
  233. unsigned long offset;
  234. pc0 = au_readl(SYS_TOYREAD);
  235. if (pc0 < last_pc0) {
  236. offset = 0xffffffff - last_pc0 + pc0;
  237. printk("offset over: %xn", (unsigned)offset);
  238. }
  239. else {
  240. offset = (unsigned long)(((pc0 - last_pc0) * 305) / 10);
  241. }
  242. if ((pc0-last_pc0) > 2*MATCH20_INC) {
  243. printk("huge offset %x, last_pc0 %x last_match20 %x pc0 %xn",
  244. (unsigned)offset, (unsigned)last_pc0,
  245. (unsigned)last_match20, (unsigned)pc0);
  246. }
  247. au_sync();
  248. return offset;
  249. #else
  250. u32 count;
  251. unsigned long res, tmp;
  252. unsigned long r0;
  253. /* Last jiffy when do_fast_gettimeoffset() was called. */
  254. static unsigned long last_jiffies=0;
  255. unsigned long quotient;
  256. /*
  257.  * Cached "1/(clocks per usec)*2^32" value.
  258.  * It has to be recalculated once each jiffy.
  259.  */
  260. static unsigned long cached_quotient=0;
  261. tmp = jiffies;
  262. quotient = cached_quotient;
  263. if (tmp && last_jiffies != tmp) {
  264. last_jiffies = tmp;
  265. if (last_jiffies != 0) {
  266. r0 = div64_32(timerhi, timerlo, tmp);
  267. quotient = div64_32(USECS_PER_JIFFY, USECS_PER_JIFFY_FRAC, r0);
  268. cached_quotient = quotient;
  269. }
  270. }
  271. /* Get last timer tick in absolute kernel time */
  272. count = read_32bit_cp0_register(CP0_COUNT);
  273. /* .. relative to previous jiffy (32 bits is enough) */
  274. count -= timerlo;
  275. __asm__("multut%1,%2nt"
  276. "mfhit%0"
  277. :"=r" (res)
  278. :"r" (count),
  279.  "r" (quotient));
  280. /*
  281.    * Due to possible jiffies inconsistencies, we need to check
  282.  * the result so that we'll get a timer that is monotonic.
  283.  */
  284. if (res >= USECS_PER_JIFFY)
  285. res = USECS_PER_JIFFY-1;
  286. return res;
  287. #endif
  288. }
  289. void do_gettimeofday(struct timeval *tv)
  290. {
  291. unsigned int flags;
  292. read_lock_irqsave (&xtime_lock, flags);
  293. *tv = xtime;
  294. tv->tv_usec += do_fast_gettimeoffset();
  295. /*
  296.  * xtime is atomically updated in timer_bh. jiffies - wall_jiffies
  297.  * is nonzero if the timer bottom half hasnt executed yet.
  298.  */
  299. if (jiffies - wall_jiffies)
  300. tv->tv_usec += USECS_PER_JIFFY;
  301. read_unlock_irqrestore (&xtime_lock, flags);
  302. if (tv->tv_usec >= 1000000) {
  303. tv->tv_usec -= 1000000;
  304. tv->tv_sec++;
  305. }
  306. }
  307. void do_settimeofday(struct timeval *tv)
  308. {
  309. write_lock_irq (&xtime_lock);
  310. /* This is revolting. We need to set the xtime.tv_usec correctly.
  311.  * However, the value in this location is is value at the last tick.
  312.  * Discover what correction gettimeofday would have done, and then
  313.  * undo it!
  314.  */
  315. tv->tv_usec -= do_fast_gettimeoffset();
  316. if (tv->tv_usec < 0) {
  317. tv->tv_usec += 1000000;
  318. tv->tv_sec--;
  319. }
  320. xtime = *tv;
  321. time_adjust = 0; /* stop active adjtime() */
  322. time_status |= STA_UNSYNC;
  323. time_maxerror = NTP_PHASE_LIMIT;
  324. time_esterror = NTP_PHASE_LIMIT;
  325. write_unlock_irq (&xtime_lock);
  326. }