[ATM]: Fix dereference of uninitialized pointer in zatm
[sfrench/cifs-2.6.git] / arch / mips / vr4181 / common / time.c
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
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>
20
21 #include <asm/system.h>
22 #include <asm/time.h>
23
24 #include <asm/vr4181/vr4181.h>
25
26 #define COUNTS_PER_JIFFY ((32768 + HZ/2) / HZ)
27
28 /*
29  * RTC ops
30  */
31
32 DEFINE_SPINLOCK(rtc_lock);
33
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)
37 {
38         unsigned short value;
39         do {
40                 value = *reg;
41                 barrier();
42         } while (value != *reg);
43         return value;
44 }
45
46 static unsigned long
47 vr4181_rtc_get_time(void)
48 {
49         unsigned short regh, regm, regl;
50
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));
61 }
62
63 static int
64 vr4181_rtc_set_time(unsigned long timeval)
65 {
66         unsigned short intreg;
67         unsigned long flags;
68
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);
80
81         return 0;
82 }
83
84
85 /*
86  * timer interrupt routine (wrapper)
87  *
88  * we need our own interrupt routine because we need to clear
89  * RTC1 interrupt.
90  */
91 static void
92 vr4181_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
93 {
94         /* Clear the interrupt. */
95         *VR4181_RTCINTREG = 0x2;
96
97         /* call the generic one */
98         timer_interrupt(irq, dev_id, regs);
99 }
100
101
102 /*
103  * vr4181_time_init:
104  *
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.
113  *
114  */
115
116 extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
117
118 static void
119 vr4181_timer_setup(struct irqaction *irq)
120 {
121         /* over-write the handler to be our own one */
122         irq->handler = vr4181_timer_interrupt;
123
124         /* sets up the frequency */
125         *VR4181_RTCL1LREG = COUNTS_PER_JIFFY;
126         *VR4181_RTCL1HREG = 0;
127
128         /* and ack any pending ints */
129         *VR4181_RTCINTREG = 0x2;
130
131         /* setup irqaction */
132         setup_irq(VR4181_IRQ_INT1, irq);
133
134 }
135
136 void
137 vr4181_init_time(void)
138 {
139         /* setup hookup functions */
140         rtc_get_time = vr4181_rtc_get_time;
141         rtc_set_time = vr4181_rtc_set_time;
142
143         board_timer_setup = vr4181_timer_setup;
144 }
145