Linux 2.6.13-rc4
[linux-2.6/next.git] / arch / mips / vr4181 / common / time.c
blob17814076b6f407b932f1813beeb8f3d86d99919f
1 /*
2 * Copyright 2001 MontaVista Software Inc.
3 * Author: jsun@mvista.com or jsun@junsun.net
5 * rtc and time ops for vr4181. Part of code is drived from
6 * linux-vr, originally written by Bradley D. LaRonde & Michael Klar.
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.
15 #include <linux/kernel.h>
16 #include <linux/spinlock.h>
17 #include <linux/param.h> /* for HZ */
18 #include <linux/time.h>
19 #include <linux/interrupt.h>
21 #include <asm/system.h>
22 #include <asm/time.h>
24 #include <asm/vr4181/vr4181.h>
26 #define COUNTS_PER_JIFFY ((32768 + HZ/2) / HZ)
29 * RTC ops
32 DEFINE_SPINLOCK(rtc_lock);
34 /* per VR41xx docs, bad data can be read if between 2 counts */
35 static inline unsigned short
36 read_time_reg(volatile unsigned short *reg)
38 unsigned short value;
39 do {
40 value = *reg;
41 barrier();
42 } while (value != *reg);
43 return value;
46 static unsigned long
47 vr4181_rtc_get_time(void)
49 unsigned short regh, regm, regl;
51 // why this crazy order, you ask? to guarantee that neither m
52 // nor l wrap before all 3 read
53 do {
54 regm = read_time_reg(VR4181_ETIMEMREG);
55 barrier();
56 regh = read_time_reg(VR4181_ETIMEHREG);
57 barrier();
58 regl = read_time_reg(VR4181_ETIMELREG);
59 } while (regm != read_time_reg(VR4181_ETIMEMREG));
60 return ((regh << 17) | (regm << 1) | (regl >> 15));
63 static int
64 vr4181_rtc_set_time(unsigned long timeval)
66 unsigned short intreg;
67 unsigned long flags;
69 spin_lock_irqsave(&rtc_lock, flags);
70 intreg = *VR4181_RTCINTREG & 0x05;
71 barrier();
72 *VR4181_ETIMELREG = timeval << 15;
73 *VR4181_ETIMEMREG = timeval >> 1;
74 *VR4181_ETIMEHREG = timeval >> 17;
75 barrier();
76 // assume that any ints that just triggered are invalid, since the
77 // time value is written non-atomically in 3 separate regs
78 *VR4181_RTCINTREG = 0x05 ^ intreg;
79 spin_unlock_irqrestore(&rtc_lock, flags);
81 return 0;
86 * timer interrupt routine (wrapper)
88 * we need our own interrupt routine because we need to clear
89 * RTC1 interrupt.
91 static void
92 vr4181_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
94 /* Clear the interrupt. */
95 *VR4181_RTCINTREG = 0x2;
97 /* call the generic one */
98 timer_interrupt(irq, dev_id, regs);
103 * vr4181_time_init:
105 * We pick the following choices:
106 * . we use elapsed timer as the RTC. We set some reasonable init data since
107 * it does not persist across reset
108 * . we use RTC1 as the system timer interrupt source.
109 * . we use CPU counter for fast_gettimeoffset and we calivrate the cpu
110 * frequency. In other words, we use calibrate_div64_gettimeoffset().
111 * . we use our own timer interrupt routine which clears the interrupt
112 * and then calls the generic high-level timer interrupt routine.
116 extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
118 static void
119 vr4181_timer_setup(struct irqaction *irq)
121 /* over-write the handler to be our own one */
122 irq->handler = vr4181_timer_interrupt;
124 /* sets up the frequency */
125 *VR4181_RTCL1LREG = COUNTS_PER_JIFFY;
126 *VR4181_RTCL1HREG = 0;
128 /* and ack any pending ints */
129 *VR4181_RTCINTREG = 0x2;
131 /* setup irqaction */
132 setup_irq(VR4181_IRQ_INT1, irq);
136 void
137 vr4181_init_time(void)
139 /* setup hookup functions */
140 rtc_get_time = vr4181_rtc_get_time;
141 rtc_set_time = vr4181_rtc_set_time;
143 board_timer_setup = vr4181_timer_setup;